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