From 0e82c108bc56d7ea2683532fb7e468546f38b5dd Mon Sep 17 00:00:00 2001 From: Tobias Waldekranz Date: Wed, 22 Jan 2025 23:58:46 +0100 Subject: [PATCH] yanger: Provide bridge-wide STP state --- .../python/yanger/ietf_interfaces/bridge.py | 68 +++++++++++++++++-- 1 file changed, 64 insertions(+), 4 deletions(-) diff --git a/src/statd/python/yanger/ietf_interfaces/bridge.py b/src/statd/python/yanger/ietf_interfaces/bridge.py index fc076859..c275ebf1 100644 --- a/src/statd/python/yanger/ietf_interfaces/bridge.py +++ b/src/statd/python/yanger/ietf_interfaces/bridge.py @@ -1,6 +1,6 @@ from functools import cache -from ..common import LOG +from ..common import LOG, YangDate from ..host import HOST from .common import iplinks, iplinks_lower_of @@ -12,7 +12,6 @@ from . import vlan def bridge_vlan(): return { v["ifname"]: v for v in HOST.run_json("bridge -j vlan show".split(), []) } - def lower(iplink): lower = {} @@ -47,6 +46,62 @@ def lower(iplink): } +def stp_bridge_id(idstr): + segs = idstr.split(".") + return { + "priority": int(segs[0], 16), + "system-id": int(segs[1], 16), + "address": segs[2].lower(), + } + +def stp_tree(brname, mst): + if not (state := HOST.run_json(["mstpctl", "-f", "json", "showtree", brname, str(mst)]), {}): + return {} + + tree = { + "priority": int(state["bridge-id"][0], 16), + "bridge-id": stp_bridge_id(state["bridge-id"]), + } + + if rport := state.get("root-port"): + tree["root-port"] = rport.split()[0] + + if state.get("topology-change-count"): + tree["topology-change"] = { + "count": int(state["topology-change-count"]), + "in-progress": True if state["topology-change"] == "yes" else False, + "port": state["topology-change-port"], + "time": str(YangDate.from_seconds(int(state["time-since-topology-change"]))), + } + + return tree + + +def stp(iplink): + state = HOST.run_json(["mstpctl", "-f", "json", "showbridge", iplink["ifname"]], + default=[{}])[0] + if not state: + return {} + + stp = { + "force-protocol": state["force-protocol-version"], + "forward-delay": int(state["forward-delay"]), + "max-age": int(state["max-age"]), + "transmit-hold-count": int(state["tx-hold-count"]), + "max-hops": int(state["max-hops"]), + + "cist": stp_tree(iplink["ifname"], 0), + } + + # This information ought to be available in `showtree`, so it is + # still an open question how we should source the per-tree root id + # when adding MSTP support + if state.get("designated-root"): + stp["cist"]["root-id"] = stp_bridge_id(state["designated-root"]) + + return stp + + def mctlq2yang_mode(mctlq): if state := mctlq.get("state"): return "proxy" if state == "proxy" else "auto" @@ -166,6 +221,11 @@ def bridge(iplink): info = iplink["linkinfo"]["info_data"] if info.get("vlan_filtering"): - return qbridge(iplink) + br = qbridge(iplink) else: - return dbridge(iplink) + br = dbridge(iplink) + + if info.get("stp_state"): + br["stp"] = stp(iplink) + + return br