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