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