use crate::cloudhv::errors::{CloudHypervisorError, Result};
use serde::{Deserialize, Serialize};
use std::fs;
use std::path::PathBuf;
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct SnapshotInfo {
pub vm_id: String,
pub name: String,
pub path: PathBuf,
pub timestamp: u64,
pub size: u64,
}
pub struct SnapshotManager {
base_path: PathBuf,
}
impl SnapshotManager {
pub fn new(base_path: impl Into<PathBuf>) -> Result<Self> {
let base_path = base_path.into();
if !base_path.exists() {
fs::create_dir_all(&base_path)?;
}
Ok(Self { base_path })
}
pub fn create_snapshot(
&self,
vm_id: &str,
name: &str,
_source_url: &str,
) -> Result<SnapshotInfo> {
let vm_dir = self.base_path.join(vm_id);
if !vm_dir.exists() {
fs::create_dir_all(&vm_dir)?;
}
let snapshot_path = vm_dir.join(format!("{}.snap", name));
if snapshot_path.exists() {
return Err(CloudHypervisorError::Snapshot(format!(
"Snapshot already exists: {}",
name
)));
}
let timestamp = std::time::SystemTime::now()
.duration_since(std::time::UNIX_EPOCH)
.map_err(|e| CloudHypervisorError::Snapshot(format!("Time error: {}", e)))?
.as_secs();
let metadata = SnapshotInfo {
vm_id: vm_id.to_string(),
name: name.to_string(),
path: snapshot_path.clone(),
timestamp,
size: 0,
};
let metadata_path = vm_dir.join(format!("{}.json", name));
let metadata_json = serde_json::to_string_pretty(&metadata)?;
fs::write(metadata_path, metadata_json)?;
Ok(metadata)
}
pub fn restore_snapshot(&self, vm_id: &str, name: &str) -> Result<PathBuf> {
let vm_dir = self.base_path.join(vm_id);
let snapshot_path = vm_dir.join(format!("{}.snap", name));
if !snapshot_path.exists() {
return Err(CloudHypervisorError::Snapshot(format!(
"Snapshot not found: {}",
name
)));
}
Ok(snapshot_path)
}
pub fn list_snapshots(&self, vm_id: &str) -> Result<Vec<SnapshotInfo>> {
let vm_dir = self.base_path.join(vm_id);
if !vm_dir.exists() {
return Ok(vec![]);
}
let mut snapshots = Vec::new();
for entry in fs::read_dir(vm_dir)? {
let entry = entry?;
let path = entry.path();
if path.extension().and_then(|s| s.to_str()) == Some("json") {
let content = fs::read_to_string(&path)?;
if let Ok(snapshot_info) = serde_json::from_str::<SnapshotInfo>(&content) {
snapshots.push(snapshot_info);
}
}
}
snapshots.sort_by(|a, b| b.timestamp.cmp(&a.timestamp));
Ok(snapshots)
}
pub fn delete_snapshot(&self, vm_id: &str, name: &str) -> Result<()> {
let vm_dir = self.base_path.join(vm_id);
let snapshot_path = vm_dir.join(format!("{}.snap", name));
let metadata_path = vm_dir.join(format!("{}.json", name));
if snapshot_path.exists() {
fs::remove_file(snapshot_path)?;
}
if metadata_path.exists() {
fs::remove_file(metadata_path)?;
}
Ok(())
}
pub fn get_snapshot_path(&self, vm_id: &str, name: &str) -> PathBuf {
self.base_path.join(vm_id).join(format!("{}.snap", name))
}
}