pull: allow pulling groups selectively
without requiring workarounds based on ownership and limited visibility/access. if a group filter is set, remove_vanished will only consider filtered groups for removal to prevent concurrent disjunct filters from trashing eachother's synced groups. Signed-off-by: Fabian Grünbichler <f.gruenbichler@proxmox.com> Reviewed-by: Dominik Csapak <d.csapak@proxmox.com> Signed-off-by: Thomas Lamprecht <t.lamprecht@proxmox.com>
This commit is contained in:
parent
6e9e6c7a54
commit
71e534631f
@ -8,7 +8,7 @@ use proxmox_schema::api;
|
||||
use proxmox_router::{ApiMethod, Router, RpcEnvironment, Permission};
|
||||
|
||||
use pbs_api_types::{
|
||||
Authid, SyncJobConfig,
|
||||
Authid, SyncJobConfig, GroupFilter, GROUP_FILTER_LIST_SCHEMA,
|
||||
DATASTORE_SCHEMA, REMOTE_ID_SCHEMA, REMOVE_VANISHED_BACKUPS_SCHEMA,
|
||||
PRIV_DATASTORE_BACKUP, PRIV_DATASTORE_PRUNE, PRIV_REMOTE_READ,
|
||||
};
|
||||
@ -50,6 +50,7 @@ impl TryFrom<&SyncJobConfig> for PullParameters {
|
||||
&sync_job.remote_store,
|
||||
sync_job.owner.as_ref().unwrap_or_else(|| Authid::root_auth_id()).clone(),
|
||||
sync_job.remove_vanished,
|
||||
None,
|
||||
)
|
||||
}
|
||||
}
|
||||
@ -151,6 +152,10 @@ pub fn do_sync_job(
|
||||
schema: REMOVE_VANISHED_BACKUPS_SCHEMA,
|
||||
optional: true,
|
||||
},
|
||||
"groups": {
|
||||
schema: GROUP_FILTER_LIST_SCHEMA,
|
||||
optional: true,
|
||||
},
|
||||
},
|
||||
},
|
||||
access: {
|
||||
@ -168,6 +173,7 @@ async fn pull (
|
||||
remote: String,
|
||||
remote_store: String,
|
||||
remove_vanished: Option<bool>,
|
||||
groups: Option<Vec<GroupFilter>>,
|
||||
_info: &ApiMethod,
|
||||
rpcenv: &mut dyn RpcEnvironment,
|
||||
) -> Result<String, Error> {
|
||||
@ -183,6 +189,7 @@ async fn pull (
|
||||
&remote_store,
|
||||
auth_id.clone(),
|
||||
remove_vanished,
|
||||
groups,
|
||||
)?;
|
||||
let client = pull_params.client().await?;
|
||||
|
||||
|
@ -12,8 +12,9 @@ use pbs_client::{display_task_log, view_task_result};
|
||||
use pbs_tools::percent_encoding::percent_encode_component;
|
||||
use pbs_tools::json::required_string_param;
|
||||
use pbs_api_types::{
|
||||
DATASTORE_SCHEMA, UPID_SCHEMA, REMOTE_ID_SCHEMA, REMOVE_VANISHED_BACKUPS_SCHEMA,
|
||||
IGNORE_VERIFIED_BACKUPS_SCHEMA, VERIFICATION_OUTDATED_AFTER_SCHEMA,
|
||||
GroupFilter,
|
||||
DATASTORE_SCHEMA, GROUP_FILTER_LIST_SCHEMA, IGNORE_VERIFIED_BACKUPS_SCHEMA, REMOTE_ID_SCHEMA,
|
||||
REMOVE_VANISHED_BACKUPS_SCHEMA, UPID_SCHEMA, VERIFICATION_OUTDATED_AFTER_SCHEMA,
|
||||
};
|
||||
|
||||
use proxmox_rest_server::wait_for_local_worker;
|
||||
@ -238,6 +239,10 @@ fn task_mgmt_cli() -> CommandLineInterface {
|
||||
schema: REMOVE_VANISHED_BACKUPS_SCHEMA,
|
||||
optional: true,
|
||||
},
|
||||
"groups": {
|
||||
schema: GROUP_FILTER_LIST_SCHEMA,
|
||||
optional: true,
|
||||
},
|
||||
"output-format": {
|
||||
schema: OUTPUT_FORMAT,
|
||||
optional: true,
|
||||
@ -251,6 +256,7 @@ async fn pull_datastore(
|
||||
remote_store: String,
|
||||
local_store: String,
|
||||
remove_vanished: Option<bool>,
|
||||
groups: Option<Vec<GroupFilter>>,
|
||||
param: Value,
|
||||
) -> Result<Value, Error> {
|
||||
|
||||
@ -264,6 +270,10 @@ async fn pull_datastore(
|
||||
"remote-store": remote_store,
|
||||
});
|
||||
|
||||
if groups.is_some() {
|
||||
args["groups"] = json!(groups);
|
||||
}
|
||||
|
||||
if let Some(remove_vanished) = remove_vanished {
|
||||
args["remove-vanished"] = Value::from(remove_vanished);
|
||||
}
|
||||
|
@ -13,8 +13,9 @@ use http::StatusCode;
|
||||
|
||||
use proxmox_router::HttpError;
|
||||
|
||||
use pbs_api_types::{Authid, GroupListItem, Remote, SnapshotListItem};
|
||||
use pbs_datastore::{DataStore, BackupInfo, BackupDir, BackupGroup, StoreProgress};
|
||||
use pbs_api_types::{Authid, GroupFilter, GroupListItem, Remote, SnapshotListItem};
|
||||
|
||||
use pbs_datastore::{BackupDir, BackupInfo, BackupGroup, DataStore, StoreProgress};
|
||||
use pbs_datastore::data_blob::DataBlob;
|
||||
use pbs_datastore::dynamic_index::DynamicIndexReader;
|
||||
use pbs_datastore::fixed_index::FixedIndexReader;
|
||||
@ -39,6 +40,7 @@ pub struct PullParameters {
|
||||
store: Arc<DataStore>,
|
||||
owner: Authid,
|
||||
remove_vanished: bool,
|
||||
group_filter: Option<Vec<GroupFilter>>,
|
||||
}
|
||||
|
||||
impl PullParameters {
|
||||
@ -48,6 +50,7 @@ impl PullParameters {
|
||||
remote_store: &str,
|
||||
owner: Authid,
|
||||
remove_vanished: Option<bool>,
|
||||
group_filter: Option<Vec<GroupFilter>>,
|
||||
) -> Result<Self, Error> {
|
||||
let store = DataStore::lookup_datastore(store)?;
|
||||
|
||||
@ -63,7 +66,7 @@ impl PullParameters {
|
||||
remote_store.to_string(),
|
||||
);
|
||||
|
||||
Ok(Self { remote, source, store, owner, remove_vanished })
|
||||
Ok(Self { remote, source, store, owner, remove_vanished, group_filter })
|
||||
}
|
||||
|
||||
pub async fn client(&self) -> Result<HttpClient, Error> {
|
||||
@ -678,8 +681,7 @@ pub async fn pull_store(
|
||||
|
||||
let mut list: Vec<GroupListItem> = serde_json::from_value(result["data"].take())?;
|
||||
|
||||
task_log!(worker, "found {} groups to sync", list.len());
|
||||
|
||||
let total_count = list.len();
|
||||
list.sort_unstable_by(|a, b| {
|
||||
let type_order = a.backup_type.cmp(&b.backup_type);
|
||||
if type_order == std::cmp::Ordering::Equal {
|
||||
@ -689,11 +691,32 @@ pub async fn pull_store(
|
||||
}
|
||||
});
|
||||
|
||||
let apply_filters = |group: &BackupGroup, filters: &[GroupFilter]| -> bool {
|
||||
filters
|
||||
.iter()
|
||||
.any(|filter| group.matches(filter))
|
||||
};
|
||||
|
||||
let list:Vec<BackupGroup> = list
|
||||
.into_iter()
|
||||
.map(|item| BackupGroup::new(item.backup_type, item.backup_id))
|
||||
.collect();
|
||||
|
||||
let list = if let Some(ref group_filter) = ¶ms.group_filter {
|
||||
let unfiltered_count = list.len();
|
||||
let list:Vec<BackupGroup> = list
|
||||
.into_iter()
|
||||
.filter(|group| {
|
||||
apply_filters(&group, group_filter)
|
||||
})
|
||||
.collect();
|
||||
task_log!(worker, "found {} groups to sync (out of {} total)", list.len(), unfiltered_count);
|
||||
list
|
||||
} else {
|
||||
task_log!(worker, "found {} groups to sync", total_count);
|
||||
list
|
||||
};
|
||||
|
||||
let mut errors = false;
|
||||
|
||||
let mut new_groups = std::collections::HashSet::new();
|
||||
@ -755,6 +778,11 @@ pub async fn pull_store(
|
||||
if new_groups.contains(&local_group) {
|
||||
continue;
|
||||
}
|
||||
if let Some(ref group_filter) = ¶ms.group_filter {
|
||||
if !apply_filters(&local_group, group_filter) {
|
||||
continue;
|
||||
}
|
||||
}
|
||||
task_log!(
|
||||
worker,
|
||||
"delete vanished group '{}/{}'",
|
||||
|
Loading…
Reference in New Issue
Block a user