Refacto structures definition
This commit is contained in:
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(),
|
||||
}),
|
||||
}),
|
||||
})
|
||||
}
|
||||
}
|
Reference in New Issue
Block a user