diff options
| author | Viacheslav Hletenko <v.gletenko@vyos.io> | 2025-02-26 13:08:47 +0200 |
|---|---|---|
| committer | GitHub <noreply@github.com> | 2025-02-26 13:08:47 +0200 |
| commit | f8b0bc6e49d64c682ec78ba9128b57262037c05d (patch) | |
| tree | 3e9b8ce293996c479c8779b001b233385dcdd2d5 | |
| parent | a6047cbf6c507587fdcf78507755823c99030778 (diff) | |
| parent | 47a8502fcdc4a371742b52176074af91debd66a7 (diff) | |
| download | vyos-1x-f8b0bc6e49d64c682ec78ba9128b57262037c05d.tar.gz vyos-1x-f8b0bc6e49d64c682ec78ba9128b57262037c05d.zip | |
Merge pull request #18 from sever-sever/VD-275
vd-275: Add loopback bvi interface for a bridge member
| -rw-r--r-- | interface-definitions/vpp.xml.in | 13 | ||||
| -rw-r--r-- | python/vyos/vpp/interface/bridge.py | 5 | ||||
| -rwxr-xr-x | smoketest/scripts/cli/test_vpp.py | 29 | ||||
| -rw-r--r-- | src/conf_mode/vpp_interfaces_bridge.py | 39 |
4 files changed, 71 insertions, 15 deletions
diff --git a/interface-definitions/vpp.xml.in b/interface-definitions/vpp.xml.in index 76968fcec..150803a0d 100644 --- a/interface-definitions/vpp.xml.in +++ b/interface-definitions/vpp.xml.in @@ -132,7 +132,7 @@ <help>Bridge member interfaces</help> </properties> <children> - <leafNode name="interface"> + <tagNode name="interface"> <properties> <help>Member interface name</help> <completionHelp> @@ -142,9 +142,16 @@ <format>txt</format> <description>Interface name</description> </valueHelp> - <multi/> </properties> - </leafNode> + <children> + <leafNode name="bvi"> + <properties> + <help>Bridge Virtual Interface (BVI)</help> + <valueless/> + </properties> + </leafNode> + </children> + </tagNode> </children> </node> </children> diff --git a/python/vyos/vpp/interface/bridge.py b/python/vyos/vpp/interface/bridge.py index 407eab08c..5ebe46d45 100644 --- a/python/vyos/vpp/interface/bridge.py +++ b/python/vyos/vpp/interface/bridge.py @@ -70,7 +70,7 @@ class BridgeInterface: """ self.vpp.api.bridge_domain_add_del_v2(is_add=False, bd_id=self.interface_suffix) - def add_member(self, member: str | int): + def add_member(self, member: str | int, port_type: int = 0): """Add member to Bridge interface Attaches a VPP interface to the Bridge interface specified by `interface_suffix`. @@ -80,6 +80,7 @@ class BridgeInterface: Args: member (str or int): The name or index of the VPP network interface to be added as a member to the bridge. + port_type: 0 - Normal port, 1 - BVI port Example: from vyos.vpp.interface import BridgeInterface @@ -96,7 +97,7 @@ class BridgeInterface: member_if_index = self.vpp.get_sw_if_index(member) return self.vpp.api.sw_interface_set_l2_bridge( - rx_sw_if_index=member_if_index, bd_id=bridge_index, port_type=0 + rx_sw_if_index=member_if_index, bd_id=bridge_index, port_type=port_type ) def detach_member(self, member: str | int): diff --git a/smoketest/scripts/cli/test_vpp.py b/smoketest/scripts/cli/test_vpp.py index 69f1410ab..426d19884 100755 --- a/smoketest/scripts/cli/test_vpp.py +++ b/smoketest/scripts/cli/test_vpp.py @@ -740,7 +740,7 @@ class TestVPP(VyOSUnitTestSHIM.TestCase): self.assertRegex(out, r'\s*eth1\s+\d+\s+\d+') # Set non exist member - # expect raise ConfigErro + # expect raise ConfigError self.cli_set( base_path + [ @@ -799,7 +799,7 @@ class TestVPP(VyOSUnitTestSHIM.TestCase): # Perform assertions based on the normalized output self.assertIn('BD-ID Index BSN Age(min)', normalized_out) - self.assertIn('10 1 1 off', normalized_out) + self.assertIn('10 1 0 off', normalized_out) self.assertIn('Learning U-Forwrd UU-Flood Flooding', normalized_out) self.assertIn('on on flood on', normalized_out) self.assertIn('Interface If-idx ISN', normalized_out) @@ -819,6 +819,31 @@ class TestVPP(VyOSUnitTestSHIM.TestCase): self.assertRegex(out, r'\s*eth1\s+\d+\s+\d+') self.assertRegex(out, r'\s*vxlan_tunnel23\s+\d+\s+\d+') + # Add Loopback BVI to the bridge + self.cli_set(base_path + ['interfaces', 'loopback', f'lo{vni}']) + self.cli_set( + base_path + + [ + 'interfaces', + 'bridge', + interface_bridge, + 'member', + 'interface', + f'lo{vni}', + 'bvi', + ] + ) + # commit changes + self.cli_commit() + + # check bridge interface + _, out = rc_cmd('sudo vppctl show bridge-domain 10 detail') + # Normalize the output for consistent whitespace + normalized_out = re.sub(r'\s+', ' ', out) + + self.assertIn('10 1 0 off', normalized_out) + self.assertRegex(out, r'\bloop23\s+\d+\s+\d+\s+\d+\s+\*\s+') + def test_08_vpp_ipip(self): interface_ipip = 'ipip12' interface_kernel = 'vpptun12' diff --git a/src/conf_mode/vpp_interfaces_bridge.py b/src/conf_mode/vpp_interfaces_bridge.py index e7a2427f9..d98067a6a 100644 --- a/src/conf_mode/vpp_interfaces_bridge.py +++ b/src/conf_mode/vpp_interfaces_bridge.py @@ -19,7 +19,7 @@ import os from vyos.config import Config -from vyos.configdict import leaf_node_changed +from vyos.configdict import node_changed from vyos import ConfigError from vyos.vpp.interface import BridgeInterface from vyos.vpp.utils import iftunnel_transform @@ -77,7 +77,7 @@ def get_config(config=None) -> dict: ) # determine which members have been removed - interfaces_removed = leaf_node_changed(conf, base + [ifname, 'member', 'interface']) + interfaces_removed = node_changed(conf, base + [ifname, 'member', 'interface']) if interfaces_removed: config['members_removed'] = interfaces_removed @@ -92,19 +92,30 @@ def verify(config): # Check if interface exists in vpp before adding to bridge-domain - allowed_prefixes = ('gre', 'geneve', 'vxlan') + allowed_prefixes = ('gre', 'geneve', 'lo', 'vxlan') if 'member' in config: - for member in config.get('member', {}).get('interface', []): - # Check if the interface is explicitly listed or starts with allowed prefixes + bvi_exists = False + for member, member_config in ( + config.get('member', {}).get('interface', {}).items() + ): + # Check if the interface exists in VPP settings or starts with allowed prefixes if not ( - member in config.get('vpp_interfaces', []) + member in config.get('vpp_interfaces', {}) or member.startswith(allowed_prefixes) ): raise ConfigError( f"Interface '{member}' not found in 'vpp settings interface' or does not start with allowed prefixes {allowed_prefixes}" ) + # Check if BVI is already defined, only one BVI per bridge domain is allowed + if 'bvi' in member_config: + if bvi_exists: + raise ConfigError("Only one BVI per bridge domain is allowed") + if not member.startswith('lo'): + raise ConfigError("BVI can only be defined on loopback interface") + bvi_exists = True + def generate(config): pass @@ -120,6 +131,9 @@ def apply(config): for member in config.get('members_removed'): if member.startswith(interface_transform_filter): member = iftunnel_transform(member) + if member.startswith('lo'): + # interface name in VPP is loopX + member = member.replace('lo', 'loop') i.detach_member(member=member) # Delete bridge domain @@ -138,10 +152,19 @@ def apply(config): # Add members to bridge if members: br = BridgeInterface(ifname) - for member in members: + port_type = 0 + for member, member_config in members.items(): if member.startswith(interface_transform_filter): member = iftunnel_transform(member) - br.add_member(member=member) + if member.startswith('lo'): + # interface name in VPP is loopX + member = member.replace('lo', 'loop') + if 'bvi' in member_config: + port_type = 1 + + br.add_member(member=member, port_type=port_type) + # set default port type 0 (not BVI) + port_type = 0 return None |
