summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorViacheslav Hletenko <v.gletenko@vyos.io>2026-03-04 15:33:41 +0200
committerGitHub <noreply@github.com>2026-03-04 15:33:41 +0200
commit9fae68c92fe769da4f877c8df358868812a660ad (patch)
tree95d8b0c47cae0efcca44509a8f01ab6244f09900
parent2a4d52b2b8c39f4f77b526ce80686247dd4d0f95 (diff)
parent32a5763872420ba8af53e3371c5c68e7369b5e62 (diff)
downloadvyos-1x-9fae68c92fe769da4f877c8df358868812a660ad.tar.gz
vyos-1x-9fae68c92fe769da4f877c8df358868812a660ad.zip
Merge pull request #5024 from natali-rs1985/T8328
vpp: T8328: Migrate xconnect interface to 'interfaces vpp xconnect'
-rw-r--r--interface-definitions/vpp.xml.in67
-rw-r--r--interface-definitions/vpp_interface_xconnnect.xml.in49
-rw-r--r--python/vyos/ifconfig/vpp/__init__.py2
-rw-r--r--python/vyos/ifconfig/vpp/xconnect.py98
-rw-r--r--python/vyos/vpp/config_deps.py4
-rw-r--r--python/vyos/vpp/config_verify.py4
-rw-r--r--python/vyos/vpp/interface/__init__.py2
-rw-r--r--python/vyos/vpp/interface/xconnect.py95
-rwxr-xr-xsmoketest/scripts/cli/test_vpp.py25
-rw-r--r--src/conf_mode/vpp_interfaces_xconnect.py73
-rw-r--r--src/migration-scripts/vpp/5-to-621
11 files changed, 207 insertions, 233 deletions
diff --git a/interface-definitions/vpp.xml.in b/interface-definitions/vpp.xml.in
index 141c40275..e0389542b 100644
--- a/interface-definitions/vpp.xml.in
+++ b/interface-definitions/vpp.xml.in
@@ -6,73 +6,6 @@
<priority>295</priority>
</properties>
<children>
- <node name='interfaces'>
- <properties>
- <help>Network interfaces</help>
- <priority>300</priority>
- </properties>
- <children>
- <!-- Disable as geneve interface index in the VPP always 0 and not configured -->
- <!-- <tagNode name="geneve" owner="${vyos_conf_scripts_dir}/vpp_interfaces_geneve.py">
- <properties>
- <priority>325</priority>
- <help>Generic Network Virtualization Encapsulation (GENEVE) Interface</help>
- <constraint>
- <regex>geneve[0-9]+</regex>
- </constraint>
- <constraintErrorMessage>GENEVE interface must be named geneveN</constraintErrorMessage>
- <valueHelp>
- <format>geneveN</format>
- <description>GENEVE interface name</description>
- </valueHelp>
- </properties>
- <children> -->
- <!-- #include <include/source-address-ipv4-ipv6.xml.i> -->
- <!-- #include <include/interface/tunnel-remote.xml.i> -->
- <!-- #include <include/vni.xml.i> -->
- <!-- #include <include/kernel-interface-tun.xml.i> -->
- <!-- </children> -->
- <!-- </tagNode> -->
- <tagNode name="xconnect" owner="${vyos_conf_scripts_dir}/vpp_interfaces_xconnect.py">
- <properties>
- <help>Layer 2 cross connect</help>
- <priority>325</priority>
- <constraint>
- <regex>xcon[0-9]+</regex>
- </constraint>
- <constraintErrorMessage>Cross connect must be named xconN</constraintErrorMessage>
- <valueHelp>
- <format>xconN</format>
- <description>Cross connect interface name</description>
- </valueHelp>
- </properties>
- <children>
- #include <include/generic-description.xml.i>
- #include <include/interface/disable.xml.i>
- <node name="member">
- <properties>
- <help>Cross connect pair mamber interfaces</help>
- </properties>
- <children>
- <leafNode name="interface">
- <properties>
- <help>Member interface name</help>
- <completionHelp>
- <script>${vyos_completion_dir}/list_interfaces --bridgeable</script>
- </completionHelp>
- <valueHelp>
- <format>txt</format>
- <description>Interface name</description>
- </valueHelp>
- <multi/>
- </properties>
- </leafNode>
- </children>
- </node>
- </children>
- </tagNode>
- </children>
- </node>
<node name="ipfix" owner="${vyos_conf_scripts_dir}/vpp_ipfix.py">
<properties>
<help>IP Flow Information Export (IPFIX) configuration</help>
diff --git a/interface-definitions/vpp_interface_xconnnect.xml.in b/interface-definitions/vpp_interface_xconnnect.xml.in
new file mode 100644
index 000000000..3480a0d41
--- /dev/null
+++ b/interface-definitions/vpp_interface_xconnnect.xml.in
@@ -0,0 +1,49 @@
+<?xml version="1.0"?>
+<interfaceDefinition>
+ <node name='interfaces'>
+ <children>
+ <node name='vpp'>
+ <children>
+ <tagNode name="xconnect" owner="${vyos_conf_scripts_dir}/vpp_interfaces_xconnect.py">
+ <properties>
+ <help>Layer 2 cross-connect</help>
+ <priority>325</priority>
+ <constraint>
+ <regex>vppxcon[0-9]+</regex>
+ </constraint>
+ <constraintErrorMessage>Cross-connect interface must be named vppxconN</constraintErrorMessage>
+ <valueHelp>
+ <format>vppxconN</format>
+ <description>Cross-connect interface name</description>
+ </valueHelp>
+ </properties>
+ <children>
+ #include <include/generic-description.xml.i>
+ #include <include/interface/disable.xml.i>
+ <node name="member">
+ <properties>
+ <help>Cross-connect pair member interfaces</help>
+ </properties>
+ <children>
+ <leafNode name="interface">
+ <properties>
+ <help>Member interface name</help>
+ <completionHelp>
+ <script>${vyos_completion_dir}/list_interfaces --bridgeable</script>
+ </completionHelp>
+ <valueHelp>
+ <format>txt</format>
+ <description>Interface name</description>
+ </valueHelp>
+ <multi/>
+ </properties>
+ </leafNode>
+ </children>
+ </node>
+ </children>
+ </tagNode>
+ </children>
+ </node>
+ </children>
+ </node>
+</interfaceDefinition>
diff --git a/python/vyos/ifconfig/vpp/__init__.py b/python/vyos/ifconfig/vpp/__init__.py
index 38f72e42f..a0a1f6930 100644
--- a/python/vyos/ifconfig/vpp/__init__.py
+++ b/python/vyos/ifconfig/vpp/__init__.py
@@ -22,6 +22,7 @@ from .interface import VPPInterface
from .ipip import VPPIPIPInterface
from .loopback import VPPLoopbackInterface
from .vxlan import VPPVXLANInterface
+from .xconnect import VPPXconnectInterface
__all__ = [
'VPPBondInterface',
@@ -31,4 +32,5 @@ __all__ = [
'VPPIPIPInterface',
'VPPLoopbackInterface',
'VPPVXLANInterface',
+ 'VPPXconnectInterface',
]
diff --git a/python/vyos/ifconfig/vpp/xconnect.py b/python/vyos/ifconfig/vpp/xconnect.py
new file mode 100644
index 000000000..a5d7d15fd
--- /dev/null
+++ b/python/vyos/ifconfig/vpp/xconnect.py
@@ -0,0 +1,98 @@
+#
+# 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.vpp.interface import VPPInterface
+from vyos.vpp.utils import iftunnel_transform
+
+
+def _transform_members(members):
+ """
+ Transform interface names to VPP names for xconnect operations.
+
+ Args:
+ members (list of str): List of two interface names.
+
+ Returns:
+ tuple: Transformed first and second interface names.
+ """
+ interface_transform_filter = ('vxlan',)
+ first_member = members[0].removeprefix('vpp')
+ second_member = 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)
+ if second_member.startswith(interface_transform_filter):
+ second_member = iftunnel_transform(second_member)
+
+ return first_member, second_member
+
+
+class VPPXconnectInterface(VPPInterface):
+ def __init__(self, ifname):
+ self.vpp_ifname = ifname
+
+ super().__init__(self.vpp_ifname)
+
+ def add_l2_xconnect(self, first, second):
+ """Add l2 cross connect
+ Example:
+ from vyos.ifconfig.vpp import VPPXconnectInterface
+ a = VPPXconnectInterface(ifname='vppxcon0')
+ a.add_l2_xconnect(first, second)
+ """
+ member_first_if_index = self.vpp.get_sw_if_index(first)
+ member_second_if_index = self.vpp.get_sw_if_index(second)
+ self.vpp.api.sw_interface_set_l2_xconnect(
+ rx_sw_if_index=member_first_if_index,
+ tx_sw_if_index=member_second_if_index,
+ enable=True,
+ )
+ self.vpp.api.sw_interface_set_l2_xconnect(
+ rx_sw_if_index=member_second_if_index,
+ tx_sw_if_index=member_first_if_index,
+ enable=True,
+ )
+
+ def del_l2_xconnect(self, first, second):
+ """Move l2 cross connect member to mode l3 (delete xconnect)
+ Example:
+ from vyos.ifconfig.vpp import VPPXconnectInterface
+ a = VPPXconnectInterface(ifname='vppxcon0')
+ a.del_l2_xconnect(first, second)
+ """
+ member_first_if_index = self.vpp.get_sw_if_index(first)
+ member_second_if_index = self.vpp.get_sw_if_index(second)
+ self.vpp.api.sw_interface_set_l2_xconnect(
+ rx_sw_if_index=member_first_if_index,
+ tx_sw_if_index=member_second_if_index,
+ enable=False,
+ )
+ self.vpp.api.sw_interface_set_l2_xconnect(
+ rx_sw_if_index=member_second_if_index,
+ tx_sw_if_index=member_first_if_index,
+ enable=False,
+ )
+
+ def remove(self, members):
+ first, second = _transform_members(members)
+ self.del_l2_xconnect(first, second)
+
+ def update(self, config):
+ members = config['member']['interface']
+ first, second = _transform_members(members)
+ self.add_l2_xconnect(first, second)
diff --git a/python/vyos/vpp/config_deps.py b/python/vyos/vpp/config_deps.py
index 4aab90a4a..43cf6036f 100644
--- a/python/vyos/vpp/config_deps.py
+++ b/python/vyos/vpp/config_deps.py
@@ -31,7 +31,7 @@ def deps_xconnect_dict(conf) -> dict[str, list[str]]:
"""
xconn_members_dict: dict[str, list[str]] = {}
config = conf.get_config_dict(
- ['vpp', 'interfaces', 'xconnect'],
+ ['interfaces', 'vpp', 'xconnect'],
key_mangling=('-', '_'),
get_first_key=True,
no_tag_node_value_mangle=True,
@@ -39,7 +39,7 @@ def deps_xconnect_dict(conf) -> dict[str, list[str]]:
for xconn_name, xconn_config in config.items():
for member_name in xconn_config.get('member', {}).get('interface', []):
- xconn_ifaces_list = xconn_members_dict.get(xconn_name, [])
+ xconn_ifaces_list = xconn_members_dict.get(member_name, [])
xconn_ifaces_list.append(xconn_name)
xconn_members_dict.update({member_name: xconn_ifaces_list})
diff --git a/python/vyos/vpp/config_verify.py b/python/vyos/vpp/config_verify.py
index dab22cf8f..d5bb70899 100644
--- a/python/vyos/vpp/config_verify.py
+++ b/python/vyos/vpp/config_verify.py
@@ -103,8 +103,8 @@ def verify_vpp_remove_xconnect_interface(config: dict):
for xconn_member, xconn_iface in config.get('xconn_members').items():
if xconn_member == config.get('ifname'):
raise ConfigError(
- f'interface "{xconn_member}" is still in use within "vpp interfaces xconnect". '
- f'Please remove it from "vpp interface xconnect {xconn_iface}" before proceeding.'
+ f'interface "{xconn_member}" is still in use within "interfaces vpp xconnect". '
+ f'Please remove it from "interface vpp xconnect {xconn_iface}" before proceeding.'
)
diff --git a/python/vyos/vpp/interface/__init__.py b/python/vyos/vpp/interface/__init__.py
index 0a3176199..3f8ae7954 100644
--- a/python/vyos/vpp/interface/__init__.py
+++ b/python/vyos/vpp/interface/__init__.py
@@ -19,12 +19,10 @@ from .ethernet import EthernetInterface
from .geneve import GeneveInterface
from .interface import Interface
from .wireguard import WireguardInterface
-from .xconnect import XconnectInterface
__all__ = [
'EthernetInterface',
'GeneveInterface',
'Interface',
'WireguardInterface',
- 'XconnectInterface',
]
diff --git a/python/vyos/vpp/interface/xconnect.py b/python/vyos/vpp/interface/xconnect.py
deleted file mode 100644
index dd61d7146..000000000
--- a/python/vyos/vpp/interface/xconnect.py
+++ /dev/null
@@ -1,95 +0,0 @@
-# VyOS implementation of VPP bridge 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.utils import iftunnel_transform
-
-
-class XconnectInterface:
- def __init__(
- self,
- ifname: str,
- members: list = [],
- state: str = 'up',
- ):
- self.ifname = ifname
- self.members = members
- self.vpp = VPPControl()
- self.initial_state = state
-
- def add_l2_xconnect(self):
- """Add l2 cross connect
- Args:
- members (list): The list of the xconnect members
- Example:
- from vyos.vpp.interface import XconnectInterface
- a = XconnectInterface(ifname='xcon0', members=['eth0', 'vxlan0'])
- a.add_l2_xconnect()
- """
- interface_transform_filter = ('vxlan', 'geneve')
- 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)
- if second_member.startswith(interface_transform_filter):
- second_member = iftunnel_transform(second_member)
-
- member_first_if_index = self.vpp.get_sw_if_index(first_member)
- member_second_if_index = self.vpp.get_sw_if_index(second_member)
- self.vpp.api.sw_interface_set_l2_xconnect(
- rx_sw_if_index=member_first_if_index,
- tx_sw_if_index=member_second_if_index,
- enable=True,
- )
- self.vpp.api.sw_interface_set_l2_xconnect(
- rx_sw_if_index=member_second_if_index,
- tx_sw_if_index=member_first_if_index,
- enable=True,
- )
-
- def del_l2_xconnect(self):
- """Move l2 cross connect member to mode l3 (delte xconnect)
- Args:
- members (list): The list of the xconnect members
- Example:
- from vyos.vpp.interface import XconnectInterface
- a = XconnectInterface(ifname='xcon0', members=['eth0', 'vxlan0'])
- a.del_l2_xconnect()
- """
- interface_transform_filter = ('vxlan', 'geneve')
- 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)
- if second_member.startswith(interface_transform_filter):
- second_member = iftunnel_transform(second_member)
-
- member_first_if_index = self.vpp.get_sw_if_index(first_member)
- member_second_if_index = self.vpp.get_sw_if_index(second_member)
- self.vpp.api.sw_interface_set_l2_xconnect(
- rx_sw_if_index=member_first_if_index,
- tx_sw_if_index=member_second_if_index,
- enable=False,
- )
- self.vpp.api.sw_interface_set_l2_xconnect(
- rx_sw_if_index=member_second_if_index,
- tx_sw_if_index=member_first_if_index,
- enable=False,
- )
diff --git a/smoketest/scripts/cli/test_vpp.py b/smoketest/scripts/cli/test_vpp.py
index 937a6844e..70ac02583 100755
--- a/smoketest/scripts/cli/test_vpp.py
+++ b/smoketest/scripts/cli/test_vpp.py
@@ -730,9 +730,10 @@ class TestVPP(VyOSUnitTestSHIM.TestCase):
self.cli_commit()
def test_09_vpp_xconnect(self):
+ xconn_path = interfaces_path + ['xconnect']
vni = '23'
interface_vxlan = f'vppvxlan{vni}'
- interface_xconnect = f'xcon{vni}'
+ interface_xconnect = f'vppxcon{vni}'
source_address = '192.0.2.1'
remote_address = '192.0.2.254'
@@ -748,15 +749,7 @@ class TestVPP(VyOSUnitTestSHIM.TestCase):
# Add xconneect
self.cli_set(
- base_path
- + [
- 'interfaces',
- 'xconnect',
- interface_xconnect,
- 'member',
- 'interface',
- interface,
- ]
+ xconn_path + [interface_xconnect, 'member', 'interface', interface]
)
# Cross connect interfaces require 2 interfaces
@@ -765,15 +758,7 @@ class TestVPP(VyOSUnitTestSHIM.TestCase):
self.cli_commit()
self.cli_set(
- base_path
- + [
- 'interfaces',
- 'xconnect',
- interface_xconnect,
- 'member',
- 'interface',
- interface_vxlan,
- ]
+ xconn_path + [interface_xconnect, 'member', 'interface', interface_vxlan]
)
# commit changes
@@ -789,7 +774,7 @@ class TestVPP(VyOSUnitTestSHIM.TestCase):
self.assertIn(required_string, out)
# delete xconnect interface
- self.cli_delete(base_path + ['interfaces', 'xconnect', interface_xconnect])
+ self.cli_delete(xconn_path + [interface_xconnect])
self.cli_commit()
# check delete xconnect interface
diff --git a/src/conf_mode/vpp_interfaces_xconnect.py b/src/conf_mode/vpp_interfaces_xconnect.py
index 994332ba2..090d913b0 100644
--- a/src/conf_mode/vpp_interfaces_xconnect.py
+++ b/src/conf_mode/vpp_interfaces_xconnect.py
@@ -16,12 +16,13 @@
# 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 import ConfigError
+from vyos.configdict import get_interface_dict
+from vyos.utils.process import is_systemd_service_active
+
+from vyos.ifconfig.vpp import VPPXconnectInterface
from vyos.vpp.config_deps import deps_xconnect_dict
-from vyos.vpp.interface import XconnectInterface
from vyos.vpp.utils import cli_ifaces_list
@@ -38,26 +39,15 @@ def get_config(config=None) -> dict:
else:
conf = Config()
- base = ['vpp', 'interfaces', 'xconnect']
- vpp_interfaces = ['vpp', 'settings', 'interface']
+ base = ['interfaces', 'vpp', 'xconnect']
- ifname = os.environ['VYOS_TAGNODE_VALUE']
-
- # 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
+ # Get effective config as we need full dictionary per interface delete
effective_config = conf.get_config_dict(
base + [ifname],
key_mangling=('-', '_'),
@@ -66,46 +56,42 @@ def get_config(config=None) -> dict:
no_tag_node_value_mangle=True,
)
- if not config:
- config['remove'] = True
-
if effective_config:
config.update({'effective': effective_config})
- # Get global vpp interfaces for verify
- config['vpp_interfaces'] = conf.get_config_dict(
- vpp_interfaces,
- key_mangling=('-', '_'),
- get_first_key=True,
- no_tag_node_value_mangle=True,
- )
-
config['xconn_members'] = deps_xconnect_dict(conf)
config['vpp_ifaces'] = cli_ifaces_list(conf, 'candidate')
- config['ifname'] = ifname
-
return config
def verify(config):
- if 'remove' in config or 'remove_vpp' in config:
+ if 'deleted' in config or 'remove_vpp' in config:
return None
+ if not is_systemd_service_active('vpp.service'):
+ raise ConfigError(
+ 'Cannot configure layer 2 cross-connect: vpp.service is not running'
+ )
+
# Xconnect requires 2 members
if len(config.get('member', {}).get('interface')) != 2:
raise ConfigError('Cross connect requires 2 members')
- # Member must belong to VPP
+ not_allowed_prefixes = ('vppbond', 'vppbridge', 'vpplo')
for iface in config.get('member', {}).get('interface', []):
+ # Ensure the interface is allowed as xconnect member
+ if iface.startswith(not_allowed_prefixes):
+ raise ConfigError(f'{iface} cannot be configured as xconnect member')
+ # Member must belong to VPP
if iface not in config['vpp_ifaces']:
raise ConfigError(f'{iface} must be a VPP interface for xconnect')
- # Each interface can belong only to one xconnect
- for xconn_member, xconn_ifaces in config['xconn_members'].items():
- if len(xconn_ifaces) > 1:
+ # Each interface can belong only to one xconnect
+ xconn_members = config['xconn_members'][iface]
+ if len(xconn_members) > 1:
raise ConfigError(
- f'Interface {xconn_member} added to more than one xconnect: {xconn_ifaces}'
+ f'Interface {iface} added to more than one xconnect: {", ".join(xconn_members)}'
)
@@ -118,22 +104,19 @@ def apply(config):
return None
ifname = config.get('ifname')
+ xconnect = VPPXconnectInterface(ifname)
# Delete xconnect
if 'effective' in config:
remove_config = config.get('effective')
- members = remove_config.get('member', {}).get('interface')
- i = XconnectInterface(ifname, members=members)
- i.del_l2_xconnect()
+ members = remove_config['member']['interface']
+ xconnect.remove(members)
- if 'remove' in config:
+ if 'deleted' in config:
return None
# Add xconnect
- members = config.get('member', {}).get('interface')
- state = 'up' if 'disable' not in config else 'down'
- i = XconnectInterface(ifname, members=members, state=state)
- i.add_l2_xconnect()
+ xconnect.update(config)
return None
diff --git a/src/migration-scripts/vpp/5-to-6 b/src/migration-scripts/vpp/5-to-6
index 0131dee33..a816bb536 100644
--- a/src/migration-scripts/vpp/5-to-6
+++ b/src/migration-scripts/vpp/5-to-6
@@ -49,6 +49,8 @@
#
# Migrate bridge interface from vpp section to 'interfaces vpp bridge' (T8327)
#
+# Migrate xconnect interface from vpp section to 'interfaces vpp xconnect' (T8328)
+#
from vyos.configtree import ConfigTree
@@ -516,6 +518,24 @@ def _migrate_vpp_bridge_interface(config: ConfigTree) -> None:
config.delete(['vpp', 'interfaces'])
+def _migrate_vpp_xconnect_interface(config: ConfigTree) -> None:
+ base = ['vpp', 'interfaces', 'xconnect']
+ new_base = ['interfaces', 'vpp', 'xconnect']
+
+ if not config.exists(base):
+ return
+
+ config.set(new_base)
+
+ for ifname in config.list_nodes(base):
+ _migrate_interface(config, 'xconnect', ifname)
+
+ config.delete(base)
+
+ if len(config.list_nodes(['vpp', 'interfaces'])) == 0:
+ config.delete(['vpp', 'interfaces'])
+
+
def migrate(config: ConfigTree) -> None:
if not config.exists(['vpp']):
# Nothing to do
@@ -537,3 +557,4 @@ def migrate(config: ConfigTree) -> None:
_migrate_vpp_loopback_interface(config)
_migrate_vpp_gre_interface(config)
_migrate_vpp_bridge_interface(config)
+ _migrate_vpp_xconnect_interface(config)