]> git.proxmox.com Git - proxmox-backup.git/blame - src/api2/types/mod.rs
move drive config to pbs_config workspace
[proxmox-backup.git] / src / api2 / types / mod.rs
CommitLineData
bf78f708
DM
1//! API Type Definitions
2
e7cb4dc5
WB
3use anyhow::bail;
4use serde::{Deserialize, Serialize};
4ebf0eab 5
9ea4bce4
WB
6use proxmox::api::{api, schema::*};
7use proxmox::const_regex;
255f378a 8
b2065dc7 9use crate::config::acl::Role;
f28d9088 10
39c5db7f
DM
11mod acme;
12pub use acme::*;
13
751f6b61 14pub use pbs_api_types::*;
86fb3877 15
255f378a
DM
16// File names: may not contain slashes, may not start with "."
17pub const FILENAME_FORMAT: ApiStringFormat = ApiStringFormat::VerifyFn(|name| {
18 if name.starts_with('.') {
19 bail!("file names may not start with '.'");
20 }
21 if name.contains('/') {
22 bail!("file names may not contain slashes");
23 }
24 Ok(())
25});
26
255f378a 27const_regex!{
255f378a 28 pub SYSTEMD_DATETIME_REGEX = r"^\d{4}-\d{2}-\d{2}( \d{2}:\d{2}(:\d{2})?)?$"; // fixme: define in common_regex ?
d0adf270 29
dbd45a72
FG
30 /// Regex for verification jobs 'DATASTORE:ACTUAL_JOB_ID'
31 pub VERIFICATION_JOB_WORKER_ID_REGEX = concat!(r"^(", PROXMOX_SAFE_ID_REGEX_STR!(), r"):");
32 /// Regex for sync jobs 'REMOTE:REMOTE_DATASTORE:LOCAL_DATASTORE:ACTUAL_JOB_ID'
33 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"):");
34
9765092e 35 pub ACL_PATH_REGEX = concat!(r"^(?:/|", r"(?:/", PROXMOX_SAFE_ID_REGEX_STR!(), ")+", r")$");
9069debc 36
926d2531
TL
37 pub SUBSCRIPTION_KEY_REGEX = concat!(r"^pbs(?:[cbsp])-[0-9a-f]{10}$");
38
7957fabf 39 pub ZPOOL_NAME_REGEX = r"^[a-zA-Z][a-z0-9A-Z\-_.:]+$";
bc228e5e 40
4c4e5c2b 41 pub DATASTORE_MAP_REGEX = concat!(r"(:?", PROXMOX_SAFE_ID_REGEX_STR!(), r"=)?", PROXMOX_SAFE_ID_REGEX_STR!());
5e42d385 42
255f378a 43}
4ebf0eab 44
255f378a
DM
45pub const SYSTEMD_DATETIME_FORMAT: ApiStringFormat =
46 ApiStringFormat::Pattern(&SYSTEMD_DATETIME_REGEX);
4ebf0eab 47
b25f313d
DM
48pub const HOSTNAME_FORMAT: ApiStringFormat =
49 ApiStringFormat::Pattern(&HOSTNAME_REGEX);
50
d724116c
WB
51pub const DNS_ALIAS_FORMAT: ApiStringFormat =
52 ApiStringFormat::Pattern(&DNS_ALIAS_REGEX);
53
ed3e60ae
DM
54pub const ACL_PATH_FORMAT: ApiStringFormat =
55 ApiStringFormat::Pattern(&ACL_PATH_REGEX);
56
68da20bf
DM
57pub const NETWORK_INTERFACE_FORMAT: ApiStringFormat =
58 ApiStringFormat::Pattern(&PROXMOX_SAFE_ID_REGEX);
454c13ed 59
926d2531
TL
60pub const SUBSCRIPTION_KEY_FORMAT: ApiStringFormat =
61 ApiStringFormat::Pattern(&SUBSCRIPTION_KEY_REGEX);
62
9069debc
DM
63pub const BLOCKDEVICE_NAME_FORMAT: ApiStringFormat =
64 ApiStringFormat::Pattern(&BLOCKDEVICE_NAME_REGEX);
76cf5208 65
4c4e5c2b
DC
66pub const DATASTORE_MAP_FORMAT: ApiStringFormat =
67 ApiStringFormat::Pattern(&DATASTORE_MAP_REGEX);
68
685e1334
DM
69pub const PASSWORD_SCHEMA: Schema = StringSchema::new("Password.")
70 .format(&PASSWORD_FORMAT)
71 .min_length(1)
b88f9c5b 72 .max_length(1024)
685e1334
DM
73 .schema();
74
75pub const PBS_PASSWORD_SCHEMA: Schema = StringSchema::new("User Password.")
76 .format(&PASSWORD_FORMAT)
77 .min_length(5)
78 .max_length(64)
79 .schema();
dcb8db66 80
255f378a
DM
81pub const CHUNK_DIGEST_SCHEMA: Schema = StringSchema::new("Chunk digest (SHA256).")
82 .format(&CHUNK_DIGEST_FORMAT)
83 .schema();
84
85pub const NODE_SCHEMA: Schema = StringSchema::new("Node name (or 'localhost')")
86 .format(&ApiStringFormat::VerifyFn(|node| {
87 if node == "localhost" || node == proxmox::tools::nodename() {
88 Ok(())
89 } else {
90 bail!("no such node '{}'", node);
91 }
92 }))
93 .schema();
94
95pub const SEARCH_DOMAIN_SCHEMA: Schema =
96 StringSchema::new("Search domain for host-name lookup.").schema();
97
98pub const FIRST_DNS_SERVER_SCHEMA: Schema =
99 StringSchema::new("First name server IP address.")
100 .format(&IP_FORMAT)
101 .schema();
102
103pub const SECOND_DNS_SERVER_SCHEMA: Schema =
104 StringSchema::new("Second name server IP address.")
105 .format(&IP_FORMAT)
106 .schema();
107
108pub const THIRD_DNS_SERVER_SCHEMA: Schema =
109 StringSchema::new("Third name server IP address.")
110 .format(&IP_FORMAT)
111 .schema();
112
76cf5208
DM
113pub const IP_V4_SCHEMA: Schema =
114 StringSchema::new("IPv4 address.")
115 .format(&IP_V4_FORMAT)
116 .max_length(15)
117 .schema();
118
119pub const IP_V6_SCHEMA: Schema =
120 StringSchema::new("IPv6 address.")
121 .format(&IP_V6_FORMAT)
122 .max_length(39)
123 .schema();
124
125pub const IP_SCHEMA: Schema =
126 StringSchema::new("IP (IPv4 or IPv6) address.")
127 .format(&IP_FORMAT)
128 .max_length(39)
129 .schema();
130
131pub const CIDR_V4_SCHEMA: Schema =
132 StringSchema::new("IPv4 address with netmask (CIDR notation).")
133 .format(&CIDR_V4_FORMAT)
134 .max_length(18)
135 .schema();
136
137pub const CIDR_V6_SCHEMA: Schema =
138 StringSchema::new("IPv6 address with netmask (CIDR notation).")
139 .format(&CIDR_V6_FORMAT)
140 .max_length(43)
141 .schema();
142
143pub const CIDR_SCHEMA: Schema =
144 StringSchema::new("IP address (IPv4 or IPv6) with netmask (CIDR notation).")
145 .format(&CIDR_FORMAT)
146 .max_length(43)
147 .schema();
148
4b40148c
DM
149pub const TIME_ZONE_SCHEMA: Schema = StringSchema::new(
150 "Time zone. The file '/usr/share/zoneinfo/zone.tab' contains the list of valid names.")
151 .format(&SINGLE_LINE_COMMENT_FORMAT)
152 .min_length(2)
153 .max_length(64)
154 .schema();
155
ca257c80
DM
156pub const ACL_PATH_SCHEMA: Schema = StringSchema::new(
157 "Access control path.")
158 .format(&ACL_PATH_FORMAT)
159 .min_length(1)
160 .max_length(128)
161 .schema();
162
163pub const ACL_PROPAGATE_SCHEMA: Schema = BooleanSchema::new(
164 "Allow to propagate (inherit) permissions.")
165 .default(true)
166 .schema();
167
168pub const ACL_UGID_TYPE_SCHEMA: Schema = StringSchema::new(
169 "Type of 'ugid' property.")
ca257c80 170 .format(&ApiStringFormat::Enum(&[
bc0d0388
DM
171 EnumEntry::new("user", "User"),
172 EnumEntry::new("group", "Group")]))
ca257c80
DM
173 .schema();
174
bf78f708
DM
175#[api(
176 properties: {
177 propagate: {
178 schema: ACL_PROPAGATE_SCHEMA,
179 },
180 path: {
181 schema: ACL_PATH_SCHEMA,
182 },
183 ugid_type: {
184 schema: ACL_UGID_TYPE_SCHEMA,
185 },
186 ugid: {
187 type: String,
188 description: "User or Group ID.",
189 },
190 roleid: {
191 type: Role,
192 }
193 }
194)]
195#[derive(Serialize, Deserialize)]
196/// ACL list entry.
197pub struct AclListItem {
198 pub path: String,
199 pub ugid: String,
200 pub ugid_type: String,
201 pub propagate: bool,
202 pub roleid: String,
203}
204
5830c205
DM
205pub const UPID_SCHEMA: Schema = StringSchema::new("Unique Process/Task ID.")
206 .max_length(256)
207 .schema();
66c49c21 208
4c4e5c2b
DC
209pub const DATASTORE_MAP_SCHEMA: Schema = StringSchema::new("Datastore mapping.")
210 .format(&DATASTORE_MAP_FORMAT)
211 .min_length(3)
212 .max_length(65)
93fb2e0d 213 .type_text("(<source>=)?<target>")
4c4e5c2b
DC
214 .schema();
215
216pub const DATASTORE_MAP_ARRAY_SCHEMA: Schema = ArraySchema::new(
217 "Datastore mapping list.", &DATASTORE_MAP_SCHEMA)
218 .schema();
219
220pub const DATASTORE_MAP_LIST_SCHEMA: Schema = StringSchema::new(
e244b9d0
DC
221 "A list of Datastore mappings (or single datastore), comma separated. \
222 For example 'a=b,e' maps the source datastore 'a' to target 'b and \
223 all other sources to the default 'e'. If no default is given, only the \
224 specified sources are mapped.")
4c4e5c2b
DC
225 .format(&ApiStringFormat::PropertyString(&DATASTORE_MAP_ARRAY_SCHEMA))
226 .schema();
227
2888b27f
DC
228pub const SYNC_SCHEDULE_SCHEMA: Schema = StringSchema::new(
229 "Run sync job at specified schedule.")
dd2162f6 230 .format(&ApiStringFormat::VerifyFn(pbs_systemd::time::verify_calendar_event))
8f02db04 231 .type_text("<calendar-event>")
2888b27f
DC
232 .schema();
233
42fdbe51
DM
234pub const GC_SCHEDULE_SCHEMA: Schema = StringSchema::new(
235 "Run garbage collection job at specified schedule.")
dd2162f6 236 .format(&ApiStringFormat::VerifyFn(pbs_systemd::time::verify_calendar_event))
8f02db04 237 .type_text("<calendar-event>")
42fdbe51
DM
238 .schema();
239
67f7ffd0
DM
240pub const PRUNE_SCHEDULE_SCHEMA: Schema = StringSchema::new(
241 "Run prune job at specified schedule.")
dd2162f6 242 .format(&ApiStringFormat::VerifyFn(pbs_systemd::time::verify_calendar_event))
8f02db04 243 .type_text("<calendar-event>")
67f7ffd0
DM
244 .schema();
245
78efafc2 246pub const VERIFICATION_SCHEDULE_SCHEMA: Schema = StringSchema::new(
f37ef25b 247 "Run verify job at specified schedule.")
dd2162f6 248 .format(&ApiStringFormat::VerifyFn(pbs_systemd::time::verify_calendar_event))
8f02db04 249 .type_text("<calendar-event>")
f37ef25b
HL
250 .schema();
251
b4900286
DM
252pub const JOB_ID_SCHEMA: Schema = StringSchema::new("Job ID.")
253 .format(&PROXMOX_SAFE_ID_FORMAT)
254 .min_length(3)
255 .max_length(32)
256 .schema();
257
258pub const REMOVE_VANISHED_BACKUPS_SCHEMA: Schema = BooleanSchema::new(
259 "Delete vanished backups. This remove the local copy if the remote backup was deleted.")
260 .default(true)
261 .schema();
262
9b2bad7a
HL
263pub const IGNORE_VERIFIED_BACKUPS_SCHEMA: Schema = BooleanSchema::new(
264 "Do not verify backups that are already verified if their verification is not outdated.")
265 .default(true)
266 .schema();
267
268pub const VERIFICATION_OUTDATED_AFTER_SCHEMA: Schema = IntegerSchema::new(
269 "Days after that a verification becomes outdated")
270 .minimum(1)
271 .schema();
272
b25f313d
DM
273pub const HOSTNAME_SCHEMA: Schema = StringSchema::new("Hostname (as defined in RFC1123).")
274 .format(&HOSTNAME_FORMAT)
275 .schema();
276
926d2531
TL
277pub const SUBSCRIPTION_KEY_SCHEMA: Schema = StringSchema::new("Proxmox Backup Server subscription key.")
278 .format(&SUBSCRIPTION_KEY_FORMAT)
279 .min_length(15)
280 .max_length(16)
281 .schema();
282
9069debc
DM
283pub const BLOCKDEVICE_NAME_SCHEMA: Schema = StringSchema::new("Block device name (/sys/block/<name>).")
284 .format(&BLOCKDEVICE_NAME_FORMAT)
285 .min_length(3)
286 .max_length(64)
287 .schema();
fc189b19
DM
288
289// Complex type definitions
290
14e08625
DC
291#[api(
292 properties: {
98afc7b1
FG
293 "gc-status": {
294 type: GarbageCollectionStatus,
295 optional: true,
296 },
297 counts: {
298 type: Counts,
299 optional: true,
300 },
14e08625
DC
301 },
302)]
303#[derive(Serialize, Deserialize)]
304#[serde(rename_all="kebab-case")]
305/// Overall Datastore status and useful information.
306pub struct DataStoreStatus {
307 /// Total space (bytes).
308 pub total: u64,
309 /// Used space (bytes).
310 pub used: u64,
311 /// Available space (bytes).
312 pub avail: u64,
313 /// Status of last GC
98afc7b1
FG
314 #[serde(skip_serializing_if="Option::is_none")]
315 pub gc_status: Option<GarbageCollectionStatus>,
14e08625 316 /// Group/Snapshot counts
98afc7b1
FG
317 #[serde(skip_serializing_if="Option::is_none")]
318 pub counts: Option<Counts>,
14e08625
DC
319}
320
5976c392
DC
321#[api()]
322#[derive(Eq, PartialEq, Debug, Serialize, Deserialize)]
323#[serde(rename_all = "lowercase")]
324pub enum TaskStateType {
325 /// Ok
326 OK,
327 /// Warning
328 Warning,
329 /// Error
330 Error,
331 /// Unknown
332 Unknown,
333}
334
ed751dc2
DM
335#[api()]
336#[derive(Debug, Copy, Clone, PartialEq, Serialize, Deserialize)]
337#[serde(rename_all = "lowercase")]
338/// Node Power command type.
339pub enum NodePowerCommand {
340 /// Restart the server
341 Reboot,
342 /// Shutdown the server
343 Shutdown,
344}
345
c357260d
DM
346#[api()]
347#[derive(Debug, Copy, Clone, PartialEq, Serialize, Deserialize)]
348#[serde(rename_all = "lowercase")]
349/// Interface configuration method
350pub enum NetworkConfigMethod {
351 /// Configuration is done manually using other tools
352 Manual,
353 /// Define interfaces with statically allocated addresses.
354 Static,
355 /// Obtain an address via DHCP
356 DHCP,
357 /// Define the loopback interface.
358 Loopback,
359}
360
bab5d18c
DM
361#[api()]
362#[derive(Debug, Copy, Clone, PartialEq, Serialize, Deserialize)]
363#[serde(rename_all = "kebab-case")]
364#[allow(non_camel_case_types)]
365#[repr(u8)]
366/// Linux Bond Mode
367pub enum LinuxBondMode {
368 /// Round-robin policy
369 balance_rr = 0,
370 /// Active-backup policy
371 active_backup = 1,
372 /// XOR policy
373 balance_xor = 2,
374 /// Broadcast policy
375 broadcast = 3,
376 /// IEEE 802.3ad Dynamic link aggregation
8f2f3dd7 377 #[serde(rename = "802.3ad")]
bab5d18c
DM
378 ieee802_3ad = 4,
379 /// Adaptive transmit load balancing
380 balance_tlb = 5,
381 /// Adaptive load balancing
382 balance_alb = 6,
383}
384
8f2f3dd7
DC
385#[api()]
386#[derive(Debug, Copy, Clone, PartialEq, Serialize, Deserialize)]
387#[serde(rename_all = "kebab-case")]
388#[allow(non_camel_case_types)]
389#[repr(u8)]
390/// Bond Transmit Hash Policy for LACP (802.3ad)
391pub enum BondXmitHashPolicy {
392 /// Layer 2
393 layer2 = 0,
394 /// Layer 2+3
395 #[serde(rename = "layer2+3")]
396 layer2_3 = 1,
397 /// Layer 3+4
398 #[serde(rename = "layer3+4")]
399 layer3_4 = 2,
400}
401
02269f3d
DM
402#[api()]
403#[derive(Debug, Copy, Clone, PartialEq, Serialize, Deserialize)]
404#[serde(rename_all = "lowercase")]
405/// Network interface type
406pub enum NetworkInterfaceType {
407 /// Loopback
408 Loopback,
409 /// Physical Ethernet device
7b22acd0 410 Eth,
02269f3d
DM
411 /// Linux Bridge
412 Bridge,
413 /// Linux Bond
414 Bond,
415 /// Linux VLAN (eth.10)
416 Vlan,
417 /// Interface Alias (eth:1)
418 Alias,
419 /// Unknown interface type
420 Unknown,
421}
422
68da20bf
DM
423pub const NETWORK_INTERFACE_NAME_SCHEMA: Schema = StringSchema::new("Network interface name.")
424 .format(&NETWORK_INTERFACE_FORMAT)
425 .min_length(1)
426 .max_length(libc::IFNAMSIZ-1)
427 .schema();
428
3aedb738 429pub const NETWORK_INTERFACE_ARRAY_SCHEMA: Schema = ArraySchema::new(
1d9a68c2
DM
430 "Network interface list.", &NETWORK_INTERFACE_NAME_SCHEMA)
431 .schema();
432
3aedb738
DM
433pub const NETWORK_INTERFACE_LIST_SCHEMA: Schema = StringSchema::new(
434 "A list of network devices, comma separated.")
435 .format(&ApiStringFormat::PropertyString(&NETWORK_INTERFACE_ARRAY_SCHEMA))
436 .schema();
437
c357260d
DM
438#[api(
439 properties: {
440 name: {
68da20bf 441 schema: NETWORK_INTERFACE_NAME_SCHEMA,
c357260d 442 },
7b22acd0 443 "type": {
02269f3d
DM
444 type: NetworkInterfaceType,
445 },
7b22acd0 446 method: {
c357260d
DM
447 type: NetworkConfigMethod,
448 optional: true,
449 },
7b22acd0 450 method6: {
c357260d
DM
451 type: NetworkConfigMethod,
452 optional: true,
453 },
7b22acd0
DM
454 cidr: {
455 schema: CIDR_V4_SCHEMA,
456 optional: true,
457 },
458 cidr6: {
459 schema: CIDR_V6_SCHEMA,
460 optional: true,
461 },
462 gateway: {
463 schema: IP_V4_SCHEMA,
464 optional: true,
465 },
466 gateway6: {
467 schema: IP_V6_SCHEMA,
468 optional: true,
469 },
470 options: {
c357260d
DM
471 description: "Option list (inet)",
472 type: Array,
473 items: {
68da20bf 474 description: "Optional attribute line.",
c357260d
DM
475 type: String,
476 },
477 },
7b22acd0 478 options6: {
c357260d
DM
479 description: "Option list (inet6)",
480 type: Array,
481 items: {
68da20bf 482 description: "Optional attribute line.",
c357260d
DM
483 type: String,
484 },
485 },
7b22acd0 486 comments: {
8a6b86b8
DM
487 description: "Comments (inet, may span multiple lines)",
488 type: String,
489 optional: true,
5f60a58f 490 },
7b22acd0 491 comments6: {
8a6b86b8
DM
492 description: "Comments (inet6, may span multiple lines)",
493 type: String,
494 optional: true,
5f60a58f 495 },
1d9a68c2 496 bridge_ports: {
3aedb738 497 schema: NETWORK_INTERFACE_ARRAY_SCHEMA,
1d9a68c2
DM
498 optional: true,
499 },
bab5d18c 500 slaves: {
3aedb738 501 schema: NETWORK_INTERFACE_ARRAY_SCHEMA,
42fbe91a
DM
502 optional: true,
503 },
bab5d18c
DM
504 bond_mode: {
505 type: LinuxBondMode,
506 optional: true,
85959a99
DC
507 },
508 "bond-primary": {
509 schema: NETWORK_INTERFACE_NAME_SCHEMA,
510 optional: true,
511 },
8f2f3dd7
DC
512 bond_xmit_hash_policy: {
513 type: BondXmitHashPolicy,
514 optional: true,
515 },
c357260d
DM
516 }
517)]
518#[derive(Debug, Serialize, Deserialize)]
519/// Network Interface configuration
520pub struct Interface {
521 /// Autostart interface
7b22acd0
DM
522 #[serde(rename = "autostart")]
523 pub autostart: bool,
c357260d
DM
524 /// Interface is active (UP)
525 pub active: bool,
526 /// Interface name
527 pub name: String,
02269f3d 528 /// Interface type
7b22acd0 529 #[serde(rename = "type")]
02269f3d 530 pub interface_type: NetworkInterfaceType,
c357260d 531 #[serde(skip_serializing_if="Option::is_none")]
7b22acd0 532 pub method: Option<NetworkConfigMethod>,
c357260d 533 #[serde(skip_serializing_if="Option::is_none")]
7b22acd0 534 pub method6: Option<NetworkConfigMethod>,
c357260d 535 #[serde(skip_serializing_if="Option::is_none")]
8b57cd44 536 /// IPv4 address with netmask
7b22acd0 537 pub cidr: Option<String>,
c357260d
DM
538 #[serde(skip_serializing_if="Option::is_none")]
539 /// IPv4 gateway
7b22acd0 540 pub gateway: Option<String>,
c357260d 541 #[serde(skip_serializing_if="Option::is_none")]
8b57cd44 542 /// IPv6 address with netmask
7b22acd0 543 pub cidr6: Option<String>,
c357260d
DM
544 #[serde(skip_serializing_if="Option::is_none")]
545 /// IPv6 gateway
7b22acd0 546 pub gateway6: Option<String>,
3fce3bc3 547
c357260d 548 #[serde(skip_serializing_if="Vec::is_empty")]
7b22acd0 549 pub options: Vec<String>,
c357260d 550 #[serde(skip_serializing_if="Vec::is_empty")]
7b22acd0 551 pub options6: Vec<String>,
2c18efd9 552
8a6b86b8 553 #[serde(skip_serializing_if="Option::is_none")]
7b22acd0 554 pub comments: Option<String>,
8a6b86b8 555 #[serde(skip_serializing_if="Option::is_none")]
7b22acd0 556 pub comments6: Option<String>,
5f60a58f 557
2c18efd9
DM
558 #[serde(skip_serializing_if="Option::is_none")]
559 /// Maximum Transmission Unit
560 pub mtu: Option<u64>,
1d9a68c2
DM
561
562 #[serde(skip_serializing_if="Option::is_none")]
563 pub bridge_ports: Option<Vec<String>>,
7b22acd0
DM
564 /// Enable bridge vlan support.
565 #[serde(skip_serializing_if="Option::is_none")]
566 pub bridge_vlan_aware: Option<bool>,
42fbe91a
DM
567
568 #[serde(skip_serializing_if="Option::is_none")]
bab5d18c
DM
569 pub slaves: Option<Vec<String>>,
570 #[serde(skip_serializing_if="Option::is_none")]
571 pub bond_mode: Option<LinuxBondMode>,
85959a99
DC
572 #[serde(skip_serializing_if="Option::is_none")]
573 #[serde(rename = "bond-primary")]
574 pub bond_primary: Option<String>,
8f2f3dd7 575 pub bond_xmit_hash_policy: Option<BondXmitHashPolicy>,
c357260d
DM
576}
577
ff620a3d
DM
578// Regression tests
579
dcb8db66 580#[test]
ff329f97 581fn test_cert_fingerprint_schema() -> Result<(), anyhow::Error> {
dcb8db66
DM
582
583 let schema = CERT_FINGERPRINT_SHA256_SCHEMA;
584
585 let invalid_fingerprints = [
586 "86:88:7c:be:26:77:a5:62:67:d9:06:f5:e4::61:3e:20:dc:cd:43:92:07:7f:fb:65:54:6c:ff:d2:96:36:f8",
587 "88:7C:BE:26:77:a5:62:67:D9:06:f5:e4:14:61:3e:20:dc:cd:43:92:07:7f:fb:65:54:6c:ff:d2:96:36:f8",
588 "86:88:7c:be:26:77:a5:62:67:d9:06:f5:e4::14:61:3e:20:dc:cd:43:92:07:7f:fb:65:54:6c:ff:d2:96:36:f8:ff",
589 "XX:88:7c:be:26:77:a5:62:67:d9:06:f5:e4::14:61:3e:20:dc:cd:43:92:07:7f:fb:65:54:6c:ff:d2:96:36:f8",
590 "86:88:Y4:be:26:77:a5:62:67:d9:06:f5:e4:14:61:3e:20:dc:cd:43:92:07:7f:fb:65:54:6c:ff:d2:96:36:f8",
591 "86:88:0:be:26:77:a5:62:67:d9:06:f5:e4:14:61:3e:20:dc:cd:43:92:07:7f:fb:65:54:6c:ff:d2:96:36:f8",
592 ];
593
594 for fingerprint in invalid_fingerprints.iter() {
3984a5fd 595 if parse_simple_value(fingerprint, &schema).is_ok() {
add5861e 596 bail!("test fingerprint '{}' failed - got Ok() while exception an error.", fingerprint);
dcb8db66
DM
597 }
598 }
599
600 let valid_fingerprints = [
601 "86:88:7c:be:26:77:a5:62:67:d9:06:f5:e4:14:61:3e:20:dc:cd:43:92:07:7f:fb:65:54:6c:ff:d2:96:36:f8",
602 "86:88:7C:BE:26:77:a5:62:67:D9:06:f5:e4:14:61:3e:20:dc:cd:43:92:07:7f:fb:65:54:6c:ff:d2:96:36:f8",
603 ];
604
605 for fingerprint in valid_fingerprints.iter() {
606 let v = match parse_simple_value(fingerprint, &schema) {
607 Ok(v) => v,
608 Err(err) => {
609 bail!("unable to parse fingerprint '{}' - {}", fingerprint, err);
610 }
611 };
612
613 if v != serde_json::json!(fingerprint) {
614 bail!("unable to parse fingerprint '{}' - got wrong value {:?}", fingerprint, v);
615 }
616 }
617
618 Ok(())
619}
620
ff620a3d 621#[test]
ff329f97 622fn test_proxmox_user_id_schema() -> Result<(), anyhow::Error> {
ff620a3d
DM
623 let invalid_user_ids = [
624 "x", // too short
625 "xx", // too short
626 "xxx", // no realm
627 "xxx@", // no realm
628 "xx x@test", // contains space
629 "xx\nx@test", // contains control character
630 "x:xx@test", // contains collon
631 "xx/x@test", // contains slash
632 "xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx@test", // too long
633 ];
634
635 for name in invalid_user_ids.iter() {
3984a5fd 636 if parse_simple_value(name, &Userid::API_SCHEMA).is_ok() {
add5861e 637 bail!("test userid '{}' failed - got Ok() while exception an error.", name);
ff620a3d
DM
638 }
639 }
640
641 let valid_user_ids = [
642 "xxx@y",
643 "name@y",
644 "xxx@test-it.com",
645 "xxx@_T_E_S_T-it.com",
646 "x_x-x.x@test-it.com",
647 ];
648
649 for name in valid_user_ids.iter() {
e7cb4dc5 650 let v = match parse_simple_value(name, &Userid::API_SCHEMA) {
ff620a3d
DM
651 Ok(v) => v,
652 Err(err) => {
653 bail!("unable to parse userid '{}' - {}", name, err);
654 }
655 };
656
657 if v != serde_json::json!(name) {
658 bail!("unable to parse userid '{}' - got wrong value {:?}", name, v);
659 }
660 }
661
662 Ok(())
663}
a2f862ee
DM
664
665#[api()]
666#[derive(Copy, Clone, Serialize, Deserialize)]
667#[serde(rename_all = "UPPERCASE")]
668pub enum RRDMode {
669 /// Maximum
670 Max,
671 /// Average
672 Average,
673}
674
675
676#[api()]
677#[repr(u64)]
678#[derive(Copy, Clone, Serialize, Deserialize)]
679#[serde(rename_all = "lowercase")]
680pub enum RRDTimeFrameResolution {
681 /// 1 min => last 70 minutes
682 Hour = 60,
683 /// 30 min => last 35 hours
684 Day = 60*30,
685 /// 3 hours => about 8 days
686 Week = 60*180,
687 /// 12 hours => last 35 days
688 Month = 60*720,
689 /// 1 week => last 490 days
690 Year = 60*10080,
691}
a4e86972
SR
692
693#[api()]
ed2beb33 694#[derive(Debug, Clone, Serialize, Deserialize)]
a4e86972
SR
695#[serde(rename_all = "PascalCase")]
696/// Describes a package for which an update is available.
697pub struct APTUpdateInfo {
698 /// Package name
699 pub package: String,
700 /// Package title
701 pub title: String,
702 /// Package architecture
703 pub arch: String,
704 /// Human readable package description
705 pub description: String,
706 /// New version to be updated to
707 pub version: String,
708 /// Old version currently installed
709 pub old_version: String,
710 /// Package origin
711 pub origin: String,
712 /// Package priority in human-readable form
713 pub priority: String,
714 /// Package section
715 pub section: String,
716 /// URL under which the package's changelog can be retrieved
717 pub change_log_url: String,
6f0073bb
TL
718 /// Custom extra field for additional package information
719 #[serde(skip_serializing_if="Option::is_none")]
2decf85d 720 pub extra_info: Option<String>,
a4e86972 721}
6e545d00
DM
722
723#[api()]
724#[derive(Debug, Copy, Clone, PartialEq, Serialize, Deserialize)]
725#[serde(rename_all = "lowercase")]
726/// When do we send notifications
727pub enum Notify {
728 /// Never send notification
729 Never,
d1d74c43 730 /// Send notifications for failed and successful jobs
6e545d00
DM
731 Always,
732 /// Send notifications for failed jobs only
733 Error,
734}
c26c9390
DM
735
736#[api(
737 properties: {
738 gc: {
739 type: Notify,
740 optional: true,
741 },
742 verify: {
743 type: Notify,
744 optional: true,
745 },
746 sync: {
747 type: Notify,
748 optional: true,
749 },
750 },
751)]
752#[derive(Debug, Serialize, Deserialize)]
753/// Datastore notify settings
754pub struct DatastoreNotify {
755 /// Garbage collection settings
756 pub gc: Option<Notify>,
757 /// Verify job setting
758 pub verify: Option<Notify>,
759 /// Sync job setting
760 pub sync: Option<Notify>,
761}
762
763pub const DATASTORE_NOTIFY_STRING_SCHEMA: Schema = StringSchema::new(
764 "Datastore notification setting")
765 .format(&ApiStringFormat::PropertyString(&DatastoreNotify::API_SCHEMA))
766 .schema();
82a103c8
DM
767
768
1689296d
DM
769#[api(
770 properties: {
771 "next-run": {
772 description: "Estimated time of the next run (UNIX epoch).",
773 optional: true,
774 type: Integer,
775 },
776 "last-run-state": {
777 description: "Result of the last run.",
778 optional: true,
779 type: String,
780 },
781 "last-run-upid": {
782 description: "Task UPID of the last run.",
783 optional: true,
784 type: String,
785 },
786 "last-run-endtime": {
787 description: "Endtime of the last run.",
788 optional: true,
789 type: Integer,
790 },
791 }
792)]
1689296d 793#[derive(Serialize,Deserialize,Default)]
3e3b505c 794#[serde(rename_all="kebab-case")]
1689296d
DM
795/// Job Scheduling Status
796pub struct JobScheduleStatus {
797 #[serde(skip_serializing_if="Option::is_none")]
798 pub next_run: Option<i64>,
799 #[serde(skip_serializing_if="Option::is_none")]
800 pub last_run_state: Option<String>,
801 #[serde(skip_serializing_if="Option::is_none")]
802 pub last_run_upid: Option<String>,
803 #[serde(skip_serializing_if="Option::is_none")]
804 pub last_run_endtime: Option<i64>,
805}
75054859
DC
806
807#[api]
808#[derive(Serialize, Deserialize, Default)]
809#[serde(rename_all = "kebab-case")]
810/// Node memory usage counters
811pub struct NodeMemoryCounters {
812 /// Total memory
813 pub total: u64,
814 /// Used memory
815 pub used: u64,
816 /// Free memory
817 pub free: u64,
818}
819
398636b6
DC
820#[api]
821#[derive(Serialize, Deserialize, Default)]
822#[serde(rename_all = "kebab-case")]
823/// Node swap usage counters
824pub struct NodeSwapCounters {
825 /// Total swap
826 pub total: u64,
827 /// Used swap
828 pub used: u64,
829 /// Free swap
830 pub free: u64,
831}
832
75054859
DC
833#[api]
834#[derive(Serialize,Deserialize,Default)]
835#[serde(rename_all = "kebab-case")]
836/// Contains general node information such as the fingerprint`
837pub struct NodeInformation {
838 /// The SSL Fingerprint
839 pub fingerprint: String,
840}
841
398636b6
DC
842#[api]
843#[derive(Serialize, Deserialize, Default)]
844#[serde(rename_all = "kebab-case")]
845/// Information about the CPU
846pub struct NodeCpuInformation {
847 /// The CPU model
848 pub model: String,
849 /// The number of CPU sockets
850 pub sockets: usize,
851 /// The number of CPU cores (incl. threads)
852 pub cpus: usize,
853}
854
75054859
DC
855#[api(
856 properties: {
857 memory: {
858 type: NodeMemoryCounters,
859 },
860 root: {
861 type: StorageStatus,
862 },
398636b6
DC
863 swap: {
864 type: NodeSwapCounters,
865 },
866 loadavg: {
867 type: Array,
868 items: {
869 type: Number,
870 description: "the load",
871 }
872 },
873 cpuinfo: {
874 type: NodeCpuInformation,
875 },
75054859
DC
876 info: {
877 type: NodeInformation,
878 }
879 },
880)]
881#[derive(Serialize, Deserialize, Default)]
882#[serde(rename_all = "kebab-case")]
883/// The Node status
884pub struct NodeStatus {
885 pub memory: NodeMemoryCounters,
886 pub root: StorageStatus,
398636b6
DC
887 pub swap: NodeSwapCounters,
888 /// The current uptime of the server.
889 pub uptime: u64,
890 /// Load for 1, 5 and 15 minutes.
891 pub loadavg: [f64; 3],
892 /// The current kernel version.
893 pub kversion: String,
75054859
DC
894 /// Total CPU usage since last query.
895 pub cpu: f64,
398636b6
DC
896 /// Total IO wait since last query.
897 pub wait: f64,
898 pub cpuinfo: NodeCpuInformation,
75054859
DC
899 pub info: NodeInformation,
900}
467bd01c
DM
901
902pub const HTTP_PROXY_SCHEMA: Schema = StringSchema::new(
903 "HTTP proxy configuration [http://]<host>[:port]")
904 .format(&ApiStringFormat::VerifyFn(|s| {
1d781c5b 905 proxmox_http::ProxyConfig::parse_proxy_url(s)?;
467bd01c
DM
906 Ok(())
907 }))
908 .min_length(1)
909 .max_length(128)
910 .type_text("[http://]<host>[:port]")
911 .schema();