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 2CE7A1FF15C for <inbox@lore.proxmox.com>; Fri, 4 Apr 2025 18:40:29 +0200 (CEST) Received: from firstgate.proxmox.com (localhost [127.0.0.1]) by firstgate.proxmox.com (Proxmox) with ESMTP id 23E8D1930; Fri, 4 Apr 2025 18:39:27 +0200 (CEST) From: Gabriel Goller <g.goller@proxmox.com> To: pve-devel@lists.proxmox.com Date: Fri, 4 Apr 2025 18:28:29 +0200 Message-Id: <20250404162908.563060-19-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.022 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-perl-rs v2 2/7] perl-rs: sdn: add CRUD helpers for OpenFabric fabric management 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> Add functionality for managing OpenFabric fabrics: - Implement Rust-backed Perl module PVE::RS::SDN::Fabrics::OpenFabric - Add CRUD methods for fabric, node, and interface configuration - Support fabric-specific parameters (hello-intervals, router-id, etc.) Signed-off-by: Gabriel Goller <g.goller@proxmox.com> --- pve-rs/Cargo.toml | 1 + pve-rs/Makefile | 1 + pve-rs/debian/control | 1 + pve-rs/src/sdn/mod.rs | 1 + pve-rs/src/sdn/openfabric.rs | 222 +++++++++++++++++++++++++++++++++++ 5 files changed, 226 insertions(+) create mode 100644 pve-rs/src/sdn/openfabric.rs diff --git a/pve-rs/Cargo.toml b/pve-rs/Cargo.toml index bdfff6adf685..5abb960d2668 100644 --- a/pve-rs/Cargo.toml +++ b/pve-rs/Cargo.toml @@ -42,6 +42,7 @@ proxmox-notify = { version = "0.5", features = ["pve-context"] } proxmox-openid = "0.10" proxmox-resource-scheduling = "0.3.0" proxmox-schema = "4.0.0" +proxmox-sdn-types = { version = "0.1" } proxmox-section-config = "2.1.1" proxmox-shared-cache = "0.1.0" proxmox-subscription = "0.5" diff --git a/pve-rs/Makefile b/pve-rs/Makefile index 86af16eb5e04..6bd9c8a2acec 100644 --- a/pve-rs/Makefile +++ b/pve-rs/Makefile @@ -32,6 +32,7 @@ PERLMOD_PACKAGES := \ PVE::RS::OpenId \ PVE::RS::ResourceScheduling::Static \ PVE::RS::SDN::Fabrics \ + PVE::RS::SDN::Fabrics::OpenFabric \ PVE::RS::TFA PERLMOD_PACKAGE_FILES := $(addsuffix .pm,$(subst ::,/,$(PERLMOD_PACKAGES))) diff --git a/pve-rs/debian/control b/pve-rs/debian/control index 5e3b8f662ad0..61631c4932ee 100644 --- a/pve-rs/debian/control +++ b/pve-rs/debian/control @@ -30,6 +30,7 @@ Build-Depends: cargo:native <!nocheck>, librust-proxmox-openid-0.10+default-dev, librust-proxmox-resource-scheduling-0.3+default-dev, 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-shared-cache-0.1+default-dev, librust-proxmox-subscription-0.5+default-dev, diff --git a/pve-rs/src/sdn/mod.rs b/pve-rs/src/sdn/mod.rs index 3e3b1376f8d6..36afb099ece0 100644 --- a/pve-rs/src/sdn/mod.rs +++ b/pve-rs/src/sdn/mod.rs @@ -1 +1,2 @@ pub mod fabrics; +pub mod openfabric; diff --git a/pve-rs/src/sdn/openfabric.rs b/pve-rs/src/sdn/openfabric.rs new file mode 100644 index 000000000000..569775857755 --- /dev/null +++ b/pve-rs/src/sdn/openfabric.rs @@ -0,0 +1,222 @@ +#[perlmod::package(name = "PVE::RS::SDN::Fabrics::OpenFabric", lib = "pve_rs")] +mod export { + use std::{net::IpAddr, str}; + + use anyhow::Context; + + use proxmox_network_types::{debian::Hostname, ip_address::Cidr}; + use proxmox_schema::property_string::PropertyString; + use proxmox_sdn_types::openfabric::{CsnpInterval, HelloInterval, HelloMultiplier}; + use proxmox_section_config::typed::{ApiSectionDataEntry, SectionConfigData}; + use proxmox_ve_config::sdn::fabric::{ + FabricId, NodeId, SectionType, Validate, + openfabric::{FabricSection, InterfaceProperties, NodeSection, OpenFabricSectionConfig}, + }; + use serde::{Deserialize, Serialize}; + + use crate::sdn::fabrics::export::PerlSectionConfig; + + perlmod::declare_magic!(Box<PerlSectionConfig<OpenFabricSectionConfig>> : &PerlSectionConfig<OpenFabricSectionConfig> as "PVE::RS::SDN::Fabrics::OpenFabric"); + + #[derive(Debug, Serialize, Deserialize)] + pub struct AddFabric { + fabric_id: FabricId, + hello_interval: Option<HelloInterval>, + loopback_prefix: Cidr, + } + + #[derive(Debug, Serialize, Deserialize)] + pub struct DeleteFabric { + fabric_id: FabricId, + } + + #[derive(Debug, Serialize, Deserialize)] + pub struct DeleteNode { + fabric_id: FabricId, + node_id: Hostname, + } + + #[derive(Debug, Serialize, Deserialize)] + pub struct DeleteInterface { + fabric_id: FabricId, + node_id: Hostname, + /// interface name + name: String, + } + + #[derive(Debug, Serialize, Deserialize)] + pub struct EditFabric { + fabric_id: FabricId, + hello_interval: Option<HelloInterval>, + } + + #[derive(Debug, Deserialize)] + pub struct AddNode { + fabric_id: FabricId, + node_id: Hostname, + router_id: IpAddr, + interfaces: Option<Vec<PropertyString<InterfaceProperties>>>, + } + + #[derive(Debug, Serialize, Deserialize)] + pub struct EditNode { + node_id: Hostname, + fabric_id: FabricId, + router_id: IpAddr, + interfaces: Option<Vec<PropertyString<InterfaceProperties>>>, + } + + #[derive(Debug, Serialize, Deserialize)] + pub struct EditInterface { + node_id: Hostname, + fabric_id: FabricId, + name: String, + passive: bool, + hello_interval: Option<HelloInterval>, + hello_multiplier: Option<HelloMultiplier>, + csnp_interval: Option<CsnpInterval>, + } + + fn interface_exists( + config: &SectionConfigData<OpenFabricSectionConfig>, + interface_name: &str, + node_name: &str, + ) -> bool { + config.sections.iter().any(|(k, v)| { + if let OpenFabricSectionConfig::Node(n) = v { + k.parse::<NodeId>().ok().is_some_and(|id| { + id.node_id.as_ref() == node_name + && n.interfaces.iter().any(|i| i.name == interface_name) + }) + } else { + false + } + }) + } + + impl PerlSectionConfig<OpenFabricSectionConfig> { + pub fn add_fabric(&self, params: AddFabric) -> Result<(), anyhow::Error> { + let mut config = self.section_config.lock().unwrap(); + if config.sections.contains_key(params.fabric_id.as_ref()) { + anyhow::bail!("fabric already exists"); + } + let new_fabric = OpenFabricSectionConfig::Fabric(FabricSection { + fabric_id: params.fabric_id.clone(), + hello_interval: params.hello_interval, + ty: SectionType::Fabric, + loopback_prefix: params.loopback_prefix, + }); + config + .sections + .insert(params.fabric_id.to_string(), new_fabric); + + config.order.push(params.fabric_id.to_string()); + OpenFabricSectionConfig::validate_as_ref(&config)?; + Ok(()) + } + + pub fn add_node(&self, params: AddNode) -> Result<(), anyhow::Error> { + let id = NodeId::new(params.fabric_id.clone(), params.node_id.clone()); + let id_string = id.to_string(); + + let mut config = self.section_config.lock().unwrap(); + + if config.sections.contains_key(&id_string) { + anyhow::bail!("node already exists"); + } + + if let Some(interfaces) = ¶ms.interfaces { + if interfaces + .iter() + .any(|i| interface_exists(&config, &i.name, id_string.as_ref())) + { + anyhow::bail!("One interface cannot be a part of two fabrics"); + } + } + let new_fabric = OpenFabricSectionConfig::Node(NodeSection { + id, + node_id: params.node_id, + fabric_id: params.fabric_id, + router_id: params.router_id, + interfaces: params.interfaces.unwrap_or_default(), + ty: SectionType::Node, + }); + config.sections.insert(id_string.clone(), new_fabric); + config.order.push(id_string); + OpenFabricSectionConfig::validate_as_ref(&config)?; + + Ok(()) + } + + pub fn edit_fabric(&self, params: EditFabric) -> Result<(), anyhow::Error> { + let mut config = self.section_config.lock().unwrap(); + + let fabricid = params.fabric_id; + + if let OpenFabricSectionConfig::Fabric(fs) = config + .sections + .get_mut(fabricid.as_ref()) + .context("fabric doesn't exist")? + { + fs.hello_interval = params.hello_interval; + } + OpenFabricSectionConfig::validate_as_ref(&config)?; + Ok(()) + } + + pub fn edit_node(&self, params: EditNode) -> Result<(), anyhow::Error> { + let router_id = params.router_id; + + let nodeid = NodeId::new(params.fabric_id, params.node_id).to_string(); + + let mut config = self.section_config.lock().unwrap(); + if let Some(node) = config.sections.get_mut(&nodeid) { + if let OpenFabricSectionConfig::Node(n) = node { + n.router_id = router_id; + n.interfaces = params.interfaces.unwrap_or_default(); + } + } else { + anyhow::bail!("node not found"); + } + + OpenFabricSectionConfig::validate_as_ref(&config)?; + Ok(()) + } + + pub fn delete_fabric(&self, params: DeleteFabric) -> Result<(), anyhow::Error> { + let mut config = self.section_config.lock().unwrap(); + + let fabricid = params.fabric_id; + + config + .sections + .remove(fabricid.as_ref()) + .ok_or(anyhow::anyhow!("fabric not found"))?; + // remove all the nodes + config.sections.retain(|k, _v| { + if let Ok(nodeid) = k.parse::<NodeId>() { + return nodeid.fabric_id != fabricid; + } + true + }); + OpenFabricSectionConfig::validate_as_ref(&config)?; + Ok(()) + } + + pub fn delete_node(&self, params: DeleteNode) -> Result<(), anyhow::Error> { + let mut config = self.section_config.lock().unwrap(); + let nodeid = NodeId::new(params.fabric_id, params.node_id).to_string(); + config + .sections + .remove(&nodeid) + .ok_or(anyhow::anyhow!("node not found"))?; + OpenFabricSectionConfig::validate_as_ref(&config)?; + Ok(()) + } + + pub fn write(&self) -> Result<String, anyhow::Error> { + let guard = self.section_config.lock().unwrap().clone(); + OpenFabricSectionConfig::write_section_config("sdn/fabrics/openfabric.cfg", &guard) + } + } +} -- 2.39.5 _______________________________________________ pve-devel mailing list pve-devel@lists.proxmox.com https://lists.proxmox.com/cgi-bin/mailman/listinfo/pve-devel