summaryrefslogtreecommitdiff
path: root/src/cmd
diff options
context:
space:
mode:
authorLars Wirzenius <liw@liw.fi>2021-03-01 10:13:15 +0200
committerLars Wirzenius <liw@liw.fi>2021-03-01 12:28:31 +0200
commita4e59f7f6363c2bc62b91298f41b5b645842385e (patch)
tree46659f0fa296fe2128d1a7bcb929008d034f51c1 /src/cmd
parent05dbbc46d7b3fad9e7a1b85e10469022e07e0b40 (diff)
downloadvmadm-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.rs28
-rw-r--r--src/cmd/delete.rs43
-rw-r--r--src/cmd/list.rs31
-rw-r--r--src/cmd/mod.rs11
-rw-r--r--src/cmd/new.rs47
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(());
+ }
+ }
+}