[pdm-devel] [PATCH proxmox v2 07/14] installer-types: implement api type for all externally-used types

Lukas Wagner l.wagner at proxmox.com
Tue Dec 9 10:52:49 CET 2025


Looks good to me.

Reviewed-by: Lukas Wagner <l.wagner at proxmox.com>

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 <c.heiss at proxmox.com>
> ---
> 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<FirstBootHookInfo>,
>  }
>  
> +/// 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<String>,
>  }
>  
> +#[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<String>,
>  }
>  
> +#[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<f64>,
>  }
>  
> +#[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<f64>,
>  }
>  
> +#[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<BtrfsCompressOption>,
>  }
>  
> +#[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 at 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<String, UdevProperties>,
>  }
>  
> +#[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<NetworkInterface>,
>  }
>  
> +#[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<String, String>,
>  }
>  
> +#[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/<node>/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.





More information about the pdm-devel mailing list