From mboxrd@z Thu Jan 1 00:00:00 1970 Return-Path: Received: from firstgate.proxmox.com (firstgate.proxmox.com [212.224.123.68]) by lore.proxmox.com (Postfix) with ESMTPS id 8202A1FF165 for ; Thu, 22 May 2025 18:21:51 +0200 (CEST) Received: from firstgate.proxmox.com (localhost [127.0.0.1]) by firstgate.proxmox.com (Proxmox) with ESMTP id 926DDAD62; Thu, 22 May 2025 18:18:23 +0200 (CEST) From: Stefan Hanreich To: pve-devel@lists.proxmox.com Date: Thu, 22 May 2025 18:16:34 +0200 Message-Id: <20250522161731.537011-19-s.hanreich@proxmox.com> X-Mailer: git-send-email 2.39.5 In-Reply-To: <20250522161731.537011-1-s.hanreich@proxmox.com> References: <20250522161731.537011-1-s.hanreich@proxmox.com> MIME-Version: 1.0 X-SPAM-LEVEL: Spam detection results: 0 AWL -0.228 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_LAZY_DOMAIN_SECURITY 1 Sending domain does not have any anti-forgery methods RDNS_NONE 0.793 Delivered to internal network by a host with no rDNS SPF_HELO_NONE 0.001 SPF: HELO does not publish an SPF Record SPF_NONE 0.001 SPF: sender does not publish an SPF Record Subject: [pve-devel] [PATCH proxmox-ve-rs v3 13/21] config: sdn: fabrics: add openfabric properties X-BeenThere: pve-devel@lists.proxmox.com X-Mailman-Version: 2.1.29 Precedence: list List-Id: Proxmox VE development discussion List-Unsubscribe: , List-Archive: List-Post: List-Help: List-Subscribe: , Reply-To: Proxmox VE development discussion Content-Type: text/plain; charset="us-ascii" Content-Transfer-Encoding: 7bit Errors-To: pve-devel-bounces@lists.proxmox.com Sender: "pve-devel" This commit adds the protocol-specific properties, that are required for an Openfabric fabric. They correspond to the respective properties in the FRR fabricd configuration. For more information, see the FRR documentation [1]. While the hello interval and csnp interval could be set on a per-interface basis, it is recommended to keep them the same across the whole fabric. This is why they can only be configured globally and the value will be applied to all interfaces that are part of the fabric. We expose the hello multiplier in the interface properties, so users can define a longer hello interval on a per-interface basis by supplying the hello multiplier parameter. The upside of this is, that everything scales from a single value and users can just edit the hello interval in the fabric and everything else will adjust based on that setting. We also introduce two new general enums: Fabric and Node. They contain the concrete FabricSection and NodeSection types that are used for each protocol and add the Openfabric sections to it. We provide dedicated updater structs for the enum as well, that fall back to the concrete updater structs of the respective variant. New protocols can simply be added by adding another variant to the Fabric and Node enums. [1] https://docs.frrouting.org/en/latest/fabricd.html Co-authored-by: Gabriel Goller Signed-off-by: Stefan Hanreich --- proxmox-ve-config/Cargo.toml | 2 + proxmox-ve-config/debian/control | 4 + .../src/sdn/fabric/section_config/fabric.rs | 85 +++++++++++++- .../src/sdn/fabric/section_config/mod.rs | 1 + .../src/sdn/fabric/section_config/node.rs | 46 ++++++++ .../sdn/fabric/section_config/protocol/mod.rs | 1 + .../section_config/protocol/openfabric.rs | 105 ++++++++++++++++++ 7 files changed, 243 insertions(+), 1 deletion(-) create mode 100644 proxmox-ve-config/src/sdn/fabric/section_config/protocol/mod.rs create mode 100644 proxmox-ve-config/src/sdn/fabric/section_config/protocol/openfabric.rs diff --git a/proxmox-ve-config/Cargo.toml b/proxmox-ve-config/Cargo.toml index 7674dc7..c5aeba9 100644 --- a/proxmox-ve-config/Cargo.toml +++ b/proxmox-ve-config/Cargo.toml @@ -22,5 +22,7 @@ proxmox-serde = { version = "0.1.2", features = [ "perl" ]} proxmox-network-types = { workspace = true, features = [ "api-types" ] } proxmox-schema = { workspace = true, features = [ "api-types" ] } +proxmox-sdn-types = { workspace = true } +proxmox-section-config = { version = "3" } proxmox-sys = "0.6.4" proxmox-sortable-macro = "0.1.3" diff --git a/proxmox-ve-config/debian/control b/proxmox-ve-config/debian/control index f416316..57d7987 100644 --- a/proxmox-ve-config/debian/control +++ b/proxmox-ve-config/debian/control @@ -14,6 +14,8 @@ Build-Depends-Arch: cargo:native , librust-proxmox-network-types-0.1+default-dev , librust-proxmox-schema-4+api-types-dev , librust-proxmox-schema-4+default-dev , + librust-proxmox-sdn-types-0.1+default-dev , + librust-proxmox-section-config-3+default-dev , librust-proxmox-serde-0.1+default-dev (>= 0.1.2-~~) , librust-proxmox-serde-0.1+perl-dev (>= 0.1.2-~~) , librust-proxmox-sortable-macro-0.1+default-dev (>= 0.1.3-~~) , @@ -45,6 +47,8 @@ Depends: librust-proxmox-network-types-0.1+default-dev, librust-proxmox-schema-4+api-types-dev, librust-proxmox-schema-4+default-dev, + librust-proxmox-sdn-types-0.1+default-dev, + librust-proxmox-section-config-3+default-dev, librust-proxmox-serde-0.1+default-dev (>= 0.1.2-~~), librust-proxmox-serde-0.1+perl-dev (>= 0.1.2-~~), librust-proxmox-sortable-macro-0.1+default-dev (>= 0.1.3-~~), diff --git a/proxmox-ve-config/src/sdn/fabric/section_config/fabric.rs b/proxmox-ve-config/src/sdn/fabric/section_config/fabric.rs index 9787d5d..3e56e2b 100644 --- a/proxmox-ve-config/src/sdn/fabric/section_config/fabric.rs +++ b/proxmox-ve-config/src/sdn/fabric/section_config/fabric.rs @@ -4,7 +4,11 @@ use serde::{Deserialize, Serialize}; use proxmox_network_types::ip_address::{Ipv4Cidr, Ipv6Cidr}; use proxmox_schema::{ api, api_string_type, const_regex, AllOfSchema, ApiStringFormat, ApiType, ObjectSchema, Schema, - Updater, + Updater, UpdaterType, +}; + +use crate::sdn::fabric::section_config::protocol::openfabric::{ + OpenfabricDeletableProperties, OpenfabricProperties, OpenfabricPropertiesUpdater, }; pub const FABRIC_ID_REGEX_STR: &str = r"(?:[a-zA-Z0-9])(?:[a-zA-Z0-9\-]){0,6}(?:[a-zA-Z0-9])?"; @@ -122,6 +126,85 @@ impl Updater for FabricSectionUpdater { } } +impl UpdaterType for FabricSection { + type Updater = FabricSectionUpdater; +} + +/// Enum containing all types of fabrics. +/// +/// It utilizes [`FabricSection`] to define all possible types of fabrics. For parsing the +/// configuration, please use the [`Section`] enum, which contains the Node sections as well. This +/// struct is used for sorting the sections into their sub-types after parsing the configuration +/// via [`Section`]. +#[api( + "id-property": "id", + "id-schema": { + type: String, + description: "Fabric ID", + format: &FABRIC_ID_FORMAT, + }, + "type-key": "protocol", +)] +#[derive(Debug, Clone, Serialize, Deserialize, Hash)] +#[serde(rename_all = "snake_case", tag = "protocol")] +pub enum Fabric { + Openfabric(FabricSection), +} + +impl UpdaterType for Fabric { + type Updater = FabricUpdater; +} + +impl Fabric { + /// Get the id of the [Fabric]. + /// + /// This is a common property for all protocols. + pub fn id(&self) -> &FabricId { + match self { + Self::Openfabric(fabric_section) => fabric_section.id(), + } + } + + /// Get the ip-prefix (IPv4 CIDR) of the [Fabric]. + /// + /// This is a common property for all protocols. + pub fn ip_prefix(&self) -> Option { + match self { + Fabric::Openfabric(fabric_section) => fabric_section.ip_prefix(), + } + } + + /// Get the ip6-prefix (IPv6 CIDR) of the [Fabric]. + /// + /// This is a common property for all protocols. + pub fn ip6_prefix(&self) -> Option { + match self { + Fabric::Openfabric(fabric_section) => fabric_section.ip6_prefix(), + } + } +} + +impl From> for Fabric { + fn from(section: FabricSection) -> Self { + Fabric::Openfabric(section) + } +} + +/// Enum containing all updater types for fabrics +#[derive(Debug, Clone, Serialize, Deserialize)] +#[serde(rename_all = "snake_case", tag = "protocol")] +pub enum FabricUpdater { + Openfabric( as UpdaterType>::Updater), +} + +impl Updater for FabricUpdater { + fn is_empty(&self) -> bool { + match self { + FabricUpdater::Openfabric(updater) => updater.is_empty(), + } + } +} + /// Deletable properties for a [`FabricSection`] #[derive(Debug, Clone, Serialize, Deserialize)] #[serde(rename_all = "snake_case", untagged)] diff --git a/proxmox-ve-config/src/sdn/fabric/section_config/mod.rs b/proxmox-ve-config/src/sdn/fabric/section_config/mod.rs index b61bc43..7db3788 100644 --- a/proxmox-ve-config/src/sdn/fabric/section_config/mod.rs +++ b/proxmox-ve-config/src/sdn/fabric/section_config/mod.rs @@ -1,3 +1,4 @@ pub mod fabric; pub mod interface; pub mod node; +pub mod protocol; diff --git a/proxmox-ve-config/src/sdn/fabric/section_config/node.rs b/proxmox-ve-config/src/sdn/fabric/section_config/node.rs index b1202a2..510bfde 100644 --- a/proxmox-ve-config/src/sdn/fabric/section_config/node.rs +++ b/proxmox-ve-config/src/sdn/fabric/section_config/node.rs @@ -12,6 +12,7 @@ use proxmox_schema::{ use crate::sdn::fabric::section_config::{ fabric::{FabricId, FABRIC_ID_REGEX_STR}, + protocol::openfabric::OpenfabricNodeProperties, }; pub const NODE_ID_REGEX_STR: &str = r"(?:[a-zA-Z0-9](?:[a-zA-Z0-9\-]){0,61}(?:[a-zA-Z0-9]){0,1})"; @@ -167,3 +168,48 @@ impl ApiType for NodeSection { ) .schema(); } + +/// Enum containing all types of nodes. +#[api( + "id-property": "id", + "id-schema": { + type: String, + description: "Node ID", + format: &NODE_ID_FORMAT, + }, + "type-key": "protocol", +)] +#[derive(Debug, Clone, Serialize, Deserialize, Hash)] +#[serde(rename_all = "snake_case", tag = "protocol")] +pub enum Node { + Openfabric(NodeSection), +} + +impl Node { + /// Get the id of the [Node]. + pub fn id(&self) -> &NodeSectionId { + match self { + Node::Openfabric(node_section) => node_section.id(), + } + } + + /// Get the ip (IPv4) of the [Node]. + pub fn ip(&self) -> Option { + match self { + Node::Openfabric(node_section) => node_section.ip(), + } + } + + /// Get the ip (IPv6) of the [Node]. + pub fn ip6(&self) -> Option { + match self { + Node::Openfabric(node_section) => node_section.ip6(), + } + } +} + +impl From> for Node { + fn from(value: NodeSection) -> Self { + Self::Openfabric(value) + } +} diff --git a/proxmox-ve-config/src/sdn/fabric/section_config/protocol/mod.rs b/proxmox-ve-config/src/sdn/fabric/section_config/protocol/mod.rs new file mode 100644 index 0000000..e5b800b --- /dev/null +++ b/proxmox-ve-config/src/sdn/fabric/section_config/protocol/mod.rs @@ -0,0 +1 @@ +pub mod openfabric; diff --git a/proxmox-ve-config/src/sdn/fabric/section_config/protocol/openfabric.rs b/proxmox-ve-config/src/sdn/fabric/section_config/protocol/openfabric.rs new file mode 100644 index 0000000..156ff2b --- /dev/null +++ b/proxmox-ve-config/src/sdn/fabric/section_config/protocol/openfabric.rs @@ -0,0 +1,105 @@ +use std::ops::Deref; + +use proxmox_network_types::ip_address::{Ipv4Cidr, Ipv6Cidr}; +use serde::{Deserialize, Serialize}; + +use proxmox_schema::{api, property_string::PropertyString, ApiStringFormat, Updater}; +use proxmox_sdn_types::openfabric::{CsnpInterval, HelloInterval, HelloMultiplier}; + +use crate::sdn::fabric::section_config::interface::InterfaceName; + +/// Protocol-specific options for an OpenFabric Fabric. +#[api] +#[derive(Debug, Clone, Serialize, Deserialize, Updater, Hash)] +pub struct OpenfabricProperties { + /// This will be distributed to all interfaces on every node. The Hello Interval for a given + /// interface in seconds. The range is 1 to 600. Hello packets are used to establish and + /// maintain adjacency between OpenFabric neighbors. + #[serde(skip_serializing_if = "Option::is_none")] + pub(crate) hello_interval: Option, + + /// This will be distributed to all interfaces on every node.The Complete Sequence Number + /// Packets (CSNP) interval in seconds. The interval range is 1 to 600. + #[serde(skip_serializing_if = "Option::is_none")] + pub(crate) csnp_interval: Option, +} + +#[derive(Debug, Clone, Serialize, Deserialize, Hash)] +#[serde(rename_all = "snake_case")] +pub enum OpenfabricDeletableProperties { + HelloInterval, + CsnpInterval, +} + +/// Properties for an OpenFabric node +#[api( + properties: { + interfaces: { + type: Array, + optional: true, + items: { + type: String, + description: "OpenFabric interface", + format: &ApiStringFormat::PropertyString(&OpenfabricInterfaceProperties::API_SCHEMA), + } + }, + } +)] +#[derive(Debug, Clone, Serialize, Deserialize, Updater, Hash)] +pub struct OpenfabricNodeProperties { + /// Interfaces for this node + #[serde(default)] + pub(crate) interfaces: Vec>, +} + +impl OpenfabricNodeProperties { + /// Returns an interator over all the interfaces. + pub fn interfaces(&self) -> impl Iterator { + self.interfaces + .iter() + .map(|property_string| property_string.deref()) + } +} + +#[derive(Debug, Clone, Serialize, Deserialize)] +#[serde(rename_all = "snake_case")] +pub enum OpenfabricNodeDeletableProperties { + Interfaces, +} + +/// Properties for an OpenFabric interface +#[api] +#[derive(Debug, Clone, Serialize, Deserialize, Updater, Hash)] +pub struct OpenfabricInterfaceProperties { + pub(crate) name: InterfaceName, + + /// The multiplier for the hello holding time on a given interface. The range is 2 to + /// 100. + #[serde(skip_serializing_if = "Option::is_none")] + pub(crate) hello_multiplier: Option, + + /// If ip and ip6 are unset, then this is an point-to-point interface + #[serde(skip_serializing_if = "Option::is_none")] + pub(crate) ip: Option, + + /// If ip6 and ip are unset, then this is an point-to-point interface + #[serde(skip_serializing_if = "Option::is_none")] + pub(crate) ip6: Option, +} + +impl OpenfabricInterfaceProperties { + /// Get the name of the interface. + pub fn name(&self) -> &InterfaceName { + &self.name + } + + /// Get the ip (IPv4) of the interface. + pub fn ip(&self) -> Option { + self.ip + } + + /// Get the ip6 (IPv6) of the interface. + pub fn ip6(&self) -> Option { + self.ip6 + } +} -- 2.39.5 _______________________________________________ pve-devel mailing list pve-devel@lists.proxmox.com https://lists.proxmox.com/cgi-bin/mailman/listinfo/pve-devel