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