use proxmox::const_regex;
use proxmox::{IPRE, IPRE_BRACKET, IPV4RE, IPV6RE, IPV4OCTET, IPV6H16, IPV6LS32};
+use pbs_datastore::catalog::CatalogEntryType;
+
use crate::{
backup::{
CryptMode,
Fingerprint,
- BACKUP_ID_REGEX,
DirEntryAttribute,
- CatalogEntryType,
},
server::UPID,
config::acl::Role,
mod tape;
pub use tape::*;
+mod file_restore;
+pub use file_restore::*;
+
+mod acme;
+pub use acme::*;
+
// File names: may not contain slashes, may not start with "."
pub const FILENAME_FORMAT: ApiStringFormat = ApiStringFormat::VerifyFn(|name| {
if name.starts_with('.') {
Ok(())
});
+macro_rules! BACKUP_ID_RE { () => (r"[A-Za-z0-9_][A-Za-z0-9._\-]*") }
+macro_rules! BACKUP_TYPE_RE { () => (r"(?:host|vm|ct)") }
+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")
+}
+macro_rules! SNAPSHOT_PATH_REGEX_STR {
+ () => (
+ concat!(r"(", BACKUP_TYPE_RE!(), ")/(", BACKUP_ID_RE!(), ")/(", BACKUP_TIME_RE!(), r")")
+ );
+}
+
macro_rules! DNS_LABEL { () => (r"(?:[a-zA-Z0-9](?:[a-zA-Z0-9\-]*[a-zA-Z0-9])?)") }
macro_rules! DNS_NAME { () => (concat!(r"(?:(?:", DNS_LABEL!() , r"\.)*", DNS_LABEL!(), ")")) }
+macro_rules! DNS_ALIAS_LABEL { () => (r"(?:[a-zA-Z0-9_](?:[a-zA-Z0-9\-]*[a-zA-Z0-9])?)") }
+macro_rules! DNS_ALIAS_NAME {
+ () => (concat!(r"(?:(?:", DNS_ALIAS_LABEL!() , r"\.)*", DNS_ALIAS_LABEL!(), ")"))
+}
+
macro_rules! CIDR_V4_REGEX_STR { () => (concat!(r"(?:", IPV4RE!(), r"/\d{1,2})$")) }
macro_rules! CIDR_V6_REGEX_STR { () => (concat!(r"(?:", IPV6RE!(), r"/\d{1,3})$")) }
pub DNS_NAME_REGEX = concat!(r"^", DNS_NAME!(), r"$");
+ pub DNS_ALIAS_REGEX = concat!(r"^", DNS_ALIAS_NAME!(), r"$");
+
pub DNS_NAME_OR_IP_REGEX = concat!(r"^(?:", DNS_NAME!(), "|", IPRE!(), r")$");
pub BACKUP_REPO_URL_REGEX = concat!(r"^^(?:(?:(", USER_ID_REGEX_STR!(), "|", APITOKEN_ID_REGEX_STR!(), ")@)?(", DNS_NAME!(), "|", IPRE_BRACKET!() ,"):)?(?:([0-9]{1,5}):)?(", PROXMOX_SAFE_ID_REGEX_STR!(), r")$");
pub UUID_REGEX = r"^[0-9a-f]{8}(?:-[0-9a-f]{4}){3}-[0-9a-f]{12}$";
+ pub BACKUP_TYPE_REGEX = concat!(r"^(", BACKUP_TYPE_RE!(), r")$");
+
+ pub BACKUP_ID_REGEX = concat!(r"^", BACKUP_ID_RE!(), r"$");
+
+ pub BACKUP_DATE_REGEX = concat!(r"^", BACKUP_TIME_RE!() ,r"$");
+
+ pub GROUP_PATH_REGEX = concat!(r"^(", BACKUP_TYPE_RE!(), ")/(", BACKUP_ID_RE!(), r")$");
+
+ pub SNAPSHOT_PATH_REGEX = concat!(r"^", SNAPSHOT_PATH_REGEX_STR!(), r"$");
+
+ pub BACKUP_FILE_REGEX = r"^.*\.([fd]idx|blob)$";
+
pub DATASTORE_MAP_REGEX = concat!(r"(:?", PROXMOX_SAFE_ID_REGEX_STR!(), r"=)?", PROXMOX_SAFE_ID_REGEX_STR!());
+
+ pub TAPE_RESTORE_SNAPSHOT_REGEX = concat!(r"^", PROXMOX_SAFE_ID_REGEX_STR!(), r":", SNAPSHOT_PATH_REGEX_STR!(), r"$");
}
pub const SYSTEMD_DATETIME_FORMAT: ApiStringFormat =
pub const DNS_NAME_FORMAT: ApiStringFormat =
ApiStringFormat::Pattern(&DNS_NAME_REGEX);
+pub const DNS_ALIAS_FORMAT: ApiStringFormat =
+ ApiStringFormat::Pattern(&DNS_ALIAS_REGEX);
+
pub const DNS_NAME_OR_IP_FORMAT: ApiStringFormat =
ApiStringFormat::Pattern(&DNS_NAME_OR_IP_REGEX);
pub const DATASTORE_MAP_FORMAT: ApiStringFormat =
ApiStringFormat::Pattern(&DATASTORE_MAP_REGEX);
+pub const TAPE_RESTORE_SNAPSHOT_FORMAT: ApiStringFormat =
+ ApiStringFormat::Pattern(&TAPE_RESTORE_SNAPSHOT_REGEX);
+
pub const PASSWORD_SCHEMA: Schema = StringSchema::new("Password.")
.format(&PASSWORD_FORMAT)
.min_length(1)
.format(&DATASTORE_MAP_FORMAT)
.min_length(3)
.max_length(65)
+ .type_text("(<source>=)?<target>")
.schema();
pub const DATASTORE_MAP_ARRAY_SCHEMA: Schema = ArraySchema::new(
.format(&ApiStringFormat::PropertyString(&DATASTORE_MAP_ARRAY_SCHEMA))
.schema();
+pub const TAPE_RESTORE_SNAPSHOT_SCHEMA: Schema = StringSchema::new(
+ "A snapshot in the format: 'store:type/id/time")
+ .format(&TAPE_RESTORE_SNAPSHOT_FORMAT)
+ .type_text("store:type/id/time")
+ .schema();
+
pub const MEDIA_SET_UUID_SCHEMA: Schema =
StringSchema::new("MediaSet Uuid (We use the all-zero Uuid to reseve an empty media for a specific pool).")
.format(&UUID_FORMAT)
.max_length(64)
.schema();
+pub const REALM_ID_SCHEMA: Schema = StringSchema::new("Realm name.")
+ .format(&PROXMOX_SAFE_ID_FORMAT)
+ .min_length(2)
+ .max_length(32)
+ .schema();
+
// Complex type definitions
#[api(
}
}
-
#[api()]
-#[derive(Serialize, Deserialize)]
+#[derive(Default, Serialize, Deserialize)]
/// Storage space usage information.
pub struct StorageStatus {
/// Total space (bytes).
}
impl ArchiveEntry {
- pub fn new(filepath: &[u8], entry_type: &DirEntryAttribute) -> Self {
+ pub fn new(filepath: &[u8], entry_type: Option<&DirEntryAttribute>) -> Self {
+ let size = match entry_type {
+ Some(DirEntryAttribute::File { size, .. }) => Some(*size),
+ _ => None,
+ };
+ Self::new_with_size(filepath, entry_type, size)
+ }
+
+ pub fn new_with_size(
+ filepath: &[u8],
+ entry_type: Option<&DirEntryAttribute>,
+ size: Option<u64>,
+ ) -> Self {
Self {
filepath: base64::encode(filepath),
text: String::from_utf8_lossy(filepath.split(|x| *x == b'/').last().unwrap())
.to_string(),
- entry_type: CatalogEntryType::from(entry_type).to_string(),
- leaf: !matches!(entry_type, DirEntryAttribute::Directory { .. }),
- size: match entry_type {
- DirEntryAttribute::File { size, .. } => Some(*size),
- _ => None
+ entry_type: match entry_type {
+ Some(entry_type) => CatalogEntryType::from(entry_type).to_string(),
+ None => "v".to_owned(),
},
+ leaf: !matches!(entry_type, None | Some(DirEntryAttribute::Directory { .. })),
+ size,
mtime: match entry_type {
- DirEntryAttribute::File { mtime, .. } => Some(*mtime),
- _ => None
+ Some(DirEntryAttribute::File { mtime, .. }) => Some(*mtime),
+ _ => None,
},
}
}
},
}
)]
-#[serde(rename_all="kebab-case")]
#[derive(Serialize,Deserialize,Default)]
+#[serde(rename_all="kebab-case")]
/// Job Scheduling Status
pub struct JobScheduleStatus {
#[serde(skip_serializing_if="Option::is_none")]
#[serde(skip_serializing_if="Option::is_none")]
pub last_run_endtime: Option<i64>,
}
+
+#[api]
+#[derive(Serialize, Deserialize, Default)]
+#[serde(rename_all = "kebab-case")]
+/// Node memory usage counters
+pub struct NodeMemoryCounters {
+ /// Total memory
+ pub total: u64,
+ /// Used memory
+ pub used: u64,
+ /// Free memory
+ pub free: u64,
+}
+
+#[api]
+#[derive(Serialize, Deserialize, Default)]
+#[serde(rename_all = "kebab-case")]
+/// Node swap usage counters
+pub struct NodeSwapCounters {
+ /// Total swap
+ pub total: u64,
+ /// Used swap
+ pub used: u64,
+ /// Free swap
+ pub free: u64,
+}
+
+#[api]
+#[derive(Serialize,Deserialize,Default)]
+#[serde(rename_all = "kebab-case")]
+/// Contains general node information such as the fingerprint`
+pub struct NodeInformation {
+ /// The SSL Fingerprint
+ pub fingerprint: String,
+}
+
+#[api]
+#[derive(Serialize, Deserialize, Default)]
+#[serde(rename_all = "kebab-case")]
+/// Information about the CPU
+pub struct NodeCpuInformation {
+ /// The CPU model
+ pub model: String,
+ /// The number of CPU sockets
+ pub sockets: usize,
+ /// The number of CPU cores (incl. threads)
+ pub cpus: usize,
+}
+
+#[api(
+ properties: {
+ memory: {
+ type: NodeMemoryCounters,
+ },
+ root: {
+ type: StorageStatus,
+ },
+ swap: {
+ type: NodeSwapCounters,
+ },
+ loadavg: {
+ type: Array,
+ items: {
+ type: Number,
+ description: "the load",
+ }
+ },
+ cpuinfo: {
+ type: NodeCpuInformation,
+ },
+ info: {
+ type: NodeInformation,
+ }
+ },
+)]
+#[derive(Serialize, Deserialize, Default)]
+#[serde(rename_all = "kebab-case")]
+/// The Node status
+pub struct NodeStatus {
+ pub memory: NodeMemoryCounters,
+ pub root: StorageStatus,
+ pub swap: NodeSwapCounters,
+ /// The current uptime of the server.
+ pub uptime: u64,
+ /// Load for 1, 5 and 15 minutes.
+ pub loadavg: [f64; 3],
+ /// The current kernel version.
+ pub kversion: String,
+ /// Total CPU usage since last query.
+ pub cpu: f64,
+ /// Total IO wait since last query.
+ pub wait: f64,
+ pub cpuinfo: NodeCpuInformation,
+ pub info: NodeInformation,
+}
+
+pub const HTTP_PROXY_SCHEMA: Schema = StringSchema::new(
+ "HTTP proxy configuration [http://]<host>[:port]")
+ .format(&ApiStringFormat::VerifyFn(|s| {
+ proxmox_http::ProxyConfig::parse_proxy_url(s)?;
+ Ok(())
+ }))
+ .min_length(1)
+ .max_length(128)
+ .type_text("[http://]<host>[:port]")
+ .schema();