From 5c42ed31ba79841202267a9bb2d9686584f1a32b Mon Sep 17 00:00:00 2001 From: Delan Azabani Date: Tue, 11 Nov 2025 15:11:39 +0800 Subject: [PATCH 1/3] monitor: Image interface --- monitor/src/image.rs | 196 ++++++++++++-------------------- monitor/src/image/macos13.rs | 48 +++++++- monitor/src/image/ubuntu2204.rs | 49 +++++++- monitor/src/image/windows10.rs | 49 +++++++- 4 files changed, 215 insertions(+), 127 deletions(-) diff --git a/monitor/src/image.rs b/monitor/src/image.rs index cf93c90b..55aa5164 100644 --- a/monitor/src/image.rs +++ b/monitor/src/image.rs @@ -11,6 +11,7 @@ use std::{ mem::take, os::unix::fs::PermissionsExt, path::{Path, PathBuf}, + sync::LazyLock, thread::{self, JoinHandle}, time::Duration, }; @@ -26,11 +27,75 @@ use settings::{ use tracing::{debug, error, info, warn}; use crate::{ + image::{macos13::Macos13, ubuntu2204::Ubuntu2204, windows10::Windows10}, libvirt::{list_rebuild_guests, list_template_guests}, policy::{runner_images_path, template_or_rebuild_images_path, Policy}, shell::{log_output_as_info, reflink_or_copy_with_warning}, }; +static IMAGES: LazyLock>> = LazyLock::new(|| { + let mut result: BTreeMap> = BTreeMap::new(); + result.insert( + "servo-macos13".to_owned(), + Box::new(Macos13::new(ByteSize::gib(90), Duration::from_secs(2000))), + ); + result.insert( + "servo-macos14".to_owned(), + Box::new(Macos13::new(ByteSize::gib(90), Duration::from_secs(2000))), + ); + result.insert( + "servo-macos15".to_owned(), + Box::new(Macos13::new(ByteSize::gib(90), Duration::from_secs(2000))), + ); + result.insert( + "servo-ubuntu2204".to_owned(), + Box::new(Ubuntu2204::new( + ByteSize::gib(90), + Duration::from_secs(2000), + )), + ); + result.insert( + "servo-ubuntu2204-bench".to_owned(), + Box::new(Ubuntu2204::new( + ByteSize::gib(90), + Duration::from_secs(1000), + )), + ); + result.insert( + "servo-ubuntu2204-rust".to_owned(), + Box::new(Ubuntu2204::new(ByteSize::gib(20), Duration::from_secs(90))), + ); + result.insert( + "servo-ubuntu2204-wpt".to_owned(), + Box::new(Ubuntu2204::new( + ByteSize::gib(90), + Duration::from_secs(2000), + )), + ); + result.insert( + "servo-windows10".to_owned(), + Box::new(Windows10::new(ByteSize::gib(90), Duration::from_secs(3000))), + ); + result +}); + +/// Image rebuild routines. +/// +/// These may shared between similar images, like `servo-ubuntu2204` and `servo-ubuntu2204-wpt`. +pub trait Image { + fn rebuild(&self, profile: &Profile, snapshot_name: &str) -> eyre::Result<()>; + fn delete_template(&self, profile: &Profile, snapshot_name: &str) -> eyre::Result<()>; + fn register_runner(&self, profile: &Profile, runner_guest_name: &str) -> eyre::Result; + fn create_runner( + &self, + profile: &Profile, + snapshot_name: &str, + runner_guest_name: &str, + runner_id: usize, + ) -> eyre::Result; + fn destroy_runner(&self, runner_guest_name: &str, runner_id: usize) -> eyre::Result<()>; +} + #[derive(Debug, Default)] pub struct Rebuilds { cached_servo_repo_update: Option>>, @@ -211,67 +276,7 @@ fn rebuild_with_rust( ) -> Result<(), eyre::Error> { info!(?snapshot_name, "Starting image rebuild"); - let base_images_path = create_template_or_rebuild_images_dir(&profile)?; - - match match &*profile.profile_name { - "servo-macos13" => macos13::rebuild( - &base_images_path, - &profile, - snapshot_name, - ByteSize::gib(90), - Duration::from_secs(2000), - ), - "servo-macos14" => macos13::rebuild( - &base_images_path, - &profile, - snapshot_name, - ByteSize::gib(90), - Duration::from_secs(2000), - ), - "servo-macos15" => macos13::rebuild( - &base_images_path, - &profile, - snapshot_name, - ByteSize::gib(90), - Duration::from_secs(2000), - ), - "servo-ubuntu2204" => ubuntu2204::rebuild( - &base_images_path, - &profile, - snapshot_name, - ByteSize::gib(90), - Duration::from_secs(2000), - ), - "servo-ubuntu2204-bench" => ubuntu2204::rebuild( - &base_images_path, - &profile, - snapshot_name, - ByteSize::gib(90), - Duration::from_secs(1000), - ), - "servo-ubuntu2204-rust" => ubuntu2204::rebuild( - &base_images_path, - &profile, - snapshot_name, - ByteSize::gib(20), - Duration::from_secs(90), - ), - "servo-ubuntu2204-wpt" => ubuntu2204::rebuild( - &base_images_path, - &profile, - snapshot_name, - ByteSize::gib(90), - Duration::from_secs(2000), - ), - "servo-windows10" => windows10::rebuild( - &base_images_path, - &profile, - snapshot_name, - ByteSize::gib(90), - Duration::from_secs(3000), - ), - other => todo!("Rebuild not yet implemented: {other}"), - } { + match IMAGES[&profile.profile_name].rebuild(&profile, snapshot_name) { result @ Ok(()) => { prune_templates(&profile)?; result @@ -285,31 +290,11 @@ fn rebuild_with_rust( } pub fn delete_template(profile: &Profile, snapshot_name: &str) -> eyre::Result<()> { - match &*profile.profile_name { - "servo-macos13" => macos13::delete_template(profile, snapshot_name), - "servo-macos14" => macos13::delete_template(profile, snapshot_name), - "servo-macos15" => macos13::delete_template(profile, snapshot_name), - "servo-ubuntu2204" => ubuntu2204::delete_template(profile, snapshot_name), - "servo-ubuntu2204-bench" => ubuntu2204::delete_template(profile, snapshot_name), - "servo-ubuntu2204-rust" => ubuntu2204::delete_template(profile, snapshot_name), - "servo-ubuntu2204-wpt" => ubuntu2204::delete_template(profile, snapshot_name), - "servo-windows10" => windows10::delete_template(profile, snapshot_name), - other => todo!("Image pruning not yet implemented: {other}"), - } + IMAGES[&profile.profile_name].delete_template(profile, snapshot_name) } pub fn register_runner(profile: &Profile, runner_guest_name: &str) -> eyre::Result { - match &*profile.profile_name { - "servo-macos13" => macos13::register_runner(profile, runner_guest_name), - "servo-macos14" => macos13::register_runner(profile, runner_guest_name), - "servo-macos15" => macos13::register_runner(profile, runner_guest_name), - "servo-ubuntu2204" => ubuntu2204::register_runner(profile, runner_guest_name), - "servo-ubuntu2204-bench" => ubuntu2204::register_runner(profile, runner_guest_name), - "servo-ubuntu2204-rust" => ubuntu2204::register_runner(profile, runner_guest_name), - "servo-ubuntu2204-wpt" => ubuntu2204::register_runner(profile, runner_guest_name), - "servo-windows10" => windows10::register_runner(profile, runner_guest_name), - other => todo!("Runner registration not yet implemented: {other}"), - } + IMAGES[&profile.profile_name].register_runner(profile, runner_guest_name) } pub fn create_runner( @@ -318,33 +303,12 @@ pub fn create_runner( runner_guest_name: &str, runner_id: usize, ) -> eyre::Result { - match &*profile.profile_name { - "servo-macos13" => { - macos13::create_runner(profile, snapshot_name, runner_guest_name, runner_id) - } - "servo-macos14" => { - macos13::create_runner(profile, snapshot_name, runner_guest_name, runner_id) - } - "servo-macos15" => { - macos13::create_runner(profile, snapshot_name, runner_guest_name, runner_id) - } - "servo-ubuntu2204" => { - ubuntu2204::create_runner(profile, snapshot_name, runner_guest_name, runner_id) - } - "servo-ubuntu2204-bench" => { - ubuntu2204::create_runner(profile, snapshot_name, runner_guest_name, runner_id) - } - "servo-ubuntu2204-rust" => { - ubuntu2204::create_runner(profile, snapshot_name, runner_guest_name, runner_id) - } - "servo-ubuntu2204-wpt" => { - ubuntu2204::create_runner(profile, snapshot_name, runner_guest_name, runner_id) - } - "servo-windows10" => { - windows10::create_runner(profile, snapshot_name, runner_guest_name, runner_id) - } - other => todo!("Runner creation not yet implemented: {other}"), - } + IMAGES[&profile.profile_name].create_runner( + profile, + snapshot_name, + runner_guest_name, + runner_id, + ) } pub fn destroy_runner( @@ -352,17 +316,7 @@ pub fn destroy_runner( runner_guest_name: &str, runner_id: usize, ) -> eyre::Result<()> { - match &*profile.profile_name { - "servo-macos13" => macos13::destroy_runner(runner_guest_name, runner_id), - "servo-macos14" => macos13::destroy_runner(runner_guest_name, runner_id), - "servo-macos15" => macos13::destroy_runner(runner_guest_name, runner_id), - "servo-ubuntu2204" => ubuntu2204::destroy_runner(runner_guest_name, runner_id), - "servo-ubuntu2204-bench" => ubuntu2204::destroy_runner(runner_guest_name, runner_id), - "servo-ubuntu2204-rust" => ubuntu2204::destroy_runner(runner_guest_name, runner_id), - "servo-ubuntu2204-wpt" => ubuntu2204::destroy_runner(runner_guest_name, runner_id), - "servo-windows10" => windows10::destroy_runner(runner_guest_name, runner_id), - other => todo!("Runner destruction not yet implemented: {other}"), - } + IMAGES[&profile.profile_name].destroy_runner(runner_guest_name, runner_id) } pub(self) fn create_template_or_rebuild_images_dir(profile: &Profile) -> eyre::Result { diff --git a/monitor/src/image/macos13.rs b/monitor/src/image/macos13.rs index e4885f53..e6280f89 100644 --- a/monitor/src/image/macos13.rs +++ b/monitor/src/image/macos13.rs @@ -14,11 +14,13 @@ use tracing::warn; use crate::data::get_profile_data_path; use crate::image::create_runner_images_dir; +use crate::image::create_template_or_rebuild_images_dir; use crate::image::delete_template_or_rebuild_image_file; use crate::image::libvirt_change_media; use crate::image::rename_guest; use crate::image::undefine_libvirt_guest; use crate::image::CdromImage; +use crate::image::Image; use crate::policy::runner_image_path; use crate::policy::template_or_rebuild_image_path; use crate::shell::atomic_symlink; @@ -29,14 +31,56 @@ use super::create_disk_image; use super::start_libvirt_guest; use super::wait_for_guest; +pub struct Macos13 { + base_image_size: ByteSize, + wait_duration: Duration, +} + +impl Macos13 { + pub const fn new(base_image_size: ByteSize, wait_duration: Duration) -> Self { + Self { + base_image_size, + wait_duration, + } + } +} + +impl Image for Macos13 { + fn rebuild(&self, profile: &Profile, snapshot_name: &str) -> eyre::Result<()> { + rebuild( + profile, + snapshot_name, + self.base_image_size, + self.wait_duration, + ) + } + fn delete_template(&self, profile: &Profile, snapshot_name: &str) -> eyre::Result<()> { + delete_template(profile, snapshot_name) + } + fn register_runner(&self, profile: &Profile, runner_guest_name: &str) -> eyre::Result { + register_runner(profile, runner_guest_name) + } + fn create_runner( + &self, + profile: &Profile, + snapshot_name: &str, + runner_guest_name: &str, + runner_id: usize, + ) -> eyre::Result { + create_runner(profile, snapshot_name, runner_guest_name, runner_id) + } + fn destroy_runner(&self, runner_guest_name: &str, runner_id: usize) -> eyre::Result<()> { + destroy_runner(runner_guest_name, runner_id) + } +} + pub(super) fn rebuild( - base_images_path: impl AsRef, profile: &Profile, snapshot_name: &str, base_image_size: ByteSize, wait_duration: Duration, ) -> eyre::Result<()> { - let base_images_path = base_images_path.as_ref(); + let base_images_path = create_template_or_rebuild_images_dir(&profile)?; let profile_name = &profile.profile_name; let snapshot_path_slug = &profile.snapshot_path_slug(snapshot_name); let rebuild_guest_name = &profile.rebuild_guest_name(snapshot_name); diff --git a/monitor/src/image/ubuntu2204.rs b/monitor/src/image/ubuntu2204.rs index 96f4c99f..31609b09 100644 --- a/monitor/src/image/ubuntu2204.rs +++ b/monitor/src/image/ubuntu2204.rs @@ -14,9 +14,11 @@ use tracing::warn; use crate::data::get_profile_configuration_path; use crate::data::get_profile_data_path; use crate::image::create_runner_images_dir; +use crate::image::create_template_or_rebuild_images_dir; use crate::image::delete_template_or_rebuild_image_file; use crate::image::rename_guest; use crate::image::undefine_libvirt_guest; +use crate::image::Image; use crate::policy::runner_image_path; use crate::policy::template_or_rebuild_image_path; use crate::shell::atomic_symlink; @@ -30,15 +32,58 @@ use super::start_libvirt_guest; use super::wait_for_guest; use super::CdromImage; +pub struct Ubuntu2204 { + base_image_size: ByteSize, + wait_duration: Duration, +} + +impl Ubuntu2204 { + pub const fn new(base_image_size: ByteSize, wait_duration: Duration) -> Self { + Self { + base_image_size, + wait_duration, + } + } +} + +impl Image for Ubuntu2204 { + fn rebuild(&self, profile: &Profile, snapshot_name: &str) -> eyre::Result<()> { + rebuild( + profile, + snapshot_name, + self.base_image_size, + self.wait_duration, + ) + } + fn delete_template(&self, profile: &Profile, snapshot_name: &str) -> eyre::Result<()> { + delete_template(profile, snapshot_name) + } + fn register_runner(&self, profile: &Profile, runner_guest_name: &str) -> eyre::Result { + register_runner(profile, runner_guest_name) + } + fn create_runner( + &self, + profile: &Profile, + snapshot_name: &str, + runner_guest_name: &str, + runner_id: usize, + ) -> eyre::Result { + create_runner(profile, snapshot_name, runner_guest_name, runner_id) + } + fn destroy_runner(&self, runner_guest_name: &str, runner_id: usize) -> eyre::Result<()> { + destroy_runner(runner_guest_name, runner_id) + } +} + pub(super) fn rebuild( - base_images_path: impl AsRef, profile: &Profile, snapshot_name: &str, base_image_size: ByteSize, wait_duration: Duration, ) -> eyre::Result<()> { - let base_images_path = base_images_path.as_ref(); + let base_images_path = create_template_or_rebuild_images_dir(&profile)?; let rebuild_guest_name = &profile.rebuild_guest_name(snapshot_name); + let profile_configuration_path = get_profile_configuration_path(&profile, None)?; let config_iso_filename = format!("config.iso@{snapshot_name}"); let config_iso_path = base_images_path.join(&config_iso_filename); diff --git a/monitor/src/image/windows10.rs b/monitor/src/image/windows10.rs index 07b818de..dd4aac30 100644 --- a/monitor/src/image/windows10.rs +++ b/monitor/src/image/windows10.rs @@ -14,9 +14,11 @@ use tracing::warn; use crate::data::get_profile_configuration_path; use crate::data::get_profile_data_path; use crate::image::create_runner_images_dir; +use crate::image::create_template_or_rebuild_images_dir; use crate::image::delete_template_or_rebuild_image_file; use crate::image::rename_guest; use crate::image::undefine_libvirt_guest; +use crate::image::Image; use crate::policy::runner_image_path; use crate::policy::template_or_rebuild_image_path; use crate::shell::atomic_symlink; @@ -30,15 +32,58 @@ use super::start_libvirt_guest; use super::wait_for_guest; use super::CdromImage; +pub struct Windows10 { + base_image_size: ByteSize, + wait_duration: Duration, +} + +impl Windows10 { + pub const fn new(base_image_size: ByteSize, wait_duration: Duration) -> Self { + Self { + base_image_size, + wait_duration, + } + } +} + +impl Image for Windows10 { + fn rebuild(&self, profile: &Profile, snapshot_name: &str) -> eyre::Result<()> { + rebuild( + profile, + snapshot_name, + self.base_image_size, + self.wait_duration, + ) + } + fn delete_template(&self, profile: &Profile, snapshot_name: &str) -> eyre::Result<()> { + delete_template(profile, snapshot_name) + } + fn register_runner(&self, profile: &Profile, runner_guest_name: &str) -> eyre::Result { + register_runner(profile, runner_guest_name) + } + fn create_runner( + &self, + profile: &Profile, + snapshot_name: &str, + runner_guest_name: &str, + runner_id: usize, + ) -> eyre::Result { + create_runner(profile, snapshot_name, runner_guest_name, runner_id) + } + fn destroy_runner(&self, runner_guest_name: &str, runner_id: usize) -> eyre::Result<()> { + destroy_runner(runner_guest_name, runner_id) + } +} + pub(super) fn rebuild( - base_images_path: impl AsRef, profile: &Profile, snapshot_name: &str, base_image_size: ByteSize, wait_duration: Duration, ) -> eyre::Result<()> { - let base_images_path = base_images_path.as_ref(); + let base_images_path = create_template_or_rebuild_images_dir(&profile)?; let rebuild_guest_name = &profile.rebuild_guest_name(snapshot_name); + let profile_configuration_path = get_profile_configuration_path(&profile, None)?; let config_iso_filename = format!("config.iso@{snapshot_name}"); let config_iso_path = base_images_path.join(&config_iso_filename); From 93d3ed542a2fdd70c4e18cd94b2cc16ad044da4e Mon Sep 17 00:00:00 2001 From: Delan Azabani Date: Tue, 11 Nov 2025 16:50:52 +0800 Subject: [PATCH 2/3] hypervisor: initial polymorphic hypervisor interface --- Cargo.lock | 30 ++++++++++++++++--- Cargo.toml | 5 +++- monitor/Cargo.toml | 5 ++-- monitor/hypervisor/Cargo.toml | 11 +++++++ .../src/impl_libvirt.rs} | 3 +- monitor/hypervisor/src/lib.rs | 30 +++++++++++++++++++ monitor/shell/Cargo.toml | 10 +++++++ monitor/{src/shell.rs => shell/src/lib.rs} | 0 monitor/src/image.rs | 4 +-- monitor/src/image/macos13.rs | 6 ++-- monitor/src/image/ubuntu2204.rs | 6 ++-- monitor/src/image/windows10.rs | 6 ++-- monitor/src/main.rs | 4 +-- monitor/src/policy.rs | 2 +- monitor/src/runner.rs | 6 ++-- 15 files changed, 100 insertions(+), 28 deletions(-) create mode 100644 monitor/hypervisor/Cargo.toml rename monitor/{src/libvirt.rs => hypervisor/src/impl_libvirt.rs} (99%) create mode 100644 monitor/hypervisor/src/lib.rs create mode 100644 monitor/shell/Cargo.toml rename monitor/{src/shell.rs => shell/src/lib.rs} (100%) diff --git a/Cargo.lock b/Cargo.lock index 292b9896..5703008c 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -895,6 +895,17 @@ dependencies = [ "windows-registry", ] +[[package]] +name = "hypervisor" +version = "0.1.0" +dependencies = [ + "cmd_lib", + "jane-eyre", + "settings", + "shell", + "tracing", +] + [[package]] name = "iana-time-zone" version = "0.1.61" @@ -1231,16 +1242,17 @@ dependencies = [ "crossbeam-channel", "dotenv", "http 0.2.12", + "hypervisor", "itertools", "jane-eyre", "mktemp", "rand 0.9.1", - "reflink", "reqwest", "rocket", "serde", "serde_json", "settings", + "shell", "subprocess", "tokio", "toml", @@ -1497,7 +1509,7 @@ dependencies = [ "quinn-udp", "rustc-hash", "rustls", - "socket2 0.5.7", + "socket2 0.6.1", "thiserror", "tokio", "tracing", @@ -1534,9 +1546,9 @@ dependencies = [ "cfg_aliases", "libc", "once_cell", - "socket2 0.5.7", + "socket2 0.6.1", "tracing", - "windows-sys 0.59.0", + "windows-sys 0.60.2", ] [[package]] @@ -1992,6 +2004,16 @@ dependencies = [ "lazy_static", ] +[[package]] +name = "shell" +version = "0.1.0" +dependencies = [ + "jane-eyre", + "mktemp", + "reflink", + "tracing", +] + [[package]] name = "shlex" version = "1.3.0" diff --git a/Cargo.toml b/Cargo.toml index 0ce39b5e..b0a6bd88 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -1,19 +1,22 @@ [workspace] resolver = "3" default-members = ["monitor"] -members = ["monitor", "monitor/cli", "monitor/settings", "monitor/web"] +members = ["monitor", "monitor/cli", "monitor/hypervisor", "monitor/settings", "monitor/shell", "monitor/web"] exclude = ["analysis", "docker"] [workspace.dependencies] bytesize = "2.0.1" chrono = { version = "0.4.39", features = ["serde"] } cli = { path = "monitor/cli" } +cmd_lib = "1.9.5" dotenv = "0.15.0" +hypervisor = { path = "monitor/hypervisor" } jane-eyre = "0.3.0" mktemp = "0.5.1" rocket = { version = "0.5.1", features = ["json"] } serde = { version = "1.0.204", features = ["derive"] } settings = { path = "monitor/settings" } +shell = { path = "monitor/shell" } toml = "0.8.15" tracing = "0.1.40" tracing-subscriber = { version = "0.3.18", features = ["env-filter"] } diff --git a/monitor/Cargo.toml b/monitor/Cargo.toml index b25c817e..553ca0d3 100644 --- a/monitor/Cargo.toml +++ b/monitor/Cargo.toml @@ -11,14 +11,14 @@ bytesize = { workspace = true } cfg-if = "1.0.1" chrono = { workspace = true } cli = { workspace = true } -cmd_lib = "1.9.5" +cmd_lib = { workspace = true } crossbeam-channel = "0.5.13" dotenv = { workspace = true } http = "0.2" +hypervisor = { workspace = true } itertools = "0.13.0" jane-eyre = { workspace = true } mktemp = { workspace = true } -reflink = "0.1.3" reqwest = { version = "0.12.24", features = ["charset", "http2", "json", "rustls-tls", "system-proxy"], default-features = false } rocket = { workspace = true } serde = { workspace = true } @@ -31,6 +31,7 @@ tracing = { workspace = true } tracing-subscriber = { workspace = true } web = { workspace = true } rand = "0.9.1" +shell.workspace = true [dev-dependencies] settings = { workspace = true, features = ["test"] } diff --git a/monitor/hypervisor/Cargo.toml b/monitor/hypervisor/Cargo.toml new file mode 100644 index 00000000..6a289abc --- /dev/null +++ b/monitor/hypervisor/Cargo.toml @@ -0,0 +1,11 @@ +[package] +name = "hypervisor" +version = "0.1.0" +edition = "2024" + +[dependencies] +cmd_lib.workspace = true +jane-eyre.workspace = true +settings.workspace = true +shell.workspace = true +tracing.workspace = true diff --git a/monitor/src/libvirt.rs b/monitor/hypervisor/src/impl_libvirt.rs similarity index 99% rename from monitor/src/libvirt.rs rename to monitor/hypervisor/src/impl_libvirt.rs index 231a9090..4f0d2030 100644 --- a/monitor/src/libvirt.rs +++ b/monitor/hypervisor/src/impl_libvirt.rs @@ -8,10 +8,9 @@ use std::{ use cmd_lib::{run_fun, spawn_with_output}; use jane_eyre::eyre; use settings::TOML; +use shell::log_output_as_trace; use tracing::debug; -use crate::shell::log_output_as_trace; - pub fn list_template_guests() -> eyre::Result> { // Output is not filtered by prefix, so we must filter it ourselves. let prefix = format!("{}-", TOML.libvirt_template_guest_prefix()); diff --git a/monitor/hypervisor/src/lib.rs b/monitor/hypervisor/src/lib.rs new file mode 100644 index 00000000..973cc930 --- /dev/null +++ b/monitor/hypervisor/src/lib.rs @@ -0,0 +1,30 @@ +#[cfg_attr(target_os = "linux", path = "impl_libvirt.rs")] +mod platform; + +use std::{net::Ipv4Addr, path::Path}; + +use jane_eyre::eyre; + +pub fn list_template_guests() -> eyre::Result> { + self::platform::list_template_guests() +} + +pub fn list_rebuild_guests() -> eyre::Result> { + self::platform::list_rebuild_guests() +} + +pub fn list_runner_guests() -> eyre::Result> { + self::platform::list_runner_guests() +} + +pub fn update_screenshot(guest_name: &str, output_dir: &Path) -> eyre::Result<()> { + self::platform::update_screenshot(guest_name, output_dir) +} + +pub fn take_screenshot(guest_name: &str, output_path: &Path) -> eyre::Result<()> { + self::platform::take_screenshot(guest_name, output_path) +} + +pub fn get_ipv4_address(guest_name: &str) -> Option { + self::platform::get_ipv4_address(guest_name) +} diff --git a/monitor/shell/Cargo.toml b/monitor/shell/Cargo.toml new file mode 100644 index 00000000..ff1eab15 --- /dev/null +++ b/monitor/shell/Cargo.toml @@ -0,0 +1,10 @@ +[package] +name = "shell" +version = "0.1.0" +edition = "2024" + +[dependencies] +jane-eyre.workspace = true +mktemp.workspace = true +reflink = "0.1.3" +tracing.workspace = true diff --git a/monitor/src/shell.rs b/monitor/shell/src/lib.rs similarity index 100% rename from monitor/src/shell.rs rename to monitor/shell/src/lib.rs diff --git a/monitor/src/image.rs b/monitor/src/image.rs index 55aa5164..92345c69 100644 --- a/monitor/src/image.rs +++ b/monitor/src/image.rs @@ -19,18 +19,18 @@ use std::{ use bytesize::ByteSize; use chrono::{SecondsFormat, Utc}; use cmd_lib::{run_cmd, run_fun, spawn_with_output}; +use hypervisor::{list_rebuild_guests, list_template_guests}; use jane_eyre::eyre::{self, bail, OptionExt}; use settings::{ profile::{parse_rebuild_guest_name, parse_template_guest_name, Profile}, TOML, }; +use shell::{log_output_as_info, reflink_or_copy_with_warning}; use tracing::{debug, error, info, warn}; use crate::{ image::{macos13::Macos13, ubuntu2204::Ubuntu2204, windows10::Windows10}, - libvirt::{list_rebuild_guests, list_template_guests}, policy::{runner_images_path, template_or_rebuild_images_path, Policy}, - shell::{log_output_as_info, reflink_or_copy_with_warning}, }; static IMAGES: LazyLock>> = LazyLock::new(|| { diff --git a/monitor/src/image/macos13.rs b/monitor/src/image/macos13.rs index e6280f89..13471215 100644 --- a/monitor/src/image/macos13.rs +++ b/monitor/src/image/macos13.rs @@ -10,6 +10,9 @@ use cmd_lib::spawn_with_output; use jane_eyre::eyre; use jane_eyre::eyre::OptionExt; use settings::profile::Profile; +use shell::atomic_symlink; +use shell::log_output_as_info; +use shell::reflink_or_copy_with_warning; use tracing::warn; use crate::data::get_profile_data_path; @@ -23,9 +26,6 @@ use crate::image::CdromImage; use crate::image::Image; use crate::policy::runner_image_path; use crate::policy::template_or_rebuild_image_path; -use crate::shell::atomic_symlink; -use crate::shell::log_output_as_info; -use crate::shell::reflink_or_copy_with_warning; use super::create_disk_image; use super::start_libvirt_guest; diff --git a/monitor/src/image/ubuntu2204.rs b/monitor/src/image/ubuntu2204.rs index 31609b09..f335b1c4 100644 --- a/monitor/src/image/ubuntu2204.rs +++ b/monitor/src/image/ubuntu2204.rs @@ -8,6 +8,9 @@ use cmd_lib::run_cmd; use cmd_lib::spawn_with_output; use jane_eyre::eyre; use settings::profile::Profile; +use shell::atomic_symlink; +use shell::log_output_as_info; +use shell::reflink_or_copy_with_warning; use tracing::info; use tracing::warn; @@ -21,9 +24,6 @@ use crate::image::undefine_libvirt_guest; use crate::image::Image; use crate::policy::runner_image_path; use crate::policy::template_or_rebuild_image_path; -use crate::shell::atomic_symlink; -use crate::shell::log_output_as_info; -use crate::shell::reflink_or_copy_with_warning; use crate::IMAGE_DEPS_DIR; use super::create_disk_image; diff --git a/monitor/src/image/windows10.rs b/monitor/src/image/windows10.rs index dd4aac30..65c73ccc 100644 --- a/monitor/src/image/windows10.rs +++ b/monitor/src/image/windows10.rs @@ -8,6 +8,9 @@ use cmd_lib::run_cmd; use cmd_lib::spawn_with_output; use jane_eyre::eyre; use settings::profile::Profile; +use shell::atomic_symlink; +use shell::log_output_as_info; +use shell::reflink_or_copy_with_warning; use tracing::info; use tracing::warn; @@ -21,9 +24,6 @@ use crate::image::undefine_libvirt_guest; use crate::image::Image; use crate::policy::runner_image_path; use crate::policy::template_or_rebuild_image_path; -use crate::shell::atomic_symlink; -use crate::shell::log_output_as_info; -use crate::shell::reflink_or_copy_with_warning; use crate::IMAGE_DEPS_DIR; use super::create_disk_image; diff --git a/monitor/src/main.rs b/monitor/src/main.rs index 78f53095..cb89f26b 100644 --- a/monitor/src/main.rs +++ b/monitor/src/main.rs @@ -2,10 +2,8 @@ mod dashboard; mod data; mod id; mod image; -mod libvirt; mod policy; mod runner; -mod shell; use core::str; use std::{ @@ -22,6 +20,7 @@ use std::{ use askama::Template; use askama_web::WebTemplate; use crossbeam_channel::{Receiver, Sender}; +use hypervisor::list_runner_guests; use jane_eyre::eyre::{self, eyre, Context, OptionExt}; use mktemp::Temp; use monitor::{ @@ -52,7 +51,6 @@ use crate::{ data::{get_profile_data_path, get_runner_data_path, run_migrations}, id::IdGen, image::{start_libvirt_guest, Rebuilds}, - libvirt::list_runner_guests, policy::{Override, Policy, RunnerCounts}, runner::{Runners, Status}, }; diff --git a/monitor/src/policy.rs b/monitor/src/policy.rs index eff1534b..abecff51 100644 --- a/monitor/src/policy.rs +++ b/monitor/src/policy.rs @@ -10,6 +10,7 @@ use std::{ }; use chrono::DateTime; +use hypervisor::{get_ipv4_address, update_screenshot}; use itertools::Itertools; use jane_eyre::eyre::{self, bail, Context, OptionExt}; use mktemp::Temp; @@ -25,7 +26,6 @@ use tracing::{debug, info, info_span, warn}; use crate::{ data::{get_profile_configuration_path, get_profile_data_path, get_runner_data_path}, image::{create_runner, destroy_runner, register_runner}, - libvirt::{get_ipv4_address, update_screenshot}, runner::{Runner, Runners, Status}, }; diff --git a/monitor/src/runner.rs b/monitor/src/runner.rs index 3c34906c..14243695 100644 --- a/monitor/src/runner.rs +++ b/monitor/src/runner.rs @@ -9,6 +9,7 @@ use std::{ }; use cfg_if::cfg_if; +use hypervisor::{get_ipv4_address, take_screenshot, update_screenshot}; use itertools::Itertools; use jane_eyre::eyre::{self, bail}; use mktemp::Temp; @@ -17,10 +18,7 @@ use serde::{Deserialize, Serialize}; use settings::{profile::ImageType, TOML}; use tracing::{error, info, trace, warn}; -use crate::{ - data::get_runner_data_path, - libvirt::{get_ipv4_address, take_screenshot, update_screenshot}, -}; +use crate::data::get_runner_data_path; #[derive(Debug, Serialize)] pub struct Runners { From 724e7177e22aed9089699d4fca222607721a18ac Mon Sep 17 00:00:00 2001 From: Delan Azabani Date: Wed, 26 Nov 2025 12:35:44 +0800 Subject: [PATCH 3/3] hypervisor: extract some functions out of image module --- monitor/hypervisor/src/impl_libvirt.rs | 74 +++++++++++++- monitor/hypervisor/src/lib.rs | 28 +++++- monitor/hypervisor/src/libvirt.rs | 79 +++++++++++++++ monitor/src/image.rs | 134 ++----------------------- monitor/src/image/macos13.rs | 22 ++-- monitor/src/image/ubuntu2204.rs | 22 ++-- monitor/src/image/windows10.rs | 22 ++-- monitor/src/main.rs | 5 +- monitor/src/policy.rs | 14 +-- 9 files changed, 225 insertions(+), 175 deletions(-) create mode 100644 monitor/hypervisor/src/libvirt.rs diff --git a/monitor/hypervisor/src/impl_libvirt.rs b/monitor/hypervisor/src/impl_libvirt.rs index 4f0d2030..91de6234 100644 --- a/monitor/hypervisor/src/impl_libvirt.rs +++ b/monitor/hypervisor/src/impl_libvirt.rs @@ -1,15 +1,19 @@ use core::str; use std::{ - fs::{create_dir_all, rename}, + collections::BTreeSet, + fs::{create_dir_all, read_dir, rename}, net::Ipv4Addr, path::Path, + time::Duration, }; -use cmd_lib::{run_fun, spawn_with_output}; -use jane_eyre::eyre; -use settings::TOML; +use cmd_lib::{run_cmd, run_fun, spawn_with_output}; +use jane_eyre::eyre::{self, OptionExt, bail}; +use settings::{TOML, profile::Profile}; use shell::log_output_as_trace; -use tracing::debug; +use tracing::{debug, info}; + +use crate::libvirt::{delete_template_or_rebuild_image_file, template_or_rebuild_images_path}; pub fn list_template_guests() -> eyre::Result> { // Output is not filtered by prefix, so we must filter it ourselves. @@ -71,6 +75,66 @@ pub fn get_ipv4_address(guest_name: &str) -> Option { .or_else(|| virsh_domifaddr(guest_name, "agent")) } +pub fn start_guest(guest_name: &str) -> eyre::Result<()> { + info!(?guest_name, "Starting guest"); + run_cmd!(virsh start -- $guest_name)?; + + Ok(()) +} + +pub fn wait_for_guest(guest_name: &str, timeout: Duration) -> eyre::Result<()> { + let timeout = timeout.as_secs(); + info!("Waiting for guest to shut down (max {timeout} seconds)"); + if !run_cmd!(time virsh event --timeout $timeout -- $guest_name lifecycle).is_ok() { + bail!("`virsh event` failed or timed out!"); + } + for _ in 0..100 { + if run_fun!(virsh domstate -- $guest_name)?.trim_ascii() == "shut off" { + return Ok(()); + } + } + + bail!("Guest did not shut down as expected") +} + +pub fn rename_guest(old_guest_name: &str, new_guest_name: &str) -> eyre::Result<()> { + run_cmd!(virsh domrename -- $old_guest_name $new_guest_name)?; + Ok(()) +} + +pub fn delete_guest(guest_name: &str) -> eyre::Result<()> { + if run_cmd!(virsh domstate -- $guest_name).is_ok() { + // FIXME make this idempotent in a less noisy way? + let _ = run_cmd!(virsh destroy -- $guest_name); + run_cmd!(virsh undefine --nvram -- $guest_name)?; + } + + Ok(()) +} + +pub fn prune_base_image_files( + profile: &Profile, + keep_snapshots: BTreeSet, +) -> eyre::Result<()> { + let base_images_path = template_or_rebuild_images_path(profile); + info!(?base_images_path, "Pruning base image files"); + create_dir_all(&base_images_path)?; + + for entry in read_dir(&base_images_path)? { + let filename = entry?.file_name(); + let filename = filename.to_str().ok_or_eyre("Unsupported path")?; + if let Some((_base, snapshot_name)) = filename.split_once("@") { + if !keep_snapshots.contains(snapshot_name) { + delete_template_or_rebuild_image_file(profile, filename); + } + } else { + delete_template_or_rebuild_image_file(profile, filename); + } + } + + Ok(()) +} + fn virsh_domifaddr(guest_name: &str, source: &str) -> Option { let output = run_fun!(virsh domifaddr --source $source $guest_name 2> /dev/null); match output { diff --git a/monitor/hypervisor/src/lib.rs b/monitor/hypervisor/src/lib.rs index 973cc930..aa682198 100644 --- a/monitor/hypervisor/src/lib.rs +++ b/monitor/hypervisor/src/lib.rs @@ -1,9 +1,12 @@ +pub mod libvirt; + #[cfg_attr(target_os = "linux", path = "impl_libvirt.rs")] mod platform; -use std::{net::Ipv4Addr, path::Path}; +use std::{collections::BTreeSet, net::Ipv4Addr, path::Path, time::Duration}; use jane_eyre::eyre; +use settings::profile::Profile; pub fn list_template_guests() -> eyre::Result> { self::platform::list_template_guests() @@ -28,3 +31,26 @@ pub fn take_screenshot(guest_name: &str, output_path: &Path) -> eyre::Result<()> pub fn get_ipv4_address(guest_name: &str) -> Option { self::platform::get_ipv4_address(guest_name) } + +pub fn start_guest(guest_name: &str) -> eyre::Result<()> { + self::platform::start_guest(guest_name) +} + +pub fn wait_for_guest(guest_name: &str, timeout: Duration) -> eyre::Result<()> { + self::platform::wait_for_guest(guest_name, timeout) +} + +pub fn rename_guest(old_guest_name: &str, new_guest_name: &str) -> eyre::Result<()> { + self::platform::rename_guest(old_guest_name, new_guest_name) +} + +pub fn delete_guest(guest_name: &str) -> eyre::Result<()> { + self::platform::delete_guest(guest_name) +} + +pub fn prune_base_image_files( + profile: &Profile, + keep_snapshots: BTreeSet, +) -> eyre::Result<()> { + self::platform::prune_base_image_files(profile, keep_snapshots) +} diff --git a/monitor/hypervisor/src/libvirt.rs b/monitor/hypervisor/src/libvirt.rs new file mode 100644 index 00000000..85c5276e --- /dev/null +++ b/monitor/hypervisor/src/libvirt.rs @@ -0,0 +1,79 @@ +use std::{ + ffi::OsStr, + fs::{create_dir_all, remove_file}, + path::{Path, PathBuf}, +}; + +use cmd_lib::run_cmd; +use jane_eyre::eyre; +use settings::profile::Profile; +use tracing::{debug, info, warn}; + +pub fn template_or_rebuild_images_path(profile: &Profile) -> PathBuf { + Path::new("/var/lib/libvirt/images/base").join(&profile.profile_name) +} + +pub fn runner_images_path() -> PathBuf { + PathBuf::from("/var/lib/libvirt/images/runner") +} + +pub fn delete_template_or_rebuild_image_file(profile: &Profile, filename: &str) { + let base_images_path = template_or_rebuild_images_path(profile); + let path = base_images_path.join(filename); + info!(?path, "Deleting"); + if let Err(error) = remove_file(&path) { + warn!(?path, ?error, "Failed to delete"); + } +} + +pub fn create_template_or_rebuild_images_dir(profile: &Profile) -> eyre::Result { + let base_images_path = template_or_rebuild_images_path(profile); + debug!(?base_images_path, "Creating base images subdirectory"); + create_dir_all(&base_images_path)?; + + Ok(base_images_path) +} + +pub fn create_runner_images_dir() -> eyre::Result { + let runner_images_path = runner_images_path(); + debug!(?runner_images_path, "Creating runner images directory"); + create_dir_all(&runner_images_path)?; + + Ok(runner_images_path) +} + +pub fn define_libvirt_guest( + profile_name: &str, + guest_name: &str, + guest_xml_path: impl AsRef, + args: &[&dyn AsRef], + cdrom_images: &[CdromImage], +) -> eyre::Result<()> { + // This dance is needed to randomise the MAC address of the guest. + let guest_xml_path = guest_xml_path.as_ref(); + let args = args.iter().map(|x| x.as_ref()).collect::>(); + run_cmd!(virsh define -- $guest_xml_path)?; + run_cmd!(virt-clone --preserve-data --check path_in_use=off -o $profile_name.init -n $guest_name $[args])?; + libvirt_change_media(guest_name, cdrom_images)?; + run_cmd!(virsh undefine -- $profile_name.init)?; + + Ok(()) +} + +pub fn libvirt_change_media(guest_name: &str, cdrom_images: &[CdromImage]) -> eyre::Result<()> { + for CdromImage { target_dev, path } in cdrom_images { + run_cmd!(virsh change-media -- $guest_name $target_dev $path)?; + } + + Ok(()) +} + +pub struct CdromImage<'path> { + pub target_dev: &'static str, + pub path: &'path str, +} +impl<'path> CdromImage<'path> { + pub fn new(target_dev: &'static str, path: &'path str) -> Self { + Self { target_dev, path } + } +} diff --git a/monitor/src/image.rs b/monitor/src/image.rs index 92345c69..3cdcef59 100644 --- a/monitor/src/image.rs +++ b/monitor/src/image.rs @@ -5,8 +5,7 @@ pub mod windows10; use core::str; use std::{ collections::{BTreeMap, BTreeSet}, - ffi::OsStr, - fs::{create_dir_all, read_dir, remove_file, set_permissions, File}, + fs::{set_permissions, File}, io::{Seek, Write}, mem::take, os::unix::fs::PermissionsExt, @@ -18,19 +17,19 @@ use std::{ use bytesize::ByteSize; use chrono::{SecondsFormat, Utc}; -use cmd_lib::{run_cmd, run_fun, spawn_with_output}; -use hypervisor::{list_rebuild_guests, list_template_guests}; -use jane_eyre::eyre::{self, bail, OptionExt}; +use cmd_lib::spawn_with_output; +use hypervisor::{delete_guest, list_rebuild_guests, list_template_guests, prune_base_image_files}; +use jane_eyre::eyre::{self, OptionExt}; use settings::{ profile::{parse_rebuild_guest_name, parse_template_guest_name, Profile}, TOML, }; use shell::{log_output_as_info, reflink_or_copy_with_warning}; -use tracing::{debug, error, info, warn}; +use tracing::{error, info, warn}; use crate::{ image::{macos13::Macos13, ubuntu2204::Ubuntu2204, windows10::Windows10}, - policy::{runner_images_path, template_or_rebuild_images_path, Policy}, + policy::Policy, }; static IMAGES: LazyLock>> = LazyLock::new(|| { @@ -119,7 +118,7 @@ impl Rebuilds { .collect::>(); for rebuild_guest_name in list_rebuild_guests()? { if !current_known_rebuild_guest_names.contains(&rebuild_guest_name) { - undefine_libvirt_guest(&rebuild_guest_name)?; + delete_guest(&rebuild_guest_name)?; let (profile_key, snapshot_name) = match parse_rebuild_guest_name(&rebuild_guest_name) { Ok(result) => result, @@ -319,22 +318,6 @@ pub fn destroy_runner( IMAGES[&profile.profile_name].destroy_runner(runner_guest_name, runner_id) } -pub(self) fn create_template_or_rebuild_images_dir(profile: &Profile) -> eyre::Result { - let base_images_path = template_or_rebuild_images_path(profile); - debug!(?base_images_path, "Creating base images subdirectory"); - create_dir_all(&base_images_path)?; - - Ok(base_images_path) -} - -pub(self) fn create_runner_images_dir() -> eyre::Result { - let runner_images_path = runner_images_path(); - debug!(?runner_images_path, "Creating runner images directory"); - create_dir_all(&runner_images_path)?; - - Ok(runner_images_path) -} - pub(self) fn prune_templates(profile: &Profile) -> eyre::Result<()> { // Build a sorted list of template guest names for this profile. let mut snapshot_names = vec![]; @@ -344,7 +327,7 @@ pub(self) fn prune_templates(profile: &Profile) -> eyre::Result<()> { snapshot_names.push(snapshot_name.to_owned()); } } else { - undefine_libvirt_guest(&template_guest_name)?; + delete_guest(&template_guest_name)?; } } snapshot_names.sort(); @@ -358,35 +341,11 @@ pub(self) fn prune_templates(profile: &Profile) -> eyre::Result<()> { } // Now delete any files that are not associated with a known snapshot. - let keep_snapshots = keep_snapshots.collect::>(); - let base_images_path = template_or_rebuild_images_path(profile); - info!(?base_images_path, "Pruning base image files"); - create_dir_all(&base_images_path)?; - - for entry in read_dir(&base_images_path)? { - let filename = entry?.file_name(); - let filename = filename.to_str().ok_or_eyre("Unsupported path")?; - if let Some((_base, snapshot_name)) = filename.split_once("@") { - if !keep_snapshots.contains(snapshot_name) { - delete_template_or_rebuild_image_file(profile, filename); - } - } else { - delete_template_or_rebuild_image_file(profile, filename); - } - } + prune_base_image_files(profile, keep_snapshots.collect())?; Ok(()) } -pub(self) fn delete_template_or_rebuild_image_file(profile: &Profile, filename: &str) { - let base_images_path = template_or_rebuild_images_path(profile); - let path = base_images_path.join(filename); - info!(?path, "Deleting"); - if let Err(error) = remove_file(&path) { - warn!(?path, ?error, "Failed to delete"); - } -} - pub(self) fn create_disk_image<'icp>( base_images_path: impl AsRef, snapshot_name: &str, @@ -423,78 +382,3 @@ pub(self) fn create_disk_image<'icp>( Ok(base_image_path) } - -pub(self) fn define_libvirt_guest( - profile_name: &str, - guest_name: &str, - guest_xml_path: impl AsRef, - args: &[&dyn AsRef], - cdrom_images: &[CdromImage], -) -> eyre::Result<()> { - // This dance is needed to randomise the MAC address of the guest. - let guest_xml_path = guest_xml_path.as_ref(); - let args = args.iter().map(|x| x.as_ref()).collect::>(); - run_cmd!(virsh define -- $guest_xml_path)?; - run_cmd!(virt-clone --preserve-data --check path_in_use=off -o $profile_name.init -n $guest_name $[args])?; - libvirt_change_media(guest_name, cdrom_images)?; - run_cmd!(virsh undefine -- $profile_name.init)?; - - Ok(()) -} - -pub(self) fn libvirt_change_media( - guest_name: &str, - cdrom_images: &[CdromImage], -) -> eyre::Result<()> { - for CdromImage { target_dev, path } in cdrom_images { - run_cmd!(virsh change-media -- $guest_name $target_dev $path)?; - } - - Ok(()) -} - -pub(self) fn undefine_libvirt_guest(guest_name: &str) -> eyre::Result<()> { - if run_cmd!(virsh domstate -- $guest_name).is_ok() { - // FIXME make this idempotent in a less noisy way? - let _ = run_cmd!(virsh destroy -- $guest_name); - run_cmd!(virsh undefine --nvram -- $guest_name)?; - } - - Ok(()) -} - -pub struct CdromImage<'path> { - pub target_dev: &'static str, - pub path: &'path str, -} -impl<'path> CdromImage<'path> { - pub fn new(target_dev: &'static str, path: &'path str) -> Self { - Self { target_dev, path } - } -} -pub fn start_libvirt_guest(guest_name: &str) -> eyre::Result<()> { - info!(?guest_name, "Starting guest"); - run_cmd!(virsh start -- $guest_name)?; - - Ok(()) -} - -pub(self) fn wait_for_guest(guest_name: &str, timeout: Duration) -> eyre::Result<()> { - let timeout = timeout.as_secs(); - info!("Waiting for guest to shut down (max {timeout} seconds)"); - if !run_cmd!(time virsh event --timeout $timeout -- $guest_name lifecycle).is_ok() { - bail!("`virsh event` failed or timed out!"); - } - for _ in 0..100 { - if run_fun!(virsh domstate -- $guest_name)?.trim_ascii() == "shut off" { - return Ok(()); - } - } - - bail!("Guest did not shut down as expected") -} - -pub(self) fn rename_guest(old_guest_name: &str, new_guest_name: &str) -> eyre::Result<()> { - run_cmd!(virsh domrename -- $old_guest_name $new_guest_name)?; - Ok(()) -} diff --git a/monitor/src/image/macos13.rs b/monitor/src/image/macos13.rs index 13471215..98c4e48b 100644 --- a/monitor/src/image/macos13.rs +++ b/monitor/src/image/macos13.rs @@ -7,6 +7,15 @@ use std::time::Duration; use bytesize::ByteSize; use cmd_lib::run_cmd; use cmd_lib::spawn_with_output; +use hypervisor::delete_guest; +use hypervisor::libvirt::create_runner_images_dir; +use hypervisor::libvirt::create_template_or_rebuild_images_dir; +use hypervisor::libvirt::delete_template_or_rebuild_image_file; +use hypervisor::libvirt::libvirt_change_media; +use hypervisor::libvirt::CdromImage; +use hypervisor::rename_guest; +use hypervisor::start_guest; +use hypervisor::wait_for_guest; use jane_eyre::eyre; use jane_eyre::eyre::OptionExt; use settings::profile::Profile; @@ -16,20 +25,11 @@ use shell::reflink_or_copy_with_warning; use tracing::warn; use crate::data::get_profile_data_path; -use crate::image::create_runner_images_dir; -use crate::image::create_template_or_rebuild_images_dir; -use crate::image::delete_template_or_rebuild_image_file; -use crate::image::libvirt_change_media; -use crate::image::rename_guest; -use crate::image::undefine_libvirt_guest; -use crate::image::CdromImage; use crate::image::Image; use crate::policy::runner_image_path; use crate::policy::template_or_rebuild_image_path; use super::create_disk_image; -use super::start_libvirt_guest; -use super::wait_for_guest; pub struct Macos13 { base_image_size: ByteSize, @@ -99,7 +99,7 @@ pub(super) fn rebuild( let ovmf_vars_path = format!("/var/lib/libvirt/images/OSX-KVM/OVMF_VARS.{snapshot_path_slug}.fd"); copy(ovmf_vars_clean_path, ovmf_vars_path)?; - start_libvirt_guest(rebuild_guest_name)?; + start_guest(rebuild_guest_name)?; wait_for_guest(rebuild_guest_name, wait_duration)?; let template_guest_name = &profile.template_guest_name(snapshot_name); @@ -131,7 +131,7 @@ fn define_base_guest( } pub(super) fn delete_template(profile: &Profile, snapshot_name: &str) -> eyre::Result<()> { - undefine_libvirt_guest(&profile.template_guest_name(snapshot_name))?; + delete_guest(&profile.template_guest_name(snapshot_name))?; delete_template_or_rebuild_image_file(profile, &format!("base.img@{snapshot_name}")); Ok(()) } diff --git a/monitor/src/image/ubuntu2204.rs b/monitor/src/image/ubuntu2204.rs index f335b1c4..7e914df9 100644 --- a/monitor/src/image/ubuntu2204.rs +++ b/monitor/src/image/ubuntu2204.rs @@ -6,6 +6,15 @@ use std::time::Duration; use bytesize::ByteSize; use cmd_lib::run_cmd; use cmd_lib::spawn_with_output; +use hypervisor::delete_guest; +use hypervisor::libvirt::create_runner_images_dir; +use hypervisor::libvirt::create_template_or_rebuild_images_dir; +use hypervisor::libvirt::define_libvirt_guest; +use hypervisor::libvirt::delete_template_or_rebuild_image_file; +use hypervisor::libvirt::CdromImage; +use hypervisor::rename_guest; +use hypervisor::start_guest; +use hypervisor::wait_for_guest; use jane_eyre::eyre; use settings::profile::Profile; use shell::atomic_symlink; @@ -16,21 +25,12 @@ use tracing::warn; use crate::data::get_profile_configuration_path; use crate::data::get_profile_data_path; -use crate::image::create_runner_images_dir; -use crate::image::create_template_or_rebuild_images_dir; -use crate::image::delete_template_or_rebuild_image_file; -use crate::image::rename_guest; -use crate::image::undefine_libvirt_guest; use crate::image::Image; use crate::policy::runner_image_path; use crate::policy::template_or_rebuild_image_path; use crate::IMAGE_DEPS_DIR; use super::create_disk_image; -use super::define_libvirt_guest; -use super::start_libvirt_guest; -use super::wait_for_guest; -use super::CdromImage; pub struct Ubuntu2204 { base_image_size: ByteSize, @@ -107,7 +107,7 @@ pub(super) fn rebuild( &base_image_path, &[CdromImage::new("sda", config_iso_path)], )?; - start_libvirt_guest(rebuild_guest_name)?; + start_guest(rebuild_guest_name)?; wait_for_guest(rebuild_guest_name, wait_duration)?; let template_guest_name = &profile.template_guest_name(snapshot_name); @@ -139,7 +139,7 @@ fn define_base_guest( } pub(super) fn delete_template(profile: &Profile, snapshot_name: &str) -> eyre::Result<()> { - undefine_libvirt_guest(&profile.template_guest_name(snapshot_name))?; + delete_guest(&profile.template_guest_name(snapshot_name))?; delete_template_or_rebuild_image_file(profile, &format!("config.iso@{snapshot_name}")); delete_template_or_rebuild_image_file(profile, &format!("base.img@{snapshot_name}")); Ok(()) diff --git a/monitor/src/image/windows10.rs b/monitor/src/image/windows10.rs index 65c73ccc..80e1be74 100644 --- a/monitor/src/image/windows10.rs +++ b/monitor/src/image/windows10.rs @@ -6,6 +6,15 @@ use std::time::Duration; use bytesize::ByteSize; use cmd_lib::run_cmd; use cmd_lib::spawn_with_output; +use hypervisor::delete_guest; +use hypervisor::libvirt::create_runner_images_dir; +use hypervisor::libvirt::create_template_or_rebuild_images_dir; +use hypervisor::libvirt::define_libvirt_guest; +use hypervisor::libvirt::delete_template_or_rebuild_image_file; +use hypervisor::libvirt::CdromImage; +use hypervisor::rename_guest; +use hypervisor::start_guest; +use hypervisor::wait_for_guest; use jane_eyre::eyre; use settings::profile::Profile; use shell::atomic_symlink; @@ -16,21 +25,12 @@ use tracing::warn; use crate::data::get_profile_configuration_path; use crate::data::get_profile_data_path; -use crate::image::create_runner_images_dir; -use crate::image::create_template_or_rebuild_images_dir; -use crate::image::delete_template_or_rebuild_image_file; -use crate::image::rename_guest; -use crate::image::undefine_libvirt_guest; use crate::image::Image; use crate::policy::runner_image_path; use crate::policy::template_or_rebuild_image_path; use crate::IMAGE_DEPS_DIR; use super::create_disk_image; -use super::define_libvirt_guest; -use super::start_libvirt_guest; -use super::wait_for_guest; -use super::CdromImage; pub struct Windows10 { base_image_size: ByteSize, @@ -113,7 +113,7 @@ pub(super) fn rebuild( CdromImage::new("sdd", config_iso_path), ], )?; - start_libvirt_guest(rebuild_guest_name)?; + start_guest(rebuild_guest_name)?; wait_for_guest(rebuild_guest_name, wait_duration)?; let template_guest_name = &profile.template_guest_name(snapshot_name); @@ -145,7 +145,7 @@ fn define_base_guest( } pub(super) fn delete_template(profile: &Profile, snapshot_name: &str) -> eyre::Result<()> { - undefine_libvirt_guest(&profile.template_guest_name(snapshot_name))?; + delete_guest(&profile.template_guest_name(snapshot_name))?; delete_template_or_rebuild_image_file(profile, &format!("config.iso@{snapshot_name}")); delete_template_or_rebuild_image_file(profile, &format!("base.img@{snapshot_name}")); Ok(()) diff --git a/monitor/src/main.rs b/monitor/src/main.rs index cb89f26b..dc2a5d3e 100644 --- a/monitor/src/main.rs +++ b/monitor/src/main.rs @@ -21,6 +21,7 @@ use askama::Template; use askama_web::WebTemplate; use crossbeam_channel::{Receiver, Sender}; use hypervisor::list_runner_guests; +use hypervisor::start_guest; use jane_eyre::eyre::{self, eyre, Context, OptionExt}; use mktemp::Temp; use monitor::{ @@ -50,7 +51,7 @@ use crate::{ dashboard::Dashboard, data::{get_profile_data_path, get_runner_data_path, run_migrations}, id::IdGen, - image::{start_libvirt_guest, Rebuilds}, + image::Rebuilds, policy::{Override, Policy, RunnerCounts}, runner::{Runners, Status}, }; @@ -556,7 +557,7 @@ fn monitor_thread() -> eyre::Result<()> { .join() .map_err(|e| eyre!("Thread panicked: {e:?}")) .and_then(|inner_result| inner_result) - .and_then(|runner_guest_name| start_libvirt_guest(&runner_guest_name)) + .and_then(|runner_guest_name| start_guest(&runner_guest_name)) { warn!(?error, "Failed to create runner: {error}"); } diff --git a/monitor/src/policy.rs b/monitor/src/policy.rs index abecff51..2e17db12 100644 --- a/monitor/src/policy.rs +++ b/monitor/src/policy.rs @@ -10,7 +10,11 @@ use std::{ }; use chrono::DateTime; -use hypervisor::{get_ipv4_address, update_screenshot}; +use hypervisor::{ + get_ipv4_address, + libvirt::{runner_images_path, template_or_rebuild_images_path}, + update_screenshot, +}; use itertools::Itertools; use jane_eyre::eyre::{self, bail, Context, OptionExt}; use mktemp::Temp; @@ -853,14 +857,6 @@ impl Policy { } } -pub fn template_or_rebuild_images_path(profile: &Profile) -> PathBuf { - Path::new("/var/lib/libvirt/images/base").join(&profile.profile_name) -} - -pub fn runner_images_path() -> PathBuf { - PathBuf::from("/var/lib/libvirt/images/runner") -} - pub fn template_or_rebuild_image_path( profile: &Profile, snapshot_name: &str,