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 3ABC21FF15C for <inbox@lore.proxmox.com>; Fri, 4 Apr 2025 18:40:01 +0200 (CEST) Received: from firstgate.proxmox.com (localhost [127.0.0.1]) by firstgate.proxmox.com (Proxmox) with ESMTP id 809C91567; Fri, 4 Apr 2025 18:39:20 +0200 (CEST) From: Gabriel Goller <g.goller@proxmox.com> To: pve-devel@lists.proxmox.com Date: Fri, 4 Apr 2025 18:28:32 +0200 Message-Id: <20250404162908.563060-22-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.072 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 PROLO_LEO1 0.1 Meta Catches all Leo drug variations so far 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 5/7] perl-rs: sdn: add CRUD helpers for OSPF 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 CRUD functions for managing OSPF fabrics. Signed-off-by: Gabriel Goller <g.goller@proxmox.com> --- pve-rs/Makefile | 1 + pve-rs/src/sdn/mod.rs | 1 + pve-rs/src/sdn/ospf.rs | 217 +++++++++++++++++++++++++++++++++++++++++ 3 files changed, 219 insertions(+) create mode 100644 pve-rs/src/sdn/ospf.rs diff --git a/pve-rs/Makefile b/pve-rs/Makefile index 6bd9c8a2acec..5bd4d3c58b36 100644 --- a/pve-rs/Makefile +++ b/pve-rs/Makefile @@ -33,6 +33,7 @@ PERLMOD_PACKAGES := \ PVE::RS::ResourceScheduling::Static \ PVE::RS::SDN::Fabrics \ PVE::RS::SDN::Fabrics::OpenFabric \ + PVE::RS::SDN::Fabrics::Ospf \ PVE::RS::TFA PERLMOD_PACKAGE_FILES := $(addsuffix .pm,$(subst ::,/,$(PERLMOD_PACKAGES))) diff --git a/pve-rs/src/sdn/mod.rs b/pve-rs/src/sdn/mod.rs index 36afb099ece0..6700c989483f 100644 --- a/pve-rs/src/sdn/mod.rs +++ b/pve-rs/src/sdn/mod.rs @@ -1,2 +1,3 @@ pub mod fabrics; pub mod openfabric; +pub mod ospf; diff --git a/pve-rs/src/sdn/ospf.rs b/pve-rs/src/sdn/ospf.rs new file mode 100644 index 000000000000..4ab1e7e505f0 --- /dev/null +++ b/pve-rs/src/sdn/ospf.rs @@ -0,0 +1,217 @@ +#[perlmod::package(name = "PVE::RS::SDN::Fabrics::Ospf", lib = "pve_rs")] +mod export { + use std::{net::Ipv4Addr, str}; + + use anyhow::Context; + + use proxmox_network_types::{debian::Hostname, ip_address::Ipv4Cidr}; + use proxmox_schema::property_string::PropertyString; + use proxmox_section_config::typed::{ApiSectionDataEntry, SectionConfigData}; + use proxmox_ve_config::sdn::fabric::{ + FabricId, NodeId, SectionType, Validate, + ospf::{Area, FabricSection, InterfaceProperties, NodeSection, OspfSectionConfig}, + }; + use serde::{Deserialize, Serialize}; + + use crate::sdn::fabrics::export::PerlSectionConfig; + + perlmod::declare_magic!(Box<PerlSectionConfig<OspfSectionConfig>> : &PerlSectionConfig<OspfSectionConfig> as "PVE::RS::SDN::Fabrics::Ospf"); + + #[derive(Debug, Serialize, Deserialize)] + pub struct AddFabric { + fabric_id: FabricId, + area: Area, + loopback_prefix: Ipv4Cidr, + } + + #[derive(Debug, Deserialize)] + pub struct AddNode { + node_id: Hostname, + fabric_id: FabricId, + router_id: Ipv4Addr, + interfaces: Option<Vec<PropertyString<InterfaceProperties>>>, + } + + #[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, + area: Area, + } + + #[derive(Debug, Serialize, Deserialize)] + pub struct EditNode { + fabric_id: FabricId, + node_id: Hostname, + + router_id: Ipv4Addr, + interfaces: Option<Vec<PropertyString<InterfaceProperties>>>, + } + + #[derive(Debug, Serialize, Deserialize)] + pub struct EditInterface { + fabric_id: FabricId, + node_id: Hostname, + name: String, + + passive: bool, + } + + fn interface_exists( + config: &SectionConfigData<OspfSectionConfig>, + interface_name: &str, + node_name: &str, + ) -> bool { + config.sections.iter().any(|(k, v)| { + if let OspfSectionConfig::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<OspfSectionConfig> { + pub fn add_fabric(&self, params: AddFabric) -> Result<(), anyhow::Error> { + let fabric_id = params.fabric_id.to_string(); + + let mut config = self.section_config.lock().unwrap(); + + if config.sections.contains_key(&fabric_id) { + anyhow::bail!("fabric already exists"); + } + + let new_fabric = OspfSectionConfig::Fabric(FabricSection { + fabric_id: params.fabric_id, + area: params.area, + ty: SectionType::Fabric, + loopback_prefix: params.loopback_prefix, + }); + + config.sections.insert(fabric_id.clone(), new_fabric); + config.order.push(fabric_id); + + OspfSectionConfig::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 = OspfSectionConfig::Node(NodeSection { + id, + fabric_id: params.fabric_id, + node_id: params.node_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); + OspfSectionConfig::validate_as_ref(&config)?; + Ok(()) + } + + pub fn edit_fabric(&self, params: EditFabric) -> Result<(), anyhow::Error> { + let mut config = self.section_config.lock().unwrap(); + + if let OspfSectionConfig::Fabric(fs) = config + .sections + .get_mut(params.fabric_id.as_ref()) + .context("fabric doesn't exist")? + { + fs.area = params.area; + } + OspfSectionConfig::validate_as_ref(&config)?; + Ok(()) + } + + pub fn edit_node(&self, params: EditNode) -> Result<(), anyhow::Error> { + 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 OspfSectionConfig::Node(n) = node { + n.router_id = params.router_id; + n.interfaces = params.interfaces.unwrap_or_default(); + } + } else { + anyhow::bail!("node not found"); + } + OspfSectionConfig::validate_as_ref(&config)?; + Ok(()) + } + + pub fn delete_fabric(&self, params: DeleteFabric) -> Result<(), anyhow::Error> { + let mut config = self.section_config.lock().unwrap(); + + let fabric_id = params.fabric_id; + config + .sections + .remove(fabric_id.as_ref()) + .ok_or(anyhow::anyhow!("no fabric found"))?; + + // remove all the nodes + config.sections.retain(|k, _v| { + if let Ok(nodeid) = k.parse::<NodeId>() { + return nodeid.fabric_id != fabric_id; + } + true + }); + OspfSectionConfig::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"))?; + OspfSectionConfig::validate_as_ref(&config)?; + Ok(()) + } + + pub fn write(&self) -> Result<String, anyhow::Error> { + let guard = self.section_config.lock().unwrap().clone(); + OspfSectionConfig::write_section_config("sdn/fabrics/ospf.cfg", &guard) + } + } +} -- 2.39.5 _______________________________________________ pve-devel mailing list pve-devel@lists.proxmox.com https://lists.proxmox.com/cgi-bin/mailman/listinfo/pve-devel