diff --git a/virtweb_backend/src/actors/libvirt_actor.rs b/virtweb_backend/src/actors/libvirt_actor.rs index 3006e06..7a9380f 100644 --- a/virtweb_backend/src/actors/libvirt_actor.rs +++ b/virtweb_backend/src/actors/libvirt_actor.rs @@ -377,3 +377,39 @@ impl Handler for LibVirtActor { XMLUuid::parse_from_str(&network.get_uuid_string()?) } } + +#[derive(Message)] +#[rtype(result = "anyhow::Result>")] +pub struct GetNetworksListReq; + +impl Handler for LibVirtActor { + type Result = anyhow::Result>; + + fn handle(&mut self, _msg: GetNetworksListReq, _ctx: &mut Self::Context) -> Self::Result { + log::debug!("Get full list of networks"); + let networks = self.m.list_all_networks(0)?; + let mut ids = Vec::with_capacity(networks.len()); + + for d in networks { + ids.push(XMLUuid::parse_from_str(&d.get_uuid_string()?)?); + } + + Ok(ids) + } +} + +#[derive(Message)] +#[rtype(result = "anyhow::Result")] +pub struct GetNetworkXMLReq(pub XMLUuid); + +impl Handler for LibVirtActor { + type Result = anyhow::Result; + + fn handle(&mut self, msg: GetNetworkXMLReq, _ctx: &mut Self::Context) -> Self::Result { + log::debug!("Get network XML:\n{}", msg.0.as_string()); + let network = Network::lookup_by_uuid_string(&self.m, &msg.0.as_string())?; + let xml = network.get_xml_desc(0)?; + log::debug!("XML = {}", xml); + Ok(serde_xml_rs::from_str(&xml)?) + } +} diff --git a/virtweb_backend/src/controllers/network_controller.rs b/virtweb_backend/src/controllers/network_controller.rs index f4e3e56..224d8d9 100644 --- a/virtweb_backend/src/controllers/network_controller.rs +++ b/virtweb_backend/src/controllers/network_controller.rs @@ -21,3 +21,15 @@ pub async fn create(client: LibVirtReq, req: web::Json) -> HttpResu Ok(HttpResponse::Ok().json(NetworkID { id })) } + +/// Get the list of networks +pub async fn list(client: LibVirtReq) -> HttpResult { + let networks = client + .get_full_networks_list() + .await? + .into_iter() + .map(|n| NetworkInfo::from_xml(n).unwrap()) + .collect::>(); + + Ok(HttpResponse::Ok().json(networks)) +} diff --git a/virtweb_backend/src/controllers/vm_controller.rs b/virtweb_backend/src/controllers/vm_controller.rs index 5c39db8..1538936 100644 --- a/virtweb_backend/src/controllers/vm_controller.rs +++ b/virtweb_backend/src/controllers/vm_controller.rs @@ -34,7 +34,7 @@ pub async fn create(client: LibVirtReq, req: web::Json) -> HttpResult { /// Get the list of domains pub async fn list_all(client: LibVirtReq) -> HttpResult { - let list = client.get_full_list().await?; + let list = client.get_full_domains_list().await?; let mut out = Vec::with_capacity(list.len()); for entry in list { diff --git a/virtweb_backend/src/libvirt_client.rs b/virtweb_backend/src/libvirt_client.rs index ba9d6b1..6a3561e 100644 --- a/virtweb_backend/src/libvirt_client.rs +++ b/virtweb_backend/src/libvirt_client.rs @@ -14,7 +14,7 @@ impl LibVirtClient { } /// Get the full list of domain - pub async fn get_full_list(&self) -> anyhow::Result> { + pub async fn get_full_domains_list(&self) -> anyhow::Result> { let ids = self.0.send(libvirt_actor::GetDomainsListReq).await??; let mut info = Vec::with_capacity(ids.len()); for id in ids { @@ -96,4 +96,19 @@ impl LibVirtClient { pub async fn update_network(&self, network: NetworkXML) -> anyhow::Result { self.0.send(libvirt_actor::DefineNetwork(network)).await? } + + /// Get the full list of networks + pub async fn get_full_networks_list(&self) -> anyhow::Result> { + let ids = self.0.send(libvirt_actor::GetNetworksListReq).await??; + let mut info = Vec::with_capacity(ids.len()); + for id in ids { + info.push(self.get_single_network(id).await?) + } + Ok(info) + } + + /// Get the information about a single network + pub async fn get_single_network(&self, id: XMLUuid) -> anyhow::Result { + self.0.send(libvirt_actor::GetNetworkXMLReq(id)).await? + } } diff --git a/virtweb_backend/src/libvirt_lib_structures.rs b/virtweb_backend/src/libvirt_lib_structures.rs index bd0a60f..fea5b5b 100644 --- a/virtweb_backend/src/libvirt_lib_structures.rs +++ b/virtweb_backend/src/libvirt_lib_structures.rs @@ -304,5 +304,6 @@ pub struct NetworkXML { pub dns: Option, #[serde(skip_serializing_if = "Option::is_none")] pub domain: Option, + #[serde(default, rename = "ip")] pub ips: Vec, } diff --git a/virtweb_backend/src/libvirt_rest_structures.rs b/virtweb_backend/src/libvirt_rest_structures.rs index c0cae2d..2ba4548 100644 --- a/virtweb_backend/src/libvirt_rest_structures.rs +++ b/virtweb_backend/src/libvirt_rest_structures.rs @@ -423,6 +423,64 @@ impl NetworkInfo { ips, }) } + + pub fn from_xml(xml: NetworkXML) -> anyhow::Result { + 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| f.dev).unwrap_or(None), + 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: i.prefix, + dhcp_range: i + .dhcp + .as_ref() + .map(|d| [extract_ipv4(d.range.start), extract_ipv4(d.range.end)]), + }), + ip_v6: xml + .ips + .iter() + .find(|i| i.family == "ipv6") + .map(|i| IPV6Config { + bridge_address: extract_ipv6(i.address), + prefix: i.prefix, + dhcp_range: i + .dhcp + .as_ref() + .map(|d| [extract_ipv6(d.range.start), extract_ipv6(d.range.end)]), + }), + }) + } +} + +fn extract_ipv4(ip: IpAddr) -> Ipv4Addr { + match ip { + IpAddr::V4(i) => i, + IpAddr::V6(_) => { + panic!("IPv6 found in IPv4 definition!") + } + } +} + +fn extract_ipv6(ip: IpAddr) -> Ipv6Addr { + match ip { + IpAddr::V4(_) => { + panic!("IPv4 found in IPv6 definition!") + } + IpAddr::V6(i) => i, + } } #[cfg(test)] diff --git a/virtweb_backend/src/main.rs b/virtweb_backend/src/main.rs index 0b86c42..45707f2 100644 --- a/virtweb_backend/src/main.rs +++ b/virtweb_backend/src/main.rs @@ -181,6 +181,7 @@ async fn main() -> std::io::Result<()> { "/api/network/create", web::post().to(network_controller::create), ) + .route("/api/network/list", web::get().to(network_controller::list)) }) .bind(&AppConfig::get().listen_address)? .run()