From 9d24bffcc47b48396c580a48ec9405411e955342 Mon Sep 17 00:00:00 2001 From: Christian Breunig Date: Wed, 7 Oct 2026 19:56:41 +0200 Subject: ethernet: T3871: an ethernet interface belongs to the Kernel and can not be deleted Every NIC used to carry a hw-id, which was enough to make its node exist. Without it a bare node is empty and never reaches the running configuration, so releasing a bond member looked exactly like a deletion: the port was torn down and left admin-down, with nothing left to bring it back. An ethernet interface is owned by the Kernel and - like 'lo' - outlives its configuration. Removing it now resets the port to the Kernel defaults and leaves the link up. --- smoketest/scripts/cli/test_interfaces_ethernet.py | 45 ++++++++++++++++++++--- 1 file changed, 40 insertions(+), 5 deletions(-) (limited to 'smoketest/scripts/cli/test_interfaces_ethernet.py') diff --git a/smoketest/scripts/cli/test_interfaces_ethernet.py b/smoketest/scripts/cli/test_interfaces_ethernet.py index 0d29d291a..473af051b 100755 --- a/smoketest/scripts/cli/test_interfaces_ethernet.py +++ b/smoketest/scripts/cli/test_interfaces_ethernet.py @@ -32,7 +32,10 @@ from vyos.ethtool import Ethtool from vyos.netlink import coalesce from vyos.frrender import mgmt_daemon from vyos.ifconfig import Section +from vyos.ifconfig.interface import Interface from vyos.utils.file import read_file +from vyos.utils.network import get_interface_config +from vyos.utils.network import interface_exists from vyos.utils.network import is_intf_addr_assigned from vyos.utils.network import is_ipv6_link_local from vyos.utils.process import cmdl @@ -52,10 +55,6 @@ class EthernetInterfaceTest(BasicInterfaceTest.TestCase): for tmp in Section.interfaces('ethernet', vlan=False): cls._interfaces.append(tmp) - cls._macs = {} - for interface in cls._interfaces: - cls._macs[interface] = read_file(f'/sys/class/net/{interface}/address') - # call base-classes classmethod super(EthernetInterfaceTest, cls).setUpClass() @@ -67,7 +66,6 @@ class EthernetInterfaceTest(BasicInterfaceTest.TestCase): self.cli_delete(self._base_path + [interface]) self.cli_set(self._base_path + [interface, 'duplex', 'auto']) self.cli_set(self._base_path + [interface, 'speed', 'auto']) - self.cli_set(self._base_path + [interface, 'hw-id', self._macs[interface]]) self.cli_commit() @@ -168,6 +166,43 @@ class EthernetInterfaceTest(BasicInterfaceTest.TestCase): # manually, else tearDown() will have problem in commit() self.cli_delete(self._base_path + [unknonw_interface]) + def test_delete_and_readd_interface(self): + # T3871: deleting an ethernet interface removes its configuration, but + # the port itself is "eternal" - it must still be present afterwards, + # keep its name, and be configurable again. An unconfigured port is + # not a deleted one. + for interface in self._interfaces: + self.cli_set(self._base_path + [interface, 'description', 'before']) + self.cli_commit() + + for interface in self._interfaces: + self.cli_delete(self._base_path + [interface]) + self.cli_commit() + + for interface in self._interfaces: + # an ethernet port belongs to the kernel - deleting its + # configuration returns it to the state the kernel gave it, it + # does not take the port away or leave it stranded admin-down + self.assertTrue(interface_exists(interface), + f'{interface} disappeared after its configuration ' + 'was deleted') + self.assertEqual(Interface(interface).get_admin_state(), 'up', + f'{interface} was left admin-down after its ' + 'configuration was deleted') + tmp = get_interface_config(interface) + self.assertEqual(tmp['mtu'], 1500, + f'{interface} kept a stale MTU after its ' + 'configuration was deleted') + + # re-adding must work and take effect + for interface in self._interfaces: + self.cli_set(self._base_path + [interface, 'description', 'after']) + self.cli_commit() + + for interface in self._interfaces: + self.assertEqual( + read_file(f'/sys/class/net/{interface}/ifalias'), 'after') + def test_speed_duplex_verify(self): for interface in self._interfaces: self.cli_set(self._base_path + [interface, 'speed', '1000']) -- cgit v1.2.3