xref: /cloud-hypervisor/vm-migration/src/lib.rs (revision f67b3f79ea19c9a66e04074cbbf5d292f6529e43)
1 // Copyright © 2019 Intel Corporation
2 //
3 // SPDX-License-Identifier: Apache-2.0 AND BSD-3-Clause
4 //
5 
6 #[macro_use]
7 extern crate serde_derive;
8 
9 use crate::protocol::MemoryRangeTable;
10 use anyhow::anyhow;
11 use serde::{Deserialize, Serialize};
12 use thiserror::Error;
13 use versionize::{VersionMap, Versionize};
14 
15 pub mod protocol;
16 
17 /// Global VMM version for versioning
18 const MAJOR_VERSION: u16 = 18;
19 const MINOR_VERSION: u16 = 0;
20 const VMM_VERSION: u16 = MAJOR_VERSION << 12 | MINOR_VERSION & 0b1111;
21 
22 pub trait VersionMapped {
23     fn version_map() -> VersionMap {
24         VersionMap::new()
25     }
26 }
27 
28 #[derive(Error, Debug)]
29 pub enum MigratableError {
30     #[error("Failed to pause migratable component: {0}")]
31     Pause(#[source] anyhow::Error),
32 
33     #[error("Failed to resume migratable component: {0}")]
34     Resume(#[source] anyhow::Error),
35 
36     #[error("Failed to snapshot migratable component: {0}")]
37     Snapshot(#[source] anyhow::Error),
38 
39     #[error("Failed to restore migratable component: {0}")]
40     Restore(#[source] anyhow::Error),
41 
42     #[error("Failed to send migratable component snapshot: {0}")]
43     MigrateSend(#[source] anyhow::Error),
44 
45     #[error("Failed to receive migratable component snapshot: {0}")]
46     MigrateReceive(#[source] anyhow::Error),
47 
48     #[error("Socket error: {0}")]
49     MigrateSocket(#[source] std::io::Error),
50 
51     #[error("Failed to start migration for migratable component: {0}")]
52     StartDirtyLog(#[source] anyhow::Error),
53 
54     #[error("Failed to stop migration for migratable component: {0}")]
55     StopDirtyLog(#[source] anyhow::Error),
56 
57     #[error("Failed to retrieve dirty ranges for migratable component: {0}")]
58     DirtyLog(#[source] anyhow::Error),
59 
60     #[error("Failed to complete migration for migratable component: {0}")]
61     CompleteMigration(#[source] anyhow::Error),
62 }
63 
64 /// A Pausable component can be paused and resumed.
65 pub trait Pausable {
66     /// Pause the component.
67     fn pause(&mut self) -> std::result::Result<(), MigratableError> {
68         Ok(())
69     }
70 
71     /// Resume the component.
72     fn resume(&mut self) -> std::result::Result<(), MigratableError> {
73         Ok(())
74     }
75 }
76 
77 /// A Snapshottable component snapshot section.
78 /// Migratable component can split their migration snapshot into
79 /// separate sections.
80 /// Splitting a component migration data into different sections
81 /// allows for easier and forward compatible extensions.
82 #[derive(Clone, Default, Deserialize, Serialize)]
83 pub struct SnapshotDataSection {
84     /// The section id.
85     pub id: String,
86 
87     /// The section serialized snapshot.
88     pub snapshot: Vec<u8>,
89 }
90 
91 impl SnapshotDataSection {
92     /// Generate the state data from the snapshot data
93     pub fn to_state<'a, T>(&'a self) -> Result<T, MigratableError>
94     where
95         T: Deserialize<'a>,
96     {
97         serde_json::from_slice(&self.snapshot).map_err(|e| {
98             MigratableError::Restore(anyhow!("Error deserialising: {} {}", self.id, e))
99         })
100     }
101 
102     /// Generate versioned state
103     pub fn to_versioned_state<T>(&self) -> Result<T, MigratableError>
104     where
105         T: Versionize + VersionMapped,
106     {
107         T::deserialize(
108             &mut self.snapshot.as_slice(),
109             &T::version_map(),
110             VMM_VERSION,
111         )
112         .map_err(|e| MigratableError::Restore(anyhow!("Error deserialising: {} {}", self.id, e)))
113     }
114 
115     /// Create from state that can be serialized
116     pub fn new_from_state<T>(id: &str, state: &T) -> Result<Self, MigratableError>
117     where
118         T: Serialize,
119     {
120         let snapshot = serde_json::to_vec(state)
121             .map_err(|e| MigratableError::Snapshot(anyhow!("Error serialising: {} {}", id, e)))?;
122 
123         let snapshot_data = SnapshotDataSection {
124             id: format!("{}-section", id),
125             snapshot,
126         };
127 
128         Ok(snapshot_data)
129     }
130 
131     /// Create from versioned state
132     pub fn new_from_versioned_state<T>(id: &str, state: &T) -> Result<Self, MigratableError>
133     where
134         T: Versionize + VersionMapped,
135     {
136         let mut snapshot = Vec::new();
137         state
138             .serialize(&mut snapshot, &T::version_map(), VMM_VERSION)
139             .map_err(|e| MigratableError::Snapshot(anyhow!("Error serialising: {} {}", id, e)))?;
140 
141         let snapshot_data = SnapshotDataSection {
142             id: format!("{}-section", id),
143             snapshot,
144         };
145 
146         Ok(snapshot_data)
147     }
148 }
149 
150 /// A Snapshottable component's snapshot is a tree of snapshots, where leafs
151 /// contain the snapshot data. Nodes of this tree track all their children
152 /// through the snapshots field, which is basically their sub-components.
153 /// Leaves will typically have an empty snapshots map, while nodes usually
154 /// carry an empty snapshot_data.
155 ///
156 /// For example, a device manager snapshot is the composition of all its
157 /// devices snapshots. The device manager Snapshot would have no snapshot_data
158 /// but one Snapshot child per tracked device. Then each device's Snapshot
159 /// would carry an empty snapshots map but a map of SnapshotDataSection, i.e.
160 /// the actual device snapshot data.
161 #[derive(Clone, Default, Deserialize, Serialize)]
162 pub struct Snapshot {
163     /// The Snapshottable component id.
164     pub id: String,
165 
166     /// The Snapshottable component snapshots.
167     pub snapshots: std::collections::BTreeMap<String, Box<Snapshot>>,
168 
169     /// The Snapshottable component's snapshot data.
170     /// A map of snapshot sections, indexed by the section ids.
171     pub snapshot_data: std::collections::HashMap<String, SnapshotDataSection>,
172 }
173 
174 impl Snapshot {
175     /// Create an empty Snapshot.
176     pub fn new(id: &str) -> Self {
177         Snapshot {
178             id: id.to_string(),
179             ..Default::default()
180         }
181     }
182 
183     /// Create from state that can be serialized
184     pub fn new_from_state<T>(id: &str, state: &T) -> Result<Self, MigratableError>
185     where
186         T: Serialize,
187     {
188         let mut snapshot_data = Snapshot::new(id);
189         snapshot_data.add_data_section(SnapshotDataSection::new_from_state(id, state)?);
190 
191         Ok(snapshot_data)
192     }
193 
194     /// Create from versioned state
195     pub fn new_from_versioned_state<T>(id: &str, state: &T) -> Result<Self, MigratableError>
196     where
197         T: Versionize + VersionMapped,
198     {
199         let mut snapshot_data = Snapshot::new(id);
200         snapshot_data.add_data_section(SnapshotDataSection::new_from_versioned_state(id, state)?);
201 
202         Ok(snapshot_data)
203     }
204 
205     /// Add a sub-component's Snapshot to the Snapshot.
206     pub fn add_snapshot(&mut self, snapshot: Snapshot) {
207         self.snapshots
208             .insert(snapshot.id.clone(), Box::new(snapshot));
209     }
210 
211     /// Add a SnapshotDatasection to the component snapshot data.
212     pub fn add_data_section(&mut self, section: SnapshotDataSection) {
213         self.snapshot_data.insert(section.id.clone(), section);
214     }
215 
216     /// Generate the state data from the snapshot
217     pub fn to_state<'a, T>(&'a self, id: &str) -> Result<T, MigratableError>
218     where
219         T: Deserialize<'a>,
220     {
221         self.snapshot_data
222             .get(&format!("{}-section", id))
223             .ok_or_else(|| MigratableError::Restore(anyhow!("Missing section for {}", id)))?
224             .to_state()
225     }
226 
227     /// Generate versioned state
228     pub fn to_versioned_state<T>(&self, id: &str) -> Result<T, MigratableError>
229     where
230         T: Versionize + VersionMapped,
231     {
232         self.snapshot_data
233             .get(&format!("{}-section", id))
234             .ok_or_else(|| MigratableError::Restore(anyhow!("Missing section for {}", id)))?
235             .to_versioned_state()
236     }
237 }
238 
239 /// A snapshottable component can be snapshotted.
240 pub trait Snapshottable: Pausable {
241     /// The snapshottable component id.
242     fn id(&self) -> String {
243         String::new()
244     }
245 
246     /// Take a component snapshot.
247     fn snapshot(&mut self) -> std::result::Result<Snapshot, MigratableError> {
248         Ok(Snapshot::new(""))
249     }
250 
251     /// Restore a component from its snapshot.
252     fn restore(&mut self, _snapshot: Snapshot) -> std::result::Result<(), MigratableError> {
253         Ok(())
254     }
255 }
256 
257 /// A transportable component can be sent or receive to a specific URL.
258 ///
259 /// This trait is meant to be used for component that have custom
260 /// transport handlers.
261 pub trait Transportable: Pausable + Snapshottable {
262     /// Send a component snapshot.
263     ///
264     /// # Arguments
265     ///
266     /// * `snapshot` - The migratable component snapshot to send.
267     /// * `destination_url` - The destination URL to send the snapshot to. This
268     ///                       could be an HTTP endpoint, a TCP address or a local file.
269     fn send(
270         &self,
271         _snapshot: &Snapshot,
272         _destination_url: &str,
273     ) -> std::result::Result<(), MigratableError> {
274         Ok(())
275     }
276 
277     /// Receive a component snapshot.
278     ///
279     /// # Arguments
280     ///
281     /// * `source_url` - The source URL to fetch the snapshot from. This could be an HTTP
282     ///                  endpoint, a TCP address or a local file.
283     fn recv(&self, _source_url: &str) -> std::result::Result<Snapshot, MigratableError> {
284         Ok(Snapshot::new(""))
285     }
286 }
287 
288 /// Trait to be implemented by any component (device, CPU, RAM, etc) that
289 /// can be migrated.
290 /// All migratable components are paused before being snapshotted, and then
291 /// eventually resumed. Thus any Migratable component must be both Pausable
292 /// and Snapshottable.
293 /// Moreover a migratable component can be transported to a remote or local
294 /// destination and thus must be Transportable.
295 pub trait Migratable: Send + Pausable + Snapshottable + Transportable {
296     fn start_dirty_log(&mut self) -> std::result::Result<(), MigratableError> {
297         Ok(())
298     }
299 
300     fn stop_dirty_log(&mut self) -> std::result::Result<(), MigratableError> {
301         Ok(())
302     }
303 
304     fn dirty_log(&mut self) -> std::result::Result<MemoryRangeTable, MigratableError> {
305         Ok(MemoryRangeTable::default())
306     }
307 
308     fn complete_migration(&mut self) -> std::result::Result<(), MigratableError> {
309         Ok(())
310     }
311 }
312