mirror of
https://git.proxmox.com/git/vma-to-pbs
synced 2025-04-28 16:28:59 +00:00
split BackupVmaToPbsArgs into PbsArgs and VmaBackupArgs
Signed-off-by: Filip Schauer <f.schauer@proxmox.com>
This commit is contained in:
parent
3c850b39a5
commit
f48dd30f86
17
src/main.rs
17
src/main.rs
@ -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(())
|
||||
}
|
||||
|
147
src/vma2pbs.rs
147
src/vma2pbs.rs
@ -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, ¬es, &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(())
|
||||
}
|
||||
|
Loading…
Reference in New Issue
Block a user