tape: changer - add export-slot config
This commit is contained in:
		| @ -1,7 +1,13 @@ | ||||
| use anyhow::{bail, Error}; | ||||
| use ::serde::{Deserialize, Serialize}; | ||||
| use serde_json::Value; | ||||
|  | ||||
| use proxmox::api::{api, Router, RpcEnvironment}; | ||||
| use proxmox::api::{ | ||||
|     api, | ||||
|     Router, | ||||
|     RpcEnvironment, | ||||
|     schema::parse_property_string, | ||||
| }; | ||||
|  | ||||
| use crate::{ | ||||
|     config, | ||||
| @ -9,6 +15,8 @@ use crate::{ | ||||
|         PROXMOX_CONFIG_DIGEST_SCHEMA, | ||||
|         CHANGER_NAME_SCHEMA, | ||||
|         LINUX_DRIVE_PATH_SCHEMA, | ||||
|         SLOT_ARRAY_SCHEMA, | ||||
|         EXPORT_SLOT_LIST_SCHEMA, | ||||
|         DriveListEntry, | ||||
|         ScsiTapeChanger, | ||||
|         LinuxTapeDrive, | ||||
| @ -30,6 +38,10 @@ use crate::{ | ||||
|             path: { | ||||
|                 schema: LINUX_DRIVE_PATH_SCHEMA, | ||||
|             }, | ||||
|             "export-slots": { | ||||
|                 schema: EXPORT_SLOT_LIST_SCHEMA, | ||||
|                 optional: true, | ||||
|             }, | ||||
|         }, | ||||
|     }, | ||||
| )] | ||||
| @ -37,6 +49,7 @@ use crate::{ | ||||
| pub fn create_changer( | ||||
|     name: String, | ||||
|     path: String, | ||||
|     export_slots: Option<String>, | ||||
| ) -> Result<(), Error> { | ||||
|  | ||||
|     let _lock = config::drive::lock()?; | ||||
| @ -54,6 +67,7 @@ pub fn create_changer( | ||||
|     let item = ScsiTapeChanger { | ||||
|         name: name.clone(), | ||||
|         path, | ||||
|         export_slots, | ||||
|     }; | ||||
|  | ||||
|     config.set_data(&name, "changer", &item)?; | ||||
| @ -139,6 +153,15 @@ pub fn list_changers( | ||||
|     rpcenv["digest"] = proxmox::tools::digest_to_hex(&digest).into(); | ||||
|     Ok(list) | ||||
| } | ||||
| #[api()] | ||||
| #[derive(Serialize, Deserialize)] | ||||
| #[allow(non_camel_case_types)] | ||||
| #[serde(rename_all = "kebab-case")] | ||||
| /// Deletable property name | ||||
| pub enum DeletableProperty { | ||||
|     /// Delete export-slots. | ||||
|     export_slots, | ||||
| } | ||||
|  | ||||
| #[api( | ||||
|     protected: true, | ||||
| @ -151,6 +174,18 @@ pub fn list_changers( | ||||
|                 schema: LINUX_DRIVE_PATH_SCHEMA, | ||||
|                 optional: true, | ||||
|             }, | ||||
|             "export-slots": { | ||||
|                 schema: EXPORT_SLOT_LIST_SCHEMA, | ||||
|                 optional: true, | ||||
|             }, | ||||
|             delete: { | ||||
|                 description: "List of properties to delete.", | ||||
|                 type: Array, | ||||
|                 optional: true, | ||||
|                 items: { | ||||
|                     type: DeletableProperty, | ||||
|                 }, | ||||
|             }, | ||||
|             digest: { | ||||
|                 schema: PROXMOX_CONFIG_DIGEST_SCHEMA, | ||||
|                 optional: true, | ||||
| @ -162,6 +197,8 @@ pub fn list_changers( | ||||
| pub fn update_changer( | ||||
|     name: String, | ||||
|     path: Option<String>, | ||||
|     export_slots: Option<String>, | ||||
|     delete: Option<Vec<DeletableProperty>>, | ||||
|     digest: Option<String>, | ||||
|     _param: Value, | ||||
| ) -> Result<(), Error> { | ||||
| @ -177,12 +214,42 @@ pub fn update_changer( | ||||
|  | ||||
|     let mut data: ScsiTapeChanger = config.lookup("changer", &name)?; | ||||
|  | ||||
|     if let Some(delete) = delete { | ||||
|         for delete_prop in delete { | ||||
|             match delete_prop { | ||||
|                 DeletableProperty::export_slots => { | ||||
|                     data.export_slots = None; | ||||
|                 } | ||||
|             } | ||||
|         } | ||||
|     } | ||||
|  | ||||
|     if let Some(path) = path { | ||||
|         let changers = linux_tape_changer_list(); | ||||
|         check_drive_path(&changers, &path)?; | ||||
|         data.path = path; | ||||
|     } | ||||
|  | ||||
|     if let Some(export_slots) = export_slots { | ||||
|         let slots: Value = parse_property_string( | ||||
|             &export_slots, &SLOT_ARRAY_SCHEMA | ||||
|         )?; | ||||
|         let mut slots: Vec<String> = slots | ||||
|             .as_array() | ||||
|             .unwrap() | ||||
|             .iter() | ||||
|             .map(|v| v.to_string()) | ||||
|             .collect(); | ||||
|         slots.sort(); | ||||
|  | ||||
|         if slots.is_empty() { | ||||
|             data.export_slots = None; | ||||
|         } else { | ||||
|             let slots = slots.join(","); | ||||
|             data.export_slots = Some(slots); | ||||
|         } | ||||
|     } | ||||
|  | ||||
|     config.set_data(&name, "changer", &data)?; | ||||
|  | ||||
|     config::drive::save_config(&config)?; | ||||
|  | ||||
| @ -4,7 +4,13 @@ use serde::{Deserialize, Serialize}; | ||||
|  | ||||
| use proxmox::api::{ | ||||
|     api, | ||||
|     schema::{Schema, StringSchema}, | ||||
|     schema::{ | ||||
|         Schema, | ||||
|         ApiStringFormat, | ||||
|         ArraySchema, | ||||
|         IntegerSchema, | ||||
|         StringSchema, | ||||
|     }, | ||||
| }; | ||||
|  | ||||
| use crate::api2::types::PROXMOX_SAFE_ID_FORMAT; | ||||
| @ -25,6 +31,20 @@ pub const MEDIA_LABEL_SCHEMA: Schema = StringSchema::new("Media Label/Barcode.") | ||||
|     .max_length(32) | ||||
|     .schema(); | ||||
|  | ||||
| pub const SLOT_ARRAY_SCHEMA: Schema = ArraySchema::new( | ||||
|     "Slot list.", &IntegerSchema::new("Slot number") | ||||
|         .minimum(1) | ||||
|         .schema()) | ||||
|     .schema(); | ||||
|  | ||||
| pub const EXPORT_SLOT_LIST_SCHEMA: Schema = StringSchema::new(r###"\ | ||||
| A list of slot numbers, comma separated. Those slots are reserved for | ||||
| Import/Export, i.e. any media in those slots are considered to be | ||||
| 'offline'. | ||||
| "###) | ||||
| .format(&ApiStringFormat::PropertyString(&SLOT_ARRAY_SCHEMA)) | ||||
| .schema(); | ||||
|  | ||||
| #[api( | ||||
|     properties: { | ||||
|         name: { | ||||
| @ -33,13 +53,20 @@ pub const MEDIA_LABEL_SCHEMA: Schema = StringSchema::new("Media Label/Barcode.") | ||||
|         path: { | ||||
|             schema: SCSI_CHANGER_PATH_SCHEMA, | ||||
|         }, | ||||
|     } | ||||
|         "export-slots": { | ||||
|             schema: EXPORT_SLOT_LIST_SCHEMA, | ||||
|             optional: true, | ||||
|         }, | ||||
|     }, | ||||
| )] | ||||
| #[derive(Serialize,Deserialize)] | ||||
| #[serde(rename_all = "kebab-case")] | ||||
| /// SCSI tape changer | ||||
| pub struct ScsiTapeChanger { | ||||
|     pub name: String, | ||||
|     pub path: String, | ||||
|     #[serde(skip_serializing_if="Option::is_none")] | ||||
|     pub export_slots: Option<String>, | ||||
| } | ||||
|  | ||||
| #[api()] | ||||
|  | ||||
| @ -173,6 +173,7 @@ fn get_config( | ||||
|     let options = default_table_format_options() | ||||
|         .column(ColumnConfig::new("name")) | ||||
|         .column(ColumnConfig::new("path")) | ||||
|         .column(ColumnConfig::new("export-slots")) | ||||
|         ; | ||||
|  | ||||
|     format_and_print_result_full(&mut data, &info.returns, &output_format, &options); | ||||
|  | ||||
		Reference in New Issue
	
	Block a user