xref: /cloud-hypervisor/vmm/src/migration.rs (revision 80b2c98a68d4c68f372f849e8d26f7cae5867000)
1 // Copyright © 2020 Intel Corporation
2 //
3 // SPDX-License-Identifier: Apache-2.0
4 
5 use std::fs::File;
6 use std::io::Read;
7 use std::path::PathBuf;
8 
9 use anyhow::anyhow;
10 use vm_migration::{MigratableError, Snapshot};
11 
12 #[cfg(all(target_arch = "x86_64", feature = "guest_debug"))]
13 use crate::coredump::GuestDebuggableError;
14 use crate::vm::VmSnapshot;
15 use crate::vm_config::VmConfig;
16 
17 pub const SNAPSHOT_STATE_FILE: &str = "state.json";
18 pub const SNAPSHOT_CONFIG_FILE: &str = "config.json";
19 
20 pub fn url_to_path(url: &str) -> std::result::Result<PathBuf, MigratableError> {
21     let path: PathBuf = url
22         .strip_prefix("file://")
23         .ok_or_else(|| {
24             MigratableError::MigrateSend(anyhow!("Could not extract path from URL: {}", url))
25         })
26         .map(|s| s.into())?;
27 
28     if !path.is_dir() {
29         return Err(MigratableError::MigrateSend(anyhow!(
30             "Destination is not a directory"
31         )));
32     }
33 
34     Ok(path)
35 }
36 
37 #[cfg(all(target_arch = "x86_64", feature = "guest_debug"))]
38 pub fn url_to_file(url: &str) -> std::result::Result<PathBuf, GuestDebuggableError> {
39     let file: PathBuf = url
40         .strip_prefix("file://")
41         .ok_or_else(|| {
42             GuestDebuggableError::Coredump(anyhow!("Could not extract file from URL: {}", url))
43         })
44         .map(|s| s.into())?;
45 
46     Ok(file)
47 }
48 
49 pub fn recv_vm_config(source_url: &str) -> std::result::Result<VmConfig, MigratableError> {
50     let mut vm_config_path = url_to_path(source_url)?;
51 
52     vm_config_path.push(SNAPSHOT_CONFIG_FILE);
53 
54     // Try opening the snapshot file
55     let mut vm_config_file =
56         File::open(vm_config_path).map_err(|e| MigratableError::MigrateReceive(e.into()))?;
57     let mut bytes = Vec::new();
58     vm_config_file
59         .read_to_end(&mut bytes)
60         .map_err(|e| MigratableError::MigrateReceive(e.into()))?;
61 
62     serde_json::from_slice(&bytes).map_err(|e| MigratableError::MigrateReceive(e.into()))
63 }
64 
65 pub fn recv_vm_state(source_url: &str) -> std::result::Result<Snapshot, MigratableError> {
66     let mut vm_state_path = url_to_path(source_url)?;
67 
68     vm_state_path.push(SNAPSHOT_STATE_FILE);
69 
70     // Try opening the snapshot file
71     let mut vm_state_file =
72         File::open(vm_state_path).map_err(|e| MigratableError::MigrateReceive(e.into()))?;
73     let mut bytes = Vec::new();
74     vm_state_file
75         .read_to_end(&mut bytes)
76         .map_err(|e| MigratableError::MigrateReceive(e.into()))?;
77 
78     serde_json::from_slice(&bytes).map_err(|e| MigratableError::MigrateReceive(e.into()))
79 }
80 
81 pub fn get_vm_snapshot(snapshot: &Snapshot) -> std::result::Result<VmSnapshot, MigratableError> {
82     if let Some(snapshot_data) = snapshot.snapshot_data.as_ref() {
83         return snapshot_data.to_state();
84     }
85 
86     Err(MigratableError::Restore(anyhow!(
87         "Could not find VM config snapshot section"
88     )))
89 }
90