All checks were successful
continuous-integration/drone/push Build is passing
426 lines
12 KiB
Rust
426 lines
12 KiB
Rust
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<String>,
|
|
}
|
|
|
|
/// 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<OSLoaderXML>,
|
|
pub smbios: Option<OSSMBiosXML>,
|
|
}
|
|
|
|
/// 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<NetIntFilterParameterXML>,
|
|
}
|
|
|
|
#[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<NetIntSourceXML>,
|
|
#[serde(skip_serializing_if = "Option::is_none")]
|
|
pub model: Option<NetIntModelXML>,
|
|
#[serde(skip_serializing_if = "Option::is_none")]
|
|
pub filterref: Option<NetIntfilterRefXML>,
|
|
}
|
|
|
|
#[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<GraphicsXML>,
|
|
|
|
/// Graphics (used for VNC)
|
|
#[serde(skip_serializing_if = "Option::is_none")]
|
|
pub video: Option<VideoXML>,
|
|
|
|
/// Disks (used for storage)
|
|
#[serde(default, rename = "disk", skip_serializing_if = "Vec::is_empty")]
|
|
pub disks: Vec<DiskXML>,
|
|
|
|
/// Networks cards
|
|
#[serde(default, rename = "interface", skip_serializing_if = "Vec::is_empty")]
|
|
pub net_interfaces: Vec<DomainNetInterfaceXML>,
|
|
|
|
/// Input devices
|
|
#[serde(default, rename = "input", skip_serializing_if = "Vec::is_empty")]
|
|
pub inputs: Vec<DomainInputXML>,
|
|
|
|
/// TPM device
|
|
#[serde(skip_serializing_if = "Option::is_none")]
|
|
pub tpm: Option<TPMDeviceXML>,
|
|
}
|
|
|
|
/// 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<DiskReadOnlyXML>,
|
|
pub boot: DiskBootXML,
|
|
#[serde(skip_serializing_if = "Option::is_none")]
|
|
pub address: Option<DiskAddressXML>,
|
|
}
|
|
|
|
#[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<String>,
|
|
#[serde(rename = "@bus")]
|
|
pub r#bus: String,
|
|
#[serde(default, skip_serializing_if = "Option::is_none", rename = "@target")]
|
|
pub r#target: Option<String>,
|
|
#[serde(default, skip_serializing_if = "Option::is_none", rename = "@unit")]
|
|
pub r#unit: Option<String>,
|
|
}
|
|
|
|
/// 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<DomainCPUTopology>,
|
|
}
|
|
|
|
#[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<OEMStringEntryXML>,
|
|
}
|
|
|
|
#[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<OEMStringsXML>,
|
|
}
|
|
|
|
/// 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<XMLUuid>,
|
|
pub genid: Option<uuid::Uuid>,
|
|
pub title: Option<String>,
|
|
pub description: Option<String>,
|
|
#[serde(default, skip_serializing_if = "Option::is_none")]
|
|
pub metadata: Option<DomainMetadataXML>,
|
|
|
|
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<SysInfoXML>,
|
|
|
|
/// Behavior when guest state change
|
|
pub on_poweroff: String,
|
|
pub on_reboot: String,
|
|
pub on_crash: String,
|
|
}
|
|
|
|
const METADATA_START_MARKER: &str =
|
|
"<virtweb:metadata xmlns:virtweb=\"https://virtweb.communiquons.org\">";
|
|
const METADATA_END_MARKER: &str = "</virtweb:metadata>";
|
|
|
|
impl DomainXML {
|
|
/// Decode Domain structure from XML definition
|
|
pub fn parse_xml(xml: &str) -> anyhow::Result<Self> {
|
|
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!("<virtweb>{s}</virtweb>");
|
|
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<String> {
|
|
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,
|
|
}
|