public inbox for pve-devel@lists.proxmox.com
 help / color / mirror / Atom feed
From: Wolfgang Bumiller <w.bumiller@proxmox.com>
To: Gabriel Goller <g.goller@proxmox.com>
Cc: pve-devel@lists.proxmox.com
Subject: Re: [pve-devel] [PATCH proxmox-perl-rs v2 4/4] fabrics: add function to get all neighbors of the fabric
Date: Mon, 25 Aug 2025 10:28:20 +0200	[thread overview]
Message-ID: <qxosl7ui5ddtkmdyphgngn7kba6asel2fywe7eowuylv5rqeex@phffxok5y2vh> (raw)
In-Reply-To: <20250822090102.102949-7-g.goller@proxmox.com>

On Fri, Aug 22, 2025 at 11:00:38AM +0200, Gabriel Goller wrote:
> In order to also display the neighbors of a specific node in the
> FabricContentView resource window get the Neighbors of the all the
> fabrics. Query frr (vtysh) to get the neighbors of both openefabric and
> ospf, parse it and then compile a array containing all neighbors and
> the fabric it relates to.
> 
> Signed-off-by: Gabriel Goller <g.goller@proxmox.com>
> ---
>  pve-rs/src/bindings/sdn/fabrics.rs | 152 +++++++++++++++++++++++++++++
>  1 file changed, 152 insertions(+)
> 
> diff --git a/pve-rs/src/bindings/sdn/fabrics.rs b/pve-rs/src/bindings/sdn/fabrics.rs
> index f1addd4364d2..c033a4072685 100644
> --- a/pve-rs/src/bindings/sdn/fabrics.rs
> +++ b/pve-rs/src/bindings/sdn/fabrics.rs
> @@ -594,6 +594,18 @@ pub mod pve_rs_sdn_fabrics {
>              section_config::{fabric::FabricId, node::Node as ConfigNode},
>          };
>  
> +        /// The status of a neighbor.
> +        ///
> +        /// Contains the neighbor, the fabric and protocol it belongs to and the some status
> +        /// information.
> +        #[derive(Debug, Serialize)]
> +        pub struct NeighborStatus {
> +            neighbor: String,
> +            status: String,
> +            fabric_id: FabricId,
> +            protocol: Protocol,
> +        }
> +
>          /// The status of a route.
>          ///
>          /// Contains the route, the fabric and protocol it belongs to and some extra nexthop
> @@ -651,6 +663,19 @@ pub mod pve_rs_sdn_fabrics {
>              pub ospf: de::Routes,
>          }
>  
> +        /// Parsed neighbors for all protocols
> +        ///
> +        /// These are the neighbors parsed from the json output of:
> +        /// `vtysh -c 'show openfabric neighbor json'` and
> +        /// `vtysh -c 'show ip ospf neighbor json'`.
> +        #[derive(Debug, Serialize)]
> +        pub struct NeighborsParsed {
> +            /// The openfabric neighbors in FRR
> +            pub openfabric: de::openfabric::Neighbors,
> +            /// The ospf neighbors in FRR
> +            pub ospf: de::ospf::Neighbors,
> +        }
> +
>          impl TryInto<Vec<RouteStatus>> for RoutesParsed {
>              type Error = anyhow::Error;
>  
> @@ -739,6 +764,90 @@ pub mod pve_rs_sdn_fabrics {
>              }
>          }
>  
> +        impl TryInto<Vec<NeighborStatus>> for NeighborsParsed {
> +            type Error = anyhow::Error;
> +
> +            fn try_into(self) -> Result<Vec<NeighborStatus>, Self::Error> {
> +                let hostname = proxmox_sys::nodename();
> +
> +                // get all nodes
> +                let raw_config = std::fs::read_to_string("/etc/pve/sdn/fabrics.cfg")?;

^ Same as the other patches.

> +                let config = FabricConfig::parse_section_config(&raw_config)?;
> +
> +                let mut stats: Vec<NeighborStatus> = Vec::new();
> +
> +                for (nodeid, node) in config.values().flat_map(|entry| {
> +                    entry
> +                        .nodes()
> +                        .map(|(id, node)| (id.to_string(), node.clone()))

^ ...
I'm sensing pattern here.

Would it make sense to add a `FabricConfig::all_nodes(&self) -> impl Iterator<...>` ?

> +                }) {
> +                    if nodeid != hostname {
> +                        continue;
> +                    }
> +                    let fabric_id = node.id().fabric_id().clone();

^ unnecessary clone

> +
> +                    match node {
> +                        ConfigNode::Openfabric(_) => {
> +                            for area in &self.openfabric.areas {
> +                                if area.area == fabric_id.as_str() {
> +                                    for circuit in &area.circuits {
> +                                        if let (Some(adj), Some(state)) =
> +                                            (&circuit.adj, &circuit.state)
> +                                        {
> +                                            stats.push(NeighborStatus {
> +                                                neighbor: adj.clone(),
> +                                                status: state.clone(),
> +                                                protocol: Protocol::Openfabric,
> +                                                fabric_id: fabric_id.clone(),
> +                                            });
> +                                        }
> +                                    }
> +                                }
> +                            }
> +                        }
> +                        ConfigNode::Ospf(node) => {
> +                            let interface_names: HashSet<&str> = node
> +                                .properties()
> +                                .interfaces()
> +                                .map(|i| i.name().as_str())
> +                                .collect();
> +
> +                            for (neighbor_key, neighbor_list) in &self.ospf.neighbors {
> +                                let mut has_matching_neighbor = false;
> +                                for neighbor in neighbor_list {
> +                                    match neighbor.interface_name.split_once(":") {
> +                                        Some((interface_name, _)) => {
> +                                            if interface_names.contains(interface_name) {
> +                                                has_matching_neighbor = true;
> +                                                break;
> +                                            }
> +                                        }
> +                                        _ => {
> +                                            continue;
> +                                        }
> +                                    }
> +                                }
> +                                if has_matching_neighbor {
> +                                    let status = neighbor_list
> +                                        .first()
> +                                        .map(|n| n.neighbor_state.clone())
> +                                        .unwrap_or_default();
> +                                    stats.push(NeighborStatus {
> +                                        neighbor: neighbor_key.clone(),
> +                                        status,
> +                                        protocol: Protocol::Ospf,
> +                                        fabric_id: fabric_id.clone(),
> +                                    });
> +                                }
> +                            }
> +                        }
> +                    }
> +                }
> +
> +                Ok(stats)
> +            }
> +        }
> +
>          impl TryInto<HashMap<FabricId, Status>> for RoutesParsed {
>              type Error = anyhow::Error;
>  
> @@ -873,6 +982,49 @@ pub mod pve_rs_sdn_fabrics {
>          route_status.try_into()
>      }
>  
> +    /// Get all the neighbors of all the fabrics on this node.
> +    ///
> +    /// Go through all fabrics that exist on this node. Then get the neighbors of them all and
> +    /// concat them into a single array.
> +    #[export]
> +    fn neighbors() -> Result<Vec<status::NeighborStatus>, Error> {
> +        let openfabric_neighbors_string = String::from_utf8(
> +            Command::new("sh")
> +                .args(["-c", "vtysh -c 'show openfabric neighbor json'"])
> +                .output()?
> +                .stdout,
> +        )?;
> +
> +        let ospf_neighbors_string = String::from_utf8(
> +            Command::new("sh")
> +                .args(["-c", "vtysh -c 'show ip ospf neighbor json'"])
> +                .output()?
> +                .stdout,
> +        )?;
> +
> +        let openfabric_neighbors: proxmox_frr::de::openfabric::Neighbors =
> +            if openfabric_neighbors_string.is_empty() {
> +                proxmox_frr::de::openfabric::Neighbors::default()
> +            } else {
> +                serde_json::from_str(&openfabric_neighbors_string)
> +                    .with_context(|| "error parsing openfabric neighbors")?
> +            };
> +
> +        let ospf_neighbors: proxmox_frr::de::ospf::Neighbors = if ospf_neighbors_string.is_empty() {
> +            proxmox_frr::de::ospf::Neighbors::default()
> +        } else {
> +            serde_json::from_str(&ospf_neighbors_string)
> +                .with_context(|| "error parsing ospf neighbors")?
> +        };
> +
> +        let neighbor_status = status::NeighborsParsed {
> +            openfabric: openfabric_neighbors,
> +            ospf: ospf_neighbors,
> +        };
> +
> +        neighbor_status.try_into()
> +    }
> +
>      /// Return the status of all fabrics on this node.
>      ///
>      /// Go through all fabrics in the config, then filter out the ones that exist on this node.
> -- 
> 2.47.2


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


  reply	other threads:[~2025-08-25  8:28 UTC|newest]

Thread overview: 25+ messages / expand[flat|nested]  mbox.gz  Atom feed  top
2025-08-22  9:00 [pve-devel] [PATCH manager/network/proxmox{-ve-rs, -perl-rs} v2 00/12] Add fabric status view Gabriel Goller
2025-08-22  9:00 ` [pve-devel] [PATCH proxmox-ve-rs v2 1/2] frr: make room for deserialization structs Gabriel Goller
2025-08-22  9:00 ` [pve-devel] [PATCH proxmox-ve-rs v2 2/2] frr: add deserialization types for openfabric and ospf Gabriel Goller
2025-08-22  9:00 ` [pve-devel] [PATCH proxmox-perl-rs v2 1/4] pve: fabrics: update proxmox-frr import path Gabriel Goller
2025-08-22  9:00 ` [pve-devel] [PATCH proxmox-perl-rs v2 2/4] fabrics: add function to get status of fabric Gabriel Goller
2025-08-25  8:11   ` Wolfgang Bumiller
2025-08-25  8:25     ` Wolfgang Bumiller
2025-08-25 11:39     ` Gabriel Goller
2025-08-25 14:37       ` Wolfgang Bumiller
2025-08-25 15:33         ` Gabriel Goller
2025-08-26  7:55           ` Wolfgang Bumiller
2025-08-26  8:29             ` Gabriel Goller
2025-08-22  9:00 ` [pve-devel] [PATCH proxmox-perl-rs v2 3/4] fabrics: add function to get all routes distributed by the fabrics Gabriel Goller
2025-08-25  8:22   ` Wolfgang Bumiller
2025-08-25 11:40     ` Gabriel Goller
2025-08-22  9:00 ` [pve-devel] [PATCH proxmox-perl-rs v2 4/4] fabrics: add function to get all neighbors of the fabric Gabriel Goller
2025-08-25  8:28   ` Wolfgang Bumiller [this message]
2025-08-25 11:41     ` Gabriel Goller
2025-08-22  9:00 ` [pve-devel] [PATCH pve-network v2 1/3] fabrics: add fabrics status to SDN::status function Gabriel Goller
2025-08-22  9:00 ` [pve-devel] [PATCH pve-network v2 2/3] fabrics: add api endpoint to return fabrics routes Gabriel Goller
2025-08-22  9:00 ` [pve-devel] [PATCH pve-network v2 3/3] fabrics: add api endpoint to return fabric neighbors Gabriel Goller
2025-08-22  9:00 ` [pve-devel] [PATCH pve-manager v2 1/3] pvestatd: add fabrics status to pvestatd Gabriel Goller
2025-08-22  9:00 ` [pve-devel] [PATCH pve-manager v2 2/3] fabrics: add resource view for fabrics Gabriel Goller
2025-08-22  9:00 ` [pve-devel] [PATCH pve-manager v2 3/3] permissions: differentiate between zone and fabric paths Gabriel Goller
2025-08-26  9:52 ` [pve-devel] [PATCH manager/network/proxmox{-ve-rs, -perl-rs} v2 00/12] Add fabric status view 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=qxosl7ui5ddtkmdyphgngn7kba6asel2fywe7eowuylv5rqeex@phffxok5y2vh \
    --to=w.bumiller@proxmox.com \
    --cc=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