From 77fa95a342f09953e3207ea0346135301ffe2664 Mon Sep 17 00:00:00 2001 From: Cara Salter Date: Thu, 26 May 2022 16:36:41 -0400 Subject: Project rename --- src/house.rs | 247 ---------------------------------------------------------- src/lib.rs | 6 +- src/planet.rs | 179 ++++++++++++++++++++++++++++++++++++++++++ src/ship.rs | 98 +++++++++++++++++++++++ src/star.rs | 240 ++++++++++++++++++++++++++++++++++++++++++++++++++++++++ src/van.rs | 80 ------------------- src/waifu.rs | 142 --------------------------------- 7 files changed, 520 insertions(+), 472 deletions(-) delete mode 100644 src/house.rs create mode 100644 src/planet.rs create mode 100644 src/ship.rs create mode 100644 src/star.rs delete mode 100644 src/van.rs delete mode 100644 src/waifu.rs (limited to 'src') diff --git a/src/house.rs b/src/house.rs deleted file mode 100644 index abd0eef..0000000 --- a/src/house.rs +++ /dev/null @@ -1,247 +0,0 @@ -/*! - * `House`s are where [crate::waifu::Waifu]s live (the physical hypervisors that - * libvirtd connects to - */ -use crate::waifu::*; -use crate::errors::Error; -use crate::van::Van; -use serde::{Serialize, Deserialize}; -use virt::{connect::Connect, domain::Domain}; -use std::process::{ExitStatus}; -use std::os::unix::process::ExitStatusExt; -use rand::Rng; - -use tokio::{process::Command}; - -#[derive(Serialize, Deserialize, Debug)] -pub enum Address { - IP(String), - Domain(String) -} - -impl ToString for Address { - fn to_string(&self) -> String { - match self { - Address::IP(s) => s.clone(), - Address::Domain(s) => s.clone(), - } - } -} - -/// Defines a "house" where waifus live -#[derive(Debug, Serialize, Deserialize)] -pub struct House { - /// Hostname - pub name: String, - /// FQDN or IP address, a way to talk to the house - pub address: String, - - /// Whether or not the House is local (same machine) or remote (networked machine) - pub remote: bool, - - /// Connection to the House, if available - #[serde(skip)] - con: Option, -} - -impl House { - /// Creates a new House based on a libvirtd connect URL - /// - /// Example: - /// ``` - /// use waifulib::house::House; - /// let mut h: House = House::new("test:///default".to_string()).unwrap(); - /// ``` - pub fn new(url: String) -> Result { - let c = Connect::open(&url.clone())?; - - let remote = if url.contains("qemu:///") || url.contains("localhost") || url.contains("127.0.0.1") { - true - } else { - false - }; - - // If the connection succeeds, we've got one! - Ok(Self { - name: c.get_hostname()?, - address: c.get_uri()?, - remote, - con: Some(c), - }) - } - - /// Lists the "inhabitants" of the House (the [Waifu]s on the machine - /// - /// ``` - /// use waifulib::house::House; - /// let mut h: House = House::new("test:///default".to_string()).unwrap(); - /// - /// assert_eq!(h.inhabitants().unwrap().len(), 1); - /// ``` - pub fn inhabitants(&mut self) -> Result, Error> { - match &self.con { - Some(c) => { - let domains = c.list_all_domains(0)?; - - let mut waifus: Vec = Vec::new(); - - for d in domains.iter() { - waifus.push(d.clone().try_into()?); - } - Ok(waifus) - }, - None => { - return Err(Error::Connection("Domain connection was None".to_string())); - } - } - } - - /// Introduces a new Waifu into the House, taking care of everything needed to make the VM run - /// fine - /// - /// If the installation image doesn't exist in the default libvirtd pool, this will fail with - /// [`Error::MissingImage`][crate::errors::Error::MissingImage]. - /// - /// ``` - /// use waifulib::house::House - /// - /// let mut h: House = House::new("test:///default".to_string()).unwrap(); - /// - /// h.introduce("test-2", 1024, 1, 20000, "test.iso").unwrap(); - /// ``` - pub async fn introduce(&mut self, name: String, max_mem: Memory, max_cpus: CpuCount, disk_size_mb: u64, van: Van) -> Result { - // Check for image on host machine - - if self.remote { - let mut output = Command::new("ssh") - .args([ - "-oStrictHostKeyChecking=accept-new", - &self.address.clone(), - "stat", - &format!("/var/lib/libvirt/images/{}", van.make_pretty_name().clone()) - ]) - .output() - .await?; - - if output.status != ExitStatus::from_raw(0) { - return Err(Error::MissingImage(van.name.clone())); - } - - // Allocate VM disk - output = Command::new("ssh") - .args([ - "-oStrictHostKeyChecking=accept-new", - &self.address.clone(), - "qemu-img", - "create", - "-f", - "qcow2", - &format!("/var/lib/libvirt/images/{}.qcow2", name.clone()), - &format!("{}M", disk_size_mb) - ]) - .output() - .await?; - - if output.status != ExitStatus::from_raw(0) { - return Err(Error::Allocation(String::from_utf8(output.stdout).unwrap())); - } - } else { - // It's local - let mut output = Command::new("stat") - .args([ - &format!("/var/lib/libvirt/images/{}", van.make_pretty_name().clone()), - ]) - .output() - .await?; - - if output.status != ExitStatus::from_raw(0) { - return Err(Error::MissingImage(van.name.clone())); - } - - output = Command::new("qemu-img") - .args([ - "create", - "-f", - "qcow2", - &format!("/var/lib/libvirt/images/{}.qcow2", name.clone()), - &format!("{}M", disk_size_mb) - ]) - .output() - .await?; - - if output.status != ExitStatus::from_raw(0) { - return Err(Error::Allocation(String::from_utf8(output.stdout).unwrap())); - } - } - - let uuid = uuid::Uuid::new_v4(); - - // Let's get that XML ready - let mut buf: Vec = vec![]; - - crate::templates::vm_xml( - &mut buf, - name.clone(), - uuid.to_string(), - random_mac().clone(), - true, - max_mem.0, - max_cpus.0, - "blank".to_string() - )?; - - let buf = String::from_utf8(buf).unwrap(); - - println!("{}", buf); - - let dom: Domain = match &self.con { - Some(c) => { - let dom = Domain::define_xml(&c, &buf)?; - - dom.create()?; - - dom - }, - None => { - return Err(Error::Connection("Connection was None".to_string())); - } - }; - - dom.try_into() - } - -} - -fn random_mac() -> String { - let mut addr = rand::thread_rng().gen::<[u8; 6]>(); - addr[0] = (addr[0] | 2) & 0xfe; - mac_address::MacAddress::new(addr).to_string() -} - -#[cfg(test)] -mod test { - use super::*; - /// Kind of a stupid test, but just a sanity check to make sure that [ToString] impl up above - /// is still working - #[test] - fn addr_to_string() { - let a: Address = Address::IP("127.0.0.1".to_string()); - let d: Address = Address::Domain("example.com".to_string()); - - assert_eq!(String::from("127.0.0.1"), a.to_string()); - assert_eq!("example.com".to_string(), d.to_string()); - } - - #[test] - fn connect_to_house() { - let _: House = House::new("test:///default".to_string()).unwrap(); - } - - #[test] - fn list_inhabitants() { - let mut h: House = House::new("test:///default".to_string()).unwrap(); - - assert_eq!(h.inhabitants().unwrap().len(), 1); - } - -} diff --git a/src/lib.rs b/src/lib.rs index 0518b63..75e7c77 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -1,9 +1,9 @@ pub mod errors; -pub mod waifu; +pub mod planet; -pub mod house; +pub mod star; -pub mod van; +pub mod ship; include!(concat!(env!("OUT_DIR"), "/templates.rs")); diff --git a/src/planet.rs b/src/planet.rs new file mode 100644 index 0000000..c29fb69 --- /dev/null +++ b/src/planet.rs @@ -0,0 +1,179 @@ +use std::convert::TryFrom; +use virt::{domain::{Domain, DomainState}}; +use serde::{Serialize, Deserialize}; + +use crate::errors::Error; + +/** + * Defines the amount of memory a planet has + */ +#[derive(Debug, Serialize, Deserialize)] +pub struct Memory(pub u64); + +impl From for Memory { + fn from(u: u64) -> Self { + Self(u) + } +} + +/** + * Defines the number of vCPUs a planet has + */ +#[derive(Debug, Serialize, Deserialize)] +pub struct CpuCount(pub u64); + +impl From for CpuCount { + fn from(u: u64) -> Self { + Self(u) + } +} + +/** + * Represents a virtual machine, that's active on some server + * + * In keeping with the theme, it's named [Planet] :) + * + * There is a private `domain` field that contains a reference to the actual domain. This will not + * be (de)serialized, and, if needed across a network, should be recreated from the `host` and + * `uuid` attributes using [virt::domain::lookup_from_uuid_string] + */ +#[derive(Debug, Serialize, Deserialize)] +pub struct Planet { + /// The reference name of the machine + pub name: String, + + /// The physical machine where this one lives + pub host: String, + + /// The UUID + pub uuid: String, + + /// The network address where this machine can be reached + pub addr: Option, + + /// The amount of RAM (in MB) assigned to this machine + pub mem: Memory, + + /// The amount of vCPUs assigned to this machine + pub cpu_count: CpuCount, + + #[serde(skip)] + domain: Option, +} + +impl PartialEq for Planet { + fn eq(&self, other: &Self) -> bool { + self.uuid == other.uuid + } +} + +impl TryFrom for Planet { + type Error = Error; + + fn try_from(d: Domain) -> Result { + let c = d.get_connect()?; + + // This... feels wrong + // + // I know it probably works + // + // Based on code by Cadey in waifud + let addr: Option = if d.is_active()? { + let mut addr: Vec = d + .interface_addresses(virt::domain::VIR_DOMAIN_INTERFACE_ADDRESSES_SRC_LEASE, 0)? + .into_iter() + .map(|iface| iface.addrs.clone()) + .filter(|addrs| addrs.get(0).is_some()) + .map(|addrs| addrs.get(0).unwrap().clone().addr) + .collect(); + + if addr.get(0).is_none() { + Some(String::from("localhost")) + } else { + Some(addr.swap_remove(0)) + } + } else { + None + }; + + Ok(Self { + name: d.get_name()?, + host: c.get_hostname()?, + addr, + uuid: d.get_uuid_string()?, + mem: d.get_max_memory()?.into(), + cpu_count: d.get_max_vcpus()?.into(), + domain: Some(d), + }) + } +} + + +impl TryFrom<&Domain> for Planet { + type Error = Error; + + fn try_from(d: &Domain) -> Result { + let c = d.get_connect()?; + + // This... feels wrong + // + // I know it probably works + // + // Based on code by Cadey in waifud + let addr: Option = if d.is_active()? { + let mut addr: Vec = d + .interface_addresses(virt::domain::VIR_DOMAIN_INTERFACE_ADDRESSES_SRC_LEASE, 0)? + .into_iter() + .map(|iface| iface.addrs.clone()) + .filter(|addrs| addrs.get(0).is_some()) + .map(|addrs| addrs.get(0).unwrap().clone().addr) + .collect(); + + if addr.get(0).is_none() { + Some(String::from("localhost")) + } else { + Some(addr.swap_remove(0)) + } + } else { + None + }; + + Ok(Self { + name: d.get_name()?, + host: c.get_hostname()?, + addr, + uuid: d.get_uuid_string()?, + mem: d.get_max_memory()?.into(), + cpu_count: d.get_max_vcpus()?.into(), + domain: Some(*d), + }) + } +} + +#[repr(u32)] +#[derive(Serialize, Deserialize)] +pub enum Health { + Unknown = 0, + Running = 1, + Blocked = 2, + Paused = 3, + ShuttingDown = 4, + ShutDown = 5, + Crashed = 6, + GuestSuspended = 7 +} + +impl Planet { + fn get_status(&self) -> Result { + let d = match self.domain { + Some(d) => d, + None => { + return Err(Error::Other(String::from("No domain connection found"))); + } + }; + + let state = d.get_state()?; + + Ok(state.0 as Health) + } +} diff --git a/src/ship.rs b/src/ship.rs new file mode 100644 index 0000000..79253e1 --- /dev/null +++ b/src/ship.rs @@ -0,0 +1,98 @@ +/*! A Ship is a star ship, or an installation ISO */ + +use tokio::process::Command; +use std::process::{ExitStatus, Output}; +use std::os::unix::process::ExitStatusExt; +use std::str::FromStr; + +use crate::{errors::Error, star::Star}; + +use serde::{Serialize, Deserialize}; + +/// Describes a hash of a file +#[derive(Serialize, Deserialize)] +pub struct Sha256(pub String); + +impl FromStr for Sha256 { + type Err = Error; + fn from_str(s: &str) -> Result { + let sum = s.clone(); + + Ok(Sha256(sum.to_string())) + } +} + +impl ToString for Sha256 { + fn to_string(&self) -> String { + self.0.clone() + } +} + +/// Describes a starship, or a way to install a distribution +#[derive(Serialize, Deserialize)] +pub struct Ship { + /// The common name of the distribution (e.g "Arch Linux") + pub name: String, + /// The SHA-256 hash of the downloaded file + pub shasum: Sha256, + /// Where the ISO can be downloaded from + pub download_url: String, + /// The commonly accepted version (e.g "rolling", "21.11", "unstable") + pub version: String, +} + +impl Ship { + pub fn new(name: String, + shasum: String, + download_url: String, + version: String + ) -> Self { + Self { + name, + shasum: Sha256(shasum), + download_url, + version + } + } + + pub async fn download(&self, target: Star) -> Result<(), Error> { + + let mut output: Output; + + if target.remote { + output = Command::new("ssh") + .args([ + "-oStrictHostKeyChecking=accept-new", + &target.address.clone(), + "wget", + "-O", + &format!("/var/lib/libvirt/images/{}", self.make_pretty_name().clone()), + &self.download_url.clone(), + ]) + .output() + .await?; + } else { + output = Command::new("wget") + .args([ + "-O", + &format!("/var/lib/libvirt/images/{}", self.make_pretty_name().clone()), + &self.download_url.clone(), + ]) + .output() + .await?; + } + + if output.status != ExitStatus::from_raw(0) { + Err(Error::RemoteCommand(String::from_utf8(output.stdout).unwrap())) + } else { + Ok(()) + } + } + + pub fn make_pretty_name(&self) -> String { + let safe_name = self.name.clone().to_lowercase().replace(" ", "-"); + let file_name = format!("{}-{}-{}.van", safe_name, self.version.clone(), self.shasum.0.clone()); + + file_name + } +} diff --git a/src/star.rs b/src/star.rs new file mode 100644 index 0000000..9cd9d14 --- /dev/null +++ b/src/star.rs @@ -0,0 +1,240 @@ +/*! + * `Star`s are where [crate::planet::Planet]s orbit (the physical hypervisors that + * libvirtd connects to + */ +use crate::planet::*; +use crate::errors::Error; +use crate::ship::Ship; +use serde::{Serialize, Deserialize}; +use virt::{connect::Connect, domain::Domain}; +use std::process::{ExitStatus}; +use std::os::unix::process::ExitStatusExt; +use rand::Rng; + +use tokio::{process::Command}; + +#[derive(Serialize, Deserialize, Debug)] +pub enum Address { + IP(String), + Domain(String) +} + +impl ToString for Address { + fn to_string(&self) -> String { + match self { + Address::IP(s) => s.clone(), + Address::Domain(s) => s.clone(), + } + } +} + +/// Defines a "star" where [crate::planet::Planet]s orbit +#[derive(Debug, Serialize, Deserialize)] +pub struct Star { + /// Hostname + pub name: String, + /// FQDN or IP address, a way to talk to the planet + pub address: String, + + /// Whether or not the Planet is local (same machine) or remote (networked machine) + pub remote: bool, + + /// Connection to the House, if available + #[serde(skip)] + con: Option, +} + +impl Star { + /// Creates a new Planet based on a libvirtd connect URL + /// + /// Example: + /// ``` + /// use waifulib::planet::Planet; + /// let mut h = Planet::new("test:///default".to_string()).unwrap(); + /// ``` + pub fn new(url: String) -> Result { + let c = Connect::open(&url.clone())?; + + let remote = if url.contains("qemu:///") || url.contains("localhost") || url.contains("127.0.0.1") { + true + } else { + false + }; + + // If the connection succeeds, we've got one! + Ok(Self { + name: c.get_hostname()?, + address: c.get_uri()?, + remote, + con: Some(c), + }) + } + + /// Lists the "inhabitants" of the House (the [Waifu]s on the machine + /// + /// ``` + /// use crate::star::Star; + /// let mut h = Star::new("test:///default".to_string()).unwrap(); + /// + /// assert_eq!(h.inhabitants().unwrap().len(), 1); + /// ``` + pub fn inhabitants(&mut self) -> Result, Error> { + match &self.con { + Some(c) => { + let domains = c.list_all_domains(0)?; + + let mut planets: Vec = Vec::new(); + + for d in domains.iter() { + planets.push(d.clone().try_into()?); + } + Ok(planets) + }, + None => { + return Err(Error::Connection("Domain connection was None".to_string())); + } + } + } + + /// Creates a new Planet orbiting the Star, taking care of everything needed to make the VM run + /// fine + /// + /// If the installation image doesn't exist in the default libvirtd pool, this will fail with + /// [`Error::MissingImage`][crate::errors::Error::MissingImage]. + /// + pub async fn planet(&mut self, name: String, max_mem: Memory, max_cpus: CpuCount, disk_size_mb: u64, ship: Ship) -> Result { + // Check for image on host machine + + if self.remote { + let mut output = Command::new("ssh") + .args([ + "-oStrictHostKeyChecking=accept-new", + &self.address.clone(), + "stat", + &format!("/var/lib/libvirt/images/{}", ship.make_pretty_name().clone()) + ]) + .output() + .await?; + + if output.status != ExitStatus::from_raw(0) { + return Err(Error::MissingImage(ship.name.clone())); + } + + // Allocate VM disk + output = Command::new("ssh") + .args([ + "-oStrictHostKeyChecking=accept-new", + &self.address.clone(), + "qemu-img", + "create", + "-f", + "qcow2", + &format!("/var/lib/libvirt/images/{}.qcow2", name.clone()), + &format!("{}M", disk_size_mb) + ]) + .output() + .await?; + + if output.status != ExitStatus::from_raw(0) { + return Err(Error::Allocation(String::from_utf8(output.stdout).unwrap())); + } + } else { + // It's local + let mut output = Command::new("stat") + .args([ + &format!("/var/lib/libvirt/images/{}", ship.make_pretty_name().clone()), + ]) + .output() + .await?; + + if output.status != ExitStatus::from_raw(0) { + return Err(Error::MissingImage(ship.name.clone())); + } + + output = Command::new("qemu-img") + .args([ + "create", + "-f", + "qcow2", + &format!("/var/lib/libvirt/images/{}.qcow2", name.clone()), + &format!("{}M", disk_size_mb) + ]) + .output() + .await?; + + if output.status != ExitStatus::from_raw(0) { + return Err(Error::Allocation(String::from_utf8(output.stdout).unwrap())); + } + } + + let uuid = uuid::Uuid::new_v4(); + + // Let's get that XML ready + let mut buf: Vec = vec![]; + + crate::templates::vm_xml( + &mut buf, + name.clone(), + uuid.to_string(), + random_mac().clone(), + true, + max_mem.0, + max_cpus.0, + "blank".to_string() + )?; + + let buf = String::from_utf8(buf).unwrap(); + + println!("{}", buf); + + let dom: Domain = match &self.con { + Some(c) => { + let dom = Domain::define_xml(&c, &buf)?; + + dom.create()?; + + dom + }, + None => { + return Err(Error::Connection("Connection was None".to_string())); + } + }; + + dom.try_into() + } + +} + +fn random_mac() -> String { + let mut addr = rand::thread_rng().gen::<[u8; 6]>(); + addr[0] = (addr[0] | 2) & 0xfe; + mac_address::MacAddress::new(addr).to_string() +} + +#[cfg(test)] +mod test { + use super::*; + /// Kind of a stupid test, but just a sanity check to make sure that [ToString] impl up above + /// is still working + #[test] + fn addr_to_string() { + let a: Address = Address::IP("127.0.0.1".to_string()); + let d: Address = Address::Domain("example.com".to_string()); + + assert_eq!(String::from("127.0.0.1"), a.to_string()); + assert_eq!("example.com".to_string(), d.to_string()); + } + + #[test] + fn connect_to_house() { + let _: House = House::new("test:///default".to_string()).unwrap(); + } + + #[test] + fn list_inhabitants() { + let mut h: House = House::new("test:///default".to_string()).unwrap(); + + assert_eq!(h.inhabitants().unwrap().len(), 1); + } + +} diff --git a/src/van.rs b/src/van.rs deleted file mode 100644 index 64e7295..0000000 --- a/src/van.rs +++ /dev/null @@ -1,80 +0,0 @@ -/*! A Van is a moving van, or an installation ISO */ - -use tokio::process::Command; -use std::process::ExitStatus; -use std::os::unix::process::ExitStatusExt; -use std::str::FromStr; - -use crate::errors::Error; - -/// Describes a hash of a file -pub struct Sha256(pub String); - -impl FromStr for Sha256 { - type Err = Error; - fn from_str(s: &str) -> Result { - let sum = s.clone(); - - Ok(Sha256(sum.to_string())) - } -} - -impl ToString for Sha256 { - fn to_string(&self) -> String { - self.0.clone() - } -} - -/// Describes a moving Van, or a way to install a distribution -pub struct Van { - /// The common name of the distribution (e.g "Arch Linux") - pub name: String, - /// The SHA-256 hash of the downloaded file - pub shasum: Sha256, - /// Where the ISO can be downloaded from - pub download_url: String, - /// The commonly accepted version (e.g "rolling", "21.11", "unstable") - pub version: String, -} - -impl Van { - pub fn new(name: String, - shasum: String, - download_url: String, - version: String - ) -> Self { - Self { - name, - shasum: Sha256(shasum), - download_url, - version - } - } - - pub async fn download(&self, target: String) -> Result<(), Error> { - let output = Command::new("ssh") - .args([ - "-oStrictHostKeyChecking=accept-new", - &target.clone(), - "wget", - "-O", - &self.download_url.clone(), - &format!("/var/lib/libvirt/images/{}", self.make_pretty_name().clone()) - ]) - .output() - .await?; - - if output.status != ExitStatus::from_raw(0) { - Err(Error::RemoteCommand(String::from_utf8(output.stdout).unwrap())) - } else { - Ok(()) - } - } - - pub fn make_pretty_name(&self) -> String { - let safe_name = self.name.clone().to_lowercase().replace(" ", "-"); - let file_name = format!("{}-{}-{}.van", safe_name, self.version.clone(), self.shasum.0.clone()); - - file_name - } -} diff --git a/src/waifu.rs b/src/waifu.rs deleted file mode 100644 index bb99d2b..0000000 --- a/src/waifu.rs +++ /dev/null @@ -1,142 +0,0 @@ -use std::convert::TryFrom; -use virt::{domain::Domain}; -use serde::{Serialize, Deserialize}; - -use crate::errors::Error; - -/** - * Defines the amount of memory a waifu has - */ -#[derive(Debug, Serialize, Deserialize)] -pub struct Memory(pub u64); - -impl From for Memory { - fn from(u: u64) -> Self { - Self(u) - } -} - -/** - * Defines the number of vCPUs a waifu has - */ -#[derive(Debug, Serialize, Deserialize)] -pub struct CpuCount(pub u64); - -impl From for CpuCount { - fn from(u: u64) -> Self { - Self(u) - } -} - -/** - * Represents a virtual machine, that's active on some server - * - * In keeping with the theme, it's named [Waifu] :) - */ -#[derive(Debug, Serialize, Deserialize)] -pub struct Waifu { - /// The reference name of the machine - pub name: String, - - /// The physical machine where this one lives - pub host: String, - - /// The UUID - pub uuid: String, - - /// The network address where this machine can be reached - pub addr: Option, - - /// The amount of RAM (in MB) assigned to this machine - pub mem: Memory, - - /// The amount of vCPUs assigned to this machine - pub cpu_count: CpuCount, -} - -impl PartialEq for Waifu { - fn eq(&self, other: &Self) -> bool { - self.uuid == other.uuid - } -} - -impl TryFrom for Waifu { - type Error = Error; - - fn try_from(d: Domain) -> Result { - let c = d.get_connect()?; - - // This... feels wrong - // - // I know it probably works - // - // Based on code by Cadey in waifud - let addr: Option = if d.is_active()? { - let mut addr: Vec = d - .interface_addresses(virt::domain::VIR_DOMAIN_INTERFACE_ADDRESSES_SRC_LEASE, 0)? - .into_iter() - .map(|iface| iface.addrs.clone()) - .filter(|addrs| addrs.get(0).is_some()) - .map(|addrs| addrs.get(0).unwrap().clone().addr) - .collect(); - - if addr.get(0).is_none() { - Some(String::from("localhost")) - } else { - Some(addr.swap_remove(0)) - } - } else { - None - }; - - Ok(Self { - name: d.get_name()?, - host: c.get_hostname()?, - addr, - uuid: d.get_uuid_string()?, - mem: d.get_max_memory()?.into(), - cpu_count: d.get_max_vcpus()?.into(), - }) - } -} - - -impl TryFrom<&Domain> for Waifu { - type Error = Error; - - fn try_from(d: &Domain) -> Result { - let c = d.get_connect()?; - - // This... feels wrong - // - // I know it probably works - // - // Based on code by Cadey in waifud - let addr: Option = if d.is_active()? { - let mut addr: Vec = d - .interface_addresses(virt::domain::VIR_DOMAIN_INTERFACE_ADDRESSES_SRC_LEASE, 0)? - .into_iter() - .map(|iface| iface.addrs.clone()) - .filter(|addrs| addrs.get(0).is_some()) - .map(|addrs| addrs.get(0).unwrap().clone().addr) - .collect(); - - if addr.get(0).is_none() { - Some(String::from("localhost")) - } else { - Some(addr.swap_remove(0)) - } - } else { - None - }; - - Ok(Self { - name: d.get_name()?, - host: c.get_hostname()?, - addr, - uuid: d.get_uuid_string()?, - mem: d.get_max_memory()?.into(), - cpu_count: d.get_max_vcpus()?.into(), - }) - } -} -- cgit v1.2.3