1 //! API Type Definitions
4 use serde
::{Deserialize, Serialize}
;
6 use proxmox
::api
::{api, schema::*}
;
7 use proxmox
::const_regex
;
8 use proxmox
::{IPRE, IPRE_BRACKET, IPV4RE, IPV6RE, IPV4OCTET, IPV6H16, IPV6LS32}
;
10 use pbs_datastore
::catalog
::CatalogEntryType
;
26 pub use file_restore
::*;
31 pub use pbs_api_types
::*;
33 // File names: may not contain slashes, may not start with "."
34 pub const FILENAME_FORMAT
: ApiStringFormat
= ApiStringFormat
::VerifyFn(|name
| {
35 if name
.starts_with('
.'
) {
36 bail
!("file names may not start with '.'");
38 if name
.contains('
/'
) {
39 bail
!("file names may not contain slashes");
44 macro_rules
! DNS_LABEL { () => (r"(?:[a-zA-Z0-9](?:[a-zA-Z0-9\-]*[a-zA-Z0-9])?)") }
45 macro_rules
! DNS_NAME { () => (concat!(r"(?:(?:", DNS_LABEL!() , r"\.)*", DNS_LABEL!(), ")")) }
47 macro_rules
! DNS_ALIAS_LABEL { () => (r"(?:[a-zA-Z0-9_](?:[a-zA-Z0-9\-]*[a-zA-Z0-9])?)") }
48 macro_rules
! DNS_ALIAS_NAME
{
49 () => (concat
!(r
"(?:(?:", DNS_ALIAS_LABEL
!() , r
"\.)*", DNS_ALIAS_LABEL
!(), ")"))
52 macro_rules
! CIDR_V4_REGEX_STR { () => (concat!(r"(?:", IPV4RE!(), r"/\d{1,2}
)$
")) }
53 macro_rules! CIDR_V6_REGEX_STR { () => (concat!(r"(?:", IPV6RE!(), r"/\d{1,3})$")) }
56 pub IP_V4_REGEX
= concat
!(r
"^", IPV4RE
!(), r
"$");
57 pub IP_V6_REGEX
= concat
!(r
"^", IPV6RE
!(), r
"$");
58 pub IP_REGEX
= concat
!(r
"^", IPRE
!(), r
"$");
59 pub CIDR_V4_REGEX
= concat
!(r
"^", CIDR_V4_REGEX_STR
!(), r
"$");
60 pub CIDR_V6_REGEX
= concat
!(r
"^", CIDR_V6_REGEX_STR
!(), r
"$");
61 pub CIDR_REGEX
= concat
!(r
"^(?:", CIDR_V4_REGEX_STR
!(), "|", CIDR_V6_REGEX_STR
!(), r
")$");
63 pub SHA256_HEX_REGEX
= r
"^[a-f0-9]{64}$"; // fixme: define in common_regex ?
64 pub SYSTEMD_DATETIME_REGEX
= r
"^\d{4}-\d{2}-\d{2}( \d{2}:\d{2}(:\d{2})?)?$"; // fixme: define in common_regex ?
66 pub PASSWORD_REGEX
= r
"^[[:^cntrl:]]*$"; // everything but control characters
68 /// Regex for verification jobs 'DATASTORE:ACTUAL_JOB_ID'
69 pub VERIFICATION_JOB_WORKER_ID_REGEX
= concat
!(r
"^(", PROXMOX_SAFE_ID_REGEX_STR
!(), r
"):");
70 /// Regex for sync jobs 'REMOTE:REMOTE_DATASTORE:LOCAL_DATASTORE:ACTUAL_JOB_ID'
71 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
"):");
73 pub HOSTNAME_REGEX
= r
"^(?:[a-zA-Z0-9](?:[a-zA-Z0-9\-]*[a-zA-Z0-9])?)$";
75 pub DNS_NAME_REGEX
= concat
!(r
"^", DNS_NAME
!(), r
"$");
77 pub DNS_ALIAS_REGEX
= concat
!(r
"^", DNS_ALIAS_NAME
!(), r
"$");
79 pub DNS_NAME_OR_IP_REGEX
= concat
!(r
"^(?:", DNS_NAME
!(), "|", IPRE
!(), r
")$");
81 pub BACKUP_REPO_URL_REGEX
= concat
!(r
"^^(?:(?:(", USER_ID_REGEX_STR
!(), "|", APITOKEN_ID_REGEX_STR
!(), ")@)?(", DNS_NAME
!(), "|", IPRE_BRACKET
!() ,"):)?(?:([0-9]{1,5}):)?(", PROXMOX_SAFE_ID_REGEX_STR
!(), r
")$");
83 pub ACL_PATH_REGEX
= concat
!(r
"^(?:/|", r
"(?:/", PROXMOX_SAFE_ID_REGEX_STR
!(), ")+", r
")$");
85 pub SUBSCRIPTION_KEY_REGEX
= concat
!(r
"^pbs(?:[cbsp])-[0-9a-f]{10}$");
87 pub BLOCKDEVICE_NAME_REGEX
= r
"^(:?(:?h|s|x?v)d[a-z]+)|(:?nvme\d+n\d+)$";
89 pub ZPOOL_NAME_REGEX
= r
"^[a-zA-Z][a-z0-9A-Z\-_.:]+$";
91 pub UUID_REGEX
= r
"^[0-9a-f]{8}(?:-[0-9a-f]{4}){3}-[0-9a-f]{12}$";
93 pub DATASTORE_MAP_REGEX
= concat
!(r
"(:?", PROXMOX_SAFE_ID_REGEX_STR
!(), r
"=)?", PROXMOX_SAFE_ID_REGEX_STR
!());
95 pub TAPE_RESTORE_SNAPSHOT_REGEX
= concat
!(r
"^", PROXMOX_SAFE_ID_REGEX_STR
!(), r
":", SNAPSHOT_PATH_REGEX_STR
!(), r
"$");
98 pub const SYSTEMD_DATETIME_FORMAT
: ApiStringFormat
=
99 ApiStringFormat
::Pattern(&SYSTEMD_DATETIME_REGEX
);
101 pub const IP_V4_FORMAT
: ApiStringFormat
=
102 ApiStringFormat
::Pattern(&IP_V4_REGEX
);
104 pub const IP_V6_FORMAT
: ApiStringFormat
=
105 ApiStringFormat
::Pattern(&IP_V6_REGEX
);
107 pub const IP_FORMAT
: ApiStringFormat
=
108 ApiStringFormat
::Pattern(&IP_REGEX
);
110 pub const PVE_CONFIG_DIGEST_FORMAT
: ApiStringFormat
=
111 ApiStringFormat
::Pattern(&SHA256_HEX_REGEX
);
113 pub const UUID_FORMAT
: ApiStringFormat
=
114 ApiStringFormat
::Pattern(&UUID_REGEX
);
116 pub const HOSTNAME_FORMAT
: ApiStringFormat
=
117 ApiStringFormat
::Pattern(&HOSTNAME_REGEX
);
119 pub const DNS_NAME_FORMAT
: ApiStringFormat
=
120 ApiStringFormat
::Pattern(&DNS_NAME_REGEX
);
122 pub const DNS_ALIAS_FORMAT
: ApiStringFormat
=
123 ApiStringFormat
::Pattern(&DNS_ALIAS_REGEX
);
125 pub const DNS_NAME_OR_IP_FORMAT
: ApiStringFormat
=
126 ApiStringFormat
::Pattern(&DNS_NAME_OR_IP_REGEX
);
128 pub const PASSWORD_FORMAT
: ApiStringFormat
=
129 ApiStringFormat
::Pattern(&PASSWORD_REGEX
);
131 pub const ACL_PATH_FORMAT
: ApiStringFormat
=
132 ApiStringFormat
::Pattern(&ACL_PATH_REGEX
);
134 pub const NETWORK_INTERFACE_FORMAT
: ApiStringFormat
=
135 ApiStringFormat
::Pattern(&PROXMOX_SAFE_ID_REGEX
);
137 pub const CIDR_V4_FORMAT
: ApiStringFormat
=
138 ApiStringFormat
::Pattern(&CIDR_V4_REGEX
);
140 pub const CIDR_V6_FORMAT
: ApiStringFormat
=
141 ApiStringFormat
::Pattern(&CIDR_V6_REGEX
);
143 pub const CIDR_FORMAT
: ApiStringFormat
=
144 ApiStringFormat
::Pattern(&CIDR_REGEX
);
146 pub const SUBSCRIPTION_KEY_FORMAT
: ApiStringFormat
=
147 ApiStringFormat
::Pattern(&SUBSCRIPTION_KEY_REGEX
);
149 pub const BLOCKDEVICE_NAME_FORMAT
: ApiStringFormat
=
150 ApiStringFormat
::Pattern(&BLOCKDEVICE_NAME_REGEX
);
152 pub const DATASTORE_MAP_FORMAT
: ApiStringFormat
=
153 ApiStringFormat
::Pattern(&DATASTORE_MAP_REGEX
);
155 pub const TAPE_RESTORE_SNAPSHOT_FORMAT
: ApiStringFormat
=
156 ApiStringFormat
::Pattern(&TAPE_RESTORE_SNAPSHOT_REGEX
);
158 pub const PASSWORD_SCHEMA
: Schema
= StringSchema
::new("Password.")
159 .format(&PASSWORD_FORMAT
)
164 pub const PBS_PASSWORD_SCHEMA
: Schema
= StringSchema
::new("User Password.")
165 .format(&PASSWORD_FORMAT
)
170 pub const TAPE_ENCRYPTION_KEY_FINGERPRINT_SCHEMA
: Schema
= StringSchema
::new(
171 "Tape encryption key fingerprint (sha256)."
173 .format(&FINGERPRINT_SHA256_FORMAT
)
176 pub const PROXMOX_CONFIG_DIGEST_SCHEMA
: Schema
= StringSchema
::new(
177 "Prevent changes if current configuration file has different \
178 SHA256 digest. This can be used to prevent concurrent \
181 .format(&PVE_CONFIG_DIGEST_FORMAT
) .schema();
184 pub const CHUNK_DIGEST_FORMAT
: ApiStringFormat
=
185 ApiStringFormat
::Pattern(&SHA256_HEX_REGEX
);
187 pub const CHUNK_DIGEST_SCHEMA
: Schema
= StringSchema
::new("Chunk digest (SHA256).")
188 .format(&CHUNK_DIGEST_FORMAT
)
191 pub const NODE_SCHEMA
: Schema
= StringSchema
::new("Node name (or 'localhost')")
192 .format(&ApiStringFormat
::VerifyFn(|node
| {
193 if node
== "localhost" || node
== proxmox
::tools
::nodename() {
196 bail
!("no such node '{}'", node
);
201 pub const SEARCH_DOMAIN_SCHEMA
: Schema
=
202 StringSchema
::new("Search domain for host-name lookup.").schema();
204 pub const FIRST_DNS_SERVER_SCHEMA
: Schema
=
205 StringSchema
::new("First name server IP address.")
209 pub const SECOND_DNS_SERVER_SCHEMA
: Schema
=
210 StringSchema
::new("Second name server IP address.")
214 pub const THIRD_DNS_SERVER_SCHEMA
: Schema
=
215 StringSchema
::new("Third name server IP address.")
219 pub const IP_V4_SCHEMA
: Schema
=
220 StringSchema
::new("IPv4 address.")
221 .format(&IP_V4_FORMAT
)
225 pub const IP_V6_SCHEMA
: Schema
=
226 StringSchema
::new("IPv6 address.")
227 .format(&IP_V6_FORMAT
)
231 pub const IP_SCHEMA
: Schema
=
232 StringSchema
::new("IP (IPv4 or IPv6) address.")
237 pub const CIDR_V4_SCHEMA
: Schema
=
238 StringSchema
::new("IPv4 address with netmask (CIDR notation).")
239 .format(&CIDR_V4_FORMAT
)
243 pub const CIDR_V6_SCHEMA
: Schema
=
244 StringSchema
::new("IPv6 address with netmask (CIDR notation).")
245 .format(&CIDR_V6_FORMAT
)
249 pub const CIDR_SCHEMA
: Schema
=
250 StringSchema
::new("IP address (IPv4 or IPv6) with netmask (CIDR notation).")
251 .format(&CIDR_FORMAT
)
255 pub const TIME_ZONE_SCHEMA
: Schema
= StringSchema
::new(
256 "Time zone. The file '/usr/share/zoneinfo/zone.tab' contains the list of valid names.")
257 .format(&SINGLE_LINE_COMMENT_FORMAT
)
262 pub const ACL_PATH_SCHEMA
: Schema
= StringSchema
::new(
263 "Access control path.")
264 .format(&ACL_PATH_FORMAT
)
269 pub const ACL_PROPAGATE_SCHEMA
: Schema
= BooleanSchema
::new(
270 "Allow to propagate (inherit) permissions.")
274 pub const ACL_UGID_TYPE_SCHEMA
: Schema
= StringSchema
::new(
275 "Type of 'ugid' property.")
276 .format(&ApiStringFormat
::Enum(&[
277 EnumEntry
::new("user", "User"),
278 EnumEntry
::new("group", "Group")]))
284 schema
: ACL_PROPAGATE_SCHEMA
,
287 schema
: ACL_PATH_SCHEMA
,
290 schema
: ACL_UGID_TYPE_SCHEMA
,
294 description
: "User or Group ID.",
301 #[derive(Serialize, Deserialize)]
303 pub struct AclListItem
{
306 pub ugid_type
: String
,
311 pub const BACKUP_ARCHIVE_NAME_SCHEMA
: Schema
=
312 StringSchema
::new("Backup archive name.")
313 .format(&PROXMOX_SAFE_ID_FORMAT
)
316 pub const BACKUP_TYPE_SCHEMA
: Schema
=
317 StringSchema
::new("Backup type.")
318 .format(&ApiStringFormat
::Enum(&[
319 EnumEntry
::new("vm", "Virtual Machine Backup"),
320 EnumEntry
::new("ct", "Container Backup"),
321 EnumEntry
::new("host", "Host Backup")]))
324 pub const BACKUP_ID_SCHEMA
: Schema
=
325 StringSchema
::new("Backup ID.")
326 .format(&BACKUP_ID_FORMAT
)
329 pub const BACKUP_TIME_SCHEMA
: Schema
=
330 IntegerSchema
::new("Backup time (Unix epoch.)")
331 .minimum(1_547_797_308)
334 pub const UPID_SCHEMA
: Schema
= StringSchema
::new("Unique Process/Task ID.")
338 pub const DATASTORE_SCHEMA
: Schema
= StringSchema
::new("Datastore name.")
339 .format(&PROXMOX_SAFE_ID_FORMAT
)
344 pub const DATASTORE_MAP_SCHEMA
: Schema
= StringSchema
::new("Datastore mapping.")
345 .format(&DATASTORE_MAP_FORMAT
)
348 .type_text("(<source>=)?<target>")
351 pub const DATASTORE_MAP_ARRAY_SCHEMA
: Schema
= ArraySchema
::new(
352 "Datastore mapping list.", &DATASTORE_MAP_SCHEMA
)
355 pub const DATASTORE_MAP_LIST_SCHEMA
: Schema
= StringSchema
::new(
356 "A list of Datastore mappings (or single datastore), comma separated. \
357 For example 'a=b,e' maps the source datastore 'a' to target 'b and \
358 all other sources to the default 'e'. If no default is given, only the \
359 specified sources are mapped.")
360 .format(&ApiStringFormat
::PropertyString(&DATASTORE_MAP_ARRAY_SCHEMA
))
363 pub const TAPE_RESTORE_SNAPSHOT_SCHEMA
: Schema
= StringSchema
::new(
364 "A snapshot in the format: 'store:type/id/time")
365 .format(&TAPE_RESTORE_SNAPSHOT_FORMAT
)
366 .type_text("store:type/id/time")
369 pub const MEDIA_SET_UUID_SCHEMA
: Schema
=
370 StringSchema
::new("MediaSet Uuid (We use the all-zero Uuid to reseve an empty media for a specific pool).")
371 .format(&UUID_FORMAT
)
374 pub const MEDIA_UUID_SCHEMA
: Schema
=
375 StringSchema
::new("Media Uuid.")
376 .format(&UUID_FORMAT
)
379 pub const SYNC_SCHEDULE_SCHEMA
: Schema
= StringSchema
::new(
380 "Run sync job at specified schedule.")
381 .format(&ApiStringFormat
::VerifyFn(crate::tools
::systemd
::time
::verify_calendar_event
))
382 .type_text("<calendar-event>")
385 pub const GC_SCHEDULE_SCHEMA
: Schema
= StringSchema
::new(
386 "Run garbage collection job at specified schedule.")
387 .format(&ApiStringFormat
::VerifyFn(crate::tools
::systemd
::time
::verify_calendar_event
))
388 .type_text("<calendar-event>")
391 pub const PRUNE_SCHEDULE_SCHEMA
: Schema
= StringSchema
::new(
392 "Run prune job at specified schedule.")
393 .format(&ApiStringFormat
::VerifyFn(crate::tools
::systemd
::time
::verify_calendar_event
))
394 .type_text("<calendar-event>")
397 pub const VERIFICATION_SCHEDULE_SCHEMA
: Schema
= StringSchema
::new(
398 "Run verify job at specified schedule.")
399 .format(&ApiStringFormat
::VerifyFn(crate::tools
::systemd
::time
::verify_calendar_event
))
400 .type_text("<calendar-event>")
403 pub const REMOTE_ID_SCHEMA
: Schema
= StringSchema
::new("Remote ID.")
404 .format(&PROXMOX_SAFE_ID_FORMAT
)
409 pub const JOB_ID_SCHEMA
: Schema
= StringSchema
::new("Job ID.")
410 .format(&PROXMOX_SAFE_ID_FORMAT
)
415 pub const REMOVE_VANISHED_BACKUPS_SCHEMA
: Schema
= BooleanSchema
::new(
416 "Delete vanished backups. This remove the local copy if the remote backup was deleted.")
420 pub const IGNORE_VERIFIED_BACKUPS_SCHEMA
: Schema
= BooleanSchema
::new(
421 "Do not verify backups that are already verified if their verification is not outdated.")
425 pub const VERIFICATION_OUTDATED_AFTER_SCHEMA
: Schema
= IntegerSchema
::new(
426 "Days after that a verification becomes outdated")
430 pub const HOSTNAME_SCHEMA
: Schema
= StringSchema
::new("Hostname (as defined in RFC1123).")
431 .format(&HOSTNAME_FORMAT
)
434 pub const DNS_NAME_OR_IP_SCHEMA
: Schema
= StringSchema
::new("DNS name or IP address.")
435 .format(&DNS_NAME_OR_IP_FORMAT
)
438 pub const SUBSCRIPTION_KEY_SCHEMA
: Schema
= StringSchema
::new("Proxmox Backup Server subscription key.")
439 .format(&SUBSCRIPTION_KEY_FORMAT
)
444 pub const BLOCKDEVICE_NAME_SCHEMA
: Schema
= StringSchema
::new("Block device name (/sys/block/<name>).")
445 .format(&BLOCKDEVICE_NAME_FORMAT
)
450 pub const REALM_ID_SCHEMA
: Schema
= StringSchema
::new("Realm name.")
451 .format(&PROXMOX_SAFE_ID_FORMAT
)
456 // Complex type definitions
461 schema
: DATASTORE_SCHEMA
,
465 schema
: SINGLE_LINE_COMMENT_SCHEMA
,
469 #[derive(Serialize, Deserialize)]
470 #[serde(rename_all="kebab-case")]
471 /// Basic information about a datastore.
472 pub struct DataStoreListItem
{
474 pub comment
: Option
<String
>,
480 schema
: BACKUP_TYPE_SCHEMA
,
483 schema
: BACKUP_ID_SCHEMA
,
486 schema
: BACKUP_TIME_SCHEMA
,
493 schema
: BACKUP_ARCHIVE_NAME_SCHEMA
502 #[derive(Serialize, Deserialize)]
503 #[serde(rename_all="kebab-case")]
504 /// Basic information about a backup group.
505 pub struct GroupListItem
{
506 pub backup_type
: String
, // enum
507 pub backup_id
: String
,
508 pub last_backup
: i64,
509 /// Number of contained snapshots
510 pub backup_count
: u64,
511 /// List of contained archive files.
512 pub files
: Vec
<String
>,
513 /// The owner of group
514 #[serde(skip_serializing_if="Option::is_none")]
515 pub owner
: Option
<Authid
>,
519 #[derive(Debug, Copy, Clone, PartialEq, Serialize, Deserialize)]
520 #[serde(rename_all = "lowercase")]
521 /// Result of a verify operation.
522 pub enum VerifyState
{
523 /// Verification was successful
525 /// Verification reported one or more errors
539 #[derive(Serialize, Deserialize)]
541 pub struct SnapshotVerifyState
{
542 /// UPID of the verify task
544 /// State of the verification. Enum.
545 pub state
: VerifyState
,
551 schema
: BACKUP_TYPE_SCHEMA
,
554 schema
: BACKUP_ID_SCHEMA
,
557 schema
: BACKUP_TIME_SCHEMA
,
560 schema
: SINGLE_LINE_COMMENT_SCHEMA
,
564 type: SnapshotVerifyState
,
573 schema
: BACKUP_ARCHIVE_NAME_SCHEMA
582 #[derive(Serialize, Deserialize)]
583 #[serde(rename_all="kebab-case")]
584 /// Basic information about backup snapshot.
585 pub struct SnapshotListItem
{
586 pub backup_type
: String
, // enum
587 pub backup_id
: String
,
588 pub backup_time
: i64,
589 /// The first line from manifest "notes"
590 #[serde(skip_serializing_if="Option::is_none")]
591 pub comment
: Option
<String
>,
592 /// The result of the last run verify task
593 #[serde(skip_serializing_if="Option::is_none")]
594 pub verification
: Option
<SnapshotVerifyState
>,
595 /// Fingerprint of encryption key
596 #[serde(skip_serializing_if="Option::is_none")]
597 pub fingerprint
: Option
<Fingerprint
>,
598 /// List of contained archive files.
599 pub files
: Vec
<BackupContent
>,
600 /// Overall snapshot size (sum of all archive sizes).
601 #[serde(skip_serializing_if="Option::is_none")]
602 pub size
: Option
<u64>,
603 /// The owner of the snapshots group
604 #[serde(skip_serializing_if="Option::is_none")]
605 pub owner
: Option
<Authid
>,
611 schema
: BACKUP_TYPE_SCHEMA
,
614 schema
: BACKUP_ID_SCHEMA
,
617 schema
: BACKUP_TIME_SCHEMA
,
621 #[derive(Serialize, Deserialize)]
622 #[serde(rename_all="kebab-case")]
624 pub struct PruneListItem
{
625 pub backup_type
: String
, // enum
626 pub backup_id
: String
,
627 pub backup_time
: i64,
632 pub const PRUNE_SCHEMA_KEEP_DAILY
: Schema
= IntegerSchema
::new(
633 "Number of daily backups to keep.")
637 pub const PRUNE_SCHEMA_KEEP_HOURLY
: Schema
= IntegerSchema
::new(
638 "Number of hourly backups to keep.")
642 pub const PRUNE_SCHEMA_KEEP_LAST
: Schema
= IntegerSchema
::new(
643 "Number of backups to keep.")
647 pub const PRUNE_SCHEMA_KEEP_MONTHLY
: Schema
= IntegerSchema
::new(
648 "Number of monthly backups to keep.")
652 pub const PRUNE_SCHEMA_KEEP_WEEKLY
: Schema
= IntegerSchema
::new(
653 "Number of weekly backups to keep.")
657 pub const PRUNE_SCHEMA_KEEP_YEARLY
: Schema
= IntegerSchema
::new(
658 "Number of yearly backups to keep.")
665 schema
: BACKUP_ARCHIVE_NAME_SCHEMA
,
673 #[derive(Serialize, Deserialize)]
674 #[serde(rename_all="kebab-case")]
675 /// Basic information about archive files inside a backup snapshot.
676 pub struct BackupContent
{
677 pub filename
: String
,
678 /// Info if file is encrypted, signed, or neither.
679 #[serde(skip_serializing_if="Option::is_none")]
680 pub crypt_mode
: Option
<CryptMode
>,
681 /// Archive size (from backup manifest).
682 #[serde(skip_serializing_if="Option::is_none")]
683 pub size
: Option
<u64>,
687 #[derive(Default, Serialize, Deserialize)]
688 /// Storage space usage information.
689 pub struct StorageStatus
{
690 /// Total space (bytes).
692 /// Used space (bytes).
694 /// Available space (bytes).
699 #[derive(Serialize, Deserialize, Default)]
700 /// Backup Type group/snapshot counts.
701 pub struct TypeCounts
{
702 /// The number of groups of the type.
704 /// The number of snapshots of the type.
728 #[derive(Serialize, Deserialize, Default)]
729 /// Counts of groups/snapshots per BackupType.
731 /// The counts for CT backups
732 pub ct
: Option
<TypeCounts
>,
733 /// The counts for Host backups
734 pub host
: Option
<TypeCounts
>,
735 /// The counts for VM backups
736 pub vm
: Option
<TypeCounts
>,
737 /// The counts for other backup types
738 pub other
: Option
<TypeCounts
>,
744 type: GarbageCollectionStatus
,
753 #[derive(Serialize, Deserialize)]
754 #[serde(rename_all="kebab-case")]
755 /// Overall Datastore status and useful information.
756 pub struct DataStoreStatus
{
757 /// Total space (bytes).
759 /// Used space (bytes).
761 /// Available space (bytes).
763 /// Status of last GC
764 #[serde(skip_serializing_if="Option::is_none")]
765 pub gc_status
: Option
<GarbageCollectionStatus
>,
766 /// Group/Snapshot counts
767 #[serde(skip_serializing_if="Option::is_none")]
768 pub counts
: Option
<Counts
>,
773 upid
: { schema: UPID_SCHEMA }
,
774 user
: { type: Authid }
,
777 #[derive(Serialize, Deserialize)]
779 pub struct TaskListItem
{
781 /// The node name where the task is running on.
785 /// The task start time (Epoch)
787 /// The task start time (Epoch)
789 /// Worker type (arbitrary ASCII string)
790 pub worker_type
: String
,
791 /// Worker ID (arbitrary ASCII string)
792 pub worker_id
: Option
<String
>,
793 /// The authenticated entity who started the task
795 /// The task end time (Epoch)
796 #[serde(skip_serializing_if="Option::is_none")]
797 pub endtime
: Option
<i64>,
799 #[serde(skip_serializing_if="Option::is_none")]
800 pub status
: Option
<String
>,
803 impl From
<crate::server
::TaskListInfo
> for TaskListItem
{
804 fn from(info
: crate::server
::TaskListInfo
) -> Self {
805 let (endtime
, status
) = info
807 .map_or_else(|| (None
, None
), |a
| (Some(a
.endtime()), Some(a
.to_string())));
811 node
: "localhost".to_string(),
812 pid
: info
.upid
.pid
as i64,
813 pstart
: info
.upid
.pstart
,
814 starttime
: info
.upid
.starttime
,
815 worker_type
: info
.upid
.worker_type
,
816 worker_id
: info
.upid
.worker_id
,
817 user
: info
.upid
.auth_id
,
825 #[derive(Eq, PartialEq, Debug, Serialize, Deserialize)]
826 #[serde(rename_all = "lowercase")]
827 pub enum TaskStateType
{
839 #[derive(Debug, Copy, Clone, PartialEq, Serialize, Deserialize)]
840 #[serde(rename_all = "lowercase")]
841 /// Node Power command type.
842 pub enum NodePowerCommand
{
843 /// Restart the server
845 /// Shutdown the server
850 #[derive(Debug, Copy, Clone, PartialEq, Serialize, Deserialize)]
851 #[serde(rename_all = "lowercase")]
852 /// Interface configuration method
853 pub enum NetworkConfigMethod
{
854 /// Configuration is done manually using other tools
856 /// Define interfaces with statically allocated addresses.
858 /// Obtain an address via DHCP
860 /// Define the loopback interface.
865 #[derive(Debug, Copy, Clone, PartialEq, Serialize, Deserialize)]
866 #[serde(rename_all = "kebab-case")]
867 #[allow(non_camel_case_types)]
870 pub enum LinuxBondMode
{
871 /// Round-robin policy
873 /// Active-backup policy
879 /// IEEE 802.3ad Dynamic link aggregation
880 #[serde(rename = "802.3ad")]
882 /// Adaptive transmit load balancing
884 /// Adaptive load balancing
889 #[derive(Debug, Copy, Clone, PartialEq, Serialize, Deserialize)]
890 #[serde(rename_all = "kebab-case")]
891 #[allow(non_camel_case_types)]
893 /// Bond Transmit Hash Policy for LACP (802.3ad)
894 pub enum BondXmitHashPolicy
{
898 #[serde(rename = "layer2+3")]
901 #[serde(rename = "layer3+4")]
906 #[derive(Debug, Copy, Clone, PartialEq, Serialize, Deserialize)]
907 #[serde(rename_all = "lowercase")]
908 /// Network interface type
909 pub enum NetworkInterfaceType
{
912 /// Physical Ethernet device
918 /// Linux VLAN (eth.10)
920 /// Interface Alias (eth:1)
922 /// Unknown interface type
926 pub const NETWORK_INTERFACE_NAME_SCHEMA
: Schema
= StringSchema
::new("Network interface name.")
927 .format(&NETWORK_INTERFACE_FORMAT
)
929 .max_length(libc
::IFNAMSIZ
-1)
932 pub const NETWORK_INTERFACE_ARRAY_SCHEMA
: Schema
= ArraySchema
::new(
933 "Network interface list.", &NETWORK_INTERFACE_NAME_SCHEMA
)
936 pub const NETWORK_INTERFACE_LIST_SCHEMA
: Schema
= StringSchema
::new(
937 "A list of network devices, comma separated.")
938 .format(&ApiStringFormat
::PropertyString(&NETWORK_INTERFACE_ARRAY_SCHEMA
))
944 schema
: NETWORK_INTERFACE_NAME_SCHEMA
,
947 type: NetworkInterfaceType
,
950 type: NetworkConfigMethod
,
954 type: NetworkConfigMethod
,
958 schema
: CIDR_V4_SCHEMA
,
962 schema
: CIDR_V6_SCHEMA
,
966 schema
: IP_V4_SCHEMA
,
970 schema
: IP_V6_SCHEMA
,
974 description
: "Option list (inet)",
977 description
: "Optional attribute line.",
982 description
: "Option list (inet6)",
985 description
: "Optional attribute line.",
990 description
: "Comments (inet, may span multiple lines)",
995 description
: "Comments (inet6, may span multiple lines)",
1000 schema
: NETWORK_INTERFACE_ARRAY_SCHEMA
,
1004 schema
: NETWORK_INTERFACE_ARRAY_SCHEMA
,
1008 type: LinuxBondMode
,
1012 schema
: NETWORK_INTERFACE_NAME_SCHEMA
,
1015 bond_xmit_hash_policy
: {
1016 type: BondXmitHashPolicy
,
1021 #[derive(Debug, Serialize, Deserialize)]
1022 /// Network Interface configuration
1023 pub struct Interface
{
1024 /// Autostart interface
1025 #[serde(rename = "autostart")]
1026 pub autostart
: bool
,
1027 /// Interface is active (UP)
1032 #[serde(rename = "type")]
1033 pub interface_type
: NetworkInterfaceType
,
1034 #[serde(skip_serializing_if="Option::is_none")]
1035 pub method
: Option
<NetworkConfigMethod
>,
1036 #[serde(skip_serializing_if="Option::is_none")]
1037 pub method6
: Option
<NetworkConfigMethod
>,
1038 #[serde(skip_serializing_if="Option::is_none")]
1039 /// IPv4 address with netmask
1040 pub cidr
: Option
<String
>,
1041 #[serde(skip_serializing_if="Option::is_none")]
1043 pub gateway
: Option
<String
>,
1044 #[serde(skip_serializing_if="Option::is_none")]
1045 /// IPv6 address with netmask
1046 pub cidr6
: Option
<String
>,
1047 #[serde(skip_serializing_if="Option::is_none")]
1049 pub gateway6
: Option
<String
>,
1051 #[serde(skip_serializing_if="Vec::is_empty")]
1052 pub options
: Vec
<String
>,
1053 #[serde(skip_serializing_if="Vec::is_empty")]
1054 pub options6
: Vec
<String
>,
1056 #[serde(skip_serializing_if="Option::is_none")]
1057 pub comments
: Option
<String
>,
1058 #[serde(skip_serializing_if="Option::is_none")]
1059 pub comments6
: Option
<String
>,
1061 #[serde(skip_serializing_if="Option::is_none")]
1062 /// Maximum Transmission Unit
1063 pub mtu
: Option
<u64>,
1065 #[serde(skip_serializing_if="Option::is_none")]
1066 pub bridge_ports
: Option
<Vec
<String
>>,
1067 /// Enable bridge vlan support.
1068 #[serde(skip_serializing_if="Option::is_none")]
1069 pub bridge_vlan_aware
: Option
<bool
>,
1071 #[serde(skip_serializing_if="Option::is_none")]
1072 pub slaves
: Option
<Vec
<String
>>,
1073 #[serde(skip_serializing_if="Option::is_none")]
1074 pub bond_mode
: Option
<LinuxBondMode
>,
1075 #[serde(skip_serializing_if="Option::is_none")]
1076 #[serde(rename = "bond-primary")]
1077 pub bond_primary
: Option
<String
>,
1078 pub bond_xmit_hash_policy
: Option
<BondXmitHashPolicy
>,
1084 fn test_cert_fingerprint_schema() -> Result
<(), anyhow
::Error
> {
1086 let schema
= CERT_FINGERPRINT_SHA256_SCHEMA
;
1088 let invalid_fingerprints
= [
1089 "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",
1090 "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",
1091 "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",
1092 "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",
1093 "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",
1094 "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",
1097 for fingerprint
in invalid_fingerprints
.iter() {
1098 if parse_simple_value(fingerprint
, &schema
).is_ok() {
1099 bail
!("test fingerprint '{}' failed - got Ok() while exception an error.", fingerprint
);
1103 let valid_fingerprints
= [
1104 "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",
1105 "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",
1108 for fingerprint
in valid_fingerprints
.iter() {
1109 let v
= match parse_simple_value(fingerprint
, &schema
) {
1112 bail
!("unable to parse fingerprint '{}' - {}", fingerprint
, err
);
1116 if v
!= serde_json
::json
!(fingerprint
) {
1117 bail
!("unable to parse fingerprint '{}' - got wrong value {:?}", fingerprint
, v
);
1125 fn test_proxmox_user_id_schema() -> Result
<(), anyhow
::Error
> {
1126 let invalid_user_ids
= [
1131 "xx x@test", // contains space
1132 "xx\nx@test", // contains control character
1133 "x:xx@test", // contains collon
1134 "xx/x@test", // contains slash
1135 "xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx@test", // too long
1138 for name
in invalid_user_ids
.iter() {
1139 if parse_simple_value(name
, &Userid
::API_SCHEMA
).is_ok() {
1140 bail
!("test userid '{}' failed - got Ok() while exception an error.", name
);
1144 let valid_user_ids
= [
1148 "xxx@_T_E_S_T-it.com",
1149 "x_x-x.x@test-it.com",
1152 for name
in valid_user_ids
.iter() {
1153 let v
= match parse_simple_value(name
, &Userid
::API_SCHEMA
) {
1156 bail
!("unable to parse userid '{}' - {}", name
, err
);
1160 if v
!= serde_json
::json
!(name
) {
1161 bail
!("unable to parse userid '{}' - got wrong value {:?}", name
, v
);
1169 #[derive(Copy, Clone, Serialize, Deserialize)]
1170 #[serde(rename_all = "UPPERCASE")]
1181 #[derive(Copy, Clone, Serialize, Deserialize)]
1182 #[serde(rename_all = "lowercase")]
1183 pub enum RRDTimeFrameResolution
{
1184 /// 1 min => last 70 minutes
1186 /// 30 min => last 35 hours
1188 /// 3 hours => about 8 days
1190 /// 12 hours => last 35 days
1192 /// 1 week => last 490 days
1197 #[derive(Debug, Clone, Serialize, Deserialize)]
1198 #[serde(rename_all = "PascalCase")]
1199 /// Describes a package for which an update is available.
1200 pub struct APTUpdateInfo
{
1202 pub package
: String
,
1205 /// Package architecture
1207 /// Human readable package description
1208 pub description
: String
,
1209 /// New version to be updated to
1210 pub version
: String
,
1211 /// Old version currently installed
1212 pub old_version
: String
,
1215 /// Package priority in human-readable form
1216 pub priority
: String
,
1218 pub section
: String
,
1219 /// URL under which the package's changelog can be retrieved
1220 pub change_log_url
: String
,
1221 /// Custom extra field for additional package information
1222 #[serde(skip_serializing_if="Option::is_none")]
1223 pub extra_info
: Option
<String
>,
1227 #[derive(Debug, Copy, Clone, PartialEq, Serialize, Deserialize)]
1228 #[serde(rename_all = "lowercase")]
1229 /// When do we send notifications
1231 /// Never send notification
1233 /// Send notifications for failed and successful jobs
1235 /// Send notifications for failed jobs only
1255 #[derive(Debug, Serialize, Deserialize)]
1256 /// Datastore notify settings
1257 pub struct DatastoreNotify
{
1258 /// Garbage collection settings
1259 pub gc
: Option
<Notify
>,
1260 /// Verify job setting
1261 pub verify
: Option
<Notify
>,
1262 /// Sync job setting
1263 pub sync
: Option
<Notify
>,
1266 /// An entry in a hierarchy of files for restore and listing.
1268 #[derive(Serialize, Deserialize)]
1269 pub struct ArchiveEntry
{
1270 /// Base64-encoded full path to the file, including the filename
1271 pub filepath
: String
,
1272 /// Displayable filename text for UIs
1274 /// File or directory type of this entry
1275 #[serde(rename = "type")]
1276 pub entry_type
: String
,
1277 /// Is this entry a leaf node, or does it have children (i.e. a directory)?
1279 /// The file size, if entry_type is 'f' (file)
1280 #[serde(skip_serializing_if="Option::is_none")]
1281 pub size
: Option
<u64>,
1282 /// The file "last modified" time stamp, if entry_type is 'f' (file)
1283 #[serde(skip_serializing_if="Option::is_none")]
1284 pub mtime
: Option
<i64>,
1288 pub fn new(filepath
: &[u8], entry_type
: Option
<&DirEntryAttribute
>) -> Self {
1289 let size
= match entry_type
{
1290 Some(DirEntryAttribute
::File { size, .. }
) => Some(*size
),
1293 Self::new_with_size(filepath
, entry_type
, size
)
1296 pub fn new_with_size(
1298 entry_type
: Option
<&DirEntryAttribute
>,
1302 filepath
: base64
::encode(filepath
),
1303 text
: String
::from_utf8_lossy(filepath
.split(|x
| *x
== b'
/'
).last().unwrap())
1305 entry_type
: match entry_type
{
1306 Some(entry_type
) => CatalogEntryType
::from(entry_type
).to_string(),
1307 None
=> "v".to_owned(),
1309 leaf
: !matches
!(entry_type
, None
| Some(DirEntryAttribute
::Directory { .. }
)),
1311 mtime
: match entry_type
{
1312 Some(DirEntryAttribute
::File { mtime, .. }
) => Some(*mtime
),
1319 pub const DATASTORE_NOTIFY_STRING_SCHEMA
: Schema
= StringSchema
::new(
1320 "Datastore notification setting")
1321 .format(&ApiStringFormat
::PropertyString(&DatastoreNotify
::API_SCHEMA
))
1325 pub const PASSWORD_HINT_SCHEMA
: Schema
= StringSchema
::new("Password hint.")
1326 .format(&SINGLE_LINE_COMMENT_FORMAT
)
1332 #[derive(Deserialize, Serialize)]
1333 /// RSA public key information
1334 pub struct RsaPubKeyInfo
{
1335 /// Path to key (if stored in a file)
1336 #[serde(skip_serializing_if="Option::is_none")]
1337 pub path
: Option
<String
>,
1339 pub exponent
: String
,
1340 /// Hex-encoded RSA modulus
1341 pub modulus
: String
,
1342 /// Key (modulus) length in bits
1346 impl std
::convert
::TryFrom
<openssl
::rsa
::Rsa
<openssl
::pkey
::Public
>> for RsaPubKeyInfo
{
1347 type Error
= anyhow
::Error
;
1349 fn try_from(value
: openssl
::rsa
::Rsa
<openssl
::pkey
::Public
>) -> Result
<Self, Self::Error
> {
1350 let modulus
= value
.n().to_hex_str()?
.to_string();
1351 let exponent
= value
.e().to_dec_str()?
.to_string();
1352 let length
= value
.size() as usize * 8;
1366 description
: "Estimated time of the next run (UNIX epoch).",
1371 description
: "Result of the last run.",
1376 description
: "Task UPID of the last run.",
1380 "last-run-endtime": {
1381 description
: "Endtime of the last run.",
1387 #[derive(Serialize,Deserialize,Default)]
1388 #[serde(rename_all="kebab-case")]
1389 /// Job Scheduling Status
1390 pub struct JobScheduleStatus
{
1391 #[serde(skip_serializing_if="Option::is_none")]
1392 pub next_run
: Option
<i64>,
1393 #[serde(skip_serializing_if="Option::is_none")]
1394 pub last_run_state
: Option
<String
>,
1395 #[serde(skip_serializing_if="Option::is_none")]
1396 pub last_run_upid
: Option
<String
>,
1397 #[serde(skip_serializing_if="Option::is_none")]
1398 pub last_run_endtime
: Option
<i64>,
1402 #[derive(Serialize, Deserialize, Default)]
1403 #[serde(rename_all = "kebab-case")]
1404 /// Node memory usage counters
1405 pub struct NodeMemoryCounters
{
1415 #[derive(Serialize, Deserialize, Default)]
1416 #[serde(rename_all = "kebab-case")]
1417 /// Node swap usage counters
1418 pub struct NodeSwapCounters
{
1428 #[derive(Serialize,Deserialize,Default)]
1429 #[serde(rename_all = "kebab-case")]
1430 /// Contains general node information such as the fingerprint`
1431 pub struct NodeInformation
{
1432 /// The SSL Fingerprint
1433 pub fingerprint
: String
,
1437 #[derive(Serialize, Deserialize, Default)]
1438 #[serde(rename_all = "kebab-case")]
1439 /// Information about the CPU
1440 pub struct NodeCpuInformation
{
1443 /// The number of CPU sockets
1445 /// The number of CPU cores (incl. threads)
1452 type: NodeMemoryCounters
,
1455 type: StorageStatus
,
1458 type: NodeSwapCounters
,
1464 description
: "the load",
1468 type: NodeCpuInformation
,
1471 type: NodeInformation
,
1475 #[derive(Serialize, Deserialize, Default)]
1476 #[serde(rename_all = "kebab-case")]
1478 pub struct NodeStatus
{
1479 pub memory
: NodeMemoryCounters
,
1480 pub root
: StorageStatus
,
1481 pub swap
: NodeSwapCounters
,
1482 /// The current uptime of the server.
1484 /// Load for 1, 5 and 15 minutes.
1485 pub loadavg
: [f64; 3],
1486 /// The current kernel version.
1487 pub kversion
: String
,
1488 /// Total CPU usage since last query.
1490 /// Total IO wait since last query.
1492 pub cpuinfo
: NodeCpuInformation
,
1493 pub info
: NodeInformation
,
1496 pub const HTTP_PROXY_SCHEMA
: Schema
= StringSchema
::new(
1497 "HTTP proxy configuration [http://]<host>[:port]")
1498 .format(&ApiStringFormat
::VerifyFn(|s
| {
1499 proxmox_http
::ProxyConfig
::parse_proxy_url(s
)?
;
1504 .type_text("[http://]<host>[:port]")