1 //! Abstraction layer over different methods of accessing a block backup
2 use std
::collections
::HashMap
;
3 use std
::future
::Future
;
4 use std
::hash
::BuildHasher
;
7 use anyhow
::{bail, Error}
;
8 use serde
::{Deserialize, Serialize}
;
9 use serde_json
::{json, Value}
;
11 use proxmox_router
::cli
::*;
12 use proxmox_schema
::api
;
14 use pbs_client
::BackupRepository
;
15 use pbs_datastore
::backup_info
::BackupDir
;
16 use pbs_datastore
::catalog
::ArchiveEntry
;
17 use pbs_datastore
::manifest
::BackupManifest
;
19 use super::block_driver_qemu
::QemuBlockDriver
;
21 /// Contains details about a snapshot that is to be accessed by block file restore
22 pub struct SnapRestoreDetails
{
23 pub repo
: BackupRepository
,
24 pub snapshot
: BackupDir
,
25 pub manifest
: BackupManifest
,
26 pub keyfile
: Option
<String
>,
29 /// Return value of a BlockRestoreDriver.status() call, 'id' must be valid for .stop(id)
30 pub struct DriverStatus
{
35 pub type Async
<R
> = Pin
<Box
<dyn Future
<Output
= R
> + Send
>>;
37 /// An abstract implementation for retrieving data out of a block file backup
38 pub trait BlockRestoreDriver
{
39 /// List ArchiveEntrys for the given image file and path
42 details
: SnapRestoreDetails
,
45 ) -> Async
<Result
<Vec
<ArchiveEntry
>, Error
>>;
48 /// Attempt to create a pxar archive of the given file path and return a reader instance for it
50 /// Attempt to read the file or folder at the given path and return the file content or a zip
54 details
: SnapRestoreDetails
,
58 ) -> Async
<Result
<Box
<dyn tokio
::io
::AsyncRead
+ Unpin
+ Send
>, Error
>>;
60 /// Return status of all running/mapped images, result value is (id, extra data), where id must
61 /// match with the ones returned from list()
62 fn status(&self) -> Async
<Result
<Vec
<DriverStatus
>, Error
>>;
63 /// Stop/Close a running restore method
64 fn stop(&self, id
: String
) -> Async
<Result
<(), Error
>>;
65 /// Returned ids must be prefixed with driver type so that they cannot collide between drivers,
66 /// the returned values must be passable to stop()
67 fn list(&self) -> Vec
<String
>;
71 #[derive(Debug, Serialize, Deserialize, PartialEq, Clone, Copy)]
72 pub enum BlockDriverType
{
73 /// Uses a small QEMU/KVM virtual machine to map images securely. Requires PVE-patched QEMU.
77 impl BlockDriverType
{
78 fn resolve(&self) -> impl BlockRestoreDriver
{
80 BlockDriverType
::Qemu
=> QemuBlockDriver {}
,
85 const DEFAULT_DRIVER
: BlockDriverType
= BlockDriverType
::Qemu
;
86 const ALL_DRIVERS
: &[BlockDriverType
] = &[BlockDriverType
::Qemu
];
88 pub async
fn data_list(
89 driver
: Option
<BlockDriverType
>,
90 details
: SnapRestoreDetails
,
93 ) -> Result
<Vec
<ArchiveEntry
>, Error
> {
94 let driver
= driver
.unwrap_or(DEFAULT_DRIVER
).resolve();
95 driver
.data_list(details
, img_file
, path
).await
98 pub async
fn data_extract(
99 driver
: Option
<BlockDriverType
>,
100 details
: SnapRestoreDetails
,
104 ) -> Result
<Box
<dyn tokio
::io
::AsyncRead
+ Send
+ Unpin
>, Error
> {
105 let driver
= driver
.unwrap_or(DEFAULT_DRIVER
).resolve();
106 driver
.data_extract(details
, img_file
, path
, pxar
).await
113 type: BlockDriverType
,
117 schema
: OUTPUT_FORMAT
,
123 /// Retrieve status information about currently running/mapped restore images
124 pub async
fn status(driver
: Option
<BlockDriverType
>, param
: Value
) -> Result
<(), Error
> {
125 let output_format
= get_output_format(¶m
);
126 let text
= output_format
== "text";
128 let mut ret
= json
!({}
);
130 for dt
in ALL_DRIVERS
{
131 if driver
.is_some() && &driver
.unwrap() != dt
{
135 let drv_name
= format
!("{:?}", dt
);
136 let drv
= dt
.resolve();
137 match drv
.status().await
{
138 Ok(data
) if data
.is_empty() => {
140 println
!("{}: no mappings", drv_name
);
142 ret
[drv_name
] = json
!({}
);
147 println
!("{}:", &drv_name
);
150 ret
[&drv_name
]["ids"] = json
!({}
);
153 println
!("{} \t({})", status
.id
, status
.data
);
155 ret
[&drv_name
]["ids"][status
.id
] = status
.data
;
161 eprintln
!("error getting status from driver '{}' - {}", drv_name
, err
);
163 ret
[drv_name
] = json
!({ "error": format!("{}
", err) });
170 format_and_print_result(&ret, &output_format);
181 description: "The name of the VM to stop
.",
186 /// Immediately stop/unmap a given image. Not typically necessary, as VMs will stop themselves
187 /// after a timer anyway.
188 pub async fn stop(name: String) -> Result<(), Error> {
189 for drv in ALL_DRIVERS.iter().map(BlockDriverType::resolve) {
190 if drv.list().contains(&name) {
191 return drv.stop(name).await;
195 bail!("no mapping with name '{}' found
", name);
198 /// Autocompletion handler for block mappings
199 pub fn complete_block_driver_ids<S: BuildHasher>(
201 _param: &HashMap<String, String, S>,
205 .map(BlockDriverType::resolve)