From mboxrd@z Thu Jan 1 00:00:00 1970 Return-Path: Received: from firstgate.proxmox.com (firstgate.proxmox.com [212.224.123.68]) by lore.proxmox.com (Postfix) with ESMTPS id B89061FF17A for ; Tue, 9 Dec 2025 10:52:18 +0100 (CET) Received: from firstgate.proxmox.com (localhost [127.0.0.1]) by firstgate.proxmox.com (Proxmox) with ESMTP id E5000216A1; Tue, 9 Dec 2025 10:52:57 +0100 (CET) Mime-Version: 1.0 Date: Tue, 09 Dec 2025 10:52:49 +0100 Message-Id: From: "Lukas Wagner" To: "Proxmox Datacenter Manager development discussion" X-Mailer: aerc 0.21.0-0-g5549850facc2-dirty References: <20251205112528.373387-1-c.heiss@proxmox.com> <20251205112528.373387-8-c.heiss@proxmox.com> In-Reply-To: <20251205112528.373387-8-c.heiss@proxmox.com> X-Bm-Milter-Handled: 55990f41-d878-4baa-be0a-ee34c49e34d2 X-Bm-Transport-Timestamp: 1765273964048 X-SPAM-LEVEL: Spam detection results: 0 AWL 0.031 Adjusted score from AWL reputation of From: address BAYES_00 -1.9 Bayes spam probability is 0 to 1% DMARC_MISSING 0.1 Missing DMARC policy KAM_DMARC_STATUS 0.01 Test Rule for DKIM or SPF Failure with Strict Alignment RCVD_IN_VALIDITY_CERTIFIED_BLOCKED 0.001 ADMINISTRATOR NOTICE: The query to Validity was blocked. See https://knowledge.validity.com/hc/en-us/articles/20961730681243 for more information. RCVD_IN_VALIDITY_RPBL_BLOCKED 0.001 ADMINISTRATOR NOTICE: The query to Validity was blocked. See https://knowledge.validity.com/hc/en-us/articles/20961730681243 for more information. RCVD_IN_VALIDITY_SAFE_BLOCKED 0.001 ADMINISTRATOR NOTICE: The query to Validity was blocked. See https://knowledge.validity.com/hc/en-us/articles/20961730681243 for more information. SPF_HELO_NONE 0.001 SPF: HELO does not publish an SPF Record SPF_PASS -0.001 SPF: sender matches SPF record URIBL_BLOCKED 0.001 ADMINISTRATOR NOTICE: The query to URIBL was blocked. See http://wiki.apache.org/spamassassin/DnsBlocklists#dnsbl-block for more information. [lib.rs, answer.rs] Subject: Re: [pdm-devel] [PATCH proxmox v2 07/14] installer-types: implement api type for all externally-used types X-BeenThere: pdm-devel@lists.proxmox.com X-Mailman-Version: 2.1.29 Precedence: list List-Id: Proxmox Datacenter Manager development discussion List-Unsubscribe: , List-Archive: List-Post: List-Help: List-Subscribe: , Reply-To: Proxmox Datacenter Manager development discussion Content-Type: text/plain; charset="us-ascii" Content-Transfer-Encoding: 7bit Errors-To: pdm-devel-bounces@lists.proxmox.com Sender: "pdm-devel" Looks good to me. Reviewed-by: Lukas Wagner On Fri Dec 5, 2025 at 12:25 PM CET, Christoph Heiss wrote: > PDM will (re-)use most of these types directly in the API, thus make > them compatible. > > Signed-off-by: Christoph Heiss > --- > Changes v1 -> v2: > * no changes > > proxmox-installer-types/Cargo.toml | 6 +- > proxmox-installer-types/debian/control | 21 ++++ > proxmox-installer-types/src/answer.rs | 119 +++++++++++++++++++++++ > proxmox-installer-types/src/lib.rs | 37 +++++++ > proxmox-installer-types/src/post_hook.rs | 56 +++++++++++ > 5 files changed, 238 insertions(+), 1 deletion(-) > > diff --git a/proxmox-installer-types/Cargo.toml b/proxmox-installer-types/Cargo.toml > index 96413fe1..8f281e01 100644 > --- a/proxmox-installer-types/Cargo.toml > +++ b/proxmox-installer-types/Cargo.toml > @@ -15,8 +15,12 @@ rust-version.workspace = true > anyhow.workspace = true > serde = { workspace = true, features = ["derive"] } > serde_plain.workspace = true > -proxmox-network-types.workspace = true > +regex = { workspace = true, optional = true } > +proxmox-network-types = { workspace = true, features = ["api-types"] } > +proxmox-schema = { workspace = true, optional = true, features = ["api-macro"] } > +proxmox-section-config = { workspace = true, optional = true } > > [features] > default = [] > +api-types = ["dep:regex", "dep:proxmox-schema", "dep:proxmox-section-config", "proxmox-network-types/api-types"] > legacy = [] > diff --git a/proxmox-installer-types/debian/control b/proxmox-installer-types/debian/control > index d208a014..30c5e7ed 100644 > --- a/proxmox-installer-types/debian/control > +++ b/proxmox-installer-types/debian/control > @@ -30,6 +30,8 @@ Depends: > librust-serde-1+default-dev, > librust-serde-1+derive-dev, > librust-serde-plain-1+default-dev > +Suggests: > + librust-proxmox-installer-types+api-types-dev (= ${binary:Version}) > Provides: > librust-proxmox-installer-types+default-dev (= ${binary:Version}), > librust-proxmox-installer-types+legacy-dev (= ${binary:Version}), > @@ -44,3 +46,22 @@ Provides: > librust-proxmox-installer-types-0.1.0+legacy-dev (= ${binary:Version}) > Description: Type definitions used within the installer - Rust source code > Source code for Debianized Rust crate "proxmox-installer-types" > + > +Package: librust-proxmox-installer-types+api-types-dev > +Architecture: any > +Multi-Arch: same > +Depends: > + ${misc:Depends}, > + librust-proxmox-installer-types-dev (= ${binary:Version}), > + librust-proxmox-network-types-0.1+api-types-dev, > + librust-proxmox-schema-5+api-macro-dev (>= 5.0.1-~~), > + librust-proxmox-schema-5+default-dev (>= 5.0.1-~~), > + librust-proxmox-section-config-3+default-dev (>= 3.1.0-~~), > + librust-regex-1+default-dev (>= 1.5-~~) > +Provides: > + librust-proxmox-installer-types-0+api-types-dev (= ${binary:Version}), > + librust-proxmox-installer-types-0.1+api-types-dev (= ${binary:Version}), > + librust-proxmox-installer-types-0.1.0+api-types-dev (= ${binary:Version}) > +Description: Type definitions used within the installer - feature "api-types" > + This metapackage enables feature "api-types" for the Rust proxmox-installer- > + types crate, by pulling in any additional dependencies needed by that feature. > diff --git a/proxmox-installer-types/src/answer.rs b/proxmox-installer-types/src/answer.rs > index 7129a941..acfadcf8 100644 > --- a/proxmox-installer-types/src/answer.rs > +++ b/proxmox-installer-types/src/answer.rs > @@ -15,15 +15,32 @@ use std::{ > }; > > use proxmox_network_types::{fqdn::Fqdn, ip_address::Cidr}; > + > +#[cfg(feature = "api-types")] > +use proxmox_schema::{api, api_types::PASSWORD_FORMAT, StringSchema, Updater, UpdaterType}; > + > +#[cfg(feature = "api-types")] > +type IpAddr = proxmox_network_types::ip_address::api_types::IpAddr; > +#[cfg(not(feature = "api-types"))] > type IpAddr = std::net::IpAddr; > > +#[cfg(feature = "api-types")] > +proxmox_schema::const_regex! { > + /// A unique two-letter country code, according to ISO 3166-1 (alpha-2). > + pub COUNTRY_CODE_REGEX = r"^[a-z]{2}$"; > +} > + > /// Defines API types used by proxmox-fetch-answer, the first part of the > /// auto-installer. > pub mod fetch { > use serde::{Deserialize, Serialize}; > > + #[cfg(feature = "api-types")] > + use proxmox_schema::api; > + > use crate::SystemInfo; > > + #[cfg_attr(feature = "api-types", api)] > #[derive(Deserialize, Serialize)] > #[serde(rename_all = "kebab-case")] > /// Metadata of the HTTP POST payload, such as schema version of the document. > @@ -50,6 +67,13 @@ pub mod fetch { > } > } > > + #[cfg_attr(feature = "api-types", api( > + properties: { > + sysinfo: { > + flatten: true, > + }, > + }, > + ))] > #[derive(Deserialize, Serialize)] > #[serde(rename_all = "kebab-case")] > /// Data sent in the body of POST request when retrieving the answer file via HTTP(S). > @@ -91,6 +115,14 @@ pub struct AutoInstallerConfig { > pub first_boot: Option, > } > > +/// Machine root password schema. > +#[cfg(feature = "api-types")] > +pub const ROOT_PASSWORD_SCHEMA: proxmox_schema::Schema = StringSchema::new("Root Password.") > + .format(&PASSWORD_FORMAT) > + .min_length(8) > + .max_length(64) > + .schema(); > + > #[derive(Clone, Default, Deserialize, Debug, Serialize, PartialEq)] > #[serde(rename_all = "kebab-case", deny_unknown_fields)] > /// General target system options for setting up the system in an automated > @@ -130,6 +162,7 @@ pub struct GlobalOptions { > pub root_ssh_keys: Vec, > } > > +#[cfg_attr(feature = "api-types", api)] > #[derive(Copy, Clone, Deserialize, Serialize, Debug, Default, PartialEq, Eq)] > #[serde(rename_all = "kebab-case", deny_unknown_fields)] > /// Action to take after the installation completed successfully. > @@ -144,6 +177,7 @@ pub enum RebootMode { > serde_plain::derive_fromstr_from_deserialize!(RebootMode); > > #[derive(Clone, Deserialize, Debug, Serialize, PartialEq)] > +#[cfg_attr(feature = "api-types", derive(Updater))] > #[serde( > untagged, > expecting = "either a fully-qualified domain name or extendend configuration for usage with DHCP must be specified" > @@ -204,6 +238,7 @@ pub enum FqdnSourceMode { > FromDhcp, > } > > +#[cfg_attr(feature = "api-types", api)] > #[derive(Clone, Deserialize, Debug, Serialize, PartialEq)] > #[serde(rename_all = "kebab-case", deny_unknown_fields)] > /// Configuration for the post-installation hook, which runs after an > @@ -216,6 +251,7 @@ pub struct PostNotificationHookInfo { > pub cert_fingerprint: Option, > } > > +#[cfg_attr(feature = "api-types", api)] > #[derive(Clone, Deserialize, Debug, PartialEq, Serialize)] > #[serde(rename_all = "kebab-case", deny_unknown_fields)] > /// Possible sources for the optional first-boot hook script/executable file. > @@ -227,6 +263,7 @@ pub enum FirstBootHookSourceMode { > FromIso, > } > > +#[cfg_attr(feature = "api-types", api)] > #[derive(Clone, Default, Deserialize, Debug, PartialEq, Serialize)] > #[serde(rename_all = "kebab-case", deny_unknown_fields)] > /// Possible orderings for the `proxmox-first-boot` systemd service. > @@ -256,6 +293,7 @@ impl FirstBootHookServiceOrdering { > } > } > > +#[cfg_attr(feature = "api-types", api)] > #[derive(Clone, Deserialize, Debug, Serialize, PartialEq)] > #[serde(rename_all = "kebab-case", deny_unknown_fields)] > /// Describes from where to fetch the first-boot hook script, either being baked into the ISO or > @@ -328,6 +366,13 @@ pub enum NetworkConfig { > FromAnswer(NetworkConfigFromAnswer), > } > > +#[cfg_attr(feature = "api-types", api( > + "id-property": "filesystem", > + "id-schema": { > + type: String, > + description: "filesystem name", > + }, > +))] > #[derive(Clone, Debug, Deserialize, Serialize, PartialEq)] > #[serde(rename_all = "kebab-case", tag = "filesystem")] > /// Filesystem-specific options to set on the root disk. > @@ -365,6 +410,7 @@ impl Display for DiskSelection { > } > } > > +#[cfg_attr(feature = "api-types", api)] > #[derive(Copy, Clone, Default, Deserialize, Debug, PartialEq, Serialize)] > #[serde(rename_all = "lowercase", deny_unknown_fields)] > /// Whether the associated filters must all match for a device or if any one > @@ -486,6 +532,7 @@ impl DiskSetup { > } > } > > +#[cfg_attr(feature = "api-types", api)] > #[derive(Clone, Deserialize, Serialize, Debug, PartialEq)] > #[serde(rename_all = "lowercase", deny_unknown_fields)] > /// Available filesystem during installation. > @@ -500,6 +547,34 @@ pub enum Filesystem { > Btrfs, > } > > +#[cfg_attr(feature = "api-types", api( > + properties: { > + ashift: { > + type: Integer, > + minimum: 9, > + maximum: 16, > + default: 12, > + optional: true, > + }, > + "arc-max": { > + type: Integer, > + // ZFS specifies 64 MiB as the absolute minimum. > + minimum: 64, > + optional: true, > + }, > + copies: { > + type: Integer, > + minimum: 1, > + maximum: 3, > + optional: true, > + }, > + hdsize: { > + type: Number, > + minimum: 2., > + optional: true, > + }, > + }, > +))] > #[derive(Clone, Copy, Default, Deserialize, Debug, Serialize, PartialEq)] > #[serde(rename_all = "kebab-case", deny_unknown_fields)] > /// ZFS-specific filesystem options. > @@ -530,6 +605,35 @@ pub struct ZfsOptions { > pub hdsize: Option, > } > > +#[cfg_attr(feature = "api-types", api( > + properties: { > + hdsize: { > + type: Number, > + minimum: 2., > + optional: true, > + }, > + swapsize: { > + type: Number, > + minimum: 0., > + optional: true, > + }, > + maxroot: { > + type: Number, > + minimum: 2., > + optional: true, > + }, > + maxvz: { > + type: Number, > + minimum: 0., > + optional: true, > + }, > + minfree: { > + type: Number, > + minimum: 0., > + optional: true, > + }, > + }, > +))] > #[derive(Clone, Copy, Default, Deserialize, Serialize, Debug, PartialEq)] > #[serde(rename_all = "kebab-case", deny_unknown_fields)] > /// LVM-specific filesystem options, when using ext4 or xfs as filesystem. > @@ -557,6 +661,14 @@ pub struct LvmOptions { > pub minfree: Option, > } > > +#[cfg_attr(feature = "api-types", api( > + properties: { > + hdsize: { > + minimum: 2., > + optional: true, > + }, > + }, > +))] > #[derive(Clone, Copy, Default, Deserialize, Debug, Serialize, PartialEq)] > #[serde(rename_all = "kebab-case", deny_unknown_fields)] > /// Btrfs-specific filesystem options. > @@ -573,6 +685,7 @@ pub struct BtrfsOptions { > pub compress: Option, > } > > +#[cfg_attr(feature = "api-types", api)] > #[derive(Copy, Clone, Deserialize, Serialize, Debug, Default, PartialEq)] > #[serde(rename_all = "kebab-case", deny_unknown_fields)] > /// Keyboard layout of the system. > @@ -664,6 +777,7 @@ impl Display for KeyboardLayout { > > serde_plain::derive_fromstr_from_deserialize!(KeyboardLayout); > > +#[cfg_attr(feature = "api-types", api)] > #[derive(Copy, Clone, Debug, Deserialize, Serialize, Eq, PartialEq)] > #[serde(rename_all(deserialize = "lowercase", serialize = "UPPERCASE"))] > /// Available Btrfs RAID levels. > @@ -681,6 +795,7 @@ pub enum BtrfsRaidLevel { > > serde_plain::derive_display_from_serialize!(BtrfsRaidLevel); > > +#[cfg_attr(feature = "api-types", api)] > #[derive(Copy, Clone, Debug, Default, Deserialize, Eq, PartialEq, Serialize)] > #[serde(rename_all = "lowercase")] > /// Possible compression algorithms usable with Btrfs. See the accompanying > @@ -708,6 +823,7 @@ pub const BTRFS_COMPRESS_OPTIONS: &[BtrfsCompressOption] = { > &[On, Off, Zlib, Lzo, Zstd] > }; > > +#[cfg_attr(feature = "api-types", api)] > #[derive(Copy, Clone, Debug, Deserialize, Serialize, Eq, PartialEq)] > #[serde(rename_all = "UPPERCASE")] > /// Available ZFS RAID levels. > @@ -734,6 +850,7 @@ pub enum ZfsRaidLevel { > > serde_plain::derive_display_from_serialize!(ZfsRaidLevel); > > +#[cfg_attr(feature = "api-types", api)] > #[derive(Copy, Clone, Debug, Default, Deserialize, Eq, PartialEq, Serialize)] > #[serde(rename_all = "lowercase")] > /// Possible compression algorithms usable with ZFS. > @@ -764,6 +881,7 @@ pub const ZFS_COMPRESS_OPTIONS: &[ZfsCompressOption] = { > &[On, Off, Lzjb, Lz4, Zle, Gzip, Zstd] > }; > > +#[cfg_attr(feature = "api-types", api)] > #[derive(Copy, Clone, Debug, Default, Deserialize, Eq, PartialEq, Serialize)] > #[serde(rename_all = "kebab-case")] > /// Possible checksum algorithms usable with ZFS. > @@ -787,6 +905,7 @@ pub const ZFS_CHECKSUM_OPTIONS: &[ZfsChecksumOption] = { > }; > > #[derive(Copy, Clone, Debug, Default, Eq, PartialEq)] > +#[cfg_attr(feature = "api-types", derive(Updater, UpdaterType))] > /// The filesystem to use for the installation. > pub enum FilesystemType { > #[default] > diff --git a/proxmox-installer-types/src/lib.rs b/proxmox-installer-types/src/lib.rs > index 12679bdc..f39d05d3 100644 > --- a/proxmox-installer-types/src/lib.rs > +++ b/proxmox-installer-types/src/lib.rs > @@ -10,6 +10,9 @@ > pub mod answer; > pub mod post_hook; > > +#[cfg(feature = "api-types")] > +use proxmox_schema::api; > + > use serde::{Deserialize, Serialize}; > use std::{ > collections::{BTreeMap, HashMap}, > @@ -21,6 +24,7 @@ use proxmox_network_types::mac_address::MacAddress; > /// Default placeholder value for the administrator email address. > pub const EMAIL_DEFAULT_PLACEHOLDER: &str = "mail@example.invalid"; > > +#[cfg_attr(feature = "api-types", api)] > #[derive(Copy, Clone, Eq, Deserialize, PartialEq, Serialize)] > #[serde(rename_all = "lowercase")] > /// Whether the system boots using legacy BIOS or (U)EFI. > @@ -43,6 +47,16 @@ pub struct UdevInfo { > pub nics: BTreeMap, > } > > +#[cfg_attr(feature = "api-types", api( > + properties: { > + network_interfaces: { > + type: Array, > + items: { > + type: NetworkInterface, > + }, > + }, > + }, > +))] > #[derive(Clone, Debug, Deserialize, Serialize, PartialEq)] > /// Information about the hardware and installer in use. > pub struct SystemInfo { > @@ -56,6 +70,7 @@ pub struct SystemInfo { > pub network_interfaces: Vec, > } > > +#[cfg_attr(feature = "api-types", api)] > #[derive(Clone, Debug, Deserialize, Serialize, PartialEq)] > /// The per-product configuration of the installer. > pub struct ProductConfig { > @@ -78,6 +93,7 @@ impl ProductConfig { > } > } > > +#[cfg_attr(feature = "api-types", api)] > #[derive(Clone, Debug, Deserialize, Serialize, PartialEq)] > /// Information about the ISO itself. > pub struct IsoInfo { > @@ -97,6 +113,25 @@ impl IsoInfo { > } > } > > +#[cfg_attr(feature = "api-types", api( > + properties: { > + baseboard: { > + type: Object, > + properties: {}, > + additional_properties: true, > + }, > + chassis: { > + type: Object, > + properties: {}, > + additional_properties: true, > + }, > + system: { > + type: Object, > + properties: {}, > + additional_properties: true, > + }, > + }, > +))] > #[derive(Clone, Debug, Deserialize, Serialize, PartialEq)] > /// Collection of various DMI information categories. > pub struct SystemDMI { > @@ -108,6 +143,7 @@ pub struct SystemDMI { > pub system: HashMap, > } > > +#[cfg_attr(feature = "api-types", api)] > #[derive(Clone, Debug, Deserialize, Serialize, PartialEq)] > /// A unique network interface. > pub struct NetworkInterface { > @@ -117,6 +153,7 @@ pub struct NetworkInterface { > pub mac: MacAddress, > } > > +#[cfg_attr(feature = "api-types", api)] > #[allow(clippy::upper_case_acronyms)] > #[derive(Debug, Clone, Copy, Deserialize, PartialEq, Serialize)] > #[serde(rename_all = "lowercase")] > diff --git a/proxmox-installer-types/src/post_hook.rs b/proxmox-installer-types/src/post_hook.rs > index 8fbe54f8..b97df954 100644 > --- a/proxmox-installer-types/src/post_hook.rs > +++ b/proxmox-installer-types/src/post_hook.rs > @@ -3,12 +3,21 @@ > use serde::{Deserialize, Serialize}; > > use proxmox_network_types::ip_address::Cidr; > +#[cfg(feature = "api-types")] > +use proxmox_schema::api; > > use crate::{ > answer::{FilesystemType, RebootMode}, > BootType, IsoInfo, ProxmoxProduct, SystemDMI, UdevProperties, > }; > > +#[cfg_attr(feature = "api-types", api( > + properties: { > + "secureboot": { > + optional: true, > + }, > + }, > +))] > #[derive(Clone, Serialize, Deserialize, PartialEq)] > /// Information about the system boot status. > pub struct BootInfo { > @@ -19,6 +28,7 @@ pub struct BootInfo { > secureboot: bool, > } > > +#[cfg_attr(feature = "api-types", api)] > #[derive(Clone, Serialize, Deserialize, PartialEq)] > /// Holds all the public keys for the different algorithms available. > pub struct SshPublicHostKeys { > @@ -30,6 +40,18 @@ pub struct SshPublicHostKeys { > pub rsa: String, > } > > +#[cfg_attr(feature = "api-types", api( > + properties: { > + "udev-properties": { > + type: Object, > + additional_properties: true, > + properties: {}, > + }, > + "is-bootdisk": { > + optional: true, > + }, > + }, > +))] > #[derive(Clone, Serialize, Deserialize, PartialEq)] > #[serde(rename_all = "kebab-case")] > /// Holds information about a single disk in the system. > @@ -43,6 +65,21 @@ pub struct DiskInfo { > pub udev_properties: UdevProperties, > } > > +#[cfg_attr(feature = "api-types", api( > + properties: { > + "udev-properties": { > + type: Object, > + additional_properties: true, > + properties: {}, > + }, > + "is-management": { > + optional: true, > + }, > + "is-pinned": { > + optional: true, > + }, > + }, > +))] > /// Holds information about the management network interface. > #[derive(Clone, Serialize, Deserialize, PartialEq)] > #[serde(rename_all = "kebab-case")] > @@ -66,6 +103,7 @@ pub struct NetworkInterfaceInfo { > pub udev_properties: UdevProperties, > } > > +#[cfg_attr(feature = "api-types", api)] > #[derive(Clone, Serialize, Deserialize, PartialEq)] > #[serde(rename_all = "kebab-case")] > /// Information about the installed product itself. > @@ -78,6 +116,7 @@ pub struct ProductInfo { > pub version: String, > } > > +#[cfg_attr(feature = "api-types", api)] > #[derive(Clone, Serialize, Deserialize, PartialEq)] > /// The current kernel version. > /// Aligns with the format as used by the `/nodes//status` API of each product. > @@ -92,6 +131,7 @@ pub struct KernelVersionInformation { > pub machine: String, > } > > +#[cfg_attr(feature = "api-types", api)] > #[derive(Clone, Serialize, Deserialize, PartialEq)] > /// Information about the CPU(s) installed in the system > pub struct CpuInfo { > @@ -109,6 +149,7 @@ pub struct CpuInfo { > pub sockets: u32, > } > > +#[cfg_attr(feature = "api-types", api)] > #[derive(Clone, Serialize, Deserialize, PartialEq)] > #[serde(rename_all = "kebab-case")] > /// Metadata of the hook, such as schema version of the document. > @@ -135,6 +176,21 @@ impl Default for PostHookInfoSchema { > } > } > > +#[cfg_attr(feature = "api-types", api( > + properties: { > + filesystem: { > + type: String, > + }, > + disks: { > + type: Array, > + items: { type: DiskInfo }, > + }, > + "network-interfaces": { > + type: Array, > + items: { type: NetworkInterfaceInfo }, > + } > + }, > +))] > #[derive(Clone, Serialize, Deserialize, PartialEq)] > #[serde(rename_all = "kebab-case")] > /// All data sent as request payload with the post-installation-webhook POST request. _______________________________________________ pdm-devel mailing list pdm-devel@lists.proxmox.com https://lists.proxmox.com/cgi-bin/mailman/listinfo/pdm-devel