nazrin/nzrd/src/ctrl/virtxml/build.rs
2023-01-16 20:42:01 -08:00

246 lines
6.6 KiB
Rust

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<P>(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<P>(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 <os/> 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
}
}