2 use serde_json
::{json, Value}
;
3 use std
::path
::PathBuf
;
5 use proxmox
::api
::{api, cli::*}
;
7 use proxmox_backup
::configdir
;
8 use proxmox_backup
::tools
;
9 use proxmox_backup
::config
;
10 use proxmox_backup
::api2
::types
::*;
11 use proxmox_backup
::client
::*;
12 use proxmox_backup
::tools
::ticket
::*;
13 use proxmox_backup
::auth_helpers
::*;
16 async
fn view_task_result(
20 ) -> Result
<(), Error
> {
21 let data
= &result
["data"];
22 if output_format
== "text" {
23 if let Some(upid
) = data
.as_str() {
24 display_task_log(client
, upid
, true).await?
;
27 format_and_print_result(&data
, &output_format
);
33 fn connect() -> Result
<HttpClient
, Error
> {
35 let uid
= nix
::unistd
::Uid
::current();
37 let client
= if uid
.is_root() {
38 let ticket
= assemble_rsa_ticket(private_auth_key(), "PBS", Some("root@pam"), None
)?
;
39 HttpClient
::new("localhost", "root@pam", Some(ticket
))?
41 HttpClient
::new("localhost", "root@pam", None
)?
47 fn datastore_commands() -> CommandLineInterface
{
49 use proxmox_backup
::api2
;
51 let cmd_def
= CliCommandMap
::new()
52 .insert("list", CliCommand
::new(&api2
::config
::datastore
::GET
))
54 CliCommand
::new(&api2
::config
::datastore
::POST
)
55 .arg_param(&["name", "path"])
58 CliCommand
::new(&api2
::config
::datastore
::DELETE
)
60 .completion_cb("name", config
::datastore
::complete_datastore_name
)
71 schema
: DATASTORE_SCHEMA
,
74 schema
: OUTPUT_FORMAT
,
80 /// Start garbage collection for a specific datastore.
81 async
fn start_garbage_collection(param
: Value
) -> Result
<Value
, Error
> {
83 let output_format
= param
["output-format"].as_str().unwrap_or("text").to_owned();
85 let store
= tools
::required_string_param(¶m
, "store")?
;
87 let mut client
= connect()?
;
89 let path
= format
!("api2/json/admin/datastore/{}/gc", store
);
91 let result
= client
.post(&path
, None
).await?
;
93 view_task_result(client
, result
, &output_format
).await?
;
102 schema
: DATASTORE_SCHEMA
,
105 schema
: OUTPUT_FORMAT
,
111 /// Show garbage collection status for a specific datastore.
112 async
fn garbage_collection_status(param
: Value
) -> Result
<Value
, Error
> {
114 let output_format
= param
["output-format"].as_str().unwrap_or("text").to_owned();
116 let store
= tools
::required_string_param(¶m
, "store")?
;
118 let client
= connect()?
;
120 let path
= format
!("api2/json/admin/datastore/{}/gc", store
);
122 let result
= client
.get(&path
, None
).await?
;
123 let data
= &result
["data"];
124 if output_format
== "text" {
125 format_and_print_result(&data
, "json-pretty");
127 format_and_print_result(&data
, &output_format
);
133 fn garbage_collection_commands() -> CommandLineInterface
{
135 let cmd_def
= CliCommandMap
::new()
137 CliCommand
::new(&API_METHOD_GARBAGE_COLLECTION_STATUS
)
138 .arg_param(&["store"])
139 .completion_cb("store", config
::datastore
::complete_datastore_name
)
142 CliCommand
::new(&API_METHOD_START_GARBAGE_COLLECTION
)
143 .arg_param(&["store"])
144 .completion_cb("store", config
::datastore
::complete_datastore_name
)
154 description
: "The maximal number of tasks to list.",
162 schema
: OUTPUT_FORMAT
,
167 description
: "Also list stopped tasks.",
173 /// List running server tasks.
174 async
fn task_list(param
: Value
) -> Result
<Value
, Error
> {
176 let output_format
= param
["output-format"].as_str().unwrap_or("text").to_owned();
178 let client
= connect()?
;
180 let limit
= param
["limit"].as_u64().unwrap_or(50) as usize;
181 let running
= !param
["all"].as_bool().unwrap_or(false);
187 let result
= client
.get("api2/json/nodes/localhost/tasks", Some(args
)).await?
;
189 let data
= &result
["data"];
191 if output_format
== "text" {
192 for item
in data
.as_array().unwrap() {
195 item
["upid"].as_str().unwrap(),
196 item
["status"].as_str().unwrap_or("running"),
200 format_and_print_result(data
, &output_format
);
215 /// Display the task log.
216 async
fn task_log(param
: Value
) -> Result
<Value
, Error
> {
218 let upid
= tools
::required_string_param(¶m
, "upid")?
;
220 let client
= connect()?
;
222 display_task_log(client
, upid
, true).await?
;
236 /// Try to stop a specific task.
237 async
fn task_stop(param
: Value
) -> Result
<Value
, Error
> {
239 let upid_str
= tools
::required_string_param(¶m
, "upid")?
;
241 let mut client
= connect()?
;
243 let path
= format
!("api2/json/nodes/localhost/tasks/{}", upid_str
);
244 let _
= client
.delete(&path
, None
).await?
;
249 fn task_mgmt_cli() -> CommandLineInterface
{
251 let task_log_cmd_def
= CliCommand
::new(&API_METHOD_TASK_LOG
)
252 .arg_param(&["upid"]);
254 let task_stop_cmd_def
= CliCommand
::new(&API_METHOD_TASK_STOP
)
255 .arg_param(&["upid"]);
257 let cmd_def
= CliCommandMap
::new()
258 .insert("list", CliCommand
::new(&API_METHOD_TASK_LIST
))
259 .insert("log", task_log_cmd_def
)
260 .insert("stop", task_stop_cmd_def
);
265 fn x509name_to_string(name
: &openssl
::x509
::X509NameRef
) -> Result
<String
, Error
> {
266 let mut parts
= Vec
::new();
267 for entry
in name
.entries() {
268 parts
.push(format
!("{} = {}", entry
.object().nid().short_name()?
, entry
.data().as_utf8()?
));
274 /// Diplay node certificate information.
275 fn cert_info() -> Result
<(), Error
> {
277 let cert_path
= PathBuf
::from(configdir
!("/proxy.pem"));
279 let cert_pem
= proxmox
::tools
::fs
::file_get_contents(&cert_path
)?
;
281 let cert
= openssl
::x509
::X509
::from_pem(&cert_pem
)?
;
283 println
!("Subject: {}", x509name_to_string(cert
.subject_name())?
);
285 if let Some(san
) = cert
.subject_alt_names() {
286 for name
in san
.iter() {
287 if let Some(v
) = name
.dnsname() {
288 println
!(" DNS:{}", v
);
289 } else if let Some(v
) = name
.ipaddress() {
290 println
!(" IP:{:?}", v
);
291 } else if let Some(v
) = name
.email() {
292 println
!(" EMAIL:{}", v
);
293 } else if let Some(v
) = name
.uri() {
294 println
!(" URI:{}", v
);
299 println
!("Issuer: {}", x509name_to_string(cert
.issuer_name())?
);
300 println
!("Validity:");
301 println
!(" Not Before: {}", cert
.not_before());
302 println
!(" Not After : {}", cert
.not_after());
304 let fp
= cert
.digest(openssl
::hash
::MessageDigest
::sha256())?
;
305 let fp_string
= proxmox
::tools
::digest_to_hex(&fp
);
306 let fp_string
= fp_string
.as_bytes().chunks(2).map(|v
| std
::str::from_utf8(v
).unwrap())
307 .collect
::<Vec
<&str>>().join(":");
309 println
!("Fingerprint (sha256): {}", fp_string
);
311 let pubkey
= cert
.public_key()?
;
312 println
!("Public key type: {}", openssl
::nid
::Nid
::from_raw(pubkey
.id().as_raw()).long_name()?
);
313 println
!("Public key bits: {}", pubkey
.bits());
322 description
: "Force generation of new SSL certifate.",
329 /// Update node certificates and generate all needed files/directories.
330 fn update_certs(force
: Option
<bool
>) -> Result
<(), Error
> {
332 config
::create_configdir()?
;
334 if let Err(err
) = generate_auth_key() {
335 bail
!("unable to generate auth key - {}", err
);
338 if let Err(err
) = generate_csrf_key() {
339 bail
!("unable to generate csrf key - {}", err
);
342 config
::update_self_signed_cert(force
.unwrap_or(false))?
;
347 fn cert_mgmt_cli() -> CommandLineInterface
{
349 let cmd_def
= CliCommandMap
::new()
350 .insert("info", CliCommand
::new(&API_METHOD_CERT_INFO
))
351 .insert("update", CliCommand
::new(&API_METHOD_UPDATE_CERTS
));
358 let cmd_def
= CliCommandMap
::new()
359 .insert("datastore", datastore_commands())
360 .insert("garbage-collection", garbage_collection_commands())
361 .insert("cert", cert_mgmt_cli())
362 .insert("task", task_mgmt_cli());
364 run_cli_command(cmd_def
);