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), |         permission: &Permission::Privilege(&["tape", "device", "{drive}"], PRIV_TAPE_WRITE, false), | ||||||
|     }, |     }, | ||||||
| )] | )] | ||||||
| /// Erase media. Check for label-text if given (cancels if wrong media). | /// Format media. Check for label-text if given (cancels if wrong media). | ||||||
| pub fn erase_media( | pub fn format_media( | ||||||
|     drive: String, |     drive: String, | ||||||
|     fast: Option<bool>, |     fast: Option<bool>, | ||||||
|     label_text: Option<String>, |     label_text: Option<String>, | ||||||
| @ -331,7 +331,7 @@ pub fn erase_media( | |||||||
|     let upid_str = run_drive_worker( |     let upid_str = run_drive_worker( | ||||||
|         rpcenv, |         rpcenv, | ||||||
|         drive.clone(), |         drive.clone(), | ||||||
|         "erase-media", |         "format-media", | ||||||
|         Some(drive.clone()), |         Some(drive.clone()), | ||||||
|         move |worker, config| { |         move |worker, config| { | ||||||
|             if let Some(ref label) = label_text { |             if let Some(ref label) = label_text { | ||||||
| @ -350,15 +350,15 @@ pub fn erase_media( | |||||||
|                     } |                     } | ||||||
|                     /* assume drive contains no or unrelated data */ |                     /* assume drive contains no or unrelated data */ | ||||||
|                     task_log!(worker, "unable to read media label: {}", err); |                     task_log!(worker, "unable to read media label: {}", err); | ||||||
|                     task_log!(worker, "erase anyways"); |                     task_log!(worker, "format anyways"); | ||||||
|                     handle.erase_media(fast.unwrap_or(true))?; |                     handle.format_media(fast.unwrap_or(true))?; | ||||||
|                 } |                 } | ||||||
|                 Ok((None, _)) => { |                 Ok((None, _)) => { | ||||||
|                     if let Some(label) = label_text { |                     if let Some(label) = label_text { | ||||||
|                         bail!("expected label '{}', found empty tape", label); |                         bail!("expected label '{}', found empty tape", label); | ||||||
|                     } |                     } | ||||||
|                     task_log!(worker, "found empty media - erase anyways"); |                     task_log!(worker, "found empty media - format anyways"); | ||||||
|                     handle.erase_media(fast.unwrap_or(true))?; |                     handle.format_media(fast.unwrap_or(true))?; | ||||||
|                 } |                 } | ||||||
|                 Ok((Some(media_id), _key_config)) => { |                 Ok((Some(media_id), _key_config)) => { | ||||||
|                     if let Some(label_text) = label_text { |                     if let Some(label_text) = label_text { | ||||||
| @ -391,7 +391,7 @@ pub fn erase_media( | |||||||
|                         inventory.remove_media(&media_id.label.uuid)?; |                         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 | /// 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. | /// 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( | pub fn label_media( | ||||||
|     drive: String, |     drive: String, | ||||||
|     pool: Option<String>, |     pool: Option<String>, | ||||||
| @ -528,7 +528,7 @@ pub fn label_media( | |||||||
|             drive.rewind()?; |             drive.rewind()?; | ||||||
|  |  | ||||||
|             match drive.read_next_file() { |             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 */ }, |                 Ok(None) => { /* EOF mark at BOT, assume tape is empty */ }, | ||||||
|                 Err(err) => { |                 Err(err) => { | ||||||
|                     println!("TEST {:?}", err); |                     println!("TEST {:?}", err); | ||||||
| @ -1092,7 +1092,7 @@ fn barcode_label_media_worker( | |||||||
|  |  | ||||||
|         match drive.read_next_file() { |         match drive.read_next_file() { | ||||||
|             Ok(Some(_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; |                 continue; | ||||||
|             } |             } | ||||||
|             Ok(None) => { /* EOF mark at BOT, assume tape is empty */ }, |             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) { |                 if err.is_errno(nix::errno::Errno::ENOSPC) || err.is_errno(nix::errno::Errno::EIO) { | ||||||
|                     /* assume tape is empty */ |                     /* assume tape is empty */ | ||||||
|                 } else { |                 } 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; |                     continue; | ||||||
|                 } |                 } | ||||||
|             } |             } | ||||||
| @ -1430,9 +1430,9 @@ pub const SUBDIRS: SubdirMap = &sorted!([ | |||||||
|             .post(&API_METHOD_EJECT_MEDIA) |             .post(&API_METHOD_EJECT_MEDIA) | ||||||
|     ), |     ), | ||||||
|     ( |     ( | ||||||
|         "erase-media", |         "format-media", | ||||||
|         &Router::new() |         &Router::new() | ||||||
|             .post(&API_METHOD_ERASE_MEDIA) |             .post(&API_METHOD_FORMAT_MEDIA) | ||||||
|     ), |     ), | ||||||
|     ( |     ( | ||||||
|         "export-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> { | fn erase(fast: Option<bool>, param: Value) -> Result<(), Error> { | ||||||
|  |  | ||||||
|     let mut handle = get_tape_handle(¶m)?; |     let mut handle = get_tape_handle(¶m)?; | ||||||
| @ -418,6 +418,35 @@ fn erase(fast: Option<bool>, param: Value) -> Result<(), Error> { | |||||||
|     Ok(()) |     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( | #[api( | ||||||
|    input: { |    input: { | ||||||
|         properties: { |         properties: { | ||||||
| @ -800,6 +829,7 @@ fn main() -> Result<(), Error> { | |||||||
|         .insert("eject", std_cmd(&API_METHOD_EJECT)) |         .insert("eject", std_cmd(&API_METHOD_EJECT)) | ||||||
|         .insert("eod", std_cmd(&API_METHOD_EOD)) |         .insert("eod", std_cmd(&API_METHOD_EOD)) | ||||||
|         .insert("erase", std_cmd(&API_METHOD_ERASE)) |         .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("fsf", std_cmd(&API_METHOD_FSF).arg_param(&["count"])) | ||||||
|         .insert("fsfm", std_cmd(&API_METHOD_FSFM).arg_param(&["count"])) |         .insert("fsfm", std_cmd(&API_METHOD_FSFM).arg_param(&["count"])) | ||||||
|         .insert("fsr", std_cmd(&API_METHOD_FSR).arg_param(&["count"])) |         .insert("fsr", std_cmd(&API_METHOD_FSR).arg_param(&["count"])) | ||||||
|  | |||||||
| @ -115,8 +115,8 @@ pub fn extract_drive_name( | |||||||
|        }, |        }, | ||||||
|     }, |     }, | ||||||
| )] | )] | ||||||
| /// Erase media | /// Format media | ||||||
| async fn erase_media(mut param: Value) -> Result<(), Error> { | async fn format_media(mut param: Value) -> Result<(), Error> { | ||||||
|  |  | ||||||
|     let output_format = get_output_format(¶m); |     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 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?; |     let result = client.post(&path, Some(param)).await?; | ||||||
|  |  | ||||||
|     view_task_result(&mut client, result, &output_format).await?; |     view_task_result(&mut client, result, &output_format).await?; | ||||||
| @ -992,8 +992,8 @@ fn main() { | |||||||
|                 .completion_cb("drive", complete_drive_name) |                 .completion_cb("drive", complete_drive_name) | ||||||
|         ) |         ) | ||||||
|         .insert( |         .insert( | ||||||
|             "erase", |             "format", | ||||||
|             CliCommand::new(&API_METHOD_ERASE_MEDIA) |             CliCommand::new(&API_METHOD_FORMAT_MEDIA) | ||||||
|                 .completion_cb("drive", complete_drive_name) |                 .completion_cb("drive", complete_drive_name) | ||||||
|         ) |         ) | ||||||
|         .insert( |         .insert( | ||||||
|  | |||||||
| @ -179,6 +179,10 @@ impl LtoTapeHandle { | |||||||
|         Ok(status) |         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> { |     pub fn load(&mut self) ->  Result<(), Error> { | ||||||
|         self.sg_tape.load() |         self.sg_tape.load() | ||||||
|     } |     } | ||||||
| @ -223,9 +227,8 @@ impl TapeDriver for LtoTapeHandle { | |||||||
|         self.sg_tape.current_file_number() |         self.sg_tape.current_file_number() | ||||||
|     } |     } | ||||||
|  |  | ||||||
|     fn erase_media(&mut self, fast: bool) -> Result<(), Error> { |     fn format_media(&mut self, fast: bool) -> Result<(), Error> { | ||||||
|         self.rewind()?; // important - erase from BOT |         self.sg_tape.format_media(fast) | ||||||
|         self.sg_tape.erase_media(fast) |  | ||||||
|     } |     } | ||||||
|  |  | ||||||
|     fn read_next_file<'a>(&'a mut self) -> Result<Option<Box<dyn TapeRead + 'a>>, std::io::Error> { |     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) |         scsi_inquiry(&mut self.file) | ||||||
|     } |     } | ||||||
|  |  | ||||||
|     pub fn erase_media(&mut self, _fast: bool) -> Result<(), Error> { |     /// Erase medium. | ||||||
|         // fixme: |     /// | ||||||
|         unimplemented!(); |     /// 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> { |     pub fn rewind(&mut self) -> Result<(), Error> { | ||||||
|  | |||||||
| @ -111,7 +111,7 @@ pub trait TapeDriver { | |||||||
|     fn current_file_number(&mut self) -> Result<u64, Error>; |     fn current_file_number(&mut self) -> Result<u64, Error>; | ||||||
|  |  | ||||||
|     /// Completely erase the media |     /// 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 |     /// Read/Open the next file | ||||||
|     fn read_next_file<'a>(&'a mut self) -> Result<Option<Box<dyn TapeRead + 'a>>, std::io::Error>; |     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) |     /// Write label to tape (erase tape content) | ||||||
|     fn label_tape(&mut self, label: &MediaLabel) -> Result<(), Error> { |     fn label_tape(&mut self, label: &MediaLabel) -> Result<(), Error> { | ||||||
|  |  | ||||||
|         self.rewind()?; |  | ||||||
|  |  | ||||||
|         self.set_encryption(None)?; |         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)?)?; |         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()?; |         let mut status = self.load_status()?; | ||||||
|         match status.current_tape { |         match status.current_tape { | ||||||
|             Some(VirtualTapeStatus { ref name, ref mut pos }) => { |             Some(VirtualTapeStatus { ref name, ref mut pos }) => { | ||||||
|  | |||||||
| @ -374,7 +374,7 @@ Ext.define('PBS.Utils', { | |||||||
| 	    dircreate: [gettext('Directory Storage'), gettext('Create')], | 	    dircreate: [gettext('Directory Storage'), gettext('Create')], | ||||||
| 	    dirremove: [gettext('Directory'), gettext('Remove')], | 	    dirremove: [gettext('Directory'), gettext('Remove')], | ||||||
| 	    'eject-media': [gettext('Drive'), gettext('Eject Media')], | 	    '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')], | 	    garbage_collection: ['Datastore', gettext('Garbage Collect')], | ||||||
| 	    'inventory-update': [gettext('Drive'), gettext('Inventory Update')], | 	    'inventory-update': [gettext('Drive'), gettext('Inventory Update')], | ||||||
| 	    'label-media': [gettext('Drive'), gettext('Label Media')], | 	    'label-media': [gettext('Drive'), gettext('Label Media')], | ||||||
|  | |||||||
| @ -84,11 +84,11 @@ Ext.define('PBS.TapeManagement.DriveStatus', { | |||||||
| 	    }).show(); | 	    }).show(); | ||||||
| 	}, | 	}, | ||||||
|  |  | ||||||
| 	erase: function() { | 	format: function() { | ||||||
| 	    let me = this; | 	    let me = this; | ||||||
| 	    let view = me.getView(); | 	    let view = me.getView(); | ||||||
| 	    let driveid = view.drive; | 	    let driveid = view.drive; | ||||||
| 	    PBS.Utils.driveCommand(driveid, 'erase-media', { | 	    PBS.Utils.driveCommand(driveid, 'format-media', { | ||||||
| 		waitMsgTarget: view, | 		waitMsgTarget: view, | ||||||
| 		method: 'POST', | 		method: 'POST', | ||||||
| 		success: function(response) { | 		success: function(response) { | ||||||
| @ -212,9 +212,9 @@ Ext.define('PBS.TapeManagement.DriveStatus', { | |||||||
| 	    }, | 	    }, | ||||||
| 	}, | 	}, | ||||||
| 	{ | 	{ | ||||||
| 	    text: gettext('Erase'), | 	    text: gettext('Format'), | ||||||
| 	    xtype: 'proxmoxButton', | 	    xtype: 'proxmoxButton', | ||||||
| 	    handler: 'erase', | 	    handler: 'format', | ||||||
| 	    iconCls: 'fa fa-trash-o', | 	    iconCls: 'fa fa-trash-o', | ||||||
| 	    dangerous: true, | 	    dangerous: true, | ||||||
| 	    confirmMsg: gettext('Are you sure you want to erase the inserted tape?'), | 	    confirmMsg: gettext('Are you sure you want to erase the inserted tape?'), | ||||||
|  | |||||||
| @ -11,13 +11,13 @@ Ext.define('PBS.TapeManagement.EraseWindow', { | |||||||
| 	return {}; | 	return {}; | ||||||
|     }, |     }, | ||||||
|  |  | ||||||
|     title: gettext('Erase'), |     title: gettext('Format/Erase'), | ||||||
|     url: `/api2/extjs/tape/drive`, |     url: `/api2/extjs/tape/drive`, | ||||||
|     showProgress: true, |     showProgress: true, | ||||||
|     submitUrl: function(url, values) { |     submitUrl: function(url, values) { | ||||||
| 	let drive = values.drive; | 	let drive = values.drive; | ||||||
| 	delete values.drive; | 	delete values.drive; | ||||||
| 	return `${url}/${drive}/erase-media`; | 	return `${url}/${drive}/format-media`; | ||||||
|     }, |     }, | ||||||
|  |  | ||||||
|     method: 'POST', |     method: 'POST', | ||||||
|  | |||||||
		Reference in New Issue
	
	Block a user