From mboxrd@z Thu Jan 1 00:00:00 1970 Return-Path: <pve-devel-bounces@lists.proxmox.com> Received: from firstgate.proxmox.com (firstgate.proxmox.com [IPv6:2a01:7e0:0:424::9]) by lore.proxmox.com (Postfix) with ESMTPS id DEBB31FF164 for <inbox@lore.proxmox.com>; Fri, 28 Mar 2025 18:15:03 +0100 (CET) Received: from firstgate.proxmox.com (localhost [127.0.0.1]) by firstgate.proxmox.com (Proxmox) with ESMTP id 4D46D856D; Fri, 28 Mar 2025 18:14:01 +0100 (CET) From: Gabriel Goller <g.goller@proxmox.com> To: pve-devel@lists.proxmox.com Date: Fri, 28 Mar 2025 18:13:04 +0100 Message-Id: <20250328171340.885413-17-g.goller@proxmox.com> X-Mailer: git-send-email 2.39.5 In-Reply-To: <20250328171340.885413-1-g.goller@proxmox.com> References: <20250328171340.885413-1-g.goller@proxmox.com> MIME-Version: 1.0 X-SPAM-LEVEL: Spam detection results: 0 AWL -0.025 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 SPF_HELO_NONE 0.001 SPF: HELO does not publish an SPF Record SPF_PASS -0.001 SPF: sender matches SPF record Subject: [pve-devel] [PATCH proxmox-ve-rs 15/17] ve-config: add validation for section-config X-BeenThere: pve-devel@lists.proxmox.com X-Mailman-Version: 2.1.29 Precedence: list List-Id: Proxmox VE development discussion <pve-devel.lists.proxmox.com> List-Unsubscribe: <https://lists.proxmox.com/cgi-bin/mailman/options/pve-devel>, <mailto:pve-devel-request@lists.proxmox.com?subject=unsubscribe> List-Archive: <http://lists.proxmox.com/pipermail/pve-devel/> List-Post: <mailto:pve-devel@lists.proxmox.com> List-Help: <mailto:pve-devel-request@lists.proxmox.com?subject=help> List-Subscribe: <https://lists.proxmox.com/cgi-bin/mailman/listinfo/pve-devel>, <mailto:pve-devel-request@lists.proxmox.com?subject=subscribe> Reply-To: Proxmox VE development discussion <pve-devel@lists.proxmox.com> Content-Type: text/plain; charset="us-ascii" Content-Transfer-Encoding: 7bit Errors-To: pve-devel-bounces@lists.proxmox.com Sender: "pve-devel" <pve-devel-bounces@lists.proxmox.com> Our section-config is nested 3 times (fabric -> node -> interfaces), but as only one indentation level (two with propertyStrings) are possible in section-config configuration files, we need to add some validation to ensure that the config is valid. In the future, more stuff to be validated can be added here, but currently we check: * if the router-id is unique * if the node refers to a existing fabric * if the router-ids are in the specified loopback prefix Our Section-Config is structured like this: fabric: test fabric-option: this node: test_pve0 node-option: that The key to the node section is called the `NodeId` and consist of two parts: `test`, which is the fabric, and `pve0`, which is the nodename. The validation checks if the `test` fabric exists. Signed-off-by: Gabriel Goller <g.goller@proxmox.com> Co-authored-by: Stefan Hanreich <s.hanreich@proxmox.com> --- proxmox-ve-config/src/sdn/fabric/mod.rs | 44 +++++++++++++++ .../src/sdn/fabric/openfabric/validation.rs | 56 +++++++++++++++++++ .../src/sdn/fabric/ospf/validation.rs | 53 ++++++++++++++++++ proxmox-ve-config/src/sdn/mod.rs | 1 + 4 files changed, 154 insertions(+) create mode 100644 proxmox-ve-config/src/sdn/fabric/mod.rs create mode 100644 proxmox-ve-config/src/sdn/fabric/openfabric/validation.rs create mode 100644 proxmox-ve-config/src/sdn/fabric/ospf/validation.rs diff --git a/proxmox-ve-config/src/sdn/fabric/mod.rs b/proxmox-ve-config/src/sdn/fabric/mod.rs new file mode 100644 index 000000000000..949486a86355 --- /dev/null +++ b/proxmox-ve-config/src/sdn/fabric/mod.rs @@ -0,0 +1,44 @@ +pub mod openfabric; +pub mod ospf; + +use openfabric::OpenFabricSectionConfig; +use ospf::OspfSectionConfig; +use proxmox_section_config::typed::ApiSectionDataEntry; +use proxmox_section_config::typed::SectionConfigData; + +use std::ops::Deref; + +use serde::de::DeserializeOwned; + +#[derive(Debug, Clone)] +pub struct Valid<T>(SectionConfigData<T>); + +impl<T> Deref for Valid<T> { + type Target = SectionConfigData<T>; + + fn deref(&self) -> &Self::Target { + &self.0 + } +} + +pub trait Validate<T> { + fn validate(data: SectionConfigData<T>) -> Result<Valid<T>, anyhow::Error>; + fn validate_as_ref(data: &SectionConfigData<T>) -> Result<(), anyhow::Error>; +} + +impl<T> Valid<T> { + pub fn into_inner(self) -> SectionConfigData<T> { + self.0 + } +} + +impl<T> Valid<T> +where + T: ApiSectionDataEntry + DeserializeOwned + Validate<T>, +{ + pub fn parse_section_config(filename: &str, data: &str) -> Result<Valid<T>, anyhow::Error> { + let config = T::parse_section_config(filename, data)?; + T::validate(config) + } +} + diff --git a/proxmox-ve-config/src/sdn/fabric/openfabric/validation.rs b/proxmox-ve-config/src/sdn/fabric/openfabric/validation.rs new file mode 100644 index 000000000000..dfb9ee94596a --- /dev/null +++ b/proxmox-ve-config/src/sdn/fabric/openfabric/validation.rs @@ -0,0 +1,56 @@ +use anyhow::{anyhow, bail}; +use std::collections::{HashMap, HashSet}; + +use proxmox_section_config::typed::SectionConfigData; + +use crate::sdn::fabric::{Valid, Validate}; + +use super::OpenFabricSectionConfig; + +impl Validate<OpenFabricSectionConfig> for OpenFabricSectionConfig { + /// This function will validate the SectionConfigData<T> and return a Valid<SectionConfigData<T>> + /// The validation checks if the every node is part of an existing fabric. This is necessary as + /// with the current SectionConfigData format, we don't get this guarantee. + fn validate( + data: SectionConfigData<OpenFabricSectionConfig>, + ) -> Result<Valid<OpenFabricSectionConfig>, anyhow::Error> { + Self::validate_as_ref(&data)?; + Ok(Valid(data)) + } + + fn validate_as_ref( + data: &SectionConfigData<OpenFabricSectionConfig>, + ) -> Result<(), anyhow::Error> { + let mut fabrics = HashMap::new(); + let mut nodes = Vec::new(); + + for (_, section) in data { + match section { + OpenFabricSectionConfig::Node(node) => { + nodes.push(node); + } + OpenFabricSectionConfig::Fabric(fabric) => { + fabrics.insert(&fabric.fabric_id, fabric); + } + } + } + + let mut router_ids = HashSet::new(); + + for node in nodes { + let fabric = fabrics + .get(&node.node_id.fabric_id) + .ok_or_else(|| anyhow!("verification error - missing fabric configuration"))?; + + if !router_ids.insert(node.router_id) { + bail!("verification error - duplicate router_id"); + } + + if !fabric.loopback_prefix.contains_address(&node.router_id) { + bail!("Loopback IP of node is not contained in Loopback IP prefix"); + } + } + + Ok(()) + } +} diff --git a/proxmox-ve-config/src/sdn/fabric/ospf/validation.rs b/proxmox-ve-config/src/sdn/fabric/ospf/validation.rs new file mode 100644 index 000000000000..e931ba279afa --- /dev/null +++ b/proxmox-ve-config/src/sdn/fabric/ospf/validation.rs @@ -0,0 +1,53 @@ +use anyhow::{anyhow, bail}; +use std::collections::{HashMap, HashSet}; + +use proxmox_section_config::typed::SectionConfigData; + +use crate::sdn::fabric::{Valid, Validate}; + +use super::OspfSectionConfig; + +impl Validate<OspfSectionConfig> for OspfSectionConfig { + /// This function will validate the SectionConfigData<T> and return a Valid<SectionConfigData<T>> + /// The validation checks if the every node is part of an existing fabric. This is necessary as + /// with the current SectionConfigData format, we don't get this guarantee. + fn validate( + data: SectionConfigData<OspfSectionConfig>, + ) -> Result<Valid<OspfSectionConfig>, anyhow::Error> { + Self::validate_as_ref(&data)?; + Ok(Valid(data)) + } + + fn validate_as_ref(data: &SectionConfigData<OspfSectionConfig>) -> Result<(), anyhow::Error> { + let mut fabrics = HashMap::new(); + let mut nodes = Vec::new(); + + for (_, section) in data { + match section { + OspfSectionConfig::Node(node) => { + nodes.push(node); + }, + OspfSectionConfig::Fabric(fabric) => { + fabrics.insert(&fabric.area, fabric); + } + } + } + + let mut router_ids = HashSet::new(); + + for node in nodes { + let fabric = fabrics.get(&node.node_id.area) + .ok_or_else(|| anyhow!("verification error - missing fabric configuration"))?; + + if !router_ids.insert(node.router_id) { + bail!("verification error - duplicate router_id"); + } + + if !fabric.loopback_prefix.contains_address(&node.router_id) { + bail!("Loopback IP of node is not contained in Loopback IP prefix"); + } + } + + Ok(()) + } +} diff --git a/proxmox-ve-config/src/sdn/mod.rs b/proxmox-ve-config/src/sdn/mod.rs index 25ed7e476b9f..515ce354f366 100644 --- a/proxmox-ve-config/src/sdn/mod.rs +++ b/proxmox-ve-config/src/sdn/mod.rs @@ -1,5 +1,6 @@ pub mod config; pub mod ipam; +pub mod fabric; use std::{error::Error, fmt::Display, str::FromStr}; -- 2.39.5 _______________________________________________ pve-devel mailing list pve-devel@lists.proxmox.com https://lists.proxmox.com/cgi-bin/mailman/listinfo/pve-devel