From: Gabriel Goller <g.goller@proxmox.com>
To: pve-devel@lists.proxmox.com
Subject: [pve-devel] [PATCH proxmox-ve-rs v2 14/15] ve-config: add section-config to frr types conversion
Date: Fri, 4 Apr 2025 18:28:26 +0200 [thread overview]
Message-ID: <20250404162908.563060-16-g.goller@proxmox.com> (raw)
In-Reply-To: <20250404162908.563060-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 | 5 +
proxmox-ve-config/debian/control | 23 +-
proxmox-ve-config/src/sdn/fabric/mod.rs | 427 ++++++++++++++++++++++++
3 files changed, 453 insertions(+), 2 deletions(-)
diff --git a/proxmox-ve-config/Cargo.toml b/proxmox-ve-config/Cargo.toml
index f58c6e2d0b8c..70c25312ac6d 100644
--- a/proxmox-ve-config/Cargo.toml
+++ b/proxmox-ve-config/Cargo.toml
@@ -11,12 +11,14 @@ log = "0.4"
anyhow = "1"
nix = "0.26"
thiserror = { workspace = true }
+tracing = "0.1"
serde = { workspace = true, features = [ "derive" ] }
serde_json = "1"
serde_plain = "1"
serde_with = { workspace = true }
+proxmox-frr = { optional = true, workspace = true }
proxmox-network-types = { workspace = true }
proxmox-sdn-types = { workspace = true }
proxmox-schema = { version = "4", features = [ "api-types" ] }
@@ -24,3 +26,6 @@ proxmox-section-config = { version = "2.1.2" }
proxmox-serde = { version = "0.1.2" }
proxmox-sys = "0.6.4"
proxmox-sortable-macro = "0.1.3"
+
+[features]
+frr = ["dep:proxmox-frr" ]
diff --git a/proxmox-ve-config/debian/control b/proxmox-ve-config/debian/control
index 0da241fed775..d0940b2c8ac5 100644
--- a/proxmox-ve-config/debian/control
+++ b/proxmox-ve-config/debian/control
@@ -22,7 +22,8 @@ Build-Depends-Arch: cargo:native <!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-thiserror-1+default-dev (>= 1.0.59-~~) <!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
@@ -51,7 +52,10 @@ Depends:
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-thiserror-1+default-dev (>= 1.0.59-~~),
+ 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}),
@@ -62,3 +66,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 45795b0e51b0..b3580ec4c7d0 100644
--- a/proxmox-ve-config/src/sdn/fabric/mod.rs
+++ b/proxmox-ve-config/src/sdn/fabric/mod.rs
@@ -1,9 +1,12 @@
pub mod openfabric;
pub mod ospf;
+use openfabric::OpenFabricSectionConfig;
+use ospf::OspfSectionConfig;
use proxmox_network_types::debian::Hostname;
use proxmox_section_config::typed::ApiSectionDataEntry;
use proxmox_section_config::typed::SectionConfigData;
+use thiserror::Error;
use std::ops::Deref;
@@ -110,6 +113,22 @@ impl std::str::FromStr for SectionType {
}
}
+#[cfg(feature = "frr")]
+use {
+ anyhow::anyhow,
+ proxmox_frr::{
+ ospf::Area,
+ route_map::{
+ AccessAction, AccessList, AccessListName, AccessListRule, ProtocolRouteMap,
+ ProtocolType, RouteMap, RouteMapMatch, RouteMapName, RouteMapSet,
+ },
+ FrrConfig, FrrWord, Interface, InterfaceName, Router, RouterName,
+ },
+ proxmox_sdn_types::net::Net,
+ std::collections::{BTreeMap, HashMap},
+ std::net::{IpAddr, Ipv4Addr},
+};
+
#[derive(Debug, Clone)]
pub struct Valid<T>(SectionConfigData<T>);
@@ -142,3 +161,411 @@ where
}
}
+#[derive(Error, Debug)]
+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).into_iter() {
+ match section {
+ OpenFabricSectionConfig::Fabric(fabric) => {
+ fabrics.insert(fabric.fabric_id.clone(), fabric);
+ }
+ OpenFabricSectionConfig::Node(node) => {
+ if node.node_id == current_node {
+ local_configuration.push(node);
+ }
+ }
+ }
+ }
+
+ let mut routemap_seq = 100;
+ let mut current_net: Option<Net> = None;
+
+ for node in local_configuration {
+ // if no interfaces are configured, don't generate any config
+ if node.interfaces.is_empty() {
+ break;
+ }
+
+ let fabric = fabrics
+ .get(&node.fabric_id)
+ .ok_or_else(|| anyhow!("could not find fabric: {}", node.fabric_id))?;
+
+ let net = current_net.get_or_insert(node.router_id.into());
+ let (router_name, router_item) =
+ Self::build_openfabric_router(&node.fabric_id, net)?;
+ router.insert(router_name, router_item);
+
+ let (interface, interface_name) =
+ Self::build_openfabric_dummy_interface(&node.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.interfaces.iter() {
+ let (interface, interface_name) = Self::build_openfabric_interface(
+ &node.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.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.fabric_id,
+ node.router_id,
+ routemap_seq,
+ )?;
+
+ routemap_seq += 10;
+
+ routemaps.push(routemap);
+ }
+
+ if !routemaps.is_empty() {
+ 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).into_iter() {
+ match section {
+ OspfSectionConfig::Fabric(fabric) => {
+ fabrics.insert(fabric.fabric_id.clone(), fabric);
+ }
+ OspfSectionConfig::Node(node) => {
+ if node.node_id == current_node {
+ local_configuration.push(node);
+ }
+ }
+ }
+ }
+
+ let mut routemap_seq = 100;
+ let mut current_router_id: Option<Ipv4Addr> = None;
+
+ for node in local_configuration {
+ // if no interfaces are configured, don't generate any config
+ if node.interfaces.is_empty() {
+ break;
+ }
+
+ let fabric = fabrics
+ .get(&node.fabric_id)
+ .ok_or_else(|| anyhow!("could not find fabric: {}", node.fabric_id))?;
+
+ let router_id = current_router_id.get_or_insert(node.router_id);
+ let (router_name, router_item) =
+ Self::build_ospf_router(&fabric.area, node, *router_id)?;
+ router.insert(router_name, router_item);
+
+ // Add dummy interface
+ let (interface, interface_name) =
+ Self::build_ospf_dummy_interface(&fabric.fabric_id, &fabric.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.interfaces.iter() {
+ let (interface, interface_name) =
+ Self::build_ospf_interface(&fabric.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.fabric_id));
+
+ 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(
+ &fabric.fabric_id,
+ node.router_id,
+ routemap_seq,
+ )?;
+ routemap_seq += 10;
+ routemaps.push(routemap);
+ }
+
+ if !routemaps.is_empty() {
+ 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,
+ router_id: Ipv4Addr,
+ ) -> Result<(RouterName, Router), anyhow::Error> {
+ let ospf_router = proxmox_frr::ospf::OspfRouter { router_id };
+ 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: &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: &FabricId,
+ area: &ospf::Area,
+ ) -> Result<(Interface, InterfaceName), anyhow::Error> {
+ let frr_word = FrrWord::new(area.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: &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: &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: &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(
+ fabric_id: &FabricId,
+ router_ip: Ipv4Addr,
+ seq: u32,
+ ) -> Result<RouteMap, anyhow::Error> {
+ let routemap_name = RouteMapName::new("ospf".to_owned());
+ // create route-map
+ let routemap = RouteMap {
+ name: routemap_name.clone(),
+ seq,
+ action: AccessAction::Permit,
+ matches: vec![RouteMapMatch::IpAddress(AccessListName::new(format!(
+ "ospf_{fabric_id}_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
next prev parent reply other threads:[~2025-04-04 16:32 UTC|newest]
Thread overview: 76+ messages / expand[flat|nested] mbox.gz Atom feed top
2025-04-04 16:28 [pve-devel] [PATCH access-control/cluster/docs/gui-tests/manager/network/proxmox{, -ve-rs, -perl-rs} v2 00/57] Add SDN Fabrics Gabriel Goller
2025-04-04 16:28 ` [pve-devel] [PATCH proxmox v2 1/1] serde: add string_as_bool module for boolean string parsing Gabriel Goller
2025-04-04 16:28 ` [pve-devel] [PATCH proxmox-ve-rs v2 01/15] sdn-types: initial commit Gabriel Goller
2025-04-04 16:28 ` [pve-devel] [PATCH proxmox-ve-rs v2 02/15] frr: create proxmox-frr crate Gabriel Goller
2025-04-04 16:28 ` [pve-devel] [PATCH proxmox-ve-rs v2 03/15] frr: add common frr types Gabriel Goller
2025-04-04 16:28 ` [pve-devel] [PATCH proxmox-ve-rs v2 04/15] frr: add openfabric types Gabriel Goller
2025-04-04 16:28 ` [pve-devel] [PATCH proxmox-ve-rs v2 05/15] frr: add ospf types Gabriel Goller
2025-04-04 16:28 ` [pve-devel] [PATCH proxmox-ve-rs v2 06/15] frr: add route-map types Gabriel Goller
2025-04-04 16:28 ` [pve-devel] [PATCH proxmox-ve-rs v2 07/15] frr: add generic types over openfabric and ospf Gabriel Goller
2025-04-04 16:28 ` [pve-devel] [PATCH proxmox-ve-rs v2 08/15] frr: add serializer for all FRR types Gabriel Goller
2025-04-04 16:28 ` [pve-devel] [PATCH proxmox-ve-rs v2 09/15] ve-config: add common section-config types for OpenFabric and OSPF Gabriel Goller
2025-04-04 16:28 ` [pve-devel] [PATCH proxmox-ve-rs v2 10/15] ve-config: add openfabric section-config Gabriel Goller
2025-04-04 16:28 ` [pve-devel] [PATCH proxmox-ve-rs v2 11/15] ve-config: add ospf section-config Gabriel Goller
2025-04-04 16:28 ` [pve-devel] [PATCH proxmox-ve-rs v2 12/15] ve-config: add FRR conversion helpers for openfabric and ospf Gabriel Goller
2025-04-04 16:28 ` [pve-devel] [PATCH proxmox-ve-rs v2 13/15] ve-config: add validation for section-config Gabriel Goller
2025-04-04 16:28 ` Gabriel Goller [this message]
2025-04-04 16:28 ` [pve-devel] [PATCH proxmox-ve-rs v2 15/15] ve-config: add integrations tests Gabriel Goller
2025-04-04 16:28 ` [pve-devel] [PATCH proxmox-perl-rs v2 1/7] perl-rs: sdn: initial fabric infrastructure Gabriel Goller
2025-04-04 16:28 ` [pve-devel] [PATCH proxmox-perl-rs v2 2/7] perl-rs: sdn: add CRUD helpers for OpenFabric fabric management Gabriel Goller
2025-04-04 16:28 ` [pve-devel] [PATCH proxmox-perl-rs v2 3/7] perl-rs: sdn: OpenFabric perlmod methods Gabriel Goller
2025-04-04 16:28 ` [pve-devel] [PATCH proxmox-perl-rs v2 4/7] perl-rs: sdn: implement Openfabric interface file generation Gabriel Goller
2025-04-04 16:28 ` [pve-devel] [PATCH proxmox-perl-rs v2 5/7] perl-rs: sdn: add CRUD helpers for OSPF fabric management Gabriel Goller
2025-04-04 16:28 ` [pve-devel] [PATCH proxmox-perl-rs v2 6/7] perl-rs: sdn: OSPF perlmod methods Gabriel Goller
2025-04-04 16:28 ` [pve-devel] [PATCH proxmox-perl-rs v2 7/7] perl-rs: sdn: implement OSPF interface file configuration generation Gabriel Goller
2025-04-04 16:28 ` [pve-devel] [PATCH pve-cluster v2 1/1] cluster: add sdn fabrics config files Gabriel Goller
2025-04-04 17:03 ` [pve-devel] applied: " Thomas Lamprecht
2025-04-04 16:28 ` [pve-devel] [PATCH pve-access-control v2 1/1] permissions: add ACL paths for SDN fabrics Gabriel Goller
2025-04-04 17:20 ` Thomas Lamprecht
2025-04-07 7:24 ` Fabian Grünbichler
2025-04-07 8:12 ` Thomas Lamprecht
2025-04-07 8:51 ` Stefan Hanreich
2025-04-07 9:27 ` Fabian Grünbichler
2025-04-07 9:44 ` Stefan Hanreich
2025-04-11 11:12 ` Stefan Hanreich
2025-04-11 11:14 ` Stefan Hanreich
2025-04-11 16:51 ` Stefan Hanreich
2025-04-07 9:34 ` Thomas Lamprecht
2025-04-07 10:08 ` Stefan Hanreich
2025-04-07 10:12 ` Thomas Lamprecht
2025-04-07 11:41 ` Gilberto Ferreira via pve-devel
[not found] ` <CAOKSTBsu8vrw8_nSu_LozwNwTc+ReTb6TEg3K_iM8uYh9oRRFg@mail.gmail.com>
2025-04-07 11:59 ` Stefan Hanreich
2025-04-07 12:22 ` Gilberto Ferreira via pve-devel
2025-04-04 16:28 ` [pve-devel] [PATCH pve-network v2 01/19] sdn: fix value returned by pending_config Gabriel Goller
2025-04-04 16:28 ` [pve-devel] [PATCH pve-network v2 02/19] debian: add dependency to proxmox-perl-rs Gabriel Goller
2025-04-04 16:28 ` [pve-devel] [PATCH pve-network v2 03/19] fabrics: add fabrics module Gabriel Goller
2025-04-04 16:28 ` [pve-devel] [PATCH pve-network v2 04/19] refactor: controller: move frr methods into helper Gabriel Goller
2025-04-04 16:28 ` [pve-devel] [PATCH pve-network v2 05/19] frr: add new helpers for reloading frr configuration Gabriel Goller
2025-04-04 16:28 ` [pve-devel] [PATCH pve-network v2 06/19] controllers: implement new api for frr config generation Gabriel Goller
2025-04-04 16:28 ` [pve-devel] [PATCH pve-network v2 07/19] sdn: add frr config generation helper Gabriel Goller
2025-04-04 16:28 ` [pve-devel] [PATCH pve-network v2 08/19] test: isis: add test for standalone configuration Gabriel Goller
2025-04-04 16:28 ` [pve-devel] [PATCH pve-network v2 09/19] sdn: frr: add daemon status to frr helper Gabriel Goller
2025-04-04 16:28 ` [pve-devel] [PATCH pve-network v2 10/19] sdn: commit fabrics config to running configuration Gabriel Goller
2025-04-04 16:28 ` [pve-devel] [PATCH pve-network v2 11/19] fabrics: generate ifupdown configuration Gabriel Goller
2025-04-04 16:28 ` [pve-devel] [PATCH pve-network v2 12/19] api: fabrics: add common helpers Gabriel Goller
2025-04-04 16:28 ` [pve-devel] [PATCH pve-network v2 13/19] api: openfabric: add api endpoints Gabriel Goller
2025-04-04 16:28 ` [pve-devel] [PATCH pve-network v2 14/19] api: openfabric: add node endpoints Gabriel Goller
2025-04-04 16:28 ` [pve-devel] [PATCH pve-network v2 15/19] api: ospf: add fabric endpoints Gabriel Goller
2025-04-04 16:28 ` [pve-devel] [PATCH pve-network v2 16/19] api: ospf: add node endpoints Gabriel Goller
2025-04-04 16:28 ` [pve-devel] [PATCH pve-network v2 17/19] api: fabrics: add module / subfolder Gabriel Goller
2025-04-04 16:28 ` [pve-devel] [PATCH pve-network v2 18/19] test: fabrics: add test cases for ospf and openfabric + evpn Gabriel Goller
2025-04-04 16:28 ` [pve-devel] [PATCH pve-network v2 19/19] frr: bump frr config version to 10.2.1 Gabriel Goller
2025-04-04 16:28 ` [pve-devel] [PATCH pve-manager v2 01/11] api: use new generalized frr and etc network config helper functions Gabriel Goller
2025-04-04 16:28 ` [pve-devel] [PATCH pve-manager v2 02/11] fabric: add common interface panel Gabriel Goller
2025-04-04 16:28 ` [pve-devel] [PATCH pve-manager v2 03/11] fabric: add OpenFabric interface properties Gabriel Goller
2025-04-04 16:28 ` [pve-devel] [PATCH pve-manager v2 04/11] fabric: add OSPF " Gabriel Goller
2025-04-04 16:29 ` [pve-devel] [PATCH pve-manager v2 05/11] fabric: add generic node edit panel Gabriel Goller
2025-04-04 16:29 ` [pve-devel] [PATCH pve-manager v2 06/11] fabric: add generic fabric " Gabriel Goller
2025-04-04 16:29 ` [pve-devel] [PATCH pve-manager v2 07/11] fabric: add OpenFabric " Gabriel Goller
2025-04-04 16:29 ` [pve-devel] [PATCH pve-manager v2 08/11] fabric: add OSPF " Gabriel Goller
2025-04-04 16:29 ` [pve-devel] [PATCH pve-manager v2 09/11] fabrics: Add main FabricView Gabriel Goller
2025-04-04 16:29 ` [pve-devel] [PATCH pve-manager v2 10/11] utils: avoid line-break in pending changes message Gabriel Goller
2025-04-04 16:29 ` [pve-devel] [PATCH pve-manager v2 11/11] ui: permissions: add ACL paths for fabrics Gabriel Goller
2025-04-04 16:29 ` [pve-devel] [PATCH pve-gui-tests v2 1/1] pve: add sdn/fabrics screenshots Gabriel Goller
2025-04-04 16:29 ` [pve-devel] [PATCH pve-docs v2 1/1] fabrics: add initial documentation for sdn fabrics Gabriel Goller
2025-04-07 8:53 ` [pve-devel] [PATCH access-control/cluster/docs/gui-tests/manager/network/proxmox{, -ve-rs, -perl-rs} v2 00/57] Add SDN Fabrics Friedrich Weber
2025-04-07 9:39 ` Stefan Hanreich
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=20250404162908.563060-16-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