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 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 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