]> git.proxmox.com Git - proxmox-backup.git/blobdiff - src/api2/config/changer.rs
api: tape: don't allow overwriting of ids in changer/drive config
[proxmox-backup.git] / src / api2 / config / changer.rs
index e6673bcdb88b31afeae747a848c1210fbce390c3..912866973e0ef840ed70d80039d978b7c36e7026 100644 (file)
@@ -1,18 +1,17 @@
-use anyhow::{bail, Error};
 use ::serde::{Deserialize, Serialize};
-use serde_json::Value;
+use anyhow::Error;
 use hex::FromHex;
+use serde_json::Value;
 
-use proxmox_router::{Router, RpcEnvironment, Permission};
-use proxmox_schema::{api, parse_property_string};
+use proxmox_router::{http_bail, Permission, Router, RpcEnvironment};
+use proxmox_schema::{api, param_bail};
 
 use pbs_api_types::{
-    Authid, ScsiTapeChanger, ScsiTapeChangerUpdater, LtoTapeDrive,
-    PROXMOX_CONFIG_DIGEST_SCHEMA, CHANGER_NAME_SCHEMA, SLOT_ARRAY_SCHEMA,
-    PRIV_TAPE_AUDIT, PRIV_TAPE_MODIFY,
+    Authid, LtoTapeDrive, ScsiTapeChanger, ScsiTapeChangerUpdater, CHANGER_NAME_SCHEMA,
+    PRIV_TAPE_AUDIT, PRIV_TAPE_MODIFY, PROXMOX_CONFIG_DIGEST_SCHEMA, SLOT_ARRAY_SCHEMA,
 };
 use pbs_config::CachedUserInfo;
-use pbs_tape::linux_list_drives::{linux_tape_changer_list, check_drive_path};
+use pbs_tape::linux_list_drives::{check_drive_path, linux_tape_changer_list};
 
 #[api(
     protected: true,
@@ -30,11 +29,14 @@ use pbs_tape::linux_list_drives::{linux_tape_changer_list, check_drive_path};
 )]
 /// Create a new changer device
 pub fn create_changer(config: ScsiTapeChanger) -> Result<(), Error> {
-
     let _lock = pbs_config::drive::lock()?;
 
     let (mut section_config, _digest) = pbs_config::drive::config()?;
 
+    if section_config.sections.get(&config.name).is_some() {
+        param_bail!("name", "Entry '{}' already exists", config.name);
+    }
+
     let linux_changers = linux_tape_changer_list();
 
     check_drive_path(&linux_changers, &config.path)?;
@@ -42,12 +44,13 @@ pub fn create_changer(config: ScsiTapeChanger) -> Result<(), Error> {
     let existing: Vec<ScsiTapeChanger> = section_config.convert_to_typed_array("changer")?;
 
     for changer in existing {
-        if changer.name == config.name {
-            bail!("Entry '{}' already exists", config.name);
-        }
-
         if changer.path == config.path {
-            bail!("Path '{}' already in use by '{}'", config.path, changer.name);
+            param_bail!(
+                "path",
+                "Path '{}' already in use by '{}'",
+                config.path,
+                changer.name
+            );
         }
     }
 
@@ -77,14 +80,13 @@ pub fn create_changer(config: ScsiTapeChanger) -> Result<(), Error> {
 pub fn get_config(
     name: String,
     _param: Value,
-    mut rpcenv: &mut dyn RpcEnvironment,
+    rpcenv: &mut dyn RpcEnvironment,
 ) -> Result<ScsiTapeChanger, Error> {
-
     let (config, digest) = pbs_config::drive::config()?;
 
     let data: ScsiTapeChanger = config.lookup("changer", &name)?;
 
-    rpcenv["digest"] = hex::encode(&digest).into();
+    rpcenv["digest"] = hex::encode(digest).into();
 
     Ok(data)
 }
@@ -108,7 +110,7 @@ pub fn get_config(
 /// List changers
 pub fn list_changers(
     _param: Value,
-    mut rpcenv: &mut dyn RpcEnvironment,
+    rpcenv: &mut dyn RpcEnvironment,
 ) -> Result<Vec<ScsiTapeChanger>, Error> {
     let auth_id: Authid = rpcenv.get_auth_id().unwrap().parse()?;
     let user_info = CachedUserInfo::new()?;
@@ -125,18 +127,19 @@ pub fn list_changers(
         })
         .collect();
 
-    rpcenv["digest"] = hex::encode(&digest).into();
+    rpcenv["digest"] = hex::encode(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,
+    ExportSlots,
+    /// Delete eject-before-unload.
+    EjectBeforeUnload,
 }
 
 #[api(
@@ -176,7 +179,6 @@ pub fn update_changer(
     digest: Option<String>,
     _param: Value,
 ) -> Result<(), Error> {
-
     let _lock = pbs_config::drive::lock()?;
 
     let (mut config, expected_digest) = pbs_config::drive::config()?;
@@ -191,9 +193,12 @@ pub fn update_changer(
     if let Some(delete) = delete {
         for delete_prop in delete {
             match delete_prop {
-                DeletableProperty::export_slots => {
+                DeletableProperty::ExportSlots => {
                     data.export_slots = None;
                 }
+                DeletableProperty::EjectBeforeUnload => {
+                    data.eject_before_unload = None;
+                }
             }
         }
     }
@@ -205,9 +210,7 @@ pub fn update_changer(
     }
 
     if let Some(export_slots) = update.export_slots {
-        let slots: Value = parse_property_string(
-            &export_slots, &SLOT_ARRAY_SCHEMA
-        )?;
+        let slots: Value = SLOT_ARRAY_SCHEMA.parse_property_string(&export_slots)?;
         let mut slots: Vec<String> = slots
             .as_array()
             .unwrap()
@@ -224,6 +227,10 @@ pub fn update_changer(
         }
     }
 
+    if let Some(eject_before_unload) = update.eject_before_unload {
+        data.eject_before_unload = Some(eject_before_unload);
+    }
+
     config.set_data(&name, "changer", &data)?;
 
     pbs_config::drive::save_config(&config)?;
@@ -246,7 +253,6 @@ pub fn update_changer(
 )]
 /// Delete a tape changer configuration
 pub fn delete_changer(name: String, _param: Value) -> Result<(), Error> {
-
     let _lock = pbs_config::drive::lock()?;
 
     let (mut config, _digest) = pbs_config::drive::config()?;
@@ -254,18 +260,31 @@ pub fn delete_changer(name: String, _param: Value) -> Result<(), Error> {
     match config.sections.get(&name) {
         Some((section_type, _)) => {
             if section_type != "changer" {
-                bail!("Entry '{}' exists, but is not a changer device", name);
+                param_bail!(
+                    "name",
+                    "Entry '{}' exists, but is not a changer device",
+                    name
+                );
             }
             config.sections.remove(&name);
-        },
-        None => bail!("Delete changer '{}' failed - no such entry", name),
+        }
+        None => http_bail!(
+            NOT_FOUND,
+            "Delete changer '{}' failed - no such entry",
+            name
+        ),
     }
 
     let drive_list: Vec<LtoTapeDrive> = config.convert_to_typed_array("lto")?;
     for drive in drive_list {
         if let Some(changer) = drive.changer {
             if changer == name {
-                bail!("Delete changer '{}' failed - used by drive '{}'", name, drive.name);
+                param_bail!(
+                    "name",
+                    "Delete changer '{}' failed - used by drive '{}'",
+                    name,
+                    drive.name
+                );
             }
         }
     }
@@ -280,7 +299,6 @@ const ITEM_ROUTER: Router = Router::new()
     .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)