1 //! API Type Definitions
4 use serde
::{Deserialize, Serialize}
;
6 use proxmox
::api
::{api, schema::*}
;
7 use proxmox
::const_regex
;
9 use pbs_datastore
::catalog
::CatalogEntryType
;
12 backup
::DirEntryAttribute
,
20 pub use file_restore
::*;
25 pub use pbs_api_types
::*;
27 // File names: may not contain slashes, may not start with "."
28 pub const FILENAME_FORMAT
: ApiStringFormat
= ApiStringFormat
::VerifyFn(|name
| {
29 if name
.starts_with('
.'
) {
30 bail
!("file names may not start with '.'");
32 if name
.contains('
/'
) {
33 bail
!("file names may not contain slashes");
39 pub SYSTEMD_DATETIME_REGEX
= r
"^\d{4}-\d{2}-\d{2}( \d{2}:\d{2}(:\d{2})?)?$"; // fixme: define in common_regex ?
41 /// Regex for verification jobs 'DATASTORE:ACTUAL_JOB_ID'
42 pub VERIFICATION_JOB_WORKER_ID_REGEX
= concat
!(r
"^(", PROXMOX_SAFE_ID_REGEX_STR
!(), r
"):");
43 /// Regex for sync jobs 'REMOTE:REMOTE_DATASTORE:LOCAL_DATASTORE:ACTUAL_JOB_ID'
44 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
"):");
46 pub ACL_PATH_REGEX
= concat
!(r
"^(?:/|", r
"(?:/", PROXMOX_SAFE_ID_REGEX_STR
!(), ")+", r
")$");
48 pub SUBSCRIPTION_KEY_REGEX
= concat
!(r
"^pbs(?:[cbsp])-[0-9a-f]{10}$");
50 pub BLOCKDEVICE_NAME_REGEX
= r
"^(:?(:?h|s|x?v)d[a-z]+)|(:?nvme\d+n\d+)$";
52 pub ZPOOL_NAME_REGEX
= r
"^[a-zA-Z][a-z0-9A-Z\-_.:]+$";
54 pub DATASTORE_MAP_REGEX
= concat
!(r
"(:?", PROXMOX_SAFE_ID_REGEX_STR
!(), r
"=)?", PROXMOX_SAFE_ID_REGEX_STR
!());
56 pub TAPE_RESTORE_SNAPSHOT_REGEX
= concat
!(r
"^", PROXMOX_SAFE_ID_REGEX_STR
!(), r
":", SNAPSHOT_PATH_REGEX_STR
!(), r
"$");
59 pub const SYSTEMD_DATETIME_FORMAT
: ApiStringFormat
=
60 ApiStringFormat
::Pattern(&SYSTEMD_DATETIME_REGEX
);
62 pub const HOSTNAME_FORMAT
: ApiStringFormat
=
63 ApiStringFormat
::Pattern(&HOSTNAME_REGEX
);
65 pub const DNS_NAME_FORMAT
: ApiStringFormat
=
66 ApiStringFormat
::Pattern(&DNS_NAME_REGEX
);
68 pub const DNS_ALIAS_FORMAT
: ApiStringFormat
=
69 ApiStringFormat
::Pattern(&DNS_ALIAS_REGEX
);
71 pub const DNS_NAME_OR_IP_FORMAT
: ApiStringFormat
=
72 ApiStringFormat
::Pattern(&DNS_NAME_OR_IP_REGEX
);
74 pub const ACL_PATH_FORMAT
: ApiStringFormat
=
75 ApiStringFormat
::Pattern(&ACL_PATH_REGEX
);
77 pub const NETWORK_INTERFACE_FORMAT
: ApiStringFormat
=
78 ApiStringFormat
::Pattern(&PROXMOX_SAFE_ID_REGEX
);
80 pub const SUBSCRIPTION_KEY_FORMAT
: ApiStringFormat
=
81 ApiStringFormat
::Pattern(&SUBSCRIPTION_KEY_REGEX
);
83 pub const BLOCKDEVICE_NAME_FORMAT
: ApiStringFormat
=
84 ApiStringFormat
::Pattern(&BLOCKDEVICE_NAME_REGEX
);
86 pub const DATASTORE_MAP_FORMAT
: ApiStringFormat
=
87 ApiStringFormat
::Pattern(&DATASTORE_MAP_REGEX
);
89 pub const TAPE_RESTORE_SNAPSHOT_FORMAT
: ApiStringFormat
=
90 ApiStringFormat
::Pattern(&TAPE_RESTORE_SNAPSHOT_REGEX
);
92 pub const PASSWORD_SCHEMA
: Schema
= StringSchema
::new("Password.")
93 .format(&PASSWORD_FORMAT
)
98 pub const PBS_PASSWORD_SCHEMA
: Schema
= StringSchema
::new("User Password.")
99 .format(&PASSWORD_FORMAT
)
104 pub const TAPE_ENCRYPTION_KEY_FINGERPRINT_SCHEMA
: Schema
= StringSchema
::new(
105 "Tape encryption key fingerprint (sha256)."
107 .format(&FINGERPRINT_SHA256_FORMAT
)
110 pub const CHUNK_DIGEST_SCHEMA
: Schema
= StringSchema
::new("Chunk digest (SHA256).")
111 .format(&CHUNK_DIGEST_FORMAT
)
114 pub const NODE_SCHEMA
: Schema
= StringSchema
::new("Node name (or 'localhost')")
115 .format(&ApiStringFormat
::VerifyFn(|node
| {
116 if node
== "localhost" || node
== proxmox
::tools
::nodename() {
119 bail
!("no such node '{}'", node
);
124 pub const SEARCH_DOMAIN_SCHEMA
: Schema
=
125 StringSchema
::new("Search domain for host-name lookup.").schema();
127 pub const FIRST_DNS_SERVER_SCHEMA
: Schema
=
128 StringSchema
::new("First name server IP address.")
132 pub const SECOND_DNS_SERVER_SCHEMA
: Schema
=
133 StringSchema
::new("Second name server IP address.")
137 pub const THIRD_DNS_SERVER_SCHEMA
: Schema
=
138 StringSchema
::new("Third name server IP address.")
142 pub const IP_V4_SCHEMA
: Schema
=
143 StringSchema
::new("IPv4 address.")
144 .format(&IP_V4_FORMAT
)
148 pub const IP_V6_SCHEMA
: Schema
=
149 StringSchema
::new("IPv6 address.")
150 .format(&IP_V6_FORMAT
)
154 pub const IP_SCHEMA
: Schema
=
155 StringSchema
::new("IP (IPv4 or IPv6) address.")
160 pub const CIDR_V4_SCHEMA
: Schema
=
161 StringSchema
::new("IPv4 address with netmask (CIDR notation).")
162 .format(&CIDR_V4_FORMAT
)
166 pub const CIDR_V6_SCHEMA
: Schema
=
167 StringSchema
::new("IPv6 address with netmask (CIDR notation).")
168 .format(&CIDR_V6_FORMAT
)
172 pub const CIDR_SCHEMA
: Schema
=
173 StringSchema
::new("IP address (IPv4 or IPv6) with netmask (CIDR notation).")
174 .format(&CIDR_FORMAT
)
178 pub const TIME_ZONE_SCHEMA
: Schema
= StringSchema
::new(
179 "Time zone. The file '/usr/share/zoneinfo/zone.tab' contains the list of valid names.")
180 .format(&SINGLE_LINE_COMMENT_FORMAT
)
185 pub const ACL_PATH_SCHEMA
: Schema
= StringSchema
::new(
186 "Access control path.")
187 .format(&ACL_PATH_FORMAT
)
192 pub const ACL_PROPAGATE_SCHEMA
: Schema
= BooleanSchema
::new(
193 "Allow to propagate (inherit) permissions.")
197 pub const ACL_UGID_TYPE_SCHEMA
: Schema
= StringSchema
::new(
198 "Type of 'ugid' property.")
199 .format(&ApiStringFormat
::Enum(&[
200 EnumEntry
::new("user", "User"),
201 EnumEntry
::new("group", "Group")]))
207 schema
: ACL_PROPAGATE_SCHEMA
,
210 schema
: ACL_PATH_SCHEMA
,
213 schema
: ACL_UGID_TYPE_SCHEMA
,
217 description
: "User or Group ID.",
224 #[derive(Serialize, Deserialize)]
226 pub struct AclListItem
{
229 pub ugid_type
: String
,
234 pub const UPID_SCHEMA
: Schema
= StringSchema
::new("Unique Process/Task ID.")
238 pub const DATASTORE_MAP_SCHEMA
: Schema
= StringSchema
::new("Datastore mapping.")
239 .format(&DATASTORE_MAP_FORMAT
)
242 .type_text("(<source>=)?<target>")
245 pub const DATASTORE_MAP_ARRAY_SCHEMA
: Schema
= ArraySchema
::new(
246 "Datastore mapping list.", &DATASTORE_MAP_SCHEMA
)
249 pub const DATASTORE_MAP_LIST_SCHEMA
: Schema
= StringSchema
::new(
250 "A list of Datastore mappings (or single datastore), comma separated. \
251 For example 'a=b,e' maps the source datastore 'a' to target 'b and \
252 all other sources to the default 'e'. If no default is given, only the \
253 specified sources are mapped.")
254 .format(&ApiStringFormat
::PropertyString(&DATASTORE_MAP_ARRAY_SCHEMA
))
257 pub const TAPE_RESTORE_SNAPSHOT_SCHEMA
: Schema
= StringSchema
::new(
258 "A snapshot in the format: 'store:type/id/time")
259 .format(&TAPE_RESTORE_SNAPSHOT_FORMAT
)
260 .type_text("store:type/id/time")
263 pub const MEDIA_SET_UUID_SCHEMA
: Schema
=
264 StringSchema
::new("MediaSet Uuid (We use the all-zero Uuid to reseve an empty media for a specific pool).")
265 .format(&UUID_FORMAT
)
268 pub const MEDIA_UUID_SCHEMA
: Schema
=
269 StringSchema
::new("Media Uuid.")
270 .format(&UUID_FORMAT
)
273 pub const SYNC_SCHEDULE_SCHEMA
: Schema
= StringSchema
::new(
274 "Run sync job at specified schedule.")
275 .format(&ApiStringFormat
::VerifyFn(crate::tools
::systemd
::time
::verify_calendar_event
))
276 .type_text("<calendar-event>")
279 pub const GC_SCHEDULE_SCHEMA
: Schema
= StringSchema
::new(
280 "Run garbage collection job at specified schedule.")
281 .format(&ApiStringFormat
::VerifyFn(crate::tools
::systemd
::time
::verify_calendar_event
))
282 .type_text("<calendar-event>")
285 pub const PRUNE_SCHEDULE_SCHEMA
: Schema
= StringSchema
::new(
286 "Run prune job at specified schedule.")
287 .format(&ApiStringFormat
::VerifyFn(crate::tools
::systemd
::time
::verify_calendar_event
))
288 .type_text("<calendar-event>")
291 pub const VERIFICATION_SCHEDULE_SCHEMA
: Schema
= StringSchema
::new(
292 "Run verify job at specified schedule.")
293 .format(&ApiStringFormat
::VerifyFn(crate::tools
::systemd
::time
::verify_calendar_event
))
294 .type_text("<calendar-event>")
297 pub const REMOTE_ID_SCHEMA
: Schema
= StringSchema
::new("Remote ID.")
298 .format(&PROXMOX_SAFE_ID_FORMAT
)
303 pub const JOB_ID_SCHEMA
: Schema
= StringSchema
::new("Job ID.")
304 .format(&PROXMOX_SAFE_ID_FORMAT
)
309 pub const REMOVE_VANISHED_BACKUPS_SCHEMA
: Schema
= BooleanSchema
::new(
310 "Delete vanished backups. This remove the local copy if the remote backup was deleted.")
314 pub const IGNORE_VERIFIED_BACKUPS_SCHEMA
: Schema
= BooleanSchema
::new(
315 "Do not verify backups that are already verified if their verification is not outdated.")
319 pub const VERIFICATION_OUTDATED_AFTER_SCHEMA
: Schema
= IntegerSchema
::new(
320 "Days after that a verification becomes outdated")
324 pub const HOSTNAME_SCHEMA
: Schema
= StringSchema
::new("Hostname (as defined in RFC1123).")
325 .format(&HOSTNAME_FORMAT
)
328 pub const DNS_NAME_OR_IP_SCHEMA
: Schema
= StringSchema
::new("DNS name or IP address.")
329 .format(&DNS_NAME_OR_IP_FORMAT
)
332 pub const SUBSCRIPTION_KEY_SCHEMA
: Schema
= StringSchema
::new("Proxmox Backup Server subscription key.")
333 .format(&SUBSCRIPTION_KEY_FORMAT
)
338 pub const BLOCKDEVICE_NAME_SCHEMA
: Schema
= StringSchema
::new("Block device name (/sys/block/<name>).")
339 .format(&BLOCKDEVICE_NAME_FORMAT
)
344 pub const REALM_ID_SCHEMA
: Schema
= StringSchema
::new("Realm name.")
345 .format(&PROXMOX_SAFE_ID_FORMAT
)
350 // Complex type definitions
355 type: GarbageCollectionStatus
,
364 #[derive(Serialize, Deserialize)]
365 #[serde(rename_all="kebab-case")]
366 /// Overall Datastore status and useful information.
367 pub struct DataStoreStatus
{
368 /// Total space (bytes).
370 /// Used space (bytes).
372 /// Available space (bytes).
374 /// Status of last GC
375 #[serde(skip_serializing_if="Option::is_none")]
376 pub gc_status
: Option
<GarbageCollectionStatus
>,
377 /// Group/Snapshot counts
378 #[serde(skip_serializing_if="Option::is_none")]
379 pub counts
: Option
<Counts
>,
384 upid
: { schema: UPID_SCHEMA }
,
385 user
: { type: Authid }
,
388 #[derive(Serialize, Deserialize)]
390 pub struct TaskListItem
{
392 /// The node name where the task is running on.
396 /// The task start time (Epoch)
398 /// The task start time (Epoch)
400 /// Worker type (arbitrary ASCII string)
401 pub worker_type
: String
,
402 /// Worker ID (arbitrary ASCII string)
403 pub worker_id
: Option
<String
>,
404 /// The authenticated entity who started the task
406 /// The task end time (Epoch)
407 #[serde(skip_serializing_if="Option::is_none")]
408 pub endtime
: Option
<i64>,
410 #[serde(skip_serializing_if="Option::is_none")]
411 pub status
: Option
<String
>,
414 impl From
<crate::server
::TaskListInfo
> for TaskListItem
{
415 fn from(info
: crate::server
::TaskListInfo
) -> Self {
416 let (endtime
, status
) = info
418 .map_or_else(|| (None
, None
), |a
| (Some(a
.endtime()), Some(a
.to_string())));
422 node
: "localhost".to_string(),
423 pid
: info
.upid
.pid
as i64,
424 pstart
: info
.upid
.pstart
,
425 starttime
: info
.upid
.starttime
,
426 worker_type
: info
.upid
.worker_type
,
427 worker_id
: info
.upid
.worker_id
,
428 user
: info
.upid
.auth_id
,
436 #[derive(Eq, PartialEq, Debug, Serialize, Deserialize)]
437 #[serde(rename_all = "lowercase")]
438 pub enum TaskStateType
{
450 #[derive(Debug, Copy, Clone, PartialEq, Serialize, Deserialize)]
451 #[serde(rename_all = "lowercase")]
452 /// Node Power command type.
453 pub enum NodePowerCommand
{
454 /// Restart the server
456 /// Shutdown the server
461 #[derive(Debug, Copy, Clone, PartialEq, Serialize, Deserialize)]
462 #[serde(rename_all = "lowercase")]
463 /// Interface configuration method
464 pub enum NetworkConfigMethod
{
465 /// Configuration is done manually using other tools
467 /// Define interfaces with statically allocated addresses.
469 /// Obtain an address via DHCP
471 /// Define the loopback interface.
476 #[derive(Debug, Copy, Clone, PartialEq, Serialize, Deserialize)]
477 #[serde(rename_all = "kebab-case")]
478 #[allow(non_camel_case_types)]
481 pub enum LinuxBondMode
{
482 /// Round-robin policy
484 /// Active-backup policy
490 /// IEEE 802.3ad Dynamic link aggregation
491 #[serde(rename = "802.3ad")]
493 /// Adaptive transmit load balancing
495 /// Adaptive load balancing
500 #[derive(Debug, Copy, Clone, PartialEq, Serialize, Deserialize)]
501 #[serde(rename_all = "kebab-case")]
502 #[allow(non_camel_case_types)]
504 /// Bond Transmit Hash Policy for LACP (802.3ad)
505 pub enum BondXmitHashPolicy
{
509 #[serde(rename = "layer2+3")]
512 #[serde(rename = "layer3+4")]
517 #[derive(Debug, Copy, Clone, PartialEq, Serialize, Deserialize)]
518 #[serde(rename_all = "lowercase")]
519 /// Network interface type
520 pub enum NetworkInterfaceType
{
523 /// Physical Ethernet device
529 /// Linux VLAN (eth.10)
531 /// Interface Alias (eth:1)
533 /// Unknown interface type
537 pub const NETWORK_INTERFACE_NAME_SCHEMA
: Schema
= StringSchema
::new("Network interface name.")
538 .format(&NETWORK_INTERFACE_FORMAT
)
540 .max_length(libc
::IFNAMSIZ
-1)
543 pub const NETWORK_INTERFACE_ARRAY_SCHEMA
: Schema
= ArraySchema
::new(
544 "Network interface list.", &NETWORK_INTERFACE_NAME_SCHEMA
)
547 pub const NETWORK_INTERFACE_LIST_SCHEMA
: Schema
= StringSchema
::new(
548 "A list of network devices, comma separated.")
549 .format(&ApiStringFormat
::PropertyString(&NETWORK_INTERFACE_ARRAY_SCHEMA
))
555 schema
: NETWORK_INTERFACE_NAME_SCHEMA
,
558 type: NetworkInterfaceType
,
561 type: NetworkConfigMethod
,
565 type: NetworkConfigMethod
,
569 schema
: CIDR_V4_SCHEMA
,
573 schema
: CIDR_V6_SCHEMA
,
577 schema
: IP_V4_SCHEMA
,
581 schema
: IP_V6_SCHEMA
,
585 description
: "Option list (inet)",
588 description
: "Optional attribute line.",
593 description
: "Option list (inet6)",
596 description
: "Optional attribute line.",
601 description
: "Comments (inet, may span multiple lines)",
606 description
: "Comments (inet6, may span multiple lines)",
611 schema
: NETWORK_INTERFACE_ARRAY_SCHEMA
,
615 schema
: NETWORK_INTERFACE_ARRAY_SCHEMA
,
623 schema
: NETWORK_INTERFACE_NAME_SCHEMA
,
626 bond_xmit_hash_policy
: {
627 type: BondXmitHashPolicy
,
632 #[derive(Debug, Serialize, Deserialize)]
633 /// Network Interface configuration
634 pub struct Interface
{
635 /// Autostart interface
636 #[serde(rename = "autostart")]
638 /// Interface is active (UP)
643 #[serde(rename = "type")]
644 pub interface_type
: NetworkInterfaceType
,
645 #[serde(skip_serializing_if="Option::is_none")]
646 pub method
: Option
<NetworkConfigMethod
>,
647 #[serde(skip_serializing_if="Option::is_none")]
648 pub method6
: Option
<NetworkConfigMethod
>,
649 #[serde(skip_serializing_if="Option::is_none")]
650 /// IPv4 address with netmask
651 pub cidr
: Option
<String
>,
652 #[serde(skip_serializing_if="Option::is_none")]
654 pub gateway
: Option
<String
>,
655 #[serde(skip_serializing_if="Option::is_none")]
656 /// IPv6 address with netmask
657 pub cidr6
: Option
<String
>,
658 #[serde(skip_serializing_if="Option::is_none")]
660 pub gateway6
: Option
<String
>,
662 #[serde(skip_serializing_if="Vec::is_empty")]
663 pub options
: Vec
<String
>,
664 #[serde(skip_serializing_if="Vec::is_empty")]
665 pub options6
: Vec
<String
>,
667 #[serde(skip_serializing_if="Option::is_none")]
668 pub comments
: Option
<String
>,
669 #[serde(skip_serializing_if="Option::is_none")]
670 pub comments6
: Option
<String
>,
672 #[serde(skip_serializing_if="Option::is_none")]
673 /// Maximum Transmission Unit
674 pub mtu
: Option
<u64>,
676 #[serde(skip_serializing_if="Option::is_none")]
677 pub bridge_ports
: Option
<Vec
<String
>>,
678 /// Enable bridge vlan support.
679 #[serde(skip_serializing_if="Option::is_none")]
680 pub bridge_vlan_aware
: Option
<bool
>,
682 #[serde(skip_serializing_if="Option::is_none")]
683 pub slaves
: Option
<Vec
<String
>>,
684 #[serde(skip_serializing_if="Option::is_none")]
685 pub bond_mode
: Option
<LinuxBondMode
>,
686 #[serde(skip_serializing_if="Option::is_none")]
687 #[serde(rename = "bond-primary")]
688 pub bond_primary
: Option
<String
>,
689 pub bond_xmit_hash_policy
: Option
<BondXmitHashPolicy
>,
695 fn test_cert_fingerprint_schema() -> Result
<(), anyhow
::Error
> {
697 let schema
= CERT_FINGERPRINT_SHA256_SCHEMA
;
699 let invalid_fingerprints
= [
700 "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",
701 "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",
702 "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",
703 "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",
704 "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",
705 "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",
708 for fingerprint
in invalid_fingerprints
.iter() {
709 if parse_simple_value(fingerprint
, &schema
).is_ok() {
710 bail
!("test fingerprint '{}' failed - got Ok() while exception an error.", fingerprint
);
714 let valid_fingerprints
= [
715 "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",
716 "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",
719 for fingerprint
in valid_fingerprints
.iter() {
720 let v
= match parse_simple_value(fingerprint
, &schema
) {
723 bail
!("unable to parse fingerprint '{}' - {}", fingerprint
, err
);
727 if v
!= serde_json
::json
!(fingerprint
) {
728 bail
!("unable to parse fingerprint '{}' - got wrong value {:?}", fingerprint
, v
);
736 fn test_proxmox_user_id_schema() -> Result
<(), anyhow
::Error
> {
737 let invalid_user_ids
= [
742 "xx x@test", // contains space
743 "xx\nx@test", // contains control character
744 "x:xx@test", // contains collon
745 "xx/x@test", // contains slash
746 "xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx@test", // too long
749 for name
in invalid_user_ids
.iter() {
750 if parse_simple_value(name
, &Userid
::API_SCHEMA
).is_ok() {
751 bail
!("test userid '{}' failed - got Ok() while exception an error.", name
);
755 let valid_user_ids
= [
759 "xxx@_T_E_S_T-it.com",
760 "x_x-x.x@test-it.com",
763 for name
in valid_user_ids
.iter() {
764 let v
= match parse_simple_value(name
, &Userid
::API_SCHEMA
) {
767 bail
!("unable to parse userid '{}' - {}", name
, err
);
771 if v
!= serde_json
::json
!(name
) {
772 bail
!("unable to parse userid '{}' - got wrong value {:?}", name
, v
);
780 #[derive(Copy, Clone, Serialize, Deserialize)]
781 #[serde(rename_all = "UPPERCASE")]
792 #[derive(Copy, Clone, Serialize, Deserialize)]
793 #[serde(rename_all = "lowercase")]
794 pub enum RRDTimeFrameResolution
{
795 /// 1 min => last 70 minutes
797 /// 30 min => last 35 hours
799 /// 3 hours => about 8 days
801 /// 12 hours => last 35 days
803 /// 1 week => last 490 days
808 #[derive(Debug, Clone, Serialize, Deserialize)]
809 #[serde(rename_all = "PascalCase")]
810 /// Describes a package for which an update is available.
811 pub struct APTUpdateInfo
{
816 /// Package architecture
818 /// Human readable package description
819 pub description
: String
,
820 /// New version to be updated to
822 /// Old version currently installed
823 pub old_version
: String
,
826 /// Package priority in human-readable form
827 pub priority
: String
,
830 /// URL under which the package's changelog can be retrieved
831 pub change_log_url
: String
,
832 /// Custom extra field for additional package information
833 #[serde(skip_serializing_if="Option::is_none")]
834 pub extra_info
: Option
<String
>,
838 #[derive(Debug, Copy, Clone, PartialEq, Serialize, Deserialize)]
839 #[serde(rename_all = "lowercase")]
840 /// When do we send notifications
842 /// Never send notification
844 /// Send notifications for failed and successful jobs
846 /// Send notifications for failed jobs only
866 #[derive(Debug, Serialize, Deserialize)]
867 /// Datastore notify settings
868 pub struct DatastoreNotify
{
869 /// Garbage collection settings
870 pub gc
: Option
<Notify
>,
871 /// Verify job setting
872 pub verify
: Option
<Notify
>,
874 pub sync
: Option
<Notify
>,
877 /// An entry in a hierarchy of files for restore and listing.
879 #[derive(Serialize, Deserialize)]
880 pub struct ArchiveEntry
{
881 /// Base64-encoded full path to the file, including the filename
882 pub filepath
: String
,
883 /// Displayable filename text for UIs
885 /// File or directory type of this entry
886 #[serde(rename = "type")]
887 pub entry_type
: String
,
888 /// Is this entry a leaf node, or does it have children (i.e. a directory)?
890 /// The file size, if entry_type is 'f' (file)
891 #[serde(skip_serializing_if="Option::is_none")]
892 pub size
: Option
<u64>,
893 /// The file "last modified" time stamp, if entry_type is 'f' (file)
894 #[serde(skip_serializing_if="Option::is_none")]
895 pub mtime
: Option
<i64>,
899 pub fn new(filepath
: &[u8], entry_type
: Option
<&DirEntryAttribute
>) -> Self {
900 let size
= match entry_type
{
901 Some(DirEntryAttribute
::File { size, .. }
) => Some(*size
),
904 Self::new_with_size(filepath
, entry_type
, size
)
907 pub fn new_with_size(
909 entry_type
: Option
<&DirEntryAttribute
>,
913 filepath
: base64
::encode(filepath
),
914 text
: String
::from_utf8_lossy(filepath
.split(|x
| *x
== b'
/'
).last().unwrap())
916 entry_type
: match entry_type
{
917 Some(entry_type
) => CatalogEntryType
::from(entry_type
).to_string(),
918 None
=> "v".to_owned(),
920 leaf
: !matches
!(entry_type
, None
| Some(DirEntryAttribute
::Directory { .. }
)),
922 mtime
: match entry_type
{
923 Some(DirEntryAttribute
::File { mtime, .. }
) => Some(*mtime
),
930 pub const DATASTORE_NOTIFY_STRING_SCHEMA
: Schema
= StringSchema
::new(
931 "Datastore notification setting")
932 .format(&ApiStringFormat
::PropertyString(&DatastoreNotify
::API_SCHEMA
))
939 description
: "Estimated time of the next run (UNIX epoch).",
944 description
: "Result of the last run.",
949 description
: "Task UPID of the last run.",
953 "last-run-endtime": {
954 description
: "Endtime of the last run.",
960 #[derive(Serialize,Deserialize,Default)]
961 #[serde(rename_all="kebab-case")]
962 /// Job Scheduling Status
963 pub struct JobScheduleStatus
{
964 #[serde(skip_serializing_if="Option::is_none")]
965 pub next_run
: Option
<i64>,
966 #[serde(skip_serializing_if="Option::is_none")]
967 pub last_run_state
: Option
<String
>,
968 #[serde(skip_serializing_if="Option::is_none")]
969 pub last_run_upid
: Option
<String
>,
970 #[serde(skip_serializing_if="Option::is_none")]
971 pub last_run_endtime
: Option
<i64>,
975 #[derive(Serialize, Deserialize, Default)]
976 #[serde(rename_all = "kebab-case")]
977 /// Node memory usage counters
978 pub struct NodeMemoryCounters
{
988 #[derive(Serialize, Deserialize, Default)]
989 #[serde(rename_all = "kebab-case")]
990 /// Node swap usage counters
991 pub struct NodeSwapCounters
{
1001 #[derive(Serialize,Deserialize,Default)]
1002 #[serde(rename_all = "kebab-case")]
1003 /// Contains general node information such as the fingerprint`
1004 pub struct NodeInformation
{
1005 /// The SSL Fingerprint
1006 pub fingerprint
: String
,
1010 #[derive(Serialize, Deserialize, Default)]
1011 #[serde(rename_all = "kebab-case")]
1012 /// Information about the CPU
1013 pub struct NodeCpuInformation
{
1016 /// The number of CPU sockets
1018 /// The number of CPU cores (incl. threads)
1025 type: NodeMemoryCounters
,
1028 type: StorageStatus
,
1031 type: NodeSwapCounters
,
1037 description
: "the load",
1041 type: NodeCpuInformation
,
1044 type: NodeInformation
,
1048 #[derive(Serialize, Deserialize, Default)]
1049 #[serde(rename_all = "kebab-case")]
1051 pub struct NodeStatus
{
1052 pub memory
: NodeMemoryCounters
,
1053 pub root
: StorageStatus
,
1054 pub swap
: NodeSwapCounters
,
1055 /// The current uptime of the server.
1057 /// Load for 1, 5 and 15 minutes.
1058 pub loadavg
: [f64; 3],
1059 /// The current kernel version.
1060 pub kversion
: String
,
1061 /// Total CPU usage since last query.
1063 /// Total IO wait since last query.
1065 pub cpuinfo
: NodeCpuInformation
,
1066 pub info
: NodeInformation
,
1069 pub const HTTP_PROXY_SCHEMA
: Schema
= StringSchema
::new(
1070 "HTTP proxy configuration [http://]<host>[:port]")
1071 .format(&ApiStringFormat
::VerifyFn(|s
| {
1072 proxmox_http
::ProxyConfig
::parse_proxy_url(s
)?
;
1077 .type_text("[http://]<host>[:port]")