135 lines
4.5 KiB
Rust
135 lines
4.5 KiB
Rust
use std::sync::Arc;
|
|
|
|
use crate::{
|
|
error::{DomainError, VirtError},
|
|
xml, Connection,
|
|
};
|
|
|
|
pub struct Domain {
|
|
inner: xml::Domain,
|
|
virt: Arc<virt::domain::Domain>,
|
|
persist: bool,
|
|
}
|
|
|
|
impl Domain {
|
|
pub(crate) async fn define(conn: &Connection, xml: xml::Domain) -> Result<Self, DomainError> {
|
|
let conn = conn.virtconn.clone();
|
|
tokio::task::spawn_blocking(move || {
|
|
let virt_domain = {
|
|
let inst_xml = quick_xml::se::to_string(&xml).map_err(DomainError::XmlError)?;
|
|
virt::domain::Domain::define_xml(&conn, &inst_xml)
|
|
.map_err(DomainError::VirtError)?
|
|
};
|
|
|
|
let built_xml = match virt_domain.get_xml_desc(0) {
|
|
Ok(xml) => {
|
|
quick_xml::de::from_str::<xml::Domain>(&xml).map_err(DomainError::XmlError)
|
|
}
|
|
Err(err) => {
|
|
if let Err(err) = virt_domain.undefine() {
|
|
tracing::warn!("Couldn't undefine domain after failure: {err}");
|
|
}
|
|
Err(DomainError::VirtError(err))
|
|
}
|
|
}?;
|
|
|
|
Ok(Self {
|
|
inner: built_xml,
|
|
virt: Arc::new(virt_domain),
|
|
persist: false,
|
|
})
|
|
})
|
|
.await
|
|
.unwrap()
|
|
}
|
|
|
|
#[inline]
|
|
// Convenience function so I can stop doing exactly this so much
|
|
async fn spawn_virt<F, R>(&self, f: F) -> R
|
|
where
|
|
F: FnOnce(Arc<virt::domain::Domain>) -> R + Send + 'static,
|
|
R: Send + 'static,
|
|
{
|
|
let virt = self.virt.clone();
|
|
tokio::task::spawn_blocking(move || f(virt)).await.unwrap()
|
|
}
|
|
|
|
pub(crate) async fn get(conn: &Connection, name: impl AsRef<str>) -> Result<Self, DomainError> {
|
|
let name = name.as_ref().to_owned();
|
|
let virtconn = conn.virtconn.clone();
|
|
|
|
// Run libvirt calls in a blocking thread
|
|
tokio::task::spawn_blocking(move || {
|
|
let dom = match virt::domain::Domain::lookup_by_name(&virtconn, &name) {
|
|
Ok(inst) => Ok(inst),
|
|
Err(err) if err.code() == virt::error::ErrorNumber::NoDomain => {
|
|
Err(DomainError::DomainNotFound)
|
|
}
|
|
Err(err) => Err(DomainError::VirtError(err)),
|
|
}?;
|
|
let domain_xml: xml::Domain = {
|
|
let xml_str = dom.get_xml_desc(0).map_err(DomainError::VirtError)?;
|
|
quick_xml::de::from_str(&xml_str).map_err(DomainError::XmlError)?
|
|
};
|
|
|
|
Ok(Self {
|
|
inner: domain_xml,
|
|
virt: Arc::new(dom),
|
|
persist: true,
|
|
})
|
|
})
|
|
.await
|
|
.unwrap()
|
|
}
|
|
|
|
/// Stops the libvirt domain forcefully.
|
|
///
|
|
/// In libvirt terminology, this is equivalent to `virsh destroy <vm>`.
|
|
pub async fn stop(&mut self) -> Result<(), VirtError> {
|
|
self.spawn_virt(|virt| virt.destroy()).await
|
|
}
|
|
|
|
/// Undefines the libvirt domain.
|
|
/// If `deep` is set to true, all connected volumes are deleted.
|
|
pub async fn undefine(&mut self, deep: bool) -> Result<(), VirtError> {
|
|
if deep {
|
|
let conn: Connection = self.virt.get_connect()?.into();
|
|
for disk in self.inner.devices.disks() {
|
|
if let (Some(pool), Some(vol)) = (&disk.source.pool, &disk.source.volume) {
|
|
if let Ok(pool) = conn.get_pool(pool).await {
|
|
if let Ok(vol) = pool.volume(vol).await {
|
|
vol.delete().await?;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
self.spawn_virt(|virt| virt.undefine()).await
|
|
}
|
|
|
|
/// Gets a reference to the inner libvirt XML.
|
|
pub async fn xml(&self) -> &xml::Domain {
|
|
&self.inner
|
|
}
|
|
|
|
pub async fn persist(&mut self) {
|
|
self.persist = true;
|
|
}
|
|
|
|
/// Sets whether the domain is autostarted. The return value, if successful,
|
|
/// represents the previous state.
|
|
pub async fn autostart(&mut self, doit: bool) -> Result<bool, VirtError> {
|
|
self.spawn_virt(move |virt| virt.set_autostart(doit)).await
|
|
}
|
|
|
|
/// Starts the domain.
|
|
pub async fn start(&self) -> Result<(), VirtError> {
|
|
self.spawn_virt(|virt| virt.create()).await?;
|
|
Ok(())
|
|
}
|
|
|
|
/// Gets the current domain state.
|
|
pub async fn state(&self) -> Result<u32, VirtError> {
|
|
self.spawn_virt(|virt| virt.get_state().map(|s| s.0)).await
|
|
}
|
|
}
|