namespace deletion: make destroying groups separate choice
And make that opt-in in the API endpoint, to avoid bad surprises by default. If not set we'll only prune empty namespaces. Signed-off-by: Thomas Lamprecht <t.lamprecht@proxmox.com>
This commit is contained in:
parent
4ac8ec11fb
commit
d1f9cceada
@ -441,22 +441,26 @@ impl DataStore {
|
|||||||
Ok(removed_all_groups)
|
Ok(removed_all_groups)
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Remove a complete backup namespace including all it's, and child namespaces', groups.
|
/// Remove a complete backup namespace optionally including all it's, and child namespaces',
|
||||||
|
/// groups. If `removed_groups` is false this only prunes empty namespaces.
|
||||||
///
|
///
|
||||||
/// Returns true if all groups were removed, and false if some were protected
|
/// Returns true if everything requested, and false if some groups were protected or if some
|
||||||
|
/// namespaces weren't empty even though all groups were deleted (race with new backup)
|
||||||
pub fn remove_namespace_recursive(
|
pub fn remove_namespace_recursive(
|
||||||
self: &Arc<Self>,
|
self: &Arc<Self>,
|
||||||
ns: &BackupNamespace,
|
ns: &BackupNamespace,
|
||||||
|
delete_groups: bool,
|
||||||
) -> Result<bool, Error> {
|
) -> Result<bool, Error> {
|
||||||
// FIXME: locking? The single groups/snapshots are already protected, so may not be
|
let store = self.name();
|
||||||
// necesarry (depends on what we all allow to do with namespaces)
|
let mut removed_all_requested = true;
|
||||||
log::info!("removing whole namespace recursively {}:/{ns}", self.name());
|
if delete_groups {
|
||||||
|
log::info!("removing whole namespace recursively below {store}:/{ns}",);
|
||||||
let mut removed_all_groups = true;
|
for ns in self.recursive_iter_backup_ns(ns.to_owned())? {
|
||||||
for ns in self.recursive_iter_backup_ns(ns.to_owned())? {
|
let removed_ns_groups = self.remove_namespace_groups(&ns?)?;
|
||||||
let removed_ns_groups = self.remove_namespace_groups(&ns?)?;
|
removed_all_requested = removed_all_requested && removed_ns_groups;
|
||||||
|
}
|
||||||
removed_all_groups = removed_all_groups && removed_ns_groups;
|
} else {
|
||||||
|
log::info!("pruning empty namespace recursively below {store}:/{ns}");
|
||||||
}
|
}
|
||||||
|
|
||||||
// now try to delete the actual namespaces, bottom up so that we can use safe rmdir that
|
// now try to delete the actual namespaces, bottom up so that we can use safe rmdir that
|
||||||
@ -477,13 +481,22 @@ impl DataStore {
|
|||||||
|
|
||||||
if !ns.is_root() {
|
if !ns.is_root() {
|
||||||
match unlinkat(Some(base_fd), &ns.path(), UnlinkatFlags::RemoveDir) {
|
match unlinkat(Some(base_fd), &ns.path(), UnlinkatFlags::RemoveDir) {
|
||||||
Ok(()) => log::info!("removed namespace {ns}"),
|
Ok(()) => log::debug!("removed namespace {ns}"),
|
||||||
Err(err) => log::error!("failed to remove namespace {ns} - {err}"),
|
Err(nix::Error::Sys(nix::errno::Errno::ENOENT)) => {
|
||||||
|
log::debug!("namespace {ns} already removed")
|
||||||
|
}
|
||||||
|
Err(nix::Error::Sys(nix::errno::Errno::ENOTEMPTY)) if !delete_groups => {
|
||||||
|
log::debug!("skip removal of non-empty namespace {ns}")
|
||||||
|
}
|
||||||
|
Err(err) => {
|
||||||
|
removed_all_requested = false;
|
||||||
|
log::warn!("failed to remove namespace {ns} - {err}")
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
Ok(removed_all_groups)
|
Ok(removed_all_requested)
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Remove a complete backup group including all snapshots.
|
/// Remove a complete backup group including all snapshots.
|
||||||
|
@ -134,6 +134,13 @@ pub fn list_namespaces(
|
|||||||
ns: {
|
ns: {
|
||||||
type: BackupNamespace,
|
type: BackupNamespace,
|
||||||
},
|
},
|
||||||
|
"delete-groups": {
|
||||||
|
type: bool,
|
||||||
|
description: "If set, all groups will be destroyed in the whole hierachy below and\
|
||||||
|
including `ns`. If not set, only empty namespaces will be pruned.",
|
||||||
|
optional: true,
|
||||||
|
default: false,
|
||||||
|
},
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
access: {
|
access: {
|
||||||
@ -144,6 +151,7 @@ pub fn list_namespaces(
|
|||||||
pub fn delete_namespace(
|
pub fn delete_namespace(
|
||||||
store: String,
|
store: String,
|
||||||
ns: BackupNamespace,
|
ns: BackupNamespace,
|
||||||
|
delete_groups: bool,
|
||||||
_info: &ApiMethod,
|
_info: &ApiMethod,
|
||||||
rpcenv: &mut dyn RpcEnvironment,
|
rpcenv: &mut dyn RpcEnvironment,
|
||||||
) -> Result<Value, Error> {
|
) -> Result<Value, Error> {
|
||||||
@ -159,7 +167,7 @@ pub fn delete_namespace(
|
|||||||
|
|
||||||
let datastore = DataStore::lookup_datastore(&store, Some(Operation::Write))?;
|
let datastore = DataStore::lookup_datastore(&store, Some(Operation::Write))?;
|
||||||
|
|
||||||
if !datastore.remove_namespace_recursive(&ns)? {
|
if !datastore.remove_namespace_recursive(&ns, delete_groups)? {
|
||||||
bail!("group only partially deleted due to protected snapshots");
|
bail!("group only partially deleted due to protected snapshots");
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -864,7 +864,7 @@ fn check_and_remove_ns(params: &PullParameters, local_ns: &BackupNamespace) -> R
|
|||||||
¶ms.owner,
|
¶ms.owner,
|
||||||
PRIV_DATASTORE_MODIFY,
|
PRIV_DATASTORE_MODIFY,
|
||||||
)?;
|
)?;
|
||||||
params.store.remove_namespace_recursive(local_ns)
|
params.store.remove_namespace_recursive(local_ns, true)
|
||||||
}
|
}
|
||||||
|
|
||||||
fn check_and_remove_vanished_ns(
|
fn check_and_remove_vanished_ns(
|
||||||
|
Loading…
Reference in New Issue
Block a user