diff options
| author | Daniil Baturin <daniil@vyos.io> | 2025-12-23 15:10:22 +0000 |
|---|---|---|
| committer | GitHub <noreply@github.com> | 2025-12-23 15:10:22 +0000 |
| commit | d171c52ff343ba80a5d5fdb7bb4c008347a6649d (patch) | |
| tree | 4cce3dea00b1a28ca5f493cf39341a85645339ed /src | |
| parent | 165841c13d6b57631b8c034c5b5c87d4d7f3469f (diff) | |
| parent | 16d0f617ed63a72fe6cf7cbbb69ba62b05973401 (diff) | |
| download | vyos-1x-d171c52ff343ba80a5d5fdb7bb4c008347a6649d.tar.gz vyos-1x-d171c52ff343ba80a5d5fdb7bb4c008347a6649d.zip | |
Merge pull request #4913 from natali-rs1985/T7203
vpp: T7203: Add op-mode to show bridge-domain
Diffstat (limited to 'src')
| -rwxr-xr-x | src/op_mode/vpp.py | 112 |
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__]) |
