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