summaryrefslogtreecommitdiff
path: root/src/libvirt.rs
diff options
context:
space:
mode:
Diffstat (limited to 'src/libvirt.rs')
-rw-r--r--src/libvirt.rs111
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(())
+}