diff --git a/src/api2/tape/backup.rs b/src/api2/tape/backup.rs index 4ae03059..c7b508c7 100644 --- a/src/api2/tape/backup.rs +++ b/src/api2/tape/backup.rs @@ -56,6 +56,11 @@ use crate::{ type: bool, optional: true, }, + "export-media-set": { + description: "Export media set upon job completion.", + type: bool, + optional: true, + }, }, }, returns: { @@ -67,6 +72,7 @@ pub fn backup( store: String, pool: String, eject_media: Option, + export_media_set: Option, rpcenv: &mut dyn RpcEnvironment, ) -> Result { @@ -84,6 +90,7 @@ pub fn backup( let to_stdout = if rpcenv.env_type() == RpcEnvironmentType::CLI { true } else { false }; let eject_media = eject_media.unwrap_or(false); + let export_media_set = export_media_set.unwrap_or(false); let upid_str = WorkerTask::new_thread( "tape-backup", @@ -91,7 +98,7 @@ pub fn backup( auth_id, to_stdout, move |worker| { - backup_worker(&worker, datastore, &pool_config, eject_media)?; + backup_worker(&worker, datastore, &pool_config, eject_media, export_media_set)?; Ok(()) } )?; @@ -108,6 +115,7 @@ fn backup_worker( datastore: Arc, pool_config: &MediaPoolConfig, eject_media: bool, + export_media_set: bool, ) -> Result<(), Error> { let status_path = Path::new(TAPE_STATUS_DIR); @@ -142,7 +150,10 @@ fn backup_worker( pool_writer.commit()?; - if eject_media { + if export_media_set { + worker.log(format!("exporting current media set")); + pool_writer.export_media_set(worker)?; + } else if eject_media { worker.log(format!("ejection backup media")); pool_writer.eject_media()?; } diff --git a/src/bin/proxmox-tape.rs b/src/bin/proxmox-tape.rs index 76c40d76..92ad157e 100644 --- a/src/bin/proxmox-tape.rs +++ b/src/bin/proxmox-tape.rs @@ -723,6 +723,11 @@ async fn clean_drive( type: bool, optional: true, }, + "export-media-set": { + description: "Export media set upon job completion.", + type: bool, + optional: true, + }, }, }, )] diff --git a/src/tape/pool_writer.rs b/src/tape/pool_writer.rs index 616b0fcd..d8b9b09a 100644 --- a/src/tape/pool_writer.rs +++ b/src/tape/pool_writer.rs @@ -117,6 +117,7 @@ impl PoolWriter { let (drive_config, _digest) = crate::config::drive::config()?; if let Some((mut changer, _)) = media_changer(&drive_config, &self.drive_name)? { + drop(status); // close drive changer.unload_media(None)?; } else { status.drive.eject_media()?; @@ -125,6 +126,37 @@ impl PoolWriter { Ok(()) } + /// Export current media set and drop PoolWriterState (close drive) + pub fn export_media_set(&mut self, worker: &WorkerTask) -> Result<(), Error> { + let mut status = self.status.take(); + + let (drive_config, _digest) = crate::config::drive::config()?; + + if let Some((mut changer, _)) = media_changer(&drive_config, &self.drive_name)? { + drop(status); // close drive + + changer.unload_media(None)?; + + for media_uuid in self.pool.current_media_list()? { + let media = self.pool.lookup_media(media_uuid)?; + let changer_id = media.changer_id(); + if let Some(slot) = changer.export_media(changer_id)? { + worker.log(format!("exported media '{}' to import/export slot {}", changer_id, slot)); + } else { + worker.warn(format!("export failed - media '{}' is not online", changer_id)); + } + } + + } else { + worker.log("standalone drive - ejecting media instead of export"); + if let Some(mut status) = status { + status.drive.eject_media()?; + } + } + + Ok(()) + } + /// commit changes to tape and catalog /// /// This is done automatically during a backupsession, but needs to