]>
Commit | Line | Data |
---|---|---|
a2479cfa | 1 | use std::path::PathBuf; |
6ce50400 | 2 | |
f7d4e4b5 | 3 | use anyhow::{bail, Error}; |
5e62d19c | 4 | use serde_json::Value; |
0a00f6e0 | 5 | use ::serde::{Deserialize, Serialize}; |
6ce50400 | 6 | |
67f7ffd0 | 7 | use proxmox::api::{api, Router, RpcEnvironment, Permission}; |
a2479cfa | 8 | |
66c49c21 | 9 | use crate::api2::types::*; |
a2479cfa | 10 | use crate::backup::*; |
67f7ffd0 | 11 | use crate::config::datastore::{self, DataStoreConfig, DIR_NAME_SCHEMA}; |
9c7fe29d | 12 | use 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 | |
28 | pub 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 | 100 | pub 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 |
141 | pub 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 | |
158 | pub 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 |
241 | pub 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 | 328 | pub 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 |
349 | const 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 | 354 | pub 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); |