Refacto structures definition
This commit is contained in:
		
							
								
								
									
										23
									
								
								virtweb_backend/src/libvirt_rest_structures/hypervisor.rs
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										23
									
								
								virtweb_backend/src/libvirt_rest_structures/hypervisor.rs
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,23 @@
 | 
			
		||||
#[derive(serde::Serialize)]
 | 
			
		||||
pub struct HypervisorInfo {
 | 
			
		||||
    pub r#type: String,
 | 
			
		||||
    pub hyp_version: u32,
 | 
			
		||||
    pub lib_version: u32,
 | 
			
		||||
    pub capabilities: String,
 | 
			
		||||
    pub free_memory: u64,
 | 
			
		||||
    pub hostname: String,
 | 
			
		||||
    pub node: HypervisorNodeInfo,
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
#[derive(serde::Serialize)]
 | 
			
		||||
pub struct HypervisorNodeInfo {
 | 
			
		||||
    pub cpu_model: String,
 | 
			
		||||
    /// Memory size in kilobytes
 | 
			
		||||
    pub memory_size: u64,
 | 
			
		||||
    pub number_of_active_cpus: u32,
 | 
			
		||||
    pub cpu_frequency_mhz: u32,
 | 
			
		||||
    pub number_of_numa_cell: u32,
 | 
			
		||||
    pub number_of_cpu_socket_per_node: u32,
 | 
			
		||||
    pub number_of_core_per_sockets: u32,
 | 
			
		||||
    pub number_of_threads_per_core: u32,
 | 
			
		||||
}
 | 
			
		||||
							
								
								
									
										15
									
								
								virtweb_backend/src/libvirt_rest_structures/mod.rs
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										15
									
								
								virtweb_backend/src/libvirt_rest_structures/mod.rs
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,15 @@
 | 
			
		||||
pub mod hypervisor;
 | 
			
		||||
pub mod net;
 | 
			
		||||
pub mod nw_filter;
 | 
			
		||||
pub mod vm;
 | 
			
		||||
 | 
			
		||||
#[derive(thiserror::Error, Debug)]
 | 
			
		||||
enum LibVirtStructError {
 | 
			
		||||
    #[error("StructureExtractionError: {0}")]
 | 
			
		||||
    StructureExtraction(&'static str),
 | 
			
		||||
    #[error("DomainExtractionError: {0}")]
 | 
			
		||||
    DomainExtraction(String),
 | 
			
		||||
 | 
			
		||||
    #[error("ParseFilteringChain: {0}")]
 | 
			
		||||
    ParseFilteringChain(String),
 | 
			
		||||
}
 | 
			
		||||
							
								
								
									
										258
									
								
								virtweb_backend/src/libvirt_rest_structures/net.rs
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										258
									
								
								virtweb_backend/src/libvirt_rest_structures/net.rs
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,258 @@
 | 
			
		||||
use crate::libvirt_lib_structures::network::*;
 | 
			
		||||
use crate::libvirt_lib_structures::XMLUuid;
 | 
			
		||||
use crate::libvirt_rest_structures::LibVirtStructError::StructureExtraction;
 | 
			
		||||
use crate::utils::net_utils::{extract_ipv4, extract_ipv6};
 | 
			
		||||
use ipnetwork::{Ipv4Network, Ipv6Network};
 | 
			
		||||
use lazy_regex::regex;
 | 
			
		||||
use std::net::{IpAddr, Ipv4Addr, Ipv6Addr};
 | 
			
		||||
 | 
			
		||||
#[derive(serde::Serialize, serde::Deserialize, Copy, Clone, Debug)]
 | 
			
		||||
pub enum NetworkForwardMode {
 | 
			
		||||
    NAT,
 | 
			
		||||
    Isolated,
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
#[derive(serde::Serialize, serde::Deserialize, Clone, Debug)]
 | 
			
		||||
pub struct DHCPv4HostReservation {
 | 
			
		||||
    mac: String,
 | 
			
		||||
    name: String,
 | 
			
		||||
    ip: Ipv4Addr,
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
#[derive(serde::Serialize, serde::Deserialize, Clone, Debug)]
 | 
			
		||||
pub struct IPv4DHCPConfig {
 | 
			
		||||
    start: Ipv4Addr,
 | 
			
		||||
    end: Ipv4Addr,
 | 
			
		||||
    hosts: Vec<DHCPv4HostReservation>,
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
#[derive(serde::Serialize, serde::Deserialize, Clone, Debug)]
 | 
			
		||||
pub struct IPV4Config {
 | 
			
		||||
    bridge_address: Ipv4Addr,
 | 
			
		||||
    prefix: u32,
 | 
			
		||||
    dhcp: Option<IPv4DHCPConfig>,
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
#[derive(serde::Serialize, serde::Deserialize, Clone, Debug)]
 | 
			
		||||
pub struct DHCPv6HostReservation {
 | 
			
		||||
    name: String,
 | 
			
		||||
    ip: Ipv6Addr,
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
#[derive(serde::Serialize, serde::Deserialize, Clone, Debug)]
 | 
			
		||||
pub struct IPv6DHCPConfig {
 | 
			
		||||
    start: Ipv6Addr,
 | 
			
		||||
    end: Ipv6Addr,
 | 
			
		||||
    hosts: Vec<DHCPv6HostReservation>,
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
#[derive(serde::Serialize, serde::Deserialize, Clone, Debug)]
 | 
			
		||||
pub struct IPV6Config {
 | 
			
		||||
    bridge_address: Ipv6Addr,
 | 
			
		||||
    prefix: u32,
 | 
			
		||||
    dhcp: Option<IPv6DHCPConfig>,
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
/// Network configuration
 | 
			
		||||
#[derive(serde::Serialize, serde::Deserialize, Clone, Debug)]
 | 
			
		||||
pub struct NetworkInfo {
 | 
			
		||||
    pub name: String,
 | 
			
		||||
    pub uuid: Option<XMLUuid>,
 | 
			
		||||
    title: Option<String>,
 | 
			
		||||
    description: Option<String>,
 | 
			
		||||
    forward_mode: NetworkForwardMode,
 | 
			
		||||
    device: Option<String>,
 | 
			
		||||
    bridge_name: Option<String>,
 | 
			
		||||
    dns_server: Option<Ipv4Addr>,
 | 
			
		||||
    domain: Option<String>,
 | 
			
		||||
    ip_v4: Option<IPV4Config>,
 | 
			
		||||
    ip_v6: Option<IPV6Config>,
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
impl NetworkInfo {
 | 
			
		||||
    pub fn as_virt_network(&self) -> anyhow::Result<NetworkXML> {
 | 
			
		||||
        if !regex!("^[a-zA-Z0-9]+$").is_match(&self.name) {
 | 
			
		||||
            return Err(StructureExtraction("network name is invalid!").into());
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        if let Some(n) = &self.title {
 | 
			
		||||
            if n.contains('\n') {
 | 
			
		||||
                return Err(StructureExtraction("Network title contain newline char!").into());
 | 
			
		||||
            }
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        if let Some(dev) = &self.device {
 | 
			
		||||
            if !regex!("^[a-zA-Z0-9]+$").is_match(dev) {
 | 
			
		||||
                return Err(StructureExtraction("Network device name is invalid!").into());
 | 
			
		||||
            }
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        if let Some(bridge) = &self.bridge_name {
 | 
			
		||||
            if !regex!("^[a-zA-Z0-9]+$").is_match(bridge) {
 | 
			
		||||
                return Err(StructureExtraction("Network bridge name is invalid!").into());
 | 
			
		||||
            }
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        if let Some(domain) = &self.domain {
 | 
			
		||||
            if !regex!("^[a-zA-Z0-9.]+$").is_match(domain) {
 | 
			
		||||
                return Err(StructureExtraction("Domain name is invalid!").into());
 | 
			
		||||
            }
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        let mut ips = Vec::with_capacity(2);
 | 
			
		||||
 | 
			
		||||
        if let Some(ipv4) = &self.ip_v4 {
 | 
			
		||||
            if ipv4.prefix > 32 {
 | 
			
		||||
                return Err(StructureExtraction("IPv4 prefix is invalid!").into());
 | 
			
		||||
            }
 | 
			
		||||
 | 
			
		||||
            ips.push(NetworkIPXML {
 | 
			
		||||
                family: "ipv4".to_string(),
 | 
			
		||||
                address: IpAddr::V4(ipv4.bridge_address),
 | 
			
		||||
                prefix: ipv4.prefix,
 | 
			
		||||
                netmask: Ipv4Network::new(ipv4.bridge_address, ipv4.prefix as u8)
 | 
			
		||||
                    .unwrap()
 | 
			
		||||
                    .mask()
 | 
			
		||||
                    .into(),
 | 
			
		||||
                dhcp: ipv4.dhcp.as_ref().map(|dhcp| NetworkDHCPXML {
 | 
			
		||||
                    range: NetworkDHCPRangeXML {
 | 
			
		||||
                        start: IpAddr::V4(dhcp.start),
 | 
			
		||||
                        end: IpAddr::V4(dhcp.end),
 | 
			
		||||
                    },
 | 
			
		||||
                    hosts: dhcp
 | 
			
		||||
                        .hosts
 | 
			
		||||
                        .iter()
 | 
			
		||||
                        .map(|c| NetworkDHCPHostXML {
 | 
			
		||||
                            mac: c.mac.to_string(),
 | 
			
		||||
                            name: c.name.to_string(),
 | 
			
		||||
                            ip: c.ip.into(),
 | 
			
		||||
                        })
 | 
			
		||||
                        .collect::<Vec<_>>(),
 | 
			
		||||
                }),
 | 
			
		||||
            })
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        if let Some(ipv6) = &self.ip_v6 {
 | 
			
		||||
            ips.push(NetworkIPXML {
 | 
			
		||||
                family: "ipv6".to_string(),
 | 
			
		||||
                address: IpAddr::V6(ipv6.bridge_address),
 | 
			
		||||
                prefix: ipv6.prefix,
 | 
			
		||||
                netmask: Ipv6Network::new(ipv6.bridge_address, ipv6.prefix as u8)
 | 
			
		||||
                    .unwrap()
 | 
			
		||||
                    .mask()
 | 
			
		||||
                    .into(),
 | 
			
		||||
                dhcp: ipv6.dhcp.as_ref().map(|dhcp| NetworkDHCPXML {
 | 
			
		||||
                    range: NetworkDHCPRangeXML {
 | 
			
		||||
                        start: IpAddr::V6(dhcp.start),
 | 
			
		||||
                        end: IpAddr::V6(dhcp.end),
 | 
			
		||||
                    },
 | 
			
		||||
                    hosts: dhcp
 | 
			
		||||
                        .hosts
 | 
			
		||||
                        .iter()
 | 
			
		||||
                        .map(|h| NetworkDHCPHostXML {
 | 
			
		||||
                            mac: "".to_string(),
 | 
			
		||||
                            name: h.name.to_string(),
 | 
			
		||||
                            ip: h.ip.into(),
 | 
			
		||||
                        })
 | 
			
		||||
                        .collect(),
 | 
			
		||||
                }),
 | 
			
		||||
            })
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        Ok(NetworkXML {
 | 
			
		||||
            name: self.name.to_string(),
 | 
			
		||||
            uuid: self.uuid,
 | 
			
		||||
            title: self.title.clone(),
 | 
			
		||||
            description: self.description.clone(),
 | 
			
		||||
            forward: match self.forward_mode {
 | 
			
		||||
                NetworkForwardMode::NAT => Some(NetworkForwardXML {
 | 
			
		||||
                    mode: "nat".to_string(),
 | 
			
		||||
                    dev: self.device.clone().unwrap_or_default(),
 | 
			
		||||
                }),
 | 
			
		||||
                NetworkForwardMode::Isolated => None,
 | 
			
		||||
            },
 | 
			
		||||
            bridge: self.bridge_name.clone().map(|b| NetworkBridgeXML {
 | 
			
		||||
                name: b.to_string(),
 | 
			
		||||
            }),
 | 
			
		||||
            dns: self.dns_server.map(|addr| NetworkDNSXML {
 | 
			
		||||
                forwarder: NetworkDNSForwarderXML { addr },
 | 
			
		||||
            }),
 | 
			
		||||
            domain: self.domain.clone().map(|name| NetworkDomainXML { name }),
 | 
			
		||||
            ips,
 | 
			
		||||
        })
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    pub fn from_xml(xml: NetworkXML) -> anyhow::Result<Self> {
 | 
			
		||||
        Ok(Self {
 | 
			
		||||
            name: xml.name,
 | 
			
		||||
            uuid: xml.uuid,
 | 
			
		||||
            title: xml.title,
 | 
			
		||||
            description: xml.description,
 | 
			
		||||
            forward_mode: match xml.forward {
 | 
			
		||||
                None => NetworkForwardMode::Isolated,
 | 
			
		||||
                Some(_) => NetworkForwardMode::NAT,
 | 
			
		||||
            },
 | 
			
		||||
            device: xml
 | 
			
		||||
                .forward
 | 
			
		||||
                .map(|f| match f.dev.is_empty() {
 | 
			
		||||
                    true => None,
 | 
			
		||||
                    false => Some(f.dev),
 | 
			
		||||
                })
 | 
			
		||||
                .unwrap_or(None),
 | 
			
		||||
            bridge_name: xml.bridge.map(|b| b.name),
 | 
			
		||||
            dns_server: xml.dns.map(|d| d.forwarder.addr),
 | 
			
		||||
            domain: xml.domain.map(|d| d.name),
 | 
			
		||||
            ip_v4: xml
 | 
			
		||||
                .ips
 | 
			
		||||
                .iter()
 | 
			
		||||
                .find(|i| i.family != "ipv6")
 | 
			
		||||
                .map(|i| IPV4Config {
 | 
			
		||||
                    bridge_address: extract_ipv4(i.address),
 | 
			
		||||
                    prefix: match i.prefix {
 | 
			
		||||
                        u32::MAX => ipnetwork::ipv4_mask_to_prefix(extract_ipv4(i.netmask))
 | 
			
		||||
                            .expect("Failed to convert IPv4 netmask to network")
 | 
			
		||||
                            as u32,
 | 
			
		||||
                        p => p,
 | 
			
		||||
                    },
 | 
			
		||||
                    dhcp: i.dhcp.as_ref().map(|d| IPv4DHCPConfig {
 | 
			
		||||
                        start: extract_ipv4(d.range.start),
 | 
			
		||||
                        end: extract_ipv4(d.range.end),
 | 
			
		||||
                        hosts: d
 | 
			
		||||
                            .hosts
 | 
			
		||||
                            .iter()
 | 
			
		||||
                            .map(|h| DHCPv4HostReservation {
 | 
			
		||||
                                mac: h.mac.to_string(),
 | 
			
		||||
                                name: h.name.to_string(),
 | 
			
		||||
                                ip: extract_ipv4(h.ip),
 | 
			
		||||
                            })
 | 
			
		||||
                            .collect(),
 | 
			
		||||
                    }),
 | 
			
		||||
                }),
 | 
			
		||||
            ip_v6: xml
 | 
			
		||||
                .ips
 | 
			
		||||
                .iter()
 | 
			
		||||
                .find(|i| i.family == "ipv6")
 | 
			
		||||
                .map(|i| IPV6Config {
 | 
			
		||||
                    bridge_address: extract_ipv6(i.address),
 | 
			
		||||
                    prefix: match i.prefix {
 | 
			
		||||
                        u32::MAX => ipnetwork::ipv6_mask_to_prefix(extract_ipv6(i.netmask))
 | 
			
		||||
                            .expect("Failed to convert IPv6 netmask to network")
 | 
			
		||||
                            as u32,
 | 
			
		||||
                        p => p,
 | 
			
		||||
                    },
 | 
			
		||||
                    dhcp: i.dhcp.as_ref().map(|d| IPv6DHCPConfig {
 | 
			
		||||
                        start: extract_ipv6(d.range.start),
 | 
			
		||||
                        end: extract_ipv6(d.range.end),
 | 
			
		||||
                        hosts: d
 | 
			
		||||
                            .hosts
 | 
			
		||||
                            .iter()
 | 
			
		||||
                            .map(|h| DHCPv6HostReservation {
 | 
			
		||||
                                name: h.name.to_string(),
 | 
			
		||||
                                ip: extract_ipv6(h.ip),
 | 
			
		||||
                            })
 | 
			
		||||
                            .collect(),
 | 
			
		||||
                    }),
 | 
			
		||||
                }),
 | 
			
		||||
        })
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
							
								
								
									
										227
									
								
								virtweb_backend/src/libvirt_rest_structures/nw_filter.rs
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										227
									
								
								virtweb_backend/src/libvirt_rest_structures/nw_filter.rs
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,227 @@
 | 
			
		||||
use crate::libvirt_lib_structures::nwfilter::NetworkFilterXML;
 | 
			
		||||
use crate::libvirt_lib_structures::XMLUuid;
 | 
			
		||||
use crate::libvirt_rest_structures::LibVirtStructError;
 | 
			
		||||
use std::net::{IpAddr, Ipv4Addr, Ipv6Addr};
 | 
			
		||||
 | 
			
		||||
#[derive(serde::Serialize, serde::Deserialize, Debug, Copy, Clone)]
 | 
			
		||||
pub enum NetworkFilterChainProtocol {
 | 
			
		||||
    Root,
 | 
			
		||||
    Mac,
 | 
			
		||||
    STP,
 | 
			
		||||
    VLAN,
 | 
			
		||||
    ARP,
 | 
			
		||||
    RARP,
 | 
			
		||||
    IPv4,
 | 
			
		||||
    IPv6,
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
impl NetworkFilterChainProtocol {
 | 
			
		||||
    pub fn from_xml(xml: &str) -> anyhow::Result<Self> {
 | 
			
		||||
        Ok(match xml {
 | 
			
		||||
            "root" => Self::Root,
 | 
			
		||||
            "mac" => Self::Mac,
 | 
			
		||||
            "stp" => Self::STP,
 | 
			
		||||
            "vlan" => Self::VLAN,
 | 
			
		||||
            "arp" => Self::ARP,
 | 
			
		||||
            "rarp" => Self::RARP,
 | 
			
		||||
            "ipv4" => Self::IPv4,
 | 
			
		||||
            "ipv6" => Self::IPv6,
 | 
			
		||||
            _ => {
 | 
			
		||||
                return Err(LibVirtStructError::ParseFilteringChain(format!(
 | 
			
		||||
                    "Unknown filtering chain: {xml}! "
 | 
			
		||||
                ))
 | 
			
		||||
                .into())
 | 
			
		||||
            }
 | 
			
		||||
        })
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    pub fn to_xml(&self) -> String {
 | 
			
		||||
        match self {
 | 
			
		||||
            Self::Root => "root",
 | 
			
		||||
            Self::Mac => "mac",
 | 
			
		||||
            Self::STP => "stp",
 | 
			
		||||
            Self::VLAN => "vlan",
 | 
			
		||||
            Self::ARP => "arp",
 | 
			
		||||
            Self::RARP => "rarp",
 | 
			
		||||
            Self::IPv4 => "ipv4",
 | 
			
		||||
            Self::IPv6 => "ipv6",
 | 
			
		||||
        }
 | 
			
		||||
        .to_string()
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
#[derive(serde::Serialize, serde::Deserialize, Debug, Clone)]
 | 
			
		||||
pub struct NetworkFilterChain {
 | 
			
		||||
    protocol: NetworkFilterChainProtocol,
 | 
			
		||||
    suffix: Option<String>,
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
impl NetworkFilterChain {
 | 
			
		||||
    pub fn from_xml(xml: &str) -> anyhow::Result<Option<Self>> {
 | 
			
		||||
        if xml.is_empty() {
 | 
			
		||||
            return Ok(None);
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        Ok(Some(match xml.split_once('-') {
 | 
			
		||||
            None => Self {
 | 
			
		||||
                protocol: NetworkFilterChainProtocol::from_xml(xml)?,
 | 
			
		||||
                suffix: None,
 | 
			
		||||
            },
 | 
			
		||||
            Some((prefix, suffix)) => Self {
 | 
			
		||||
                protocol: NetworkFilterChainProtocol::from_xml(prefix)?,
 | 
			
		||||
                suffix: Some(suffix.to_string()),
 | 
			
		||||
            },
 | 
			
		||||
        }))
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    pub fn to_xml(&self) -> String {
 | 
			
		||||
        match &self.suffix {
 | 
			
		||||
            None => self.protocol.to_xml(),
 | 
			
		||||
            Some(s) => format!("{}-{s}", self.protocol.to_xml()),
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
/// Network filter definition
 | 
			
		||||
#[derive(serde::Serialize, serde::Deserialize, Debug, Clone)]
 | 
			
		||||
pub struct NetworkFilter {
 | 
			
		||||
    name: String,
 | 
			
		||||
    chain: Option<NetworkFilterChain>,
 | 
			
		||||
    priority: Option<i32>,
 | 
			
		||||
    uuid: Option<XMLUuid>,
 | 
			
		||||
    /// Referenced filters rules
 | 
			
		||||
    join_rules: Vec<String>,
 | 
			
		||||
    rules: Vec<NetworkFilterRule>,
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
impl NetworkFilter {
 | 
			
		||||
    pub fn from_xml(xml: NetworkFilterXML) -> anyhow::Result<Self> {
 | 
			
		||||
        Ok(Self {
 | 
			
		||||
            name: xml.name,
 | 
			
		||||
            uuid: xml.uuid,
 | 
			
		||||
            chain: NetworkFilterChain::from_xml(&xml.chain)?,
 | 
			
		||||
            priority: xml.priority,
 | 
			
		||||
            join_rules: xml
 | 
			
		||||
                .filterrefs
 | 
			
		||||
                .iter()
 | 
			
		||||
                .map(|i| i.filter.to_string())
 | 
			
		||||
                .collect(),
 | 
			
		||||
            rules: vec![], // TODO !
 | 
			
		||||
        })
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
#[derive(serde::Serialize, serde::Deserialize, Debug, Copy, Clone)]
 | 
			
		||||
pub enum NetworkFilterAction {
 | 
			
		||||
    /// matching the rule silently discards the packet with no further analysis
 | 
			
		||||
    Drop,
 | 
			
		||||
    /// matching the rule generates an ICMP reject message with no further analysis
 | 
			
		||||
    Reject,
 | 
			
		||||
    /// matching the rule accepts the packet with no further analysis
 | 
			
		||||
    Accept,
 | 
			
		||||
    /// matching the rule passes this filter, but returns control to the calling filter for further
 | 
			
		||||
    /// analysis
 | 
			
		||||
    Return,
 | 
			
		||||
    /// matching the rule goes on to the next rule for further analysis
 | 
			
		||||
    Continue,
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
#[derive(serde::Serialize, serde::Deserialize, Debug, Clone)]
 | 
			
		||||
pub enum NetworkFilterDirection {
 | 
			
		||||
    In,
 | 
			
		||||
    Out,
 | 
			
		||||
    InOut,
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
#[derive(serde::Serialize, serde::Deserialize, Debug, Clone)]
 | 
			
		||||
pub enum Layer4State {
 | 
			
		||||
    NEW,
 | 
			
		||||
    ESTABLISHED,
 | 
			
		||||
    RELATED,
 | 
			
		||||
    INVALID,
 | 
			
		||||
    NONE,
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
#[derive(serde::Serialize, serde::Deserialize, Debug, Clone)]
 | 
			
		||||
pub enum Layer4Type {
 | 
			
		||||
    TCP,
 | 
			
		||||
    UDP,
 | 
			
		||||
    SCTP,
 | 
			
		||||
    ICMP,
 | 
			
		||||
    TCPipv6,
 | 
			
		||||
    UDPipv6,
 | 
			
		||||
    SCTPipv6,
 | 
			
		||||
    ICMPipv6,
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
#[derive(serde::Serialize, serde::Deserialize, Debug, Clone)]
 | 
			
		||||
pub struct NetworkFilterSelectorIP<IPv> {
 | 
			
		||||
    srcmacaddr: Option<String>,
 | 
			
		||||
    srcmacmask: Option<String>,
 | 
			
		||||
    dstmacaddr: Option<String>,
 | 
			
		||||
    dstmacmask: Option<String>,
 | 
			
		||||
    srcipaddr: Option<IPv>,
 | 
			
		||||
    srcipmask: Option<u8>,
 | 
			
		||||
    dstipaddr: Option<IPv>,
 | 
			
		||||
    dstipmask: Option<u8>,
 | 
			
		||||
    comment: Option<String>,
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
#[derive(serde::Serialize, serde::Deserialize, Debug, Clone)]
 | 
			
		||||
pub enum NetworkFilterSelector {
 | 
			
		||||
    All,
 | 
			
		||||
    Mac {
 | 
			
		||||
        src_mac_addr: Option<String>,
 | 
			
		||||
        src_mac_mask: Option<String>,
 | 
			
		||||
        dst_mac_addr: Option<String>,
 | 
			
		||||
        dst_mac_mask: Option<String>,
 | 
			
		||||
        comment: Option<String>,
 | 
			
		||||
    },
 | 
			
		||||
    Arp {
 | 
			
		||||
        srcmacaddr: Option<String>,
 | 
			
		||||
        srcmacmask: Option<String>,
 | 
			
		||||
        dstmacaddr: Option<String>,
 | 
			
		||||
        dstmacmask: Option<String>,
 | 
			
		||||
        arpsrcipaddr: Option<IpAddr>,
 | 
			
		||||
        arpsrcipmask: Option<u8>,
 | 
			
		||||
        arpdstipaddr: Option<IpAddr>,
 | 
			
		||||
        arpdstipmask: Option<u8>,
 | 
			
		||||
        comment: Option<String>,
 | 
			
		||||
    },
 | 
			
		||||
    IPv4(NetworkFilterSelectorIP<Ipv4Addr>),
 | 
			
		||||
    IPv6(NetworkFilterSelectorIP<Ipv6Addr>),
 | 
			
		||||
    Layer4 {
 | 
			
		||||
        r#type: Layer4Type,
 | 
			
		||||
        srcmacaddr: Option<String>,
 | 
			
		||||
        srcipaddr: Option<IpAddr>,
 | 
			
		||||
        srcipmask: Option<u8>,
 | 
			
		||||
        dstipaddr: Option<IpAddr>,
 | 
			
		||||
        dstipmask: Option<u8>,
 | 
			
		||||
        /// Start of range of source IP address
 | 
			
		||||
        srcipfrom: Option<IpAddr>,
 | 
			
		||||
        /// End of range of source IP address
 | 
			
		||||
        srcipto: Option<IpAddr>,
 | 
			
		||||
        /// Start of range of destination IP address
 | 
			
		||||
        dstipfrom: Option<IpAddr>,
 | 
			
		||||
        /// End of range of destination IP address
 | 
			
		||||
        dstipto: Option<IpAddr>,
 | 
			
		||||
        srcportstart: Option<u16>,
 | 
			
		||||
        srcportend: Option<u16>,
 | 
			
		||||
        dstportstart: Option<u16>,
 | 
			
		||||
        dstportend: Option<u16>,
 | 
			
		||||
        state: Option<Layer4State>,
 | 
			
		||||
        comment: Option<String>,
 | 
			
		||||
    },
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
#[derive(serde::Serialize, serde::Deserialize, Debug, Clone)]
 | 
			
		||||
pub struct NetworkFilterRule {
 | 
			
		||||
    action: NetworkFilterAction,
 | 
			
		||||
    direction: NetworkFilterDirection,
 | 
			
		||||
    /// optional; the priority of the rule controls the order in which the rule will be instantiated
 | 
			
		||||
    /// relative to other rules
 | 
			
		||||
    ///
 | 
			
		||||
    /// Valid values are in the range of -1000 to 1000.
 | 
			
		||||
    priority: Option<i32>,
 | 
			
		||||
    selectors: Vec<NetworkFilterSelector>,
 | 
			
		||||
}
 | 
			
		||||
							
								
								
									
										386
									
								
								virtweb_backend/src/libvirt_rest_structures/vm.rs
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										386
									
								
								virtweb_backend/src/libvirt_rest_structures/vm.rs
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,386 @@
 | 
			
		||||
use crate::app_config::AppConfig;
 | 
			
		||||
use crate::constants;
 | 
			
		||||
use crate::libvirt_lib_structures::domain::*;
 | 
			
		||||
use crate::libvirt_lib_structures::XMLUuid;
 | 
			
		||||
use crate::libvirt_rest_structures::LibVirtStructError;
 | 
			
		||||
use crate::libvirt_rest_structures::LibVirtStructError::StructureExtraction;
 | 
			
		||||
use crate::utils::disks_utils::Disk;
 | 
			
		||||
use crate::utils::files_utils;
 | 
			
		||||
use crate::utils::files_utils::convert_size_unit_to_mb;
 | 
			
		||||
use lazy_regex::regex;
 | 
			
		||||
use num::Integer;
 | 
			
		||||
 | 
			
		||||
#[derive(serde::Serialize, serde::Deserialize)]
 | 
			
		||||
pub enum BootType {
 | 
			
		||||
    UEFI,
 | 
			
		||||
    UEFISecureBoot,
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
#[derive(serde::Serialize, serde::Deserialize)]
 | 
			
		||||
pub enum VMArchitecture {
 | 
			
		||||
    #[serde(rename = "i686")]
 | 
			
		||||
    I686,
 | 
			
		||||
    #[serde(rename = "x86_64")]
 | 
			
		||||
    X86_64,
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
#[derive(serde::Serialize, serde::Deserialize)]
 | 
			
		||||
pub struct Network {
 | 
			
		||||
    mac: String,
 | 
			
		||||
    #[serde(flatten)]
 | 
			
		||||
    r#type: NetworkType,
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
#[derive(serde::Serialize, serde::Deserialize)]
 | 
			
		||||
#[serde(tag = "type")]
 | 
			
		||||
pub enum NetworkType {
 | 
			
		||||
    UserspaceSLIRPStack,
 | 
			
		||||
    DefinedNetwork { network: String }, // TODO : complete network types
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
#[derive(serde::Serialize, serde::Deserialize)]
 | 
			
		||||
pub struct VMInfo {
 | 
			
		||||
    /// VM name (alphanumeric characters only)
 | 
			
		||||
    pub name: String,
 | 
			
		||||
    pub uuid: Option<XMLUuid>,
 | 
			
		||||
    pub genid: Option<XMLUuid>,
 | 
			
		||||
    pub title: Option<String>,
 | 
			
		||||
    pub description: Option<String>,
 | 
			
		||||
    pub boot_type: BootType,
 | 
			
		||||
    pub architecture: VMArchitecture,
 | 
			
		||||
    /// VM allocated memory, in megabytes
 | 
			
		||||
    pub memory: usize,
 | 
			
		||||
    /// Number of vCPU for the VM
 | 
			
		||||
    pub number_vcpu: usize,
 | 
			
		||||
    /// Enable VNC access through admin console
 | 
			
		||||
    pub vnc_access: bool,
 | 
			
		||||
    /// Attach ISO file(s)
 | 
			
		||||
    pub iso_files: Vec<String>,
 | 
			
		||||
    /// Storage - https://access.redhat.com/documentation/fr-fr/red_hat_enterprise_linux/6/html/virtualization_administration_guide/sect-virtualization-virtualized_block_devices-adding_storage_devices_to_guests#sect-Virtualization-Adding_storage_devices_to_guests-Adding_file_based_storage_to_a_guest
 | 
			
		||||
    pub disks: Vec<Disk>,
 | 
			
		||||
    /// Network cards
 | 
			
		||||
    pub networks: Vec<Network>,
 | 
			
		||||
    /// Add a TPM v2.0 module
 | 
			
		||||
    pub tpm_module: bool,
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
impl VMInfo {
 | 
			
		||||
    /// Turn this VM into a domain
 | 
			
		||||
    pub fn as_tomain(&self) -> anyhow::Result<DomainXML> {
 | 
			
		||||
        if !regex!("^[a-zA-Z0-9]+$").is_match(&self.name) {
 | 
			
		||||
            return Err(StructureExtraction("VM name is invalid!").into());
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        let uuid = if let Some(n) = self.uuid {
 | 
			
		||||
            if !n.is_valid() {
 | 
			
		||||
                return Err(StructureExtraction("VM UUID is invalid!").into());
 | 
			
		||||
            }
 | 
			
		||||
            n
 | 
			
		||||
        } else {
 | 
			
		||||
            XMLUuid::new_random()
 | 
			
		||||
        };
 | 
			
		||||
 | 
			
		||||
        if let Some(n) = &self.genid {
 | 
			
		||||
            if !n.is_valid() {
 | 
			
		||||
                return Err(StructureExtraction("VM genid is invalid!").into());
 | 
			
		||||
            }
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        if let Some(n) = &self.title {
 | 
			
		||||
            if n.contains('\n') {
 | 
			
		||||
                return Err(StructureExtraction("VM title contain newline char!").into());
 | 
			
		||||
            }
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        if self.memory < constants::MIN_VM_MEMORY || self.memory > constants::MAX_VM_MEMORY {
 | 
			
		||||
            return Err(StructureExtraction("VM memory is invalid!").into());
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        if self.number_vcpu == 0 || (self.number_vcpu != 1 && self.number_vcpu.is_odd()) {
 | 
			
		||||
            return Err(StructureExtraction("Invalid number of vCPU specified!").into());
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        let mut disks = vec![];
 | 
			
		||||
 | 
			
		||||
        for iso_file in &self.iso_files {
 | 
			
		||||
            if !files_utils::check_file_name(iso_file) {
 | 
			
		||||
                return Err(StructureExtraction("ISO filename is invalid!").into());
 | 
			
		||||
            }
 | 
			
		||||
 | 
			
		||||
            let path = AppConfig::get().iso_storage_path().join(iso_file);
 | 
			
		||||
 | 
			
		||||
            if !path.exists() {
 | 
			
		||||
                return Err(StructureExtraction("Specified ISO file does not exists!").into());
 | 
			
		||||
            }
 | 
			
		||||
 | 
			
		||||
            disks.push(DiskXML {
 | 
			
		||||
                r#type: "file".to_string(),
 | 
			
		||||
                device: "cdrom".to_string(),
 | 
			
		||||
                driver: DiskDriverXML {
 | 
			
		||||
                    name: "qemu".to_string(),
 | 
			
		||||
                    r#type: "raw".to_string(),
 | 
			
		||||
                    cache: "none".to_string(),
 | 
			
		||||
                },
 | 
			
		||||
                source: DiskSourceXML {
 | 
			
		||||
                    file: path.to_string_lossy().to_string(),
 | 
			
		||||
                },
 | 
			
		||||
                target: DiskTargetXML {
 | 
			
		||||
                    dev: format!(
 | 
			
		||||
                        "hd{}",
 | 
			
		||||
                        ["a", "b", "c", "d", "e", "f", "g", "h", "i", "j", "k", "l"][disks.len()]
 | 
			
		||||
                    ),
 | 
			
		||||
                    bus: "sata".to_string(),
 | 
			
		||||
                },
 | 
			
		||||
                readonly: Some(DiskReadOnlyXML {}),
 | 
			
		||||
                boot: DiskBootXML {
 | 
			
		||||
                    order: (disks.len() + 1).to_string(),
 | 
			
		||||
                },
 | 
			
		||||
                address: None,
 | 
			
		||||
            })
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        let (vnc_graphics, vnc_video) = match self.vnc_access {
 | 
			
		||||
            true => (
 | 
			
		||||
                Some(GraphicsXML {
 | 
			
		||||
                    r#type: "vnc".to_string(),
 | 
			
		||||
                    socket: AppConfig::get()
 | 
			
		||||
                        .vnc_socket_for_domain(&self.name)
 | 
			
		||||
                        .to_string_lossy()
 | 
			
		||||
                        .to_string(),
 | 
			
		||||
                }),
 | 
			
		||||
                Some(VideoXML {
 | 
			
		||||
                    model: VideoModelXML {
 | 
			
		||||
                        r#type: "virtio".to_string(), //"qxl".to_string(),
 | 
			
		||||
                    },
 | 
			
		||||
                }),
 | 
			
		||||
            ),
 | 
			
		||||
            false => (None, None),
 | 
			
		||||
        };
 | 
			
		||||
 | 
			
		||||
        // Check disks name for duplicates
 | 
			
		||||
        for disk in &self.disks {
 | 
			
		||||
            if self.disks.iter().filter(|d| d.name == disk.name).count() > 1 {
 | 
			
		||||
                return Err(StructureExtraction("Two different disks have the same name!").into());
 | 
			
		||||
            }
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        // Apply disks configuration
 | 
			
		||||
        for disk in &self.disks {
 | 
			
		||||
            disk.check_config()?;
 | 
			
		||||
            disk.apply_config(uuid)?;
 | 
			
		||||
 | 
			
		||||
            if disk.delete {
 | 
			
		||||
                continue;
 | 
			
		||||
            }
 | 
			
		||||
 | 
			
		||||
            disks.push(DiskXML {
 | 
			
		||||
                r#type: "file".to_string(),
 | 
			
		||||
                device: "disk".to_string(),
 | 
			
		||||
                driver: DiskDriverXML {
 | 
			
		||||
                    name: "qemu".to_string(),
 | 
			
		||||
                    r#type: "raw".to_string(),
 | 
			
		||||
                    cache: "none".to_string(),
 | 
			
		||||
                },
 | 
			
		||||
                source: DiskSourceXML {
 | 
			
		||||
                    file: disk.disk_path(uuid).to_string_lossy().to_string(),
 | 
			
		||||
                },
 | 
			
		||||
                target: DiskTargetXML {
 | 
			
		||||
                    dev: format!(
 | 
			
		||||
                        "vd{}",
 | 
			
		||||
                        ["a", "b", "c", "d", "e", "f", "g", "h", "i", "j", "k", "l"][disks.len()]
 | 
			
		||||
                    ),
 | 
			
		||||
                    bus: "virtio".to_string(),
 | 
			
		||||
                },
 | 
			
		||||
                readonly: None,
 | 
			
		||||
                boot: DiskBootXML {
 | 
			
		||||
                    order: (disks.len() + 1).to_string(),
 | 
			
		||||
                },
 | 
			
		||||
                address: None,
 | 
			
		||||
            })
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        let mut networks = vec![];
 | 
			
		||||
        for n in &self.networks {
 | 
			
		||||
            networks.push(match &n.r#type {
 | 
			
		||||
                NetworkType::UserspaceSLIRPStack => DomainNetInterfaceXML {
 | 
			
		||||
                    mac: NetMacAddress {
 | 
			
		||||
                        address: n.mac.to_string(),
 | 
			
		||||
                    },
 | 
			
		||||
                    r#type: "user".to_string(),
 | 
			
		||||
                    source: None,
 | 
			
		||||
                    model: Some(NetIntModelXML {
 | 
			
		||||
                        r#type: "virtio".to_string(),
 | 
			
		||||
                    }),
 | 
			
		||||
                },
 | 
			
		||||
                NetworkType::DefinedNetwork { network } => DomainNetInterfaceXML {
 | 
			
		||||
                    mac: NetMacAddress {
 | 
			
		||||
                        address: n.mac.to_string(),
 | 
			
		||||
                    },
 | 
			
		||||
                    r#type: "network".to_string(),
 | 
			
		||||
                    source: Some(NetIntSourceXML {
 | 
			
		||||
                        network: network.to_string(),
 | 
			
		||||
                    }),
 | 
			
		||||
                    model: Some(NetIntModelXML {
 | 
			
		||||
                        r#type: "virtio".to_string(),
 | 
			
		||||
                    }),
 | 
			
		||||
                },
 | 
			
		||||
            })
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        Ok(DomainXML {
 | 
			
		||||
            r#type: "kvm".to_string(),
 | 
			
		||||
            name: self.name.to_string(),
 | 
			
		||||
            uuid: Some(uuid),
 | 
			
		||||
            genid: self.genid.map(|i| i.0),
 | 
			
		||||
            title: self.title.clone(),
 | 
			
		||||
            description: self.description.clone(),
 | 
			
		||||
 | 
			
		||||
            os: OSXML {
 | 
			
		||||
                r#type: OSTypeXML {
 | 
			
		||||
                    arch: match self.architecture {
 | 
			
		||||
                        VMArchitecture::I686 => "i686",
 | 
			
		||||
                        VMArchitecture::X86_64 => "x86_64",
 | 
			
		||||
                    }
 | 
			
		||||
                    .to_string(),
 | 
			
		||||
                    machine: "q35".to_string(),
 | 
			
		||||
                    body: "hvm".to_string(),
 | 
			
		||||
                },
 | 
			
		||||
                firmware: "efi".to_string(),
 | 
			
		||||
                loader: Some(OSLoaderXML {
 | 
			
		||||
                    secure: match self.boot_type {
 | 
			
		||||
                        BootType::UEFI => "no".to_string(),
 | 
			
		||||
                        BootType::UEFISecureBoot => "yes".to_string(),
 | 
			
		||||
                    },
 | 
			
		||||
                }),
 | 
			
		||||
            },
 | 
			
		||||
 | 
			
		||||
            features: FeaturesXML { acpi: ACPIXML {} },
 | 
			
		||||
 | 
			
		||||
            devices: DevicesXML {
 | 
			
		||||
                graphics: vnc_graphics,
 | 
			
		||||
                video: vnc_video,
 | 
			
		||||
                disks,
 | 
			
		||||
                net_interfaces: networks,
 | 
			
		||||
                inputs: vec![
 | 
			
		||||
                    DomainInputXML {
 | 
			
		||||
                        r#type: "mouse".to_string(),
 | 
			
		||||
                    },
 | 
			
		||||
                    DomainInputXML {
 | 
			
		||||
                        r#type: "keyboard".to_string(),
 | 
			
		||||
                    },
 | 
			
		||||
                    DomainInputXML {
 | 
			
		||||
                        r#type: "tablet".to_string(),
 | 
			
		||||
                    },
 | 
			
		||||
                ],
 | 
			
		||||
                tpm: match self.tpm_module {
 | 
			
		||||
                    true => Some(TPMDeviceXML {
 | 
			
		||||
                        model: "tpm-tis".to_string(),
 | 
			
		||||
                        backend: TPMBackendXML {
 | 
			
		||||
                            r#type: "emulator".to_string(),
 | 
			
		||||
                            version: "2.0".to_string(),
 | 
			
		||||
                        },
 | 
			
		||||
                    }),
 | 
			
		||||
                    false => None,
 | 
			
		||||
                },
 | 
			
		||||
            },
 | 
			
		||||
 | 
			
		||||
            memory: DomainMemoryXML {
 | 
			
		||||
                unit: "MB".to_string(),
 | 
			
		||||
                memory: self.memory,
 | 
			
		||||
            },
 | 
			
		||||
 | 
			
		||||
            vcpu: DomainVCPUXML {
 | 
			
		||||
                body: self.number_vcpu,
 | 
			
		||||
            },
 | 
			
		||||
 | 
			
		||||
            cpu: DomainCPUXML {
 | 
			
		||||
                mode: "host-passthrough".to_string(),
 | 
			
		||||
                topology: Some(DomainCPUTopology {
 | 
			
		||||
                    sockets: 1,
 | 
			
		||||
                    cores: match self.number_vcpu {
 | 
			
		||||
                        1 => 1,
 | 
			
		||||
                        v => v / 2,
 | 
			
		||||
                    },
 | 
			
		||||
                    threads: match self.number_vcpu {
 | 
			
		||||
                        1 => 1,
 | 
			
		||||
                        _ => 2,
 | 
			
		||||
                    },
 | 
			
		||||
                }),
 | 
			
		||||
            },
 | 
			
		||||
 | 
			
		||||
            on_poweroff: "destroy".to_string(),
 | 
			
		||||
            on_reboot: "restart".to_string(),
 | 
			
		||||
            on_crash: "destroy".to_string(),
 | 
			
		||||
        })
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    /// Turn a domain into a vm
 | 
			
		||||
    pub fn from_domain(domain: DomainXML) -> anyhow::Result<Self> {
 | 
			
		||||
        Ok(Self {
 | 
			
		||||
            name: domain.name,
 | 
			
		||||
            uuid: domain.uuid,
 | 
			
		||||
            genid: domain.genid.map(XMLUuid),
 | 
			
		||||
            title: domain.title,
 | 
			
		||||
            description: domain.description,
 | 
			
		||||
            boot_type: match domain.os.loader {
 | 
			
		||||
                None => BootType::UEFI,
 | 
			
		||||
                Some(l) => match l.secure.as_str() {
 | 
			
		||||
                    "yes" => BootType::UEFISecureBoot,
 | 
			
		||||
                    _ => BootType::UEFI,
 | 
			
		||||
                },
 | 
			
		||||
            },
 | 
			
		||||
            architecture: match domain.os.r#type.arch.as_str() {
 | 
			
		||||
                "i686" => VMArchitecture::I686,
 | 
			
		||||
                "x86_64" => VMArchitecture::X86_64,
 | 
			
		||||
                a => {
 | 
			
		||||
                    return Err(LibVirtStructError::DomainExtraction(format!(
 | 
			
		||||
                        "Unknown architecture: {a}! "
 | 
			
		||||
                    ))
 | 
			
		||||
                    .into());
 | 
			
		||||
                }
 | 
			
		||||
            },
 | 
			
		||||
            number_vcpu: domain.vcpu.body,
 | 
			
		||||
            memory: convert_size_unit_to_mb(&domain.memory.unit, domain.memory.memory)?,
 | 
			
		||||
            vnc_access: domain.devices.graphics.is_some(),
 | 
			
		||||
            iso_files: domain
 | 
			
		||||
                .devices
 | 
			
		||||
                .disks
 | 
			
		||||
                .iter()
 | 
			
		||||
                .filter(|d| d.device == "cdrom")
 | 
			
		||||
                .map(|d| d.source.file.rsplit_once('/').unwrap().1.to_string())
 | 
			
		||||
                .collect(),
 | 
			
		||||
 | 
			
		||||
            disks: domain
 | 
			
		||||
                .devices
 | 
			
		||||
                .disks
 | 
			
		||||
                .iter()
 | 
			
		||||
                .filter(|d| d.device == "disk")
 | 
			
		||||
                .map(|d| Disk::load_from_file(&d.source.file).unwrap())
 | 
			
		||||
                .collect(),
 | 
			
		||||
 | 
			
		||||
            networks: domain
 | 
			
		||||
                .devices
 | 
			
		||||
                .net_interfaces
 | 
			
		||||
                .iter()
 | 
			
		||||
                .map(|d| {
 | 
			
		||||
                    Ok(Network {
 | 
			
		||||
                        mac: d.mac.address.to_string(),
 | 
			
		||||
                        r#type: match d.r#type.as_str() {
 | 
			
		||||
                            "user" => NetworkType::UserspaceSLIRPStack,
 | 
			
		||||
                            "network" => NetworkType::DefinedNetwork {
 | 
			
		||||
                                network: d.source.as_ref().unwrap().network.to_string(),
 | 
			
		||||
                            },
 | 
			
		||||
                            a => {
 | 
			
		||||
                                return Err(LibVirtStructError::DomainExtraction(format!(
 | 
			
		||||
                                    "Unknown network interface type: {a}! "
 | 
			
		||||
                                )));
 | 
			
		||||
                            }
 | 
			
		||||
                        },
 | 
			
		||||
                    })
 | 
			
		||||
                })
 | 
			
		||||
                .collect::<Result<Vec<_>, _>>()?,
 | 
			
		||||
 | 
			
		||||
            tpm_module: domain.devices.tpm.is_some(),
 | 
			
		||||
        })
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
		Reference in New Issue
	
	Block a user