public inbox for pdm-devel@lists.proxmox.com
 help / color / mirror / Atom feed
From: Christoph Heiss <c.heiss@proxmox.com>
To: pdm-devel@lists.proxmox.com
Subject: [PATCH installer v4 33/40] tree-wide: switch to filesystem types from proxmox-installer-types
Date: Thu, 30 Apr 2026 14:47:02 +0200	[thread overview]
Message-ID: <20260430124712.1614305-34-c.heiss@proxmox.com> (raw)
In-Reply-To: <20260430124712.1614305-1-c.heiss@proxmox.com>

No functional changes.

Reviewed-by: Lukas Wagner <l.wagner@proxmox.com>
Signed-off-by: Christoph Heiss <c.heiss@proxmox.com>
---
Changes v3 -> v4:
  * no changes

Changes v2 -> v3:
  * new patch

 Cargo.toml                                  |   2 +
 proxmox-auto-installer/Cargo.toml           |   1 +
 proxmox-auto-installer/src/answer.rs        |  26 +-
 proxmox-auto-installer/src/utils.rs         |  16 +-
 proxmox-chroot/Cargo.toml                   |   1 +
 proxmox-chroot/src/main.rs                  |  60 +-
 proxmox-installer-common/Cargo.toml         |   1 +
 proxmox-installer-common/src/dmi.rs         |  43 ++
 proxmox-installer-common/src/lib.rs         |   1 +
 proxmox-installer-common/src/options.rs     | 197 ++----
 proxmox-installer-common/src/setup.rs       |   5 +-
 proxmox-post-hook/Cargo.toml                |   3 +-
 proxmox-post-hook/src/main.rs               | 688 ++++++++------------
 proxmox-tui-installer/Cargo.toml            |   1 +
 proxmox-tui-installer/src/options.rs        |  21 +-
 proxmox-tui-installer/src/views/bootdisk.rs |  30 +-
 16 files changed, 426 insertions(+), 670 deletions(-)
 create mode 100644 proxmox-installer-common/src/dmi.rs

diff --git a/Cargo.toml b/Cargo.toml
index 379ee6b..2466822 100644
--- a/Cargo.toml
+++ b/Cargo.toml
@@ -28,8 +28,10 @@ toml = "0.8"
 proxmox-auto-installer.path = "./proxmox-auto-installer"
 proxmox-installer-common.path = "./proxmox-installer-common"
 proxmox-network-types = "1.0"
+proxmox-installer-types = { version = "0.1", features = ["legacy"] }
 
 # Local path overrides
 # NOTE: You must run `cargo update` after changing this for it to take effect!
 [patch.crates-io]
 # proxmox-network-types.path = "../proxmox/proxmox-network-types"
+# proxmox-installer-types.path = "../proxmox/proxmox-installer-types"
diff --git a/proxmox-auto-installer/Cargo.toml b/proxmox-auto-installer/Cargo.toml
index 0086e5d..5ef2f4f 100644
--- a/proxmox-auto-installer/Cargo.toml
+++ b/proxmox-auto-installer/Cargo.toml
@@ -15,6 +15,7 @@ anyhow.workspace = true
 log.workspace = true
 proxmox-installer-common = { workspace = true, features = ["http"] }
 proxmox-network-types.workspace = true
+proxmox-installer-types.workspace = true
 serde = { workspace = true, features = ["derive"] }
 serde_json.workspace = true
 serde_plain.workspace = true
diff --git a/proxmox-auto-installer/src/answer.rs b/proxmox-auto-installer/src/answer.rs
index acb0d5b..eec5b58 100644
--- a/proxmox-auto-installer/src/answer.rs
+++ b/proxmox-auto-installer/src/answer.rs
@@ -7,9 +7,9 @@ use std::{
 };
 
 use proxmox_installer_common::options::{
-    BtrfsCompressOption, BtrfsRaidLevel, FsType, NetworkInterfacePinningOptions, ZfsChecksumOption,
-    ZfsCompressOption, ZfsRaidLevel,
+    BtrfsCompressOption, NetworkInterfacePinningOptions, ZfsChecksumOption, ZfsCompressOption,
 };
+use proxmox_installer_types::answer::{BtrfsRaidLevel, FilesystemType, ZfsRaidLevel};
 use proxmox_network_types::{Cidr, fqdn::Fqdn};
 
 // NOTE New answer file properties must use kebab-case, but should allow snake_case for backwards
@@ -314,7 +314,7 @@ pub struct DiskSetup {
 #[derive(Clone, Debug, Deserialize)]
 #[serde(try_from = "DiskSetup", deny_unknown_fields)]
 pub struct Disks {
-    pub fs_type: FsType,
+    pub fs_type: FilesystemType,
     pub disk_selection: DiskSelection,
     pub filter_match: Option<FilterMatch>,
     pub fs_options: FsOptions,
@@ -351,11 +351,17 @@ impl TryFrom<DiskSetup> for Disks {
         let (fs, fs_options) = match source.filesystem {
             Filesystem::Xfs => {
                 lvm_checks(&source)?;
-                (FsType::Xfs, FsOptions::LVM(source.lvm.unwrap_or_default()))
+                (
+                    FilesystemType::Xfs,
+                    FsOptions::LVM(source.lvm.unwrap_or_default()),
+                )
             }
             Filesystem::Ext4 => {
                 lvm_checks(&source)?;
-                (FsType::Ext4, FsOptions::LVM(source.lvm.unwrap_or_default()))
+                (
+                    FilesystemType::Ext4,
+                    FsOptions::LVM(source.lvm.unwrap_or_default()),
+                )
             }
             Filesystem::Zfs => {
                 if source.lvm.is_some() || source.btrfs.is_some() {
@@ -365,7 +371,10 @@ impl TryFrom<DiskSetup> for Disks {
                     None | Some(ZfsOptions { raid: None, .. }) => {
                         return Err("ZFS raid level 'zfs.raid' must be set");
                     }
-                    Some(opts) => (FsType::Zfs(opts.raid.unwrap()), FsOptions::ZFS(opts)),
+                    Some(opts) => (
+                        FilesystemType::Zfs(opts.raid.unwrap()),
+                        FsOptions::ZFS(opts),
+                    ),
                 }
             }
             Filesystem::Btrfs => {
@@ -376,7 +385,10 @@ impl TryFrom<DiskSetup> for Disks {
                     None | Some(BtrfsOptions { raid: None, .. }) => {
                         return Err("BTRFS raid level 'btrfs.raid' must be set");
                     }
-                    Some(opts) => (FsType::Btrfs(opts.raid.unwrap()), FsOptions::BTRFS(opts)),
+                    Some(opts) => (
+                        FilesystemType::Btrfs(opts.raid.unwrap()),
+                        FsOptions::BTRFS(opts),
+                    ),
                 }
             }
         };
diff --git a/proxmox-auto-installer/src/utils.rs b/proxmox-auto-installer/src/utils.rs
index f9cfcdd..83be913 100644
--- a/proxmox-auto-installer/src/utils.rs
+++ b/proxmox-auto-installer/src/utils.rs
@@ -16,12 +16,13 @@ use crate::{
 use proxmox_installer_common::{
     ROOT_PASSWORD_MIN_LENGTH,
     disk_checks::check_swapsize,
-    options::{FsType, NetworkOptions, ZfsChecksumOption, ZfsCompressOption, email_validate},
+    options::{NetworkOptions, RaidLevel, ZfsChecksumOption, ZfsCompressOption, email_validate},
     setup::{
         InstallBtrfsOption, InstallConfig, InstallFirstBootSetup, InstallRootPassword,
         InstallZfsOption, LocaleInfo, RuntimeInfo, SetupInfo,
     },
 };
+use proxmox_installer_types::answer::FilesystemType;
 use serde::{Deserialize, Serialize};
 
 fn get_network_settings(
@@ -211,8 +212,10 @@ fn set_disks(
     config: &mut InstallConfig,
 ) -> Result<()> {
     match config.filesys {
-        FsType::Ext4 | FsType::Xfs => set_single_disk(answer, udev_info, runtime_info, config),
-        FsType::Zfs(_) | FsType::Btrfs(_) => {
+        FilesystemType::Ext4 | FilesystemType::Xfs => {
+            set_single_disk(answer, udev_info, runtime_info, config)
+        }
+        FilesystemType::Zfs(_) | FilesystemType::Btrfs(_) => {
             set_selected_disks(answer, udev_info, runtime_info, config)
         }
     }
@@ -410,7 +413,12 @@ pub fn verify_email_and_root_password_settings(answer: &Answer) -> Result<()> {
 
 pub fn verify_disks_settings(answer: &Answer) -> Result<()> {
     if let DiskSelection::Selection(selection) = &answer.disks.disk_selection {
-        let min_disks = answer.disks.fs_type.get_min_disks();
+        let min_disks = match answer.disks.fs_type {
+            FilesystemType::Ext4 | FilesystemType::Xfs => 1,
+            FilesystemType::Zfs(level) => level.get_min_disks(),
+            FilesystemType::Btrfs(level) => level.get_min_disks(),
+        };
+
         if selection.len() < min_disks {
             bail!(
                 "{}: need at least {} disks",
diff --git a/proxmox-chroot/Cargo.toml b/proxmox-chroot/Cargo.toml
index a6a705d..e1e0e4c 100644
--- a/proxmox-chroot/Cargo.toml
+++ b/proxmox-chroot/Cargo.toml
@@ -10,5 +10,6 @@ homepage = "https://www.proxmox.com"
 [dependencies]
 anyhow.workspace = true
 proxmox-installer-common = { workspace = true, features = [ "cli" ] }
+proxmox-installer-types.workspace = true
 serde = { workspace = true, features = [ "derive" ] }
 serde_json.workspace = true
diff --git a/proxmox-chroot/src/main.rs b/proxmox-chroot/src/main.rs
index 2cff630..5f087bb 100644
--- a/proxmox-chroot/src/main.rs
+++ b/proxmox-chroot/src/main.rs
@@ -5,20 +5,19 @@
 
 #![forbid(unsafe_code)]
 
+use anyhow::{Result, bail};
+use serde::Deserialize;
 use std::{
     env, fs, io,
     path::{self, Path, PathBuf},
     process::{self, Command},
-    str::FromStr,
 };
 
-use anyhow::{Result, bail};
 use proxmox_installer_common::{
     RUNTIME_DIR, cli,
-    options::FsType,
     setup::{InstallConfig, SetupInfo},
 };
-use serde::Deserialize;
+use proxmox_installer_types::answer::Filesystem;
 
 const ANSWER_MP: &str = "answer";
 static BINDMOUNTS: [&str; 4] = ["dev", "proc", "run", "sys"];
@@ -29,7 +28,7 @@ const ZPOOL_NAME: &str = "rpool";
 struct CommandPrepareArgs {
     /// Filesystem used for the installation. Will try to automatically detect it after a
     /// successful installation.
-    filesystem: Option<Filesystems>,
+    filesystem: Option<Filesystem>,
 
     /// Numerical ID of the `rpool` ZFS pool to import. Needed if multiple pools of name `rpool`
     /// are present.
@@ -74,7 +73,7 @@ OPTIONS:
 /// Arguments for the `cleanup` command.
 struct CommandCleanupArgs {
     /// Filesystem used for the installation. Will try to automatically detect it by default.
-    filesystem: Option<Filesystems>,
+    filesystem: Option<Filesystem>,
 }
 
 impl cli::Subcommand for CommandCleanupArgs {
@@ -105,39 +104,6 @@ OPTIONS:
     }
 }
 
-#[derive(Copy, Clone, Debug)]
-enum Filesystems {
-    Zfs,
-    Ext4,
-    Xfs,
-    Btrfs,
-}
-
-impl From<FsType> for Filesystems {
-    fn from(fs: FsType) -> Self {
-        match fs {
-            FsType::Xfs => Self::Xfs,
-            FsType::Ext4 => Self::Ext4,
-            FsType::Zfs(_) => Self::Zfs,
-            FsType::Btrfs(_) => Self::Btrfs,
-        }
-    }
-}
-
-impl FromStr for Filesystems {
-    type Err = anyhow::Error;
-
-    fn from_str(s: &str) -> Result<Self> {
-        match s {
-            "ext4" => Ok(Filesystems::Ext4),
-            "xfs" => Ok(Filesystems::Xfs),
-            _ if s.starts_with("zfs") => Ok(Filesystems::Zfs),
-            _ if s.starts_with("btrfs") => Ok(Filesystems::Btrfs),
-            _ => bail!("unknown filesystem"),
-        }
-    }
-}
-
 fn main() -> process::ExitCode {
     cli::run(cli::AppInfo {
         global_help: &format!(
@@ -171,10 +137,10 @@ fn prepare(args: &CommandPrepareArgs) -> Result<()> {
     fs::create_dir_all(TARGET_DIR)?;
 
     match fs {
-        Filesystems::Zfs => mount_zpool(args.rpool_id)?,
-        Filesystems::Xfs => mount_fs()?,
-        Filesystems::Ext4 => mount_fs()?,
-        Filesystems::Btrfs => mount_btrfs(args.btrfs_uuid.clone())?,
+        Filesystem::Zfs => mount_zpool(args.rpool_id)?,
+        Filesystem::Xfs => mount_fs()?,
+        Filesystem::Ext4 => mount_fs()?,
+        Filesystem::Btrfs => mount_btrfs(args.btrfs_uuid.clone())?,
     }
 
     if let Err(e) = bindmount() {
@@ -193,15 +159,15 @@ fn cleanup(args: &CommandCleanupArgs) -> Result<()> {
     }
 
     match fs {
-        Filesystems::Zfs => umount_zpool(),
-        Filesystems::Btrfs | Filesystems::Xfs | Filesystems::Ext4 => umount(Path::new(TARGET_DIR))?,
+        Filesystem::Zfs => umount_zpool(),
+        Filesystem::Btrfs | Filesystem::Xfs | Filesystem::Ext4 => umount(Path::new(TARGET_DIR))?,
     }
 
     println!("Chroot cleanup done. You can now reboot or leave the shell.");
     Ok(())
 }
 
-fn get_fs(filesystem: Option<Filesystems>) -> Result<Filesystems> {
+fn get_fs(filesystem: Option<Filesystem>) -> Result<Filesystem> {
     let fs = match filesystem {
         None => {
             let low_level_config = match get_low_level_config() {
@@ -210,7 +176,7 @@ fn get_fs(filesystem: Option<Filesystems>) -> Result<Filesystems> {
                     "Could not fetch config from previous installation. Please specify file system with -f."
                 ),
             };
-            Filesystems::from(low_level_config.filesys)
+            low_level_config.filesys.into()
         }
         Some(fs) => fs,
     };
diff --git a/proxmox-installer-common/Cargo.toml b/proxmox-installer-common/Cargo.toml
index 7469627..7682680 100644
--- a/proxmox-installer-common/Cargo.toml
+++ b/proxmox-installer-common/Cargo.toml
@@ -14,6 +14,7 @@ serde = { workspace = true, features = [ "derive" ] }
 serde_json.workspace = true
 serde_plain.workspace = true
 proxmox-network-types.workspace = true
+proxmox-installer-types.workspace = true
 
 # `http` feature
 hex = { version = "0.4", optional = true }
diff --git a/proxmox-installer-common/src/dmi.rs b/proxmox-installer-common/src/dmi.rs
new file mode 100644
index 0000000..76ae4a5
--- /dev/null
+++ b/proxmox-installer-common/src/dmi.rs
@@ -0,0 +1,43 @@
+use std::{collections::HashMap, fs};
+
+use anyhow::{Result, bail};
+use proxmox_installer_types::SystemDMI;
+
+const DMI_PATH: &str = "/sys/devices/virtual/dmi/id";
+
+pub fn get() -> Result<SystemDMI> {
+    let system_files = [
+        "product_serial",
+        "product_sku",
+        "product_uuid",
+        "product_name",
+    ];
+    let baseboard_files = ["board_asset_tag", "board_serial", "board_name"];
+    let chassis_files = ["chassis_serial", "chassis_sku", "chassis_asset_tag"];
+
+    Ok(SystemDMI {
+        system: get_dmi_infos_for(&system_files)?,
+        baseboard: get_dmi_infos_for(&baseboard_files)?,
+        chassis: get_dmi_infos_for(&chassis_files)?,
+    })
+}
+
+fn get_dmi_infos_for(files: &[&str]) -> Result<HashMap<String, String>> {
+    let mut res: HashMap<String, String> = HashMap::new();
+
+    for file in files {
+        let path = format!("{DMI_PATH}/{file}");
+        let content = match fs::read_to_string(&path) {
+            Err(ref err) if err.kind() == std::io::ErrorKind::NotFound => continue,
+            Err(ref err) if err.kind() == std::io::ErrorKind::PermissionDenied => {
+                bail!("Could not read data. Are you running as root or with sudo?")
+            }
+            Err(err) => bail!("Error: '{err}' on '{path}'"),
+            Ok(content) => content.trim().into(),
+        };
+        let key = file.splitn(2, '_').last().unwrap();
+        res.insert(key.into(), content);
+    }
+
+    Ok(res)
+}
diff --git a/proxmox-installer-common/src/lib.rs b/proxmox-installer-common/src/lib.rs
index 7cdb1de..05445d5 100644
--- a/proxmox-installer-common/src/lib.rs
+++ b/proxmox-installer-common/src/lib.rs
@@ -1,4 +1,5 @@
 pub mod disk_checks;
+pub mod dmi;
 pub mod options;
 pub mod setup;
 pub mod sysinfo;
diff --git a/proxmox-installer-common/src/options.rs b/proxmox-installer-common/src/options.rs
index f903f7e..8e19663 100644
--- a/proxmox-installer-common/src/options.rs
+++ b/proxmox-installer-common/src/options.rs
@@ -1,36 +1,23 @@
 use anyhow::{Result, bail};
 use regex::{Regex, RegexBuilder};
 use serde::{Deserialize, Serialize};
-use std::collections::HashMap;
-use std::net::{IpAddr, Ipv4Addr};
-use std::str::FromStr;
-use std::sync::OnceLock;
-use std::{cmp, fmt};
+use std::{
+    cmp,
+    collections::HashMap,
+    fmt,
+    net::{IpAddr, Ipv4Addr},
+    sync::OnceLock,
+};
 
 use crate::disk_checks::check_raid_min_disks;
 use crate::net::{MAX_IFNAME_LEN, MIN_IFNAME_LEN};
 use crate::setup::{LocaleInfo, NetworkInfo, RuntimeInfo, SetupInfo};
+use proxmox_installer_types::answer::{BtrfsRaidLevel, FilesystemType, ZfsRaidLevel};
 use proxmox_network_types::{fqdn::Fqdn, ip_address::Cidr};
 
-#[derive(Copy, Clone, Debug, Deserialize, Serialize, Eq, PartialEq)]
-#[serde(rename_all(deserialize = "lowercase", serialize = "UPPERCASE"))]
-pub enum BtrfsRaidLevel {
-    #[serde(alias = "RAID0")]
-    Raid0,
-    #[serde(alias = "RAID1")]
-    Raid1,
-    #[serde(alias = "RAID10")]
-    Raid10,
-}
-
-impl BtrfsRaidLevel {
-    pub fn get_min_disks(&self) -> usize {
-        match self {
-            BtrfsRaidLevel::Raid0 => 1,
-            BtrfsRaidLevel::Raid1 => 2,
-            BtrfsRaidLevel::Raid10 => 4,
-        }
-    }
+pub trait RaidLevel {
+    /// Returns the minimum number of disks needed for this RAID level.
+    fn get_min_disks(&self) -> usize;
 
     /// Checks whether a user-supplied Btrfs RAID setup is valid or not, such as minimum
     /// number of disks.
@@ -38,42 +25,31 @@ impl BtrfsRaidLevel {
     /// # Arguments
     ///
     /// * `disks` - List of disks designated as RAID targets.
-    pub fn check_raid_disks_setup(&self, disks: &[Disk]) -> Result<(), String> {
+    fn check_raid_disks_setup(&self, disks: &[Disk]) -> Result<(), String>;
+
+    /// Checks whether the given disk sizes are compatible for the RAID level, if it is a mirror.
+    fn check_mirror_size(&self, _disk1: &Disk, _disk2: &Disk) -> Result<(), String> {
+        Ok(())
+    }
+}
+
+impl RaidLevel for BtrfsRaidLevel {
+    fn get_min_disks(&self) -> usize {
+        match self {
+            Self::Raid0 => 1,
+            Self::Raid1 => 2,
+            Self::Raid10 => 4,
+        }
+    }
+
+    fn check_raid_disks_setup(&self, disks: &[Disk]) -> Result<(), String> {
         check_raid_min_disks(disks, self.get_min_disks())?;
         Ok(())
     }
 }
 
-serde_plain::derive_display_from_serialize!(BtrfsRaidLevel);
-
-#[derive(Copy, Clone, Debug, Deserialize, Serialize, Eq, PartialEq)]
-#[serde(rename_all(deserialize = "lowercase", serialize = "UPPERCASE"))]
-pub enum ZfsRaidLevel {
-    #[serde(alias = "RAID0")]
-    Raid0,
-    #[serde(alias = "RAID1")]
-    Raid1,
-    #[serde(alias = "RAID10")]
-    Raid10,
-    #[serde(
-        alias = "RAIDZ-1",
-        rename(deserialize = "raidz-1", serialize = "RAIDZ-1")
-    )]
-    RaidZ,
-    #[serde(
-        alias = "RAIDZ-2",
-        rename(deserialize = "raidz-2", serialize = "RAIDZ-2")
-    )]
-    RaidZ2,
-    #[serde(
-        alias = "RAIDZ-3",
-        rename(deserialize = "raidz-3", serialize = "RAIDZ-3")
-    )]
-    RaidZ3,
-}
-
-impl ZfsRaidLevel {
-    pub fn get_min_disks(&self) -> usize {
+impl RaidLevel for ZfsRaidLevel {
+    fn get_min_disks(&self) -> usize {
         match self {
             ZfsRaidLevel::Raid0 => 1,
             ZfsRaidLevel::Raid1 => 2,
@@ -84,23 +60,7 @@ impl ZfsRaidLevel {
         }
     }
 
-    fn check_mirror_size(&self, disk1: &Disk, disk2: &Disk) -> Result<(), String> {
-        if (disk1.size - disk2.size).abs() > disk1.size / 10. {
-            Err(format!(
-                "Mirrored disks must have same size:\n\n  * {disk1}\n  * {disk2}"
-            ))
-        } else {
-            Ok(())
-        }
-    }
-
-    /// Checks whether a user-supplied ZFS RAID setup is valid or not, such as disk sizes andminimum
-    /// number of disks.
-    ///
-    /// # Arguments
-    ///
-    /// * `disks` - List of disks designated as RAID targets.
-    pub fn check_raid_disks_setup(&self, disks: &[Disk]) -> Result<(), String> {
+    fn check_raid_disks_setup(&self, disks: &[Disk]) -> Result<(), String> {
         check_raid_min_disks(disks, self.get_min_disks())?;
 
         match self {
@@ -130,93 +90,18 @@ impl ZfsRaidLevel {
 
         Ok(())
     }
-}
 
-serde_plain::derive_display_from_serialize!(ZfsRaidLevel);
-
-#[derive(Copy, Clone, Debug, Eq, PartialEq)]
-pub enum FsType {
-    Ext4,
-    Xfs,
-    Zfs(ZfsRaidLevel),
-    Btrfs(BtrfsRaidLevel),
-}
-
-impl FsType {
-    pub fn is_btrfs(&self) -> bool {
-        matches!(self, FsType::Btrfs(_))
-    }
-
-    /// Returns true if the filesystem is used on top of LVM, e.g. ext4 or XFS.
-    pub fn is_lvm(&self) -> bool {
-        matches!(self, FsType::Ext4 | FsType::Xfs)
-    }
-
-    pub fn get_min_disks(&self) -> usize {
-        match self {
-            FsType::Ext4 => 1,
-            FsType::Xfs => 1,
-            FsType::Zfs(level) => level.get_min_disks(),
-            FsType::Btrfs(level) => level.get_min_disks(),
+    fn check_mirror_size(&self, disk1: &Disk, disk2: &Disk) -> Result<(), String> {
+        if (disk1.size - disk2.size).abs() > disk1.size / 10. {
+            Err(format!(
+                "Mirrored disks must have same size:\n\n  * {disk1}\n  * {disk2}"
+            ))
+        } else {
+            Ok(())
         }
     }
 }
 
-impl fmt::Display for FsType {
-    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
-        // Values displayed to the user in the installer UI
-        match self {
-            FsType::Ext4 => write!(f, "ext4"),
-            FsType::Xfs => write!(f, "XFS"),
-            FsType::Zfs(level) => write!(f, "ZFS ({level})"),
-            FsType::Btrfs(level) => write!(f, "BTRFS ({level})"),
-        }
-    }
-}
-
-impl Serialize for FsType {
-    fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
-    where
-        S: serde::Serializer,
-    {
-        // These values must match exactly what the low-level installer expects
-        let value = match self {
-            // proxinstall::$fssetup
-            FsType::Ext4 => "ext4",
-            FsType::Xfs => "xfs",
-            // proxinstall::get_zfs_raid_setup()
-            FsType::Zfs(level) => &format!("zfs ({level})"),
-            // proxinstall::get_btrfs_raid_setup()
-            FsType::Btrfs(level) => &format!("btrfs ({level})"),
-        };
-
-        serializer.collect_str(value)
-    }
-}
-
-impl FromStr for FsType {
-    type Err = String;
-
-    fn from_str(s: &str) -> Result<Self, Self::Err> {
-        match s {
-            "ext4" => Ok(FsType::Ext4),
-            "xfs" => Ok(FsType::Xfs),
-            "zfs (RAID0)" => Ok(FsType::Zfs(ZfsRaidLevel::Raid0)),
-            "zfs (RAID1)" => Ok(FsType::Zfs(ZfsRaidLevel::Raid1)),
-            "zfs (RAID10)" => Ok(FsType::Zfs(ZfsRaidLevel::Raid10)),
-            "zfs (RAIDZ-1)" => Ok(FsType::Zfs(ZfsRaidLevel::RaidZ)),
-            "zfs (RAIDZ-2)" => Ok(FsType::Zfs(ZfsRaidLevel::RaidZ2)),
-            "zfs (RAIDZ-3)" => Ok(FsType::Zfs(ZfsRaidLevel::RaidZ3)),
-            "btrfs (RAID0)" => Ok(FsType::Btrfs(BtrfsRaidLevel::Raid0)),
-            "btrfs (RAID1)" => Ok(FsType::Btrfs(BtrfsRaidLevel::Raid1)),
-            "btrfs (RAID10)" => Ok(FsType::Btrfs(BtrfsRaidLevel::Raid10)),
-            _ => Err(format!("Could not find file system: {s}")),
-        }
-    }
-}
-
-serde_plain::derive_deserialize_from_fromstr!(FsType, "valid filesystem");
-
 #[derive(Clone, Debug)]
 pub struct LvmBootdiskOptions {
     pub total_size: f64,
@@ -426,7 +311,7 @@ impl cmp::Ord for Disk {
 #[derive(Clone, Debug)]
 pub struct BootdiskOptions {
     pub disks: Vec<Disk>,
-    pub fstype: FsType,
+    pub fstype: FilesystemType,
     pub advanced: AdvancedBootdiskOptions,
 }
 
@@ -434,7 +319,7 @@ impl BootdiskOptions {
     pub fn defaults_from(disk: &Disk) -> Self {
         Self {
             disks: vec![disk.clone()],
-            fstype: FsType::Ext4,
+            fstype: FilesystemType::Ext4,
             advanced: AdvancedBootdiskOptions::Lvm(LvmBootdiskOptions::defaults_from(disk)),
         }
     }
diff --git a/proxmox-installer-common/src/setup.rs b/proxmox-installer-common/src/setup.rs
index 35a5436..91f1250 100644
--- a/proxmox-installer-common/src/setup.rs
+++ b/proxmox-installer-common/src/setup.rs
@@ -14,9 +14,10 @@ use proxmox_network_types::Cidr;
 use serde::{Deserialize, Deserializer, Serialize, Serializer, de};
 
 use crate::options::{
-    BtrfsBootdiskOptions, BtrfsCompressOption, Disk, FsType, NetworkInterfacePinningOptions,
+    BtrfsBootdiskOptions, BtrfsCompressOption, Disk, NetworkInterfacePinningOptions,
     ZfsBootdiskOptions, ZfsChecksumOption, ZfsCompressOption,
 };
+use proxmox_installer_types::answer::FilesystemType;
 
 #[allow(clippy::upper_case_acronyms)]
 #[derive(Debug, Clone, Copy, Deserialize, PartialEq, Serialize)]
@@ -565,7 +566,7 @@ pub fn spawn_low_level_installer(test_mode: bool) -> io::Result<process::Child>
 pub struct InstallConfig {
     pub autoreboot: usize,
 
-    pub filesys: FsType,
+    pub filesys: FilesystemType,
     pub hdsize: f64,
     #[serde(skip_serializing_if = "Option::is_none")]
     pub swapsize: Option<f64>,
diff --git a/proxmox-post-hook/Cargo.toml b/proxmox-post-hook/Cargo.toml
index beaaa26..748b922 100644
--- a/proxmox-post-hook/Cargo.toml
+++ b/proxmox-post-hook/Cargo.toml
@@ -12,8 +12,9 @@ homepage = "https://www.proxmox.com"
 
 [dependencies]
 anyhow.workspace = true
-proxmox-auto-installer.workspace = true
 proxmox-installer-common = { workspace = true, features = ["http"] }
 proxmox-network-types.workspace = true
+proxmox-installer-types.workspace = true
 serde = { workspace = true, features = ["derive"] }
 serde_json.workspace = true
+toml.workspace = true
diff --git a/proxmox-post-hook/src/main.rs b/proxmox-post-hook/src/main.rs
index 8afe310..9086dc1 100644
--- a/proxmox-post-hook/src/main.rs
+++ b/proxmox-post-hook/src/main.rs
@@ -9,212 +9,49 @@
 //! Relies on `proxmox-chroot` as an external dependency to (bind-)mount the
 //! previously installed system.
 
-use anyhow::{Context, Result, anyhow, bail};
-use serde::Serialize;
+use anyhow::{Context, Result, bail};
 use std::{
-    collections::HashSet,
-    ffi::CStr,
-    fs::{self, File},
-    io::BufReader,
-    os::unix::fs::FileExt,
-    path::PathBuf,
+    fs,
+    io::Read,
     process::{Command, ExitCode},
 };
 
-use proxmox_auto_installer::{
-    answer::{
-        Answer, FqdnConfig, FqdnExtendedConfig, FqdnSourceMode, PostNotificationHookInfo,
-        RebootMode,
-    },
-    udevinfo::{UdevInfo, UdevProperties},
-};
-use proxmox_installer_common::{
-    http::{self, header::HeaderMap},
-    options::{Disk, FsType, NetworkOptions},
-    setup::{
-        BootType, InstallConfig, IsoInfo, ProxmoxProduct, RuntimeInfo, SetupInfo,
-        load_installer_setup_files,
-    },
-    sysinfo::SystemDMI,
-};
-use proxmox_network_types::ip_address::Cidr;
+use proxmox_installer_common::http::{self, header::HeaderMap};
+use proxmox_installer_types::answer::{AutoInstallerConfig, PostNotificationHookInfo};
 
-/// Information about the system boot status.
-#[derive(Serialize)]
-struct BootInfo {
-    /// Whether the system is booted using UEFI or legacy BIOS.
-    mode: BootType,
-    /// Whether SecureBoot is enabled for the installation.
-    #[serde(skip_serializing_if = "bool_is_false")]
-    secureboot: bool,
-}
+/// Current version of the schema sent by this implementation.
+const POST_HOOK_SCHEMA_VERSION: &str = "1.2";
 
-/// Holds all the public keys for the different algorithms available.
-#[derive(Serialize)]
-struct SshPublicHostKeys {
-    // ECDSA-based public host key
-    ecdsa: String,
-    // ED25519-based public host key
-    ed25519: String,
-    // RSA-based public host key
-    rsa: String,
-}
+mod detail {
+    use anyhow::{Context, Result, anyhow, bail};
+    use std::{
+        collections::HashSet,
+        ffi::CStr,
+        fs::{self, File},
+        io::BufReader,
+        os::unix::fs::FileExt,
+        path::PathBuf,
+        process::Command,
+    };
 
-/// Holds information about a single disk in the system.
-#[derive(Serialize)]
-#[serde(rename_all = "kebab-case")]
-struct DiskInfo {
-    /// Size in bytes
-    size: usize,
-    /// Set to true if the disk is used for booting.
-    #[serde(skip_serializing_if = "bool_is_false")]
-    is_bootdisk: bool,
-    /// Properties about the device as given by udev.
-    udev_properties: UdevProperties,
-}
+    use proxmox_installer_common::{
+        options::{Disk, NetworkOptions},
+        setup::{
+            InstallConfig, ProxmoxProduct, RuntimeInfo, SetupInfo, load_installer_setup_files,
+        },
+    };
+    use proxmox_installer_types::{
+        BootType, IsoInfo, UdevInfo,
+        answer::{AutoInstallerConfig, FqdnConfig, FqdnFromDhcpConfig, FqdnSourceMode},
+        post_hook::{
+            BootInfo, CpuInfo, DiskInfo, KernelVersionInformation, NetworkInterfaceInfo,
+            PostHookInfo, PostHookInfoSchema, ProductInfo, SshPublicHostKeys,
+        },
+    };
 
-/// Holds information about the management network interface.
-#[derive(Serialize)]
-#[serde(rename_all = "kebab-case")]
-struct NetworkInterfaceInfo {
-    /// Name of the interface
-    name: String,
-    /// MAC address of the interface
-    mac: String,
-    /// (Designated) IP address of the interface
-    #[serde(skip_serializing_if = "Option::is_none")]
-    address: Option<Cidr>,
-    /// Set to true if the interface is the chosen management interface during
-    /// installation.
-    #[serde(skip_serializing_if = "bool_is_false")]
-    is_management: bool,
-    /// Set to true if the network interface name was pinned based on the MAC
-    /// address during the installation.
-    #[serde(skip_serializing_if = "bool_is_false")]
-    is_pinned: bool,
-    /// Properties about the device as given by udev.
-    udev_properties: UdevProperties,
-}
+    /// Defines the size of a gibibyte in bytes.
+    const SIZE_GIB: usize = 1024 * 1024 * 1024;
 
-fn bool_is_false(value: &bool) -> bool {
-    !value
-}
-
-/// Information about the installed product itself.
-#[derive(Serialize)]
-#[serde(rename_all = "kebab-case")]
-struct ProductInfo {
-    /// Full name of the product
-    fullname: String,
-    /// Product abbreviation
-    short: ProxmoxProduct,
-    /// Version of the installed product
-    version: String,
-}
-
-/// The current kernel version.
-/// Aligns with the format as used by the `/nodes/<node>/status` API of each product.
-#[derive(Serialize)]
-struct KernelVersionInformation {
-    /// The systemname/nodename
-    pub sysname: String,
-    /// The kernel release number
-    pub release: String,
-    /// The kernel version
-    pub version: String,
-    /// The machine architecture
-    pub machine: String,
-}
-
-/// Information about the CPU(s) installed in the system
-#[derive(Serialize)]
-struct CpuInfo {
-    /// Number of physical CPU cores.
-    cores: usize,
-    /// Number of logical CPU cores aka. threads.
-    cpus: usize,
-    /// CPU feature flag set as a space-delimited list.
-    flags: String,
-    /// Whether hardware-accelerated virtualization is supported.
-    hvm: bool,
-    /// Reported model of the CPU(s)
-    model: String,
-    /// Number of physical CPU sockets
-    sockets: usize,
-}
-
-/// Metadata of the hook, such as schema version of the document.
-#[derive(Serialize)]
-#[serde(rename_all = "kebab-case")]
-struct PostHookInfoSchema {
-    /// major.minor version describing the schema version of this document, in a semanticy-version
-    /// way.
-    ///
-    /// major: Incremented for incompatible/breaking API changes, e.g. removing an existing
-    /// field.
-    /// minor: Incremented when adding functionality in a backwards-compatible matter, e.g.
-    /// adding a new field.
-    version: String,
-}
-
-impl PostHookInfoSchema {
-    const SCHEMA_VERSION: &str = "1.2";
-}
-
-impl Default for PostHookInfoSchema {
-    fn default() -> Self {
-        Self {
-            version: Self::SCHEMA_VERSION.to_owned(),
-        }
-    }
-}
-
-/// All data sent as request payload with the post-installation-webhook POST request.
-///
-/// NOTE: The format is versioned through `schema.version` (`$schema.version` in the
-/// resulting JSON), ensure you update it when this struct or any of its members gets modified.
-#[derive(Serialize)]
-#[serde(rename_all = "kebab-case")]
-struct PostHookInfo {
-    // This field is prefixed by `$` on purpose, to indicate that it is document metadata and not
-    // part of the actual content itself. (E.g. JSON Schema uses a similar naming scheme)
-    #[serde(rename = "$schema")]
-    schema: PostHookInfoSchema,
-    /// major.minor version of Debian as installed, retrieved from /etc/debian_version
-    debian_version: String,
-    /// PVE/PMG/PBS/PDM version as reported by `pveversion`, `pmgversion`,
-    /// `proxmox-backup-manager version` or `proxmox-datacenter-manager version`, respectively.
-    product: ProductInfo,
-    /// Release information for the ISO used for the installation.
-    iso: IsoInfo,
-    /// Installed kernel version
-    kernel_version: KernelVersionInformation,
-    /// Describes the boot mode of the machine and the SecureBoot status.
-    boot_info: BootInfo,
-    /// Information about the installed CPU(s)
-    cpu_info: CpuInfo,
-    /// DMI information about the system
-    dmi: SystemDMI,
-    /// Filesystem used for boot disk(s)
-    filesystem: FsType,
-    /// Fully qualified domain name of the installed system
-    fqdn: String,
-    /// Unique systemd-id128 identifier of the installed system (128-bit, 16 bytes)
-    machine_id: String,
-    /// All disks detected on the system.
-    disks: Vec<DiskInfo>,
-    /// All network interfaces detected on the system.
-    network_interfaces: Vec<NetworkInterfaceInfo>,
-    /// Public parts of SSH host keys of the installed system
-    ssh_public_host_keys: SshPublicHostKeys,
-    /// Action to will be performed, i.e. either reboot or power off the machine.
-    reboot_mode: RebootMode,
-}
-
-/// Defines the size of a gibibyte in bytes.
-const SIZE_GIB: usize = 1024 * 1024 * 1024;
-
-impl PostHookInfo {
     /// Gathers all needed information about the newly installed system for sending
     /// it to a specified server.
     ///
@@ -222,7 +59,7 @@ impl PostHookInfo {
     ///
     /// * `target_path` - Path to where the chroot environment root is mounted
     /// * `answer` - Answer file as provided by the user
-    fn gather(target_path: &str, answer: &Answer) -> Result<Self> {
+    pub fn gather(target_path: &str, answer: &AutoInstallerConfig) -> Result<PostHookInfo> {
         println!("Gathering installed system data ...");
 
         let config: InstallConfig =
@@ -265,34 +102,42 @@ impl PostHookInfo {
 
         let fqdn = match &answer.global.fqdn {
             FqdnConfig::Simple(name) => name.to_string(),
-            FqdnConfig::Extended(FqdnExtendedConfig {
+            FqdnConfig::FromDhcp(FqdnFromDhcpConfig {
                 source: FqdnSourceMode::FromDhcp,
                 domain,
             }) => NetworkOptions::construct_fqdn(
                 &run_env.network,
-                setup_info.config.product.default_hostname(),
+                &setup_info.config.product.to_string(),
                 domain.as_deref(),
             )
             .to_string(),
         };
 
-        Ok(Self {
-            schema: PostHookInfoSchema::default(),
+        Ok(PostHookInfo {
+            schema: PostHookInfoSchema {
+                version: super::POST_HOOK_SCHEMA_VERSION.to_owned(),
+            },
             debian_version: read_file("/etc/debian_version")?,
-            product: Self::gather_product_info(&setup_info, &run_cmd)?,
-            iso: setup_info.iso_info.clone(),
-            kernel_version: Self::gather_kernel_version(&run_cmd, &open_file)?,
+            product: gather_product_info(&setup_info, &run_cmd)?,
+            iso: IsoInfo {
+                release: setup_info.iso_info.release,
+                isorelease: setup_info.iso_info.isorelease,
+            },
+            kernel_version: gather_kernel_version(&run_cmd, &open_file)?,
             boot_info: BootInfo {
-                mode: run_env.boot_type,
+                mode: match run_env.boot_type {
+                    proxmox_installer_common::setup::BootType::Bios => BootType::Bios,
+                    proxmox_installer_common::setup::BootType::Efi => BootType::Efi,
+                },
                 secureboot: run_env.secure_boot,
             },
-            cpu_info: Self::gather_cpu_info(&run_env)?,
-            dmi: SystemDMI::get()?,
-            filesystem: answer.disks.fs_type,
+            cpu_info: gather_cpu_info(&run_env)?,
+            dmi: proxmox_installer_common::dmi::get()?,
+            filesystem: answer.disks.filesystem_details()?.to_type(),
             fqdn,
             machine_id: read_file("/etc/machine-id")?,
-            disks: Self::gather_disks(&config, &run_env, &udev)?,
-            network_interfaces: Self::gather_nic(&config, &run_env, &udev)?,
+            disks: gather_disks(&config, &run_env, &udev)?,
+            network_interfaces: gather_nic(&config, &run_env, &udev)?,
             ssh_public_host_keys: SshPublicHostKeys {
                 ecdsa: read_file("/etc/ssh/ssh_host_ecdsa_key.pub")?,
                 ed25519: read_file("/etc/ssh/ssh_host_ed25519_key.pub")?,
@@ -335,10 +180,10 @@ impl PostHookInfo {
                         .target_hd
                         .as_ref()
                         .map(|hd| *hd == disk.path)
-                        .unwrap_or_default();
+                        .unwrap_or(false);
 
                     anyhow::Ok(DiskInfo {
-                        size: (config.hdsize * (SIZE_GIB as f64)) as usize,
+                        size: (config.hdsize * (SIZE_GIB as f64)) as u64,
                         is_bootdisk,
                         udev_properties: get_udev_properties(disk)?,
                     })
@@ -346,7 +191,7 @@ impl PostHookInfo {
                 .collect()
         } else {
             // If the filesystem is not LVM-based (thus Btrfs or ZFS), `config.disk_selection`
-            // contains a list of indices identifiying the boot disks, as given by udev.
+            // contains a list of indices identifying the boot disks, as given by udev.
             let selected_disks_indices: Vec<&String> = config.disk_selection.values().collect();
 
             run_env
@@ -356,7 +201,7 @@ impl PostHookInfo {
                     let is_bootdisk = selected_disks_indices.contains(&&disk.index);
 
                     anyhow::Ok(DiskInfo {
-                        size: (config.hdsize * (SIZE_GIB as f64)) as usize,
+                        size: (config.hdsize * (SIZE_GIB as f64)) as u64,
                         is_bootdisk,
                         udev_properties: get_udev_properties(disk)?,
                     })
@@ -443,7 +288,12 @@ impl PostHookInfo {
 
         Ok(ProductInfo {
             fullname: setup_info.config.fullname.clone(),
-            short: setup_info.config.product,
+            short: match setup_info.config.product {
+                ProxmoxProduct::PVE => proxmox_installer_types::ProxmoxProduct::Pve,
+                ProxmoxProduct::PBS => proxmox_installer_types::ProxmoxProduct::Pbs,
+                ProxmoxProduct::PMG => proxmox_installer_types::ProxmoxProduct::Pmg,
+                ProxmoxProduct::PDM => proxmox_installer_types::ProxmoxProduct::Pdm,
+            },
             version,
         })
     }
@@ -465,7 +315,7 @@ impl PostHookInfo {
         run_cmd: &dyn Fn(&[&str]) -> Result<String>,
         open_file: &dyn Fn(&str) -> Result<File>,
     ) -> Result<KernelVersionInformation> {
-        let file = open_file(&Self::find_kernel_image_path(run_cmd)?)?;
+        let file = open_file(&find_kernel_image_path(run_cmd)?)?;
 
         // Read the 2-byte `kernel_version` field at offset 0x20e [0] from the file ..
         // https://www.kernel.org/doc/html/latest/arch/x86/boot.html#the-real-mode-kernel-header
@@ -525,7 +375,7 @@ impl PostHookInfo {
         run_cmd: &dyn Fn(&[&str]) -> Result<String>,
         _open_file: &dyn Fn(&str) -> Result<File>,
     ) -> Result<KernelVersionInformation> {
-        let image_path = Self::find_kernel_image_path(run_cmd)?;
+        let image_path = find_kernel_image_path(run_cmd)?;
 
         let release = image_path
             .strip_prefix("/boot/vmlinuz-")
@@ -537,11 +387,7 @@ impl PostHookInfo {
             .map(|v| {
                 // /proc/version: "Linux version 6.17.2-1-pve (...) #1 SMP ..."
                 // extract everything after the second space
-                v.splitn(3, ' ')
-                    .nth(2)
-                    .unwrap_or("")
-                    .trim()
-                    .to_owned()
+                v.splitn(3, ' ').nth(2).unwrap_or("").trim().to_owned()
             })
             .unwrap_or_default();
 
@@ -560,7 +406,7 @@ impl PostHookInfo {
     ///
     /// * `run_cmd` - Callback to run a command inside the target chroot.
     fn find_kernel_image_path(run_cmd: &dyn Fn(&[&str]) -> Result<String>) -> Result<String> {
-        let pkg_name = Self::find_kernel_package_name(run_cmd)?;
+        let pkg_name = find_kernel_package_name(run_cmd)?;
 
         let all_files = run_cmd(&["dpkg-query", "--listfiles", &pkg_name])?;
         for file in all_files.lines() {
@@ -667,6 +513,200 @@ impl PostHookInfo {
 
         Ok(result)
     }
+
+    #[cfg(test)]
+    mod tests {
+        use super::{find_kernel_image_path, find_kernel_package_name};
+
+        #[test]
+        fn finds_correct_kernel_package_name() {
+            let mocked_run_cmd = |cmd: &[&str]| {
+                if cmd[0] == "dpkg" {
+                    assert_eq!(cmd, &["dpkg", "--print-architecture"]);
+                    Ok("amd64\n".to_owned())
+                } else {
+                    assert_eq!(
+                        cmd,
+                        &[
+                            "dpkg-query",
+                            "--showformat",
+                            "${db:Status-Abbrev}|${Architecture}|${Package}\\n",
+                            "--show",
+                            "proxmox-kernel-[0-9]*",
+                        ]
+                    );
+                    Ok(r#"ii |all|proxmox-kernel-6.8
+un ||proxmox-kernel-6.8.8-2-pve
+ii |amd64|proxmox-kernel-6.8.8-2-pve-signed
+                "#
+                    .to_owned())
+                }
+            };
+
+            assert_eq!(
+                find_kernel_package_name(&mocked_run_cmd).unwrap(),
+                "proxmox-kernel-6.8.8-2-pve-signed"
+            );
+        }
+
+        #[test]
+        fn finds_correct_kernel_package_name_arm64() {
+            let mocked_run_cmd = |cmd: &[&str]| {
+                if cmd[0] == "dpkg" {
+                    assert_eq!(cmd, &["dpkg", "--print-architecture"]);
+                    Ok("arm64\n".to_owned())
+                } else {
+                    assert_eq!(
+                        cmd,
+                        &[
+                            "dpkg-query",
+                            "--showformat",
+                            "${db:Status-Abbrev}|${Architecture}|${Package}\\n",
+                            "--show",
+                            "proxmox-kernel-[0-9]*",
+                        ]
+                    );
+                    Ok(r#"ii |all|proxmox-kernel-6.17
+un ||proxmox-kernel-6.17.2-1-pve
+ii |arm64|proxmox-kernel-6.17.2-1-pve-signed
+                "#
+                    .to_owned())
+                }
+            };
+
+            assert_eq!(
+                find_kernel_package_name(&mocked_run_cmd).unwrap(),
+                "proxmox-kernel-6.17.2-1-pve-signed"
+            );
+        }
+
+        #[test]
+        fn find_kernel_package_name_fails_on_wrong_architecture() {
+            let mocked_run_cmd = |cmd: &[&str]| {
+                if cmd[0] == "dpkg" {
+                    assert_eq!(cmd, &["dpkg", "--print-architecture"]);
+                    Ok("arm64\n".to_owned())
+                } else {
+                    assert_eq!(
+                        cmd,
+                        &[
+                            "dpkg-query",
+                            "--showformat",
+                            "${db:Status-Abbrev}|${Architecture}|${Package}\\n",
+                            "--show",
+                            "proxmox-kernel-[0-9]*",
+                        ]
+                    );
+                    Ok(r#"ii |all|proxmox-kernel-6.8
+un ||proxmox-kernel-6.8.8-2-pve
+ii |amd64|proxmox-kernel-6.8.8-2-pve-signed
+                "#
+                    .to_owned())
+                }
+            };
+
+            assert_eq!(
+                find_kernel_package_name(&mocked_run_cmd)
+                    .unwrap_err()
+                    .to_string(),
+                "failed to find installed kernel package"
+            );
+        }
+
+        #[test]
+        fn find_kernel_package_name_fails_on_missing_package() {
+            let mocked_run_cmd = |cmd: &[&str]| {
+                if cmd[0] == "dpkg" {
+                    assert_eq!(cmd, &["dpkg", "--print-architecture"]);
+                    Ok("amd64\n".to_owned())
+                } else {
+                    assert_eq!(
+                        cmd,
+                        &[
+                            "dpkg-query",
+                            "--showformat",
+                            "${db:Status-Abbrev}|${Architecture}|${Package}\\n",
+                            "--show",
+                            "proxmox-kernel-[0-9]*",
+                        ]
+                    );
+                    Ok(r#"ii |all|proxmox-kernel-6.8
+un ||proxmox-kernel-6.8.8-2-pve
+                "#
+                    .to_owned())
+                }
+            };
+
+            assert_eq!(
+                find_kernel_package_name(&mocked_run_cmd)
+                    .unwrap_err()
+                    .to_string(),
+                "failed to find installed kernel package"
+            );
+        }
+
+        #[test]
+        fn finds_correct_absolute_kernel_image_path() {
+            let mocked_run_cmd = |cmd: &[&str]| {
+                if cmd[0] == "dpkg" {
+                    assert_eq!(cmd, &["dpkg", "--print-architecture"]);
+                    Ok("amd64\n".to_owned())
+                } else if cmd[0..=1] == ["dpkg-query", "--showformat"] {
+                    assert_eq!(
+                        cmd,
+                        &[
+                            "dpkg-query",
+                            "--showformat",
+                            "${db:Status-Abbrev}|${Architecture}|${Package}\\n",
+                            "--show",
+                            "proxmox-kernel-[0-9]*",
+                        ]
+                    );
+                    Ok(r#"ii |all|proxmox-kernel-6.8
+un ||proxmox-kernel-6.8.8-2-pve
+ii |amd64|proxmox-kernel-6.8.8-2-pve-signed
+                "#
+                    .to_owned())
+                } else {
+                    assert_eq!(
+                        cmd,
+                        [
+                            "dpkg-query",
+                            "--listfiles",
+                            "proxmox-kernel-6.8.8-2-pve-signed"
+                        ]
+                    );
+                    Ok(r#"
+/.
+/boot
+/boot/System.map-6.8.8-2-pve
+/boot/config-6.8.8-2-pve
+/boot/vmlinuz-6.8.8-2-pve
+/lib
+/lib/modules
+/lib/modules/6.8.8-2-pve
+/lib/modules/6.8.8-2-pve/kernel
+/lib/modules/6.8.8-2-pve/kernel/arch
+/lib/modules/6.8.8-2-pve/kernel/arch/x86
+/lib/modules/6.8.8-2-pve/kernel/arch/x86/crypto
+/lib/modules/6.8.8-2-pve/kernel/arch/x86/crypto/aegis128-aesni.ko
+/lib/modules/6.8.8-2-pve/kernel/arch/x86/crypto/aesni-intel.ko
+/lib/modules/6.8.8-2-pve/kernel/arch/x86/crypto/aria-aesni-avx-x86_64.ko
+/lib/modules/6.8.8-2-pve/kernel/arch/x86/crypto/aria-aesni-avx2-x86_64.ko
+/lib/modules/6.8.8-2-pve/kernel/arch/x86/crypto/aria-gfni-avx512-x86_64.ko
+/lib/modules/6.8.8-2-pve/kernel/arch/x86/crypto/blowfish-x86_64.ko
+/lib/modules/6.8.8-2-pve/kernel/arch/x86/crypto/camellia-aesni-avx-x86_64.ko
+                "#
+                    .to_owned())
+                }
+            };
+
+            assert_eq!(
+                find_kernel_image_path(&mocked_run_cmd).unwrap(),
+                "/boot/vmlinuz-6.8.8-2-pve"
+            );
+        }
+    }
 }
 
 /// Runs the specified callback with the mounted chroot, passing along the
@@ -704,7 +744,9 @@ fn with_chroot<R, F: FnOnce(&str) -> Result<R>>(callback: F) -> Result<R> {
 /// optional certificate fingerprint for HTTPS). If configured, retrieves all relevant information
 /// about the installed system and sends them to the given endpoint.
 fn do_main() -> Result<()> {
-    let answer = Answer::try_from_reader(std::io::stdin().lock())?;
+    let mut raw_toml = String::new();
+    std::io::stdin().read_to_string(&mut raw_toml)?;
+    let answer: AutoInstallerConfig = toml::from_str(&raw_toml)?;
 
     if let Some(PostNotificationHookInfo {
         url,
@@ -713,7 +755,7 @@ fn do_main() -> Result<()> {
     {
         println!("Found post-installation-webhook; sending POST request to '{url}'.");
 
-        let info = with_chroot(|target_path| PostHookInfo::gather(target_path, &answer))?;
+        let info = with_chroot(|target_path| detail::gather(target_path, &answer))?;
 
         if let Err(err) = fs::write(
             "/run/proxmox-installer/post-hook-data.json",
@@ -747,197 +789,3 @@ fn main() -> ExitCode {
         }
     }
 }
-
-#[cfg(test)]
-mod tests {
-    use crate::PostHookInfo;
-
-    #[test]
-    fn finds_correct_kernel_package_name() {
-        let mocked_run_cmd = |cmd: &[&str]| {
-            if cmd[0] == "dpkg" {
-                assert_eq!(cmd, &["dpkg", "--print-architecture"]);
-                Ok("amd64\n".to_owned())
-            } else {
-                assert_eq!(
-                    cmd,
-                    &[
-                        "dpkg-query",
-                        "--showformat",
-                        "${db:Status-Abbrev}|${Architecture}|${Package}\\n",
-                        "--show",
-                        "proxmox-kernel-[0-9]*",
-                    ]
-                );
-                Ok(r#"ii |all|proxmox-kernel-6.8
-un ||proxmox-kernel-6.8.8-2-pve
-ii |amd64|proxmox-kernel-6.8.8-2-pve-signed
-                "#
-                .to_owned())
-            }
-        };
-
-        assert_eq!(
-            PostHookInfo::find_kernel_package_name(&mocked_run_cmd).unwrap(),
-            "proxmox-kernel-6.8.8-2-pve-signed"
-        );
-    }
-
-    #[test]
-    fn finds_correct_kernel_package_name_arm64() {
-        let mocked_run_cmd = |cmd: &[&str]| {
-            if cmd[0] == "dpkg" {
-                assert_eq!(cmd, &["dpkg", "--print-architecture"]);
-                Ok("arm64\n".to_owned())
-            } else {
-                assert_eq!(
-                    cmd,
-                    &[
-                        "dpkg-query",
-                        "--showformat",
-                        "${db:Status-Abbrev}|${Architecture}|${Package}\\n",
-                        "--show",
-                        "proxmox-kernel-[0-9]*",
-                    ]
-                );
-                Ok(r#"ii |all|proxmox-kernel-6.17
-un ||proxmox-kernel-6.17.2-1-pve
-ii |arm64|proxmox-kernel-6.17.2-1-pve-signed
-                "#
-                .to_owned())
-            }
-        };
-
-        assert_eq!(
-            PostHookInfo::find_kernel_package_name(&mocked_run_cmd).unwrap(),
-            "proxmox-kernel-6.17.2-1-pve-signed"
-        );
-    }
-
-    #[test]
-    fn find_kernel_package_name_fails_on_wrong_architecture() {
-        let mocked_run_cmd = |cmd: &[&str]| {
-            if cmd[0] == "dpkg" {
-                assert_eq!(cmd, &["dpkg", "--print-architecture"]);
-                Ok("arm64\n".to_owned())
-            } else {
-                assert_eq!(
-                    cmd,
-                    &[
-                        "dpkg-query",
-                        "--showformat",
-                        "${db:Status-Abbrev}|${Architecture}|${Package}\\n",
-                        "--show",
-                        "proxmox-kernel-[0-9]*",
-                    ]
-                );
-                Ok(r#"ii |all|proxmox-kernel-6.8
-un ||proxmox-kernel-6.8.8-2-pve
-ii |amd64|proxmox-kernel-6.8.8-2-pve-signed
-                "#
-                .to_owned())
-            }
-        };
-
-        assert_eq!(
-            PostHookInfo::find_kernel_package_name(&mocked_run_cmd)
-                .unwrap_err()
-                .to_string(),
-            "failed to find installed kernel package"
-        );
-    }
-
-    #[test]
-    fn find_kernel_package_name_fails_on_missing_package() {
-        let mocked_run_cmd = |cmd: &[&str]| {
-            if cmd[0] == "dpkg" {
-                assert_eq!(cmd, &["dpkg", "--print-architecture"]);
-                Ok("amd64\n".to_owned())
-            } else {
-                assert_eq!(
-                    cmd,
-                    &[
-                        "dpkg-query",
-                        "--showformat",
-                        "${db:Status-Abbrev}|${Architecture}|${Package}\\n",
-                        "--show",
-                        "proxmox-kernel-[0-9]*",
-                    ]
-                );
-                Ok(r#"ii |all|proxmox-kernel-6.8
-un ||proxmox-kernel-6.8.8-2-pve
-                "#
-                .to_owned())
-            }
-        };
-
-        assert_eq!(
-            PostHookInfo::find_kernel_package_name(&mocked_run_cmd)
-                .unwrap_err()
-                .to_string(),
-            "failed to find installed kernel package"
-        );
-    }
-
-    #[test]
-    fn finds_correct_absolute_kernel_image_path() {
-        let mocked_run_cmd = |cmd: &[&str]| {
-            if cmd[0] == "dpkg" {
-                assert_eq!(cmd, &["dpkg", "--print-architecture"]);
-                Ok("amd64\n".to_owned())
-            } else if cmd[0..=1] == ["dpkg-query", "--showformat"] {
-                assert_eq!(
-                    cmd,
-                    &[
-                        "dpkg-query",
-                        "--showformat",
-                        "${db:Status-Abbrev}|${Architecture}|${Package}\\n",
-                        "--show",
-                        "proxmox-kernel-[0-9]*",
-                    ]
-                );
-                Ok(r#"ii |all|proxmox-kernel-6.8
-un ||proxmox-kernel-6.8.8-2-pve
-ii |amd64|proxmox-kernel-6.8.8-2-pve-signed
-                "#
-                .to_owned())
-            } else {
-                assert_eq!(
-                    cmd,
-                    [
-                        "dpkg-query",
-                        "--listfiles",
-                        "proxmox-kernel-6.8.8-2-pve-signed"
-                    ]
-                );
-                Ok(r#"
-/.
-/boot
-/boot/System.map-6.8.8-2-pve
-/boot/config-6.8.8-2-pve
-/boot/vmlinuz-6.8.8-2-pve
-/lib
-/lib/modules
-/lib/modules/6.8.8-2-pve
-/lib/modules/6.8.8-2-pve/kernel
-/lib/modules/6.8.8-2-pve/kernel/arch
-/lib/modules/6.8.8-2-pve/kernel/arch/x86
-/lib/modules/6.8.8-2-pve/kernel/arch/x86/crypto
-/lib/modules/6.8.8-2-pve/kernel/arch/x86/crypto/aegis128-aesni.ko
-/lib/modules/6.8.8-2-pve/kernel/arch/x86/crypto/aesni-intel.ko
-/lib/modules/6.8.8-2-pve/kernel/arch/x86/crypto/aria-aesni-avx-x86_64.ko
-/lib/modules/6.8.8-2-pve/kernel/arch/x86/crypto/aria-aesni-avx2-x86_64.ko
-/lib/modules/6.8.8-2-pve/kernel/arch/x86/crypto/aria-gfni-avx512-x86_64.ko
-/lib/modules/6.8.8-2-pve/kernel/arch/x86/crypto/blowfish-x86_64.ko
-/lib/modules/6.8.8-2-pve/kernel/arch/x86/crypto/camellia-aesni-avx-x86_64.ko
-                "#
-                .to_owned())
-            }
-        };
-
-        assert_eq!(
-            PostHookInfo::find_kernel_image_path(&mocked_run_cmd).unwrap(),
-            "/boot/vmlinuz-6.8.8-2-pve"
-        );
-    }
-}
diff --git a/proxmox-tui-installer/Cargo.toml b/proxmox-tui-installer/Cargo.toml
index 1ca91cb..56395a4 100644
--- a/proxmox-tui-installer/Cargo.toml
+++ b/proxmox-tui-installer/Cargo.toml
@@ -10,6 +10,7 @@ homepage = "https://www.proxmox.com"
 [dependencies]
 proxmox-installer-common.workspace = true
 proxmox-network-types.workspace = true
+proxmox-installer-types.workspace = true
 anyhow.workspace = true
 serde_json.workspace = true
 
diff --git a/proxmox-tui-installer/src/options.rs b/proxmox-tui-installer/src/options.rs
index c80877f..ff15fa0 100644
--- a/proxmox-tui-installer/src/options.rs
+++ b/proxmox-tui-installer/src/options.rs
@@ -2,29 +2,10 @@ use crate::SummaryOption;
 
 use proxmox_installer_common::{
     EMAIL_DEFAULT_PLACEHOLDER,
-    options::{
-        BootdiskOptions, BtrfsRaidLevel, FsType, NetworkOptions, TimezoneOptions, ZfsRaidLevel,
-    },
+    options::{BootdiskOptions, NetworkOptions, TimezoneOptions},
     setup::LocaleInfo,
 };
 
-pub const FS_TYPES: &[FsType] = {
-    use FsType::*;
-    &[
-        Ext4,
-        Xfs,
-        Zfs(ZfsRaidLevel::Raid0),
-        Zfs(ZfsRaidLevel::Raid1),
-        Zfs(ZfsRaidLevel::Raid10),
-        Zfs(ZfsRaidLevel::RaidZ),
-        Zfs(ZfsRaidLevel::RaidZ2),
-        Zfs(ZfsRaidLevel::RaidZ3),
-        Btrfs(BtrfsRaidLevel::Raid0),
-        Btrfs(BtrfsRaidLevel::Raid1),
-        Btrfs(BtrfsRaidLevel::Raid10),
-    ]
-};
-
 #[derive(Clone)]
 pub struct PasswordOptions {
     pub email: String,
diff --git a/proxmox-tui-installer/src/views/bootdisk.rs b/proxmox-tui-installer/src/views/bootdisk.rs
index 5ec3e83..ed3936f 100644
--- a/proxmox-tui-installer/src/views/bootdisk.rs
+++ b/proxmox-tui-installer/src/views/bootdisk.rs
@@ -16,7 +16,6 @@ use cursive::{
 
 use super::{DiskSizeEditView, FormView, IntegerEditView, TabbedView};
 use crate::InstallerState;
-use crate::options::FS_TYPES;
 
 use proxmox_installer_common::{
     disk_checks::{
@@ -24,11 +23,12 @@ use proxmox_installer_common::{
     },
     options::{
         AdvancedBootdiskOptions, BTRFS_COMPRESS_OPTIONS, BootdiskOptions, BtrfsBootdiskOptions,
-        Disk, FsType, LvmBootdiskOptions, ZFS_CHECKSUM_OPTIONS, ZFS_COMPRESS_OPTIONS,
+        Disk, LvmBootdiskOptions, RaidLevel, ZFS_CHECKSUM_OPTIONS, ZFS_COMPRESS_OPTIONS,
         ZfsBootdiskOptions,
     },
     setup::{BootType, ProductConfig, ProxmoxProduct, RuntimeInfo},
 };
+use proxmox_installer_types::answer::{FILESYSTEM_TYPE_OPTIONS, FilesystemType};
 
 /// OpenZFS specifies 64 MiB as the absolute minimum:
 /// <https://openzfs.github.io/openzfs-docs/Performance%20and%20Tuning/Module%20Parameters.html#zfs-arc-max>
@@ -125,19 +125,19 @@ impl AdvancedBootdiskOptionsView {
         product_conf: ProductConfig,
     ) -> Self {
         let filter_btrfs =
-            |fstype: &&FsType| -> bool { product_conf.enable_btrfs || !fstype.is_btrfs() };
+            |fstype: &&FilesystemType| -> bool { product_conf.enable_btrfs || !fstype.is_btrfs() };
         let options = options_ref.lock().unwrap();
 
         let fstype_select = SelectView::new()
             .popup()
             .with_all(
-                FS_TYPES
+                FILESYSTEM_TYPE_OPTIONS
                     .iter()
                     .filter(filter_btrfs)
                     .map(|t| (t.to_string(), *t)),
             )
             .selected(
-                FS_TYPES
+                FILESYSTEM_TYPE_OPTIONS
                     .iter()
                     .filter(filter_btrfs)
                     .position(|t| *t == options.fstype)
@@ -185,7 +185,11 @@ impl AdvancedBootdiskOptionsView {
     /// * `fstype` - The chosen filesystem type by the user, for which the UI should be
     ///   updated accordingly
     /// * `options_ref` - [`BootdiskOptionsRef`] where advanced disk options should be saved to
-    fn fstype_on_submit(siv: &mut Cursive, fstype: &FsType, options_ref: BootdiskOptionsRef) {
+    fn fstype_on_submit(
+        siv: &mut Cursive,
+        fstype: &FilesystemType,
+        options_ref: BootdiskOptionsRef,
+    ) {
         let state = siv.user_data::<InstallerState>().unwrap();
         let runinfo = state.runtime_info.clone();
         let product_conf = state.setup_info.config.clone();
@@ -208,16 +212,16 @@ impl AdvancedBootdiskOptionsView {
             {
                 view.remove_child(3);
                 match fstype {
-                    FsType::Ext4 | FsType::Xfs => {
+                    FilesystemType::Ext4 | FilesystemType::Xfs => {
                         view.add_child(LvmBootdiskOptionsView::new_with_defaults(
                             &selected_lvm_disk,
                             &product_conf,
                         ))
                     }
-                    FsType::Zfs(_) => {
+                    FilesystemType::Zfs(_) => {
                         view.add_child(ZfsBootdiskOptionsView::new_with_defaults(&runinfo))
                     }
-                    FsType::Btrfs(_) => {
+                    FilesystemType::Btrfs(_) => {
                         view.add_child(BtrfsBootdiskOptionsView::new_with_defaults(&runinfo))
                     }
                 }
@@ -236,7 +240,7 @@ impl AdvancedBootdiskOptionsView {
         siv.call_on_name(
             "bootdisk-options-target-disk",
             move |view: &mut FormView| match fstype {
-                FsType::Ext4 | FsType::Xfs => {
+                FilesystemType::Ext4 | FilesystemType::Xfs => {
                     view.replace_child(
                         0,
                         target_bootdisk_selectview(&runinfo.disks, options_ref, &selected_lvm_disk),
@@ -252,7 +256,7 @@ impl AdvancedBootdiskOptionsView {
             .view
             .get_child(1)
             .and_then(|v| v.downcast_ref::<FormView>())
-            .and_then(|v| v.get_value::<SelectView<FsType>, _>(0))
+            .and_then(|v| v.get_value::<SelectView<FilesystemType>, _>(0))
             .ok_or("Failed to retrieve filesystem type".to_owned())?;
 
         let advanced = self
@@ -279,7 +283,7 @@ impl AdvancedBootdiskOptionsView {
                 .get_values()
                 .ok_or("Failed to retrieve advanced bootdisk options")?;
 
-            if let FsType::Zfs(level) = fstype {
+            if let FilesystemType::Zfs(level) = fstype {
                 level
                     .check_raid_disks_setup(&disks)
                     .map_err(|err| format!("{fstype}: {err}"))?;
@@ -295,7 +299,7 @@ impl AdvancedBootdiskOptionsView {
                 .get_values()
                 .ok_or("Failed to retrieve advanced bootdisk options")?;
 
-            if let FsType::Btrfs(level) = fstype {
+            if let FilesystemType::Btrfs(level) = fstype {
                 level
                     .check_raid_disks_setup(&disks)
                     .map_err(|err| format!("{fstype}: {err}"))?;
-- 
2.53.0





  parent reply	other threads:[~2026-04-30 12:50 UTC|newest]

Thread overview: 41+ messages / expand[flat|nested]  mbox.gz  Atom feed  top
2026-04-30 12:46 [PATCH datacenter-manager/installer/proxmox/yew-comp v4 00/40] add auto-installer integration Christoph Heiss
2026-04-30 12:46 ` [PATCH proxmox v4 01/40] api-macro: allow $ in identifier name Christoph Heiss
2026-04-30 12:46 ` [PATCH proxmox v4 02/40] schema: oneOf: allow single string variant Christoph Heiss
2026-04-30 12:46 ` [PATCH proxmox v4 03/40] schema: implement UpdaterType for HashMap and BTreeMap Christoph Heiss
2026-04-30 12:46 ` [PATCH proxmox v4 04/40] network-types: move `Fqdn` type from proxmox-installer-common Christoph Heiss
2026-04-30 12:46 ` [PATCH proxmox v4 05/40] network-types: implement api type for Fqdn Christoph Heiss
2026-04-30 12:46 ` [PATCH proxmox v4 06/40] network-types: add api wrapper type for std::net::IpAddr Christoph Heiss
2026-04-30 12:46 ` [PATCH proxmox v4 07/40] network-types: cidr: implement generic `IpAddr::new` constructor Christoph Heiss
2026-04-30 12:46 ` [PATCH proxmox v4 08/40] network-types: fqdn: implement standard library Error for Fqdn Christoph Heiss
2026-04-30 12:46 ` [PATCH proxmox v4 09/40] node-status: make KernelVersionInformation Clone + PartialEq Christoph Heiss
2026-04-30 12:46 ` [PATCH proxmox v4 10/40] installer-types: add common types used by the installer Christoph Heiss
2026-04-30 12:46 ` [PATCH proxmox v4 11/40] installer-types: add types used by the auto-installer Christoph Heiss
2026-04-30 12:46 ` [PATCH proxmox v4 12/40] installer-types: implement api type for all externally-used types Christoph Heiss
2026-04-30 12:46 ` [PATCH yew-comp v4 13/40] widget: kvlist: add widget for user-modifiable data tables Christoph Heiss
2026-04-30 12:46 ` [PATCH datacenter-manager v4 14/40] api-types, cli: use ReturnType::new() instead of constructing it manually Christoph Heiss
2026-04-30 12:46 ` [PATCH datacenter-manager v4 15/40] api-types: add api types for auto-installer integration Christoph Heiss
2026-04-30 12:46 ` [PATCH datacenter-manager v4 16/40] config: add auto-installer configuration module Christoph Heiss
2026-04-30 12:46 ` [PATCH datacenter-manager v4 17/40] acl: wire up new /system/auto-installation acl path Christoph Heiss
2026-04-30 12:46 ` [PATCH datacenter-manager v4 18/40] server: api: add auto-installer integration module Christoph Heiss
2026-04-30 12:46 ` [PATCH datacenter-manager v4 19/40] server: api: auto-installer: add access token management endpoints Christoph Heiss
2026-04-30 12:46 ` [PATCH datacenter-manager v4 20/40] client: add bindings for auto-installer endpoints Christoph Heiss
2026-04-30 12:46 ` [PATCH datacenter-manager v4 21/40] ui: auto-installer: add installations overview panel Christoph Heiss
2026-04-30 12:46 ` [PATCH datacenter-manager v4 22/40] ui: auto-installer: add prepared answer configuration panel Christoph Heiss
2026-04-30 12:46 ` [PATCH datacenter-manager v4 23/40] ui: auto-installer: add access token " Christoph Heiss
2026-04-30 12:46 ` [PATCH datacenter-manager v4 24/40] docs: add documentation for auto-installer integration Christoph Heiss
2026-04-30 12:46 ` [PATCH installer v4 25/40] install: iso env: use JSON boolean literals for product config Christoph Heiss
2026-04-30 12:46 ` [PATCH installer v4 26/40] common: http: allow passing custom headers to post() Christoph Heiss
2026-04-30 12:46 ` [PATCH installer v4 27/40] common: http: retrieve error message from body on post() Christoph Heiss
2026-04-30 12:46 ` [PATCH installer v4 28/40] common: options: move regex construction out of loop Christoph Heiss
2026-04-30 12:46 ` [PATCH installer v4 29/40] assistant: support adding an authorization token for HTTP-based answers Christoph Heiss
2026-04-30 12:46 ` [PATCH installer v4 30/40] post-hook: run cargo fmt Christoph Heiss
2026-04-30 12:47 ` [PATCH installer v4 31/40] tree-wide: used moved `Fqdn` type to proxmox-network-types Christoph Heiss
2026-04-30 12:47 ` [PATCH installer v4 32/40] tree-wide: use `Cidr` type from proxmox-network-types Christoph Heiss
2026-04-30 12:47 ` Christoph Heiss [this message]
2026-04-30 12:47 ` [PATCH installer v4 34/40] auto: sysinfo: switch to types from proxmox-installer-types Christoph Heiss
2026-04-30 12:47 ` [PATCH installer v4 35/40] fetch-answer: " Christoph Heiss
2026-04-30 12:47 ` [PATCH installer v4 36/40] fetch-answer: http: prefer json over toml for answer format Christoph Heiss
2026-04-30 12:47 ` [PATCH installer v4 37/40] fetch-answer: send auto-installer HTTP authorization token if set Christoph Heiss
2026-04-30 12:47 ` [PATCH installer v4 38/40] fetch-answer: print full error messages when fetching failed Christoph Heiss
2026-04-30 12:47 ` [PATCH installer v4 39/40] tree-wide: switch out `Answer` -> `AutoInstallerConfig` types Christoph Heiss
2026-04-30 12:47 ` [PATCH installer v4 40/40] auto: drop now-dead answer file definitions Christoph Heiss

Reply instructions:

You may reply publicly to this message via plain-text email
using any one of the following methods:

* Save the following mbox file, import it into your mail client,
  and reply-to-all from there: mbox

  Avoid top-posting and favor interleaved quoting:
  https://en.wikipedia.org/wiki/Posting_style#Interleaved_style

* Reply using the --to, --cc, and --in-reply-to
  switches of git-send-email(1):

  git send-email \
    --in-reply-to=20260430124712.1614305-34-c.heiss@proxmox.com \
    --to=c.heiss@proxmox.com \
    --cc=pdm-devel@lists.proxmox.com \
    /path/to/YOUR_REPLY

  https://kernel.org/pub/software/scm/git/docs/git-send-email.html

* If your mail client supports setting the In-Reply-To header
  via mailto: links, try the mailto: link
Be sure your reply has a Subject: header at the top and a blank line before the message body.
This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox
Service provided by Proxmox Server Solutions GmbH | Privacy | Legal