4 use serde_json
::{json, Value}
;
8 tools
::fs
::file_get_contents
,
11 use pbs_api_types
::SnapshotListItem
;
12 use pbs_client
::tools
::key_source
::get_encryption_key_password
;
13 use pbs_datastore
::{BackupGroup, CryptMode, CryptConfig, decrypt_key}
;
14 use pbs_datastore
::data_blob
::DataBlob
;
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 mut 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 mut 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 mut 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?
;
361 fn notes_cli() -> CliCommandMap
{
365 CliCommand
::new(&API_METHOD_SHOW_NOTES
)
366 .arg_param(&["snapshot"])
367 .completion_cb("snapshot", complete_backup_snapshot
),
371 CliCommand
::new(&API_METHOD_UPDATE_NOTES
)
372 .arg_param(&["snapshot", "notes"])
373 .completion_cb("snapshot", complete_backup_snapshot
),
377 pub fn snapshot_mgtm_cli() -> CliCommandMap
{
379 .insert("notes", notes_cli())
382 CliCommand
::new(&API_METHOD_LIST_SNAPSHOTS
)
383 .arg_param(&["group"])
384 .completion_cb("group", complete_backup_group
)
385 .completion_cb("repository", complete_repository
)
389 CliCommand
::new(&API_METHOD_LIST_SNAPSHOT_FILES
)
390 .arg_param(&["snapshot"])
391 .completion_cb("repository", complete_repository
)
392 .completion_cb("snapshot", complete_backup_snapshot
)
396 CliCommand
::new(&API_METHOD_FORGET_SNAPSHOTS
)
397 .arg_param(&["snapshot"])
398 .completion_cb("repository", complete_repository
)
399 .completion_cb("snapshot", complete_backup_snapshot
)
403 CliCommand
::new(&API_METHOD_UPLOAD_LOG
)
404 .arg_param(&["snapshot", "logfile"])
405 .completion_cb("snapshot", complete_backup_snapshot
)
406 .completion_cb("logfile", pbs_tools
::fs
::complete_file_name
)
407 .completion_cb("keyfile", pbs_tools
::fs
::complete_file_name
)
408 .completion_cb("repository", complete_repository
)