From mboxrd@z Thu Jan 1 00:00:00 1970 Return-Path: Received: from firstgate.proxmox.com (firstgate.proxmox.com [IPv6:2a01:7e0:0:424::9]) by lore.proxmox.com (Postfix) with ESMTPS id 47DA31FF13E for ; Fri, 03 Apr 2026 18:57:49 +0200 (CEST) Received: from firstgate.proxmox.com (localhost [127.0.0.1]) by firstgate.proxmox.com (Proxmox) with ESMTP id 102E792BE; Fri, 3 Apr 2026 18:58:20 +0200 (CEST) From: Christoph Heiss To: pdm-devel@lists.proxmox.com Subject: [PATCH installer v3 38/38] auto: drop now-dead answer file definitions Date: Fri, 3 Apr 2026 18:54:10 +0200 Message-ID: <20260403165437.2166551-39-c.heiss@proxmox.com> X-Mailer: git-send-email 2.53.0 In-Reply-To: <20260403165437.2166551-1-c.heiss@proxmox.com> References: <20260403165437.2166551-1-c.heiss@proxmox.com> MIME-Version: 1.0 Content-Transfer-Encoding: 8bit X-Bm-Milter-Handled: 55990f41-d878-4baa-be0a-ee34c49e34d2 X-Bm-Transport-Timestamp: 1775235422087 X-SPAM-LEVEL: Spam detection results: 0 AWL -2.432 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 KAM_SOMETLD_ARE_BAD_TLD 5 .bar, .beauty, .buzz, .cam, .casa, .cfd, .club, .date, .guru, .link, .live, .monster, .online, .press, .pw, .quest, .rest, .sbs, .shop, .stream, .top, .trade, .wiki, .work, .xyz TLD abuse SPF_HELO_NONE 0.001 SPF: HELO does not publish an SPF Record SPF_PASS -0.001 SPF: sender matches SPF record Message-ID-Hash: V4LN5Z6DCU5JAUVUCDEOH7JDFMPYOOPJ X-Message-ID-Hash: V4LN5Z6DCU5JAUVUCDEOH7JDFMPYOOPJ X-MailFrom: c.heiss@proxmox.com X-Mailman-Rule-Misses: dmarc-mitigation; no-senders; approved; loop; banned-address; emergency; member-moderation; nonmember-moderation; administrivia; implicit-dest; max-recipients; max-size; news-moderation; no-subject; digests; suspicious-header X-Mailman-Version: 3.3.10 Precedence: list List-Id: Proxmox Datacenter Manager development discussion List-Help: List-Owner: List-Post: List-Subscribe: List-Unsubscribe: These types are now wholly unused, so drop them. No functional changes. Signed-off-by: Christoph Heiss --- Changes v2 -> v3: * new patch proxmox-auto-installer/src/answer.rs | 511 ------------------------- proxmox-auto-installer/src/lib.rs | 2 - proxmox-auto-installer/src/udevinfo.rs | 11 - 3 files changed, 524 deletions(-) delete mode 100644 proxmox-auto-installer/src/answer.rs delete mode 100644 proxmox-auto-installer/src/udevinfo.rs diff --git a/proxmox-auto-installer/src/answer.rs b/proxmox-auto-installer/src/answer.rs deleted file mode 100644 index c7e7298..0000000 --- a/proxmox-auto-installer/src/answer.rs +++ /dev/null @@ -1,511 +0,0 @@ -use anyhow::{Result, bail, format_err}; -use serde::{Deserialize, Serialize}; -use std::{ - collections::{BTreeMap, HashMap}, - io::BufRead, - net::IpAddr, -}; - -use proxmox_installer_common::options::NetworkInterfacePinningOptions; -use proxmox_installer_types::answer::{ - BtrfsCompressOption, BtrfsRaidLevel, FilesystemType, ZfsChecksumOption, ZfsCompressOption, - ZfsRaidLevel, -}; -use proxmox_network_types::{Cidr, fqdn::Fqdn}; - -// NOTE New answer file properties must use kebab-case, but should allow snake_case for backwards -// compatibility. TODO Remove the snake_cased variants in a future major version (e.g. PVE 10). - -// BTreeMap is used to store filters as the order of the filters will be stable, compared to -// storing them in a HashMap - -#[derive(Clone, Deserialize, Debug)] -#[serde(rename_all = "kebab-case", deny_unknown_fields)] -pub struct Answer { - pub global: Global, - pub network: Network, - #[serde(rename = "disk-setup")] - pub disks: Disks, - pub post_installation_webhook: Option, - pub first_boot: Option, -} - -impl Answer { - pub fn try_from_reader(reader: impl BufRead) -> Result { - let mut buffer = String::new(); - let lines = reader.lines(); - for line in lines { - buffer.push_str(&line.unwrap()); - buffer.push('\n'); - } - - toml::from_str(&buffer).map_err(|err| format_err!("Failed parsing answer file: {err}")) - } -} - -#[derive(Clone, Deserialize, Debug)] -#[serde(rename_all = "kebab-case", deny_unknown_fields)] -pub struct Global { - pub country: String, - /// FQDN to set for the installed system. - pub fqdn: FqdnConfig, - pub keyboard: KeyboardLayout, - pub mailto: String, - pub timezone: String, - #[serde(alias = "root_password")] - pub root_password: Option, - #[serde(alias = "root_password_hashed")] - pub root_password_hashed: Option, - #[serde(alias = "reboot_on_error", default)] - pub reboot_on_error: bool, - #[serde(alias = "reboot_mode", default)] - pub reboot_mode: RebootMode, - #[serde(alias = "root_ssh_keys", default)] - pub root_ssh_keys: Vec, -} - -#[derive(Copy, Clone, Deserialize, Serialize, Debug, Default, PartialEq, Eq)] -#[serde(rename_all = "kebab-case", deny_unknown_fields)] -pub enum RebootMode { - #[default] - Reboot, - PowerOff, -} - -/// Allow the user to either set the FQDN of the installation to either some -/// fixed value or retrieve it dynamically via e.g.DHCP. -#[derive(Clone, Deserialize, Debug)] -#[serde( - untagged, - expecting = "either a fully-qualified domain name or extendend configuration for usage with DHCP must be specified" -)] -pub enum FqdnConfig { - /// Sets the FQDN to the exact value. - Simple(Fqdn), - /// Extended configuration, e.g. to use hostname and domain from DHCP. - Extended(FqdnExtendedConfig), -} - -/// Extended configuration for retrieving the FQDN from external sources. -#[derive(Clone, Deserialize, Debug)] -#[serde(rename_all = "kebab-case", deny_unknown_fields)] -pub struct FqdnExtendedConfig { - /// Source to gather the FQDN from. - #[serde(default)] - pub source: FqdnSourceMode, - /// Domain to use if none is received via DHCP. - #[serde(default, deserialize_with = "deserialize_non_empty_string_maybe")] - pub domain: Option, -} - -/// Describes the source to retrieve the FQDN of the installation. -#[derive(Clone, Deserialize, Debug, Default, PartialEq)] -#[serde(rename_all = "kebab-case", deny_unknown_fields)] -pub enum FqdnSourceMode { - #[default] - FromDhcp, -} - -#[derive(Clone, Deserialize, Debug)] -#[serde(rename_all = "kebab-case", deny_unknown_fields)] -pub struct PostNotificationHookInfo { - /// URL to send a POST request to - pub url: String, - /// SHA256 cert fingerprint if certificate pinning should be used. - #[serde(alias = "cert_fingerprint")] - pub cert_fingerprint: Option, -} - -/// Possible sources for the optional first-boot hook script/executable file. -#[derive(Clone, Deserialize, Debug, PartialEq)] -#[serde(rename_all = "kebab-case", deny_unknown_fields)] -pub enum FirstBootHookSourceMode { - /// Fetch the executable file from an URL, specified in the parent. - FromUrl, - /// The executable file has been baked into the ISO at a known location, - /// and should be retrieved from there. - FromIso, -} - -/// Possible orderings for the `proxmox-first-boot` systemd service. -/// -/// Determines the final value of `Unit.Before` and `Unit.Wants` in the service -/// file. -// Must be kept in sync with Proxmox::Install::Config and the service files in the -// proxmox-first-boot package. -#[derive(Clone, Default, Deserialize, Debug, PartialEq)] -#[serde(rename_all = "kebab-case", deny_unknown_fields)] -pub enum FirstBootHookServiceOrdering { - /// Needed for bringing up the network itself, runs before any networking is attempted. - BeforeNetwork, - /// Network needs to be already online, runs after networking was brought up. - NetworkOnline, - /// Runs after the system has successfully booted up completely. - #[default] - FullyUp, -} - -impl FirstBootHookServiceOrdering { - /// Maps the enum to the appropriate systemd target name, without the '.target' suffix. - pub fn as_systemd_target_name(&self) -> &str { - match self { - FirstBootHookServiceOrdering::BeforeNetwork => "network-pre", - FirstBootHookServiceOrdering::NetworkOnline => "network-online", - FirstBootHookServiceOrdering::FullyUp => "multi-user", - } - } -} - -/// Describes from where to fetch the first-boot hook script, either being baked into the ISO or -/// from a URL. -#[derive(Clone, Deserialize, Debug)] -#[serde(rename_all = "kebab-case", deny_unknown_fields)] -pub struct FirstBootHookInfo { - /// Mode how to retrieve the first-boot executable file, either from an URL or from the ISO if - /// it has been baked-in. - pub source: FirstBootHookSourceMode, - /// Determines the service order when the hook will run on first boot. - #[serde(default)] - pub ordering: FirstBootHookServiceOrdering, - /// Retrieve the post-install script from a URL, if source == "from-url". - pub url: Option, - /// SHA256 cert fingerprint if certificate pinning should be used, if source == "from-url". - #[serde(alias = "cert_fingerprint")] - pub cert_fingerprint: Option, -} - -#[derive(Clone, Deserialize, Debug, Default, PartialEq)] -#[serde(rename_all = "kebab-case", deny_unknown_fields)] -enum NetworkConfigMode { - #[default] - FromDhcp, - FromAnswer, -} - -/// Options controlling the behaviour of the network interface pinning (by -/// creating appropriate systemd.link files) during the installation. -#[derive(Clone, Debug, Default, PartialEq, Deserialize)] -#[serde(rename_all = "kebab-case", deny_unknown_fields)] -pub struct NetworkInterfacePinningOptionsAnswer { - /// Whether interfaces should be pinned during the installation. - pub enabled: bool, - /// Maps MAC address to custom name - #[serde(default)] - pub mapping: HashMap, -} - -#[derive(Clone, Deserialize, Debug)] -#[serde(rename_all = "kebab-case", deny_unknown_fields)] -struct NetworkInAnswer { - #[serde(default)] - pub source: NetworkConfigMode, - pub cidr: Option, - pub dns: Option, - pub gateway: Option, - #[serde(default)] - pub filter: BTreeMap, - /// Controls network interface pinning behaviour during installation. - /// Off by default. Allowed for both `from-dhcp` and `from-answer` modes. - #[serde(default)] - pub interface_name_pinning: Option, -} - -#[derive(Clone, Deserialize, Debug)] -#[serde(try_from = "NetworkInAnswer", deny_unknown_fields)] -pub struct Network { - pub network_settings: NetworkSettings, - /// Controls network interface pinning behaviour during installation. - pub interface_name_pinning: Option, -} - -impl TryFrom for Network { - type Error = anyhow::Error; - - fn try_from(network: NetworkInAnswer) -> Result { - let interface_name_pinning = match network.interface_name_pinning { - Some(opts) if opts.enabled => { - let opts = NetworkInterfacePinningOptions { - mapping: opts - .mapping - .iter() - .map(|(k, v)| (k.to_lowercase(), v.clone())) - .collect(), - }; - - opts.verify()?; - Some(opts) - } - _ => None, - }; - - if network.source == NetworkConfigMode::FromAnswer { - if network.cidr.is_none() { - bail!("Field 'cidr' must be set."); - } - if network.dns.is_none() { - bail!("Field 'dns' must be set."); - } - if network.gateway.is_none() { - bail!("Field 'gateway' must be set."); - } - if network.filter.is_empty() { - bail!("Field 'filter' must be set."); - } - - Ok(Network { - network_settings: NetworkSettings::Manual(NetworkManual { - cidr: network.cidr.unwrap(), - dns: network.dns.unwrap(), - gateway: network.gateway.unwrap(), - filter: network.filter, - }), - interface_name_pinning, - }) - } else { - if network.cidr.is_some() { - bail!("Field 'cidr' not supported for 'from-dhcp' config."); - } - if network.dns.is_some() { - bail!("Field 'dns' not supported for 'from-dhcp' config."); - } - if network.gateway.is_some() { - bail!("Field 'gateway' not supported for 'from-dhcp' config."); - } - if !network.filter.is_empty() { - bail!("Field 'filter' not supported for 'from-dhcp' config."); - } - - Ok(Network { - network_settings: NetworkSettings::FromDhcp, - interface_name_pinning, - }) - } - } -} - -#[derive(Clone, Debug)] -pub enum NetworkSettings { - FromDhcp, - Manual(NetworkManual), -} - -#[derive(Clone, Debug)] -pub struct NetworkManual { - pub cidr: Cidr, - pub dns: IpAddr, - pub gateway: IpAddr, - pub filter: BTreeMap, -} - -#[derive(Clone, Debug, Deserialize)] -#[serde(rename_all = "kebab-case", deny_unknown_fields)] -pub struct DiskSetup { - pub filesystem: Filesystem, - #[serde(alias = "disk_list", default)] - pub disk_list: Vec, - #[serde(default)] - pub filter: BTreeMap, - #[serde(alias = "filter_match")] - pub filter_match: Option, - pub zfs: Option, - pub lvm: Option, - pub btrfs: Option, -} - -#[derive(Clone, Debug, Deserialize)] -#[serde(try_from = "DiskSetup", deny_unknown_fields)] -pub struct Disks { - pub fs_type: FilesystemType, - pub disk_selection: DiskSelection, - pub filter_match: Option, - pub fs_options: FsOptions, -} - -impl TryFrom for Disks { - type Error = &'static str; - - fn try_from(source: DiskSetup) -> Result { - if source.disk_list.is_empty() && source.filter.is_empty() { - return Err("Need either 'disk-list' or 'filter' set"); - } - if !source.disk_list.is_empty() && !source.filter.is_empty() { - return Err("Cannot use both, 'disk-list' and 'filter'"); - } - - let disk_selection = if !source.disk_list.is_empty() { - DiskSelection::Selection(source.disk_list.clone()) - } else { - DiskSelection::Filter(source.filter.clone()) - }; - - let lvm_checks = |source: &DiskSetup| -> Result<(), Self::Error> { - if source.zfs.is_some() || source.btrfs.is_some() { - return Err("make sure only 'lvm' options are set"); - } - if source.disk_list.len() > 1 { - return Err("make sure to define only one disk for ext4 and xfs"); - } - Ok(()) - }; - // TODO: improve checks for foreign FS options. E.g. less verbose and handling new FS types - // automatically - let (fs, fs_options) = match source.filesystem { - Filesystem::Xfs => { - lvm_checks(&source)?; - ( - FilesystemType::Xfs, - FsOptions::LVM(source.lvm.unwrap_or_default()), - ) - } - Filesystem::Ext4 => { - lvm_checks(&source)?; - ( - FilesystemType::Ext4, - FsOptions::LVM(source.lvm.unwrap_or_default()), - ) - } - Filesystem::Zfs => { - if source.lvm.is_some() || source.btrfs.is_some() { - return Err("make sure only 'zfs' options are set"); - } - match source.zfs { - None | Some(ZfsOptions { raid: None, .. }) => { - return Err("ZFS raid level 'zfs.raid' must be set"); - } - Some(opts) => ( - FilesystemType::Zfs(opts.raid.unwrap()), - FsOptions::ZFS(opts), - ), - } - } - Filesystem::Btrfs => { - if source.zfs.is_some() || source.lvm.is_some() { - return Err("make sure only 'btrfs' options are set"); - } - match source.btrfs { - None | Some(BtrfsOptions { raid: None, .. }) => { - return Err("BTRFS raid level 'btrfs.raid' must be set"); - } - Some(opts) => ( - FilesystemType::Btrfs(opts.raid.unwrap()), - FsOptions::BTRFS(opts), - ), - } - } - }; - - let res = Disks { - fs_type: fs, - disk_selection, - filter_match: source.filter_match, - fs_options, - }; - Ok(res) - } -} - -#[derive(Clone, Debug)] -pub enum FsOptions { - LVM(LvmOptions), - ZFS(ZfsOptions), - BTRFS(BtrfsOptions), -} - -#[derive(Clone, Debug)] -pub enum DiskSelection { - Selection(Vec), - Filter(BTreeMap), -} - -#[derive(Clone, Deserialize, Debug, PartialEq)] -#[serde(rename_all = "lowercase", deny_unknown_fields)] -pub enum FilterMatch { - Any, - All, -} - -serde_plain::derive_fromstr_from_deserialize!(FilterMatch); - -#[derive(Clone, Deserialize, Serialize, Debug, PartialEq)] -#[serde(rename_all = "lowercase", deny_unknown_fields)] -pub enum Filesystem { - Ext4, - Xfs, - Zfs, - Btrfs, -} - -#[derive(Clone, Copy, Default, Deserialize, Debug)] -#[serde(rename_all = "kebab-case", deny_unknown_fields)] -pub struct ZfsOptions { - pub raid: Option, - pub ashift: Option, - #[serde(alias = "arc_max")] - pub arc_max: Option, - pub checksum: Option, - pub compress: Option, - pub copies: Option, - pub hdsize: Option, -} - -#[derive(Clone, Copy, Default, Deserialize, Serialize, Debug)] -#[serde(rename_all = "kebab-case", deny_unknown_fields)] -pub struct LvmOptions { - pub hdsize: Option, - pub swapsize: Option, - pub maxroot: Option, - pub maxvz: Option, - pub minfree: Option, -} - -#[derive(Clone, Copy, Default, Deserialize, Debug)] -#[serde(rename_all = "kebab-case", deny_unknown_fields)] -pub struct BtrfsOptions { - pub hdsize: Option, - pub raid: Option, - pub compress: Option, -} - -#[derive(Clone, Deserialize, Serialize, Debug, PartialEq)] -#[serde(rename_all = "kebab-case", deny_unknown_fields)] -pub enum KeyboardLayout { - De, - DeCh, - Dk, - EnGb, - EnUs, - Es, - Fi, - Fr, - FrBe, - FrCa, - FrCh, - Hu, - Is, - It, - Jp, - Lt, - Mk, - Nl, - No, - Pl, - Pt, - PtBr, - Se, - Si, - Tr, -} - -serde_plain::derive_display_from_serialize!(KeyboardLayout); - -fn deserialize_non_empty_string_maybe<'de, D>(deserializer: D) -> Result, D::Error> -where - D: serde::Deserializer<'de>, -{ - let val: Option = Deserialize::deserialize(deserializer)?; - - match val { - Some(s) if !s.is_empty() => Ok(Some(s)), - _ => Ok(None), - } -} diff --git a/proxmox-auto-installer/src/lib.rs b/proxmox-auto-installer/src/lib.rs index 3bdf0b5..8c51a07 100644 --- a/proxmox-auto-installer/src/lib.rs +++ b/proxmox-auto-installer/src/lib.rs @@ -1,5 +1,3 @@ -pub mod answer; pub mod log; pub mod sysinfo; -pub mod udevinfo; pub mod utils; diff --git a/proxmox-auto-installer/src/udevinfo.rs b/proxmox-auto-installer/src/udevinfo.rs deleted file mode 100644 index 677f3f6..0000000 --- a/proxmox-auto-installer/src/udevinfo.rs +++ /dev/null @@ -1,11 +0,0 @@ -use serde::Deserialize; -use std::collections::BTreeMap; - -/// Uses a BTreeMap to have the keys sorted -pub type UdevProperties = BTreeMap; - -#[derive(Clone, Deserialize, Debug)] -pub struct UdevInfo { - pub disks: BTreeMap, - pub nics: BTreeMap, -} -- 2.53.0