* [pbs-devel] [RFC proxmox{, -datacenter-manager} 0/6] Add fallback variants to PBS API types
@ 2025-11-20 14:50 Stefan Hanreich
2025-11-20 14:50 ` [pbs-devel] [PATCH proxmox 1/5] proxmox-upgrade-checks: fix meta package version check Stefan Hanreich
` (5 more replies)
0 siblings, 6 replies; 9+ messages in thread
From: Stefan Hanreich @ 2025-11-20 14:50 UTC (permalink / raw)
To: pbs-devel
## Introduction
This patch series adds fallback variants to all enums that are an ApiType and
public in pbs-api-types. The main motivation is to provide forward compatibility
for PDM in case any of the enums gets extended in the future. The main
difference between the PBS and PVE api type implementation is, that those
fallback variants are guarded behind a feature flag, so PBS can use the strict
types internally, but PDM can use the lenient types.
A lot of the same reasoning and problems from the respective pve-api-types patch
series [1] apply, so I'm linking it here for reference.
## Maintainer's Notes
* pve-api-types depends on the new proxmox-fixed-string
* pbs-api-types depends on the new proxmox-fixed-string
* proxmox-datacenter-manager requires pbs-api-types with new feature flag
[1] https://lore.proxmox.com/pdm-devel/88845236-a159-4c98-be8a-def920e49a8b@proxmox.com/T/#mce9e11c9644df097fdcfeffa0e2c6e2ccfb8fb4d
proxmox:
Stefan Hanreich (5):
proxmox-upgrade-checks: fix meta package version check
proxmox-fixed-string: extract FixedString into own crate
proxmox-fixed-string: implement hash trait
pve-api-types: add proxmox-fixed-string
pbs-api-types: add fallback variants to enums in public API
Cargo.toml | 2 ++
pbs-api-types/Cargo.toml | 4 +++
pbs-api-types/debian/control | 8 ++++-
pbs-api-types/src/crypto.rs | 6 ++++
pbs-api-types/src/datastore.rs | 36 +++++++++++++++++++
pbs-api-types/src/file_restore.rs | 6 ++++
pbs-api-types/src/jobs.rs | 13 +++++++
pbs-api-types/src/key_derivation.rs | 6 ++++
pbs-api-types/src/ldap.rs | 9 +++++
pbs-api-types/src/lib.rs | 12 +++++++
pbs-api-types/src/maintenance.rs | 6 ++++
pbs-api-types/src/metrics.rs | 10 ++++++
pbs-api-types/src/node.rs | 5 +++
pbs-api-types/src/tape/changer.rs | 6 ++++
pbs-api-types/src/tape/device.rs | 6 ++++
pbs-api-types/src/tape/drive.rs | 9 +++++
pbs-api-types/src/tape/media_status.rs | 6 ++++
pbs-api-types/src/zfs.rs | 8 +++++
proxmox-fixed-string/Cargo.toml | 14 ++++++++
proxmox-fixed-string/debian/changelog | 5 +++
proxmox-fixed-string/debian/control | 34 ++++++++++++++++++
proxmox-fixed-string/debian/copyright | 18 ++++++++++
proxmox-fixed-string/debian/debcargo.toml | 7 ++++
.../src/lib.rs | 7 ++++
proxmox-upgrade-checks/src/lib.rs | 8 ++++-
pve-api-types/Cargo.toml | 1 +
pve-api-types/debian/control | 2 ++
pve-api-types/src/types/mod.rs | 3 +-
28 files changed, 253 insertions(+), 4 deletions(-)
create mode 100644 proxmox-fixed-string/Cargo.toml
create mode 100644 proxmox-fixed-string/debian/changelog
create mode 100644 proxmox-fixed-string/debian/control
create mode 100644 proxmox-fixed-string/debian/copyright
create mode 100644 proxmox-fixed-string/debian/debcargo.toml
rename pve-api-types/src/types/fixed_string.rs => proxmox-fixed-string/src/lib.rs (98%)
proxmox-datacenter-manager:
Stefan Hanreich (1):
tree-wide: add enum fallback variants for pbs api types
Cargo.toml | 2 +-
server/src/metric_collection/rrd_task.rs | 4 ++++
ui/Cargo.toml | 2 +-
ui/src/pbs/snapshot_list.rs | 7 +++++++
4 files changed, 13 insertions(+), 2 deletions(-)
Summary over all repositories:
32 files changed, 266 insertions(+), 6 deletions(-)
--
Generated by git-murpp 0.8.0
_______________________________________________
pbs-devel mailing list
pbs-devel@lists.proxmox.com
https://lists.proxmox.com/cgi-bin/mailman/listinfo/pbs-devel
^ permalink raw reply [flat|nested] 9+ messages in thread
* [pbs-devel] [PATCH proxmox 1/5] proxmox-upgrade-checks: fix meta package version check
2025-11-20 14:50 [pbs-devel] [RFC proxmox{, -datacenter-manager} 0/6] Add fallback variants to PBS API types Stefan Hanreich
@ 2025-11-20 14:50 ` Stefan Hanreich
2025-11-20 15:04 ` Shannon Sterz
2025-11-20 14:50 ` [pbs-devel] [PATCH proxmox 2/5] proxmox-fixed-string: extract FixedString into own crate Stefan Hanreich
` (4 subsequent siblings)
5 siblings, 1 reply; 9+ messages in thread
From: Stefan Hanreich @ 2025-11-20 14:50 UTC (permalink / raw)
To: pbs-devel
old_version was changed to be an Option in proxmox-apt-api-types,
so handle that here as well.
Signed-off-by: Stefan Hanreich <s.hanreich@proxmox.com>
---
Not 100% sure about how to handle the case of a missing old_version.
Could you please take a look @Shannon?
proxmox-upgrade-checks/src/lib.rs | 8 +++++++-
1 file changed, 7 insertions(+), 1 deletion(-)
diff --git a/proxmox-upgrade-checks/src/lib.rs b/proxmox-upgrade-checks/src/lib.rs
index 4f76555a..207af04b 100644
--- a/proxmox-upgrade-checks/src/lib.rs
+++ b/proxmox-upgrade-checks/src/lib.rs
@@ -204,7 +204,13 @@ impl UpgradeChecker {
if let Some(meta_pkg) = meta_pkg {
let pkg_version = Regex::new(r"^(\d+)\.(\d+)[.-](\d+)")?;
- let captures = pkg_version.captures(&meta_pkg.old_version);
+
+ let captures = meta_pkg
+ .old_version
+ .as_ref()
+ .map(|old_version| pkg_version.captures(old_version))
+ .flatten();
+
if let Some(captures) = captures {
let maj = Self::extract_version_from_captures(1, &captures)?;
let min = Self::extract_version_from_captures(2, &captures)?;
--
2.47.3
_______________________________________________
pbs-devel mailing list
pbs-devel@lists.proxmox.com
https://lists.proxmox.com/cgi-bin/mailman/listinfo/pbs-devel
^ permalink raw reply [flat|nested] 9+ messages in thread
* [pbs-devel] [PATCH proxmox 2/5] proxmox-fixed-string: extract FixedString into own crate
2025-11-20 14:50 [pbs-devel] [RFC proxmox{, -datacenter-manager} 0/6] Add fallback variants to PBS API types Stefan Hanreich
2025-11-20 14:50 ` [pbs-devel] [PATCH proxmox 1/5] proxmox-upgrade-checks: fix meta package version check Stefan Hanreich
@ 2025-11-20 14:50 ` Stefan Hanreich
2025-11-20 14:50 ` [pbs-devel] [PATCH proxmox 3/5] proxmox-fixed-string: implement hash trait Stefan Hanreich
` (3 subsequent siblings)
5 siblings, 0 replies; 9+ messages in thread
From: Stefan Hanreich @ 2025-11-20 14:50 UTC (permalink / raw)
To: pbs-devel
As a preparation for using FixedString in pbs-api-types, extract the
type into its own crate, so it can be shared between pbs-api-types and
pve-api-types. This code is taken directly from the pve-api-types
implementation without any functional changes.
Signed-off-by: Stefan Hanreich <s.hanreich@proxmox.com>
---
Cargo.toml | 2 +
proxmox-fixed-string/Cargo.toml | 14 ++
proxmox-fixed-string/debian/changelog | 5 +
proxmox-fixed-string/debian/control | 34 +++
proxmox-fixed-string/debian/copyright | 18 ++
proxmox-fixed-string/debian/debcargo.toml | 7 +
proxmox-fixed-string/src/lib.rs | 274 ++++++++++++++++++++++
7 files changed, 354 insertions(+)
create mode 100644 proxmox-fixed-string/Cargo.toml
create mode 100644 proxmox-fixed-string/debian/changelog
create mode 100644 proxmox-fixed-string/debian/control
create mode 100644 proxmox-fixed-string/debian/copyright
create mode 100644 proxmox-fixed-string/debian/debcargo.toml
create mode 100644 proxmox-fixed-string/src/lib.rs
diff --git a/Cargo.toml b/Cargo.toml
index b4691a2d..c07795f3 100644
--- a/Cargo.toml
+++ b/Cargo.toml
@@ -15,6 +15,7 @@ members = [
"proxmox-config-digest",
"proxmox-daemon",
"proxmox-dns-api",
+ "proxmox-fixed-string",
"proxmox-http",
"proxmox-http-error",
"proxmox-human-byte",
@@ -148,6 +149,7 @@ proxmox-async = { version = "0.5.0", path = "proxmox-async" }
proxmox-base64 = { version = "1.0.0", path = "proxmox-base64" }
proxmox-compression = { version = "1.0.0", path = "proxmox-compression" }
proxmox-daemon = { version = "1.0.0", path = "proxmox-daemon" }
+proxmox-fixed-string = { version = "0.1.0", path = "proxmox-fixed-string" }
proxmox-http = { version = "1.0.3", path = "proxmox-http" }
proxmox-http-error = { version = "1.0.0", path = "proxmox-http-error" }
proxmox-human-byte = { version = "1.0.0", path = "proxmox-human-byte" }
diff --git a/proxmox-fixed-string/Cargo.toml b/proxmox-fixed-string/Cargo.toml
new file mode 100644
index 00000000..01273d40
--- /dev/null
+++ b/proxmox-fixed-string/Cargo.toml
@@ -0,0 +1,14 @@
+[package]
+name = "proxmox-fixed-string"
+version = "0.1.0"
+authors.workspace = true
+edition.workspace = true
+license.workspace = true
+repository.workspace = true
+homepage.workspace = true
+exclude.workspace = true
+rust-version.workspace = true
+
+[dependencies]
+serde.workspace = true
+serde_plain.workspace = true
diff --git a/proxmox-fixed-string/debian/changelog b/proxmox-fixed-string/debian/changelog
new file mode 100644
index 00000000..ba1b56b5
--- /dev/null
+++ b/proxmox-fixed-string/debian/changelog
@@ -0,0 +1,5 @@
+rust-proxmox-fixed-string (0.1.0) trixie; urgency=medium
+
+ * Initial release.
+
+ -- Proxmox Support Team <support@proxmox.com> Thu, 20 Nov 2025 13:09:14 +0100
diff --git a/proxmox-fixed-string/debian/control b/proxmox-fixed-string/debian/control
new file mode 100644
index 00000000..99a754b6
--- /dev/null
+++ b/proxmox-fixed-string/debian/control
@@ -0,0 +1,34 @@
+Source: rust-proxmox-fixed-string
+Section: rust
+Priority: optional
+Build-Depends: debhelper-compat (= 13),
+ dh-sequence-cargo
+Build-Depends-Arch: cargo:native <!nocheck>,
+ rustc:native (>= 1.82) <!nocheck>,
+ libstd-rust-dev <!nocheck>,
+ librust-serde-1+default-dev <!nocheck>,
+ librust-serde-plain-1+default-dev <!nocheck>
+Maintainer: Proxmox Support Team <support@proxmox.com>
+Standards-Version: 4.7.2
+Vcs-Git: git://git.proxmox.com/git/proxmox.git
+Vcs-Browser: https://git.proxmox.com/?p=proxmox.git
+Homepage: https://proxmox.com
+X-Cargo-Crate: proxmox-fixed-string
+
+Package: librust-proxmox-fixed-string-dev
+Architecture: any
+Multi-Arch: same
+Depends:
+ ${misc:Depends},
+ librust-serde-1+default-dev,
+ librust-serde-plain-1+default-dev
+Provides:
+ librust-proxmox-fixed-string+default-dev (= ${binary:Version}),
+ librust-proxmox-fixed-string-0-dev (= ${binary:Version}),
+ librust-proxmox-fixed-string-0+default-dev (= ${binary:Version}),
+ librust-proxmox-fixed-string-0.1-dev (= ${binary:Version}),
+ librust-proxmox-fixed-string-0.1+default-dev (= ${binary:Version}),
+ librust-proxmox-fixed-string-0.1.0-dev (= ${binary:Version}),
+ librust-proxmox-fixed-string-0.1.0+default-dev (= ${binary:Version})
+Description: Rust crate "proxmox-fixed-string" - Rust source code
+ Source code for Debianized Rust crate "proxmox-fixed-string"
diff --git a/proxmox-fixed-string/debian/copyright b/proxmox-fixed-string/debian/copyright
new file mode 100644
index 00000000..1ea8a56b
--- /dev/null
+++ b/proxmox-fixed-string/debian/copyright
@@ -0,0 +1,18 @@
+Format: https://www.debian.org/doc/packaging-manuals/copyright-format/1.0/
+
+Files:
+ *
+Copyright: 2019 - 2025 Proxmox Server Solutions GmbH <support@proxmox.com>
+License: AGPL-3.0-or-later
+ This program is free software: you can redistribute it and/or modify it under
+ the terms of the GNU Affero General Public License as published by the Free
+ Software Foundation, either version 3 of the License, or (at your option) any
+ later version.
+ .
+ This program is distributed in the hope that it will be useful, but WITHOUT
+ ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
+ FOR A PARTICULAR PURPOSE. See the GNU Affero General Public License for more
+ details.
+ .
+ You should have received a copy of the GNU Affero General Public License along
+ with this program. If not, see <https://www.gnu.org/licenses/>.
diff --git a/proxmox-fixed-string/debian/debcargo.toml b/proxmox-fixed-string/debian/debcargo.toml
new file mode 100644
index 00000000..b7864cdb
--- /dev/null
+++ b/proxmox-fixed-string/debian/debcargo.toml
@@ -0,0 +1,7 @@
+overlay = "."
+crate_src_path = ".."
+maintainer = "Proxmox Support Team <support@proxmox.com>"
+
+[source]
+vcs_git = "git://git.proxmox.com/git/proxmox.git"
+vcs_browser = "https://git.proxmox.com/?p=proxmox.git"
diff --git a/proxmox-fixed-string/src/lib.rs b/proxmox-fixed-string/src/lib.rs
new file mode 100644
index 00000000..e70e1327
--- /dev/null
+++ b/proxmox-fixed-string/src/lib.rs
@@ -0,0 +1,274 @@
+use std::borrow::Borrow;
+use std::cmp::Ordering;
+use std::error::Error;
+use std::fmt;
+use std::ops::Deref;
+use std::str::FromStr;
+
+use serde::{Deserialize, Serialize};
+
+/// Error type used by constructors of [`FixedString`]
+#[derive(Clone, Copy, Debug)]
+pub struct TooLongError;
+
+impl Error for TooLongError {}
+
+impl fmt::Display for TooLongError {
+ fn fmt(&self, f: &mut fmt::Formatter<'_>) -> Result<(), fmt::Error> {
+ f.write_str("string is longer than 23 characters")
+ }
+}
+
+/// An immutable string type with a maximum size of 23 bytes.
+///
+/// After construction it is guaranteed that its contents are:
+/// * valid utf-8
+/// * not longer than 23 characters
+///
+/// FixedString is immutable, therefore it is sufficient to validate the invariants only at
+/// construction time to guarantee that they will always hold during the lifecycle of the
+/// struct.
+#[derive(Clone, Copy)]
+pub struct FixedString {
+ buf: [u8; 23],
+ len: u8,
+}
+
+impl FixedString {
+ /// Creates a new FixedString instance from a str reference.
+ ///
+ /// # Errors
+ /// This function will return an error if:
+ /// * The passed string is longer than 23 bytes
+ pub fn new(value: &str) -> Result<Self, TooLongError> {
+ if value.len() > 23 {
+ return Err(TooLongError);
+ }
+
+ let mut buf = [0; 23];
+ buf[..value.len()].copy_from_slice(value.as_bytes());
+
+ Ok(Self {
+ buf,
+ // SAFETY: self.len is at least 0 and at most 23, which fits into u8
+ len: value.len() as u8,
+ })
+ }
+
+ /// Returns a str reference to the stored data
+ #[inline]
+ pub fn as_str(&self) -> &str {
+ // SAFETY: self.buf must be a valid utf-8 string by construction
+ unsafe { str::from_utf8_unchecked(self.as_bytes()) }
+ }
+
+ /// Returns a reference to the set bytes in the stored buffer
+ #[inline]
+ pub fn as_bytes(&self) -> &[u8] {
+ // SAFETY: self.len >= 0 and self.len <= 23 by construction
+ unsafe { self.buf.get_unchecked(..self.len as usize) }
+ }
+}
+
+macro_rules! forward_impl_to_bytes {
+ ($($trait:ident {$fn:ident -> $out:ty })+) => {
+ $(
+ impl $trait for FixedString {
+ #[inline]
+ fn $fn(&self, other: &Self) -> $out {
+ <[u8] as $trait>::$fn(self.as_bytes(), other.as_bytes())
+ }
+ }
+ )+
+ };
+}
+
+forward_impl_to_bytes! {
+ PartialEq { eq -> bool }
+ PartialOrd { partial_cmp -> Option<Ordering> }
+ Ord { cmp -> Ordering }
+}
+
+macro_rules! forward_impl_to_str_bidir {
+ ($($trait:ident {$fn:ident -> $out:ty })+) => {
+ $(
+ impl $trait<str> for FixedString {
+ #[inline]
+ fn $fn(&self, other: &str) -> $out {
+ <str as $trait>::$fn(self.as_str(), other)
+ }
+ }
+
+ impl $trait<FixedString> for &str {
+ #[inline]
+ fn $fn(&self, other: &FixedString) -> $out {
+ <str as $trait>::$fn(self, other.as_str())
+ }
+ }
+ )+
+ };
+}
+
+forward_impl_to_str_bidir! {
+ PartialEq { eq -> bool }
+ PartialOrd { partial_cmp -> Option<Ordering> }
+}
+
+impl Eq for FixedString {}
+
+impl fmt::Display for FixedString {
+ fn fmt(&self, f: &mut fmt::Formatter<'_>) -> Result<(), fmt::Error> {
+ fmt::Display::fmt(self.as_str(), f)
+ }
+}
+
+impl fmt::Debug for FixedString {
+ fn fmt(&self, f: &mut fmt::Formatter<'_>) -> Result<(), fmt::Error> {
+ fmt::Display::fmt(self.as_str(), f)
+ }
+}
+
+impl Deref for FixedString {
+ type Target = str;
+
+ fn deref(&self) -> &str {
+ self.as_str()
+ }
+}
+
+impl AsRef<str> for FixedString {
+ fn as_ref(&self) -> &str {
+ self.as_str()
+ }
+}
+
+impl AsRef<[u8]> for FixedString {
+ fn as_ref(&self) -> &[u8] {
+ self.as_bytes()
+ }
+}
+
+impl Borrow<str> for FixedString {
+ fn borrow(&self) -> &str {
+ self.as_str()
+ }
+}
+
+impl TryFrom<String> for FixedString {
+ type Error = TooLongError;
+
+ fn try_from(value: String) -> Result<Self, Self::Error> {
+ FixedString::new(value.as_str())
+ }
+}
+
+impl FromStr for FixedString {
+ type Err = TooLongError;
+
+ fn from_str(value: &str) -> Result<Self, Self::Err> {
+ FixedString::new(value)
+ }
+}
+
+impl TryFrom<&str> for FixedString {
+ type Error = TooLongError;
+
+ fn try_from(value: &str) -> Result<Self, Self::Error> {
+ FixedString::new(value)
+ }
+}
+
+impl Serialize for FixedString {
+ fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
+ where
+ S: serde::Serializer,
+ {
+ serializer.serialize_str(self.as_str())
+ }
+}
+
+impl<'de> Deserialize<'de> for FixedString {
+ fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
+ where
+ D: serde::Deserializer<'de>,
+ {
+ struct FixedStringVisitor;
+
+ impl<'de> serde::de::Visitor<'de> for FixedStringVisitor {
+ type Value = FixedString;
+
+ fn expecting(&self, f: &mut fmt::Formatter) -> std::fmt::Result {
+ f.write_str("a string that is at most 23 bytes long")
+ }
+
+ fn visit_str<E>(self, v: &str) -> Result<Self::Value, E>
+ where
+ E: serde::de::Error,
+ {
+ v.try_into().map_err(E::custom)
+ }
+ }
+
+ deserializer.deserialize_str(FixedStringVisitor)
+ }
+}
+
+#[cfg(test)]
+mod tests {
+ use super::*;
+
+ use serde_plain;
+
+ #[test]
+ fn test_construct() {
+ let fixed_string = FixedString::new("").expect("empty string is valid");
+ assert_eq!("", fixed_string);
+
+ let fixed_string = FixedString::new("a").expect("valid string");
+ assert_eq!("a", fixed_string);
+
+ let fixed_string = FixedString::new("🌏🌏🌏🌏🌏").expect("valid string");
+ assert_eq!("🌏🌏🌏🌏🌏", fixed_string);
+
+ let fixed_string =
+ FixedString::new("aaaaaaaaaaaaaaaaaaaaaaa").expect("23 characters are allowed");
+ assert_eq!("aaaaaaaaaaaaaaaaaaaaaaa", fixed_string);
+
+ FixedString::new("🌏🌏🌏🌏🌏🌏").expect_err("string too long");
+ FixedString::new("aaaaaaaaaaaaaaaaaaaaaaaa").expect_err("string too long");
+ }
+
+ #[test]
+ fn test_serialize_deserialize() {
+ let valid_string = "aaaaaaaaaaaaaaaaaaaaaaa";
+
+ let fixed_string: FixedString =
+ serde_plain::from_str("aaaaaaaaaaaaaaaaaaaaaaa").expect("deserialization works");
+ assert_eq!(valid_string, fixed_string);
+
+ let serialized_string =
+ serde_plain::to_string(&fixed_string).expect("can be serialized into a string");
+ assert_eq!(valid_string, serialized_string);
+
+ serde_plain::from_str::<FixedString>("aaaaaaaaaaaaaaaaaaaaaaaa")
+ .expect_err("cannot deserialize string that is too long");
+ }
+
+ #[test]
+ fn test_ord() {
+ let fixed_string = FixedString::new("abc").expect("valid string");
+
+ assert!(fixed_string == fixed_string);
+ assert!(fixed_string >= fixed_string);
+ assert!(fixed_string <= fixed_string);
+
+ assert!("ab" < fixed_string);
+ assert!("abc" == fixed_string);
+ assert!("abcd" > fixed_string);
+
+ let larger_fixed_string = FixedString::new("abcde").expect("valid string");
+
+ assert!(larger_fixed_string > fixed_string);
+ assert!(fixed_string < larger_fixed_string);
+ }
+}
--
2.47.3
_______________________________________________
pbs-devel mailing list
pbs-devel@lists.proxmox.com
https://lists.proxmox.com/cgi-bin/mailman/listinfo/pbs-devel
^ permalink raw reply [flat|nested] 9+ messages in thread
* [pbs-devel] [PATCH proxmox 3/5] proxmox-fixed-string: implement hash trait
2025-11-20 14:50 [pbs-devel] [RFC proxmox{, -datacenter-manager} 0/6] Add fallback variants to PBS API types Stefan Hanreich
2025-11-20 14:50 ` [pbs-devel] [PATCH proxmox 1/5] proxmox-upgrade-checks: fix meta package version check Stefan Hanreich
2025-11-20 14:50 ` [pbs-devel] [PATCH proxmox 2/5] proxmox-fixed-string: extract FixedString into own crate Stefan Hanreich
@ 2025-11-20 14:50 ` Stefan Hanreich
2025-11-20 14:50 ` [pbs-devel] [PATCH proxmox 4/5] pve-api-types: add proxmox-fixed-string Stefan Hanreich
` (2 subsequent siblings)
5 siblings, 0 replies; 9+ messages in thread
From: Stefan Hanreich @ 2025-11-20 14:50 UTC (permalink / raw)
To: pbs-devel
Some enums in pbs-api-types implement Hash, so FixedString needs to
implement it too in order to be usable there.
While it should have been fine to use the whole struct here because
the buffer is zeroed on construction and the length is constant for
equal strings - still only use the actual bytes from the buffer that
contain the current string to avoid potential shenanigans.
Signed-off-by: Stefan Hanreich <s.hanreich@proxmox.com>
---
proxmox-fixed-string/src/lib.rs | 7 +++++++
1 file changed, 7 insertions(+)
diff --git a/proxmox-fixed-string/src/lib.rs b/proxmox-fixed-string/src/lib.rs
index e70e1327..aa3fca2b 100644
--- a/proxmox-fixed-string/src/lib.rs
+++ b/proxmox-fixed-string/src/lib.rs
@@ -2,6 +2,7 @@ use std::borrow::Borrow;
use std::cmp::Ordering;
use std::error::Error;
use std::fmt;
+use std::hash::{Hash, Hasher};
use std::ops::Deref;
use std::str::FromStr;
@@ -154,6 +155,12 @@ impl Borrow<str> for FixedString {
}
}
+impl Hash for FixedString {
+ fn hash<H: Hasher>(&self, state: &mut H) {
+ self.as_bytes().hash(state);
+ }
+}
+
impl TryFrom<String> for FixedString {
type Error = TooLongError;
--
2.47.3
_______________________________________________
pbs-devel mailing list
pbs-devel@lists.proxmox.com
https://lists.proxmox.com/cgi-bin/mailman/listinfo/pbs-devel
^ permalink raw reply [flat|nested] 9+ messages in thread
* [pbs-devel] [PATCH proxmox 4/5] pve-api-types: add proxmox-fixed-string
2025-11-20 14:50 [pbs-devel] [RFC proxmox{, -datacenter-manager} 0/6] Add fallback variants to PBS API types Stefan Hanreich
` (2 preceding siblings ...)
2025-11-20 14:50 ` [pbs-devel] [PATCH proxmox 3/5] proxmox-fixed-string: implement hash trait Stefan Hanreich
@ 2025-11-20 14:50 ` Stefan Hanreich
2025-11-20 14:50 ` [pbs-devel] [PATCH proxmox 5/5] pbs-api-types: add fallback variants to enums in public API Stefan Hanreich
2025-11-20 14:50 ` [pbs-devel] [PATCH proxmox-datacenter-manager 1/1] tree-wide: add enum fallback variants for pbs api types Stefan Hanreich
5 siblings, 0 replies; 9+ messages in thread
From: Stefan Hanreich @ 2025-11-20 14:50 UTC (permalink / raw)
To: pbs-devel
The FixedString type has been moved into its own crate, so it can be
shared with pbs-api-types. Delete the existing implementation and use
the proxmox-fixed-string crate instead. No functional changes
intended.
Signed-off-by: Stefan Hanreich <s.hanreich@proxmox.com>
---
pve-api-types/Cargo.toml | 1 +
pve-api-types/debian/control | 2 +
pve-api-types/src/types/fixed_string.rs | 274 ------------------------
pve-api-types/src/types/mod.rs | 3 +-
4 files changed, 4 insertions(+), 276 deletions(-)
delete mode 100644 pve-api-types/src/types/fixed_string.rs
diff --git a/pve-api-types/Cargo.toml b/pve-api-types/Cargo.toml
index 0088777a..98671079 100644
--- a/pve-api-types/Cargo.toml
+++ b/pve-api-types/Cargo.toml
@@ -22,6 +22,7 @@ serde_json.workspace = true
serde_plain.workspace = true
#
proxmox-api-macro.workspace = true
+proxmox-fixed-string.workspace = true
proxmox-serde = { workspace = true, features = [ "perl" ] }
proxmox-schema = { workspace = true, features = [ "api-types", "api-macro" ] }
diff --git a/pve-api-types/debian/control b/pve-api-types/debian/control
index 5e9cdbc1..ca45b5ab 100644
--- a/pve-api-types/debian/control
+++ b/pve-api-types/debian/control
@@ -9,6 +9,7 @@ Build-Depends-Arch: cargo:native <!nocheck>,
librust-anyhow-1+default-dev <!nocheck>,
librust-async-trait-0.1+default-dev <!nocheck>,
librust-proxmox-api-macro-1+default-dev (>= 1.4.3-~~) <!nocheck>,
+ librust-proxmox-fixed-string-0.1+default-dev <!nocheck>,
librust-proxmox-schema-5+api-macro-dev (>= 5.0.1-~~) <!nocheck>,
librust-proxmox-schema-5+api-types-dev (>= 5.0.1-~~) <!nocheck>,
librust-proxmox-schema-5+default-dev (>= 5.0.1-~~) <!nocheck>,
@@ -35,6 +36,7 @@ Depends:
librust-anyhow-1+default-dev,
librust-async-trait-0.1+default-dev,
librust-proxmox-api-macro-1+default-dev (>= 1.4.3-~~),
+ librust-proxmox-fixed-string-0.1+default-dev,
librust-proxmox-schema-5+api-macro-dev (>= 5.0.1-~~),
librust-proxmox-schema-5+api-types-dev (>= 5.0.1-~~),
librust-proxmox-schema-5+default-dev (>= 5.0.1-~~),
diff --git a/pve-api-types/src/types/fixed_string.rs b/pve-api-types/src/types/fixed_string.rs
deleted file mode 100644
index e70e1327..00000000
--- a/pve-api-types/src/types/fixed_string.rs
+++ /dev/null
@@ -1,274 +0,0 @@
-use std::borrow::Borrow;
-use std::cmp::Ordering;
-use std::error::Error;
-use std::fmt;
-use std::ops::Deref;
-use std::str::FromStr;
-
-use serde::{Deserialize, Serialize};
-
-/// Error type used by constructors of [`FixedString`]
-#[derive(Clone, Copy, Debug)]
-pub struct TooLongError;
-
-impl Error for TooLongError {}
-
-impl fmt::Display for TooLongError {
- fn fmt(&self, f: &mut fmt::Formatter<'_>) -> Result<(), fmt::Error> {
- f.write_str("string is longer than 23 characters")
- }
-}
-
-/// An immutable string type with a maximum size of 23 bytes.
-///
-/// After construction it is guaranteed that its contents are:
-/// * valid utf-8
-/// * not longer than 23 characters
-///
-/// FixedString is immutable, therefore it is sufficient to validate the invariants only at
-/// construction time to guarantee that they will always hold during the lifecycle of the
-/// struct.
-#[derive(Clone, Copy)]
-pub struct FixedString {
- buf: [u8; 23],
- len: u8,
-}
-
-impl FixedString {
- /// Creates a new FixedString instance from a str reference.
- ///
- /// # Errors
- /// This function will return an error if:
- /// * The passed string is longer than 23 bytes
- pub fn new(value: &str) -> Result<Self, TooLongError> {
- if value.len() > 23 {
- return Err(TooLongError);
- }
-
- let mut buf = [0; 23];
- buf[..value.len()].copy_from_slice(value.as_bytes());
-
- Ok(Self {
- buf,
- // SAFETY: self.len is at least 0 and at most 23, which fits into u8
- len: value.len() as u8,
- })
- }
-
- /// Returns a str reference to the stored data
- #[inline]
- pub fn as_str(&self) -> &str {
- // SAFETY: self.buf must be a valid utf-8 string by construction
- unsafe { str::from_utf8_unchecked(self.as_bytes()) }
- }
-
- /// Returns a reference to the set bytes in the stored buffer
- #[inline]
- pub fn as_bytes(&self) -> &[u8] {
- // SAFETY: self.len >= 0 and self.len <= 23 by construction
- unsafe { self.buf.get_unchecked(..self.len as usize) }
- }
-}
-
-macro_rules! forward_impl_to_bytes {
- ($($trait:ident {$fn:ident -> $out:ty })+) => {
- $(
- impl $trait for FixedString {
- #[inline]
- fn $fn(&self, other: &Self) -> $out {
- <[u8] as $trait>::$fn(self.as_bytes(), other.as_bytes())
- }
- }
- )+
- };
-}
-
-forward_impl_to_bytes! {
- PartialEq { eq -> bool }
- PartialOrd { partial_cmp -> Option<Ordering> }
- Ord { cmp -> Ordering }
-}
-
-macro_rules! forward_impl_to_str_bidir {
- ($($trait:ident {$fn:ident -> $out:ty })+) => {
- $(
- impl $trait<str> for FixedString {
- #[inline]
- fn $fn(&self, other: &str) -> $out {
- <str as $trait>::$fn(self.as_str(), other)
- }
- }
-
- impl $trait<FixedString> for &str {
- #[inline]
- fn $fn(&self, other: &FixedString) -> $out {
- <str as $trait>::$fn(self, other.as_str())
- }
- }
- )+
- };
-}
-
-forward_impl_to_str_bidir! {
- PartialEq { eq -> bool }
- PartialOrd { partial_cmp -> Option<Ordering> }
-}
-
-impl Eq for FixedString {}
-
-impl fmt::Display for FixedString {
- fn fmt(&self, f: &mut fmt::Formatter<'_>) -> Result<(), fmt::Error> {
- fmt::Display::fmt(self.as_str(), f)
- }
-}
-
-impl fmt::Debug for FixedString {
- fn fmt(&self, f: &mut fmt::Formatter<'_>) -> Result<(), fmt::Error> {
- fmt::Display::fmt(self.as_str(), f)
- }
-}
-
-impl Deref for FixedString {
- type Target = str;
-
- fn deref(&self) -> &str {
- self.as_str()
- }
-}
-
-impl AsRef<str> for FixedString {
- fn as_ref(&self) -> &str {
- self.as_str()
- }
-}
-
-impl AsRef<[u8]> for FixedString {
- fn as_ref(&self) -> &[u8] {
- self.as_bytes()
- }
-}
-
-impl Borrow<str> for FixedString {
- fn borrow(&self) -> &str {
- self.as_str()
- }
-}
-
-impl TryFrom<String> for FixedString {
- type Error = TooLongError;
-
- fn try_from(value: String) -> Result<Self, Self::Error> {
- FixedString::new(value.as_str())
- }
-}
-
-impl FromStr for FixedString {
- type Err = TooLongError;
-
- fn from_str(value: &str) -> Result<Self, Self::Err> {
- FixedString::new(value)
- }
-}
-
-impl TryFrom<&str> for FixedString {
- type Error = TooLongError;
-
- fn try_from(value: &str) -> Result<Self, Self::Error> {
- FixedString::new(value)
- }
-}
-
-impl Serialize for FixedString {
- fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
- where
- S: serde::Serializer,
- {
- serializer.serialize_str(self.as_str())
- }
-}
-
-impl<'de> Deserialize<'de> for FixedString {
- fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
- where
- D: serde::Deserializer<'de>,
- {
- struct FixedStringVisitor;
-
- impl<'de> serde::de::Visitor<'de> for FixedStringVisitor {
- type Value = FixedString;
-
- fn expecting(&self, f: &mut fmt::Formatter) -> std::fmt::Result {
- f.write_str("a string that is at most 23 bytes long")
- }
-
- fn visit_str<E>(self, v: &str) -> Result<Self::Value, E>
- where
- E: serde::de::Error,
- {
- v.try_into().map_err(E::custom)
- }
- }
-
- deserializer.deserialize_str(FixedStringVisitor)
- }
-}
-
-#[cfg(test)]
-mod tests {
- use super::*;
-
- use serde_plain;
-
- #[test]
- fn test_construct() {
- let fixed_string = FixedString::new("").expect("empty string is valid");
- assert_eq!("", fixed_string);
-
- let fixed_string = FixedString::new("a").expect("valid string");
- assert_eq!("a", fixed_string);
-
- let fixed_string = FixedString::new("🌏🌏🌏🌏🌏").expect("valid string");
- assert_eq!("🌏🌏🌏🌏🌏", fixed_string);
-
- let fixed_string =
- FixedString::new("aaaaaaaaaaaaaaaaaaaaaaa").expect("23 characters are allowed");
- assert_eq!("aaaaaaaaaaaaaaaaaaaaaaa", fixed_string);
-
- FixedString::new("🌏🌏🌏🌏🌏🌏").expect_err("string too long");
- FixedString::new("aaaaaaaaaaaaaaaaaaaaaaaa").expect_err("string too long");
- }
-
- #[test]
- fn test_serialize_deserialize() {
- let valid_string = "aaaaaaaaaaaaaaaaaaaaaaa";
-
- let fixed_string: FixedString =
- serde_plain::from_str("aaaaaaaaaaaaaaaaaaaaaaa").expect("deserialization works");
- assert_eq!(valid_string, fixed_string);
-
- let serialized_string =
- serde_plain::to_string(&fixed_string).expect("can be serialized into a string");
- assert_eq!(valid_string, serialized_string);
-
- serde_plain::from_str::<FixedString>("aaaaaaaaaaaaaaaaaaaaaaaa")
- .expect_err("cannot deserialize string that is too long");
- }
-
- #[test]
- fn test_ord() {
- let fixed_string = FixedString::new("abc").expect("valid string");
-
- assert!(fixed_string == fixed_string);
- assert!(fixed_string >= fixed_string);
- assert!(fixed_string <= fixed_string);
-
- assert!("ab" < fixed_string);
- assert!("abc" == fixed_string);
- assert!("abcd" > fixed_string);
-
- let larger_fixed_string = FixedString::new("abcde").expect("valid string");
-
- assert!(larger_fixed_string > fixed_string);
- assert!(fixed_string < larger_fixed_string);
- }
-}
diff --git a/pve-api-types/src/types/mod.rs b/pve-api-types/src/types/mod.rs
index 63209d84..e5eed817 100644
--- a/pve-api-types/src/types/mod.rs
+++ b/pve-api-types/src/types/mod.rs
@@ -17,8 +17,7 @@ pub mod array;
pub mod stringlist;
pub mod verifiers;
-mod fixed_string;
-pub use fixed_string::{FixedString, TooLongError};
+use proxmox_fixed_string::FixedString;
include!("../generated/types.rs");
--
2.47.3
_______________________________________________
pbs-devel mailing list
pbs-devel@lists.proxmox.com
https://lists.proxmox.com/cgi-bin/mailman/listinfo/pbs-devel
^ permalink raw reply [flat|nested] 9+ messages in thread
* [pbs-devel] [PATCH proxmox 5/5] pbs-api-types: add fallback variants to enums in public API
2025-11-20 14:50 [pbs-devel] [RFC proxmox{, -datacenter-manager} 0/6] Add fallback variants to PBS API types Stefan Hanreich
` (3 preceding siblings ...)
2025-11-20 14:50 ` [pbs-devel] [PATCH proxmox 4/5] pve-api-types: add proxmox-fixed-string Stefan Hanreich
@ 2025-11-20 14:50 ` Stefan Hanreich
2025-11-20 14:50 ` [pbs-devel] [PATCH proxmox-datacenter-manager 1/1] tree-wide: add enum fallback variants for pbs api types Stefan Hanreich
5 siblings, 0 replies; 9+ messages in thread
From: Stefan Hanreich @ 2025-11-20 14:50 UTC (permalink / raw)
To: pbs-devel
In order to be able to provide forwards-compatibility in the Proxmox
Datacenter Manager, add fallback variants to all enums that are
exposed in the public API. This prevents breakage on PDM side whenever
a new enum variant is added in PBS.
The fallbacks are feature-guarded, so PBS can use the strict enums
without having to handle any fallback variants. This is unnecessary,
because pbs-api-types and proxmox-backup are updated in tandem, so
proxmox-backup should never run into the situation of having to handle
unknown variants. Only the datacenter manager will use this feature
flag for now, since it needs to potentially handle unknown variants.
Signed-off-by: Stefan Hanreich <s.hanreich@proxmox.com>
---
pbs-api-types/Cargo.toml | 4 +++
pbs-api-types/debian/control | 8 +++++-
pbs-api-types/src/crypto.rs | 6 +++++
pbs-api-types/src/datastore.rs | 36 ++++++++++++++++++++++++++
pbs-api-types/src/file_restore.rs | 6 +++++
pbs-api-types/src/jobs.rs | 13 ++++++++++
pbs-api-types/src/key_derivation.rs | 6 +++++
pbs-api-types/src/ldap.rs | 9 +++++++
pbs-api-types/src/lib.rs | 12 +++++++++
pbs-api-types/src/maintenance.rs | 6 +++++
pbs-api-types/src/metrics.rs | 10 +++++++
pbs-api-types/src/node.rs | 5 ++++
pbs-api-types/src/tape/changer.rs | 6 +++++
pbs-api-types/src/tape/device.rs | 6 +++++
pbs-api-types/src/tape/drive.rs | 9 +++++++
pbs-api-types/src/tape/media_status.rs | 6 +++++
pbs-api-types/src/zfs.rs | 8 ++++++
17 files changed, 155 insertions(+), 1 deletion(-)
diff --git a/pbs-api-types/Cargo.toml b/pbs-api-types/Cargo.toml
index dacbf6cd..eca67e9a 100644
--- a/pbs-api-types/Cargo.toml
+++ b/pbs-api-types/Cargo.toml
@@ -18,6 +18,7 @@ serde_plain.workspace = true
proxmox-auth-api = { workspace = true, features = [ "api-types" ] }
proxmox-apt-api-types.workspace = true
+proxmox-fixed-string.workspace = true
proxmox-human-byte.workspace = true
proxmox-lang.workspace=true
proxmox-s3-client = { workspace = true, features = [ "api-types" ] }
@@ -25,3 +26,6 @@ proxmox-schema = { workspace = true, features = [ "api-macro" ] }
proxmox-serde.workspace = true
proxmox-time.workspace = true
proxmox-uuid = { workspace = true, features = [ "serde" ] }
+
+[features]
+enum-fallback = []
diff --git a/pbs-api-types/debian/control b/pbs-api-types/debian/control
index 13dc4db4..cf21fdb8 100644
--- a/pbs-api-types/debian/control
+++ b/pbs-api-types/debian/control
@@ -13,6 +13,7 @@ Build-Depends-Arch: cargo:native <!nocheck>,
librust-proxmox-apt-api-types-2+default-dev (>= 2.0.2-~~) <!nocheck>,
librust-proxmox-auth-api-1+api-types-dev (>= 1.0.5-~~) <!nocheck>,
librust-proxmox-auth-api-1+default-dev (>= 1.0.5-~~) <!nocheck>,
+ librust-proxmox-fixed-string-0.1+default-dev <!nocheck>,
librust-proxmox-human-byte-1+default-dev <!nocheck>,
librust-proxmox-lang-1+default-dev (>= 1.5-~~) <!nocheck>,
librust-proxmox-s3-client-1+api-types-dev (>= 1.2-~~) <!nocheck>,
@@ -45,6 +46,7 @@ Depends:
librust-proxmox-apt-api-types-2+default-dev (>= 2.0.2-~~),
librust-proxmox-auth-api-1+api-types-dev (>= 1.0.5-~~),
librust-proxmox-auth-api-1+default-dev (>= 1.0.5-~~),
+ librust-proxmox-fixed-string-0.1+default-dev,
librust-proxmox-human-byte-1+default-dev,
librust-proxmox-lang-1+default-dev (>= 1.5-~~),
librust-proxmox-s3-client-1+api-types-dev (>= 1.2-~~),
@@ -61,11 +63,15 @@ Depends:
librust-serde-plain-1+default-dev
Provides:
librust-pbs-api-types+default-dev (= ${binary:Version}),
+ librust-pbs-api-types+enum-fallback-dev (= ${binary:Version}),
librust-pbs-api-types-1-dev (= ${binary:Version}),
librust-pbs-api-types-1+default-dev (= ${binary:Version}),
+ librust-pbs-api-types-1+enum-fallback-dev (= ${binary:Version}),
librust-pbs-api-types-1.0-dev (= ${binary:Version}),
librust-pbs-api-types-1.0+default-dev (= ${binary:Version}),
+ librust-pbs-api-types-1.0+enum-fallback-dev (= ${binary:Version}),
librust-pbs-api-types-1.0.6-dev (= ${binary:Version}),
- librust-pbs-api-types-1.0.6+default-dev (= ${binary:Version})
+ librust-pbs-api-types-1.0.6+default-dev (= ${binary:Version}),
+ librust-pbs-api-types-1.0.6+enum-fallback-dev (= ${binary:Version})
Description: API types for Proxmox Backup Server - Rust source code
Source code for Debianized Rust crate "pbs-api-types"
diff --git a/pbs-api-types/src/crypto.rs b/pbs-api-types/src/crypto.rs
index d229eee0..536c53b4 100644
--- a/pbs-api-types/src/crypto.rs
+++ b/pbs-api-types/src/crypto.rs
@@ -3,6 +3,9 @@ use std::fmt::{self, Display};
use anyhow::Error;
use serde::{Deserialize, Serialize};
+#[cfg(feature = "enum-fallback")]
+use proxmox_fixed_string::FixedString;
+
use proxmox_schema::api;
#[api(default: "encrypt")]
@@ -16,6 +19,9 @@ pub enum CryptMode {
Encrypt,
/// Only sign.
SignOnly,
+ #[cfg(feature = "enum-fallback")]
+ #[serde(untagged)]
+ UnknownEnumValue(FixedString),
}
#[derive(Debug, Eq, PartialEq, Hash, Clone, Deserialize, Serialize)]
diff --git a/pbs-api-types/src/datastore.rs b/pbs-api-types/src/datastore.rs
index a214ac25..7dd365c6 100644
--- a/pbs-api-types/src/datastore.rs
+++ b/pbs-api-types/src/datastore.rs
@@ -8,6 +8,9 @@ use anyhow::{bail, format_err, Error};
use const_format::concatcp;
use serde::{Deserialize, Serialize};
+#[cfg(feature = "enum-fallback")]
+use proxmox_fixed_string::FixedString;
+
use proxmox_human_byte::HumanByte;
use proxmox_schema::{
api, const_regex, ApiStringFormat, ApiType, ArraySchema, EnumEntry, IntegerSchema, ReturnType,
@@ -177,6 +180,9 @@ pub enum ChunkOrder {
/// Iterate chunks in inode order
#[default]
Inode,
+ #[cfg(feature = "enum-fallback")]
+ #[serde(untagged)]
+ UnknownEnumValue(FixedString),
}
#[api]
@@ -191,6 +197,9 @@ pub enum DataStoreMountStatus {
/// Datastore is not removable, so there is no mount status.
#[default]
NonRemovable,
+ #[cfg(feature = "enum-fallback")]
+ #[serde(untagged)]
+ UnknownEnumValue(FixedString),
}
#[api]
@@ -222,6 +231,9 @@ pub enum DatastoreFSyncLevel {
/// and consistency.
#[default]
Filesystem,
+ #[cfg(feature = "enum-fallback")]
+ #[serde(untagged)]
+ UnknownEnumValue(FixedString),
}
pub const GC_ATIME_CUTOFF_SCHEMA: Schema = IntegerSchema::new(
@@ -297,6 +309,9 @@ pub enum DatastoreBackendType {
Filesystem,
/// S3 object store
S3,
+ #[cfg(feature = "enum-fallback")]
+ #[serde(untagged)]
+ UnknownEnumValue(FixedString),
}
serde_plain::derive_display_from_serialize!(DatastoreBackendType);
serde_plain::derive_fromstr_from_deserialize!(DatastoreBackendType);
@@ -382,6 +397,10 @@ impl FromStr for DatastoreBackendConfig {
bail!("missing option bucket, required for backend type s3");
}
}
+ #[cfg(feature = "enum-fallback")]
+ DatastoreBackendType::UnknownEnumValue(s) => {
+ bail!("unknown backend type: {s}");
+ }
}
Ok(backend_config)
}
@@ -516,6 +535,9 @@ pub enum NotificationMode {
/// Emit notification events to the notification system
#[default]
NotificationSystem,
+ #[cfg(feature = "enum-fallback")]
+ #[serde(untagged)]
+ UnknownEnumValue(FixedString),
}
impl DataStoreConfig {
@@ -576,6 +598,10 @@ impl DataStoreConfig {
}
}
}
+ #[cfg(feature = "enum-fallback")]
+ Some(MaintenanceType::UnknownEnumValue(s)) => {
+ bail!("unknown maintenance type: {s}")
+ }
None => { /* always OK */ }
}
@@ -697,6 +723,9 @@ pub enum VerifyState {
Ok,
/// Verification reported one or more errors
Failed,
+ #[cfg(feature = "enum-fallback")]
+ #[serde(untagged)]
+ UnknownEnumValue(FixedString),
}
#[api(
@@ -1058,6 +1087,9 @@ pub enum BackupType {
/// "Host" backups.
Host,
+ #[cfg(feature = "enum-fallback")]
+ #[serde(untagged)]
+ UnknownEnumValue(FixedString),
// NOTE: if you add new types, don't forget to adapt the iter below!
}
@@ -1067,6 +1099,8 @@ impl BackupType {
BackupType::Vm => "vm",
BackupType::Ct => "ct",
BackupType::Host => "host",
+ #[cfg(feature = "enum-fallback")]
+ BackupType::UnknownEnumValue(_) => "unknown backup type",
}
}
@@ -1076,6 +1110,8 @@ impl BackupType {
BackupType::Ct => 0,
BackupType::Host => 1,
BackupType::Vm => 2,
+ #[cfg(feature = "enum-fallback")]
+ BackupType::UnknownEnumValue(_) => u8::MAX,
}
}
diff --git a/pbs-api-types/src/file_restore.rs b/pbs-api-types/src/file_restore.rs
index 90657d65..8093e2eb 100644
--- a/pbs-api-types/src/file_restore.rs
+++ b/pbs-api-types/src/file_restore.rs
@@ -1,5 +1,8 @@
use serde::{Deserialize, Serialize};
+#[cfg(feature = "enum-fallback")]
+use proxmox_fixed_string::FixedString;
+
use proxmox_schema::api;
#[api]
@@ -27,4 +30,7 @@ pub enum FileRestoreFormat {
Zip,
/// TAR archive
Tar,
+ #[cfg(feature = "enum-fallback")]
+ #[serde(untagged)]
+ UnknownEnumValue(FixedString),
}
diff --git a/pbs-api-types/src/jobs.rs b/pbs-api-types/src/jobs.rs
index 13317c9d..7e6dfb94 100644
--- a/pbs-api-types/src/jobs.rs
+++ b/pbs-api-types/src/jobs.rs
@@ -5,6 +5,9 @@ use const_format::concatcp;
use regex::Regex;
use serde::{Deserialize, Serialize};
+#[cfg(feature = "enum-fallback")]
+use proxmox_fixed_string::FixedString;
+
use proxmox_schema::*;
use crate::{
@@ -134,6 +137,9 @@ pub enum Notify {
Always,
/// Send notifications for failed jobs only
Error,
+ #[cfg(feature = "enum-fallback")]
+ #[serde(untagged)]
+ UnknownEnumValue(FixedString),
}
#[api(
@@ -549,6 +555,9 @@ pub enum SyncDirection {
Pull,
/// Sync direction push
Push,
+ #[cfg(feature = "enum-fallback")]
+ #[serde(untagged)]
+ UnknownEnumValue(FixedString),
}
impl std::fmt::Display for SyncDirection {
@@ -556,6 +565,10 @@ impl std::fmt::Display for SyncDirection {
match self {
SyncDirection::Pull => f.write_str("pull"),
SyncDirection::Push => f.write_str("push"),
+ #[cfg(feature = "enum-fallback")]
+ SyncDirection::UnknownEnumValue(s) => {
+ write!(f, "unknown sync direction: {s}")
+ }
}
}
}
diff --git a/pbs-api-types/src/key_derivation.rs b/pbs-api-types/src/key_derivation.rs
index 8d6cbc89..57ae353a 100644
--- a/pbs-api-types/src/key_derivation.rs
+++ b/pbs-api-types/src/key_derivation.rs
@@ -1,5 +1,8 @@
use serde::{Deserialize, Serialize};
+#[cfg(feature = "enum-fallback")]
+use proxmox_fixed_string::FixedString;
+
use proxmox_schema::api;
use crate::CERT_FINGERPRINT_SHA256_SCHEMA;
@@ -15,6 +18,9 @@ pub enum Kdf {
Scrypt,
/// Encrtypt the Key with a password using PBKDF2
PBKDF2,
+ #[cfg(feature = "enum-fallback")]
+ #[serde(untagged)]
+ UnknownEnumValue(FixedString),
}
impl Default for Kdf {
diff --git a/pbs-api-types/src/ldap.rs b/pbs-api-types/src/ldap.rs
index 51146160..ff380ffe 100644
--- a/pbs-api-types/src/ldap.rs
+++ b/pbs-api-types/src/ldap.rs
@@ -1,5 +1,8 @@
use serde::{Deserialize, Serialize};
+#[cfg(feature = "enum-fallback")]
+use proxmox_fixed_string::FixedString;
+
use proxmox_schema::{api, ApiStringFormat, ApiType, ArraySchema, Schema, StringSchema, Updater};
use super::{REALM_ID_SCHEMA, SINGLE_LINE_COMMENT_SCHEMA};
@@ -18,6 +21,9 @@ pub enum LdapMode {
/// Secure LDAPS connection
#[serde(rename = "ldaps")]
Ldaps,
+ #[cfg(feature = "enum-fallback")]
+ #[serde(untagged)]
+ UnknownEnumValue(FixedString),
}
#[api(
@@ -145,6 +151,9 @@ pub enum RemoveVanished {
Entry,
/// Remove vanished properties from users (e.g. email)
Properties,
+ #[cfg(feature = "enum-fallback")]
+ #[serde(untagged)]
+ UnknownEnumValue(FixedString),
}
pub const LDAP_DOMAIN_SCHEMA: Schema = StringSchema::new("LDAP Domain").schema();
diff --git a/pbs-api-types/src/lib.rs b/pbs-api-types/src/lib.rs
index 519f6f20..54547291 100644
--- a/pbs-api-types/src/lib.rs
+++ b/pbs-api-types/src/lib.rs
@@ -13,6 +13,9 @@ use proxmox_time::parse_daily_duration;
use proxmox_auth_api::types::{APITOKEN_ID_REGEX_STR, USER_ID_REGEX_STR};
+#[cfg(feature = "enum-fallback")]
+use proxmox_fixed_string::FixedString;
+
pub use proxmox_schema::api_types::SAFE_ID_FORMAT as PROXMOX_SAFE_ID_FORMAT;
pub use proxmox_schema::api_types::SAFE_ID_REGEX as PROXMOX_SAFE_ID_REGEX;
pub use proxmox_schema::api_types::SAFE_ID_REGEX_STR as PROXMOX_SAFE_ID_REGEX_STR;
@@ -269,6 +272,9 @@ pub enum NodePowerCommand {
Reboot,
/// Shutdown the server
Shutdown,
+ #[cfg(feature = "enum-fallback")]
+ #[serde(untagged)]
+ UnknownEnumValue(FixedString),
}
#[api()]
@@ -284,6 +290,9 @@ pub enum TaskStateType {
Error,
/// Unknown
Unknown,
+ #[cfg(feature = "enum-fallback")]
+ #[serde(untagged)]
+ UnknownEnumValue(FixedString),
}
#[api(
@@ -337,6 +346,9 @@ pub enum RealmType {
Ldap,
/// An Active Directory (AD) realm
Ad,
+ #[cfg(feature = "enum-fallback")]
+ #[serde(untagged)]
+ UnknownEnumValue(FixedString),
}
serde_plain::derive_display_from_serialize!(RealmType);
diff --git a/pbs-api-types/src/maintenance.rs b/pbs-api-types/src/maintenance.rs
index a516a1d9..6b97ff10 100644
--- a/pbs-api-types/src/maintenance.rs
+++ b/pbs-api-types/src/maintenance.rs
@@ -2,6 +2,9 @@ use anyhow::{bail, Error};
use serde::{Deserialize, Serialize};
use std::borrow::Cow;
+#[cfg(feature = "enum-fallback")]
+use proxmox_fixed_string::FixedString;
+
use proxmox_schema::{api, const_regex, ApiStringFormat, Schema, StringSchema};
const_regex! {
@@ -51,6 +54,9 @@ pub enum MaintenanceType {
Unmount,
/// The S3 cache store is being refreshed.
S3Refresh,
+ #[cfg(feature = "enum-fallback")]
+ #[serde(untagged)]
+ UnknownEnumValue(FixedString),
}
serde_plain::derive_display_from_serialize!(MaintenanceType);
serde_plain::derive_fromstr_from_deserialize!(MaintenanceType);
diff --git a/pbs-api-types/src/metrics.rs b/pbs-api-types/src/metrics.rs
index ee901276..5ce2c9fc 100644
--- a/pbs-api-types/src/metrics.rs
+++ b/pbs-api-types/src/metrics.rs
@@ -3,6 +3,10 @@ use serde::{Deserialize, Serialize};
use crate::{
HOST_PORT_SCHEMA, HTTP_URL_SCHEMA, PROXMOX_SAFE_ID_FORMAT, SINGLE_LINE_COMMENT_SCHEMA,
};
+
+#[cfg(feature = "enum-fallback")]
+use proxmox_fixed_string::FixedString;
+
use proxmox_schema::{api, Schema, StringSchema, Updater};
pub const METRIC_SERVER_ID_SCHEMA: Schema = StringSchema::new("Metrics Server ID.")
@@ -156,6 +160,9 @@ pub enum MetricServerType {
/// InfluxDB UDP
#[serde(rename = "influxdb-udp")]
InfluxDbUdp,
+ #[cfg(feature = "enum-fallback")]
+ #[serde(untagged)]
+ UnknownEnumValue(FixedString),
}
#[api(
@@ -249,6 +256,9 @@ pub enum MetricDataType {
Counter,
/// derive.
Derive,
+ #[cfg(feature = "enum-fallback")]
+ #[serde(untagged)]
+ UnknownEnumValue(FixedString),
}
serde_plain::derive_display_from_serialize!(MetricDataType);
diff --git a/pbs-api-types/src/node.rs b/pbs-api-types/src/node.rs
index 4231f312..e5b3526c 100644
--- a/pbs-api-types/src/node.rs
+++ b/pbs-api-types/src/node.rs
@@ -3,6 +3,8 @@ use std::ffi::OsStr;
use serde::{Deserialize, Serialize};
use proxmox_auth_api::types::Authid;
+#[cfg(feature = "enum-fallback")]
+use proxmox_fixed_string::FixedString;
use proxmox_schema::*;
use crate::StorageStatus;
@@ -86,6 +88,9 @@ pub enum BootMode {
Efi,
/// The BootMode is Legacy BIOS
LegacyBios,
+ #[cfg(feature = "enum-fallback")]
+ #[serde(untagged)]
+ UnknownEnumValue(FixedString),
}
#[api]
diff --git a/pbs-api-types/src/tape/changer.rs b/pbs-api-types/src/tape/changer.rs
index df3823cf..1f818844 100644
--- a/pbs-api-types/src/tape/changer.rs
+++ b/pbs-api-types/src/tape/changer.rs
@@ -2,6 +2,9 @@
use serde::{Deserialize, Serialize};
+#[cfg(feature = "enum-fallback")]
+use proxmox_fixed_string::FixedString;
+
use proxmox_schema::{
api, ApiStringFormat, ArraySchema, IntegerSchema, Schema, StringSchema, Updater,
};
@@ -102,6 +105,9 @@ pub enum MtxEntryKind {
Slot,
/// Import/Export Slot
ImportExport,
+ #[cfg(feature = "enum-fallback")]
+ #[serde(untagged)]
+ UnknownEnumValue(FixedString),
}
#[api(
diff --git a/pbs-api-types/src/tape/device.rs b/pbs-api-types/src/tape/device.rs
index ff335cdf..0fe2ae41 100644
--- a/pbs-api-types/src/tape/device.rs
+++ b/pbs-api-types/src/tape/device.rs
@@ -1,5 +1,8 @@
use ::serde::{Deserialize, Serialize};
+#[cfg(feature = "enum-fallback")]
+use proxmox_fixed_string::FixedString;
+
use proxmox_schema::api;
#[api()]
@@ -27,6 +30,9 @@ pub enum DeviceKind {
Changer,
/// Normal SCSI tape device
Tape,
+ #[cfg(feature = "enum-fallback")]
+ #[serde(untagged)]
+ UnknownEnumValue(FixedString),
}
#[api(
diff --git a/pbs-api-types/src/tape/drive.rs b/pbs-api-types/src/tape/drive.rs
index e00665cd..720ba5e4 100644
--- a/pbs-api-types/src/tape/drive.rs
+++ b/pbs-api-types/src/tape/drive.rs
@@ -2,6 +2,9 @@
use anyhow::{bail, Error};
use serde::{Deserialize, Serialize};
+#[cfg(feature = "enum-fallback")]
+use proxmox_fixed_string::FixedString;
+
use proxmox_schema::{api, IntegerSchema, Schema, StringSchema, Updater};
use crate::{OptionalDeviceIdentification, CHANGER_NAME_SCHEMA, PROXMOX_SAFE_ID_FORMAT};
@@ -136,6 +139,9 @@ pub enum TapeDensity {
LTO8,
/// LTO9
LTO9,
+ #[cfg(feature = "enum-fallback")]
+ #[serde(untagged)]
+ UnknownEnumValue(FixedString),
}
impl TryFrom<u8> for TapeDensity {
@@ -321,6 +327,9 @@ pub enum DeviceActivity {
ReadingEncrypted,
/// Writing encrypted data
WritingEncrypted,
+ #[cfg(feature = "enum-fallback")]
+ #[serde(untagged)]
+ UnknownEnumValue(FixedString),
}
impl TryFrom<u8> for DeviceActivity {
diff --git a/pbs-api-types/src/tape/media_status.rs b/pbs-api-types/src/tape/media_status.rs
index fdb4e6a0..4a5cc120 100644
--- a/pbs-api-types/src/tape/media_status.rs
+++ b/pbs-api-types/src/tape/media_status.rs
@@ -1,5 +1,8 @@
use serde::{Deserialize, Serialize};
+#[cfg(feature = "enum-fallback")]
+use proxmox_fixed_string::FixedString;
+
use proxmox_schema::api;
#[api()]
@@ -18,4 +21,7 @@ pub enum MediaStatus {
Damaged,
/// Media is marked as retired
Retired,
+ #[cfg(feature = "enum-fallback")]
+ #[serde(untagged)]
+ UnknownEnumValue(FixedString),
}
diff --git a/pbs-api-types/src/zfs.rs b/pbs-api-types/src/zfs.rs
index 57fa5cf4..2cfbd403 100644
--- a/pbs-api-types/src/zfs.rs
+++ b/pbs-api-types/src/zfs.rs
@@ -1,5 +1,7 @@
use serde::{Deserialize, Serialize};
+#[cfg(feature = "enum-fallback")]
+use proxmox_fixed_string::FixedString;
use proxmox_schema::*;
const_regex! {
@@ -35,6 +37,9 @@ pub enum ZfsCompressionType {
On,
/// Disable compression.
Off,
+ #[cfg(feature = "enum-fallback")]
+ #[serde(untagged)]
+ UnknownEnumValue(FixedString),
}
#[api()]
@@ -54,6 +59,9 @@ pub enum ZfsRaidLevel {
RaidZ2,
/// RaidZ3
RaidZ3,
+ #[cfg(feature = "enum-fallback")]
+ #[serde(untagged)]
+ UnknownEnumValue(FixedString),
}
#[api()]
--
2.47.3
_______________________________________________
pbs-devel mailing list
pbs-devel@lists.proxmox.com
https://lists.proxmox.com/cgi-bin/mailman/listinfo/pbs-devel
^ permalink raw reply [flat|nested] 9+ messages in thread
* [pbs-devel] [PATCH proxmox-datacenter-manager 1/1] tree-wide: add enum fallback variants for pbs api types
2025-11-20 14:50 [pbs-devel] [RFC proxmox{, -datacenter-manager} 0/6] Add fallback variants to PBS API types Stefan Hanreich
` (4 preceding siblings ...)
2025-11-20 14:50 ` [pbs-devel] [PATCH proxmox 5/5] pbs-api-types: add fallback variants to enums in public API Stefan Hanreich
@ 2025-11-20 14:50 ` Stefan Hanreich
5 siblings, 0 replies; 9+ messages in thread
From: Stefan Hanreich @ 2025-11-20 14:50 UTC (permalink / raw)
To: pbs-devel
pbs-api-types added fallback variants to all enums that are part of
the REST API. Fix all occurences of the enums that got new fallback
variants in PDM by handling them properly. In the backend, unknown
values are logged, while in the frontend they're simply rendered with
a special icon / font color.
Signed-off-by: Stefan Hanreich <s.hanreich@proxmox.com>
---
Cargo.toml | 2 +-
server/src/metric_collection/rrd_task.rs | 4 ++++
ui/Cargo.toml | 2 +-
ui/src/pbs/snapshot_list.rs | 7 +++++++
4 files changed, 13 insertions(+), 2 deletions(-)
diff --git a/Cargo.toml b/Cargo.toml
index 6ae8f69..c44b181 100644
--- a/Cargo.toml
+++ b/Cargo.toml
@@ -83,7 +83,7 @@ proxmox-node-status = "1"
# API types for PVE (and later PMG?)
pve-api-types = "8.1.0"
# API types for PBS
-pbs-api-types = "1.0.5"
+pbs-api-types = { version = "1.0.5", features = [ "enum-fallback" ] }
# PDM workspace
server = { path = "server" }
diff --git a/server/src/metric_collection/rrd_task.rs b/server/src/metric_collection/rrd_task.rs
index f48968b..48b6de9 100644
--- a/server/src/metric_collection/rrd_task.rs
+++ b/server/src/metric_collection/rrd_task.rs
@@ -165,6 +165,10 @@ fn store_metric_pbs(cache: &RrdCache, remote_name: &str, data_point: &MetricData
MetricDataType::Gauge => DataSourceType::Gauge,
MetricDataType::Counter => DataSourceType::Counter,
MetricDataType::Derive => DataSourceType::Derive,
+ MetricDataType::UnknownEnumValue(s) => {
+ log::warn!("unknown metric data type: {s}");
+ return;
+ }
};
cache.update_value(
diff --git a/ui/Cargo.toml b/ui/Cargo.toml
index d9acbe9..ef032ea 100644
--- a/ui/Cargo.toml
+++ b/ui/Cargo.toml
@@ -40,7 +40,7 @@ proxmox-login = "1"
proxmox-schema = "5"
proxmox-rrd-api-types = "1"
proxmox-node-status = "1"
-pbs-api-types = "1.0.3"
+pbs-api-types = { version = "1.0.3", features = [ "enum-fallback" ] }
pdm-api-types = { version = "0.9", path = "../lib/pdm-api-types" }
pdm-client = { version = "0.9", path = "../lib/pdm-client" }
diff --git a/ui/src/pbs/snapshot_list.rs b/ui/src/pbs/snapshot_list.rs
index b82ffd5..564288b 100644
--- a/ui/src/pbs/snapshot_list.rs
+++ b/ui/src/pbs/snapshot_list.rs
@@ -114,6 +114,7 @@ impl SnapshotListComp {
BackupType::Vm => "desktop",
BackupType::Ct => "cube",
BackupType::Host => "building",
+ BackupType::UnknownEnumValue(_) => "question-circle-o",
},
group.to_string(),
),
@@ -234,6 +235,7 @@ impl Component for SnapshotListComp {
match state.state {
VerifyState::Ok => verify_state.ok += 1,
VerifyState::Failed => verify_state.failed += 1,
+ VerifyState::UnknownEnumValue(_) => {}
}
let age_days = (now - state.upid.starttime) / (30 * 24 * 60 * 60);
@@ -436,6 +438,11 @@ fn render_verification(entry: &SnapshotTreeEntry) -> Html {
let (text, icon_class, class) = match state.state {
VerifyState::Ok => (tr!("Ok"), "check", FontColor::Success),
VerifyState::Failed => (tr!("Failed"), "times", FontColor::Warning),
+ VerifyState::UnknownEnumValue(s) => (
+ tr!("Unknown ({0})", s),
+ "question-circle-o",
+ FontColor::Error,
+ ),
};
let icon = Fa::new(icon_class).class(class).padding_end(2);
Tooltip::new(html! {<>{icon}<span>{text}</span></>})
--
2.47.3
_______________________________________________
pbs-devel mailing list
pbs-devel@lists.proxmox.com
https://lists.proxmox.com/cgi-bin/mailman/listinfo/pbs-devel
^ permalink raw reply [flat|nested] 9+ messages in thread
* Re: [pbs-devel] [PATCH proxmox 1/5] proxmox-upgrade-checks: fix meta package version check
2025-11-20 14:50 ` [pbs-devel] [PATCH proxmox 1/5] proxmox-upgrade-checks: fix meta package version check Stefan Hanreich
@ 2025-11-20 15:04 ` Shannon Sterz
2025-11-20 15:08 ` Stefan Hanreich
0 siblings, 1 reply; 9+ messages in thread
From: Shannon Sterz @ 2025-11-20 15:04 UTC (permalink / raw)
To: Stefan Hanreich; +Cc: Proxmox Backup Server development discussion
On Thu Nov 20, 2025 at 3:50 PM CET, Stefan Hanreich wrote:
> old_version was changed to be an Option in proxmox-apt-api-types,
> so handle that here as well.
>
> Signed-off-by: Stefan Hanreich <s.hanreich@proxmox.com>
> ---
> Not 100% sure about how to handle the case of a missing old_version.
> Could you please take a look @Shannon?
>
> proxmox-upgrade-checks/src/lib.rs | 8 +++++++-
> 1 file changed, 7 insertions(+), 1 deletion(-)
>
> diff --git a/proxmox-upgrade-checks/src/lib.rs b/proxmox-upgrade-checks/src/lib.rs
> index 4f76555a..207af04b 100644
> --- a/proxmox-upgrade-checks/src/lib.rs
> +++ b/proxmox-upgrade-checks/src/lib.rs
> @@ -204,7 +204,13 @@ impl UpgradeChecker {
>
> if let Some(meta_pkg) = meta_pkg {
> let pkg_version = Regex::new(r"^(\d+)\.(\d+)[.-](\d+)")?;
> - let captures = pkg_version.captures(&meta_pkg.old_version);
> +
> + let captures = meta_pkg
> + .old_version
> + .as_ref()
> + .map(|old_version| pkg_version.captures(old_version))
> + .flatten();
> +
> if let Some(captures) = captures {
> let maj = Self::extract_version_from_captures(1, &captures)?;
> let min = Self::extract_version_from_captures(2, &captures)?;
ah thanks for catching this, i somehow missed this, i'll need to ping my
series finally switching pdm over to this crate again. anyhow, for now
this is fine, though i would appreciate this approach a bit more:
```
if let Some(old_version) = meta_pkg.and_then(|m| m.old_version.as_ref()) {
let pkg_version = Regex::new(r"^(\d+)\.(\d+)[.-](\d+)")?;
let captures = pkg_version.captures(old_version);
```
there isn't really anything we can do if we don't have information on
the previous package information. if you do want to stick with your
approach i'd suggest switching to this:
```
let captures = meta_pkg
.old_version
.as_ref()
.and_then(|v| pkg_version.captures(v));
```
_______________________________________________
pbs-devel mailing list
pbs-devel@lists.proxmox.com
https://lists.proxmox.com/cgi-bin/mailman/listinfo/pbs-devel
^ permalink raw reply [flat|nested] 9+ messages in thread
* Re: [pbs-devel] [PATCH proxmox 1/5] proxmox-upgrade-checks: fix meta package version check
2025-11-20 15:04 ` Shannon Sterz
@ 2025-11-20 15:08 ` Stefan Hanreich
0 siblings, 0 replies; 9+ messages in thread
From: Stefan Hanreich @ 2025-11-20 15:08 UTC (permalink / raw)
To: Shannon Sterz; +Cc: Proxmox Backup Server development discussion
On 11/20/25 4:03 PM, Shannon Sterz wrote:
> ah thanks for catching this, i somehow missed this, i'll need to ping my
> series finally switching pdm over to this crate again. anyhow, for now
> this is fine, though i would appreciate this approach a bit more:
>
> ```
> if let Some(old_version) = meta_pkg.and_then(|m| m.old_version.as_ref()) {
> let pkg_version = Regex::new(r"^(\d+)\.(\d+)[.-](\d+)")?;
> let captures = pkg_version.captures(old_version);
> ```
yeah, this seems indeed better - Idk why I always default to map over
and_then in such cases :P
_______________________________________________
pbs-devel mailing list
pbs-devel@lists.proxmox.com
https://lists.proxmox.com/cgi-bin/mailman/listinfo/pbs-devel
^ permalink raw reply [flat|nested] 9+ messages in thread
end of thread, other threads:[~2025-11-20 15:08 UTC | newest]
Thread overview: 9+ messages (download: mbox.gz / follow: Atom feed)
-- links below jump to the message on this page --
2025-11-20 14:50 [pbs-devel] [RFC proxmox{, -datacenter-manager} 0/6] Add fallback variants to PBS API types Stefan Hanreich
2025-11-20 14:50 ` [pbs-devel] [PATCH proxmox 1/5] proxmox-upgrade-checks: fix meta package version check Stefan Hanreich
2025-11-20 15:04 ` Shannon Sterz
2025-11-20 15:08 ` Stefan Hanreich
2025-11-20 14:50 ` [pbs-devel] [PATCH proxmox 2/5] proxmox-fixed-string: extract FixedString into own crate Stefan Hanreich
2025-11-20 14:50 ` [pbs-devel] [PATCH proxmox 3/5] proxmox-fixed-string: implement hash trait Stefan Hanreich
2025-11-20 14:50 ` [pbs-devel] [PATCH proxmox 4/5] pve-api-types: add proxmox-fixed-string Stefan Hanreich
2025-11-20 14:50 ` [pbs-devel] [PATCH proxmox 5/5] pbs-api-types: add fallback variants to enums in public API Stefan Hanreich
2025-11-20 14:50 ` [pbs-devel] [PATCH proxmox-datacenter-manager 1/1] tree-wide: add enum fallback variants for pbs api types Stefan Hanreich
This is an external index of several public inboxes,
see mirroring instructions on how to clone and mirror
all data and code used by this external index.