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 /// Migratable component can split their migration snapshot into 67 /// separate sections. 68 /// Splitting a component migration data into different sections 69 /// allows for easier and forward compatible extensions. 70 #[derive(Clone, Default, Deserialize, Serialize)] 71 pub struct SnapshotData { 72 state: String, 73 } 74 75 impl SnapshotData { 76 /// Generate the state data from the snapshot data 77 pub fn to_state<'a, T>(&'a self) -> Result<T, MigratableError> 78 where 79 T: Deserialize<'a>, 80 { 81 serde_json::from_str(&self.state) 82 .map_err(|e| MigratableError::Restore(anyhow!("Error deserialising: {}", e))) 83 } 84 85 /// Create from state that can be serialized 86 pub fn new_from_state<T>(state: &T) -> Result<Self, MigratableError> 87 where 88 T: Serialize, 89 { 90 let state = serde_json::to_string(state) 91 .map_err(|e| MigratableError::Snapshot(anyhow!("Error serialising: {}", e)))?; 92 93 Ok(SnapshotData { state }) 94 } 95 } 96 97 /// A Snapshottable component's snapshot is a tree of snapshots, where leafs 98 /// contain the snapshot data. Nodes of this tree track all their children 99 /// through the snapshots field, which is basically their sub-components. 100 /// Leaves will typically have an empty snapshots map, while nodes usually 101 /// carry an empty snapshot_data. 102 /// 103 /// For example, a device manager snapshot is the composition of all its 104 /// devices snapshots. The device manager Snapshot would have no snapshot_data 105 /// but one Snapshot child per tracked device. Then each device's Snapshot 106 /// would carry an empty snapshots map but a map of SnapshotData, i.e. 107 /// the actual device snapshot data. 108 #[derive(Clone, Default, Deserialize, Serialize)] 109 pub struct Snapshot { 110 /// The Snapshottable component snapshots. 111 pub snapshots: std::collections::BTreeMap<String, Snapshot>, 112 113 /// The Snapshottable component's snapshot data. 114 /// A map of snapshot sections, indexed by the section ids. 115 pub snapshot_data: Option<SnapshotData>, 116 } 117 118 impl Snapshot { 119 pub fn from_data(data: SnapshotData) -> Self { 120 Snapshot { 121 snapshot_data: Some(data), 122 ..Default::default() 123 } 124 } 125 126 /// Create from state that can be serialized 127 pub fn new_from_state<T>(state: &T) -> Result<Self, MigratableError> 128 where 129 T: Serialize, 130 { 131 Ok(Snapshot::from_data(SnapshotData::new_from_state(state)?)) 132 } 133 134 /// Add a sub-component's Snapshot to the Snapshot. 135 pub fn add_snapshot(&mut self, id: String, snapshot: Snapshot) { 136 self.snapshots.insert(id, snapshot); 137 } 138 139 /// Generate the state data from the snapshot 140 pub fn to_state<'a, T>(&'a self) -> Result<T, MigratableError> 141 where 142 T: Deserialize<'a>, 143 { 144 self.snapshot_data 145 .as_ref() 146 .ok_or_else(|| MigratableError::Restore(anyhow!("Missing snapshot data")))? 147 .to_state() 148 } 149 } 150 151 pub fn snapshot_from_id(snapshot: Option<&Snapshot>, id: &str) -> Option<Snapshot> { 152 snapshot.and_then(|s| s.snapshots.get(id).cloned()) 153 } 154 155 pub fn state_from_id<'a, T>(s: Option<&'a Snapshot>, id: &str) -> Result<Option<T>, MigratableError> 156 where 157 T: Deserialize<'a>, 158 { 159 if let Some(s) = s.as_ref() { 160 s.snapshots.get(id).map(|s| s.to_state()).transpose() 161 } else { 162 Ok(None) 163 } 164 } 165 166 /// A snapshottable component can be snapshotted. 167 pub trait Snapshottable: Pausable { 168 /// The snapshottable component id. 169 fn id(&self) -> String { 170 String::new() 171 } 172 173 /// Take a component snapshot. 174 fn snapshot(&mut self) -> std::result::Result<Snapshot, MigratableError> { 175 Ok(Snapshot::default()) 176 } 177 } 178 179 /// A transportable component can be sent or receive to a specific URL. 180 /// 181 /// This trait is meant to be used for component that have custom 182 /// transport handlers. 183 pub trait Transportable: Pausable + Snapshottable { 184 /// Send a component snapshot. 185 /// 186 /// # Arguments 187 /// 188 /// * `snapshot` - The migratable component snapshot to send. 189 /// * `destination_url` - The destination URL to send the snapshot to. This 190 /// could be an HTTP endpoint, a TCP address or a local file. 191 fn send( 192 &self, 193 _snapshot: &Snapshot, 194 _destination_url: &str, 195 ) -> std::result::Result<(), MigratableError> { 196 Ok(()) 197 } 198 199 /// Receive a component snapshot. 200 /// 201 /// # Arguments 202 /// 203 /// * `source_url` - The source URL to fetch the snapshot from. This could be an HTTP 204 /// endpoint, a TCP address or a local file. 205 fn recv(&self, _source_url: &str) -> std::result::Result<Snapshot, MigratableError> { 206 Ok(Snapshot::default()) 207 } 208 } 209 210 /// Trait to be implemented by any component (device, CPU, RAM, etc) that 211 /// can be migrated. 212 /// All migratable components are paused before being snapshotted, and then 213 /// eventually resumed. Thus any Migratable component must be both Pausable 214 /// and Snapshottable. 215 /// Moreover a migratable component can be transported to a remote or local 216 /// destination and thus must be Transportable. 217 pub trait Migratable: Send + Pausable + Snapshottable + Transportable { 218 fn start_dirty_log(&mut self) -> std::result::Result<(), MigratableError> { 219 Ok(()) 220 } 221 222 fn stop_dirty_log(&mut self) -> std::result::Result<(), MigratableError> { 223 Ok(()) 224 } 225 226 fn dirty_log(&mut self) -> std::result::Result<MemoryRangeTable, MigratableError> { 227 Ok(MemoryRangeTable::default()) 228 } 229 230 fn start_migration(&mut self) -> std::result::Result<(), MigratableError> { 231 Ok(()) 232 } 233 234 fn complete_migration(&mut self) -> std::result::Result<(), MigratableError> { 235 Ok(()) 236 } 237 } 238