]> git.proxmox.com Git - proxmox-backup.git/blame - src/bin/proxmox-backup-manager.rs
add API to manage openid realms
[proxmox-backup.git] / src / bin / proxmox-backup-manager.rs
CommitLineData
331b869d 1use std::collections::HashMap;
941342f7 2use std::io::{self, Write};
ea0b8b6e 3
b29d046e 4use anyhow::{format_err, Error};
9894469e 5use serde_json::{json, Value};
9894469e 6
380bd7df 7use proxmox::api::{api, cli::*, RpcEnvironment};
769f8c99
DM
8
9use proxmox_backup::tools;
a220a456 10use proxmox_backup::config;
9894469e 11use proxmox_backup::api2::{self, types::* };
769f8c99 12use proxmox_backup::client::*;
769f8c99 13
a220a456
DM
14mod proxmox_backup_manager;
15use proxmox_backup_manager::*;
16
769f8c99
DM
17#[api(
18 input: {
19 properties: {
20 store: {
21 schema: DATASTORE_SCHEMA,
22 },
23 "output-format": {
24 schema: OUTPUT_FORMAT,
25 optional: true,
26 },
27 }
28 }
29)]
30/// Start garbage collection for a specific datastore.
31async fn start_garbage_collection(param: Value) -> Result<Value, Error> {
691c89a0 32
ac3faaf5 33 let output_format = get_output_format(&param);
691c89a0 34
769f8c99
DM
35 let store = tools::required_string_param(&param, "store")?;
36
6b68e5d5 37 let mut client = connect_to_localhost()?;
769f8c99
DM
38
39 let path = format!("api2/json/admin/datastore/{}/gc", store);
40
41 let result = client.post(&path, None).await?;
42
e68269fc 43 view_task_result(&mut client, result, &output_format).await?;
769f8c99
DM
44
45 Ok(Value::Null)
46}
47
48#[api(
49 input: {
50 properties: {
51 store: {
52 schema: DATASTORE_SCHEMA,
53 },
54 "output-format": {
55 schema: OUTPUT_FORMAT,
56 optional: true,
57 },
58 }
59 }
60)]
61/// Show garbage collection status for a specific datastore.
62async fn garbage_collection_status(param: Value) -> Result<Value, Error> {
63
ac3faaf5 64 let output_format = get_output_format(&param);
769f8c99
DM
65
66 let store = tools::required_string_param(&param, "store")?;
67
6b68e5d5 68 let client = connect_to_localhost()?;
769f8c99
DM
69
70 let path = format!("api2/json/admin/datastore/{}/gc", store);
71
9894469e
DM
72 let mut result = client.get(&path, None).await?;
73 let mut data = result["data"].take();
b2362a12 74 let return_type = &api2::admin::datastore::API_METHOD_GARBAGE_COLLECTION_STATUS.returns;
9894469e 75
ac3faaf5 76 let options = default_table_format_options();
9894469e 77
b2362a12 78 format_and_print_result_full(&mut data, return_type, &output_format, &options);
769f8c99
DM
79
80 Ok(Value::Null)
81}
82
83fn garbage_collection_commands() -> CommandLineInterface {
691c89a0
DM
84
85 let cmd_def = CliCommandMap::new()
86 .insert("status",
769f8c99 87 CliCommand::new(&API_METHOD_GARBAGE_COLLECTION_STATUS)
49fddd98 88 .arg_param(&["store"])
9ac1045c 89 .completion_cb("store", config::datastore::complete_datastore_name)
48ef3c33 90 )
691c89a0 91 .insert("start",
769f8c99 92 CliCommand::new(&API_METHOD_START_GARBAGE_COLLECTION)
49fddd98 93 .arg_param(&["store"])
9ac1045c 94 .completion_cb("store", config::datastore::complete_datastore_name)
48ef3c33 95 );
691c89a0
DM
96
97 cmd_def.into()
98}
99
47d47121
DM
100#[api(
101 input: {
102 properties: {
103 limit: {
104 description: "The maximal number of tasks to list.",
105 type: Integer,
106 optional: true,
107 minimum: 1,
108 maximum: 1000,
109 default: 50,
110 },
111 "output-format": {
112 schema: OUTPUT_FORMAT,
113 optional: true,
114 },
115 all: {
116 type: Boolean,
117 description: "Also list stopped tasks.",
118 optional: true,
119 }
120 }
121 }
122)]
123/// List running server tasks.
124async fn task_list(param: Value) -> Result<Value, Error> {
125
ac3faaf5 126 let output_format = get_output_format(&param);
47d47121 127
6b68e5d5 128 let client = connect_to_localhost()?;
47d47121
DM
129
130 let limit = param["limit"].as_u64().unwrap_or(50) as usize;
131 let running = !param["all"].as_bool().unwrap_or(false);
132 let args = json!({
133 "running": running,
134 "start": 0,
135 "limit": limit,
136 });
9894469e 137 let mut result = client.get("api2/json/nodes/localhost/tasks", Some(args)).await?;
47d47121 138
9894469e 139 let mut data = result["data"].take();
b2362a12 140 let return_type = &api2::node::tasks::API_METHOD_LIST_TASKS.returns;
47d47121 141
ac3faaf5 142 let options = default_table_format_options()
4939255f
DM
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))
93fbb4ef 145 .column(ColumnConfig::new("upid"))
4939255f 146 .column(ColumnConfig::new("status").renderer(tools::format::render_task_status));
9894469e 147
b2362a12 148 format_and_print_result_full(&mut data, return_type, &output_format, &options);
47d47121
DM
149
150 Ok(Value::Null)
151}
152
153#[api(
154 input: {
155 properties: {
156 upid: {
157 schema: UPID_SCHEMA,
158 },
159 }
160 }
161)]
162/// Display the task log.
163async fn task_log(param: Value) -> Result<Value, Error> {
164
165 let upid = tools::required_string_param(&param, "upid")?;
166
e68269fc 167 let mut client = connect_to_localhost()?;
47d47121 168
e68269fc 169 display_task_log(&mut client, upid, true).await?;
47d47121
DM
170
171 Ok(Value::Null)
172}
173
174#[api(
175 input: {
176 properties: {
177 upid: {
178 schema: UPID_SCHEMA,
179 },
180 }
181 }
182)]
183/// Try to stop a specific task.
184async fn task_stop(param: Value) -> Result<Value, Error> {
185
186 let upid_str = tools::required_string_param(&param, "upid")?;
187
6b68e5d5 188 let mut client = connect_to_localhost()?;
47d47121 189
968a0ab2 190 let path = format!("api2/json/nodes/localhost/tasks/{}", tools::percent_encode_component(upid_str));
47d47121
DM
191 let _ = client.delete(&path, None).await?;
192
193 Ok(Value::Null)
194}
195
196fn task_mgmt_cli() -> CommandLineInterface {
197
198 let task_log_cmd_def = CliCommand::new(&API_METHOD_TASK_LOG)
199 .arg_param(&["upid"]);
200
201 let task_stop_cmd_def = CliCommand::new(&API_METHOD_TASK_STOP)
202 .arg_param(&["upid"]);
203
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);
208
209 cmd_def.into()
210}
211
4b4eba0b 212// fixme: avoid API redefinition
0eb0e024
DM
213#[api(
214 input: {
215 properties: {
eb506c83 216 "local-store": {
0eb0e024
DM
217 schema: DATASTORE_SCHEMA,
218 },
219 remote: {
167971ed 220 schema: REMOTE_ID_SCHEMA,
0eb0e024
DM
221 },
222 "remote-store": {
223 schema: DATASTORE_SCHEMA,
224 },
40dc1031
DC
225 "remove-vanished": {
226 schema: REMOVE_VANISHED_BACKUPS_SCHEMA,
4b4eba0b 227 optional: true,
4b4eba0b 228 },
0eb0e024
DM
229 "output-format": {
230 schema: OUTPUT_FORMAT,
231 optional: true,
232 },
233 }
234 }
235)]
eb506c83
DM
236/// Sync datastore from another repository
237async fn pull_datastore(
0eb0e024
DM
238 remote: String,
239 remote_store: String,
eb506c83 240 local_store: String,
40dc1031 241 remove_vanished: Option<bool>,
ac3faaf5 242 param: Value,
0eb0e024
DM
243) -> Result<Value, Error> {
244
ac3faaf5 245 let output_format = get_output_format(&param);
0eb0e024 246
6b68e5d5 247 let mut client = connect_to_localhost()?;
0eb0e024 248
8c877436 249 let mut args = json!({
eb506c83 250 "store": local_store,
94609e23 251 "remote": remote,
0eb0e024 252 "remote-store": remote_store,
0eb0e024
DM
253 });
254
8c877436
DC
255 if let Some(remove_vanished) = remove_vanished {
256 args["remove-vanished"] = Value::from(remove_vanished);
257 }
258
eb506c83 259 let result = client.post("api2/json/pull", Some(args)).await?;
0eb0e024 260
e68269fc 261 view_task_result(&mut client, result, &output_format).await?;
0eb0e024
DM
262
263 Ok(Value::Null)
264}
265
355c055e
DM
266#[api(
267 input: {
268 properties: {
269 "store": {
270 schema: DATASTORE_SCHEMA,
271 },
60abf03f
HL
272 "ignore-verified": {
273 schema: IGNORE_VERIFIED_BACKUPS_SCHEMA,
274 optional: true,
275 },
276 "outdated-after": {
277 schema: VERIFICATION_OUTDATED_AFTER_SCHEMA,
278 optional: true,
279 },
355c055e
DM
280 "output-format": {
281 schema: OUTPUT_FORMAT,
282 optional: true,
283 },
284 }
285 }
286)]
287/// Verify backups
288async fn verify(
289 store: String,
60abf03f 290 mut param: Value,
355c055e
DM
291) -> Result<Value, Error> {
292
60abf03f 293 let output_format = extract_output_format(&mut param);
355c055e 294
6b68e5d5 295 let mut client = connect_to_localhost()?;
355c055e 296
60abf03f 297 let args = json!(param);
355c055e
DM
298
299 let path = format!("api2/json/admin/datastore/{}/verify", store);
300
301 let result = client.post(&path, Some(args)).await?;
302
e68269fc 303 view_task_result(&mut client, result, &output_format).await?;
355c055e
DM
304
305 Ok(Value::Null)
306}
307
9a556c8a
HL
308#[api()]
309/// System report
310async fn report() -> Result<Value, Error> {
941342f7
TL
311 let report = proxmox_backup::server::generate_report();
312 io::stdout().write_all(report.as_bytes())?;
9a556c8a
HL
313 Ok(Value::Null)
314}
315
c100fe91
ML
316#[api(
317 input: {
318 properties: {
319 verbose: {
320 type: Boolean,
321 optional: true,
322 default: false,
323 description: "Output verbose package information. It is ignored if output-format is specified.",
324 },
325 "output-format": {
326 schema: OUTPUT_FORMAT,
327 optional: true,
328 }
329 }
330 }
331)]
332/// List package versions for important Proxmox Backup Server packages.
333async fn get_versions(verbose: bool, param: Value) -> Result<Value, Error> {
294466ee 334 let output_format = get_output_format(&param);
c100fe91 335
5e293f13 336 let packages = crate::api2::node::apt::get_versions()?;
fc5a0120 337 let mut packages = json!(if verbose { &packages[..] } else { &packages[1..2] });
c100fe91 338
294466ee
TL
339 let options = default_table_format_options()
340 .disable_sort()
d1d74c43 341 .noborder(true) // just not helpful for version info which gets copy pasted often
294466ee
TL
342 .column(ColumnConfig::new("Package"))
343 .column(ColumnConfig::new("Version"))
344 .column(ColumnConfig::new("ExtraInfo").header("Extra Info"))
345 ;
b2362a12 346 let return_type = &crate::api2::node::apt::API_METHOD_GET_VERSIONS.returns;
294466ee 347
b2362a12 348 format_and_print_result_full(&mut packages, return_type, &output_format, &options);
c100fe91
ML
349
350 Ok(Value::Null)
351}
352
211fabd7
DM
353fn main() {
354
ac7513e3
DM
355 proxmox_backup::tools::setup_safe_path_env();
356
6460764d 357 let cmd_def = CliCommandMap::new()
ed3e60ae 358 .insert("acl", acl_commands())
48ef3c33 359 .insert("datastore", datastore_commands())
8e40aa63 360 .insert("disk", disk_commands())
14627d67 361 .insert("dns", dns_commands())
ca0e5347 362 .insert("network", network_commands())
72e311c6 363 .insert("node", node_commands())
579728c6 364 .insert("user", user_commands())
f357390c 365 .insert("remote", remote_commands())
47d47121 366 .insert("garbage-collection", garbage_collection_commands())
72bd8293 367 .insert("acme", acme_mgmt_cli())
550e0d88 368 .insert("cert", cert_mgmt_cli())
2762481c 369 .insert("subscription", subscription_commands())
a3016d65 370 .insert("sync-job", sync_job_commands())
4a874665 371 .insert("verify-job", verify_job_commands())
0eb0e024
DM
372 .insert("task", task_mgmt_cli())
373 .insert(
eb506c83
DM
374 "pull",
375 CliCommand::new(&API_METHOD_PULL_DATASTORE)
376 .arg_param(&["remote", "remote-store", "local-store"])
377 .completion_cb("local-store", config::datastore::complete_datastore_name)
f357390c 378 .completion_cb("remote", config::remote::complete_remote_name)
331b869d 379 .completion_cb("remote-store", complete_remote_datastore_name)
355c055e
DM
380 )
381 .insert(
382 "verify",
383 CliCommand::new(&API_METHOD_VERIFY)
384 .arg_param(&["store"])
385 .completion_cb("store", config::datastore::complete_datastore_name)
9a556c8a
HL
386 )
387 .insert("report",
388 CliCommand::new(&API_METHOD_REPORT)
c100fe91
ML
389 )
390 .insert("versions",
391 CliCommand::new(&API_METHOD_GET_VERSIONS)
0eb0e024 392 );
34d3ba52 393
355c055e
DM
394
395
7b22acd0 396 let mut rpcenv = CliEnvironment::new();
e6dc35ac 397 rpcenv.set_auth_id(Some(String::from("root@pam")));
525008f7 398
7b22acd0 399 proxmox_backup::tools::runtime::main(run_async_cli_command(cmd_def, rpcenv));
ea0b8b6e 400}
331b869d
DM
401
402// shell completion helper
403pub fn complete_remote_datastore_name(_arg: &str, param: &HashMap<String, String>) -> Vec<String> {
404
405 let mut list = Vec::new();
406
9ea4bce4 407 let _ = proxmox::try_block!({
331b869d 408 let remote = param.get("remote").ok_or_else(|| format_err!("no remote"))?;
331b869d 409
e0100d61
FG
410 let data = crate::tools::runtime::block_on(async move {
411 crate::api2::config::remote::scan_remote_datastores(remote.clone()).await
412 })?;
331b869d 413
e0100d61
FG
414 for item in data {
415 list.push(item.store);
331b869d
DM
416 }
417
418 Ok(())
419 }).map_err(|_err: Error| { /* ignore */ });
420
421 list
422}