diff options
author | Lars Wirzenius <liw@liw.fi> | 2021-08-06 09:41:34 +0300 |
---|---|---|
committer | Lars Wirzenius <liw@liw.fi> | 2021-08-06 10:21:58 +0300 |
commit | a54cbbb7f3d3750f4c3fea3ba999dac978ec394a (patch) | |
tree | ab941b542ff104664af5feb174e97eb271bbfc54 /src | |
parent | 6fc3ad8a6758bb6c764ba208d814c1ab82d764ac (diff) | |
download | vmadm-a54cbbb7f3d3750f4c3fea3ba999dac978ec394a.tar.gz |
feat: add progress reporting
Sponsored-by: author
Diffstat (limited to 'src')
-rw-r--r-- | src/bin/vmadm.rs | 41 | ||||
-rw-r--r-- | src/cmd/cloud_init.rs | 14 | ||||
-rw-r--r-- | src/cmd/config.rs | 5 | ||||
-rw-r--r-- | src/cmd/delete.rs | 12 | ||||
-rw-r--r-- | src/cmd/list.rs | 4 | ||||
-rw-r--r-- | src/cmd/new.rs | 21 | ||||
-rw-r--r-- | src/cmd/shutdown.rs | 12 | ||||
-rw-r--r-- | src/cmd/spec.rs | 5 | ||||
-rw-r--r-- | src/cmd/start.rs | 10 | ||||
-rw-r--r-- | src/lib.rs | bin | 681 -> 699 bytes | |||
-rw-r--r-- | src/progress.rs | 40 |
11 files changed, 128 insertions, 36 deletions
diff --git a/src/bin/vmadm.rs b/src/bin/vmadm.rs index a288036..5e27761 100644 --- a/src/bin/vmadm.rs +++ b/src/bin/vmadm.rs @@ -5,6 +5,7 @@ use std::path::{Path, PathBuf}; use structopt::StructOpt; use vmadm::cmd; use vmadm::config::Configuration; +use vmadm::progress::{MessageKind, Progress}; use vmadm::spec::Specification; const QUALIFIER: &str = ""; @@ -85,6 +86,12 @@ enum Command { struct CommonOptions { #[structopt(short, long, parse(from_os_str))] config: Option<PathBuf>, + + #[structopt(short, long)] + quiet: bool, + + #[structopt(short, long)] + verbose: bool, } fn main() -> anyhow::Result<()> { @@ -94,38 +101,45 @@ fn main() -> anyhow::Result<()> { match cli.cmd { Command::New { common, spec } => { + let progress = get_progress(&common); let specs = get_specs(&common, &spec)?; - cmd::new(&specs)?; + cmd::new(&specs, &progress)?; } Command::Config { common } => { + let progress = get_progress(&common); let config = config(&common)?; - cmd::config(&config)?; + cmd::config(&config, &progress)?; } Command::Spec { common, spec } => { + let progress = get_progress(&common); let specs = get_specs(&common, &spec)?; - cmd::spec(&specs)?; + cmd::spec(&specs, &progress)?; } Command::List { common } => { + let progress = get_progress(&common); let config = config(&common)?; - cmd::list(&config)?; + cmd::list(&config, &progress)?; } Command::Delete { common, spec } => { + let progress = get_progress(&common); let specs = get_specs(&common, &spec)?; - cmd::delete(&specs)?; + cmd::delete(&specs, &progress)?; } Command::Start { common, spec } => { + let progress = get_progress(&common); let specs = get_specs(&common, &spec)?; - cmd::start(&specs)?; + cmd::start(&specs, &progress)?; } Command::Shutdown { common, spec } => { + let progress = get_progress(&common); let specs = get_specs(&common, &spec)?; - cmd::shutdown(&specs)?; + cmd::shutdown(&specs, &progress)?; } Command::CloudInit { @@ -133,13 +147,24 @@ fn main() -> anyhow::Result<()> { spec, dirname, } => { + let progress = get_progress(&common); let specs = get_specs(&common, &spec)?; - cmd::cloud_init(&specs, &dirname)?; + cmd::cloud_init(&specs, &progress, &dirname)?; } } Ok(()) } +fn get_progress(common: &CommonOptions) -> Progress { + if common.quiet { + Progress::new(MessageKind::OnlyErrors) + } else if common.verbose { + Progress::new(MessageKind::Everything) + } else { + Progress::new(MessageKind::Steps) + } +} + fn get_specs(common: &CommonOptions, spec: &Path) -> anyhow::Result<Vec<Specification>> { let config = config(common)?; let specs = Specification::from_file(&config, spec)?; diff --git a/src/cmd/cloud_init.rs b/src/cmd/cloud_init.rs index dc9af66..cfa08f9 100644 --- a/src/cmd/cloud_init.rs +++ b/src/cmd/cloud_init.rs @@ -1,8 +1,8 @@ //! The `cloud-init` sub-command. use crate::cloudinit::{CloudInitConfig, CloudInitError}; +use crate::progress::Progress; use crate::spec::Specification; -use log::{debug, info}; use std::path::Path; /// Errors from this module. @@ -22,18 +22,22 @@ pub enum CloudInitCommandError { /// This sub-command generates the cloud-init configuration based on /// specifications provided by the caller and writes them to files in /// a directory named by the caller. -pub fn cloud_init(specs: &[Specification], dirname: &Path) -> Result<(), CloudInitCommandError> { +pub fn cloud_init( + specs: &[Specification], + progress: &Progress, + dirname: &Path, +) -> Result<(), CloudInitCommandError> { for spec in specs { let dirname = dirname.join(&spec.name); - info!( + progress.chatty(&format!( "generating cloud-init configuration for {} into {}", spec.name, dirname.display() - ); + )); let init = CloudInitConfig::from(spec)?; - debug!("creating directory {}", dirname.display()); + progress.chatty(&format!("creating directory {}", dirname.display())); std::fs::create_dir_all(&dirname)?; init.create_dir(&dirname)?; } diff --git a/src/cmd/config.rs b/src/cmd/config.rs index 0a78a64..e45f7c0 100644 --- a/src/cmd/config.rs +++ b/src/cmd/config.rs @@ -3,13 +3,16 @@ use std::io::Write; use crate::config::Configuration; +use crate::progress::Progress; /// The `config` sub-command. /// /// Write the actual run-time configuration to stdout as JSON. We /// convert the config to JSON to make it clear we parse it the right /// way. -pub fn config(config: &Configuration) -> Result<(), std::io::Error> { +pub fn config(config: &Configuration, progress: &Progress) -> Result<(), std::io::Error> { + progress.chatty("showing runtime configuration"); + let config = serde_json::to_vec(&config).unwrap(); std::io::stdout().write_all(&config)?; Ok(()) diff --git a/src/cmd/delete.rs b/src/cmd/delete.rs index fbb6ab5..620ec98 100644 --- a/src/cmd/delete.rs +++ b/src/cmd/delete.rs @@ -1,22 +1,26 @@ //! The `delete` sub-command. use crate::libvirt::{Libvirt, VirtError}; +use crate::progress::Progress; use crate::spec::Specification; -use log::info; /// Delete VMs corresponding to specifications. /// /// Delete the VM corresponding to each specification provided by the caller. -pub fn delete(specs: &[Specification]) -> Result<(), VirtError> { +pub fn delete(specs: &[Specification], progress: &Progress) -> Result<(), VirtError> { + progress.chatty("deleting virtual machines"); + let libvirt = Libvirt::connect("qemu:///system")?; for spec in specs { - info!("asking virtual machine {} to shutdown", spec.name); + progress.chatty(&format!("asking virtual machine {} to shutdown", spec.name)); libvirt.trigger_shutdown(&spec.name)?; } for spec in specs { - info!("deleting virtual machine {}", spec.name); + progress.step(&format!("deleting virtual machine {}", spec.name)); libvirt.shutdown(&spec.name)?; libvirt.delete(&spec.name, &spec.image)?; } + + progress.chatty("deletion successful"); Ok(()) } diff --git a/src/cmd/list.rs b/src/cmd/list.rs index 40a1910..1148fd9 100644 --- a/src/cmd/list.rs +++ b/src/cmd/list.rs @@ -2,12 +2,14 @@ use crate::config::Configuration; use crate::libvirt::{Libvirt, VirtError}; +use crate::progress::Progress; /// The `list` sub-command. /// /// Return the names of all the virtual machines existing on the /// libvirt instance. -pub fn list(_config: &Configuration) -> Result<(), VirtError> { +pub fn list(_config: &Configuration, progress: &Progress) -> Result<(), VirtError> { + progress.chatty("listing virtual machines"); let libvirt = Libvirt::connect("qemu:///system")?; for name in libvirt.names()? { println!("{}", name); diff --git a/src/cmd/new.rs b/src/cmd/new.rs index 197b3dc..8583ae9 100644 --- a/src/cmd/new.rs +++ b/src/cmd/new.rs @@ -4,11 +4,11 @@ use crate::cloudinit::{CloudInitConfig, CloudInitError}; use crate::image::{ImageError, VirtualMachineImage}; use crate::install::{virt_install, VirtInstallArgs, VirtInstallError}; use crate::libvirt::{Libvirt, VirtError}; +use crate::progress::Progress; use crate::spec::Specification; use crate::util::wait_for_ssh; use bytesize::GIB; -use log::info; use tempfile::tempdir; /// Errors returned by this module. @@ -39,25 +39,27 @@ pub enum NewError { /// /// Create all the new virtual machines specified by the caller. Wait /// until each VM's SSH port listens for connections. -pub fn new(specs: &[Specification]) -> Result<(), NewError> { +pub fn new(specs: &[Specification], progress: &Progress) -> Result<(), NewError> { + progress.chatty("Creating new virtual machines"); + let libvirt = Libvirt::connect("qemu:///system")?; for spec in specs { - info!("creating new VM {}", spec.name); + progress.step(&format!("creating new VM {}", spec.name)); - info!("creating cloud-init config"); + progress.chatty("creating cloud-init config"); let init = CloudInitConfig::from(spec)?; - info!( + progress.chatty(&format!( "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); + progress.chatty(&format!("resizing image to {} GiB", spec.image_size_gib)); image.resize(spec.image_size_gib * GIB)?; - info!("creating VM"); + progress.chatty("creating VM"); let dir = tempdir()?; let iso = dir.path().join("cloudinit.iso"); let mut args = VirtInstallArgs::new(&spec.name, &image, &init); @@ -70,12 +72,13 @@ pub fn new(specs: &[Specification]) -> Result<(), NewError> { } for spec in specs { - info!("waiting for {} to open its SSH port", spec.name); + progress.chatty(&format!("waiting for {} to open its SSH port", spec.name)); wait_for_ssh(&spec.name); libvirt.detach_cloud_init_iso(&spec.name)?; libvirt.set_autostart(&spec.name, spec.autostart)?; } + progress.chatty("creation successful"); Ok(()) } diff --git a/src/cmd/shutdown.rs b/src/cmd/shutdown.rs index cdd3069..1226b5c 100644 --- a/src/cmd/shutdown.rs +++ b/src/cmd/shutdown.rs @@ -1,19 +1,23 @@ //! The `shutdown` sub-command. use crate::libvirt::{Libvirt, VirtError}; +use crate::progress::Progress; use crate::spec::Specification; -use log::{debug, info}; /// Shut down VMs corresponding to specifications. -pub fn shutdown(specs: &[Specification]) -> Result<(), VirtError> { +pub fn shutdown(specs: &[Specification], progress: &Progress) -> Result<(), VirtError> { + progress.chatty("shutting down virtual machines"); + let libvirt = Libvirt::connect("qemu:///system")?; for spec in specs { - info!("shutting down virtual machine {}", spec.name); + progress.step(&format!("shutting down virtual machine {}", spec.name)); libvirt.trigger_shutdown(&spec.name)?; } for spec in specs { - debug!("waiting for {} to become inactive", spec.name); + progress.chatty(&format!("waiting for {} to become inactive", spec.name)); libvirt.wait_for_inactive(&spec.name)?; } + + progress.chatty("shut down OK"); Ok(()) } diff --git a/src/cmd/spec.rs b/src/cmd/spec.rs index 7922cb5..4f1a764 100644 --- a/src/cmd/spec.rs +++ b/src/cmd/spec.rs @@ -2,6 +2,7 @@ use std::io::Write; +use crate::progress::Progress; use crate::spec::Specification; /// The `spec` sub-command. @@ -9,7 +10,9 @@ use crate::spec::Specification; /// Write the actual VM specifications to stdout as JSON. We convert /// the spec to JSON to make it more clear that we parse the config /// and spec in the right way. -pub fn spec(specs: &[Specification]) -> Result<(), std::io::Error> { +pub fn spec(specs: &[Specification], progress: &Progress) -> Result<(), std::io::Error> { + progress.chatty("showing specification for virtual machines"); + let spec = serde_json::to_vec(specs).unwrap(); std::io::stdout().write_all(&spec)?; Ok(()) diff --git a/src/cmd/start.rs b/src/cmd/start.rs index 8e74c49..8590d80 100644 --- a/src/cmd/start.rs +++ b/src/cmd/start.rs @@ -1,9 +1,9 @@ //! The `start` sub-command. use crate::libvirt::{Libvirt, VirtError}; +use crate::progress::Progress; use crate::spec::Specification; use crate::util::wait_for_ssh; -use log::info; /// Errors from this module. #[derive(Debug, thiserror::Error)] @@ -18,14 +18,18 @@ pub enum StartError { } /// Start existing VMs corresponding to specifications. -pub fn start(specs: &[Specification]) -> Result<(), StartError> { +pub fn start(specs: &[Specification], progress: &Progress) -> Result<(), StartError> { + progress.chatty("starting virtual machines"); + let libvirt = Libvirt::connect("qemu:///system")?; for spec in specs { - info!("starting virtual machine {}", spec.name); + progress.step(&format!("starting virtual machine {}", spec.name)); libvirt.trigger_start(&spec.name)?; } for spec in specs { wait_for_ssh(&spec.name); } + + progress.chatty("started OK"); Ok(()) } Binary files differdiff --git a/src/progress.rs b/src/progress.rs new file mode 100644 index 0000000..52ade35 --- /dev/null +++ b/src/progress.rs @@ -0,0 +1,40 @@ +//! Show progress, or not. + +#[derive(Debug, Clone, Copy)] +pub enum MessageKind { + OnlyErrors, + Steps, + Everything, +} + +pub struct Progress { + level: MessageKind, +} + +impl Progress { + pub fn new(level: MessageKind) -> Self { + Self { level } + } + + fn message(&self, prefix: &str, msg: &str) { + eprintln!("{}: {}", prefix, msg); + } + + pub fn chatty(&self, msg: &str) { + if let MessageKind::Everything = self.level { + self.message("DEBUG", msg); + } + } + + pub fn step(&self, msg: &str) { + match self.level { + MessageKind::Everything | MessageKind::Steps => self.message("INFO", msg), + _ => (), + } + } + + pub fn error(&self, msg: &str) { + // Errors are always written out. + self.message("ERROR", msg); + } +} |