summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorViacheslav Hletenko <v.gletenko@vyos.io>2025-02-26 13:08:47 +0200
committerGitHub <noreply@github.com>2025-02-26 13:08:47 +0200
commitf8b0bc6e49d64c682ec78ba9128b57262037c05d (patch)
tree3e9b8ce293996c479c8779b001b233385dcdd2d5
parenta6047cbf6c507587fdcf78507755823c99030778 (diff)
parent47a8502fcdc4a371742b52176074af91debd66a7 (diff)
downloadvyos-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.in13
-rw-r--r--python/vyos/vpp/interface/bridge.py5
-rwxr-xr-xsmoketest/scripts/cli/test_vpp.py29
-rw-r--r--src/conf_mode/vpp_interfaces_bridge.py39
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