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