]>
Commit | Line | Data |
---|---|---|
155f657f WB |
1 | //! Basic API types used by most of the PBS code. |
2 | ||
c23192d3 WB |
3 | use serde::{Deserialize, Serialize}; |
4 | ||
5 | use proxmox::api::api; | |
7b570c17 WB |
6 | use proxmox::api::schema::{ |
7 | ApiStringFormat, ApiType, ArraySchema, EnumEntry, IntegerSchema, ReturnType, Schema, | |
8 | StringSchema, | |
9 | }; | |
86fb3877 | 10 | use proxmox::const_regex; |
75f83c6a | 11 | use proxmox::{IPRE, IPRE_BRACKET, IPV4OCTET, IPV4RE, IPV6H16, IPV6LS32, IPV6RE}; |
86fb3877 | 12 | |
bfff4eaa WB |
13 | #[rustfmt::skip] |
14 | #[macro_export] | |
15 | macro_rules! PROXMOX_SAFE_ID_REGEX_STR { () => { r"(?:[A-Za-z0-9_][A-Za-z0-9._\-]*)" }; } | |
16 | ||
17 | #[rustfmt::skip] | |
18 | #[macro_export] | |
19 | macro_rules! BACKUP_ID_RE { () => (r"[A-Za-z0-9_][A-Za-z0-9._\-]*") } | |
20 | ||
21 | #[rustfmt::skip] | |
22 | #[macro_export] | |
23 | macro_rules! BACKUP_TYPE_RE { () => (r"(?:host|vm|ct)") } | |
24 | ||
25 | #[rustfmt::skip] | |
26 | #[macro_export] | |
27 | macro_rules! BACKUP_TIME_RE { () => (r"[0-9]{4}-[0-9]{2}-[0-9]{2}T[0-9]{2}:[0-9]{2}:[0-9]{2}Z") } | |
28 | ||
29 | #[rustfmt::skip] | |
30 | #[macro_export] | |
31 | macro_rules! SNAPSHOT_PATH_REGEX_STR { | |
32 | () => ( | |
33 | concat!(r"(", BACKUP_TYPE_RE!(), ")/(", BACKUP_ID_RE!(), ")/(", BACKUP_TIME_RE!(), r")") | |
34 | ); | |
35 | } | |
36 | ||
e3619d41 DM |
37 | mod jobs; |
38 | pub use jobs::*; | |
39 | ||
45d5d873 DM |
40 | mod key_derivation; |
41 | pub use key_derivation::{Kdf, KeyInfo}; | |
42 | ||
751f6b61 WB |
43 | #[macro_use] |
44 | mod userid; | |
45 | pub use userid::Authid; | |
46 | pub use userid::Userid; | |
47 | pub use userid::{Realm, RealmRef}; | |
48 | pub use userid::{Tokenname, TokennameRef}; | |
49 | pub use userid::{Username, UsernameRef}; | |
50 | pub use userid::{PROXMOX_GROUP_ID_SCHEMA, PROXMOX_TOKEN_ID_SCHEMA, PROXMOX_TOKEN_NAME_SCHEMA}; | |
51 | ||
2b7f8dd5 WB |
52 | #[macro_use] |
53 | mod user; | |
54 | pub use user::{ApiToken, User, UserWithTokens}; | |
55 | pub use user::{ | |
56 | EMAIL_SCHEMA, ENABLE_USER_SCHEMA, EXPIRE_USER_SCHEMA, FIRST_NAME_SCHEMA, LAST_NAME_SCHEMA, | |
57 | }; | |
58 | ||
95f9d67c | 59 | pub mod upid; |
c23192d3 | 60 | pub use upid::UPID; |
95f9d67c | 61 | |
ea584a75 WB |
62 | mod crypto; |
63 | pub use crypto::{CryptMode, Fingerprint}; | |
64 | ||
013b1e8b WB |
65 | pub mod file_restore; |
66 | ||
6afdda88 DM |
67 | mod remote; |
68 | pub use remote::*; | |
69 | ||
1ce8e905 DM |
70 | mod tape; |
71 | pub use tape::*; | |
72 | ||
75f83c6a WB |
73 | #[rustfmt::skip] |
74 | #[macro_use] | |
75 | mod local_macros { | |
76 | macro_rules! DNS_LABEL { () => (r"(?:[a-zA-Z0-9](?:[a-zA-Z0-9\-]*[a-zA-Z0-9])?)") } | |
77 | macro_rules! DNS_NAME { () => (concat!(r"(?:(?:", DNS_LABEL!() , r"\.)*", DNS_LABEL!(), ")")) } | |
78 | macro_rules! CIDR_V4_REGEX_STR { () => (concat!(r"(?:", IPV4RE!(), r"/\d{1,2})$")) } | |
79 | macro_rules! CIDR_V6_REGEX_STR { () => (concat!(r"(?:", IPV6RE!(), r"/\d{1,3})$")) } | |
80 | macro_rules! DNS_ALIAS_LABEL { () => (r"(?:[a-zA-Z0-9_](?:[a-zA-Z0-9\-]*[a-zA-Z0-9])?)") } | |
81 | macro_rules! DNS_ALIAS_NAME { | |
82 | () => (concat!(r"(?:(?:", DNS_ALIAS_LABEL!() , r"\.)*", DNS_ALIAS_LABEL!(), ")")) | |
83 | } | |
84 | } | |
85 | ||
86fb3877 | 86 | const_regex! { |
75f83c6a WB |
87 | pub IP_V4_REGEX = concat!(r"^", IPV4RE!(), r"$"); |
88 | pub IP_V6_REGEX = concat!(r"^", IPV6RE!(), r"$"); | |
89 | pub IP_REGEX = concat!(r"^", IPRE!(), r"$"); | |
90 | pub CIDR_V4_REGEX = concat!(r"^", CIDR_V4_REGEX_STR!(), r"$"); | |
91 | pub CIDR_V6_REGEX = concat!(r"^", CIDR_V6_REGEX_STR!(), r"$"); | |
92 | pub CIDR_REGEX = concat!(r"^(?:", CIDR_V4_REGEX_STR!(), "|", CIDR_V6_REGEX_STR!(), r")$"); | |
93 | pub HOSTNAME_REGEX = r"^(?:[a-zA-Z0-9](?:[a-zA-Z0-9\-]*[a-zA-Z0-9])?)$"; | |
94 | pub DNS_NAME_REGEX = concat!(r"^", DNS_NAME!(), r"$"); | |
95 | pub DNS_ALIAS_REGEX = concat!(r"^", DNS_ALIAS_NAME!(), r"$"); | |
96 | pub DNS_NAME_OR_IP_REGEX = concat!(r"^(?:", DNS_NAME!(), "|", IPRE!(), r")$"); | |
97 | ||
98 | pub SHA256_HEX_REGEX = r"^[a-f0-9]{64}$"; // fixme: define in common_regex ? | |
99 | ||
100 | pub PASSWORD_REGEX = r"^[[:^cntrl:]]*$"; // everything but control characters | |
101 | ||
102 | pub UUID_REGEX = r"^[0-9a-f]{8}(?:-[0-9a-f]{4}){3}-[0-9a-f]{12}$"; | |
103 | ||
bfff4eaa WB |
104 | pub BACKUP_TYPE_REGEX = concat!(r"^(", BACKUP_TYPE_RE!(), r")$"); |
105 | ||
106 | pub BACKUP_ID_REGEX = concat!(r"^", BACKUP_ID_RE!(), r"$"); | |
107 | ||
108 | pub BACKUP_DATE_REGEX = concat!(r"^", BACKUP_TIME_RE!() ,r"$"); | |
109 | ||
110 | pub GROUP_PATH_REGEX = concat!(r"^(", BACKUP_TYPE_RE!(), ")/(", BACKUP_ID_RE!(), r")$"); | |
111 | ||
112 | pub BACKUP_FILE_REGEX = r"^.*\.([fd]idx|blob)$"; | |
113 | ||
114 | pub SNAPSHOT_PATH_REGEX = concat!(r"^", SNAPSHOT_PATH_REGEX_STR!(), r"$"); | |
115 | ||
86fb3877 | 116 | pub FINGERPRINT_SHA256_REGEX = r"^(?:[0-9a-fA-F][0-9a-fA-F])(?::[0-9a-fA-F][0-9a-fA-F]){31}$"; |
3c430e9a WB |
117 | |
118 | /// Regex for safe identifiers. | |
119 | /// | |
120 | /// This | |
121 | /// [article](https://dwheeler.com/essays/fixing-unix-linux-filenames.html) | |
122 | /// contains further information why it is reasonable to restict | |
123 | /// names this way. This is not only useful for filenames, but for | |
124 | /// any identifier command line tools work with. | |
125 | pub PROXMOX_SAFE_ID_REGEX = concat!(r"^", PROXMOX_SAFE_ID_REGEX_STR!(), r"$"); | |
126 | ||
127 | pub SINGLE_LINE_COMMENT_REGEX = r"^[[:^cntrl:]]*$"; | |
75f83c6a WB |
128 | |
129 | pub BACKUP_REPO_URL_REGEX = concat!( | |
130 | r"^^(?:(?:(", | |
131 | USER_ID_REGEX_STR!(), "|", APITOKEN_ID_REGEX_STR!(), | |
132 | ")@)?(", | |
133 | DNS_NAME!(), "|", IPRE_BRACKET!(), | |
134 | "):)?(?:([0-9]{1,5}):)?(", PROXMOX_SAFE_ID_REGEX_STR!(), r")$" | |
135 | ); | |
4c1b7761 WB |
136 | |
137 | pub BLOCKDEVICE_NAME_REGEX = r"^(:?(:?h|s|x?v)d[a-z]+)|(:?nvme\d+n\d+)$"; | |
86fb3877 WB |
138 | } |
139 | ||
75f83c6a WB |
140 | pub const IP_V4_FORMAT: ApiStringFormat = ApiStringFormat::Pattern(&IP_V4_REGEX); |
141 | pub const IP_V6_FORMAT: ApiStringFormat = ApiStringFormat::Pattern(&IP_V6_REGEX); | |
142 | pub const IP_FORMAT: ApiStringFormat = ApiStringFormat::Pattern(&IP_REGEX); | |
143 | pub const CIDR_V4_FORMAT: ApiStringFormat = ApiStringFormat::Pattern(&CIDR_V4_REGEX); | |
144 | pub const CIDR_V6_FORMAT: ApiStringFormat = ApiStringFormat::Pattern(&CIDR_V6_REGEX); | |
145 | pub const CIDR_FORMAT: ApiStringFormat = ApiStringFormat::Pattern(&CIDR_REGEX); | |
146 | ||
6afdda88 DM |
147 | pub const DNS_NAME_FORMAT: ApiStringFormat = |
148 | ApiStringFormat::Pattern(&DNS_NAME_REGEX); | |
149 | ||
150 | pub const DNS_NAME_OR_IP_FORMAT: ApiStringFormat = | |
151 | ApiStringFormat::Pattern(&DNS_NAME_OR_IP_REGEX); | |
152 | ||
153 | pub const DNS_NAME_OR_IP_SCHEMA: Schema = StringSchema::new("DNS name or IP address.") | |
154 | .format(&DNS_NAME_OR_IP_FORMAT) | |
155 | .schema(); | |
156 | ||
ea584a75 WB |
157 | pub const BACKUP_ID_SCHEMA: Schema = StringSchema::new("Backup ID.") |
158 | .format(&BACKUP_ID_FORMAT) | |
159 | .schema(); | |
160 | pub const BACKUP_TYPE_SCHEMA: Schema = StringSchema::new("Backup type.") | |
161 | .format(&ApiStringFormat::Enum(&[ | |
162 | EnumEntry::new("vm", "Virtual Machine Backup"), | |
163 | EnumEntry::new("ct", "Container Backup"), | |
164 | EnumEntry::new("host", "Host Backup"), | |
165 | ])) | |
166 | .schema(); | |
167 | pub const BACKUP_TIME_SCHEMA: Schema = IntegerSchema::new("Backup time (Unix epoch.)") | |
168 | .minimum(1_547_797_308) | |
169 | .schema(); | |
170 | ||
171 | pub const DATASTORE_SCHEMA: Schema = StringSchema::new("Datastore name.") | |
172 | .format(&PROXMOX_SAFE_ID_FORMAT) | |
173 | .min_length(3) | |
174 | .max_length(32) | |
175 | .schema(); | |
176 | ||
21211748 DM |
177 | pub const REALM_ID_SCHEMA: Schema = StringSchema::new("Realm name.") |
178 | .format(&PROXMOX_SAFE_ID_FORMAT) | |
179 | .min_length(2) | |
180 | .max_length(32) | |
181 | .schema(); | |
182 | ||
86fb3877 WB |
183 | pub const FINGERPRINT_SHA256_FORMAT: ApiStringFormat = |
184 | ApiStringFormat::Pattern(&FINGERPRINT_SHA256_REGEX); | |
185 | ||
186 | pub const CERT_FINGERPRINT_SHA256_SCHEMA: Schema = | |
187 | StringSchema::new("X509 certificate fingerprint (sha256).") | |
188 | .format(&FINGERPRINT_SHA256_FORMAT) | |
189 | .schema(); | |
3c430e9a | 190 | |
2b7f8dd5 | 191 | pub const PRUNE_SCHEMA_KEEP_DAILY: Schema = IntegerSchema::new("Number of daily backups to keep.") |
ced69458 DC |
192 | .minimum(1) |
193 | .schema(); | |
194 | ||
2b7f8dd5 WB |
195 | pub const PRUNE_SCHEMA_KEEP_HOURLY: Schema = |
196 | IntegerSchema::new("Number of hourly backups to keep.") | |
197 | .minimum(1) | |
198 | .schema(); | |
ced69458 | 199 | |
2b7f8dd5 | 200 | pub const PRUNE_SCHEMA_KEEP_LAST: Schema = IntegerSchema::new("Number of backups to keep.") |
ced69458 DC |
201 | .minimum(1) |
202 | .schema(); | |
203 | ||
2b7f8dd5 WB |
204 | pub const PRUNE_SCHEMA_KEEP_MONTHLY: Schema = |
205 | IntegerSchema::new("Number of monthly backups to keep.") | |
206 | .minimum(1) | |
207 | .schema(); | |
ced69458 | 208 | |
2b7f8dd5 WB |
209 | pub const PRUNE_SCHEMA_KEEP_WEEKLY: Schema = |
210 | IntegerSchema::new("Number of weekly backups to keep.") | |
211 | .minimum(1) | |
212 | .schema(); | |
ced69458 | 213 | |
2b7f8dd5 WB |
214 | pub const PRUNE_SCHEMA_KEEP_YEARLY: Schema = |
215 | IntegerSchema::new("Number of yearly backups to keep.") | |
216 | .minimum(1) | |
217 | .schema(); | |
ced69458 | 218 | |
3c430e9a WB |
219 | pub const PROXMOX_SAFE_ID_FORMAT: ApiStringFormat = |
220 | ApiStringFormat::Pattern(&PROXMOX_SAFE_ID_REGEX); | |
221 | ||
222 | pub const SINGLE_LINE_COMMENT_FORMAT: ApiStringFormat = | |
223 | ApiStringFormat::Pattern(&SINGLE_LINE_COMMENT_REGEX); | |
224 | ||
225 | pub const SINGLE_LINE_COMMENT_SCHEMA: Schema = StringSchema::new("Comment (single line).") | |
226 | .format(&SINGLE_LINE_COMMENT_FORMAT) | |
227 | .schema(); | |
bfff4eaa | 228 | |
2b7f8dd5 WB |
229 | pub const PROXMOX_CONFIG_DIGEST_SCHEMA: Schema = StringSchema::new( |
230 | "Prevent changes if current configuration file has different \ | |
231 | SHA256 digest. This can be used to prevent concurrent \ | |
232 | modifications.", | |
233 | ) | |
234 | .format(&PVE_CONFIG_DIGEST_FORMAT) | |
235 | .schema(); | |
236 | ||
bfff4eaa | 237 | pub const BACKUP_ID_FORMAT: ApiStringFormat = ApiStringFormat::Pattern(&BACKUP_ID_REGEX); |
c23192d3 | 238 | |
75f83c6a WB |
239 | /// API schema format definition for repository URLs |
240 | pub const BACKUP_REPO_URL: ApiStringFormat = ApiStringFormat::Pattern(&BACKUP_REPO_URL_REGEX); | |
241 | ||
c23192d3 WB |
242 | #[api( |
243 | properties: { | |
244 | "upid": { | |
245 | optional: true, | |
246 | type: UPID, | |
247 | }, | |
248 | }, | |
249 | )] | |
250 | #[derive(Clone, Serialize, Deserialize)] | |
251 | #[serde(rename_all = "kebab-case")] | |
252 | /// Garbage collection status. | |
253 | pub struct GarbageCollectionStatus { | |
254 | pub upid: Option<String>, | |
255 | /// Number of processed index files. | |
256 | pub index_file_count: usize, | |
257 | /// Sum of bytes referred by index files. | |
258 | pub index_data_bytes: u64, | |
259 | /// Bytes used on disk. | |
260 | pub disk_bytes: u64, | |
261 | /// Chunks used on disk. | |
262 | pub disk_chunks: usize, | |
263 | /// Sum of removed bytes. | |
264 | pub removed_bytes: u64, | |
265 | /// Number of removed chunks. | |
266 | pub removed_chunks: usize, | |
267 | /// Sum of pending bytes (pending removal - kept for safety). | |
268 | pub pending_bytes: u64, | |
269 | /// Number of pending chunks (pending removal - kept for safety). | |
270 | pub pending_chunks: usize, | |
271 | /// Number of chunks marked as .bad by verify that have been removed by GC. | |
272 | pub removed_bad: usize, | |
273 | /// Number of chunks still marked as .bad after garbage collection. | |
274 | pub still_bad: usize, | |
275 | } | |
276 | ||
277 | impl Default for GarbageCollectionStatus { | |
278 | fn default() -> Self { | |
279 | GarbageCollectionStatus { | |
280 | upid: None, | |
281 | index_file_count: 0, | |
282 | index_data_bytes: 0, | |
283 | disk_bytes: 0, | |
284 | disk_chunks: 0, | |
285 | removed_bytes: 0, | |
286 | removed_chunks: 0, | |
287 | pending_bytes: 0, | |
288 | pending_chunks: 0, | |
289 | removed_bad: 0, | |
290 | still_bad: 0, | |
291 | } | |
292 | } | |
293 | } | |
75f83c6a WB |
294 | |
295 | pub const PVE_CONFIG_DIGEST_FORMAT: ApiStringFormat = ApiStringFormat::Pattern(&SHA256_HEX_REGEX); | |
296 | pub const CHUNK_DIGEST_FORMAT: ApiStringFormat = ApiStringFormat::Pattern(&SHA256_HEX_REGEX); | |
297 | ||
298 | pub const PASSWORD_FORMAT: ApiStringFormat = ApiStringFormat::Pattern(&PASSWORD_REGEX); | |
299 | ||
300 | pub const UUID_FORMAT: ApiStringFormat = ApiStringFormat::Pattern(&UUID_REGEX); | |
ea584a75 WB |
301 | |
302 | pub const BACKUP_ARCHIVE_NAME_SCHEMA: Schema = StringSchema::new("Backup archive name.") | |
303 | .format(&PROXMOX_SAFE_ID_FORMAT) | |
304 | .schema(); | |
305 | ||
306 | // Complex type definitions | |
307 | ||
308 | #[api( | |
309 | properties: { | |
310 | "filename": { | |
311 | schema: BACKUP_ARCHIVE_NAME_SCHEMA, | |
312 | }, | |
313 | "crypt-mode": { | |
314 | type: CryptMode, | |
315 | optional: true, | |
316 | }, | |
317 | }, | |
318 | )] | |
319 | #[derive(Serialize, Deserialize)] | |
320 | #[serde(rename_all = "kebab-case")] | |
321 | /// Basic information about archive files inside a backup snapshot. | |
322 | pub struct BackupContent { | |
323 | pub filename: String, | |
324 | /// Info if file is encrypted, signed, or neither. | |
325 | #[serde(skip_serializing_if = "Option::is_none")] | |
326 | pub crypt_mode: Option<CryptMode>, | |
327 | /// Archive size (from backup manifest). | |
328 | #[serde(skip_serializing_if = "Option::is_none")] | |
329 | pub size: Option<u64>, | |
330 | } | |
331 | ||
332 | #[api()] | |
333 | #[derive(Debug, Copy, Clone, PartialEq, Serialize, Deserialize)] | |
334 | #[serde(rename_all = "lowercase")] | |
335 | /// Result of a verify operation. | |
336 | pub enum VerifyState { | |
337 | /// Verification was successful | |
338 | Ok, | |
339 | /// Verification reported one or more errors | |
340 | Failed, | |
341 | } | |
342 | ||
343 | #[api( | |
344 | properties: { | |
345 | upid: { | |
346 | type: UPID, | |
347 | }, | |
348 | state: { | |
349 | type: VerifyState, | |
350 | }, | |
351 | }, | |
352 | )] | |
353 | #[derive(Serialize, Deserialize)] | |
354 | /// Task properties. | |
355 | pub struct SnapshotVerifyState { | |
356 | /// UPID of the verify task | |
357 | pub upid: UPID, | |
358 | /// State of the verification. Enum. | |
359 | pub state: VerifyState, | |
360 | } | |
361 | ||
362 | #[api( | |
363 | properties: { | |
364 | "backup-type": { | |
365 | schema: BACKUP_TYPE_SCHEMA, | |
366 | }, | |
367 | "backup-id": { | |
368 | schema: BACKUP_ID_SCHEMA, | |
369 | }, | |
370 | "backup-time": { | |
371 | schema: BACKUP_TIME_SCHEMA, | |
372 | }, | |
373 | comment: { | |
374 | schema: SINGLE_LINE_COMMENT_SCHEMA, | |
375 | optional: true, | |
376 | }, | |
377 | verification: { | |
378 | type: SnapshotVerifyState, | |
379 | optional: true, | |
380 | }, | |
381 | fingerprint: { | |
382 | type: String, | |
383 | optional: true, | |
384 | }, | |
385 | files: { | |
386 | items: { | |
387 | schema: BACKUP_ARCHIVE_NAME_SCHEMA | |
388 | }, | |
389 | }, | |
390 | owner: { | |
391 | type: Authid, | |
392 | optional: true, | |
393 | }, | |
394 | }, | |
395 | )] | |
396 | #[derive(Serialize, Deserialize)] | |
397 | #[serde(rename_all = "kebab-case")] | |
398 | /// Basic information about backup snapshot. | |
399 | pub struct SnapshotListItem { | |
400 | pub backup_type: String, // enum | |
401 | pub backup_id: String, | |
402 | pub backup_time: i64, | |
403 | /// The first line from manifest "notes" | |
404 | #[serde(skip_serializing_if = "Option::is_none")] | |
405 | pub comment: Option<String>, | |
406 | /// The result of the last run verify task | |
407 | #[serde(skip_serializing_if = "Option::is_none")] | |
408 | pub verification: Option<SnapshotVerifyState>, | |
409 | /// Fingerprint of encryption key | |
410 | #[serde(skip_serializing_if = "Option::is_none")] | |
411 | pub fingerprint: Option<Fingerprint>, | |
412 | /// List of contained archive files. | |
413 | pub files: Vec<BackupContent>, | |
414 | /// Overall snapshot size (sum of all archive sizes). | |
415 | #[serde(skip_serializing_if = "Option::is_none")] | |
416 | pub size: Option<u64>, | |
417 | /// The owner of the snapshots group | |
418 | #[serde(skip_serializing_if = "Option::is_none")] | |
419 | pub owner: Option<Authid>, | |
420 | } | |
421 | ||
422 | #[api( | |
423 | properties: { | |
424 | "backup-type": { | |
425 | schema: BACKUP_TYPE_SCHEMA, | |
426 | }, | |
427 | "backup-id": { | |
428 | schema: BACKUP_ID_SCHEMA, | |
429 | }, | |
430 | "last-backup": { | |
431 | schema: BACKUP_TIME_SCHEMA, | |
432 | }, | |
433 | "backup-count": { | |
434 | type: Integer, | |
435 | }, | |
436 | files: { | |
437 | items: { | |
438 | schema: BACKUP_ARCHIVE_NAME_SCHEMA | |
439 | }, | |
440 | }, | |
441 | owner: { | |
442 | type: Authid, | |
443 | optional: true, | |
444 | }, | |
445 | }, | |
446 | )] | |
447 | #[derive(Serialize, Deserialize)] | |
448 | #[serde(rename_all = "kebab-case")] | |
449 | /// Basic information about a backup group. | |
450 | pub struct GroupListItem { | |
451 | pub backup_type: String, // enum | |
452 | pub backup_id: String, | |
453 | pub last_backup: i64, | |
454 | /// Number of contained snapshots | |
455 | pub backup_count: u64, | |
456 | /// List of contained archive files. | |
457 | pub files: Vec<String>, | |
458 | /// The owner of group | |
459 | #[serde(skip_serializing_if = "Option::is_none")] | |
460 | pub owner: Option<Authid>, | |
d6688884 | 461 | /// The first line from group "notes" |
2b7f8dd5 | 462 | #[serde(skip_serializing_if = "Option::is_none")] |
d6688884 | 463 | pub comment: Option<String>, |
ea584a75 WB |
464 | } |
465 | ||
466 | #[api( | |
467 | properties: { | |
468 | store: { | |
469 | schema: DATASTORE_SCHEMA, | |
470 | }, | |
471 | comment: { | |
472 | optional: true, | |
473 | schema: SINGLE_LINE_COMMENT_SCHEMA, | |
474 | }, | |
475 | }, | |
476 | )] | |
477 | #[derive(Serialize, Deserialize)] | |
478 | #[serde(rename_all = "kebab-case")] | |
479 | /// Basic information about a datastore. | |
480 | pub struct DataStoreListItem { | |
481 | pub store: String, | |
482 | pub comment: Option<String>, | |
483 | } | |
484 | ||
485 | #[api( | |
486 | properties: { | |
487 | "backup-type": { | |
488 | schema: BACKUP_TYPE_SCHEMA, | |
489 | }, | |
490 | "backup-id": { | |
491 | schema: BACKUP_ID_SCHEMA, | |
492 | }, | |
493 | "backup-time": { | |
494 | schema: BACKUP_TIME_SCHEMA, | |
495 | }, | |
496 | }, | |
497 | )] | |
498 | #[derive(Serialize, Deserialize)] | |
499 | #[serde(rename_all = "kebab-case")] | |
500 | /// Prune result. | |
501 | pub struct PruneListItem { | |
502 | pub backup_type: String, // enum | |
503 | pub backup_id: String, | |
504 | pub backup_time: i64, | |
505 | /// Keep snapshot | |
506 | pub keep: bool, | |
507 | } | |
51ec8a3c WB |
508 | |
509 | #[api()] | |
510 | #[derive(Default, Serialize, Deserialize)] | |
511 | /// Storage space usage information. | |
512 | pub struct StorageStatus { | |
513 | /// Total space (bytes). | |
514 | pub total: u64, | |
515 | /// Used space (bytes). | |
516 | pub used: u64, | |
517 | /// Available space (bytes). | |
518 | pub avail: u64, | |
519 | } | |
520 | ||
521 | #[api()] | |
522 | #[derive(Serialize, Deserialize, Default)] | |
523 | /// Backup Type group/snapshot counts. | |
524 | pub struct TypeCounts { | |
525 | /// The number of groups of the type. | |
526 | pub groups: u64, | |
527 | /// The number of snapshots of the type. | |
528 | pub snapshots: u64, | |
529 | } | |
530 | ||
531 | #[api( | |
532 | properties: { | |
533 | ct: { | |
534 | type: TypeCounts, | |
535 | optional: true, | |
536 | }, | |
537 | host: { | |
538 | type: TypeCounts, | |
539 | optional: true, | |
540 | }, | |
541 | vm: { | |
542 | type: TypeCounts, | |
543 | optional: true, | |
544 | }, | |
545 | other: { | |
546 | type: TypeCounts, | |
547 | optional: true, | |
548 | }, | |
549 | }, | |
550 | )] | |
551 | #[derive(Serialize, Deserialize, Default)] | |
552 | /// Counts of groups/snapshots per BackupType. | |
553 | pub struct Counts { | |
554 | /// The counts for CT backups | |
555 | pub ct: Option<TypeCounts>, | |
556 | /// The counts for Host backups | |
557 | pub host: Option<TypeCounts>, | |
558 | /// The counts for VM backups | |
559 | pub vm: Option<TypeCounts>, | |
560 | /// The counts for other backup types | |
561 | pub other: Option<TypeCounts>, | |
562 | } | |
eb5e0ae6 WB |
563 | |
564 | pub const PASSWORD_HINT_SCHEMA: Schema = StringSchema::new("Password hint.") | |
565 | .format(&SINGLE_LINE_COMMENT_FORMAT) | |
566 | .min_length(1) | |
567 | .max_length(64) | |
568 | .schema(); | |
569 | ||
570 | ||
571 | #[api] | |
572 | #[derive(Deserialize, Serialize)] | |
573 | /// RSA public key information | |
574 | pub struct RsaPubKeyInfo { | |
575 | /// Path to key (if stored in a file) | |
576 | #[serde(skip_serializing_if="Option::is_none")] | |
577 | pub path: Option<String>, | |
578 | /// RSA exponent | |
579 | pub exponent: String, | |
580 | /// Hex-encoded RSA modulus | |
581 | pub modulus: String, | |
582 | /// Key (modulus) length in bits | |
583 | pub length: usize, | |
584 | } | |
585 | ||
586 | impl std::convert::TryFrom<openssl::rsa::Rsa<openssl::pkey::Public>> for RsaPubKeyInfo { | |
587 | type Error = anyhow::Error; | |
588 | ||
589 | fn try_from(value: openssl::rsa::Rsa<openssl::pkey::Public>) -> Result<Self, Self::Error> { | |
590 | let modulus = value.n().to_hex_str()?.to_string(); | |
591 | let exponent = value.e().to_dec_str()?.to_string(); | |
592 | let length = value.size() as usize * 8; | |
593 | ||
594 | Ok(Self { | |
595 | path: None, | |
596 | exponent, | |
597 | modulus, | |
598 | length, | |
599 | }) | |
600 | } | |
601 | } | |
7b570c17 WB |
602 | |
603 | #[api( | |
604 | properties: { | |
605 | upid: { schema: UPID::API_SCHEMA }, | |
606 | }, | |
607 | )] | |
608 | #[derive(Serialize, Deserialize)] | |
609 | /// Task properties. | |
610 | pub struct TaskListItem { | |
611 | pub upid: String, | |
612 | /// The node name where the task is running on. | |
613 | pub node: String, | |
614 | /// The Unix PID | |
615 | pub pid: i64, | |
616 | /// The task start time (Epoch) | |
617 | pub pstart: u64, | |
618 | /// The task start time (Epoch) | |
619 | pub starttime: i64, | |
620 | /// Worker type (arbitrary ASCII string) | |
621 | pub worker_type: String, | |
622 | /// Worker ID (arbitrary ASCII string) | |
623 | pub worker_id: Option<String>, | |
624 | /// The authenticated entity who started the task | |
625 | pub user: Authid, | |
626 | /// The task end time (Epoch) | |
627 | #[serde(skip_serializing_if="Option::is_none")] | |
628 | pub endtime: Option<i64>, | |
629 | /// Task end status | |
630 | #[serde(skip_serializing_if="Option::is_none")] | |
631 | pub status: Option<String>, | |
632 | } | |
633 | ||
634 | pub const ADMIN_DATASTORE_LIST_SNAPSHOTS_RETURN_TYPE: ReturnType = ReturnType { | |
635 | optional: false, | |
636 | schema: &ArraySchema::new( | |
637 | "Returns the list of snapshots.", | |
638 | &SnapshotListItem::API_SCHEMA, | |
639 | ).schema(), | |
640 | }; | |
641 | ||
642 | pub const ADMIN_DATASTORE_LIST_SNAPSHOT_FILES_RETURN_TYPE: ReturnType = ReturnType { | |
643 | optional: false, | |
644 | schema: &ArraySchema::new( | |
645 | "Returns the list of archive files inside a backup snapshots.", | |
646 | &BackupContent::API_SCHEMA, | |
647 | ).schema(), | |
648 | }; | |
649 | ||
650 | pub const ADMIN_DATASTORE_LIST_GROUPS_RETURN_TYPE: ReturnType = ReturnType { | |
651 | optional: false, | |
652 | schema: &ArraySchema::new( | |
653 | "Returns the list of backup groups.", | |
654 | &GroupListItem::API_SCHEMA, | |
655 | ).schema(), | |
656 | }; | |
657 | ||
658 | pub const ADMIN_DATASTORE_PRUNE_RETURN_TYPE: ReturnType = ReturnType { | |
659 | optional: false, | |
660 | schema: &ArraySchema::new( | |
661 | "Returns the list of snapshots and a flag indicating if there are kept or removed.", | |
662 | &PruneListItem::API_SCHEMA, | |
663 | ).schema(), | |
664 | }; | |
665 | ||
666 | pub const NODE_TASKS_LIST_TASKS_RETURN_TYPE: ReturnType = ReturnType { | |
667 | optional: false, | |
668 | schema: &ArraySchema::new( | |
669 | "A list of tasks.", | |
670 | &TaskListItem::API_SCHEMA, | |
671 | ).schema(), | |
672 | }; | |
e3619d41 DM |
673 | |
674 | #[api()] | |
675 | #[derive(Debug, Clone, Serialize, Deserialize)] | |
676 | #[serde(rename_all = "PascalCase")] | |
677 | /// Describes a package for which an update is available. | |
678 | pub struct APTUpdateInfo { | |
679 | /// Package name | |
680 | pub package: String, | |
681 | /// Package title | |
682 | pub title: String, | |
683 | /// Package architecture | |
684 | pub arch: String, | |
685 | /// Human readable package description | |
686 | pub description: String, | |
687 | /// New version to be updated to | |
688 | pub version: String, | |
689 | /// Old version currently installed | |
690 | pub old_version: String, | |
691 | /// Package origin | |
692 | pub origin: String, | |
693 | /// Package priority in human-readable form | |
694 | pub priority: String, | |
695 | /// Package section | |
696 | pub section: String, | |
697 | /// URL under which the package's changelog can be retrieved | |
698 | pub change_log_url: String, | |
699 | /// Custom extra field for additional package information | |
700 | #[serde(skip_serializing_if="Option::is_none")] | |
701 | pub extra_info: Option<String>, | |
702 | } |