133d718fe4
We decided to go this route because it'll most likely be safer in the API as we need to explicitly add namespaces support to the various API endpoints this way. For example, 'pull' should have 2 namespaces: local and remote, and the GroupFilter (which would otherwise contain exactly *one* namespace parameter) needs to be applied for both sides (to decide what to pull from the remote, and what to *remove* locally as cleanup). The *datastore* types still contain the namespace and have a `.backup_ns()` getter. Note that the datastore's `Display` implementations are no longer safe to use as a deserializable string. Additionally, some datastore based methods now have been exposed via the BackupGroup/BackupDir types to avoid a "round trip" in code. Signed-off-by: Wolfgang Bumiller <w.bumiller@proxmox.com> Signed-off-by: Thomas Lamprecht <t.lamprecht@proxmox.com>
143 lines
4.0 KiB
Rust
143 lines
4.0 KiB
Rust
use std::sync::Arc;
|
|
|
|
use anyhow::Error;
|
|
|
|
use proxmox_sys::{task_log, task_warn};
|
|
|
|
use pbs_api_types::{Authid, BackupNamespace, Operation, PruneOptions, PRIV_DATASTORE_MODIFY};
|
|
use pbs_config::CachedUserInfo;
|
|
use pbs_datastore::prune::compute_prune_info;
|
|
use pbs_datastore::DataStore;
|
|
use proxmox_rest_server::WorkerTask;
|
|
|
|
use crate::server::jobstate::Job;
|
|
|
|
pub fn prune_datastore(
|
|
worker: Arc<WorkerTask>,
|
|
auth_id: Authid,
|
|
prune_options: PruneOptions,
|
|
datastore: Arc<DataStore>,
|
|
ns: BackupNamespace,
|
|
//max_depth: Option<usize>, // FIXME
|
|
dry_run: bool,
|
|
) -> Result<(), Error> {
|
|
let store = &datastore.name();
|
|
task_log!(worker, "Starting datastore prune on store \"{}\"", store);
|
|
|
|
if dry_run {
|
|
task_log!(worker, "(dry test run)");
|
|
}
|
|
|
|
let keep_all = !pbs_datastore::prune::keeps_something(&prune_options);
|
|
|
|
if keep_all {
|
|
task_log!(worker, "No prune selection - keeping all files.");
|
|
} else {
|
|
task_log!(
|
|
worker,
|
|
"retention options: {}",
|
|
pbs_datastore::prune::cli_options_string(&prune_options)
|
|
);
|
|
}
|
|
|
|
let user_info = CachedUserInfo::new()?;
|
|
let privs = user_info.lookup_privs(&auth_id, &["datastore", store]);
|
|
let has_privs = privs & PRIV_DATASTORE_MODIFY != 0;
|
|
|
|
// FIXME: Namespace recursion!
|
|
for group in datastore.iter_backup_groups(ns.clone())? {
|
|
let ns_recursed = &ns; // remove_backup_dir might need the inner one
|
|
let group = group?;
|
|
let list = group.list_backups()?;
|
|
|
|
if !has_privs && !datastore.owns_backup(&ns_recursed, group.as_ref(), &auth_id)? {
|
|
continue;
|
|
}
|
|
|
|
let mut prune_info = compute_prune_info(list, &prune_options)?;
|
|
prune_info.reverse(); // delete older snapshots first
|
|
|
|
task_log!(
|
|
worker,
|
|
"Starting prune on store \"{}\" group \"{}/{}\"",
|
|
store,
|
|
group.backup_type(),
|
|
group.backup_id()
|
|
);
|
|
|
|
for (info, mark) in prune_info {
|
|
let keep = keep_all || mark.keep();
|
|
task_log!(
|
|
worker,
|
|
"{} {}/{}/{}",
|
|
mark,
|
|
group.backup_type(),
|
|
group.backup_id(),
|
|
info.backup_dir.backup_time_string()
|
|
);
|
|
if !keep && !dry_run {
|
|
if let Err(err) =
|
|
datastore.remove_backup_dir(ns_recursed, info.backup_dir.as_ref(), false)
|
|
{
|
|
task_warn!(
|
|
worker,
|
|
"failed to remove dir {:?}: {}",
|
|
info.backup_dir.relative_path(),
|
|
err,
|
|
);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
Ok(())
|
|
}
|
|
|
|
pub fn do_prune_job(
|
|
mut job: Job,
|
|
prune_options: PruneOptions,
|
|
store: String,
|
|
auth_id: &Authid,
|
|
schedule: Option<String>,
|
|
) -> Result<String, Error> {
|
|
let datastore = DataStore::lookup_datastore(&store, Some(Operation::Write))?;
|
|
|
|
let worker_type = job.jobtype().to_string();
|
|
let auth_id = auth_id.clone();
|
|
let upid_str = WorkerTask::new_thread(
|
|
&worker_type,
|
|
Some(job.jobname().to_string()),
|
|
auth_id.to_string(),
|
|
false,
|
|
move |worker| {
|
|
job.start(&worker.upid().to_string())?;
|
|
|
|
if let Some(event_str) = schedule {
|
|
task_log!(worker, "task triggered by schedule '{}'", event_str);
|
|
}
|
|
|
|
let result = prune_datastore(
|
|
worker.clone(),
|
|
auth_id,
|
|
prune_options,
|
|
datastore,
|
|
BackupNamespace::default(),
|
|
false,
|
|
);
|
|
|
|
let status = worker.create_state(&result);
|
|
|
|
if let Err(err) = job.finish(status) {
|
|
eprintln!(
|
|
"could not finish job state for {}: {}",
|
|
job.jobtype().to_string(),
|
|
err
|
|
);
|
|
}
|
|
|
|
result
|
|
},
|
|
)?;
|
|
Ok(upid_str)
|
|
}
|