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}