summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorChristian Breunig <christian@breunig.cc>2026-02-25 22:39:46 +0100
committerGitHub <noreply@github.com>2026-02-25 22:39:46 +0100
commit61d8e945ee356befde46c94cfed859d3e9f37d55 (patch)
tree90a995015c35ceb5fc3ded3b21ee699648748dae
parentc0c2d0bde843a4fd2921ba8b70c15565e8f618d6 (diff)
parentdec957d8bf09f2a186bc12478eec275a88ef3896 (diff)
downloadvyos-1x-61d8e945ee356befde46c94cfed859d3e9f37d55.tar.gz
vyos-1x-61d8e945ee356befde46c94cfed859d3e9f37d55.zip
Merge pull request #5001 from natali-rs1985/T8283
vpp: T8283: Move bonding interface from vpp section to 'interfaces vpp bonding'
-rw-r--r--interface-definitions/include/version/vpp-version.xml.i2
-rw-r--r--interface-definitions/vpp.xml.in98
-rw-r--r--interface-definitions/vpp_interface_bonding.xml.in118
-rw-r--r--python/vyos/ifconfig/vpp/__init__.py24
-rw-r--r--python/vyos/ifconfig/vpp/bond.py152
-rw-r--r--python/vyos/ifconfig/vpp/interface.py56
-rw-r--r--python/vyos/vpp/interface/__init__.py2
-rw-r--r--python/vyos/vpp/interface/bond.py122
-rw-r--r--python/vyos/vpp/utils.py4
-rw-r--r--smoketest/configs/assert/vpp6
-rwxr-xr-xsmoketest/scripts/cli/test_vpp.py97
-rwxr-xr-xsrc/conf_mode/vpp.py31
-rw-r--r--src/conf_mode/vpp_interfaces_bonding.py137
-rw-r--r--src/conf_mode/vpp_interfaces_bridge.py10
-rw-r--r--src/conf_mode/vpp_ipfix.py5
-rw-r--r--src/migration-scripts/vpp/8-to-988
-rwxr-xr-xsrc/op_mode/vpp.py17
17 files changed, 550 insertions, 419 deletions
diff --git a/interface-definitions/include/version/vpp-version.xml.i b/interface-definitions/include/version/vpp-version.xml.i
index 59d13f8cf..b354faa08 100644
--- a/interface-definitions/include/version/vpp-version.xml.i
+++ b/interface-definitions/include/version/vpp-version.xml.i
@@ -1,3 +1,3 @@
<!-- include start from include/version/vpp-version.xml.i -->
-<syntaxVersion component='vpp' version='8'></syntaxVersion>
+<syntaxVersion component='vpp' version='9'></syntaxVersion>
<!-- include end -->
diff --git a/interface-definitions/vpp.xml.in b/interface-definitions/vpp.xml.in
index 86568c1e9..9f245aaf3 100644
--- a/interface-definitions/vpp.xml.in
+++ b/interface-definitions/vpp.xml.in
@@ -12,104 +12,6 @@
<priority>300</priority>
</properties>
<children>
- <tagNode name="bonding" owner="${vyos_conf_scripts_dir}/vpp_interfaces_bonding.py">
- <properties>
- <priority>324</priority>
- <help>Bonding Interface/Link Aggregation</help>
- <constraint>
- <regex>bond[0-9]+</regex>
- </constraint>
- <constraintErrorMessage>Bonding interface must be named bondN</constraintErrorMessage>
- <valueHelp>
- <format>bondN</format>
- <description>Bonding interface name</description>
- </valueHelp>
- </properties>
- <children>
- #include <include/generic-description.xml.i>
- #include <include/interface/disable.xml.i>
- <leafNode name="hash-policy">
- <properties>
- <help>Bonding transmit hash policy</help>
- <completionHelp>
- <list>layer2 layer2+3 layer3+4</list>
- </completionHelp>
- <valueHelp>
- <format>layer2</format>
- <description>use MAC addresses to generate the hash</description>
- </valueHelp>
- <valueHelp>
- <format>layer2+3</format>
- <description>combine MAC address and IP address to make hash</description>
- </valueHelp>
- <valueHelp>
- <format>layer3+4</format>
- <description>combine IP address and port to make hash</description>
- </valueHelp>
- <constraint>
- <regex>(layer2\+3|layer3\+4|layer2)</regex>
- </constraint>
- <constraintErrorMessage>hash-policy must be layer2 layer2+3 layer3+4</constraintErrorMessage>
- </properties>
- <defaultValue>layer2</defaultValue>
- </leafNode>
- #include <include/interface/mac.xml.i>
- #include <include/kernel-interface-tun.xml.i>
- <leafNode name="mode">
- <properties>
- <help>Bonding mode</help>
- <completionHelp>
- <list>802.3ad active-backup broadcast round-robin xor-hash</list>
- </completionHelp>
- <valueHelp>
- <format>802.3ad</format>
- <description>IEEE 802.3ad Dynamic link aggregation</description>
- </valueHelp>
- <valueHelp>
- <format>active-backup</format>
- <description>Fault tolerant: only one slave in the bond is active</description>
- </valueHelp>
- <valueHelp>
- <format>broadcast</format>
- <description>Fault tolerant: transmits everything on all slave interfaces</description>
- </valueHelp>
- <valueHelp>
- <format>round-robin</format>
- <description>Load balance: transmit packets in sequential order</description>
- </valueHelp>
- <valueHelp>
- <format>xor-hash</format>
- <description>Distribute based on MAC address</description>
- </valueHelp>
- <constraint>
- <regex>(802.3ad|active-backup|broadcast|round-robin|xor-hash)</regex>
- </constraint>
- <constraintErrorMessage>mode must be 802.3ad, active-backup, broadcast, round-robin, or xor</constraintErrorMessage>
- </properties>
- <defaultValue>802.3ad</defaultValue>
- </leafNode>
- <node name="member">
- <properties>
- <help>Bonding member interfaces</help>
- </properties>
- <children>
- <leafNode name="interface">
- <properties>
- <help>Member interface name</help>
- <completionHelp>
- <script>${vyos_completion_dir}/list_interfaces --bondable</script>
- </completionHelp>
- <valueHelp>
- <format>txt</format>
- <description>Interface name</description>
- </valueHelp>
- <multi/>
- </properties>
- </leafNode>
- </children>
- </node>
- </children>
- </tagNode>
<tagNode name="bridge" owner="${vyos_conf_scripts_dir}/vpp_interfaces_bridge.py">
<properties>
<priority>327</priority>
diff --git a/interface-definitions/vpp_interface_bonding.xml.in b/interface-definitions/vpp_interface_bonding.xml.in
new file mode 100644
index 000000000..9f114d157
--- /dev/null
+++ b/interface-definitions/vpp_interface_bonding.xml.in
@@ -0,0 +1,118 @@
+<?xml version="1.0"?>
+<interfaceDefinition>
+ <node name='interfaces'>
+ <children>
+ <node name='vpp'>
+ <properties>
+ <help>Network interfaces</help>
+ <priority>300</priority>
+ </properties>
+ <children>
+ <tagNode name="bonding" owner="${vyos_conf_scripts_dir}/vpp_interfaces_bonding.py">
+ <properties>
+ <priority>324</priority>
+ <help>Bonding Interface/Link Aggregation</help>
+ <constraint>
+ <regex>vppbond[0-9]+</regex>
+ </constraint>
+ <constraintErrorMessage>Bonding interface must be named vppbondN</constraintErrorMessage>
+ <valueHelp>
+ <format>vppbondN</format>
+ <description>Bonding interface name</description>
+ </valueHelp>
+ </properties>
+ <children>
+ #include <include/generic-description.xml.i>
+ #include <include/interface/disable.xml.i>
+ <leafNode name="hash-policy">
+ <properties>
+ <help>Bonding transmit hash policy</help>
+ <completionHelp>
+ <list>layer2 layer2+3 layer3+4</list>
+ </completionHelp>
+ <valueHelp>
+ <format>layer2</format>
+ <description>use MAC addresses to generate the hash</description>
+ </valueHelp>
+ <valueHelp>
+ <format>layer2+3</format>
+ <description>combine MAC address and IP address to make hash</description>
+ </valueHelp>
+ <valueHelp>
+ <format>layer3+4</format>
+ <description>combine IP address and port to make hash</description>
+ </valueHelp>
+ <constraint>
+ <regex>(layer2\+3|layer3\+4|layer2)</regex>
+ </constraint>
+ <constraintErrorMessage>hash-policy must be layer2 layer2+3 layer3+4</constraintErrorMessage>
+ </properties>
+ <defaultValue>layer2</defaultValue>
+ </leafNode>
+ #include <include/interface/mac.xml.i>
+ <leafNode name="mode">
+ <properties>
+ <help>Bonding mode</help>
+ <completionHelp>
+ <list>802.3ad active-backup broadcast round-robin xor-hash</list>
+ </completionHelp>
+ <valueHelp>
+ <format>802.3ad</format>
+ <description>IEEE 802.3ad Dynamic link aggregation</description>
+ </valueHelp>
+ <valueHelp>
+ <format>active-backup</format>
+ <description>Fault tolerant: only one slave in the bond is active</description>
+ </valueHelp>
+ <valueHelp>
+ <format>broadcast</format>
+ <description>Fault tolerant: transmits everything on all slave interfaces</description>
+ </valueHelp>
+ <valueHelp>
+ <format>round-robin</format>
+ <description>Load balance: transmit packets in sequential order</description>
+ </valueHelp>
+ <valueHelp>
+ <format>xor-hash</format>
+ <description>Distribute based on MAC address</description>
+ </valueHelp>
+ <constraint>
+ <regex>(802.3ad|active-backup|broadcast|round-robin|xor-hash)</regex>
+ </constraint>
+ <constraintErrorMessage>mode must be 802.3ad, active-backup, broadcast, round-robin, or xor-hash</constraintErrorMessage>
+ </properties>
+ <defaultValue>802.3ad</defaultValue>
+ </leafNode>
+ <node name="member">
+ <properties>
+ <help>Bonding member interfaces</help>
+ </properties>
+ <children>
+ <leafNode name="interface">
+ <properties>
+ <help>Member interface name</help>
+ <completionHelp>
+ <script>${vyos_completion_dir}/list_interfaces --bondable</script>
+ </completionHelp>
+ <valueHelp>
+ <format>txt</format>
+ <description>Interface name</description>
+ </valueHelp>
+ <constraint>
+ #include <include/constraint/interface-name.xml.i>
+ </constraint>
+ <multi/>
+ </properties>
+ </leafNode>
+ </children>
+ </node>
+ #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
new file mode 100644
index 000000000..117acb8d0
--- /dev/null
+++ b/python/vyos/ifconfig/vpp/__init__.py
@@ -0,0 +1,24 @@
+#
+# 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 .bond import VPPBondInterface
+from .interface import VPPInterface
+
+__all__ = [
+ 'VPPBondInterface',
+ 'VPPInterface',
+]
diff --git a/python/vyos/ifconfig/vpp/bond.py b/python/vyos/ifconfig/vpp/bond.py
new file mode 100644
index 000000000..75849a620
--- /dev/null
+++ b/python/vyos/ifconfig/vpp/bond.py
@@ -0,0 +1,152 @@
+#
+# 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 VPPBondInterface(Interface, VPPInterface):
+ def __init__(self, ifname, config):
+ self.ifname = ifname
+ self.instance = int(ifname.removeprefix('vppbond'))
+ self.vpp_ifname = f'BondEthernet{self.instance}'
+
+ # Initialize Interface (kernel) and VPP part
+ super().__init__(ifname)
+ VPPInterface.__init__(self, self.vpp_ifname)
+
+ self.index = self.vpp.get_sw_if_index(self.vpp_ifname)
+ self.state = 'up' if 'disable' not in config else 'down'
+ self.mode = config.get('mode')
+ self.load_balance = config.get('hash_policy')
+ self.mac = config.get('mac')
+
+ def _create(self):
+ pass
+
+ def add_bond(self):
+ """Create Bond interface
+ https://github.com/FDio/vpp/blob/stable/2306/src/vnet/bonding/bond.api
+ Example:
+ from vyos.ifconfig.vpp import VPPBondInterface
+ a = BondInterface(ifname='vppbond0', config)
+ a.add_bond()
+ """
+ # Create interface 'BondEthernetX'
+ create_args = {
+ 'id': self.instance,
+ 'mode': self.mode,
+ 'lb': self.load_balance,
+ }
+ if self.mac:
+ create_args.update({'use_custom_mac': True, 'mac_address': self.mac})
+ self.vpp.api.bond_create2(**create_args)
+ # 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_bond(self):
+ """Delete Bond interface
+ Example:
+ from vyos.ifconfig.vpp import VPPBondInterface
+ a = VPPBondInterface(ifname='vppbond0', config)
+ a.delete_bond()
+ """
+ self.vpp.api.bond_delete(sw_if_index=self.index)
+
+ def add_member(self, interface):
+ """Add member to Bond interface
+ Example:
+ from vyos.ifconfig.vpp import VPPBondInterface
+ a = VPPBondInterface(ifname='vppbond0', config)
+ a.add_member(interface='eth0')
+ """
+ member_if_index = self.vpp.get_sw_if_index(interface)
+ self.vpp.api.bond_add_member(
+ bond_sw_if_index=self.index, sw_if_index=member_if_index
+ )
+ self.vpp.api.sw_interface_set_promisc(
+ sw_if_index=member_if_index, promisc_on=True
+ )
+
+ def detach_member(self, interface):
+ """Detach member from Bond interface
+ Example:
+ from vyos.ifconfig.vpp import VPPBondInterface
+ a = VPPBondInterface(ifname='vppbond0')
+ a.detach_member(interface='eth0')
+ """
+ member_if_index = self.vpp.get_sw_if_index(interface)
+ self.vpp.api.bond_detach_member(sw_if_index=member_if_index)
+
+ def get_members(self):
+ members = []
+ tmp = self.vpp.api.sw_member_interface_dump(sw_if_index=self.index)
+ for member in tmp:
+ members.append(member.interface_name)
+ return members
+
+ def kernel_add(self):
+ """Add LCP pair
+ Example:
+ from vyos.ifconfig.vpp import VPPBondInterface
+ a = VPPBondInterface(ifname='vppbond0')
+ 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 VPPBondInterface
+ a = VPPBondInterface(ifname='vppbond0')
+ a.kernel_delete()
+ """
+ self.vpp.lcp_pair_del(self.vpp_ifname, self.ifname)
+
+ def remove(self):
+ if self.index:
+ # Detach all existing members
+ members = self.get_members()
+ for member in members:
+ self.detach_member(interface=member)
+
+ # Delete lcp pair interface
+ if self.vpp.lcp_pair_find(vpp_name_hw=self.vpp_ifname):
+ self.kernel_delete()
+
+ # Delete bonding interface
+ self.delete_bond()
+
+ def update(self, config):
+ # Add bond interface
+ self.add_bond()
+
+ # Add members to bond
+ for member in config.get('member', {}).get('interface', []):
+ self.add_member(interface=member)
+
+ # Set rx-mode
+ rx_mode = config.get('vpp_settings', {}).get('interface_rx_mode')
+ if rx_mode:
+ self.set_rx_mode(rx_mode)
+
+ # Apply all settings to the lcp pair (kernel) interface
+ super().update(config)
diff --git a/python/vyos/ifconfig/vpp/interface.py b/python/vyos/ifconfig/vpp/interface.py
new file mode 100644
index 000000000..1b87af4d2
--- /dev/null
+++ b/python/vyos/ifconfig/vpp/interface.py
@@ -0,0 +1,56 @@
+#
+# 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
+
+
+class VPPInterface:
+ def __init__(self, vpp_ifname):
+ self.vpp_ifname = vpp_ifname
+ self.vpp = VPPControl()
+
+ def set_state(self, state: str):
+ """Set interface state to UP or DOWN
+ Args:
+ state (str): The state of the interface. Options are 'up' and 'down'.
+ Example:
+ from vyos.interface.vpp import VPPInterface
+ a = VPPInterface(vpp_ifname='eth0')
+ a.set_state(state='up')
+ """
+ if state not in ['up', 'down']:
+ raise ValueError(f"Invalid state: {state}")
+ state_flag = 1 if state == 'up' else 0
+ if_index = self.vpp.get_sw_if_index(self.vpp_ifname)
+ self.vpp.api.sw_interface_set_flags(sw_if_index=if_index, flags=state_flag)
+
+ def get_state(self):
+ """Get interface state
+ Example:
+ from vyos.interface.vpp import VPPInterface
+ a = VPPInterface(vpp_ifname='eth0')
+ a.get_state()
+ """
+ if_index = self.vpp.get_sw_if_index(self.vpp_ifname)
+ return self.vpp.api.sw_interface_dump(sw_if_index=if_index)[0]['flags']
+
+ def set_rx_mode(self, rx_mode):
+ lcp_pair = self.vpp.lcp_pair_find(vpp_name_hw=self.vpp_ifname)
+ if lcp_pair:
+ lcp_name = lcp_pair.get('vpp_name_kernel')
+ if lcp_name:
+ self.vpp.iface_rxmode(lcp_name, rx_mode)
diff --git a/python/vyos/vpp/interface/__init__.py b/python/vyos/vpp/interface/__init__.py
index 903b7cbfc..c54e5ca60 100644
--- a/python/vyos/vpp/interface/__init__.py
+++ b/python/vyos/vpp/interface/__init__.py
@@ -15,7 +15,6 @@
# with this program; if not, write to the Free Software Foundation, Inc.,
# 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
-from .bond import BondInterface
from .bridge import BridgeInterface
from .ethernet import EthernetInterface
from .geneve import GeneveInterface
@@ -28,7 +27,6 @@ from .wireguard import WireguardInterface
from .xconnect import XconnectInterface
__all__ = [
- 'BondInterface',
'BridgeInterface',
'EthernetInterface',
'GeneveInterface',
diff --git a/python/vyos/vpp/interface/bond.py b/python/vyos/vpp/interface/bond.py
deleted file mode 100644
index abfcf5d0d..000000000
--- a/python/vyos/vpp/interface/bond.py
+++ /dev/null
@@ -1,122 +0,0 @@
-#
-# 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.control_host import set_promisc
-from vyos.vpp.interface.interface import Interface
-
-
-class BondInterface(Interface):
- def __init__(
- self,
- ifname,
- mode: str = '',
- load_balance: int = 0,
- mac: str = '',
- kernel_interface: str = '',
- state: str = 'up',
- ):
- super().__init__(ifname)
- self.instance = int(ifname.removeprefix('bond'))
- self.ifname = f'BondEthernet{self.instance}'
- self.mode = mode
- self.load_balance = load_balance
- self.mac = mac
- self.kernel_interface = kernel_interface
- self.state = state
-
- def add(self):
- """Create Bond interface
- https://github.com/FDio/vpp/blob/stable/2306/src/vnet/bonding/bond.api
- Example:
- from vyos.vpp.interface import BondInterface
- a = BondInterface(ifname='bond0', mode=5)
- a.add()
- """
- # Create interface 'bondX'
- create_args = {
- 'id': self.instance,
- 'mode': self.mode,
- 'lb': self.load_balance,
- }
- if self.mac:
- create_args.update({'use_custom_mac': True, 'mac_address': self.mac})
- self.vpp.api.bond_create2(**create_args)
- if self.kernel_interface:
- self.vpp.lcp_pair_add(self.ifname, self.kernel_interface)
- # Set interface state
- self.set_state(self.state)
-
- def delete(self):
- """Delete Bond interface
- Example:
- from vyos.vpp.interface import BondInterface
- a = BondInterface(ifname='bond0')
- a.delete()
- """
- bond_if_index = self.vpp.get_sw_if_index(self.ifname)
- self.vpp.api.bond_delete(sw_if_index=bond_if_index)
-
- def add_member(self, interface):
- """Add member to Bond interface
- Example:
- from vyos.vpp.interface import BondInterface
- a = BondInterface(ifname='bond0')
- a.add_member(interface='eth0')
- """
- bond_if_index = self.vpp.get_sw_if_index(f'BondEthernet{self.instance}')
- member_if_index = self.vpp.get_sw_if_index(interface)
- member_if_type = self.vpp.get_sw_if_dev_type(interface)
- self.vpp.api.bond_add_member(
- bond_sw_if_index=bond_if_index, sw_if_index=member_if_index
- )
- self.vpp.api.sw_interface_set_promisc(
- sw_if_index=member_if_index, promisc_on=True
- )
- if member_if_type == 'AF_XDP interface':
- set_promisc(f'defunct_{interface}', 'on')
-
- def detach_member(self, interface):
- """Detach member from Bond interface
- Example:
- from vyos.vpp.interface import BondInterface
- a = BondInterface(ifname='bond0')
- a.detach_member(interface='eth0')
- """
- member_if_index = self.vpp.get_sw_if_index(interface)
- self.vpp.api.bond_detach_member(sw_if_index=member_if_index)
-
- def kernel_add(self):
- """Add LCP pair
- Example:
- from vyos.vpp.interface import BondInterface
- a = BondInterface(ifname='bond0', mode=5)
- 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 BondInterface
- a = BondInterface(ifname='bond0', mode=5)
- a.kernel_delete()
- """
- self.vpp.lcp_pair_del(self.ifname, self.kernel_interface)
-
- def lcp_pair_exists(self):
- """Check if LCP pair exists"""
- return bool(self.vpp.lcp_pair_find(self.kernel_interface))
diff --git a/python/vyos/vpp/utils.py b/python/vyos/vpp/utils.py
index 0c247e48a..5ae4cd296 100644
--- a/python/vyos/vpp/utils.py
+++ b/python/vyos/vpp/utils.py
@@ -58,9 +58,9 @@ def vpp_iface_name_transform(iface: str) -> str:
str: Interface name formatted as recognized by VPP (e.g., "BondEthernet0").
"""
vpp_iface_name = iface
- if vpp_iface_name.startswith('bond'):
+ if vpp_iface_name.startswith('vppbond'):
# interface name in VPP is BondEthernetX
- vpp_iface_name = vpp_iface_name.replace('bond', 'BondEthernet')
+ vpp_iface_name = vpp_iface_name.replace('vppbond', 'BondEthernet')
return vpp_iface_name
diff --git a/smoketest/configs/assert/vpp b/smoketest/configs/assert/vpp
index 536d1a656..0859e7a1a 100644
--- a/smoketest/configs/assert/vpp
+++ b/smoketest/configs/assert/vpp
@@ -19,9 +19,9 @@ set system option kernel memory hugepage-size 2M hugepage-count '1800'
set system option time-format '24-hour'
set system syslog local facility all level 'info'
set system syslog local facility local7 level 'debug'
-set vpp interfaces bonding bond0 hash-policy 'layer2+3'
-set vpp interfaces bonding bond0 member interface 'eth4'
-set vpp interfaces bonding bond0 mode '802.3ad'
+set interfaces vpp bonding vppbond0 hash-policy 'layer2+3'
+set interfaces vpp bonding vppbond0 member interface 'eth4'
+set interfaces vpp bonding vppbond0 mode '802.3ad'
set vpp interfaces gre gre10 description 'Layer-3'
set vpp interfaces gre gre10 mode 'point-to-point'
set vpp interfaces gre gre10 remote '192.0.2.2'
diff --git a/smoketest/scripts/cli/test_vpp.py b/smoketest/scripts/cli/test_vpp.py
index 88c146022..db4c811cc 100755
--- a/smoketest/scripts/cli/test_vpp.py
+++ b/smoketest/scripts/cli/test_vpp.py
@@ -40,6 +40,7 @@ PROCESS_NAME = 'vpp_main'
VPP_CONF = '/run/vpp/vpp.conf'
base_path = ['vpp']
resource_path = base_path + ['settings', 'resource-allocation']
+interfaces_path = ['interfaces', 'vpp']
interface = 'eth1'
@@ -115,6 +116,7 @@ class TestVPP(VyOSUnitTestSHIM.TestCase):
finally:
# Ensure these cleanup operations always run
self.cli_delete(base_path)
+ self.cli_delete(interfaces_path)
self.cli_commit()
# delete address for Ethernet interface
@@ -667,31 +669,17 @@ class TestVPP(VyOSUnitTestSHIM.TestCase):
self.cli_commit()
def test_06_vpp_bonding(self):
- interface_bond = 'bond23'
- interface_kernel = 'vpptun23'
+ bond_path = interfaces_path + ['bonding']
+ interface_bond = 'vppbond23'
hash = 'layer3+4'
mode = '802.3ad'
description = 'Interface-Bonding'
vlans = ['123', '456']
vlan_description = 'My-vlan-123'
- self.cli_set(
- base_path
- + [
- 'interfaces',
- 'bonding',
- interface_bond,
- 'member',
- 'interface',
- interface,
- ]
- )
- self.cli_set(
- base_path + ['interfaces', 'bonding', interface_bond, 'hash-policy', hash]
- )
- self.cli_set(
- base_path + ['interfaces', 'bonding', interface_bond, 'mode', mode]
- )
+ self.cli_set(bond_path + [interface_bond, 'member', 'interface', interface])
+ self.cli_set(bond_path + [interface_bond, 'hash-policy', hash])
+ self.cli_set(bond_path + [interface_bond, 'mode', mode])
# commit changes
self.cli_commit()
@@ -706,42 +694,21 @@ class TestVPP(VyOSUnitTestSHIM.TestCase):
"Interface BondEthernet23 is not in the expected state 'up'.",
)
- # set kernel interface
- self.cli_set(
- base_path
- + [
- 'interfaces',
- 'bonding',
- interface_bond,
- 'kernel-interface',
- interface_kernel,
- ]
- )
- self.cli_set(
- base_path
- + ['kernel-interfaces', interface_kernel, 'description', description]
- )
+ self.cli_set(bond_path + [interface_bond, 'description', description])
for vlan in vlans:
self.cli_set(
- base_path
- + [
- 'kernel-interfaces',
- interface_kernel,
- 'vif',
- vlan,
- 'description',
- vlan_description,
- ]
+ bond_path
+ + [interface_bond, 'vif', vlan, 'description', vlan_description]
)
# 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_kernel}.{vlan}'))
+ self.assertTrue(os.path.isdir(f'/sys/class/net/{interface_bond}'))
+ self.assertTrue(os.path.isdir(f'/sys/class/net/{interface_bond}.{vlan}'))
- current_alias = read_file(f'/sys/class/net/{interface_kernel}/ifalias')
- vlan_alias = read_file(f'/sys/class/net/{interface_kernel}.{vlan}/ifalias')
+ current_alias = read_file(f'/sys/class/net/{interface_bond}/ifalias')
+ vlan_alias = read_file(f'/sys/class/net/{interface_bond}.{vlan}/ifalias')
self.assertEqual(current_alias, description)
self.assertEqual(vlan_alias, vlan_description)
@@ -772,23 +739,12 @@ class TestVPP(VyOSUnitTestSHIM.TestCase):
)
# delete vpp kernel-interface vlan
- self.cli_delete(base_path + ['kernel-interfaces', interface_kernel, 'vif'])
+ self.cli_delete(bond_path + [interface_bond, 'vif'])
self.cli_commit()
- self.assertFalse(os.path.isdir(f'/sys/class/net/{interface_kernel}.{vlan}'))
-
- # delete vpp kernel-interface
- self.cli_delete(base_path + ['kernel-interfaces', interface_kernel])
- self.cli_commit()
-
- # delete bonding kernel-interface
- self.cli_delete(
- base_path + ['interfaces', 'bonding', interface_bond, 'kernel-interface']
- )
- self.cli_commit()
- self.assertFalse(os.path.isdir(f'/sys/class/net/{interface_kernel}'))
+ self.assertFalse(os.path.isdir(f'/sys/class/net/{interface_bond}.{vlan}'))
# delete bonding interface
- self.cli_delete(base_path + ['interfaces', 'bonding'])
+ self.cli_delete(bond_path)
self.cli_commit()
# check deleting bonding interface
@@ -1278,10 +1234,8 @@ class TestVPP(VyOSUnitTestSHIM.TestCase):
def test_14_2_vpp_cgnat_bond_with_vifs(self):
base_cgnat = base_path + ['nat', 'cgnat']
- base_kernel = base_path + ['kernel-interfaces']
- base_bond = base_path + ['interfaces', 'bonding']
- iface_kernel = 'vpptun0'
- iface_bond = 'bond0'
+ base_bond = interfaces_path + ['bonding']
+ iface_bond = 'vppbond0'
vif_1 = '23'
vif_2 = '24'
iface_out = f'{iface_bond}.{vif_1}'
@@ -1289,11 +1243,9 @@ class TestVPP(VyOSUnitTestSHIM.TestCase):
address_1 = '100.64.0.23/32'
address_2 = '192.0.2.1/32'
- self.cli_set(base_bond + [iface_bond, 'kernel-interface', iface_kernel])
self.cli_set(base_bond + [iface_bond, 'member', 'interface', interface])
-
- self.cli_set(base_kernel + [iface_kernel, 'vif', vif_1, 'address', address_1])
- self.cli_set(base_kernel + [iface_kernel, 'vif', vif_2, 'address', address_2])
+ self.cli_set(base_bond + [iface_bond, 'vif', vif_1, 'address', address_1])
+ self.cli_set(base_bond + [iface_bond, 'vif', vif_2, 'address', address_2])
self.cli_set(base_cgnat + ['interface', 'inside', iface_inside])
self.cli_set(base_cgnat + ['interface', 'outside', iface_out])
@@ -1701,13 +1653,12 @@ class TestVPP(VyOSUnitTestSHIM.TestCase):
def test_21_2_vpp_ipfix_bond(self):
base_ipfix = base_path + ['ipfix']
- base_bond = base_path + ['interfaces', 'bonding']
- iface_bond = 'bond0'
+ base_bond = interfaces_path + ['bonding']
+ iface_bond = 'vppbond0'
collector_ip = '127.0.0.2'
collector_src = '127.0.0.1'
- self.cli_set(base_bond + [iface_bond, 'kernel-interface', 'vpptun0'])
- self.cli_set(base_bond + [iface_bond, 'member', 'interface', iface_bond])
+ self.cli_set(base_bond + [iface_bond, 'member', 'interface', interface])
self.cli_set(
base_ipfix + ['collector', collector_ip, 'source-address', collector_src]
diff --git a/src/conf_mode/vpp.py b/src/conf_mode/vpp.py
index 90356e6f4..6d2e2866f 100755
--- a/src/conf_mode/vpp.py
+++ b/src/conf_mode/vpp.py
@@ -279,6 +279,15 @@ def get_config(config=None):
iface for iface in pppoe_ifaces if iface.split('.')[0] in tmp
]
+ interfaces_config = conf.get_config_dict(
+ ['interfaces', 'vpp'],
+ key_mangling=('-', '_'),
+ get_first_key=True,
+ no_tag_node_value_mangle=True,
+ with_defaults=True,
+ with_recursive_defaults=True,
+ )
+
if not conf.exists(base):
if changed_pppoe_ifaces:
set_dependents('pppoe_server', conf)
@@ -286,6 +295,8 @@ def get_config(config=None):
'removed_ifaces': removed_ifaces,
'xconn_members': xconn_members,
'persist_config': eth_ifaces_persist,
+ 'interfaces_vpp': interfaces_config,
+ 'remove': {},
}
config = conf.get_config_dict(
@@ -423,6 +434,8 @@ def get_config(config=None):
config['removed_ifaces'] = removed_ifaces
config['xconn_members'] = xconn_members
+ config['interfaces_vpp'] = interfaces_config
+
# Dependencies
for dependency, interface_type in dependency_interface_type_map.items():
# if conf.exists(base + ['interfaces', interface_type]):
@@ -435,6 +448,13 @@ def get_config(config=None):
iface_filter_eth(conf, iface)
set_dependents(dependency, conf, iface)
+ for dependency, interface_type in dependency_interface_type_map.items():
+ if conf.exists(['interfaces', 'vpp', interface_type]):
+ for iface, iface_config in interfaces_config.get(
+ interface_type, {}
+ ).items():
+ set_dependents(dependency, conf, iface)
+
config['ipoe_conf'] = conf.get_config_dict(
['service', 'ipoe-server'],
key_mangling=('-', '_'),
@@ -484,8 +504,13 @@ def verify(config):
config, 'IPFIX monitoring', config.get('ipfix', {}).get('interface', {})
)
+ if config.get('interfaces_vpp') and 'remove' in config:
+ raise ConfigError(
+ 'VPP cannot be removed while VPP interfaces exist. Remove all "interfaces vpp" first!'
+ )
+
# bail out early - looks like removal from running config
- if not config or ('removed_ifaces' in config and 'settings' not in config):
+ if not config or 'remove' in config:
return None
if 'settings' not in config:
@@ -640,7 +665,7 @@ def verify(config):
def generate(config):
- if not config or ('removed_ifaces' in config and 'settings' not in config):
+ if not config or 'remove' in config:
# Remove old config and return
service_conf.unlink(missing_ok=True)
return None
@@ -692,7 +717,7 @@ def apply(config):
modules = ('vfio_iommu_type1', 'vfio_pci', 'vfio_pci_core', 'vfio')
# Open persistent config
# It is required for operations with interfaces
- if not config or ('removed_ifaces' in config and 'settings' not in config):
+ if not config or 'remove' in config:
# Cleanup persistent config
with JSONStorage('vpp_conf') as persist_config:
persist_config.delete()
diff --git a/src/conf_mode/vpp_interfaces_bonding.py b/src/conf_mode/vpp_interfaces_bonding.py
index 23d0720bf..89376ea32 100644
--- a/src/conf_mode/vpp_interfaces_bonding.py
+++ b/src/conf_mode/vpp_interfaces_bonding.py
@@ -16,22 +16,17 @@
# 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 leaf_node_changed
+from vyos.configdict import get_interface_dict
from vyos.configdep import set_dependents, call_dependents
from vyos import ConfigError
+from vyos.utils.assertion import assert_mac
+from vyos.utils.process import is_systemd_service_active
-from vyos.vpp.interface import BondInterface
+from vyos.ifconfig.vpp import VPPBondInterface
from vyos.vpp.config_deps import deps_xconnect_dict
-from vyos.vpp.config_verify import (
- verify_vpp_remove_xconnect_interface,
- verify_vpp_remove_kernel_interface,
- verify_vpp_change_kernel_interface,
- verify_vpp_exists_kernel_interface,
-)
-from vyos.vpp.utils import cli_ifaces_list, cli_ifaces_lcp_kernel_list
+from vyos.vpp.config_verify import verify_vpp_remove_xconnect_interface
+from vyos.vpp.utils import cli_ifaces_list
def _get_bond_mode(mode_name: str) -> int:
@@ -71,65 +66,29 @@ def get_config(config=None) -> dict:
else:
conf = Config()
- base = ['vpp', 'interfaces', 'bonding']
- base_kernel_interfaces = ['vpp', 'kernel-interfaces']
+ base = ['interfaces', 'vpp', 'bonding']
- ifname = os.environ['VYOS_TAGNODE_VALUE']
+ ifname, config = get_interface_dict(conf, base)
- # 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,
- )
-
- 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
-
- if effective_config:
- config.update({'effective': effective_config})
-
config['vpp_ifaces'] = cli_ifaces_list(conf, 'candidate')
- # 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,
- )
-
- # list of all kernel interfaces `vpp interface xxx kernel-interface xxx`
- config['candidate_kernel_interfaces'] = cli_ifaces_lcp_kernel_list(conf)
-
# convert values to VPP compatible
if 'mode' in config:
config['mode'] = _get_bond_mode(config['mode'])
if 'hash_policy' in config:
config['hash_policy'] = _get_bond_lb(config['hash_policy'])
- tmp = leaf_node_changed(conf, base + [ifname, 'kernel-interface'])
- if tmp:
- config['kernel_interface_removed'] = tmp
+ # 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,
+ )
# Dependency
config['xconn_members'] = deps_xconnect_dict(conf)
@@ -137,12 +96,6 @@ def get_config(config=None) -> dict:
for xconn_iface in config['xconn_members'][ifname]:
set_dependents('vpp_interfaces_xconnect', conf, xconn_iface)
- if conf.exists(base + [ifname, 'kernel-interface']):
- if effective_config.get('kernel_interface') or __name__ != '__main__':
- iface = config.get('kernel_interface')
- if conf.exists(['vpp', 'kernel-interfaces', iface]):
- set_dependents('vpp_kernel_interface', conf, iface)
-
# NAT dependency
if conf.exists(['vpp', 'nat', 'nat44']):
set_dependents('vpp_nat_nat44', conf)
@@ -157,8 +110,6 @@ def get_config(config=None) -> dict:
if conf.exists(['vpp', 'ipfix']):
set_dependents('vpp_ipfix', conf)
- config['ifname'] = ifname
-
return config
@@ -166,19 +117,29 @@ def verify(config):
if 'remove_vpp' in config:
return None
- verify_vpp_remove_kernel_interface(config)
verify_vpp_remove_xconnect_interface(config)
+ if 'deleted' in config:
+ return None
+
+ if not is_systemd_service_active('vpp.service'):
+ raise ConfigError(
+ 'Cannot configure VPP bonding interface: vpp.service is not running'
+ )
+
# Member must belong to VPP
for iface in config.get('member', {}).get('interface', []):
if iface not in config['vpp_ifaces']:
raise ConfigError(f'{iface} must be a VPP interface for bonding')
- if 'remove' in config:
- return None
-
- verify_vpp_change_kernel_interface(config)
- verify_vpp_exists_kernel_interface(config)
+ if 'mac' in config:
+ mac = config['mac']
+ try:
+ assert_mac(mac, test_all_zero=False)
+ except:
+ raise ConfigError(
+ f'Cannot use {mac}: it is a multicast MAC address. Please provide a unicast MAC address.'
+ )
def generate(config):
@@ -190,37 +151,13 @@ def apply(config):
return None
ifname = config.get('ifname')
- # remove old members
- if 'effective' in config:
- members = config['effective'].get('member', {}).get('interface', [])
-
- kernel_interface = config['effective'].get('kernel_interface', '')
- i = BondInterface(ifname, kernel_interface=kernel_interface)
- for member in members:
- i.detach_member(interface=member)
+ bond = VPPBondInterface(ifname, config)
+ bond.remove()
- if 'kernel_interface' in config['effective'] and i.lcp_pair_exists():
- i.kernel_delete()
- # Delete bonding interface
- i.delete()
-
- if 'remove' in config:
- return None
+ if 'deleted' in config:
+ return
- # Create a new one
- mode = config.get('mode')
- lb = config.get('hash_policy')
- members = config.get('member', {}).get('interface', [])
- mac = config.get('mac', '')
- kernel_interface = config.get('kernel_interface', '')
- state = 'up' if 'disable' not in config else 'down'
-
- i = BondInterface(ifname, mode, lb, mac, kernel_interface, state)
- i.add()
- # Add members to bond
- if members:
- for member in members:
- i.add_member(interface=member)
+ bond.update(config)
call_dependents()
diff --git a/src/conf_mode/vpp_interfaces_bridge.py b/src/conf_mode/vpp_interfaces_bridge.py
index 64722c10c..e86381cc3 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 = ('bond', 'gre', 'geneve', 'lo', 'vxlan')
+ allowed_prefixes = ('vppbond', 'gre', 'geneve', 'lo', 'vxlan')
if 'member' in config:
bvi_exists = False
@@ -141,9 +141,9 @@ def apply(config):
if member.startswith('lo'):
# interface name in VPP is loopX
member = member.replace('lo', 'loop')
- elif member.startswith('bond'):
+ elif member.startswith('vppbond'):
# interface name in VPP is BondEthernetX
- member = member.replace('bond', 'BondEthernet')
+ member = member.replace('vppbond', 'BondEthernet')
i.detach_member(member=member)
# Delete bridge domain
@@ -171,9 +171,9 @@ def apply(config):
member = member.replace('lo', 'loop')
if 'bvi' in member_config:
port_type = 1
- elif member.startswith('bond'):
+ elif member.startswith('vppbond'):
# interface name in VPP is BondEthernetX
- member = member.replace('bond', 'BondEthernet')
+ member = member.replace('vppbond', 'BondEthernet')
br.add_member(member=member, port_type=port_type)
# set default port type 0 (not BVI)
diff --git a/src/conf_mode/vpp_ipfix.py b/src/conf_mode/vpp_ipfix.py
index a23498c99..b69651ee4 100644
--- a/src/conf_mode/vpp_ipfix.py
+++ b/src/conf_mode/vpp_ipfix.py
@@ -20,6 +20,7 @@ from vyos.config import Config
from vyos.vpp.ipfix import IPFIX
from vyos.vpp.utils import cli_ifaces_list
from vyos.vpp.utils import vpp_iface_name_transform
+from vyos.vpp.control_vpp import VPPControl
def get_config(config=None) -> dict:
@@ -73,8 +74,10 @@ def verify(config):
)
# Verify that all interfaces specified exist in VPP
+ vpp = VPPControl()
for interface in config['interface']:
- if interface not in config['vpp_ifaces']:
+ vpp_iface_name = vpp_iface_name_transform(interface)
+ if vpp.get_sw_if_index(vpp_iface_name) is None:
raise ConfigError(
f'{interface} must be a VPP interface for IPFIX monitoring'
)
diff --git a/src/migration-scripts/vpp/8-to-9 b/src/migration-scripts/vpp/8-to-9
new file mode 100644
index 000000000..2232692a5
--- /dev/null
+++ b/src/migration-scripts/vpp/8-to-9
@@ -0,0 +1,88 @@
+# Copyright VyOS maintainers and contributors <maintainers@vyos.io>
+#
+# This library is free software; you can redistribute it and/or
+# modify it under the terms of the GNU Lesser General Public
+# License as published by the Free Software Foundation; either
+# version 2.1 of the License, or (at your option) any later version.
+#
+# This library 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
+# Lesser General Public License for more details.
+#
+# You should have received a copy of the GNU Lesser General Public License
+# along with this library. If not, see <http://www.gnu.org/licenses/>.
+
+# Move bonding interface from vpp section to 'interfaces vpp bonding' (T8283)
+
+
+from vyos.configtree import ConfigTree
+
+base = ['vpp', 'interfaces', 'bonding']
+new_base = ['interfaces', 'vpp', 'bonding']
+kernel_interface_base = ['vpp', 'kernel-interfaces']
+
+def migrate(config: ConfigTree) -> None:
+ if not config.exists(base):
+ return
+
+ 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])
+
+ for feature in ['nat44', 'cgnat']:
+ for direction in ['inside', 'outside']:
+ tmp_path = ['vpp', 'nat', feature, 'interface', direction]
+
+ if not config.exists(tmp_path):
+ continue
+
+ names = config.return_values(tmp_path)
+ for name in names:
+ if name.split('.')[0] == ifname:
+ new_name = f'vpp{name}'
+ config.delete_value(tmp_path, name)
+ config.set(tmp_path, value=new_name, replace=False)
+
+ ipfix_path = ['vpp', 'ipfix', 'interface']
+ if config.exists(ipfix_path):
+ for name in config.list_nodes(ipfix_path):
+ if name.split('.')[0] == ifname:
+ 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)
+
+ 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'])
diff --git a/src/op_mode/vpp.py b/src/op_mode/vpp.py
index 5de7b3f74..868f780ae 100755
--- a/src/op_mode/vpp.py
+++ b/src/op_mode/vpp.py
@@ -31,13 +31,12 @@ def _verify(target: typing.Optional[str]):
"""Decorator checks if config for VPP feature exists"""
from functools import wraps
- target = target.split() if target else []
+ path = target.split() if target else []
def _verify_target(func):
@wraps(func)
def _wrapper(*args, **kwargs):
config = ConfigTreeQuery()
- path = ['vpp'] + target
if not config.exists(path):
raise vyos.opmode.UnconfiguredSubsystem(
f'"{" ".join(path)}" is not configured'
@@ -371,17 +370,17 @@ class VPPShow:
# -----------------------------
# VyOS IPFIX op-mode entries
# -----------------------------
-@_verify('ipfix interface')
+@_verify('vpp ipfix interface')
def show_ipfix_interfaces(raw: bool):
return VPPShow().ipfix_interfaces(raw)
-@_verify('ipfix collector')
+@_verify('vpp ipfix collector')
def show_ipfix_collectors(raw: bool):
return VPPShow().ipfix_collectors(raw)
-@_verify('ipfix')
+@_verify('vpp ipfix')
def show_ipfix_table(raw: bool):
return VPPShow().ipfix_table(raw)
@@ -389,12 +388,12 @@ def show_ipfix_table(raw: bool):
# -----------------------------
# VPP LACP information
# -----------------------------
-@_verify('interfaces bonding')
+@_verify('interfaces vpp bonding')
def show_lacp(raw: bool, ifname: typing.Optional[str]):
return VPPShow().lacp_info(raw, ifname)
-@_verify('interfaces bonding')
+@_verify('interfaces vpp bonding')
def show_lacp_details(raw: bool, ifname: typing.Optional[str]):
return VPPShow().lacp_details(raw, ifname)
@@ -402,12 +401,12 @@ def show_lacp_details(raw: bool, ifname: typing.Optional[str]):
# -----------------------------
# Bridge op-mode entries
# -----------------------------
-@_verify('interfaces bridge')
+@_verify('vpp interfaces bridge')
def show_bridge(raw: bool, ifname: typing.Optional[str] = None):
return VPPShow().bridge_domain(raw, ifname)
-@_verify('interfaces bridge')
+@_verify('vpp interfaces bridge')
def show_bridge_details(raw: bool, ifname: typing.Optional[str] = None):
return VPPShow().bridge_domain_details(raw, ifname)