api types: rust fmt

Signed-off-by: Thomas Lamprecht <t.lamprecht@proxmox.com>
This commit is contained in:
Thomas Lamprecht 2022-04-10 17:53:42 +02:00
parent 4ad118c613
commit b22d785c18
18 changed files with 497 additions and 469 deletions

View File

@ -62,18 +62,16 @@ fn as_fingerprint(bytes: &[u8]) -> String {
.as_bytes() .as_bytes()
.chunks(2) .chunks(2)
.map(|v| unsafe { std::str::from_utf8_unchecked(v) }) // it's a hex string .map(|v| unsafe { std::str::from_utf8_unchecked(v) }) // it's a hex string
.collect::<Vec<&str>>().join(":") .collect::<Vec<&str>>()
.join(":")
} }
pub mod bytes_as_fingerprint { pub mod bytes_as_fingerprint {
use std::mem::MaybeUninit; use std::mem::MaybeUninit;
use serde::{Deserialize, Serializer, Deserializer}; use serde::{Deserialize, Deserializer, Serializer};
pub fn serialize<S>( pub fn serialize<S>(bytes: &[u8; 32], serializer: S) -> Result<S::Ok, S::Error>
bytes: &[u8; 32],
serializer: S,
) -> Result<S::Ok, S::Error>
where where
S: Serializer, S: Serializer,
{ {
@ -81,9 +79,7 @@ pub mod bytes_as_fingerprint {
serializer.serialize_str(&s) serializer.serialize_str(&s)
} }
pub fn deserialize<'de, D>( pub fn deserialize<'de, D>(deserializer: D) -> Result<[u8; 32], D::Error>
deserializer: D,
) -> Result<[u8; 32], D::Error>
where where
D: Deserializer<'de>, D: Deserializer<'de>,
{ {

View File

@ -6,13 +6,12 @@ use proxmox_schema::{
}; };
use crate::{ use crate::{
PROXMOX_SAFE_ID_FORMAT, SHA256_HEX_REGEX, SINGLE_LINE_COMMENT_SCHEMA, CryptMode, UPID, Authid, CryptMode, Fingerprint, Userid, DATASTORE_NOTIFY_STRING_SCHEMA, GC_SCHEDULE_SCHEMA,
Fingerprint, Userid, Authid, PROXMOX_SAFE_ID_FORMAT, PRUNE_SCHEDULE_SCHEMA, SHA256_HEX_REGEX, SINGLE_LINE_COMMENT_SCHEMA,
GC_SCHEDULE_SCHEMA, DATASTORE_NOTIFY_STRING_SCHEMA, PRUNE_SCHEDULE_SCHEMA, UPID,
}; };
const_regex!{ const_regex! {
pub BACKUP_TYPE_REGEX = concat!(r"^(", BACKUP_TYPE_RE!(), r")$"); pub BACKUP_TYPE_REGEX = concat!(r"^(", BACKUP_TYPE_RE!(), r")$");
pub BACKUP_ID_REGEX = concat!(r"^", BACKUP_ID_RE!(), r"$"); pub BACKUP_ID_REGEX = concat!(r"^", BACKUP_ID_RE!(), r"$");
@ -81,17 +80,19 @@ pub const DATASTORE_MAP_SCHEMA: Schema = StringSchema::new("Datastore mapping.")
.type_text("(<source>=)?<target>") .type_text("(<source>=)?<target>")
.schema(); .schema();
pub const DATASTORE_MAP_ARRAY_SCHEMA: Schema = ArraySchema::new( pub const DATASTORE_MAP_ARRAY_SCHEMA: Schema =
"Datastore mapping list.", &DATASTORE_MAP_SCHEMA) ArraySchema::new("Datastore mapping list.", &DATASTORE_MAP_SCHEMA).schema();
.schema();
pub const DATASTORE_MAP_LIST_SCHEMA: Schema = StringSchema::new( pub const DATASTORE_MAP_LIST_SCHEMA: Schema = StringSchema::new(
"A list of Datastore mappings (or single datastore), comma separated. \ "A list of Datastore mappings (or single datastore), comma separated. \
For example 'a=b,e' maps the source datastore 'a' to target 'b and \ For example 'a=b,e' maps the source datastore 'a' to target 'b and \
all other sources to the default 'e'. If no default is given, only the \ all other sources to the default 'e'. If no default is given, only the \
specified sources are mapped.") specified sources are mapped.",
.format(&ApiStringFormat::PropertyString(&DATASTORE_MAP_ARRAY_SCHEMA)) )
.schema(); .format(&ApiStringFormat::PropertyString(
&DATASTORE_MAP_ARRAY_SCHEMA,
))
.schema();
pub const PRUNE_SCHEMA_KEEP_DAILY: Schema = IntegerSchema::new("Number of daily backups to keep.") pub const PRUNE_SCHEMA_KEEP_DAILY: Schema = IntegerSchema::new("Number of daily backups to keep.")
.minimum(1) .minimum(1)
@ -153,17 +154,17 @@ pub const PRUNE_SCHEMA_KEEP_YEARLY: Schema =
#[serde(rename_all = "kebab-case")] #[serde(rename_all = "kebab-case")]
/// Common pruning options /// Common pruning options
pub struct PruneOptions { pub struct PruneOptions {
#[serde(skip_serializing_if="Option::is_none")] #[serde(skip_serializing_if = "Option::is_none")]
pub keep_last: Option<u64>, pub keep_last: Option<u64>,
#[serde(skip_serializing_if="Option::is_none")] #[serde(skip_serializing_if = "Option::is_none")]
pub keep_hourly: Option<u64>, pub keep_hourly: Option<u64>,
#[serde(skip_serializing_if="Option::is_none")] #[serde(skip_serializing_if = "Option::is_none")]
pub keep_daily: Option<u64>, pub keep_daily: Option<u64>,
#[serde(skip_serializing_if="Option::is_none")] #[serde(skip_serializing_if = "Option::is_none")]
pub keep_weekly: Option<u64>, pub keep_weekly: Option<u64>,
#[serde(skip_serializing_if="Option::is_none")] #[serde(skip_serializing_if = "Option::is_none")]
pub keep_monthly: Option<u64>, pub keep_monthly: Option<u64>,
#[serde(skip_serializing_if="Option::is_none")] #[serde(skip_serializing_if = "Option::is_none")]
pub keep_yearly: Option<u64>, pub keep_yearly: Option<u64>,
} }
@ -194,9 +195,10 @@ pub struct DatastoreTuning {
pub chunk_order: Option<ChunkOrder>, pub chunk_order: Option<ChunkOrder>,
} }
pub const DATASTORE_TUNING_STRING_SCHEMA: Schema = StringSchema::new( pub const DATASTORE_TUNING_STRING_SCHEMA: Schema = StringSchema::new("Datastore tuning options")
"Datastore tuning options") .format(&ApiStringFormat::PropertyString(
.format(&ApiStringFormat::PropertyString(&DatastoreTuning::API_SCHEMA)) &DatastoreTuning::API_SCHEMA,
))
.schema(); .schema();
#[api( #[api(
@ -262,43 +264,43 @@ pub const DATASTORE_TUNING_STRING_SCHEMA: Schema = StringSchema::new(
}, },
} }
)] )]
#[derive(Serialize,Deserialize,Updater)] #[derive(Serialize, Deserialize, Updater)]
#[serde(rename_all="kebab-case")] #[serde(rename_all = "kebab-case")]
/// Datastore configuration properties. /// Datastore configuration properties.
pub struct DataStoreConfig { pub struct DataStoreConfig {
#[updater(skip)] #[updater(skip)]
pub name: String, pub name: String,
#[updater(skip)] #[updater(skip)]
pub path: String, pub path: String,
#[serde(skip_serializing_if="Option::is_none")] #[serde(skip_serializing_if = "Option::is_none")]
pub comment: Option<String>, pub comment: Option<String>,
#[serde(skip_serializing_if="Option::is_none")] #[serde(skip_serializing_if = "Option::is_none")]
pub gc_schedule: Option<String>, pub gc_schedule: Option<String>,
#[serde(skip_serializing_if="Option::is_none")] #[serde(skip_serializing_if = "Option::is_none")]
pub prune_schedule: Option<String>, pub prune_schedule: Option<String>,
#[serde(skip_serializing_if="Option::is_none")] #[serde(skip_serializing_if = "Option::is_none")]
pub keep_last: Option<u64>, pub keep_last: Option<u64>,
#[serde(skip_serializing_if="Option::is_none")] #[serde(skip_serializing_if = "Option::is_none")]
pub keep_hourly: Option<u64>, pub keep_hourly: Option<u64>,
#[serde(skip_serializing_if="Option::is_none")] #[serde(skip_serializing_if = "Option::is_none")]
pub keep_daily: Option<u64>, pub keep_daily: Option<u64>,
#[serde(skip_serializing_if="Option::is_none")] #[serde(skip_serializing_if = "Option::is_none")]
pub keep_weekly: Option<u64>, pub keep_weekly: Option<u64>,
#[serde(skip_serializing_if="Option::is_none")] #[serde(skip_serializing_if = "Option::is_none")]
pub keep_monthly: Option<u64>, pub keep_monthly: Option<u64>,
#[serde(skip_serializing_if="Option::is_none")] #[serde(skip_serializing_if = "Option::is_none")]
pub keep_yearly: Option<u64>, pub keep_yearly: Option<u64>,
/// If enabled, all backups will be verified right after completion. /// If enabled, all backups will be verified right after completion.
#[serde(skip_serializing_if="Option::is_none")] #[serde(skip_serializing_if = "Option::is_none")]
pub verify_new: Option<bool>, pub verify_new: Option<bool>,
/// Send job email notification to this user /// Send job email notification to this user
#[serde(skip_serializing_if="Option::is_none")] #[serde(skip_serializing_if = "Option::is_none")]
pub notify_user: Option<Userid>, pub notify_user: Option<Userid>,
/// Send notification only for job errors /// Send notification only for job errors
#[serde(skip_serializing_if="Option::is_none")] #[serde(skip_serializing_if = "Option::is_none")]
pub notify: Option<String>, pub notify: Option<String>,
/// Datastore tuning options /// Datastore tuning options
#[serde(skip_serializing_if="Option::is_none")] #[serde(skip_serializing_if = "Option::is_none")]
pub tuning: Option<String>, pub tuning: Option<String>,
} }
@ -375,7 +377,6 @@ pub struct SnapshotVerifyState {
pub state: VerifyState, pub state: VerifyState,
} }
#[api( #[api(
properties: { properties: {
"backup-type": { "backup-type": {
@ -616,7 +617,7 @@ impl Default for GarbageCollectionStatus {
}, },
)] )]
#[derive(Serialize, Deserialize)] #[derive(Serialize, Deserialize)]
#[serde(rename_all="kebab-case")] #[serde(rename_all = "kebab-case")]
/// Overall Datastore status and useful information. /// Overall Datastore status and useful information.
pub struct DataStoreStatus { pub struct DataStoreStatus {
/// Total space (bytes). /// Total space (bytes).
@ -626,10 +627,10 @@ pub struct DataStoreStatus {
/// Available space (bytes). /// Available space (bytes).
pub avail: u64, pub avail: u64,
/// Status of last GC /// Status of last GC
#[serde(skip_serializing_if="Option::is_none")] #[serde(skip_serializing_if = "Option::is_none")]
pub gc_status: Option<GarbageCollectionStatus>, pub gc_status: Option<GarbageCollectionStatus>,
/// Group/Snapshot counts /// Group/Snapshot counts
#[serde(skip_serializing_if="Option::is_none")] #[serde(skip_serializing_if = "Option::is_none")]
pub counts: Option<Counts>, pub counts: Option<Counts>,
} }
@ -649,7 +650,7 @@ pub struct DataStoreStatus {
}, },
)] )]
#[derive(Serialize, Deserialize)] #[derive(Serialize, Deserialize)]
#[serde(rename_all="kebab-case")] #[serde(rename_all = "kebab-case")]
/// Status of a Datastore /// Status of a Datastore
pub struct DataStoreStatusListItem { pub struct DataStoreStatusListItem {
pub store: String, pub store: String,
@ -660,23 +661,23 @@ pub struct DataStoreStatusListItem {
/// The available bytes of the underlying storage. (-1 on error) /// The available bytes of the underlying storage. (-1 on error)
pub avail: i64, pub avail: i64,
/// A list of usages of the past (last Month). /// A list of usages of the past (last Month).
#[serde(skip_serializing_if="Option::is_none")] #[serde(skip_serializing_if = "Option::is_none")]
pub history: Option<Vec<Option<f64>>>, pub history: Option<Vec<Option<f64>>>,
/// History start time (epoch) /// History start time (epoch)
#[serde(skip_serializing_if="Option::is_none")] #[serde(skip_serializing_if = "Option::is_none")]
pub history_start: Option<u64>, pub history_start: Option<u64>,
/// History resolution (seconds) /// History resolution (seconds)
#[serde(skip_serializing_if="Option::is_none")] #[serde(skip_serializing_if = "Option::is_none")]
pub history_delta: Option<u64>, pub history_delta: Option<u64>,
/// Estimation of the UNIX epoch when the storage will be full. /// Estimation of the UNIX epoch when the storage will be full.
/// This is calculated via a simple Linear Regression (Least /// This is calculated via a simple Linear Regression (Least
/// Squares) of RRD data of the last Month. Missing if there are /// Squares) of RRD data of the last Month. Missing if there are
/// not enough data points yet. If the estimate lies in the past, /// not enough data points yet. If the estimate lies in the past,
/// the usage is decreasing or not changing. /// the usage is decreasing or not changing.
#[serde(skip_serializing_if="Option::is_none")] #[serde(skip_serializing_if = "Option::is_none")]
pub estimated_full_date: Option<i64>, pub estimated_full_date: Option<i64>,
/// An error description, for example, when the datastore could not be looked up /// An error description, for example, when the datastore could not be looked up
#[serde(skip_serializing_if="Option::is_none")] #[serde(skip_serializing_if = "Option::is_none")]
pub error: Option<String>, pub error: Option<String>,
} }
@ -685,7 +686,8 @@ pub const ADMIN_DATASTORE_LIST_SNAPSHOTS_RETURN_TYPE: ReturnType = ReturnType {
schema: &ArraySchema::new( schema: &ArraySchema::new(
"Returns the list of snapshots.", "Returns the list of snapshots.",
&SnapshotListItem::API_SCHEMA, &SnapshotListItem::API_SCHEMA,
).schema(), )
.schema(),
}; };
pub const ADMIN_DATASTORE_LIST_SNAPSHOT_FILES_RETURN_TYPE: ReturnType = ReturnType { pub const ADMIN_DATASTORE_LIST_SNAPSHOT_FILES_RETURN_TYPE: ReturnType = ReturnType {
@ -693,7 +695,8 @@ pub const ADMIN_DATASTORE_LIST_SNAPSHOT_FILES_RETURN_TYPE: ReturnType = ReturnTy
schema: &ArraySchema::new( schema: &ArraySchema::new(
"Returns the list of archive files inside a backup snapshots.", "Returns the list of archive files inside a backup snapshots.",
&BackupContent::API_SCHEMA, &BackupContent::API_SCHEMA,
).schema(), )
.schema(),
}; };
pub const ADMIN_DATASTORE_LIST_GROUPS_RETURN_TYPE: ReturnType = ReturnType { pub const ADMIN_DATASTORE_LIST_GROUPS_RETURN_TYPE: ReturnType = ReturnType {
@ -701,7 +704,8 @@ pub const ADMIN_DATASTORE_LIST_GROUPS_RETURN_TYPE: ReturnType = ReturnType {
schema: &ArraySchema::new( schema: &ArraySchema::new(
"Returns the list of backup groups.", "Returns the list of backup groups.",
&GroupListItem::API_SCHEMA, &GroupListItem::API_SCHEMA,
).schema(), )
.schema(),
}; };
pub const ADMIN_DATASTORE_PRUNE_RETURN_TYPE: ReturnType = ReturnType { pub const ADMIN_DATASTORE_PRUNE_RETURN_TYPE: ReturnType = ReturnType {
@ -709,5 +713,6 @@ pub const ADMIN_DATASTORE_PRUNE_RETURN_TYPE: ReturnType = ReturnType {
schema: &ArraySchema::new( schema: &ArraySchema::new(
"Returns the list of snapshots and a flag indicating if there are kept or removed.", "Returns the list of snapshots and a flag indicating if there are kept or removed.",
&PruneListItem::API_SCHEMA, &PruneListItem::API_SCHEMA,
).schema(), )
.schema(),
}; };

View File

@ -101,7 +101,8 @@ fn strip_unit(v: &str) -> (&str, SizeUnit) {
}; };
let mut unit = SizeUnit::Byte; let mut unit = SizeUnit::Byte;
(v.strip_suffix(|c: char| match c { #[rustfmt::skip]
let value = v.strip_suffix(|c: char| match c {
'k' | 'K' if !binary => { unit = SizeUnit::KByte; true } 'k' | 'K' if !binary => { unit = SizeUnit::KByte; true }
'm' | 'M' if !binary => { unit = SizeUnit::MByte; true } 'm' | 'M' if !binary => { unit = SizeUnit::MByte; true }
'g' | 'G' if !binary => { unit = SizeUnit::GByte; true } 'g' | 'G' if !binary => { unit = SizeUnit::GByte; true }
@ -114,7 +115,9 @@ fn strip_unit(v: &str) -> (&str, SizeUnit) {
't' | 'T' if binary => { unit = SizeUnit::Tebi; true } 't' | 'T' if binary => { unit = SizeUnit::Tebi; true }
'p' | 'P' if binary => { unit = SizeUnit::Pebi; true } 'p' | 'P' if binary => { unit = SizeUnit::Pebi; true }
_ => false _ => false
}).unwrap_or(v).trim_end(), unit) }).unwrap_or(v).trim_end();
(value, unit)
} }
/// Byte size which can be displayed in a human friendly way /// Byte size which can be displayed in a human friendly way
@ -154,13 +157,19 @@ impl HumanByte {
/// Create a new instance with optimal binary unit computed /// Create a new instance with optimal binary unit computed
pub fn new_binary(size: f64) -> Self { pub fn new_binary(size: f64) -> Self {
let unit = SizeUnit::auto_scale(size, true); let unit = SizeUnit::auto_scale(size, true);
HumanByte { size: size / unit.factor(), unit } HumanByte {
size: size / unit.factor(),
unit,
}
} }
/// Create a new instance with optimal decimal unit computed /// Create a new instance with optimal decimal unit computed
pub fn new_decimal(size: f64) -> Self { pub fn new_decimal(size: f64) -> Self {
let unit = SizeUnit::auto_scale(size, false); let unit = SizeUnit::auto_scale(size, false);
HumanByte { size: size / unit.factor(), unit } HumanByte {
size: size / unit.factor(),
unit,
}
} }
/// Returns the size as u64 number of bytes /// Returns the size as u64 number of bytes
@ -228,7 +237,12 @@ fn test_human_byte_parser() -> Result<(), Error> {
bail!("got unexpected size for '{}' ({} != {})", v, h.size, size); bail!("got unexpected size for '{}' ({} != {})", v, h.size, size);
} }
if h.unit != unit { if h.unit != unit {
bail!("got unexpected unit for '{}' ({:?} != {:?})", v, h.unit, unit); bail!(
"got unexpected unit for '{}' ({:?} != {:?})",
v,
h.unit,
unit
);
} }
let new = h.to_string(); let new = h.to_string();
@ -265,7 +279,12 @@ fn test_human_byte_parser() -> Result<(), Error> {
assert_eq!(&format!("{:.7}", h), "1.2345678 B"); assert_eq!(&format!("{:.7}", h), "1.2345678 B");
assert_eq!(&format!("{:.8}", h), "1.2345678 B"); assert_eq!(&format!("{:.8}", h), "1.2345678 B");
assert!(test("987654321", 987654321.0, SizeUnit::Byte, "987654321 B")); assert!(test(
"987654321",
987654321.0,
SizeUnit::Byte,
"987654321 B"
));
assert!(test("1300b", 1300.0, SizeUnit::Byte, "1300 B")); assert!(test("1300b", 1300.0, SizeUnit::Byte, "1300 B"));
assert!(test("1300B", 1300.0, SizeUnit::Byte, "1300 B")); assert!(test("1300B", 1300.0, SizeUnit::Byte, "1300 B"));

View File

@ -7,13 +7,12 @@ use serde::{Deserialize, Serialize};
use proxmox_schema::*; use proxmox_schema::*;
use crate::{ use crate::{
Userid, Authid, RateLimitConfig, Authid, RateLimitConfig, Userid, BACKUP_GROUP_SCHEMA, BACKUP_TYPE_SCHEMA, DATASTORE_SCHEMA,
REMOTE_ID_SCHEMA, DRIVE_NAME_SCHEMA, MEDIA_POOL_NAME_SCHEMA, DRIVE_NAME_SCHEMA, MEDIA_POOL_NAME_SCHEMA, PROXMOX_SAFE_ID_FORMAT, REMOTE_ID_SCHEMA,
SINGLE_LINE_COMMENT_SCHEMA, PROXMOX_SAFE_ID_FORMAT, DATASTORE_SCHEMA, SINGLE_LINE_COMMENT_SCHEMA,
BACKUP_GROUP_SCHEMA, BACKUP_TYPE_SCHEMA,
}; };
const_regex!{ const_regex! {
/// Regex for verification jobs 'DATASTORE:ACTUAL_JOB_ID' /// Regex for verification jobs 'DATASTORE:ACTUAL_JOB_ID'
pub VERIFICATION_JOB_WORKER_ID_REGEX = concat!(r"^(", PROXMOX_SAFE_ID_REGEX_STR!(), r"):"); pub VERIFICATION_JOB_WORKER_ID_REGEX = concat!(r"^(", PROXMOX_SAFE_ID_REGEX_STR!(), r"):");
@ -27,34 +26,41 @@ pub const JOB_ID_SCHEMA: Schema = StringSchema::new("Job ID.")
.max_length(32) .max_length(32)
.schema(); .schema();
pub const SYNC_SCHEDULE_SCHEMA: Schema = StringSchema::new( pub const SYNC_SCHEDULE_SCHEMA: Schema = StringSchema::new("Run sync job at specified schedule.")
"Run sync job at specified schedule.") .format(&ApiStringFormat::VerifyFn(
.format(&ApiStringFormat::VerifyFn(proxmox_time::verify_calendar_event)) proxmox_time::verify_calendar_event,
))
.type_text("<calendar-event>") .type_text("<calendar-event>")
.schema(); .schema();
pub const GC_SCHEDULE_SCHEMA: Schema = StringSchema::new( pub const GC_SCHEDULE_SCHEMA: Schema =
"Run garbage collection job at specified schedule.") StringSchema::new("Run garbage collection job at specified schedule.")
.format(&ApiStringFormat::VerifyFn(proxmox_time::verify_calendar_event)) .format(&ApiStringFormat::VerifyFn(
proxmox_time::verify_calendar_event,
))
.type_text("<calendar-event>") .type_text("<calendar-event>")
.schema(); .schema();
pub const PRUNE_SCHEDULE_SCHEMA: Schema = StringSchema::new( pub const PRUNE_SCHEDULE_SCHEMA: Schema = StringSchema::new("Run prune job at specified schedule.")
"Run prune job at specified schedule.") .format(&ApiStringFormat::VerifyFn(
.format(&ApiStringFormat::VerifyFn(proxmox_time::verify_calendar_event)) proxmox_time::verify_calendar_event,
))
.type_text("<calendar-event>") .type_text("<calendar-event>")
.schema(); .schema();
pub const VERIFICATION_SCHEDULE_SCHEMA: Schema = StringSchema::new( pub const VERIFICATION_SCHEDULE_SCHEMA: Schema =
"Run verify job at specified schedule.") StringSchema::new("Run verify job at specified schedule.")
.format(&ApiStringFormat::VerifyFn(proxmox_time::verify_calendar_event)) .format(&ApiStringFormat::VerifyFn(
proxmox_time::verify_calendar_event,
))
.type_text("<calendar-event>") .type_text("<calendar-event>")
.schema(); .schema();
pub const REMOVE_VANISHED_BACKUPS_SCHEMA: Schema = BooleanSchema::new( pub const REMOVE_VANISHED_BACKUPS_SCHEMA: Schema = BooleanSchema::new(
"Delete vanished backups. This remove the local copy if the remote backup was deleted.") "Delete vanished backups. This remove the local copy if the remote backup was deleted.",
.default(false) )
.schema(); .default(false)
.schema();
#[api( #[api(
properties: { properties: {
@ -80,17 +86,17 @@ pub const REMOVE_VANISHED_BACKUPS_SCHEMA: Schema = BooleanSchema::new(
}, },
} }
)] )]
#[derive(Serialize,Deserialize,Default)] #[derive(Serialize, Deserialize, Default)]
#[serde(rename_all="kebab-case")] #[serde(rename_all = "kebab-case")]
/// Job Scheduling Status /// Job Scheduling Status
pub struct JobScheduleStatus { pub struct JobScheduleStatus {
#[serde(skip_serializing_if="Option::is_none")] #[serde(skip_serializing_if = "Option::is_none")]
pub next_run: Option<i64>, pub next_run: Option<i64>,
#[serde(skip_serializing_if="Option::is_none")] #[serde(skip_serializing_if = "Option::is_none")]
pub last_run_state: Option<String>, pub last_run_state: Option<String>,
#[serde(skip_serializing_if="Option::is_none")] #[serde(skip_serializing_if = "Option::is_none")]
pub last_run_upid: Option<String>, pub last_run_upid: Option<String>,
#[serde(skip_serializing_if="Option::is_none")] #[serde(skip_serializing_if = "Option::is_none")]
pub last_run_endtime: Option<i64>, pub last_run_endtime: Option<i64>,
} }
@ -134,18 +140,21 @@ pub struct DatastoreNotify {
pub sync: Option<Notify>, pub sync: Option<Notify>,
} }
pub const DATASTORE_NOTIFY_STRING_SCHEMA: Schema = StringSchema::new( pub const DATASTORE_NOTIFY_STRING_SCHEMA: Schema =
"Datastore notification setting") StringSchema::new("Datastore notification setting")
.format(&ApiStringFormat::PropertyString(&DatastoreNotify::API_SCHEMA)) .format(&ApiStringFormat::PropertyString(
&DatastoreNotify::API_SCHEMA,
))
.schema(); .schema();
pub const IGNORE_VERIFIED_BACKUPS_SCHEMA: Schema = BooleanSchema::new( pub const IGNORE_VERIFIED_BACKUPS_SCHEMA: Schema = BooleanSchema::new(
"Do not verify backups that are already verified if their verification is not outdated.") "Do not verify backups that are already verified if their verification is not outdated.",
.default(true) )
.schema(); .default(true)
.schema();
pub const VERIFICATION_OUTDATED_AFTER_SCHEMA: Schema = IntegerSchema::new( pub const VERIFICATION_OUTDATED_AFTER_SCHEMA: Schema =
"Days after that a verification becomes outdated. (0 means always)") IntegerSchema::new("Days after that a verification becomes outdated. (0 means always)")
.minimum(0) .minimum(0)
.schema(); .schema();
@ -175,8 +184,8 @@ pub const VERIFICATION_OUTDATED_AFTER_SCHEMA: Schema = IntegerSchema::new(
}, },
} }
)] )]
#[derive(Serialize,Deserialize,Updater)] #[derive(Serialize, Deserialize, Updater)]
#[serde(rename_all="kebab-case")] #[serde(rename_all = "kebab-case")]
/// Verification Job /// Verification Job
pub struct VerificationJobConfig { pub struct VerificationJobConfig {
/// unique ID to address this job /// unique ID to address this job
@ -184,16 +193,16 @@ pub struct VerificationJobConfig {
pub id: String, pub id: String,
/// the datastore ID this verificaiton job affects /// the datastore ID this verificaiton job affects
pub store: String, pub store: String,
#[serde(skip_serializing_if="Option::is_none")] #[serde(skip_serializing_if = "Option::is_none")]
/// if not set to false, check the age of the last snapshot verification to filter /// if not set to false, check the age of the last snapshot verification to filter
/// out recent ones, depending on 'outdated_after' configuration. /// out recent ones, depending on 'outdated_after' configuration.
pub ignore_verified: Option<bool>, pub ignore_verified: Option<bool>,
#[serde(skip_serializing_if="Option::is_none")] #[serde(skip_serializing_if = "Option::is_none")]
/// Reverify snapshots after X days, never if 0. Ignored if 'ignore_verified' is false. /// Reverify snapshots after X days, never if 0. Ignored if 'ignore_verified' is false.
pub outdated_after: Option<i64>, pub outdated_after: Option<i64>,
#[serde(skip_serializing_if="Option::is_none")] #[serde(skip_serializing_if = "Option::is_none")]
pub comment: Option<String>, pub comment: Option<String>,
#[serde(skip_serializing_if="Option::is_none")] #[serde(skip_serializing_if = "Option::is_none")]
/// when to schedule this job in calendar event notation /// when to schedule this job in calendar event notation
pub schedule: Option<String>, pub schedule: Option<String>,
} }
@ -208,8 +217,8 @@ pub struct VerificationJobConfig {
}, },
}, },
)] )]
#[derive(Serialize,Deserialize)] #[derive(Serialize, Deserialize)]
#[serde(rename_all="kebab-case")] #[serde(rename_all = "kebab-case")]
/// Status of Verification Job /// Status of Verification Job
pub struct VerificationJobStatus { pub struct VerificationJobStatus {
#[serde(flatten)] #[serde(flatten)]
@ -254,23 +263,23 @@ pub struct VerificationJobStatus {
}, },
} }
)] )]
#[derive(Serialize,Deserialize,Clone,Updater)] #[derive(Serialize, Deserialize, Clone, Updater)]
#[serde(rename_all="kebab-case")] #[serde(rename_all = "kebab-case")]
/// Tape Backup Job Setup /// Tape Backup Job Setup
pub struct TapeBackupJobSetup { pub struct TapeBackupJobSetup {
pub store: String, pub store: String,
pub pool: String, pub pool: String,
pub drive: String, pub drive: String,
#[serde(skip_serializing_if="Option::is_none")] #[serde(skip_serializing_if = "Option::is_none")]
pub eject_media: Option<bool>, pub eject_media: Option<bool>,
#[serde(skip_serializing_if="Option::is_none")] #[serde(skip_serializing_if = "Option::is_none")]
pub export_media_set: Option<bool>, pub export_media_set: Option<bool>,
#[serde(skip_serializing_if="Option::is_none")] #[serde(skip_serializing_if = "Option::is_none")]
pub latest_only: Option<bool>, pub latest_only: Option<bool>,
/// Send job email notification to this user /// Send job email notification to this user
#[serde(skip_serializing_if="Option::is_none")] #[serde(skip_serializing_if = "Option::is_none")]
pub notify_user: Option<Userid>, pub notify_user: Option<Userid>,
#[serde(skip_serializing_if="Option::is_none")] #[serde(skip_serializing_if = "Option::is_none")]
pub group_filter: Option<Vec<GroupFilter>>, pub group_filter: Option<Vec<GroupFilter>>,
} }
@ -292,17 +301,17 @@ pub struct TapeBackupJobSetup {
}, },
} }
)] )]
#[derive(Serialize,Deserialize,Clone,Updater)] #[derive(Serialize, Deserialize, Clone, Updater)]
#[serde(rename_all="kebab-case")] #[serde(rename_all = "kebab-case")]
/// Tape Backup Job /// Tape Backup Job
pub struct TapeBackupJobConfig { pub struct TapeBackupJobConfig {
#[updater(skip)] #[updater(skip)]
pub id: String, pub id: String,
#[serde(flatten)] #[serde(flatten)]
pub setup: TapeBackupJobSetup, pub setup: TapeBackupJobSetup,
#[serde(skip_serializing_if="Option::is_none")] #[serde(skip_serializing_if = "Option::is_none")]
pub comment: Option<String>, pub comment: Option<String>,
#[serde(skip_serializing_if="Option::is_none")] #[serde(skip_serializing_if = "Option::is_none")]
pub schedule: Option<String>, pub schedule: Option<String>,
} }
@ -316,8 +325,8 @@ pub struct TapeBackupJobConfig {
}, },
}, },
)] )]
#[derive(Serialize,Deserialize)] #[derive(Serialize, Deserialize)]
#[serde(rename_all="kebab-case")] #[serde(rename_all = "kebab-case")]
/// Status of Tape Backup Job /// Status of Tape Backup Job
pub struct TapeBackupJobStatus { pub struct TapeBackupJobStatus {
#[serde(flatten)] #[serde(flatten)]
@ -325,7 +334,7 @@ pub struct TapeBackupJobStatus {
#[serde(flatten)] #[serde(flatten)]
pub status: JobScheduleStatus, pub status: JobScheduleStatus,
/// Next tape used (best guess) /// Next tape used (best guess)
#[serde(skip_serializing_if="Option::is_none")] #[serde(skip_serializing_if = "Option::is_none")]
pub next_media_label: Option<String>, pub next_media_label: Option<String>,
} }
@ -378,7 +387,8 @@ pub const GROUP_FILTER_SCHEMA: Schema = StringSchema::new(
.type_text("<type:<vm|ct|host>|group:GROUP|regex:RE>") .type_text("<type:<vm|ct|host>|group:GROUP|regex:RE>")
.schema(); .schema();
pub const GROUP_FILTER_LIST_SCHEMA: Schema = ArraySchema::new("List of group filters.", &GROUP_FILTER_SCHEMA).schema(); pub const GROUP_FILTER_LIST_SCHEMA: Schema =
ArraySchema::new("List of group filters.", &GROUP_FILTER_SCHEMA).schema();
#[api( #[api(
properties: { properties: {
@ -419,24 +429,24 @@ pub const GROUP_FILTER_LIST_SCHEMA: Schema = ArraySchema::new("List of group fil
}, },
} }
)] )]
#[derive(Serialize,Deserialize,Clone,Updater)] #[derive(Serialize, Deserialize, Clone, Updater)]
#[serde(rename_all="kebab-case")] #[serde(rename_all = "kebab-case")]
/// Sync Job /// Sync Job
pub struct SyncJobConfig { pub struct SyncJobConfig {
#[updater(skip)] #[updater(skip)]
pub id: String, pub id: String,
pub store: String, pub store: String,
#[serde(skip_serializing_if="Option::is_none")] #[serde(skip_serializing_if = "Option::is_none")]
pub owner: Option<Authid>, pub owner: Option<Authid>,
pub remote: String, pub remote: String,
pub remote_store: String, pub remote_store: String,
#[serde(skip_serializing_if="Option::is_none")] #[serde(skip_serializing_if = "Option::is_none")]
pub remove_vanished: Option<bool>, pub remove_vanished: Option<bool>,
#[serde(skip_serializing_if="Option::is_none")] #[serde(skip_serializing_if = "Option::is_none")]
pub comment: Option<String>, pub comment: Option<String>,
#[serde(skip_serializing_if="Option::is_none")] #[serde(skip_serializing_if = "Option::is_none")]
pub schedule: Option<String>, pub schedule: Option<String>,
#[serde(skip_serializing_if="Option::is_none")] #[serde(skip_serializing_if = "Option::is_none")]
pub group_filter: Option<Vec<GroupFilter>>, pub group_filter: Option<Vec<GroupFilter>>,
#[serde(flatten)] #[serde(flatten)]
pub limit: RateLimitConfig, pub limit: RateLimitConfig,
@ -452,9 +462,8 @@ pub struct SyncJobConfig {
}, },
}, },
)] )]
#[derive(Serialize, Deserialize)]
#[derive(Serialize,Deserialize)] #[serde(rename_all = "kebab-case")]
#[serde(rename_all="kebab-case")]
/// Status of Sync Job /// Status of Sync Job
pub struct SyncJobStatus { pub struct SyncJobStatus {
#[serde(flatten)] #[serde(flatten)]

View File

@ -39,7 +39,7 @@ impl Default for Kdf {
/// Encryption Key Information /// Encryption Key Information
pub struct KeyInfo { pub struct KeyInfo {
/// Path to key (if stored in a file) /// Path to key (if stored in a file)
#[serde(skip_serializing_if="Option::is_none")] #[serde(skip_serializing_if = "Option::is_none")]
pub path: Option<String>, pub path: Option<String>,
pub kdf: Kdf, pub kdf: Kdf,
/// Key creation time /// Key creation time
@ -47,10 +47,9 @@ pub struct KeyInfo {
/// Key modification time /// Key modification time
pub modified: i64, pub modified: i64,
/// Key fingerprint /// Key fingerprint
#[serde(skip_serializing_if="Option::is_none")] #[serde(skip_serializing_if = "Option::is_none")]
pub fingerprint: Option<String>, pub fingerprint: Option<String>,
/// Password hint /// Password hint
#[serde(skip_serializing_if="Option::is_none")] #[serde(skip_serializing_if = "Option::is_none")]
pub hint: Option<String>, pub hint: Option<String>,
} }

View File

@ -6,7 +6,7 @@ pub mod common_regex;
pub mod percent_encoding; pub mod percent_encoding;
use proxmox_schema::{ use proxmox_schema::{
api, const_regex, ApiStringFormat, ApiType, ArraySchema, Schema, StringSchema, ReturnType, api, const_regex, ApiStringFormat, ApiType, ArraySchema, ReturnType, Schema, StringSchema,
}; };
use proxmox_time::parse_daily_duration; use proxmox_time::parse_daily_duration;
@ -68,7 +68,7 @@ pub use user::*;
pub use proxmox_schema::upid::*; pub use proxmox_schema::upid::*;
mod crypto; mod crypto;
pub use crypto::{CryptMode, Fingerprint, bytes_as_fingerprint}; pub use crypto::{bytes_as_fingerprint, CryptMode, Fingerprint};
pub mod file_restore; pub mod file_restore;
@ -87,7 +87,6 @@ pub use traffic_control::*;
mod zfs; mod zfs;
pub use zfs::*; pub use zfs::*;
#[rustfmt::skip] #[rustfmt::skip]
#[macro_use] #[macro_use]
mod local_macros { mod local_macros {
@ -160,14 +159,17 @@ pub const CIDR_FORMAT: ApiStringFormat = ApiStringFormat::Pattern(&CIDR_REGEX);
pub const PVE_CONFIG_DIGEST_FORMAT: ApiStringFormat = ApiStringFormat::Pattern(&SHA256_HEX_REGEX); pub const PVE_CONFIG_DIGEST_FORMAT: ApiStringFormat = ApiStringFormat::Pattern(&SHA256_HEX_REGEX);
pub const PASSWORD_FORMAT: ApiStringFormat = ApiStringFormat::Pattern(&PASSWORD_REGEX); pub const PASSWORD_FORMAT: ApiStringFormat = ApiStringFormat::Pattern(&PASSWORD_REGEX);
pub const UUID_FORMAT: ApiStringFormat = ApiStringFormat::Pattern(&UUID_REGEX); pub const UUID_FORMAT: ApiStringFormat = ApiStringFormat::Pattern(&UUID_REGEX);
pub const BLOCKDEVICE_NAME_FORMAT: ApiStringFormat = ApiStringFormat::Pattern(&BLOCKDEVICE_NAME_REGEX); pub const BLOCKDEVICE_NAME_FORMAT: ApiStringFormat =
pub const SUBSCRIPTION_KEY_FORMAT: ApiStringFormat = ApiStringFormat::Pattern(&SUBSCRIPTION_KEY_REGEX); ApiStringFormat::Pattern(&BLOCKDEVICE_NAME_REGEX);
pub const SYSTEMD_DATETIME_FORMAT: ApiStringFormat = ApiStringFormat::Pattern(&SYSTEMD_DATETIME_REGEX); pub const SUBSCRIPTION_KEY_FORMAT: ApiStringFormat =
ApiStringFormat::Pattern(&SUBSCRIPTION_KEY_REGEX);
pub const SYSTEMD_DATETIME_FORMAT: ApiStringFormat =
ApiStringFormat::Pattern(&SYSTEMD_DATETIME_REGEX);
pub const HOSTNAME_FORMAT: ApiStringFormat = ApiStringFormat::Pattern(&HOSTNAME_REGEX); pub const HOSTNAME_FORMAT: ApiStringFormat = ApiStringFormat::Pattern(&HOSTNAME_REGEX);
pub const OPENSSL_CIPHERS_TLS_FORMAT: ApiStringFormat = ApiStringFormat::Pattern(&OPENSSL_CIPHERS_REGEX); pub const OPENSSL_CIPHERS_TLS_FORMAT: ApiStringFormat =
ApiStringFormat::Pattern(&OPENSSL_CIPHERS_REGEX);
pub const DNS_ALIAS_FORMAT: ApiStringFormat = pub const DNS_ALIAS_FORMAT: ApiStringFormat = ApiStringFormat::Pattern(&DNS_ALIAS_REGEX);
ApiStringFormat::Pattern(&DNS_ALIAS_REGEX);
pub const DAILY_DURATION_FORMAT: ApiStringFormat = pub const DAILY_DURATION_FORMAT: ApiStringFormat =
ApiStringFormat::VerifyFn(|s| parse_daily_duration(s).map(drop)); ApiStringFormat::VerifyFn(|s| parse_daily_duration(s).map(drop));
@ -175,18 +177,15 @@ pub const DAILY_DURATION_FORMAT: ApiStringFormat =
pub const SEARCH_DOMAIN_SCHEMA: Schema = pub const SEARCH_DOMAIN_SCHEMA: Schema =
StringSchema::new("Search domain for host-name lookup.").schema(); StringSchema::new("Search domain for host-name lookup.").schema();
pub const FIRST_DNS_SERVER_SCHEMA: Schema = pub const FIRST_DNS_SERVER_SCHEMA: Schema = StringSchema::new("First name server IP address.")
StringSchema::new("First name server IP address.")
.format(&IP_FORMAT) .format(&IP_FORMAT)
.schema(); .schema();
pub const SECOND_DNS_SERVER_SCHEMA: Schema = pub const SECOND_DNS_SERVER_SCHEMA: Schema = StringSchema::new("Second name server IP address.")
StringSchema::new("Second name server IP address.")
.format(&IP_FORMAT) .format(&IP_FORMAT)
.schema(); .schema();
pub const THIRD_DNS_SERVER_SCHEMA: Schema = pub const THIRD_DNS_SERVER_SCHEMA: Schema = StringSchema::new("Third name server IP address.")
StringSchema::new("Third name server IP address.")
.format(&IP_FORMAT) .format(&IP_FORMAT)
.schema(); .schema();
@ -194,48 +193,47 @@ pub const HOSTNAME_SCHEMA: Schema = StringSchema::new("Hostname (as defined in R
.format(&HOSTNAME_FORMAT) .format(&HOSTNAME_FORMAT)
.schema(); .schema();
pub const OPENSSL_CIPHERS_TLS_1_2_SCHEMA: Schema = StringSchema::new("OpenSSL cipher list used by the proxy for TLS <= 1.2") pub const OPENSSL_CIPHERS_TLS_1_2_SCHEMA: Schema =
StringSchema::new("OpenSSL cipher list used by the proxy for TLS <= 1.2")
.format(&OPENSSL_CIPHERS_TLS_FORMAT) .format(&OPENSSL_CIPHERS_TLS_FORMAT)
.schema(); .schema();
pub const OPENSSL_CIPHERS_TLS_1_3_SCHEMA: Schema = StringSchema::new("OpenSSL ciphersuites list used by the proxy for TLS 1.3") pub const OPENSSL_CIPHERS_TLS_1_3_SCHEMA: Schema =
StringSchema::new("OpenSSL ciphersuites list used by the proxy for TLS 1.3")
.format(&OPENSSL_CIPHERS_TLS_FORMAT) .format(&OPENSSL_CIPHERS_TLS_FORMAT)
.schema(); .schema();
pub const DNS_NAME_FORMAT: ApiStringFormat = pub const DNS_NAME_FORMAT: ApiStringFormat = ApiStringFormat::Pattern(&DNS_NAME_REGEX);
ApiStringFormat::Pattern(&DNS_NAME_REGEX);
pub const DNS_NAME_OR_IP_FORMAT: ApiStringFormat = pub const DNS_NAME_OR_IP_FORMAT: ApiStringFormat = ApiStringFormat::Pattern(&DNS_NAME_OR_IP_REGEX);
ApiStringFormat::Pattern(&DNS_NAME_OR_IP_REGEX);
pub const DNS_NAME_OR_IP_SCHEMA: Schema = StringSchema::new("DNS name or IP address.") pub const DNS_NAME_OR_IP_SCHEMA: Schema = StringSchema::new("DNS name or IP address.")
.format(&DNS_NAME_OR_IP_FORMAT) .format(&DNS_NAME_OR_IP_FORMAT)
.schema(); .schema();
pub const NODE_SCHEMA: Schema = StringSchema::new("Node name (or 'localhost')") pub const NODE_SCHEMA: Schema = StringSchema::new("Node name (or 'localhost')")
.format(&HOSTNAME_FORMAT) .format(&HOSTNAME_FORMAT)
.schema(); .schema();
pub const TIME_ZONE_SCHEMA: Schema = StringSchema::new( pub const TIME_ZONE_SCHEMA: Schema = StringSchema::new(
"Time zone. The file '/usr/share/zoneinfo/zone.tab' contains the list of valid names.") "Time zone. The file '/usr/share/zoneinfo/zone.tab' contains the list of valid names.",
.format(&SINGLE_LINE_COMMENT_FORMAT) )
.min_length(2) .format(&SINGLE_LINE_COMMENT_FORMAT)
.max_length(64) .min_length(2)
.schema(); .max_length(64)
.schema();
pub const BLOCKDEVICE_NAME_SCHEMA: Schema = StringSchema::new("Block device name (/sys/block/<name>).") pub const BLOCKDEVICE_NAME_SCHEMA: Schema =
StringSchema::new("Block device name (/sys/block/<name>).")
.format(&BLOCKDEVICE_NAME_FORMAT) .format(&BLOCKDEVICE_NAME_FORMAT)
.min_length(3) .min_length(3)
.max_length(64) .max_length(64)
.schema(); .schema();
pub const DISK_ARRAY_SCHEMA: Schema = ArraySchema::new( pub const DISK_ARRAY_SCHEMA: Schema =
"Disk name list.", &BLOCKDEVICE_NAME_SCHEMA) ArraySchema::new("Disk name list.", &BLOCKDEVICE_NAME_SCHEMA).schema();
.schema();
pub const DISK_LIST_SCHEMA: Schema = StringSchema::new( pub const DISK_LIST_SCHEMA: Schema = StringSchema::new("A list of disk names, comma separated.")
"A list of disk names, comma separated.")
.format(&ApiStringFormat::PropertyString(&DISK_ARRAY_SCHEMA)) .format(&ApiStringFormat::PropertyString(&DISK_ARRAY_SCHEMA))
.schema(); .schema();
@ -282,15 +280,14 @@ pub const MULTI_LINE_COMMENT_SCHEMA: Schema = StringSchema::new("Comment (multip
.format(&MULTI_LINE_COMMENT_FORMAT) .format(&MULTI_LINE_COMMENT_FORMAT)
.schema(); .schema();
pub const SUBSCRIPTION_KEY_SCHEMA: Schema = StringSchema::new("Proxmox Backup Server subscription key.") pub const SUBSCRIPTION_KEY_SCHEMA: Schema =
StringSchema::new("Proxmox Backup Server subscription key.")
.format(&SUBSCRIPTION_KEY_FORMAT) .format(&SUBSCRIPTION_KEY_FORMAT)
.min_length(15) .min_length(15)
.max_length(16) .max_length(16)
.schema(); .schema();
pub const SERVICE_ID_SCHEMA: Schema = StringSchema::new("Service ID.") pub const SERVICE_ID_SCHEMA: Schema = StringSchema::new("Service ID.").max_length(256).schema();
.max_length(256)
.schema();
pub const PROXMOX_CONFIG_DIGEST_SCHEMA: Schema = StringSchema::new( pub const PROXMOX_CONFIG_DIGEST_SCHEMA: Schema = StringSchema::new(
"Prevent changes if current configuration file has different \ "Prevent changes if current configuration file has different \
@ -303,10 +300,8 @@ pub const PROXMOX_CONFIG_DIGEST_SCHEMA: Schema = StringSchema::new(
/// API schema format definition for repository URLs /// API schema format definition for repository URLs
pub const BACKUP_REPO_URL: ApiStringFormat = ApiStringFormat::Pattern(&BACKUP_REPO_URL_REGEX); pub const BACKUP_REPO_URL: ApiStringFormat = ApiStringFormat::Pattern(&BACKUP_REPO_URL_REGEX);
// Complex type definitions // Complex type definitions
#[api()] #[api()]
#[derive(Default, Serialize, Deserialize)] #[derive(Default, Serialize, Deserialize)]
/// Storage space usage information. /// Storage space usage information.
@ -325,7 +320,6 @@ pub const PASSWORD_HINT_SCHEMA: Schema = StringSchema::new("Password hint.")
.max_length(64) .max_length(64)
.schema(); .schema();
#[api()] #[api()]
#[derive(Debug, Clone, Serialize, Deserialize)] #[derive(Debug, Clone, Serialize, Deserialize)]
#[serde(rename_all = "PascalCase")] #[serde(rename_all = "PascalCase")]
@ -352,11 +346,10 @@ pub struct APTUpdateInfo {
/// URL under which the package's changelog can be retrieved /// URL under which the package's changelog can be retrieved
pub change_log_url: String, pub change_log_url: String,
/// Custom extra field for additional package information /// Custom extra field for additional package information
#[serde(skip_serializing_if="Option::is_none")] #[serde(skip_serializing_if = "Option::is_none")]
pub extra_info: Option<String>, pub extra_info: Option<String>,
} }
#[api()] #[api()]
#[derive(Debug, Copy, Clone, PartialEq, Serialize, Deserialize)] #[derive(Debug, Copy, Clone, PartialEq, Serialize, Deserialize)]
#[serde(rename_all = "lowercase")] #[serde(rename_all = "lowercase")]
@ -368,7 +361,6 @@ pub enum NodePowerCommand {
Shutdown, Shutdown,
} }
#[api()] #[api()]
#[derive(Eq, PartialEq, Debug, Serialize, Deserialize)] #[derive(Eq, PartialEq, Debug, Serialize, Deserialize)]
#[serde(rename_all = "lowercase")] #[serde(rename_all = "lowercase")]
@ -407,19 +399,16 @@ pub struct TaskListItem {
/// The authenticated entity who started the task /// The authenticated entity who started the task
pub user: String, pub user: String,
/// The task end time (Epoch) /// The task end time (Epoch)
#[serde(skip_serializing_if="Option::is_none")] #[serde(skip_serializing_if = "Option::is_none")]
pub endtime: Option<i64>, pub endtime: Option<i64>,
/// Task end status /// Task end status
#[serde(skip_serializing_if="Option::is_none")] #[serde(skip_serializing_if = "Option::is_none")]
pub status: Option<String>, pub status: Option<String>,
} }
pub const NODE_TASKS_LIST_TASKS_RETURN_TYPE: ReturnType = ReturnType { pub const NODE_TASKS_LIST_TASKS_RETURN_TYPE: ReturnType = ReturnType {
optional: false, optional: false,
schema: &ArraySchema::new( schema: &ArraySchema::new("A list of tasks.", &TaskListItem::API_SCHEMA).schema(),
"A list of tasks.",
&TaskListItem::API_SCHEMA,
).schema(),
}; };
#[api()] #[api()]

View File

@ -3,40 +3,34 @@ use serde::{Deserialize, Serialize};
use proxmox_schema::*; use proxmox_schema::*;
use crate::{ use crate::{
CIDR_FORMAT, CIDR_V4_FORMAT, CIDR_V6_FORMAT, IP_FORMAT, IP_V4_FORMAT, IP_V6_FORMAT,
PROXMOX_SAFE_ID_REGEX, PROXMOX_SAFE_ID_REGEX,
IP_V4_FORMAT, IP_V6_FORMAT, IP_FORMAT,
CIDR_V4_FORMAT, CIDR_V6_FORMAT, CIDR_FORMAT,
}; };
pub const NETWORK_INTERFACE_FORMAT: ApiStringFormat = pub const NETWORK_INTERFACE_FORMAT: ApiStringFormat =
ApiStringFormat::Pattern(&PROXMOX_SAFE_ID_REGEX); ApiStringFormat::Pattern(&PROXMOX_SAFE_ID_REGEX);
pub const IP_V4_SCHEMA: Schema = pub const IP_V4_SCHEMA: Schema = StringSchema::new("IPv4 address.")
StringSchema::new("IPv4 address.")
.format(&IP_V4_FORMAT) .format(&IP_V4_FORMAT)
.max_length(15) .max_length(15)
.schema(); .schema();
pub const IP_V6_SCHEMA: Schema = pub const IP_V6_SCHEMA: Schema = StringSchema::new("IPv6 address.")
StringSchema::new("IPv6 address.")
.format(&IP_V6_FORMAT) .format(&IP_V6_FORMAT)
.max_length(39) .max_length(39)
.schema(); .schema();
pub const IP_SCHEMA: Schema = pub const IP_SCHEMA: Schema = StringSchema::new("IP (IPv4 or IPv6) address.")
StringSchema::new("IP (IPv4 or IPv6) address.")
.format(&IP_FORMAT) .format(&IP_FORMAT)
.max_length(39) .max_length(39)
.schema(); .schema();
pub const CIDR_V4_SCHEMA: Schema = pub const CIDR_V4_SCHEMA: Schema = StringSchema::new("IPv4 address with netmask (CIDR notation).")
StringSchema::new("IPv4 address with netmask (CIDR notation).")
.format(&CIDR_V4_FORMAT) .format(&CIDR_V4_FORMAT)
.max_length(18) .max_length(18)
.schema(); .schema();
pub const CIDR_V6_SCHEMA: Schema = pub const CIDR_V6_SCHEMA: Schema = StringSchema::new("IPv6 address with netmask (CIDR notation).")
StringSchema::new("IPv6 address with netmask (CIDR notation).")
.format(&CIDR_V6_FORMAT) .format(&CIDR_V6_FORMAT)
.max_length(43) .max_length(43)
.schema(); .schema();
@ -130,13 +124,14 @@ pub const NETWORK_INTERFACE_NAME_SCHEMA: Schema = StringSchema::new("Network int
.max_length(15) // libc::IFNAMSIZ-1 .max_length(15) // libc::IFNAMSIZ-1
.schema(); .schema();
pub const NETWORK_INTERFACE_ARRAY_SCHEMA: Schema = ArraySchema::new( pub const NETWORK_INTERFACE_ARRAY_SCHEMA: Schema =
"Network interface list.", &NETWORK_INTERFACE_NAME_SCHEMA) ArraySchema::new("Network interface list.", &NETWORK_INTERFACE_NAME_SCHEMA).schema();
.schema();
pub const NETWORK_INTERFACE_LIST_SCHEMA: Schema = StringSchema::new( pub const NETWORK_INTERFACE_LIST_SCHEMA: Schema =
"A list of network devices, comma separated.") StringSchema::new("A list of network devices, comma separated.")
.format(&ApiStringFormat::PropertyString(&NETWORK_INTERFACE_ARRAY_SCHEMA)) .format(&ApiStringFormat::PropertyString(
&NETWORK_INTERFACE_ARRAY_SCHEMA,
))
.schema(); .schema();
#[api( #[api(
@ -232,48 +227,48 @@ pub struct Interface {
/// Interface type /// Interface type
#[serde(rename = "type")] #[serde(rename = "type")]
pub interface_type: NetworkInterfaceType, pub interface_type: NetworkInterfaceType,
#[serde(skip_serializing_if="Option::is_none")] #[serde(skip_serializing_if = "Option::is_none")]
pub method: Option<NetworkConfigMethod>, pub method: Option<NetworkConfigMethod>,
#[serde(skip_serializing_if="Option::is_none")] #[serde(skip_serializing_if = "Option::is_none")]
pub method6: Option<NetworkConfigMethod>, pub method6: Option<NetworkConfigMethod>,
#[serde(skip_serializing_if="Option::is_none")] #[serde(skip_serializing_if = "Option::is_none")]
/// IPv4 address with netmask /// IPv4 address with netmask
pub cidr: Option<String>, pub cidr: Option<String>,
#[serde(skip_serializing_if="Option::is_none")] #[serde(skip_serializing_if = "Option::is_none")]
/// IPv4 gateway /// IPv4 gateway
pub gateway: Option<String>, pub gateway: Option<String>,
#[serde(skip_serializing_if="Option::is_none")] #[serde(skip_serializing_if = "Option::is_none")]
/// IPv6 address with netmask /// IPv6 address with netmask
pub cidr6: Option<String>, pub cidr6: Option<String>,
#[serde(skip_serializing_if="Option::is_none")] #[serde(skip_serializing_if = "Option::is_none")]
/// IPv6 gateway /// IPv6 gateway
pub gateway6: Option<String>, pub gateway6: Option<String>,
#[serde(skip_serializing_if="Vec::is_empty")] #[serde(skip_serializing_if = "Vec::is_empty")]
pub options: Vec<String>, pub options: Vec<String>,
#[serde(skip_serializing_if="Vec::is_empty")] #[serde(skip_serializing_if = "Vec::is_empty")]
pub options6: Vec<String>, pub options6: Vec<String>,
#[serde(skip_serializing_if="Option::is_none")] #[serde(skip_serializing_if = "Option::is_none")]
pub comments: Option<String>, pub comments: Option<String>,
#[serde(skip_serializing_if="Option::is_none")] #[serde(skip_serializing_if = "Option::is_none")]
pub comments6: Option<String>, pub comments6: Option<String>,
#[serde(skip_serializing_if="Option::is_none")] #[serde(skip_serializing_if = "Option::is_none")]
/// Maximum Transmission Unit /// Maximum Transmission Unit
pub mtu: Option<u64>, pub mtu: Option<u64>,
#[serde(skip_serializing_if="Option::is_none")] #[serde(skip_serializing_if = "Option::is_none")]
pub bridge_ports: Option<Vec<String>>, pub bridge_ports: Option<Vec<String>>,
/// Enable bridge vlan support. /// Enable bridge vlan support.
#[serde(skip_serializing_if="Option::is_none")] #[serde(skip_serializing_if = "Option::is_none")]
pub bridge_vlan_aware: Option<bool>, pub bridge_vlan_aware: Option<bool>,
#[serde(skip_serializing_if="Option::is_none")] #[serde(skip_serializing_if = "Option::is_none")]
pub slaves: Option<Vec<String>>, pub slaves: Option<Vec<String>>,
#[serde(skip_serializing_if="Option::is_none")] #[serde(skip_serializing_if = "Option::is_none")]
pub bond_mode: Option<LinuxBondMode>, pub bond_mode: Option<LinuxBondMode>,
#[serde(skip_serializing_if="Option::is_none")] #[serde(skip_serializing_if = "Option::is_none")]
#[serde(rename = "bond-primary")] #[serde(rename = "bond-primary")]
pub bond_primary: Option<String>, pub bond_primary: Option<String>,
pub bond_xmit_hash_policy: Option<BondXmitHashPolicy>, pub bond_xmit_hash_policy: Option<BondXmitHashPolicy>,

View File

@ -1,23 +1,19 @@
use serde::{Deserialize, Serialize}; use serde::{Deserialize, Serialize};
use proxmox_schema::{ use proxmox_schema::{api, ApiStringFormat, ArraySchema, Schema, StringSchema, Updater};
api, ApiStringFormat, ArraySchema, Schema, StringSchema, Updater,
};
use super::{ use super::{
PROXMOX_SAFE_ID_REGEX, PROXMOX_SAFE_ID_FORMAT, REALM_ID_SCHEMA, PROXMOX_SAFE_ID_FORMAT, PROXMOX_SAFE_ID_REGEX, REALM_ID_SCHEMA, SINGLE_LINE_COMMENT_SCHEMA,
SINGLE_LINE_COMMENT_SCHEMA,
}; };
pub const OPENID_SCOPE_FORMAT: ApiStringFormat = pub const OPENID_SCOPE_FORMAT: ApiStringFormat = ApiStringFormat::Pattern(&PROXMOX_SAFE_ID_REGEX);
ApiStringFormat::Pattern(&PROXMOX_SAFE_ID_REGEX);
pub const OPENID_SCOPE_SCHEMA: Schema = StringSchema::new("OpenID Scope Name.") pub const OPENID_SCOPE_SCHEMA: Schema = StringSchema::new("OpenID Scope Name.")
.format(&OPENID_SCOPE_FORMAT) .format(&OPENID_SCOPE_FORMAT)
.schema(); .schema();
pub const OPENID_SCOPE_ARRAY_SCHEMA: Schema = ArraySchema::new( pub const OPENID_SCOPE_ARRAY_SCHEMA: Schema =
"Array of OpenId Scopes.", &OPENID_SCOPE_SCHEMA).schema(); ArraySchema::new("Array of OpenId Scopes.", &OPENID_SCOPE_SCHEMA).schema();
pub const OPENID_SCOPE_LIST_FORMAT: ApiStringFormat = pub const OPENID_SCOPE_LIST_FORMAT: ApiStringFormat =
ApiStringFormat::PropertyString(&OPENID_SCOPE_ARRAY_SCHEMA); ApiStringFormat::PropertyString(&OPENID_SCOPE_ARRAY_SCHEMA);
@ -28,15 +24,15 @@ pub const OPENID_SCOPE_LIST_SCHEMA: Schema = StringSchema::new("OpenID Scope Lis
.default(OPENID_DEFAILT_SCOPE_LIST) .default(OPENID_DEFAILT_SCOPE_LIST)
.schema(); .schema();
pub const OPENID_ACR_FORMAT: ApiStringFormat = pub const OPENID_ACR_FORMAT: ApiStringFormat = ApiStringFormat::Pattern(&PROXMOX_SAFE_ID_REGEX);
ApiStringFormat::Pattern(&PROXMOX_SAFE_ID_REGEX);
pub const OPENID_ACR_SCHEMA: Schema = StringSchema::new("OpenID Authentication Context Class Reference.") pub const OPENID_ACR_SCHEMA: Schema =
StringSchema::new("OpenID Authentication Context Class Reference.")
.format(&OPENID_SCOPE_FORMAT) .format(&OPENID_SCOPE_FORMAT)
.schema(); .schema();
pub const OPENID_ACR_ARRAY_SCHEMA: Schema = ArraySchema::new( pub const OPENID_ACR_ARRAY_SCHEMA: Schema =
"Array of OpenId ACRs.", &OPENID_ACR_SCHEMA).schema(); ArraySchema::new("Array of OpenId ACRs.", &OPENID_ACR_SCHEMA).schema();
pub const OPENID_ACR_LIST_FORMAT: ApiStringFormat = pub const OPENID_ACR_LIST_FORMAT: ApiStringFormat =
ApiStringFormat::PropertyString(&OPENID_ACR_ARRAY_SCHEMA); ApiStringFormat::PropertyString(&OPENID_ACR_ARRAY_SCHEMA);
@ -50,10 +46,12 @@ pub const OPENID_USERNAME_CLAIM_SCHEMA: Schema = StringSchema::new(
is up to the identity provider to guarantee the uniqueness. The \ is up to the identity provider to guarantee the uniqueness. The \
OpenID specification only guarantees that Subject ('sub') is \ OpenID specification only guarantees that Subject ('sub') is \
unique. Also make sure that the user is not allowed to change that \ unique. Also make sure that the user is not allowed to change that \
attribute by himself!") attribute by himself!",
.max_length(64) )
.min_length(1) .max_length(64)
.format(&PROXMOX_SAFE_ID_FORMAT) .schema(); .min_length(1)
.format(&PROXMOX_SAFE_ID_FORMAT)
.schema();
#[api( #[api(
properties: { properties: {
@ -92,7 +90,7 @@ pub const OPENID_USERNAME_CLAIM_SCHEMA: Schema = StringSchema::new(
}, },
)] )]
#[derive(Serialize, Deserialize, Updater)] #[derive(Serialize, Deserialize, Updater)]
#[serde(rename_all="kebab-case")] #[serde(rename_all = "kebab-case")]
/// OpenID configuration properties. /// OpenID configuration properties.
pub struct OpenIdRealmConfig { pub struct OpenIdRealmConfig {
#[updater(skip)] #[updater(skip)]
@ -101,21 +99,21 @@ pub struct OpenIdRealmConfig {
pub issuer_url: String, pub issuer_url: String,
/// OpenID Client ID /// OpenID Client ID
pub client_id: String, pub client_id: String,
#[serde(skip_serializing_if="Option::is_none")] #[serde(skip_serializing_if = "Option::is_none")]
pub scopes: Option<String>, pub scopes: Option<String>,
#[serde(skip_serializing_if="Option::is_none")] #[serde(skip_serializing_if = "Option::is_none")]
pub acr_values: Option<String>, pub acr_values: Option<String>,
#[serde(skip_serializing_if="Option::is_none")] #[serde(skip_serializing_if = "Option::is_none")]
pub prompt: Option<String>, pub prompt: Option<String>,
/// OpenID Client Key /// OpenID Client Key
#[serde(skip_serializing_if="Option::is_none")] #[serde(skip_serializing_if = "Option::is_none")]
pub client_key: Option<String>, pub client_key: Option<String>,
#[serde(skip_serializing_if="Option::is_none")] #[serde(skip_serializing_if = "Option::is_none")]
pub comment: Option<String>, pub comment: Option<String>,
/// Automatically create users if they do not exist. /// Automatically create users if they do not exist.
#[serde(skip_serializing_if="Option::is_none")] #[serde(skip_serializing_if = "Option::is_none")]
pub autocreate: Option<bool>, pub autocreate: Option<bool>,
#[updater(skip)] #[updater(skip)]
#[serde(skip_serializing_if="Option::is_none")] #[serde(skip_serializing_if = "Option::is_none")]
pub username_claim: Option<String>, pub username_claim: Option<String>,
} }

View File

@ -3,13 +3,15 @@ use serde::{Deserialize, Serialize};
use super::*; use super::*;
use proxmox_schema::*; use proxmox_schema::*;
pub const REMOTE_PASSWORD_SCHEMA: Schema = StringSchema::new("Password or auth token for remote host.") pub const REMOTE_PASSWORD_SCHEMA: Schema =
StringSchema::new("Password or auth token for remote host.")
.format(&PASSWORD_FORMAT) .format(&PASSWORD_FORMAT)
.min_length(1) .min_length(1)
.max_length(1024) .max_length(1024)
.schema(); .schema();
pub const REMOTE_PASSWORD_BASE64_SCHEMA: Schema = StringSchema::new("Password or auth token for remote host (stored as base64 string).") pub const REMOTE_PASSWORD_BASE64_SCHEMA: Schema =
StringSchema::new("Password or auth token for remote host (stored as base64 string).")
.format(&PASSWORD_FORMAT) .format(&PASSWORD_FORMAT)
.min_length(1) .min_length(1)
.max_length(1024) .max_length(1024)
@ -21,7 +23,6 @@ pub const REMOTE_ID_SCHEMA: Schema = StringSchema::new("Remote ID.")
.max_length(32) .max_length(32)
.schema(); .schema();
#[api( #[api(
properties: { properties: {
comment: { comment: {
@ -45,17 +46,17 @@ pub const REMOTE_ID_SCHEMA: Schema = StringSchema::new("Remote ID.")
}, },
}, },
)] )]
#[derive(Serialize,Deserialize,Updater)] #[derive(Serialize, Deserialize, Updater)]
#[serde(rename_all = "kebab-case")] #[serde(rename_all = "kebab-case")]
/// Remote configuration properties. /// Remote configuration properties.
pub struct RemoteConfig { pub struct RemoteConfig {
#[serde(skip_serializing_if="Option::is_none")] #[serde(skip_serializing_if = "Option::is_none")]
pub comment: Option<String>, pub comment: Option<String>,
pub host: String, pub host: String,
#[serde(skip_serializing_if="Option::is_none")] #[serde(skip_serializing_if = "Option::is_none")]
pub port: Option<u16>, pub port: Option<u16>,
pub auth_id: Authid, pub auth_id: Authid,
#[serde(skip_serializing_if="Option::is_none")] #[serde(skip_serializing_if = "Option::is_none")]
pub fingerprint: Option<String>, pub fingerprint: Option<String>,
} }
@ -72,13 +73,13 @@ pub struct RemoteConfig {
}, },
}, },
)] )]
#[derive(Serialize,Deserialize)] #[derive(Serialize, Deserialize)]
#[serde(rename_all = "kebab-case")] #[serde(rename_all = "kebab-case")]
/// Remote properties. /// Remote properties.
pub struct Remote { pub struct Remote {
pub name: String, pub name: String,
// Note: The stored password is base64 encoded // Note: The stored password is base64 encoded
#[serde(skip_serializing_if="String::is_empty")] #[serde(skip_serializing_if = "String::is_empty")]
#[serde(with = "proxmox_serde::string_as_base64")] #[serde(with = "proxmox_serde::string_as_base64")]
pub password: String, pub password: String,
#[serde(flatten)] #[serde(flatten)]

View File

@ -3,23 +3,23 @@ use ::serde::{Deserialize, Serialize};
use proxmox_schema::api; use proxmox_schema::api;
#[api()] #[api()]
#[derive(Serialize,Deserialize)] #[derive(Serialize, Deserialize)]
#[serde(rename_all = "kebab-case")] #[serde(rename_all = "kebab-case")]
/// Optional Device Identification Attributes /// Optional Device Identification Attributes
pub struct OptionalDeviceIdentification { pub struct OptionalDeviceIdentification {
/// Vendor (autodetected) /// Vendor (autodetected)
#[serde(skip_serializing_if="Option::is_none")] #[serde(skip_serializing_if = "Option::is_none")]
pub vendor: Option<String>, pub vendor: Option<String>,
/// Model (autodetected) /// Model (autodetected)
#[serde(skip_serializing_if="Option::is_none")] #[serde(skip_serializing_if = "Option::is_none")]
pub model: Option<String>, pub model: Option<String>,
/// Serial number (autodetected) /// Serial number (autodetected)
#[serde(skip_serializing_if="Option::is_none")] #[serde(skip_serializing_if = "Option::is_none")]
pub serial: Option<String>, pub serial: Option<String>,
} }
#[api()] #[api()]
#[derive(Debug,Serialize,Deserialize)] #[derive(Debug, Serialize, Deserialize)]
#[serde(rename_all = "kebab-case")] #[serde(rename_all = "kebab-case")]
/// Kind of device /// Kind of device
pub enum DeviceKind { pub enum DeviceKind {
@ -36,7 +36,7 @@ pub enum DeviceKind {
}, },
}, },
)] )]
#[derive(Debug,Serialize,Deserialize)] #[derive(Debug, Serialize, Deserialize)]
/// Tape device information /// Tape device information
pub struct TapeDeviceInfo { pub struct TapeDeviceInfo {
pub kind: DeviceKind, pub kind: DeviceKind,

View File

@ -4,13 +4,9 @@ use std::convert::TryFrom;
use anyhow::{bail, Error}; use anyhow::{bail, Error};
use serde::{Deserialize, Serialize}; use serde::{Deserialize, Serialize};
use proxmox_schema::{api, Schema, IntegerSchema, StringSchema, Updater}; use proxmox_schema::{api, IntegerSchema, Schema, StringSchema, Updater};
use crate::{ use crate::{OptionalDeviceIdentification, CHANGER_NAME_SCHEMA, PROXMOX_SAFE_ID_FORMAT};
PROXMOX_SAFE_ID_FORMAT,
CHANGER_NAME_SCHEMA,
OptionalDeviceIdentification,
};
pub const DRIVE_NAME_SCHEMA: Schema = StringSchema::new("Drive Identifier.") pub const DRIVE_NAME_SCHEMA: Schema = StringSchema::new("Drive Identifier.")
.format(&PROXMOX_SAFE_ID_FORMAT) .format(&PROXMOX_SAFE_ID_FORMAT)
@ -18,12 +14,11 @@ pub const DRIVE_NAME_SCHEMA: Schema = StringSchema::new("Drive Identifier.")
.max_length(32) .max_length(32)
.schema(); .schema();
pub const LTO_DRIVE_PATH_SCHEMA: Schema = StringSchema::new( pub const LTO_DRIVE_PATH_SCHEMA: Schema =
"The path to a LTO SCSI-generic tape device (i.e. '/dev/sg0')") StringSchema::new("The path to a LTO SCSI-generic tape device (i.e. '/dev/sg0')").schema();
.schema();
pub const CHANGER_DRIVENUM_SCHEMA: Schema = IntegerSchema::new( pub const CHANGER_DRIVENUM_SCHEMA: Schema =
"Associated changer drive number (requires option changer)") IntegerSchema::new("Associated changer drive number (requires option changer)")
.minimum(0) .minimum(0)
.maximum(255) .maximum(255)
.default(0) .default(0)
@ -36,7 +31,7 @@ pub const CHANGER_DRIVENUM_SCHEMA: Schema = IntegerSchema::new(
} }
} }
)] )]
#[derive(Serialize,Deserialize)] #[derive(Serialize, Deserialize)]
/// Simulate tape drives (only for test and debug) /// Simulate tape drives (only for test and debug)
#[serde(rename_all = "kebab-case")] #[serde(rename_all = "kebab-case")]
pub struct VirtualTapeDrive { pub struct VirtualTapeDrive {
@ -44,7 +39,7 @@ pub struct VirtualTapeDrive {
/// Path to directory /// Path to directory
pub path: String, pub path: String,
/// Virtual tape size /// Virtual tape size
#[serde(skip_serializing_if="Option::is_none")] #[serde(skip_serializing_if = "Option::is_none")]
pub max_size: Option<usize>, pub max_size: Option<usize>,
} }
@ -66,16 +61,16 @@ pub struct VirtualTapeDrive {
}, },
} }
)] )]
#[derive(Serialize,Deserialize,Updater)] #[derive(Serialize, Deserialize, Updater)]
#[serde(rename_all = "kebab-case")] #[serde(rename_all = "kebab-case")]
/// Lto SCSI tape driver /// Lto SCSI tape driver
pub struct LtoTapeDrive { pub struct LtoTapeDrive {
#[updater(skip)] #[updater(skip)]
pub name: String, pub name: String,
pub path: String, pub path: String,
#[serde(skip_serializing_if="Option::is_none")] #[serde(skip_serializing_if = "Option::is_none")]
pub changer: Option<String>, pub changer: Option<String>,
#[serde(skip_serializing_if="Option::is_none")] #[serde(skip_serializing_if = "Option::is_none")]
pub changer_drivenum: Option<u64>, pub changer_drivenum: Option<u64>,
} }
@ -89,7 +84,7 @@ pub struct LtoTapeDrive {
}, },
}, },
)] )]
#[derive(Serialize,Deserialize)] #[derive(Serialize, Deserialize)]
#[serde(rename_all = "kebab-case")] #[serde(rename_all = "kebab-case")]
/// Drive list entry /// Drive list entry
pub struct DriveListEntry { pub struct DriveListEntry {
@ -98,12 +93,12 @@ pub struct DriveListEntry {
#[serde(flatten)] #[serde(flatten)]
pub info: OptionalDeviceIdentification, pub info: OptionalDeviceIdentification,
/// the state of the drive if locked /// the state of the drive if locked
#[serde(skip_serializing_if="Option::is_none")] #[serde(skip_serializing_if = "Option::is_none")]
pub state: Option<String>, pub state: Option<String>,
} }
#[api()] #[api()]
#[derive(Serialize,Deserialize)] #[derive(Serialize, Deserialize)]
/// Medium auxiliary memory attributes (MAM) /// Medium auxiliary memory attributes (MAM)
pub struct MamAttribute { pub struct MamAttribute {
/// Attribute id /// Attribute id
@ -115,7 +110,7 @@ pub struct MamAttribute {
} }
#[api()] #[api()]
#[derive(Serialize,Deserialize,Copy,Clone,Debug)] #[derive(Serialize, Deserialize, Copy, Clone, Debug)]
pub enum TapeDensity { pub enum TapeDensity {
/// Unknown (no media loaded) /// Unknown (no media loaded)
Unknown, Unknown,
@ -168,7 +163,7 @@ impl TryFrom<u8> for TapeDensity {
}, },
}, },
)] )]
#[derive(Serialize,Deserialize)] #[derive(Serialize, Deserialize)]
#[serde(rename_all = "kebab-case")] #[serde(rename_all = "kebab-case")]
/// Drive/Media status for Lto SCSI drives. /// Drive/Media status for Lto SCSI drives.
/// ///
@ -190,35 +185,35 @@ pub struct LtoDriveAndMediaStatus {
/// Tape density /// Tape density
pub density: TapeDensity, pub density: TapeDensity,
/// Media is write protected /// Media is write protected
#[serde(skip_serializing_if="Option::is_none")] #[serde(skip_serializing_if = "Option::is_none")]
pub write_protect: Option<bool>, pub write_protect: Option<bool>,
/// Tape Alert Flags /// Tape Alert Flags
#[serde(skip_serializing_if="Option::is_none")] #[serde(skip_serializing_if = "Option::is_none")]
pub alert_flags: Option<String>, pub alert_flags: Option<String>,
/// Current file number /// Current file number
#[serde(skip_serializing_if="Option::is_none")] #[serde(skip_serializing_if = "Option::is_none")]
pub file_number: Option<u64>, pub file_number: Option<u64>,
/// Current block number /// Current block number
#[serde(skip_serializing_if="Option::is_none")] #[serde(skip_serializing_if = "Option::is_none")]
pub block_number: Option<u64>, pub block_number: Option<u64>,
/// Medium Manufacture Date (epoch) /// Medium Manufacture Date (epoch)
#[serde(skip_serializing_if="Option::is_none")] #[serde(skip_serializing_if = "Option::is_none")]
pub manufactured: Option<i64>, pub manufactured: Option<i64>,
/// Total Bytes Read in Medium Life /// Total Bytes Read in Medium Life
#[serde(skip_serializing_if="Option::is_none")] #[serde(skip_serializing_if = "Option::is_none")]
pub bytes_read: Option<u64>, pub bytes_read: Option<u64>,
/// Total Bytes Written in Medium Life /// Total Bytes Written in Medium Life
#[serde(skip_serializing_if="Option::is_none")] #[serde(skip_serializing_if = "Option::is_none")]
pub bytes_written: Option<u64>, pub bytes_written: Option<u64>,
/// Number of mounts for the current volume (i.e., Thread Count) /// Number of mounts for the current volume (i.e., Thread Count)
#[serde(skip_serializing_if="Option::is_none")] #[serde(skip_serializing_if = "Option::is_none")]
pub volume_mounts: Option<u64>, pub volume_mounts: Option<u64>,
/// Count of the total number of times the medium has passed over /// Count of the total number of times the medium has passed over
/// the head. /// the head.
#[serde(skip_serializing_if="Option::is_none")] #[serde(skip_serializing_if = "Option::is_none")]
pub medium_passes: Option<u64>, pub medium_passes: Option<u64>,
/// Estimated tape wearout factor (assuming max. 16000 end-to-end passes) /// Estimated tape wearout factor (assuming max. 16000 end-to-end passes)
#[serde(skip_serializing_if="Option::is_none")] #[serde(skip_serializing_if = "Option::is_none")]
pub medium_wearout: Option<f64>, pub medium_wearout: Option<f64>,
} }

View File

@ -3,19 +3,15 @@ use ::serde::{Deserialize, Serialize};
use proxmox_schema::*; use proxmox_schema::*;
use proxmox_uuid::Uuid; use proxmox_uuid::Uuid;
use crate::{ use crate::{MediaLocation, MediaStatus, UUID_FORMAT};
UUID_FORMAT,
MediaStatus,
MediaLocation,
};
pub const MEDIA_SET_UUID_SCHEMA: Schema = pub const MEDIA_SET_UUID_SCHEMA: Schema = StringSchema::new(
StringSchema::new("MediaSet Uuid (We use the all-zero Uuid to reseve an empty media for a specific pool).") "MediaSet Uuid (We use the all-zero Uuid to reseve an empty media for a specific pool).",
.format(&UUID_FORMAT) )
.schema(); .format(&UUID_FORMAT)
.schema();
pub const MEDIA_UUID_SCHEMA: Schema = pub const MEDIA_UUID_SCHEMA: Schema = StringSchema::new("Media Uuid.")
StringSchema::new("Media Uuid.")
.format(&UUID_FORMAT) .format(&UUID_FORMAT)
.schema(); .schema();
@ -26,7 +22,7 @@ pub const MEDIA_UUID_SCHEMA: Schema =
}, },
}, },
)] )]
#[derive(Serialize,Deserialize)] #[derive(Serialize, Deserialize)]
#[serde(rename_all = "kebab-case")] #[serde(rename_all = "kebab-case")]
/// Media Set list entry /// Media Set list entry
pub struct MediaSetListEntry { pub struct MediaSetListEntry {
@ -56,7 +52,7 @@ pub struct MediaSetListEntry {
}, },
}, },
)] )]
#[derive(Serialize,Deserialize)] #[derive(Serialize, Deserialize)]
#[serde(rename_all = "kebab-case")] #[serde(rename_all = "kebab-case")]
/// Media list entry /// Media list entry
pub struct MediaListEntry { pub struct MediaListEntry {
@ -72,18 +68,18 @@ pub struct MediaListEntry {
/// Catalog status OK /// Catalog status OK
pub catalog: bool, pub catalog: bool,
/// Media set name /// Media set name
#[serde(skip_serializing_if="Option::is_none")] #[serde(skip_serializing_if = "Option::is_none")]
pub media_set_name: Option<String>, pub media_set_name: Option<String>,
#[serde(skip_serializing_if="Option::is_none")] #[serde(skip_serializing_if = "Option::is_none")]
pub media_set_uuid: Option<Uuid>, pub media_set_uuid: Option<Uuid>,
/// Media set seq_nr /// Media set seq_nr
#[serde(skip_serializing_if="Option::is_none")] #[serde(skip_serializing_if = "Option::is_none")]
pub seq_nr: Option<u64>, pub seq_nr: Option<u64>,
/// MediaSet creation time stamp /// MediaSet creation time stamp
#[serde(skip_serializing_if="Option::is_none")] #[serde(skip_serializing_if = "Option::is_none")]
pub media_set_ctime: Option<i64>, pub media_set_ctime: Option<i64>,
/// Media Pool /// Media Pool
#[serde(skip_serializing_if="Option::is_none")] #[serde(skip_serializing_if = "Option::is_none")]
pub pool: Option<String>, pub pool: Option<String>,
} }
@ -98,7 +94,7 @@ pub struct MediaListEntry {
}, },
}, },
)] )]
#[derive(Serialize,Deserialize)] #[derive(Serialize, Deserialize)]
#[serde(rename_all = "kebab-case")] #[serde(rename_all = "kebab-case")]
/// Media label info /// Media label info
pub struct MediaIdFlat { pub struct MediaIdFlat {
@ -110,18 +106,18 @@ pub struct MediaIdFlat {
pub ctime: i64, pub ctime: i64,
// All MediaSet properties are optional here // All MediaSet properties are optional here
/// MediaSet Pool /// MediaSet Pool
#[serde(skip_serializing_if="Option::is_none")] #[serde(skip_serializing_if = "Option::is_none")]
pub pool: Option<String>, pub pool: Option<String>,
#[serde(skip_serializing_if="Option::is_none")] #[serde(skip_serializing_if = "Option::is_none")]
pub media_set_uuid: Option<Uuid>, pub media_set_uuid: Option<Uuid>,
/// MediaSet media sequence number /// MediaSet media sequence number
#[serde(skip_serializing_if="Option::is_none")] #[serde(skip_serializing_if = "Option::is_none")]
pub seq_nr: Option<u64>, pub seq_nr: Option<u64>,
/// MediaSet Creation time stamp /// MediaSet Creation time stamp
#[serde(skip_serializing_if="Option::is_none")] #[serde(skip_serializing_if = "Option::is_none")]
pub media_set_ctime: Option<i64>, pub media_set_ctime: Option<i64>,
/// Encryption key fingerprint /// Encryption key fingerprint
#[serde(skip_serializing_if="Option::is_none")] #[serde(skip_serializing_if = "Option::is_none")]
pub encryption_key_fingerprint: Option<String>, pub encryption_key_fingerprint: Option<String>,
} }
@ -133,7 +129,7 @@ pub struct MediaIdFlat {
}, },
}, },
)] )]
#[derive(Serialize,Deserialize)] #[derive(Serialize, Deserialize)]
#[serde(rename_all = "kebab-case")] #[serde(rename_all = "kebab-case")]
/// Label with optional Uuid /// Label with optional Uuid
pub struct LabelUuidMap { pub struct LabelUuidMap {
@ -153,7 +149,7 @@ pub struct LabelUuidMap {
}, },
}, },
)] )]
#[derive(Serialize,Deserialize)] #[derive(Serialize, Deserialize)]
#[serde(rename_all = "kebab-case")] #[serde(rename_all = "kebab-case")]
/// Media content list entry /// Media content list entry
pub struct MediaContentEntry { pub struct MediaContentEntry {

View File

@ -9,14 +9,12 @@ use std::str::FromStr;
use anyhow::Error; use anyhow::Error;
use serde::{Deserialize, Serialize}; use serde::{Deserialize, Serialize};
use proxmox_schema::{api, Schema, StringSchema, ApiStringFormat, Updater}; use proxmox_schema::{api, ApiStringFormat, Schema, StringSchema, Updater};
use proxmox_time::{CalendarEvent, TimeSpan}; use proxmox_time::{CalendarEvent, TimeSpan};
use crate::{ use crate::{
PROXMOX_SAFE_ID_FORMAT, PROXMOX_SAFE_ID_FORMAT, SINGLE_LINE_COMMENT_FORMAT, SINGLE_LINE_COMMENT_SCHEMA,
SINGLE_LINE_COMMENT_FORMAT,
SINGLE_LINE_COMMENT_SCHEMA,
TAPE_ENCRYPTION_KEY_FINGERPRINT_SCHEMA, TAPE_ENCRYPTION_KEY_FINGERPRINT_SCHEMA,
}; };
@ -27,17 +25,20 @@ pub const MEDIA_POOL_NAME_SCHEMA: Schema = StringSchema::new("Media pool name.")
.schema(); .schema();
pub const MEDIA_SET_NAMING_TEMPLATE_SCHEMA: Schema = StringSchema::new( pub const MEDIA_SET_NAMING_TEMPLATE_SCHEMA: Schema = StringSchema::new(
"Media set naming template (may contain strftime() time format specifications).") "Media set naming template (may contain strftime() time format specifications).",
.format(&SINGLE_LINE_COMMENT_FORMAT) )
.min_length(2) .format(&SINGLE_LINE_COMMENT_FORMAT)
.max_length(64) .min_length(2)
.schema(); .max_length(64)
.schema();
pub const MEDIA_SET_ALLOCATION_POLICY_FORMAT: ApiStringFormat = pub const MEDIA_SET_ALLOCATION_POLICY_FORMAT: ApiStringFormat = ApiStringFormat::VerifyFn(|s| {
ApiStringFormat::VerifyFn(|s| { MediaSetPolicy::from_str(s)?; Ok(()) }); MediaSetPolicy::from_str(s)?;
Ok(())
});
pub const MEDIA_SET_ALLOCATION_POLICY_SCHEMA: Schema = StringSchema::new( pub const MEDIA_SET_ALLOCATION_POLICY_SCHEMA: Schema =
"Media set allocation policy ('continue', 'always', or a calendar event).") StringSchema::new("Media set allocation policy ('continue', 'always', or a calendar event).")
.format(&MEDIA_SET_ALLOCATION_POLICY_FORMAT) .format(&MEDIA_SET_ALLOCATION_POLICY_FORMAT)
.schema(); .schema();
@ -68,11 +69,13 @@ impl std::str::FromStr for MediaSetPolicy {
} }
} }
pub const MEDIA_RETENTION_POLICY_FORMAT: ApiStringFormat = pub const MEDIA_RETENTION_POLICY_FORMAT: ApiStringFormat = ApiStringFormat::VerifyFn(|s| {
ApiStringFormat::VerifyFn(|s| { RetentionPolicy::from_str(s)?; Ok(()) }); RetentionPolicy::from_str(s)?;
Ok(())
});
pub const MEDIA_RETENTION_POLICY_SCHEMA: Schema = StringSchema::new( pub const MEDIA_RETENTION_POLICY_SCHEMA: Schema =
"Media retention policy ('overwrite', 'keep', or time span).") StringSchema::new("Media retention policy ('overwrite', 'keep', or time span).")
.format(&MEDIA_RETENTION_POLICY_FORMAT) .format(&MEDIA_RETENTION_POLICY_FORMAT)
.schema(); .schema();
@ -130,29 +133,29 @@ impl std::str::FromStr for RetentionPolicy {
}, },
}, },
)] )]
#[derive(Serialize,Deserialize,Updater)] #[derive(Serialize, Deserialize, Updater)]
/// Media pool configuration /// Media pool configuration
pub struct MediaPoolConfig { pub struct MediaPoolConfig {
/// The pool name /// The pool name
#[updater(skip)] #[updater(skip)]
pub name: String, pub name: String,
/// Media Set allocation policy /// Media Set allocation policy
#[serde(skip_serializing_if="Option::is_none")] #[serde(skip_serializing_if = "Option::is_none")]
pub allocation: Option<String>, pub allocation: Option<String>,
/// Media retention policy /// Media retention policy
#[serde(skip_serializing_if="Option::is_none")] #[serde(skip_serializing_if = "Option::is_none")]
pub retention: Option<String>, pub retention: Option<String>,
/// Media set naming template (default "%c") /// Media set naming template (default "%c")
/// ///
/// The template is UTF8 text, and can include strftime time /// The template is UTF8 text, and can include strftime time
/// format specifications. /// format specifications.
#[serde(skip_serializing_if="Option::is_none")] #[serde(skip_serializing_if = "Option::is_none")]
pub template: Option<String>, pub template: Option<String>,
/// Encryption key fingerprint /// Encryption key fingerprint
/// ///
/// If set, encrypt all data using the specified key. /// If set, encrypt all data using the specified key.
#[serde(skip_serializing_if="Option::is_none")] #[serde(skip_serializing_if = "Option::is_none")]
pub encrypt: Option<String>, pub encrypt: Option<String>,
#[serde(skip_serializing_if="Option::is_none")] #[serde(skip_serializing_if = "Option::is_none")]
pub comment: Option<String>, pub comment: Option<String>,
} }

View File

@ -24,28 +24,25 @@ pub use media::*;
use serde::{Deserialize, Serialize}; use serde::{Deserialize, Serialize};
use proxmox_schema::{api, const_regex, Schema, StringSchema, ApiStringFormat}; use proxmox_schema::{api, const_regex, ApiStringFormat, Schema, StringSchema};
use proxmox_uuid::Uuid; use proxmox_uuid::Uuid;
use crate::{ use crate::{BACKUP_ID_SCHEMA, BACKUP_TYPE_SCHEMA, FINGERPRINT_SHA256_FORMAT};
FINGERPRINT_SHA256_FORMAT, BACKUP_ID_SCHEMA, BACKUP_TYPE_SCHEMA,
};
const_regex!{ const_regex! {
pub TAPE_RESTORE_SNAPSHOT_REGEX = concat!(r"^", PROXMOX_SAFE_ID_REGEX_STR!(), r":", SNAPSHOT_PATH_REGEX_STR!(), r"$"); pub TAPE_RESTORE_SNAPSHOT_REGEX = concat!(r"^", PROXMOX_SAFE_ID_REGEX_STR!(), r":", SNAPSHOT_PATH_REGEX_STR!(), r"$");
} }
pub const TAPE_RESTORE_SNAPSHOT_FORMAT: ApiStringFormat = pub const TAPE_RESTORE_SNAPSHOT_FORMAT: ApiStringFormat =
ApiStringFormat::Pattern(&TAPE_RESTORE_SNAPSHOT_REGEX); ApiStringFormat::Pattern(&TAPE_RESTORE_SNAPSHOT_REGEX);
pub const TAPE_ENCRYPTION_KEY_FINGERPRINT_SCHEMA: Schema = StringSchema::new( pub const TAPE_ENCRYPTION_KEY_FINGERPRINT_SCHEMA: Schema =
"Tape encryption key fingerprint (sha256)." StringSchema::new("Tape encryption key fingerprint (sha256).")
)
.format(&FINGERPRINT_SHA256_FORMAT) .format(&FINGERPRINT_SHA256_FORMAT)
.schema(); .schema();
pub const TAPE_RESTORE_SNAPSHOT_SCHEMA: Schema = StringSchema::new( pub const TAPE_RESTORE_SNAPSHOT_SCHEMA: Schema =
"A snapshot in the format: 'store:type/id/time") StringSchema::new("A snapshot in the format: 'store:type/id/time")
.format(&TAPE_RESTORE_SNAPSHOT_FORMAT) .format(&TAPE_RESTORE_SNAPSHOT_FORMAT)
.type_text("store:type/id/time") .type_text("store:type/id/time")
.schema(); .schema();
@ -78,8 +75,8 @@ pub const TAPE_RESTORE_SNAPSHOT_SCHEMA: Schema = StringSchema::new(
}, },
}, },
)] )]
#[derive(Serialize,Deserialize)] #[derive(Serialize, Deserialize)]
#[serde(rename_all="kebab-case")] #[serde(rename_all = "kebab-case")]
/// Content list filter parameters /// Content list filter parameters
pub struct MediaContentListFilter { pub struct MediaContentListFilter {
pub pool: Option<String>, pub pool: Option<String>,

View File

@ -1,14 +1,14 @@
use serde::{Deserialize, Serialize}; use serde::{Deserialize, Serialize};
use proxmox_schema::{api, Schema, IntegerSchema, StringSchema, Updater}; use proxmox_schema::{api, IntegerSchema, Schema, StringSchema, Updater};
use crate::{ use crate::{
HumanByte, CIDR_SCHEMA, DAILY_DURATION_FORMAT, HumanByte, CIDR_SCHEMA, DAILY_DURATION_FORMAT, PROXMOX_SAFE_ID_FORMAT,
PROXMOX_SAFE_ID_FORMAT, SINGLE_LINE_COMMENT_SCHEMA, SINGLE_LINE_COMMENT_SCHEMA,
}; };
pub const TRAFFIC_CONTROL_TIMEFRAME_SCHEMA: Schema = StringSchema::new( pub const TRAFFIC_CONTROL_TIMEFRAME_SCHEMA: Schema =
"Timeframe to specify when the rule is actice.") StringSchema::new("Timeframe to specify when the rule is actice.")
.format(&DAILY_DURATION_FORMAT) .format(&DAILY_DURATION_FORMAT)
.schema(); .schema();
@ -18,13 +18,13 @@ pub const TRAFFIC_CONTROL_ID_SCHEMA: Schema = StringSchema::new("Rule ID.")
.max_length(32) .max_length(32)
.schema(); .schema();
pub const TRAFFIC_CONTROL_RATE_SCHEMA: Schema = IntegerSchema::new( pub const TRAFFIC_CONTROL_RATE_SCHEMA: Schema =
"Rate limit (for Token bucket filter) in bytes/second.") IntegerSchema::new("Rate limit (for Token bucket filter) in bytes/second.")
.minimum(100_000) .minimum(100_000)
.schema(); .schema();
pub const TRAFFIC_CONTROL_BURST_SCHEMA: Schema = IntegerSchema::new( pub const TRAFFIC_CONTROL_BURST_SCHEMA: Schema =
"Size of the token bucket (for Token bucket filter) in bytes.") IntegerSchema::new("Size of the token bucket (for Token bucket filter) in bytes.")
.minimum(1000) .minimum(1000)
.schema(); .schema();
@ -48,17 +48,17 @@ pub const TRAFFIC_CONTROL_BURST_SCHEMA: Schema = IntegerSchema::new(
}, },
}, },
)] )]
#[derive(Serialize,Deserialize,Default,Clone,Updater)] #[derive(Serialize, Deserialize, Default, Clone, Updater)]
#[serde(rename_all = "kebab-case")] #[serde(rename_all = "kebab-case")]
/// Rate Limit Configuration /// Rate Limit Configuration
pub struct RateLimitConfig { pub struct RateLimitConfig {
#[serde(skip_serializing_if="Option::is_none")] #[serde(skip_serializing_if = "Option::is_none")]
pub rate_in: Option<HumanByte>, pub rate_in: Option<HumanByte>,
#[serde(skip_serializing_if="Option::is_none")] #[serde(skip_serializing_if = "Option::is_none")]
pub burst_in: Option<HumanByte>, pub burst_in: Option<HumanByte>,
#[serde(skip_serializing_if="Option::is_none")] #[serde(skip_serializing_if = "Option::is_none")]
pub rate_out: Option<HumanByte>, pub rate_out: Option<HumanByte>,
#[serde(skip_serializing_if="Option::is_none")] #[serde(skip_serializing_if = "Option::is_none")]
pub burst_out: Option<HumanByte>, pub burst_out: Option<HumanByte>,
} }
@ -100,13 +100,13 @@ impl RateLimitConfig {
}, },
}, },
)] )]
#[derive(Serialize,Deserialize, Updater)] #[derive(Serialize, Deserialize, Updater)]
#[serde(rename_all = "kebab-case")] #[serde(rename_all = "kebab-case")]
/// Traffic control rule /// Traffic control rule
pub struct TrafficControlRule { pub struct TrafficControlRule {
#[updater(skip)] #[updater(skip)]
pub name: String, pub name: String,
#[serde(skip_serializing_if="Option::is_none")] #[serde(skip_serializing_if = "Option::is_none")]
pub comment: Option<String>, pub comment: Option<String>,
/// Rule applies to Source IPs within this networks /// Rule applies to Source IPs within this networks
pub network: Vec<String>, pub network: Vec<String>,
@ -117,6 +117,6 @@ pub struct TrafficControlRule {
// #[serde(skip_serializing_if="Option::is_none")] // #[serde(skip_serializing_if="Option::is_none")]
// pub shared: Option<bool>, // pub shared: Option<bool>,
/// Enable the rule at specific times /// Enable the rule at specific times
#[serde(skip_serializing_if="Option::is_none")] #[serde(skip_serializing_if = "Option::is_none")]
pub timeframe: Option<Vec<String>>, pub timeframe: Option<Vec<String>>,
} }

View File

@ -1,22 +1,22 @@
use serde::{Deserialize, Serialize}; use serde::{Deserialize, Serialize};
use proxmox_schema::{ use proxmox_schema::{api, BooleanSchema, IntegerSchema, Schema, StringSchema, Updater};
api, BooleanSchema, IntegerSchema, Schema, StringSchema, Updater,
};
use super::{SINGLE_LINE_COMMENT_FORMAT, SINGLE_LINE_COMMENT_SCHEMA};
use super::userid::{Authid, Userid, PROXMOX_TOKEN_ID_SCHEMA}; use super::userid::{Authid, Userid, PROXMOX_TOKEN_ID_SCHEMA};
use super::{SINGLE_LINE_COMMENT_FORMAT, SINGLE_LINE_COMMENT_SCHEMA};
pub const ENABLE_USER_SCHEMA: Schema = BooleanSchema::new( pub const ENABLE_USER_SCHEMA: Schema = BooleanSchema::new(
"Enable the account (default). You can set this to '0' to disable the account.") "Enable the account (default). You can set this to '0' to disable the account.",
.default(true) )
.schema(); .default(true)
.schema();
pub const EXPIRE_USER_SCHEMA: Schema = IntegerSchema::new( pub const EXPIRE_USER_SCHEMA: Schema = IntegerSchema::new(
"Account expiration date (seconds since epoch). '0' means no expiration date.") "Account expiration date (seconds since epoch). '0' means no expiration date.",
.default(0) )
.minimum(0) .default(0)
.schema(); .minimum(0)
.schema();
pub const FIRST_NAME_SCHEMA: Schema = StringSchema::new("First name.") pub const FIRST_NAME_SCHEMA: Schema = StringSchema::new("First name.")
.format(&SINGLE_LINE_COMMENT_FORMAT) .format(&SINGLE_LINE_COMMENT_FORMAT)
@ -75,23 +75,23 @@ pub const EMAIL_SCHEMA: Schema = StringSchema::new("E-Mail Address.")
}, },
} }
)] )]
#[derive(Serialize,Deserialize)] #[derive(Serialize, Deserialize)]
/// User properties with added list of ApiTokens /// User properties with added list of ApiTokens
pub struct UserWithTokens { pub struct UserWithTokens {
pub userid: Userid, pub userid: Userid,
#[serde(skip_serializing_if="Option::is_none")] #[serde(skip_serializing_if = "Option::is_none")]
pub comment: Option<String>, pub comment: Option<String>,
#[serde(skip_serializing_if="Option::is_none")] #[serde(skip_serializing_if = "Option::is_none")]
pub enable: Option<bool>, pub enable: Option<bool>,
#[serde(skip_serializing_if="Option::is_none")] #[serde(skip_serializing_if = "Option::is_none")]
pub expire: Option<i64>, pub expire: Option<i64>,
#[serde(skip_serializing_if="Option::is_none")] #[serde(skip_serializing_if = "Option::is_none")]
pub firstname: Option<String>, pub firstname: Option<String>,
#[serde(skip_serializing_if="Option::is_none")] #[serde(skip_serializing_if = "Option::is_none")]
pub lastname: Option<String>, pub lastname: Option<String>,
#[serde(skip_serializing_if="Option::is_none")] #[serde(skip_serializing_if = "Option::is_none")]
pub email: Option<String>, pub email: Option<String>,
#[serde(skip_serializing_if="Vec::is_empty", default)] #[serde(skip_serializing_if = "Vec::is_empty", default)]
pub tokens: Vec<ApiToken>, pub tokens: Vec<ApiToken>,
} }
@ -114,15 +114,15 @@ pub struct UserWithTokens {
}, },
} }
)] )]
#[derive(Serialize,Deserialize)] #[derive(Serialize, Deserialize)]
/// ApiToken properties. /// ApiToken properties.
pub struct ApiToken { pub struct ApiToken {
pub tokenid: Authid, pub tokenid: Authid,
#[serde(skip_serializing_if="Option::is_none")] #[serde(skip_serializing_if = "Option::is_none")]
pub comment: Option<String>, pub comment: Option<String>,
#[serde(skip_serializing_if="Option::is_none")] #[serde(skip_serializing_if = "Option::is_none")]
pub enable: Option<bool>, pub enable: Option<bool>,
#[serde(skip_serializing_if="Option::is_none")] #[serde(skip_serializing_if = "Option::is_none")]
pub expire: Option<i64>, pub expire: Option<i64>,
} }
@ -172,22 +172,22 @@ impl ApiToken {
}, },
} }
)] )]
#[derive(Serialize,Deserialize,Updater)] #[derive(Serialize, Deserialize, Updater)]
/// User properties. /// User properties.
pub struct User { pub struct User {
#[updater(skip)] #[updater(skip)]
pub userid: Userid, pub userid: Userid,
#[serde(skip_serializing_if="Option::is_none")] #[serde(skip_serializing_if = "Option::is_none")]
pub comment: Option<String>, pub comment: Option<String>,
#[serde(skip_serializing_if="Option::is_none")] #[serde(skip_serializing_if = "Option::is_none")]
pub enable: Option<bool>, pub enable: Option<bool>,
#[serde(skip_serializing_if="Option::is_none")] #[serde(skip_serializing_if = "Option::is_none")]
pub expire: Option<i64>, pub expire: Option<i64>,
#[serde(skip_serializing_if="Option::is_none")] #[serde(skip_serializing_if = "Option::is_none")]
pub firstname: Option<String>, pub firstname: Option<String>,
#[serde(skip_serializing_if="Option::is_none")] #[serde(skip_serializing_if = "Option::is_none")]
pub lastname: Option<String>, pub lastname: Option<String>,
#[serde(skip_serializing_if="Option::is_none")] #[serde(skip_serializing_if = "Option::is_none")]
pub email: Option<String>, pub email: Option<String>,
} }

View File

@ -39,15 +39,35 @@ use proxmox_schema::{
// slash is not allowed because it is used as pve API delimiter // slash is not allowed because it is used as pve API delimiter
// also see "man useradd" // also see "man useradd"
#[macro_export] #[macro_export]
macro_rules! USER_NAME_REGEX_STR { () => (r"(?:[^\s:/[:cntrl:]]+)") } macro_rules! USER_NAME_REGEX_STR {
() => {
r"(?:[^\s:/[:cntrl:]]+)"
};
}
#[macro_export] #[macro_export]
macro_rules! GROUP_NAME_REGEX_STR { () => (USER_NAME_REGEX_STR!()) } macro_rules! GROUP_NAME_REGEX_STR {
() => {
USER_NAME_REGEX_STR!()
};
}
#[macro_export] #[macro_export]
macro_rules! TOKEN_NAME_REGEX_STR { () => (PROXMOX_SAFE_ID_REGEX_STR!()) } macro_rules! TOKEN_NAME_REGEX_STR {
() => {
PROXMOX_SAFE_ID_REGEX_STR!()
};
}
#[macro_export] #[macro_export]
macro_rules! USER_ID_REGEX_STR { () => (concat!(USER_NAME_REGEX_STR!(), r"@", PROXMOX_SAFE_ID_REGEX_STR!())) } macro_rules! USER_ID_REGEX_STR {
() => {
concat!(USER_NAME_REGEX_STR!(), r"@", PROXMOX_SAFE_ID_REGEX_STR!())
};
}
#[macro_export] #[macro_export]
macro_rules! APITOKEN_ID_REGEX_STR { () => (concat!(USER_ID_REGEX_STR!() , r"!", TOKEN_NAME_REGEX_STR!())) } macro_rules! APITOKEN_ID_REGEX_STR {
() => {
concat!(USER_ID_REGEX_STR!(), r"!", TOKEN_NAME_REGEX_STR!())
};
}
const_regex! { const_regex! {
pub PROXMOX_USER_NAME_REGEX = concat!(r"^", USER_NAME_REGEX_STR!(), r"$"); pub PROXMOX_USER_NAME_REGEX = concat!(r"^", USER_NAME_REGEX_STR!(), r"$");
@ -238,7 +258,8 @@ impl TryFrom<String> for Realm {
type Error = Error; type Error = Error;
fn try_from(s: String) -> Result<Self, Error> { fn try_from(s: String) -> Result<Self, Error> {
PROXMOX_AUTH_REALM_STRING_SCHEMA.check_constraints(&s) PROXMOX_AUTH_REALM_STRING_SCHEMA
.check_constraints(&s)
.map_err(|_| format_err!("invalid realm"))?; .map_err(|_| format_err!("invalid realm"))?;
Ok(Self(s)) Ok(Self(s))
@ -249,7 +270,8 @@ impl<'a> TryFrom<&'a str> for &'a RealmRef {
type Error = Error; type Error = Error;
fn try_from(s: &'a str) -> Result<&'a RealmRef, Error> { fn try_from(s: &'a str) -> Result<&'a RealmRef, Error> {
PROXMOX_AUTH_REALM_STRING_SCHEMA.check_constraints(s) PROXMOX_AUTH_REALM_STRING_SCHEMA
.check_constraints(s)
.map_err(|_| format_err!("invalid realm"))?; .map_err(|_| format_err!("invalid realm"))?;
Ok(RealmRef::new(s)) Ok(RealmRef::new(s))
@ -482,7 +504,8 @@ impl std::str::FromStr for Userid {
bail!("invalid user name in user id"); bail!("invalid user name in user id");
} }
PROXMOX_AUTH_REALM_STRING_SCHEMA.check_constraints(realm) PROXMOX_AUTH_REALM_STRING_SCHEMA
.check_constraints(realm)
.map_err(|_| format_err!("invalid realm in user id"))?; .map_err(|_| format_err!("invalid realm in user id"))?;
Ok(Self::from((UsernameRef::new(name), RealmRef::new(realm)))) Ok(Self::from((UsernameRef::new(name), RealmRef::new(realm))))
@ -503,7 +526,8 @@ impl TryFrom<String> for Userid {
bail!("invalid user name in user id"); bail!("invalid user name in user id");
} }
PROXMOX_AUTH_REALM_STRING_SCHEMA.check_constraints(&data[(name_len + 1)..]) PROXMOX_AUTH_REALM_STRING_SCHEMA
.check_constraints(&data[(name_len + 1)..])
.map_err(|_| format_err!("invalid realm in user id"))?; .map_err(|_| format_err!("invalid realm in user id"))?;
Ok(Self { data, name_len }) Ok(Self { data, name_len })
@ -532,7 +556,7 @@ impl PartialEq<String> for Userid {
#[derive(Clone, Debug, Eq, PartialEq, Hash, UpdaterType)] #[derive(Clone, Debug, Eq, PartialEq, Hash, UpdaterType)]
pub struct Authid { pub struct Authid {
user: Userid, user: Userid,
tokenname: Option<Tokenname> tokenname: Option<Tokenname>,
} }
impl ApiType for Authid { impl ApiType for Authid {
@ -652,7 +676,7 @@ impl TryFrom<String> for Authid {
data.truncate(realm_end); data.truncate(realm_end);
let user:Userid = data.parse()?; let user: Userid = data.parse()?;
Ok(Self { user, tokenname }) Ok(Self { user, tokenname })
} }
@ -679,7 +703,10 @@ fn test_token_id() {
let token_userid = auth_id.user(); let token_userid = auth_id.user();
assert_eq!(&userid, token_userid); assert_eq!(&userid, token_userid);
assert!(auth_id.is_token()); assert!(auth_id.is_token());
assert_eq!(auth_id.tokenname().expect("Token has tokenname").as_str(), TokennameRef::new("bar").as_str()); assert_eq!(
auth_id.tokenname().expect("Token has tokenname").as_str(),
TokennameRef::new("bar").as_str()
);
assert_eq!(auth_id.to_string(), "test@pam!bar".to_string()); assert_eq!(auth_id.to_string(), "test@pam!bar".to_string());
} }

View File

@ -6,8 +6,7 @@ const_regex! {
pub ZPOOL_NAME_REGEX = r"^[a-zA-Z][a-z0-9A-Z\-_.:]+$"; pub ZPOOL_NAME_REGEX = r"^[a-zA-Z][a-z0-9A-Z\-_.:]+$";
} }
pub const ZFS_ASHIFT_SCHEMA: Schema = IntegerSchema::new( pub const ZFS_ASHIFT_SCHEMA: Schema = IntegerSchema::new("Pool sector size exponent.")
"Pool sector size exponent.")
.minimum(9) .minimum(9)
.maximum(16) .maximum(16)
.default(12) .default(12)
@ -59,7 +58,7 @@ pub enum ZfsRaidLevel {
#[api()] #[api()]
#[derive(Debug, Serialize, Deserialize)] #[derive(Debug, Serialize, Deserialize)]
#[serde(rename_all="kebab-case")] #[serde(rename_all = "kebab-case")]
/// zpool list item /// zpool list item
pub struct ZpoolListItem { pub struct ZpoolListItem {
/// zpool name /// zpool name