herolib-virt 0.3.13

Virtualization and container management for herolib (buildah, nerdctl, kubernetes)
Documentation
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))
    }
}