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