tape: implement format/erase

This commit is contained in:
Dietmar Maurer 2021-03-31 09:19:19 +02:00
parent a79082a0dd
commit e29f456efc
10 changed files with 108 additions and 38 deletions

View File

@ -321,8 +321,8 @@ pub fn unload(
permission: &Permission::Privilege(&["tape", "device", "{drive}"], PRIV_TAPE_WRITE, false),
},
)]
/// Erase media. Check for label-text if given (cancels if wrong media).
pub fn erase_media(
/// Format media. Check for label-text if given (cancels if wrong media).
pub fn format_media(
drive: String,
fast: Option<bool>,
label_text: Option<String>,
@ -331,7 +331,7 @@ pub fn erase_media(
let upid_str = run_drive_worker(
rpcenv,
drive.clone(),
"erase-media",
"format-media",
Some(drive.clone()),
move |worker, config| {
if let Some(ref label) = label_text {
@ -350,15 +350,15 @@ pub fn erase_media(
}
/* assume drive contains no or unrelated data */
task_log!(worker, "unable to read media label: {}", err);
task_log!(worker, "erase anyways");
handle.erase_media(fast.unwrap_or(true))?;
task_log!(worker, "format anyways");
handle.format_media(fast.unwrap_or(true))?;
}
Ok((None, _)) => {
if let Some(label) = label_text {
bail!("expected label '{}', found empty tape", label);
}
task_log!(worker, "found empty media - erase anyways");
handle.erase_media(fast.unwrap_or(true))?;
task_log!(worker, "found empty media - format anyways");
handle.format_media(fast.unwrap_or(true))?;
}
Ok((Some(media_id), _key_config)) => {
if let Some(label_text) = label_text {
@ -391,7 +391,7 @@ pub fn erase_media(
inventory.remove_media(&media_id.label.uuid)?;
};
handle.erase_media(fast.unwrap_or(true))?;
handle.format_media(fast.unwrap_or(true))?;
}
}
@ -503,7 +503,7 @@ pub fn eject_media(
/// Write a new media label to the media in 'drive'. The media is
/// assigned to the specified 'pool', or else to the free media pool.
///
/// Note: The media need to be empty (you may want to erase it first).
/// Note: The media need to be empty (you may want to format it first).
pub fn label_media(
drive: String,
pool: Option<String>,
@ -528,7 +528,7 @@ pub fn label_media(
drive.rewind()?;
match drive.read_next_file() {
Ok(Some(_file)) => bail!("media is not empty (erase first)"),
Ok(Some(_file)) => bail!("media is not empty (format it first)"),
Ok(None) => { /* EOF mark at BOT, assume tape is empty */ },
Err(err) => {
println!("TEST {:?}", err);
@ -1092,7 +1092,7 @@ fn barcode_label_media_worker(
match drive.read_next_file() {
Ok(Some(_file)) => {
worker.log(format!("media '{}' is not empty (erase first)", label_text));
worker.log(format!("media '{}' is not empty (format it first)", label_text));
continue;
}
Ok(None) => { /* EOF mark at BOT, assume tape is empty */ },
@ -1100,7 +1100,7 @@ fn barcode_label_media_worker(
if err.is_errno(nix::errno::Errno::ENOSPC) || err.is_errno(nix::errno::Errno::EIO) {
/* assume tape is empty */
} else {
worker.warn(format!("media '{}' read error (maybe not empty - erase first)", label_text));
worker.warn(format!("media '{}' read error (maybe not empty - format it first)", label_text));
continue;
}
}
@ -1430,9 +1430,9 @@ pub const SUBDIRS: SubdirMap = &sorted!([
.post(&API_METHOD_EJECT_MEDIA)
),
(
"erase-media",
"format-media",
&Router::new()
.post(&API_METHOD_ERASE_MEDIA)
.post(&API_METHOD_FORMAT_MEDIA)
),
(
"export-media",

View File

@ -409,7 +409,7 @@ fn eod(param: Value) -> Result<(), Error> {
},
},
)]
/// Erase media
/// Erase media (from current position)
fn erase(fast: Option<bool>, param: Value) -> Result<(), Error> {
let mut handle = get_tape_handle(&param)?;
@ -418,6 +418,35 @@ fn erase(fast: Option<bool>, param: Value) -> Result<(), Error> {
Ok(())
}
#[api(
input: {
properties: {
drive: {
schema: DRIVE_NAME_SCHEMA,
optional: true,
},
device: {
schema: LTO_DRIVE_PATH_SCHEMA,
optional: true,
},
fast: {
description: "Use fast erase.",
type: bool,
optional: true,
default: true,
},
},
},
)]
/// Format media, single partition
fn format(fast: Option<bool>, param: Value) -> Result<(), Error> {
let mut handle = get_tape_handle(&param)?;
handle.format_media(fast.unwrap_or(true))?;
Ok(())
}
#[api(
input: {
properties: {
@ -800,6 +829,7 @@ fn main() -> Result<(), Error> {
.insert("eject", std_cmd(&API_METHOD_EJECT))
.insert("eod", std_cmd(&API_METHOD_EOD))
.insert("erase", std_cmd(&API_METHOD_ERASE))
.insert("format", std_cmd(&API_METHOD_FORMAT))
.insert("fsf", std_cmd(&API_METHOD_FSF).arg_param(&["count"]))
.insert("fsfm", std_cmd(&API_METHOD_FSFM).arg_param(&["count"]))
.insert("fsr", std_cmd(&API_METHOD_FSR).arg_param(&["count"]))

View File

@ -115,8 +115,8 @@ pub fn extract_drive_name(
},
},
)]
/// Erase media
async fn erase_media(mut param: Value) -> Result<(), Error> {
/// Format media
async fn format_media(mut param: Value) -> Result<(), Error> {
let output_format = get_output_format(&param);
@ -126,7 +126,7 @@ async fn erase_media(mut param: Value) -> Result<(), Error> {
let mut client = connect_to_localhost()?;
let path = format!("api2/json/tape/drive/{}/erase-media", drive);
let path = format!("api2/json/tape/drive/{}/format-media", drive);
let result = client.post(&path, Some(param)).await?;
view_task_result(&mut client, result, &output_format).await?;
@ -992,8 +992,8 @@ fn main() {
.completion_cb("drive", complete_drive_name)
)
.insert(
"erase",
CliCommand::new(&API_METHOD_ERASE_MEDIA)
"format",
CliCommand::new(&API_METHOD_FORMAT_MEDIA)
.completion_cb("drive", complete_drive_name)
)
.insert(

View File

@ -179,6 +179,10 @@ impl LtoTapeHandle {
Ok(status)
}
pub fn erase_media(&mut self, fast: bool) -> Result<(), Error> {
self.sg_tape.erase_media(fast)
}
pub fn load(&mut self) -> Result<(), Error> {
self.sg_tape.load()
}
@ -223,9 +227,8 @@ impl TapeDriver for LtoTapeHandle {
self.sg_tape.current_file_number()
}
fn erase_media(&mut self, fast: bool) -> Result<(), Error> {
self.rewind()?; // important - erase from BOT
self.sg_tape.erase_media(fast)
fn format_media(&mut self, fast: bool) -> Result<(), Error> {
self.sg_tape.format_media(fast)
}
fn read_next_file<'a>(&'a mut self) -> Result<Option<Box<dyn TapeRead + 'a>>, std::io::Error> {

View File

@ -100,9 +100,48 @@ impl SgTape {
scsi_inquiry(&mut self.file)
}
pub fn erase_media(&mut self, _fast: bool) -> Result<(), Error> {
// fixme:
unimplemented!();
/// Erase medium.
///
/// EOD is written at the current position, which marks it as end
/// of data. After the command is successfully completed, the
/// drive is positioned immediately before End Of Data (not End Of
/// Tape).
pub fn erase_media(&mut self, fast: bool) -> Result<(), Error> {
let mut sg_raw = SgRaw::new(&mut self.file, 16)?;
sg_raw.set_timeout(Self::SCSI_TAPE_DEFAULT_TIMEOUT);
let mut cmd = Vec::new();
cmd.push(0x19);
if fast {
cmd.push(0); // LONG=0
} else {
cmd.push(1); // LONG=1
}
cmd.extend(&[0, 0, 0, 0]);
sg_raw.do_command(&cmd)
.map_err(|err| format_err!("erase failed - {}", err))?;
Ok(())
}
/// Format media, single partition
pub fn format_media(&mut self, fast: bool) -> Result<(), Error> {
self.rewind()?;
let mut sg_raw = SgRaw::new(&mut self.file, 16)?;
sg_raw.set_timeout(Self::SCSI_TAPE_DEFAULT_TIMEOUT);
let mut cmd = Vec::new();
cmd.extend(&[0x04, 0, 0, 0, 0, 0]);
sg_raw.do_command(&cmd)
.map_err(|err| format_err!("erase failed - {}", err))?;
if !fast {
self.erase_media(false)?; // overwrite everything
}
Ok(())
}
pub fn rewind(&mut self) -> Result<(), Error> {

View File

@ -111,7 +111,7 @@ pub trait TapeDriver {
fn current_file_number(&mut self) -> Result<u64, Error>;
/// Completely erase the media
fn erase_media(&mut self, fast: bool) -> Result<(), Error>;
fn format_media(&mut self, fast: bool) -> Result<(), Error>;
/// Read/Open the next file
fn read_next_file<'a>(&'a mut self) -> Result<Option<Box<dyn TapeRead + 'a>>, std::io::Error>;
@ -122,11 +122,9 @@ pub trait TapeDriver {
/// Write label to tape (erase tape content)
fn label_tape(&mut self, label: &MediaLabel) -> Result<(), Error> {
self.rewind()?;
self.set_encryption(None)?;
self.erase_media(true)?;
self.format_media(true)?; // this rewinds the tape
let raw = serde_json::to_string_pretty(&serde_json::to_value(&label)?)?;

View File

@ -360,7 +360,7 @@ impl TapeDriver for VirtualTapeHandle {
}
}
fn erase_media(&mut self, _fast: bool) -> Result<(), Error> {
fn format_media(&mut self, _fast: bool) -> Result<(), Error> {
let mut status = self.load_status()?;
match status.current_tape {
Some(VirtualTapeStatus { ref name, ref mut pos }) => {

View File

@ -374,7 +374,7 @@ Ext.define('PBS.Utils', {
dircreate: [gettext('Directory Storage'), gettext('Create')],
dirremove: [gettext('Directory'), gettext('Remove')],
'eject-media': [gettext('Drive'), gettext('Eject Media')],
'erase-media': [gettext('Drive'), gettext('Erase Media')],
"format-media": [gettext('Drive'), gettext('Format media')],
garbage_collection: ['Datastore', gettext('Garbage Collect')],
'inventory-update': [gettext('Drive'), gettext('Inventory Update')],
'label-media': [gettext('Drive'), gettext('Label Media')],

View File

@ -84,11 +84,11 @@ Ext.define('PBS.TapeManagement.DriveStatus', {
}).show();
},
erase: function() {
format: function() {
let me = this;
let view = me.getView();
let driveid = view.drive;
PBS.Utils.driveCommand(driveid, 'erase-media', {
PBS.Utils.driveCommand(driveid, 'format-media', {
waitMsgTarget: view,
method: 'POST',
success: function(response) {
@ -212,9 +212,9 @@ Ext.define('PBS.TapeManagement.DriveStatus', {
},
},
{
text: gettext('Erase'),
text: gettext('Format'),
xtype: 'proxmoxButton',
handler: 'erase',
handler: 'format',
iconCls: 'fa fa-trash-o',
dangerous: true,
confirmMsg: gettext('Are you sure you want to erase the inserted tape?'),

View File

@ -11,13 +11,13 @@ Ext.define('PBS.TapeManagement.EraseWindow', {
return {};
},
title: gettext('Erase'),
title: gettext('Format/Erase'),
url: `/api2/extjs/tape/drive`,
showProgress: true,
submitUrl: function(url, values) {
let drive = values.drive;
delete values.drive;
return `${url}/${drive}/erase-media`;
return `${url}/${drive}/format-media`;
},
method: 'POST',