2020-12-10 09:52:27 +00:00
|
|
|
use anyhow::{bail, Error};
|
|
|
|
use ::serde::{Deserialize, Serialize};
|
|
|
|
|
|
|
|
use proxmox::{
|
|
|
|
api::{
|
|
|
|
api,
|
|
|
|
Router,
|
|
|
|
RpcEnvironment,
|
2021-03-03 11:10:00 +00:00
|
|
|
Permission,
|
2020-12-10 09:52:27 +00:00
|
|
|
},
|
|
|
|
};
|
|
|
|
|
|
|
|
use crate::{
|
|
|
|
api2::types::{
|
2021-03-03 11:10:00 +00:00
|
|
|
Authid,
|
2020-12-10 09:52:27 +00:00
|
|
|
MEDIA_POOL_NAME_SCHEMA,
|
|
|
|
MEDIA_SET_NAMING_TEMPLATE_SCHEMA,
|
|
|
|
MEDIA_SET_ALLOCATION_POLICY_SCHEMA,
|
|
|
|
MEDIA_RETENTION_POLICY_SCHEMA,
|
2021-01-18 07:16:44 +00:00
|
|
|
TAPE_ENCRYPTION_KEY_FINGERPRINT_SCHEMA,
|
2021-03-02 10:42:58 +00:00
|
|
|
SINGLE_LINE_COMMENT_SCHEMA,
|
2020-12-10 09:52:27 +00:00
|
|
|
MediaPoolConfig,
|
|
|
|
},
|
2021-03-03 11:10:00 +00:00
|
|
|
config::{
|
|
|
|
self,
|
|
|
|
cached_user_info::CachedUserInfo,
|
|
|
|
acl::{
|
|
|
|
PRIV_TAPE_AUDIT,
|
|
|
|
PRIV_TAPE_MODIFY,
|
|
|
|
},
|
|
|
|
},
|
2020-12-10 09:52:27 +00:00
|
|
|
};
|
|
|
|
|
|
|
|
#[api(
|
2021-01-28 14:42:32 +00:00
|
|
|
protected: true,
|
2020-12-10 09:52:27 +00:00
|
|
|
input: {
|
|
|
|
properties: {
|
2021-03-02 10:42:58 +00:00
|
|
|
config: {
|
|
|
|
type: MediaPoolConfig,
|
|
|
|
flatten: true,
|
2021-01-18 07:16:44 +00:00
|
|
|
},
|
2020-12-10 09:52:27 +00:00
|
|
|
},
|
|
|
|
},
|
2021-03-03 11:10:00 +00:00
|
|
|
access: {
|
|
|
|
permission: &Permission::Privilege(&["tape", "pool"], PRIV_TAPE_MODIFY, false),
|
|
|
|
},
|
2020-12-10 09:52:27 +00:00
|
|
|
)]
|
|
|
|
/// Create a new media pool
|
2020-12-10 10:08:29 +00:00
|
|
|
pub fn create_pool(
|
2021-03-02 10:42:58 +00:00
|
|
|
config: MediaPoolConfig,
|
2020-12-10 09:52:27 +00:00
|
|
|
) -> Result<(), Error> {
|
|
|
|
|
|
|
|
let _lock = config::media_pool::lock()?;
|
|
|
|
|
2021-03-02 10:42:58 +00:00
|
|
|
let (mut section_config, _digest) = config::media_pool::config()?;
|
2020-12-10 09:52:27 +00:00
|
|
|
|
2021-03-02 10:42:58 +00:00
|
|
|
if section_config.sections.get(&config.name).is_some() {
|
|
|
|
bail!("Media pool '{}' already exists", config.name);
|
2020-12-10 09:52:27 +00:00
|
|
|
}
|
|
|
|
|
2021-03-02 10:42:58 +00:00
|
|
|
section_config.set_data(&config.name, "pool", &config)?;
|
2020-12-10 09:52:27 +00:00
|
|
|
|
2021-03-02 10:42:58 +00:00
|
|
|
config::media_pool::save_config(§ion_config)?;
|
2020-12-10 09:52:27 +00:00
|
|
|
|
|
|
|
Ok(())
|
|
|
|
}
|
|
|
|
|
|
|
|
#[api(
|
|
|
|
returns: {
|
|
|
|
description: "The list of configured media pools (with config digest).",
|
|
|
|
type: Array,
|
|
|
|
items: {
|
|
|
|
type: MediaPoolConfig,
|
|
|
|
},
|
|
|
|
},
|
2021-03-03 11:10:00 +00:00
|
|
|
access: {
|
|
|
|
description: "List configured media pools filtered by Tape.Audit privileges",
|
|
|
|
permission: &Permission::Anybody,
|
|
|
|
},
|
2020-12-10 09:52:27 +00:00
|
|
|
)]
|
|
|
|
/// List media pools
|
2020-12-10 10:08:29 +00:00
|
|
|
pub fn list_pools(
|
2020-12-10 09:52:27 +00:00
|
|
|
mut rpcenv: &mut dyn RpcEnvironment,
|
|
|
|
) -> Result<Vec<MediaPoolConfig>, Error> {
|
2021-03-03 11:10:00 +00:00
|
|
|
let auth_id: Authid = rpcenv.get_auth_id().unwrap().parse()?;
|
|
|
|
let user_info = CachedUserInfo::new()?;
|
2020-12-10 09:52:27 +00:00
|
|
|
|
|
|
|
let (config, digest) = config::media_pool::config()?;
|
|
|
|
|
2021-03-03 11:10:00 +00:00
|
|
|
let list = config.convert_to_typed_array::<MediaPoolConfig>("pool")?;
|
|
|
|
|
|
|
|
let list = list
|
|
|
|
.into_iter()
|
|
|
|
.filter(|pool| {
|
|
|
|
let privs = user_info.lookup_privs(&auth_id, &["tape", "pool", &pool.name]);
|
|
|
|
privs & PRIV_TAPE_AUDIT != 0
|
|
|
|
})
|
|
|
|
.collect();
|
2020-12-10 09:52:27 +00:00
|
|
|
|
|
|
|
rpcenv["digest"] = proxmox::tools::digest_to_hex(&digest).into();
|
|
|
|
|
|
|
|
Ok(list)
|
|
|
|
}
|
|
|
|
|
|
|
|
#[api(
|
|
|
|
input: {
|
|
|
|
properties: {
|
|
|
|
name: {
|
|
|
|
schema: MEDIA_POOL_NAME_SCHEMA,
|
|
|
|
},
|
|
|
|
},
|
|
|
|
},
|
|
|
|
returns: {
|
|
|
|
type: MediaPoolConfig,
|
|
|
|
},
|
2021-03-03 11:10:00 +00:00
|
|
|
access: {
|
|
|
|
permission: &Permission::Privilege(&["tape", "pool", "{name}"], PRIV_TAPE_AUDIT, false),
|
|
|
|
},
|
2020-12-10 09:52:27 +00:00
|
|
|
)]
|
|
|
|
/// Get media pool configuration
|
2020-12-10 10:08:29 +00:00
|
|
|
pub fn get_config(name: String) -> Result<MediaPoolConfig, Error> {
|
2020-12-10 09:52:27 +00:00
|
|
|
|
|
|
|
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
|
2020-12-10 10:08:29 +00:00
|
|
|
pub enum DeletableProperty {
|
2020-12-10 09:52:27 +00:00
|
|
|
/// Delete media set allocation policy.
|
|
|
|
allocation,
|
|
|
|
/// Delete pool retention policy
|
|
|
|
retention,
|
|
|
|
/// Delete media set naming template
|
|
|
|
template,
|
2021-01-18 07:16:44 +00:00
|
|
|
/// Delete encryption fingerprint
|
|
|
|
encrypt,
|
2021-03-02 10:42:58 +00:00
|
|
|
/// Delete comment
|
|
|
|
comment,
|
2020-12-10 09:52:27 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
#[api(
|
2021-01-28 14:42:32 +00:00
|
|
|
protected: true,
|
2020-12-10 09:52:27 +00:00
|
|
|
input: {
|
|
|
|
properties: {
|
|
|
|
name: {
|
|
|
|
schema: MEDIA_POOL_NAME_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,
|
|
|
|
},
|
2021-01-18 07:16:44 +00:00
|
|
|
encrypt: {
|
|
|
|
schema: TAPE_ENCRYPTION_KEY_FINGERPRINT_SCHEMA,
|
|
|
|
optional: true,
|
|
|
|
},
|
2021-03-02 10:42:58 +00:00
|
|
|
comment: {
|
|
|
|
optional: true,
|
|
|
|
schema: SINGLE_LINE_COMMENT_SCHEMA,
|
|
|
|
},
|
2020-12-10 09:52:27 +00:00
|
|
|
delete: {
|
|
|
|
description: "List of properties to delete.",
|
|
|
|
type: Array,
|
|
|
|
optional: true,
|
|
|
|
items: {
|
|
|
|
type: DeletableProperty,
|
|
|
|
}
|
|
|
|
},
|
|
|
|
},
|
|
|
|
},
|
2021-03-03 11:10:00 +00:00
|
|
|
access: {
|
|
|
|
permission: &Permission::Privilege(&["tape", "pool", "{name}"], PRIV_TAPE_MODIFY, false),
|
|
|
|
},
|
2020-12-10 09:52:27 +00:00
|
|
|
)]
|
|
|
|
/// Update media pool settings
|
2020-12-10 10:08:29 +00:00
|
|
|
pub fn update_pool(
|
2020-12-10 09:52:27 +00:00
|
|
|
name: String,
|
|
|
|
allocation: Option<String>,
|
|
|
|
retention: Option<String>,
|
|
|
|
template: Option<String>,
|
2021-01-18 07:16:44 +00:00
|
|
|
encrypt: Option<String>,
|
2021-03-02 10:42:58 +00:00
|
|
|
comment: Option<String>,
|
2020-12-10 09:52:27 +00:00
|
|
|
delete: Option<Vec<DeletableProperty>>,
|
|
|
|
) -> 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; },
|
2021-01-18 07:16:44 +00:00
|
|
|
DeletableProperty::encrypt => { data.encrypt = None; },
|
2021-03-02 10:42:58 +00:00
|
|
|
DeletableProperty::comment => { data.comment = None; },
|
2020-12-10 09:52:27 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
if allocation.is_some() { data.allocation = allocation; }
|
|
|
|
if retention.is_some() { data.retention = retention; }
|
|
|
|
if template.is_some() { data.template = template; }
|
2021-01-18 07:16:44 +00:00
|
|
|
if encrypt.is_some() { data.encrypt = encrypt; }
|
2020-12-10 09:52:27 +00:00
|
|
|
|
2021-03-02 10:42:58 +00:00
|
|
|
if let Some(comment) = comment {
|
|
|
|
let comment = comment.trim();
|
|
|
|
if comment.is_empty() {
|
|
|
|
data.comment = None;
|
|
|
|
} else {
|
|
|
|
data.comment = Some(comment.to_string());
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2020-12-10 09:52:27 +00:00
|
|
|
config.set_data(&name, "pool", &data)?;
|
|
|
|
|
|
|
|
config::media_pool::save_config(&config)?;
|
|
|
|
|
|
|
|
Ok(())
|
|
|
|
}
|
|
|
|
|
|
|
|
#[api(
|
2021-01-28 14:42:32 +00:00
|
|
|
protected: true,
|
2020-12-10 09:52:27 +00:00
|
|
|
input: {
|
|
|
|
properties: {
|
|
|
|
name: {
|
|
|
|
schema: MEDIA_POOL_NAME_SCHEMA,
|
|
|
|
},
|
|
|
|
},
|
|
|
|
},
|
2021-03-03 11:10:00 +00:00
|
|
|
access: {
|
|
|
|
permission: &Permission::Privilege(&["tape", "pool", "{name}"], PRIV_TAPE_MODIFY, false),
|
|
|
|
},
|
2020-12-10 09:52:27 +00:00
|
|
|
)]
|
|
|
|
/// Delete a media pool configuration
|
2020-12-10 10:08:29 +00:00
|
|
|
pub fn delete_pool(name: String) -> Result<(), Error> {
|
2020-12-10 09:52:27 +00:00
|
|
|
|
|
|
|
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);
|