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.gol...@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 <supp...@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