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