sync/pull: make namespace aware

Allow pulling all groups from a certain source namespace, and
possibly sub namespaces until max-depth, into a target namespace.

If any sub-namespaces get pulled, they will be mapped relatively from
the source parent namespace to the target parent namespace.

Signed-off-by: Fabian Grünbichler <f.gruenbichler@proxmox.com>
Signed-off-by: Thomas Lamprecht <t.lamprecht@proxmox.com>
This commit is contained in:
Fabian Grünbichler 2022-04-29 13:46:14 +02:00 committed by Thomas Lamprecht
parent 8f21c992a7
commit 718504f164
2 changed files with 36 additions and 5 deletions

View File

@ -1060,9 +1060,22 @@ pub struct DatastoreWithNamespace {
impl fmt::Display for DatastoreWithNamespace { impl fmt::Display for DatastoreWithNamespace {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
if self.ns.is_root() { if self.ns.is_root() {
write!(f, "{}", self.store) write!(f, "datastore {}, root namespace", self.store)
} else { } else {
write!(f, "{}/{}", self.store, self.ns) write!(f, "datastore '{}', namespace '{}'", self.store, self.ns)
}
}
}
impl DatastoreWithNamespace {
pub fn acl_path(&self) -> Vec<&str> {
let mut path: Vec<&str> = vec!["datastore", &self.store];
if self.ns.is_root() {
path
} else {
path.extend(self.ns.inner.iter().map(|comp| comp.as_str()));
path
} }
} }
} }

View File

@ -9,15 +9,15 @@ use proxmox_schema::*;
use crate::{ use crate::{
Authid, BackupNamespace, BackupType, RateLimitConfig, Userid, BACKUP_GROUP_SCHEMA, Authid, BackupNamespace, BackupType, RateLimitConfig, Userid, BACKUP_GROUP_SCHEMA,
BACKUP_NAMESPACE_SCHEMA, DATASTORE_SCHEMA, DRIVE_NAME_SCHEMA, MEDIA_POOL_NAME_SCHEMA, BACKUP_NAMESPACE_SCHEMA, DATASTORE_SCHEMA, DRIVE_NAME_SCHEMA, MEDIA_POOL_NAME_SCHEMA,
PROXMOX_SAFE_ID_FORMAT, REMOTE_ID_SCHEMA, SINGLE_LINE_COMMENT_SCHEMA, NS_MAX_DEPTH_SCHEMA, PROXMOX_SAFE_ID_FORMAT, REMOTE_ID_SCHEMA, SINGLE_LINE_COMMENT_SCHEMA,
}; };
const_regex! { const_regex! {
/// Regex for verification jobs 'DATASTORE:ACTUAL_JOB_ID' /// Regex for verification jobs 'DATASTORE:ACTUAL_JOB_ID'
pub VERIFICATION_JOB_WORKER_ID_REGEX = concat!(r"^(", PROXMOX_SAFE_ID_REGEX_STR!(), r"):"); pub VERIFICATION_JOB_WORKER_ID_REGEX = concat!(r"^(", PROXMOX_SAFE_ID_REGEX_STR!(), r"):");
/// Regex for sync jobs 'REMOTE:REMOTE_DATASTORE:LOCAL_DATASTORE:ACTUAL_JOB_ID' /// Regex for sync jobs 'REMOTE:REMOTE_DATASTORE:LOCAL_DATASTORE:(?:LOCAL_NS_ANCHOR:)ACTUAL_JOB_ID'
pub SYNC_JOB_WORKER_ID_REGEX = concat!(r"^(", PROXMOX_SAFE_ID_REGEX_STR!(), r"):(", PROXMOX_SAFE_ID_REGEX_STR!(), r"):(", PROXMOX_SAFE_ID_REGEX_STR!(), r"):"); pub SYNC_JOB_WORKER_ID_REGEX = concat!(r"^(", PROXMOX_SAFE_ID_REGEX_STR!(), r"):(", PROXMOX_SAFE_ID_REGEX_STR!(), r"):(", PROXMOX_SAFE_ID_REGEX_STR!(), r"):(?:", BACKUP_NS_RE!(), r"):");
} }
pub const JOB_ID_SCHEMA: Schema = StringSchema::new("Job ID.") pub const JOB_ID_SCHEMA: Schema = StringSchema::new("Job ID.")
@ -413,6 +413,10 @@ pub const GROUP_FILTER_LIST_SCHEMA: Schema =
store: { store: {
schema: DATASTORE_SCHEMA, schema: DATASTORE_SCHEMA,
}, },
ns: {
type: BackupNamespace,
optional: true,
},
"owner": { "owner": {
type: Authid, type: Authid,
optional: true, optional: true,
@ -423,10 +427,18 @@ pub const GROUP_FILTER_LIST_SCHEMA: Schema =
"remote-store": { "remote-store": {
schema: DATASTORE_SCHEMA, schema: DATASTORE_SCHEMA,
}, },
"remote-ns": {
type: BackupNamespace,
optional: true,
},
"remove-vanished": { "remove-vanished": {
schema: REMOVE_VANISHED_BACKUPS_SCHEMA, schema: REMOVE_VANISHED_BACKUPS_SCHEMA,
optional: true, optional: true,
}, },
"max-depth": {
schema: NS_MAX_DEPTH_SCHEMA,
optional: true,
},
comment: { comment: {
optional: true, optional: true,
schema: SINGLE_LINE_COMMENT_SCHEMA, schema: SINGLE_LINE_COMMENT_SCHEMA,
@ -452,11 +464,17 @@ pub struct SyncJobConfig {
pub id: String, pub id: String,
pub store: String, pub store: String,
#[serde(skip_serializing_if = "Option::is_none")] #[serde(skip_serializing_if = "Option::is_none")]
pub ns: Option<BackupNamespace>,
#[serde(skip_serializing_if = "Option::is_none")]
pub owner: Option<Authid>, pub owner: Option<Authid>,
pub remote: String, pub remote: String,
pub remote_store: String, pub remote_store: String,
#[serde(skip_serializing_if = "Option::is_none")] #[serde(skip_serializing_if = "Option::is_none")]
pub remote_ns: Option<BackupNamespace>,
#[serde(skip_serializing_if = "Option::is_none")]
pub remove_vanished: Option<bool>, pub remove_vanished: Option<bool>,
#[serde(default)]
pub max_depth: usize,
#[serde(skip_serializing_if = "Option::is_none")] #[serde(skip_serializing_if = "Option::is_none")]
pub comment: Option<String>, pub comment: Option<String>,
#[serde(skip_serializing_if = "Option::is_none")] #[serde(skip_serializing_if = "Option::is_none")]