4 use serde_json
::{json, Value}
;
6 use proxmox_sys
::fs
::file_get_contents
;
7 use proxmox_router
::cli
::*;
8 use proxmox_schema
::api
;
10 use pbs_tools
::crypt_config
::CryptConfig
;
11 use pbs_config
::key_config
::decrypt_key
;
12 use pbs_api_types
::{SnapshotListItem, CryptMode}
;
13 use pbs_client
::tools
::key_source
::get_encryption_key_password
;
14 use pbs_datastore
::{DataBlob, BackupGroup}
;
15 use pbs_tools
::json
::required_string_param
;
22 api_datastore_list_snapshots
,
23 complete_backup_snapshot
,
24 complete_backup_group
,
28 extract_repository_from_value
,
36 schema
: REPO_URL_SCHEMA
,
41 description
: "Backup group.",
45 schema
: OUTPUT_FORMAT
,
51 /// List backup snapshots.
52 async
fn list_snapshots(param
: Value
) -> Result
<Value
, Error
> {
54 let repo
= extract_repository_from_value(¶m
)?
;
56 let output_format
= get_output_format(¶m
);
58 let client
= connect(&repo
)?
;
60 let group
: Option
<BackupGroup
> = if let Some(path
) = param
["group"].as_str() {
66 let mut data
= api_datastore_list_snapshots(&client
, repo
.store(), group
).await?
;
68 record_repository(&repo
);
70 let render_snapshot_path
= |_v
: &Value
, record
: &Value
| -> Result
<String
, Error
> {
71 let item
: SnapshotListItem
= serde_json
::from_value(record
.to_owned())?
;
72 let snapshot
= BackupDir
::new(item
.backup_type
, item
.backup_id
, item
.backup_time
)?
;
73 Ok(snapshot
.relative_path().to_str().unwrap().to_owned())
76 let render_files
= |_v
: &Value
, record
: &Value
| -> Result
<String
, Error
> {
77 let item
: SnapshotListItem
= serde_json
::from_value(record
.to_owned())?
;
78 let mut filenames
= Vec
::new();
79 for file
in &item
.files
{
80 filenames
.push(file
.filename
.to_string());
82 Ok(pbs_tools
::format
::render_backup_file_list(&filenames
[..]))
85 let options
= default_table_format_options()
86 .sortby("backup-type", false)
87 .sortby("backup-id", false)
88 .sortby("backup-time", false)
89 .column(ColumnConfig
::new("backup-id").renderer(render_snapshot_path
).header("snapshot"))
90 .column(ColumnConfig
::new("size").renderer(pbs_tools
::format
::render_bytes_human_readable
))
91 .column(ColumnConfig
::new("files").renderer(render_files
))
94 let return_type
= &pbs_api_types
::ADMIN_DATASTORE_LIST_SNAPSHOTS_RETURN_TYPE
;
96 format_and_print_result_full(&mut data
, return_type
, &output_format
, &options
);
105 schema
: REPO_URL_SCHEMA
,
110 description
: "Snapshot path.",
113 schema
: OUTPUT_FORMAT
,
119 /// List snapshot files.
120 async
fn list_snapshot_files(param
: Value
) -> Result
<Value
, Error
> {
122 let repo
= extract_repository_from_value(¶m
)?
;
124 let path
= required_string_param(¶m
, "snapshot")?
;
125 let snapshot
: BackupDir
= path
.parse()?
;
127 let output_format
= get_output_format(¶m
);
129 let client
= connect(&repo
)?
;
131 let path
= format
!("api2/json/admin/datastore/{}/files", repo
.store());
133 let mut result
= client
.get(&path
, Some(json
!({
134 "backup-type": snapshot
.group().backup_type(),
135 "backup-id": snapshot
.group().backup_id(),
136 "backup-time": snapshot
.backup_time(),
139 record_repository(&repo
);
141 let return_type
= &pbs_api_types
::ADMIN_DATASTORE_LIST_SNAPSHOT_FILES_RETURN_TYPE
;
143 let mut data
: Value
= result
["data"].take();
145 let options
= default_table_format_options();
147 format_and_print_result_full(&mut data
, return_type
, &output_format
, &options
);
156 schema
: REPO_URL_SCHEMA
,
161 description
: "Snapshot path.",
166 /// Forget (remove) backup snapshots.
167 async
fn forget_snapshots(param
: Value
) -> Result
<Value
, Error
> {
169 let repo
= extract_repository_from_value(¶m
)?
;
171 let path
= required_string_param(¶m
, "snapshot")?
;
172 let snapshot
: BackupDir
= path
.parse()?
;
174 let client
= connect(&repo
)?
;
176 let path
= format
!("api2/json/admin/datastore/{}/snapshots", repo
.store());
178 let result
= client
.delete(&path
, Some(json
!({
179 "backup-type": snapshot
.group().backup_type(),
180 "backup-id": snapshot
.group().backup_id(),
181 "backup-time": snapshot
.backup_time(),
184 record_repository(&repo
);
193 schema
: REPO_URL_SCHEMA
,
198 description
: "Group/Snapshot path.",
202 description
: "The path to the log file you want to upload.",
205 schema
: KEYFILE_SCHEMA
,
209 schema
: KEYFD_SCHEMA
,
219 /// Upload backup log file.
220 async
fn upload_log(param
: Value
) -> Result
<Value
, Error
> {
222 let logfile
= required_string_param(¶m
, "logfile")?
;
223 let repo
= extract_repository_from_value(¶m
)?
;
225 let snapshot
= required_string_param(¶m
, "snapshot")?
;
226 let snapshot
: BackupDir
= snapshot
.parse()?
;
228 let client
= connect(&repo
)?
;
230 let crypto
= crypto_parameters(¶m
)?
;
232 let crypt_config
= match crypto
.enc_key
{
235 let (key
, _created
, _
) = decrypt_key(&key
.key
, &get_encryption_key_password
)?
;
236 let crypt_config
= CryptConfig
::new(key
)?
;
237 Some(Arc
::new(crypt_config
))
241 let data
= file_get_contents(logfile
)?
;
243 // fixme: howto sign log?
244 let blob
= match crypto
.mode
{
245 CryptMode
::None
| CryptMode
::SignOnly
=> DataBlob
::encode(&data
, None
, true)?
,
246 CryptMode
::Encrypt
=> DataBlob
::encode(&data
, crypt_config
.as_ref().map(Arc
::as_ref
), true)?
,
249 let raw_data
= blob
.into_inner();
251 let path
= format
!("api2/json/admin/datastore/{}/upload-backup-log", repo
.store());
254 "backup-type": snapshot
.group().backup_type(),
255 "backup-id": snapshot
.group().backup_id(),
256 "backup-time": snapshot
.backup_time(),
259 let body
= hyper
::Body
::from(raw_data
);
261 client
.upload("application/octet-stream", body
, &path
, Some(args
)).await
268 schema
: REPO_URL_SCHEMA
,
273 description
: "Snapshot path.",
276 schema
: OUTPUT_FORMAT
,
283 async
fn show_notes(param
: Value
) -> Result
<Value
, Error
> {
284 let repo
= extract_repository_from_value(¶m
)?
;
285 let path
= required_string_param(¶m
, "snapshot")?
;
287 let snapshot
: BackupDir
= path
.parse()?
;
288 let client
= connect(&repo
)?
;
290 let path
= format
!("api2/json/admin/datastore/{}/notes", repo
.store());
293 "backup-type": snapshot
.group().backup_type(),
294 "backup-id": snapshot
.group().backup_id(),
295 "backup-time": snapshot
.backup_time(),
298 let output_format
= get_output_format(¶m
);
300 let mut result
= client
.get(&path
, Some(args
)).await?
;
302 let notes
= result
["data"].take();
304 if output_format
== "text" {
305 if let Some(notes
) = notes
.as_str() {
306 println
!("{}", notes
);
309 format_and_print_result(
324 schema
: REPO_URL_SCHEMA
,
329 description
: "Snapshot path.",
333 description
: "The Notes.",
339 async
fn update_notes(param
: Value
) -> Result
<Value
, Error
> {
340 let repo
= extract_repository_from_value(¶m
)?
;
341 let path
= required_string_param(¶m
, "snapshot")?
;
342 let notes
= required_string_param(¶m
, "notes")?
;
344 let snapshot
: BackupDir
= path
.parse()?
;
345 let client
= connect(&repo
)?
;
347 let path
= format
!("api2/json/admin/datastore/{}/notes", repo
.store());
350 "backup-type": snapshot
.group().backup_type(),
351 "backup-id": snapshot
.group().backup_id(),
352 "backup-time": snapshot
.backup_time(),
356 client
.put(&path
, Some(args
)).await?
;
365 schema
: REPO_URL_SCHEMA
,
370 description
: "Snapshot path.",
373 schema
: OUTPUT_FORMAT
,
379 /// Show protection status of the specified snapshot
380 async
fn show_protection(param
: Value
) -> Result
<(), Error
> {
381 let repo
= extract_repository_from_value(¶m
)?
;
382 let path
= required_string_param(¶m
, "snapshot")?
;
384 let snapshot
: BackupDir
= path
.parse()?
;
385 let client
= connect(&repo
)?
;
387 let path
= format
!("api2/json/admin/datastore/{}/protected", repo
.store());
390 "backup-type": snapshot
.group().backup_type(),
391 "backup-id": snapshot
.group().backup_id(),
392 "backup-time": snapshot
.backup_time(),
395 let output_format
= get_output_format(¶m
);
397 let mut result
= client
.get(&path
, Some(args
)).await?
;
399 let protected
= result
["data"].take();
401 if output_format
== "text" {
402 if let Some(protected
) = protected
.as_bool() {
403 println
!("{}", protected
);
406 format_and_print_result(
408 "protected": protected
,
421 schema
: REPO_URL_SCHEMA
,
426 description
: "Snapshot path.",
430 description
: "The protection status.",
435 /// Update Protection Status of a snapshot
436 async
fn update_protection(protected
: bool
, param
: Value
) -> Result
<(), Error
> {
437 let repo
= extract_repository_from_value(¶m
)?
;
438 let path
= required_string_param(¶m
, "snapshot")?
;
440 let snapshot
: BackupDir
= path
.parse()?
;
441 let client
= connect(&repo
)?
;
443 let path
= format
!("api2/json/admin/datastore/{}/protected", repo
.store());
446 "backup-type": snapshot
.group().backup_type(),
447 "backup-id": snapshot
.group().backup_id(),
448 "backup-time": snapshot
.backup_time(),
449 "protected": protected
,
452 client
.put(&path
, Some(args
)).await?
;
457 fn protected_cli() -> CliCommandMap
{
461 CliCommand
::new(&API_METHOD_SHOW_PROTECTION
)
462 .arg_param(&["snapshot"])
463 .completion_cb("snapshot", complete_backup_snapshot
),
467 CliCommand
::new(&API_METHOD_UPDATE_PROTECTION
)
468 .arg_param(&["snapshot", "protected"])
469 .completion_cb("snapshot", complete_backup_snapshot
),
473 fn notes_cli() -> CliCommandMap
{
477 CliCommand
::new(&API_METHOD_SHOW_NOTES
)
478 .arg_param(&["snapshot"])
479 .completion_cb("snapshot", complete_backup_snapshot
),
483 CliCommand
::new(&API_METHOD_UPDATE_NOTES
)
484 .arg_param(&["snapshot", "notes"])
485 .completion_cb("snapshot", complete_backup_snapshot
),
489 pub fn snapshot_mgtm_cli() -> CliCommandMap
{
491 .insert("notes", notes_cli())
492 .insert("protected", protected_cli())
495 CliCommand
::new(&API_METHOD_LIST_SNAPSHOTS
)
496 .arg_param(&["group"])
497 .completion_cb("group", complete_backup_group
)
498 .completion_cb("repository", complete_repository
)
502 CliCommand
::new(&API_METHOD_LIST_SNAPSHOT_FILES
)
503 .arg_param(&["snapshot"])
504 .completion_cb("repository", complete_repository
)
505 .completion_cb("snapshot", complete_backup_snapshot
)
509 CliCommand
::new(&API_METHOD_FORGET_SNAPSHOTS
)
510 .arg_param(&["snapshot"])
511 .completion_cb("repository", complete_repository
)
512 .completion_cb("snapshot", complete_backup_snapshot
)
516 CliCommand
::new(&API_METHOD_UPLOAD_LOG
)
517 .arg_param(&["snapshot", "logfile"])
518 .completion_cb("snapshot", complete_backup_snapshot
)
519 .completion_cb("logfile", complete_file_name
)
520 .completion_cb("keyfile", complete_file_name
)
521 .completion_cb("repository", complete_repository
)