[pbs-devel] [RFC proxmox-backup 2/2] client: add --compresion flag to create_backup
Maximiliano Sandoval R
m.sandoval at proxmox.com
Fri Nov 3 16:17:07 CET 2023
This partially addresses
https://bugzilla.proxmox.com/show_bug.cgi?id=4900.
We take from borg the idea of passing compression levels in the form
`--compression=zstd,3`. This future proof in the sense that it can
accommodate future algorithms which might have more than one parameter,
it also works with negative arguments.
Signed-off-by: Maximiliano Sandoval R <m.sandoval at proxmox.com>
---
pbs-datastore/src/data_blob.rs | 40 +++++++++++++++++++++++++++++++
proxmox-backup-client/src/main.rs | 29 ++++++++++++++++------
2 files changed, 62 insertions(+), 7 deletions(-)
diff --git a/pbs-datastore/src/data_blob.rs b/pbs-datastore/src/data_blob.rs
index 765f5a98..2ea986af 100644
--- a/pbs-datastore/src/data_blob.rs
+++ b/pbs-datastore/src/data_blob.rs
@@ -4,6 +4,7 @@ use anyhow::{bail, Error};
use openssl::symm::{decrypt_aead, Mode};
use proxmox_io::{ReadExt, WriteExt};
+use proxmox_schema::{Schema, StringSchema};
use pbs_api_types::CryptMode;
use pbs_tools::crypt_config::CryptConfig;
@@ -24,6 +25,45 @@ impl Default for Compression {
}
}
+impl std::str::FromStr for Compression {
+ type Err = anyhow::Error;
+
+ fn from_str(s: &str) -> Result<Self, Self::Err> {
+ if s == "none" {
+ return Ok(Self::None);
+ }
+
+ if let Some(("zstd", level)) = s.split_once(',') {
+ let level = level.parse::<i32>()?;
+
+ if zstd::compression_level_range().contains(&level) {
+ Ok(Self::Zstd(level))
+ } else {
+ bail!("Invalid ZSTD compression level: {level}");
+ }
+ } else {
+ bail!("Unknown compression: {s}");
+ }
+ }
+}
+
+impl std::fmt::Display for Compression {
+ fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
+ match self {
+ Compression::None => write!(f, "none"),
+ Compression::Zstd(level) => write!(f, "zstd,{level}"),
+ }
+ }
+}
+
+proxmox_serde::forward_deserialize_to_from_str!(Compression);
+proxmox_serde::forward_serialize_to_display!(Compression);
+
+impl proxmox_schema::ApiType for Compression {
+ const API_SCHEMA: Schema =
+ StringSchema::new("Compression (e.g. 'none', 'zstd,<level>')").schema();
+}
+
/// Encoded data chunk with digest and positional information
pub struct ChunkInfo {
pub chunk: DataBlob,
diff --git a/proxmox-backup-client/src/main.rs b/proxmox-backup-client/src/main.rs
index e5d67c7d..3aaf127e 100644
--- a/proxmox-backup-client/src/main.rs
+++ b/proxmox-backup-client/src/main.rs
@@ -526,6 +526,7 @@ struct CatalogUploadResult {
fn spawn_catalog_upload(
client: Arc<BackupWriter>,
encrypt: bool,
+ compression: Compression,
) -> Result<CatalogUploadResult, Error> {
let (catalog_tx, catalog_rx) = std::sync::mpsc::sync_channel(10); // allow to buffer 10 writes
let catalog_stream = proxmox_async::blocking::StdChannelStream(catalog_rx);
@@ -540,7 +541,7 @@ fn spawn_catalog_upload(
let upload_options = UploadOptions {
encrypt,
- compression: Compression::default(),
+ compression,
..UploadOptions::default()
};
@@ -585,6 +586,10 @@ fn spawn_catalog_upload(
description: "Path to file.",
}
},
+ "compression": {
+ optional: true,
+ type: Compression,
+ },
"all-file-systems": {
type: Boolean,
description: "Include all mounted subdirectories.",
@@ -720,6 +725,16 @@ async fn create_backup(
let empty = Vec::new();
let exclude_args = param["exclude"].as_array().unwrap_or(&empty);
+ let compression = match param["compression"].as_str() {
+ Some(c) => {
+ match Compression::from_str(c) {
+ Err(err) => bail!("Invalid compression '{c}': {err}"),
+ Ok(comp) => comp,
+ }
+ },
+ None => Compression::default(),
+ };
+
let mut pattern_list = Vec::with_capacity(exclude_args.len());
for entry in exclude_args {
let entry = entry
@@ -948,7 +963,7 @@ async fn create_backup(
// no dry-run
(BackupSpecificationType::CONFIG, false) => {
let upload_options = UploadOptions {
- compression: Compression::default(),
+ compression,
encrypt: crypto.mode == CryptMode::Encrypt,
..UploadOptions::default()
};
@@ -962,7 +977,7 @@ async fn create_backup(
(BackupSpecificationType::LOGFILE, false) => {
// fixme: remove - not needed anymore ?
let upload_options = UploadOptions {
- compression: Compression::default(),
+ compression,
encrypt: crypto.mode == CryptMode::Encrypt,
..UploadOptions::default()
};
@@ -977,7 +992,7 @@ async fn create_backup(
// start catalog upload on first use
if catalog.is_none() {
let catalog_upload_res =
- spawn_catalog_upload(client.clone(), crypto.mode == CryptMode::Encrypt)?;
+ spawn_catalog_upload(client.clone(), crypto.mode == CryptMode::Encrypt, compression)?;
catalog = Some(catalog_upload_res.catalog_writer);
catalog_result_rx = Some(catalog_upload_res.result);
}
@@ -998,7 +1013,7 @@ async fn create_backup(
let upload_options = UploadOptions {
previous_manifest: previous_manifest.clone(),
- compression: Compression::default(),
+ compression,
encrypt: crypto.mode == CryptMode::Encrypt,
..UploadOptions::default()
};
@@ -1022,7 +1037,7 @@ async fn create_backup(
let upload_options = UploadOptions {
previous_manifest: previous_manifest.clone(),
fixed_size: Some(size),
- compression: Compression::default(),
+ compression,
encrypt: crypto.mode == CryptMode::Encrypt,
};
@@ -1077,7 +1092,7 @@ async fn create_backup(
log::debug!("Upload index.json to '{}'", repo);
let options = UploadOptions {
- compression: Compression::default(),
+ compression,
encrypt: false,
..UploadOptions::default()
};
--
2.39.2
More information about the pbs-devel
mailing list