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