From mboxrd@z Thu Jan 1 00:00:00 1970 Return-Path: Received: from firstgate.proxmox.com (firstgate.proxmox.com [IPv6:2a01:7e0:0:424::9]) by lore.proxmox.com (Postfix) with ESMTPS id D8EB31FF187 for ; Mon, 3 Nov 2025 15:50:00 +0100 (CET) Received: from firstgate.proxmox.com (localhost [127.0.0.1]) by firstgate.proxmox.com (Proxmox) with ESMTP id EDF961FFF3; Mon, 3 Nov 2025 15:50:36 +0100 (CET) Message-ID: Date: Mon, 3 Nov 2025 15:50:32 +0100 MIME-Version: 1.0 User-Agent: Mozilla Thunderbird To: Proxmox VE development discussion , Stefan Hanreich References: <20251030154851.540408-1-s.hanreich@proxmox.com> <20251030154851.540408-5-s.hanreich@proxmox.com> Content-Language: en-US From: Hannes Laimer In-Reply-To: <20251030154851.540408-5-s.hanreich@proxmox.com> X-Bm-Milter-Handled: 55990f41-d878-4baa-be0a-ee34c49e34d2 X-Bm-Transport-Timestamp: 1762181415586 X-SPAM-LEVEL: Spam detection results: 0 AWL 0.041 Adjusted score from AWL reputation of From: address BAYES_00 -1.9 Bayes spam probability is 0 to 1% DMARC_MISSING 0.1 Missing DMARC policy KAM_DMARC_STATUS 0.01 Test Rule for DKIM or SPF Failure with Strict Alignment RCVD_IN_VALIDITY_CERTIFIED_BLOCKED 0.001 ADMINISTRATOR NOTICE: The query to Validity was blocked. See https://knowledge.validity.com/hc/en-us/articles/20961730681243 for more information. RCVD_IN_VALIDITY_RPBL_BLOCKED 0.001 ADMINISTRATOR NOTICE: The query to Validity was blocked. See https://knowledge.validity.com/hc/en-us/articles/20961730681243 for more information. RCVD_IN_VALIDITY_SAFE_BLOCKED 0.001 ADMINISTRATOR NOTICE: The query to Validity was blocked. See https://knowledge.validity.com/hc/en-us/articles/20961730681243 for more information. SPF_HELO_NONE 0.001 SPF: HELO does not publish an SPF Record SPF_PASS -0.001 SPF: sender matches SPF record URIBL_BLOCKED 0.001 ADMINISTRATOR NOTICE: The query to URIBL was blocked. See http://wiki.apache.org/spamassassin/DnsBlocklists#dnsbl-block for more information. [mod.rs, lib.rs, ospf.rs, openfabric.rs] Subject: Re: [pve-devel] [PATCH proxmox-ve-rs 2/6] frr: add deserialization types for openfabric and ospf X-BeenThere: pve-devel@lists.proxmox.com X-Mailman-Version: 2.1.29 Precedence: list List-Id: Proxmox VE development discussion List-Unsubscribe: , List-Archive: List-Post: List-Help: List-Subscribe: , Reply-To: Proxmox VE development discussion Content-Transfer-Encoding: 7bit Content-Type: text/plain; charset="us-ascii"; Format="flowed" Errors-To: pve-devel-bounces@lists.proxmox.com Sender: "pve-devel" I think some tests would make sense here On 10/30/25 16:48, Stefan Hanreich wrote: > From: Gabriel Goller > > 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 > > Signed-off-by: Gabriel Goller > Signed-off-by: Stefan Hanreich > --- > proxmox-frr/Cargo.toml | 1 + > proxmox-frr/debian/control | 4 ++ > proxmox-frr/src/de/mod.rs | 56 +++++++++++++++++ > proxmox-frr/src/de/openfabric.rs | 101 +++++++++++++++++++++++++++++++ > proxmox-frr/src/de/ospf.rs | 64 ++++++++++++++++++++ > proxmox-frr/src/lib.rs | 1 + > 6 files changed, 227 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 8b01fa4..8ada547 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 9996619..bce0c70 100644 > --- a/proxmox-frr/debian/control > +++ b/proxmox-frr/debian/control > @@ -9,6 +9,8 @@ Build-Depends-Arch: cargo:native , > 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 > Maintainer: Proxmox Support Team > @@ -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 0000000..a6674d9 > --- /dev/null > +++ b/proxmox-frr/src/de/mod.rs > @@ -0,0 +1,56 @@ > +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 { > + #[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, > + /// Weight of the nexthop > + pub weight: i32, > + /// If this nexthop entry is a duplicate of another (the first one has this unset) > + pub duplicate: Option, > +} > + > +/// route > +#[derive(Debug, Serialize, Deserialize, Clone)] > +pub struct Route { > + /// 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, > + /// Metric of the route > + pub metric: i32, > + /// Protocol from which the route originates > + pub protocol: String, > + #[serde(rename = "vrfName")] > + pub vrf_name: String, > + /// If the route is installed in the kernel routing table > + pub installed: Option, > +} > + > +/// 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 json`. > +/// This struct can be used the deserialize the output of that command. > +#[derive(Debug, Serialize, Deserialize, Default)] > +pub struct Routes(pub HashMap>); > diff --git a/proxmox-frr/src/de/openfabric.rs b/proxmox-frr/src/de/openfabric.rs > new file mode 100644 > index 0000000..837159b > --- /dev/null > +++ b/proxmox-frr/src/de/openfabric.rs > @@ -0,0 +1,101 @@ > +use serde::{Deserialize, Serialize}; > + > +/// State of the adjacency of a OpenFabric neighbor > +#[derive(Debug, Clone, Copy, Serialize, Deserialize)] > +pub enum AdjacencyState { > + Initializing, > + Up, > + Down, > + Unknown, > +} > + > +/// Neighbor Interface > +/// > +/// Interface used to communicate with a specific neighbor > +#[derive(Debug, Serialize, Deserialize)] > +pub struct NeighborInterface { > + /// The name of the interface > + pub name: String, > + /// The state of the adjacency, this is "Up" when everything is well > + pub state: Option, > + /// Time since the last adj-flap (basically the uptime) > + #[serde(rename = "last-ago")] > + pub last_ago: String, > +} > + > +/// Adjacency information > +/// > +/// Circuits are Layer-2 Broadcast domains (Either point-to-point or LAN). > +#[derive(Debug, Serialize, Deserialize)] > +pub struct Circuit { > + /// The hostname of the adjacency peer > + pub adj: Option, > + /// The interface of the neighbor > + pub interface: Option, > +} > + > +/// 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, > +} > + > +/// 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, > +} > + > +/// The NetworkType of a OpenFabric interface > +#[derive(Debug, Clone, Copy, Serialize, Deserialize)] > +pub enum NetworkType { > + #[serde(rename(deserialize = "p2p", serialize = "Point-To-Point"))] > + PointToPoint, > + #[serde(rename(deserialize = "lan", serialize = "Broadcast"))] > + Lan, > + #[serde(rename(deserialize = "loopback", serialize = "Loopback"))] > + Loopback, > + #[serde(rename = "Unknown")] > + Unknown, > +} > + > +/// The State of a OpenFabric interface > +#[derive(Debug, Clone, Copy, Serialize, Deserialize)] > +pub enum CircuitState { > + Init, > + Config, > + Up, > + Unknown, > +} > + > +#[derive(Debug, Serialize, Deserialize)] > +#[serde(rename_all = "kebab-case")] > +pub struct Interface { > + pub name: String, > + pub state: CircuitState, > + #[serde(rename = "type")] > + pub ty: NetworkType, > +} > + > +#[derive(Debug, Serialize, Deserialize)] > +pub struct InterfaceCircuits { > + pub interface: Interface, > +} > + > +#[derive(Debug, Serialize, Deserialize, Default)] > +pub struct InterfaceArea { > + pub area: String, > + pub circuits: Vec, > +} > + > +#[derive(Debug, Serialize, Deserialize, Default)] > +pub struct Interfaces { > + pub areas: Vec, > +} > diff --git a/proxmox-frr/src/de/ospf.rs b/proxmox-frr/src/de/ospf.rs > new file mode 100644 > index 0000000..a75609d > --- /dev/null > +++ b/proxmox-frr/src/de/ospf.rs > @@ -0,0 +1,64 @@ > +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, > + /// The uptime of the interface > + #[serde(rename = "upTime")] > + pub up_time: 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, > +} > + > +/// The parsed OSPF neighbors > +#[derive(Debug, Deserialize, Default)] > +pub struct Neighbors { > + /// The OSPF neighbors. This is nearly always a ip-address - neighbor mapping. > + pub neighbors: HashMap>, > +} > + > +/// All possible OSPF network-types that can be returned from frr > +#[derive(Debug, Clone, Copy, Serialize, Deserialize)] > +pub enum NetworkType { > + #[serde(rename = "Null")] > + Null, > + #[serde(rename(deserialize = "POINTOPOINT", serialize = "Point-To-Point"))] > + PointToPoint, > + #[serde(rename(deserialize = "BROADCAST", serialize = "Broadcast"))] > + Broadcast, > + #[serde(rename = "NBMA")] > + Nbma, > + #[serde(rename(deserialize = "POINTOMULTIPOINT", serialize = "Point-To-Multipoint"))] > + PointToMultipoint, > + #[serde(rename(deserialize = "VIRTUALLINK", serialize = "Virtual Link"))] > + VirtualLink, > + #[serde(rename(deserialize = "LOOPBACK", serialize = "Loopback"))] > + Loopback, > +} > + > +#[derive(Debug, Deserialize)] > +#[serde(rename_all = "camelCase")] > +pub struct Interface { > + /// The interface state > + pub if_up: bool, > + /// The network type (e.g. point-to-point, broadcast, etc.) > + /// > + /// Note there is also a "state" property, but that models the state of the interface (ism), > + /// which can also be "point-to-point", but it can also be e.g. "Down" or e.g. "DROther"! > + /// So networkType is the configured network type and state is the state of interface, which > + /// sometimes is the same as the networkType. > + pub network_type: NetworkType, > +} > + > +#[derive(Debug, Deserialize, Default)] > +pub struct Interfaces { > + pub interfaces: HashMap, > +} > diff --git a/proxmox-frr/src/lib.rs b/proxmox-frr/src/lib.rs > index 35b62cb..2e6ab62 100644 > --- a/proxmox-frr/src/lib.rs > +++ b/proxmox-frr/src/lib.rs > @@ -1 +1,2 @@ > +pub mod de; > pub mod ser; _______________________________________________ pve-devel mailing list pve-devel@lists.proxmox.com https://lists.proxmox.com/cgi-bin/mailman/listinfo/pve-devel