diff options
author | Lars Wirzenius <liw@liw.fi> | 2021-03-13 18:49:46 +0200 |
---|---|---|
committer | Lars Wirzenius <liw@liw.fi> | 2021-03-13 19:01:37 +0200 |
commit | f5b5030e588c1b363a02bf8ce737741ee13c409c (patch) | |
tree | 4c75f2ec3cc182de5932a4fe9b0e555c952b4796 /src/libvirt.rs | |
parent | 8501490cefc1d1561bade98d1afdb4fb4b7edc36 (diff) | |
download | vmadm-f5b5030e588c1b363a02bf8ce737741ee13c409c.tar.gz |
refactor: add a light abstraction over libvirt bindings
These do only what vmadm needs, no more.
Diffstat (limited to 'src/libvirt.rs')
-rw-r--r-- | src/libvirt.rs | 111 |
1 files changed, 111 insertions, 0 deletions
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<Self, VirtError> { + debug!("connecting to libvirtd {}", url); + let conn = Connect::open(url)?; + Ok(Self { conn }) + } + + fn get_domains(&self) -> Result<Vec<Domain>, VirtError> { + debug!("listing all domains"); + Ok(self.conn.list_all_domains(0)?) + } + + fn get_domain(&self, name: &str) -> Result<Option<Domain>, VirtError> { + for domain in self.get_domains()? { + if domain.get_name()? == name { + return Ok(Some(domain)); + } + } + Ok(None) + } + + pub fn names(&self) -> Result<Vec<String>, 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<bool, VirtError> { + 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(()) +} |