]> git.proxmox.com Git - proxmox-backup.git/blame - pbs-api-types/src/jobs.rs
tape job cleanup: user Updater
[proxmox-backup.git] / pbs-api-types / src / jobs.rs
CommitLineData
e3619d41
DM
1use serde::{Deserialize, Serialize};
2
3use proxmox::const_regex;
4
5use proxmox::api::{api, schema::*};
6
7use crate::{
8 Userid, Authid, REMOTE_ID_SCHEMA, DRIVE_NAME_SCHEMA, MEDIA_POOL_NAME_SCHEMA,
9 SINGLE_LINE_COMMENT_SCHEMA, PROXMOX_SAFE_ID_FORMAT, DATASTORE_SCHEMA,
10};
11
12const_regex!{
13
14 /// Regex for verification jobs 'DATASTORE:ACTUAL_JOB_ID'
15 pub VERIFICATION_JOB_WORKER_ID_REGEX = concat!(r"^(", PROXMOX_SAFE_ID_REGEX_STR!(), r"):");
16 /// Regex for sync jobs 'REMOTE:REMOTE_DATASTORE:LOCAL_DATASTORE:ACTUAL_JOB_ID'
17 pub SYNC_JOB_WORKER_ID_REGEX = concat!(r"^(", PROXMOX_SAFE_ID_REGEX_STR!(), r"):(", PROXMOX_SAFE_ID_REGEX_STR!(), r"):(", PROXMOX_SAFE_ID_REGEX_STR!(), r"):");
18}
19
20pub const UPID_SCHEMA: Schema = StringSchema::new("Unique Process/Task ID.")
21 .max_length(256)
22 .schema();
23
24pub const JOB_ID_SCHEMA: Schema = StringSchema::new("Job ID.")
25 .format(&PROXMOX_SAFE_ID_FORMAT)
26 .min_length(3)
27 .max_length(32)
28 .schema();
29
30pub const SYNC_SCHEDULE_SCHEMA: Schema = StringSchema::new(
31 "Run sync job at specified schedule.")
32 .format(&ApiStringFormat::VerifyFn(pbs_systemd::time::verify_calendar_event))
33 .type_text("<calendar-event>")
34 .schema();
35
36pub const GC_SCHEDULE_SCHEMA: Schema = StringSchema::new(
37 "Run garbage collection job at specified schedule.")
38 .format(&ApiStringFormat::VerifyFn(pbs_systemd::time::verify_calendar_event))
39 .type_text("<calendar-event>")
40 .schema();
41
42pub const PRUNE_SCHEDULE_SCHEMA: Schema = StringSchema::new(
43 "Run prune job at specified schedule.")
44 .format(&ApiStringFormat::VerifyFn(pbs_systemd::time::verify_calendar_event))
45 .type_text("<calendar-event>")
46 .schema();
47
48pub const VERIFICATION_SCHEDULE_SCHEMA: Schema = StringSchema::new(
49 "Run verify job at specified schedule.")
50 .format(&ApiStringFormat::VerifyFn(pbs_systemd::time::verify_calendar_event))
51 .type_text("<calendar-event>")
52 .schema();
53
54pub const REMOVE_VANISHED_BACKUPS_SCHEMA: Schema = BooleanSchema::new(
55 "Delete vanished backups. This remove the local copy if the remote backup was deleted.")
56 .default(true)
57 .schema();
58
59#[api(
60 properties: {
61 "next-run": {
62 description: "Estimated time of the next run (UNIX epoch).",
63 optional: true,
64 type: Integer,
65 },
66 "last-run-state": {
67 description: "Result of the last run.",
68 optional: true,
69 type: String,
70 },
71 "last-run-upid": {
72 description: "Task UPID of the last run.",
73 optional: true,
74 type: String,
75 },
76 "last-run-endtime": {
77 description: "Endtime of the last run.",
78 optional: true,
79 type: Integer,
80 },
81 }
82)]
83#[derive(Serialize,Deserialize,Default)]
84#[serde(rename_all="kebab-case")]
85/// Job Scheduling Status
86pub struct JobScheduleStatus {
87 #[serde(skip_serializing_if="Option::is_none")]
88 pub next_run: Option<i64>,
89 #[serde(skip_serializing_if="Option::is_none")]
90 pub last_run_state: Option<String>,
91 #[serde(skip_serializing_if="Option::is_none")]
92 pub last_run_upid: Option<String>,
93 #[serde(skip_serializing_if="Option::is_none")]
94 pub last_run_endtime: Option<i64>,
95}
96
97#[api()]
98#[derive(Debug, Copy, Clone, PartialEq, Serialize, Deserialize)]
99#[serde(rename_all = "lowercase")]
100/// When do we send notifications
101pub enum Notify {
102 /// Never send notification
103 Never,
104 /// Send notifications for failed and successful jobs
105 Always,
106 /// Send notifications for failed jobs only
107 Error,
108}
109
110#[api(
111 properties: {
112 gc: {
113 type: Notify,
114 optional: true,
115 },
116 verify: {
117 type: Notify,
118 optional: true,
119 },
120 sync: {
121 type: Notify,
122 optional: true,
123 },
124 },
125)]
126#[derive(Debug, Serialize, Deserialize)]
127/// Datastore notify settings
128pub struct DatastoreNotify {
129 /// Garbage collection settings
130 pub gc: Option<Notify>,
131 /// Verify job setting
132 pub verify: Option<Notify>,
133 /// Sync job setting
134 pub sync: Option<Notify>,
135}
136
137pub const DATASTORE_NOTIFY_STRING_SCHEMA: Schema = StringSchema::new(
138 "Datastore notification setting")
139 .format(&ApiStringFormat::PropertyString(&DatastoreNotify::API_SCHEMA))
140 .schema();
141
142pub const IGNORE_VERIFIED_BACKUPS_SCHEMA: Schema = BooleanSchema::new(
143 "Do not verify backups that are already verified if their verification is not outdated.")
144 .default(true)
145 .schema();
146
147pub const VERIFICATION_OUTDATED_AFTER_SCHEMA: Schema = IntegerSchema::new(
148 "Days after that a verification becomes outdated")
149 .minimum(1)
150 .schema();
151
152#[api(
153 properties: {
154 id: {
155 schema: JOB_ID_SCHEMA,
156 },
157 store: {
158 schema: DATASTORE_SCHEMA,
159 },
160 "ignore-verified": {
161 optional: true,
162 schema: IGNORE_VERIFIED_BACKUPS_SCHEMA,
163 },
164 "outdated-after": {
165 optional: true,
166 schema: VERIFICATION_OUTDATED_AFTER_SCHEMA,
167 },
168 comment: {
169 optional: true,
170 schema: SINGLE_LINE_COMMENT_SCHEMA,
171 },
172 schedule: {
173 optional: true,
174 schema: VERIFICATION_SCHEDULE_SCHEMA,
175 },
176 }
177)]
ffa403b5 178#[derive(Serialize,Deserialize,Updater)]
e3619d41
DM
179#[serde(rename_all="kebab-case")]
180/// Verification Job
181pub struct VerificationJobConfig {
182 /// unique ID to address this job
ffa403b5 183 #[updater(skip)]
e3619d41
DM
184 pub id: String,
185 /// the datastore ID this verificaiton job affects
186 pub store: String,
187 #[serde(skip_serializing_if="Option::is_none")]
188 /// if not set to false, check the age of the last snapshot verification to filter
189 /// out recent ones, depending on 'outdated_after' configuration.
190 pub ignore_verified: Option<bool>,
191 #[serde(skip_serializing_if="Option::is_none")]
192 /// Reverify snapshots after X days, never if 0. Ignored if 'ignore_verified' is false.
193 pub outdated_after: Option<i64>,
194 #[serde(skip_serializing_if="Option::is_none")]
195 pub comment: Option<String>,
196 #[serde(skip_serializing_if="Option::is_none")]
197 /// when to schedule this job in calendar event notation
198 pub schedule: Option<String>,
199}
200
201#[api(
202 properties: {
203 config: {
204 type: VerificationJobConfig,
205 },
206 status: {
207 type: JobScheduleStatus,
208 },
209 },
210)]
211#[derive(Serialize,Deserialize)]
212#[serde(rename_all="kebab-case")]
213/// Status of Verification Job
214pub struct VerificationJobStatus {
215 #[serde(flatten)]
216 pub config: VerificationJobConfig,
217 #[serde(flatten)]
218 pub status: JobScheduleStatus,
219}
220
221#[api(
222 properties: {
223 store: {
224 schema: DATASTORE_SCHEMA,
225 },
226 pool: {
227 schema: MEDIA_POOL_NAME_SCHEMA,
228 },
229 drive: {
230 schema: DRIVE_NAME_SCHEMA,
231 },
232 "eject-media": {
233 description: "Eject media upon job completion.",
234 type: bool,
235 optional: true,
236 },
237 "export-media-set": {
238 description: "Export media set upon job completion.",
239 type: bool,
240 optional: true,
241 },
242 "latest-only": {
243 description: "Backup latest snapshots only.",
244 type: bool,
245 optional: true,
246 },
247 "notify-user": {
248 optional: true,
249 type: Userid,
250 },
251 }
252)]
cdc83c4e 253#[derive(Serialize,Deserialize,Clone,Updater)]
e3619d41
DM
254#[serde(rename_all="kebab-case")]
255/// Tape Backup Job Setup
256pub struct TapeBackupJobSetup {
257 pub store: String,
258 pub pool: String,
259 pub drive: String,
260 #[serde(skip_serializing_if="Option::is_none")]
261 pub eject_media: Option<bool>,
262 #[serde(skip_serializing_if="Option::is_none")]
263 pub export_media_set: Option<bool>,
264 #[serde(skip_serializing_if="Option::is_none")]
265 pub latest_only: Option<bool>,
266 /// Send job email notification to this user
267 #[serde(skip_serializing_if="Option::is_none")]
268 pub notify_user: Option<Userid>,
269}
270
271#[api(
272 properties: {
273 id: {
274 schema: JOB_ID_SCHEMA,
275 },
276 setup: {
277 type: TapeBackupJobSetup,
278 },
279 comment: {
280 optional: true,
281 schema: SINGLE_LINE_COMMENT_SCHEMA,
282 },
283 schedule: {
284 optional: true,
285 schema: SYNC_SCHEDULE_SCHEMA,
286 },
287 }
288)]
cdc83c4e 289#[derive(Serialize,Deserialize,Clone,Updater)]
e3619d41
DM
290#[serde(rename_all="kebab-case")]
291/// Tape Backup Job
292pub struct TapeBackupJobConfig {
cdc83c4e 293 #[updater(skip)]
e3619d41
DM
294 pub id: String,
295 #[serde(flatten)]
296 pub setup: TapeBackupJobSetup,
297 #[serde(skip_serializing_if="Option::is_none")]
298 pub comment: Option<String>,
299 #[serde(skip_serializing_if="Option::is_none")]
300 pub schedule: Option<String>,
301}
302
303#[api(
304 properties: {
305 config: {
306 type: TapeBackupJobConfig,
307 },
308 status: {
309 type: JobScheduleStatus,
310 },
311 },
312)]
313#[derive(Serialize,Deserialize)]
314#[serde(rename_all="kebab-case")]
315/// Status of Tape Backup Job
316pub struct TapeBackupJobStatus {
317 #[serde(flatten)]
318 pub config: TapeBackupJobConfig,
319 #[serde(flatten)]
320 pub status: JobScheduleStatus,
321 /// Next tape used (best guess)
322 #[serde(skip_serializing_if="Option::is_none")]
323 pub next_media_label: Option<String>,
324}
325
326#[api(
327 properties: {
328 id: {
329 schema: JOB_ID_SCHEMA,
330 },
331 store: {
332 schema: DATASTORE_SCHEMA,
333 },
334 "owner": {
335 type: Authid,
336 optional: true,
337 },
338 remote: {
339 schema: REMOTE_ID_SCHEMA,
340 },
341 "remote-store": {
342 schema: DATASTORE_SCHEMA,
343 },
344 "remove-vanished": {
345 schema: REMOVE_VANISHED_BACKUPS_SCHEMA,
346 optional: true,
347 },
348 comment: {
349 optional: true,
350 schema: SINGLE_LINE_COMMENT_SCHEMA,
351 },
352 schedule: {
353 optional: true,
354 schema: SYNC_SCHEDULE_SCHEMA,
355 },
356 }
357)]
5bd77f00 358#[derive(Serialize,Deserialize,Clone,Updater)]
e3619d41
DM
359#[serde(rename_all="kebab-case")]
360/// Sync Job
361pub struct SyncJobConfig {
5bd77f00 362 #[updater(skip)]
e3619d41
DM
363 pub id: String,
364 pub store: String,
365 #[serde(skip_serializing_if="Option::is_none")]
366 pub owner: Option<Authid>,
367 pub remote: String,
368 pub remote_store: String,
369 #[serde(skip_serializing_if="Option::is_none")]
370 pub remove_vanished: Option<bool>,
371 #[serde(skip_serializing_if="Option::is_none")]
372 pub comment: Option<String>,
373 #[serde(skip_serializing_if="Option::is_none")]
374 pub schedule: Option<String>,
375}
376
377#[api(
378 properties: {
379 config: {
380 type: SyncJobConfig,
381 },
382 status: {
383 type: JobScheduleStatus,
384 },
385 },
386)]
387
388#[derive(Serialize,Deserialize)]
389#[serde(rename_all="kebab-case")]
390/// Status of Sync Job
391pub struct SyncJobStatus {
392 #[serde(flatten)]
393 pub config: SyncJobConfig,
394 #[serde(flatten)]
395 pub status: JobScheduleStatus,
396}