1 use std
::collections
::HashMap
;
3 use anyhow
::{format_err, Error}
;
4 use serde_json
::{json, Value}
;
6 use proxmox
::api
::{api, cli::*, RpcEnvironment}
;
8 use proxmox_backup
::tools
;
9 use proxmox_backup
::config
;
10 use proxmox_backup
::api2
::{self, types::* }
;
11 use proxmox_backup
::client
::*;
12 use proxmox_backup
::tools
::ticket
::*;
13 use proxmox_backup
::auth_helpers
::*;
15 mod proxmox_backup_manager
;
16 use proxmox_backup_manager
::*;
18 async
fn view_task_result(
22 ) -> Result
<(), Error
> {
23 let data
= &result
["data"];
24 if output_format
== "text" {
25 if let Some(upid
) = data
.as_str() {
26 display_task_log(client
, upid
, true).await?
;
29 format_and_print_result(&data
, &output_format
);
35 fn connect() -> Result
<HttpClient
, Error
> {
37 let uid
= nix
::unistd
::Uid
::current();
39 let mut options
= HttpClientOptions
::new()
40 .prefix(Some("proxmox-backup".to_string()))
41 .verify_cert(false); // not required for connection to localhost
43 let client
= if uid
.is_root() {
44 let ticket
= assemble_rsa_ticket(private_auth_key(), "PBS", Some("root@pam"), None
)?
;
45 options
= options
.password(Some(ticket
));
46 HttpClient
::new("localhost", "root@pam", options
)?
48 options
= options
.ticket_cache(true).interactive(true);
49 HttpClient
::new("localhost", "root@pam", options
)?
59 schema
: DATASTORE_SCHEMA
,
62 schema
: OUTPUT_FORMAT
,
68 /// Start garbage collection for a specific datastore.
69 async
fn start_garbage_collection(param
: Value
) -> Result
<Value
, Error
> {
71 let output_format
= get_output_format(¶m
);
73 let store
= tools
::required_string_param(¶m
, "store")?
;
75 let mut client
= connect()?
;
77 let path
= format
!("api2/json/admin/datastore/{}/gc", store
);
79 let result
= client
.post(&path
, None
).await?
;
81 view_task_result(client
, result
, &output_format
).await?
;
90 schema
: DATASTORE_SCHEMA
,
93 schema
: OUTPUT_FORMAT
,
99 /// Show garbage collection status for a specific datastore.
100 async
fn garbage_collection_status(param
: Value
) -> Result
<Value
, Error
> {
102 let output_format
= get_output_format(¶m
);
104 let store
= tools
::required_string_param(¶m
, "store")?
;
106 let client
= connect()?
;
108 let path
= format
!("api2/json/admin/datastore/{}/gc", store
);
110 let mut result
= client
.get(&path
, None
).await?
;
111 let mut data
= result
["data"].take();
112 let schema
= api2
::admin
::datastore
::API_RETURN_SCHEMA_GARBAGE_COLLECTION_STATUS
;
114 let options
= default_table_format_options();
116 format_and_print_result_full(&mut data
, schema
, &output_format
, &options
);
121 fn garbage_collection_commands() -> CommandLineInterface
{
123 let cmd_def
= CliCommandMap
::new()
125 CliCommand
::new(&API_METHOD_GARBAGE_COLLECTION_STATUS
)
126 .arg_param(&["store"])
127 .completion_cb("store", config
::datastore
::complete_datastore_name
)
130 CliCommand
::new(&API_METHOD_START_GARBAGE_COLLECTION
)
131 .arg_param(&["store"])
132 .completion_cb("store", config
::datastore
::complete_datastore_name
)
142 description
: "The maximal number of tasks to list.",
150 schema
: OUTPUT_FORMAT
,
155 description
: "Also list stopped tasks.",
161 /// List running server tasks.
162 async
fn task_list(param
: Value
) -> Result
<Value
, Error
> {
164 let output_format
= get_output_format(¶m
);
166 let client
= connect()?
;
168 let limit
= param
["limit"].as_u64().unwrap_or(50) as usize;
169 let running
= !param
["all"].as_bool().unwrap_or(false);
175 let mut result
= client
.get("api2/json/nodes/localhost/tasks", Some(args
)).await?
;
177 let mut data
= result
["data"].take();
178 let schema
= api2
::node
::tasks
::API_RETURN_SCHEMA_LIST_TASKS
;
180 let options
= default_table_format_options()
181 .column(ColumnConfig
::new("starttime").right_align(false).renderer(tools
::format
::render_epoch
))
182 .column(ColumnConfig
::new("endtime").right_align(false).renderer(tools
::format
::render_epoch
))
183 .column(ColumnConfig
::new("upid"))
184 .column(ColumnConfig
::new("status").renderer(tools
::format
::render_task_status
));
186 format_and_print_result_full(&mut data
, schema
, &output_format
, &options
);
200 /// Display the task log.
201 async
fn task_log(param
: Value
) -> Result
<Value
, Error
> {
203 let upid
= tools
::required_string_param(¶m
, "upid")?
;
205 let client
= connect()?
;
207 display_task_log(client
, upid
, true).await?
;
221 /// Try to stop a specific task.
222 async
fn task_stop(param
: Value
) -> Result
<Value
, Error
> {
224 let upid_str
= tools
::required_string_param(¶m
, "upid")?
;
226 let mut client
= connect()?
;
228 let path
= format
!("api2/json/nodes/localhost/tasks/{}", upid_str
);
229 let _
= client
.delete(&path
, None
).await?
;
234 fn task_mgmt_cli() -> CommandLineInterface
{
236 let task_log_cmd_def
= CliCommand
::new(&API_METHOD_TASK_LOG
)
237 .arg_param(&["upid"]);
239 let task_stop_cmd_def
= CliCommand
::new(&API_METHOD_TASK_STOP
)
240 .arg_param(&["upid"]);
242 let cmd_def
= CliCommandMap
::new()
243 .insert("list", CliCommand
::new(&API_METHOD_TASK_LIST
))
244 .insert("log", task_log_cmd_def
)
245 .insert("stop", task_stop_cmd_def
);
250 // fixme: avoid API redefinition
255 schema
: DATASTORE_SCHEMA
,
258 schema
: REMOTE_ID_SCHEMA
,
261 schema
: DATASTORE_SCHEMA
,
264 schema
: REMOVE_VANISHED_BACKUPS_SCHEMA
,
268 schema
: OUTPUT_FORMAT
,
274 /// Sync datastore from another repository
275 async
fn pull_datastore(
277 remote_store
: String
,
279 remove_vanished
: Option
<bool
>,
281 ) -> Result
<Value
, Error
> {
283 let output_format
= get_output_format(¶m
);
285 let mut client
= connect()?
;
288 "store": local_store
,
290 "remote-store": remote_store
,
291 "remove-vanished": remove_vanished
,
294 let result
= client
.post("api2/json/pull", Some(args
)).await?
;
296 view_task_result(client
, result
, &output_format
).await?
;
303 let cmd_def
= CliCommandMap
::new()
304 .insert("acl", acl_commands())
305 .insert("datastore", datastore_commands())
306 .insert("dns", dns_commands())
307 .insert("network", network_commands())
308 .insert("user", user_commands())
309 .insert("remote", remote_commands())
310 .insert("garbage-collection", garbage_collection_commands())
311 .insert("cert", cert_mgmt_cli())
312 .insert("sync-job", sync_job_commands())
313 .insert("task", task_mgmt_cli())
316 CliCommand
::new(&API_METHOD_PULL_DATASTORE
)
317 .arg_param(&["remote", "remote-store", "local-store"])
318 .completion_cb("local-store", config
::datastore
::complete_datastore_name
)
319 .completion_cb("remote", config
::remote
::complete_remote_name
)
320 .completion_cb("remote-store", complete_remote_datastore_name
)
323 let mut rpcenv
= CliEnvironment
::new();
324 rpcenv
.set_user(Some(String
::from("root@pam")));
326 proxmox_backup
::tools
::runtime
::main(run_async_cli_command(cmd_def
, rpcenv
));
329 // shell completion helper
330 pub fn complete_remote_datastore_name(_arg
: &str, param
: &HashMap
<String
, String
>) -> Vec
<String
> {
332 let mut list
= Vec
::new();
334 let _
= proxmox
::try_block
!({
335 let remote
= param
.get("remote").ok_or_else(|| format_err
!("no remote"))?
;
336 let (remote_config
, _digest
) = config
::remote
::config()?
;
338 let remote
: config
::remote
::Remote
= remote_config
.lookup("remote", &remote
)?
;
340 let options
= HttpClientOptions
::new()
341 .password(Some(remote
.password
.clone()))
342 .fingerprint(remote
.fingerprint
.clone());
344 let client
= HttpClient
::new(
350 let result
= crate::tools
::runtime
::block_on(client
.get("api2/json/admin/datastore", None
))?
;
352 if let Some(data
) = result
["data"].as_array() {
354 if let Some(store
) = item
["store"].as_str() {
355 list
.push(store
.to_owned());
361 }).map_err(|_err
: Error
| { /* ignore */ }
);