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