summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorViacheslav Hletenko <v.gletenko@vyos.io>2026-03-02 17:21:21 +0200
committerGitHub <noreply@github.com>2026-03-02 17:21:21 +0200
commit00361b88005948802344adbe6184057cee91cde4 (patch)
tree76124d6c8502666c6fe91c2433ee938a2ef09feb
parentef710a5acbd7af9f60ba63716912b0c9a20f5329 (diff)
parent61d607295fa826e707b82432c46668a2fb94ab22 (diff)
downloadvyos-1x-00361b88005948802344adbe6184057cee91cde4.tar.gz
vyos-1x-00361b88005948802344adbe6184057cee91cde4.zip
Merge pull request #5019 from natali-rs1985/T8324
vpp: T8324: Migrate loopback interface to 'interfaces vpp loopback'
-rw-r--r--interface-definitions/vpp.xml.in19
-rw-r--r--interface-definitions/vpp_interface_loopback.xml.in32
-rw-r--r--python/vyos/ifconfig/vpp/__init__.py2
-rw-r--r--python/vyos/ifconfig/vpp/loopback.py105
-rw-r--r--python/vyos/vpp/interface/__init__.py2
-rw-r--r--python/vyos/vpp/interface/loopback.py72
-rw-r--r--smoketest/configs/assert/vpp2
-rwxr-xr-xsmoketest/scripts/cli/test_vpp.py78
-rw-r--r--src/conf_mode/vpp_interfaces_bridge.py12
-rw-r--r--src/conf_mode/vpp_interfaces_loopback.py98
-rw-r--r--src/migration-scripts/vpp/5-to-627
11 files changed, 202 insertions, 247 deletions
diff --git a/interface-definitions/vpp.xml.in b/interface-definitions/vpp.xml.in
index ca9838af0..733e8eb83 100644
--- a/interface-definitions/vpp.xml.in
+++ b/interface-definitions/vpp.xml.in
@@ -146,25 +146,6 @@
#include <include/kernel-interface-tun.xml.i>
</children>
</tagNode>
- <tagNode name="loopback" owner="${vyos_conf_scripts_dir}/vpp_interfaces_loopback.py">
- <properties>
- <priority>325</priority>
- <help>Loopback Interface</help>
- <constraint>
- <regex>lo[0-9]+</regex>
- </constraint>
- <constraintErrorMessage>Loopback interface must be named loN</constraintErrorMessage>
- <valueHelp>
- <format>loN</format>
- <description>Loopback interface name</description>
- </valueHelp>
- </properties>
- <children>
- #include <include/generic-description.xml.i>
- #include <include/interface/disable.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>
diff --git a/interface-definitions/vpp_interface_loopback.xml.in b/interface-definitions/vpp_interface_loopback.xml.in
new file mode 100644
index 000000000..9998c34d2
--- /dev/null
+++ b/interface-definitions/vpp_interface_loopback.xml.in
@@ -0,0 +1,32 @@
+<?xml version="1.0"?>
+<interfaceDefinition>
+ <node name='interfaces'>
+ <children>
+ <node name='vpp'>
+ <children>
+ <tagNode name="loopback" owner="${vyos_conf_scripts_dir}/vpp_interfaces_loopback.py">
+ <properties>
+ <priority>325</priority>
+ <help>Loopback Interface</help>
+ <constraint>
+ <regex>vpplo[0-9]+</regex>
+ </constraint>
+ <constraintErrorMessage>Loopback interface must be named vpploN</constraintErrorMessage>
+ <valueHelp>
+ <format>vpploN</format>
+ <description>Loopback interface name</description>
+ </valueHelp>
+ </properties>
+ <children>
+ #include <include/generic-description.xml.i>
+ #include <include/interface/disable.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 a7cfe79a4..b0a477c28 100644
--- a/python/vyos/ifconfig/vpp/__init__.py
+++ b/python/vyos/ifconfig/vpp/__init__.py
@@ -18,11 +18,13 @@
from .bond import VPPBondInterface
from .interface import VPPInterface
from .ipip import VPPIPIPInterface
+from .loopback import VPPLoopbackInterface
from .vxlan import VPPVXLANInterface
__all__ = [
'VPPBondInterface',
'VPPInterface',
'VPPIPIPInterface',
+ 'VPPLoopbackInterface',
'VPPVXLANInterface',
]
diff --git a/python/vyos/ifconfig/vpp/loopback.py b/python/vyos/ifconfig/vpp/loopback.py
new file mode 100644
index 000000000..36a30cb58
--- /dev/null
+++ b/python/vyos/ifconfig/vpp/loopback.py
@@ -0,0 +1,105 @@
+# VyOS implementation of VPP Loopback 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 VPPLoopbackInterface(Interface, VPPInterface):
+ """Interface Loopback"""
+
+ def __init__(self, ifname, config):
+ self.ifname = ifname
+ self.instance = int(ifname.removeprefix('vpplo'))
+ self.vpp_ifname = f'loop{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'
+
+ def _create(self):
+ pass
+
+ def add(self):
+ """Create Loopback interface
+ https://github.com/FDio/vpp/blob/stable/2306/src/vnet/interface.api
+ Example:
+ from vyos.ifconfig.vpp import VPPLoopbackInterface
+ a = VPPLoopbackInterface(ifname='vpplo1', config)
+ a.add()
+ """
+ self.vpp.api.create_loopback_instance(
+ is_specified=True, user_instance=self.instance
+ )
+ # 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(self):
+ """Delete Loopback interface
+ Example:
+ from vyos.ifconfig.vpp import VPPLoopbackInterface
+ a = VPPLoopbackInterface(ifname='vpplo1', config)
+ a.delete()
+ """
+ return self.vpp.api.delete_loopback(sw_if_index=self.index)
+
+ def kernel_add(self):
+ """Add LCP pair
+ Example:
+ from vyos.ifconfig.vpp import VPPLoopbackInterface
+ a = VPPLoopbackInterface(ifname='vpplo1')
+ 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 VPPLoopbackInterface
+ a = VPPLoopbackInterface(ifname='vpplo1')
+ 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 loopback interface
+ self.delete()
+
+ def update(self, config):
+ # Add loopback interface
+ self.add()
+
+ # 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/vpp/interface/__init__.py b/python/vyos/vpp/interface/__init__.py
index f970edb63..4ae96f486 100644
--- a/python/vyos/vpp/interface/__init__.py
+++ b/python/vyos/vpp/interface/__init__.py
@@ -20,7 +20,6 @@ from .ethernet import EthernetInterface
from .geneve import GeneveInterface
from .gre import GREInterface
from .interface import Interface
-from .loopback import LoopbackInterface
from .wireguard import WireguardInterface
from .xconnect import XconnectInterface
@@ -30,7 +29,6 @@ __all__ = [
'GeneveInterface',
'GREInterface',
'Interface',
- 'LoopbackInterface',
'WireguardInterface',
'XconnectInterface',
]
diff --git a/python/vyos/vpp/interface/loopback.py b/python/vyos/vpp/interface/loopback.py
deleted file mode 100644
index 357423780..000000000
--- a/python/vyos/vpp/interface/loopback.py
+++ /dev/null
@@ -1,72 +0,0 @@
-# VyOS implementation of VPP Loopback 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.interface.interface import Interface
-
-
-class LoopbackInterface(Interface):
- """Interface Loopback"""
-
- def __init__(self, ifname, kernel_interface: str = '', state: str = 'up'):
- super().__init__(ifname)
- self.instance = int(ifname.removeprefix('lo'))
- self.ifname = f'loop{self.instance}'
- self.kernel_interface = kernel_interface
- self.initial_state = state
-
- def add(self):
- """Create Loopback interface
- https://github.com/FDio/vpp/blob/stable/2306/src/vnet/interface.api
- Example:
- from vyos.vpp.interface import LoopbackInterface
- a = LoopbackInterface(ifname='lo1')
- a.add()
- """
- self.vpp.api.create_loopback_instance(
- is_specified=True, user_instance=self.instance
- )
- # Set interface state
- self.set_state(self.initial_state)
-
- def delete(self):
- """Delete Loopback interface
- Example:
- from vyos.vpp.interface import LoopbackInterface
- a = LoopbackInterface(ifname='lo1')
- a.delete()
- """
- loopback_if_index = self.vpp.get_sw_if_index(f'loop{self.instance}')
- return self.vpp.api.delete_loopback(sw_if_index=loopback_if_index)
-
- def kernel_add(self):
- """Add LCP pair
- Example:
- from vyos.vpp.interface import LoopbackInterface
- a = LoopbackInterface(ifname='lo1')
- 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 LoopbackInterface
- a = LoopbackInterface(ifname='lo1')
- a.kernel_delete()
- """
- self.vpp.lcp_pair_del(self.ifname, self.kernel_interface)
diff --git a/smoketest/configs/assert/vpp b/smoketest/configs/assert/vpp
index 8a68852b7..f8cab98f4 100644
--- a/smoketest/configs/assert/vpp
+++ b/smoketest/configs/assert/vpp
@@ -38,7 +38,7 @@ set vpp interfaces gre gre12 source-address '192.0.2.21'
set vpp interfaces gre gre12 tunnel-type 'l3'
set interfaces vpp ipip vppipip20 remote '192.0.2.2'
set interfaces vpp ipip vppipip20 source-address '192.0.2.1'
-set vpp interfaces loopback lo12 description 'Loop12'
+set interfaces vpp loopback vpplo12 description 'Loop12'
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'
diff --git a/smoketest/scripts/cli/test_vpp.py b/smoketest/scripts/cli/test_vpp.py
index 20baed178..90e4e4538 100755
--- a/smoketest/scripts/cli/test_vpp.py
+++ b/smoketest/scripts/cli/test_vpp.py
@@ -517,84 +517,28 @@ class TestVPP(VyOSUnitTestSHIM.TestCase):
self.cli_commit()
def test_05_vpp_loopback(self):
- interface_loopback = 'lo11'
- interface_kernel = 'vpptun11'
- new_interface_kernel = 'vpptun12'
- kernel_address = '192.0.2.54'
+ loopback_path = interfaces_path + ['loopback']
+ interface_loopback = 'vpplo11'
+ address = '192.0.2.54'
- self.cli_set(base_path + ['interfaces', 'loopback', interface_loopback])
- self.cli_set(
- base_path
- + [
- 'interfaces',
- 'loopback',
- interface_loopback,
- 'kernel-interface',
- interface_kernel,
- ]
- )
- self.cli_set(
- base_path
- + ['kernel-interfaces', interface_kernel, 'address', f'{kernel_address}/25']
- )
+ self.cli_set(loopback_path + [interface_loopback])
+ self.cli_set(loopback_path + [interface_loopback, 'address', f'{address}/25'])
# 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_loopback}'))
- current_address = get_address(interface_kernel)
- self.assertEqual(kernel_address, current_address)
+ current_address = get_address(interface_loopback)
+ self.assertEqual(address, current_address)
# check loopback interface
_, out = rc_cmd('sudo vppctl show interface loop11')
required_str = 'loop11'
self.assertIn(required_str, out)
- # delete loopback kernel-interface but do not delete 'vpp kernel-interface'
- # expect raise ConfigError
- self.cli_delete(
- base_path
- + [
- 'interfaces',
- 'loopback',
- interface_loopback,
- 'kernel-interface',
- interface_kernel,
- ]
- )
- with self.assertRaises(ConfigSessionError):
- self.cli_commit()
-
- # update loopback kernel-interface but do not change 'vpp kernel-interface'
- # expect raise ConfigError
- self.cli_set(
- base_path
- + [
- 'interfaces',
- 'loopback',
- interface_loopback,
- '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 loopback kernel-interface
- self.cli_delete(
- base_path
- + ['interfaces', 'loopback', interface_loopback, 'kernel-interface']
- )
- self.cli_commit()
- self.assertFalse(os.path.isdir(f'/sys/class/net/{interface_kernel}'))
-
# delete loopback interface
- self.cli_delete(base_path + ['interfaces', 'loopback', interface_loopback])
+ self.cli_delete(loopback_path + [interface_loopback])
self.cli_commit()
def test_06_vpp_bonding(self):
@@ -801,7 +745,7 @@ class TestVPP(VyOSUnitTestSHIM.TestCase):
self.assertRegex(out, r'\s*vxlan_tunnel23\s+\d+\s+\d+')
# Add Loopback BVI to the bridge
- self.cli_set(base_path + ['interfaces', 'loopback', f'lo{vni}'])
+ self.cli_set(interfaces_path + ['loopback', f'vpplo{vni}'])
self.cli_set(
base_path
+ [
@@ -810,7 +754,7 @@ class TestVPP(VyOSUnitTestSHIM.TestCase):
interface_bridge,
'member',
'interface',
- f'lo{vni}',
+ f'vpplo{vni}',
'bvi',
]
)
diff --git a/src/conf_mode/vpp_interfaces_bridge.py b/src/conf_mode/vpp_interfaces_bridge.py
index 8d25cba00..d53173f04 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', 'vppvxlan')
+ allowed_prefixes = ('vppbond', 'gre', 'geneve', 'vpplo', 'vppvxlan')
if 'member' in config:
bvi_exists = False
@@ -116,7 +116,7 @@ def verify(config):
if 'bvi' in member_config:
if bvi_exists:
raise ConfigError("Only one BVI per bridge domain is allowed")
- if not member.startswith('lo'):
+ if not member.startswith('vpplo'):
raise ConfigError("BVI can only be defined on loopback interface")
bvi_exists = True
@@ -138,9 +138,9 @@ def apply(config):
for member in config.get('members_removed'):
if member.startswith(interface_transform_filter):
member = iftunnel_transform(member)
- if member.startswith('lo'):
+ if member.startswith('vpplo'):
# interface name in VPP is loopX
- member = member.replace('lo', 'loop')
+ member = member.replace('vpplo', 'loop')
elif member.startswith('vppbond'):
# interface name in VPP is BondEthernetX
member = member.replace('vppbond', 'BondEthernet')
@@ -166,9 +166,9 @@ def apply(config):
for member, member_config in members.items():
if member.startswith(interface_transform_filter):
member = iftunnel_transform(member)
- if member.startswith('lo'):
+ if member.startswith('vpplo'):
# interface name in VPP is loopX
- member = member.replace('lo', 'loop')
+ member = member.replace('vpplo', 'loop')
if 'bvi' in member_config:
port_type = 1
elif member.startswith('vppbond'):
diff --git a/src/conf_mode/vpp_interfaces_loopback.py b/src/conf_mode/vpp_interfaces_loopback.py
index d08557959..2f7b59354 100644
--- a/src/conf_mode/vpp_interfaces_loopback.py
+++ b/src/conf_mode/vpp_interfaces_loopback.py
@@ -16,22 +16,14 @@
# 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 LoopbackInterface
-from vyos.vpp.config_verify import (
- verify_vpp_remove_kernel_interface,
- verify_vpp_change_kernel_interface,
- verify_vpp_exists_kernel_interface,
-)
-from vyos.vpp.utils import cli_ifaces_lcp_kernel_list
+from vyos.ifconfig.vpp import VPPLoopbackInterface
def get_config(config=None) -> dict:
@@ -47,62 +39,22 @@ def get_config(config=None) -> dict:
else:
conf = Config()
- base = ['vpp', 'interfaces', 'loopback']
- base_kernel_interfaces = ['vpp', 'kernel-interfaces']
-
- ifname = os.environ['VYOS_TAGNODE_VALUE']
+ base = ['interfaces', 'vpp', 'loopback']
- # 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],
+ # Get 'vpp settings' config
+ config['vpp_settings'] = conf.get_config_dict(
+ ['vpp', 'settings'],
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)
-
- # Dependency
- 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)
-
# NAT dependency
if conf.exists(['vpp', 'nat', 'nat44']):
set_dependents('vpp_nat_nat44', conf)
@@ -113,7 +65,6 @@ def get_config(config=None) -> dict:
if conf.exists(['vpp', 'acl']):
set_dependents('vpp_acl', conf)
- config['ifname'] = ifname
return config
@@ -122,13 +73,10 @@ def verify(config):
if 'remove_vpp' in config:
return None
- verify_vpp_remove_kernel_interface(config)
-
- if 'remove' in config:
- return None
-
- verify_vpp_change_kernel_interface(config)
- verify_vpp_exists_kernel_interface(config)
+ if not is_systemd_service_active('vpp.service'):
+ raise ConfigError(
+ 'Cannot configure VPP loopback interface: vpp.service is not running'
+ )
def generate(config):
@@ -140,23 +88,13 @@ def apply(config):
return None
ifname = config.get('ifname')
- # Delete interface
- if 'effective' in config:
- i = LoopbackInterface(ifname)
- i.delete()
-
- if 'remove' in config:
- return None
+ loopback = VPPLoopbackInterface(ifname, config)
+ loopback.remove()
- # Add interface
- kernel_interface = config.get('kernel_interface', '')
- state = 'up' if 'disable' not in config else 'down'
- i = LoopbackInterface(ifname, kernel_interface, state)
- i.add()
+ if 'deleted' in config:
+ return
- # Add kernel-interface (LCP) if interface is not exist
- if 'kernel_interface' in config and not is_interface(kernel_interface):
- i.kernel_add()
+ loopback.update(config)
call_dependents()
diff --git a/src/migration-scripts/vpp/5-to-6 b/src/migration-scripts/vpp/5-to-6
index 18f0f70d2..23b2d4eaa 100644
--- a/src/migration-scripts/vpp/5-to-6
+++ b/src/migration-scripts/vpp/5-to-6
@@ -43,6 +43,8 @@
#
# Move ipip interface from vpp section to 'interfaces vpp ipip' (T8314)
#
+# Migrate loopback interface from vpp section to 'interfaces vpp loopback' (T8324)
+#
from vyos.configtree import ConfigTree
@@ -437,6 +439,30 @@ def _migrate_vpp_ipip_interface(config: ConfigTree) -> None:
config.delete(['vpp', 'interfaces'])
+def _migrate_vpp_loopback_interface(config: ConfigTree) -> None:
+ base = ['vpp', 'interfaces', 'loopback']
+ new_base = ['interfaces', 'vpp', 'loopback']
+ 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, 'loopback', ifname)
+
+ _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(config: ConfigTree) -> None:
if not config.exists(['vpp']):
# Nothing to do
@@ -455,3 +481,4 @@ def migrate(config: ConfigTree) -> None:
_migrate_vpp_bonding_interface(config)
_migrate_vpp_vxlan_interface(config)
_migrate_vpp_ipip_interface(config)
+ _migrate_vpp_loopback_interface(config)