public inbox for pve-devel@lists.proxmox.com
 help / color / mirror / Atom feed
From: Gabriel Goller <g.goller@proxmox.com>
To: pve-devel@lists.proxmox.com
Subject: [pve-devel] [PATCH proxmox-ve-rs 16/17] ve-config: add section-config to frr types conversion
Date: Fri, 28 Mar 2025 18:13:05 +0100	[thread overview]
Message-ID: <20250328171340.885413-18-g.goller@proxmox.com> (raw)
In-Reply-To: <20250328171340.885413-1-g.goller@proxmox.com>

Add a FabricConfig builder which iterates through nodes and generates
the frr config for the specified current_node. This part also
distributes the fabric options on all the interfaces – e.g. the
hello-interval option on the fabric will be added to all interfaces
here.

We mainly need to add these objects to FRR:

* interfaces
    We simply iterate through all configured interfaces and add them FRR
    with a short config line telling the daemon to enable
    openfabric/ospf on this interface.

* routers
    The tell the FRR daemon to initiate the openfabric/ospf daemon on
    every node.

* access-lists
    We throw all the router-ips of all the other nodes in the same
    fabric in access-list. This way we can simply use a route-map to
    match on it.

* route-maps
    We add a route-map to every fabric so that we rewrite the source
    address to the current router-ip which is on the local
    dummy_interface.

* ip-protocol statements
    These add the route-map to the protocol and all the routes from the
    protocol are going through the route-map.

Signed-off-by: Gabriel Goller <g.goller@proxmox.com>
---
 proxmox-ve-config/Cargo.toml            |   7 +
 proxmox-ve-config/debian/control        |  37 ++-
 proxmox-ve-config/src/sdn/fabric/mod.rs | 416 ++++++++++++++++++++++++
 3 files changed, 454 insertions(+), 6 deletions(-)

diff --git a/proxmox-ve-config/Cargo.toml b/proxmox-ve-config/Cargo.toml
index 3f7639efa153..231e237fb82f 100644
--- a/proxmox-ve-config/Cargo.toml
+++ b/proxmox-ve-config/Cargo.toml
@@ -24,3 +24,10 @@ proxmox-serde = { version = "0.1.2" }
 proxmox-sys = "0.6.4"
 proxmox-sortable-macro = "0.1.3"
 proxmox-network-types = { version = "0.1", path = "../proxmox-network-types/" }
+proxmox-frr = { version = "0.1", path = "../proxmox-frr/", optional = true }
+
+[features]
+frr = ["dep:proxmox-frr" ]
+
+[dev-dependencies]
+similar-asserts = "1"
diff --git a/proxmox-ve-config/debian/control b/proxmox-ve-config/debian/control
index 60ebcbc40e1c..5556ba747b8a 100644
--- a/proxmox-ve-config/debian/control
+++ b/proxmox-ve-config/debian/control
@@ -2,22 +2,26 @@ Source: rust-proxmox-ve-config
 Section: rust
 Priority: optional
 Build-Depends: debhelper-compat (= 13),
- dh-sequence-cargo,
- cargo:native <!nocheck>,
+ dh-sequence-cargo
+Build-Depends-Arch: cargo:native <!nocheck>,
  rustc:native <!nocheck>,
  libstd-rust-dev <!nocheck>,
  librust-anyhow-1+default-dev <!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+default-dev <!nocheck>,
+ librust-proxmox-section-config-2+default-dev (>= 2.1.1-~~) <!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>,
  librust-serde-1+derive-dev <!nocheck>,
  librust-serde-json-1+default-dev <!nocheck>,
  librust-serde-plain-1+default-dev <!nocheck>,
- librust-serde-with-3+default-dev <!nocheck>,
- librust-thiserror-1+default-dev (>= 1.0.59-~~) <!nocheck>
+ librust-serde-with-3+default-dev (>= 3.8.1-~~) <!nocheck>,
+ librust-thiserror-2+default-dev <!nocheck>,
+ librust-tracing-0.1+default-dev <!nocheck>
 Maintainer: Proxmox Support Team <support@proxmox.com>
 Standards-Version: 4.7.0
 Vcs-Git: git://git.proxmox.com/git/proxmox-ve-rs.git
@@ -33,15 +37,21 @@ Depends:
  librust-anyhow-1+default-dev,
  librust-log-0.4+default-dev,
  librust-nix-0.26+default-dev,
+ librust-proxmox-network-types-0.1+default-dev,
  librust-proxmox-schema-4+default-dev,
+ librust-proxmox-section-config-2+default-dev (>= 2.1.1-~~),
+ 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,
  librust-serde-1+derive-dev,
  librust-serde-json-1+default-dev,
  librust-serde-plain-1+default-dev,
- librust-serde-with-3+default-dev,
- librust-thiserror-1+default-dev (>= 1.0.59-~~)
+ librust-serde-with-3+default-dev (>= 3.8.1-~~),
+ librust-thiserror-2+default-dev,
+ librust-tracing-0.1+default-dev
+Suggests:
+ librust-proxmox-ve-config+frr-dev (= ${binary:Version})
 Provides:
  librust-proxmox-ve-config+default-dev (= ${binary:Version}),
  librust-proxmox-ve-config-0-dev (= ${binary:Version}),
@@ -52,3 +62,18 @@ Provides:
  librust-proxmox-ve-config-0.2.2+default-dev (= ${binary:Version})
 Description: Rust crate "proxmox-ve-config" - Rust source code
  Source code for Debianized Rust crate "proxmox-ve-config"
+
+Package: librust-proxmox-ve-config+frr-dev
+Architecture: any
+Multi-Arch: same
+Depends:
+ ${misc:Depends},
+ librust-proxmox-ve-config-dev (= ${binary:Version}),
+ librust-proxmox-frr-0.1+default-dev
+Provides:
+ librust-proxmox-ve-config-0+frr-dev (= ${binary:Version}),
+ librust-proxmox-ve-config-0.2+frr-dev (= ${binary:Version}),
+ librust-proxmox-ve-config-0.2.2+frr-dev (= ${binary:Version})
+Description: Rust crate "proxmox-ve-config" - feature "frr"
+ This metapackage enables feature "frr" for the Rust proxmox-ve-config crate, by
+ pulling in any additional dependencies needed by that feature.
diff --git a/proxmox-ve-config/src/sdn/fabric/mod.rs b/proxmox-ve-config/src/sdn/fabric/mod.rs
index 949486a86355..5dd4866e33bb 100644
--- a/proxmox-ve-config/src/sdn/fabric/mod.rs
+++ b/proxmox-ve-config/src/sdn/fabric/mod.rs
@@ -3,12 +3,32 @@ pub mod ospf;
 
 use openfabric::OpenFabricSectionConfig;
 use ospf::OspfSectionConfig;
+use proxmox_network_types::net::Net;
 use proxmox_section_config::typed::ApiSectionDataEntry;
 use proxmox_section_config::typed::SectionConfigData;
 
+use std::net::{IpAddr, Ipv4Addr};
 use std::ops::Deref;
+use std::collections::HashMap;
 
 use serde::de::DeserializeOwned;
+use serde::{Deserialize, Serialize};
+use thiserror::Error;
+
+#[cfg(feature = "frr")]
+use {
+    anyhow::anyhow,
+    proxmox_frr::{
+        ospf::Area,
+        route_map::{
+            AccessAction, AccessList, AccessListName, AccessListRule, ProtocolRouteMap,
+            RouteMap, RouteMapName, RouteMapSet, RouteMapMatch, ProtocolType
+        },
+        FrrConfig, FrrWord, Interface, InterfaceName, Router, RouterName,
+    },
+    proxmox_network_types::hostname::Hostname,
+    std::collections::BTreeMap,
+};
 
 #[derive(Debug, Clone)]
 pub struct Valid<T>(SectionConfigData<T>);
@@ -42,3 +62,399 @@ where
     }
 }
 
+#[derive(Serialize, Deserialize, Debug, Clone, PartialEq, Eq, PartialOrd, Ord, Hash, Error)]
+pub enum ConfigError {
+    #[error("node id has invalid format")]
+    InvalidNodeId,
+}
+
+#[derive(Default, Clone)]
+pub struct FabricConfig {
+    openfabric: Option<Valid<OpenFabricSectionConfig>>,
+    ospf: Option<Valid<OspfSectionConfig>>,
+}
+
+impl FabricConfig {
+    pub fn new(raw_openfabric: &str, raw_ospf: &str) -> Result<Self, anyhow::Error> {
+        let openfabric = <Valid<OpenFabricSectionConfig>>::parse_section_config(
+            "openfabric.cfg",
+            raw_openfabric,
+        )?;
+        let ospf = <Valid<OspfSectionConfig>>::parse_section_config("ospf.cfg", raw_ospf)?;
+
+        Ok(Self {
+            openfabric: Some(openfabric),
+            ospf: Some(ospf),
+        })
+    }
+
+    pub fn openfabric(&self) -> &Option<Valid<OpenFabricSectionConfig>> {
+        &self.openfabric
+    }
+    pub fn ospf(&self) -> &Option<Valid<OspfSectionConfig>> {
+        &self.ospf
+    }
+
+    pub fn with_openfabric(config: Valid<OpenFabricSectionConfig>) -> FabricConfig {
+        Self {
+            openfabric: Some(config),
+            ospf: None,
+        }
+    }
+
+    pub fn with_ospf(config: Valid<OspfSectionConfig>) -> FabricConfig {
+        Self {
+            ospf: Some(config),
+            openfabric: None,
+        }
+    }
+}
+
+pub trait FromSectionConfig
+where
+    Self: Sized + TryFrom<SectionConfigData<Self::Section>>,
+    <Self as TryFrom<SectionConfigData<Self::Section>>>::Error: std::fmt::Debug,
+{
+    type Section: ApiSectionDataEntry + DeserializeOwned;
+
+    fn from_section_config(raw: &str) -> Result<Self, anyhow::Error> {
+        let section_config_data = Self::Section::section_config()
+            .parse(Self::filename(), raw)?
+            .try_into()?;
+
+        let output = Self::try_from(section_config_data).unwrap();
+        Ok(output)
+    }
+
+    fn filename() -> String;
+}
+
+/// Builder that helps building the FrrConfig.
+#[derive(Default)]
+#[cfg(feature = "frr")]
+pub struct FrrConfigBuilder {
+    fabrics: FabricConfig,
+}
+
+#[cfg(feature = "frr")]
+impl FrrConfigBuilder {
+    /// Add fabrics to the builder
+    pub fn add_fabrics(mut self, fabric: FabricConfig) -> FrrConfigBuilder {
+        self.fabrics = fabric;
+        self
+    }
+
+    /// Build the complete [`FrrConfig`] from this builder configuration given the hostname of the
+    /// node for which we want to build the config. We also inject the common fabric-level options
+    /// into the interfaces here. (e.g. the fabric-level "hello-interval" gets added to every
+    /// interface if there isn't a more specific one.)
+    pub fn build(self, current_node: Hostname) -> Result<FrrConfig, anyhow::Error> {
+        let mut router: BTreeMap<RouterName, Router> = BTreeMap::new();
+        let mut interfaces: BTreeMap<InterfaceName, Interface> = BTreeMap::new();
+        let mut access_lists: BTreeMap<AccessListName, AccessList> = BTreeMap::new();
+        let mut routemaps: Vec<RouteMap> = Vec::new();
+        let mut protocol_routemaps: Vec<ProtocolRouteMap> = Vec::new();
+
+        if let Some(openfabric) = self.fabrics.openfabric {
+            let mut fabrics = HashMap::new();
+            let mut local_configuration = Vec::new();
+
+            for (_, section) in openfabric.iter() {
+                match section {
+                    OpenFabricSectionConfig::Fabric(fabric) => {
+                        fabrics.insert(fabric.fabric_id.clone(), fabric);
+                    },
+                    OpenFabricSectionConfig::Node(node) => {
+                        if node.node_id.node == current_node {
+                            local_configuration.push(node);
+                        }
+                    }
+                }
+            }
+
+            let mut routemap_seq = 100;
+
+            for node in local_configuration {
+                let fabric = fabrics.get(&node.node_id.fabric_id)
+                    .ok_or_else(|| anyhow!("could not find fabric: {}", node.node_id.fabric_id))?;
+
+                let (router_name, router_item) = Self::build_openfabric_router(
+                    &node.node_id.fabric_id,
+                    &node.router_id.into(),
+                )?;
+                router.insert(router_name, router_item);
+
+                let (interface, interface_name) = Self::build_openfabric_dummy_interface(
+                    &node.node_id.fabric_id,
+                    node.router_id,
+                )?;
+
+                if interfaces.insert(interface_name, interface).is_some() {
+                    tracing::error!(
+                        "An interface with the same name as the dummy interface exists"
+                    );
+                }
+
+                for interface in node.interface.iter() {
+                    let (interface, interface_name) = Self::build_openfabric_interface(
+                        &node.node_id.fabric_id,
+                        interface,
+                        fabric,
+                        node.router_id,
+                    )?;
+
+                    if interfaces.insert(interface_name, interface).is_some() {
+                        tracing::warn!(
+                            "An interface cannot be in multiple openfabric fabrics"
+                        );
+                    }
+                }
+
+                let access_list_name = AccessListName::new(format!(
+                    "openfabric_{}_ips",
+                    node.node_id.fabric_id
+                ));
+
+                let rule = AccessListRule {
+                    action: AccessAction::Permit,
+                    network: fabric.loopback_prefix,
+                    seq: None,
+                };
+
+                access_lists
+                    .entry(access_list_name.clone())
+                    .and_modify(|l| l.rules.push(rule.clone()))
+                    .or_insert(AccessList {
+                        name: access_list_name,
+                        rules: vec![rule],
+                    });
+
+                let routemap = Self::build_openfabric_dummy_routemap(
+                    &node.node_id.fabric_id,
+                    node.router_id,
+                    routemap_seq
+                )?;
+
+                routemap_seq += 10;
+
+                routemaps.push(routemap);
+
+                let protocol_routemap = ProtocolRouteMap {
+                    protocol: ProtocolType::OpenFabric,
+                    routemap_name: RouteMapName::new("openfabric".to_owned()),
+                };
+
+                protocol_routemaps.push(protocol_routemap);
+            }
+        }
+
+        if let Some(ospf) = self.fabrics.ospf {
+            let mut fabrics = HashMap::new();
+            let mut local_configuration = Vec::new();
+
+            for (_, section) in ospf.iter() {
+                match section {
+                    OspfSectionConfig::Fabric(fabric) => {
+                        fabrics.insert(fabric.area.clone(), fabric);
+                    },
+                    OspfSectionConfig::Node(node) => {
+                        if node.node_id.node == current_node {
+                            local_configuration.push(node);
+                        }
+                    }
+                }
+            }
+
+            for node in local_configuration {
+                let fabric = fabrics.get(&node.node_id.area)
+                    .ok_or_else(|| anyhow!("could not find fabric: {}", node.node_id.area))?;
+
+                let (router_name, router_item) =
+                    Self::build_ospf_router(&node.node_id.area, node)?;
+                router.insert(router_name, router_item);
+
+                // Add dummy interface
+                let (interface, interface_name) =
+                    Self::build_ospf_dummy_interface(&node.node_id.area)?;
+
+                if interfaces.insert(interface_name, interface).is_some() {
+                    tracing::error!(
+                        "An interface with the same name as the dummy interface exists"
+                    );
+                }
+
+                for interface in node.interface.iter() {
+                    let (interface, interface_name) = Self::build_ospf_interface(
+                        &node.node_id.area,
+                        interface,
+                    )?;
+
+                    if interfaces.insert(interface_name, interface).is_some() {
+                        tracing::warn!(
+                            "An interface cannot be in multiple openfabric fabrics"
+                        );
+                    }
+                }
+
+                let access_list_name = AccessListName::new(format!(
+                    "ospf_{}_ips",
+                    node.node_id.area
+                ));
+
+                let rule = AccessListRule {
+                    action: AccessAction::Permit,
+                    network: fabric.loopback_prefix.into(),
+                    seq: None,
+                };
+
+                access_lists
+                    .entry(access_list_name.clone())
+                    .and_modify(|l| l.rules.push(rule.clone()))
+                    .or_insert(AccessList {
+                        name: access_list_name,
+                        rules: vec![rule],
+                    });
+
+                let routemap = Self::build_ospf_dummy_routemap(
+                    &node.node_id.area,
+                    node.router_id,
+                )?;
+                routemaps.push(routemap);
+
+                let protocol_routemap = ProtocolRouteMap {
+                    protocol: ProtocolType::Ospf,
+                    routemap_name: RouteMapName::new("ospf".to_owned()),
+                };
+
+                protocol_routemaps.push(protocol_routemap);
+            }
+        }
+
+        Ok(FrrConfig {
+            router,
+            interfaces,
+            access_lists,
+            routemaps,
+            protocol_routemaps,
+        })
+    }
+
+    fn build_ospf_router(
+        area: &ospf::Area,
+        node_config: &ospf::NodeSection,
+    ) -> Result<(RouterName, Router), anyhow::Error> {
+        let ospf_router: proxmox_frr::ospf::OspfRouter = node_config.to_owned().into();
+        let router_item = Router::Ospf(ospf_router);
+        let frr_word_id = FrrWord::new(area.to_string())?;
+        let router_name = RouterName::Ospf(proxmox_frr::ospf::OspfRouterName::from(Area::new(
+            frr_word_id,
+        )?));
+        Ok((router_name, router_item))
+    }
+
+    fn build_openfabric_router(
+        fabric_id: &openfabric::FabricId,
+        net: &Net,
+    ) -> Result<(RouterName, Router), anyhow::Error> {
+        let ofr = proxmox_frr::openfabric::OpenFabricRouter { net: net.clone() };
+        let router_item = Router::OpenFabric(ofr);
+        let frr_word_id = FrrWord::new(fabric_id.to_string())?;
+        let router_name = RouterName::OpenFabric(frr_word_id.into());
+        Ok((router_name, router_item))
+    }
+
+    fn build_ospf_interface(
+        area: &ospf::Area,
+        interface: &ospf::InterfaceProperties,
+    ) -> Result<(Interface, InterfaceName), anyhow::Error> {
+        let frr_interface: proxmox_frr::ospf::OspfInterface = interface.to_frr_interface(area)?;
+
+        let interface_name = InterfaceName::Ospf(interface.name.parse()?);
+        Ok((frr_interface.into(), interface_name))
+    }
+
+    fn build_ospf_dummy_interface(
+        fabric_id: &ospf::Area,
+    ) -> Result<(Interface, InterfaceName), anyhow::Error> {
+        let frr_word = FrrWord::new(fabric_id.to_string())?;
+        let frr_interface = proxmox_frr::ospf::OspfInterface {
+            area: frr_word.try_into()?,
+            passive: Some(true),
+            network_type: None,
+        };
+        let interface_name = InterfaceName::OpenFabric(format!("dummy_{}", fabric_id).parse()?);
+        Ok((frr_interface.into(), interface_name))
+    }
+
+    fn build_openfabric_interface(
+        fabric_id: &openfabric::FabricId,
+        interface: &openfabric::InterfaceProperties,
+        fabric_config: &openfabric::FabricSection,
+        router_id: IpAddr,
+    ) -> Result<(Interface, InterfaceName), anyhow::Error> {
+        let mut frr_interface: proxmox_frr::openfabric::OpenFabricInterface =
+            interface.to_frr_interface(fabric_id, router_id.is_ipv6())?;
+        // If no specific hello_interval is set, get default one from fabric
+        // config
+        if frr_interface.hello_interval().is_none() {
+            frr_interface.set_hello_interval(fabric_config.hello_interval);
+        }
+        let interface_name = InterfaceName::OpenFabric(interface.name.parse()?);
+        Ok((frr_interface.into(), interface_name))
+    }
+
+    fn build_openfabric_dummy_interface(
+        fabric_id: &openfabric::FabricId,
+        router_id: IpAddr,
+    ) -> Result<(Interface, InterfaceName), anyhow::Error> {
+        let frr_word = FrrWord::new(fabric_id.to_string())?;
+        let frr_interface = proxmox_frr::openfabric::OpenFabricInterface {
+            fabric_id: frr_word.into(),
+            hello_interval: None,
+            passive: Some(true),
+            csnp_interval: None,
+            hello_multiplier: None,
+            is_ipv6: router_id.is_ipv6(),
+        };
+        let interface_name = InterfaceName::OpenFabric(format!("dummy_{}", fabric_id).parse()?);
+        Ok((frr_interface.into(), interface_name))
+    }
+
+    fn build_openfabric_dummy_routemap(
+        fabric_id: &openfabric::FabricId,
+        router_ip: IpAddr,
+        seq: u32
+    ) -> Result<RouteMap, anyhow::Error> {
+        let routemap_name = RouteMapName::new("openfabric".to_owned());
+        // create route-map
+        let routemap = RouteMap {
+            name: routemap_name.clone(),
+            seq,
+            action: AccessAction::Permit,
+            matches: vec![RouteMapMatch::IpAddress(AccessListName::new(format!(
+                "openfabric_{fabric_id}_ips"
+            )))],
+            sets: vec![RouteMapSet::IpSrc(router_ip)],
+        };
+        Ok(routemap)
+    }
+
+    fn build_ospf_dummy_routemap(
+        area: &ospf::Area,
+        router_ip: Ipv4Addr,
+    ) -> Result<RouteMap, anyhow::Error> {
+        let routemap_name = RouteMapName::new("ospf".to_owned());
+        // create route-map
+        let routemap = RouteMap {
+            name: routemap_name.clone(),
+            seq: 10,
+            action: AccessAction::Permit,
+            matches: vec![RouteMapMatch::IpAddress(AccessListName::new(format!(
+                "ospf_{area}_ips"
+            )))],
+            sets: vec![RouteMapSet::IpSrc(IpAddr::from(router_ip))],
+        };
+
+        Ok(routemap)
+    }
+}
-- 
2.39.5



_______________________________________________
pve-devel mailing list
pve-devel@lists.proxmox.com
https://lists.proxmox.com/cgi-bin/mailman/listinfo/pve-devel

  parent reply	other threads:[~2025-03-28 17:16 UTC|newest]

Thread overview: 96+ messages / expand[flat|nested]  mbox.gz  Atom feed  top
2025-03-28 17:12 [pve-devel] [PATCH cluster/docs/manager/network/proxmox{, -ve-rs, -firewall, -perl-rs} 00/52] Add SDN Fabrics Gabriel Goller
2025-03-28 17:12 ` [pve-devel] [PATCH proxmox 1/1] serde: add string_as_bool module for boolean string parsing Gabriel Goller
2025-03-28 17:12 ` [pve-devel] [PATCH proxmox-ve-rs 01/17] add proxmox-network-types crate Gabriel Goller
2025-03-31 14:09   ` Thomas Lamprecht
2025-03-31 14:38     ` Stefan Hanreich
2025-03-31 16:20       ` Thomas Lamprecht
2025-03-28 17:12 ` [pve-devel] [PATCH proxmox-ve-rs 02/17] network-types: add common hostname and openfabric types Gabriel Goller
2025-03-28 17:12 ` [pve-devel] [PATCH proxmox-ve-rs 03/17] network-types: add openfabric NET type Gabriel Goller
2025-03-28 17:12 ` [pve-devel] [PATCH proxmox-ve-rs 04/17] network-types: move Ipv4Cidr and Ipv6Cidr types Gabriel Goller
2025-03-28 17:12 ` [pve-devel] [PATCH proxmox-ve-rs 05/17] frr: create proxmox-frr crate Gabriel Goller
2025-03-28 17:12 ` [pve-devel] [PATCH proxmox-ve-rs 06/17] frr: add common frr types Gabriel Goller
2025-03-28 17:12 ` [pve-devel] [PATCH proxmox-ve-rs 07/17] frr: add openfabric types Gabriel Goller
2025-03-28 17:12 ` [pve-devel] [PATCH proxmox-ve-rs 08/17] frr: add ospf types Gabriel Goller
2025-03-28 17:12 ` [pve-devel] [PATCH proxmox-ve-rs 09/17] frr: add route-map types Gabriel Goller
2025-03-28 17:12 ` [pve-devel] [PATCH proxmox-ve-rs 10/17] frr: add generic types over openfabric and ospf Gabriel Goller
2025-03-28 17:13 ` [pve-devel] [PATCH proxmox-ve-rs 11/17] frr: add serializer for all FRR types Gabriel Goller
2025-03-28 17:13 ` [pve-devel] [PATCH proxmox-ve-rs 12/17] ve-config: add openfabric section-config Gabriel Goller
2025-03-31 13:48   ` Christoph Heiss
2025-03-31 15:04     ` Gabriel Goller
2025-03-28 17:13 ` [pve-devel] [PATCH proxmox-ve-rs 13/17] ve-config: add ospf section-config Gabriel Goller
2025-03-28 17:13 ` [pve-devel] [PATCH proxmox-ve-rs 14/17] ve-config: add FRR conversion helpers for openfabric and ospf Gabriel Goller
2025-03-28 17:13 ` [pve-devel] [PATCH proxmox-ve-rs 15/17] ve-config: add validation for section-config Gabriel Goller
2025-03-28 17:13 ` Gabriel Goller [this message]
2025-03-31 13:51   ` [pve-devel] [PATCH proxmox-ve-rs 16/17] ve-config: add section-config to frr types conversion Christoph Heiss
2025-03-31 14:31     ` Stefan Hanreich
2025-03-28 17:13 ` [pve-devel] [PATCH proxmox-ve-rs 17/17] ve-config: add integrations tests Gabriel Goller
2025-03-28 17:13 ` [pve-devel] [PATCH proxmox-firewall 1/1] firewall: nftables: migrate to proxmox-network-types Gabriel Goller
2025-03-28 17:13 ` [pve-devel] [PATCH proxmox-perl-rs 1/7] perl-rs: sdn: initial fabric infrastructure Gabriel Goller
2025-03-28 17:13 ` [pve-devel] [PATCH proxmox-perl-rs 2/7] perl-rs: sdn: add CRUD helpers for OpenFabric fabric management Gabriel Goller
2025-03-28 17:13 ` [pve-devel] [PATCH proxmox-perl-rs 3/7] perl-rs: sdn: OpenFabric perlmod methods Gabriel Goller
2025-03-28 17:13 ` [pve-devel] [PATCH proxmox-perl-rs 4/7] perl-rs: sdn: implement OSPF interface file configuration generation Gabriel Goller
2025-03-28 17:13 ` [pve-devel] [PATCH proxmox-perl-rs 5/7] perl-rs: sdn: add CRUD helpers for OSPF fabric management Gabriel Goller
2025-03-28 17:13 ` [pve-devel] [PATCH proxmox-perl-rs 6/7] perl-rs: sdn: OSPF perlmod methods Gabriel Goller
2025-03-28 17:13 ` [pve-devel] [PATCH proxmox-perl-rs 7/7] perl-rs: sdn: implement OSPF interface file configuration generation Gabriel Goller
2025-03-28 17:13 ` [pve-devel] [PATCH pve-cluster 1/1] cluster: add sdn fabrics config files Gabriel Goller
2025-03-28 17:13 ` [pve-devel] [PATCH pve-network 01/17] sdn: fix value returned by pending_config Gabriel Goller
2025-03-28 17:13 ` [pve-devel] [PATCH pve-network 02/17] debian: add dependency to proxmox-perl-rs Gabriel Goller
2025-03-28 17:13 ` [pve-devel] [PATCH pve-network 03/17] fabrics: add fabrics module Gabriel Goller
2025-03-28 17:13 ` [pve-devel] [PATCH pve-network 04/17] refactor: controller: move frr methods into helper Gabriel Goller
2025-03-28 17:13 ` [pve-devel] [PATCH pve-network 05/17] controllers: implement new api for frr config generation Gabriel Goller
2025-03-28 17:13 ` [pve-devel] [PATCH pve-network 06/17] sdn: add frr config generation helper Gabriel Goller
2025-03-28 17:13 ` [pve-devel] [PATCH pve-network 07/17] test: isis: add test for standalone configuration Gabriel Goller
2025-03-28 17:13 ` [pve-devel] [PATCH pve-network 08/17] sdn: frr: add daemon status to frr helper Gabriel Goller
2025-04-02 10:41   ` Fabian Grünbichler
2025-04-02 10:50     ` Stefan Hanreich
2025-03-28 17:13 ` [pve-devel] [PATCH pve-network 09/17] sdn: running: apply fabrics config Gabriel Goller
2025-04-02 10:41   ` Fabian Grünbichler
2025-04-02 12:26     ` Stefan Hanreich
2025-03-28 17:13 ` [pve-devel] [PATCH pve-network 10/17] fabrics: generate ifupdown configuration Gabriel Goller
2025-03-28 17:13 ` [pve-devel] [PATCH pve-network 11/17] api: add fabrics subfolder Gabriel Goller
2025-04-02 10:41   ` Fabian Grünbichler
2025-04-02 12:20     ` Stefan Hanreich
2025-04-02 12:29       ` Fabian Grünbichler
2025-03-28 17:13 ` [pve-devel] [PATCH pve-network 12/17] api: fabrics: add common helpers Gabriel Goller
2025-04-02 10:41   ` Fabian Grünbichler
2025-03-28 17:13 ` [pve-devel] [PATCH pve-network 13/17] fabric: openfabric: add api endpoints Gabriel Goller
2025-04-02 10:37   ` Fabian Grünbichler
2025-03-28 17:13 ` [pve-devel] [PATCH pve-network 14/17] fabric: ospf: " Gabriel Goller
2025-04-02 10:37   ` Fabian Grünbichler
2025-03-28 17:13 ` [pve-devel] [PATCH pve-network 15/17] test: fabrics: add test cases for ospf and openfabric + evpn Gabriel Goller
2025-03-28 17:13 ` [pve-devel] [PATCH pve-network 16/17] frr: bump frr config version to 10.2.1 Gabriel Goller
2025-03-28 17:13 ` [pve-devel] [PATCH pve-network 17/17] frr: fix reloading frr configuration Gabriel Goller
2025-04-02 10:37   ` Fabian Grünbichler
2025-04-02 10:42     ` Stefan Hanreich
2025-03-28 17:13 ` [pve-devel] [PATCH pve-manager 1/7] api: use new generalized frr and etc network config helper functions Gabriel Goller
2025-03-28 17:13 ` [pve-devel] [PATCH pve-manager 2/7] fabrics: add common interface panel Gabriel Goller
2025-04-02  9:26   ` Friedrich Weber
2025-04-02 10:04     ` Gabriel Goller
2025-04-02 10:10       ` Friedrich Weber
2025-03-28 17:13 ` [pve-devel] [PATCH pve-manager 3/7] fabrics: add additional interface fields for openfabric and ospf Gabriel Goller
2025-03-28 17:13 ` [pve-devel] [PATCH pve-manager 4/7] fabrics: add FabricEdit components Gabriel Goller
2025-03-28 17:13 ` [pve-devel] [PATCH pve-manager 5/7] fabrics: add NodeEdit components Gabriel Goller
2025-04-03  9:16   ` Christoph Heiss
2025-04-04 15:45     ` Gabriel Goller
2025-03-28 17:13 ` [pve-devel] [PATCH pve-manager 6/7] fabrics: Add main FabricView Gabriel Goller
2025-04-02  9:26   ` Friedrich Weber
2025-04-02  9:50   ` Christoph Heiss
2025-04-02 10:40     ` Gabriel Goller
2025-03-28 17:13 ` [pve-devel] [PATCH pve-manager 7/7] utils: avoid line-break in pending changes message Gabriel Goller
2025-03-28 17:13 ` [pve-devel] [PATCH pve-docs 1/1] fabrics: add initial documentation for sdn fabrics Gabriel Goller
2025-03-31  8:44   ` Shannon Sterz
2025-03-31 12:24     ` Gabriel Goller
2025-04-02  8:43       ` Gabriel Goller
2025-04-02  8:49   ` Christoph Heiss
2025-04-02  9:09     ` Gabriel Goller
2025-04-02  9:16       ` Christoph Heiss
2025-04-03  8:30 ` [pve-devel] [PATCH cluster/docs/manager/network/proxmox{, -ve-rs, -firewall, -perl-rs} 00/52] Add SDN Fabrics Friedrich Weber
2025-04-03 10:21   ` Gabriel Goller
2025-04-03 13:44     ` Friedrich Weber
2025-04-03 14:03       ` Stefan Hanreich
2025-04-03 14:20         ` Friedrich Weber
2025-04-04  7:53           ` Stefan Hanreich
2025-04-04 10:55 ` Hannes Duerr
2025-04-04 12:48   ` Gabriel Goller
2025-04-04 12:53     ` Hannes Duerr
2025-04-04 14:26       ` Gabriel Goller

Reply instructions:

You may reply publicly to this message via plain-text email
using any one of the following methods:

* Save the following mbox file, import it into your mail client,
  and reply-to-all from there: mbox

  Avoid top-posting and favor interleaved quoting:
  https://en.wikipedia.org/wiki/Posting_style#Interleaved_style

* Reply using the --to, --cc, and --in-reply-to
  switches of git-send-email(1):

  git send-email \
    --in-reply-to=20250328171340.885413-18-g.goller@proxmox.com \
    --to=g.goller@proxmox.com \
    --cc=pve-devel@lists.proxmox.com \
    /path/to/YOUR_REPLY

  https://kernel.org/pub/software/scm/git/docs/git-send-email.html

* If your mail client supports setting the In-Reply-To header
  via mailto: links, try the mailto: link
Be sure your reply has a Subject: header at the top and a blank line before the message body.
This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox
Service provided by Proxmox Server Solutions GmbH | Privacy | Legal