feat: metrics tree with process information

This commit is contained in:
Alex Zenla
2024-04-12 07:21:21 +00:00
parent 8bebda62ad
commit 6fa61a72be
20 changed files with 479 additions and 121 deletions

View File

@ -27,7 +27,6 @@ serde_json = { workspace = true }
sys-mount = { workspace = true }
sysinfo = { workspace = true }
tokio = { workspace = true }
walkdir = { workspace = true }
[lib]
name = "krataguest"

View File

@ -1,6 +1,7 @@
use crate::{
childwait::{ChildEvent, ChildWait},
death,
metrics::MetricsCollector,
};
use anyhow::Result;
use cgroups_rs::Cgroup;
@ -13,7 +14,6 @@ use krata::idm::{
};
use log::debug;
use nix::unistd::Pid;
use sysinfo::System;
use tokio::{select, sync::broadcast};
pub struct GuestBackground {
@ -90,12 +90,9 @@ impl GuestBackground {
}
Some(Request::Metrics(_)) => {
let mut sys = System::new();
sys.refresh_memory();
let response = IdmMetricsResponse {
total_memory_bytes: sys.total_memory(),
used_memory_bytes: sys.used_memory(),
};
let metrics = MetricsCollector::new()?;
let root = metrics.collect()?;
let response = IdmMetricsResponse { root: Some(root) };
self.idm.respond(id, Response::Metrics(response)).await?;
}

View File

@ -17,14 +17,12 @@ use std::fs::{File, OpenOptions, Permissions};
use std::io;
use std::net::{Ipv4Addr, Ipv6Addr};
use std::os::fd::AsRawFd;
use std::os::linux::fs::MetadataExt;
use std::os::unix::ffi::OsStrExt;
use std::os::unix::fs::{chroot, PermissionsExt};
use std::path::{Path, PathBuf};
use std::str::FromStr;
use sys_mount::{FilesystemType, Mount, MountFlags};
use tokio::fs;
use walkdir::WalkDir;
use crate::background::GuestBackground;
@ -88,7 +86,6 @@ impl GuestInit {
let launch = self.parse_launch_config().await?;
self.mount_new_root().await?;
self.nuke_initrd().await?;
self.bind_new_root().await?;
if let Some(hostname) = launch.hostname.clone() {
@ -271,40 +268,6 @@ impl GuestInit {
Ok(serde_json::from_str(&content)?)
}
async fn nuke_initrd(&mut self) -> Result<()> {
trace!("nuking initrd");
let initrd_dev = fs::metadata("/").await?.st_dev();
for item in WalkDir::new("/")
.same_file_system(true)
.follow_links(false)
.contents_first(true)
{
if item.is_err() {
continue;
}
let item = item?;
let metadata = match item.metadata() {
Ok(value) => value,
Err(_) => continue,
};
if metadata.st_dev() != initrd_dev {
continue;
}
if metadata.is_symlink() || metadata.is_file() {
let _ = fs::remove_file(item.path()).await;
trace!("deleting file {:?}", item.path());
} else if metadata.is_dir() {
let _ = fs::remove_dir(item.path()).await;
trace!("deleting directory {:?}", item.path());
}
}
trace!("nuked initrd");
Ok(())
}
async fn bind_new_root(&mut self) -> Result<()> {
self.mount_move_subtree(Path::new(SYS_PATH), Path::new(NEW_ROOT_SYS_PATH))
.await?;

View File

@ -7,6 +7,7 @@ use xenstore::{XsdClient, XsdInterface};
pub mod background;
pub mod childwait;
pub mod init;
pub mod metrics;
pub async fn death(code: c_int) -> Result<()> {
let store = XsdClient::open().await?;

121
crates/guest/src/metrics.rs Normal file
View File

@ -0,0 +1,121 @@
use std::{ops::Add, path::Path};
use anyhow::Result;
use krata::idm::protocol::{IdmMetricFormat, IdmMetricNode};
use sysinfo::Process;
pub struct MetricsCollector {}
impl MetricsCollector {
pub fn new() -> Result<Self> {
Ok(MetricsCollector {})
}
pub fn collect(&self) -> Result<IdmMetricNode> {
let mut sysinfo = sysinfo::System::new();
Ok(IdmMetricNode::structural(
"guest",
vec![
self.collect_system(&mut sysinfo)?,
self.collect_processes(&mut sysinfo)?,
],
))
}
fn collect_system(&self, sysinfo: &mut sysinfo::System) -> Result<IdmMetricNode> {
sysinfo.refresh_memory();
Ok(IdmMetricNode::structural(
"system",
vec![IdmMetricNode::structural(
"memory",
vec![
IdmMetricNode::value("total", sysinfo.total_memory(), IdmMetricFormat::Bytes),
IdmMetricNode::value("used", sysinfo.used_memory(), IdmMetricFormat::Bytes),
IdmMetricNode::value("free", sysinfo.free_memory(), IdmMetricFormat::Bytes),
],
)],
))
}
fn collect_processes(&self, sysinfo: &mut sysinfo::System) -> Result<IdmMetricNode> {
sysinfo.refresh_processes();
let mut processes = Vec::new();
let mut sysinfo_processes = sysinfo.processes().values().collect::<Vec<_>>();
sysinfo_processes.sort_by_key(|x| x.pid());
for process in sysinfo_processes {
if process.thread_kind().is_some() {
continue;
}
processes.push(MetricsCollector::process_node(process)?);
}
Ok(IdmMetricNode::structural("process", processes))
}
fn process_node(process: &Process) -> Result<IdmMetricNode> {
let mut metrics = vec![];
if let Some(parent) = process.parent() {
metrics.push(IdmMetricNode::value(
"parent",
parent.as_u32() as u64,
IdmMetricFormat::Integer,
));
}
if let Some(exe) = process.exe().and_then(path_as_str) {
metrics.push(IdmMetricNode::raw_value("executable", exe));
}
if let Some(working_directory) = process.cwd().and_then(path_as_str) {
metrics.push(IdmMetricNode::raw_value("cwd", working_directory));
}
let cmdline = process.cmd().to_vec();
metrics.push(IdmMetricNode::raw_value("cmdline", cmdline));
metrics.push(IdmMetricNode::structural(
"memory",
vec![
IdmMetricNode::value("resident", process.memory(), IdmMetricFormat::Bytes),
IdmMetricNode::value("virtual", process.virtual_memory(), IdmMetricFormat::Bytes),
],
));
metrics.push(IdmMetricNode::value(
"lifetime",
process.run_time(),
IdmMetricFormat::DurationSeconds,
));
metrics.push(IdmMetricNode::value(
"uid",
process.user_id().map(|x| (*x).add(0)).unwrap_or(0) as f64,
IdmMetricFormat::Integer,
));
metrics.push(IdmMetricNode::value(
"gid",
process.group_id().map(|x| (*x).add(0)).unwrap_or(0) as f64,
IdmMetricFormat::Integer,
));
metrics.push(IdmMetricNode::value(
"euid",
process
.effective_user_id()
.map(|x| (*x).add(0))
.unwrap_or(0) as f64,
IdmMetricFormat::Integer,
));
metrics.push(IdmMetricNode::value(
"egid",
process.effective_group_id().map(|x| x.add(0)).unwrap_or(0) as f64,
IdmMetricFormat::Integer,
));
Ok(IdmMetricNode::structural(
process.pid().to_string(),
metrics,
))
}
}
fn path_as_str(path: &Path) -> Option<String> {
String::from_utf8(path.as_os_str().as_encoded_bytes().to_vec()).ok()
}