Skip to content

Commit 0f09e84

Browse files
committed
monitor: compute image age from zfs creation property (#6)
1 parent 326cd78 commit 0f09e84

File tree

5 files changed

+81
-7
lines changed

5 files changed

+81
-7
lines changed

monitor/src/get-snapshot-creation.sh

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,6 @@
1+
#!/usr/bin/env zsh
2+
# usage: get-snapshot-creation.sh <pool/path/to/zvol@snapshot>
3+
set -euo pipefail -o bsdecho
4+
dataset_and_snapshot=$1; shift
5+
6+
zfs get -Hpo value creation "$dataset_and_snapshot"

monitor/src/main.rs

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -403,10 +403,11 @@ fn monitor_thread() -> eyre::Result<()> {
403403
busy,
404404
excess_idle,
405405
wanted,
406+
image_age,
406407
},
407408
) in profile_runner_counts.iter()
408409
{
409-
info!("profile {key}: {healthy}/{target} healthy runners ({idle} idle, {reserved} reserved, {busy} busy, {started_or_crashed} started or crashed, {excess_idle} excess idle, {wanted} wanted)");
410+
info!("profile {key}: image age {image_age:?}, {healthy}/{target} healthy runners ({idle} idle, {reserved} reserved, {busy} busy, {started_or_crashed} started or crashed, {excess_idle} excess idle, {wanted} wanted)");
410411
}
411412
for (_id, runner) in runners.iter() {
412413
runner.log_info();

monitor/src/profile.rs

Lines changed: 28 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,13 +1,17 @@
1-
use std::process::Command;
1+
use std::{
2+
process::Command,
3+
time::{Duration, SystemTime, UNIX_EPOCH},
4+
};
25

3-
use jane_eyre::eyre;
6+
use jane_eyre::eyre::{self, Context};
47
use serde::{Deserialize, Serialize};
58
use tracing::{debug, info};
69

710
use crate::{
811
data::get_profile_data_path,
912
libvirt::update_screenshot,
1013
runner::{Runner, Runners, Status},
14+
zfs::snapshot_creation_time_unix,
1115
DOTENV,
1216
};
1317

@@ -30,6 +34,7 @@ pub struct RunnerCounts {
3034
pub busy: usize,
3135
pub excess_idle: usize,
3236
pub wanted: usize,
37+
pub image_age: Option<Duration>,
3338
}
3439

3540
impl Profile {
@@ -96,6 +101,7 @@ impl Profile {
96101
busy: self.busy_runner_count(runners),
97102
excess_idle: self.excess_idle_runner_count(runners),
98103
wanted: self.wanted_runner_count(runners),
104+
image_age: self.image_age().ok().flatten(),
99105
}
100106
}
101107

@@ -166,6 +172,26 @@ impl Profile {
166172
}
167173
}
168174

175+
pub fn image_age(&self) -> eyre::Result<Option<Duration>> {
176+
let now = SystemTime::now()
177+
.duration_since(UNIX_EPOCH)
178+
.wrap_err("Failed to get current time")?;
179+
let creation_time =
180+
match snapshot_creation_time_unix(&self.base_vm_name, &self.base_image_snapshot) {
181+
Ok(result) => result,
182+
Err(error) => {
183+
debug!(
184+
self.base_vm_name,
185+
?error,
186+
"Failed to get snapshot creation time"
187+
);
188+
return Ok(None);
189+
}
190+
};
191+
192+
Ok(Some(now - creation_time))
193+
}
194+
169195
pub fn update_screenshot(&self) {
170196
if let Err(error) = self.try_update_screenshot() {
171197
debug!(

monitor/src/settings.rs

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -20,7 +20,7 @@ pub struct Dotenv {
2020
pub github_api_suffix: String,
2121
pub libvirt_prefix: String,
2222
pub zfs_prefix: String,
23-
// SERVO_CI_ZFS_CLONE_PREFIX not used
23+
pub zfs_clone_prefix: String,
2424
pub monitor_data_path: Option<String>,
2525
// SERVO_CI_ZVOL_BLOCK_DEVICE_TIMEOUT not used
2626
pub monitor_poll_interval: Duration,
@@ -54,6 +54,7 @@ impl Dotenv {
5454
github_api_suffix: env_string("SERVO_CI_GITHUB_API_SUFFIX"),
5555
libvirt_prefix: env_string("SERVO_CI_LIBVIRT_PREFIX"),
5656
zfs_prefix: env_string("SERVO_CI_ZFS_PREFIX"),
57+
zfs_clone_prefix: env_string("SERVO_CI_ZFS_CLONE_PREFIX"),
5758
monitor_data_path: env_option_string("SERVO_CI_MONITOR_DATA_PATH"),
5859
monitor_poll_interval: env_duration_secs("SERVO_CI_MONITOR_POLL_INTERVAL"),
5960
api_cache_timeout: env_duration_secs("SERVO_CI_API_CACHE_TIMEOUT"),

monitor/src/zfs.rs

Lines changed: 43 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,9 +1,12 @@
11
use core::str;
2-
use std::process::{Command, Stdio};
2+
use std::{
3+
process::{Command, Stdio},
4+
time::Duration,
5+
};
36

4-
use jane_eyre::eyre::{self, Context};
7+
use jane_eyre::eyre::{self, bail, eyre, Context, OptionExt};
58

6-
use crate::DOTENV;
9+
use crate::{shell::SHELL, DOTENV};
710

811
pub fn list_runner_volumes() -> eyre::Result<Vec<String>> {
912
let output = Command::new("../list-runner-volumes.sh")
@@ -26,3 +29,40 @@ pub fn list_runner_volumes() -> eyre::Result<Vec<String>> {
2629

2730
Ok(result.collect())
2831
}
32+
33+
pub fn snapshot_creation_time_unix(zvol_name: &str, snapshot_name: &str) -> eyre::Result<Duration> {
34+
let dataset_and_snapshot = format!("{}/{zvol_name}@{snapshot_name}", DOTENV.zfs_clone_prefix);
35+
let output = SHELL
36+
.lock()
37+
.map_err(|e| eyre!("Mutex poisoned: {e:?}"))?
38+
.run(
39+
include_str!("get-snapshot-creation.sh"),
40+
[dataset_and_snapshot],
41+
)?
42+
.stdout(Stdio::piped())
43+
.stderr(Stdio::piped())
44+
.spawn()?
45+
.wait_with_output()?;
46+
if !output.status.success() {
47+
let stdout = str::from_utf8(&output.stdout)
48+
.to_owned()
49+
.map_err(|_| output.stdout.clone());
50+
let stderr = str::from_utf8(&output.stderr)
51+
.to_owned()
52+
.map_err(|_| output.stderr.clone());
53+
bail!(
54+
"Command exited with status {}: stdout {:?}, stderr {:?}",
55+
output.status,
56+
stdout,
57+
stderr
58+
);
59+
}
60+
let result = str::from_utf8(&output.stdout)
61+
.wrap_err("Failed to decode UTF-8")?
62+
.strip_suffix('\n')
63+
.ok_or_eyre("Failed to strip trailing newline")?
64+
.parse::<u64>()
65+
.wrap_err("Failed to parse as u64")?;
66+
67+
Ok(Duration::from_secs(result))
68+
}

0 commit comments

Comments
 (0)