diff options
| author | Nataliia Solomko <natalirs1985@gmail.com> | 2026-02-26 15:27:27 +0200 |
|---|---|---|
| committer | Nataliia Solomko <natalirs1985@gmail.com> | 2026-02-27 21:26:00 +0200 |
| commit | 3755dfa7612ffa7edc94537220340a00c9db13ad (patch) | |
| tree | 5ab1f39a140deec9c2c3f98e502b7551848b8125 | |
| parent | bfb8537c4e79e18e960cf15064658d3ee50fd05a (diff) | |
| download | vyos-1x-3755dfa7612ffa7edc94537220340a00c9db13ad.tar.gz vyos-1x-3755dfa7612ffa7edc94537220340a00c9db13ad.zip | |
vpp: T8296: Move vxlan interface from vpp section to 'interfaces vpp vxlan'
| -rw-r--r-- | interface-definitions/vpp.xml.in | 22 | ||||
| -rw-r--r-- | interface-definitions/vpp_interface_vxlan.xml.in | 35 | ||||
| -rw-r--r-- | python/vyos/ifconfig/vpp/__init__.py | 2 | ||||
| -rw-r--r-- | python/vyos/ifconfig/vpp/vxlan.py | 125 | ||||
| -rw-r--r-- | python/vyos/vpp/config_verify.py | 2 | ||||
| -rw-r--r-- | python/vyos/vpp/interface/__init__.py | 2 | ||||
| -rw-r--r-- | python/vyos/vpp/interface/vxlan.py | 106 | ||||
| -rw-r--r-- | python/vyos/vpp/interface/xconnect.py | 8 | ||||
| -rw-r--r-- | python/vyos/vpp/utils.py | 18 | ||||
| -rw-r--r-- | smoketest/configs/assert/vpp | 6 | ||||
| -rwxr-xr-x | smoketest/scripts/cli/test_vpp.py | 123 | ||||
| -rw-r--r-- | src/conf_mode/vpp_interfaces_bridge.py | 6 | ||||
| -rw-r--r-- | src/conf_mode/vpp_interfaces_vxlan.py | 118 | ||||
| -rw-r--r-- | src/migration-scripts/vpp/5-to-6 | 106 |
14 files changed, 320 insertions, 359 deletions
diff --git a/interface-definitions/vpp.xml.in b/interface-definitions/vpp.xml.in index 9f245aaf3..cf947a29d 100644 --- a/interface-definitions/vpp.xml.in +++ b/interface-definitions/vpp.xml.in @@ -186,28 +186,6 @@ #include <include/kernel-interface-tun.xml.i> </children> </tagNode> - <tagNode name="vxlan" owner="${vyos_conf_scripts_dir}/vpp_interfaces_vxlan.py"> - <properties> - <priority>325</priority> - <help>Virtual Extensible LAN (VXLAN) Interface</help> - <constraint> - <regex>vxlan[0-9]+</regex> - </constraint> - <constraintErrorMessage>VXLAN interface must be named vxlanN</constraintErrorMessage> - <valueHelp> - <format>vxlanN</format> - <description>VXLAN interface name</description> - </valueHelp> - </properties> - <children> - #include <include/generic-description.xml.i> - #include <include/interface/disable.xml.i> - #include <include/source-address-ipv4-ipv6.xml.i> - #include <include/interface/tunnel-remote.xml.i> - #include <include/vni.xml.i> - #include <include/kernel-interface-tap.xml.i> - </children> - </tagNode> <tagNode name="xconnect" owner="${vyos_conf_scripts_dir}/vpp_interfaces_xconnect.py"> <properties> <help>Layer 2 cross connect</help> diff --git a/interface-definitions/vpp_interface_vxlan.xml.in b/interface-definitions/vpp_interface_vxlan.xml.in new file mode 100644 index 000000000..2d0ae42ab --- /dev/null +++ b/interface-definitions/vpp_interface_vxlan.xml.in @@ -0,0 +1,35 @@ +<?xml version="1.0"?> +<interfaceDefinition> + <node name='interfaces'> + <children> + <node name='vpp'> + <children> + <tagNode name="vxlan" owner="${vyos_conf_scripts_dir}/vpp_interfaces_vxlan.py"> + <properties> + <priority>325</priority> + <help>Virtual Extensible LAN (VXLAN) Interface</help> + <constraint> + <regex>vppvxlan[0-9]+</regex> + </constraint> + <constraintErrorMessage>VXLAN interface must be named vppvxlanN</constraintErrorMessage> + <valueHelp> + <format>vppvxlanN</format> + <description>VXLAN interface name</description> + </valueHelp> + </properties> + <children> + #include <include/generic-description.xml.i> + #include <include/interface/disable.xml.i> + #include <include/source-address-ipv4-ipv6.xml.i> + #include <include/interface/tunnel-remote.xml.i> + #include <include/vni.xml.i> + #include <include/interface/address-ipv4-ipv6.xml.i> + #include <include/interface/mtu-68-16000.xml.i> + #include <include/vpp/vif.xml.i> + </children> + </tagNode> + </children> + </node> + </children> + </node> +</interfaceDefinition> diff --git a/python/vyos/ifconfig/vpp/__init__.py b/python/vyos/ifconfig/vpp/__init__.py index 117acb8d0..bda2f76c8 100644 --- a/python/vyos/ifconfig/vpp/__init__.py +++ b/python/vyos/ifconfig/vpp/__init__.py @@ -17,8 +17,10 @@ from .bond import VPPBondInterface from .interface import VPPInterface +from .vxlan import VPPVXLANInterface __all__ = [ 'VPPBondInterface', 'VPPInterface', + 'VPPVXLANInterface', ] diff --git a/python/vyos/ifconfig/vpp/vxlan.py b/python/vyos/ifconfig/vpp/vxlan.py new file mode 100644 index 000000000..8ca9bee70 --- /dev/null +++ b/python/vyos/ifconfig/vpp/vxlan.py @@ -0,0 +1,125 @@ +# VyOS implementation of VPP VXLAN interface +# +# Copyright (C) VyOS Inc. +# +# This program is free software; you can redistribute it and/or modify +# it under the terms of the GNU General Public License as published by +# the Free Software Foundation; either version 2 of the License, or +# (at your option) any later version. +# +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. +# +# You should have received a copy of the GNU General Public License along +# with this program; if not, write to the Free Software Foundation, Inc., +# 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. + +from vyos.ifconfig import Interface +from vyos.ifconfig.vpp.interface import VPPInterface + + +class VPPVXLANInterface(Interface, VPPInterface): + """Interface VXLAN""" + + def __init__(self, ifname, config): + self.ifname = ifname + self.instance = int(ifname.removeprefix('vppvxlan')) + self.vpp_ifname = f'vxlan_tunnel{self.instance}' + + super().__init__(ifname) + VPPInterface.__init__(self, self.vpp_ifname) + + self.index = self.vpp.get_sw_if_index(self.vpp_ifname) + self.src_address = config.get('source_address') + self.dst_address = config.get('remote') + self.vni = int(config.get('vni', 0)) + self.state = 'up' if 'disable' not in config else 'down' + + def _create(self): + pass + + def get_vxlan(self): + tunnels = self.vpp.api.vxlan_tunnel_dump(sw_if_index=self.index) + return tunnels[0] if tunnels else None + + def add_vxlan(self): + """Create VXLAN interface + https://github.com/FDio/vpp/blob/stable/2306/src/plugins/vxlan/vxlan.api + + Example: + from vyos.ifconfig.vpp import VPPVXLANInterface + a = VPPVXLANInterface(ifname='vppvxlan23', config) + a.add_vxlan() + """ + self.vpp.api.vxlan_add_del_tunnel_v3( + is_add=True, + src_address=self.src_address, + dst_address=self.dst_address, + vni=self.vni, + instance=self.instance, + decap_next_index=1, + is_l3=False, + ) + # Add LCP pair (kernel) interface + self.kernel_add() + # Set interface state + self.set_state(self.state) + self.set_admin_state(self.state) + self.index = self.vpp.get_sw_if_index(self.vpp_ifname) + + def delete_vxlan(self): + """Delete VXLAN interface + Example: + from vyos.ifconfig.vpp import VPPVXLANInterface + a = VPPVXLANInterface(ifname='vppvxlan23', config) + a.delete_vxlan() + """ + vxlan = self.get_vxlan() + if vxlan: + return self.vpp.api.vxlan_add_del_tunnel_v3( + is_add=False, + src_address=vxlan.src_address, + dst_address=vxlan.dst_address, + vni=vxlan.vni, + is_l3=False, + ) + + def kernel_add(self): + """Add LCP pair + Example: + from vyos.ifconfig.vpp import VPPVXLANInterface + a = VPPVXLANInterface(ifname='vppvxlan23', config) + a.kernel_add() + """ + self.vpp.lcp_pair_add(self.vpp_ifname, self.ifname) + + def kernel_delete(self): + """Delete LCP pair + Example: + from vyos.ifconfig.vpp import VPPVXLANInterface + a = VPPVXLANInterface(ifname='vppvxlan23', config) + a.kernel_delete() + """ + self.vpp.lcp_pair_del(self.vpp_ifname, self.ifname) + + def remove(self): + if self.index: + # Delete lcp pair interface + if self.vpp.lcp_pair_find(vpp_name_hw=self.vpp_ifname): + self.kernel_delete() + + # Delete vxlan interface + self.delete_vxlan() + + def update(self, config): + # Add vxlan interface + self.add_vxlan() + + # Set rx-mode + rx_mode = config.get('vpp_settings', {}).get('interface_rx_mode') + if rx_mode: + self.set_rx_mode(rx_mode) + + super().update(config) diff --git a/python/vyos/vpp/config_verify.py b/python/vyos/vpp/config_verify.py index 1e56457da..84c32f1e6 100644 --- a/python/vyos/vpp/config_verify.py +++ b/python/vyos/vpp/config_verify.py @@ -98,7 +98,7 @@ def verify_vpp_exists_kernel_interface(config: dict): def verify_vpp_remove_xconnect_interface(config: dict): - if not config.get('remove'): + if not 'deleted' in config: return for xconn_member, xconn_iface in config.get('xconn_members').items(): if xconn_member == config.get('ifname'): diff --git a/python/vyos/vpp/interface/__init__.py b/python/vyos/vpp/interface/__init__.py index c54e5ca60..f17a12d43 100644 --- a/python/vyos/vpp/interface/__init__.py +++ b/python/vyos/vpp/interface/__init__.py @@ -22,7 +22,6 @@ from .gre import GREInterface from .interface import Interface from .ipip import IPIPInterface from .loopback import LoopbackInterface -from .vxlan import VXLANInterface from .wireguard import WireguardInterface from .xconnect import XconnectInterface @@ -34,7 +33,6 @@ __all__ = [ 'Interface', 'IPIPInterface', 'LoopbackInterface', - 'VXLANInterface', 'WireguardInterface', 'XconnectInterface', ] diff --git a/python/vyos/vpp/interface/vxlan.py b/python/vyos/vpp/interface/vxlan.py deleted file mode 100644 index 4be5b5ec9..000000000 --- a/python/vyos/vpp/interface/vxlan.py +++ /dev/null @@ -1,106 +0,0 @@ -# VyOS implementation of VPP VXLAN interface -# -# Copyright (C) VyOS Inc. -# -# This program is free software; you can redistribute it and/or modify -# it under the terms of the GNU General Public License as published by -# the Free Software Foundation; either version 2 of the License, or -# (at your option) any later version. -# -# This program is distributed in the hope that it will be useful, -# but WITHOUT ANY WARRANTY; without even the implied warranty of -# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -# GNU General Public License for more details. -# -# You should have received a copy of the GNU General Public License along -# with this program; if not, write to the Free Software Foundation, Inc., -# 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. - -from vyos.vpp import VPPControl -from vyos.vpp.interface.interface import Interface - - -def show(): - """Show VXLAN interface - Example: - from vyos.vpp.interface import vxlan - vxlan.show() - """ - vpp = VPPControl() - return vpp.api.vxlan_tunnel_dump() - - -class VXLANInterface(Interface): - """Interface VXLAN""" - - def __init__( - self, - ifname, - source_address, - remote, - vni, - kernel_interface: str = '', - state: str = 'up', - ): - super().__init__(ifname) - self.instance = int(ifname.removeprefix('vxlan')) - self.ifname = f'vxlan_tunnel{self.instance}' - self.src_address = source_address - self.dst_address = remote - self.vni = vni - self.kernel_interface = kernel_interface - self.initial_state = state - - def add(self): - """Create VXLAN interface - https://github.com/FDio/vpp/blob/stable/2306/src/plugins/vxlan/vxlan.api - - Example: - from vyos.vpp.interface import VXLANInterface - a = VXLANInterface(ifname='vxlan23', source_address='192.0.2.1', remote='203.0.113.23', vni=23) - a.add() - """ - self.vpp.api.vxlan_add_del_tunnel_v3( - is_add=True, - src_address=self.src_address, - dst_address=self.dst_address, - vni=self.vni, - instance=self.instance, - decap_next_index=1, - is_l3=False, - ) - # Set interface state - self.set_state(self.initial_state) - - def delete(self): - """Delete VXLAN interface - Example: - from vyos.vpp.interface import VXLANInterface - a = VXLANInterface(ifname='vxlan23', source_address='192.0.2.1', remote='203.0.113.23', vni=23) - a.delete() - """ - return self.vpp.api.vxlan_add_del_tunnel_v3( - is_add=False, - src_address=self.src_address, - dst_address=self.dst_address, - vni=self.vni, - is_l3=False, - ) - - def kernel_add(self): - """Add LCP pair - Example: - from vyos.vpp.interface import VXLANInterface - a = VXLANInterface(ifname='vxlan23', source_address='192.0.2.1', remote='203.0.113.23', vni=23, kernel_interface='vpptap10') - a.kernel_add() - """ - self.vpp.lcp_pair_add(self.ifname, self.kernel_interface) - - def kernel_delete(self): - """Delete LCP pair - Example: - from vyos.vpp.interface import VXLANInterface - a = VXLANInterface(ifname='vxlan23', source_address='192.0.2.1', remote='203.0.113.23', vni=23) - a.kernel_delete() - """ - self.vpp.lcp_pair_del(self.ifname, self.kernel_interface) diff --git a/python/vyos/vpp/interface/xconnect.py b/python/vyos/vpp/interface/xconnect.py index 299a61405..603f7f90b 100644 --- a/python/vyos/vpp/interface/xconnect.py +++ b/python/vyos/vpp/interface/xconnect.py @@ -42,8 +42,8 @@ class XconnectInterface: a.add_l2_xconnect() """ interface_transform_filter = ('vxlan', 'gre') - first_member = self.members[0] - second_member = self.members[1] + first_member = self.members[0].removeprefix('vpp') + second_member = self.members[1].removeprefix('vpp') # Check if member in required filter to transform 'vxlanX' => 'vxlan_tunnelX' if first_member.startswith(interface_transform_filter): first_member = iftunnel_transform(first_member) @@ -73,8 +73,8 @@ class XconnectInterface: a.del_l2_xconnect() """ interface_transform_filter = ('vxlan', 'gre') - first_member = self.members[0] - second_member = self.members[1] + first_member = self.members[0].removeprefix('vpp') + second_member = self.members[1].removeprefix('vpp') # Check if member in required filter to transform 'vxlanX' => 'vxlan_tunnelX' if first_member.startswith(interface_transform_filter): first_member = iftunnel_transform(first_member) diff --git a/python/vyos/vpp/utils.py b/python/vyos/vpp/utils.py index 5ae4cd296..5976b2189 100644 --- a/python/vyos/vpp/utils.py +++ b/python/vyos/vpp/utils.py @@ -27,7 +27,7 @@ mem_shift = {'K': 10, 'KB': 10, 'M': 20, 'MB': 20, 'G': 30, 'GB': 30} def iftunnel_transform(iface: str) -> str: - """Transform interface name from `xxxNN` to `xxx_tunnelNN` + """Transform interface name from `vppxxxNN` to `xxx_tunnelNN` Args: iface (str): original interface name @@ -38,6 +38,8 @@ def iftunnel_transform(iface: str) -> str: Returns: str: Transformed interface name """ + # Remove vpp prefix + iface = iface.removeprefix('vpp') # Check format if not iface[0].isascii() or not iface[-1].isdecimal(): raise ValueError(f'Wrong interface name format: {iface}') @@ -87,6 +89,15 @@ def cli_ifaces_list(config_instance, mode: str = 'candidate') -> list[str]: with_recursive_defaults=True, ) + interfaces_config = config_instance.get_config_dict( + ['interfaces', 'vpp'], + key_mangling=('-', '_'), + effective=effective_mode, + get_first_key=True, + no_tag_node_value_mangle=True, + with_recursive_defaults=True, + ) + vpp_ifaces: list[str] = [] # Get a list of Ethernet interfaces @@ -98,6 +109,11 @@ def cli_ifaces_list(config_instance, mode: str = 'candidate') -> list[str]: for iface in config.get('interfaces', {}).get(iface_type, {}).keys(): vpp_ifaces.append(iface) + # Get a list of interfaces VPP + for iface_type in interfaces_config.keys(): + for iface in interfaces_config.get(iface_type, {}).keys(): + vpp_ifaces.append(iface) + return vpp_ifaces diff --git a/smoketest/configs/assert/vpp b/smoketest/configs/assert/vpp index 0859e7a1a..495b41f36 100644 --- a/smoketest/configs/assert/vpp +++ b/smoketest/configs/assert/vpp @@ -39,9 +39,9 @@ set vpp interfaces gre gre12 tunnel-type 'l3' set vpp interfaces ipip ipip20 remote '192.0.2.2' set vpp interfaces ipip ipip20 source-address '192.0.2.1' set vpp interfaces loopback lo12 description 'Loop12' -set vpp interfaces vxlan vxlan10 remote '192.0.2.2' -set vpp interfaces vxlan vxlan10 source-address '192.0.2.1' -set vpp interfaces vxlan vxlan10 vni '10' +set interfaces vpp vxlan vppvxlan10 remote '192.0.2.2' +set interfaces vpp vxlan vppvxlan10 source-address '192.0.2.1' +set interfaces vpp vxlan vppvxlan10 vni '10' set vpp settings interface eth1 set vpp settings interface eth2 set vpp settings interface eth3 diff --git a/smoketest/scripts/cli/test_vpp.py b/smoketest/scripts/cli/test_vpp.py index db4c811cc..c2a903229 100755 --- a/smoketest/scripts/cli/test_vpp.py +++ b/smoketest/scripts/cli/test_vpp.py @@ -211,57 +211,34 @@ class TestVPP(VyOSUnitTestSHIM.TestCase): self.assertTrue(icmpv6_ra_punt_feature.is_enabled) def test_02_vpp_vxlan(self): + vxlan_path = interfaces_path + ['vxlan'] vni = '23' - interface_vxlan = f'vxlan{vni}' - interface_kernel = f'vpptap{vni}' - new_interface_kernel = f'vpptap1{vni}' + interface_vxlan = f'vppvxlan{vni}' source_address = '192.0.2.1' new_source_address = '192.0.2.3' remote_address = '192.0.2.254' - kernel_address = '203.0.113.1' + address = '203.0.113.1' self.cli_set(['interfaces', 'ethernet', interface, 'address', '192.0.2.1/24']) - self.cli_set( - base_path - + ['interfaces', 'vxlan', interface_vxlan, 'source-address', source_address] - ) - self.cli_set(base_path + ['interfaces', 'vxlan', interface_vxlan, 'vni', vni]) + self.cli_set(vxlan_path + [interface_vxlan, 'source-address', source_address]) + self.cli_set(vxlan_path + [interface_vxlan, 'vni', vni]) # remote and source address must not be the same # expect raise ConfigError - self.cli_set( - base_path - + ['interfaces', 'vxlan', interface_vxlan, 'remote', source_address] - ) + self.cli_set(vxlan_path + [interface_vxlan, 'remote', source_address]) with self.assertRaises(ConfigSessionError): self.cli_commit() - self.cli_set( - base_path - + ['interfaces', 'vxlan', interface_vxlan, 'remote', remote_address] - ) - self.cli_set( - base_path - + [ - 'interfaces', - 'vxlan', - interface_vxlan, - 'kernel-interface', - interface_kernel, - ] - ) - self.cli_set( - base_path - + ['kernel-interfaces', interface_kernel, 'address', f'{kernel_address}/24'] - ) + self.cli_set(vxlan_path + [interface_vxlan, 'remote', remote_address]) + self.cli_set(vxlan_path + [interface_vxlan, 'address', f'{address}/24']) # commit changes self.cli_commit() - self.assertTrue(os.path.isdir(f'/sys/class/net/{interface_kernel}')) + self.assertTrue(os.path.isdir(f'/sys/class/net/{interface_vxlan}')) - current_address = get_address(interface_kernel) - self.assertEqual(kernel_address, current_address) + current_address = get_address(interface_vxlan) + self.assertEqual(address, current_address) # check vxlan interface _, out = rc_cmd('sudo vppctl show vxlan tunnel') @@ -270,14 +247,7 @@ class TestVPP(VyOSUnitTestSHIM.TestCase): # update vxlan interface self.cli_set( - base_path - + [ - 'interfaces', - 'vxlan', - interface_vxlan, - 'source-address', - new_source_address, - ] + vxlan_path + [interface_vxlan, 'source-address', new_source_address] ) # source address of the tunnel interface should be configured @@ -304,8 +274,8 @@ class TestVPP(VyOSUnitTestSHIM.TestCase): f'[0] instance {vni} src {new_source_address} dst {remote_address}' ) self.assertIn(required_str, out) - self.assertTrue(os.path.isdir(f'/sys/class/net/{interface_kernel}')) - self.assertEqual(kernel_address, current_address) + self.assertTrue(os.path.isdir(f'/sys/class/net/{interface_vxlan}')) + self.assertEqual(address, current_address) # change vpp settings self.cli_set(base_path + ['settings', 'poll-sleep-usec', '5']) @@ -314,49 +284,8 @@ class TestVPP(VyOSUnitTestSHIM.TestCase): config = read_file(VPP_CONF) self.assertIn('poll-sleep-usec 5', config) - # delete vxlan kernel-interface but do not delete 'vpp kernel-interface' - # expect raise ConfigError - self.cli_delete( - base_path - + [ - 'interfaces', - 'vxlan', - interface_vxlan, - 'kernel-interface', - interface_kernel, - ] - ) - with self.assertRaises(ConfigSessionError): - self.cli_commit() - - # update vxlan kernel-interface but do not change 'vpp kernel-interface' - # expect raise ConfigError - self.cli_set( - base_path - + [ - 'interfaces', - 'vxlan', - interface_vxlan, - 'kernel-interface', - new_interface_kernel, - ] - ) - with self.assertRaises(ConfigSessionError): - self.cli_commit() - - # delete vpp kernel-interface - self.cli_delete(base_path + ['kernel-interfaces', interface_kernel]) - self.cli_commit() - - # delete vxlan kernel-interface - self.cli_delete( - base_path + ['interfaces', 'vxlan', interface_vxlan, 'kernel-interface'] - ) - self.cli_commit() - self.assertFalse(os.path.isdir(f'/sys/class/net/{interface_kernel}')) - # delete vxlan interface - self.cli_delete(base_path + ['interfaces', 'vxlan', interface_vxlan]) + self.cli_delete(vxlan_path + [interface_vxlan]) self.cli_commit() # delete vif Ethernet interface @@ -756,7 +685,7 @@ class TestVPP(VyOSUnitTestSHIM.TestCase): members = [interface] interface_bridge = 'br10' vni = '23' - interface_vxlan = f'vxlan{vni}' + interface_vxlan = f'vppvxlan{vni}' source_address = '192.0.2.1' remote_address = '192.0.2.254' @@ -822,14 +751,13 @@ class TestVPP(VyOSUnitTestSHIM.TestCase): # Add VXLAN to the bridge self.cli_set( - base_path - + ['interfaces', 'vxlan', interface_vxlan, 'source-address', source_address] + interfaces_path + + ['vxlan', interface_vxlan, 'source-address', source_address] ) self.cli_set( - base_path - + ['interfaces', 'vxlan', interface_vxlan, 'remote', remote_address] + interfaces_path + ['vxlan', interface_vxlan, 'remote', remote_address] ) - self.cli_set(base_path + ['interfaces', 'vxlan', interface_vxlan, 'vni', vni]) + self.cli_set(interfaces_path + ['vxlan', interface_vxlan, 'vni', vni]) self.cli_set( base_path + [ @@ -1017,21 +945,20 @@ class TestVPP(VyOSUnitTestSHIM.TestCase): def test_09_vpp_xconnect(self): vni = '23' - interface_vxlan = f'vxlan{vni}' + interface_vxlan = f'vppvxlan{vni}' interface_xconnect = f'xcon{vni}' source_address = '192.0.2.1' remote_address = '192.0.2.254' self.cli_set(['interfaces', 'ethernet', interface, 'address', '192.0.2.1/24']) self.cli_set( - base_path - + ['interfaces', 'vxlan', interface_vxlan, 'source-address', source_address] + interfaces_path + + ['vxlan', interface_vxlan, 'source-address', source_address] ) self.cli_set( - base_path - + ['interfaces', 'vxlan', interface_vxlan, 'remote', remote_address] + interfaces_path + ['vxlan', interface_vxlan, 'remote', remote_address] ) - self.cli_set(base_path + ['interfaces', 'vxlan', interface_vxlan, 'vni', vni]) + self.cli_set(interfaces_path + ['vxlan', interface_vxlan, 'vni', vni]) # Add xconneect self.cli_set( diff --git a/src/conf_mode/vpp_interfaces_bridge.py b/src/conf_mode/vpp_interfaces_bridge.py index e86381cc3..8d25cba00 100644 --- a/src/conf_mode/vpp_interfaces_bridge.py +++ b/src/conf_mode/vpp_interfaces_bridge.py @@ -96,7 +96,7 @@ def verify(config): # Check if interface exists in vpp before adding to bridge-domain - allowed_prefixes = ('vppbond', 'gre', 'geneve', 'lo', 'vxlan') + allowed_prefixes = ('vppbond', 'gre', 'geneve', 'lo', 'vppvxlan') if 'member' in config: bvi_exists = False @@ -130,8 +130,8 @@ def apply(config): return None ifname = config.get('ifname') - # vxlan10 in the vpp is vxlan_tunnel10 - interface_transform_filter = ('geneve', 'vxlan') + # vppvxlan10 in the vpp is vxlan_tunnel10 + interface_transform_filter = ('geneve', 'vppvxlan') # update members if 'members_removed' in config: i = BridgeInterface(ifname) diff --git a/src/conf_mode/vpp_interfaces_vxlan.py b/src/conf_mode/vpp_interfaces_vxlan.py index 4a0b157e5..9c24b6a01 100644 --- a/src/conf_mode/vpp_interfaces_vxlan.py +++ b/src/conf_mode/vpp_interfaces_vxlan.py @@ -16,26 +16,21 @@ # 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 import ConfigError from vyos.config import Config -from vyos.configdict import leaf_node_changed +from vyos.configdict import get_interface_dict from vyos.configdep import set_dependents, call_dependents -from vyos.template import is_interface +from vyos.utils.process import is_systemd_service_active -from vyos.vpp.interface import VXLANInterface +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_kernel_interface, - verify_vpp_change_kernel_interface, verify_vpp_remove_xconnect_interface, - verify_vpp_exists_kernel_interface, verify_vpp_tunnel_source_address, ) -from vyos.vpp.utils import cli_ifaces_lcp_kernel_list, cli_ethernet_with_vifs_ifaces +from vyos.vpp.utils import cli_ethernet_with_vifs_ifaces def get_config(config=None) -> dict: @@ -51,55 +46,14 @@ def get_config(config=None) -> dict: else: conf = Config() - base = ['vpp', 'interfaces', 'vxlan'] - base_kernel_interfaces = ['vpp', 'kernel-interfaces'] - - ifname = os.environ['VYOS_TAGNODE_VALUE'] + base = ['interfaces', 'vpp', 'vxlan'] - # 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], - 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 kernel-interfaces' for verify - config['vpp_kernel_interfaces'] = conf.get_config_dict( - base_kernel_interfaces, - key_mangling=('-', '_'), - get_first_key=True, - no_tag_node_value_mangle=True, - ) - - tmp = leaf_node_changed(conf, base + [ifname, 'kernel-interface']) - if tmp: - config['kernel_interface_removed'] = tmp - - # list of all kernel interfaces `vpp interface xxx kernel-interface xxx` - config['candidate_kernel_interfaces'] = cli_ifaces_lcp_kernel_list(conf) - # list of all Ethernet interfaces with vifs config['vpp_ether_vif_ifaces'] = cli_ethernet_with_vifs_ifaces(conf) @@ -114,11 +68,14 @@ def get_config(config=None) -> dict: for bridge_iface in config['bridge_members'][ifname]: set_dependents('vpp_interfaces_bridge', conf, bridge_iface) - if effective_config.get('kernel_interface'): - if conf.exists(base + [ifname, 'kernel-interface']): - iface = config.get('kernel_interface') - if conf.exists(['vpp', 'kernel-interfaces', iface]): - set_dependents('vpp_kernel_interface', conf, iface) + # Get 'vpp settings' config with default values + config['vpp_settings'] = conf.get_config_dict( + ['vpp', 'settings'], + key_mangling=('-', '_'), + get_first_key=True, + no_tag_node_value_mangle=True, + with_defaults=True, + ) # NAT dependency if conf.exists(['vpp', 'nat', 'nat44']): @@ -130,7 +87,6 @@ def get_config(config=None) -> dict: if conf.exists(['vpp', 'acl']): set_dependents('vpp_acl', conf) - config['ifname'] = ifname return config @@ -139,16 +95,16 @@ def verify(config): if 'remove_vpp' in config: return None - # Verify that removed kernel interface is not used in 'vpp kernel-interfaces'. - # vpp interfaces vxlan vxlanX kernel-interface vpp-tunX - # vpp kernel-interface vpp-tunX - verify_vpp_remove_kernel_interface(config) - verify_vpp_remove_xconnect_interface(config) - if 'remove' in config: + if 'deleted' in config: return None + if not is_systemd_service_active('vpp.service'): + raise ConfigError( + 'Cannot configure VPP vxlan interface: vpp.service is not running' + ) + required_keys = {'source_address', 'remote', 'vni'} if not all(key in config for key in required_keys): missing_keys = required_keys - set(config.keys()) @@ -161,12 +117,6 @@ def verify(config): if config.get('source_address') == config.get('remote'): raise ConfigError('Remote address must not be the same as source address') - # Change 'vpp interfaces vxlan greX kernel-interface vpp-tunX' - # => 'vpp interfaces gre vxlanX kernel-interface vpp-tunY' - # check if we have kernel interface config 'vpp kernel-interface vpp-tunX' - verify_vpp_change_kernel_interface(config) - verify_vpp_exists_kernel_interface(config) - def generate(config): pass @@ -178,29 +128,13 @@ def apply(config): ifname = config.get('ifname') # Delete interface - if 'effective' in config: - remove_config = config.get('effective') - src_addr = remove_config.get('source_address') - dst_addr = remove_config.get('remote') - vni = int(remove_config.get('vni')) - i = VXLANInterface(ifname, src_addr, dst_addr, vni) - i.delete() - - if 'remove' in config: + vxlan = VPPVXLANInterface(ifname, config) + vxlan.remove() + + if 'deleted' in config: return None - # Add interface - src_addr = config.get('source_address') - dst_addr = config.get('remote') - vni = int(config.get('vni')) - kernel_interface = config.get('kernel_interface', '') - state = 'up' if 'disable' not in config else 'down' - i = VXLANInterface(ifname, src_addr, dst_addr, vni, kernel_interface, state) - i.add() - - # Add kernel-interface (LCP) if interface is not exist - if 'kernel_interface' in config and not is_interface(kernel_interface): - i.kernel_add() + vxlan.update(config) call_dependents() diff --git a/src/migration-scripts/vpp/5-to-6 b/src/migration-scripts/vpp/5-to-6 index 84b91ce40..25529cf36 100644 --- a/src/migration-scripts/vpp/5-to-6 +++ b/src/migration-scripts/vpp/5-to-6 @@ -39,6 +39,8 @@ # # Move bonding interface from vpp section to 'interfaces vpp bonding' (T8283) # +# Move vxlan interface from vpp section to 'interfaces vpp vxlan' (T8296) +# from vyos.configtree import ConfigTree @@ -290,6 +292,55 @@ def _migrate_vpp_resources(config: ConfigTree) -> None: config.delete(new_base) +def _migrate_interface(config, type, ifname): + base = ['vpp', 'interfaces', type] + new_base = ['interfaces', 'vpp', type] + kernel_interface_base = ['vpp', 'kernel-interfaces'] + + kernel_interface_path = base + [ifname, 'kernel-interface'] + kernel_iface = None + if config.exists(kernel_interface_path): + kernel_iface = config.return_value(kernel_interface_path) + config.delete(kernel_interface_path) + new_ifname = f'vpp{ifname}' + iface_base = new_base + [new_ifname] + config.copy(base + [ifname], iface_base) + config.set_tag(new_base) + + if kernel_iface and config.exists(kernel_interface_base + [kernel_iface]): + if config.exists(kernel_interface_base + [kernel_iface, 'vif']): + config.copy(kernel_interface_base + [kernel_iface, 'vif'], iface_base + ['vif']) + if config.exists(kernel_interface_base + [kernel_iface, 'mtu']): + config.copy(kernel_interface_base + [kernel_iface, 'mtu'], iface_base + ['mtu']) + if config.exists(kernel_interface_base + [kernel_iface, 'address']): + config.copy(kernel_interface_base + [kernel_iface, 'address'], iface_base + ['address']) + config.delete(kernel_interface_base + [kernel_iface]) + + +def _migrate_members_bridge(config, ifname): + bridge_path = ['vpp', 'interfaces', 'bridge'] + if config.exists(bridge_path): + for iface in config.list_nodes(bridge_path): + tmp = bridge_path + [iface, 'member', 'interface'] + if config.exists(tmp): + for name in config.list_nodes(tmp): + if name == ifname: + new_name = f'vpp{name}' + config.rename(tmp + [name], new_name) + + +def _migrate_members_xconnect(config, ifname): + xconnect_path = ['vpp', 'interfaces', 'xconnect'] + if config.exists(xconnect_path): + for iface in config.list_nodes(xconnect_path): + tmp = xconnect_path + [iface, 'member', 'interface'] + if config.exists(tmp): + for name in config.return_values(tmp): + if name == ifname: + new_name = f'vpp{name}' + config.delete_value(tmp, name) + config.set(tmp, value=new_name, replace=False) + def _migrate_vpp_bonding_interface(config: ConfigTree) -> None: base = ['vpp', 'interfaces', 'bonding'] new_base = ['interfaces', 'vpp', 'bonding'] @@ -301,24 +352,7 @@ def _migrate_vpp_bonding_interface(config: ConfigTree) -> None: config.set(new_base) for ifname in config.list_nodes(base): - bond_kernel_interface_path = base + [ifname, 'kernel-interface'] - kernel_iface = None - if config.exists(bond_kernel_interface_path): - kernel_iface = config.return_value(bond_kernel_interface_path) - config.delete(bond_kernel_interface_path) - new_ifname = f'vpp{ifname}' - iface_base = new_base + [new_ifname] - config.copy(base + [ifname], iface_base) - config.set_tag(new_base) - - if kernel_iface and config.exists(kernel_interface_base + [kernel_iface]): - if config.exists(kernel_interface_base + [kernel_iface, 'vif']): - config.copy(kernel_interface_base + [kernel_iface, 'vif'], iface_base + ['vif']) - if config.exists(kernel_interface_base + [kernel_iface, 'mtu']): - config.copy(kernel_interface_base + [kernel_iface, 'mtu'], iface_base + ['mtu']) - if config.exists(kernel_interface_base + [kernel_iface, 'address']): - config.copy(kernel_interface_base + [kernel_iface, 'address'], iface_base + ['address']) - config.delete(kernel_interface_base + [kernel_iface]) + _migrate_interface(config, 'bonding', ifname) for feature in ['nat44', 'cgnat']: for direction in ['inside', 'outside']: @@ -341,15 +375,32 @@ def _migrate_vpp_bonding_interface(config: ConfigTree) -> None: new_name = f'vpp{name}' config.rename(ipfix_path + [name], new_name) - bridge_path = ['vpp', 'interfaces', 'bridge'] - if config.exists(bridge_path): - for iface in config.list_nodes(bridge_path): - tmp = bridge_path + [iface, 'member', 'interface'] - if config.exists(tmp): - for name in config.list_nodes(tmp): - if name == ifname: - new_name = f'vpp{name}' - config.rename(tmp + [name], new_name) + _migrate_members_bridge(config, ifname) + + config.delete(base) + + if config.exists(kernel_interface_base) and len(config.list_nodes(kernel_interface_base)) == 0: + config.delete(['vpp', 'kernel-interfaces']) + + if len(config.list_nodes(['vpp', 'interfaces'])) == 0: + config.delete(['vpp', 'interfaces']) + + +def _migrate_vpp_vxlan_interface(config: ConfigTree) -> None: + base = ['vpp', 'interfaces', 'vxlan'] + new_base = ['interfaces', 'vpp', 'vxlan'] + kernel_interface_base = ['vpp', 'kernel-interfaces'] + + if not config.exists(base): + return + + config.set(new_base) + + for ifname in config.list_nodes(base): + _migrate_interface(config, 'vxlan', ifname) + + _migrate_members_bridge(config, ifname) + _migrate_members_xconnect(config, ifname) config.delete(base) @@ -376,3 +427,4 @@ def migrate(config: ConfigTree) -> None: _migrate_vpp_dpdk_options(config) _migrate_vpp_resources(config) _migrate_vpp_bonding_interface(config) + _migrate_vpp_vxlan_interface(config) |
