1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
use super::traits::ImageHandler;
use crate::util::path_exists;
use anyhow::{anyhow, Result};
use std::{
    fs::remove_file,
    path::PathBuf,
    process::{Command, Stdio},
};

pub const QEMU_IMG_PATH: &str = "qemu-img";
pub const QEMU_IMG_DEFAULT_FORMAT: &str = "qcow2";

pub fn qemu_img_name() -> String {
    format!(
        "qemu-{}.{}",
        std::time::SystemTime::now()
            .duration_since(std::time::UNIX_EPOCH)
            .unwrap()
            .as_secs(),
        QEMU_IMG_DEFAULT_FORMAT,
    )
}

#[derive(Debug, Clone)]
pub struct QEmuImageHandler {
    format: String,
}

impl Default for QEmuImageHandler {
    fn default() -> Self {
        Self {
            format: QEMU_IMG_DEFAULT_FORMAT.to_string(),
        }
    }
}

impl ImageHandler for QEmuImageHandler {
    fn import(&self, new_file: PathBuf, orig_file: PathBuf, format: String) -> Result<()> {
        Command::new(QEMU_IMG_PATH)
            .args(vec![
                "convert",
                "-f",
                &format,
                "-O",
                &self.format,
                orig_file.to_str().unwrap(),
                new_file.to_str().unwrap(),
            ])
            .status()?;
        Ok(())
    }

    fn create(&self, target: PathBuf, gbs: usize) -> Result<()> {
        let filename = target.join(qemu_img_name());

        if path_exists(filename.clone()) {
            return Err(anyhow!(
                "filename already exists; did you already create this vm?",
            ));
        }

        let status = Command::new(QEMU_IMG_PATH)
            .args(vec![
                "create",
                "-f",
                &self.format,
                filename.to_str().unwrap(),
                &format!("{}G", gbs),
            ])
            .stderr(Stdio::null())
            .stdout(Stdio::null())
            .status();

        match status {
            Ok(st) => {
                if st.success() {
                    return Ok(());
                } else {
                    return Err(anyhow!(
                        "process exited with code: {}",
                        st.code().expect("unknown")
                    ));
                }
            }
            Err(e) => return Err(anyhow!(e)),
        }
    }

    fn remove(&self, disk: PathBuf) -> Result<()> {
        if !path_exists(disk.clone()) {
            return Err(anyhow!("filename does not exist"));
        }

        Ok(remove_file(disk)?)
    }

    fn clone_image(&self, old: PathBuf, new: PathBuf) -> Result<()> {
        Ok(std::fs::copy(old, new).and_then(|_| Ok(()))?)
    }
}