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}
;
38 pub use file_restore
::*;
40 // File names: may not contain slashes, may not start with "."
41 pub const FILENAME_FORMAT
: ApiStringFormat
= ApiStringFormat
::VerifyFn(|name
| {
42 if name
.starts_with('
.'
) {
43 bail
!("file names may not start with '.'");
45 if name
.contains('
/'
) {
46 bail
!("file names may not contain slashes");
51 macro_rules
! DNS_LABEL { () => (r"(?:[a-zA-Z0-9](?:[a-zA-Z0-9\-]*[a-zA-Z0-9])?)") }
52 macro_rules
! DNS_NAME { () => (concat!(r"(?:(?:", DNS_LABEL!() , r"\.)*", DNS_LABEL!(), ")")) }
54 macro_rules
! CIDR_V4_REGEX_STR { () => (concat!(r"(?:", IPV4RE!(), r"/\d{1,2}
)$
")) }
55 macro_rules! CIDR_V6_REGEX_STR { () => (concat!(r"(?:", IPV6RE!(), r"/\d{1,3})$")) }
58 pub IP_V4_REGEX
= concat
!(r
"^", IPV4RE
!(), r
"$");
59 pub IP_V6_REGEX
= concat
!(r
"^", IPV6RE
!(), r
"$");
60 pub IP_REGEX
= concat
!(r
"^", IPRE
!(), r
"$");
61 pub CIDR_V4_REGEX
= concat
!(r
"^", CIDR_V4_REGEX_STR
!(), r
"$");
62 pub CIDR_V6_REGEX
= concat
!(r
"^", CIDR_V6_REGEX_STR
!(), r
"$");
63 pub CIDR_REGEX
= concat
!(r
"^(?:", CIDR_V4_REGEX_STR
!(), "|", CIDR_V6_REGEX_STR
!(), r
")$");
65 pub SHA256_HEX_REGEX
= r
"^[a-f0-9]{64}$"; // fixme: define in common_regex ?
66 pub SYSTEMD_DATETIME_REGEX
= r
"^\d{4}-\d{2}-\d{2}( \d{2}:\d{2}(:\d{2})?)?$"; // fixme: define in common_regex ?
68 pub PASSWORD_REGEX
= r
"^[[:^cntrl:]]*$"; // everything but control characters
70 /// Regex for safe identifiers.
73 /// [article](https://dwheeler.com/essays/fixing-unix-linux-filenames.html)
74 /// contains further information why it is reasonable to restict
75 /// names this way. This is not only useful for filenames, but for
76 /// any identifier command line tools work with.
77 pub PROXMOX_SAFE_ID_REGEX
= concat
!(r
"^", PROXMOX_SAFE_ID_REGEX_STR
!(), r
"$");
79 /// Regex for verification jobs 'DATASTORE:ACTUAL_JOB_ID'
80 pub VERIFICATION_JOB_WORKER_ID_REGEX
= concat
!(r
"^(", PROXMOX_SAFE_ID_REGEX_STR
!(), r
"):");
81 /// Regex for sync jobs 'REMOTE:REMOTE_DATASTORE:LOCAL_DATASTORE:ACTUAL_JOB_ID'
82 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
"):");
84 pub SINGLE_LINE_COMMENT_REGEX
= r
"^[[:^cntrl:]]*$";
86 pub HOSTNAME_REGEX
= r
"^(?:[a-zA-Z0-9](?:[a-zA-Z0-9\-]*[a-zA-Z0-9])?)$";
88 pub DNS_NAME_REGEX
= concat
!(r
"^", DNS_NAME
!(), r
"$");
90 pub DNS_NAME_OR_IP_REGEX
= concat
!(r
"^(?:", DNS_NAME
!(), "|", IPRE
!(), r
")$");
92 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
")$");
94 pub FINGERPRINT_SHA256_REGEX
= r
"^(?:[0-9a-fA-F][0-9a-fA-F])(?::[0-9a-fA-F][0-9a-fA-F]){31}$";
96 pub ACL_PATH_REGEX
= concat
!(r
"^(?:/|", r
"(?:/", PROXMOX_SAFE_ID_REGEX_STR
!(), ")+", r
")$");
98 pub SUBSCRIPTION_KEY_REGEX
= concat
!(r
"^pbs(?:[cbsp])-[0-9a-f]{10}$");
100 pub BLOCKDEVICE_NAME_REGEX
= r
"^(:?(:?h|s|x?v)d[a-z]+)|(:?nvme\d+n\d+)$";
102 pub ZPOOL_NAME_REGEX
= r
"^[a-zA-Z][a-z0-9A-Z\-_.:]+$";
104 pub UUID_REGEX
= r
"^[0-9a-f]{8}(?:-[0-9a-f]{4}){3}-[0-9a-f]{12}$";
106 pub DATASTORE_MAP_REGEX
= concat
!(r
"(:?", PROXMOX_SAFE_ID_REGEX_STR
!(), r
"=)?", PROXMOX_SAFE_ID_REGEX_STR
!());
109 pub const SYSTEMD_DATETIME_FORMAT
: ApiStringFormat
=
110 ApiStringFormat
::Pattern(&SYSTEMD_DATETIME_REGEX
);
112 pub const IP_V4_FORMAT
: ApiStringFormat
=
113 ApiStringFormat
::Pattern(&IP_V4_REGEX
);
115 pub const IP_V6_FORMAT
: ApiStringFormat
=
116 ApiStringFormat
::Pattern(&IP_V6_REGEX
);
118 pub const IP_FORMAT
: ApiStringFormat
=
119 ApiStringFormat
::Pattern(&IP_REGEX
);
121 pub const PVE_CONFIG_DIGEST_FORMAT
: ApiStringFormat
=
122 ApiStringFormat
::Pattern(&SHA256_HEX_REGEX
);
124 pub const FINGERPRINT_SHA256_FORMAT
: ApiStringFormat
=
125 ApiStringFormat
::Pattern(&FINGERPRINT_SHA256_REGEX
);
127 pub const PROXMOX_SAFE_ID_FORMAT
: ApiStringFormat
=
128 ApiStringFormat
::Pattern(&PROXMOX_SAFE_ID_REGEX
);
130 pub const BACKUP_ID_FORMAT
: ApiStringFormat
=
131 ApiStringFormat
::Pattern(&BACKUP_ID_REGEX
);
133 pub const UUID_FORMAT
: ApiStringFormat
=
134 ApiStringFormat
::Pattern(&UUID_REGEX
);
136 pub const SINGLE_LINE_COMMENT_FORMAT
: ApiStringFormat
=
137 ApiStringFormat
::Pattern(&SINGLE_LINE_COMMENT_REGEX
);
139 pub const HOSTNAME_FORMAT
: ApiStringFormat
=
140 ApiStringFormat
::Pattern(&HOSTNAME_REGEX
);
142 pub const DNS_NAME_FORMAT
: ApiStringFormat
=
143 ApiStringFormat
::Pattern(&DNS_NAME_REGEX
);
145 pub const DNS_NAME_OR_IP_FORMAT
: ApiStringFormat
=
146 ApiStringFormat
::Pattern(&DNS_NAME_OR_IP_REGEX
);
148 pub const PASSWORD_FORMAT
: ApiStringFormat
=
149 ApiStringFormat
::Pattern(&PASSWORD_REGEX
);
151 pub const ACL_PATH_FORMAT
: ApiStringFormat
=
152 ApiStringFormat
::Pattern(&ACL_PATH_REGEX
);
154 pub const NETWORK_INTERFACE_FORMAT
: ApiStringFormat
=
155 ApiStringFormat
::Pattern(&PROXMOX_SAFE_ID_REGEX
);
157 pub const CIDR_V4_FORMAT
: ApiStringFormat
=
158 ApiStringFormat
::Pattern(&CIDR_V4_REGEX
);
160 pub const CIDR_V6_FORMAT
: ApiStringFormat
=
161 ApiStringFormat
::Pattern(&CIDR_V6_REGEX
);
163 pub const CIDR_FORMAT
: ApiStringFormat
=
164 ApiStringFormat
::Pattern(&CIDR_REGEX
);
166 pub const SUBSCRIPTION_KEY_FORMAT
: ApiStringFormat
=
167 ApiStringFormat
::Pattern(&SUBSCRIPTION_KEY_REGEX
);
169 pub const BLOCKDEVICE_NAME_FORMAT
: ApiStringFormat
=
170 ApiStringFormat
::Pattern(&BLOCKDEVICE_NAME_REGEX
);
172 pub const DATASTORE_MAP_FORMAT
: ApiStringFormat
=
173 ApiStringFormat
::Pattern(&DATASTORE_MAP_REGEX
);
175 pub const PASSWORD_SCHEMA
: Schema
= StringSchema
::new("Password.")
176 .format(&PASSWORD_FORMAT
)
181 pub const PBS_PASSWORD_SCHEMA
: Schema
= StringSchema
::new("User Password.")
182 .format(&PASSWORD_FORMAT
)
187 pub const CERT_FINGERPRINT_SHA256_SCHEMA
: Schema
= StringSchema
::new(
188 "X509 certificate fingerprint (sha256)."
190 .format(&FINGERPRINT_SHA256_FORMAT
)
193 pub const TAPE_ENCRYPTION_KEY_FINGERPRINT_SCHEMA
: Schema
= StringSchema
::new(
194 "Tape encryption key fingerprint (sha256)."
196 .format(&FINGERPRINT_SHA256_FORMAT
)
199 pub const PROXMOX_CONFIG_DIGEST_SCHEMA
: Schema
= StringSchema
::new(
200 "Prevent changes if current configuration file has different \
201 SHA256 digest. This can be used to prevent concurrent \
204 .format(&PVE_CONFIG_DIGEST_FORMAT
) .schema();
207 pub const CHUNK_DIGEST_FORMAT
: ApiStringFormat
=
208 ApiStringFormat
::Pattern(&SHA256_HEX_REGEX
);
210 pub const CHUNK_DIGEST_SCHEMA
: Schema
= StringSchema
::new("Chunk digest (SHA256).")
211 .format(&CHUNK_DIGEST_FORMAT
)
214 pub const NODE_SCHEMA
: Schema
= StringSchema
::new("Node name (or 'localhost')")
215 .format(&ApiStringFormat
::VerifyFn(|node
| {
216 if node
== "localhost" || node
== proxmox
::tools
::nodename() {
219 bail
!("no such node '{}'", node
);
224 pub const SEARCH_DOMAIN_SCHEMA
: Schema
=
225 StringSchema
::new("Search domain for host-name lookup.").schema();
227 pub const FIRST_DNS_SERVER_SCHEMA
: Schema
=
228 StringSchema
::new("First name server IP address.")
232 pub const SECOND_DNS_SERVER_SCHEMA
: Schema
=
233 StringSchema
::new("Second name server IP address.")
237 pub const THIRD_DNS_SERVER_SCHEMA
: Schema
=
238 StringSchema
::new("Third name server IP address.")
242 pub const IP_V4_SCHEMA
: Schema
=
243 StringSchema
::new("IPv4 address.")
244 .format(&IP_V4_FORMAT
)
248 pub const IP_V6_SCHEMA
: Schema
=
249 StringSchema
::new("IPv6 address.")
250 .format(&IP_V6_FORMAT
)
254 pub const IP_SCHEMA
: Schema
=
255 StringSchema
::new("IP (IPv4 or IPv6) address.")
260 pub const CIDR_V4_SCHEMA
: Schema
=
261 StringSchema
::new("IPv4 address with netmask (CIDR notation).")
262 .format(&CIDR_V4_FORMAT
)
266 pub const CIDR_V6_SCHEMA
: Schema
=
267 StringSchema
::new("IPv6 address with netmask (CIDR notation).")
268 .format(&CIDR_V6_FORMAT
)
272 pub const CIDR_SCHEMA
: Schema
=
273 StringSchema
::new("IP address (IPv4 or IPv6) with netmask (CIDR notation).")
274 .format(&CIDR_FORMAT
)
278 pub const TIME_ZONE_SCHEMA
: Schema
= StringSchema
::new(
279 "Time zone. The file '/usr/share/zoneinfo/zone.tab' contains the list of valid names.")
280 .format(&SINGLE_LINE_COMMENT_FORMAT
)
285 pub const ACL_PATH_SCHEMA
: Schema
= StringSchema
::new(
286 "Access control path.")
287 .format(&ACL_PATH_FORMAT
)
292 pub const ACL_PROPAGATE_SCHEMA
: Schema
= BooleanSchema
::new(
293 "Allow to propagate (inherit) permissions.")
297 pub const ACL_UGID_TYPE_SCHEMA
: Schema
= StringSchema
::new(
298 "Type of 'ugid' property.")
299 .format(&ApiStringFormat
::Enum(&[
300 EnumEntry
::new("user", "User"),
301 EnumEntry
::new("group", "Group")]))
307 schema
: ACL_PROPAGATE_SCHEMA
,
310 schema
: ACL_PATH_SCHEMA
,
313 schema
: ACL_UGID_TYPE_SCHEMA
,
317 description
: "User or Group ID.",
324 #[derive(Serialize, Deserialize)]
326 pub struct AclListItem
{
329 pub ugid_type
: String
,
334 pub const BACKUP_ARCHIVE_NAME_SCHEMA
: Schema
=
335 StringSchema
::new("Backup archive name.")
336 .format(&PROXMOX_SAFE_ID_FORMAT
)
339 pub const BACKUP_TYPE_SCHEMA
: Schema
=
340 StringSchema
::new("Backup type.")
341 .format(&ApiStringFormat
::Enum(&[
342 EnumEntry
::new("vm", "Virtual Machine Backup"),
343 EnumEntry
::new("ct", "Container Backup"),
344 EnumEntry
::new("host", "Host Backup")]))
347 pub const BACKUP_ID_SCHEMA
: Schema
=
348 StringSchema
::new("Backup ID.")
349 .format(&BACKUP_ID_FORMAT
)
352 pub const BACKUP_TIME_SCHEMA
: Schema
=
353 IntegerSchema
::new("Backup time (Unix epoch.)")
354 .minimum(1_547_797_308)
357 pub const UPID_SCHEMA
: Schema
= StringSchema
::new("Unique Process/Task ID.")
361 pub const DATASTORE_SCHEMA
: Schema
= StringSchema
::new("Datastore name.")
362 .format(&PROXMOX_SAFE_ID_FORMAT
)
367 pub const DATASTORE_MAP_SCHEMA
: Schema
= StringSchema
::new("Datastore mapping.")
368 .format(&DATASTORE_MAP_FORMAT
)
371 .type_text("(<source>=)?<target>")
374 pub const DATASTORE_MAP_ARRAY_SCHEMA
: Schema
= ArraySchema
::new(
375 "Datastore mapping list.", &DATASTORE_MAP_SCHEMA
)
378 pub const DATASTORE_MAP_LIST_SCHEMA
: Schema
= StringSchema
::new(
379 "A list of Datastore mappings (or single datastore), comma separated. \
380 For example 'a=b,e' maps the source datastore 'a' to target 'b and \
381 all other sources to the default 'e'. If no default is given, only the \
382 specified sources are mapped.")
383 .format(&ApiStringFormat
::PropertyString(&DATASTORE_MAP_ARRAY_SCHEMA
))
386 pub const MEDIA_SET_UUID_SCHEMA
: Schema
=
387 StringSchema
::new("MediaSet Uuid (We use the all-zero Uuid to reseve an empty media for a specific pool).")
388 .format(&UUID_FORMAT
)
391 pub const MEDIA_UUID_SCHEMA
: Schema
=
392 StringSchema
::new("Media Uuid.")
393 .format(&UUID_FORMAT
)
396 pub const SYNC_SCHEDULE_SCHEMA
: Schema
= StringSchema
::new(
397 "Run sync job at specified schedule.")
398 .format(&ApiStringFormat
::VerifyFn(crate::tools
::systemd
::time
::verify_calendar_event
))
399 .type_text("<calendar-event>")
402 pub const GC_SCHEDULE_SCHEMA
: Schema
= StringSchema
::new(
403 "Run garbage collection job at specified schedule.")
404 .format(&ApiStringFormat
::VerifyFn(crate::tools
::systemd
::time
::verify_calendar_event
))
405 .type_text("<calendar-event>")
408 pub const PRUNE_SCHEDULE_SCHEMA
: Schema
= StringSchema
::new(
409 "Run prune job at specified schedule.")
410 .format(&ApiStringFormat
::VerifyFn(crate::tools
::systemd
::time
::verify_calendar_event
))
411 .type_text("<calendar-event>")
414 pub const VERIFICATION_SCHEDULE_SCHEMA
: Schema
= StringSchema
::new(
415 "Run verify job at specified schedule.")
416 .format(&ApiStringFormat
::VerifyFn(crate::tools
::systemd
::time
::verify_calendar_event
))
417 .type_text("<calendar-event>")
420 pub const REMOTE_ID_SCHEMA
: Schema
= StringSchema
::new("Remote ID.")
421 .format(&PROXMOX_SAFE_ID_FORMAT
)
426 pub const JOB_ID_SCHEMA
: Schema
= StringSchema
::new("Job ID.")
427 .format(&PROXMOX_SAFE_ID_FORMAT
)
432 pub const REMOVE_VANISHED_BACKUPS_SCHEMA
: Schema
= BooleanSchema
::new(
433 "Delete vanished backups. This remove the local copy if the remote backup was deleted.")
437 pub const IGNORE_VERIFIED_BACKUPS_SCHEMA
: Schema
= BooleanSchema
::new(
438 "Do not verify backups that are already verified if their verification is not outdated.")
442 pub const VERIFICATION_OUTDATED_AFTER_SCHEMA
: Schema
= IntegerSchema
::new(
443 "Days after that a verification becomes outdated")
447 pub const SINGLE_LINE_COMMENT_SCHEMA
: Schema
= StringSchema
::new("Comment (single line).")
448 .format(&SINGLE_LINE_COMMENT_FORMAT
)
451 pub const HOSTNAME_SCHEMA
: Schema
= StringSchema
::new("Hostname (as defined in RFC1123).")
452 .format(&HOSTNAME_FORMAT
)
455 pub const DNS_NAME_OR_IP_SCHEMA
: Schema
= StringSchema
::new("DNS name or IP address.")
456 .format(&DNS_NAME_OR_IP_FORMAT
)
459 pub const SUBSCRIPTION_KEY_SCHEMA
: Schema
= StringSchema
::new("Proxmox Backup Server subscription key.")
460 .format(&SUBSCRIPTION_KEY_FORMAT
)
465 pub const BLOCKDEVICE_NAME_SCHEMA
: Schema
= StringSchema
::new("Block device name (/sys/block/<name>).")
466 .format(&BLOCKDEVICE_NAME_FORMAT
)
471 // Complex type definitions
476 schema
: DATASTORE_SCHEMA
,
480 schema
: SINGLE_LINE_COMMENT_SCHEMA
,
484 #[derive(Serialize, Deserialize)]
485 #[serde(rename_all="kebab-case")]
486 /// Basic information about a datastore.
487 pub struct DataStoreListItem
{
489 pub comment
: Option
<String
>,
495 schema
: BACKUP_TYPE_SCHEMA
,
498 schema
: BACKUP_ID_SCHEMA
,
501 schema
: BACKUP_TIME_SCHEMA
,
508 schema
: BACKUP_ARCHIVE_NAME_SCHEMA
517 #[derive(Serialize, Deserialize)]
518 #[serde(rename_all="kebab-case")]
519 /// Basic information about a backup group.
520 pub struct GroupListItem
{
521 pub backup_type
: String
, // enum
522 pub backup_id
: String
,
523 pub last_backup
: i64,
524 /// Number of contained snapshots
525 pub backup_count
: u64,
526 /// List of contained archive files.
527 pub files
: Vec
<String
>,
528 /// The owner of group
529 #[serde(skip_serializing_if="Option::is_none")]
530 pub owner
: Option
<Authid
>,
534 #[derive(Debug, Copy, Clone, PartialEq, Serialize, Deserialize)]
535 #[serde(rename_all = "lowercase")]
536 /// Result of a verify operation.
537 pub enum VerifyState
{
538 /// Verification was successful
540 /// Verification reported one or more errors
554 #[derive(Serialize, Deserialize)]
556 pub struct SnapshotVerifyState
{
557 /// UPID of the verify task
559 /// State of the verification. Enum.
560 pub state
: VerifyState
,
566 schema
: BACKUP_TYPE_SCHEMA
,
569 schema
: BACKUP_ID_SCHEMA
,
572 schema
: BACKUP_TIME_SCHEMA
,
575 schema
: SINGLE_LINE_COMMENT_SCHEMA
,
579 type: SnapshotVerifyState
,
588 schema
: BACKUP_ARCHIVE_NAME_SCHEMA
597 #[derive(Serialize, Deserialize)]
598 #[serde(rename_all="kebab-case")]
599 /// Basic information about backup snapshot.
600 pub struct SnapshotListItem
{
601 pub backup_type
: String
, // enum
602 pub backup_id
: String
,
603 pub backup_time
: i64,
604 /// The first line from manifest "notes"
605 #[serde(skip_serializing_if="Option::is_none")]
606 pub comment
: Option
<String
>,
607 /// The result of the last run verify task
608 #[serde(skip_serializing_if="Option::is_none")]
609 pub verification
: Option
<SnapshotVerifyState
>,
610 /// Fingerprint of encryption key
611 #[serde(skip_serializing_if="Option::is_none")]
612 pub fingerprint
: Option
<Fingerprint
>,
613 /// List of contained archive files.
614 pub files
: Vec
<BackupContent
>,
615 /// Overall snapshot size (sum of all archive sizes).
616 #[serde(skip_serializing_if="Option::is_none")]
617 pub size
: Option
<u64>,
618 /// The owner of the snapshots group
619 #[serde(skip_serializing_if="Option::is_none")]
620 pub owner
: Option
<Authid
>,
626 schema
: BACKUP_TYPE_SCHEMA
,
629 schema
: BACKUP_ID_SCHEMA
,
632 schema
: BACKUP_TIME_SCHEMA
,
636 #[derive(Serialize, Deserialize)]
637 #[serde(rename_all="kebab-case")]
639 pub struct PruneListItem
{
640 pub backup_type
: String
, // enum
641 pub backup_id
: String
,
642 pub backup_time
: i64,
647 pub const PRUNE_SCHEMA_KEEP_DAILY
: Schema
= IntegerSchema
::new(
648 "Number of daily backups to keep.")
652 pub const PRUNE_SCHEMA_KEEP_HOURLY
: Schema
= IntegerSchema
::new(
653 "Number of hourly backups to keep.")
657 pub const PRUNE_SCHEMA_KEEP_LAST
: Schema
= IntegerSchema
::new(
658 "Number of backups to keep.")
662 pub const PRUNE_SCHEMA_KEEP_MONTHLY
: Schema
= IntegerSchema
::new(
663 "Number of monthly backups to keep.")
667 pub const PRUNE_SCHEMA_KEEP_WEEKLY
: Schema
= IntegerSchema
::new(
668 "Number of weekly backups to keep.")
672 pub const PRUNE_SCHEMA_KEEP_YEARLY
: Schema
= IntegerSchema
::new(
673 "Number of yearly backups to keep.")
680 schema
: BACKUP_ARCHIVE_NAME_SCHEMA
,
688 #[derive(Serialize, Deserialize)]
689 #[serde(rename_all="kebab-case")]
690 /// Basic information about archive files inside a backup snapshot.
691 pub struct BackupContent
{
692 pub filename
: String
,
693 /// Info if file is encrypted, signed, or neither.
694 #[serde(skip_serializing_if="Option::is_none")]
695 pub crypt_mode
: Option
<CryptMode
>,
696 /// Archive size (from backup manifest).
697 #[serde(skip_serializing_if="Option::is_none")]
698 pub size
: Option
<u64>,
709 #[derive(Clone, Serialize, Deserialize)]
710 #[serde(rename_all="kebab-case")]
711 /// Garbage collection status.
712 pub struct GarbageCollectionStatus
{
713 pub upid
: Option
<String
>,
714 /// Number of processed index files.
715 pub index_file_count
: usize,
716 /// Sum of bytes referred by index files.
717 pub index_data_bytes
: u64,
718 /// Bytes used on disk.
720 /// Chunks used on disk.
721 pub disk_chunks
: usize,
722 /// Sum of removed bytes.
723 pub removed_bytes
: u64,
724 /// Number of removed chunks.
725 pub removed_chunks
: usize,
726 /// Sum of pending bytes (pending removal - kept for safety).
727 pub pending_bytes
: u64,
728 /// Number of pending chunks (pending removal - kept for safety).
729 pub pending_chunks
: usize,
730 /// Number of chunks marked as .bad by verify that have been removed by GC.
731 pub removed_bad
: usize,
732 /// Number of chunks still marked as .bad after garbage collection.
733 pub still_bad
: usize,
736 impl Default
for GarbageCollectionStatus
{
737 fn default() -> Self {
738 GarbageCollectionStatus
{
756 #[derive(Serialize, Deserialize)]
757 /// Storage space usage information.
758 pub struct StorageStatus
{
759 /// Total space (bytes).
761 /// Used space (bytes).
763 /// Available space (bytes).
768 #[derive(Serialize, Deserialize, Default)]
769 /// Backup Type group/snapshot counts.
770 pub struct TypeCounts
{
771 /// The number of groups of the type.
773 /// The number of snapshots of the type.
797 #[derive(Serialize, Deserialize, Default)]
798 /// Counts of groups/snapshots per BackupType.
800 /// The counts for CT backups
801 pub ct
: Option
<TypeCounts
>,
802 /// The counts for Host backups
803 pub host
: Option
<TypeCounts
>,
804 /// The counts for VM backups
805 pub vm
: Option
<TypeCounts
>,
806 /// The counts for other backup types
807 pub other
: Option
<TypeCounts
>,
813 type: GarbageCollectionStatus
,
822 #[derive(Serialize, Deserialize)]
823 #[serde(rename_all="kebab-case")]
824 /// Overall Datastore status and useful information.
825 pub struct DataStoreStatus
{
826 /// Total space (bytes).
828 /// Used space (bytes).
830 /// Available space (bytes).
832 /// Status of last GC
833 #[serde(skip_serializing_if="Option::is_none")]
834 pub gc_status
: Option
<GarbageCollectionStatus
>,
835 /// Group/Snapshot counts
836 #[serde(skip_serializing_if="Option::is_none")]
837 pub counts
: Option
<Counts
>,
842 upid
: { schema: UPID_SCHEMA }
,
843 user
: { type: Authid }
,
846 #[derive(Serialize, Deserialize)]
848 pub struct TaskListItem
{
850 /// The node name where the task is running on.
854 /// The task start time (Epoch)
856 /// The task start time (Epoch)
858 /// Worker type (arbitrary ASCII string)
859 pub worker_type
: String
,
860 /// Worker ID (arbitrary ASCII string)
861 pub worker_id
: Option
<String
>,
862 /// The authenticated entity who started the task
864 /// The task end time (Epoch)
865 #[serde(skip_serializing_if="Option::is_none")]
866 pub endtime
: Option
<i64>,
868 #[serde(skip_serializing_if="Option::is_none")]
869 pub status
: Option
<String
>,
872 impl From
<crate::server
::TaskListInfo
> for TaskListItem
{
873 fn from(info
: crate::server
::TaskListInfo
) -> Self {
874 let (endtime
, status
) = info
876 .map_or_else(|| (None
, None
), |a
| (Some(a
.endtime()), Some(a
.to_string())));
880 node
: "localhost".to_string(),
881 pid
: info
.upid
.pid
as i64,
882 pstart
: info
.upid
.pstart
,
883 starttime
: info
.upid
.starttime
,
884 worker_type
: info
.upid
.worker_type
,
885 worker_id
: info
.upid
.worker_id
,
886 user
: info
.upid
.auth_id
,
894 #[derive(Eq, PartialEq, Debug, Serialize, Deserialize)]
895 #[serde(rename_all = "lowercase")]
896 pub enum TaskStateType
{
908 #[derive(Debug, Copy, Clone, PartialEq, Serialize, Deserialize)]
909 #[serde(rename_all = "lowercase")]
910 /// Node Power command type.
911 pub enum NodePowerCommand
{
912 /// Restart the server
914 /// Shutdown the server
919 #[derive(Debug, Copy, Clone, PartialEq, Serialize, Deserialize)]
920 #[serde(rename_all = "lowercase")]
921 /// Interface configuration method
922 pub enum NetworkConfigMethod
{
923 /// Configuration is done manually using other tools
925 /// Define interfaces with statically allocated addresses.
927 /// Obtain an address via DHCP
929 /// Define the loopback interface.
934 #[derive(Debug, Copy, Clone, PartialEq, Serialize, Deserialize)]
935 #[serde(rename_all = "kebab-case")]
936 #[allow(non_camel_case_types)]
939 pub enum LinuxBondMode
{
940 /// Round-robin policy
942 /// Active-backup policy
948 /// IEEE 802.3ad Dynamic link aggregation
949 #[serde(rename = "802.3ad")]
951 /// Adaptive transmit load balancing
953 /// Adaptive load balancing
958 #[derive(Debug, Copy, Clone, PartialEq, Serialize, Deserialize)]
959 #[serde(rename_all = "kebab-case")]
960 #[allow(non_camel_case_types)]
962 /// Bond Transmit Hash Policy for LACP (802.3ad)
963 pub enum BondXmitHashPolicy
{
967 #[serde(rename = "layer2+3")]
970 #[serde(rename = "layer3+4")]
975 #[derive(Debug, Copy, Clone, PartialEq, Serialize, Deserialize)]
976 #[serde(rename_all = "lowercase")]
977 /// Network interface type
978 pub enum NetworkInterfaceType
{
981 /// Physical Ethernet device
987 /// Linux VLAN (eth.10)
989 /// Interface Alias (eth:1)
991 /// Unknown interface type
995 pub const NETWORK_INTERFACE_NAME_SCHEMA
: Schema
= StringSchema
::new("Network interface name.")
996 .format(&NETWORK_INTERFACE_FORMAT
)
998 .max_length(libc
::IFNAMSIZ
-1)
1001 pub const NETWORK_INTERFACE_ARRAY_SCHEMA
: Schema
= ArraySchema
::new(
1002 "Network interface list.", &NETWORK_INTERFACE_NAME_SCHEMA
)
1005 pub const NETWORK_INTERFACE_LIST_SCHEMA
: Schema
= StringSchema
::new(
1006 "A list of network devices, comma separated.")
1007 .format(&ApiStringFormat
::PropertyString(&NETWORK_INTERFACE_ARRAY_SCHEMA
))
1013 schema
: NETWORK_INTERFACE_NAME_SCHEMA
,
1016 type: NetworkInterfaceType
,
1019 type: NetworkConfigMethod
,
1023 type: NetworkConfigMethod
,
1027 schema
: CIDR_V4_SCHEMA
,
1031 schema
: CIDR_V6_SCHEMA
,
1035 schema
: IP_V4_SCHEMA
,
1039 schema
: IP_V6_SCHEMA
,
1043 description
: "Option list (inet)",
1046 description
: "Optional attribute line.",
1051 description
: "Option list (inet6)",
1054 description
: "Optional attribute line.",
1059 description
: "Comments (inet, may span multiple lines)",
1064 description
: "Comments (inet6, may span multiple lines)",
1069 schema
: NETWORK_INTERFACE_ARRAY_SCHEMA
,
1073 schema
: NETWORK_INTERFACE_ARRAY_SCHEMA
,
1077 type: LinuxBondMode
,
1081 schema
: NETWORK_INTERFACE_NAME_SCHEMA
,
1084 bond_xmit_hash_policy
: {
1085 type: BondXmitHashPolicy
,
1090 #[derive(Debug, Serialize, Deserialize)]
1091 /// Network Interface configuration
1092 pub struct Interface
{
1093 /// Autostart interface
1094 #[serde(rename = "autostart")]
1095 pub autostart
: bool
,
1096 /// Interface is active (UP)
1101 #[serde(rename = "type")]
1102 pub interface_type
: NetworkInterfaceType
,
1103 #[serde(skip_serializing_if="Option::is_none")]
1104 pub method
: Option
<NetworkConfigMethod
>,
1105 #[serde(skip_serializing_if="Option::is_none")]
1106 pub method6
: Option
<NetworkConfigMethod
>,
1107 #[serde(skip_serializing_if="Option::is_none")]
1108 /// IPv4 address with netmask
1109 pub cidr
: Option
<String
>,
1110 #[serde(skip_serializing_if="Option::is_none")]
1112 pub gateway
: Option
<String
>,
1113 #[serde(skip_serializing_if="Option::is_none")]
1114 /// IPv6 address with netmask
1115 pub cidr6
: Option
<String
>,
1116 #[serde(skip_serializing_if="Option::is_none")]
1118 pub gateway6
: Option
<String
>,
1120 #[serde(skip_serializing_if="Vec::is_empty")]
1121 pub options
: Vec
<String
>,
1122 #[serde(skip_serializing_if="Vec::is_empty")]
1123 pub options6
: Vec
<String
>,
1125 #[serde(skip_serializing_if="Option::is_none")]
1126 pub comments
: Option
<String
>,
1127 #[serde(skip_serializing_if="Option::is_none")]
1128 pub comments6
: Option
<String
>,
1130 #[serde(skip_serializing_if="Option::is_none")]
1131 /// Maximum Transmission Unit
1132 pub mtu
: Option
<u64>,
1134 #[serde(skip_serializing_if="Option::is_none")]
1135 pub bridge_ports
: Option
<Vec
<String
>>,
1136 /// Enable bridge vlan support.
1137 #[serde(skip_serializing_if="Option::is_none")]
1138 pub bridge_vlan_aware
: Option
<bool
>,
1140 #[serde(skip_serializing_if="Option::is_none")]
1141 pub slaves
: Option
<Vec
<String
>>,
1142 #[serde(skip_serializing_if="Option::is_none")]
1143 pub bond_mode
: Option
<LinuxBondMode
>,
1144 #[serde(skip_serializing_if="Option::is_none")]
1145 #[serde(rename = "bond-primary")]
1146 pub bond_primary
: Option
<String
>,
1147 pub bond_xmit_hash_policy
: Option
<BondXmitHashPolicy
>,
1153 fn test_cert_fingerprint_schema() -> Result
<(), anyhow
::Error
> {
1155 let schema
= CERT_FINGERPRINT_SHA256_SCHEMA
;
1157 let invalid_fingerprints
= [
1158 "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",
1159 "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",
1160 "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",
1161 "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",
1162 "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",
1163 "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",
1166 for fingerprint
in invalid_fingerprints
.iter() {
1167 if parse_simple_value(fingerprint
, &schema
).is_ok() {
1168 bail
!("test fingerprint '{}' failed - got Ok() while exception an error.", fingerprint
);
1172 let valid_fingerprints
= [
1173 "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 "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",
1177 for fingerprint
in valid_fingerprints
.iter() {
1178 let v
= match parse_simple_value(fingerprint
, &schema
) {
1181 bail
!("unable to parse fingerprint '{}' - {}", fingerprint
, err
);
1185 if v
!= serde_json
::json
!(fingerprint
) {
1186 bail
!("unable to parse fingerprint '{}' - got wrong value {:?}", fingerprint
, v
);
1194 fn test_proxmox_user_id_schema() -> Result
<(), anyhow
::Error
> {
1195 let invalid_user_ids
= [
1200 "xx x@test", // contains space
1201 "xx\nx@test", // contains control character
1202 "x:xx@test", // contains collon
1203 "xx/x@test", // contains slash
1204 "xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx@test", // too long
1207 for name
in invalid_user_ids
.iter() {
1208 if parse_simple_value(name
, &Userid
::API_SCHEMA
).is_ok() {
1209 bail
!("test userid '{}' failed - got Ok() while exception an error.", name
);
1213 let valid_user_ids
= [
1217 "xxx@_T_E_S_T-it.com",
1218 "x_x-x.x@test-it.com",
1221 for name
in valid_user_ids
.iter() {
1222 let v
= match parse_simple_value(name
, &Userid
::API_SCHEMA
) {
1225 bail
!("unable to parse userid '{}' - {}", name
, err
);
1229 if v
!= serde_json
::json
!(name
) {
1230 bail
!("unable to parse userid '{}' - got wrong value {:?}", name
, v
);
1238 #[derive(Copy, Clone, Serialize, Deserialize)]
1239 #[serde(rename_all = "UPPERCASE")]
1250 #[derive(Copy, Clone, Serialize, Deserialize)]
1251 #[serde(rename_all = "lowercase")]
1252 pub enum RRDTimeFrameResolution
{
1253 /// 1 min => last 70 minutes
1255 /// 30 min => last 35 hours
1257 /// 3 hours => about 8 days
1259 /// 12 hours => last 35 days
1261 /// 1 week => last 490 days
1266 #[derive(Debug, Clone, Serialize, Deserialize)]
1267 #[serde(rename_all = "PascalCase")]
1268 /// Describes a package for which an update is available.
1269 pub struct APTUpdateInfo
{
1271 pub package
: String
,
1274 /// Package architecture
1276 /// Human readable package description
1277 pub description
: String
,
1278 /// New version to be updated to
1279 pub version
: String
,
1280 /// Old version currently installed
1281 pub old_version
: String
,
1284 /// Package priority in human-readable form
1285 pub priority
: String
,
1287 pub section
: String
,
1288 /// URL under which the package's changelog can be retrieved
1289 pub change_log_url
: String
,
1290 /// Custom extra field for additional package information
1291 #[serde(skip_serializing_if="Option::is_none")]
1292 pub extra_info
: Option
<String
>,
1296 #[derive(Debug, Copy, Clone, PartialEq, Serialize, Deserialize)]
1297 #[serde(rename_all = "lowercase")]
1298 /// When do we send notifications
1300 /// Never send notification
1302 /// Send notifications for failed and successful jobs
1304 /// Send notifications for failed jobs only
1324 #[derive(Debug, Serialize, Deserialize)]
1325 /// Datastore notify settings
1326 pub struct DatastoreNotify
{
1327 /// Garbage collection settings
1328 pub gc
: Option
<Notify
>,
1329 /// Verify job setting
1330 pub verify
: Option
<Notify
>,
1331 /// Sync job setting
1332 pub sync
: Option
<Notify
>,
1335 /// An entry in a hierarchy of files for restore and listing.
1337 #[derive(Serialize, Deserialize)]
1338 pub struct ArchiveEntry
{
1339 /// Base64-encoded full path to the file, including the filename
1340 pub filepath
: String
,
1341 /// Displayable filename text for UIs
1343 /// File or directory type of this entry
1344 #[serde(rename = "type")]
1345 pub entry_type
: String
,
1346 /// Is this entry a leaf node, or does it have children (i.e. a directory)?
1348 /// The file size, if entry_type is 'f' (file)
1349 #[serde(skip_serializing_if="Option::is_none")]
1350 pub size
: Option
<u64>,
1351 /// The file "last modified" time stamp, if entry_type is 'f' (file)
1352 #[serde(skip_serializing_if="Option::is_none")]
1353 pub mtime
: Option
<i64>,
1357 pub fn new(filepath
: &[u8], entry_type
: Option
<&DirEntryAttribute
>) -> Self {
1359 filepath
: base64
::encode(filepath
),
1360 text
: String
::from_utf8_lossy(filepath
.split(|x
| *x
== b'
/'
).last().unwrap())
1362 entry_type
: match entry_type
{
1363 Some(entry_type
) => CatalogEntryType
::from(entry_type
).to_string(),
1364 None
=> "v".to_owned(),
1366 leaf
: !matches
!(entry_type
, None
| Some(DirEntryAttribute
::Directory { .. }
)),
1367 size
: match entry_type
{
1368 Some(DirEntryAttribute
::File { size, .. }
) => Some(*size
),
1371 mtime
: match entry_type
{
1372 Some(DirEntryAttribute
::File { mtime, .. }
) => Some(*mtime
),
1379 pub const DATASTORE_NOTIFY_STRING_SCHEMA
: Schema
= StringSchema
::new(
1380 "Datastore notification setting")
1381 .format(&ApiStringFormat
::PropertyString(&DatastoreNotify
::API_SCHEMA
))
1385 pub const PASSWORD_HINT_SCHEMA
: Schema
= StringSchema
::new("Password hint.")
1386 .format(&SINGLE_LINE_COMMENT_FORMAT
)
1391 #[api(default: "scrypt")]
1392 #[derive(Clone, Copy, Debug, Deserialize, Serialize)]
1393 #[serde(rename_all = "lowercase")]
1394 /// Key derivation function for password protected encryption keys.
1396 /// Do not encrypt the key.
1398 /// Encrypt they key with a password using SCrypt.
1400 /// Encrtypt the Key with a password using PBKDF2
1404 impl Default
for Kdf
{
1406 fn default() -> Self {
1417 schema
: CERT_FINGERPRINT_SHA256_SCHEMA
,
1422 #[derive(Deserialize, Serialize)]
1423 /// Encryption Key Information
1424 pub struct KeyInfo
{
1425 /// Path to key (if stored in a file)
1426 #[serde(skip_serializing_if="Option::is_none")]
1427 pub path
: Option
<String
>,
1429 /// Key creation time
1431 /// Key modification time
1434 #[serde(skip_serializing_if="Option::is_none")]
1435 pub fingerprint
: Option
<String
>,
1437 #[serde(skip_serializing_if="Option::is_none")]
1438 pub hint
: Option
<String
>,
1442 #[derive(Deserialize, Serialize)]
1443 /// RSA public key information
1444 pub struct RsaPubKeyInfo
{
1445 /// Path to key (if stored in a file)
1446 #[serde(skip_serializing_if="Option::is_none")]
1447 pub path
: Option
<String
>,
1449 pub exponent
: String
,
1450 /// Hex-encoded RSA modulus
1451 pub modulus
: String
,
1452 /// Key (modulus) length in bits
1456 impl std
::convert
::TryFrom
<openssl
::rsa
::Rsa
<openssl
::pkey
::Public
>> for RsaPubKeyInfo
{
1457 type Error
= anyhow
::Error
;
1459 fn try_from(value
: openssl
::rsa
::Rsa
<openssl
::pkey
::Public
>) -> Result
<Self, Self::Error
> {
1460 let modulus
= value
.n().to_hex_str()?
.to_string();
1461 let exponent
= value
.e().to_dec_str()?
.to_string();
1462 let length
= value
.size() as usize * 8;
1476 description
: "Estimated time of the next run (UNIX epoch).",
1481 description
: "Result of the last run.",
1486 description
: "Task UPID of the last run.",
1490 "last-run-endtime": {
1491 description
: "Endtime of the last run.",
1497 #[serde(rename_all="kebab-case")]
1498 #[derive(Serialize,Deserialize,Default)]
1499 /// Job Scheduling Status
1500 pub struct JobScheduleStatus
{
1501 #[serde(skip_serializing_if="Option::is_none")]
1502 pub next_run
: Option
<i64>,
1503 #[serde(skip_serializing_if="Option::is_none")]
1504 pub last_run_state
: Option
<String
>,
1505 #[serde(skip_serializing_if="Option::is_none")]
1506 pub last_run_upid
: Option
<String
>,
1507 #[serde(skip_serializing_if="Option::is_none")]
1508 pub last_run_endtime
: Option
<i64>,