tt_framework/
model.rs

1//!
2//! # Design Model
3//!
4//! Design of the core infrastructure.
5//!
6
7use crate::err::*;
8use ruc::{err::*, *};
9use std::{
10    collections::{HashMap, HashSet},
11    fmt::Debug,
12    fs, mem,
13    path::{Path, PathBuf},
14    sync::Arc,
15};
16
17/// Use `u64` to express an ID,
18/// e.g. [EnvId](self::Env::id), [VmId](self::Vm::id) ...
19pub type Id = u64;
20
21/// ID alias for ENV.
22pub type EnvId = Id;
23
24/// ID alias for VM.
25pub type VmId = Id;
26
27/// Use `u16` to express a socket port.
28pub type SockPort = u16;
29
30/// Service ports within the host machine.
31pub type PubSockPort = SockPort;
32
33/// Service ports within the VM.
34pub type InnerSockPort = SockPort;
35
36/// Use `String` to express a network address,
37/// in the the perspective of the client, see [NetAddr](self::Vm::addr).
38pub type NetAddr = String;
39
40/// Inner IP(v4) address of VM.
41pub type IpAddr = [u8; 4];
42
43/// MAC address of VM.
44pub type MacAddr = [u8; 6];
45
46/// Use `u32` as the uint of supported vm-engine-features.
47pub type VmEngineFeat = u32;
48
49/// The base unit of TT,
50/// stands for a complete workspace for client.
51#[derive(Debug, Default)]
52pub struct Env {
53    /// UUID of the ENV
54    pub id: Id,
55    /// Name of the ENV.
56    pub name: Option<String>,
57    /// The start timestamp of this ENV,
58    /// can NOT be changed.
59    pub start_timestamp: u64,
60    /// The end timestamp of this ENV,
61    /// permit use to change it .
62    pub end_timestamp: u64,
63    /// All [VmId](self::Vm::id)s under this ENV.
64    pub vm_set: HashSet<Id>,
65    /// Info about the state of ENV.
66    pub state: EnvState,
67}
68
69/// Info about the state of ENV.
70#[derive(Debug, Default)]
71pub struct EnvState {
72    /// Whether this ENV is stopped.
73    pub is_stopped: bool,
74    /// If true, all VMs of this ENV are denied to Internet.
75    pub deny_outgoing: bool,
76    /// The timestamp of the lastest manage-operation,
77    /// such as 'stop/start/snapshot...'.
78    ///
79    /// This kind of operation can NOT be called frequently,
80    /// because them will take a long time to complete.
81    pub last_mgmt_timestamp: u64,
82}
83
84/// Infomations about a VM instance.
85#[derive(Debug)]
86pub struct Vm {
87    /// UUID of this `VM`
88    pub id: Id,
89    /// Name of the `VM`.
90    pub name: Option<String>,
91    /// Created by which engine.
92    pub engine: Arc<dyn VmEngine>,
93    /// Template of `runtime_image`, that is,
94    /// the runtime image is created based on the template.
95    pub template: Arc<VmTemplate>,
96    /// Runtime image of Vm.
97    ///
98    /// Use 'String' instead of 'PathBuf', because
99    /// `runtime_image` may not be a regular file path,
100    /// such as `ZFS` stroage.
101    ///
102    /// E.g. zroot/tt/[VmId](self::Vm::id)
103    pub runtime_image: String,
104    /// Network kind of this VM.
105    pub net_kind: NetKind,
106    /// SnapshotName => Snapshot
107    pub snapshots: HashMap<String, Snapshot>,
108    /// The latest cached config-file.
109    pub latest_meta: Option<PathBuf>,
110    /// Info about the state of VM.
111    pub state: VmState,
112    /// Info about the resource of VM.
113    pub resource: VmResource,
114    /// Usually an 'IP' or a 'domain url'.
115    ///
116    /// Only meaningful from the perspective of the client,
117    /// to indicate how to connect to it from the client.
118    ///
119    /// This has different meanings with the
120    /// [ip_addr](self::VmResource::ip_addr) in [VmResource](self::VmResource).
121    pub addr: NetAddr,
122}
123
124/// Kind of network.
125#[derive(Debug)]
126pub enum NetKind {
127    /// Like VxLan or Flannel(used in k8s).
128    Flatten,
129    /// Need firewall to do ports-forwarding.
130    Nat,
131}
132
133impl Default for NetKind {
134    fn default() -> Self {
135        Self::Nat
136    }
137}
138
139/// Infomations about the template of VM,
140/// or in other word, the base image of VM.
141#[derive(Debug, Default)]
142pub struct VmTemplate {
143    /// Globally unique name,
144    /// e.g. "github.com/ktmlm/alpine".
145    pub name: String,
146    /// Path which pointing to the template.
147    /// May not be a regular file path, such as `ZFS`.
148    pub path: String,
149    /// Description of the template image, that is,
150    /// the low-level infrastructure of the runtime image.
151    pub memo: Option<String>,
152    /// Engines(name) that can use this template.
153    pub compatible_engines: Vec<String>,
154    /// Features that may be used by some compatible engines.
155    pub features: Vec<String>,
156}
157
158/// Info about the state of VM.
159#[derive(Debug, Default)]
160pub struct VmState {
161    /// Whether has been stopped.
162    pub during_stop: bool,
163    /// Whether keep image NOT to be destroyed,
164    /// when the life cycle of `Vm` ends.
165    pub keep_image: bool,
166    /// Whether generate a random uuid for this VM,
167    /// if NOT, `machine-id` of the VM may be empty.
168    pub rand_uuid: bool,
169    /// VM can NOT connect to the addrs in this list.
170    pub net_blacklist: Vec<IpAddr>,
171}
172
173/// Info about the resource of VM.
174#[derive(Debug, Default)]
175pub struct VmResource {
176    /// CPU number
177    pub cpu_num: u16,
178    /// Memory size in MB
179    pub mem_size: u32,
180    /// Disk size in MB
181    pub disk_size: u32,
182    /// Inner IP address, e.g. '[10,0,0,2]',
183    /// IP is generated from 'MAC address',
184    /// use the last three fields of MAC.
185    pub ip_addr: IpAddr,
186    /// MAC address, e.g. '[0x82,0x17,0x0d,0x6a,0xbc,0x80]',
187    /// used to generate the responding IP address.
188    pub mac_addr: MacAddr,
189    /// Ports allocation for NAT, that is:
190    /// {Private Port within VM} => {Public Port within Host}.
191    ///
192    /// If the type of network is [Flatten](self::NetKind::Flatten),
193    /// this field should be empty(and be ignored).
194    pub port_map: HashMap<InnerSockPort, PubSockPort>,
195}
196
197/// Snapshot management.
198#[derive(Debug, Default)]
199pub struct Snapshot {
200    /// The name of snapshot.
201    pub name: String,
202    /// The data path of snapshot,
203    /// May not be a regular file path, such as `ZFS`.
204    pub path: String,
205    /// The corresponding metadata to the snapshot.
206    pub meta_path: PathBuf,
207    /// If set this, snapshot will be cleaned when
208    /// the number of seconds it exists exceeds this value.
209    pub life_time: Option<u64>,
210}
211
212impl Snapshot {
213    /// Init a new snapshot instance without `life_time` limition.
214    pub fn new(name: String, meta_path: PathBuf) -> Self {
215        Self::newx(name, None, meta_path)
216    }
217
218    /// Init a new snapshot instance.
219    pub fn newx(name: String, life_time: Option<u64>, meta_path: PathBuf) -> Self {
220        Snapshot {
221            name,
222            life_time,
223            path: String::new(),
224            meta_path,
225        }
226    }
227}
228
229/// Common methods for each engine,
230/// such as 'Firecracker', 'Qemu', 'Docker' ...
231pub trait VmEngine: Send + Sync + Debug + Network + Storage {
232    /// Will be called once during system starting.
233    fn init(&self) -> Result<()>;
234
235    /// Check if all features the client wanted can be supported.
236    fn check_feat(&self, feat_wanted: &[VmEngineFeat]) -> Result<bool>;
237
238    /// Create the VM instance, and update necessary data of the `Vm`.
239    fn create_vm(&self, vm: &mut Vm) -> Result<()> {
240        self.create_image(vm)
241            .c(e!(ERR_TT_STORAGE_CREATE_IMAGE))
242            .and_then(|_| self.set_net(vm).c(e!(ERR_TT_FIREWALL_SET_NET)))
243    }
244
245    /// Destroy the VM instance, and update necessary data of the `Vm`.
246    fn destroy_vm(&self, vm: &mut Vm) -> Result<()> {
247        self.destroy_image(vm)
248            .c(e!(ERR_TT_STORAGE_DESTROY_IMAGE))
249            .and_then(|_| self.unset_net(vm).c(e!(ERR_TT_FIREWALL_UNSET_NET)))
250    }
251
252    /// Start a `stopped` VM.
253    fn start_vm(&self, vm: &mut Vm) -> Result<()>;
254
255    /// Stop(aka pause) a running VM.
256    fn stop_vm(&self, vm: &mut Vm) -> Result<()>;
257
258    /// Use the new `Vm` instead of the old one,
259    /// apply all configs in the new `Vm`.
260    fn update_vm(&mut self, vm: Vm) -> Result<()>;
261
262    /// Cache all infomations of the 'Vm' to disk.
263    fn cache_meta(&self, vm: &Vm) -> Result<PathBuf>;
264
265    /// Remove a cached config of `Vm`.
266    fn rm_meta(&self, vm: &mut Vm, path: &Path) -> Result<()> {
267        fs::remove_file(path).c(e!(ERR_TT_SYS_IO)).map(|_| {
268            if let Some(ref p) = vm.latest_meta {
269                if p == path {
270                    vm.latest_meta = None;
271                }
272            }
273        })
274    }
275
276    /// Restruct a `Vm` from a cached config.
277    fn gen_vm_from_meta(&self, path: &Path) -> Result<Vm>;
278
279    /// Add a snapshot for the runtime image:
280    ///
281    /// 1. stop the runtime instance
282    /// 2. cache current meta-config
283    /// 3. snapshot storage
284    /// 4. restart the runtime instance
285    fn create_snapshot(
286        &self,
287        vm: &mut Vm,
288        name: &str,
289        life_time: Option<u64>,
290    ) -> Result<()> {
291        self.stop_vm(vm)
292            .c(e!(ERR_TT_STOP_VM))
293            .and_then(|_| {
294                self.cache_meta(vm)
295                    .c(e!(ERR_TT_META_CREATE_CACHE))
296                    .and_then(|meta| {
297                        let mut snapshot =
298                            Snapshot::newx(name.to_owned(), life_time, meta);
299                        <Self as Storage>::create_snapshot(self, vm, &snapshot)
300                            .c(e!(ERR_TT_SNAPSHOT_CREATE))
301                            .map(|path| {
302                                snapshot.path = path;
303                                vm.snapshots.insert(name.to_owned(), snapshot);
304                            })
305                    })
306            })
307            .and_then(|_| self.start_vm(vm).c(e!(ERR_TT_START_VM)))
308    }
309
310    /// Delete a snapshot of the runtime image:
311    ///
312    /// 1. remove the storage of snapshot
313    /// 2. remove the cached-meta of snapshot
314    fn destroy_snapshot(&self, vm: &mut Vm, name: &str) -> Result<()> {
315        vm.snapshots.remove(name).ok_or(eg!()).and_then(|snapshot| {
316            <Self as Storage>::destroy_snapshot(self, vm, &snapshot.path)
317                .c(e!(ERR_TT_SNAPSHOT_DESTROY))
318                .and_then(|_| {
319                    self.rm_meta(vm, &snapshot.meta_path)
320                        .c(e!(ERR_TT_META_REMOVE_CACHE))
321                })
322        })
323    }
324
325    /// Revert to the state of this snapshot:
326    ///
327    /// 1. stop the runtime instance
328    /// 3. relink runtime image to the one in snapshot
329    /// 2. restore the responding [Vm](self::Vm) from cached-meta
330    /// 4. restart the runtime instance
331    fn apply_snapshot(&mut self, vm: &mut Vm, name: &str) -> Result<()> {
332        self.stop_vm(vm)
333            .and_then(|_| {
334                let snapshot = vm.snapshots.get(name).ok_or(eg!())?;
335                let mut cached_vm = self
336                    .gen_vm_from_meta(&snapshot.meta_path)
337                    .c(e!(ERR_TT_META_RESTORE_CACHE))?;
338                <Self as Storage>::apply_snapshot(self, &cached_vm, &snapshot)
339                    .c(e!(ERR_TT_SNAPSHOT_APPLY))?;
340                cached_vm.snapshots = mem::take(&mut vm.snapshots);
341                self.update_vm(cached_vm).c(e!(ERR_TT_UPDATE_VM))
342            })
343            .and_then(|_| self.start_vm(vm).c(e!(ERR_TT_START_VM)))
344    }
345}
346
347/// This trait describes how to manage the network,
348/// such as 'firewall rule' in the [NAT](self::NetKind::Nat) mode.
349pub trait Network {
350    /// Set network for the VM.
351    fn set_net(&self, vm: &mut Vm) -> Result<()>;
352    /// Unset network for the VM.
353    fn unset_net(&self, vm: &mut Vm) -> Result<()>;
354
355    /// Disable VM's active access to the Internet.
356    fn deny_outgoing(&self, vm: &mut Vm) -> Result<()>;
357    /// Enable VM's active access to the Internet.
358    fn allow_outgoing(&self, vm: &mut Vm) -> Result<()>;
359
360    /// There needs NOT a reponsponding `unset_` method,
361    /// we can get an equal effect by clear the [net_blacklist](self::VmState::net_blacklist).
362    fn set_blacklist(&self, vm: &mut Vm) -> Result<()>;
363}
364
365/// This trait describes how to manage the 'runtime image'.
366pub trait Storage {
367    /// Create a runtime image from it's template.
368    fn create_image(&self, vm: &mut Vm) -> Result<()>;
369    /// Destroy a runtime image.
370    fn destroy_image(&self, vm: &mut Vm) -> Result<()>;
371
372    /// Add a snapshot for the runtime image.
373    fn create_snapshot(&self, vm: &mut Vm, snapshot: &Snapshot) -> Result<String>;
374    /// Delete a snapshot of the runtime image.
375    fn destroy_snapshot(&self, vm: &Vm, snapshot_path: &str) -> Result<()>;
376    /// Revert to the state of this snapshot.
377    fn apply_snapshot(&self, vm: &Vm, snapshot: &Snapshot) -> Result<()>;
378}