Pierre HUBERT 644fd6f1bb
All checks were successful
continuous-integration/drone/push Build is passing
Add backend SMBios support
2025-05-22 21:13:00 +02:00

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,
}