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