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")]
17 Pause(#[source] anyhow::Error),
18
19 #[error("Failed to resume migratable component")]
20 Resume(#[source] anyhow::Error),
21
22 #[error("Failed to snapshot migratable component")]
23 Snapshot(#[source] anyhow::Error),
24
25 #[error("Failed to restore migratable component")]
26 Restore(#[source] anyhow::Error),
27
28 #[error("Failed to send migratable component snapshot")]
29 MigrateSend(#[source] anyhow::Error),
30
31 #[error("Failed to receive migratable component snapshot")]
32 MigrateReceive(#[source] anyhow::Error),
33
34 #[error("Socket error")]
35 MigrateSocket(#[source] std::io::Error),
36
37 #[error("Failed to start migration for migratable component")]
38 StartDirtyLog(#[source] anyhow::Error),
39
40 #[error("Failed to stop migration for migratable component")]
41 StopDirtyLog(#[source] anyhow::Error),
42
43 #[error("Failed to retrieve dirty ranges for migratable component")]
44 DirtyLog(#[source] anyhow::Error),
45
46 #[error("Failed to start migration for migratable component")]
47 StartMigration(#[source] anyhow::Error),
48
49 #[error("Failed to complete migration for migratable component")]
50 CompleteMigration(#[source] anyhow::Error),
51
52 #[error("Failed to release a disk lock before the migration")]
53 UnlockError(#[source] anyhow::Error),
54 }
55
56 /// A Pausable component can be paused and resumed.
57 pub trait Pausable {
58 /// Pause the component.
pause(&mut self) -> std::result::Result<(), MigratableError>59 fn pause(&mut self) -> std::result::Result<(), MigratableError> {
60 Ok(())
61 }
62
63 /// Resume the component.
resume(&mut self) -> std::result::Result<(), MigratableError>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
to_state<'a, T>(&'a self) -> Result<T, MigratableError> where T: Deserialize<'a>,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
new_from_state<T>(state: &T) -> Result<Self, MigratableError> where T: Serialize,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 leaves
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 {
from_data(data: SnapshotData) -> Self126 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
new_from_state<T>(state: &T) -> Result<Self, MigratableError> where T: Serialize,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.
add_snapshot(&mut self, id: String, snapshot: 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
to_state<'a, T>(&'a self) -> Result<T, MigratableError> where T: Deserialize<'a>,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
snapshot_from_id(snapshot: Option<&Snapshot>, id: &str) -> Option<Snapshot>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
state_from_id<'a, T>(s: Option<&'a Snapshot>, id: &str) -> Result<Option<T>, MigratableError> where T: Deserialize<'a>,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.
id(&self) -> String176 fn id(&self) -> String {
177 String::new()
178 }
179
180 /// Take a component snapshot.
snapshot(&mut self) -> std::result::Result<Snapshot, MigratableError>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.
send( &self, _snapshot: &Snapshot, _destination_url: &str, ) -> std::result::Result<(), MigratableError>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.
recv(&self, _source_url: &str) -> std::result::Result<Snapshot, MigratableError>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 {
start_dirty_log(&mut self) -> std::result::Result<(), MigratableError>226 fn start_dirty_log(&mut self) -> std::result::Result<(), MigratableError> {
227 Ok(())
228 }
229
stop_dirty_log(&mut self) -> std::result::Result<(), MigratableError>230 fn stop_dirty_log(&mut self) -> std::result::Result<(), MigratableError> {
231 Ok(())
232 }
233
dirty_log(&mut self) -> std::result::Result<MemoryRangeTable, MigratableError>234 fn dirty_log(&mut self) -> std::result::Result<MemoryRangeTable, MigratableError> {
235 Ok(MemoryRangeTable::default())
236 }
237
start_migration(&mut self) -> std::result::Result<(), MigratableError>238 fn start_migration(&mut self) -> std::result::Result<(), MigratableError> {
239 Ok(())
240 }
241
complete_migration(&mut self) -> std::result::Result<(), MigratableError>242 fn complete_migration(&mut self) -> std::result::Result<(), MigratableError> {
243 Ok(())
244 }
245 }
246