]> git.proxmox.com Git - proxmox-backup.git/blobdiff - src/api2/config/changer.rs
update to proxmox-sys 0.2 crate
[proxmox-backup.git] / src / api2 / config / changer.rs
index f97b6d05b56fe1a06bb1780fc4f19d24f74870fd..e6673bcdb88b31afeae747a848c1210fbce390c3 100644 (file)
@@ -1,62 +1,59 @@
 use anyhow::{bail, Error};
+use ::serde::{Deserialize, Serialize};
 use serde_json::Value;
+use hex::FromHex;
 
-use proxmox::api::{api, Router, RpcEnvironment};
+use proxmox_router::{Router, RpcEnvironment, Permission};
+use proxmox_schema::{api, parse_property_string};
 
-use crate::{
-    config,
-    api2::types::{
-        PROXMOX_CONFIG_DIGEST_SCHEMA,
-        CHANGER_ID_SCHEMA,
-        LINUX_DRIVE_PATH_SCHEMA,
-        DriveListEntry,
-        ScsiTapeChanger,
-    },
-    tape::{
-        linux_tape_changer_list,
-        check_drive_path,
-        lookup_drive,
-    },
+use pbs_api_types::{
+    Authid, ScsiTapeChanger, ScsiTapeChangerUpdater, LtoTapeDrive,
+    PROXMOX_CONFIG_DIGEST_SCHEMA, CHANGER_NAME_SCHEMA, SLOT_ARRAY_SCHEMA,
+    PRIV_TAPE_AUDIT, PRIV_TAPE_MODIFY,
 };
+use pbs_config::CachedUserInfo;
+use pbs_tape::linux_list_drives::{linux_tape_changer_list, check_drive_path};
 
 #[api(
+    protected: true,
     input: {
         properties: {
-            name: {
-                schema: CHANGER_ID_SCHEMA,
-            },
-            path: {
-                schema: LINUX_DRIVE_PATH_SCHEMA,
+            config: {
+                type: ScsiTapeChanger,
+                flatten: true,
             },
         },
     },
+    access: {
+        permission: &Permission::Privilege(&["tape", "device"], PRIV_TAPE_MODIFY, false),
+    },
 )]
 /// Create a new changer device
-pub fn create_changer(
-    name: String,
-    path: String,
-) -> Result<(), Error> {
+pub fn create_changer(config: ScsiTapeChanger) -> Result<(), Error> {
 
-    let _lock = config::drive::lock()?;
+    let _lock = pbs_config::drive::lock()?;
 
-    let (mut config, _digest) = config::drive::config()?;
+    let (mut section_config, _digest) = pbs_config::drive::config()?;
 
     let linux_changers = linux_tape_changer_list();
 
-    check_drive_path(&linux_changers, &path)?;
+    check_drive_path(&linux_changers, &config.path)?;
 
-    if config.sections.get(&name).is_some() {
-        bail!("Entry '{}' already exists", name);
-    }
+    let existing: Vec<ScsiTapeChanger> = section_config.convert_to_typed_array("changer")?;
 
-    let item = ScsiTapeChanger {
-        name: name.clone(),
-        path,
-    };
+    for changer in existing {
+        if changer.name == config.name {
+            bail!("Entry '{}' already exists", config.name);
+        }
 
-    config.set_data(&name, "changer", &item)?;
+        if changer.path == config.path {
+            bail!("Path '{}' already in use by '{}'", config.path, changer.name);
+        }
+    }
 
-    config::drive::save_config(&config)?;
+    section_config.set_data(&config.name, "changer", &config)?;
+
+    pbs_config::drive::save_config(&section_config)?;
 
     Ok(())
 }
@@ -65,14 +62,16 @@ pub fn create_changer(
     input: {
         properties: {
             name: {
-                schema: CHANGER_ID_SCHEMA,
+                schema: CHANGER_NAME_SCHEMA,
             },
         },
     },
     returns: {
         type: ScsiTapeChanger,
     },
-
+    access: {
+        permission: &Permission::Privilege(&["tape", "device", "{name}"], PRIV_TAPE_AUDIT, false),
+    },
 )]
 /// Get tape changer configuration
 pub fn get_config(
@@ -81,11 +80,11 @@ pub fn get_config(
     mut rpcenv: &mut dyn RpcEnvironment,
 ) -> Result<ScsiTapeChanger, Error> {
 
-    let (config, digest) = config::drive::config()?;
+    let (config, digest) = pbs_config::drive::config()?;
 
     let data: ScsiTapeChanger = config.lookup("changer", &name)?;
 
-    rpcenv["digest"] = proxmox::tools::digest_to_hex(&digest).into();
+    rpcenv["digest"] = hex::encode(&digest).into();
 
     Ok(data)
 }
@@ -98,55 +97,66 @@ pub fn get_config(
         description: "The list of configured changers (with config digest).",
         type: Array,
         items: {
-            type: DriveListEntry,
+            type: ScsiTapeChanger,
         },
     },
+    access: {
+        description: "List configured tape changer filtered by Tape.Audit privileges",
+        permission: &Permission::Anybody,
+    },
 )]
 /// List changers
 pub fn list_changers(
     _param: Value,
     mut rpcenv: &mut dyn RpcEnvironment,
-) -> Result<Vec<DriveListEntry>, Error> {
+) -> Result<Vec<ScsiTapeChanger>, Error> {
+    let auth_id: Authid = rpcenv.get_auth_id().unwrap().parse()?;
+    let user_info = CachedUserInfo::new()?;
 
-    let (config, digest) = config::drive::config()?;
+    let (config, digest) = pbs_config::drive::config()?;
 
-    let linux_changers = linux_tape_changer_list();
+    let list: Vec<ScsiTapeChanger> = config.convert_to_typed_array("changer")?;
 
-    let changer_list: Vec<ScsiTapeChanger> = 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());
-        }
+    let list = list
+        .into_iter()
+        .filter(|changer| {
+            let privs = user_info.lookup_privs(&auth_id, &["tape", "device", &changer.name]);
+            privs & PRIV_TAPE_AUDIT != 0
+        })
+        .collect();
 
-        list.push(entry);
-    }
+    rpcenv["digest"] = hex::encode(&digest).into();
 
-    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,
     input: {
         properties: {
             name: {
-                schema: CHANGER_ID_SCHEMA,
+                schema: CHANGER_NAME_SCHEMA,
             },
-            path: {
-                schema: LINUX_DRIVE_PATH_SCHEMA,
+            update: {
+                type: ScsiTapeChangerUpdater,
+                flatten: true,
+            },
+            delete: {
+                description: "List of properties to delete.",
+                type: Array,
                 optional: true,
+                items: {
+                    type: DeletableProperty,
+                },
             },
             digest: {
                 schema: PROXMOX_CONFIG_DIGEST_SCHEMA,
@@ -154,54 +164,92 @@ pub fn list_changers(
             },
          },
     },
+    access: {
+        permission: &Permission::Privilege(&["tape", "device", "{name}"], PRIV_TAPE_MODIFY, false),
+    },
 )]
 /// Update a tape changer configuration
 pub fn update_changer(
     name: String,
-    path: Option<String>,
+    update: ScsiTapeChangerUpdater,
+    delete: Option<Vec<DeletableProperty>>,
     digest: Option<String>,
     _param: Value,
 ) -> Result<(), Error> {
 
-    let _lock = config::drive::lock()?;
+    let _lock = pbs_config::drive::lock()?;
 
-    let (mut config, expected_digest) = config::drive::config()?;
+    let (mut config, expected_digest) = pbs_config::drive::config()?;
 
     if let Some(ref digest) = digest {
-        let digest = proxmox::tools::hex_to_digest(digest)?;
+        let digest = <[u8; 32]>::from_hex(digest)?;
         crate::tools::detect_modified_configuration_file(&digest, &expected_digest)?;
     }
 
     let mut data: ScsiTapeChanger = config.lookup("changer", &name)?;
 
-    if let Some(path) = path {
+    if let Some(delete) = delete {
+        for delete_prop in delete {
+            match delete_prop {
+                DeletableProperty::export_slots => {
+                    data.export_slots = None;
+                }
+            }
+        }
+    }
+
+    if let Some(path) = update.path {
         let changers = linux_tape_changer_list();
         check_drive_path(&changers, &path)?;
         data.path = path;
     }
 
+    if let Some(export_slots) = update.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)?;
+    pbs_config::drive::save_config(&config)?;
 
     Ok(())
 }
 
 #[api(
+    protected: true,
     input: {
         properties: {
             name: {
-                schema: CHANGER_ID_SCHEMA,
+                schema: CHANGER_NAME_SCHEMA,
             },
         },
     },
+    access: {
+        permission: &Permission::Privilege(&["tape", "device", "{name}"], PRIV_TAPE_MODIFY, false),
+    },
 )]
 /// Delete a tape changer configuration
 pub fn delete_changer(name: String, _param: Value) -> Result<(), Error> {
 
-    let _lock = config::drive::lock()?;
+    let _lock = pbs_config::drive::lock()?;
 
-    let (mut config, _digest) = config::drive::config()?;
+    let (mut config, _digest) = pbs_config::drive::config()?;
 
     match config.sections.get(&name) {
         Some((section_type, _)) => {
@@ -213,7 +261,16 @@ pub fn delete_changer(name: String, _param: Value) -> Result<(), Error> {
         None => bail!("Delete changer '{}' failed - no such entry", name),
     }
 
-    config::drive::save_config(&config)?;
+    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);
+            }
+        }
+    }
+
+    pbs_config::drive::save_config(&config)?;
 
     Ok(())
 }