diff --git a/src/api2/tape/backup.rs b/src/api2/tape/backup.rs index e4fba59c..4da22b81 100644 --- a/src/api2/tape/backup.rs +++ b/src/api2/tape/backup.rs @@ -33,6 +33,7 @@ use crate::{ }, server::{ lookup_user_email, + TapeBackupJobSummary, jobstate::{ Job, JobState, @@ -198,13 +199,16 @@ pub fn do_tape_backup_job( let notify_user = setup.notify_user.as_ref().unwrap_or_else(|| &Userid::root_userid()); let email = lookup_user_email(notify_user); - let job_result = backup_worker( + let (job_result, summary) = match backup_worker( &worker, datastore, &pool_config, &setup, email.clone(), - ); + ) { + Ok(summary) => (Ok(()), summary), + Err(err) => (Err(err), Default::default()), + }; let status = worker.create_state(&job_result); @@ -214,6 +218,7 @@ pub fn do_tape_backup_job( Some(job.jobname()), &setup, &job_result, + summary, ) { eprintln!("send tape backup notification failed: {}", err); } @@ -340,13 +345,17 @@ pub fn backup( move |worker| { let _drive_lock = drive_lock; // keep lock guard set_tape_device_state(&setup.drive, &worker.upid().to_string())?; - let job_result = backup_worker( + + let (job_result, summary) = match backup_worker( &worker, datastore, &pool_config, &setup, email.clone(), - ); + ) { + Ok(summary) => (Ok(()), summary), + Err(err) => (Err(err), Default::default()), + }; if let Some(email) = email { if let Err(err) = crate::server::send_tape_backup_status( @@ -354,6 +363,7 @@ pub fn backup( None, &setup, &job_result, + summary, ) { eprintln!("send tape backup notification failed: {}", err); } @@ -374,9 +384,11 @@ fn backup_worker( pool_config: &MediaPoolConfig, setup: &TapeBackupJobSetup, email: Option, -) -> Result<(), Error> { +) -> Result { let status_path = Path::new(TAPE_STATUS_DIR); + let start = std::time::Instant::now(); + let mut summary: TapeBackupJobSummary = Default::default(); let _lock = MediaPool::lock(status_path, &pool_config.name)?; @@ -422,8 +434,11 @@ fn backup_worker( task_log!(worker, "skip snapshot {}", info.backup_dir); continue; } + let snapshot_name = info.backup_dir.to_string(); if !backup_snapshot(worker, &mut pool_writer, datastore.clone(), info.backup_dir)? { errors = true; + } else { + summary.snapshot_list.push(snapshot_name); } progress.done_snapshots = 1; task_log!( @@ -439,8 +454,11 @@ fn backup_worker( task_log!(worker, "skip snapshot {}", info.backup_dir); continue; } + let snapshot_name = info.backup_dir.to_string(); if !backup_snapshot(worker, &mut pool_writer, datastore.clone(), info.backup_dir)? { errors = true; + } else { + summary.snapshot_list.push(snapshot_name); } progress.done_snapshots = snapshot_number as u64 + 1; task_log!( @@ -478,7 +496,9 @@ fn backup_worker( bail!("Tape backup finished with some errors. Please check the task log."); } - Ok(()) + summary.duration = start.elapsed(); + + Ok(summary) } // Try to update the the media online status diff --git a/src/server/email_notifications.rs b/src/server/email_notifications.rs index e92c8091..4e07ed13 100644 --- a/src/server/email_notifications.rs +++ b/src/server/email_notifications.rs @@ -149,6 +149,14 @@ Datastore: {{job.store}} Tape Pool: {{job.pool}} Tape Drive: {{job.drive}} +{{#if snapshot-list ~}} +Snapshots included: + +{{#each snapshot-list~}} +{{this}} +{{/each~}} +{{/if}} +Duration: {{duration}} Tape Backup successful. @@ -215,6 +223,15 @@ lazy_static::lazy_static!{ }; } +/// Summary of a successful Tape Job +#[derive(Default)] +pub struct TapeBackupJobSummary { + /// The list of snaphots backed up + pub snapshot_list: Vec, + /// The total time of the backup job + pub duration: std::time::Duration, +} + fn send_job_status_mail( email: &str, subject: &str, @@ -412,14 +429,18 @@ pub fn send_tape_backup_status( id: Option<&str>, job: &TapeBackupJobSetup, result: &Result<(), Error>, + summary: TapeBackupJobSummary, ) -> Result<(), Error> { let (fqdn, port) = get_server_url(); + let duration: crate::tools::systemd::time::TimeSpan = summary.duration.into(); let mut data = json!({ "job": job, "fqdn": fqdn, "port": port, "id": id, + "snapshot-list": summary.snapshot_list, + "duration": duration.to_string(), }); let text = match result {