From mboxrd@z Thu Jan 1 00:00:00 1970 Return-Path: Received: from gate001.proxmox.com (gate001.proxmox.com [IPv6:2a0f:8001:1:32::40]) by lore.proxmox.com (Postfix) with ESMTPS id B7F5E1FF09C for ; Mon, 02 Nov 2026 13:46:42 +0100 (CET) Received: from gate001.proxmox.com (localhost.localdomain [127.0.0.1]) by gate001.proxmox.com (Proxmox) with ESMTP id 4ED4D21769; Mon, 02 Nov 2026 13:46:30 +0100 (CET) From: Christoph Heiss To: pve-devel@lists.proxmox.com Subject: [PATCH proxmox v4 05/30] installer-types: answer: add options for configuring management vlan Date: Mon, 2 Nov 2026 13:45:11 +0100 Message-ID: <20261102124539.1008075-6-c.heiss@proxmox.com> X-Mailer: git-send-email 2.55.0 In-Reply-To: <20261102124539.1008075-1-c.heiss@proxmox.com> References: <20261102124539.1008075-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: 1793623570209 X-SPAM-LEVEL: Spam detection results: 0 AWL 0.224 Adjusted score from AWL reputation of From: address DMARC_MISSING 0.1 Missing DMARC policy KAM_DMARC_STATUS 0.01 Test Rule for DKIM or SPF Failure with Strict Alignment (newer systems) RCVD_IN_DNSWL_MED -2.3 Sender listed at https://www.dnswl.org/, medium trust 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: AVPXGPMTFL2KSB5YXJSWCUBYAAKDZ57B X-Message-ID-Hash: AVPXGPMTFL2KSB5YXJSWCUBYAAKDZ57B 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 VE development discussion List-Help: List-Owner: List-Post: List-Subscribe: List-Unsubscribe: Adds a new subsection `vlan` in [network]. This allows for configuring a bond for the management network interface during the installation. E.g. [network.vlan] vid = 5 Although there is only one property to set (for now), use a dedicated struct here too, in the same vein as `NetworkBondOptions`. Makes things - a bit more equally structured and - leaves open the option for easily adding new VLAN-specific options in the future, if needed. Signed-off-by: Christoph Heiss --- Changes v3 -> v4: * restrict vlan range to 2-4094 Changes v2 -> v3: * new patch proxmox-installer-types/src/answer.rs | 73 +++++++++++++++++++++++++++ 1 file changed, 73 insertions(+) diff --git a/proxmox-installer-types/src/answer.rs b/proxmox-installer-types/src/answer.rs index 8482356c..c97f287a 100644 --- a/proxmox-installer-types/src/answer.rs +++ b/proxmox-installer-types/src/answer.rs @@ -593,6 +593,40 @@ impl NetworkBondOptions { } } +#[cfg_attr(feature = "api-types", api( + properties: { + vid: { + type: Integer, + // 0 and 4095 are reserved, 1 is the untagged default PVID of VLAN-aware bridges on + // Linux. + minimum: 2, + maximum: 4094, + default: 0, + } + }, +))] +#[derive(Clone, Debug, Deserialize, Serialize, PartialEq)] +#[serde(rename_all = "kebab-case")] +/// Options for setting up a VLAN configuration on the management interface. +pub struct NetworkVlanOptions { + /// VLAN tag to configure. + pub vid: u16, +} + +impl NetworkVlanOptions { + /// Verifies whether the VLAN configuration is valid. + pub fn verify(&self) -> Result<()> { + if self.vid < 2 || self.vid > 4094 { + bail!( + "VLAN id must be between 2 and 4094 inclusive, got {}", + self.vid + ); + } + + Ok(()) + } +} + #[cfg_attr(feature = "api-types", api( properties: { filter: { @@ -622,6 +656,9 @@ pub struct NetworkConfigFromAnswer { /// If set, sets up a Linux bond for the management interface. #[serde(default, skip_serializing_if = "Option::is_none")] pub bond: Option, + /// If set, configures the given VLAN for the management interface. + #[serde(default, skip_serializing_if = "Option::is_none")] + pub vlan: Option, } #[cfg_attr(feature = "api-types", api)] @@ -636,6 +673,9 @@ pub struct NetworkConfigFromDhcp { /// If set, sets up a Linux bond for the management interface. #[serde(default, skip_serializing_if = "Option::is_none")] pub bond: Option, + /// If set, configures the given VLAN for the management interface. + #[serde(default, skip_serializing_if = "Option::is_none")] + pub vlan: Option, } #[cfg_attr(feature = "api-types", api( @@ -672,6 +712,14 @@ impl NetworkConfig { Self::FromAnswer(answer) => answer.bond.as_ref(), } } + + /// Returns the management network Linux bond options, if any. + pub const fn vlan(&self) -> Option<&NetworkVlanOptions> { + match self { + Self::FromDhcp(dhcp) => dhcp.vlan.as_ref(), + Self::FromAnswer(answer) => answer.vlan.as_ref(), + } + } } #[derive(Clone, Debug, Deserialize, Serialize, PartialEq)] @@ -1715,4 +1763,29 @@ mod tests { "primary bond interface AB:12:CD:34:EF:56 must be a member of the bond (members: 12:34:56:AB:CD:DF, AB:CD:DF:12:34:56)" ); } + + #[test] + fn network_vlan_id_must_be_valid() { + let mut vlan = NetworkVlanOptions { vid: 2 }; + assert!(vlan.verify().is_ok()); + + vlan.vid = 4094; + assert!(vlan.verify().is_ok()); + + vlan.vid = 1; + let res = vlan.verify(); + assert!(res.is_err()); + assert_eq!( + res.unwrap_err().to_string(), + "VLAN id must be between 2 and 4094 inclusive, got 1" + ); + + vlan.vid = 4095; + let res = vlan.verify(); + assert!(res.is_err()); + assert_eq!( + res.unwrap_err().to_string(), + "VLAN id must be between 2 and 4094 inclusive, got 4095" + ); + } } -- 2.55.0