tape: improve export media to directly export from drive, add CLI
This commit is contained in:
parent
c92e3832bf
commit
483da89d03
@ -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: {
|
||||
|
@ -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(¶m, &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();
|
||||
|
@ -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>;
|
||||
}
|
||||
|
@ -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
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -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)
|
||||
|
Loading…
Reference in New Issue
Block a user