mirror of
https://codeberg.org/Toasterson/solstice-ci.git
synced 2026-04-10 13:20:41 +00:00
Switch orchestrator from libvirt crate to virt crate for Linux hypervisor backend
This commit replaces the `libvirt` crate with the `virt` crate for managing the libvirt backend on Linux. Key changes include: - Updated `Cargo.toml` dependencies and feature configuration. - Refactored hypervisor implementation to align with `virt` crate API. - Improved error handling and lifecycle management for VMs and networks.
This commit is contained in:
parent
6568183d86
commit
d05121b378
3 changed files with 42 additions and 40 deletions
|
|
@ -4,8 +4,8 @@ version = "0.1.0"
|
|||
edition = "2024"
|
||||
|
||||
[features]
|
||||
# Enable libvirt backend on Linux hosts
|
||||
libvirt = ["dep:libvirt"]
|
||||
# Enable libvirt backend on Linux hosts (uses virt crate on Linux)
|
||||
libvirt = []
|
||||
|
||||
[dependencies]
|
||||
common = { path = "../common" }
|
||||
|
|
@ -22,8 +22,6 @@ bytes = "1"
|
|||
path-absolutize = "3"
|
||||
# Compression/decompression
|
||||
zstd = "0.13"
|
||||
# Linux-only optional libvirt bindings (feature-gated)
|
||||
libvirt = { version = "0.1", optional = true }
|
||||
# DB (optional basic persistence)
|
||||
sea-orm = { version = "0.12", default-features = false, features = ["sqlx-postgres", "sqlx-sqlite", "runtime-tokio-rustls", "macros", "with-uuid", "with-chrono" ] }
|
||||
migration = { path = "../migration" }
|
||||
|
|
@ -34,3 +32,6 @@ once_cell = "1"
|
|||
dashmap = "6"
|
||||
async-trait = "0.1"
|
||||
uuid = { version = "1", features = ["v4", "serde"] }
|
||||
|
||||
[target.'cfg(target_os = "linux")'.dependencies]
|
||||
virt = { version = "0.3" }
|
||||
|
|
|
|||
|
|
@ -193,24 +193,24 @@ impl LibvirtHypervisor {
|
|||
#[async_trait]
|
||||
impl Hypervisor for LibvirtHypervisor {
|
||||
async fn prepare(&self, spec: &VmSpec, ctx: &JobContext) -> Result<VmHandle> {
|
||||
use libvirt::{Connect, Network, Domain};
|
||||
use std::process::Command;
|
||||
|
||||
let id = format!("job-{}", ctx.request_id);
|
||||
let work_dir = self.mk_work_dir(&id);
|
||||
|
||||
// Connect and ensure network is active
|
||||
// Ensure network is active via virt crate; best-effort
|
||||
let uri = self.uri.clone();
|
||||
let net_name = self.network.clone();
|
||||
tokio::task::spawn_blocking(move || -> miette::Result<()> {
|
||||
let conn = Connect::open(&uri).map_err(|e| miette::miette!("libvirt connect error: {e}"))?;
|
||||
let net: Network = conn.network_lookup_by_name(&net_name)
|
||||
.map_err(|e| miette::miette!("libvirt network '{}' not found: {e}", net_name))?;
|
||||
if !net.is_active().unwrap_or(false) {
|
||||
net.create().map_err(|e| miette::miette!("failed to activate network '{}': {e}", net_name))?;
|
||||
}
|
||||
if !net.get_autostart().unwrap_or(true) {
|
||||
net.set_autostart(true).ok();
|
||||
use virt::{connect::Connect, network::Network};
|
||||
let conn = Connect::open(Some(&uri)).map_err(|e| miette::miette!("libvirt connect failed: {e}"))?;
|
||||
if let Ok(net) = Network::lookup_by_name(&conn, &net_name) {
|
||||
// If not active, try to create (activate). Then set autostart.
|
||||
let active = net.is_active().unwrap_or(false);
|
||||
if !active {
|
||||
let _ = net.create();
|
||||
}
|
||||
let _ = net.set_autostart(true);
|
||||
}
|
||||
Ok(())
|
||||
}).await.into_diagnostic()??;
|
||||
|
|
@ -280,16 +280,14 @@ impl Hypervisor for LibvirtHypervisor {
|
|||
id, mem, vcpus, overlay_str, cdrom, net)
|
||||
};
|
||||
|
||||
// Define domain
|
||||
// Define via virt crate
|
||||
let uri2 = self.uri.clone();
|
||||
tokio::task::spawn_blocking({
|
||||
let xml = xml.clone();
|
||||
move || -> miette::Result<()> {
|
||||
let conn = Connect::open(&uri2).map_err(|e| miette::miette!("libvirt connect error: {e}"))?;
|
||||
let _dom: Domain = conn.domain_define_xml(&xml)
|
||||
.map_err(|e| miette::miette!("domain define failed: {e}"))?;
|
||||
Ok(())
|
||||
}
|
||||
let xml_clone = xml.clone();
|
||||
tokio::task::spawn_blocking(move || -> miette::Result<()> {
|
||||
use virt::{connect::Connect, domain::Domain};
|
||||
let conn = Connect::open(Some(&uri2)).map_err(|e| miette::miette!("libvirt connect failed: {e}"))?;
|
||||
let _dom = Domain::define_xml(&conn, &xml_clone).map_err(|e| miette::miette!("define domain failed: {e}"))?;
|
||||
Ok(())
|
||||
}).await.into_diagnostic()??;
|
||||
|
||||
info!(domain = %id, image = ?spec.image_path, cpu = spec.cpu, ram_mb = spec.ram_mb, "libvirt prepared");
|
||||
|
|
@ -297,13 +295,14 @@ impl Hypervisor for LibvirtHypervisor {
|
|||
}
|
||||
|
||||
async fn start(&self, vm: &VmHandle) -> Result<()> {
|
||||
use libvirt::Connect;
|
||||
let id = vm.id.clone();
|
||||
let uri = self.uri.clone();
|
||||
tokio::task::spawn_blocking(move || -> miette::Result<()> {
|
||||
let conn = Connect::open(&uri).map_err(|e| miette::miette!("libvirt connect error: {e}"))?;
|
||||
let dom = conn.domain_lookup_by_name(&id).map_err(|e| miette::miette!("lookup domain {}: {e}", id))?;
|
||||
dom.create().map_err(|e| miette::miette!("start domain {} failed: {e}", id))?;
|
||||
use virt::{connect::Connect, domain::Domain};
|
||||
let conn = Connect::open(Some(&uri)).map_err(|e| miette::miette!("libvirt connect failed: {e}"))?;
|
||||
// Lookup domain by name and start
|
||||
let dom = Domain::lookup_by_name(&conn, &id).map_err(|e| miette::miette!("lookup domain failed: {e}"))?;
|
||||
dom.create().map_err(|e| miette::miette!("domain start failed: {e}"))?;
|
||||
Ok(())
|
||||
}).await.into_diagnostic()??;
|
||||
info!(domain = %vm.id, "libvirt started");
|
||||
|
|
@ -311,22 +310,23 @@ impl Hypervisor for LibvirtHypervisor {
|
|||
}
|
||||
|
||||
async fn stop(&self, vm: &VmHandle, t: Duration) -> Result<()> {
|
||||
use libvirt::Connect;
|
||||
let id = vm.id.clone();
|
||||
let uri = self.uri.clone();
|
||||
tokio::task::spawn_blocking(move || -> miette::Result<()> {
|
||||
let conn = Connect::open(&uri).map_err(|e| miette::miette!("libvirt connect error: {e}"))?;
|
||||
let dom = conn.domain_lookup_by_name(&id).map_err(|e| miette::miette!("lookup domain {}: {e}", id))?;
|
||||
dom.shutdown().ok();
|
||||
// Poll for inactive up to timeout
|
||||
use virt::{connect::Connect, domain::Domain};
|
||||
let conn = Connect::open(Some(&uri)).map_err(|e| miette::miette!("libvirt connect failed: {e}"))?;
|
||||
let dom = Domain::lookup_by_name(&conn, &id).map_err(|e| miette::miette!("lookup domain failed: {e}"))?;
|
||||
let _ = dom.shutdown();
|
||||
let start = std::time::Instant::now();
|
||||
while start.elapsed() < t {
|
||||
if !dom.is_active().unwrap_or(true) { break; }
|
||||
match dom.is_active() {
|
||||
Ok(false) => break,
|
||||
_ => {}
|
||||
}
|
||||
std::thread::sleep(std::time::Duration::from_millis(500));
|
||||
}
|
||||
if dom.is_active().unwrap_or(false) {
|
||||
dom.destroy().ok();
|
||||
}
|
||||
// Force destroy if still active
|
||||
let _ = dom.destroy();
|
||||
Ok(())
|
||||
}).await.into_diagnostic()??;
|
||||
info!(domain = %vm.id, "libvirt stopped");
|
||||
|
|
@ -334,12 +334,13 @@ impl Hypervisor for LibvirtHypervisor {
|
|||
}
|
||||
|
||||
async fn destroy(&self, vm: VmHandle) -> Result<()> {
|
||||
use libvirt::Connect;
|
||||
let id = vm.id.clone();
|
||||
let uri = self.uri.clone();
|
||||
let id_for_task = id.clone();
|
||||
tokio::task::spawn_blocking(move || -> miette::Result<()> {
|
||||
let conn = Connect::open(&uri).map_err(|e| miette::miette!("libvirt connect error: {e}"))?;
|
||||
if let Ok(dom) = conn.domain_lookup_by_name(&id) {
|
||||
use virt::{connect::Connect, domain::Domain};
|
||||
let conn = Connect::open(Some(&uri)).map_err(|e| miette::miette!("libvirt connect failed: {e}"))?;
|
||||
if let Ok(dom) = Domain::lookup_by_name(&conn, &id_for_task) {
|
||||
let _ = dom.undefine();
|
||||
}
|
||||
Ok(())
|
||||
|
|
|
|||
|
|
@ -3,7 +3,7 @@ mod hypervisor;
|
|||
mod scheduler;
|
||||
mod persist;
|
||||
|
||||
use std::{collections::HashMap, path::PathBuf, time::Duration};
|
||||
use std::{collections::HashMap, path::PathBuf};
|
||||
|
||||
use clap::Parser;
|
||||
use miette::{IntoDiagnostic as _, Result};
|
||||
|
|
|
|||
Loading…
Add table
Reference in a new issue