fix #4719: wait for tape to be available in changer

instead of aborting. If the tape is currently e.g. offline, in an
import/export slot or in the wrong drive, this gives the user the chance to
manually move it/insert it, so that the backup job can continue.

Send an e-mail like we do on a standalone drive, but adapt the messages
to contain the changer instead of the drive.

This can help when not all tapes are currently available in the changer.

Signed-off-by: Dominik Csapak <d.csapak@proxmox.com>
This commit is contained in:
Dominik Csapak 2023-05-08 13:03:41 +02:00 committed by Dietmar Maurer
parent 2ebe7bb54a
commit bdce7fa154
2 changed files with 49 additions and 29 deletions

View File

@ -540,27 +540,34 @@ pub fn send_tape_backup_status(
/// Send email to a person to request a manual media change /// Send email to a person to request a manual media change
pub fn send_load_media_email( pub fn send_load_media_email(
drive: &str, changer: bool,
device: &str,
label_text: &str, label_text: &str,
to: &str, to: &str,
reason: Option<String>, reason: Option<String>,
) -> Result<(), Error> { ) -> Result<(), Error> {
use std::fmt::Write as _; use std::fmt::Write as _;
let subject = format!("Load Media '{label_text}' request for drive '{drive}'"); let device_type = if changer { "changer" } else { "drive" };
let subject = format!("Load Media '{label_text}' request for {device_type} '{device}'");
let mut text = String::new(); let mut text = String::new();
if let Some(reason) = reason { if let Some(reason) = reason {
let _ = write!( let _ = write!(
text, text,
"The drive has the wrong or no tape inserted. Error:\n{reason}\n\n" "The {device_type} has the wrong or no tape(s) inserted. Error:\n{reason}\n\n"
); );
} }
text.push_str("Please insert the requested media into the backup drive.\n\n"); if changer {
text.push_str("Please insert the requested media into the changer.\n\n");
let _ = writeln!(text, "Drive: {drive}"); let _ = writeln!(text, "Changer: {device}");
} else {
text.push_str("Please insert the requested media into the backup drive.\n\n");
let _ = writeln!(text, "Drive: {device}");
}
let _ = writeln!(text, "Media: {label_text}"); let _ = writeln!(text, "Media: {label_text}");
send_job_status_mail(to, &subject, &text) send_job_status_mail(to, &subject, &text)

View File

@ -298,6 +298,7 @@ enum TapeRequestError {
OpenFailed(String), OpenFailed(String),
WrongLabel(String), WrongLabel(String),
ReadFailed(String), ReadFailed(String),
LoadingFailed(String),
} }
impl std::fmt::Display for TapeRequestError { impl std::fmt::Display for TapeRequestError {
@ -321,6 +322,9 @@ impl std::fmt::Display for TapeRequestError {
TapeRequestError::ReadFailed(reason) => { TapeRequestError::ReadFailed(reason) => {
write!(f, "tape read failed - {}", reason) write!(f, "tape read failed - {}", reason)
} }
TapeRequestError::LoadingFailed(reason) => {
write!(f, "could not load tape into drive - {}", reason)
}
} }
} }
} }
@ -374,40 +378,31 @@ pub fn request_and_load_media(
let label_text = label.label_text.clone(); let label_text = label.label_text.clone();
if drive_config.changer.is_some() {
task_log!(
worker,
"loading media '{}' into drive '{}'",
label_text,
drive
);
let mut changer = MtxMediaChanger::with_drive_config(&drive_config)?;
changer.load_media(&label_text)?;
let mut handle: Box<dyn TapeDriver> =
Box::new(open_lto_tape_drive(&drive_config)?);
let media_id = check_label(handle.as_mut(), &label.uuid)?;
return Ok((handle, media_id));
}
let mut last_error = TapeRequestError::None; let mut last_error = TapeRequestError::None;
let changer = &drive_config.changer;
let update_and_log_request_error = let update_and_log_request_error =
|old: &mut TapeRequestError, new: TapeRequestError| -> Result<(), Error> { |old: &mut TapeRequestError, new: TapeRequestError| -> Result<(), Error> {
if new != *old { if new != *old {
task_log!(worker, "{}", new); task_log!(worker, "{}", new);
let (device_type, device) = if let Some(changer) = changer {
("changer", changer.as_str())
} else {
("drive", drive)
};
task_log!( task_log!(
worker, worker,
"Please insert media '{}' into drive '{}'", "Please insert media '{}' into {} '{}'",
label_text, label_text,
drive device_type,
device
); );
if let Some(to) = notify_email { if let Some(to) = notify_email {
send_load_media_email( send_load_media_email(
drive, changer.is_some(),
device,
&label_text, &label_text,
to, to,
Some(new.to_string()), Some(new.to_string()),
@ -427,13 +422,31 @@ pub fn request_and_load_media(
worker.check_abort()?; worker.check_abort()?;
std::thread::sleep(std::time::Duration::from_millis(100)); std::thread::sleep(std::time::Duration::from_millis(100));
} }
} else { } else if drive_config.changer.is_none() {
task_log!( task_log!(
worker, worker,
"Checking for media '{}' in drive '{}'", "Checking for media '{}' in drive '{}'",
label_text, label_text,
drive drive
); );
} else {
task_log!(
worker,
"trying to load media '{}' into drive '{}'",
label_text,
drive
);
}
if drive_config.changer.is_some() {
let mut changer = MtxMediaChanger::with_drive_config(&drive_config)?;
if let Err(err) = changer.load_media(&label_text) {
update_and_log_request_error(
&mut last_error,
TapeRequestError::LoadingFailed(err.to_string()),
)?;
continue;
}
} }
let mut handle = match open_lto_tape_drive(&drive_config) { let mut handle = match open_lto_tape_drive(&drive_config) {