summaryrefslogtreecommitdiff
path: root/src
diff options
context:
space:
mode:
authorLars Wirzenius <liw@liw.fi>2021-08-06 09:41:34 +0300
committerLars Wirzenius <liw@liw.fi>2021-08-06 10:21:58 +0300
commita54cbbb7f3d3750f4c3fea3ba999dac978ec394a (patch)
treeab941b542ff104664af5feb174e97eb271bbfc54 /src
parent6fc3ad8a6758bb6c764ba208d814c1ab82d764ac (diff)
downloadvmadm-a54cbbb7f3d3750f4c3fea3ba999dac978ec394a.tar.gz
feat: add progress reporting
Sponsored-by: author
Diffstat (limited to 'src')
-rw-r--r--src/bin/vmadm.rs41
-rw-r--r--src/cmd/cloud_init.rs14
-rw-r--r--src/cmd/config.rs5
-rw-r--r--src/cmd/delete.rs12
-rw-r--r--src/cmd/list.rs4
-rw-r--r--src/cmd/new.rs21
-rw-r--r--src/cmd/shutdown.rs12
-rw-r--r--src/cmd/spec.rs5
-rw-r--r--src/cmd/start.rs10
-rw-r--r--src/lib.rsbin681 -> 699 bytes
-rw-r--r--src/progress.rs40
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(())
}
diff --git a/src/lib.rs b/src/lib.rs
index 3d94461..f9a4ab8 100644
--- a/src/lib.rs
+++ b/src/lib.rs
Binary files differ
diff --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);
+ }
+}