use log::*; use super::*; #[derive(Default)] pub struct DomainBuilder { domain: Domain, } impl DomainBuilder { /// Sets the name attribute of the domain. /// /// From libvirt documentation: /// /// > The content of the name element provides a short /// > name for the virtual machine. This name should consist /// > only of alphanumeric characters and is required to be /// > unique within the scope of a single host. It is often /// > used to form the filename for storing the persistent /// > configuration file. pub fn name(mut self, name: &str) -> Self { self.domain.name = name.to_owned(); self } #[cfg(test)] pub fn uuid(mut self, uuid: uuid::Uuid) -> Self { self.domain.uuid = uuid; self } /// Sets the title attribute of the domain. /// /// From libvirt documentation: /// /// > The optional element title provides space for a short /// > description of the domain. The title should not /// > contain any newlines. pub fn title(mut self, title: &str) -> Self { self.domain.title = Some(title.to_owned()); self } /// Sets the description attribute of the domain. /// /// From libvirt documentation: /// /// > The content of the description element provides a /// > human readable description of the virtual machine. /// > This data is not used by libvirt in any way, it can /// > contain any information the user wants. pub fn description(mut self, desc: &str) -> Self { self.domain.description = Some(desc.to_owned()); self } /// Sets what action is performed on the domain when /// powered off. pub fn poweroff_action(mut self, action: PowerAction) -> Self { self.domain.on_poweroff = Some(action); self } /// Sets what action is performed on the domain when /// rebooted. pub fn reboot_action(mut self, action: PowerAction) -> Self { self.domain.on_reboot = Some(action); self } /// Sets what action is performed on the domain when /// it crashes. pub fn crash_action(mut self, action: PowerAction) -> Self { self.domain.on_crash = Some(action); self } /// Adds a network interface to the domain. pub fn net_device

(mut self, net_func: P) -> Self where P: Fn(IfaceBuilder) -> IfaceBuilder, { let netdev = net_func(IfaceBuilder::new()); self.domain.devices.push(Device::Interface { interface: netdev.build(), }); self } /// Adds a disk device to the domain. pub fn disk_device

(mut self, disk_func: P) -> Self where P: Fn(DiskBuilder) -> DiskBuilder, { let diskdev = disk_func(DiskBuilder::new()); self.domain.devices.push(Device::Disk { disk: diskdev.build(), }); self } pub fn memory(mut self, size: SizeInfo) -> Self { self.domain.memory = size; self } pub fn serial_device(mut self, serial_type: SerialType) -> Self { self.domain.devices.push(Device::Console { console: SerialDevice { r#type: serial_type, }, }); self } pub fn cpu_topology(mut self, sockets: u8, dies: u8, cores: u8, threads: u8) -> Self { self.domain.cpu.topology = CpuTopology { sockets, dies, cores, threads, }; self.domain.vcpu.value = (sockets * dies * cores * threads).into(); self } pub fn build(mut self) -> Domain { if self.domain.devices.disk.iter().any(|d| d.boot.is_some()) { debug!("Disk has boot order, removing style boot..."); self.domain.os.boot = None; } self.domain } } pub struct IfaceBuilder { iface: NetDevice, } impl IfaceBuilder { fn new() -> Self { Self { iface: NetDevice::default(), } } /// Uses a bridge as the source device. pub fn with_bridge(mut self, bridge: &str) -> Self { self.iface.r#type = IfaceType::Bridge; self.iface.source.bridge = Some(bridge.to_owned()); self } /// Uses a libvirt-defined network as the source device. pub fn with_network(mut self, network: &str) -> Self { self.iface.r#type = IfaceType::Network; self.iface.source.network = Some(network.to_owned()); self } /// Defines the MAC address the interface should use. pub fn mac_addr(mut self, addr: &MacAddr) -> Self { self.iface.mac = Some(NetMac { address: addr.clone(), }); self } /// Defines the model type to emulate. /// /// By default, interfaces will be created as virtio. To /// see what models you can use on your hypervisor, run: /// /// ``` /// qemu-system-x86_64 -net nic,model=? /dev/null /// ``` pub fn model(mut self, model: &str) -> Self { self.iface.model.r#type = model.to_owned(); self } pub fn target_dev(mut self, name: &str) -> Self { self.iface.target = Some(NetTarget { dev: name.to_owned(), }); self } fn build(self) -> NetDevice { self.iface } } #[derive(Default)] pub struct DiskBuilder { disk: DiskDevice, } impl DiskBuilder { pub fn new() -> Self { Self::default() } /// Set the source for a file pub fn file_source(mut self, filename: &str) -> Self { self.disk.r#type = DiskType::File; self.disk.source.file = Some(filename.to_owned()); self } /// Set the source for a block device pub fn block_source(mut self, dev: &str) -> Self { self.disk.r#type = DiskType::Block; self.disk.source.dev = Some(dev.to_owned()); self } /// Set the source for a volume drive (e.g., zfs) pub fn volume_source(mut self, pool: &str, volume: &str) -> Self { self.disk.r#type = DiskType::Volume; self.disk.source.pool = Some(pool.to_owned()); self.disk.source.volume = Some(volume.to_owned()); self } /// Set the target for the disk to attach to. pub fn target(mut self, dev: &str, bus: &str) -> Self { self.disk.target.bus = bus.to_owned(); self.disk.target.dev = dev.to_owned(); self } pub fn boot_order(mut self, order: u32) -> Self { self.disk.boot = Some(DiskBoot { order }); self } pub fn device_type(mut self, devtype: DiskDeviceType) -> Self { self.disk.device = devtype; self } pub fn build(self) -> DiskDevice { self.disk } }