diff options
author | Lars Wirzenius <liw@liw.fi> | 2021-03-01 10:13:15 +0200 |
---|---|---|
committer | Lars Wirzenius <liw@liw.fi> | 2021-03-01 12:28:31 +0200 |
commit | a4e59f7f6363c2bc62b91298f41b5b645842385e (patch) | |
tree | 46659f0fa296fe2128d1a7bcb929008d034f51c1 /src/cmd | |
parent | 05dbbc46d7b3fad9e7a1b85e10469022e07e0b40 (diff) | |
download | vmadm-a4e59f7f6363c2bc62b91298f41b5b645842385e.tar.gz |
feat: change how command line interface works
Easier to use now. --config before subcommand was annoying.
Diffstat (limited to 'src/cmd')
-rw-r--r-- | src/cmd/cloud_init.rs | 28 | ||||
-rw-r--r-- | src/cmd/delete.rs | 43 | ||||
-rw-r--r-- | src/cmd/list.rs | 31 | ||||
-rw-r--r-- | src/cmd/mod.rs | 11 | ||||
-rw-r--r-- | src/cmd/new.rs | 47 |
5 files changed, 160 insertions, 0 deletions
diff --git a/src/cmd/cloud_init.rs b/src/cmd/cloud_init.rs new file mode 100644 index 0000000..3ab4139 --- /dev/null +++ b/src/cmd/cloud_init.rs @@ -0,0 +1,28 @@ +use crate::cloudinit::CloudInitConfig; +use crate::spec::Specification; +use log::{debug, info}; +use std::path::Path; + +pub fn cloud_init(spec: &Specification, dirname: &Path) -> anyhow::Result<()> { + info!( + "generating cloud-init configuration into {}", + dirname.display() + ); + + let init = CloudInitConfig::from(&spec)?; + + debug!("creating directory {}", dirname.display()); + std::fs::create_dir_all(dirname)?; + init.create_dir(dirname)?; + + Ok(()) +} + +pub fn cloud_init_iso(spec: &Specification, iso: &Path) -> anyhow::Result<()> { + info!("generating cloud-init ISO into {}", iso.display()); + + let init = CloudInitConfig::from(&spec)?; + init.create_iso(iso)?; + + Ok(()) +} diff --git a/src/cmd/delete.rs b/src/cmd/delete.rs new file mode 100644 index 0000000..ee8f0e9 --- /dev/null +++ b/src/cmd/delete.rs @@ -0,0 +1,43 @@ +use crate::spec::Specification; +use log::{debug, info}; +use std::thread; +use std::time::Duration; +use virt::connect::Connect; + +pub fn delete(spec: &Specification) -> anyhow::Result<()> { + info!("deleting virtual machine {}", spec.name); + + debug!("connecting to libvirtd"); + let conn = Connect::open("qemu:///system")?; + + debug!("listing all domains"); + let domains = conn.list_all_domains(0)?; + + for domain in domains { + debug!("considering {}", domain.get_name()?); + if domain.get_name()? == spec.name { + debug!("shutdown {}", spec.name); + domain.shutdown().ok(); + + let briefly = Duration::from_millis(1000); + loop { + thread::sleep(briefly); + match domain.is_active() { + Ok(true) => (), + Ok(false) => break, + Err(err) => { + debug!("is_active: {}", err); + } + } + debug!("{} is still running", spec.name); + } + + debug!("undefine {}", spec.name); + domain.undefine()?; + + debug!("removing image file {}", spec.image.display()); + std::fs::remove_file(&spec.image)?; + } + } + Ok(()) +} diff --git a/src/cmd/list.rs b/src/cmd/list.rs new file mode 100644 index 0000000..e58db3d --- /dev/null +++ b/src/cmd/list.rs @@ -0,0 +1,31 @@ +use crate::config::Configuration; + +use virt::connect::Connect; + +pub fn list(_config: &Configuration) -> anyhow::Result<()> { + let conn = Connect::open("qemu:///system")?; + let domains = conn.list_all_domains(0)?; + for domain in domains { + let name = domain.get_name()?; + let (state, _) = domain.get_state()?; + let state = state_name(state); + println!("{} {}", name, state); + } + + Ok(()) +} + +fn state_name(state: virt::domain::DomainState) -> String { + let name = match state { + virt::domain::VIR_DOMAIN_NOSTATE => "none", + virt::domain::VIR_DOMAIN_RUNNING => "running", + virt::domain::VIR_DOMAIN_BLOCKED => "blocked", + virt::domain::VIR_DOMAIN_PAUSED => "paused", + virt::domain::VIR_DOMAIN_SHUTDOWN => "shutdown", + virt::domain::VIR_DOMAIN_SHUTOFF => "shutoff", + virt::domain::VIR_DOMAIN_CRASHED => "crashed", + virt::domain::VIR_DOMAIN_PMSUSPENDED => "power management suspended", + _ => "unknown", + }; + name.to_string() +} diff --git a/src/cmd/mod.rs b/src/cmd/mod.rs new file mode 100644 index 0000000..ad32e49 --- /dev/null +++ b/src/cmd/mod.rs @@ -0,0 +1,11 @@ +pub mod new; +pub use new::new; + +pub mod list; +pub use list::list; + +pub mod delete; +pub use delete::delete; + +pub mod cloud_init; +pub use cloud_init::{cloud_init, cloud_init_iso}; diff --git a/src/cmd/new.rs b/src/cmd/new.rs new file mode 100644 index 0000000..9930379 --- /dev/null +++ b/src/cmd/new.rs @@ -0,0 +1,47 @@ +use crate::cloudinit::CloudInitConfig; +use crate::image::VirtualMachineImage; +use crate::install::{virt_install, VirtInstallArgs}; +use crate::spec::Specification; + +use bytesize::GIB; +use log::info; +use std::net::TcpStream; + +const SSH_PORT: i32 = 22; + +pub fn new(spec: &Specification) -> anyhow::Result<()> { + info!("creating new VM {}", spec.name); + + info!("creating cloud-init config"); + let init = CloudInitConfig::from(&spec)?; + + info!( + "creating VM image {} from {}", + spec.image.display(), + spec.base.display() + ); + let image = VirtualMachineImage::new_from_base(&spec.base, &spec.image)?; + + info!("resizing image to {} GiB", spec.image_size_gib); + image.resize(spec.image_size_gib * GIB)?; + + info!("creating VM"); + let mut args = VirtInstallArgs::new(&spec.name, &image, &init); + args.set_memory(spec.memory_mib); + args.set_vcpus(spec.cpus); + virt_install(&args)?; + + info!("waiting for {} to open its SSH port", spec.name); + wait_for_port(&spec.name, SSH_PORT)?; + + Ok(()) +} + +fn wait_for_port(name: &str, port: i32) -> anyhow::Result<()> { + let addr = format!("{}:{}", name, port); + loop { + if TcpStream::connect(&addr).is_ok() { + return Ok(()); + } + } +} |