]> git.proxmox.com Git - proxmox-backup.git/blob - src/api2/config/datastore.rs
remove timer and lock functions, fix building with proxmox 0.3.2
[proxmox-backup.git] / src / api2 / config / datastore.rs
1 use std::path::PathBuf;
2
3 use anyhow::{bail, Error};
4 use serde_json::Value;
5 use ::serde::{Deserialize, Serialize};
6
7 use proxmox::api::{api, Router, RpcEnvironment, Permission};
8 use proxmox::tools::fs::open_file_locked;
9
10 use crate::api2::types::*;
11 use crate::backup::*;
12 use crate::config::datastore::{self, DataStoreConfig, DIR_NAME_SCHEMA};
13 use crate::config::acl::{PRIV_DATASTORE_AUDIT, PRIV_DATASTORE_MODIFY};
14
15 #[api(
16 input: {
17 properties: {},
18 },
19 returns: {
20 description: "List the configured datastores (with config digest).",
21 type: Array,
22 items: { type: datastore::DataStoreConfig },
23 },
24 access: {
25 permission: &Permission::Privilege(&["datastore"], PRIV_DATASTORE_AUDIT, false),
26 },
27 )]
28 /// List all datastores
29 pub fn list_datastores(
30 _param: Value,
31 mut rpcenv: &mut dyn RpcEnvironment,
32 ) -> Result<Vec<DataStoreConfig>, Error> {
33
34 let (config, digest) = datastore::config()?;
35
36 let list = config.convert_to_typed_array("datastore")?;
37
38 rpcenv["digest"] = proxmox::tools::digest_to_hex(&digest).into();
39
40 Ok(list)
41 }
42
43
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
47
48 #[api(
49 protected: true,
50 input: {
51 properties: {
52 name: {
53 schema: DATASTORE_SCHEMA,
54 },
55 path: {
56 schema: DIR_NAME_SCHEMA,
57 },
58 comment: {
59 optional: true,
60 schema: SINGLE_LINE_COMMENT_SCHEMA,
61 },
62 "gc-schedule": {
63 optional: true,
64 schema: GC_SCHEDULE_SCHEMA,
65 },
66 "prune-schedule": {
67 optional: true,
68 schema: PRUNE_SCHEDULE_SCHEMA,
69 },
70 "keep-last": {
71 optional: true,
72 schema: PRUNE_SCHEMA_KEEP_LAST,
73 },
74 "keep-hourly": {
75 optional: true,
76 schema: PRUNE_SCHEMA_KEEP_HOURLY,
77 },
78 "keep-daily": {
79 optional: true,
80 schema: PRUNE_SCHEMA_KEEP_DAILY,
81 },
82 "keep-weekly": {
83 optional: true,
84 schema: PRUNE_SCHEMA_KEEP_WEEKLY,
85 },
86 "keep-monthly": {
87 optional: true,
88 schema: PRUNE_SCHEMA_KEEP_MONTHLY,
89 },
90 "keep-yearly": {
91 optional: true,
92 schema: PRUNE_SCHEMA_KEEP_YEARLY,
93 },
94 },
95 },
96 access: {
97 permission: &Permission::Privilege(&["datastore"], PRIV_DATASTORE_MODIFY, false),
98 },
99 )]
100 /// Create new datastore config.
101 pub fn create_datastore(param: Value) -> Result<(), Error> {
102
103 let _lock = open_file_locked(datastore::DATASTORE_CFG_LOCKFILE, std::time::Duration::new(10, 0))?;
104
105 let datastore: datastore::DataStoreConfig = serde_json::from_value(param.clone())?;
106
107 let (mut config, _digest) = datastore::config()?;
108
109 if let Some(_) = config.sections.get(&datastore.name) {
110 bail!("datastore '{}' already exists.", datastore.name);
111 }
112
113 let path: PathBuf = datastore.path.clone().into();
114
115 let backup_user = crate::backup::backup_user()?;
116 let _store = ChunkStore::create(&datastore.name, path, backup_user.uid, backup_user.gid)?;
117
118 config.set_data(&datastore.name, "datastore", &datastore)?;
119
120 datastore::save_config(&config)?;
121
122 Ok(())
123 }
124
125 #[api(
126 input: {
127 properties: {
128 name: {
129 schema: DATASTORE_SCHEMA,
130 },
131 },
132 },
133 returns: {
134 description: "The datastore configuration (with config digest).",
135 type: datastore::DataStoreConfig,
136 },
137 access: {
138 permission: &Permission::Privilege(&["datastore", "{name}"], PRIV_DATASTORE_AUDIT, false),
139 },
140 )]
141 /// Read a datastore configuration.
142 pub fn read_datastore(
143 name: String,
144 mut rpcenv: &mut dyn RpcEnvironment,
145 ) -> Result<DataStoreConfig, Error> {
146 let (config, digest) = datastore::config()?;
147
148 let store_config = config.lookup("datastore", &name)?;
149 rpcenv["digest"] = proxmox::tools::digest_to_hex(&digest).into();
150
151 Ok(store_config)
152 }
153
154 #[api()]
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.
161 comment,
162 /// Delete the garbage collection schedule.
163 gc_schedule,
164 /// Delete the prune job schedule.
165 prune_schedule,
166 /// Delete the keep-last property
167 keep_last,
168 /// Delete the keep-hourly property
169 keep_hourly,
170 /// Delete the keep-daily property
171 keep_daily,
172 /// Delete the keep-weekly property
173 keep_weekly,
174 /// Delete the keep-monthly property
175 keep_monthly,
176 /// Delete the keep-yearly property
177 keep_yearly,
178 }
179
180 #[api(
181 protected: true,
182 input: {
183 properties: {
184 name: {
185 schema: DATASTORE_SCHEMA,
186 },
187 comment: {
188 optional: true,
189 schema: SINGLE_LINE_COMMENT_SCHEMA,
190 },
191 "gc-schedule": {
192 optional: true,
193 schema: GC_SCHEDULE_SCHEMA,
194 },
195 "prune-schedule": {
196 optional: true,
197 schema: PRUNE_SCHEDULE_SCHEMA,
198 },
199 "keep-last": {
200 optional: true,
201 schema: PRUNE_SCHEMA_KEEP_LAST,
202 },
203 "keep-hourly": {
204 optional: true,
205 schema: PRUNE_SCHEMA_KEEP_HOURLY,
206 },
207 "keep-daily": {
208 optional: true,
209 schema: PRUNE_SCHEMA_KEEP_DAILY,
210 },
211 "keep-weekly": {
212 optional: true,
213 schema: PRUNE_SCHEMA_KEEP_WEEKLY,
214 },
215 "keep-monthly": {
216 optional: true,
217 schema: PRUNE_SCHEMA_KEEP_MONTHLY,
218 },
219 "keep-yearly": {
220 optional: true,
221 schema: PRUNE_SCHEMA_KEEP_YEARLY,
222 },
223 delete: {
224 description: "List of properties to delete.",
225 type: Array,
226 optional: true,
227 items: {
228 type: DeletableProperty,
229 }
230 },
231 digest: {
232 optional: true,
233 schema: PROXMOX_CONFIG_DIGEST_SCHEMA,
234 },
235 },
236 },
237 access: {
238 permission: &Permission::Privilege(&["datastore", "{name}"], PRIV_DATASTORE_MODIFY, false),
239 },
240 )]
241 /// Update datastore config.
242 pub fn update_datastore(
243 name: String,
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> {
256
257 let _lock = open_file_locked(datastore::DATASTORE_CFG_LOCKFILE, std::time::Duration::new(10, 0))?;
258
259 // pass/compare digest
260 let (mut config, expected_digest) = datastore::config()?;
261
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)?;
265 }
266
267 let mut data: datastore::DataStoreConfig = config.lookup("datastore", &name)?;
268
269 if let Some(delete) = delete {
270 for delete_prop in delete {
271 match delete_prop {
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; },
281 }
282 }
283 }
284
285 if let Some(comment) = comment {
286 let comment = comment.trim().to_string();
287 if comment.is_empty() {
288 data.comment = None;
289 } else {
290 data.comment = Some(comment);
291 }
292 }
293
294 if gc_schedule.is_some() { data.gc_schedule = gc_schedule; }
295 if prune_schedule.is_some() { data.prune_schedule = prune_schedule; }
296
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; }
303
304 config.set_data(&name, "datastore", &data)?;
305
306 datastore::save_config(&config)?;
307
308 Ok(())
309 }
310
311 #[api(
312 protected: true,
313 input: {
314 properties: {
315 name: {
316 schema: DATASTORE_SCHEMA,
317 },
318 digest: {
319 optional: true,
320 schema: PROXMOX_CONFIG_DIGEST_SCHEMA,
321 },
322 },
323 },
324 access: {
325 permission: &Permission::Privilege(&["datastore", "{name}"], PRIV_DATASTORE_MODIFY, false),
326 },
327 )]
328 /// Remove a datastore configuration.
329 pub fn delete_datastore(name: String, digest: Option<String>) -> Result<(), Error> {
330
331 let _lock = open_file_locked(datastore::DATASTORE_CFG_LOCKFILE, std::time::Duration::new(10, 0))?;
332
333 let (mut config, expected_digest) = datastore::config()?;
334
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)?;
338 }
339
340 match config.sections.get(&name) {
341 Some(_) => { config.sections.remove(&name); },
342 None => bail!("datastore '{}' does not exist.", name),
343 }
344
345 datastore::save_config(&config)?;
346
347 Ok(())
348 }
349
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);
354
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);