1 use std
::path
::PathBuf
;
3 use anyhow
::{bail, Error}
;
5 use ::serde
::{Deserialize, Serialize}
;
7 use proxmox
::api
::{api, Router, RpcEnvironment, Permission}
;
8 use proxmox
::tools
::fs
::open_file_locked
;
10 use crate::api2
::types
::*;
12 use crate::config
::cached_user_info
::CachedUserInfo
;
13 use crate::config
::datastore
::{self, DataStoreConfig, DIR_NAME_SCHEMA}
;
14 use crate::config
::acl
::{PRIV_DATASTORE_AUDIT, PRIV_DATASTORE_MODIFY}
;
21 description
: "List the configured datastores (with config digest).",
23 items
: { type: datastore::DataStoreConfig }
,
26 permission
: &Permission
::Anybody
,
29 /// List all datastores
30 pub fn list_datastores(
32 mut rpcenv
: &mut dyn RpcEnvironment
,
33 ) -> Result
<Vec
<DataStoreConfig
>, Error
> {
35 let (config
, digest
) = datastore
::config()?
;
37 let userid
: Userid
= rpcenv
.get_user().unwrap().parse()?
;
38 let user_info
= CachedUserInfo
::new()?
;
40 rpcenv
["digest"] = proxmox
::tools
::digest_to_hex(&digest
).into();
42 let list
:Vec
<DataStoreConfig
> = config
.convert_to_typed_array("datastore")?
;
43 let filter_by_privs
= |store
: &DataStoreConfig
| {
44 let user_privs
= user_info
.lookup_privs(&userid
, &["datastore", &store
.name
]);
45 (user_privs
& PRIV_DATASTORE_AUDIT
) != 0
48 Ok(list
.into_iter().filter(filter_by_privs
).collect())
52 // fixme: impl. const fn get_object_schema(datastore::DataStoreConfig::API_SCHEMA),
53 // but this need support for match inside const fn
54 // see: https://github.com/rust-lang/rust/issues/49146
61 schema
: DATASTORE_SCHEMA
,
64 schema
: DIR_NAME_SCHEMA
,
68 schema
: SINGLE_LINE_COMMENT_SCHEMA
,
72 schema
: GC_SCHEDULE_SCHEMA
,
76 schema
: PRUNE_SCHEDULE_SCHEMA
,
80 schema
: VERIFY_SCHEDULE_SCHEMA
,
84 schema
: PRUNE_SCHEMA_KEEP_LAST
,
88 schema
: PRUNE_SCHEMA_KEEP_HOURLY
,
92 schema
: PRUNE_SCHEMA_KEEP_DAILY
,
96 schema
: PRUNE_SCHEMA_KEEP_WEEKLY
,
100 schema
: PRUNE_SCHEMA_KEEP_MONTHLY
,
104 schema
: PRUNE_SCHEMA_KEEP_YEARLY
,
109 permission
: &Permission
::Privilege(&["datastore"], PRIV_DATASTORE_MODIFY
, false),
112 /// Create new datastore config.
113 pub fn create_datastore(param
: Value
) -> Result
<(), Error
> {
115 let _lock
= open_file_locked(datastore
::DATASTORE_CFG_LOCKFILE
, std
::time
::Duration
::new(10, 0))?
;
117 let datastore
: datastore
::DataStoreConfig
= serde_json
::from_value(param
.clone())?
;
119 let (mut config
, _digest
) = datastore
::config()?
;
121 if let Some(_
) = config
.sections
.get(&datastore
.name
) {
122 bail
!("datastore '{}' already exists.", datastore
.name
);
125 let path
: PathBuf
= datastore
.path
.clone().into();
127 let backup_user
= crate::backup
::backup_user()?
;
128 let _store
= ChunkStore
::create(&datastore
.name
, path
, backup_user
.uid
, backup_user
.gid
)?
;
130 config
.set_data(&datastore
.name
, "datastore", &datastore
)?
;
132 datastore
::save_config(&config
)?
;
141 schema
: DATASTORE_SCHEMA
,
146 description
: "The datastore configuration (with config digest).",
147 type: datastore
::DataStoreConfig
,
150 permission
: &Permission
::Privilege(&["datastore", "{name}"], PRIV_DATASTORE_AUDIT
, false),
153 /// Read a datastore configuration.
154 pub fn read_datastore(
156 mut rpcenv
: &mut dyn RpcEnvironment
,
157 ) -> Result
<DataStoreConfig
, Error
> {
158 let (config
, digest
) = datastore
::config()?
;
160 let store_config
= config
.lookup("datastore", &name
)?
;
161 rpcenv
["digest"] = proxmox
::tools
::digest_to_hex(&digest
).into();
167 #[derive(Serialize, Deserialize)]
168 #[serde(rename_all="kebab-case")]
169 #[allow(non_camel_case_types)]
170 /// Deletable property name
171 pub enum DeletableProperty
{
172 /// Delete the comment property.
174 /// Delete the garbage collection schedule.
176 /// Delete the prune job schedule.
178 /// Delete the keep-last property
180 /// Delete the keep-hourly property
182 /// Delete the keep-daily property
184 /// Delete the keep-weekly property
186 /// Delete the keep-monthly property
188 /// Delete the keep-yearly property
197 schema
: DATASTORE_SCHEMA
,
201 schema
: SINGLE_LINE_COMMENT_SCHEMA
,
205 schema
: GC_SCHEDULE_SCHEMA
,
209 schema
: PRUNE_SCHEDULE_SCHEMA
,
213 schema
: VERIFY_SCHEDULE_SCHEMA
,
217 schema
: PRUNE_SCHEMA_KEEP_LAST
,
221 schema
: PRUNE_SCHEMA_KEEP_HOURLY
,
225 schema
: PRUNE_SCHEMA_KEEP_DAILY
,
229 schema
: PRUNE_SCHEMA_KEEP_WEEKLY
,
233 schema
: PRUNE_SCHEMA_KEEP_MONTHLY
,
237 schema
: PRUNE_SCHEMA_KEEP_YEARLY
,
240 description
: "List of properties to delete.",
244 type: DeletableProperty
,
249 schema
: PROXMOX_CONFIG_DIGEST_SCHEMA
,
254 permission
: &Permission
::Privilege(&["datastore", "{name}"], PRIV_DATASTORE_MODIFY
, false),
257 /// Update datastore config.
258 pub fn update_datastore(
260 comment
: Option
<String
>,
261 gc_schedule
: Option
<String
>,
262 prune_schedule
: Option
<String
>,
263 verify_schedule
: Option
<String
>,
264 keep_last
: Option
<u64>,
265 keep_hourly
: Option
<u64>,
266 keep_daily
: Option
<u64>,
267 keep_weekly
: Option
<u64>,
268 keep_monthly
: Option
<u64>,
269 keep_yearly
: Option
<u64>,
270 delete
: Option
<Vec
<DeletableProperty
>>,
271 digest
: Option
<String
>,
272 ) -> Result
<(), Error
> {
274 let _lock
= open_file_locked(datastore
::DATASTORE_CFG_LOCKFILE
, std
::time
::Duration
::new(10, 0))?
;
276 // pass/compare digest
277 let (mut config
, expected_digest
) = datastore
::config()?
;
279 if let Some(ref digest
) = digest
{
280 let digest
= proxmox
::tools
::hex_to_digest(digest
)?
;
281 crate::tools
::detect_modified_configuration_file(&digest
, &expected_digest
)?
;
284 let mut data
: datastore
::DataStoreConfig
= config
.lookup("datastore", &name
)?
;
286 if let Some(delete
) = delete
{
287 for delete_prop
in delete
{
289 DeletableProperty
::comment
=> { data.comment = None; }
,
290 DeletableProperty
::gc_schedule
=> { data.gc_schedule = None; }
,
291 DeletableProperty
::prune_schedule
=> { data.prune_schedule = None; }
,
292 DeletableProperty
::keep_last
=> { data.keep_last = None; }
,
293 DeletableProperty
::keep_hourly
=> { data.keep_hourly = None; }
,
294 DeletableProperty
::keep_daily
=> { data.keep_daily = None; }
,
295 DeletableProperty
::keep_weekly
=> { data.keep_weekly = None; }
,
296 DeletableProperty
::keep_monthly
=> { data.keep_monthly = None; }
,
297 DeletableProperty
::keep_yearly
=> { data.keep_yearly = None; }
,
302 if let Some(comment
) = comment
{
303 let comment
= comment
.trim().to_string();
304 if comment
.is_empty() {
307 data
.comment
= Some(comment
);
311 if gc_schedule
.is_some() { data.gc_schedule = gc_schedule; }
312 if prune_schedule
.is_some() { data.prune_schedule = prune_schedule; }
313 if verify_schedule
.is_some() { data.verify_schedule = verify_schedule; }
315 if keep_last
.is_some() { data.keep_last = keep_last; }
316 if keep_hourly
.is_some() { data.keep_hourly = keep_hourly; }
317 if keep_daily
.is_some() { data.keep_daily = keep_daily; }
318 if keep_weekly
.is_some() { data.keep_weekly = keep_weekly; }
319 if keep_monthly
.is_some() { data.keep_monthly = keep_monthly; }
320 if keep_yearly
.is_some() { data.keep_yearly = keep_yearly; }
322 config
.set_data(&name
, "datastore", &data
)?
;
324 datastore
::save_config(&config
)?
;
334 schema
: DATASTORE_SCHEMA
,
338 schema
: PROXMOX_CONFIG_DIGEST_SCHEMA
,
343 permission
: &Permission
::Privilege(&["datastore", "{name}"], PRIV_DATASTORE_MODIFY
, false),
346 /// Remove a datastore configuration.
347 pub fn delete_datastore(name
: String
, digest
: Option
<String
>) -> Result
<(), Error
> {
349 let _lock
= open_file_locked(datastore
::DATASTORE_CFG_LOCKFILE
, std
::time
::Duration
::new(10, 0))?
;
351 let (mut config
, expected_digest
) = datastore
::config()?
;
353 if let Some(ref digest
) = digest
{
354 let digest
= proxmox
::tools
::hex_to_digest(digest
)?
;
355 crate::tools
::detect_modified_configuration_file(&digest
, &expected_digest
)?
;
358 match config
.sections
.get(&name
) {
359 Some(_
) => { config.sections.remove(&name); }
,
360 None
=> bail
!("datastore '{}' does not exist.", name
),
363 datastore
::save_config(&config
)?
;
368 const ITEM_ROUTER
: Router
= Router
::new()
369 .get(&API_METHOD_READ_DATASTORE
)
370 .put(&API_METHOD_UPDATE_DATASTORE
)
371 .delete(&API_METHOD_DELETE_DATASTORE
);
373 pub const ROUTER
: Router
= Router
::new()
374 .get(&API_METHOD_LIST_DATASTORES
)
375 .post(&API_METHOD_CREATE_DATASTORE
)
376 .match_all("name", &ITEM_ROUTER
);