use crate::config::{Result, TestbedError};
use super::QemuVm;
#[derive(Debug, Clone)]
pub struct SnapshotInfo {
pub id: u32,
pub tag: String,
pub date: String,
pub vm_clock_size: u64,
}
pub fn save(vm: &mut QemuVm, name: &str) -> Result<()> {
let output = vm.monitor_command(&format!("savevm {name}"))?;
if output.to_lowercase().contains("error") {
return Err(TestbedError::SnapshotFailed {
name: name.to_string(),
reason: output,
});
}
Ok(())
}
pub fn load(vm: &mut QemuVm, name: &str) -> Result<()> {
let output = vm.monitor_command(&format!("loadvm {name}"))?;
if output.to_lowercase().contains("error") {
return Err(TestbedError::SnapshotFailed {
name: name.to_string(),
reason: output,
});
}
Ok(())
}
pub fn delete(vm: &mut QemuVm, name: &str) -> Result<()> {
let output = vm.monitor_command(&format!("delvm {name}"))?;
if output.to_lowercase().contains("error") {
return Err(TestbedError::SnapshotFailed {
name: name.to_string(),
reason: output,
});
}
Ok(())
}
pub fn list(vm: &mut QemuVm) -> Result<Vec<SnapshotInfo>> {
let output = vm.monitor_command("info snapshots")?;
if output.contains("No snapshots") || output.is_empty() {
return Ok(Vec::new());
}
let mut snapshots = Vec::new();
for line in output.lines() {
let line = line.trim();
if line.starts_with("ID") || line.is_empty() {
continue;
}
let parts: Vec<&str> = line.split_whitespace().collect();
if parts.len() < 5 {
continue;
}
let id = parts[0].parse::<u32>().unwrap_or(0);
let tag = parts[1].to_string();
let date = if parts.len() >= 6 {
format!("{} {}", parts[3], parts[4])
} else {
parts[3].to_string()
};
let vm_clock_size = parse_size_string(parts[2]);
snapshots.push(SnapshotInfo {
id,
tag,
date,
vm_clock_size,
});
}
Ok(snapshots)
}
pub fn parse_size_string(s: &str) -> u64 {
let s = s.to_uppercase();
if let Some(num) = s.strip_suffix("MB") {
(num.parse::<f64>().unwrap_or(0.0) * 1_048_576.0) as u64
} else if let Some(num) = s.strip_suffix("GB") {
(num.parse::<f64>().unwrap_or(0.0) * 1_073_741_824.0) as u64
} else if let Some(num) = s.strip_suffix("KB") {
(num.parse::<f64>().unwrap_or(0.0) * 1_024.0) as u64
} else {
s.parse::<u64>().unwrap_or(0)
}
}