From mboxrd@z Thu Jan 1 00:00:00 1970 Return-Path: Received: from firstgate.proxmox.com (firstgate.proxmox.com [212.224.123.68]) by lore.proxmox.com (Postfix) with ESMTPS id 70FA11FF2D6 for ; Mon, 15 Jul 2024 12:23:10 +0200 (CEST) Received: from firstgate.proxmox.com (localhost [127.0.0.1]) by firstgate.proxmox.com (Proxmox) with ESMTP id D48D2512; Mon, 15 Jul 2024 12:23:37 +0200 (CEST) From: Christian Ebner To: pbs-devel@lists.proxmox.com Date: Mon, 15 Jul 2024 12:16:00 +0200 Message-Id: <20240715101602.274244-23-c.ebner@proxmox.com> X-Mailer: git-send-email 2.39.2 In-Reply-To: <20240715101602.274244-1-c.ebner@proxmox.com> References: <20240715101602.274244-1-c.ebner@proxmox.com> MIME-Version: 1.0 X-SPAM-LEVEL: Spam detection results: 0 AWL 0.021 Adjusted score from AWL reputation of From: address BAYES_00 -1.9 Bayes spam probability is 0 to 1% DMARC_MISSING 0.1 Missing DMARC policy KAM_DMARC_STATUS 0.01 Test Rule for DKIM or SPF Failure with Strict Alignment SPF_HELO_NONE 0.001 SPF: HELO does not publish an SPF Record SPF_PASS -0.001 SPF: sender matches SPF record Subject: [pbs-devel] [RFC proxmox-backup 22/24] bin: manager: add datastore push cli command X-BeenThere: pbs-devel@lists.proxmox.com X-Mailman-Version: 2.1.29 Precedence: list List-Id: Proxmox Backup Server development discussion List-Unsubscribe: , List-Archive: List-Post: List-Help: List-Subscribe: , Reply-To: Proxmox Backup Server development discussion Content-Type: text/plain; charset="us-ascii" Content-Transfer-Encoding: 7bit Errors-To: pbs-devel-bounces@lists.proxmox.com Sender: "pbs-devel" Expose the push api endpoint to be callable via the command line interface. Signed-off-by: Christian Ebner --- src/bin/proxmox-backup-manager.rs | 216 +++++++++++++++++++++++------- 1 file changed, 169 insertions(+), 47 deletions(-) diff --git a/src/bin/proxmox-backup-manager.rs b/src/bin/proxmox-backup-manager.rs index b57f60223..768701829 100644 --- a/src/bin/proxmox-backup-manager.rs +++ b/src/bin/proxmox-backup-manager.rs @@ -11,7 +11,7 @@ use proxmox_sys::fs::CreateOptions; use pbs_api_types::percent_encoding::percent_encode_component; use pbs_api_types::{ - BackupNamespace, GroupFilter, RateLimitConfig, SyncJobConfig, DATASTORE_SCHEMA, + BackupNamespace, GroupFilter, RateLimitConfig, SyncDirection, SyncJobConfig, DATASTORE_SCHEMA, GROUP_FILTER_LIST_SCHEMA, IGNORE_VERIFIED_BACKUPS_SCHEMA, NS_MAX_DEPTH_SCHEMA, REMOTE_ID_SCHEMA, REMOVE_VANISHED_BACKUPS_SCHEMA, TRANSFER_LAST_SCHEMA, UPID_SCHEMA, VERIFICATION_OUTDATED_AFTER_SCHEMA, @@ -293,6 +293,72 @@ fn task_mgmt_cli() -> CommandLineInterface { cmd_def.into() } +/// Sync datastore by pulling from another repository +#[allow(clippy::too_many_arguments)] +async fn sync_datastore( + remote: String, + remote_store: String, + remote_ns: Option, + store: String, + ns: Option, + remove_vanished: Option, + max_depth: Option, + group_filter: Option>, + limit: RateLimitConfig, + transfer_last: Option, + param: Value, + sync_direction: SyncDirection, +) -> Result { + let output_format = get_output_format(¶m); + + let client = connect_to_localhost()?; + let mut args = json!({ + "store": store, + "remote": remote, + "remote-store": remote_store, + }); + + if remote_ns.is_some() { + args["remote-ns"] = json!(remote_ns); + } + + if ns.is_some() { + args["ns"] = json!(ns); + } + + if max_depth.is_some() { + args["max-depth"] = json!(max_depth); + } + + if group_filter.is_some() { + args["group-filter"] = json!(group_filter); + } + + if let Some(remove_vanished) = remove_vanished { + args["remove-vanished"] = Value::from(remove_vanished); + } + + if transfer_last.is_some() { + args["transfer-last"] = json!(transfer_last) + } + + let mut limit_json = json!(limit); + let limit_map = limit_json + .as_object_mut() + .ok_or_else(|| format_err!("limit is not an Object"))?; + + args.as_object_mut().unwrap().append(limit_map); + + let result = match sync_direction { + SyncDirection::Pull => client.post("api2/json/pull", Some(args)).await?, + SyncDirection::Push => client.post("api2/json/push", Some(args)).await?, + }; + + view_task_result(&client, result, &output_format).await?; + + Ok(Value::Null) +} + // fixme: avoid API redefinition #[api( input: { @@ -341,7 +407,7 @@ fn task_mgmt_cli() -> CommandLineInterface { } } )] -/// Sync datastore from another repository +/// Sync datastore by pulling from another repository #[allow(clippy::too_many_arguments)] async fn pull_datastore( remote: String, @@ -356,52 +422,100 @@ async fn pull_datastore( transfer_last: Option, param: Value, ) -> Result { - let output_format = get_output_format(¶m); - - let client = connect_to_localhost()?; - - let mut args = json!({ - "store": store, - "remote": remote, - "remote-store": remote_store, - }); - - if remote_ns.is_some() { - args["remote-ns"] = json!(remote_ns); - } - - if ns.is_some() { - args["ns"] = json!(ns); - } - - if max_depth.is_some() { - args["max-depth"] = json!(max_depth); - } - - if group_filter.is_some() { - args["group-filter"] = json!(group_filter); - } - - if let Some(remove_vanished) = remove_vanished { - args["remove-vanished"] = Value::from(remove_vanished); - } - - if transfer_last.is_some() { - args["transfer-last"] = json!(transfer_last) - } - - let mut limit_json = json!(limit); - let limit_map = limit_json - .as_object_mut() - .ok_or_else(|| format_err!("limit is not an Object"))?; - - args.as_object_mut().unwrap().append(limit_map); - - let result = client.post("api2/json/pull", Some(args)).await?; - - view_task_result(&client, result, &output_format).await?; + sync_datastore( + remote, + remote_store, + remote_ns, + store, + ns, + remove_vanished, + max_depth, + group_filter, + limit, + transfer_last, + param, + SyncDirection::Pull, + ) + .await +} - Ok(Value::Null) +#[api( + input: { + properties: { + "store": { + schema: DATASTORE_SCHEMA, + }, + "ns": { + type: BackupNamespace, + optional: true, + }, + remote: { + schema: REMOTE_ID_SCHEMA, + }, + "remote-store": { + schema: DATASTORE_SCHEMA, + }, + "remote-ns": { + type: BackupNamespace, + optional: true, + }, + "remove-vanished": { + schema: REMOVE_VANISHED_BACKUPS_SCHEMA, + optional: true, + }, + "max-depth": { + schema: NS_MAX_DEPTH_SCHEMA, + optional: true, + }, + "group-filter": { + schema: GROUP_FILTER_LIST_SCHEMA, + optional: true, + }, + limit: { + type: RateLimitConfig, + flatten: true, + }, + "output-format": { + schema: OUTPUT_FORMAT, + optional: true, + }, + "transfer-last": { + schema: TRANSFER_LAST_SCHEMA, + optional: true, + }, + } + } +)] +/// Sync datastore from another repository +#[allow(clippy::too_many_arguments)] +async fn push_datastore( + remote: String, + remote_store: String, + remote_ns: Option, + store: String, + ns: Option, + remove_vanished: Option, + max_depth: Option, + group_filter: Option>, + limit: RateLimitConfig, + transfer_last: Option, + param: Value, +) -> Result { + sync_datastore( + remote, + remote_store, + remote_ns, + store, + ns, + remove_vanished, + max_depth, + group_filter, + limit, + transfer_last, + param, + SyncDirection::Push, + ) + .await } #[api( @@ -527,6 +641,14 @@ async fn run() -> Result<(), Error> { .completion_cb("group-filter", complete_remote_datastore_group_filter) .completion_cb("remote-ns", complete_remote_datastore_namespace), ) + .insert( + "push", + CliCommand::new(&API_METHOD_PUSH_DATASTORE) + .arg_param(&["store", "remote", "remote-store"]) + .completion_cb("store", pbs_config::datastore::complete_datastore_name) + .completion_cb("remote", pbs_config::remote::complete_remote_name) + .completion_cb("remote-store", complete_remote_datastore_name), + ) .insert( "verify", CliCommand::new(&API_METHOD_VERIFY) -- 2.39.2 _______________________________________________ pbs-devel mailing list pbs-devel@lists.proxmox.com https://lists.proxmox.com/cgi-bin/mailman/listinfo/pbs-devel