2021-07-16 08:53:23 +00:00
|
|
|
use std::sync::Arc;
|
2020-10-30 08:07:24 +00:00
|
|
|
|
2021-07-16 08:53:23 +00:00
|
|
|
use anyhow::Error;
|
2020-10-30 08:07:24 +00:00
|
|
|
|
2021-07-07 12:37:47 +00:00
|
|
|
use pbs_datastore::{task_log, task_warn};
|
2021-08-30 09:49:22 +00:00
|
|
|
use pbs_datastore::backup_info::BackupInfo;
|
2021-09-10 07:21:27 +00:00
|
|
|
use pbs_datastore::prune::compute_prune_info;
|
|
|
|
use pbs_api_types::{Authid, PRIV_DATASTORE_MODIFY, PruneOptions};
|
2021-09-10 04:53:53 +00:00
|
|
|
use pbs_config::CachedUserInfo;
|
2021-07-07 12:37:47 +00:00
|
|
|
|
2020-10-30 08:07:24 +00:00
|
|
|
use crate::{
|
2021-08-30 09:49:22 +00:00
|
|
|
backup::DataStore,
|
2020-10-30 08:07:24 +00:00
|
|
|
server::jobstate::Job,
|
|
|
|
server::WorkerTask,
|
|
|
|
};
|
|
|
|
|
2021-07-16 08:53:23 +00:00
|
|
|
pub fn prune_datastore(
|
|
|
|
worker: Arc<WorkerTask>,
|
2021-07-16 08:53:25 +00:00
|
|
|
auth_id: Authid,
|
2021-07-16 08:53:23 +00:00
|
|
|
prune_options: PruneOptions,
|
|
|
|
store: &str,
|
|
|
|
datastore: Arc<DataStore>,
|
2021-07-16 08:53:26 +00:00
|
|
|
dry_run: bool,
|
2021-07-16 08:53:23 +00:00
|
|
|
) -> Result<(), Error> {
|
|
|
|
task_log!(worker, "Starting datastore prune on store \"{}\"", store);
|
|
|
|
|
2021-07-16 08:53:26 +00:00
|
|
|
if dry_run {
|
|
|
|
task_log!(worker, "(dry test run)");
|
|
|
|
}
|
|
|
|
|
2021-09-10 07:21:27 +00:00
|
|
|
let keep_all = !pbs_datastore::prune::keeps_something(&prune_options);
|
2021-07-16 08:53:24 +00:00
|
|
|
|
|
|
|
if keep_all {
|
|
|
|
task_log!(worker, "No prune selection - keeping all files.");
|
|
|
|
} else {
|
|
|
|
task_log!(
|
|
|
|
worker,
|
|
|
|
"retention options: {}",
|
2021-09-10 07:21:27 +00:00
|
|
|
pbs_datastore::prune::cli_options_string(&prune_options)
|
2021-07-16 08:53:24 +00:00
|
|
|
);
|
|
|
|
}
|
2021-07-16 08:53:23 +00:00
|
|
|
|
2021-07-16 08:53:25 +00:00
|
|
|
let user_info = CachedUserInfo::new()?;
|
|
|
|
let privs = user_info.lookup_privs(&auth_id, &["datastore", store]);
|
|
|
|
let has_privs = privs & PRIV_DATASTORE_MODIFY != 0;
|
|
|
|
|
2021-07-16 08:53:23 +00:00
|
|
|
let base_path = datastore.base_path();
|
|
|
|
|
|
|
|
let groups = BackupInfo::list_backup_groups(&base_path)?;
|
|
|
|
for group in groups {
|
|
|
|
let list = group.list_backups(&base_path)?;
|
2021-07-16 08:53:25 +00:00
|
|
|
|
|
|
|
if !has_privs && !datastore.owns_backup(&group, &auth_id)? {
|
|
|
|
continue;
|
|
|
|
}
|
|
|
|
|
2021-07-16 08:53:23 +00:00
|
|
|
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()
|
|
|
|
);
|
|
|
|
|
2021-07-16 08:53:24 +00:00
|
|
|
for (info, mut keep) in prune_info {
|
|
|
|
if keep_all { keep = true; }
|
2021-07-16 08:53:23 +00:00
|
|
|
task_log!(
|
|
|
|
worker,
|
|
|
|
"{} {}/{}/{}",
|
|
|
|
if keep { "keep" } else { "remove" },
|
|
|
|
group.backup_type(),
|
|
|
|
group.backup_id(),
|
|
|
|
info.backup_dir.backup_time_string()
|
|
|
|
);
|
2021-07-16 08:53:26 +00:00
|
|
|
if !keep && !dry_run {
|
2021-07-16 08:53:23 +00:00
|
|
|
if let Err(err) = datastore.remove_backup_dir(&info.backup_dir, false) {
|
|
|
|
task_warn!(
|
|
|
|
worker,
|
|
|
|
"failed to remove dir {:?}: {}",
|
|
|
|
info.backup_dir.relative_path(),
|
|
|
|
err,
|
|
|
|
);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
Ok(())
|
|
|
|
}
|
|
|
|
|
2020-10-30 08:07:24 +00:00
|
|
|
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)?;
|
|
|
|
|
|
|
|
let worker_type = job.jobtype().to_string();
|
2021-07-16 08:53:25 +00:00
|
|
|
let auth_id = auth_id.clone();
|
2020-10-30 08:07:24 +00:00
|
|
|
let upid_str = WorkerTask::new_thread(
|
|
|
|
&worker_type,
|
|
|
|
Some(job.jobname().to_string()),
|
2021-09-21 10:14:19 +00:00
|
|
|
auth_id.to_string(),
|
2020-10-30 08:07:24 +00:00
|
|
|
false,
|
|
|
|
move |worker| {
|
|
|
|
job.start(&worker.upid().to_string())?;
|
|
|
|
|
2021-07-16 08:53:23 +00:00
|
|
|
if let Some(event_str) = schedule {
|
|
|
|
task_log!(worker, "task triggered by schedule '{}'", event_str);
|
|
|
|
}
|
2020-10-30 08:07:24 +00:00
|
|
|
|
2021-07-16 08:53:26 +00:00
|
|
|
let result = prune_datastore(worker.clone(), auth_id, prune_options, &store, datastore, false);
|
2020-10-30 08:07:24 +00:00
|
|
|
|
|
|
|
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)
|
|
|
|
}
|