From f5b5030e588c1b363a02bf8ce737741ee13c409c Mon Sep 17 00:00:00 2001 From: Lars Wirzenius Date: Sat, 13 Mar 2021 18:49:46 +0200 Subject: refactor: add a light abstraction over libvirt bindings These do only what vmadm needs, no more. --- src/cmd/delete.rs | 55 +++------------------------ src/cmd/list.rs | 43 ++++----------------- src/lib.rs | Bin 650 -> 667 bytes src/libvirt.rs | 111 ++++++++++++++++++++++++++++++++++++++++++++++++++++++ 4 files changed, 123 insertions(+), 86 deletions(-) create mode 100644 src/libvirt.rs diff --git a/src/cmd/delete.rs b/src/cmd/delete.rs index a9a0660..66eb23f 100644 --- a/src/cmd/delete.rs +++ b/src/cmd/delete.rs @@ -1,62 +1,17 @@ //! The `delete` sub-command. +use crate::libvirt::{Libvirt, VirtError}; use crate::spec::Specification; -use log::{debug, info}; -use std::thread; -use std::time::Duration; -use virt::connect::Connect; - -/// Errors from this module. -#[derive(Debug, thiserror::Error)] -pub enum DeleteError { - /// Error creating virtual machine. - #[error(transparent)] - VirtError(#[from] virt::error::Error), - - /// Error doing I/O. - #[error(transparent)] - IoError(#[from] std::io::Error), -} +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<(), DeleteError> { +pub fn delete(specs: &[Specification]) -> Result<(), VirtError> { + let libvirt = Libvirt::connect("qemu:///system")?; for spec in specs { 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)?; - } - } + libvirt.delete(&spec.name, &spec.image)?; } Ok(()) } diff --git a/src/cmd/list.rs b/src/cmd/list.rs index c83be26..40a1910 100644 --- a/src/cmd/list.rs +++ b/src/cmd/list.rs @@ -1,45 +1,16 @@ //! The `list` sub-command. use crate::config::Configuration; - -use virt::connect::Connect; - -/// Errors returned from this module. -#[derive(Debug, thiserror::Error)] -pub enum ListError { - /// An error from libvirt. - #[error(transparent)] - VirtError(#[from] virt::error::Error), -} +use crate::libvirt::{Libvirt, VirtError}; /// The `list` sub-command. /// -/// Return all the virtual machines existing on the libvirt instance, -/// and their current state. -pub fn list(_config: &Configuration) -> Result<(), ListError> { - 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); +/// Return the names of all the virtual machines existing on the +/// libvirt instance. +pub fn list(_config: &Configuration) -> Result<(), VirtError> { + let libvirt = Libvirt::connect("qemu:///system")?; + for name in libvirt.names()? { + println!("{}", name); } - 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/lib.rs b/src/lib.rs index e571e80..f734bc8 100644 Binary files a/src/lib.rs and b/src/lib.rs differ diff --git a/src/libvirt.rs b/src/libvirt.rs new file mode 100644 index 0000000..0dbc4e3 --- /dev/null +++ b/src/libvirt.rs @@ -0,0 +1,111 @@ +//! An abstraction on top of the libvirt bindings. + +use log::debug; +use std::path::Path; +use std::thread; +use std::time::Duration; +use virt::connect::Connect; +use virt::domain::Domain; + +/// Errors from this module. +#[derive(Debug, thiserror::Error)] +pub enum VirtError { + /// Error creating virtual machine. + #[error(transparent)] + VirtError(#[from] virt::error::Error), + + /// Error doing I/O. + #[error(transparent)] + IoError(#[from] std::io::Error), +} + +/// Access libvirt for all the things this program needs. +pub struct Libvirt { + conn: Connect, +} + +impl Libvirt { + pub fn connect(url: &str) -> Result { + debug!("connecting to libvirtd {}", url); + let conn = Connect::open(url)?; + Ok(Self { conn }) + } + + fn get_domains(&self) -> Result, VirtError> { + debug!("listing all domains"); + Ok(self.conn.list_all_domains(0)?) + } + + fn get_domain(&self, name: &str) -> Result, VirtError> { + for domain in self.get_domains()? { + if domain.get_name()? == name { + return Ok(Some(domain)); + } + } + Ok(None) + } + + pub fn names(&self) -> Result, VirtError> { + let mut ret = vec![]; + for domain in self.get_domains()? { + ret.push(domain.get_name()?); + } + Ok(ret) + } + + pub fn is_active(&self, name: &str) -> Result { + if let Some(domain) = self.get_domain(name)? { + Ok(domain.is_active()?) + } else { + Ok(false) + } + } + + pub fn start(&self, name: &str) -> Result<(), VirtError> { + if let Some(domain) = self.get_domain(name)? { + domain.create()?; + } + Ok(()) + } + + pub fn shutdown(&self, name: &str) -> Result<(), VirtError> { + if let Some(domain) = self.get_domain(name)? { + domain.shutdown()?; + } + Ok(()) + } + + pub fn delete(&self, name: &str, image: &Path) -> Result<(), VirtError> { + if let Some(domain) = self.get_domain(name)? { + debug!("shutting down {}", name); + domain.shutdown()?; + + wait_until_inactive(&domain, name)?; + + debug!("undefine {}", name); + domain.undefine()?; + + debug!("removing image file {}", image.display()); + std::fs::remove_file(image)?; + } + + Ok(()) + } +} + +fn wait_until_inactive(domain: &Domain, name: &str) -> Result<(), VirtError> { + debug!("waiting for domain {} to become inactive", name); + 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!("domain {} is still running", name); + } + Ok(()) +} -- cgit v1.2.1