]> git.proxmox.com Git - proxmox-backup.git/blame - src/api2/config/datastore.rs
src/config/datastore.rs: add prune settings
[proxmox-backup.git] / src / api2 / config / datastore.rs
CommitLineData
a2479cfa 1use std::path::PathBuf;
6ce50400 2
f7d4e4b5 3use anyhow::{bail, Error};
5e62d19c 4use serde_json::Value;
0a00f6e0 5use ::serde::{Deserialize, Serialize};
6ce50400 6
67f7ffd0 7use proxmox::api::{api, Router, RpcEnvironment, Permission};
a2479cfa 8
66c49c21 9use crate::api2::types::*;
a2479cfa 10use crate::backup::*;
67f7ffd0 11use crate::config::datastore::{self, DataStoreConfig, DIR_NAME_SCHEMA};
9c7fe29d 12use crate::config::acl::{PRIV_DATASTORE_AUDIT, PRIV_DATASTORE_MODIFY};
567713b4 13
688fbe07
DM
14#[api(
15 input: {
16 properties: {},
17 },
18 returns: {
f3ec5dae 19 description: "List the configured datastores (with config digest).",
688fbe07 20 type: Array,
67f7ffd0 21 items: { type: datastore::DataStoreConfig },
688fbe07 22 },
c0ef209a
DM
23 access: {
24 permission: &Permission::Privilege(&["datastore"], PRIV_DATASTORE_AUDIT, false),
25 },
688fbe07
DM
26)]
27/// List all datastores
28pub fn list_datastores(
6049b71f 29 _param: Value,
67f7ffd0
DM
30 mut rpcenv: &mut dyn RpcEnvironment,
31) -> Result<Vec<DataStoreConfig>, Error> {
567713b4 32
d0187a51 33 let (config, digest) = datastore::config()?;
b65eaac6 34
67f7ffd0
DM
35 let list = config.convert_to_typed_array("datastore")?;
36
37 rpcenv["digest"] = proxmox::tools::digest_to_hex(&digest).into();
38
39 Ok(list)
ea0b8b6e
DM
40}
41
67f7ffd0
DM
42
43// fixme: impl. const fn get_object_schema(datastore::DataStoreConfig::API_SCHEMA),
44// but this need support for match inside const fn
45// see: https://github.com/rust-lang/rust/issues/49146
46
688fbe07
DM
47#[api(
48 protected: true,
49 input: {
50 properties: {
51 name: {
52 schema: DATASTORE_SCHEMA,
53 },
67f7ffd0
DM
54 path: {
55 schema: DIR_NAME_SCHEMA,
56 },
688fbe07
DM
57 comment: {
58 optional: true,
454c13ed 59 schema: SINGLE_LINE_COMMENT_SCHEMA,
688fbe07 60 },
42fdbe51
DM
61 "gc-schedule": {
62 optional: true,
63 schema: GC_SCHEDULE_SCHEMA,
64 },
67f7ffd0
DM
65 "prune-schedule": {
66 optional: true,
67 schema: PRUNE_SCHEDULE_SCHEMA,
68 },
69 "keep-last": {
70 optional: true,
71 schema: PRUNE_SCHEMA_KEEP_LAST,
72 },
73 "keep-hourly": {
74 optional: true,
75 schema: PRUNE_SCHEMA_KEEP_HOURLY,
76 },
77 "keep-daily": {
78 optional: true,
79 schema: PRUNE_SCHEMA_KEEP_DAILY,
80 },
81 "keep-weekly": {
82 optional: true,
83 schema: PRUNE_SCHEMA_KEEP_WEEKLY,
84 },
85 "keep-monthly": {
86 optional: true,
87 schema: PRUNE_SCHEMA_KEEP_MONTHLY,
88 },
89 "keep-yearly": {
90 optional: true,
91 schema: PRUNE_SCHEMA_KEEP_YEARLY,
688fbe07
DM
92 },
93 },
94 },
c0ef209a 95 access: {
9c7fe29d 96 permission: &Permission::Privilege(&["datastore"], PRIV_DATASTORE_MODIFY, false),
c0ef209a 97 },
688fbe07
DM
98)]
99/// Create new datastore config.
67f7ffd0 100pub fn create_datastore(param: Value) -> Result<(), Error> {
ea0b8b6e 101
347834df 102 let _lock = crate::tools::open_file_locked(datastore::DATASTORE_CFG_LOCKFILE, std::time::Duration::new(10, 0))?;
652c1190 103
688fbe07 104 let datastore: datastore::DataStoreConfig = serde_json::from_value(param.clone())?;
652c1190 105
d0187a51 106 let (mut config, _digest) = datastore::config()?;
652c1190 107
67f7ffd0
DM
108 if let Some(_) = config.sections.get(&datastore.name) {
109 bail!("datastore '{}' already exists.", datastore.name);
652c1190
DM
110 }
111
688fbe07
DM
112 let path: PathBuf = datastore.path.clone().into();
113
114 let backup_user = crate::backup::backup_user()?;
67f7ffd0 115 let _store = ChunkStore::create(&datastore.name, path, backup_user.uid, backup_user.gid)?;
688fbe07 116
67f7ffd0 117 config.set_data(&datastore.name, "datastore", &datastore)?;
652c1190
DM
118
119 datastore::save_config(&config)?;
120
688fbe07 121 Ok(())
6ce50400
DM
122}
123
c5799e40
DM
124#[api(
125 input: {
126 properties: {
127 name: {
128 schema: DATASTORE_SCHEMA,
129 },
130 },
131 },
f3ec5dae
DM
132 returns: {
133 description: "The datastore configuration (with config digest).",
134 type: datastore::DataStoreConfig,
135 },
c0ef209a
DM
136 access: {
137 permission: &Permission::Privilege(&["datastore", "{name}"], PRIV_DATASTORE_AUDIT, false),
138 },
c5799e40
DM
139)]
140/// Read a datastore configuration.
67f7ffd0
DM
141pub fn read_datastore(
142 name: String,
143 mut rpcenv: &mut dyn RpcEnvironment,
144) -> Result<DataStoreConfig, Error> {
c5799e40 145 let (config, digest) = datastore::config()?;
67f7ffd0
DM
146
147 let store_config = config.lookup("datastore", &name)?;
148 rpcenv["digest"] = proxmox::tools::digest_to_hex(&digest).into();
149
150 Ok(store_config)
c5799e40
DM
151}
152
0a00f6e0
DM
153#[api()]
154#[derive(Serialize, Deserialize)]
42fdbe51 155#[serde(rename_all="kebab-case")]
0a00f6e0
DM
156#[allow(non_camel_case_types)]
157/// Deletable property name
158pub enum DeletableProperty {
159 /// Delete the comment property.
160 comment,
42fdbe51
DM
161 /// Delete the garbage collection schedule.
162 gc_schedule,
67f7ffd0
DM
163 /// Delete the prune job schedule.
164 prune_schedule,
165 /// Delete the keep-last property
166 keep_last,
167 /// Delete the keep-hourly property
168 keep_hourly,
169 /// Delete the keep-daily property
170 keep_daily,
171 /// Delete the keep-weekly property
172 keep_weekly,
173 /// Delete the keep-monthly property
174 keep_monthly,
175 /// Delete the keep-yearly property
176 keep_yearly,
0a00f6e0
DM
177}
178
c5799e40
DM
179#[api(
180 protected: true,
181 input: {
182 properties: {
183 name: {
184 schema: DATASTORE_SCHEMA,
185 },
186 comment: {
187 optional: true,
188 schema: SINGLE_LINE_COMMENT_SCHEMA,
189 },
42fdbe51
DM
190 "gc-schedule": {
191 optional: true,
192 schema: GC_SCHEDULE_SCHEMA,
193 },
67f7ffd0
DM
194 "prune-schedule": {
195 optional: true,
196 schema: PRUNE_SCHEDULE_SCHEMA,
197 },
198 "keep-last": {
199 optional: true,
200 schema: PRUNE_SCHEMA_KEEP_LAST,
201 },
202 "keep-hourly": {
203 optional: true,
204 schema: PRUNE_SCHEMA_KEEP_HOURLY,
205 },
206 "keep-daily": {
207 optional: true,
208 schema: PRUNE_SCHEMA_KEEP_DAILY,
209 },
210 "keep-weekly": {
211 optional: true,
212 schema: PRUNE_SCHEMA_KEEP_WEEKLY,
213 },
214 "keep-monthly": {
215 optional: true,
216 schema: PRUNE_SCHEMA_KEEP_MONTHLY,
217 },
218 "keep-yearly": {
219 optional: true,
220 schema: PRUNE_SCHEMA_KEEP_YEARLY,
221 },
0a00f6e0
DM
222 delete: {
223 description: "List of properties to delete.",
224 type: Array,
225 optional: true,
226 items: {
227 type: DeletableProperty,
228 }
229 },
002a191a
DM
230 digest: {
231 optional: true,
232 schema: PROXMOX_CONFIG_DIGEST_SCHEMA,
233 },
c5799e40
DM
234 },
235 },
c0ef209a 236 access: {
9c7fe29d 237 permission: &Permission::Privilege(&["datastore", "{name}"], PRIV_DATASTORE_MODIFY, false),
c0ef209a 238 },
c5799e40 239)]
2ea7bf1b 240/// Update datastore config.
c5799e40
DM
241pub fn update_datastore(
242 name: String,
243 comment: Option<String>,
42fdbe51 244 gc_schedule: Option<String>,
67f7ffd0
DM
245 prune_schedule: Option<String>,
246 keep_last: Option<i64>,
247 keep_hourly: Option<i64>,
248 keep_daily: Option<i64>,
249 keep_weekly: Option<i64>,
250 keep_monthly: Option<i64>,
251 keep_yearly: Option<i64>,
0a00f6e0 252 delete: Option<Vec<DeletableProperty>>,
002a191a 253 digest: Option<String>,
c5799e40
DM
254) -> Result<(), Error> {
255
347834df
DM
256 let _lock = crate::tools::open_file_locked(datastore::DATASTORE_CFG_LOCKFILE, std::time::Duration::new(10, 0))?;
257
c5799e40 258 // pass/compare digest
002a191a
DM
259 let (mut config, expected_digest) = datastore::config()?;
260
261 if let Some(ref digest) = digest {
262 let digest = proxmox::tools::hex_to_digest(digest)?;
263 crate::tools::detect_modified_configuration_file(&digest, &expected_digest)?;
264 }
c5799e40
DM
265
266 let mut data: datastore::DataStoreConfig = config.lookup("datastore", &name)?;
267
0a00f6e0
DM
268 if let Some(delete) = delete {
269 for delete_prop in delete {
270 match delete_prop {
271 DeletableProperty::comment => { data.comment = None; },
42fdbe51 272 DeletableProperty::gc_schedule => { data.gc_schedule = None; },
67f7ffd0
DM
273 DeletableProperty::prune_schedule => { data.prune_schedule = None; },
274 DeletableProperty::keep_last => { data.keep_last = None; },
275 DeletableProperty::keep_hourly => { data.keep_hourly = None; },
276 DeletableProperty::keep_daily => { data.keep_daily = None; },
277 DeletableProperty::keep_weekly => { data.keep_weekly = None; },
278 DeletableProperty::keep_monthly => { data.keep_monthly = None; },
279 DeletableProperty::keep_yearly => { data.keep_yearly = None; },
0a00f6e0
DM
280 }
281 }
282 }
283
c5799e40
DM
284 if let Some(comment) = comment {
285 let comment = comment.trim().to_string();
286 if comment.is_empty() {
287 data.comment = None;
288 } else {
289 data.comment = Some(comment);
290 }
291 }
c5799e40 292
42fdbe51 293 if gc_schedule.is_some() { data.gc_schedule = gc_schedule; }
67f7ffd0
DM
294 if prune_schedule.is_some() { data.prune_schedule = prune_schedule; }
295
296 if keep_last.is_some() { data.keep_last = keep_last; }
297 if keep_hourly.is_some() { data.keep_hourly = keep_hourly; }
298 if keep_daily.is_some() { data.keep_daily = keep_daily; }
299 if keep_weekly.is_some() { data.keep_weekly = keep_weekly; }
300 if keep_monthly.is_some() { data.keep_monthly = keep_monthly; }
301 if keep_yearly.is_some() { data.keep_yearly = keep_yearly; }
42fdbe51 302
c5799e40
DM
303 config.set_data(&name, "datastore", &data)?;
304
305 datastore::save_config(&config)?;
306
307 Ok(())
308}
309
688fbe07
DM
310#[api(
311 protected: true,
312 input: {
313 properties: {
314 name: {
315 schema: DATASTORE_SCHEMA,
316 },
c0ef209a
DM
317 digest: {
318 optional: true,
319 schema: PROXMOX_CONFIG_DIGEST_SCHEMA,
320 },
688fbe07
DM
321 },
322 },
c0ef209a 323 access: {
9c7fe29d 324 permission: &Permission::Privilege(&["datastore", "{name}"], PRIV_DATASTORE_MODIFY, false),
c0ef209a 325 },
688fbe07
DM
326)]
327/// Remove a datastore configuration.
c0ef209a 328pub fn delete_datastore(name: String, digest: Option<String>) -> Result<(), Error> {
34d3ba52 329
c0ef209a 330 let _lock = crate::tools::open_file_locked(datastore::DATASTORE_CFG_LOCKFILE, std::time::Duration::new(10, 0))?;
34d3ba52 331
c0ef209a
DM
332 let (mut config, expected_digest) = datastore::config()?;
333
334 if let Some(ref digest) = digest {
335 let digest = proxmox::tools::hex_to_digest(digest)?;
336 crate::tools::detect_modified_configuration_file(&digest, &expected_digest)?;
337 }
34d3ba52 338
688fbe07
DM
339 match config.sections.get(&name) {
340 Some(_) => { config.sections.remove(&name); },
34d3ba52
DM
341 None => bail!("datastore '{}' does not exist.", name),
342 }
343
344 datastore::save_config(&config)?;
345
688fbe07 346 Ok(())
34d3ba52
DM
347}
348
c5799e40
DM
349const ITEM_ROUTER: Router = Router::new()
350 .get(&API_METHOD_READ_DATASTORE)
351 .put(&API_METHOD_UPDATE_DATASTORE)
352 .delete(&API_METHOD_DELETE_DATASTORE);
353
255f378a 354pub const ROUTER: Router = Router::new()
688fbe07
DM
355 .get(&API_METHOD_LIST_DATASTORES)
356 .post(&API_METHOD_CREATE_DATASTORE)
c5799e40 357 .match_all("name", &ITEM_ROUTER);