Thanks for tackling this so quickly! nit: a subject-prefix for the repository is missing.
As I noticed the messages from pvestatd in my journal - I gave this a quick spin - it does fix the ``` Nov 14 11:36:26 rosa pvestatd[4474]: network status update error: error parsing running-config: missing field `fabrics` at line 1 column 97 ``` logspam for me. I quickly looked through the code and to me it looks like it does what it says on the tin. consider this: Tested-by: Stoiko Ivanov <[email protected]> Reviewed-by: Stoiko Ivanov <[email protected]> On Fri, 14 Nov 2025 11:30:09 +0100 Gabriel Goller <[email protected]> wrote: > 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 <[email protected]> > Reported-by: Stoiko Ivanov <[email protected]> > Signed-off-by: Gabriel Goller <[email protected]> > --- > 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") > } _______________________________________________ pve-devel mailing list [email protected] https://lists.proxmox.com/cgi-bin/mailman/listinfo/pve-devel
