1 // Copyright © 2019 Intel Corporation 2 // 3 // SPDX-License-Identifier: Apache-2.0 AND BSD-3-Clause 4 // 5 6 #[macro_use] 7 extern crate serde_derive; 8 9 use anyhow::anyhow; 10 use serde::{Deserialize, Serialize}; 11 use thiserror::Error; 12 use versionize::{VersionMap, Versionize}; 13 14 pub mod protocol; 15 16 /// Global VMM version for versioning 17 const MAJOR_VERSION: u16 = 16; 18 const MINOR_VERSION: u16 = 0; 19 const VMM_VERSION: u16 = MAJOR_VERSION << 12 | MINOR_VERSION & 0b1111; 20 21 pub trait VersionMapped { 22 fn version_map() -> VersionMap { 23 VersionMap::new() 24 } 25 } 26 27 #[derive(Error, Debug)] 28 pub enum MigratableError { 29 #[error("Failed to pause migratable component: {0}")] 30 Pause(#[source] anyhow::Error), 31 32 #[error("Failed to resume migratable component: {0}")] 33 Resume(#[source] anyhow::Error), 34 35 #[error("Failed to snapshot migratable component: {0}")] 36 Snapshot(#[source] anyhow::Error), 37 38 #[error("Failed to restore migratable component: {0}")] 39 Restore(#[source] anyhow::Error), 40 41 #[error("Failed to send migratable component snapshot: {0}")] 42 MigrateSend(#[source] anyhow::Error), 43 44 #[error("Failed to receive migratable component snapshot: {0}")] 45 MigrateReceive(#[source] anyhow::Error), 46 47 #[error("Socket error: {0}")] 48 MigrateSocket(#[source] std::io::Error), 49 } 50 51 /// A Pausable component can be paused and resumed. 52 pub trait Pausable { 53 /// Pause the component. 54 fn pause(&mut self) -> std::result::Result<(), MigratableError> { 55 Ok(()) 56 } 57 58 /// Resume the component. 59 fn resume(&mut self) -> std::result::Result<(), MigratableError> { 60 Ok(()) 61 } 62 } 63 64 /// A Snapshottable component snapshot section. 65 /// Migratable component can split their migration snapshot into 66 /// separate sections. 67 /// Splitting a component migration data into different sections 68 /// allows for easier and forward compatible extensions. 69 #[derive(Clone, Default, Deserialize, Serialize)] 70 pub struct SnapshotDataSection { 71 /// The section id. 72 pub id: String, 73 74 /// The section serialized snapshot. 75 pub snapshot: Vec<u8>, 76 } 77 78 impl SnapshotDataSection { 79 /// Generate the state data from the snapshot data 80 pub fn to_state<'a, T>(&'a self) -> Result<T, MigratableError> 81 where 82 T: Deserialize<'a>, 83 { 84 serde_json::from_slice(&self.snapshot).map_err(|e| { 85 MigratableError::Restore(anyhow!("Error deserialising: {} {}", self.id, e)) 86 }) 87 } 88 89 /// Generate versioned state 90 pub fn to_versioned_state<T>(&self) -> Result<T, MigratableError> 91 where 92 T: Versionize + VersionMapped, 93 { 94 T::deserialize( 95 &mut self.snapshot.as_slice(), 96 &T::version_map(), 97 VMM_VERSION, 98 ) 99 .map_err(|e| MigratableError::Restore(anyhow!("Error deserialising: {} {}", self.id, e))) 100 } 101 102 /// Create from state that can be serialized 103 pub fn new_from_state<T>(id: &str, state: &T) -> Result<Self, MigratableError> 104 where 105 T: Serialize, 106 { 107 let snapshot = serde_json::to_vec(state) 108 .map_err(|e| MigratableError::Snapshot(anyhow!("Error serialising: {} {}", id, e)))?; 109 110 let snapshot_data = SnapshotDataSection { 111 id: format!("{}-section", id), 112 snapshot, 113 }; 114 115 Ok(snapshot_data) 116 } 117 118 /// Create from versioned state 119 pub fn new_from_versioned_state<T>(id: &str, state: &T) -> Result<Self, MigratableError> 120 where 121 T: Versionize + VersionMapped, 122 { 123 let mut snapshot = Vec::new(); 124 state 125 .serialize(&mut snapshot, &T::version_map(), VMM_VERSION) 126 .map_err(|e| MigratableError::Snapshot(anyhow!("Error serialising: {} {}", id, e)))?; 127 128 let snapshot_data = SnapshotDataSection { 129 id: format!("{}-section", id), 130 snapshot, 131 }; 132 133 Ok(snapshot_data) 134 } 135 } 136 137 /// A Snapshottable component's snapshot is a tree of snapshots, where leafs 138 /// contain the snapshot data. Nodes of this tree track all their children 139 /// through the snapshots field, which is basically their sub-components. 140 /// Leaves will typically have an empty snapshots map, while nodes usually 141 /// carry an empty snapshot_data. 142 /// 143 /// For example, a device manager snapshot is the composition of all its 144 /// devices snapshots. The device manager Snapshot would have no snapshot_data 145 /// but one Snapshot child per tracked device. Then each device's Snapshot 146 /// would carry an empty snapshots map but a map of SnapshotDataSection, i.e. 147 /// the actual device snapshot data. 148 #[derive(Clone, Default, Deserialize, Serialize)] 149 pub struct Snapshot { 150 /// The Snapshottable component id. 151 pub id: String, 152 153 /// The Snapshottable component snapshots. 154 pub snapshots: std::collections::BTreeMap<String, Box<Snapshot>>, 155 156 /// The Snapshottable component's snapshot data. 157 /// A map of snapshot sections, indexed by the section ids. 158 pub snapshot_data: std::collections::HashMap<String, SnapshotDataSection>, 159 } 160 161 impl Snapshot { 162 /// Create an empty Snapshot. 163 pub fn new(id: &str) -> Self { 164 Snapshot { 165 id: id.to_string(), 166 ..Default::default() 167 } 168 } 169 170 /// Create from state that can be serialized 171 pub fn new_from_state<T>(id: &str, state: &T) -> Result<Self, MigratableError> 172 where 173 T: Serialize, 174 { 175 let mut snapshot_data = Snapshot::new(id); 176 snapshot_data.add_data_section(SnapshotDataSection::new_from_state(id, state)?); 177 178 Ok(snapshot_data) 179 } 180 181 /// Create from versioned state 182 pub fn new_from_versioned_state<T>(id: &str, state: &T) -> Result<Self, MigratableError> 183 where 184 T: Versionize + VersionMapped, 185 { 186 let mut snapshot_data = Snapshot::new(id); 187 snapshot_data.add_data_section(SnapshotDataSection::new_from_versioned_state(id, state)?); 188 189 Ok(snapshot_data) 190 } 191 192 /// Add a sub-component's Snapshot to the Snapshot. 193 pub fn add_snapshot(&mut self, snapshot: Snapshot) { 194 self.snapshots 195 .insert(snapshot.id.clone(), Box::new(snapshot)); 196 } 197 198 /// Add a SnapshotDatasection to the component snapshot data. 199 pub fn add_data_section(&mut self, section: SnapshotDataSection) { 200 self.snapshot_data.insert(section.id.clone(), section); 201 } 202 203 /// Generate the state data from the snapshot 204 pub fn to_state<'a, T>(&'a self, id: &str) -> Result<T, MigratableError> 205 where 206 T: Deserialize<'a>, 207 { 208 self.snapshot_data 209 .get(&format!("{}-section", id)) 210 .ok_or_else(|| MigratableError::Restore(anyhow!("Missing section for {}", id)))? 211 .to_state() 212 } 213 214 /// Generate versioned state 215 pub fn to_versioned_state<T>(&self, id: &str) -> Result<T, MigratableError> 216 where 217 T: Versionize + VersionMapped, 218 { 219 self.snapshot_data 220 .get(&format!("{}-section", id)) 221 .ok_or_else(|| MigratableError::Restore(anyhow!("Missing section for {}", id)))? 222 .to_versioned_state() 223 } 224 } 225 226 /// A snapshottable component can be snapshotted. 227 pub trait Snapshottable: Pausable { 228 /// The snapshottable component id. 229 fn id(&self) -> String { 230 String::new() 231 } 232 233 /// Take a component snapshot. 234 fn snapshot(&mut self) -> std::result::Result<Snapshot, MigratableError> { 235 Ok(Snapshot::new("")) 236 } 237 238 /// Restore a component from its snapshot. 239 fn restore(&mut self, _snapshot: Snapshot) -> std::result::Result<(), MigratableError> { 240 Ok(()) 241 } 242 } 243 244 /// A transportable component can be sent or receive to a specific URL. 245 /// 246 /// This trait is meant to be used for component that have custom 247 /// transport handlers. 248 pub trait Transportable: Pausable + Snapshottable { 249 /// Send a component snapshot. 250 /// 251 /// # Arguments 252 /// 253 /// * `snapshot` - The migratable component snapshot to send. 254 /// * `destination_url` - The destination URL to send the snapshot to. This 255 /// could be an HTTP endpoint, a TCP address or a local file. 256 fn send( 257 &self, 258 _snapshot: &Snapshot, 259 _destination_url: &str, 260 ) -> std::result::Result<(), MigratableError> { 261 Ok(()) 262 } 263 264 /// Receive a component snapshot. 265 /// 266 /// # Arguments 267 /// 268 /// * `source_url` - The source URL to fetch the snapshot from. This could be an HTTP 269 /// endpoint, a TCP address or a local file. 270 fn recv(&self, _source_url: &str) -> std::result::Result<Snapshot, MigratableError> { 271 Ok(Snapshot::new("")) 272 } 273 } 274 275 /// Trait to be implemented by any component (device, CPU, RAM, etc) that 276 /// can be migrated. 277 /// All migratable components are paused before being snapshotted, and then 278 /// eventually resumed. Thus any Migratable component must be both Pausable 279 /// and Snapshottable. 280 /// Moreover a migratable component can be transported to a remote or local 281 /// destination and thus must be Transportable. 282 pub trait Migratable: Send + Pausable + Snapshottable + Transportable {} 283