diff options
| author | Daniil Baturin <daniil@vyos.io> | 2026-03-12 16:13:10 +0000 |
|---|---|---|
| committer | GitHub <noreply@github.com> | 2026-03-12 16:13:10 +0000 |
| commit | dbe48341d7eb804bdbe4955e7e0728bfea560b88 (patch) | |
| tree | 7d7483818fd36f47581ca28bea4a43bfd8d923e3 | |
| parent | af8e4b83017e0c1b28cd1e447f760fdc32e4f2ff (diff) | |
| parent | 78e770e21a2b294566fe8b34ac6497657bbc0b4e (diff) | |
| download | vyos-1x-dbe48341d7eb804bdbe4955e7e0728bfea560b88.tar.gz vyos-1x-dbe48341d7eb804bdbe4955e7e0728bfea560b88.zip | |
Merge pull request #5028 from natali-rs1985/T8342
vpp: T8342: Add verification of members for bond and bridge interfaces
| -rw-r--r-- | python/vyos/vpp/config_deps.py | 30 | ||||
| -rwxr-xr-x | src/conf_mode/vpp.py | 10 | ||||
| -rw-r--r-- | src/conf_mode/vpp_interfaces_bonding.py | 18 | ||||
| -rw-r--r-- | src/conf_mode/vpp_interfaces_bridge.py | 20 |
4 files changed, 77 insertions, 1 deletions
diff --git a/python/vyos/vpp/config_deps.py b/python/vyos/vpp/config_deps.py index 43cf6036f..a822825c3 100644 --- a/python/vyos/vpp/config_deps.py +++ b/python/vyos/vpp/config_deps.py @@ -74,3 +74,33 @@ def deps_bridge_dict(conf) -> dict[str, list[str]]: bridge_members_dict.update({member_name: bridge_ifaces_list}) return bridge_members_dict + + +def deps_bond_dict(conf) -> dict[str, list[str]]: + """Get a dict of all bonding interface members: + + keys: members + + values: bridge interfaces + + Args: + conf (config): VyOS config object + + Returns: + dict[str, list[str]]: dict of members + """ + bond_members_dict: dict[str, list[str]] = {} + config = conf.get_config_dict( + ['interfaces', 'vpp', 'bonding'], + key_mangling=('-', '_'), + get_first_key=True, + no_tag_node_value_mangle=True, + ) + + for bond_name, bond_config in config.items(): + for member_name in bond_config.get('member', {}).get('interface', []): + bond_ifaces_list = bond_members_dict.get(member_name, []) + bond_ifaces_list.append(bond_name) + bond_members_dict.update({member_name: bond_ifaces_list}) + + return bond_members_dict diff --git a/src/conf_mode/vpp.py b/src/conf_mode/vpp.py index e6b739179..22cf04020 100755 --- a/src/conf_mode/vpp.py +++ b/src/conf_mode/vpp.py @@ -48,6 +48,7 @@ from vyos.utils.process import is_systemd_service_active from vyos.vpp import VPPControl from vyos.vpp import control_host from vyos.vpp import VppNotRunningError +from vyos.vpp.config_deps import deps_bond_dict from vyos.vpp.config_deps import deps_bridge_dict from vyos.vpp.config_deps import deps_xconnect_dict from vyos.vpp.config_verify import ( @@ -257,6 +258,7 @@ def get_config(config=None): xconn_members = deps_xconnect_dict(conf) bridge_members = deps_bridge_dict(conf) + bond_members = deps_bond_dict(conf) removed_ifaces = [] tmp = node_changed(conf, base_settings + ['interface']) @@ -298,6 +300,7 @@ def get_config(config=None): 'removed_ifaces': removed_ifaces, 'xconn_members': xconn_members, 'bridge_members': bridge_members, + 'bond_members': bond_members, 'persist_config': eth_ifaces_persist, 'interfaces_vpp': interfaces_config, 'remove': {}, @@ -438,6 +441,7 @@ def get_config(config=None): config['removed_ifaces'] = removed_ifaces config['xconn_members'] = xconn_members config['bridge_members'] = bridge_members + config['bond_members'] = bond_members config['interfaces_vpp'] = interfaces_config @@ -601,7 +605,7 @@ def verify(config): f'RX mode {rx_mode} is not supported for interface {iface}' ) - # Check if deleted interfaces are not xconnect members or not bridge members + # Check if deleted interfaces are not xconnect/bridge/bond members for iface_config in config.get('removed_ifaces', []): if iface_config['iface_name'] in config.get('xconn_members', {}): raise ConfigError( @@ -611,6 +615,10 @@ def verify(config): raise ConfigError( f'Interface {iface_config["iface_name"]} is a bridge member and cannot be removed' ) + if iface_config['iface_name'] in config.get('bond_members', {}): + raise ConfigError( + f'Interface {iface_config["iface_name"]} is a bond member and cannot be removed' + ) verify_routes_count(config['settings']) diff --git a/src/conf_mode/vpp_interfaces_bonding.py b/src/conf_mode/vpp_interfaces_bonding.py index 553200832..582b82d99 100644 --- a/src/conf_mode/vpp_interfaces_bonding.py +++ b/src/conf_mode/vpp_interfaces_bonding.py @@ -24,6 +24,7 @@ from vyos.utils.assertion import assert_mac from vyos.utils.process import is_systemd_service_active from vyos.ifconfig.vpp import VPPBondInterface +from vyos.vpp.config_deps import deps_bond_dict from vyos.vpp.config_deps import deps_bridge_dict from vyos.vpp.config_deps import deps_xconnect_dict from vyos.vpp.config_verify import verify_vpp_remove_bridge_interface @@ -92,6 +93,8 @@ def get_config(config=None) -> dict: no_tag_node_value_mangle=True, ) + config['bond_members'] = deps_bond_dict(conf) + # Dependency config['xconn_members'] = deps_xconnect_dict(conf) if ifname in config['xconn_members']: @@ -140,6 +143,21 @@ def verify(config): if iface not in config['vpp_ifaces']: raise ConfigError(f'{iface} must be a VPP interface for bonding') + # Each interface can belong only to one bond + bond_members = config['bond_members'][iface] + if len(bond_members) > 1: + raise ConfigError( + f'Interface {iface} cannot be a member of multiple bonding interfaces: {", ".join(bond_members)}' + ) + + # Interface cannot be a member of a bridge and a bond at the same time + bridge_members = config['bridge_members'].get(iface) + if bridge_members: + raise ConfigError( + f'Interface {iface} cannot be a member of a bond because ' + f'it already belongs to bridge interface: {", ".join(bridge_members)}.' + ) + if 'mac' in config: mac = config['mac'] try: diff --git a/src/conf_mode/vpp_interfaces_bridge.py b/src/conf_mode/vpp_interfaces_bridge.py index 37cec7b94..162345e6e 100644 --- a/src/conf_mode/vpp_interfaces_bridge.py +++ b/src/conf_mode/vpp_interfaces_bridge.py @@ -22,6 +22,8 @@ from vyos import ConfigError from vyos.utils.process import is_systemd_service_active from vyos.ifconfig.vpp import VPPBridgeInterface +from vyos.vpp.config_deps import deps_bond_dict +from vyos.vpp.config_deps import deps_bridge_dict def get_config(config=None) -> dict: @@ -63,6 +65,9 @@ def get_config(config=None) -> dict: with_recursive_defaults=True, ) + config['bond_members'] = deps_bond_dict(conf) + config['bridge_members'] = deps_bridge_dict(conf) + return config @@ -92,6 +97,21 @@ def verify(config): f"Interface '{member}' not found in 'vpp settings interface' or does not start with allowed prefixes {allowed_prefixes}" ) + # Each interface can belong only to one bridge + bridge_members = config['bridge_members'][member] + if len(bridge_members) > 1: + raise ConfigError( + f'Interface {member} is added to more than one bridge: {", ".join(bridge_members)}' + ) + + # Interface cannot be a member of a bridge and a bond at the same time + bond_members = config['bond_members'].get(member) + if bond_members: + raise ConfigError( + f'Interface {member} cannot be a member of a bridge ' + f'because it already belongs to bonding interface: {", ".join(bond_members)}.' + ) + # Check if BVI is already defined, only one BVI per bridge domain is allowed if 'bvi' in member_config: if bvi_exists: |
