]>
Commit | Line | Data |
---|---|---|
9b2bad7a HL |
1 | use anyhow::{bail, Error}; |
2 | use serde_json::Value; | |
3 | use ::serde::{Deserialize, Serialize}; | |
4 | ||
b7ce2e57 | 5 | use proxmox::api::{api, Permission, Router, RpcEnvironment}; |
9b2bad7a HL |
6 | use proxmox::tools::fs::open_file_locked; |
7 | ||
8 | use crate::api2::types::*; | |
b7ce2e57 FG |
9 | |
10 | use crate::config::acl::{ | |
11 | PRIV_DATASTORE_AUDIT, | |
12 | PRIV_DATASTORE_BACKUP, | |
13 | PRIV_DATASTORE_VERIFY, | |
14 | }; | |
15 | ||
9b2bad7a HL |
16 | use crate::config::verify::{self, VerificationJobConfig}; |
17 | ||
18 | #[api( | |
19 | input: { | |
20 | properties: {}, | |
21 | }, | |
22 | returns: { | |
23 | description: "List configured jobs.", | |
24 | type: Array, | |
25 | items: { type: verify::VerificationJobConfig }, | |
26 | }, | |
b7ce2e57 FG |
27 | access: { |
28 | permission: &Permission::Privilege( | |
29 | &["datastore", "{store}"], | |
30 | PRIV_DATASTORE_AUDIT | PRIV_DATASTORE_BACKUP | PRIV_DATASTORE_VERIFY, | |
31 | true), | |
32 | }, | |
9b2bad7a HL |
33 | )] |
34 | /// List all verification jobs | |
35 | pub fn list_verification_jobs( | |
36 | _param: Value, | |
37 | mut rpcenv: &mut dyn RpcEnvironment, | |
38 | ) -> Result<Vec<VerificationJobConfig>, Error> { | |
39 | ||
40 | let (config, digest) = verify::config()?; | |
41 | ||
42 | let list = config.convert_to_typed_array("verification")?; | |
43 | ||
44 | rpcenv["digest"] = proxmox::tools::digest_to_hex(&digest).into(); | |
45 | ||
46 | Ok(list) | |
47 | } | |
48 | ||
49 | ||
50 | #[api( | |
51 | protected: true, | |
52 | input: { | |
53 | properties: { | |
54 | id: { | |
55 | schema: JOB_ID_SCHEMA, | |
56 | }, | |
57 | store: { | |
58 | schema: DATASTORE_SCHEMA, | |
59 | }, | |
60 | "ignore-verified": { | |
61 | optional: true, | |
62 | schema: IGNORE_VERIFIED_BACKUPS_SCHEMA, | |
63 | }, | |
64 | "outdated-after": { | |
65 | optional: true, | |
66 | schema: VERIFICATION_OUTDATED_AFTER_SCHEMA, | |
67 | }, | |
68 | comment: { | |
69 | optional: true, | |
70 | schema: SINGLE_LINE_COMMENT_SCHEMA, | |
71 | }, | |
72 | schedule: { | |
73 | optional: true, | |
74 | schema: VERIFICATION_SCHEDULE_SCHEMA, | |
75 | }, | |
76 | } | |
b7ce2e57 FG |
77 | }, |
78 | access: { | |
79 | permission: &Permission::Privilege( | |
80 | &["datastore", "{store}"], | |
81 | PRIV_DATASTORE_VERIFY, | |
82 | true), | |
83 | }, | |
9b2bad7a HL |
84 | )] |
85 | /// Create a new verification job. | |
86 | pub fn create_verification_job(param: Value) -> Result<(), Error> { | |
87 | ||
88 | let _lock = open_file_locked(verify::VERIFICATION_CFG_LOCKFILE, std::time::Duration::new(10, 0), true)?; | |
89 | ||
90 | let verification_job: verify::VerificationJobConfig = serde_json::from_value(param.clone())?; | |
91 | ||
92 | let (mut config, _digest) = verify::config()?; | |
93 | ||
94 | if let Some(_) = config.sections.get(&verification_job.id) { | |
95 | bail!("job '{}' already exists.", verification_job.id); | |
96 | } | |
97 | ||
98 | config.set_data(&verification_job.id, "verification", &verification_job)?; | |
99 | ||
100 | verify::save_config(&config)?; | |
101 | ||
1298618a | 102 | crate::server::jobstate::create_state_file("verificationjob", &verification_job.id)?; |
9b2bad7a HL |
103 | |
104 | Ok(()) | |
105 | } | |
106 | ||
107 | #[api( | |
108 | input: { | |
109 | properties: { | |
110 | id: { | |
111 | schema: JOB_ID_SCHEMA, | |
112 | }, | |
113 | }, | |
114 | }, | |
115 | returns: { | |
116 | description: "The verification job configuration.", | |
117 | type: verify::VerificationJobConfig, | |
118 | }, | |
b7ce2e57 FG |
119 | access: { |
120 | permission: &Permission::Privilege( | |
121 | &["datastore", "{store}"], | |
122 | PRIV_DATASTORE_AUDIT | PRIV_DATASTORE_BACKUP | PRIV_DATASTORE_VERIFY, | |
123 | true), | |
124 | }, | |
9b2bad7a HL |
125 | )] |
126 | /// Read a verification job configuration. | |
127 | pub fn read_verification_job( | |
128 | id: String, | |
129 | mut rpcenv: &mut dyn RpcEnvironment, | |
130 | ) -> Result<VerificationJobConfig, Error> { | |
131 | let (config, digest) = verify::config()?; | |
132 | ||
133 | let verification_job = config.lookup("verification", &id)?; | |
134 | rpcenv["digest"] = proxmox::tools::digest_to_hex(&digest).into(); | |
135 | ||
136 | Ok(verification_job) | |
137 | } | |
138 | ||
139 | #[api()] | |
140 | #[derive(Serialize, Deserialize)] | |
141 | #[serde(rename_all="kebab-case")] | |
142 | /// Deletable property name | |
143 | pub enum DeletableProperty { | |
144 | /// Delete the ignore verified property. | |
145 | IgnoreVerified, | |
146 | /// Delete the comment property. | |
147 | Comment, | |
148 | /// Delete the job schedule. | |
149 | Schedule, | |
150 | /// Delete outdated after property. | |
151 | OutdatedAfter | |
152 | } | |
153 | ||
154 | #[api( | |
155 | protected: true, | |
156 | input: { | |
157 | properties: { | |
158 | id: { | |
159 | schema: JOB_ID_SCHEMA, | |
160 | }, | |
161 | store: { | |
162 | optional: true, | |
163 | schema: DATASTORE_SCHEMA, | |
164 | }, | |
165 | "ignore-verified": { | |
166 | optional: true, | |
167 | schema: IGNORE_VERIFIED_BACKUPS_SCHEMA, | |
168 | }, | |
169 | "outdated-after": { | |
170 | optional: true, | |
171 | schema: VERIFICATION_OUTDATED_AFTER_SCHEMA, | |
172 | }, | |
173 | comment: { | |
174 | optional: true, | |
175 | schema: SINGLE_LINE_COMMENT_SCHEMA, | |
176 | }, | |
177 | schedule: { | |
178 | optional: true, | |
179 | schema: VERIFICATION_SCHEDULE_SCHEMA, | |
180 | }, | |
181 | delete: { | |
182 | description: "List of properties to delete.", | |
183 | type: Array, | |
184 | optional: true, | |
185 | items: { | |
186 | type: DeletableProperty, | |
187 | } | |
188 | }, | |
189 | digest: { | |
190 | optional: true, | |
191 | schema: PROXMOX_CONFIG_DIGEST_SCHEMA, | |
192 | }, | |
193 | }, | |
194 | }, | |
b7ce2e57 FG |
195 | access: { |
196 | permission: &Permission::Privilege( | |
197 | &["datastore", "{store}"], | |
198 | PRIV_DATASTORE_VERIFY, | |
199 | true), | |
200 | }, | |
9b2bad7a HL |
201 | )] |
202 | /// Update verification job config. | |
203 | pub fn update_verification_job( | |
204 | id: String, | |
205 | store: Option<String>, | |
206 | ignore_verified: Option<bool>, | |
207 | outdated_after: Option<i64>, | |
208 | comment: Option<String>, | |
209 | schedule: Option<String>, | |
210 | delete: Option<Vec<DeletableProperty>>, | |
211 | digest: Option<String>, | |
212 | ) -> Result<(), Error> { | |
213 | ||
214 | let _lock = open_file_locked(verify::VERIFICATION_CFG_LOCKFILE, std::time::Duration::new(10, 0), true)?; | |
215 | ||
216 | // pass/compare digest | |
217 | let (mut config, expected_digest) = verify::config()?; | |
218 | ||
219 | if let Some(ref digest) = digest { | |
220 | let digest = proxmox::tools::hex_to_digest(digest)?; | |
221 | crate::tools::detect_modified_configuration_file(&digest, &expected_digest)?; | |
222 | } | |
223 | ||
224 | let mut data: verify::VerificationJobConfig = config.lookup("verification", &id)?; | |
225 | ||
226 | if let Some(delete) = delete { | |
227 | for delete_prop in delete { | |
228 | match delete_prop { | |
229 | DeletableProperty::IgnoreVerified => { data.ignore_verified = None; }, | |
230 | DeletableProperty::OutdatedAfter => { data.outdated_after = None; }, | |
231 | DeletableProperty::Comment => { data.comment = None; }, | |
232 | DeletableProperty::Schedule => { data.schedule = None; }, | |
233 | } | |
234 | } | |
235 | } | |
236 | ||
237 | if let Some(comment) = comment { | |
238 | let comment = comment.trim().to_string(); | |
239 | if comment.is_empty() { | |
240 | data.comment = None; | |
241 | } else { | |
242 | data.comment = Some(comment); | |
243 | } | |
244 | } | |
245 | ||
246 | if let Some(store) = store { data.store = store; } | |
247 | ||
248 | if ignore_verified.is_some() { data.ignore_verified = ignore_verified; } | |
249 | if outdated_after.is_some() { data.outdated_after = outdated_after; } | |
250 | if schedule.is_some() { data.schedule = schedule; } | |
251 | ||
252 | config.set_data(&id, "verification", &data)?; | |
253 | ||
254 | verify::save_config(&config)?; | |
255 | ||
256 | Ok(()) | |
257 | } | |
258 | ||
259 | #[api( | |
260 | protected: true, | |
261 | input: { | |
262 | properties: { | |
263 | id: { | |
264 | schema: JOB_ID_SCHEMA, | |
265 | }, | |
266 | digest: { | |
267 | optional: true, | |
268 | schema: PROXMOX_CONFIG_DIGEST_SCHEMA, | |
269 | }, | |
270 | }, | |
271 | }, | |
b7ce2e57 FG |
272 | access: { |
273 | permission: &Permission::Privilege( | |
274 | &["datastore", "{store}"], | |
275 | PRIV_DATASTORE_VERIFY, | |
276 | true), | |
277 | }, | |
9b2bad7a HL |
278 | )] |
279 | /// Remove a verification job configuration | |
280 | pub fn delete_verification_job(id: String, digest: Option<String>) -> Result<(), Error> { | |
281 | ||
282 | let _lock = open_file_locked(verify::VERIFICATION_CFG_LOCKFILE, std::time::Duration::new(10, 0), true)?; | |
283 | ||
284 | let (mut config, expected_digest) = verify::config()?; | |
285 | ||
286 | if let Some(ref digest) = digest { | |
287 | let digest = proxmox::tools::hex_to_digest(digest)?; | |
288 | crate::tools::detect_modified_configuration_file(&digest, &expected_digest)?; | |
289 | } | |
290 | ||
291 | match config.sections.get(&id) { | |
292 | Some(_) => { config.sections.remove(&id); }, | |
293 | None => bail!("job '{}' does not exist.", id), | |
294 | } | |
295 | ||
296 | verify::save_config(&config)?; | |
297 | ||
1298618a | 298 | crate::server::jobstate::remove_state_file("verificationjob", &id)?; |
9b2bad7a HL |
299 | |
300 | Ok(()) | |
301 | } | |
302 | ||
303 | const ITEM_ROUTER: Router = Router::new() | |
304 | .get(&API_METHOD_READ_VERIFICATION_JOB) | |
305 | .put(&API_METHOD_UPDATE_VERIFICATION_JOB) | |
306 | .delete(&API_METHOD_DELETE_VERIFICATION_JOB); | |
307 | ||
308 | pub const ROUTER: Router = Router::new() | |
309 | .get(&API_METHOD_LIST_VERIFICATION_JOBS) | |
310 | .post(&API_METHOD_CREATE_VERIFICATION_JOB) | |
1298618a | 311 | .match_all("id", &ITEM_ROUTER); |