1 //! Basic API types used by most of the PBS code.
3 use serde
::{Deserialize, Serialize}
;
6 use proxmox
::api
::schema
::{ApiStringFormat, EnumEntry, IntegerSchema, Schema, StringSchema}
;
7 use proxmox
::const_regex
;
8 use proxmox
::{IPRE, IPRE_BRACKET, IPV4OCTET, IPV4RE, IPV6H16, IPV6LS32, IPV6RE}
;
12 macro_rules
! PROXMOX_SAFE_ID_REGEX_STR { () => { r"(?:[A-Za-z0-9_][A-Za-z0-9._\-]*)" }
; }
16 macro_rules
! BACKUP_ID_RE { () => (r"[A-Za-z0-9_][A-Za-z0-9._\-]*") }
20 macro_rules
! BACKUP_TYPE_RE { () => (r"(?:host|vm|ct)") }
24 macro_rules
! BACKUP_TIME_RE { () => (r"[0-9]{4}
-[0-9]{2}
-[0-9]{2}T
[0-9]{2}
:[0-9]{2}
:[0-9]{2}Z
") }
28 macro_rules! SNAPSHOT_PATH_REGEX_STR {
30 concat!(r"(", BACKUP_TYPE_RE!(), ")/(", BACKUP_ID_RE!(), ")/(", BACKUP_TIME_RE!(), r")")
36 pub use userid::Authid;
37 pub use userid::Userid;
38 pub use userid::{Realm, RealmRef};
39 pub use userid::{Tokenname, TokennameRef};
40 pub use userid::{Username, UsernameRef};
41 pub use userid::{PROXMOX_GROUP_ID_SCHEMA, PROXMOX_TOKEN_ID_SCHEMA, PROXMOX_TOKEN_NAME_SCHEMA};
45 pub use user::{ApiToken, User, UserWithTokens};
47 EMAIL_SCHEMA, ENABLE_USER_SCHEMA, EXPIRE_USER_SCHEMA, FIRST_NAME_SCHEMA, LAST_NAME_SCHEMA,
54 pub use crypto::{CryptMode, Fingerprint};
59 macro_rules! DNS_LABEL { () => (r"(?:[a-zA-Z0-9](?:[a-zA-Z0-9\-]*[a-zA-Z0-9])?)") }
60 macro_rules! DNS_NAME { () => (concat!(r"(?:(?:", DNS_LABEL!() , r"\.)*", DNS_LABEL!(), ")")) }
61 macro_rules! CIDR_V4_REGEX_STR { () => (concat!(r"(?:", IPV4RE!(), r"/\d{1,2})$")) }
62 macro_rules
! CIDR_V6_REGEX_STR { () => (concat!(r"(?:", IPV6RE!(), r"/\d{1,3}
)$
")) }
63 macro_rules! DNS_ALIAS_LABEL { () => (r"(?:[a-zA-Z0-9_](?:[a-zA-Z0-9\-]*[a-zA-Z0-9])?)") }
64 macro_rules! DNS_ALIAS_NAME {
65 () => (concat!(r"(?
:(?
:", DNS_ALIAS_LABEL!() , r"\.)*", DNS_ALIAS_LABEL!(), ")"))
70 pub IP_V4_REGEX = concat!(r"^
", IPV4RE!(), r"$
");
71 pub IP_V6_REGEX = concat!(r"^
", IPV6RE!(), r"$
");
72 pub IP_REGEX = concat!(r"^
", IPRE!(), r"$
");
73 pub CIDR_V4_REGEX = concat!(r"^
", CIDR_V4_REGEX_STR!(), r"$
");
74 pub CIDR_V6_REGEX = concat!(r"^
", CIDR_V6_REGEX_STR!(), r"$
");
75 pub CIDR_REGEX = concat!(r"^
(?
:", CIDR_V4_REGEX_STR!(), "|", CIDR_V6_REGEX_STR!(), r")$
");
76 pub HOSTNAME_REGEX = r"^
(?
:[a
-zA
-Z0
-9](?
:[a
-zA
-Z0
-9\-]*[a
-zA
-Z0
-9])?
)$
";
77 pub DNS_NAME_REGEX = concat!(r"^
", DNS_NAME!(), r"$
");
78 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 SHA256_HEX_REGEX = r"^
[a
-f0
-9]{64}$
"; // fixme: define in common_regex ?
83 pub PASSWORD_REGEX = r"^
[[:^cntrl
:]]*$
"; // everything but control characters
85 pub UUID_REGEX = r"^
[0-9a
-f
]{8}
(?
:-[0-9a
-f
]{4}
){3}
-[0-9a
-f
]{12}$
";
87 pub BACKUP_TYPE_REGEX = concat!(r"^
(", BACKUP_TYPE_RE!(), r")$
");
89 pub BACKUP_ID_REGEX = concat!(r"^
", BACKUP_ID_RE!(), r"$
");
91 pub BACKUP_DATE_REGEX = concat!(r"^
", BACKUP_TIME_RE!() ,r"$
");
93 pub GROUP_PATH_REGEX = concat!(r"^
(", BACKUP_TYPE_RE!(), ")/(", BACKUP_ID_RE!(), r")$
");
95 pub BACKUP_FILE_REGEX = r"^
.*\.([fd
]idx
|blob
)$
";
97 pub SNAPSHOT_PATH_REGEX = concat!(r"^
", SNAPSHOT_PATH_REGEX_STR!(), r"$
");
99 pub FINGERPRINT_SHA256_REGEX = r"^
(?
:[0-9a
-fA
-F
][0-9a
-fA
-F
])(?
::[0-9a
-fA
-F
][0-9a
-fA
-F
]){31}$
";
101 /// Regex for safe identifiers.
104 /// [article](https://dwheeler.com/essays/fixing-unix-linux-filenames.html)
105 /// contains further information why it is reasonable to restict
106 /// names this way. This is not only useful for filenames, but for
107 /// any identifier command line tools work with.
108 pub PROXMOX_SAFE_ID_REGEX = concat!(r"^
", PROXMOX_SAFE_ID_REGEX_STR!(), r"$
");
110 pub SINGLE_LINE_COMMENT_REGEX = r"^
[[:^cntrl
:]]*$
";
112 pub BACKUP_REPO_URL_REGEX = concat!(
114 USER_ID_REGEX_STR!(), "|", APITOKEN_ID_REGEX_STR!(),
116 DNS_NAME!(), "|", IPRE_BRACKET!(),
117 "):)?
(?
:([0-9]{1,5}
):)?
(", PROXMOX_SAFE_ID_REGEX_STR!(), r")$
"
121 pub const IP_V4_FORMAT: ApiStringFormat = ApiStringFormat::Pattern(&IP_V4_REGEX);
122 pub const IP_V6_FORMAT: ApiStringFormat = ApiStringFormat::Pattern(&IP_V6_REGEX);
123 pub const IP_FORMAT: ApiStringFormat = ApiStringFormat::Pattern(&IP_REGEX);
124 pub const CIDR_V4_FORMAT: ApiStringFormat = ApiStringFormat::Pattern(&CIDR_V4_REGEX);
125 pub const CIDR_V6_FORMAT: ApiStringFormat = ApiStringFormat::Pattern(&CIDR_V6_REGEX);
126 pub const CIDR_FORMAT: ApiStringFormat = ApiStringFormat::Pattern(&CIDR_REGEX);
128 pub const BACKUP_ID_SCHEMA: Schema = StringSchema::new("Backup ID
.")
129 .format(&BACKUP_ID_FORMAT)
131 pub const BACKUP_TYPE_SCHEMA: Schema = StringSchema::new("Backup
type.")
132 .format(&ApiStringFormat::Enum(&[
133 EnumEntry::new("vm
", "Virtual Machine Backup
"),
134 EnumEntry::new("ct
", "Container Backup
"),
135 EnumEntry::new("host
", "Host Backup
"),
138 pub const BACKUP_TIME_SCHEMA: Schema = IntegerSchema::new("Backup
time (Unix epoch
.)")
139 .minimum(1_547_797_308)
142 pub const DATASTORE_SCHEMA: Schema = StringSchema::new("Datastore name
.")
143 .format(&PROXMOX_SAFE_ID_FORMAT)
148 pub const FINGERPRINT_SHA256_FORMAT: ApiStringFormat =
149 ApiStringFormat::Pattern(&FINGERPRINT_SHA256_REGEX);
151 pub const CERT_FINGERPRINT_SHA256_SCHEMA: Schema =
152 StringSchema::new("X509 certificate
fingerprint (sha256
).")
153 .format(&FINGERPRINT_SHA256_FORMAT)
156 pub const PRUNE_SCHEMA_KEEP_DAILY: Schema = IntegerSchema::new("Number of daily backups to keep
.")
160 pub const PRUNE_SCHEMA_KEEP_HOURLY: Schema =
161 IntegerSchema::new("Number of hourly backups to keep
.")
165 pub const PRUNE_SCHEMA_KEEP_LAST: Schema = IntegerSchema::new("Number of backups to keep
.")
169 pub const PRUNE_SCHEMA_KEEP_MONTHLY: Schema =
170 IntegerSchema::new("Number of monthly backups to keep
.")
174 pub const PRUNE_SCHEMA_KEEP_WEEKLY: Schema =
175 IntegerSchema::new("Number of weekly backups to keep
.")
179 pub const PRUNE_SCHEMA_KEEP_YEARLY: Schema =
180 IntegerSchema::new("Number of yearly backups to keep
.")
184 pub const PROXMOX_SAFE_ID_FORMAT: ApiStringFormat =
185 ApiStringFormat::Pattern(&PROXMOX_SAFE_ID_REGEX);
187 pub const SINGLE_LINE_COMMENT_FORMAT: ApiStringFormat =
188 ApiStringFormat::Pattern(&SINGLE_LINE_COMMENT_REGEX);
190 pub const SINGLE_LINE_COMMENT_SCHEMA: Schema = StringSchema::new("Comment (single line
).")
191 .format(&SINGLE_LINE_COMMENT_FORMAT)
194 pub const PROXMOX_CONFIG_DIGEST_SCHEMA: Schema = StringSchema::new(
195 "Prevent changes
if current configuration file has different
\
196 SHA256 digest
. This can be used to prevent concurrent
\
199 .format(&PVE_CONFIG_DIGEST_FORMAT)
202 pub const BACKUP_ID_FORMAT: ApiStringFormat = ApiStringFormat::Pattern(&BACKUP_ID_REGEX);
204 /// API schema format definition for repository URLs
205 pub const BACKUP_REPO_URL: ApiStringFormat = ApiStringFormat::Pattern(&BACKUP_REPO_URL_REGEX);
215 #[derive(Clone, Serialize, Deserialize)]
216 #[serde(rename_all = "kebab
-case
")]
217 /// Garbage collection status.
218 pub struct GarbageCollectionStatus {
219 pub upid: Option<String>,
220 /// Number of processed index files.
221 pub index_file_count: usize,
222 /// Sum of bytes referred by index files.
223 pub index_data_bytes: u64,
224 /// Bytes used on disk.
226 /// Chunks used on disk.
227 pub disk_chunks: usize,
228 /// Sum of removed bytes.
229 pub removed_bytes: u64,
230 /// Number of removed chunks.
231 pub removed_chunks: usize,
232 /// Sum of pending bytes (pending removal - kept for safety).
233 pub pending_bytes: u64,
234 /// Number of pending chunks (pending removal - kept for safety).
235 pub pending_chunks: usize,
236 /// Number of chunks marked as .bad by verify that have been removed by GC.
237 pub removed_bad: usize,
238 /// Number of chunks still marked as .bad after garbage collection.
239 pub still_bad: usize,
242 impl Default for GarbageCollectionStatus {
243 fn default() -> Self {
244 GarbageCollectionStatus {
260 pub const PVE_CONFIG_DIGEST_FORMAT: ApiStringFormat = ApiStringFormat::Pattern(&SHA256_HEX_REGEX);
261 pub const CHUNK_DIGEST_FORMAT: ApiStringFormat = ApiStringFormat::Pattern(&SHA256_HEX_REGEX);
263 pub const PASSWORD_FORMAT: ApiStringFormat = ApiStringFormat::Pattern(&PASSWORD_REGEX);
265 pub const UUID_FORMAT: ApiStringFormat = ApiStringFormat::Pattern(&UUID_REGEX);
267 pub const BACKUP_ARCHIVE_NAME_SCHEMA: Schema = StringSchema::new("Backup archive name
.")
268 .format(&PROXMOX_SAFE_ID_FORMAT)
271 // Complex type definitions
276 schema: BACKUP_ARCHIVE_NAME_SCHEMA,
284 #[derive(Serialize, Deserialize)]
285 #[serde(rename_all = "kebab
-case
")]
286 /// Basic information about archive files inside a backup snapshot.
287 pub struct BackupContent {
288 pub filename: String,
289 /// Info if file is encrypted, signed, or neither.
290 #[serde(skip_serializing_if = "Option
::is_none
")]
291 pub crypt_mode: Option<CryptMode>,
292 /// Archive size (from backup manifest).
293 #[serde(skip_serializing_if = "Option
::is_none
")]
294 pub size: Option<u64>,
298 #[derive(Debug, Copy, Clone, PartialEq, Serialize, Deserialize)]
299 #[serde(rename_all = "lowercase
")]
300 /// Result of a verify operation.
301 pub enum VerifyState {
302 /// Verification was successful
304 /// Verification reported one or more errors
318 #[derive(Serialize, Deserialize)]
320 pub struct SnapshotVerifyState {
321 /// UPID of the verify task
323 /// State of the verification. Enum.
324 pub state: VerifyState,
330 schema: BACKUP_TYPE_SCHEMA,
333 schema: BACKUP_ID_SCHEMA,
336 schema: BACKUP_TIME_SCHEMA,
339 schema: SINGLE_LINE_COMMENT_SCHEMA,
343 type: SnapshotVerifyState,
352 schema: BACKUP_ARCHIVE_NAME_SCHEMA
361 #[derive(Serialize, Deserialize)]
362 #[serde(rename_all = "kebab
-case
")]
363 /// Basic information about backup snapshot.
364 pub struct SnapshotListItem {
365 pub backup_type: String, // enum
366 pub backup_id: String,
367 pub backup_time: i64,
368 /// The first line from manifest "notes
"
369 #[serde(skip_serializing_if = "Option
::is_none
")]
370 pub comment: Option<String>,
371 /// The result of the last run verify task
372 #[serde(skip_serializing_if = "Option
::is_none
")]
373 pub verification: Option<SnapshotVerifyState>,
374 /// Fingerprint of encryption key
375 #[serde(skip_serializing_if = "Option
::is_none
")]
376 pub fingerprint: Option<Fingerprint>,
377 /// List of contained archive files.
378 pub files: Vec<BackupContent>,
379 /// Overall snapshot size (sum of all archive sizes).
380 #[serde(skip_serializing_if = "Option
::is_none
")]
381 pub size: Option<u64>,
382 /// The owner of the snapshots group
383 #[serde(skip_serializing_if = "Option
::is_none
")]
384 pub owner: Option<Authid>,
390 schema: BACKUP_TYPE_SCHEMA,
393 schema: BACKUP_ID_SCHEMA,
396 schema: BACKUP_TIME_SCHEMA,
403 schema: BACKUP_ARCHIVE_NAME_SCHEMA
412 #[derive(Serialize, Deserialize)]
413 #[serde(rename_all = "kebab
-case
")]
414 /// Basic information about a backup group.
415 pub struct GroupListItem {
416 pub backup_type: String, // enum
417 pub backup_id: String,
418 pub last_backup: i64,
419 /// Number of contained snapshots
420 pub backup_count: u64,
421 /// List of contained archive files.
422 pub files: Vec<String>,
423 /// The owner of group
424 #[serde(skip_serializing_if = "Option
::is_none
")]
425 pub owner: Option<Authid>,
426 /// The first line from group "notes
"
427 #[serde(skip_serializing_if = "Option
::is_none
")]
428 pub comment: Option<String>,
434 schema: DATASTORE_SCHEMA,
438 schema: SINGLE_LINE_COMMENT_SCHEMA,
442 #[derive(Serialize, Deserialize)]
443 #[serde(rename_all = "kebab
-case
")]
444 /// Basic information about a datastore.
445 pub struct DataStoreListItem {
447 pub comment: Option<String>,
453 schema: BACKUP_TYPE_SCHEMA,
456 schema: BACKUP_ID_SCHEMA,
459 schema: BACKUP_TIME_SCHEMA,
463 #[derive(Serialize, Deserialize)]
464 #[serde(rename_all = "kebab
-case
")]
466 pub struct PruneListItem {
467 pub backup_type: String, // enum
468 pub backup_id: String,
469 pub backup_time: i64,