summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorLars Wirzenius <liw@liw.fi>2021-03-13 18:49:46 +0200
committerLars Wirzenius <liw@liw.fi>2021-03-13 19:01:37 +0200
commitf5b5030e588c1b363a02bf8ce737741ee13c409c (patch)
tree4c75f2ec3cc182de5932a4fe9b0e555c952b4796
parent8501490cefc1d1561bade98d1afdb4fb4b7edc36 (diff)
downloadvmadm-f5b5030e588c1b363a02bf8ce737741ee13c409c.tar.gz
refactor: add a light abstraction over libvirt bindings
These do only what vmadm needs, no more.
-rw-r--r--src/cmd/delete.rs55
-rw-r--r--src/cmd/list.rs43
-rw-r--r--src/lib.rsbin650 -> 667 bytes
-rw-r--r--src/libvirt.rs111
4 files changed, 123 insertions, 86 deletions
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
--- a/src/lib.rs
+++ b/src/lib.rs
Binary files 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<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(())
+}