tape: implement format/erase
This commit is contained in:
parent
a79082a0dd
commit
e29f456efc
@ -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",
|
||||
|
@ -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(¶m)?;
|
||||
@ -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(¶m)?;
|
||||
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"]))
|
||||
|
@ -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(¶m);
|
||||
|
||||
@ -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(
|
||||
|
@ -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> {
|
||||
|
@ -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> {
|
||||
|
@ -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)?)?;
|
||||
|
||||
|
@ -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 }) => {
|
||||
|
@ -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')],
|
||||
|
@ -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?'),
|
||||
|
@ -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',
|
||||
|
Loading…
Reference in New Issue
Block a user