all lists on 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 proxmox-ve-rs v2 2/2] frr: add deserialization types for openfabric and ospf
Date: Fri, 22 Aug 2025 11:00:34 +0200	[thread overview]
Message-ID: <20250822090102.102949-3-g.goller@proxmox.com> (raw)
In-Reply-To: <20250822090102.102949-1-g.goller@proxmox.com>

These are used to deserialize `vtysh` command outputs. The output is in
json if the `json` parameter is appended to the command. Currently the
following commands are parsed:

 * show openfabric neighbor
 * show ip ospf neighbor
 * show ip route <protocol>

Signed-off-by: Gabriel Goller <g.goller@proxmox.com>
---
 proxmox-frr/Cargo.toml           |   1 +
 proxmox-frr/debian/control       |   4 ++
 proxmox-frr/src/de/mod.rs        | 104 +++++++++++++++++++++++++++++++
 proxmox-frr/src/de/openfabric.rs |  42 +++++++++++++
 proxmox-frr/src/de/ospf.rs       |  57 +++++++++++++++++
 proxmox-frr/src/lib.rs           |   1 +
 6 files changed, 209 insertions(+)
 create mode 100644 proxmox-frr/src/de/mod.rs
 create mode 100644 proxmox-frr/src/de/openfabric.rs
 create mode 100644 proxmox-frr/src/de/ospf.rs

diff --git a/proxmox-frr/Cargo.toml b/proxmox-frr/Cargo.toml
index 47fb8bb3969c..d1a24a899b55 100644
--- a/proxmox-frr/Cargo.toml
+++ b/proxmox-frr/Cargo.toml
@@ -13,6 +13,7 @@ rust-version.workspace = true
 thiserror = { workspace = true }
 anyhow = "1"
 tracing = "0.1"
+serde = { workspace = true, features = [ "derive" ] }
 
 proxmox-network-types = { workspace = true }
 proxmox-sdn-types = { workspace = true }
diff --git a/proxmox-frr/debian/control b/proxmox-frr/debian/control
index 544fc3ec9ec4..aa74860f2b2f 100644
--- a/proxmox-frr/debian/control
+++ b/proxmox-frr/debian/control
@@ -9,6 +9,8 @@ Build-Depends-Arch: cargo:native <!nocheck>,
  librust-anyhow-1+default-dev <!nocheck>,
  librust-proxmox-network-types-0.1+default-dev (>= 0.1.1-~~) <!nocheck>,
  librust-proxmox-sdn-types-0.1+default-dev <!nocheck>,
+ librust-serde-1+default-dev <!nocheck>,
+ librust-serde-1+derive-dev <!nocheck>,
  librust-thiserror-2+default-dev <!nocheck>,
  librust-tracing-0.1+default-dev <!nocheck>
 Maintainer: Proxmox Support Team <support@proxmox.com>
@@ -27,6 +29,8 @@ Depends:
  librust-anyhow-1+default-dev,
  librust-proxmox-network-types-0.1+default-dev (>= 0.1.1-~~),
  librust-proxmox-sdn-types-0.1+default-dev,
+ librust-serde-1+default-dev,
+ librust-serde-1+derive-dev,
  librust-thiserror-2+default-dev,
  librust-tracing-0.1+default-dev
 Provides:
diff --git a/proxmox-frr/src/de/mod.rs b/proxmox-frr/src/de/mod.rs
new file mode 100644
index 000000000000..43890506253d
--- /dev/null
+++ b/proxmox-frr/src/de/mod.rs
@@ -0,0 +1,104 @@
+use std::{collections::HashMap, net::IpAddr};
+
+use proxmox_network_types::ip_address::Cidr;
+use serde::{Deserialize, Serialize};
+
+pub mod openfabric;
+pub mod ospf;
+
+/// A nexthop of a route
+#[derive(Debug, Serialize, Deserialize, Clone)]
+pub struct NextHop {
+    /// Flags
+    pub flags: i32,
+    /// If the route is in the FIB (Forward Information Base)
+    pub fib: Option<bool>,
+    /// IP of the nexthop
+    pub ip: Option<IpAddr>,
+    /// AFI (either IPv4, IPv6 or something else)
+    pub afi: String,
+    /// Index of the outgoing interface
+    #[serde(rename = "interfaceIndex")]
+    pub interface_index: i32,
+    #[serde(rename = "interfaceName")]
+    /// Name of the outgoing interface
+    pub interface_name: String,
+    /// If the nexthop is active
+    pub active: bool,
+    /// If the route has the onlink flag. Onlink means that we pretend that the nexthop is
+    /// directly attached to this link, even if it does not match any interface prefix.
+    #[serde(rename = "onLink")]
+    pub on_link: bool,
+    /// Remap-Source, this rewrites the source address to the following address, if this
+    /// nexthop is used.
+    #[serde(rename = "rmapSource")]
+    pub remap_source: Option<IpAddr>,
+    /// Weight of the nexthop
+    pub weight: i32,
+}
+
+/// route
+#[derive(Debug, Serialize, Deserialize, Clone)]
+pub struct Route {
+    /// Prefix of the route
+    pub prefix: Cidr,
+    /// Prefix Length
+    #[serde(rename = "prefixLen")]
+    pub prefix_len: u32,
+    /// Protocol from which the route originates
+    pub protocol: String,
+    /// VRF id
+    #[serde(rename = "vrfId")]
+    pub vrf_id: u32,
+    /// VRF name
+    #[serde(rename = "vrfName")]
+    pub vrf_name: String,
+    /// If the route has been selected (if multiple of the same routes from different
+    /// daemons exist, the one with the shortest distance is selected).
+    pub selected: Option<bool>,
+    /// Destination Selected
+    #[serde(rename = "destSelected")]
+    pub destination_selected: Option<bool>,
+    /// Distance of the route
+    pub distance: Option<i32>,
+    /// Metric of the route
+    pub metric: i32,
+    /// If the route is installed in the kernel routing table
+    pub installed: Option<bool>,
+    /// The id of the routing table
+    pub table: i32,
+    /// Internal Status
+    #[serde(rename = "internalStatus")]
+    pub internal_status: i32,
+    /// Internal Flags
+    #[serde(rename = "internalFlags")]
+    pub internal_flags: i32,
+    /// Internal Nexthop Num, this is the id to lookup the nexthop (visible in e.g. `ip
+    /// nexthop ls`).
+    #[serde(rename = "internalNextHopNum")]
+    pub internal_nexthop_num: i32,
+    /// Internal Nexthop Active Num
+    #[serde(rename = "internalNextHopActiveNum")]
+    pub internal_nexthop_active_num: i32,
+    /// Nexthop Group Id
+    #[serde(rename = "nexthopGroupId")]
+    pub nexthop_group_id: i32,
+    /// Installed Nexthop Group Id
+    #[serde(rename = "installedNexthopGroupId")]
+    pub installed_nexthop_group_id: Option<i32>,
+    /// The uptime of the route
+    pub uptime: String,
+
+    /// Array of all the nexthops associated with this route. When you have e.g. two
+    /// connections between two nodes, there is going to be one route, but two nexthops.
+    pub nexthops: Vec<NextHop>,
+}
+
+/// Struct to parse zebra routes by FRR.
+///
+/// To get the routes from FRR, instead of asking the daemon of every protocol for their
+/// routes we simply ask zebra which routes have been inserted and filter them by protocol.
+/// The following command is used to accomplish this: `show ip route <protocol> json`.
+/// This struct can be used the deserialize the output of that command.
+#[derive(Debug, Serialize, Deserialize, Default)]
+pub struct Routes(pub HashMap<Cidr, Vec<Route>>);
diff --git a/proxmox-frr/src/de/openfabric.rs b/proxmox-frr/src/de/openfabric.rs
new file mode 100644
index 000000000000..99d281f24bcd
--- /dev/null
+++ b/proxmox-frr/src/de/openfabric.rs
@@ -0,0 +1,42 @@
+use serde::{Deserialize, Serialize};
+
+/// Adjacency information
+///
+/// Circuits are Layer-2 Broadcast domains (Either point-to-point or LAN).
+#[derive(Debug, Serialize, Deserialize)]
+pub struct Circuit {
+    /// The circuit id
+    pub circuit: u32,
+    /// The hostname of the adjacency peer
+    pub adj: Option<String>,
+    /// The interface on which this adjacency exists
+    pub interface: Option<String>,
+    /// If the adjacent router is a L1 or L2 router
+    pub level: Option<u32>,
+    /// The state of the adjacency, this is "Up" when everything is well
+    pub state: Option<String>,
+    /// When the adjacency expires
+    #[serde(rename = "expires-in")]
+    pub expires_in: Option<String>,
+    /// Subnetwork Point of Attachment
+    pub snpa: Option<String>,
+}
+
+/// An openfabric area the same as SDN fabric.
+#[derive(Debug, Serialize, Deserialize)]
+pub struct Area {
+    /// The are name, this is the same as the fabric_id, so the name of the fabric.
+    pub area: String,
+    /// Circuits are Layer-2 Broadcast domains (Either point-to-point or LAN).
+    pub circuits: Vec<Circuit>,
+}
+
+/// The parsed neighbors.
+///
+/// This models the output of:
+/// `vtysh -c 'show openfabric neighbor json'`.
+#[derive(Debug, Serialize, Deserialize, Default)]
+pub struct Neighbors {
+    /// Every sdn fabric is also an openfabric 'area'
+    pub areas: Vec<Area>,
+}
diff --git a/proxmox-frr/src/de/ospf.rs b/proxmox-frr/src/de/ospf.rs
new file mode 100644
index 000000000000..0e813ff1e614
--- /dev/null
+++ b/proxmox-frr/src/de/ospf.rs
@@ -0,0 +1,57 @@
+use std::collections::HashMap;
+
+use serde::{Deserialize, Serialize};
+
+/// Information about the Neighbor (Peer) of the Adjacency.
+#[derive(Debug, Serialize, Deserialize)]
+#[serde(rename_all = "camelCase")]
+pub struct Neighbor {
+    /// The full state of the neighbor. This is "{converged}/{role}".
+    #[serde(rename = "nbrState")]
+    pub neighbor_state: String,
+    /// Priority of the Neighbor
+    #[serde(rename = "nbrPriority")]
+    pub neighbor_priority: u32,
+    /// The current state of the adjancecy, this is a complex state machine with many
+    /// states. The most important ones are "Full" if the full table has been exchanged
+    /// and "Init" when the adjacency has been formed but no routing information has
+    /// been exchanged.
+    pub converged: String,
+    /// Role of the peer (If he's a designated router (DR) or not (DROther)
+    pub role: String,
+    /// Uptime in milliseconds
+    #[serde(rename = "upTimeInMsec")]
+    pub up_time_in_msec: u64,
+    /// Router Dead Interval Timer Due in milliseconds
+    #[serde(rename = "routerDeadIntervalTimerDueMsec")]
+    pub router_dead_interval_timer_due_msec: u64,
+    /// Uptime of the adjacency
+    #[serde(rename = "upTime")]
+    pub up_time: String,
+    /// Expires in countdown
+    #[serde(rename = "deadTime")]
+    pub dead_time: String,
+    /// The remote interface address, so the address of the other peer.
+    #[serde(rename = "ifaceAddress")]
+    pub interface_address: String,
+    /// The interface name of this adjacency. This is always a combination of interface
+    /// name and address. e.g. "ens21:5.5.5.3".
+    #[serde(rename = "ifaceName")]
+    pub interface_name: String,
+    /// Link State Retransmission List Counter
+    #[serde(rename = "linkStateRetransmissionListCounter")]
+    pub link_state_retransmission_list_counter: u32,
+    /// Link State Request List Counter
+    #[serde(rename = "linkStateRequestListCounter")]
+    pub link_state_request_list_counter: u32,
+    /// Database Summary List Counter
+    #[serde(rename = "databaseSummaryListCounter")]
+    pub database_summary_list_counter: u32,
+}
+
+/// The parsed OSPF neighbors
+#[derive(Debug, Serialize, Deserialize, Default)]
+pub struct Neighbors {
+    /// The OSPF neighbors. This is nearly always a ip-address - neighbor mapping.
+    pub neighbors: HashMap<String, Vec<Neighbor>>,
+}
diff --git a/proxmox-frr/src/lib.rs b/proxmox-frr/src/lib.rs
index 35b62cb39c91..2e6ab62c6119 100644
--- a/proxmox-frr/src/lib.rs
+++ b/proxmox-frr/src/lib.rs
@@ -1 +1,2 @@
+pub mod de;
 pub mod ser;
-- 
2.47.2



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


  parent reply	other threads:[~2025-08-22  9:02 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 ` Gabriel Goller [this message]
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
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=20250822090102.102949-3-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 an external index of several public inboxes,
see mirroring instructions on how to clone and mirror
all data and code used by this external index.
Service provided by Proxmox Server Solutions GmbH | Privacy | Legal