From 05e90d64630daa4d2445927ce38aa8a3dafaf51a Mon Sep 17 00:00:00 2001 From: Dietmar Maurer Date: Thu, 10 Dec 2020 10:52:27 +0100 Subject: [PATCH] tape: add media pool config api --- src/api2/config.rs | 2 + src/api2/config/media_pool.rs | 252 ++++++++++++++++++++++++++++++++++ 2 files changed, 254 insertions(+) create mode 100644 src/api2/config/media_pool.rs diff --git a/src/api2/config.rs b/src/api2/config.rs index 173c8783..0e269b77 100644 --- a/src/api2/config.rs +++ b/src/api2/config.rs @@ -7,11 +7,13 @@ pub mod sync; pub mod verify; pub mod drive; pub mod changer; +pub mod media_pool; const SUBDIRS: SubdirMap = &[ ("changer", &changer::ROUTER), ("datastore", &datastore::ROUTER), ("drive", &drive::ROUTER), + ("media-pool", &media_pool::ROUTER), ("remote", &remote::ROUTER), ("sync", &sync::ROUTER), ("verify", &verify::ROUTER) diff --git a/src/api2/config/media_pool.rs b/src/api2/config/media_pool.rs new file mode 100644 index 00000000..65cb4687 --- /dev/null +++ b/src/api2/config/media_pool.rs @@ -0,0 +1,252 @@ +use anyhow::{bail, Error}; +use ::serde::{Deserialize, Serialize}; + +use proxmox::{ + api::{ + api, + Router, + RpcEnvironment, + }, +}; + +use crate::{ + api2::types::{ + DRIVE_ID_SCHEMA, + MEDIA_POOL_NAME_SCHEMA, + MEDIA_SET_NAMING_TEMPLATE_SCHEMA, + MEDIA_SET_ALLOCATION_POLICY_SCHEMA, + MEDIA_RETENTION_POLICY_SCHEMA, + MediaPoolConfig, + }, + config::{ + self, + drive::{ + check_drive_exists, + }, + }, +}; + +#[api( + input: { + properties: { + name: { + schema: MEDIA_POOL_NAME_SCHEMA, + }, + drive: { + schema: DRIVE_ID_SCHEMA, + }, + allocation: { + schema: MEDIA_SET_ALLOCATION_POLICY_SCHEMA, + optional: true, + }, + retention: { + schema: MEDIA_RETENTION_POLICY_SCHEMA, + optional: true, + }, + template: { + schema: MEDIA_SET_NAMING_TEMPLATE_SCHEMA, + optional: true, + }, + }, + }, +)] +/// Create a new media pool +fn create_pool( + name: String, + drive: String, + allocation: Option, + retention: Option, + template: Option, +) -> Result<(), Error> { + + let _lock = config::media_pool::lock()?; + + let (mut config, _digest) = config::media_pool::config()?; + + if config.sections.get(&name).is_some() { + bail!("Media pool '{}' already exists", name); + } + + let (drive_config, _) = config::drive::config()?; + check_drive_exists(&drive_config, &drive)?; + + let item = MediaPoolConfig { + name: name.clone(), + drive, + allocation, + retention, + template, + }; + + config.set_data(&name, "pool", &item)?; + + config::media_pool::save_config(&config)?; + + Ok(()) +} + +#[api( + returns: { + description: "The list of configured media pools (with config digest).", + type: Array, + items: { + type: MediaPoolConfig, + }, + }, +)] +/// List media pools +fn list_pools( + mut rpcenv: &mut dyn RpcEnvironment, +) -> Result, Error> { + + let (config, digest) = config::media_pool::config()?; + + let list = config.convert_to_typed_array("pool")?; + + rpcenv["digest"] = proxmox::tools::digest_to_hex(&digest).into(); + + Ok(list) +} + +#[api( + input: { + properties: { + name: { + schema: MEDIA_POOL_NAME_SCHEMA, + }, + }, + }, + returns: { + type: MediaPoolConfig, + }, +)] +/// Get media pool configuration +fn get_config(name: String) -> Result { + + let (config, _digest) = config::media_pool::config()?; + + let data: MediaPoolConfig = config.lookup("pool", &name)?; + + Ok(data) +} + +#[api()] +#[derive(Serialize, Deserialize)] +#[allow(non_camel_case_types)] +/// Deletable property name +enum DeletableProperty { + /// Delete media set allocation policy. + allocation, + /// Delete pool retention policy + retention, + /// Delete media set naming template + template, +} + +#[api( + input: { + properties: { + name: { + schema: MEDIA_POOL_NAME_SCHEMA, + }, + drive: { + schema: DRIVE_ID_SCHEMA, + optional: true, + }, + allocation: { + schema: MEDIA_SET_ALLOCATION_POLICY_SCHEMA, + optional: true, + }, + retention: { + schema: MEDIA_RETENTION_POLICY_SCHEMA, + optional: true, + }, + template: { + schema: MEDIA_SET_NAMING_TEMPLATE_SCHEMA, + optional: true, + }, + delete: { + description: "List of properties to delete.", + type: Array, + optional: true, + items: { + type: DeletableProperty, + } + }, + }, + }, +)] +/// Update media pool settings +fn update_pool( + name: String, + drive: Option, + allocation: Option, + retention: Option, + template: Option, + delete: Option>, +) -> Result<(), Error> { + + let _lock = config::media_pool::lock()?; + + let (mut config, _digest) = config::media_pool::config()?; + + let mut data: MediaPoolConfig = config.lookup("pool", &name)?; + + if let Some(delete) = delete { + for delete_prop in delete { + match delete_prop { + DeletableProperty::allocation => { data.allocation = None; }, + DeletableProperty::retention => { data.retention = None; }, + DeletableProperty::template => { data.template = None; }, + } + } + } + + if let Some(drive) = drive { data.drive = drive; } + if allocation.is_some() { data.allocation = allocation; } + if retention.is_some() { data.retention = retention; } + if template.is_some() { data.template = template; } + + config.set_data(&name, "pool", &data)?; + + config::media_pool::save_config(&config)?; + + Ok(()) +} + +#[api( + input: { + properties: { + name: { + schema: MEDIA_POOL_NAME_SCHEMA, + }, + }, + }, +)] +/// Delete a media pool configuration +fn delete_pool(name: String) -> Result<(), Error> { + + let _lock = config::media_pool::lock()?; + + let (mut config, _digest) = config::media_pool::config()?; + + match config.sections.get(&name) { + Some(_) => { config.sections.remove(&name); }, + None => bail!("delete pool '{}' failed - no such pool", name), + } + + config::media_pool::save_config(&config)?; + + Ok(()) +} + +const ITEM_ROUTER: Router = Router::new() + .get(&API_METHOD_GET_CONFIG) + .put(&API_METHOD_UPDATE_POOL) + .delete(&API_METHOD_DELETE_POOL); + + +pub const ROUTER: Router = Router::new() + .get(&API_METHOD_LIST_POOLS) + .post(&API_METHOD_CREATE_POOL) + .match_all("name", &ITEM_ROUTER);