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