]>
Commit | Line | Data |
---|---|---|
155f657f WB |
1 | //! Basic API types used by most of the PBS code. |
2 | ||
c23192d3 | 3 | use serde::{Deserialize, Serialize}; |
8cc3760e | 4 | use anyhow::bail; |
c23192d3 | 5 | |
6ef1b649 WB |
6 | use proxmox_schema::{ |
7 | api, const_regex, ApiStringFormat, ApiType, ArraySchema, Schema, StringSchema, ReturnType, | |
8 | }; | |
75f83c6a | 9 | use proxmox::{IPRE, IPRE_BRACKET, IPV4OCTET, IPV4RE, IPV6H16, IPV6LS32, IPV6RE}; |
15cc41b6 | 10 | use proxmox_time::parse_daily_duration; |
86fb3877 | 11 | |
bfff4eaa WB |
12 | #[rustfmt::skip] |
13 | #[macro_export] | |
14 | macro_rules! PROXMOX_SAFE_ID_REGEX_STR { () => { r"(?:[A-Za-z0-9_][A-Za-z0-9._\-]*)" }; } | |
15 | ||
16 | #[rustfmt::skip] | |
17 | #[macro_export] | |
18 | macro_rules! BACKUP_ID_RE { () => (r"[A-Za-z0-9_][A-Za-z0-9._\-]*") } | |
19 | ||
20 | #[rustfmt::skip] | |
21 | #[macro_export] | |
22 | macro_rules! BACKUP_TYPE_RE { () => (r"(?:host|vm|ct)") } | |
23 | ||
24 | #[rustfmt::skip] | |
25 | #[macro_export] | |
26 | 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") } | |
27 | ||
28 | #[rustfmt::skip] | |
29 | #[macro_export] | |
30 | macro_rules! SNAPSHOT_PATH_REGEX_STR { | |
31 | () => ( | |
32 | concat!(r"(", BACKUP_TYPE_RE!(), ")/(", BACKUP_ID_RE!(), ")/(", BACKUP_TIME_RE!(), r")") | |
33 | ); | |
34 | } | |
35 | ||
8cc3760e DM |
36 | mod acl; |
37 | pub use acl::*; | |
38 | ||
39 | mod datastore; | |
40 | pub use datastore::*; | |
41 | ||
a58a5cf7 TL |
42 | mod human_byte; |
43 | pub use human_byte::HumanByte; | |
44 | ||
e3619d41 DM |
45 | mod jobs; |
46 | pub use jobs::*; | |
47 | ||
45d5d873 DM |
48 | mod key_derivation; |
49 | pub use key_derivation::{Kdf, KeyInfo}; | |
50 | ||
6f422880 DM |
51 | mod network; |
52 | pub use network::*; | |
53 | ||
751f6b61 WB |
54 | #[macro_use] |
55 | mod userid; | |
56 | pub use userid::Authid; | |
57 | pub use userid::Userid; | |
58 | pub use userid::{Realm, RealmRef}; | |
59 | pub use userid::{Tokenname, TokennameRef}; | |
60 | pub use userid::{Username, UsernameRef}; | |
61 | pub use userid::{PROXMOX_GROUP_ID_SCHEMA, PROXMOX_TOKEN_ID_SCHEMA, PROXMOX_TOKEN_NAME_SCHEMA}; | |
62 | ||
2b7f8dd5 WB |
63 | #[macro_use] |
64 | mod user; | |
b65dfff5 | 65 | pub use user::*; |
2b7f8dd5 | 66 | |
6ef1b649 | 67 | pub use proxmox_schema::upid::*; |
95f9d67c | 68 | |
ea584a75 | 69 | mod crypto; |
c42a5479 | 70 | pub use crypto::{CryptMode, Fingerprint, bytes_as_fingerprint}; |
ea584a75 | 71 | |
013b1e8b WB |
72 | pub mod file_restore; |
73 | ||
10beed11 DM |
74 | mod openid; |
75 | pub use openid::*; | |
76 | ||
6afdda88 DM |
77 | mod remote; |
78 | pub use remote::*; | |
79 | ||
1ce8e905 DM |
80 | mod tape; |
81 | pub use tape::*; | |
82 | ||
bfd12e87 DM |
83 | mod traffic_control; |
84 | pub use traffic_control::*; | |
85 | ||
8cc3760e DM |
86 | mod zfs; |
87 | pub use zfs::*; | |
88 | ||
89 | ||
75f83c6a WB |
90 | #[rustfmt::skip] |
91 | #[macro_use] | |
92 | mod 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 | 103 | const_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 |
148 | pub const IP_V4_FORMAT: ApiStringFormat = ApiStringFormat::Pattern(&IP_V4_REGEX); |
149 | pub const IP_V6_FORMAT: ApiStringFormat = ApiStringFormat::Pattern(&IP_V6_REGEX); | |
150 | pub const IP_FORMAT: ApiStringFormat = ApiStringFormat::Pattern(&IP_REGEX); | |
151 | pub const CIDR_V4_FORMAT: ApiStringFormat = ApiStringFormat::Pattern(&CIDR_V4_REGEX); | |
152 | pub const CIDR_V6_FORMAT: ApiStringFormat = ApiStringFormat::Pattern(&CIDR_V6_REGEX); | |
153 | pub const CIDR_FORMAT: ApiStringFormat = ApiStringFormat::Pattern(&CIDR_REGEX); | |
8cc3760e DM |
154 | pub const PVE_CONFIG_DIGEST_FORMAT: ApiStringFormat = ApiStringFormat::Pattern(&SHA256_HEX_REGEX); |
155 | pub const PASSWORD_FORMAT: ApiStringFormat = ApiStringFormat::Pattern(&PASSWORD_REGEX); | |
156 | pub const UUID_FORMAT: ApiStringFormat = ApiStringFormat::Pattern(&UUID_REGEX); | |
157 | pub const BLOCKDEVICE_NAME_FORMAT: ApiStringFormat = ApiStringFormat::Pattern(&BLOCKDEVICE_NAME_REGEX); | |
158 | pub const SUBSCRIPTION_KEY_FORMAT: ApiStringFormat = ApiStringFormat::Pattern(&SUBSCRIPTION_KEY_REGEX); | |
159 | pub const SYSTEMD_DATETIME_FORMAT: ApiStringFormat = ApiStringFormat::Pattern(&SYSTEMD_DATETIME_REGEX); | |
160 | pub const HOSTNAME_FORMAT: ApiStringFormat = ApiStringFormat::Pattern(&HOSTNAME_REGEX); | |
161 | ||
162 | pub const DNS_ALIAS_FORMAT: ApiStringFormat = | |
163 | ApiStringFormat::Pattern(&DNS_ALIAS_REGEX); | |
164 | ||
bfd12e87 DM |
165 | pub const DAILY_DURATION_FORMAT: ApiStringFormat = |
166 | ApiStringFormat::VerifyFn(|s| parse_daily_duration(s).map(drop)); | |
167 | ||
8cc3760e DM |
168 | pub const SEARCH_DOMAIN_SCHEMA: Schema = |
169 | StringSchema::new("Search domain for host-name lookup.").schema(); | |
170 | ||
171 | pub const FIRST_DNS_SERVER_SCHEMA: Schema = | |
172 | StringSchema::new("First name server IP address.") | |
173 | .format(&IP_FORMAT) | |
174 | .schema(); | |
175 | ||
176 | pub const SECOND_DNS_SERVER_SCHEMA: Schema = | |
177 | StringSchema::new("Second name server IP address.") | |
178 | .format(&IP_FORMAT) | |
179 | .schema(); | |
180 | ||
181 | pub const THIRD_DNS_SERVER_SCHEMA: Schema = | |
182 | StringSchema::new("Third name server IP address.") | |
183 | .format(&IP_FORMAT) | |
184 | .schema(); | |
185 | ||
186 | pub const HOSTNAME_SCHEMA: Schema = StringSchema::new("Hostname (as defined in RFC1123).") | |
187 | .format(&HOSTNAME_FORMAT) | |
188 | .schema(); | |
75f83c6a | 189 | |
6afdda88 DM |
190 | pub const DNS_NAME_FORMAT: ApiStringFormat = |
191 | ApiStringFormat::Pattern(&DNS_NAME_REGEX); | |
192 | ||
193 | pub const DNS_NAME_OR_IP_FORMAT: ApiStringFormat = | |
194 | ApiStringFormat::Pattern(&DNS_NAME_OR_IP_REGEX); | |
195 | ||
196 | pub 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 |
200 | pub 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 | |
210 | pub 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 |
217 | pub 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 | ||
223 | pub const DISK_ARRAY_SCHEMA: Schema = ArraySchema::new( | |
224 | "Disk name list.", &BLOCKDEVICE_NAME_SCHEMA) | |
225 | .schema(); | |
226 | ||
227 | pub 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 | ||
232 | pub const PASSWORD_SCHEMA: Schema = StringSchema::new("Password.") | |
233 | .format(&PASSWORD_FORMAT) | |
234 | .min_length(1) | |
235 | .max_length(1024) | |
236 | .schema(); | |
237 | ||
238 | pub 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 |
244 | pub 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 |
250 | pub const FINGERPRINT_SHA256_FORMAT: ApiStringFormat = |
251 | ApiStringFormat::Pattern(&FINGERPRINT_SHA256_REGEX); | |
252 | ||
253 | pub const CERT_FINGERPRINT_SHA256_SCHEMA: Schema = | |
254 | StringSchema::new("X509 certificate fingerprint (sha256).") | |
255 | .format(&FINGERPRINT_SHA256_FORMAT) | |
256 | .schema(); | |
3c430e9a WB |
257 | |
258 | pub const PROXMOX_SAFE_ID_FORMAT: ApiStringFormat = | |
259 | ApiStringFormat::Pattern(&PROXMOX_SAFE_ID_REGEX); | |
260 | ||
261 | pub const SINGLE_LINE_COMMENT_FORMAT: ApiStringFormat = | |
262 | ApiStringFormat::Pattern(&SINGLE_LINE_COMMENT_REGEX); | |
263 | ||
264 | pub const SINGLE_LINE_COMMENT_SCHEMA: Schema = StringSchema::new("Comment (single line).") | |
265 | .format(&SINGLE_LINE_COMMENT_FORMAT) | |
266 | .schema(); | |
bfff4eaa | 267 | |
8cc3760e DM |
268 | pub 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 | ||
274 | pub const SERVICE_ID_SCHEMA: Schema = StringSchema::new("Service ID.") | |
275 | .max_length(256) | |
276 | .schema(); | |
277 | ||
2b7f8dd5 WB |
278 | pub 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 |
287 | pub 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. | |
296 | pub 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 |
305 | pub 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 | |
315 | pub 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 | ||
327 | impl 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. | |
348 | pub 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. | |
379 | pub 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")] | |
390 | pub 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. | |
408 | pub 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 | ||
432 | pub 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 | |
444 | pub 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 | |
455 | pub 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 | } |