]> git.proxmox.com Git - proxmox-backup.git/blame - pbs-api-types/src/lib.rs
bump version to 2.1.2-1
[proxmox-backup.git] / pbs-api-types / src / lib.rs
CommitLineData
155f657f
WB
1//! Basic API types used by most of the PBS code.
2
c23192d3 3use serde::{Deserialize, Serialize};
8cc3760e 4use anyhow::bail;
c23192d3 5
6ef1b649
WB
6use proxmox_schema::{
7 api, const_regex, ApiStringFormat, ApiType, ArraySchema, Schema, StringSchema, ReturnType,
8};
75f83c6a 9use proxmox::{IPRE, IPRE_BRACKET, IPV4OCTET, IPV4RE, IPV6H16, IPV6LS32, IPV6RE};
15cc41b6 10use proxmox_time::parse_daily_duration;
86fb3877 11
bfff4eaa
WB
12#[rustfmt::skip]
13#[macro_export]
14macro_rules! PROXMOX_SAFE_ID_REGEX_STR { () => { r"(?:[A-Za-z0-9_][A-Za-z0-9._\-]*)" }; }
15
16#[rustfmt::skip]
17#[macro_export]
18macro_rules! BACKUP_ID_RE { () => (r"[A-Za-z0-9_][A-Za-z0-9._\-]*") }
19
20#[rustfmt::skip]
21#[macro_export]
22macro_rules! BACKUP_TYPE_RE { () => (r"(?:host|vm|ct)") }
23
24#[rustfmt::skip]
25#[macro_export]
26macro_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") }
27
28#[rustfmt::skip]
29#[macro_export]
30macro_rules! SNAPSHOT_PATH_REGEX_STR {
31 () => (
32 concat!(r"(", BACKUP_TYPE_RE!(), ")/(", BACKUP_ID_RE!(), ")/(", BACKUP_TIME_RE!(), r")")
33 );
34}
35
8cc3760e
DM
36mod acl;
37pub use acl::*;
38
39mod datastore;
40pub use datastore::*;
41
a58a5cf7
TL
42mod human_byte;
43pub use human_byte::HumanByte;
44
e3619d41
DM
45mod jobs;
46pub use jobs::*;
47
45d5d873
DM
48mod key_derivation;
49pub use key_derivation::{Kdf, KeyInfo};
50
6f422880
DM
51mod network;
52pub use network::*;
53
751f6b61
WB
54#[macro_use]
55mod userid;
56pub use userid::Authid;
57pub use userid::Userid;
58pub use userid::{Realm, RealmRef};
59pub use userid::{Tokenname, TokennameRef};
60pub use userid::{Username, UsernameRef};
61pub use userid::{PROXMOX_GROUP_ID_SCHEMA, PROXMOX_TOKEN_ID_SCHEMA, PROXMOX_TOKEN_NAME_SCHEMA};
62
2b7f8dd5
WB
63#[macro_use]
64mod user;
b65dfff5 65pub use user::*;
2b7f8dd5 66
6ef1b649 67pub use proxmox_schema::upid::*;
95f9d67c 68
ea584a75 69mod crypto;
c42a5479 70pub use crypto::{CryptMode, Fingerprint, bytes_as_fingerprint};
ea584a75 71
013b1e8b
WB
72pub mod file_restore;
73
10beed11
DM
74mod openid;
75pub use openid::*;
76
6afdda88
DM
77mod remote;
78pub use remote::*;
79
1ce8e905
DM
80mod tape;
81pub use tape::*;
82
bfd12e87
DM
83mod traffic_control;
84pub use traffic_control::*;
85
8cc3760e
DM
86mod zfs;
87pub use zfs::*;
88
89
75f83c6a
WB
90#[rustfmt::skip]
91#[macro_use]
92mod local_macros {
93 macro_rules! DNS_LABEL { () => (r"(?:[a-zA-Z0-9](?:[a-zA-Z0-9\-]*[a-zA-Z0-9])?)") }
94 macro_rules! DNS_NAME { () => (concat!(r"(?:(?:", DNS_LABEL!() , r"\.)*", DNS_LABEL!(), ")")) }
95 macro_rules! CIDR_V4_REGEX_STR { () => (concat!(r"(?:", IPV4RE!(), r"/\d{1,2})$")) }
96 macro_rules! CIDR_V6_REGEX_STR { () => (concat!(r"(?:", IPV6RE!(), r"/\d{1,3})$")) }
97 macro_rules! DNS_ALIAS_LABEL { () => (r"(?:[a-zA-Z0-9_](?:[a-zA-Z0-9\-]*[a-zA-Z0-9])?)") }
98 macro_rules! DNS_ALIAS_NAME {
99 () => (concat!(r"(?:(?:", DNS_ALIAS_LABEL!() , r"\.)*", DNS_ALIAS_LABEL!(), ")"))
100 }
101}
102
86fb3877 103const_regex! {
75f83c6a
WB
104 pub IP_V4_REGEX = concat!(r"^", IPV4RE!(), r"$");
105 pub IP_V6_REGEX = concat!(r"^", IPV6RE!(), r"$");
106 pub IP_REGEX = concat!(r"^", IPRE!(), r"$");
107 pub CIDR_V4_REGEX = concat!(r"^", CIDR_V4_REGEX_STR!(), r"$");
108 pub CIDR_V6_REGEX = concat!(r"^", CIDR_V6_REGEX_STR!(), r"$");
109 pub CIDR_REGEX = concat!(r"^(?:", CIDR_V4_REGEX_STR!(), "|", CIDR_V6_REGEX_STR!(), r")$");
110 pub HOSTNAME_REGEX = r"^(?:[a-zA-Z0-9](?:[a-zA-Z0-9\-]*[a-zA-Z0-9])?)$";
111 pub DNS_NAME_REGEX = concat!(r"^", DNS_NAME!(), r"$");
112 pub DNS_ALIAS_REGEX = concat!(r"^", DNS_ALIAS_NAME!(), r"$");
113 pub DNS_NAME_OR_IP_REGEX = concat!(r"^(?:", DNS_NAME!(), "|", IPRE!(), r")$");
114
115 pub SHA256_HEX_REGEX = r"^[a-f0-9]{64}$"; // fixme: define in common_regex ?
116
117 pub PASSWORD_REGEX = r"^[[:^cntrl:]]*$"; // everything but control characters
118
119 pub UUID_REGEX = r"^[0-9a-f]{8}(?:-[0-9a-f]{4}){3}-[0-9a-f]{12}$";
120
8cc3760e 121 pub SYSTEMD_DATETIME_REGEX = r"^\d{4}-\d{2}-\d{2}( \d{2}:\d{2}(:\d{2})?)?$"; // fixme: define in common_regex ?
bfff4eaa 122
86fb3877 123 pub FINGERPRINT_SHA256_REGEX = r"^(?:[0-9a-fA-F][0-9a-fA-F])(?::[0-9a-fA-F][0-9a-fA-F]){31}$";
3c430e9a
WB
124
125 /// Regex for safe identifiers.
126 ///
127 /// This
128 /// [article](https://dwheeler.com/essays/fixing-unix-linux-filenames.html)
129 /// contains further information why it is reasonable to restict
130 /// names this way. This is not only useful for filenames, but for
131 /// any identifier command line tools work with.
132 pub PROXMOX_SAFE_ID_REGEX = concat!(r"^", PROXMOX_SAFE_ID_REGEX_STR!(), r"$");
133
134 pub SINGLE_LINE_COMMENT_REGEX = r"^[[:^cntrl:]]*$";
75f83c6a
WB
135
136 pub BACKUP_REPO_URL_REGEX = concat!(
137 r"^^(?:(?:(",
138 USER_ID_REGEX_STR!(), "|", APITOKEN_ID_REGEX_STR!(),
139 ")@)?(",
140 DNS_NAME!(), "|", IPRE_BRACKET!(),
141 "):)?(?:([0-9]{1,5}):)?(", PROXMOX_SAFE_ID_REGEX_STR!(), r")$"
142 );
4c1b7761
WB
143
144 pub BLOCKDEVICE_NAME_REGEX = r"^(:?(:?h|s|x?v)d[a-z]+)|(:?nvme\d+n\d+)$";
8cc3760e 145 pub SUBSCRIPTION_KEY_REGEX = concat!(r"^pbs(?:[cbsp])-[0-9a-f]{10}$");
86fb3877
WB
146}
147
75f83c6a
WB
148pub const IP_V4_FORMAT: ApiStringFormat = ApiStringFormat::Pattern(&IP_V4_REGEX);
149pub const IP_V6_FORMAT: ApiStringFormat = ApiStringFormat::Pattern(&IP_V6_REGEX);
150pub const IP_FORMAT: ApiStringFormat = ApiStringFormat::Pattern(&IP_REGEX);
151pub const CIDR_V4_FORMAT: ApiStringFormat = ApiStringFormat::Pattern(&CIDR_V4_REGEX);
152pub const CIDR_V6_FORMAT: ApiStringFormat = ApiStringFormat::Pattern(&CIDR_V6_REGEX);
153pub const CIDR_FORMAT: ApiStringFormat = ApiStringFormat::Pattern(&CIDR_REGEX);
8cc3760e
DM
154pub const PVE_CONFIG_DIGEST_FORMAT: ApiStringFormat = ApiStringFormat::Pattern(&SHA256_HEX_REGEX);
155pub const PASSWORD_FORMAT: ApiStringFormat = ApiStringFormat::Pattern(&PASSWORD_REGEX);
156pub const UUID_FORMAT: ApiStringFormat = ApiStringFormat::Pattern(&UUID_REGEX);
157pub const BLOCKDEVICE_NAME_FORMAT: ApiStringFormat = ApiStringFormat::Pattern(&BLOCKDEVICE_NAME_REGEX);
158pub const SUBSCRIPTION_KEY_FORMAT: ApiStringFormat = ApiStringFormat::Pattern(&SUBSCRIPTION_KEY_REGEX);
159pub const SYSTEMD_DATETIME_FORMAT: ApiStringFormat = ApiStringFormat::Pattern(&SYSTEMD_DATETIME_REGEX);
160pub const HOSTNAME_FORMAT: ApiStringFormat = ApiStringFormat::Pattern(&HOSTNAME_REGEX);
161
162pub const DNS_ALIAS_FORMAT: ApiStringFormat =
163 ApiStringFormat::Pattern(&DNS_ALIAS_REGEX);
164
bfd12e87
DM
165pub const DAILY_DURATION_FORMAT: ApiStringFormat =
166 ApiStringFormat::VerifyFn(|s| parse_daily_duration(s).map(drop));
167
8cc3760e
DM
168pub const SEARCH_DOMAIN_SCHEMA: Schema =
169 StringSchema::new("Search domain for host-name lookup.").schema();
170
171pub const FIRST_DNS_SERVER_SCHEMA: Schema =
172 StringSchema::new("First name server IP address.")
173 .format(&IP_FORMAT)
174 .schema();
175
176pub const SECOND_DNS_SERVER_SCHEMA: Schema =
177 StringSchema::new("Second name server IP address.")
178 .format(&IP_FORMAT)
179 .schema();
180
181pub const THIRD_DNS_SERVER_SCHEMA: Schema =
182 StringSchema::new("Third name server IP address.")
183 .format(&IP_FORMAT)
184 .schema();
185
186pub const HOSTNAME_SCHEMA: Schema = StringSchema::new("Hostname (as defined in RFC1123).")
187 .format(&HOSTNAME_FORMAT)
188 .schema();
75f83c6a 189
6afdda88
DM
190pub const DNS_NAME_FORMAT: ApiStringFormat =
191 ApiStringFormat::Pattern(&DNS_NAME_REGEX);
192
193pub const DNS_NAME_OR_IP_FORMAT: ApiStringFormat =
194 ApiStringFormat::Pattern(&DNS_NAME_OR_IP_REGEX);
195
196pub const DNS_NAME_OR_IP_SCHEMA: Schema = StringSchema::new("DNS name or IP address.")
197 .format(&DNS_NAME_OR_IP_FORMAT)
198 .schema();
199
8cc3760e
DM
200pub const NODE_SCHEMA: Schema = StringSchema::new("Node name (or 'localhost')")
201 .format(&ApiStringFormat::VerifyFn(|node| {
202 if node == "localhost" || node == proxmox::tools::nodename() {
203 Ok(())
204 } else {
205 bail!("no such node '{}'", node);
206 }
207 }))
ea584a75 208 .schema();
8cc3760e
DM
209
210pub const TIME_ZONE_SCHEMA: Schema = StringSchema::new(
211 "Time zone. The file '/usr/share/zoneinfo/zone.tab' contains the list of valid names.")
212 .format(&SINGLE_LINE_COMMENT_FORMAT)
213 .min_length(2)
214 .max_length(64)
ea584a75
WB
215 .schema();
216
8cc3760e
DM
217pub const BLOCKDEVICE_NAME_SCHEMA: Schema = StringSchema::new("Block device name (/sys/block/<name>).")
218 .format(&BLOCKDEVICE_NAME_FORMAT)
ea584a75 219 .min_length(3)
8cc3760e
DM
220 .max_length(64)
221 .schema();
222
223pub const DISK_ARRAY_SCHEMA: Schema = ArraySchema::new(
224 "Disk name list.", &BLOCKDEVICE_NAME_SCHEMA)
225 .schema();
226
227pub const DISK_LIST_SCHEMA: Schema = StringSchema::new(
228 "A list of disk names, comma separated.")
229 .format(&ApiStringFormat::PropertyString(&DISK_ARRAY_SCHEMA))
230 .schema();
231
232pub const PASSWORD_SCHEMA: Schema = StringSchema::new("Password.")
233 .format(&PASSWORD_FORMAT)
234 .min_length(1)
235 .max_length(1024)
236 .schema();
237
238pub const PBS_PASSWORD_SCHEMA: Schema = StringSchema::new("User Password.")
239 .format(&PASSWORD_FORMAT)
240 .min_length(5)
241 .max_length(64)
ea584a75
WB
242 .schema();
243
21211748
DM
244pub const REALM_ID_SCHEMA: Schema = StringSchema::new("Realm name.")
245 .format(&PROXMOX_SAFE_ID_FORMAT)
246 .min_length(2)
247 .max_length(32)
248 .schema();
249
86fb3877
WB
250pub const FINGERPRINT_SHA256_FORMAT: ApiStringFormat =
251 ApiStringFormat::Pattern(&FINGERPRINT_SHA256_REGEX);
252
253pub const CERT_FINGERPRINT_SHA256_SCHEMA: Schema =
254 StringSchema::new("X509 certificate fingerprint (sha256).")
255 .format(&FINGERPRINT_SHA256_FORMAT)
256 .schema();
3c430e9a
WB
257
258pub const PROXMOX_SAFE_ID_FORMAT: ApiStringFormat =
259 ApiStringFormat::Pattern(&PROXMOX_SAFE_ID_REGEX);
260
261pub const SINGLE_LINE_COMMENT_FORMAT: ApiStringFormat =
262 ApiStringFormat::Pattern(&SINGLE_LINE_COMMENT_REGEX);
263
264pub const SINGLE_LINE_COMMENT_SCHEMA: Schema = StringSchema::new("Comment (single line).")
265 .format(&SINGLE_LINE_COMMENT_FORMAT)
266 .schema();
bfff4eaa 267
8cc3760e
DM
268pub const SUBSCRIPTION_KEY_SCHEMA: Schema = StringSchema::new("Proxmox Backup Server subscription key.")
269 .format(&SUBSCRIPTION_KEY_FORMAT)
270 .min_length(15)
271 .max_length(16)
272 .schema();
273
274pub const SERVICE_ID_SCHEMA: Schema = StringSchema::new("Service ID.")
275 .max_length(256)
276 .schema();
277
2b7f8dd5
WB
278pub const PROXMOX_CONFIG_DIGEST_SCHEMA: Schema = StringSchema::new(
279 "Prevent changes if current configuration file has different \
280 SHA256 digest. This can be used to prevent concurrent \
281 modifications.",
282)
283.format(&PVE_CONFIG_DIGEST_FORMAT)
284.schema();
285
75f83c6a
WB
286/// API schema format definition for repository URLs
287pub const BACKUP_REPO_URL: ApiStringFormat = ApiStringFormat::Pattern(&BACKUP_REPO_URL_REGEX);
288
75f83c6a 289
ea584a75
WB
290// Complex type definitions
291
51ec8a3c
WB
292
293#[api()]
294#[derive(Default, Serialize, Deserialize)]
295/// Storage space usage information.
296pub struct StorageStatus {
297 /// Total space (bytes).
298 pub total: u64,
299 /// Used space (bytes).
300 pub used: u64,
301 /// Available space (bytes).
302 pub avail: u64,
303}
304
eb5e0ae6
WB
305pub const PASSWORD_HINT_SCHEMA: Schema = StringSchema::new("Password hint.")
306 .format(&SINGLE_LINE_COMMENT_FORMAT)
307 .min_length(1)
308 .max_length(64)
309 .schema();
310
311
312#[api]
313#[derive(Deserialize, Serialize)]
314/// RSA public key information
315pub struct RsaPubKeyInfo {
316 /// Path to key (if stored in a file)
317 #[serde(skip_serializing_if="Option::is_none")]
318 pub path: Option<String>,
319 /// RSA exponent
320 pub exponent: String,
321 /// Hex-encoded RSA modulus
322 pub modulus: String,
323 /// Key (modulus) length in bits
324 pub length: usize,
325}
326
327impl std::convert::TryFrom<openssl::rsa::Rsa<openssl::pkey::Public>> for RsaPubKeyInfo {
328 type Error = anyhow::Error;
329
330 fn try_from(value: openssl::rsa::Rsa<openssl::pkey::Public>) -> Result<Self, Self::Error> {
331 let modulus = value.n().to_hex_str()?.to_string();
332 let exponent = value.e().to_dec_str()?.to_string();
333 let length = value.size() as usize * 8;
334
335 Ok(Self {
336 path: None,
337 exponent,
338 modulus,
339 length,
340 })
341 }
342}
7b570c17 343
e3619d41
DM
344#[api()]
345#[derive(Debug, Clone, Serialize, Deserialize)]
346#[serde(rename_all = "PascalCase")]
347/// Describes a package for which an update is available.
348pub struct APTUpdateInfo {
349 /// Package name
350 pub package: String,
351 /// Package title
352 pub title: String,
353 /// Package architecture
354 pub arch: String,
355 /// Human readable package description
356 pub description: String,
357 /// New version to be updated to
358 pub version: String,
359 /// Old version currently installed
360 pub old_version: String,
361 /// Package origin
362 pub origin: String,
363 /// Package priority in human-readable form
364 pub priority: String,
365 /// Package section
366 pub section: String,
367 /// URL under which the package's changelog can be retrieved
368 pub change_log_url: String,
369 /// Custom extra field for additional package information
370 #[serde(skip_serializing_if="Option::is_none")]
371 pub extra_info: Option<String>,
372}
8cc3760e 373
8cc3760e
DM
374
375#[api()]
376#[derive(Debug, Copy, Clone, PartialEq, Serialize, Deserialize)]
377#[serde(rename_all = "lowercase")]
378/// Node Power command type.
379pub enum NodePowerCommand {
380 /// Restart the server
381 Reboot,
382 /// Shutdown the server
383 Shutdown,
384}
81867f05
DM
385
386
387#[api()]
388#[derive(Eq, PartialEq, Debug, Serialize, Deserialize)]
389#[serde(rename_all = "lowercase")]
390pub enum TaskStateType {
391 /// Ok
392 OK,
393 /// Warning
394 Warning,
395 /// Error
396 Error,
397 /// Unknown
398 Unknown,
399}
400
401#[api(
402 properties: {
403 upid: { schema: UPID::API_SCHEMA },
404 },
405)]
406#[derive(Serialize, Deserialize)]
407/// Task properties.
408pub struct TaskListItem {
409 pub upid: String,
410 /// The node name where the task is running on.
411 pub node: String,
412 /// The Unix PID
413 pub pid: i64,
414 /// The task start time (Epoch)
415 pub pstart: u64,
416 /// The task start time (Epoch)
417 pub starttime: i64,
418 /// Worker type (arbitrary ASCII string)
419 pub worker_type: String,
420 /// Worker ID (arbitrary ASCII string)
421 pub worker_id: Option<String>,
422 /// The authenticated entity who started the task
423 pub user: String,
424 /// The task end time (Epoch)
425 #[serde(skip_serializing_if="Option::is_none")]
426 pub endtime: Option<i64>,
427 /// Task end status
428 #[serde(skip_serializing_if="Option::is_none")]
429 pub status: Option<String>,
430}
431
432pub const NODE_TASKS_LIST_TASKS_RETURN_TYPE: ReturnType = ReturnType {
433 optional: false,
434 schema: &ArraySchema::new(
435 "A list of tasks.",
436 &TaskListItem::API_SCHEMA,
437 ).schema(),
438};
d1c3bc53 439
c68fa58a
DM
440#[api()]
441#[derive(Copy, Clone, Serialize, Deserialize)]
442#[serde(rename_all = "UPPERCASE")]
443/// RRD consolidation mode
444pub enum RRDMode {
445 /// Maximum
446 Max,
447 /// Average
448 Average,
449}
450
451#[api()]
452#[derive(Copy, Clone, Serialize, Deserialize)]
453#[serde(rename_all = "lowercase")]
454/// RRD time frame
455pub enum RRDTimeFrame {
456 /// Hour
457 Hour,
458 /// Day
459 Day,
460 /// Week
461 Week,
462 /// Month
463 Month,
464 /// Year
465 Year,
466 /// Decade (10 years)
467 Decade,
468}