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}
;
27 pub use userid
::{Realm, RealmRef}
;
28 pub use userid
::{Tokenname, TokennameRef}
;
29 pub use userid
::{Username, UsernameRef}
;
30 pub use userid
::Userid
;
31 pub use userid
::Authid
;
32 pub use userid
::{PROXMOX_TOKEN_ID_SCHEMA, PROXMOX_TOKEN_NAME_SCHEMA, PROXMOX_GROUP_ID_SCHEMA}
;
37 // File names: may not contain slashes, may not start with "."
38 pub const FILENAME_FORMAT
: ApiStringFormat
= ApiStringFormat
::VerifyFn(|name
| {
39 if name
.starts_with('
.'
) {
40 bail
!("file names may not start with '.'");
42 if name
.contains('
/'
) {
43 bail
!("file names may not contain slashes");
48 macro_rules
! DNS_LABEL { () => (r"(?:[a-zA-Z0-9](?:[a-zA-Z0-9\-]*[a-zA-Z0-9])?)") }
49 macro_rules
! DNS_NAME { () => (concat!(r"(?:(?:", DNS_LABEL!() , r"\.)*", DNS_LABEL!(), ")")) }
51 macro_rules
! CIDR_V4_REGEX_STR { () => (concat!(r"(?:", IPV4RE!(), r"/\d{1,2}
)$
")) }
52 macro_rules! CIDR_V6_REGEX_STR { () => (concat!(r"(?:", IPV6RE!(), r"/\d{1,3})$")) }
55 pub IP_V4_REGEX
= concat
!(r
"^", IPV4RE
!(), r
"$");
56 pub IP_V6_REGEX
= concat
!(r
"^", IPV6RE
!(), r
"$");
57 pub IP_REGEX
= concat
!(r
"^", IPRE
!(), r
"$");
58 pub CIDR_V4_REGEX
= concat
!(r
"^", CIDR_V4_REGEX_STR
!(), r
"$");
59 pub CIDR_V6_REGEX
= concat
!(r
"^", CIDR_V6_REGEX_STR
!(), r
"$");
60 pub CIDR_REGEX
= concat
!(r
"^(?:", CIDR_V4_REGEX_STR
!(), "|", CIDR_V6_REGEX_STR
!(), r
")$");
62 pub SHA256_HEX_REGEX
= r
"^[a-f0-9]{64}$"; // fixme: define in common_regex ?
63 pub SYSTEMD_DATETIME_REGEX
= r
"^\d{4}-\d{2}-\d{2}( \d{2}:\d{2}(:\d{2})?)?$"; // fixme: define in common_regex ?
65 pub PASSWORD_REGEX
= r
"^[[:^cntrl:]]*$"; // everything but control characters
67 /// Regex for safe identifiers.
70 /// [article](https://dwheeler.com/essays/fixing-unix-linux-filenames.html)
71 /// contains further information why it is reasonable to restict
72 /// names this way. This is not only useful for filenames, but for
73 /// any identifier command line tools work with.
74 pub PROXMOX_SAFE_ID_REGEX
= concat
!(r
"^", PROXMOX_SAFE_ID_REGEX_STR
!(), r
"$");
76 /// Regex for verification jobs 'DATASTORE:ACTUAL_JOB_ID'
77 pub VERIFICATION_JOB_WORKER_ID_REGEX
= concat
!(r
"^(", PROXMOX_SAFE_ID_REGEX_STR
!(), r
"):");
78 /// Regex for sync jobs 'REMOTE:REMOTE_DATASTORE:LOCAL_DATASTORE:ACTUAL_JOB_ID'
79 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
"):");
81 pub SINGLE_LINE_COMMENT_REGEX
= r
"^[[:^cntrl:]]*$";
83 pub HOSTNAME_REGEX
= r
"^(?:[a-zA-Z0-9](?:[a-zA-Z0-9\-]*[a-zA-Z0-9])?)$";
85 pub DNS_NAME_REGEX
= concat
!(r
"^", DNS_NAME
!(), r
"$");
87 pub DNS_NAME_OR_IP_REGEX
= concat
!(r
"^(?:", DNS_NAME
!(), "|", IPRE
!(), r
")$");
89 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
")$");
91 pub FINGERPRINT_SHA256_REGEX
= r
"^(?:[0-9a-fA-F][0-9a-fA-F])(?::[0-9a-fA-F][0-9a-fA-F]){31}$";
93 pub ACL_PATH_REGEX
= concat
!(r
"^(?:/|", r
"(?:/", PROXMOX_SAFE_ID_REGEX_STR
!(), ")+", r
")$");
95 pub SUBSCRIPTION_KEY_REGEX
= concat
!(r
"^pbs(?:[cbsp])-[0-9a-f]{10}$");
97 pub BLOCKDEVICE_NAME_REGEX
= r
"^(:?(:?h|s|x?v)d[a-z]+)|(:?nvme\d+n\d+)$";
99 pub ZPOOL_NAME_REGEX
= r
"^[a-zA-Z][a-z0-9A-Z\-_.:]+$";
101 pub UUID_REGEX
= r
"^[0-9a-f]{8}(?:-[0-9a-f]{4}){3}-[0-9a-f]{12}$";
103 pub DATASTORE_MAP_REGEX
= concat
!(r
"(:?", PROXMOX_SAFE_ID_REGEX_STR
!(), r
"=)?", PROXMOX_SAFE_ID_REGEX_STR
!());
106 pub const SYSTEMD_DATETIME_FORMAT
: ApiStringFormat
=
107 ApiStringFormat
::Pattern(&SYSTEMD_DATETIME_REGEX
);
109 pub const IP_V4_FORMAT
: ApiStringFormat
=
110 ApiStringFormat
::Pattern(&IP_V4_REGEX
);
112 pub const IP_V6_FORMAT
: ApiStringFormat
=
113 ApiStringFormat
::Pattern(&IP_V6_REGEX
);
115 pub const IP_FORMAT
: ApiStringFormat
=
116 ApiStringFormat
::Pattern(&IP_REGEX
);
118 pub const PVE_CONFIG_DIGEST_FORMAT
: ApiStringFormat
=
119 ApiStringFormat
::Pattern(&SHA256_HEX_REGEX
);
121 pub const FINGERPRINT_SHA256_FORMAT
: ApiStringFormat
=
122 ApiStringFormat
::Pattern(&FINGERPRINT_SHA256_REGEX
);
124 pub const PROXMOX_SAFE_ID_FORMAT
: ApiStringFormat
=
125 ApiStringFormat
::Pattern(&PROXMOX_SAFE_ID_REGEX
);
127 pub const BACKUP_ID_FORMAT
: ApiStringFormat
=
128 ApiStringFormat
::Pattern(&BACKUP_ID_REGEX
);
130 pub const UUID_FORMAT
: ApiStringFormat
=
131 ApiStringFormat
::Pattern(&UUID_REGEX
);
133 pub const SINGLE_LINE_COMMENT_FORMAT
: ApiStringFormat
=
134 ApiStringFormat
::Pattern(&SINGLE_LINE_COMMENT_REGEX
);
136 pub const HOSTNAME_FORMAT
: ApiStringFormat
=
137 ApiStringFormat
::Pattern(&HOSTNAME_REGEX
);
139 pub const DNS_NAME_FORMAT
: ApiStringFormat
=
140 ApiStringFormat
::Pattern(&DNS_NAME_REGEX
);
142 pub const DNS_NAME_OR_IP_FORMAT
: ApiStringFormat
=
143 ApiStringFormat
::Pattern(&DNS_NAME_OR_IP_REGEX
);
145 pub const PASSWORD_FORMAT
: ApiStringFormat
=
146 ApiStringFormat
::Pattern(&PASSWORD_REGEX
);
148 pub const ACL_PATH_FORMAT
: ApiStringFormat
=
149 ApiStringFormat
::Pattern(&ACL_PATH_REGEX
);
151 pub const NETWORK_INTERFACE_FORMAT
: ApiStringFormat
=
152 ApiStringFormat
::Pattern(&PROXMOX_SAFE_ID_REGEX
);
154 pub const CIDR_V4_FORMAT
: ApiStringFormat
=
155 ApiStringFormat
::Pattern(&CIDR_V4_REGEX
);
157 pub const CIDR_V6_FORMAT
: ApiStringFormat
=
158 ApiStringFormat
::Pattern(&CIDR_V6_REGEX
);
160 pub const CIDR_FORMAT
: ApiStringFormat
=
161 ApiStringFormat
::Pattern(&CIDR_REGEX
);
163 pub const SUBSCRIPTION_KEY_FORMAT
: ApiStringFormat
=
164 ApiStringFormat
::Pattern(&SUBSCRIPTION_KEY_REGEX
);
166 pub const BLOCKDEVICE_NAME_FORMAT
: ApiStringFormat
=
167 ApiStringFormat
::Pattern(&BLOCKDEVICE_NAME_REGEX
);
169 pub const DATASTORE_MAP_FORMAT
: ApiStringFormat
=
170 ApiStringFormat
::Pattern(&DATASTORE_MAP_REGEX
);
172 pub const PASSWORD_SCHEMA
: Schema
= StringSchema
::new("Password.")
173 .format(&PASSWORD_FORMAT
)
178 pub const PBS_PASSWORD_SCHEMA
: Schema
= StringSchema
::new("User Password.")
179 .format(&PASSWORD_FORMAT
)
184 pub const CERT_FINGERPRINT_SHA256_SCHEMA
: Schema
= StringSchema
::new(
185 "X509 certificate fingerprint (sha256)."
187 .format(&FINGERPRINT_SHA256_FORMAT
)
190 pub const TAPE_ENCRYPTION_KEY_FINGERPRINT_SCHEMA
: Schema
= StringSchema
::new(
191 "Tape encryption key fingerprint (sha256)."
193 .format(&FINGERPRINT_SHA256_FORMAT
)
196 pub const PROXMOX_CONFIG_DIGEST_SCHEMA
: Schema
= StringSchema
::new(
197 "Prevent changes if current configuration file has different \
198 SHA256 digest. This can be used to prevent concurrent \
201 .format(&PVE_CONFIG_DIGEST_FORMAT
) .schema();
204 pub const CHUNK_DIGEST_FORMAT
: ApiStringFormat
=
205 ApiStringFormat
::Pattern(&SHA256_HEX_REGEX
);
207 pub const CHUNK_DIGEST_SCHEMA
: Schema
= StringSchema
::new("Chunk digest (SHA256).")
208 .format(&CHUNK_DIGEST_FORMAT
)
211 pub const NODE_SCHEMA
: Schema
= StringSchema
::new("Node name (or 'localhost')")
212 .format(&ApiStringFormat
::VerifyFn(|node
| {
213 if node
== "localhost" || node
== proxmox
::tools
::nodename() {
216 bail
!("no such node '{}'", node
);
221 pub const SEARCH_DOMAIN_SCHEMA
: Schema
=
222 StringSchema
::new("Search domain for host-name lookup.").schema();
224 pub const FIRST_DNS_SERVER_SCHEMA
: Schema
=
225 StringSchema
::new("First name server IP address.")
229 pub const SECOND_DNS_SERVER_SCHEMA
: Schema
=
230 StringSchema
::new("Second name server IP address.")
234 pub const THIRD_DNS_SERVER_SCHEMA
: Schema
=
235 StringSchema
::new("Third name server IP address.")
239 pub const IP_V4_SCHEMA
: Schema
=
240 StringSchema
::new("IPv4 address.")
241 .format(&IP_V4_FORMAT
)
245 pub const IP_V6_SCHEMA
: Schema
=
246 StringSchema
::new("IPv6 address.")
247 .format(&IP_V6_FORMAT
)
251 pub const IP_SCHEMA
: Schema
=
252 StringSchema
::new("IP (IPv4 or IPv6) address.")
257 pub const CIDR_V4_SCHEMA
: Schema
=
258 StringSchema
::new("IPv4 address with netmask (CIDR notation).")
259 .format(&CIDR_V4_FORMAT
)
263 pub const CIDR_V6_SCHEMA
: Schema
=
264 StringSchema
::new("IPv6 address with netmask (CIDR notation).")
265 .format(&CIDR_V6_FORMAT
)
269 pub const CIDR_SCHEMA
: Schema
=
270 StringSchema
::new("IP address (IPv4 or IPv6) with netmask (CIDR notation).")
271 .format(&CIDR_FORMAT
)
275 pub const TIME_ZONE_SCHEMA
: Schema
= StringSchema
::new(
276 "Time zone. The file '/usr/share/zoneinfo/zone.tab' contains the list of valid names.")
277 .format(&SINGLE_LINE_COMMENT_FORMAT
)
282 pub const ACL_PATH_SCHEMA
: Schema
= StringSchema
::new(
283 "Access control path.")
284 .format(&ACL_PATH_FORMAT
)
289 pub const ACL_PROPAGATE_SCHEMA
: Schema
= BooleanSchema
::new(
290 "Allow to propagate (inherit) permissions.")
294 pub const ACL_UGID_TYPE_SCHEMA
: Schema
= StringSchema
::new(
295 "Type of 'ugid' property.")
296 .format(&ApiStringFormat
::Enum(&[
297 EnumEntry
::new("user", "User"),
298 EnumEntry
::new("group", "Group")]))
304 schema
: ACL_PROPAGATE_SCHEMA
,
307 schema
: ACL_PATH_SCHEMA
,
310 schema
: ACL_UGID_TYPE_SCHEMA
,
314 description
: "User or Group ID.",
321 #[derive(Serialize, Deserialize)]
323 pub struct AclListItem
{
326 pub ugid_type
: String
,
331 pub const BACKUP_ARCHIVE_NAME_SCHEMA
: Schema
=
332 StringSchema
::new("Backup archive name.")
333 .format(&PROXMOX_SAFE_ID_FORMAT
)
336 pub const BACKUP_TYPE_SCHEMA
: Schema
=
337 StringSchema
::new("Backup type.")
338 .format(&ApiStringFormat
::Enum(&[
339 EnumEntry
::new("vm", "Virtual Machine Backup"),
340 EnumEntry
::new("ct", "Container Backup"),
341 EnumEntry
::new("host", "Host Backup")]))
344 pub const BACKUP_ID_SCHEMA
: Schema
=
345 StringSchema
::new("Backup ID.")
346 .format(&BACKUP_ID_FORMAT
)
349 pub const BACKUP_TIME_SCHEMA
: Schema
=
350 IntegerSchema
::new("Backup time (Unix epoch.)")
351 .minimum(1_547_797_308)
354 pub const UPID_SCHEMA
: Schema
= StringSchema
::new("Unique Process/Task ID.")
358 pub const DATASTORE_SCHEMA
: Schema
= StringSchema
::new("Datastore name.")
359 .format(&PROXMOX_SAFE_ID_FORMAT
)
364 pub const DATASTORE_MAP_SCHEMA
: Schema
= StringSchema
::new("Datastore mapping.")
365 .format(&DATASTORE_MAP_FORMAT
)
368 .type_text("(<source>=)?<target>")
371 pub const DATASTORE_MAP_ARRAY_SCHEMA
: Schema
= ArraySchema
::new(
372 "Datastore mapping list.", &DATASTORE_MAP_SCHEMA
)
375 pub const DATASTORE_MAP_LIST_SCHEMA
: Schema
= StringSchema
::new(
376 "A list of Datastore mappings (or single datastore), comma separated. \
377 For example 'a=b,e' maps the source datastore 'a' to target 'b and \
378 all other sources to the default 'e'. If no default is given, only the \
379 specified sources are mapped.")
380 .format(&ApiStringFormat
::PropertyString(&DATASTORE_MAP_ARRAY_SCHEMA
))
383 pub const MEDIA_SET_UUID_SCHEMA
: Schema
=
384 StringSchema
::new("MediaSet Uuid (We use the all-zero Uuid to reseve an empty media for a specific pool).")
385 .format(&UUID_FORMAT
)
388 pub const MEDIA_UUID_SCHEMA
: Schema
=
389 StringSchema
::new("Media Uuid.")
390 .format(&UUID_FORMAT
)
393 pub const SYNC_SCHEDULE_SCHEMA
: Schema
= StringSchema
::new(
394 "Run sync job at specified schedule.")
395 .format(&ApiStringFormat
::VerifyFn(crate::tools
::systemd
::time
::verify_calendar_event
))
396 .type_text("<calendar-event>")
399 pub const GC_SCHEDULE_SCHEMA
: Schema
= StringSchema
::new(
400 "Run garbage collection job at specified schedule.")
401 .format(&ApiStringFormat
::VerifyFn(crate::tools
::systemd
::time
::verify_calendar_event
))
402 .type_text("<calendar-event>")
405 pub const PRUNE_SCHEDULE_SCHEMA
: Schema
= StringSchema
::new(
406 "Run prune job at specified schedule.")
407 .format(&ApiStringFormat
::VerifyFn(crate::tools
::systemd
::time
::verify_calendar_event
))
408 .type_text("<calendar-event>")
411 pub const VERIFICATION_SCHEDULE_SCHEMA
: Schema
= StringSchema
::new(
412 "Run verify job at specified schedule.")
413 .format(&ApiStringFormat
::VerifyFn(crate::tools
::systemd
::time
::verify_calendar_event
))
414 .type_text("<calendar-event>")
417 pub const REMOTE_ID_SCHEMA
: Schema
= StringSchema
::new("Remote ID.")
418 .format(&PROXMOX_SAFE_ID_FORMAT
)
423 pub const JOB_ID_SCHEMA
: Schema
= StringSchema
::new("Job ID.")
424 .format(&PROXMOX_SAFE_ID_FORMAT
)
429 pub const REMOVE_VANISHED_BACKUPS_SCHEMA
: Schema
= BooleanSchema
::new(
430 "Delete vanished backups. This remove the local copy if the remote backup was deleted.")
434 pub const IGNORE_VERIFIED_BACKUPS_SCHEMA
: Schema
= BooleanSchema
::new(
435 "Do not verify backups that are already verified if their verification is not outdated.")
439 pub const VERIFICATION_OUTDATED_AFTER_SCHEMA
: Schema
= IntegerSchema
::new(
440 "Days after that a verification becomes outdated")
444 pub const SINGLE_LINE_COMMENT_SCHEMA
: Schema
= StringSchema
::new("Comment (single line).")
445 .format(&SINGLE_LINE_COMMENT_FORMAT
)
448 pub const HOSTNAME_SCHEMA
: Schema
= StringSchema
::new("Hostname (as defined in RFC1123).")
449 .format(&HOSTNAME_FORMAT
)
452 pub const DNS_NAME_OR_IP_SCHEMA
: Schema
= StringSchema
::new("DNS name or IP address.")
453 .format(&DNS_NAME_OR_IP_FORMAT
)
456 pub const SUBSCRIPTION_KEY_SCHEMA
: Schema
= StringSchema
::new("Proxmox Backup Server subscription key.")
457 .format(&SUBSCRIPTION_KEY_FORMAT
)
462 pub const BLOCKDEVICE_NAME_SCHEMA
: Schema
= StringSchema
::new("Block device name (/sys/block/<name>).")
463 .format(&BLOCKDEVICE_NAME_FORMAT
)
468 // Complex type definitions
473 schema
: DATASTORE_SCHEMA
,
477 schema
: SINGLE_LINE_COMMENT_SCHEMA
,
481 #[derive(Serialize, Deserialize)]
482 #[serde(rename_all="kebab-case")]
483 /// Basic information about a datastore.
484 pub struct DataStoreListItem
{
486 pub comment
: Option
<String
>,
492 schema
: BACKUP_TYPE_SCHEMA
,
495 schema
: BACKUP_ID_SCHEMA
,
498 schema
: BACKUP_TIME_SCHEMA
,
505 schema
: BACKUP_ARCHIVE_NAME_SCHEMA
514 #[derive(Serialize, Deserialize)]
515 #[serde(rename_all="kebab-case")]
516 /// Basic information about a backup group.
517 pub struct GroupListItem
{
518 pub backup_type
: String
, // enum
519 pub backup_id
: String
,
520 pub last_backup
: i64,
521 /// Number of contained snapshots
522 pub backup_count
: u64,
523 /// List of contained archive files.
524 pub files
: Vec
<String
>,
525 /// The owner of group
526 #[serde(skip_serializing_if="Option::is_none")]
527 pub owner
: Option
<Authid
>,
531 #[derive(Debug, Copy, Clone, PartialEq, Serialize, Deserialize)]
532 #[serde(rename_all = "lowercase")]
533 /// Result of a verify operation.
534 pub enum VerifyState
{
535 /// Verification was successful
537 /// Verification reported one or more errors
551 #[derive(Serialize, Deserialize)]
553 pub struct SnapshotVerifyState
{
554 /// UPID of the verify task
556 /// State of the verification. Enum.
557 pub state
: VerifyState
,
563 schema
: BACKUP_TYPE_SCHEMA
,
566 schema
: BACKUP_ID_SCHEMA
,
569 schema
: BACKUP_TIME_SCHEMA
,
572 schema
: SINGLE_LINE_COMMENT_SCHEMA
,
576 type: SnapshotVerifyState
,
585 schema
: BACKUP_ARCHIVE_NAME_SCHEMA
594 #[derive(Serialize, Deserialize)]
595 #[serde(rename_all="kebab-case")]
596 /// Basic information about backup snapshot.
597 pub struct SnapshotListItem
{
598 pub backup_type
: String
, // enum
599 pub backup_id
: String
,
600 pub backup_time
: i64,
601 /// The first line from manifest "notes"
602 #[serde(skip_serializing_if="Option::is_none")]
603 pub comment
: Option
<String
>,
604 /// The result of the last run verify task
605 #[serde(skip_serializing_if="Option::is_none")]
606 pub verification
: Option
<SnapshotVerifyState
>,
607 /// Fingerprint of encryption key
608 #[serde(skip_serializing_if="Option::is_none")]
609 pub fingerprint
: Option
<Fingerprint
>,
610 /// List of contained archive files.
611 pub files
: Vec
<BackupContent
>,
612 /// Overall snapshot size (sum of all archive sizes).
613 #[serde(skip_serializing_if="Option::is_none")]
614 pub size
: Option
<u64>,
615 /// The owner of the snapshots group
616 #[serde(skip_serializing_if="Option::is_none")]
617 pub owner
: Option
<Authid
>,
623 schema
: BACKUP_TYPE_SCHEMA
,
626 schema
: BACKUP_ID_SCHEMA
,
629 schema
: BACKUP_TIME_SCHEMA
,
633 #[derive(Serialize, Deserialize)]
634 #[serde(rename_all="kebab-case")]
636 pub struct PruneListItem
{
637 pub backup_type
: String
, // enum
638 pub backup_id
: String
,
639 pub backup_time
: i64,
644 pub const PRUNE_SCHEMA_KEEP_DAILY
: Schema
= IntegerSchema
::new(
645 "Number of daily backups to keep.")
649 pub const PRUNE_SCHEMA_KEEP_HOURLY
: Schema
= IntegerSchema
::new(
650 "Number of hourly backups to keep.")
654 pub const PRUNE_SCHEMA_KEEP_LAST
: Schema
= IntegerSchema
::new(
655 "Number of backups to keep.")
659 pub const PRUNE_SCHEMA_KEEP_MONTHLY
: Schema
= IntegerSchema
::new(
660 "Number of monthly backups to keep.")
664 pub const PRUNE_SCHEMA_KEEP_WEEKLY
: Schema
= IntegerSchema
::new(
665 "Number of weekly backups to keep.")
669 pub const PRUNE_SCHEMA_KEEP_YEARLY
: Schema
= IntegerSchema
::new(
670 "Number of yearly backups to keep.")
677 schema
: BACKUP_ARCHIVE_NAME_SCHEMA
,
685 #[derive(Serialize, Deserialize)]
686 #[serde(rename_all="kebab-case")]
687 /// Basic information about archive files inside a backup snapshot.
688 pub struct BackupContent
{
689 pub filename
: String
,
690 /// Info if file is encrypted, signed, or neither.
691 #[serde(skip_serializing_if="Option::is_none")]
692 pub crypt_mode
: Option
<CryptMode
>,
693 /// Archive size (from backup manifest).
694 #[serde(skip_serializing_if="Option::is_none")]
695 pub size
: Option
<u64>,
706 #[derive(Clone, Serialize, Deserialize)]
707 #[serde(rename_all="kebab-case")]
708 /// Garbage collection status.
709 pub struct GarbageCollectionStatus
{
710 pub upid
: Option
<String
>,
711 /// Number of processed index files.
712 pub index_file_count
: usize,
713 /// Sum of bytes referred by index files.
714 pub index_data_bytes
: u64,
715 /// Bytes used on disk.
717 /// Chunks used on disk.
718 pub disk_chunks
: usize,
719 /// Sum of removed bytes.
720 pub removed_bytes
: u64,
721 /// Number of removed chunks.
722 pub removed_chunks
: usize,
723 /// Sum of pending bytes (pending removal - kept for safety).
724 pub pending_bytes
: u64,
725 /// Number of pending chunks (pending removal - kept for safety).
726 pub pending_chunks
: usize,
727 /// Number of chunks marked as .bad by verify that have been removed by GC.
728 pub removed_bad
: usize,
729 /// Number of chunks still marked as .bad after garbage collection.
730 pub still_bad
: usize,
733 impl Default
for GarbageCollectionStatus
{
734 fn default() -> Self {
735 GarbageCollectionStatus
{
753 #[derive(Serialize, Deserialize)]
754 /// Storage space usage information.
755 pub struct StorageStatus
{
756 /// Total space (bytes).
758 /// Used space (bytes).
760 /// Available space (bytes).
765 #[derive(Serialize, Deserialize, Default)]
766 /// Backup Type group/snapshot counts.
767 pub struct TypeCounts
{
768 /// The number of groups of the type.
770 /// The number of snapshots of the type.
794 #[derive(Serialize, Deserialize, Default)]
795 /// Counts of groups/snapshots per BackupType.
797 /// The counts for CT backups
798 pub ct
: Option
<TypeCounts
>,
799 /// The counts for Host backups
800 pub host
: Option
<TypeCounts
>,
801 /// The counts for VM backups
802 pub vm
: Option
<TypeCounts
>,
803 /// The counts for other backup types
804 pub other
: Option
<TypeCounts
>,
810 type: GarbageCollectionStatus
,
819 #[derive(Serialize, Deserialize)]
820 #[serde(rename_all="kebab-case")]
821 /// Overall Datastore status and useful information.
822 pub struct DataStoreStatus
{
823 /// Total space (bytes).
825 /// Used space (bytes).
827 /// Available space (bytes).
829 /// Status of last GC
830 #[serde(skip_serializing_if="Option::is_none")]
831 pub gc_status
: Option
<GarbageCollectionStatus
>,
832 /// Group/Snapshot counts
833 #[serde(skip_serializing_if="Option::is_none")]
834 pub counts
: Option
<Counts
>,
839 upid
: { schema: UPID_SCHEMA }
,
840 user
: { type: Authid }
,
843 #[derive(Serialize, Deserialize)]
845 pub struct TaskListItem
{
847 /// The node name where the task is running on.
851 /// The task start time (Epoch)
853 /// The task start time (Epoch)
855 /// Worker type (arbitrary ASCII string)
856 pub worker_type
: String
,
857 /// Worker ID (arbitrary ASCII string)
858 pub worker_id
: Option
<String
>,
859 /// The authenticated entity who started the task
861 /// The task end time (Epoch)
862 #[serde(skip_serializing_if="Option::is_none")]
863 pub endtime
: Option
<i64>,
865 #[serde(skip_serializing_if="Option::is_none")]
866 pub status
: Option
<String
>,
869 impl From
<crate::server
::TaskListInfo
> for TaskListItem
{
870 fn from(info
: crate::server
::TaskListInfo
) -> Self {
871 let (endtime
, status
) = info
873 .map_or_else(|| (None
, None
), |a
| (Some(a
.endtime()), Some(a
.to_string())));
877 node
: "localhost".to_string(),
878 pid
: info
.upid
.pid
as i64,
879 pstart
: info
.upid
.pstart
,
880 starttime
: info
.upid
.starttime
,
881 worker_type
: info
.upid
.worker_type
,
882 worker_id
: info
.upid
.worker_id
,
883 user
: info
.upid
.auth_id
,
891 #[derive(Eq, PartialEq, Debug, Serialize, Deserialize)]
892 #[serde(rename_all = "lowercase")]
893 pub enum TaskStateType
{
905 #[derive(Debug, Copy, Clone, PartialEq, Serialize, Deserialize)]
906 #[serde(rename_all = "lowercase")]
907 /// Node Power command type.
908 pub enum NodePowerCommand
{
909 /// Restart the server
911 /// Shutdown the server
916 #[derive(Debug, Copy, Clone, PartialEq, Serialize, Deserialize)]
917 #[serde(rename_all = "lowercase")]
918 /// Interface configuration method
919 pub enum NetworkConfigMethod
{
920 /// Configuration is done manually using other tools
922 /// Define interfaces with statically allocated addresses.
924 /// Obtain an address via DHCP
926 /// Define the loopback interface.
931 #[derive(Debug, Copy, Clone, PartialEq, Serialize, Deserialize)]
932 #[serde(rename_all = "kebab-case")]
933 #[allow(non_camel_case_types)]
936 pub enum LinuxBondMode
{
937 /// Round-robin policy
939 /// Active-backup policy
945 /// IEEE 802.3ad Dynamic link aggregation
946 #[serde(rename = "802.3ad")]
948 /// Adaptive transmit load balancing
950 /// Adaptive load balancing
955 #[derive(Debug, Copy, Clone, PartialEq, Serialize, Deserialize)]
956 #[serde(rename_all = "kebab-case")]
957 #[allow(non_camel_case_types)]
959 /// Bond Transmit Hash Policy for LACP (802.3ad)
960 pub enum BondXmitHashPolicy
{
964 #[serde(rename = "layer2+3")]
967 #[serde(rename = "layer3+4")]
972 #[derive(Debug, Copy, Clone, PartialEq, Serialize, Deserialize)]
973 #[serde(rename_all = "lowercase")]
974 /// Network interface type
975 pub enum NetworkInterfaceType
{
978 /// Physical Ethernet device
984 /// Linux VLAN (eth.10)
986 /// Interface Alias (eth:1)
988 /// Unknown interface type
992 pub const NETWORK_INTERFACE_NAME_SCHEMA
: Schema
= StringSchema
::new("Network interface name.")
993 .format(&NETWORK_INTERFACE_FORMAT
)
995 .max_length(libc
::IFNAMSIZ
-1)
998 pub const NETWORK_INTERFACE_ARRAY_SCHEMA
: Schema
= ArraySchema
::new(
999 "Network interface list.", &NETWORK_INTERFACE_NAME_SCHEMA
)
1002 pub const NETWORK_INTERFACE_LIST_SCHEMA
: Schema
= StringSchema
::new(
1003 "A list of network devices, comma separated.")
1004 .format(&ApiStringFormat
::PropertyString(&NETWORK_INTERFACE_ARRAY_SCHEMA
))
1010 schema
: NETWORK_INTERFACE_NAME_SCHEMA
,
1013 type: NetworkInterfaceType
,
1016 type: NetworkConfigMethod
,
1020 type: NetworkConfigMethod
,
1024 schema
: CIDR_V4_SCHEMA
,
1028 schema
: CIDR_V6_SCHEMA
,
1032 schema
: IP_V4_SCHEMA
,
1036 schema
: IP_V6_SCHEMA
,
1040 description
: "Option list (inet)",
1043 description
: "Optional attribute line.",
1048 description
: "Option list (inet6)",
1051 description
: "Optional attribute line.",
1056 description
: "Comments (inet, may span multiple lines)",
1061 description
: "Comments (inet6, may span multiple lines)",
1066 schema
: NETWORK_INTERFACE_ARRAY_SCHEMA
,
1070 schema
: NETWORK_INTERFACE_ARRAY_SCHEMA
,
1074 type: LinuxBondMode
,
1078 schema
: NETWORK_INTERFACE_NAME_SCHEMA
,
1081 bond_xmit_hash_policy
: {
1082 type: BondXmitHashPolicy
,
1087 #[derive(Debug, Serialize, Deserialize)]
1088 /// Network Interface configuration
1089 pub struct Interface
{
1090 /// Autostart interface
1091 #[serde(rename = "autostart")]
1092 pub autostart
: bool
,
1093 /// Interface is active (UP)
1098 #[serde(rename = "type")]
1099 pub interface_type
: NetworkInterfaceType
,
1100 #[serde(skip_serializing_if="Option::is_none")]
1101 pub method
: Option
<NetworkConfigMethod
>,
1102 #[serde(skip_serializing_if="Option::is_none")]
1103 pub method6
: Option
<NetworkConfigMethod
>,
1104 #[serde(skip_serializing_if="Option::is_none")]
1105 /// IPv4 address with netmask
1106 pub cidr
: Option
<String
>,
1107 #[serde(skip_serializing_if="Option::is_none")]
1109 pub gateway
: Option
<String
>,
1110 #[serde(skip_serializing_if="Option::is_none")]
1111 /// IPv6 address with netmask
1112 pub cidr6
: Option
<String
>,
1113 #[serde(skip_serializing_if="Option::is_none")]
1115 pub gateway6
: Option
<String
>,
1117 #[serde(skip_serializing_if="Vec::is_empty")]
1118 pub options
: Vec
<String
>,
1119 #[serde(skip_serializing_if="Vec::is_empty")]
1120 pub options6
: Vec
<String
>,
1122 #[serde(skip_serializing_if="Option::is_none")]
1123 pub comments
: Option
<String
>,
1124 #[serde(skip_serializing_if="Option::is_none")]
1125 pub comments6
: Option
<String
>,
1127 #[serde(skip_serializing_if="Option::is_none")]
1128 /// Maximum Transmission Unit
1129 pub mtu
: Option
<u64>,
1131 #[serde(skip_serializing_if="Option::is_none")]
1132 pub bridge_ports
: Option
<Vec
<String
>>,
1133 /// Enable bridge vlan support.
1134 #[serde(skip_serializing_if="Option::is_none")]
1135 pub bridge_vlan_aware
: Option
<bool
>,
1137 #[serde(skip_serializing_if="Option::is_none")]
1138 pub slaves
: Option
<Vec
<String
>>,
1139 #[serde(skip_serializing_if="Option::is_none")]
1140 pub bond_mode
: Option
<LinuxBondMode
>,
1141 #[serde(skip_serializing_if="Option::is_none")]
1142 #[serde(rename = "bond-primary")]
1143 pub bond_primary
: Option
<String
>,
1144 pub bond_xmit_hash_policy
: Option
<BondXmitHashPolicy
>,
1150 fn test_cert_fingerprint_schema() -> Result
<(), anyhow
::Error
> {
1152 let schema
= CERT_FINGERPRINT_SHA256_SCHEMA
;
1154 let invalid_fingerprints
= [
1155 "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",
1156 "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",
1157 "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",
1158 "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",
1159 "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",
1160 "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",
1163 for fingerprint
in invalid_fingerprints
.iter() {
1164 if parse_simple_value(fingerprint
, &schema
).is_ok() {
1165 bail
!("test fingerprint '{}' failed - got Ok() while exception an error.", fingerprint
);
1169 let valid_fingerprints
= [
1170 "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",
1171 "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",
1174 for fingerprint
in valid_fingerprints
.iter() {
1175 let v
= match parse_simple_value(fingerprint
, &schema
) {
1178 bail
!("unable to parse fingerprint '{}' - {}", fingerprint
, err
);
1182 if v
!= serde_json
::json
!(fingerprint
) {
1183 bail
!("unable to parse fingerprint '{}' - got wrong value {:?}", fingerprint
, v
);
1191 fn test_proxmox_user_id_schema() -> Result
<(), anyhow
::Error
> {
1192 let invalid_user_ids
= [
1197 "xx x@test", // contains space
1198 "xx\nx@test", // contains control character
1199 "x:xx@test", // contains collon
1200 "xx/x@test", // contains slash
1201 "xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx@test", // too long
1204 for name
in invalid_user_ids
.iter() {
1205 if parse_simple_value(name
, &Userid
::API_SCHEMA
).is_ok() {
1206 bail
!("test userid '{}' failed - got Ok() while exception an error.", name
);
1210 let valid_user_ids
= [
1214 "xxx@_T_E_S_T-it.com",
1215 "x_x-x.x@test-it.com",
1218 for name
in valid_user_ids
.iter() {
1219 let v
= match parse_simple_value(name
, &Userid
::API_SCHEMA
) {
1222 bail
!("unable to parse userid '{}' - {}", name
, err
);
1226 if v
!= serde_json
::json
!(name
) {
1227 bail
!("unable to parse userid '{}' - got wrong value {:?}", name
, v
);
1235 #[derive(Copy, Clone, Serialize, Deserialize)]
1236 #[serde(rename_all = "UPPERCASE")]
1247 #[derive(Copy, Clone, Serialize, Deserialize)]
1248 #[serde(rename_all = "lowercase")]
1249 pub enum RRDTimeFrameResolution
{
1250 /// 1 min => last 70 minutes
1252 /// 30 min => last 35 hours
1254 /// 3 hours => about 8 days
1256 /// 12 hours => last 35 days
1258 /// 1 week => last 490 days
1263 #[derive(Debug, Clone, Serialize, Deserialize)]
1264 #[serde(rename_all = "PascalCase")]
1265 /// Describes a package for which an update is available.
1266 pub struct APTUpdateInfo
{
1268 pub package
: String
,
1271 /// Package architecture
1273 /// Human readable package description
1274 pub description
: String
,
1275 /// New version to be updated to
1276 pub version
: String
,
1277 /// Old version currently installed
1278 pub old_version
: String
,
1281 /// Package priority in human-readable form
1282 pub priority
: String
,
1284 pub section
: String
,
1285 /// URL under which the package's changelog can be retrieved
1286 pub change_log_url
: String
,
1287 /// Custom extra field for additional package information
1288 #[serde(skip_serializing_if="Option::is_none")]
1289 pub extra_info
: Option
<String
>,
1293 #[derive(Debug, Copy, Clone, PartialEq, Serialize, Deserialize)]
1294 #[serde(rename_all = "lowercase")]
1295 /// When do we send notifications
1297 /// Never send notification
1299 /// Send notifications for failed and successful jobs
1301 /// Send notifications for failed jobs only
1321 #[derive(Debug, Serialize, Deserialize)]
1322 /// Datastore notify settings
1323 pub struct DatastoreNotify
{
1324 /// Garbage collection settings
1325 pub gc
: Option
<Notify
>,
1326 /// Verify job setting
1327 pub verify
: Option
<Notify
>,
1328 /// Sync job setting
1329 pub sync
: Option
<Notify
>,
1332 /// An entry in a hierarchy of files for restore and listing.
1334 #[derive(Serialize, Deserialize)]
1335 pub struct ArchiveEntry
{
1336 /// Base64-encoded full path to the file, including the filename
1337 pub filepath
: String
,
1338 /// Displayable filename text for UIs
1340 /// File or directory type of this entry
1341 #[serde(rename = "type")]
1342 pub entry_type
: String
,
1343 /// Is this entry a leaf node, or does it have children (i.e. a directory)?
1345 /// The file size, if entry_type is 'f' (file)
1346 #[serde(skip_serializing_if="Option::is_none")]
1347 pub size
: Option
<u64>,
1348 /// The file "last modified" time stamp, if entry_type is 'f' (file)
1349 #[serde(skip_serializing_if="Option::is_none")]
1350 pub mtime
: Option
<i64>,
1354 pub fn new(filepath
: &[u8], entry_type
: &DirEntryAttribute
) -> Self {
1356 filepath
: base64
::encode(filepath
),
1357 text
: String
::from_utf8_lossy(filepath
.split(|x
| *x
== b'
/'
).last().unwrap())
1359 entry_type
: CatalogEntryType
::from(entry_type
).to_string(),
1360 leaf
: !matches
!(entry_type
, DirEntryAttribute
::Directory { .. }
),
1361 size
: match entry_type
{
1362 DirEntryAttribute
::File { size, .. }
=> Some(*size
),
1365 mtime
: match entry_type
{
1366 DirEntryAttribute
::File { mtime, .. }
=> Some(*mtime
),
1373 pub const DATASTORE_NOTIFY_STRING_SCHEMA
: Schema
= StringSchema
::new(
1374 "Datastore notification setting")
1375 .format(&ApiStringFormat
::PropertyString(&DatastoreNotify
::API_SCHEMA
))
1379 pub const PASSWORD_HINT_SCHEMA
: Schema
= StringSchema
::new("Password hint.")
1380 .format(&SINGLE_LINE_COMMENT_FORMAT
)
1385 #[api(default: "scrypt")]
1386 #[derive(Clone, Copy, Debug, Deserialize, Serialize)]
1387 #[serde(rename_all = "lowercase")]
1388 /// Key derivation function for password protected encryption keys.
1390 /// Do not encrypt the key.
1392 /// Encrypt they key with a password using SCrypt.
1394 /// Encrtypt the Key with a password using PBKDF2
1398 impl Default
for Kdf
{
1400 fn default() -> Self {
1411 schema
: CERT_FINGERPRINT_SHA256_SCHEMA
,
1416 #[derive(Deserialize, Serialize)]
1417 /// Encryption Key Information
1418 pub struct KeyInfo
{
1419 /// Path to key (if stored in a file)
1420 #[serde(skip_serializing_if="Option::is_none")]
1421 pub path
: Option
<String
>,
1423 /// Key creation time
1425 /// Key modification time
1428 #[serde(skip_serializing_if="Option::is_none")]
1429 pub fingerprint
: Option
<String
>,
1431 #[serde(skip_serializing_if="Option::is_none")]
1432 pub hint
: Option
<String
>,
1436 #[derive(Deserialize, Serialize)]
1437 /// RSA public key information
1438 pub struct RsaPubKeyInfo
{
1439 /// Path to key (if stored in a file)
1440 #[serde(skip_serializing_if="Option::is_none")]
1441 pub path
: Option
<String
>,
1443 pub exponent
: String
,
1444 /// Hex-encoded RSA modulus
1445 pub modulus
: String
,
1446 /// Key (modulus) length in bits
1450 impl std
::convert
::TryFrom
<openssl
::rsa
::Rsa
<openssl
::pkey
::Public
>> for RsaPubKeyInfo
{
1451 type Error
= anyhow
::Error
;
1453 fn try_from(value
: openssl
::rsa
::Rsa
<openssl
::pkey
::Public
>) -> Result
<Self, Self::Error
> {
1454 let modulus
= value
.n().to_hex_str()?
.to_string();
1455 let exponent
= value
.e().to_dec_str()?
.to_string();
1456 let length
= value
.size() as usize * 8;
1470 description
: "Estimated time of the next run (UNIX epoch).",
1475 description
: "Result of the last run.",
1480 description
: "Task UPID of the last run.",
1484 "last-run-endtime": {
1485 description
: "Endtime of the last run.",
1491 #[serde(rename_all="kebab-case")]
1492 #[derive(Serialize,Deserialize,Default)]
1493 /// Job Scheduling Status
1494 pub struct JobScheduleStatus
{
1495 #[serde(skip_serializing_if="Option::is_none")]
1496 pub next_run
: Option
<i64>,
1497 #[serde(skip_serializing_if="Option::is_none")]
1498 pub last_run_state
: Option
<String
>,
1499 #[serde(skip_serializing_if="Option::is_none")]
1500 pub last_run_upid
: Option
<String
>,
1501 #[serde(skip_serializing_if="Option::is_none")]
1502 pub last_run_endtime
: Option
<i64>,