tape: implement format/erase
This commit is contained in:
		| @ -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', | ||||
|  | ||||
		Reference in New Issue
	
	Block a user