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
::datastore
::{self, DataStoreConfig, DIR_NAME_SCHEMA}
;
13 use crate::config
::acl
::{PRIV_DATASTORE_AUDIT, PRIV_DATASTORE_MODIFY}
;
20 description
: "List the configured datastores (with config digest).",
22 items
: { type: datastore::DataStoreConfig }
,
25 permission
: &Permission
::Privilege(&["datastore"], PRIV_DATASTORE_AUDIT
, false),
28 /// List all datastores
29 pub fn list_datastores(
31 mut rpcenv
: &mut dyn RpcEnvironment
,
32 ) -> Result
<Vec
<DataStoreConfig
>, Error
> {
34 let (config
, digest
) = datastore
::config()?
;
36 let list
= config
.convert_to_typed_array("datastore")?
;
38 rpcenv
["digest"] = proxmox
::tools
::digest_to_hex(&digest
).into();
44 // fixme: impl. const fn get_object_schema(datastore::DataStoreConfig::API_SCHEMA),
45 // but this need support for match inside const fn
46 // see: https://github.com/rust-lang/rust/issues/49146
53 schema
: DATASTORE_SCHEMA
,
56 schema
: DIR_NAME_SCHEMA
,
60 schema
: SINGLE_LINE_COMMENT_SCHEMA
,
64 schema
: GC_SCHEDULE_SCHEMA
,
68 schema
: PRUNE_SCHEDULE_SCHEMA
,
72 schema
: PRUNE_SCHEMA_KEEP_LAST
,
76 schema
: PRUNE_SCHEMA_KEEP_HOURLY
,
80 schema
: PRUNE_SCHEMA_KEEP_DAILY
,
84 schema
: PRUNE_SCHEMA_KEEP_WEEKLY
,
88 schema
: PRUNE_SCHEMA_KEEP_MONTHLY
,
92 schema
: PRUNE_SCHEMA_KEEP_YEARLY
,
97 permission
: &Permission
::Privilege(&["datastore"], PRIV_DATASTORE_MODIFY
, false),
100 /// Create new datastore config.
101 pub fn create_datastore(param
: Value
) -> Result
<(), Error
> {
103 let _lock
= open_file_locked(datastore
::DATASTORE_CFG_LOCKFILE
, std
::time
::Duration
::new(10, 0))?
;
105 let datastore
: datastore
::DataStoreConfig
= serde_json
::from_value(param
.clone())?
;
107 let (mut config
, _digest
) = datastore
::config()?
;
109 if let Some(_
) = config
.sections
.get(&datastore
.name
) {
110 bail
!("datastore '{}' already exists.", datastore
.name
);
113 let path
: PathBuf
= datastore
.path
.clone().into();
115 let backup_user
= crate::backup
::backup_user()?
;
116 let _store
= ChunkStore
::create(&datastore
.name
, path
, backup_user
.uid
, backup_user
.gid
)?
;
118 config
.set_data(&datastore
.name
, "datastore", &datastore
)?
;
120 datastore
::save_config(&config
)?
;
129 schema
: DATASTORE_SCHEMA
,
134 description
: "The datastore configuration (with config digest).",
135 type: datastore
::DataStoreConfig
,
138 permission
: &Permission
::Privilege(&["datastore", "{name}"], PRIV_DATASTORE_AUDIT
, false),
141 /// Read a datastore configuration.
142 pub fn read_datastore(
144 mut rpcenv
: &mut dyn RpcEnvironment
,
145 ) -> Result
<DataStoreConfig
, Error
> {
146 let (config
, digest
) = datastore
::config()?
;
148 let store_config
= config
.lookup("datastore", &name
)?
;
149 rpcenv
["digest"] = proxmox
::tools
::digest_to_hex(&digest
).into();
155 #[derive(Serialize, Deserialize)]
156 #[serde(rename_all="kebab-case")]
157 #[allow(non_camel_case_types)]
158 /// Deletable property name
159 pub enum DeletableProperty
{
160 /// Delete the comment property.
162 /// Delete the garbage collection schedule.
164 /// Delete the prune job schedule.
166 /// Delete the keep-last property
168 /// Delete the keep-hourly property
170 /// Delete the keep-daily property
172 /// Delete the keep-weekly property
174 /// Delete the keep-monthly property
176 /// Delete the keep-yearly property
185 schema
: DATASTORE_SCHEMA
,
189 schema
: SINGLE_LINE_COMMENT_SCHEMA
,
193 schema
: GC_SCHEDULE_SCHEMA
,
197 schema
: PRUNE_SCHEDULE_SCHEMA
,
201 schema
: PRUNE_SCHEMA_KEEP_LAST
,
205 schema
: PRUNE_SCHEMA_KEEP_HOURLY
,
209 schema
: PRUNE_SCHEMA_KEEP_DAILY
,
213 schema
: PRUNE_SCHEMA_KEEP_WEEKLY
,
217 schema
: PRUNE_SCHEMA_KEEP_MONTHLY
,
221 schema
: PRUNE_SCHEMA_KEEP_YEARLY
,
224 description
: "List of properties to delete.",
228 type: DeletableProperty
,
233 schema
: PROXMOX_CONFIG_DIGEST_SCHEMA
,
238 permission
: &Permission
::Privilege(&["datastore", "{name}"], PRIV_DATASTORE_MODIFY
, false),
241 /// Update datastore config.
242 pub fn update_datastore(
244 comment
: Option
<String
>,
245 gc_schedule
: Option
<String
>,
246 prune_schedule
: Option
<String
>,
247 keep_last
: Option
<u64>,
248 keep_hourly
: Option
<u64>,
249 keep_daily
: Option
<u64>,
250 keep_weekly
: Option
<u64>,
251 keep_monthly
: Option
<u64>,
252 keep_yearly
: Option
<u64>,
253 delete
: Option
<Vec
<DeletableProperty
>>,
254 digest
: Option
<String
>,
255 ) -> Result
<(), Error
> {
257 let _lock
= open_file_locked(datastore
::DATASTORE_CFG_LOCKFILE
, std
::time
::Duration
::new(10, 0))?
;
259 // pass/compare digest
260 let (mut config
, expected_digest
) = datastore
::config()?
;
262 if let Some(ref digest
) = digest
{
263 let digest
= proxmox
::tools
::hex_to_digest(digest
)?
;
264 crate::tools
::detect_modified_configuration_file(&digest
, &expected_digest
)?
;
267 let mut data
: datastore
::DataStoreConfig
= config
.lookup("datastore", &name
)?
;
269 if let Some(delete
) = delete
{
270 for delete_prop
in delete
{
272 DeletableProperty
::comment
=> { data.comment = None; }
,
273 DeletableProperty
::gc_schedule
=> { data.gc_schedule = None; }
,
274 DeletableProperty
::prune_schedule
=> { data.prune_schedule = None; }
,
275 DeletableProperty
::keep_last
=> { data.keep_last = None; }
,
276 DeletableProperty
::keep_hourly
=> { data.keep_hourly = None; }
,
277 DeletableProperty
::keep_daily
=> { data.keep_daily = None; }
,
278 DeletableProperty
::keep_weekly
=> { data.keep_weekly = None; }
,
279 DeletableProperty
::keep_monthly
=> { data.keep_monthly = None; }
,
280 DeletableProperty
::keep_yearly
=> { data.keep_yearly = None; }
,
285 if let Some(comment
) = comment
{
286 let comment
= comment
.trim().to_string();
287 if comment
.is_empty() {
290 data
.comment
= Some(comment
);
294 if gc_schedule
.is_some() { data.gc_schedule = gc_schedule; }
295 if prune_schedule
.is_some() { data.prune_schedule = prune_schedule; }
297 if keep_last
.is_some() { data.keep_last = keep_last; }
298 if keep_hourly
.is_some() { data.keep_hourly = keep_hourly; }
299 if keep_daily
.is_some() { data.keep_daily = keep_daily; }
300 if keep_weekly
.is_some() { data.keep_weekly = keep_weekly; }
301 if keep_monthly
.is_some() { data.keep_monthly = keep_monthly; }
302 if keep_yearly
.is_some() { data.keep_yearly = keep_yearly; }
304 config
.set_data(&name
, "datastore", &data
)?
;
306 datastore
::save_config(&config
)?
;
316 schema
: DATASTORE_SCHEMA
,
320 schema
: PROXMOX_CONFIG_DIGEST_SCHEMA
,
325 permission
: &Permission
::Privilege(&["datastore", "{name}"], PRIV_DATASTORE_MODIFY
, false),
328 /// Remove a datastore configuration.
329 pub fn delete_datastore(name
: String
, digest
: Option
<String
>) -> Result
<(), Error
> {
331 let _lock
= open_file_locked(datastore
::DATASTORE_CFG_LOCKFILE
, std
::time
::Duration
::new(10, 0))?
;
333 let (mut config
, expected_digest
) = datastore
::config()?
;
335 if let Some(ref digest
) = digest
{
336 let digest
= proxmox
::tools
::hex_to_digest(digest
)?
;
337 crate::tools
::detect_modified_configuration_file(&digest
, &expected_digest
)?
;
340 match config
.sections
.get(&name
) {
341 Some(_
) => { config.sections.remove(&name); }
,
342 None
=> bail
!("datastore '{}' does not exist.", name
),
345 datastore
::save_config(&config
)?
;
350 const ITEM_ROUTER
: Router
= Router
::new()
351 .get(&API_METHOD_READ_DATASTORE
)
352 .put(&API_METHOD_UPDATE_DATASTORE
)
353 .delete(&API_METHOD_DELETE_DATASTORE
);
355 pub const ROUTER
: Router
= Router
::new()
356 .get(&API_METHOD_LIST_DATASTORES
)
357 .post(&API_METHOD_CREATE_DATASTORE
)
358 .match_all("name", &ITEM_ROUTER
);