summaryrefslogtreecommitdiff
path: root/src
diff options
context:
space:
mode:
authorNataliia Solomko <natalirs1985@gmail.com>2025-12-22 12:22:21 +0200
committerNataliia Solomko <natalirs1985@gmail.com>2025-12-23 17:04:44 +0200
commit16d0f617ed63a72fe6cf7cbbb69ba62b05973401 (patch)
tree85afdd73f0f31995c639b2d8dfe861a22330b87c /src
parent617c99b89aa2b9df985f573159d78212983a5451 (diff)
downloadvyos-1x-16d0f617ed63a72fe6cf7cbbb69ba62b05973401.tar.gz
vyos-1x-16d0f617ed63a72fe6cf7cbbb69ba62b05973401.zip
vpp: T7203: Add op-mode to show bridge-domain
Diffstat (limited to 'src')
-rwxr-xr-xsrc/op_mode/vpp.py112
1 files changed, 112 insertions, 0 deletions
diff --git a/src/op_mode/vpp.py b/src/op_mode/vpp.py
index 006ea79ff..3b7de4cf7 100755
--- a/src/op_mode/vpp.py
+++ b/src/op_mode/vpp.py
@@ -24,6 +24,9 @@ from vyos.configquery import ConfigTreeQuery
import vyos.opmode
+NO_INDEX = 0xFFFFFFFF
+
+
def _verify(target: typing.Optional[str]):
"""Decorator checks if config for VPP feature exists"""
from functools import wraps
@@ -268,6 +271,102 @@ class VPPShow:
return data.reply
+ # -----------------------------
+ # Bridge-domain information
+ # -----------------------------
+ def _parse_bridge_id(self, ifname: typing.Optional[str]) -> typing.Optional[int]:
+ if ifname is None:
+ return None
+
+ if not ifname.startswith('br') and not ifname[2:].isdigit():
+ raise vyos.opmode.IncorrectValue(
+ f'"{ifname}" is not a valid bridge interface name (expected brN)'
+ )
+
+ if not self.config.exists(['vpp', 'interfaces', 'bridge', ifname]):
+ raise vyos.opmode.IncorrectValue(
+ f'Bridge interface {ifname} does not exist'
+ )
+
+ return int(ifname[2:])
+
+ def _get_bridge_domain_raw(
+ self, bd_id: typing.Optional[int] = None
+ ) -> typing.List[dict]:
+ # Dump bridge domains
+ domains = self.vpp.api.bridge_domain_dump(
+ bd_id=bd_id if bd_id is not None else NO_INDEX
+ )
+
+ result = []
+ for d in domains:
+ domain_info = {
+ 'bd_id': d.bd_id,
+ 'learning': bool(d.learn),
+ 'forward': bool(d.forward),
+ 'uu_flood': bool(d.uu_flood),
+ 'flood': bool(d.flood),
+ 'arp_term': bool(d.arp_term),
+ 'arp_ufwd': bool(d.arp_ufwd),
+ 'mac_age': d.mac_age,
+ 'bvi_interface': d.bvi_sw_if_index,
+ 'n_sw_ifs': d.n_sw_ifs,
+ 'members': [
+ {
+ 'ifname': self.vpp.get_interface_name(m.sw_if_index),
+ 'sw_if_index': m.sw_if_index,
+ 'shg': m.shg,
+ }
+ for m in d.sw_if_details
+ ],
+ }
+ result.append(domain_info)
+
+ result.sort(key=lambda x: x['bd_id'])
+
+ return result
+
+ def _show_bridge_domain_formatted(self, data: typing.List[dict]) -> str:
+ if not data:
+ return 'No bridge domains configured.'
+
+ table_data = [
+ {
+ 'BD-ID': d['bd_id'],
+ 'Age(min)': 'off' if d['mac_age'] == 0 else d['mac_age'],
+ 'Learning': 'on' if d['learning'] else 'off',
+ 'U-Forwrd': 'on' if d['forward'] else 'off',
+ 'UU-Flood': 'flood' if d['uu_flood'] else 'drop',
+ 'Flooding': 'on' if d['flood'] else 'off',
+ 'ARP-Term': 'on' if d['arp_term'] else 'off',
+ 'arp-ufwd': 'on' if d['arp_ufwd'] else 'off',
+ 'BVI-Intf': (
+ self.vpp.get_interface_name(d['bvi_interface'])
+ if d['bvi_interface'] != NO_INDEX
+ else 'N/A'
+ ),
+ }
+ for d in data
+ ]
+ return tabulate(table_data, headers='keys', tablefmt='simple', numalign='left')
+
+ def bridge_domain(self, raw: bool, ifname: typing.Optional[str] = None):
+ bd_id = self._parse_bridge_id(ifname)
+ data = self._get_bridge_domain_raw(bd_id)
+ return data if raw else self._show_bridge_domain_formatted(data)
+
+ def bridge_domain_details(self, raw: bool, ifname: typing.List):
+ bd_id = self._parse_bridge_id(ifname)
+
+ # VPP API call is not so informative -> use CLI command
+ cmd_command = f'show bridge-domain {bd_id} detail'
+ data = self.vpp.cli_cmd(cmd_command)
+
+ if raw:
+ return [data.reply]
+
+ return data.reply
+
# -----------------------------
# VyOS IPFIX op-mode entries
@@ -297,6 +396,19 @@ def show_lacp_details(raw: bool, ifname: typing.Optional[str]):
return VPPShow().lacp_details(raw, ifname)
+# -----------------------------
+# Bridge op-mode entries
+# -----------------------------
+@_verify('interfaces bridge')
+def show_bridge(raw: bool, ifname: typing.Optional[str] = None):
+ return VPPShow().bridge_domain(raw, ifname)
+
+
+@_verify('interfaces bridge')
+def show_bridge_details(raw: bool, ifname: typing.Optional[str] = None):
+ return VPPShow().bridge_domain_details(raw, ifname)
+
+
if __name__ == '__main__':
try:
res = vyos.opmode.run(sys.modules[__name__])