use crate::libvirt_lib_structures::XMLUuid; /// VirtWeb specific metadata #[derive(serde::Serialize, serde::Deserialize, Default, Debug, Clone)] #[serde(rename = "virtweb", default)] pub struct DomainMetadataVirtWebXML { #[serde(rename = "@xmlns:virtweb", default)] pub ns: String, #[serde(skip_serializing_if = "Option::is_none")] pub group: Option, } /// Domain metadata #[derive(serde::Serialize, serde::Deserialize, Default, Debug, Clone)] #[serde(rename = "metadata")] pub struct DomainMetadataXML { #[serde(rename = "virtweb:metadata", default)] pub virtweb: DomainMetadataVirtWebXML, } /// OS information #[derive(Debug, Clone, serde::Serialize, serde::Deserialize)] #[serde(rename = "os")] pub struct OSXML { #[serde(rename = "@firmware", default)] pub firmware: String, pub r#type: OSTypeXML, pub loader: Option, pub smbios: Option, } /// OS Type information #[derive(Debug, Clone, serde::Serialize, serde::Deserialize)] #[serde(rename = "os")] pub struct OSTypeXML { #[serde(rename = "@arch")] pub arch: String, #[serde(rename = "@machine")] pub machine: String, #[serde(rename = "$value")] pub body: String, } /// OS Loader information #[derive(Debug, Clone, serde::Serialize, serde::Deserialize)] #[serde(rename = "loader")] pub struct OSLoaderXML { #[serde(rename = "@secure")] pub secure: String, } /// SMBIOS System information #[derive(Debug, Clone, serde::Serialize, serde::Deserialize)] #[serde(rename = "smbios")] pub struct OSSMBiosXML { #[serde(rename = "@mode")] pub mode: String, } /// Hypervisor features #[derive(serde::Serialize, serde::Deserialize, Clone, Default, Debug)] #[serde(rename = "features")] pub struct FeaturesXML { pub acpi: ACPIXML, } /// ACPI feature #[derive(serde::Serialize, serde::Deserialize, Clone, Default, Debug)] #[serde(rename = "acpi")] pub struct ACPIXML {} #[derive(Debug, Clone, serde::Serialize, serde::Deserialize)] #[serde(rename = "mac")] pub struct NetMacAddress { #[serde(rename = "@address")] pub address: String, } #[derive(Debug, Clone, serde::Serialize, serde::Deserialize)] #[serde(rename = "source")] pub struct NetIntSourceXML { #[serde(rename = "@network")] pub network: String, } #[derive(Debug, Clone, serde::Serialize, serde::Deserialize)] #[serde(rename = "model")] pub struct NetIntModelXML { #[serde(rename = "@type")] pub r#type: String, } #[derive(Debug, Clone, serde::Serialize, serde::Deserialize)] #[serde(rename = "filterref")] pub struct NetIntFilterParameterXML { #[serde(rename = "@name")] pub name: String, #[serde(rename = "@value")] pub value: String, } #[derive(Debug, Clone, serde::Serialize, serde::Deserialize)] #[serde(rename = "filterref")] pub struct NetIntfilterRefXML { #[serde(rename = "@filter")] pub filter: String, #[serde(rename = "parameter", default)] pub parameters: Vec, } #[derive(Debug, Clone, serde::Serialize, serde::Deserialize)] #[serde(rename = "interface")] pub struct DomainNetInterfaceXML { #[serde(rename = "@type")] pub r#type: String, pub mac: NetMacAddress, #[serde(skip_serializing_if = "Option::is_none")] pub source: Option, #[serde(skip_serializing_if = "Option::is_none")] pub model: Option, #[serde(skip_serializing_if = "Option::is_none")] pub filterref: Option, } #[derive(Debug, Clone, serde::Serialize, serde::Deserialize)] #[serde(rename = "input")] pub struct DomainInputXML { #[serde(rename = "@type")] pub r#type: String, } #[derive(Debug, Clone, serde::Serialize, serde::Deserialize)] #[serde(rename = "backend")] pub struct TPMBackendXML { #[serde(rename = "@type")] pub r#type: String, #[serde(rename = "@version")] pub r#version: String, } #[derive(Debug, Clone, serde::Serialize, serde::Deserialize)] #[serde(rename = "tpm")] pub struct TPMDeviceXML { #[serde(rename = "@model")] pub model: String, pub backend: TPMBackendXML, } /// Devices information #[derive(Debug, Clone, serde::Serialize, serde::Deserialize)] #[serde(rename = "devices")] pub struct DevicesXML { /// Graphics (used for VNC) #[serde(skip_serializing_if = "Option::is_none")] pub graphics: Option, /// Graphics (used for VNC) #[serde(skip_serializing_if = "Option::is_none")] pub video: Option, /// Disks (used for storage) #[serde(default, rename = "disk", skip_serializing_if = "Vec::is_empty")] pub disks: Vec, /// Networks cards #[serde(default, rename = "interface", skip_serializing_if = "Vec::is_empty")] pub net_interfaces: Vec, /// Input devices #[serde(default, rename = "input", skip_serializing_if = "Vec::is_empty")] pub inputs: Vec, /// TPM device #[serde(skip_serializing_if = "Option::is_none")] pub tpm: Option, } /// Graphics information #[derive(Debug, Clone, serde::Serialize, serde::Deserialize)] #[serde(rename = "graphics")] pub struct GraphicsXML { #[serde(rename = "@type")] pub r#type: String, #[serde(rename = "@socket")] pub socket: String, } /// Video device information #[derive(Debug, Clone, serde::Serialize, serde::Deserialize)] #[serde(rename = "video")] pub struct VideoXML { pub model: VideoModelXML, } /// Video model device information #[derive(Debug, Clone, serde::Serialize, serde::Deserialize)] #[serde(rename = "model")] pub struct VideoModelXML { #[serde(rename = "@type")] pub r#type: String, } /// Disk information #[derive(Debug, Clone, serde::Serialize, serde::Deserialize)] #[serde(rename = "disk")] pub struct DiskXML { #[serde(rename = "@type")] pub r#type: String, #[serde(rename = "@device")] pub r#device: String, pub driver: DiskDriverXML, pub source: DiskSourceXML, pub target: DiskTargetXML, #[serde(skip_serializing_if = "Option::is_none")] pub readonly: Option, pub boot: DiskBootXML, #[serde(skip_serializing_if = "Option::is_none")] pub address: Option, } #[derive(Debug, Clone, serde::Serialize, serde::Deserialize)] #[serde(rename = "driver")] pub struct DiskDriverXML { #[serde(rename = "@name")] pub name: String, #[serde(rename = "@type")] pub r#type: String, #[serde(default, rename = "@cache")] pub r#cache: String, } #[derive(Debug, Clone, serde::Serialize, serde::Deserialize)] #[serde(rename = "source")] pub struct DiskSourceXML { #[serde(rename = "@file")] pub file: String, } #[derive(Debug, Clone, serde::Serialize, serde::Deserialize)] #[serde(rename = "target")] pub struct DiskTargetXML { #[serde(rename = "@dev")] pub dev: String, #[serde(rename = "@bus")] pub bus: String, } #[derive(Debug, Clone, serde::Serialize, serde::Deserialize)] #[serde(rename = "readonly")] pub struct DiskReadOnlyXML {} #[derive(Debug, Clone, serde::Serialize, serde::Deserialize)] #[serde(rename = "boot")] pub struct DiskBootXML { #[serde(rename = "@order")] pub order: String, } #[derive(Debug, Clone, serde::Serialize, serde::Deserialize)] #[serde(rename = "address")] pub struct DiskAddressXML { #[serde(rename = "@type")] pub r#type: String, #[serde( default, skip_serializing_if = "Option::is_none", rename = "@controller" )] pub r#controller: Option, #[serde(rename = "@bus")] pub r#bus: String, #[serde(default, skip_serializing_if = "Option::is_none", rename = "@target")] pub r#target: Option, #[serde(default, skip_serializing_if = "Option::is_none", rename = "@unit")] pub r#unit: Option, } /// Domain RAM information #[derive(Debug, Clone, serde::Serialize, serde::Deserialize)] #[serde(rename = "memory")] pub struct DomainMemoryXML { #[serde(rename = "@unit")] pub unit: String, #[serde(rename = "$value")] pub memory: usize, } #[derive(Debug, Clone, serde::Serialize, serde::Deserialize)] #[serde(rename = "topology")] pub struct DomainCPUTopology { #[serde(rename = "@sockets")] pub sockets: usize, #[serde(rename = "@cores")] pub cores: usize, #[serde(rename = "@threads")] pub threads: usize, } #[derive(Debug, Clone, serde::Serialize, serde::Deserialize)] #[serde(rename = "cpu")] pub struct DomainVCPUXML { #[serde(rename = "$value")] pub body: usize, } #[derive(Debug, Clone, serde::Serialize, serde::Deserialize)] #[serde(rename = "cpu")] pub struct DomainCPUXML { #[serde(rename = "@mode")] pub mode: String, pub topology: Option, } #[derive(Debug, Clone, serde::Serialize, serde::Deserialize)] #[serde(rename = "entry")] pub struct OEMStringEntryXML { #[serde(rename = "$text")] pub content: String, } #[derive(Debug, Clone, serde::Serialize, serde::Deserialize)] #[serde(rename = "oemStrings")] pub struct OEMStringsXML { #[serde(rename = "entry")] pub entries: Vec, } #[derive(Debug, Clone, serde::Serialize, serde::Deserialize)] #[serde(rename = "sysinfo")] pub struct SysInfoXML { #[serde(rename = "@type")] pub r#type: String, #[serde(rename = "oemStrings")] pub oem_strings: Option, } /// Domain information, see https://libvirt.org/formatdomain.html #[derive(Debug, Clone, serde::Serialize, serde::Deserialize)] #[serde(rename = "domain")] pub struct DomainXML { /// Domain type (kvm) #[serde(rename = "@type")] pub r#type: String, pub name: String, pub uuid: Option, pub genid: Option, pub title: Option, pub description: Option, #[serde(default, skip_serializing_if = "Option::is_none")] pub metadata: Option, pub os: OSXML, #[serde(default)] pub features: FeaturesXML, pub devices: DevicesXML, /// The maximum allocation of memory for the guest at boot time pub memory: DomainMemoryXML, /// Number of vCPU pub vcpu: DomainVCPUXML, /// CPU information pub cpu: DomainCPUXML, /// SMBios strings pub sysinfo: Option, /// Behavior when guest state change pub on_poweroff: String, pub on_reboot: String, pub on_crash: String, } const METADATA_START_MARKER: &str = ""; const METADATA_END_MARKER: &str = ""; impl DomainXML { /// Decode Domain structure from XML definition pub fn parse_xml(xml: &str) -> anyhow::Result { let mut res: Self = quick_xml::de::from_str(xml)?; // Handle custom metadata parsing issue // // https://github.com/tafia/quick-xml/pull/797 if xml.contains(METADATA_START_MARKER) && xml.contains(METADATA_END_MARKER) { let s = xml .split_once(METADATA_START_MARKER) .unwrap() .1 .split_once(METADATA_END_MARKER) .unwrap() .0; let s = format!("{s}"); let metadata: DomainMetadataVirtWebXML = quick_xml::de::from_str(&s)?; res.metadata = Some(DomainMetadataXML { virtweb: metadata }); } Ok(res) } /// Turn this domain into its XML definition pub fn as_xml(&self) -> anyhow::Result { Ok(quick_xml::se::to_string(self)?) } } /// Domain state #[derive(serde::Serialize, Debug, Copy, Clone)] pub enum DomainState { NoState, Running, Blocked, Paused, Shutdown, Shutoff, Crashed, PowerManagementSuspended, Other, }