summaryrefslogtreecommitdiff
path: root/src
diff options
context:
space:
mode:
authorChristian Breunig <christian@breunig.cc>2026-03-19 19:50:37 +0100
committerGitHub <noreply@github.com>2026-03-19 19:50:37 +0100
commitfc06cc45148a42171baf9f4ae01da33c3f0df1aa (patch)
tree11eaf65a8a5b73dd3e736201599b3f388aeab725 /src
parent7957857e6b5d6572d2c9831e0e66dac80e7a415e (diff)
parent8d1dd6f610fa10d8ba9da03749da67bdd0717b17 (diff)
downloadvyos-1x-fc06cc45148a42171baf9f4ae01da33c3f0df1aa.tar.gz
vyos-1x-fc06cc45148a42171baf9f4ae01da33c3f0df1aa.zip
Merge pull request #5064 from natali-rs1985/T8352
T8352: VPP add op-mode commands to show bonding interfaces
Diffstat (limited to 'src')
-rw-r--r--src/conf_mode/vpp_interfaces_bonding.py2
-rwxr-xr-xsrc/op_mode/vpp.py95
2 files changed, 93 insertions, 4 deletions
diff --git a/src/conf_mode/vpp_interfaces_bonding.py b/src/conf_mode/vpp_interfaces_bonding.py
index 8d9930a20..fa49f2da5 100644
--- a/src/conf_mode/vpp_interfaces_bonding.py
+++ b/src/conf_mode/vpp_interfaces_bonding.py
@@ -53,7 +53,7 @@ def _get_bond_lb(lb_name: str) -> int:
'layer3+4': 1,
}
- return lb_mapping.get(lb_name, 5)
+ return lb_mapping.get(lb_name, 0)
def get_config(config=None) -> dict:
diff --git a/src/op_mode/vpp.py b/src/op_mode/vpp.py
index 503e2fccc..0cdb94e3a 100755
--- a/src/op_mode/vpp.py
+++ b/src/op_mode/vpp.py
@@ -20,6 +20,7 @@ import typing
from tabulate import tabulate
from vyos.vpp import VPPControl
+from vyos.vpp.utils import vpp_iface_name_transform
from vyos.configquery import ConfigTreeQuery
import vyos.opmode
@@ -42,6 +43,21 @@ class VPPShow:
3: 'COLLECTING_DISTRIBUTING',
}
PTX_STATES = {0: 'NO_PERIODIC', 1: 'FAST', 2: 'SLOW', 3: 'PERIODIC_TX'}
+ BOND_MODE = {
+ 1: 'round-robin',
+ 2: 'active-backup',
+ 3: 'xor',
+ 4: 'broadcast',
+ 5: 'lacp',
+ }
+ BOND_LB = {
+ 0: 'layer2',
+ 1: 'layer3+4',
+ 2: 'layer2+3',
+ 3: 'round-robin',
+ 4: 'broadcast',
+ 5: 'active-backup',
+ }
def __init__(self):
self.config = ConfigTreeQuery()
@@ -159,7 +175,7 @@ class VPPShow:
return data if raw else self._show_ipfix_table_formatted()
# -----------------------------
- # LACP information
+ # Bonding information
# -----------------------------
def _get_raw_output(self, data_dump: typing.List[dict]) -> list[dict]:
data = [json.loads(json.dumps(d._asdict(), default=str)) for d in data_dump]
@@ -226,6 +242,47 @@ class VPPShow:
f'PTX-state: {self.PTX_STATES[d["ptx_state"]]}'
)
+ def _get_bond_raw(self, index: typing.Optional[str]) -> list[dict]:
+ bond_dump = self.vpp.api.sw_bond_interface_dump(sw_if_index=index)
+
+ result = []
+ for bond in bond_dump:
+ bond_info = {
+ 'interface_name': bond.interface_name,
+ 'sw_if_index': bond.sw_if_index,
+ 'mode': self.BOND_MODE[bond.mode],
+ 'hash_policy': self.BOND_LB[bond.lb],
+ 'active_members': bond.active_members,
+ 'members': {},
+ }
+ members = self.vpp.api.sw_member_interface_dump(
+ sw_if_index=bond.sw_if_index
+ )
+ for member in members:
+ bond_info['members'][member.interface_name] = {
+ 'sw_if_index': member.sw_if_index,
+ 'is_passive': member.is_passive,
+ 'is_long_timeout': member.is_long_timeout,
+ 'is_local_numa': member.is_local_numa,
+ 'weight': member.weight,
+ }
+ result.append(bond_info)
+
+ return result
+
+ def _show_bond_info_formatted(self, data: typing.List[dict]) -> str:
+ table_data = [
+ {
+ 'Interface': d['interface_name'],
+ 'Mode': d['mode'],
+ 'Hash': d['hash_policy'],
+ 'Members': '\n'.join(sorted(d['members'].keys())),
+ 'Active members': d['active_members'],
+ }
+ for d in data
+ ]
+ return tabulate(table_data, headers='keys', tablefmt='simple', numalign='left')
+
def lacp_info(self, raw: bool, ifname: typing.Optional[str]):
data = self._get_lacp_raw(ifname)
@@ -252,6 +309,31 @@ class VPPShow:
return data.reply
+ def bond_info(self, raw: bool, ifname: typing.Optional[str]) -> str:
+ index = NO_INDEX
+ if ifname:
+ if not ifname.startswith('vppbond') or not ifname[7:].isdigit():
+ raise vyos.opmode.IncorrectValue(
+ f'"{ifname}" is not a valid bonding interface name (expected vppbondN)'
+ )
+
+ ifname_vpp = vpp_iface_name_transform(ifname)
+ index = self.vpp.get_sw_if_index(ifname_vpp)
+ if index is None:
+ raise vyos.opmode.IncorrectValue(
+ f'Bonding interface {ifname} does not exist in VPP'
+ )
+
+ data = self._get_bond_raw(index)
+
+ return data if raw else self._show_bond_info_formatted(data)
+
+ def bond_details(self, raw: bool) -> str:
+ # VPP API call is not so informative -> use CLI command
+ cmd_command = 'show bond details'
+ data = self.vpp.cli_cmd(cmd_command)
+ return [data.reply] if raw else data.reply
+
# -----------------------------
# Bridge-domain information
# -----------------------------
@@ -343,7 +425,6 @@ class VPPShow:
cmd_command = f'show bridge-domain {bd_id} detail'
data = self.vpp.cli_cmd(cmd_command)
-
if raw:
return [data.reply]
@@ -390,7 +471,7 @@ def show_ipfix_table(raw: bool):
return VPPShow().ipfix_table(raw)
# -----------------------------
-# VPP LACP information
+# VPP Bonding information
# -----------------------------
@vyos.opmode.verify_cli_exists(['interfaces', 'vpp', 'bonding'])
def show_lacp(raw: bool, ifname: typing.Optional[str]):
@@ -400,6 +481,14 @@ def show_lacp(raw: bool, ifname: typing.Optional[str]):
def show_lacp_details(raw: bool, ifname: typing.Optional[str]):
return VPPShow().lacp_details(raw, ifname)
+@vyos.opmode.verify_cli_exists(['interfaces', 'vpp', 'bonding'])
+def show_bond(raw: bool, ifname: typing.Optional[str]):
+ return VPPShow().bond_info(raw, ifname)
+
+@vyos.opmode.verify_cli_exists(['interfaces', 'vpp', 'bonding'])
+def show_bond_details(raw: bool):
+ return VPPShow().bond_details(raw)
+
# -----------------------------
# Bridge op-mode entrie
# -----------------------------