Skip to content

Commit bd0dea2

Browse files
committed
hypervisor: extract some functions out of image module
1 parent 714b357 commit bd0dea2

File tree

8 files changed

+132
-111
lines changed

8 files changed

+132
-111
lines changed

monitor/hypervisor/src/impl_libvirt.rs

Lines changed: 41 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -3,13 +3,14 @@ use std::{
33
fs::{create_dir_all, rename},
44
net::Ipv4Addr,
55
path::Path,
6+
time::Duration,
67
};
78

8-
use cmd_lib::{run_fun, spawn_with_output};
9-
use jane_eyre::eyre;
9+
use cmd_lib::{run_cmd, run_fun, spawn_with_output};
10+
use jane_eyre::eyre::{self, bail};
1011
use settings::TOML;
1112
use shell::log_output_as_trace;
12-
use tracing::debug;
13+
use tracing::{debug, info};
1314

1415
pub fn list_template_guests() -> eyre::Result<Vec<String>> {
1516
// Output is not filtered by prefix, so we must filter it ourselves.
@@ -71,6 +72,43 @@ pub fn get_ipv4_address(guest_name: &str) -> Option<Ipv4Addr> {
7172
.or_else(|| virsh_domifaddr(guest_name, "agent"))
7273
}
7374

75+
pub fn start_guest(guest_name: &str) -> eyre::Result<()> {
76+
info!(?guest_name, "Starting guest");
77+
run_cmd!(virsh start -- $guest_name)?;
78+
79+
Ok(())
80+
}
81+
82+
pub fn wait_for_guest(guest_name: &str, timeout: Duration) -> eyre::Result<()> {
83+
let timeout = timeout.as_secs();
84+
info!("Waiting for guest to shut down (max {timeout} seconds)");
85+
if !run_cmd!(time virsh event --timeout $timeout -- $guest_name lifecycle).is_ok() {
86+
bail!("`virsh event` failed or timed out!");
87+
}
88+
for _ in 0..100 {
89+
if run_fun!(virsh domstate -- $guest_name)?.trim_ascii() == "shut off" {
90+
return Ok(());
91+
}
92+
}
93+
94+
bail!("Guest did not shut down as expected")
95+
}
96+
97+
pub fn rename_guest(old_guest_name: &str, new_guest_name: &str) -> eyre::Result<()> {
98+
run_cmd!(virsh domrename -- $old_guest_name $new_guest_name)?;
99+
Ok(())
100+
}
101+
102+
pub fn delete_guest(guest_name: &str) -> eyre::Result<()> {
103+
if run_cmd!(virsh domstate -- $guest_name).is_ok() {
104+
// FIXME make this idempotent in a less noisy way?
105+
let _ = run_cmd!(virsh destroy -- $guest_name);
106+
run_cmd!(virsh undefine --nvram -- $guest_name)?;
107+
}
108+
109+
Ok(())
110+
}
111+
74112
fn virsh_domifaddr(guest_name: &str, source: &str) -> Option<Ipv4Addr> {
75113
let output = run_fun!(virsh domifaddr --source $source $guest_name 2> /dev/null);
76114
match output {

monitor/hypervisor/src/lib.rs

Lines changed: 19 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,9 @@
1+
pub mod libvirt;
2+
13
#[cfg_attr(target_os = "linux", path = "impl_libvirt.rs")]
24
mod platform;
35

4-
use std::{net::Ipv4Addr, path::Path};
6+
use std::{net::Ipv4Addr, path::Path, time::Duration};
57

68
use jane_eyre::eyre;
79

@@ -28,3 +30,19 @@ pub fn take_screenshot(guest_name: &str, output_path: &Path) -> eyre::Result<()>
2830
pub fn get_ipv4_address(guest_name: &str) -> Option<Ipv4Addr> {
2931
self::platform::get_ipv4_address(guest_name)
3032
}
33+
34+
pub fn start_guest(guest_name: &str) -> eyre::Result<()> {
35+
self::platform::start_guest(guest_name)
36+
}
37+
38+
pub fn wait_for_guest(guest_name: &str, timeout: Duration) -> eyre::Result<()> {
39+
self::platform::wait_for_guest(guest_name, timeout)
40+
}
41+
42+
pub fn rename_guest(old_guest_name: &str, new_guest_name: &str) -> eyre::Result<()> {
43+
self::platform::rename_guest(old_guest_name, new_guest_name)
44+
}
45+
46+
pub fn delete_guest(guest_name: &str) -> eyre::Result<()> {
47+
self::platform::delete_guest(guest_name)
48+
}

monitor/hypervisor/src/libvirt.rs

Lines changed: 40 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,40 @@
1+
use std::{ffi::OsStr, path::Path};
2+
3+
use cmd_lib::run_cmd;
4+
use jane_eyre::eyre;
5+
6+
pub fn define_libvirt_guest(
7+
profile_name: &str,
8+
guest_name: &str,
9+
guest_xml_path: impl AsRef<Path>,
10+
args: &[&dyn AsRef<OsStr>],
11+
cdrom_images: &[CdromImage],
12+
) -> eyre::Result<()> {
13+
// This dance is needed to randomise the MAC address of the guest.
14+
let guest_xml_path = guest_xml_path.as_ref();
15+
let args = args.iter().map(|x| x.as_ref()).collect::<Vec<_>>();
16+
run_cmd!(virsh define -- $guest_xml_path)?;
17+
run_cmd!(virt-clone --preserve-data --check path_in_use=off -o $profile_name.init -n $guest_name $[args])?;
18+
libvirt_change_media(guest_name, cdrom_images)?;
19+
run_cmd!(virsh undefine -- $profile_name.init)?;
20+
21+
Ok(())
22+
}
23+
24+
pub fn libvirt_change_media(guest_name: &str, cdrom_images: &[CdromImage]) -> eyre::Result<()> {
25+
for CdromImage { target_dev, path } in cdrom_images {
26+
run_cmd!(virsh change-media -- $guest_name $target_dev $path)?;
27+
}
28+
29+
Ok(())
30+
}
31+
32+
pub struct CdromImage<'path> {
33+
pub target_dev: &'static str,
34+
pub path: &'path str,
35+
}
36+
impl<'path> CdromImage<'path> {
37+
pub fn new(target_dev: &'static str, path: &'path str) -> Self {
38+
Self { target_dev, path }
39+
}
40+
}

monitor/src/image.rs

Lines changed: 5 additions & 81 deletions
Original file line numberDiff line numberDiff line change
@@ -5,7 +5,6 @@ pub mod windows10;
55
use core::str;
66
use std::{
77
collections::{BTreeMap, BTreeSet},
8-
ffi::OsStr,
98
fs::{create_dir_all, read_dir, remove_file, set_permissions, File},
109
io::{Seek, Write},
1110
mem::take,
@@ -18,9 +17,9 @@ use std::{
1817

1918
use bytesize::ByteSize;
2019
use chrono::{SecondsFormat, Utc};
21-
use cmd_lib::{run_cmd, run_fun, spawn_with_output};
22-
use hypervisor::{list_rebuild_guests, list_template_guests};
23-
use jane_eyre::eyre::{self, bail, OptionExt};
20+
use cmd_lib::spawn_with_output;
21+
use hypervisor::{delete_guest, list_rebuild_guests, list_template_guests};
22+
use jane_eyre::eyre::{self, OptionExt};
2423
use settings::{
2524
profile::{parse_rebuild_guest_name, parse_template_guest_name, Profile},
2625
TOML,
@@ -119,7 +118,7 @@ impl Rebuilds {
119118
.collect::<BTreeSet<_>>();
120119
for rebuild_guest_name in list_rebuild_guests()? {
121120
if !current_known_rebuild_guest_names.contains(&rebuild_guest_name) {
122-
undefine_libvirt_guest(&rebuild_guest_name)?;
121+
delete_guest(&rebuild_guest_name)?;
123122
let (profile_key, snapshot_name) =
124123
match parse_rebuild_guest_name(&rebuild_guest_name) {
125124
Ok(result) => result,
@@ -344,7 +343,7 @@ pub(self) fn prune_templates(profile: &Profile) -> eyre::Result<()> {
344343
snapshot_names.push(snapshot_name.to_owned());
345344
}
346345
} else {
347-
undefine_libvirt_guest(&template_guest_name)?;
346+
delete_guest(&template_guest_name)?;
348347
}
349348
}
350349
snapshot_names.sort();
@@ -423,78 +422,3 @@ pub(self) fn create_disk_image<'icp>(
423422

424423
Ok(base_image_path)
425424
}
426-
427-
pub(self) fn define_libvirt_guest(
428-
profile_name: &str,
429-
guest_name: &str,
430-
guest_xml_path: impl AsRef<Path>,
431-
args: &[&dyn AsRef<OsStr>],
432-
cdrom_images: &[CdromImage],
433-
) -> eyre::Result<()> {
434-
// This dance is needed to randomise the MAC address of the guest.
435-
let guest_xml_path = guest_xml_path.as_ref();
436-
let args = args.iter().map(|x| x.as_ref()).collect::<Vec<_>>();
437-
run_cmd!(virsh define -- $guest_xml_path)?;
438-
run_cmd!(virt-clone --preserve-data --check path_in_use=off -o $profile_name.init -n $guest_name $[args])?;
439-
libvirt_change_media(guest_name, cdrom_images)?;
440-
run_cmd!(virsh undefine -- $profile_name.init)?;
441-
442-
Ok(())
443-
}
444-
445-
pub(self) fn libvirt_change_media(
446-
guest_name: &str,
447-
cdrom_images: &[CdromImage],
448-
) -> eyre::Result<()> {
449-
for CdromImage { target_dev, path } in cdrom_images {
450-
run_cmd!(virsh change-media -- $guest_name $target_dev $path)?;
451-
}
452-
453-
Ok(())
454-
}
455-
456-
pub(self) fn undefine_libvirt_guest(guest_name: &str) -> eyre::Result<()> {
457-
if run_cmd!(virsh domstate -- $guest_name).is_ok() {
458-
// FIXME make this idempotent in a less noisy way?
459-
let _ = run_cmd!(virsh destroy -- $guest_name);
460-
run_cmd!(virsh undefine --nvram -- $guest_name)?;
461-
}
462-
463-
Ok(())
464-
}
465-
466-
pub struct CdromImage<'path> {
467-
pub target_dev: &'static str,
468-
pub path: &'path str,
469-
}
470-
impl<'path> CdromImage<'path> {
471-
pub fn new(target_dev: &'static str, path: &'path str) -> Self {
472-
Self { target_dev, path }
473-
}
474-
}
475-
pub fn start_libvirt_guest(guest_name: &str) -> eyre::Result<()> {
476-
info!(?guest_name, "Starting guest");
477-
run_cmd!(virsh start -- $guest_name)?;
478-
479-
Ok(())
480-
}
481-
482-
pub(self) fn wait_for_guest(guest_name: &str, timeout: Duration) -> eyre::Result<()> {
483-
let timeout = timeout.as_secs();
484-
info!("Waiting for guest to shut down (max {timeout} seconds)");
485-
if !run_cmd!(time virsh event --timeout $timeout -- $guest_name lifecycle).is_ok() {
486-
bail!("`virsh event` failed or timed out!");
487-
}
488-
for _ in 0..100 {
489-
if run_fun!(virsh domstate -- $guest_name)?.trim_ascii() == "shut off" {
490-
return Ok(());
491-
}
492-
}
493-
494-
bail!("Guest did not shut down as expected")
495-
}
496-
497-
pub(self) fn rename_guest(old_guest_name: &str, new_guest_name: &str) -> eyre::Result<()> {
498-
run_cmd!(virsh domrename -- $old_guest_name $new_guest_name)?;
499-
Ok(())
500-
}

monitor/src/image/macos13.rs

Lines changed: 8 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,12 @@ use std::time::Duration;
77
use bytesize::ByteSize;
88
use cmd_lib::run_cmd;
99
use cmd_lib::spawn_with_output;
10+
use hypervisor::delete_guest;
11+
use hypervisor::libvirt::libvirt_change_media;
12+
use hypervisor::libvirt::CdromImage;
13+
use hypervisor::rename_guest;
14+
use hypervisor::start_guest;
15+
use hypervisor::wait_for_guest;
1016
use jane_eyre::eyre;
1117
use jane_eyre::eyre::OptionExt;
1218
use settings::profile::Profile;
@@ -19,17 +25,11 @@ use crate::data::get_profile_data_path;
1925
use crate::image::create_runner_images_dir;
2026
use crate::image::create_template_or_rebuild_images_dir;
2127
use crate::image::delete_template_or_rebuild_image_file;
22-
use crate::image::libvirt_change_media;
23-
use crate::image::rename_guest;
24-
use crate::image::undefine_libvirt_guest;
25-
use crate::image::CdromImage;
2628
use crate::image::Image;
2729
use crate::policy::runner_image_path;
2830
use crate::policy::template_or_rebuild_image_path;
2931

3032
use super::create_disk_image;
31-
use super::start_libvirt_guest;
32-
use super::wait_for_guest;
3333

3434
pub struct Macos13 {
3535
base_image_size: ByteSize,
@@ -99,7 +99,7 @@ pub(super) fn rebuild(
9999
let ovmf_vars_path =
100100
format!("/var/lib/libvirt/images/OSX-KVM/OVMF_VARS.{snapshot_path_slug}.fd");
101101
copy(ovmf_vars_clean_path, ovmf_vars_path)?;
102-
start_libvirt_guest(rebuild_guest_name)?;
102+
start_guest(rebuild_guest_name)?;
103103
wait_for_guest(rebuild_guest_name, wait_duration)?;
104104

105105
let template_guest_name = &profile.template_guest_name(snapshot_name);
@@ -131,7 +131,7 @@ fn define_base_guest(
131131
}
132132

133133
pub(super) fn delete_template(profile: &Profile, snapshot_name: &str) -> eyre::Result<()> {
134-
undefine_libvirt_guest(&profile.template_guest_name(snapshot_name))?;
134+
delete_guest(&profile.template_guest_name(snapshot_name))?;
135135
delete_template_or_rebuild_image_file(profile, &format!("base.img@{snapshot_name}"));
136136
Ok(())
137137
}

monitor/src/image/ubuntu2204.rs

Lines changed: 8 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,12 @@ use std::time::Duration;
66
use bytesize::ByteSize;
77
use cmd_lib::run_cmd;
88
use cmd_lib::spawn_with_output;
9+
use hypervisor::delete_guest;
10+
use hypervisor::libvirt::define_libvirt_guest;
11+
use hypervisor::libvirt::CdromImage;
12+
use hypervisor::rename_guest;
13+
use hypervisor::start_guest;
14+
use hypervisor::wait_for_guest;
915
use jane_eyre::eyre;
1016
use settings::profile::Profile;
1117
use shell::atomic_symlink;
@@ -19,18 +25,12 @@ use crate::data::get_profile_data_path;
1925
use crate::image::create_runner_images_dir;
2026
use crate::image::create_template_or_rebuild_images_dir;
2127
use crate::image::delete_template_or_rebuild_image_file;
22-
use crate::image::rename_guest;
23-
use crate::image::undefine_libvirt_guest;
2428
use crate::image::Image;
2529
use crate::policy::runner_image_path;
2630
use crate::policy::template_or_rebuild_image_path;
2731
use crate::IMAGE_DEPS_DIR;
2832

2933
use super::create_disk_image;
30-
use super::define_libvirt_guest;
31-
use super::start_libvirt_guest;
32-
use super::wait_for_guest;
33-
use super::CdromImage;
3434

3535
pub struct Ubuntu2204 {
3636
base_image_size: ByteSize,
@@ -107,7 +107,7 @@ pub(super) fn rebuild(
107107
&base_image_path,
108108
&[CdromImage::new("sda", config_iso_path)],
109109
)?;
110-
start_libvirt_guest(rebuild_guest_name)?;
110+
start_guest(rebuild_guest_name)?;
111111
wait_for_guest(rebuild_guest_name, wait_duration)?;
112112

113113
let template_guest_name = &profile.template_guest_name(snapshot_name);
@@ -139,7 +139,7 @@ fn define_base_guest(
139139
}
140140

141141
pub(super) fn delete_template(profile: &Profile, snapshot_name: &str) -> eyre::Result<()> {
142-
undefine_libvirt_guest(&profile.template_guest_name(snapshot_name))?;
142+
delete_guest(&profile.template_guest_name(snapshot_name))?;
143143
delete_template_or_rebuild_image_file(profile, &format!("config.iso@{snapshot_name}"));
144144
delete_template_or_rebuild_image_file(profile, &format!("base.img@{snapshot_name}"));
145145
Ok(())

0 commit comments

Comments
 (0)