1 use std
::collections
::HashMap
;
2 use std
::io
::{self, Write}
;
4 use anyhow
::{format_err, Error}
;
5 use serde_json
::{json, Value}
;
7 use proxmox
::api
::{api, cli::*, RpcEnvironment}
;
9 use proxmox_backup
::tools
;
10 use proxmox_backup
::config
;
11 use proxmox_backup
::api2
::{self, types::* }
;
12 use proxmox_backup
::client
::*;
14 mod proxmox_backup_manager
;
15 use proxmox_backup_manager
::*;
21 schema
: DATASTORE_SCHEMA
,
24 schema
: OUTPUT_FORMAT
,
30 /// Start garbage collection for a specific datastore.
31 async
fn start_garbage_collection(param
: Value
) -> Result
<Value
, Error
> {
33 let output_format
= get_output_format(¶m
);
35 let store
= tools
::required_string_param(¶m
, "store")?
;
37 let mut client
= connect_to_localhost()?
;
39 let path
= format
!("api2/json/admin/datastore/{}/gc", store
);
41 let result
= client
.post(&path
, None
).await?
;
43 view_task_result(&mut client
, result
, &output_format
).await?
;
52 schema
: DATASTORE_SCHEMA
,
55 schema
: OUTPUT_FORMAT
,
61 /// Show garbage collection status for a specific datastore.
62 async
fn garbage_collection_status(param
: Value
) -> Result
<Value
, Error
> {
64 let output_format
= get_output_format(¶m
);
66 let store
= tools
::required_string_param(¶m
, "store")?
;
68 let client
= connect_to_localhost()?
;
70 let path
= format
!("api2/json/admin/datastore/{}/gc", store
);
72 let mut result
= client
.get(&path
, None
).await?
;
73 let mut data
= result
["data"].take();
74 let return_type
= &api2
::admin
::datastore
::API_METHOD_GARBAGE_COLLECTION_STATUS
.returns
;
76 let options
= default_table_format_options();
78 format_and_print_result_full(&mut data
, return_type
, &output_format
, &options
);
83 fn garbage_collection_commands() -> CommandLineInterface
{
85 let cmd_def
= CliCommandMap
::new()
87 CliCommand
::new(&API_METHOD_GARBAGE_COLLECTION_STATUS
)
88 .arg_param(&["store"])
89 .completion_cb("store", config
::datastore
::complete_datastore_name
)
92 CliCommand
::new(&API_METHOD_START_GARBAGE_COLLECTION
)
93 .arg_param(&["store"])
94 .completion_cb("store", config
::datastore
::complete_datastore_name
)
104 description
: "The maximal number of tasks to list.",
112 schema
: OUTPUT_FORMAT
,
117 description
: "Also list stopped tasks.",
123 /// List running server tasks.
124 async
fn task_list(param
: Value
) -> Result
<Value
, Error
> {
126 let output_format
= get_output_format(¶m
);
128 let client
= connect_to_localhost()?
;
130 let limit
= param
["limit"].as_u64().unwrap_or(50) as usize;
131 let running
= !param
["all"].as_bool().unwrap_or(false);
137 let mut result
= client
.get("api2/json/nodes/localhost/tasks", Some(args
)).await?
;
139 let mut data
= result
["data"].take();
140 let return_type
= &api2
::node
::tasks
::API_METHOD_LIST_TASKS
.returns
;
142 let options
= default_table_format_options()
143 .column(ColumnConfig
::new("starttime").right_align(false).renderer(tools
::format
::render_epoch
))
144 .column(ColumnConfig
::new("endtime").right_align(false).renderer(tools
::format
::render_epoch
))
145 .column(ColumnConfig
::new("upid"))
146 .column(ColumnConfig
::new("status").renderer(tools
::format
::render_task_status
));
148 format_and_print_result_full(&mut data
, return_type
, &output_format
, &options
);
162 /// Display the task log.
163 async
fn task_log(param
: Value
) -> Result
<Value
, Error
> {
165 let upid
= tools
::required_string_param(¶m
, "upid")?
;
167 let mut client
= connect_to_localhost()?
;
169 display_task_log(&mut client
, upid
, true).await?
;
183 /// Try to stop a specific task.
184 async
fn task_stop(param
: Value
) -> Result
<Value
, Error
> {
186 let upid_str
= tools
::required_string_param(¶m
, "upid")?
;
188 let mut client
= connect_to_localhost()?
;
190 let path
= format
!("api2/json/nodes/localhost/tasks/{}", tools
::percent_encode_component(upid_str
));
191 let _
= client
.delete(&path
, None
).await?
;
196 fn task_mgmt_cli() -> CommandLineInterface
{
198 let task_log_cmd_def
= CliCommand
::new(&API_METHOD_TASK_LOG
)
199 .arg_param(&["upid"]);
201 let task_stop_cmd_def
= CliCommand
::new(&API_METHOD_TASK_STOP
)
202 .arg_param(&["upid"]);
204 let cmd_def
= CliCommandMap
::new()
205 .insert("list", CliCommand
::new(&API_METHOD_TASK_LIST
))
206 .insert("log", task_log_cmd_def
)
207 .insert("stop", task_stop_cmd_def
);
212 // fixme: avoid API redefinition
217 schema
: DATASTORE_SCHEMA
,
220 schema
: REMOTE_ID_SCHEMA
,
223 schema
: DATASTORE_SCHEMA
,
226 schema
: REMOVE_VANISHED_BACKUPS_SCHEMA
,
230 schema
: OUTPUT_FORMAT
,
236 /// Sync datastore from another repository
237 async
fn pull_datastore(
239 remote_store
: String
,
241 remove_vanished
: Option
<bool
>,
243 ) -> Result
<Value
, Error
> {
245 let output_format
= get_output_format(¶m
);
247 let mut client
= connect_to_localhost()?
;
249 let mut args
= json
!({
250 "store": local_store
,
252 "remote-store": remote_store
,
255 if let Some(remove_vanished
) = remove_vanished
{
256 args
["remove-vanished"] = Value
::from(remove_vanished
);
259 let result
= client
.post("api2/json/pull", Some(args
)).await?
;
261 view_task_result(&mut client
, result
, &output_format
).await?
;
270 schema
: DATASTORE_SCHEMA
,
273 schema
: IGNORE_VERIFIED_BACKUPS_SCHEMA
,
277 schema
: VERIFICATION_OUTDATED_AFTER_SCHEMA
,
281 schema
: OUTPUT_FORMAT
,
291 ) -> Result
<Value
, Error
> {
293 let output_format
= extract_output_format(&mut param
);
295 let mut client
= connect_to_localhost()?
;
297 let args
= json
!(param
);
299 let path
= format
!("api2/json/admin/datastore/{}/verify", store
);
301 let result
= client
.post(&path
, Some(args
)).await?
;
303 view_task_result(&mut client
, result
, &output_format
).await?
;
310 async
fn report() -> Result
<Value
, Error
> {
311 let report
= proxmox_backup
::server
::generate_report();
312 io
::stdout().write_all(report
.as_bytes())?
;
323 description
: "Output verbose package information. It is ignored if output-format is specified.",
326 schema
: OUTPUT_FORMAT
,
332 /// List package versions for important Proxmox Backup Server packages.
333 async
fn get_versions(verbose
: bool
, param
: Value
) -> Result
<Value
, Error
> {
334 let output_format
= get_output_format(¶m
);
336 let packages
= crate::api2
::node
::apt
::get_versions()?
;
337 let mut packages
= json
!(if verbose { &packages[..] }
else { &packages[1..2] }
);
339 let options
= default_table_format_options()
341 .noborder(true) // just not helpful for version info which gets copy pasted often
342 .column(ColumnConfig
::new("Package"))
343 .column(ColumnConfig
::new("Version"))
344 .column(ColumnConfig
::new("ExtraInfo").header("Extra Info"))
346 let return_type
= &crate::api2
::node
::apt
::API_METHOD_GET_VERSIONS
.returns
;
348 format_and_print_result_full(&mut packages
, return_type
, &output_format
, &options
);
355 proxmox_backup
::tools
::setup_safe_path_env();
357 let cmd_def
= CliCommandMap
::new()
358 .insert("acl", acl_commands())
359 .insert("datastore", datastore_commands())
360 .insert("disk", disk_commands())
361 .insert("dns", dns_commands())
362 .insert("network", network_commands())
363 .insert("node", node_commands())
364 .insert("user", user_commands())
365 .insert("openid", openid_commands())
366 .insert("remote", remote_commands())
367 .insert("garbage-collection", garbage_collection_commands())
368 .insert("acme", acme_mgmt_cli())
369 .insert("cert", cert_mgmt_cli())
370 .insert("subscription", subscription_commands())
371 .insert("sync-job", sync_job_commands())
372 .insert("verify-job", verify_job_commands())
373 .insert("task", task_mgmt_cli())
376 CliCommand
::new(&API_METHOD_PULL_DATASTORE
)
377 .arg_param(&["remote", "remote-store", "local-store"])
378 .completion_cb("local-store", config
::datastore
::complete_datastore_name
)
379 .completion_cb("remote", config
::remote
::complete_remote_name
)
380 .completion_cb("remote-store", complete_remote_datastore_name
)
384 CliCommand
::new(&API_METHOD_VERIFY
)
385 .arg_param(&["store"])
386 .completion_cb("store", config
::datastore
::complete_datastore_name
)
389 CliCommand
::new(&API_METHOD_REPORT
)
392 CliCommand
::new(&API_METHOD_GET_VERSIONS
)
397 let mut rpcenv
= CliEnvironment
::new();
398 rpcenv
.set_auth_id(Some(String
::from("root@pam")));
400 pbs_runtime
::main(run_async_cli_command(cmd_def
, rpcenv
));
403 // shell completion helper
404 pub fn complete_remote_datastore_name(_arg
: &str, param
: &HashMap
<String
, String
>) -> Vec
<String
> {
406 let mut list
= Vec
::new();
408 let _
= proxmox
::try_block
!({
409 let remote
= param
.get("remote").ok_or_else(|| format_err
!("no remote"))?
;
411 let data
= pbs_runtime
::block_on(async
move {
412 crate::api2
::config
::remote
::scan_remote_datastores(remote
.clone()).await
416 list
.push(item
.store
);
420 }).map_err(|_err
: Error
| { /* ignore */ }
);