all lists on 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 ve-rs 3/4] ve-config: fabrics: use new proxmox-frr structs to generate frr config
Date: Fri, 19 Sep 2025 11:41:15 +0200	[thread overview]
Message-ID: <20250919094122.73373-4-g.goller@proxmox.com> (raw)
In-Reply-To: <20250919094122.73373-1-g.goller@proxmox.com>

The proxmox-frr FrrConfig structs changed a bit so that we can serialize
them nicely using templates -- this means we also need to change the
FrrConfig generation code a bit.

Due to the decision that every protocol corresponds to a single template
file (with a few exceptions, e.g. common interface options etc.) we also
have per-protocol structs that contain all the structs needed for that
fabric to work.

So we no longer have:

config/
├─ interfaces/
│  ├─ openfabric
│  ├─ ospf
├─ routers/
│  ├─ openfabric
│  ├─ ospf

but:

config/
├─ openfabric/
│  ├─ interfaces
│  ├─ routers
├─ ospf/
│  ├─ interfaces
│  ├─ routers

Thought honestly I don't think this change is so impacting -- except a
few enums that we can remove.

Signed-off-by: Gabriel Goller <g.goller@proxmox.com>
---
 proxmox-ve-config/src/sdn/fabric/frr.rs | 145 +++++++++++++-----------
 proxmox-ve-config/src/sdn/frr.rs        |  11 +-
 2 files changed, 81 insertions(+), 75 deletions(-)

diff --git a/proxmox-ve-config/src/sdn/fabric/frr.rs b/proxmox-ve-config/src/sdn/fabric/frr.rs
index 486f7dc51dcb..86c77b8b4c57 100644
--- a/proxmox-ve-config/src/sdn/fabric/frr.rs
+++ b/proxmox-ve-config/src/sdn/fabric/frr.rs
@@ -1,17 +1,20 @@
 use std::net::{IpAddr, Ipv4Addr};
+
 use tracing;
 
-use proxmox_frr::ospf::{self, NetworkType};
+use proxmox_frr::openfabric::{OpenfabricInterface, OpenfabricRouter, OpenfabricRouterName};
+use proxmox_frr::ospf::{self, NetworkType, OspfInterface, OspfRouter, OspfRouterName};
 use proxmox_frr::route_map::{
-    AccessAction, AccessList, AccessListName, AccessListRule, ProtocolRouteMap, ProtocolType,
-    RouteMap, RouteMapMatch, RouteMapMatchInner, RouteMapName, RouteMapSet,
+    AccessAction, AccessList, AccessListName, AccessListRule, ProtocolRouteMap, RouteMap,
+    RouteMapMatch, RouteMapMatchInner, RouteMapName, RouteMapSet,
+};
+use proxmox_frr::{
+    FrrConfig, FrrWord, Interface, InterfaceName, OpenfabricFrrConfig, OspfFrrConfig,
 };
-use proxmox_frr::{FrrConfig, FrrWord, Interface, InterfaceName, Router, RouterName};
 use proxmox_network_types::ip_address::Cidr;
 use proxmox_sdn_types::net::Net;
 
 use crate::common::valid::Valid;
-
 use crate::sdn::fabric::section_config::protocol::{
     openfabric::{OpenfabricInterfaceProperties, OpenfabricProperties},
     ospf::OspfInterfaceProperties,
@@ -31,6 +34,8 @@ pub fn build_fabric(
     let mut routemap_seq = 100;
     let mut current_router_id: Option<Ipv4Addr> = None;
     let mut current_net: Option<Net> = None;
+    let mut new_openfabric_config = OpenfabricFrrConfig::default();
+    let mut new_ospf_config = OspfFrrConfig::default();
 
     for (fabric_id, entry) in config.into_inner().iter() {
         match entry {
@@ -53,7 +58,9 @@ pub fn build_fabric(
                     .as_ref()
                     .ok_or_else(|| anyhow::anyhow!("no IPv4 or IPv6 set for node"))?;
                 let (router_name, router_item) = build_openfabric_router(fabric_id, net.clone())?;
-                frr_config.router.insert(router_name, router_item);
+                new_openfabric_config
+                    .router
+                    .insert(router_name, router_item);
 
                 // Create dummy interface for fabric
                 let (interface, interface_name) = build_openfabric_dummy_interface(
@@ -62,7 +69,7 @@ pub fn build_fabric(
                     node.ip6().is_some(),
                 )?;
 
-                if frr_config
+                if new_openfabric_config
                     .interfaces
                     .insert(interface_name, interface)
                     .is_some()
@@ -83,7 +90,7 @@ pub fn build_fabric(
                         node.ip6().is_some(),
                     )?;
 
-                    if frr_config
+                    if new_openfabric_config
                         .interfaces
                         .insert(interface_name, interface)
                         .is_some()
@@ -96,11 +103,12 @@ pub fn build_fabric(
                     let rule = AccessListRule {
                         action: AccessAction::Permit,
                         network: Cidr::from(ipv4cidr),
+                        is_ipv6: false,
                         seq: None,
                     };
                     let access_list_name =
                         AccessListName::new(format!("pve_openfabric_{}_ips", fabric_id));
-                    frr_config.access_lists.push(AccessList {
+                    new_openfabric_config.access_lists.push(AccessList {
                         name: access_list_name,
                         rules: vec![rule],
                     });
@@ -109,11 +117,12 @@ pub fn build_fabric(
                     let rule = AccessListRule {
                         action: AccessAction::Permit,
                         network: Cidr::from(ipv6cidr),
+                        is_ipv6: true,
                         seq: None,
                     };
                     let access_list_name =
                         AccessListName::new(format!("pve_openfabric_{}_ip6s", fabric_id));
-                    frr_config.access_lists.push(AccessList {
+                    new_openfabric_config.access_lists.push(AccessList {
                         name: access_list_name,
                         rules: vec![rule],
                     });
@@ -121,37 +130,43 @@ pub fn build_fabric(
 
                 if let Some(ipv4) = node.ip() {
                     // create route-map
-                    frr_config.routemaps.push(build_openfabric_routemap(
-                        fabric_id,
-                        IpAddr::V4(ipv4),
-                        routemap_seq,
-                    ));
+                    new_openfabric_config
+                        .routemaps
+                        .push(build_openfabric_routemap(
+                            fabric_id,
+                            IpAddr::V4(ipv4),
+                            routemap_seq,
+                        ));
                     routemap_seq += 10;
 
                     let protocol_routemap = ProtocolRouteMap {
                         is_ipv6: false,
-                        protocol: ProtocolType::Openfabric,
                         routemap_name: RouteMapName::new("pve_openfabric".to_owned()),
                     };
 
-                    frr_config.protocol_routemaps.insert(protocol_routemap);
+                    new_openfabric_config
+                        .protocol_routemaps
+                        .insert(protocol_routemap);
                 }
                 if let Some(ipv6) = node.ip6() {
                     // create route-map
-                    frr_config.routemaps.push(build_openfabric_routemap(
-                        fabric_id,
-                        IpAddr::V6(ipv6),
-                        routemap_seq,
-                    ));
+                    new_openfabric_config
+                        .routemaps
+                        .push(build_openfabric_routemap(
+                            fabric_id,
+                            IpAddr::V6(ipv6),
+                            routemap_seq,
+                        ));
                     routemap_seq += 10;
 
                     let protocol_routemap = ProtocolRouteMap {
                         is_ipv6: true,
-                        protocol: ProtocolType::Openfabric,
                         routemap_name: RouteMapName::new("pve_openfabric6".to_owned()),
                     };
 
-                    frr_config.protocol_routemaps.insert(protocol_routemap);
+                    new_openfabric_config
+                        .protocol_routemaps
+                        .insert(protocol_routemap);
                 }
             }
             FabricEntry::Ospf(ospf_entry) => {
@@ -167,13 +182,13 @@ pub fn build_fabric(
                 let frr_word_area = FrrWord::new(fabric.properties().area.to_string())?;
                 let frr_area = ospf::Area::new(frr_word_area)?;
                 let (router_name, router_item) = build_ospf_router(*router_id)?;
-                frr_config.router.insert(router_name, router_item);
+                new_ospf_config.router.insert(router_name, router_item);
 
                 // Add dummy interface
                 let (interface, interface_name) =
                     build_ospf_dummy_interface(fabric_id, frr_area.clone())?;
 
-                if frr_config
+                if new_ospf_config
                     .interfaces
                     .insert(interface_name, interface)
                     .is_some()
@@ -187,7 +202,7 @@ pub fn build_fabric(
                     let (interface, interface_name) =
                         build_ospf_interface(frr_area.clone(), interface)?;
 
-                    if frr_config
+                    if new_ospf_config
                         .interfaces
                         .insert(interface_name, interface)
                         .is_some()
@@ -203,10 +218,11 @@ pub fn build_fabric(
                     network: Cidr::from(
                         fabric.ip_prefix().expect("fabric must have a ipv4 prefix"),
                     ),
+                    is_ipv6: false,
                     seq: None,
                 };
 
-                frr_config.access_lists.push(AccessList {
+                new_ospf_config.access_lists.push(AccessList {
                     name: access_list_name,
                     rules: vec![rule],
                 });
@@ -218,26 +234,26 @@ pub fn build_fabric(
                 )?;
 
                 routemap_seq += 10;
-                frr_config.routemaps.push(routemap);
+                new_ospf_config.routemaps.push(routemap);
 
                 let protocol_routemap = ProtocolRouteMap {
                     is_ipv6: false,
-                    protocol: ProtocolType::Ospf,
                     routemap_name: RouteMapName::new("pve_ospf".to_owned()),
                 };
 
-                frr_config.protocol_routemaps.insert(protocol_routemap);
+                new_ospf_config.protocol_routemaps.insert(protocol_routemap);
             }
         }
     }
+    frr_config.ospf = new_ospf_config;
+    frr_config.openfabric = new_openfabric_config;
     Ok(())
 }
 
 /// Helper that builds a OSPF router with a the router_id.
-fn build_ospf_router(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 router_name = RouterName::Ospf(proxmox_frr::ospf::OspfRouterName);
+fn build_ospf_router(router_id: Ipv4Addr) -> Result<(OspfRouterName, OspfRouter), anyhow::Error> {
+    let router_item = proxmox_frr::ospf::OspfRouter { router_id };
+    let router_name = proxmox_frr::ospf::OspfRouterName;
     Ok((router_name, router_item))
 }
 
@@ -245,11 +261,10 @@ fn build_ospf_router(router_id: Ipv4Addr) -> Result<(RouterName, Router), anyhow
 fn build_openfabric_router(
     fabric_id: &FabricId,
     net: Net,
-) -> Result<(RouterName, Router), anyhow::Error> {
-    let ofr = proxmox_frr::openfabric::OpenfabricRouter { net };
-    let router_item = Router::Openfabric(ofr);
+) -> Result<(OpenfabricRouterName, OpenfabricRouter), anyhow::Error> {
+    let router_item = proxmox_frr::openfabric::OpenfabricRouter { net };
     let frr_word_id = FrrWord::new(fabric_id.to_string())?;
-    let router_name = RouterName::Openfabric(frr_word_id.into());
+    let router_name = frr_word_id.into();
     Ok((router_name, router_item))
 }
 
@@ -257,10 +272,10 @@ fn build_openfabric_router(
 fn build_ospf_interface(
     area: ospf::Area,
     interface: &OspfInterfaceProperties,
-) -> Result<(Interface, InterfaceName), anyhow::Error> {
+) -> Result<(Interface<OspfInterface>, InterfaceName), anyhow::Error> {
     let frr_interface = proxmox_frr::ospf::OspfInterface {
         area,
-        // Interfaces are always none-passive
+        // Interfaces are always non-passive
         passive: None,
         network_type: if interface.ip.is_some() {
             None
@@ -269,7 +284,7 @@ fn build_ospf_interface(
         },
     };
 
-    let interface_name = InterfaceName::Ospf(interface.name.as_str().try_into()?);
+    let interface_name = interface.name.as_str().try_into()?;
     Ok((frr_interface.into(), interface_name))
 }
 
@@ -277,13 +292,12 @@ fn build_ospf_interface(
 fn build_ospf_dummy_interface(
     fabric_id: &FabricId,
     area: ospf::Area,
-) -> Result<(Interface, InterfaceName), anyhow::Error> {
-    let frr_interface = proxmox_frr::ospf::OspfInterface {
-        area,
-        passive: Some(true),
-        network_type: None,
-    };
-    let interface_name = InterfaceName::Openfabric(format!("dummy_{}", fabric_id).try_into()?);
+) -> Result<(Interface<OspfInterface>, InterfaceName), anyhow::Error> {
+    let frr_interface = proxmox_frr::ospf::OspfInterface::builder()
+        .area(area)
+        .passive(true)
+        .build();
+    let interface_name = format!("dummy_{}", fabric_id).try_into()?;
     Ok((frr_interface.into(), interface_name))
 }
 
@@ -297,7 +311,7 @@ fn build_openfabric_interface(
     fabric_config: &OpenfabricProperties,
     is_ipv4: bool,
     is_ipv6: bool,
-) -> Result<(Interface, InterfaceName), anyhow::Error> {
+) -> Result<(Interface<OpenfabricInterface>, InterfaceName), anyhow::Error> {
     let frr_word = FrrWord::new(fabric_id.to_string())?;
     let mut frr_interface = proxmox_frr::openfabric::OpenfabricInterface {
         fabric_id: frr_word.into(),
@@ -315,7 +329,7 @@ fn build_openfabric_interface(
     if frr_interface.hello_interval.is_none() {
         frr_interface.hello_interval = fabric_config.hello_interval;
     }
-    let interface_name = InterfaceName::Openfabric(interface.name.as_str().try_into()?);
+    let interface_name = interface.name.as_str().try_into()?;
     Ok((frr_interface.into(), interface_name))
 }
 
@@ -324,18 +338,15 @@ fn build_openfabric_dummy_interface(
     fabric_id: &FabricId,
     is_ipv4: bool,
     is_ipv6: bool,
-) -> Result<(Interface, InterfaceName), anyhow::Error> {
+) -> Result<(Interface<OpenfabricInterface>, 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_ipv4,
-        is_ipv6,
-    };
-    let interface_name = InterfaceName::Openfabric(format!("dummy_{}", fabric_id).try_into()?);
+    let frr_interface = proxmox_frr::openfabric::OpenfabricInterface::builder()
+        .fabric_id(frr_word.into())
+        .passive(true)
+        .is_ipv4(is_ipv4)
+        .is_ipv6(is_ipv6)
+        .build();
+    let interface_name = format!("dummy_{}", fabric_id).try_into()?;
     Ok((frr_interface.into(), interface_name))
 }
 
@@ -350,14 +361,14 @@ fn build_openfabric_routemap(fabric_id: &FabricId, router_ip: IpAddr, seq: u32)
         seq,
         action: AccessAction::Permit,
         matches: vec![match router_ip {
-            IpAddr::V4(_) => RouteMapMatch::V4(RouteMapMatchInner::IpAddress(AccessListName::new(
+            IpAddr::V4(_) => RouteMapMatch::V4(RouteMapMatchInner::Address(AccessListName::new(
                 format!("pve_openfabric_{fabric_id}_ips"),
             ))),
-            IpAddr::V6(_) => RouteMapMatch::V6(RouteMapMatchInner::IpAddress(AccessListName::new(
+            IpAddr::V6(_) => RouteMapMatch::V6(RouteMapMatchInner::Address(AccessListName::new(
                 format!("pve_openfabric_{fabric_id}_ip6s"),
             ))),
         }],
-        sets: vec![RouteMapSet::IpSrc(router_ip)],
+        sets: vec![RouteMapSet::Src(router_ip)],
     }
 }
 
@@ -373,10 +384,10 @@ fn build_ospf_dummy_routemap(
         name: routemap_name.clone(),
         seq,
         action: AccessAction::Permit,
-        matches: vec![RouteMapMatch::V4(RouteMapMatchInner::IpAddress(
+        matches: vec![RouteMapMatch::V4(RouteMapMatchInner::Address(
             AccessListName::new(format!("pve_ospf_{fabric_id}_ips")),
         ))],
-        sets: vec![RouteMapSet::IpSrc(IpAddr::from(router_ip))],
+        sets: vec![RouteMapSet::Src(IpAddr::from(router_ip))],
     };
 
     Ok(routemap)
diff --git a/proxmox-ve-config/src/sdn/frr.rs b/proxmox-ve-config/src/sdn/frr.rs
index f7929c1f6c16..bc98440b9912 100644
--- a/proxmox-ve-config/src/sdn/frr.rs
+++ b/proxmox-ve-config/src/sdn/frr.rs
@@ -1,6 +1,4 @@
-use std::collections::{BTreeMap, BTreeSet};
-
-use proxmox_frr::FrrConfig;
+use proxmox_frr::{FrrConfig, OpenfabricFrrConfig, OspfFrrConfig};
 
 use crate::common::valid::Valid;
 use crate::sdn::fabric::{section_config::node::NodeId, FabricConfig};
@@ -28,11 +26,8 @@ impl FrrConfigBuilder {
     /// interface if there isn't a more specific one.)
     pub fn build(self, current_node: NodeId) -> Result<FrrConfig, anyhow::Error> {
         let mut frr_config = FrrConfig {
-            router: BTreeMap::new(),
-            interfaces: BTreeMap::new(),
-            access_lists: Vec::new(),
-            routemaps: Vec::new(),
-            protocol_routemaps: BTreeSet::new(),
+            openfabric: OpenfabricFrrConfig::default(),
+            ospf: OspfFrrConfig::default(),
         };
 
         crate::sdn::fabric::frr::build_fabric(current_node, self.fabrics, &mut frr_config)?;
-- 
2.47.3



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

  parent reply	other threads:[~2025-09-19  9:41 UTC|newest]

Thread overview: 9+ messages / expand[flat|nested]  mbox.gz  Atom feed  top
2025-09-19  9:41 [pve-devel] [RFC network/ve-rs 0/8] Template-based FRR config generation Gabriel Goller
2025-09-19  9:41 ` [pve-devel] [PATCH ve-rs 1/4] frr: add templates and structs to represent the frr config Gabriel Goller
2025-09-19  9:41 ` [pve-devel] [PATCH ve-rs 2/4] sdn-types: forward serialize to display for NET Gabriel Goller
2025-09-19  9:41 ` Gabriel Goller [this message]
2025-09-19  9:41 ` [pve-devel] [PATCH ve-rs 4/4] tests: always prepend the frr delimiter/comment "!" to the block Gabriel Goller
2025-09-19  9:41 ` [pve-devel] [PATCH network 1/4] sdn: remove duplicate comment line '!' in frr config Gabriel Goller
2025-09-19  9:41 ` [pve-devel] [PATCH network 2/4] sdn: add trailing newline " Gabriel Goller
2025-09-19  9:41 ` [pve-devel] [PATCH network 3/4] sdn: tests: add missing comment '!' " Gabriel Goller
2025-09-19  9:41 ` [pve-devel] [PATCH network 4/4] tests: use Test::Differences to make test assertions 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=20250919094122.73373-4-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 an external index of several public inboxes,
see mirroring instructions on how to clone and mirror
all data and code used by this external index.
Service provided by Proxmox Server Solutions GmbH | Privacy | Legal