diff options
| author | Nataliia Solomko <natalirs1985@gmail.com> | 2026-02-27 21:22:15 +0200 |
|---|---|---|
| committer | Nataliia Solomko <natalirs1985@gmail.com> | 2026-03-03 17:52:40 +0200 |
| commit | c90512f68dbabe6f005c259f6e787abe527fe975 (patch) | |
| tree | 86166dcd8b9ddf17611a0b6f2142d1130604e981 /src | |
| parent | d47be4a8e2119e2be0be5fe9d7c29d7d8eaf24d9 (diff) | |
| download | vyos-1x-c90512f68dbabe6f005c259f6e787abe527fe975.tar.gz vyos-1x-c90512f68dbabe6f005c259f6e787abe527fe975.zip | |
vpp: T8327: Migrate bridge interface to 'interfaces vpp bridge'
Diffstat (limited to 'src')
| -rwxr-xr-x | src/conf_mode/vpp.py | 30 | ||||
| -rw-r--r-- | src/conf_mode/vpp_interfaces_bonding.py | 8 | ||||
| -rw-r--r-- | src/conf_mode/vpp_interfaces_bridge.py | 129 | ||||
| -rw-r--r-- | src/conf_mode/vpp_interfaces_gre.py | 14 | ||||
| -rw-r--r-- | src/conf_mode/vpp_interfaces_vxlan.py | 8 | ||||
| -rw-r--r-- | src/migration-scripts/vpp/5-to-6 | 21 | ||||
| -rwxr-xr-x | src/op_mode/vpp.py | 12 |
7 files changed, 98 insertions, 124 deletions
diff --git a/src/conf_mode/vpp.py b/src/conf_mode/vpp.py index 92777a16a..396cce548 100755 --- a/src/conf_mode/vpp.py +++ b/src/conf_mode/vpp.py @@ -45,6 +45,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_bridge_dict from vyos.vpp.config_deps import deps_xconnect_dict from vyos.vpp.config_verify import ( verify_dev_driver, @@ -254,6 +255,7 @@ def get_config(config=None): ) xconn_members = deps_xconnect_dict(conf) + bridge_members = deps_bridge_dict(conf) removed_ifaces = [] tmp = node_changed(conf, base_settings + ['interface']) @@ -294,6 +296,7 @@ def get_config(config=None): return { 'removed_ifaces': removed_ifaces, 'xconn_members': xconn_members, + 'bridge_members': bridge_members, 'persist_config': eth_ifaces_persist, 'interfaces_vpp': interfaces_config, 'remove': {}, @@ -433,6 +436,7 @@ def get_config(config=None): if removed_ifaces: config['removed_ifaces'] = removed_ifaces config['xconn_members'] = xconn_members + config['bridge_members'] = bridge_members config['interfaces_vpp'] = interfaces_config @@ -611,32 +615,16 @@ def verify(config): f'RX mode {rx_mode} is not supported for interface {iface}' ) - # check GRE tunnels as part of the bridge, only tunnel-type teb is allowed - # set vpp interfaces bridge br1 member interface vppgre1 - # set interfaces vpp gre vppgre1 tunnel-type teb - if 'interfaces' in config: - if 'bridge' in config['interfaces']: - for iface, iface_config in config['interfaces']['bridge'].items(): - if 'member' in iface_config: - for member in iface_config['member'].get('interface', []): - if member.startswith('vppgre'): - if ( - 'gre' in config['interfaces_vpp'] - and config['interfaces_vpp']['gre'] - .get(member, {}) - .get('tunnel_type') - != 'teb' - ): - raise ConfigError( - f'Only tunnel-type teb is allowed for GRE interfaces in bridge {iface}' - ) - - # Check if deleted interfaces are not xconnect memebrs + # Check if deleted interfaces are not xconnect members or not bridge members for iface_config in config.get('removed_ifaces', []): if iface_config['iface_name'] in config.get('xconn_members', {}): raise ConfigError( f'Interface {iface_config["iface_name"]} is an xconnect member and cannot be removed' ) + if iface_config['iface_name'] in config.get('bridge_members', {}): + raise ConfigError( + f'Interface {iface_config["iface_name"]} is a bridge 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 89376ea32..553200832 100644 --- a/src/conf_mode/vpp_interfaces_bonding.py +++ b/src/conf_mode/vpp_interfaces_bonding.py @@ -24,7 +24,9 @@ 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_bridge_dict from vyos.vpp.config_deps import deps_xconnect_dict +from vyos.vpp.config_verify import verify_vpp_remove_bridge_interface from vyos.vpp.config_verify import verify_vpp_remove_xconnect_interface from vyos.vpp.utils import cli_ifaces_list @@ -96,6 +98,11 @@ def get_config(config=None) -> dict: for xconn_iface in config['xconn_members'][ifname]: set_dependents('vpp_interfaces_xconnect', conf, xconn_iface) + config['bridge_members'] = deps_bridge_dict(conf) + if ifname in config['bridge_members']: + for bridge_iface in config['bridge_members'][ifname]: + set_dependents('vpp_interfaces_bridge', conf, bridge_iface) + # NAT dependency if conf.exists(['vpp', 'nat', 'nat44']): set_dependents('vpp_nat_nat44', conf) @@ -118,6 +125,7 @@ def verify(config): return None verify_vpp_remove_xconnect_interface(config) + verify_vpp_remove_bridge_interface(config) if 'deleted' in config: return None diff --git a/src/conf_mode/vpp_interfaces_bridge.py b/src/conf_mode/vpp_interfaces_bridge.py index edf3f2dd2..37cec7b94 100644 --- a/src/conf_mode/vpp_interfaces_bridge.py +++ b/src/conf_mode/vpp_interfaces_bridge.py @@ -16,13 +16,12 @@ # with this program; if not, write to the Free Software Foundation, Inc., # 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. -import os - from vyos.config import Config -from vyos.configdict import node_changed +from vyos.configdict import get_interface_dict from vyos import ConfigError -from vyos.vpp.interface import BridgeInterface -from vyos.vpp.utils import iftunnel_transform +from vyos.utils.process import is_systemd_service_active + +from vyos.ifconfig.vpp import VPPBridgeInterface def get_config(config=None) -> dict: @@ -38,65 +37,46 @@ def get_config(config=None) -> dict: else: conf = Config() - base = ['vpp', 'interfaces', 'bridge'] - vpp_interfaces = ['vpp', 'settings', 'interface'] - - ifname = os.environ['VYOS_TAGNODE_VALUE'] + base = ['interfaces', 'vpp', 'bridge'] - # Get config_dict with default values - config = conf.get_config_dict( - base + [ifname], - key_mangling=('-', '_'), - get_first_key=True, - no_tag_node_value_mangle=True, - with_defaults=True, - with_recursive_defaults=True, - ) + ifname, config = get_interface_dict(conf, base) - if not conf.exists(['vpp']): + if not conf.exists(['vpp']) and not conf.exists(base): config['remove_vpp'] = True return config - # Get effective config as we need full dicitonary per interface delete - effective_config = conf.get_config_dict( - base + [ifname], + # Get global vpp interfaces for verify + config['vpp_interfaces'] = conf.get_config_dict( + ['vpp', 'settings', 'interface'], key_mangling=('-', '_'), - effective=True, get_first_key=True, no_tag_node_value_mangle=True, ) - if effective_config: - config.update({'effective': effective_config}) - - if not conf.exists(base + [ifname]): - config['remove'] = True - - # Get global vpp interfaces for verify - config['vpp_interfaces'] = conf.get_config_dict( - vpp_interfaces, + # Get all gre interfaces config + config['gre_interfaces'] = conf.get_config_dict( + ['interfaces', 'vpp', 'gre'], key_mangling=('-', '_'), get_first_key=True, no_tag_node_value_mangle=True, + with_defaults=True, + with_recursive_defaults=True, ) - # determine which members have been removed - interfaces_removed = node_changed(conf, base + [ifname, 'member', 'interface']) - if interfaces_removed: - config['members_removed'] = interfaces_removed - - config['ifname'] = ifname - return config def verify(config): - if 'remove' in config or 'remove_vpp' in config: + if 'deleted' in config or 'remove_vpp' in config: return None - # Check if interface exists in vpp before adding to bridge-domain + if not is_systemd_service_active('vpp.service'): + raise ConfigError( + 'Cannot configure VPP bridge interface: vpp.service is not running' + ) - allowed_prefixes = ('vppbond', 'vppgre', 'geneve', 'vpplo', 'vppvxlan') + # Check if interface exists in vpp before adding to bridge-domain + allowed_prefixes = ('vppbond', 'vppgre', 'vpplo', 'vppvxlan') if 'member' in config: bvi_exists = False @@ -120,6 +100,18 @@ def verify(config): raise ConfigError("BVI can only be defined on loopback interface") bvi_exists = True + # check GRE tunnels as part of the bridge, only tunnel-type "teb" is allowed + # set interfaces vpp bridge vppbr1 member interface vppgre1 + # set interfaces vpp gre vppgre1 tunnel-type teb + if member.startswith('vppgre'): + if member in config.get('gre_interfaces'): + gre_config = config.get('gre_interfaces').get(member) + if gre_config.get('tunnel_type') != 'teb': + raise ConfigError( + f'GRE interface "{member}" in bridge must have tunnel-type "teb". ' + f'Current tunnel-type is "{gre_config.get("tunnel_type")}".' + ) + def generate(config): pass @@ -130,54 +122,13 @@ def apply(config): return None ifname = config.get('ifname') - # vppvxlan10 in the vpp is vxlan_tunnel10 - interface_transform_filter = ('geneve', 'vppvxlan') - # update members - if 'members_removed' in config: - i = BridgeInterface(ifname) - for member in config.get('members_removed'): - if member.startswith(interface_transform_filter): - member = iftunnel_transform(member) - if member.startswith('vpplo'): - # interface name in VPP is loopX - member = member.replace('vpplo', 'loop') - elif member.startswith('vppbond'): - # interface name in VPP is BondEthernetX - member = member.replace('vppbond', 'BondEthernet') - i.detach_member(member=member) - - # Delete bridge domain - if 'effective' in config: - ifname = config.get('ifname') - i = BridgeInterface(ifname) - i.delete() - - if 'remove' in config: - return None + bridge = VPPBridgeInterface(ifname) + bridge.remove() + + if 'deleted' in config: + return - # Add bridge domain - members = config.get('member', {}).get('interface', '') - i = BridgeInterface(ifname) - i.add() - # Add members to bridge - if members: - br = BridgeInterface(ifname) - port_type = 0 - for member, member_config in members.items(): - if member.startswith(interface_transform_filter): - member = iftunnel_transform(member) - if member.startswith('vpplo'): - # interface name in VPP is loopX - member = member.replace('vpplo', 'loop') - if 'bvi' in member_config: - port_type = 1 - elif member.startswith('vppbond'): - # interface name in VPP is BondEthernetX - member = member.replace('vppbond', 'BondEthernet') - - br.add_member(member=member, port_type=port_type) - # set default port type 0 (not BVI) - port_type = 0 + bridge.update(config) return None diff --git a/src/conf_mode/vpp_interfaces_gre.py b/src/conf_mode/vpp_interfaces_gre.py index 453845bcf..1258f906c 100644 --- a/src/conf_mode/vpp_interfaces_gre.py +++ b/src/conf_mode/vpp_interfaces_gre.py @@ -24,11 +24,11 @@ from vyos.configdep import set_dependents, call_dependents from vyos.utils.process import is_systemd_service_active from vyos.ifconfig.vpp import VPPGREInterface +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_xconnect_interface, - verify_vpp_tunnel_source_address, -) +from vyos.vpp.config_verify import verify_vpp_remove_bridge_interface +from vyos.vpp.config_verify import verify_vpp_remove_xconnect_interface +from vyos.vpp.config_verify import verify_vpp_tunnel_source_address from vyos.vpp.utils import cli_ethernet_with_vifs_ifaces @@ -62,6 +62,11 @@ def get_config(config=None) -> dict: for xconn_iface in config['xconn_members'][ifname]: set_dependents('vpp_interfaces_xconnect', conf, xconn_iface) + config['bridge_members'] = deps_bridge_dict(conf) + if ifname in config['bridge_members']: + for bridge_iface in config['bridge_members'][ifname]: + set_dependents('vpp_interfaces_bridge', conf, bridge_iface) + # Get 'vpp settings' config config['vpp_settings'] = conf.get_config_dict( ['vpp', 'settings'], @@ -99,6 +104,7 @@ def verify(config): return None verify_vpp_remove_xconnect_interface(config) + verify_vpp_remove_bridge_interface(config) # config removed if 'deleted' in config: diff --git a/src/conf_mode/vpp_interfaces_vxlan.py b/src/conf_mode/vpp_interfaces_vxlan.py index 9c24b6a01..38349cb4c 100644 --- a/src/conf_mode/vpp_interfaces_vxlan.py +++ b/src/conf_mode/vpp_interfaces_vxlan.py @@ -26,10 +26,9 @@ from vyos.utils.process import is_systemd_service_active from vyos.ifconfig.vpp import VPPVXLANInterface 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_xconnect_interface, - verify_vpp_tunnel_source_address, -) +from vyos.vpp.config_verify import verify_vpp_remove_bridge_interface +from vyos.vpp.config_verify import verify_vpp_remove_xconnect_interface +from vyos.vpp.config_verify import verify_vpp_tunnel_source_address from vyos.vpp.utils import cli_ethernet_with_vifs_ifaces @@ -96,6 +95,7 @@ def verify(config): return None verify_vpp_remove_xconnect_interface(config) + verify_vpp_remove_bridge_interface(config) if 'deleted' in config: return None diff --git a/src/migration-scripts/vpp/5-to-6 b/src/migration-scripts/vpp/5-to-6 index 03c20d970..0131dee33 100644 --- a/src/migration-scripts/vpp/5-to-6 +++ b/src/migration-scripts/vpp/5-to-6 @@ -47,6 +47,8 @@ # # Migrate gre interface to 'interfaces vpp gre', remove interfaces with mode "point-to-multipoint" (T8325) # +# Migrate bridge interface from vpp section to 'interfaces vpp bridge' (T8327) +# from vyos.configtree import ConfigTree @@ -496,6 +498,24 @@ def _migrate_vpp_gre_interface(config: ConfigTree) -> None: config.delete(['vpp', 'interfaces']) +def _migrate_vpp_bridge_interface(config: ConfigTree) -> None: + base = ['vpp', 'interfaces', 'bridge'] + new_base = ['interfaces', 'vpp', 'bridge'] + + if not config.exists(base): + return + + config.set(new_base) + + for ifname in config.list_nodes(base): + _migrate_interface(config, 'bridge', ifname) + + config.delete(base) + + if len(config.list_nodes(['vpp', 'interfaces'])) == 0: + config.delete(['vpp', 'interfaces']) + + def migrate(config: ConfigTree) -> None: if not config.exists(['vpp']): # Nothing to do @@ -516,3 +536,4 @@ def migrate(config: ConfigTree) -> None: _migrate_vpp_ipip_interface(config) _migrate_vpp_loopback_interface(config) _migrate_vpp_gre_interface(config) + _migrate_vpp_bridge_interface(config) diff --git a/src/op_mode/vpp.py b/src/op_mode/vpp.py index 868f780ae..54b46cf68 100755 --- a/src/op_mode/vpp.py +++ b/src/op_mode/vpp.py @@ -277,17 +277,17 @@ class VPPShow: if ifname is None: return None - if not ifname.startswith('br') and not ifname[2:].isdigit(): + if not ifname.startswith('vppbr') and not ifname[5:].isdigit(): raise vyos.opmode.IncorrectValue( - f'"{ifname}" is not a valid bridge interface name (expected brN)' + f'"{ifname}" is not a valid bridge interface name (expected vppbrN)' ) - if not self.config.exists(['vpp', 'interfaces', 'bridge', ifname]): + if not self.config.exists(['interfaces', 'vpp', 'bridge', ifname]): raise vyos.opmode.IncorrectValue( f'Bridge interface {ifname} does not exist' ) - return int(ifname[2:]) + return int(ifname[5:]) def _get_bridge_domain_raw( self, bd_id: typing.Optional[int] = None @@ -401,12 +401,12 @@ def show_lacp_details(raw: bool, ifname: typing.Optional[str]): # ----------------------------- # Bridge op-mode entries # ----------------------------- -@_verify('vpp interfaces bridge') +@_verify('interfaces vpp bridge') def show_bridge(raw: bool, ifname: typing.Optional[str] = None): return VPPShow().bridge_domain(raw, ifname) -@_verify('vpp interfaces bridge') +@_verify('interfaces vpp bridge') def show_bridge_details(raw: bool, ifname: typing.Optional[str] = None): return VPPShow().bridge_domain_details(raw, ifname) |
