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:
		@ -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(
 | 
				
			||||||
 | 
				
			|||||||
		Reference in New Issue
	
	Block a user