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