[pve-devel] [PATCH] pve-rs: fabrics: handle missing running-config and no fabrics config
Gabriel Goller
g.goller at proxmox.com
Fri Nov 14 11:30:09 CET 2025
Correctly handle if there is a missing `/etc/pve/sdn/.running-config`
file. This happens, e.g. on a clean installation without any sdn "apply"
activation. Also handle a config where there is no fabric configured.
The `status()` functions gets called every few seconds to populate the
network resources in pve, so don't return an error if there is no
running-config or no fabric config. The other functions (neighbors,
interfaces, routes) only get called by the api, so we can return an
error on those.
Reported-by: Thomas Lamprecht <t.lamprecht at proxmox.com>
Reported-by: Stoiko Ivanov <s.ivanov at proxmox.com>
Signed-off-by: Gabriel Goller <g.goller at proxmox.com>
---
pve-rs/src/bindings/sdn/fabrics.rs | 36 ++++++++++++++++++++++++------
pve-rs/src/sdn/status.rs | 10 ++++++---
2 files changed, 36 insertions(+), 10 deletions(-)
diff --git a/pve-rs/src/bindings/sdn/fabrics.rs b/pve-rs/src/bindings/sdn/fabrics.rs
index c9e5cf526c77..54606e7b6bb9 100644
--- a/pve-rs/src/bindings/sdn/fabrics.rs
+++ b/pve-rs/src/bindings/sdn/fabrics.rs
@@ -595,14 +595,21 @@ pub mod pve_rs_sdn_fabrics {
/// Read and parse the running-config and get the fabrics section
///
/// This will return a valid FabricConfig. Note that we read the file manually and not through
- /// the cluster filesystem as with perl, so this will be slower.
- fn get_fabrics_config() -> Result<Valid<FabricConfig>, anyhow::Error> {
+ /// the cluster filesystem as with perl, so this will be slower. If the running config exists,
+ /// but no fabric is configured, `Ok(None)` is returned.
+ fn get_fabrics_config() -> Result<Option<Valid<FabricConfig>>, anyhow::Error> {
let raw_config = std::fs::read_to_string("/etc/pve/sdn/.running-config")?;
let running_config: RunningConfig =
serde_json::from_str(&raw_config).with_context(|| "error parsing running-config")?;
- let section_config = SectionConfigData::from_iter(running_config.fabrics.ids);
- FabricConfig::from_section_config(section_config)
- .with_context(|| "error converting section config to fabricconfig")
+ let Some(fabrics) = running_config.fabrics else {
+ return Ok(None);
+ };
+ let section_config = SectionConfigData::from_iter(fabrics.ids);
+ Some(
+ FabricConfig::from_section_config(section_config)
+ .with_context(|| "error converting section config to fabricconfig"),
+ )
+ .transpose()
}
/// Get the routes that have been learned and distributed by this specific fabric on this node.
@@ -614,6 +621,9 @@ pub mod pve_rs_sdn_fabrics {
fn routes(fabric_id: FabricId) -> Result<Vec<status::RouteStatus>, Error> {
// Read fabric config to get protocol of fabric
let config = get_fabrics_config()?;
+ let Some(config) = config else {
+ anyhow::bail!("no fabrics configured");
+ };
let fabric = config.get_fabric(&fabric_id)?;
match fabric {
@@ -679,6 +689,9 @@ pub mod pve_rs_sdn_fabrics {
fn neighbors(fabric_id: FabricId) -> Result<status::NeighborStatus, Error> {
// Read fabric config to get protocol of fabric
let config = get_fabrics_config()?;
+ let Some(config) = config else {
+ anyhow::bail!("no fabrics configured");
+ };
let fabric = config.get_fabric(&fabric_id)?;
@@ -734,6 +747,9 @@ pub mod pve_rs_sdn_fabrics {
fn interfaces(fabric_id: FabricId) -> Result<status::InterfaceStatus, Error> {
// Read fabric config to get protocol of fabric
let config = get_fabrics_config()?;
+ let Some(config) = config else {
+ anyhow::bail!("no fabrics configured");
+ };
let fabric = config.get_fabric(&fabric_id)?;
@@ -789,6 +805,14 @@ pub mod pve_rs_sdn_fabrics {
/// config. If there are, show "ok" as status, otherwise "not ok".
#[export]
fn status() -> Result<HashMap<FabricId, status::Status>, Error> {
+ // This gets called every few seconds from pve to get the status of the fabrics into the
+ // network resources. It is possible that the .running-config doesn't exist or no fabric is
+ // configured -- in that case just return nothing.
+ let config = get_fabrics_config();
+ let Ok(Some(config)) = config else {
+ return Ok(HashMap::new());
+ };
+
let openfabric_ipv4_routes_string = String::from_utf8(
Command::new("sh")
.args(["-c", "vtysh -c 'show ip route openfabric json'"])
@@ -831,8 +855,6 @@ pub mod pve_rs_sdn_fabrics {
.with_context(|| "error parsing ospf routes")?
};
- let config = get_fabrics_config()?;
-
let route_status = status::RoutesParsed {
openfabric: openfabric_routes,
ospf: ospf_routes,
diff --git a/pve-rs/src/sdn/status.rs b/pve-rs/src/sdn/status.rs
index 78b55502b837..e1e336297ac9 100644
--- a/pve-rs/src/sdn/status.rs
+++ b/pve-rs/src/sdn/status.rs
@@ -178,7 +178,7 @@ pub struct RoutesParsed {
/// Config used to parse the fabric part of the running-config
#[derive(Deserialize)]
pub struct RunningConfig {
- pub fabrics: FabricsRunningConfig,
+ pub fabrics: Option<FabricsRunningConfig>,
}
/// Map of ids for all the fabrics in the running-config
@@ -647,7 +647,9 @@ mod tests {
let running_config: RunningConfig =
serde_json::from_str(raw_config).expect("error parsing running-config");
- let section_config = SectionConfigData::from_iter(running_config.fabrics.ids);
+ let section_config = SectionConfigData::from_iter(
+ running_config.fabrics.expect("no fabrics configured").ids,
+ );
FabricConfig::from_section_config(section_config)
.expect("error converting section config to fabricconfig")
}
@@ -695,7 +697,9 @@ mod tests {
"#;
let running_config: RunningConfig =
serde_json::from_str(raw_config).expect("error parsing running-config");
- let section_config = SectionConfigData::from_iter(running_config.fabrics.ids);
+ let section_config = SectionConfigData::from_iter(
+ running_config.fabrics.expect("no fabrics configured").ids,
+ );
FabricConfig::from_section_config(section_config)
.expect("error converting section config to fabricconfig")
}
--
2.47.3
More information about the pve-devel
mailing list