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 [212.224.123.68]) by lore.proxmox.com (Postfix) with ESMTPS id C744A1FF15C for <inbox@lore.proxmox.com>; Fri, 4 Apr 2025 18:33:11 +0200 (CEST) Received: from firstgate.proxmox.com (localhost [127.0.0.1]) by firstgate.proxmox.com (Proxmox) with ESMTP id C1C822EF; Fri, 4 Apr 2025 18:29:59 +0200 (CEST) From: Gabriel Goller <g.goller@proxmox.com> To: pve-devel@lists.proxmox.com Date: Fri, 4 Apr 2025 18:28:22 +0200 Message-Id: <20250404162908.563060-12-g.goller@proxmox.com> X-Mailer: git-send-email 2.39.5 In-Reply-To: <20250404162908.563060-1-g.goller@proxmox.com> References: <20250404162908.563060-1-g.goller@proxmox.com> MIME-Version: 1.0 X-SPAM-LEVEL: Spam detection results: 0 AWL -0.023 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 v2 10/15] ve-config: add openfabric 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> This is the main openfabric configuration. It is used to parse from the section-config file (`/etc/pve/sdn/fabrics/openfabric.cfg`) and is also returned from the api. Signed-off-by: Gabriel Goller <g.goller@proxmox.com> --- Cargo.toml | 4 +- proxmox-ve-config/Cargo.toml | 5 +- proxmox-ve-config/debian/control | 8 + .../src/sdn/fabric/openfabric/mod.rs | 228 ++++++++++++++++++ 4 files changed, 243 insertions(+), 2 deletions(-) create mode 100644 proxmox-ve-config/src/sdn/fabric/openfabric/mod.rs diff --git a/Cargo.toml b/Cargo.toml index 0cc84ecda48e..ddd32e6c9f97 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -23,4 +23,6 @@ serde = { version = "1" } serde_with = "3" thiserror = "1.0.59" -proxmox-sdn-types = { version = "0.1", path = "proxmox-sdn-types" } +proxmox-frr = { version = "0.1.0", path = "proxmox-frr" } +proxmox-sdn-types = { version = "0.1.0", path = "proxmox-sdn-types" } +proxmox-ve-config = { version = "0.2.2", path = "proxmox-ve-config" } diff --git a/proxmox-ve-config/Cargo.toml b/proxmox-ve-config/Cargo.toml index d8735e33653b..f58c6e2d0b8c 100644 --- a/proxmox-ve-config/Cargo.toml +++ b/proxmox-ve-config/Cargo.toml @@ -18,6 +18,9 @@ serde_plain = "1" serde_with = { workspace = true } proxmox-network-types = { workspace = true } -proxmox-schema = "4" +proxmox-sdn-types = { workspace = true } +proxmox-schema = { version = "4", features = [ "api-types" ] } +proxmox-section-config = { version = "2.1.2" } +proxmox-serde = { version = "0.1.2" } 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 d4985202caef..0da241fed775 100644 --- a/proxmox-ve-config/debian/control +++ b/proxmox-ve-config/debian/control @@ -10,7 +10,11 @@ Build-Depends-Arch: cargo:native <!nocheck>, librust-log-0.4+default-dev <!nocheck>, librust-nix-0.26+default-dev <!nocheck>, librust-proxmox-network-types-0.1+default-dev <!nocheck>, + librust-proxmox-schema-4+api-types-dev <!nocheck>, librust-proxmox-schema-4+default-dev <!nocheck>, + librust-proxmox-sdn-types-0.1+default-dev <!nocheck>, + librust-proxmox-section-config-2+default-dev (>= 2.1.2-~~) <!nocheck>, + librust-proxmox-serde-0.1+default-dev (>= 0.1.2-~~) <!nocheck>, librust-proxmox-sortable-macro-0.1+default-dev (>= 0.1.3-~~) <!nocheck>, librust-proxmox-sys-0.6+default-dev (>= 0.6.4-~~) <!nocheck>, librust-serde-1+default-dev <!nocheck>, @@ -35,7 +39,11 @@ Depends: librust-log-0.4+default-dev, librust-nix-0.26+default-dev, 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-2+default-dev (>= 2.1.2-~~), + librust-proxmox-serde-0.1+default-dev (>= 0.1.2-~~), librust-proxmox-sortable-macro-0.1+default-dev (>= 0.1.3-~~), librust-proxmox-sys-0.6+default-dev (>= 0.6.4-~~), librust-serde-1+default-dev, diff --git a/proxmox-ve-config/src/sdn/fabric/openfabric/mod.rs b/proxmox-ve-config/src/sdn/fabric/openfabric/mod.rs new file mode 100644 index 000000000000..6e7f4e947a0e --- /dev/null +++ b/proxmox-ve-config/src/sdn/fabric/openfabric/mod.rs @@ -0,0 +1,228 @@ +#[cfg(feature = "frr")] +pub mod frr; + +use proxmox_network_types::{ + debian::Hostname, + ip_address::{Cidr, Ipv4Cidr, Ipv6Cidr}, +}; + +use proxmox_schema::property_string::PropertyString; +use proxmox_sdn_types::openfabric::{CsnpInterval, HelloInterval, HelloMultiplier}; +use proxmox_sortable_macro::sortable; +use std::{net::IpAddr, sync::OnceLock}; + +use proxmox_schema::{ + api_types::{CIDR_FORMAT, CIDR_V4_FORMAT, CIDR_V6_FORMAT}, + ApiStringFormat, ApiType, ArraySchema, BooleanSchema, IntegerSchema, ObjectSchema, Schema, + StringSchema, +}; +use proxmox_section_config::{typed::ApiSectionDataEntry, SectionConfig, SectionConfigPlugin}; +use proxmox_serde::string_as_bool; +use serde::{Deserialize, Serialize}; + +use crate::sdn::fabric::{FabricId, NodeId, SectionType}; + +#[sortable] +const FABRIC_SCHEMA: ObjectSchema = ObjectSchema::new( + "fabric schema", + &sorted!([ + ("fabric_id", false, &StringSchema::new("FabricId").schema()), + ( + "hello_interval", + true, + &IntegerSchema::new("OpenFabric hello_interval in seconds") + .minimum(1) + .maximum(600) + .schema(), + ), + ( + "loopback_prefix", + false, + &StringSchema::new("Loopback IP prefix") + .format(&CIDR_FORMAT) + .schema() + ), + ]), +); + +#[sortable] +const INTERFACE_SCHEMA: Schema = ObjectSchema::new( + "interface", + &sorted!([ + ( + "name", + false, + &StringSchema::new("Interface name") + .min_length(1) + .max_length(15) + .schema(), + ), + ( + "ip", + true, + &StringSchema::new("Interface IPv4 address") + .format(&CIDR_V4_FORMAT) + .schema() + ), + ( + "ipv6", + true, + &StringSchema::new("Interface IPv6 address") + .format(&CIDR_V6_FORMAT) + .schema() + ), + ( + "passive", + true, + &BooleanSchema::new("OpenFabric passive mode for this interface").schema(), + ), + ( + "hello_interval", + true, + &IntegerSchema::new("OpenFabric Hello interval in seconds") + .minimum(1) + .maximum(600) + .schema(), + ), + ( + "csnp_interval", + true, + &IntegerSchema::new("OpenFabric csnp interval in seconds") + .minimum(1) + .maximum(600) + .schema() + ), + ( + "hello_multiplier", + true, + &IntegerSchema::new("OpenFabric multiplier for Hello holding time") + .minimum(2) + .maximum(100) + .schema() + ), + ]), +) +.schema(); + +#[sortable] +const NODE_SCHEMA: ObjectSchema = ObjectSchema::new( + "node schema", + &sorted!([ + ( + "id", + false, + &StringSchema::new("NodeId containing the fabric_id and hostname").schema(), + ), + ("fabric_id", false, &StringSchema::new("FabricId").schema()), + ("node_id", false, &StringSchema::new("NodeId").schema()), + ( + "interfaces", + true, + &ArraySchema::new( + "OpenFabric name", + &StringSchema::new("OpenFabric Interface") + .format(&ApiStringFormat::PropertyString(&INTERFACE_SCHEMA)) + .schema(), + ) + .schema(), + ), + ( + "router_id", + false, + &StringSchema::new("OpenFabric router-id") + .min_length(3) + .schema(), + ), + ]), +); + +const ID_SCHEMA: Schema = StringSchema::new("id").min_length(2).schema(); + +#[derive(Serialize, Deserialize, Debug, Clone, PartialEq, Eq, PartialOrd, Ord, Hash)] +pub struct FabricSection { + pub fabric_id: FabricId, + #[serde(rename = "type")] + pub ty: SectionType, + #[serde(skip_serializing_if = "Option::is_none")] + pub hello_interval: Option<HelloInterval>, + pub loopback_prefix: Cidr, +} + +#[derive(Serialize, Deserialize, Debug, Clone, PartialEq, Eq, PartialOrd, Ord)] +pub struct NodeSection { + pub id: NodeId, + pub fabric_id: FabricId, + pub node_id: Hostname, + #[serde(rename = "type")] + pub ty: SectionType, + pub router_id: IpAddr, + #[serde(default)] + pub interfaces: Vec<PropertyString<InterfaceProperties>>, +} + +#[derive(Serialize, Deserialize, Debug, Clone, PartialEq, Eq, PartialOrd, Ord)] +pub struct InterfaceProperties { + pub name: String, + #[serde(skip_serializing_if = "Option::is_none")] + #[serde(default, with = "string_as_bool")] + pub passive: Option<bool>, + #[serde(skip_serializing_if = "Option::is_none")] + pub hello_interval: Option<HelloInterval>, + #[serde(skip_serializing_if = "Option::is_none")] + pub csnp_interval: Option<CsnpInterval>, + #[serde(skip_serializing_if = "Option::is_none")] + pub hello_multiplier: Option<HelloMultiplier>, + #[serde(skip_serializing_if = "Option::is_none")] + pub ip: Option<Ipv4Cidr>, + #[serde(skip_serializing_if = "Option::is_none")] + pub ipv6: Option<Ipv6Cidr>, +} + +impl InterfaceProperties { + pub fn passive(&self) -> Option<bool> { + self.passive + } +} + +impl ApiType for InterfaceProperties { + const API_SCHEMA: Schema = INTERFACE_SCHEMA; +} + +#[derive(Serialize, Deserialize, Debug, Clone)] +#[serde(untagged)] +pub enum OpenFabricSectionConfig { + Fabric(FabricSection), + Node(NodeSection), +} + +impl ApiSectionDataEntry for OpenFabricSectionConfig { + const INTERNALLY_TAGGED: Option<&'static str> = Some("type"); + + fn section_config() -> &'static SectionConfig { + static SC: OnceLock<SectionConfig> = OnceLock::new(); + + SC.get_or_init(|| { + let mut config = SectionConfig::new(&ID_SCHEMA); + + let fabric_plugin = SectionConfigPlugin::new( + "fabric".to_string(), + Some("fabric_id".to_string()), + &FABRIC_SCHEMA, + ); + config.register_plugin(fabric_plugin); + + let node_plugin = + SectionConfigPlugin::new("node".to_string(), Some("id".to_string()), &NODE_SCHEMA); + config.register_plugin(node_plugin); + + config + }) + } + + fn section_type(&self) -> &'static str { + match self { + Self::Node(_) => "node", + Self::Fabric(_) => "fabric", + } + } +} -- 2.39.5 _______________________________________________ pve-devel mailing list pve-devel@lists.proxmox.com https://lists.proxmox.com/cgi-bin/mailman/listinfo/pve-devel