aboutsummaryrefslogtreecommitdiff
path: root/src/house.rs
diff options
context:
space:
mode:
authorCara Salter <cara@devcara.com>2022-04-20 13:08:28 -0400
committerCara Salter <cara@devcara.com>2022-04-20 13:08:28 -0400
commit6b995785c780dd47cb0e02821001f446cf4ec211 (patch)
tree2bbe9147151056527468c1dcf70b6b98140ccb40 /src/house.rs
parent5978befd317189f1f18dddbab3db7ddd0061c236 (diff)
downloadsolarlib-6b995785c780dd47cb0e02821001f446cf4ec211.tar.gz
solarlib-6b995785c780dd47cb0e02821001f446cf4ec211.zip
house: Allow for the introduction of new VMs
Templates the XML and creates the disk images
Diffstat (limited to 'src/house.rs')
-rw-r--r--src/house.rs115
1 files changed, 111 insertions, 4 deletions
diff --git a/src/house.rs b/src/house.rs
index 06dd62f..ad6e055 100644
--- a/src/house.rs
+++ b/src/house.rs
@@ -5,7 +5,12 @@
use crate::waifu::*;
use crate::errors::Error;
use serde::{Serialize, Deserialize};
-use virt::connect::Connect;
+use virt::{connect::Connect, domain::Domain};
+use std::process::{ExitStatus};
+use std::os::unix::process::ExitStatusExt;
+use rand::Rng;
+
+use tokio::{process::Command, task::spawn_blocking};
#[derive(Serialize, Deserialize, Debug)]
pub enum Address {
@@ -36,6 +41,13 @@ pub struct House {
}
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<Self, Error> {
let mut c = Connect::open(&url.clone())?;
@@ -47,6 +59,14 @@ impl House {
})
}
+ /// 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<Vec<Waifu>, Error> {
match &self.con {
Some(c) => {
@@ -65,10 +85,97 @@ impl House {
}
}
- /// TODO: Implement and figure out what the hell I need to define one of these
- pub fn introduce(&mut self, name: String, max_mem: Memory, max_cpus: CpuCount) -> Result<&Waifu, Error> {
- unimplemented!();
+ /// 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, image_name: String) -> Result<Waifu, Error> {
+ // Check for image on host machine
+
+ let mut output = Command::new("ssh")
+ .args([
+ "-oStrictHostKeyChecking=accept-new",
+ &self.address.clone(),
+ "stat",
+ &format!("/var/lib/libvirt/images/{}", image_name.clone())
+ ])
+ .output()
+ .await?;
+
+ if output.status != ExitStatus::from_raw(0) {
+ return Err(Error::MissingImage(image_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()));
+ }
+
+ let uuid = uuid::Uuid::new_v4();
+
+ // Let's get that XML ready
+ let mut buf: Vec<u8> = vec![];
+
+ crate::templates::vm_xml(
+ &mut buf,
+ name.clone(),
+ uuid.to_string(),
+ random_mac().clone(),
+ true,
+ max_mem.count(),
+ max_cpus.count(),
+ "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)]