src/api2/pull.rs: implement delete flag for vanished groups
This commit is contained in:
parent
11d89239c3
commit
4b4eba0b9e
@ -240,6 +240,7 @@ pub async fn pull_group(
|
|||||||
src_repo: &BackupRepository,
|
src_repo: &BackupRepository,
|
||||||
tgt_store: Arc<DataStore>,
|
tgt_store: Arc<DataStore>,
|
||||||
group: &BackupGroup,
|
group: &BackupGroup,
|
||||||
|
delete: bool,
|
||||||
) -> Result<(), Error> {
|
) -> Result<(), Error> {
|
||||||
|
|
||||||
let path = format!("api2/json/admin/datastore/{}/snapshots", src_repo.store());
|
let path = format!("api2/json/admin/datastore/{}/snapshots", src_repo.store());
|
||||||
@ -285,6 +286,10 @@ pub async fn pull_group(
|
|||||||
pull_snapshot_from(worker, reader, tgt_store.clone(), &snapshot).await?;
|
pull_snapshot_from(worker, reader, tgt_store.clone(), &snapshot).await?;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if delete {
|
||||||
|
// fixme: implement me
|
||||||
|
}
|
||||||
|
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -293,6 +298,7 @@ pub async fn pull_store(
|
|||||||
client: &HttpClient,
|
client: &HttpClient,
|
||||||
src_repo: &BackupRepository,
|
src_repo: &BackupRepository,
|
||||||
tgt_store: Arc<DataStore>,
|
tgt_store: Arc<DataStore>,
|
||||||
|
delete: bool,
|
||||||
) -> Result<(), Error> {
|
) -> Result<(), Error> {
|
||||||
|
|
||||||
let path = format!("api2/json/admin/datastore/{}/groups", src_repo.store());
|
let path = format!("api2/json/admin/datastore/{}/groups", src_repo.store());
|
||||||
@ -312,13 +318,35 @@ pub async fn pull_store(
|
|||||||
|
|
||||||
let mut errors = false;
|
let mut errors = false;
|
||||||
|
|
||||||
|
let mut new_groups = std::collections::HashSet::new();
|
||||||
|
|
||||||
for item in list {
|
for item in list {
|
||||||
let group = BackupGroup::new(&item.backup_type, &item.backup_id);
|
let group = BackupGroup::new(&item.backup_type, &item.backup_id);
|
||||||
if let Err(err) = pull_group(worker, client, src_repo, tgt_store.clone(), &group).await {
|
if let Err(err) = pull_group(worker, client, src_repo, tgt_store.clone(), &group, delete).await {
|
||||||
worker.log(format!("sync group {}/{} failed - {}", item.backup_type, item.backup_id, err));
|
worker.log(format!("sync group {}/{} failed - {}", item.backup_type, item.backup_id, err));
|
||||||
errors = true;
|
errors = true;
|
||||||
// continue
|
// do not stop here, instead continue
|
||||||
}
|
}
|
||||||
|
new_groups.insert(group);
|
||||||
|
}
|
||||||
|
|
||||||
|
if delete {
|
||||||
|
let result: Result<(), Error> = proxmox::tools::try_block!({
|
||||||
|
let local_groups = BackupGroup::list_groups(&tgt_store.base_path())?;
|
||||||
|
for local_group in local_groups {
|
||||||
|
if new_groups.contains(&local_group) { continue; }
|
||||||
|
worker.log(format!("delete vanished group '{}/{}'", local_group.backup_type(), local_group.backup_id()));
|
||||||
|
if let Err(err) = tgt_store.remove_backup_group(&local_group) {
|
||||||
|
worker.log(format!("delete failed: {}", err));
|
||||||
|
errors = true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
Ok(())
|
||||||
|
});
|
||||||
|
if let Err(err) = result {
|
||||||
|
worker.log(format!("error during cleanup: {}", err));
|
||||||
|
errors = true;
|
||||||
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
if errors {
|
if errors {
|
||||||
@ -340,6 +368,12 @@ pub async fn pull_store(
|
|||||||
"remote-store": {
|
"remote-store": {
|
||||||
schema: DATASTORE_SCHEMA,
|
schema: DATASTORE_SCHEMA,
|
||||||
},
|
},
|
||||||
|
delete: {
|
||||||
|
description: "Delete vanished backups. This remove the local copy if the remote backup was deleted.",
|
||||||
|
type: Boolean,
|
||||||
|
optional: true,
|
||||||
|
default: true,
|
||||||
|
},
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
)]
|
)]
|
||||||
@ -348,12 +382,15 @@ async fn pull (
|
|||||||
store: String,
|
store: String,
|
||||||
remote: String,
|
remote: String,
|
||||||
remote_store: String,
|
remote_store: String,
|
||||||
|
delete: Option<bool>,
|
||||||
_info: &ApiMethod,
|
_info: &ApiMethod,
|
||||||
rpcenv: &mut dyn RpcEnvironment,
|
rpcenv: &mut dyn RpcEnvironment,
|
||||||
) -> Result<String, Error> {
|
) -> Result<String, Error> {
|
||||||
|
|
||||||
let username = rpcenv.get_user().unwrap();
|
let username = rpcenv.get_user().unwrap();
|
||||||
|
|
||||||
|
let delete = delete.unwrap_or(true);
|
||||||
|
|
||||||
let tgt_store = DataStore::lookup_datastore(&store)?;
|
let tgt_store = DataStore::lookup_datastore(&store)?;
|
||||||
|
|
||||||
let (remote_config, _digest) = remote::config()?;
|
let (remote_config, _digest) = remote::config()?;
|
||||||
@ -374,7 +411,7 @@ async fn pull (
|
|||||||
// explicit create shared lock to prevent GC on newly created chunks
|
// explicit create shared lock to prevent GC on newly created chunks
|
||||||
let _shared_store_lock = tgt_store.try_shared_chunk_store_lock()?;
|
let _shared_store_lock = tgt_store.try_shared_chunk_store_lock()?;
|
||||||
|
|
||||||
pull_store(&worker, &client, &src_repo, tgt_store.clone()).await?;
|
pull_store(&worker, &client, &src_repo, tgt_store.clone(), delete).await?;
|
||||||
|
|
||||||
worker.log(format!("sync datastore '{}' end", store));
|
worker.log(format!("sync datastore '{}' end", store));
|
||||||
|
|
||||||
|
@ -6,7 +6,7 @@ use std::sync::{Arc, Mutex};
|
|||||||
use failure::*;
|
use failure::*;
|
||||||
use lazy_static::lazy_static;
|
use lazy_static::lazy_static;
|
||||||
|
|
||||||
use super::backup_info::BackupDir;
|
use super::backup_info::{BackupGroup, BackupDir};
|
||||||
use super::chunk_store::{ChunkStore, GarbageCollectionStatus};
|
use super::chunk_store::{ChunkStore, GarbageCollectionStatus};
|
||||||
use super::dynamic_index::{DynamicIndexReader, DynamicIndexWriter};
|
use super::dynamic_index::{DynamicIndexReader, DynamicIndexWriter};
|
||||||
use super::fixed_index::{FixedIndexReader, FixedIndexWriter};
|
use super::fixed_index::{FixedIndexReader, FixedIndexWriter};
|
||||||
@ -168,6 +168,20 @@ impl DataStore {
|
|||||||
|
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Remove a complete backup group including all snapshots
|
||||||
|
pub fn remove_backup_group(&self, backup_group: &BackupGroup,
|
||||||
|
) -> Result<(), io::Error> {
|
||||||
|
|
||||||
|
let mut full_path = self.base_path();
|
||||||
|
full_path.push(backup_group.group_path());
|
||||||
|
|
||||||
|
log::info!("removing backup group {:?}", full_path);
|
||||||
|
std::fs::remove_dir_all(full_path)?;
|
||||||
|
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
|
||||||
/// Remove a backup directory including all content
|
/// Remove a backup directory including all content
|
||||||
pub fn remove_backup_dir(&self, backup_dir: &BackupDir,
|
pub fn remove_backup_dir(&self, backup_dir: &BackupDir,
|
||||||
) -> Result<(), io::Error> {
|
) -> Result<(), io::Error> {
|
||||||
|
@ -386,6 +386,7 @@ fn cert_mgmt_cli() -> CommandLineInterface {
|
|||||||
cmd_def.into()
|
cmd_def.into()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// fixme: avoid API redefinition
|
||||||
#[api(
|
#[api(
|
||||||
input: {
|
input: {
|
||||||
properties: {
|
properties: {
|
||||||
@ -398,6 +399,12 @@ fn cert_mgmt_cli() -> CommandLineInterface {
|
|||||||
"remote-store": {
|
"remote-store": {
|
||||||
schema: DATASTORE_SCHEMA,
|
schema: DATASTORE_SCHEMA,
|
||||||
},
|
},
|
||||||
|
delete: {
|
||||||
|
description: "Delete vanished backups. This remove the local copy if the remote backup was deleted.",
|
||||||
|
type: Boolean,
|
||||||
|
optional: true,
|
||||||
|
default: true,
|
||||||
|
},
|
||||||
"output-format": {
|
"output-format": {
|
||||||
schema: OUTPUT_FORMAT,
|
schema: OUTPUT_FORMAT,
|
||||||
optional: true,
|
optional: true,
|
||||||
@ -410,6 +417,7 @@ async fn pull_datastore(
|
|||||||
remote: String,
|
remote: String,
|
||||||
remote_store: String,
|
remote_store: String,
|
||||||
local_store: String,
|
local_store: String,
|
||||||
|
delete: Option<bool>,
|
||||||
output_format: Option<String>,
|
output_format: Option<String>,
|
||||||
) -> Result<Value, Error> {
|
) -> Result<Value, Error> {
|
||||||
|
|
||||||
@ -417,12 +425,16 @@ async fn pull_datastore(
|
|||||||
|
|
||||||
let mut client = connect()?;
|
let mut client = connect()?;
|
||||||
|
|
||||||
let args = json!({
|
let mut args = json!({
|
||||||
"store": local_store,
|
"store": local_store,
|
||||||
"remote": remote,
|
"remote": remote,
|
||||||
"remote-store": remote_store,
|
"remote-store": remote_store,
|
||||||
});
|
});
|
||||||
|
|
||||||
|
if let Some(delete) = delete {
|
||||||
|
args["delete"] = delete.into();
|
||||||
|
}
|
||||||
|
|
||||||
let result = client.post("api2/json/pull", Some(args)).await?;
|
let result = client.post("api2/json/pull", Some(args)).await?;
|
||||||
|
|
||||||
view_task_result(client, result, &output_format).await?;
|
view_task_result(client, result, &output_format).await?;
|
||||||
|
Loading…
Reference in New Issue
Block a user