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