use crate::err::*;
use ruc::{err::*, *};
use std::{
collections::{HashMap, HashSet},
fmt::Debug,
fs, mem,
path::{Path, PathBuf},
sync::Arc,
};
pub type Id = u64;
pub type EnvId = Id;
pub type VmId = Id;
pub type SockPort = u16;
pub type PubSockPort = SockPort;
pub type InnerSockPort = SockPort;
pub type NetAddr = String;
pub type IpAddr = [u8; 4];
pub type MacAddr = [u8; 6];
pub type VmEngineFeat = u32;
#[derive(Debug, Default)]
pub struct Env {
pub id: Id,
pub name: Option<String>,
pub start_timestamp: u64,
pub end_timestamp: u64,
pub vm_set: HashSet<Id>,
pub state: EnvState,
}
#[derive(Debug, Default)]
pub struct EnvState {
pub is_stopped: bool,
pub deny_outgoing: bool,
pub last_mgmt_timestamp: u64,
}
#[derive(Debug)]
pub struct Vm {
pub id: Id,
pub name: Option<String>,
pub engine: Arc<dyn VmEngine>,
pub template: Arc<VmTemplate>,
pub runtime_image: String,
pub net_kind: NetKind,
pub snapshots: HashMap<String, Snapshot>,
pub latest_meta: Option<PathBuf>,
pub state: VmState,
pub resource: VmResource,
pub addr: NetAddr,
}
#[derive(Debug)]
pub enum NetKind {
Flatten,
Nat,
}
impl Default for NetKind {
fn default() -> Self {
Self::Nat
}
}
#[derive(Debug, Default)]
pub struct VmTemplate {
pub name: String,
pub path: String,
pub memo: Option<String>,
pub compatible_engines: Vec<String>,
pub features: Vec<String>,
}
#[derive(Debug, Default)]
pub struct VmState {
pub during_stop: bool,
pub keep_image: bool,
pub rand_uuid: bool,
pub net_blacklist: Vec<IpAddr>,
}
#[derive(Debug, Default)]
pub struct VmResource {
pub cpu_num: u16,
pub mem_size: u32,
pub disk_size: u32,
pub ip_addr: IpAddr,
pub mac_addr: MacAddr,
pub port_map: HashMap<InnerSockPort, PubSockPort>,
}
#[derive(Debug, Default)]
pub struct Snapshot {
pub name: String,
pub path: String,
pub meta_path: PathBuf,
pub life_time: Option<u64>,
}
impl Snapshot {
pub fn new(name: String, meta_path: PathBuf) -> Self {
Self::newx(name, None, meta_path)
}
pub fn newx(name: String, life_time: Option<u64>, meta_path: PathBuf) -> Self {
Snapshot {
name,
life_time,
path: String::new(),
meta_path,
}
}
}
pub trait VmEngine: Send + Sync + Debug + Network + Storage {
fn init(&self) -> Result<()>;
fn check_feat(&self, feat_wanted: &[VmEngineFeat]) -> Result<bool>;
fn create_vm(&self, vm: &mut Vm) -> Result<()> {
self.create_image(vm)
.c(e!(ERR_TT_STORAGE_CREATE_IMAGE))
.and_then(|_| self.set_net(vm).c(e!(ERR_TT_FIREWALL_SET_NET)))
}
fn destroy_vm(&self, vm: &mut Vm) -> Result<()> {
self.destroy_image(vm)
.c(e!(ERR_TT_STORAGE_DESTROY_IMAGE))
.and_then(|_| self.unset_net(vm).c(e!(ERR_TT_FIREWALL_UNSET_NET)))
}
fn start_vm(&self, vm: &mut Vm) -> Result<()>;
fn stop_vm(&self, vm: &mut Vm) -> Result<()>;
fn update_vm(&mut self, vm: Vm) -> Result<()>;
fn cache_meta(&self, vm: &Vm) -> Result<PathBuf>;
fn rm_meta(&self, vm: &mut Vm, path: &Path) -> Result<()> {
fs::remove_file(path).c(e!(ERR_TT_SYS_IO)).map(|_| {
if let Some(ref p) = vm.latest_meta {
if p == path {
vm.latest_meta = None;
}
}
})
}
fn gen_vm_from_meta(&self, path: &Path) -> Result<Vm>;
fn create_snapshot(
&self,
vm: &mut Vm,
name: &str,
life_time: Option<u64>,
) -> Result<()> {
self.stop_vm(vm)
.c(e!(ERR_TT_STOP_VM))
.and_then(|_| {
self.cache_meta(vm)
.c(e!(ERR_TT_META_CREATE_CACHE))
.and_then(|meta| {
let mut snapshot =
Snapshot::newx(name.to_owned(), life_time, meta);
<Self as Storage>::create_snapshot(self, vm, &snapshot)
.c(e!(ERR_TT_SNAPSHOT_CREATE))
.map(|path| {
snapshot.path = path;
vm.snapshots.insert(name.to_owned(), snapshot);
})
})
})
.and_then(|_| self.start_vm(vm).c(e!(ERR_TT_START_VM)))
}
fn destroy_snapshot(&self, vm: &mut Vm, name: &str) -> Result<()> {
vm.snapshots.remove(name).ok_or(eg!()).and_then(|snapshot| {
<Self as Storage>::destroy_snapshot(self, vm, &snapshot.path)
.c(e!(ERR_TT_SNAPSHOT_DESTROY))
.and_then(|_| {
self.rm_meta(vm, &snapshot.meta_path)
.c(e!(ERR_TT_META_REMOVE_CACHE))
})
})
}
fn apply_snapshot(&mut self, vm: &mut Vm, name: &str) -> Result<()> {
self.stop_vm(vm)
.and_then(|_| {
let snapshot = vm.snapshots.get(name).ok_or(eg!())?;
let mut cached_vm = self
.gen_vm_from_meta(&snapshot.meta_path)
.c(e!(ERR_TT_META_RESTORE_CACHE))?;
<Self as Storage>::apply_snapshot(self, &cached_vm, &snapshot)
.c(e!(ERR_TT_SNAPSHOT_APPLY))?;
cached_vm.snapshots = mem::take(&mut vm.snapshots);
self.update_vm(cached_vm).c(e!(ERR_TT_UPDATE_VM))
})
.and_then(|_| self.start_vm(vm).c(e!(ERR_TT_START_VM)))
}
}
pub trait Network {
fn set_net(&self, vm: &mut Vm) -> Result<()>;
fn unset_net(&self, vm: &mut Vm) -> Result<()>;
fn deny_outgoing(&self, vm: &mut Vm) -> Result<()>;
fn allow_outgoing(&self, vm: &mut Vm) -> Result<()>;
fn set_blacklist(&self, vm: &mut Vm) -> Result<()>;
}
pub trait Storage {
fn create_image(&self, vm: &mut Vm) -> Result<()>;
fn destroy_image(&self, vm: &mut Vm) -> Result<()>;
fn create_snapshot(&self, vm: &mut Vm, snapshot: &Snapshot) -> Result<String>;
fn destroy_snapshot(&self, vm: &Vm, snapshot_path: &str) -> Result<()>;
fn apply_snapshot(&self, vm: &Vm, snapshot: &Snapshot) -> Result<()>;
}