Skip to content

Commit 724e717

Browse files
committed
hypervisor: extract some functions out of image module
1 parent 93d3ed5 commit 724e717

File tree

9 files changed

+225
-175
lines changed

9 files changed

+225
-175
lines changed

monitor/hypervisor/src/impl_libvirt.rs

Lines changed: 69 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -1,15 +1,19 @@
11
use core::str;
22
use std::{
3-
fs::{create_dir_all, rename},
3+
collections::BTreeSet,
4+
fs::{create_dir_all, read_dir, rename},
45
net::Ipv4Addr,
56
path::Path,
7+
time::Duration,
68
};
79

8-
use cmd_lib::{run_fun, spawn_with_output};
9-
use jane_eyre::eyre;
10-
use settings::TOML;
10+
use cmd_lib::{run_cmd, run_fun, spawn_with_output};
11+
use jane_eyre::eyre::{self, OptionExt, bail};
12+
use settings::{TOML, profile::Profile};
1113
use shell::log_output_as_trace;
12-
use tracing::debug;
14+
use tracing::{debug, info};
15+
16+
use crate::libvirt::{delete_template_or_rebuild_image_file, template_or_rebuild_images_path};
1317

1418
pub fn list_template_guests() -> eyre::Result<Vec<String>> {
1519
// 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<Ipv4Addr> {
7175
.or_else(|| virsh_domifaddr(guest_name, "agent"))
7276
}
7377

78+
pub fn start_guest(guest_name: &str) -> eyre::Result<()> {
79+
info!(?guest_name, "Starting guest");
80+
run_cmd!(virsh start -- $guest_name)?;
81+
82+
Ok(())
83+
}
84+
85+
pub fn wait_for_guest(guest_name: &str, timeout: Duration) -> eyre::Result<()> {
86+
let timeout = timeout.as_secs();
87+
info!("Waiting for guest to shut down (max {timeout} seconds)");
88+
if !run_cmd!(time virsh event --timeout $timeout -- $guest_name lifecycle).is_ok() {
89+
bail!("`virsh event` failed or timed out!");
90+
}
91+
for _ in 0..100 {
92+
if run_fun!(virsh domstate -- $guest_name)?.trim_ascii() == "shut off" {
93+
return Ok(());
94+
}
95+
}
96+
97+
bail!("Guest did not shut down as expected")
98+
}
99+
100+
pub fn rename_guest(old_guest_name: &str, new_guest_name: &str) -> eyre::Result<()> {
101+
run_cmd!(virsh domrename -- $old_guest_name $new_guest_name)?;
102+
Ok(())
103+
}
104+
105+
pub fn delete_guest(guest_name: &str) -> eyre::Result<()> {
106+
if run_cmd!(virsh domstate -- $guest_name).is_ok() {
107+
// FIXME make this idempotent in a less noisy way?
108+
let _ = run_cmd!(virsh destroy -- $guest_name);
109+
run_cmd!(virsh undefine --nvram -- $guest_name)?;
110+
}
111+
112+
Ok(())
113+
}
114+
115+
pub fn prune_base_image_files(
116+
profile: &Profile,
117+
keep_snapshots: BTreeSet<String>,
118+
) -> eyre::Result<()> {
119+
let base_images_path = template_or_rebuild_images_path(profile);
120+
info!(?base_images_path, "Pruning base image files");
121+
create_dir_all(&base_images_path)?;
122+
123+
for entry in read_dir(&base_images_path)? {
124+
let filename = entry?.file_name();
125+
let filename = filename.to_str().ok_or_eyre("Unsupported path")?;
126+
if let Some((_base, snapshot_name)) = filename.split_once("@") {
127+
if !keep_snapshots.contains(snapshot_name) {
128+
delete_template_or_rebuild_image_file(profile, filename);
129+
}
130+
} else {
131+
delete_template_or_rebuild_image_file(profile, filename);
132+
}
133+
}
134+
135+
Ok(())
136+
}
137+
74138
fn virsh_domifaddr(guest_name: &str, source: &str) -> Option<Ipv4Addr> {
75139
let output = run_fun!(virsh domifaddr --source $source $guest_name 2> /dev/null);
76140
match output {

monitor/hypervisor/src/lib.rs

Lines changed: 27 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,9 +1,12 @@
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::{collections::BTreeSet, net::Ipv4Addr, path::Path, time::Duration};
57

68
use jane_eyre::eyre;
9+
use settings::profile::Profile;
710

811
pub fn list_template_guests() -> eyre::Result<Vec<String>> {
912
self::platform::list_template_guests()
@@ -28,3 +31,26 @@ pub fn take_screenshot(guest_name: &str, output_path: &Path) -> eyre::Result<()>
2831
pub fn get_ipv4_address(guest_name: &str) -> Option<Ipv4Addr> {
2932
self::platform::get_ipv4_address(guest_name)
3033
}
34+
35+
pub fn start_guest(guest_name: &str) -> eyre::Result<()> {
36+
self::platform::start_guest(guest_name)
37+
}
38+
39+
pub fn wait_for_guest(guest_name: &str, timeout: Duration) -> eyre::Result<()> {
40+
self::platform::wait_for_guest(guest_name, timeout)
41+
}
42+
43+
pub fn rename_guest(old_guest_name: &str, new_guest_name: &str) -> eyre::Result<()> {
44+
self::platform::rename_guest(old_guest_name, new_guest_name)
45+
}
46+
47+
pub fn delete_guest(guest_name: &str) -> eyre::Result<()> {
48+
self::platform::delete_guest(guest_name)
49+
}
50+
51+
pub fn prune_base_image_files(
52+
profile: &Profile,
53+
keep_snapshots: BTreeSet<String>,
54+
) -> eyre::Result<()> {
55+
self::platform::prune_base_image_files(profile, keep_snapshots)
56+
}

monitor/hypervisor/src/libvirt.rs

Lines changed: 79 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,79 @@
1+
use std::{
2+
ffi::OsStr,
3+
fs::{create_dir_all, remove_file},
4+
path::{Path, PathBuf},
5+
};
6+
7+
use cmd_lib::run_cmd;
8+
use jane_eyre::eyre;
9+
use settings::profile::Profile;
10+
use tracing::{debug, info, warn};
11+
12+
pub fn template_or_rebuild_images_path(profile: &Profile) -> PathBuf {
13+
Path::new("/var/lib/libvirt/images/base").join(&profile.profile_name)
14+
}
15+
16+
pub fn runner_images_path() -> PathBuf {
17+
PathBuf::from("/var/lib/libvirt/images/runner")
18+
}
19+
20+
pub fn delete_template_or_rebuild_image_file(profile: &Profile, filename: &str) {
21+
let base_images_path = template_or_rebuild_images_path(profile);
22+
let path = base_images_path.join(filename);
23+
info!(?path, "Deleting");
24+
if let Err(error) = remove_file(&path) {
25+
warn!(?path, ?error, "Failed to delete");
26+
}
27+
}
28+
29+
pub fn create_template_or_rebuild_images_dir(profile: &Profile) -> eyre::Result<PathBuf> {
30+
let base_images_path = template_or_rebuild_images_path(profile);
31+
debug!(?base_images_path, "Creating base images subdirectory");
32+
create_dir_all(&base_images_path)?;
33+
34+
Ok(base_images_path)
35+
}
36+
37+
pub fn create_runner_images_dir() -> eyre::Result<PathBuf> {
38+
let runner_images_path = runner_images_path();
39+
debug!(?runner_images_path, "Creating runner images directory");
40+
create_dir_all(&runner_images_path)?;
41+
42+
Ok(runner_images_path)
43+
}
44+
45+
pub fn define_libvirt_guest(
46+
profile_name: &str,
47+
guest_name: &str,
48+
guest_xml_path: impl AsRef<Path>,
49+
args: &[&dyn AsRef<OsStr>],
50+
cdrom_images: &[CdromImage],
51+
) -> eyre::Result<()> {
52+
// This dance is needed to randomise the MAC address of the guest.
53+
let guest_xml_path = guest_xml_path.as_ref();
54+
let args = args.iter().map(|x| x.as_ref()).collect::<Vec<_>>();
55+
run_cmd!(virsh define -- $guest_xml_path)?;
56+
run_cmd!(virt-clone --preserve-data --check path_in_use=off -o $profile_name.init -n $guest_name $[args])?;
57+
libvirt_change_media(guest_name, cdrom_images)?;
58+
run_cmd!(virsh undefine -- $profile_name.init)?;
59+
60+
Ok(())
61+
}
62+
63+
pub fn libvirt_change_media(guest_name: &str, cdrom_images: &[CdromImage]) -> eyre::Result<()> {
64+
for CdromImage { target_dev, path } in cdrom_images {
65+
run_cmd!(virsh change-media -- $guest_name $target_dev $path)?;
66+
}
67+
68+
Ok(())
69+
}
70+
71+
pub struct CdromImage<'path> {
72+
pub target_dev: &'static str,
73+
pub path: &'path str,
74+
}
75+
impl<'path> CdromImage<'path> {
76+
pub fn new(target_dev: &'static str, path: &'path str) -> Self {
77+
Self { target_dev, path }
78+
}
79+
}

0 commit comments

Comments
 (0)