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