]> git.proxmox.com Git - proxmox-backup.git/commitdiff
tape: improve export media to directly export from drive, add CLI
authorDietmar Maurer <dietmar@proxmox.com>
Sun, 10 Jan 2021 12:44:44 +0000 (13:44 +0100)
committerDietmar Maurer <dietmar@proxmox.com>
Sun, 10 Jan 2021 12:44:44 +0000 (13:44 +0100)
src/api2/tape/drive.rs
src/bin/proxmox-tape.rs
src/tape/changer/mod.rs
src/tape/changer/mtx.rs
src/tape/drive/virtual_tape.rs

index a9372975116ebb02e0c910f259bb38304671e8d4..4d9e906593c1f10fffef7a3d3f79ea94affb48c9 100644 (file)
@@ -88,6 +88,7 @@ pub async fn load_media(drive: String, changer_id: String) -> Result<(), Error>
         changer.load_media(&changer_id)
     }).await?
 }
+
 #[api(
     input: {
         properties: {
@@ -114,6 +115,37 @@ pub async fn load_slot(drive: String, source_slot: u64) -> Result<(), Error> {
     }).await?
 }
 
+#[api(
+    input: {
+        properties: {
+            drive: {
+                schema: DRIVE_NAME_SCHEMA,
+            },
+            "changer-id": {
+                schema: MEDIA_LABEL_SCHEMA,
+            },
+        },
+    },
+    returns: {
+        description: "The import-export slot number the media was transfered to.",
+        type: u64,
+        minimum: 1,
+    },
+)]
+/// Export media with specified label
+pub async fn export_media(drive: String, changer_id: String) -> Result<u64, Error> {
+
+    let (config, _digest) = config::drive::config()?;
+
+    tokio::task::spawn_blocking(move || {
+        let (mut changer, changer_name) = required_media_changer(&config, &drive)?;
+        match changer.export_media(&changer_id)? {
+            Some(slot) => Ok(slot),
+            None => bail!("media '{}' is not online (via changer '{}')", changer_id, changer_name),
+        }
+    }).await?
+}
+
 #[api(
     input: {
         properties: {
index 92ad157e52b30fe3668a06a5ef419ff8268aa3bd..4850008a10e623bc390132568f87366773eb2c6a 100644 (file)
@@ -236,6 +236,39 @@ async fn load_media(
     Ok(())
 }
 
+#[api(
+    input: {
+        properties: {
+            drive: {
+                schema: DRIVE_NAME_SCHEMA,
+                optional: true,
+            },
+            "changer-id": {
+                schema: MEDIA_LABEL_SCHEMA,
+            },
+        },
+    },
+)]
+/// Export media with specified label
+async fn export_media(
+    mut param: Value,
+    rpcenv: &mut dyn RpcEnvironment,
+) -> Result<(), Error> {
+
+    let (config, _digest) = config::drive::config()?;
+
+    param["drive"] = lookup_drive_name(&param, &config)?.into();
+
+    let info = &api2::tape::drive::API_METHOD_EXPORT_MEDIA;
+
+    match info.handler {
+        ApiHandler::Async(handler) => (handler)(param, info, rpcenv).await?,
+        _ => unreachable!(),
+    };
+
+    Ok(())
+}
+
 #[api(
     input: {
         properties: {
@@ -932,6 +965,13 @@ fn main() {
             CliCommand::new(&API_METHOD_UNLOAD_MEDIA)
                 .completion_cb("drive", complete_drive_name)
         )
+        .insert(
+            "export-media",
+            CliCommand::new(&API_METHOD_EXPORT_MEDIA)
+                .arg_param(&["changer-id"])
+                .completion_cb("drive", complete_drive_name)
+                .completion_cb("changer-id", complete_media_changer_id)
+        )
         ;
 
     let mut rpcenv = CliEnvironment::new();
index aa2b35ff1679302ca5659f1a15bd964283fa3a38..954a879db5b0a14286e1032cf69c8c37c184db32 100644 (file)
@@ -10,7 +10,7 @@ pub use mtx_wrapper::*;
 mod mtx;
 pub use mtx::*;
 
-use anyhow::{bail, Error};
+use anyhow::Error;
 
 /// Interface to media change devices
 pub trait MediaChange {
@@ -80,34 +80,5 @@ pub trait MediaChange {
     /// By moving the media to an empty import-export slot. Returns
     /// Some(slot) if the media was exported. Returns None if the media is
     /// not online (already exported).
-    fn export_media(&mut self, changer_id: &str) -> Result<Option<u64>, Error> {
-        let status = self.status()?;
-
-        let mut from = None;
-        let mut to = None;
-
-        for (i, (import_export, element_status)) in status.slots.iter().enumerate() {
-            if *import_export {
-                if to.is_some() { continue; }
-                if let ElementStatus::Empty = element_status {
-                    to = Some(i as u64 + 1);
-                }
-            } else {
-                if let ElementStatus::VolumeTag(ref tag) = element_status {
-                    if tag == changer_id {
-                        from = Some(i as u64 + 1);
-                    }
-                }
-            }
-        }
-        match (from, to) {
-            (Some(from), Some(to)) => {
-                self.transfer_media(from, to)?;
-                Ok(Some(to))
-            }
-            (Some(_from), None) => bail!("unable to find free export slot"),
-            (None, _) => Ok(None), // not online
-        }
-    }
-
+    fn export_media(&mut self, changer_id: &str) -> Result<Option<u64>, Error>;
 }
index 0f5c497bd547370a52ef12f32ecdbdea0c286947..f409642915e6bd0b445e6e282547b3a4ad1f6397 100644 (file)
@@ -48,6 +48,7 @@ fn unload_to_free_slot(drive_name: &str, path: &str, status: &MtxStatus, drivenu
     }
     let drive_status = &status.drives[drivenum as usize];
     if let Some(slot) = drive_status.loaded_slot {
+        // fixme: check if slot is free
         mtx_unload(path, slot, drivenum)
     } else {
         let mut free_slot = None;
@@ -174,4 +175,54 @@ impl MediaChange for MtxMediaChanger {
 
         Ok(())
     }
+
+    fn export_media(&mut self, changer_id: &str) -> Result<Option<u64>, Error> {
+        let status = self.status()?;
+
+        let mut from_drive = None;
+        if let Some(drive_status) = status.drives.get(self.drivenum as usize) {
+            if let ElementStatus::VolumeTag(ref tag) = drive_status.status {
+                if tag == changer_id {
+                    from_drive = Some(self.drivenum);
+                }
+            }
+        }
+
+        let mut from = None;
+        let mut to = None;
+
+        for (i, (import_export, element_status)) in status.slots.iter().enumerate() {
+            if *import_export {
+                if to.is_some() { continue; }
+                if let ElementStatus::Empty = element_status {
+                    to = Some(i as u64 + 1);
+                }
+            } else {
+                if let ElementStatus::VolumeTag(ref tag) = element_status {
+                    if tag == changer_id {
+                        from = Some(i as u64 + 1);
+                    }
+                }
+            }
+        }
+
+        if let Some(drivenum) = from_drive {
+            match to {
+                Some(to) => {
+                    mtx_unload(&self.config.path, to, drivenum)?;
+                    Ok(Some(to))
+                }
+                None =>  bail!("unable to find free export slot"),
+            }
+        } else {
+            match (from, to) {
+                (Some(from), Some(to)) => {
+                    self.transfer_media(from, to)?;
+                    Ok(Some(to))
+                }
+                (Some(_from), None) => bail!("unable to find free export slot"),
+                (None, _) => Ok(None), // not online
+            }
+        }
+    }
 }
index 998201dc5c62dada11ea8ff129dc4ccfae119306..ce6fe7c8c5cf342faee32893ab8b599734964490 100644 (file)
@@ -394,7 +394,11 @@ impl MediaChange for VirtualTapeHandle {
     }
 
     fn transfer_media(&mut self, _from: u64, _to: u64) -> Result<(), Error> {
-        bail!("medfia tranfer is not implemented!");
+        bail!("media tranfer is not implemented!");
+    }
+
+    fn export_media(&mut self, _changer_id: &str) -> Result<Option<u64>, Error> {
+        bail!("media export is not implemented!");
     }
 
     fn load_media_from_slot(&mut self, slot: u64) -> Result<(), Error> {
@@ -461,6 +465,11 @@ impl MediaChange for VirtualTapeDrive {
         handle.transfer_media(from, to)
     }
 
+    fn export_media(&mut self, changer_id: &str) -> Result<Option<u64>, Error> {
+        let mut handle = self.open()?;
+        handle.export_media(changer_id)
+    }
+
     fn load_media_from_slot(&mut self, slot: u64) -> Result<(), Error> {
         let mut handle = self.open()?;
         handle.load_media_from_slot(slot)