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