diff --git a/src/api2/config.rs b/src/api2/config.rs index aed73b81..34b04d55 100644 --- a/src/api2/config.rs +++ b/src/api2/config.rs @@ -6,11 +6,15 @@ pub mod remote; pub mod sync; pub mod verify; pub mod drive; +pub mod changer; const SUBDIRS: SubdirMap = &[ + ("changer", &changer::ROUTER), ("datastore", &datastore::ROUTER), ("drive", &drive::ROUTER), ("remote", &remote::ROUTER), + ("scan-changers", &changer::SCAN_CHANGERS), + ("scan-drives", &drive::SCAN_DRIVES), ("sync", &sync::ROUTER), ("verify", &verify::ROUTER) ]; diff --git a/src/api2/config/changer.rs b/src/api2/config/changer.rs new file mode 100644 index 00000000..71631ac9 --- /dev/null +++ b/src/api2/config/changer.rs @@ -0,0 +1,244 @@ +use anyhow::{bail, Error}; +use serde_json::Value; + +use proxmox::api::{api, Router, RpcEnvironment}; + +use crate::{ + config, + api2::types::{ + CHANGER_ID_SCHEMA, + LINUX_DRIVE_PATH_SCHEMA, + DriveListEntry, + ScsiTapeChanger, + TapeDeviceInfo, + }, + tape::{ + linux_tape_changer_list, + check_drive_path, + lookup_drive, + }, +}; + +#[api( + input: { + properties: { + name: { + schema: CHANGER_ID_SCHEMA, + }, + path: { + schema: LINUX_DRIVE_PATH_SCHEMA, + }, + }, + }, +)] +/// Create a new changer device +pub fn create_changer( + name: String, + path: String, +) -> Result<(), Error> { + + let _lock = config::drive::lock()?; + + let (mut config, _digest) = config::drive::config()?; + + let linux_changers = linux_tape_changer_list(); + + check_drive_path(&linux_changers, &path)?; + + if config.sections.get(&name).is_some() { + bail!("Entry '{}' already exists", name); + } + + let item = ScsiTapeChanger { + name: name.clone(), + path, + }; + + config.set_data(&name, "changer", &item)?; + + config::drive::save_config(&config)?; + + Ok(()) +} + +#[api( + input: { + properties: { + name: { + schema: CHANGER_ID_SCHEMA, + }, + }, + }, + returns: { + type: ScsiTapeChanger, + }, + +)] +/// Get tape changer configuration +pub fn get_config( + name: String, + _param: Value, + mut rpcenv: &mut dyn RpcEnvironment, +) -> Result { + + let (config, digest) = config::drive::config()?; + + let data: ScsiTapeChanger = config.lookup("changer", &name)?; + + rpcenv["digest"] = proxmox::tools::digest_to_hex(&digest).into(); + + Ok(data) +} + +#[api( + input: { + properties: {}, + }, + returns: { + description: "The list of configured changers (with config digest).", + type: Array, + items: { + type: DriveListEntry, + }, + }, +)] +/// List changers +pub fn list_changers( + _param: Value, + mut rpcenv: &mut dyn RpcEnvironment, +) -> Result, Error> { + + let (config, digest) = config::drive::config()?; + + let linux_changers = linux_tape_changer_list(); + + let changer_list: Vec = config.convert_to_typed_array("changer")?; + + let mut list = Vec::new(); + + for changer in changer_list { + let mut entry = DriveListEntry { + name: changer.name, + path: changer.path.clone(), + changer: None, + vendor: None, + model: None, + serial: None, + }; + if let Some(info) = lookup_drive(&linux_changers, &changer.path) { + entry.vendor = Some(info.vendor.clone()); + entry.model = Some(info.model.clone()); + entry.serial = Some(info.serial.clone()); + } + + list.push(entry); + } + + rpcenv["digest"] = proxmox::tools::digest_to_hex(&digest).into(); + Ok(list) +} + +#[api( + input: { + properties: { + name: { + schema: CHANGER_ID_SCHEMA, + }, + path: { + schema: LINUX_DRIVE_PATH_SCHEMA, + optional: true, + }, + }, + }, +)] +/// Update a tape changer configuration +pub fn update_changer( + name: String, + path: Option, + _param: Value, +) -> Result<(), Error> { + + let _lock = config::drive::lock()?; + + let (mut config, _digest) = config::drive::config()?; + + let mut data: ScsiTapeChanger = config.lookup("changer", &name)?; + + if let Some(path) = path { + let changers = linux_tape_changer_list(); + check_drive_path(&changers, &path)?; + data.path = path; + } + + config.set_data(&name, "changer", &data)?; + + config::drive::save_config(&config)?; + + Ok(()) +} + +#[api( + input: { + properties: { + name: { + schema: CHANGER_ID_SCHEMA, + }, + }, + }, +)] +/// Delete a tape changer configuration +pub fn delete_changer(name: String, _param: Value) -> Result<(), Error> { + + let _lock = config::drive::lock()?; + + let (mut config, _digest) = config::drive::config()?; + + match config.sections.get(&name) { + Some((section_type, _)) => { + if section_type != "changer" { + bail!("Entry '{}' exists, but is not a changer device", name); + } + config.sections.remove(&name); + }, + None => bail!("Delete changer '{}' failed - no such entry", name), + } + + config::drive::save_config(&config)?; + + Ok(()) +} + +#[api( + input: { + properties: {}, + }, + returns: { + description: "The list of autodetected tape changers.", + type: Array, + items: { + type: TapeDeviceInfo, + }, + }, +)] +/// Scan for SCSI tape changers +pub fn scan_changers(_param: Value) -> Result, Error> { + + let list = linux_tape_changer_list(); + + Ok(list) +} + +pub const SCAN_CHANGERS: Router = Router::new() + .get(&API_METHOD_SCAN_CHANGERS); + + +const ITEM_ROUTER: Router = Router::new() + .get(&API_METHOD_GET_CONFIG) + .put(&API_METHOD_UPDATE_CHANGER) + .delete(&API_METHOD_DELETE_CHANGER); + + +pub const ROUTER: Router = Router::new() + .get(&API_METHOD_LIST_CHANGERS) + .post(&API_METHOD_CREATE_CHANGER) + .match_all("name", &ITEM_ROUTER); diff --git a/src/api2/config/drive.rs b/src/api2/config/drive.rs index 6316b16b..1702e476 100644 --- a/src/api2/config/drive.rs +++ b/src/api2/config/drive.rs @@ -226,7 +226,7 @@ pub fn delete_drive(name: String, _param: Value) -> Result<(), Error> { }, )] /// Scan tape drives -pub fn scan_tape_drives(_param: Value) -> Result, Error> { +pub fn scan_drives(_param: Value) -> Result, Error> { let list = linux_tape_device_list(); @@ -234,15 +234,14 @@ pub fn scan_tape_drives(_param: Value) -> Result, Error> { } -const SCAN_DRIVES: Router = Router::new() - .get(&API_METHOD_SCAN_TAPE_DRIVES); +pub const SCAN_DRIVES: Router = Router::new() + .get(&API_METHOD_SCAN_DRIVES); const ITEM_ROUTER: Router = Router::new() .get(&API_METHOD_GET_CONFIG) .put(&API_METHOD_UPDATE_DRIVE) - .delete(&API_METHOD_DELETE_DRIVE) - .subdirs(&[("scan", &SCAN_DRIVES)]); + .delete(&API_METHOD_DELETE_DRIVE); pub const ROUTER: Router = Router::new()