use crate::exporters::Exporter;
use crate::sensors::Topology;
use crate::sensors::{utils::ProcessRecord, Sensor};
use std::{fs, io, thread, time};
pub struct QemuExporter {
topology: Topology,
}
impl Exporter for QemuExporter {
fn run(&mut self) {
info!("Starting qemu exporter");
let path = "/var/lib/libvirt/scaphandre";
let cleaner_step = 120;
let mut timer = time::Duration::from_secs(cleaner_step);
loop {
self.iterate(String::from(path));
let step = time::Duration::from_secs(5);
thread::sleep(step);
if timer - step > time::Duration::from_millis(0) {
timer -= step;
} else {
self.topology
.proc_tracker
.clean_terminated_process_records_vectors();
timer = time::Duration::from_secs(cleaner_step);
}
}
}
fn kind(&self) -> &str {
"qemu"
}
}
impl QemuExporter {
pub fn new(sensor: &dyn Sensor) -> QemuExporter {
let topology = sensor
.get_topology()
.expect("sensor topology should be available");
QemuExporter { topology }
}
pub fn iterate(&mut self, path: String) {
trace!("path: {}", path);
self.topology.refresh();
if let Some(topo_energy) = self.topology.get_records_diff_power_microwatts() {
let processes = self.topology.proc_tracker.get_alive_processes();
let qemu_processes = QemuExporter::filter_qemu_vm_processes(&processes);
for qp in qemu_processes {
if qp.len() > 2 {
let last = qp.first().unwrap();
let vm_name = QemuExporter::get_vm_name_from_cmdline(
&last.process.cmdline(&self.topology.proc_tracker).unwrap(),
);
let first_domain_path = format!("{path}/{vm_name}/intel-rapl:0:0");
if fs::read_dir(&first_domain_path).is_err() {
match fs::create_dir_all(&first_domain_path) {
Ok(_) => info!("Created {} folder.", &path),
Err(error) => panic!("Couldn't create {}. Got: {}", &path, error),
}
}
if let Some(ratio) = self
.topology
.get_process_cpu_usage_percentage(last.process.pid)
{
let uj_to_add = ratio.value.parse::<f64>().unwrap()
* topo_energy.value.parse::<f64>().unwrap()
/ 100.0;
let complete_path = format!("{path}/{vm_name}/intel-rapl:0");
match QemuExporter::add_or_create(&complete_path, uj_to_add as u64) {
Ok(result) => {
trace!("{:?}", result);
debug!("Updated {}", complete_path);
}
Err(err) => {
error!(
"Could'nt edit {}. Please check file permissions : {}",
complete_path, err
);
}
}
}
}
}
}
}
fn get_vm_name_from_cmdline(cmdline: &[String]) -> String {
for elmt in cmdline {
if elmt.starts_with("guest=") {
let mut splitted = elmt.split('=');
splitted.next();
return String::from(splitted.next().unwrap().split(',').next().unwrap());
}
}
String::from("") }
fn add_or_create(path: &str, uj_value: u64) -> io::Result<()> {
let mut content = 0;
if fs::read_dir(path).is_err() {
match fs::create_dir_all(path) {
Ok(_) => info!("Created {} folder.", path),
Err(error) => panic!("Couldn't create {}. Got: {}", path, error),
}
}
let file_path = format!("{}/{}", path, "energy_uj");
if let Ok(file) = fs::read_to_string(&file_path) {
content = file.parse::<u64>().unwrap();
content += uj_value;
}
fs::write(file_path, content.to_string())
}
fn filter_qemu_vm_processes(processes: &[&Vec<ProcessRecord>]) -> Vec<Vec<ProcessRecord>> {
let mut qemu_processes: Vec<Vec<ProcessRecord>> = vec![];
trace!("Got {} processes to filter.", processes.len());
for vecp in processes.iter() {
if !vecp.is_empty() {
if let Some(pr) = vecp.first() {
if let Some(res) = pr
.process
.cmdline
.iter()
.find(|x| x.contains("qemu-system"))
{
debug!("Found a process with {}", res);
let mut tmp: Vec<ProcessRecord> = vec![];
for p in vecp.iter() {
tmp.push(p.clone());
}
qemu_processes.push(tmp);
}
}
}
}
qemu_processes
}
}