split BackupVmaToPbsArgs into PbsArgs and VmaBackupArgs

Signed-off-by: Filip Schauer <f.schauer@proxmox.com>
This commit is contained in:
Filip Schauer 2024-10-30 14:55:32 +01:00 committed by Fabian Grünbichler
parent 3c850b39a5
commit f48dd30f86
2 changed files with 124 additions and 88 deletions

View File

@ -6,7 +6,7 @@ use proxmox_time::epoch_i64;
mod vma;
mod vma2pbs;
use vma2pbs::{backup_vma_to_pbs, BackupVmaToPbsArgs};
use vma2pbs::{vma2pbs, BackupVmaToPbsArgs, PbsArgs, VmaBackupArgs};
const CMD_HELP: &str = "\
Usage: vma-to-pbs [OPTIONS] --repository <auth_id@host:port:datastore> --vmid <VMID> [vma_file]
@ -184,12 +184,9 @@ fn parse_args() -> Result<BackupVmaToPbsArgs, Error> {
None
};
let options = BackupVmaToPbsArgs {
vma_file_path: vma_file_path.cloned(),
let pbs_args = PbsArgs {
pbs_repository,
namespace,
backup_id: vmid,
backup_time,
pbs_password,
keyfile,
key_password,
@ -197,16 +194,24 @@ fn parse_args() -> Result<BackupVmaToPbsArgs, Error> {
fingerprint,
compress,
encrypt,
};
let vma_args = VmaBackupArgs {
vma_file_path: vma_file_path.cloned(),
backup_id: vmid,
backup_time,
notes,
log_file_path,
};
let options = BackupVmaToPbsArgs { pbs_args, vma_args };
Ok(options)
}
fn main() -> Result<(), Error> {
let args = parse_args()?;
backup_vma_to_pbs(args)?;
vma2pbs(args)?;
Ok(())
}

View File

@ -29,11 +29,13 @@ use crate::vma::VmaReader;
const VMA_CLUSTER_SIZE: usize = 65536;
pub struct BackupVmaToPbsArgs {
pub vma_file_path: Option<OsString>,
pub pbs_args: PbsArgs,
pub vma_args: VmaBackupArgs,
}
pub struct PbsArgs {
pub pbs_repository: String,
pub namespace: Option<String>,
pub backup_id: String,
pub backup_time: i64,
pub pbs_password: String,
pub keyfile: Option<String>,
pub key_password: Option<String>,
@ -41,6 +43,12 @@ pub struct BackupVmaToPbsArgs {
pub fingerprint: String,
pub compress: bool,
pub encrypt: bool,
}
pub struct VmaBackupArgs {
pub vma_file_path: Option<OsString>,
pub backup_id: String,
pub backup_time: i64,
pub notes: Option<String>,
pub log_file_path: Option<OsString>,
}
@ -61,25 +69,23 @@ fn handle_pbs_error(pbs_err: *mut c_char, function_name: &str) -> Result<(), Err
bail!("{function_name} failed: {pbs_err_str}");
}
fn create_pbs_backup_task(args: &BackupVmaToPbsArgs) -> Result<*mut ProxmoxBackupHandle, Error> {
println!("PBS repository: {}", args.pbs_repository);
if let Some(ns) = &args.namespace {
println!("PBS namespace: {}", ns);
}
println!("PBS fingerprint: {}", args.fingerprint);
println!("compress: {}", args.compress);
println!("encrypt: {}", args.encrypt);
println!("backup time: {}", epoch_to_rfc3339(args.backup_time)?);
fn create_pbs_backup_task(
pbs_args: &PbsArgs,
backup_args: &VmaBackupArgs,
) -> Result<*mut ProxmoxBackupHandle, Error> {
println!(
"backup time: {}",
epoch_to_rfc3339(backup_args.backup_time)?
);
let mut pbs_err: *mut c_char = ptr::null_mut();
let pbs_repository_cstr = CString::new(args.pbs_repository.as_str())?;
let ns_cstr = CString::new(args.namespace.as_deref().unwrap_or(""))?;
let backup_id_cstr = CString::new(args.backup_id.as_str())?;
let pbs_password_cstr = CString::new(args.pbs_password.as_str())?;
let fingerprint_cstr = CString::new(args.fingerprint.as_str())?;
let keyfile_cstr = args
let pbs_repository_cstr = CString::new(pbs_args.pbs_repository.as_str())?;
let ns_cstr = CString::new(pbs_args.namespace.as_deref().unwrap_or(""))?;
let backup_id_cstr = CString::new(backup_args.backup_id.as_str())?;
let pbs_password_cstr = CString::new(pbs_args.pbs_password.as_str())?;
let fingerprint_cstr = CString::new(pbs_args.fingerprint.as_str())?;
let keyfile_cstr = pbs_args
.keyfile
.as_ref()
.map(|v| CString::new(v.as_str()).unwrap());
@ -87,7 +93,7 @@ fn create_pbs_backup_task(args: &BackupVmaToPbsArgs) -> Result<*mut ProxmoxBacku
.as_ref()
.map(|v| v.as_ptr())
.unwrap_or(ptr::null());
let key_password_cstr = args
let key_password_cstr = pbs_args
.key_password
.as_ref()
.map(|v| CString::new(v.as_str()).unwrap());
@ -95,7 +101,7 @@ fn create_pbs_backup_task(args: &BackupVmaToPbsArgs) -> Result<*mut ProxmoxBacku
.as_ref()
.map(|v| v.as_ptr())
.unwrap_or(ptr::null());
let master_keyfile_cstr = args
let master_keyfile_cstr = pbs_args
.master_keyfile
.as_ref()
.map(|v| CString::new(v.as_str()).unwrap());
@ -108,14 +114,14 @@ fn create_pbs_backup_task(args: &BackupVmaToPbsArgs) -> Result<*mut ProxmoxBacku
pbs_repository_cstr.as_ptr(),
ns_cstr.as_ptr(),
backup_id_cstr.as_ptr(),
args.backup_time as u64,
backup_args.backup_time as u64,
PROXMOX_BACKUP_DEFAULT_CHUNK_SIZE,
pbs_password_cstr.as_ptr(),
keyfile_ptr,
key_password_ptr,
master_keyfile_ptr,
args.compress,
args.encrypt,
pbs_args.compress,
pbs_args.encrypt,
fingerprint_cstr.as_ptr(),
&mut pbs_err,
);
@ -361,17 +367,24 @@ where
Ok(())
}
fn pbs_client_setup(args: &BackupVmaToPbsArgs) -> Result<(HttpClient, String, Value), Error> {
let repo: BackupRepository = args.pbs_repository.parse()?;
fn pbs_client_setup(
pbs_args: &PbsArgs,
backup_args: &VmaBackupArgs,
) -> Result<(HttpClient, String, Value), Error> {
let repo: BackupRepository = pbs_args.pbs_repository.parse()?;
let options = HttpClientOptions::new_interactive(
Some(args.pbs_password.clone()),
Some(args.fingerprint.clone()),
Some(pbs_args.pbs_password.clone()),
Some(pbs_args.fingerprint.clone()),
);
let client = HttpClient::new(repo.host(), repo.port(), repo.auth_id(), options)?;
let backup_dir = BackupDir::from((BackupType::Vm, args.backup_id.clone(), args.backup_time));
let backup_dir = BackupDir::from((
BackupType::Vm,
backup_args.backup_id.clone(),
backup_args.backup_time,
));
let namespace = match &args.namespace {
let namespace = match &pbs_args.namespace {
Some(namespace) => BackupNamespace::new(namespace)?,
None => BackupNamespace::root(),
};
@ -386,21 +399,21 @@ fn pbs_client_setup(args: &BackupVmaToPbsArgs) -> Result<(HttpClient, String, Va
fn upload_log(
client: &HttpClient,
args: &BackupVmaToPbsArgs,
log_file_path: &OsString,
pbs_args: &PbsArgs,
store: &str,
request_args: Value,
) -> Result<(), Error> {
if let Some(log_file_path) = &args.log_file_path {
let path = format!("api2/json/admin/datastore/{}/upload-backup-log", store);
let data = std::fs::read(log_file_path)?;
let blob = if args.encrypt {
let crypt_config = match &args.keyfile {
let blob = if pbs_args.encrypt {
let crypt_config = match &pbs_args.keyfile {
None => None,
Some(keyfile) => {
let key = std::fs::read(keyfile)?;
let (key, _created, _) = decrypt_key(&key, &|| -> Result<Vec<u8>, Error> {
match &args.key_password {
match &pbs_args.key_password {
Some(key_password) => Ok(key_password.clone().into_bytes()),
None => bail!("no key password provided"),
}
@ -410,10 +423,10 @@ fn upload_log(
}
};
DataBlob::encode(&data, crypt_config.as_ref(), args.compress)?
DataBlob::encode(&data, crypt_config.as_ref(), pbs_args.compress)?
} else {
// fixme: howto sign log?
DataBlob::encode(&data, None, args.compress)?
DataBlob::encode(&data, None, pbs_args.compress)?
};
let body = hyper::Body::from(blob.into_inner());
@ -424,7 +437,6 @@ fn upload_log(
.await
.unwrap();
});
}
Ok(())
}
@ -444,8 +456,32 @@ fn set_notes(
Ok(())
}
pub fn backup_vma_to_pbs(args: BackupVmaToPbsArgs) -> Result<(), Error> {
let vma_file: Box<dyn BufRead> = match &args.vma_file_path {
pub fn vma2pbs(args: BackupVmaToPbsArgs) -> Result<(), Error> {
let pbs_args = &args.pbs_args;
println!("PBS repository: {}", pbs_args.pbs_repository);
if let Some(ns) = &pbs_args.namespace {
println!("PBS namespace: {}", ns);
}
println!("PBS fingerprint: {}", pbs_args.fingerprint);
println!("compress: {}", pbs_args.compress);
println!("encrypt: {}", pbs_args.encrypt);
let start_transfer_time = SystemTime::now();
upload_vma_file(pbs_args, &args.vma_args)?;
let transfer_duration = SystemTime::now().duration_since(start_transfer_time)?;
let total_seconds = transfer_duration.as_secs();
let minutes = total_seconds / 60;
let seconds = total_seconds % 60;
let milliseconds = transfer_duration.as_millis() % 1000;
println!("Backup finished within {minutes} minutes, {seconds} seconds and {milliseconds} ms");
Ok(())
}
fn upload_vma_file(pbs_args: &PbsArgs, backup_args: &VmaBackupArgs) -> Result<(), Error> {
let vma_file: Box<dyn BufRead> = match &backup_args.vma_file_path {
Some(vma_file_path) => match File::open(vma_file_path) {
Err(why) => return Err(anyhow!("Couldn't open file: {}", why)),
Ok(file) => Box::new(BufReader::new(file)),
@ -454,7 +490,7 @@ pub fn backup_vma_to_pbs(args: BackupVmaToPbsArgs) -> Result<(), Error> {
};
let vma_reader = VmaReader::new(vma_file)?;
let pbs = create_pbs_backup_task(&args)?;
let pbs = create_pbs_backup_task(pbs_args, backup_args)?;
defer! {
proxmox_backup_disconnect(pbs);
@ -467,10 +503,6 @@ pub fn backup_vma_to_pbs(args: BackupVmaToPbsArgs) -> Result<(), Error> {
handle_pbs_error(pbs_err, "proxmox_backup_connect")?;
}
println!("Connected to Proxmox Backup Server");
let start_transfer_time = SystemTime::now();
upload_configs(&vma_reader, pbs)?;
upload_block_devices(vma_reader, pbs)?;
@ -478,24 +510,23 @@ pub fn backup_vma_to_pbs(args: BackupVmaToPbsArgs) -> Result<(), Error> {
handle_pbs_error(pbs_err, "proxmox_backup_finish")?;
}
if args.notes.is_some() || args.log_file_path.is_some() {
let (client, store, request_args) = pbs_client_setup(&args)?;
if backup_args.notes.is_some() || backup_args.log_file_path.is_some() {
let (client, store, request_args) = pbs_client_setup(pbs_args, backup_args)?;
if args.log_file_path.is_some() {
upload_log(&client, &args, &store, request_args.clone())?;
if let Some(log_file_path) = &backup_args.log_file_path {
upload_log(
&client,
log_file_path,
pbs_args,
&store,
request_args.clone(),
)?;
}
if let Some(notes) = args.notes {
set_notes(&client, &notes, &store, request_args)?;
if let Some(notes) = &backup_args.notes {
set_notes(&client, notes, &store, request_args)?;
}
}
let transfer_duration = SystemTime::now().duration_since(start_transfer_time)?;
let total_seconds = transfer_duration.as_secs();
let minutes = total_seconds / 60;
let seconds = total_seconds % 60;
let milliseconds = transfer_duration.as_millis() % 1000;
println!("Backup finished within {minutes} minutes, {seconds} seconds and {milliseconds} ms");
Ok(())
}