Refacto structures definition

This commit is contained in:
2023-12-28 19:29:26 +01:00
parent f7777fe085
commit 9d4f19822d
20 changed files with 1860 additions and 1839 deletions

View File

@ -0,0 +1,354 @@
use crate::libvirt_lib_structures::XMLUuid;
/// OS information
#[derive(serde::Serialize, serde::Deserialize)]
#[serde(rename = "os")]
pub struct OSXML {
#[serde(rename(serialize = "@firmware"), default)]
pub firmware: String,
pub r#type: OSTypeXML,
pub loader: Option<OSLoaderXML>,
}
/// OS Type information
#[derive(serde::Serialize, serde::Deserialize)]
#[serde(rename = "os")]
pub struct OSTypeXML {
#[serde(rename(serialize = "@arch"))]
pub arch: String,
#[serde(rename(serialize = "@machine"))]
pub machine: String,
#[serde(rename = "$value")]
pub body: String,
}
/// OS Loader information
#[derive(serde::Serialize, serde::Deserialize)]
#[serde(rename = "loader")]
pub struct OSLoaderXML {
#[serde(rename(serialize = "@secure"))]
pub secure: String,
}
/// Hypervisor features
#[derive(serde::Serialize, serde::Deserialize, Default)]
#[serde(rename = "features")]
pub struct FeaturesXML {
pub acpi: ACPIXML,
}
/// ACPI feature
#[derive(serde::Serialize, serde::Deserialize, Default)]
#[serde(rename = "acpi")]
pub struct ACPIXML {}
#[derive(serde::Serialize, serde::Deserialize)]
#[serde(rename = "mac")]
pub struct NetMacAddress {
#[serde(rename(serialize = "@address"))]
pub address: String,
}
#[derive(serde::Serialize, serde::Deserialize)]
#[serde(rename = "source")]
pub struct NetIntSourceXML {
#[serde(rename(serialize = "@network"))]
pub network: String,
}
#[derive(serde::Serialize, serde::Deserialize)]
#[serde(rename = "model")]
pub struct NetIntModelXML {
#[serde(rename(serialize = "@type"))]
pub r#type: String,
}
#[derive(serde::Serialize, serde::Deserialize)]
#[serde(rename = "interface")]
pub struct DomainNetInterfaceXML {
#[serde(rename(serialize = "@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>,
}
#[derive(serde::Serialize, serde::Deserialize)]
#[serde(rename = "input")]
pub struct DomainInputXML {
#[serde(rename(serialize = "@type"))]
pub r#type: String,
}
#[derive(serde::Serialize, serde::Deserialize)]
#[serde(rename = "backend")]
pub struct TPMBackendXML {
#[serde(rename(serialize = "@type"))]
pub r#type: String,
#[serde(rename(serialize = "@version"))]
pub r#version: String,
}
#[derive(serde::Serialize, serde::Deserialize)]
#[serde(rename = "tpm")]
pub struct TPMDeviceXML {
#[serde(rename(serialize = "@model"))]
pub model: String,
pub backend: TPMBackendXML,
}
/// Devices information
#[derive(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(serde::Serialize, serde::Deserialize)]
#[serde(rename = "graphics")]
pub struct GraphicsXML {
#[serde(rename(serialize = "@type"))]
pub r#type: String,
#[serde(rename(serialize = "@socket"))]
pub socket: String,
}
/// Video device information
#[derive(serde::Serialize, serde::Deserialize)]
#[serde(rename = "video")]
pub struct VideoXML {
pub model: VideoModelXML,
}
/// Video model device information
#[derive(serde::Serialize, serde::Deserialize)]
#[serde(rename = "model")]
pub struct VideoModelXML {
#[serde(rename(serialize = "@type"))]
pub r#type: String,
}
/// Disk information
#[derive(serde::Serialize, serde::Deserialize)]
#[serde(rename = "disk")]
pub struct DiskXML {
#[serde(rename(serialize = "@type"))]
pub r#type: String,
#[serde(rename(serialize = "@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(serde::Serialize, serde::Deserialize)]
#[serde(rename = "driver")]
pub struct DiskDriverXML {
#[serde(rename(serialize = "@name"))]
pub name: String,
#[serde(rename(serialize = "@type"))]
pub r#type: String,
#[serde(default, rename(serialize = "@cache"))]
pub r#cache: String,
}
#[derive(serde::Serialize, serde::Deserialize)]
#[serde(rename = "source")]
pub struct DiskSourceXML {
#[serde(rename(serialize = "@file"))]
pub file: String,
}
#[derive(serde::Serialize, serde::Deserialize)]
#[serde(rename = "target")]
pub struct DiskTargetXML {
#[serde(rename(serialize = "@dev"))]
pub dev: String,
#[serde(rename(serialize = "@bus"))]
pub bus: String,
}
#[derive(serde::Serialize, serde::Deserialize)]
#[serde(rename = "readonly")]
pub struct DiskReadOnlyXML {}
#[derive(serde::Serialize, serde::Deserialize)]
#[serde(rename = "boot")]
pub struct DiskBootXML {
#[serde(rename(serialize = "@order"))]
pub order: String,
}
#[derive(serde::Serialize, serde::Deserialize)]
#[serde(rename = "address")]
pub struct DiskAddressXML {
#[serde(rename(serialize = "@type"))]
pub r#type: String,
#[serde(
default,
skip_serializing_if = "Option::is_none",
rename(serialize = "@controller")
)]
pub r#controller: Option<String>,
#[serde(rename(serialize = "@bus"))]
pub r#bus: String,
#[serde(
default,
skip_serializing_if = "Option::is_none",
rename(serialize = "@target")
)]
pub r#target: Option<String>,
#[serde(
default,
skip_serializing_if = "Option::is_none",
rename(serialize = "@unit")
)]
pub r#unit: Option<String>,
}
/// Domain RAM information
#[derive(serde::Serialize, serde::Deserialize)]
#[serde(rename = "memory")]
pub struct DomainMemoryXML {
#[serde(rename(serialize = "@unit"))]
pub unit: String,
#[serde(rename = "$value")]
pub memory: usize,
}
#[derive(serde::Serialize, serde::Deserialize)]
#[serde(rename = "topology")]
pub struct DomainCPUTopology {
#[serde(rename(serialize = "@sockets"))]
pub sockets: usize,
#[serde(rename(serialize = "@cores"))]
pub cores: usize,
#[serde(rename(serialize = "@threads"))]
pub threads: usize,
}
#[derive(serde::Serialize, serde::Deserialize)]
#[serde(rename = "cpu")]
pub struct DomainVCPUXML {
#[serde(rename = "$value")]
pub body: usize,
}
#[derive(serde::Serialize, serde::Deserialize)]
#[serde(rename = "cpu")]
pub struct DomainCPUXML {
#[serde(rename(serialize = "@mode"))]
pub mode: String,
pub topology: Option<DomainCPUTopology>,
}
/// Domain information, see https://libvirt.org/formatdomain.html
#[derive(serde::Serialize, serde::Deserialize)]
#[serde(rename = "domain")]
pub struct DomainXML {
/// Domain type (kvm)
#[serde(rename(serialize = "@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>,
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,
pub on_poweroff: String,
pub on_reboot: String,
pub on_crash: String,
}
impl DomainXML {
/// Turn this domain into its XML definition
pub fn into_xml(mut self) -> anyhow::Result<String> {
// A issue with the disks & network interface definition serialization needs them to be serialized aside
let mut devices_xml = Vec::with_capacity(self.devices.disks.len());
for disk in self.devices.disks {
let disk_xml = serde_xml_rs::to_string(&disk)?;
let start_offset = disk_xml.find("<disk").unwrap();
devices_xml.push(disk_xml[start_offset..].to_string());
}
for network in self.devices.net_interfaces {
let network_xml = serde_xml_rs::to_string(&network)?;
let start_offset = network_xml.find("<interface").unwrap();
devices_xml.push(network_xml[start_offset..].to_string());
}
for input in self.devices.inputs {
let input_xml = serde_xml_rs::to_string(&input)?;
let start_offset = input_xml.find("<input").unwrap();
devices_xml.push(input_xml[start_offset..].to_string());
}
self.devices.disks = vec![];
self.devices.net_interfaces = vec![];
self.devices.inputs = vec![];
let mut xml = serde_xml_rs::to_string(&self)?;
let disks_xml = devices_xml.join("\n");
xml = xml.replacen("<devices>", &format!("<devices>{disks_xml}"), 1);
Ok(xml)
}
}
/// Domain state
#[derive(serde::Serialize, Debug, Copy, Clone)]
pub enum DomainState {
NoState,
Running,
Blocked,
Paused,
Shutdown,
Shutoff,
Crashed,
PowerManagementSuspended,
Other,
}

View File

@ -0,0 +1,24 @@
#[derive(serde::Serialize, serde::Deserialize, Clone, Copy, Debug)]
pub struct XMLUuid(pub uuid::Uuid);
impl XMLUuid {
pub fn parse_from_str(s: &str) -> anyhow::Result<Self> {
Ok(Self(uuid::Uuid::parse_str(s)?))
}
pub fn new_random() -> Self {
Self(uuid::Uuid::new_v4())
}
pub fn as_string(&self) -> String {
self.0.to_string()
}
pub fn is_valid(&self) -> bool {
log::debug!("UUID version ({}): {}", self.0, self.0.get_version_num());
self.0.get_version_num() == 4
}
}
pub mod domain;
pub mod network;
pub mod nwfilter;

View File

@ -0,0 +1,177 @@
use crate::libvirt_lib_structures::XMLUuid;
use std::net::{IpAddr, Ipv4Addr};
/// Network forward information
#[derive(serde::Serialize, serde::Deserialize, Debug)]
#[serde(rename = "forward")]
pub struct NetworkForwardXML {
#[serde(rename(serialize = "@mode"))]
pub mode: String,
#[serde(
default,
rename(serialize = "@dev"),
skip_serializing_if = "String::is_empty"
)]
pub dev: String,
}
/// Network bridge information
#[derive(serde::Serialize, serde::Deserialize, Debug)]
#[serde(rename = "bridge")]
pub struct NetworkBridgeXML {
#[serde(rename(serialize = "@name"))]
pub name: String,
}
/// Network DNS information
#[derive(serde::Serialize, serde::Deserialize, Debug)]
#[serde(rename = "dns")]
pub struct NetworkDNSXML {
pub forwarder: NetworkDNSForwarderXML,
}
/// Network DNS information
#[derive(serde::Serialize, serde::Deserialize, Debug)]
#[serde(rename = "forwarder")]
pub struct NetworkDNSForwarderXML {
/// Address of the DNS server
#[serde(rename(serialize = "@addr"))]
pub addr: Ipv4Addr,
}
/// Network DNS information
#[derive(serde::Serialize, serde::Deserialize, Debug)]
#[serde(rename = "domain")]
pub struct NetworkDomainXML {
#[serde(rename(serialize = "@name"))]
pub name: String,
}
fn invalid_prefix() -> u32 {
u32::MAX
}
fn invalid_ip() -> IpAddr {
IpAddr::V4(Ipv4Addr::BROADCAST)
}
/// Network ip information
#[derive(serde::Serialize, serde::Deserialize, Debug)]
#[serde(rename = "ip")]
pub struct NetworkIPXML {
#[serde(default, rename(serialize = "@family"))]
pub family: String,
#[serde(rename(serialize = "@address"))]
pub address: IpAddr,
/// Network Prefix
#[serde(rename(serialize = "@prefix"), default = "invalid_prefix")]
pub prefix: u32,
/// Network Netmask. This field is never serialized, but because we can't know if LibVirt will
/// provide us netmask or prefix, we need to handle both of these fields
#[serde(
rename(serialize = "@netmask"),
default = "invalid_ip",
skip_serializing
)]
pub netmask: IpAddr,
pub dhcp: Option<NetworkDHCPXML>,
}
impl NetworkIPXML {
pub fn into_xml(mut self) -> anyhow::Result<String> {
let mut hosts_xml = vec![];
if let Some(dhcp) = &mut self.dhcp {
for host in &dhcp.hosts {
let mut host_xml = serde_xml_rs::to_string(&host)?;
// In case of IPv6, mac address should not be specified
host_xml = host_xml.replace("mac=\"\"", "");
// strip xml tag
let start_offset = host_xml.find("<host").unwrap();
hosts_xml.push(host_xml[start_offset..].to_string());
}
dhcp.hosts = vec![];
}
let mut res = serde_xml_rs::to_string(&self)?;
let hosts_xml = hosts_xml.join("\n");
res = res.replace("</dhcp>", &format!("{hosts_xml}</dhcp>"));
Ok(res)
}
}
#[derive(serde::Serialize, serde::Deserialize, Debug)]
#[serde(rename = "dhcp")]
pub struct NetworkDHCPXML {
pub range: NetworkDHCPRangeXML,
#[serde(default, rename = "host", skip_serializing_if = "Vec::is_empty")]
pub hosts: Vec<NetworkDHCPHostXML>,
}
#[derive(serde::Serialize, serde::Deserialize, Debug)]
#[serde(rename = "host")]
pub struct NetworkDHCPHostXML {
#[serde(rename(serialize = "@mac"), default)]
pub mac: String,
#[serde(rename(serialize = "@name"))]
pub name: String,
#[serde(rename(serialize = "@ip"))]
pub ip: IpAddr,
}
#[derive(serde::Serialize, serde::Deserialize, Debug)]
#[serde(rename = "dhcp")]
pub struct NetworkDHCPRangeXML {
#[serde(rename(serialize = "@start"))]
pub start: IpAddr,
#[serde(rename(serialize = "@end"))]
pub end: IpAddr,
}
/// Network information, see https://libvirt.org/formatnetwork.html
#[derive(serde::Serialize, serde::Deserialize, Debug)]
#[serde(rename = "network")]
pub struct NetworkXML {
pub name: String,
#[serde(skip_serializing_if = "Option::is_none")]
pub uuid: Option<XMLUuid>,
#[serde(skip_serializing_if = "Option::is_none")]
pub title: Option<String>,
#[serde(skip_serializing_if = "Option::is_none")]
pub description: Option<String>,
#[serde(skip_serializing_if = "Option::is_none")]
pub forward: Option<NetworkForwardXML>,
#[serde(skip_serializing_if = "Option::is_none")]
pub bridge: Option<NetworkBridgeXML>,
#[serde(skip_serializing_if = "Option::is_none")]
pub dns: Option<NetworkDNSXML>,
#[serde(skip_serializing_if = "Option::is_none")]
pub domain: Option<NetworkDomainXML>,
#[serde(default, rename = "ip")]
pub ips: Vec<NetworkIPXML>,
}
impl NetworkXML {
pub fn into_xml(mut self) -> anyhow::Result<String> {
// A issue with the IPs definition serialization needs them to be serialized aside
let mut ips_xml = Vec::with_capacity(self.ips.len());
for ip in self.ips {
log::debug!("Serialize {ip:?}");
let ip_xml = ip.into_xml()?;
// strip xml tag
let start_offset = ip_xml.find("<ip").unwrap();
ips_xml.push(ip_xml[start_offset..].to_string());
}
self.ips = vec![];
let mut network_xml = serde_xml_rs::to_string(&self)?;
log::trace!("Serialize network XML start: {network_xml}");
let ips_xml = ips_xml.join("\n");
network_xml = network_xml.replacen("</network>", &format!("{ips_xml}</network>"), 1);
Ok(network_xml)
}
}

View File

@ -0,0 +1,316 @@
use crate::libvirt_lib_structures::XMLUuid;
use std::fmt::Display;
use std::net::IpAddr;
#[derive(serde::Serialize, serde::Deserialize, Debug)]
#[serde(rename = "filterref")]
pub struct NetworkFilterRefXML {
#[serde(rename(serialize = "@filter"))]
pub filter: String,
}
#[derive(serde::Serialize, serde::Deserialize, Debug)]
#[serde(rename = "all")]
pub struct NetworkFilterRuleProtocolAll {}
#[derive(serde::Serialize, serde::Deserialize, Debug)]
#[serde(rename = "mac")]
pub struct NetworkFilterRuleProtocolMac {
#[serde(
rename(serialize = "@srcmacaddr"),
skip_serializing_if = "Option::is_none"
)]
srcmacaddr: Option<String>,
#[serde(
rename(serialize = "@srcmacmask"),
skip_serializing_if = "Option::is_none"
)]
srcmacmask: Option<String>,
#[serde(
rename(serialize = "@dstmacaddr"),
skip_serializing_if = "Option::is_none"
)]
dstmacaddr: Option<String>,
#[serde(
rename(serialize = "@dstmacmask"),
skip_serializing_if = "Option::is_none"
)]
dstmacmask: Option<String>,
#[serde(
rename(serialize = "@comment"),
skip_serializing_if = "Option::is_none"
)]
comment: Option<String>,
}
#[derive(serde::Serialize, serde::Deserialize, Debug)]
#[serde(rename = "arp")]
pub struct NetworkFilterRuleProtocolArp {
#[serde(
rename(serialize = "@srcmacaddr"),
skip_serializing_if = "Option::is_none"
)]
srcmacaddr: Option<String>,
#[serde(
rename(serialize = "@srcmacmask"),
skip_serializing_if = "Option::is_none"
)]
srcmacmask: Option<String>,
#[serde(
rename(serialize = "@dstmacaddr"),
skip_serializing_if = "Option::is_none"
)]
dstmacaddr: Option<String>,
#[serde(
rename(serialize = "@dstmacmask"),
skip_serializing_if = "Option::is_none"
)]
dstmacmask: Option<String>,
#[serde(
rename(serialize = "@arpsrcipaddr"),
skip_serializing_if = "Option::is_none"
)]
arpsrcipaddr: Option<String>,
#[serde(
rename(serialize = "@arpsrcipmask"),
skip_serializing_if = "Option::is_none"
)]
arpsrcipmask: Option<u8>,
#[serde(
rename(serialize = "@arpdstipaddr"),
skip_serializing_if = "Option::is_none"
)]
arpdstipaddr: Option<String>,
#[serde(
rename(serialize = "@arpdstipmask"),
skip_serializing_if = "Option::is_none"
)]
arpdstipmask: Option<u8>,
#[serde(
rename(serialize = "@comment"),
skip_serializing_if = "Option::is_none"
)]
comment: Option<String>,
}
#[derive(serde::Serialize, serde::Deserialize, Debug)]
#[serde(rename = "ipvx")]
pub struct NetworkFilterRuleProtocolIpvx {
#[serde(
rename(serialize = "@srcmacaddr"),
skip_serializing_if = "Option::is_none"
)]
srcmacaddr: Option<String>,
#[serde(
rename(serialize = "@srcmacmask"),
skip_serializing_if = "Option::is_none"
)]
srcmacmask: Option<String>,
#[serde(
rename(serialize = "@dstmacaddr"),
skip_serializing_if = "Option::is_none"
)]
dstmacaddr: Option<String>,
#[serde(
rename(serialize = "@dstmacmask"),
skip_serializing_if = "Option::is_none"
)]
dstmacmask: Option<String>,
#[serde(
rename(serialize = "@srcipaddr"),
skip_serializing_if = "Option::is_none"
)]
srcipaddr: Option<String>,
#[serde(
rename(serialize = "@srcipmask"),
skip_serializing_if = "Option::is_none"
)]
srcipmask: Option<u8>,
#[serde(
rename(serialize = "@dstipaddr"),
skip_serializing_if = "Option::is_none"
)]
dstipaddr: Option<String>,
#[serde(
rename(serialize = "@dstipmask"),
skip_serializing_if = "Option::is_none"
)]
dstipmask: Option<u8>,
#[serde(
rename(serialize = "@comment"),
skip_serializing_if = "Option::is_none"
)]
comment: Option<String>,
}
#[derive(serde::Serialize, serde::Deserialize, Debug)]
#[serde(rename = "layer4")]
pub struct NetworkFilterRuleProtocolLayer4 {
#[serde(
rename(serialize = "@srcmacaddr"),
skip_serializing_if = "Option::is_none"
)]
srcmacaddr: Option<String>,
#[serde(
rename(serialize = "@srcipaddr"),
skip_serializing_if = "Option::is_none"
)]
srcipaddr: Option<IpAddr>,
#[serde(
rename(serialize = "@srcipmask"),
skip_serializing_if = "Option::is_none"
)]
srcipmask: Option<u8>,
#[serde(
rename(serialize = "@dstipaddr"),
skip_serializing_if = "Option::is_none"
)]
dstipaddr: Option<IpAddr>,
#[serde(
rename(serialize = "@dstipmask"),
skip_serializing_if = "Option::is_none"
)]
dstipmask: Option<u8>,
/// Start of range of source IP address
#[serde(
rename(serialize = "@srcipfrom"),
skip_serializing_if = "Option::is_none"
)]
srcipfrom: Option<IpAddr>,
/// End of range of source IP address
#[serde(
rename(serialize = "@srcipto"),
skip_serializing_if = "Option::is_none"
)]
srcipto: Option<IpAddr>,
/// Start of range of destination IP address
#[serde(
rename(serialize = "@dstipfrom"),
skip_serializing_if = "Option::is_none"
)]
dstipfrom: Option<IpAddr>,
/// End of range of destination IP address
#[serde(
rename(serialize = "@dstipto"),
skip_serializing_if = "Option::is_none"
)]
dstipto: Option<IpAddr>,
#[serde(
rename(serialize = "@srcportstart"),
skip_serializing_if = "Option::is_none"
)]
srcportstart: Option<u16>,
#[serde(
rename(serialize = "@srcportend"),
skip_serializing_if = "Option::is_none"
)]
srcportend: Option<u16>,
#[serde(
rename(serialize = "@dstportstart"),
skip_serializing_if = "Option::is_none"
)]
dstportstart: Option<u16>,
#[serde(
rename(serialize = "@dstportend"),
skip_serializing_if = "Option::is_none"
)]
dstportend: Option<u16>,
#[serde(rename(serialize = "@state"), skip_serializing_if = "Option::is_none")]
state: Option<String>,
#[serde(
rename(serialize = "@comment"),
skip_serializing_if = "Option::is_none"
)]
comment: Option<String>,
}
#[derive(serde::Serialize, serde::Deserialize, Debug)]
#[serde(rename = "rule")]
pub struct NetworkFilterRuleXML {
#[serde(rename(serialize = "@action"))]
pub action: String,
#[serde(rename(serialize = "@direction"))]
pub direction: String,
#[serde(rename(serialize = "@priority"))]
pub priority: Option<i32>,
/// Match all protocols
#[serde(skip_serializing_if = "Option::is_none")]
pub all: Option<NetworkFilterRuleProtocolAll>,
/// Match mac protocol
#[serde(default, rename = "mac", skip_serializing_if = "Vec::is_empty")]
pub mac_rules: Vec<NetworkFilterRuleProtocolMac>,
/// Match arp protocol
#[serde(default, rename = "arp", skip_serializing_if = "Vec::is_empty")]
pub arp_rules: Vec<NetworkFilterRuleProtocolArp>,
/// Match IPv4 protocol
#[serde(default, rename = "ip", skip_serializing_if = "Vec::is_empty")]
pub ipv4_rules: Vec<NetworkFilterRuleProtocolIpvx>,
/// Match IPv6 protocol
#[serde(default, rename = "ipv6", skip_serializing_if = "Vec::is_empty")]
pub ipv6_rules: Vec<NetworkFilterRuleProtocolIpvx>,
/// Match TCP protocol
#[serde(default, rename = "tcp", skip_serializing_if = "Vec::is_empty")]
pub tcp_rules: Vec<NetworkFilterRuleProtocolLayer4>,
/// Match UDP protocol
#[serde(default, rename = "udp", skip_serializing_if = "Vec::is_empty")]
pub udp_rules: Vec<NetworkFilterRuleProtocolLayer4>,
/// Match SCTP protocol
#[serde(default, rename = "sctp", skip_serializing_if = "Vec::is_empty")]
pub sctp_rules: Vec<NetworkFilterRuleProtocolLayer4>,
}
#[derive(serde::Serialize, serde::Deserialize, Debug)]
#[serde(rename = "filter")]
pub struct NetworkFilterXML {
#[serde(rename(serialize = "@name"))]
pub name: String,
#[serde(rename(serialize = "@chain"), default)]
pub chain: String,
#[serde(
skip_serializing_if = "Option::is_none",
rename(serialize = "@priority"),
default
)]
pub priority: Option<i32>,
#[serde(skip_serializing_if = "Option::is_none")]
pub uuid: Option<XMLUuid>,
#[serde(default, rename = "filterref", skip_serializing_if = "Vec::is_empty")]
pub filterrefs: Vec<NetworkFilterRefXML>,
#[serde(default, rename = "rule", skip_serializing_if = "Vec::is_empty")]
pub rules: Vec<NetworkFilterRuleXML>,
}
impl NetworkFilterXML {
pub fn parse_xml<D: Display>(xml: D) -> anyhow::Result<Self> {
let xml = xml.to_string();
// We need to put all filter refs at the same location
let mut filter_refs = Vec::new();
let xml = lazy_regex::regex_replace_all!(r#"<filterref.*/>"#, &xml, |r: &str| {
filter_refs.push(r.to_string());
if r.contains('\n') {
log::warn!("A filterref contain a new line. This is a symptom of a new unsupported child attribute of <filterref /> object!");
}
""
});
let filter_refs = filter_refs.join("\n");
let xml = xml.replace("</filter>", &format!("{filter_refs}</filter>"));
log::debug!("Effective NW filter rule parsed: {xml}");
Ok(serde_xml_rs::from_str(&xml)?)
}
}