From mboxrd@z Thu Jan 1 00:00:00 1970 Return-Path: Received: from firstgate.proxmox.com (firstgate.proxmox.com [212.224.123.68]) (using TLSv1.3 with cipher TLS_AES_256_GCM_SHA384 (256/256 bits) key-exchange X25519 server-signature RSA-PSS (2048 bits)) (No client certificate requested) by lists.proxmox.com (Postfix) with ESMTPS id 64E5968BD8 for ; Thu, 22 Jul 2021 16:36:05 +0200 (CEST) Received: from firstgate.proxmox.com (localhost [127.0.0.1]) by firstgate.proxmox.com (Proxmox) with ESMTP id 584FD1300D for ; Thu, 22 Jul 2021 16:35:35 +0200 (CEST) Received: from proxmox-new.maurer-it.com (proxmox-new.maurer-it.com [94.136.29.106]) (using TLSv1.3 with cipher TLS_AES_256_GCM_SHA384 (256/256 bits) key-exchange X25519 server-signature RSA-PSS (2048 bits)) (No client certificate requested) by firstgate.proxmox.com (Proxmox) with ESMTPS id A4B5812FF7 for ; Thu, 22 Jul 2021 16:35:30 +0200 (CEST) Received: from proxmox-new.maurer-it.com (localhost.localdomain [127.0.0.1]) by proxmox-new.maurer-it.com (Proxmox) with ESMTP id 7C11D42587 for ; Thu, 22 Jul 2021 16:35:30 +0200 (CEST) From: =?UTF-8?q?Fabian=20Gr=C3=BCnbichler?= To: pbs-devel@lists.proxmox.com Date: Thu, 22 Jul 2021 16:35:06 +0200 Message-Id: <20210722143510.238967-4-f.gruenbichler@proxmox.com> X-Mailer: git-send-email 2.30.2 In-Reply-To: <20210722143510.238967-1-f.gruenbichler@proxmox.com> References: <20210722143510.238967-1-f.gruenbichler@proxmox.com> MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit X-SPAM-LEVEL: Spam detection results: 0 AWL 0.457 Adjusted score from AWL reputation of From: address BAYES_00 -1.9 Bayes spam probability is 0 to 1% 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] [PATCH proxmox-backup 3/7] sync: add group filtering 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: , X-List-Received-Date: Thu, 22 Jul 2021 14:36:05 -0000 like for manual pulls, but persisted in the sync job config and visible in the relevant GUI parts. Signed-off-by: Fabian Grünbichler --- Notes: if we want to make this configurable over the GUI, we probably want to switch the job edit window to a tabpanel and add a second grid tab for selecting the groups. src/api2/config/sync.rs | 22 ++++++++++++++++++++++ src/api2/pull.rs | 31 +++++++++++++++++++------------ src/config/sync.rs | 10 ++++++++++ www/config/SyncView.js | 13 ++++++++++++- www/window/SyncJobEdit.js | 12 ++++++++++++ 5 files changed, 75 insertions(+), 13 deletions(-) diff --git a/src/api2/config/sync.rs b/src/api2/config/sync.rs index bc7b9f24..28c04179 100644 --- a/src/api2/config/sync.rs +++ b/src/api2/config/sync.rs @@ -137,6 +137,14 @@ pub fn list_sync_jobs( optional: true, schema: SYNC_SCHEDULE_SCHEMA, }, + groups: { + type: Array, + description: "List of group identifiers to filter for. All if unspecified.", + items: { + schema: BACKUP_GROUP_SCHEMA, + }, + optional: true, + }, }, }, access: { @@ -222,6 +230,8 @@ pub enum DeletableProperty { schedule, /// Delete the remove-vanished flag. remove_vanished, + /// Delete the groups property. + groups, } #[api( @@ -259,6 +269,14 @@ pub enum DeletableProperty { optional: true, schema: SYNC_SCHEDULE_SCHEMA, }, + groups: { + type: Array, + description: "List of group identifiers to filter for. All if unspecified.", + items: { + schema: BACKUP_GROUP_SCHEMA, + }, + optional: true, + }, delete: { description: "List of properties to delete.", type: Array, @@ -289,6 +307,7 @@ pub fn update_sync_job( remove_vanished: Option, comment: Option, schedule: Option, + groups: Option>, delete: Option>, digest: Option, rpcenv: &mut dyn RpcEnvironment, @@ -315,6 +334,7 @@ pub fn update_sync_job( DeletableProperty::comment => { data.comment = None; }, DeletableProperty::schedule => { data.schedule = None; }, DeletableProperty::remove_vanished => { data.remove_vanished = None; }, + DeletableProperty::groups => { data.groups = None; }, } } } @@ -332,6 +352,7 @@ pub fn update_sync_job( if let Some(remote) = remote { data.remote = remote; } if let Some(remote_store) = remote_store { data.remote_store = remote_store; } if let Some(owner) = owner { data.owner = Some(owner); } + if let Some(groups) = groups { data.groups = Some(groups); } let schedule_changed = data.schedule != schedule; if schedule.is_some() { data.schedule = schedule; } @@ -451,6 +472,7 @@ acl:1:/remote/remote1/remotestore1:write@pbs:RemoteSyncOperator owner: Some(write_auth_id.clone()), comment: None, remove_vanished: None, + groups: None, schedule: None, }; diff --git a/src/api2/pull.rs b/src/api2/pull.rs index 36149761..fbcabb11 100644 --- a/src/api2/pull.rs +++ b/src/api2/pull.rs @@ -1,4 +1,5 @@ //! Sync datastore from remote server +use std::collections::HashSet; use std::sync::{Arc}; use std::str::FromStr; @@ -61,6 +62,20 @@ pub async fn get_pull_parameters( Ok((client, src_repo, tgt_store)) } +fn convert_group_filter(groups: Option>) -> Result>, Error> { + match groups { + Some(filter) => { + let mut groups = std::collections::HashSet::with_capacity(filter.len()); + for group in filter { + let group = BackupGroup::from_str(&group)?; + groups.insert(group); + } + Ok(Some(groups)) + }, + None => Ok(None) + } +} + pub fn do_sync_job( mut job: Job, sync_job: SyncJobConfig, @@ -102,7 +117,9 @@ pub fn do_sync_job( worker.log(format!("Sync datastore '{}' from '{}/{}'", sync_job.store, sync_job.remote, sync_job.remote_store)); - pull_store(&worker, &client, &src_repo, tgt_store.clone(), delete, sync_owner, None).await?; + let sync_group_filter = convert_group_filter(sync_job.groups)?; + + pull_store(&worker, &client, &src_repo, tgt_store.clone(), delete, sync_owner, sync_group_filter).await?; worker.log(format!("sync job '{}' end", &job_id)); @@ -195,17 +212,7 @@ async fn pull ( worker.log(format!("sync datastore '{}' start", store)); - let groups = match groups { - Some(filter) => { - let mut groups = std::collections::HashSet::with_capacity(filter.len()); - for group in filter { - let group = BackupGroup::from_str(&group)?; - groups.insert(group); - } - Some(groups) - }, - None => None, - }; + let groups = convert_group_filter(groups)?; let pull_future = pull_store(&worker, &client, &src_repo, tgt_store.clone(), delete, auth_id, groups); let future = select!{ success = pull_future.fuse() => success, diff --git a/src/config/sync.rs b/src/config/sync.rs index 5d5b2060..c088e08a 100644 --- a/src/config/sync.rs +++ b/src/config/sync.rs @@ -49,6 +49,14 @@ lazy_static! { optional: true, schema: SYNC_SCHEDULE_SCHEMA, }, + groups: { + type: Array, + description: "List of group identifiers to filter for. All if unspecified.", + items: { + schema: BACKUP_GROUP_SCHEMA, + }, + optional: true, + }, } )] #[derive(Serialize,Deserialize,Clone)] @@ -67,6 +75,8 @@ pub struct SyncJobConfig { pub comment: Option, #[serde(skip_serializing_if="Option::is_none")] pub schedule: Option, + #[serde(skip_serializing_if="Option::is_none")] + pub groups: Option>, } #[api( diff --git a/www/config/SyncView.js b/www/config/SyncView.js index 7d7e751c..d2a3954f 100644 --- a/www/config/SyncView.js +++ b/www/config/SyncView.js @@ -1,7 +1,7 @@ Ext.define('pbs-sync-jobs-status', { extend: 'Ext.data.Model', fields: [ - 'id', 'owner', 'remote', 'remote-store', 'store', 'schedule', + 'id', 'owner', 'remote', 'remote-store', 'store', 'schedule', 'groups', 'next-run', 'last-run-upid', 'last-run-state', 'last-run-endtime', { name: 'duration', @@ -106,6 +106,11 @@ Ext.define('PBS.config.SyncJobView', { return Ext.String.htmlEncode(value, metadata, record); }, + render_optional_groups: function(value, metadata, record) { + if (!value) return gettext('All'); + return Ext.String.htmlEncode(value, metadata, record); + }, + startStore: function() { this.getView().getStore().rstore.startUpdate(); }, stopStore: function() { this.getView().getStore().rstore.stopUpdate(); }, @@ -214,6 +219,12 @@ Ext.define('PBS.config.SyncJobView', { flex: 2, sortable: true, }, + { + header: gettext('Backup Groups'), + dataIndex: 'groups', + renderer: 'render_optional_groups', + width: 80, + }, { header: gettext('Schedule'), dataIndex: 'schedule', diff --git a/www/window/SyncJobEdit.js b/www/window/SyncJobEdit.js index 47e65ae3..2399f11f 100644 --- a/www/window/SyncJobEdit.js +++ b/www/window/SyncJobEdit.js @@ -199,6 +199,18 @@ Ext.define('PBS.window.SyncJobEdit', { ], columnB: [ + { + fieldLabel: gettext('Backup Groups'), + xtype: 'displayfield', + name: 'groups', + renderer: function(value, metadata, record) { + if (!value) return gettext('All'); + return Ext.String.htmlEncode(value, metadata, record); + }, + cbind: { + hidden: '{isCreate}', + }, + }, { fieldLabel: gettext('Comment'), xtype: 'proxmoxtextfield', -- 2.30.2