]> git.ipfire.org Git - thirdparty/systemd.git/commitdiff
test-network: check that network configuration is stable with KeepConfiguration=yes
authorMatteo Croce <teknoraver@meta.com>
Wed, 12 Mar 2025 13:36:14 +0000 (14:36 +0100)
committerLuca Boccassi <luca.boccassi@gmail.com>
Tue, 8 Apr 2025 19:52:11 +0000 (20:52 +0100)
Check that when networkd restarts, and the network configures
KeepConfiguration=yes, the network configuration is never changed.

Ensure this by dumping the `ip monitor` output when networkd is restarting.

Co-authored-by: Yu Watanabe <watanabe.yu+github@gmail.com>
(cherry picked from commit 912a48572de1411cff2964452e0d7a021b43921f)
(cherry picked from commit f17b5e6766807c9e6540a7cf6ee283b1567d0ef8)

test/test-network/conf/85-static-ipv6.network [new file with mode: 0644]
test/test-network/systemd-networkd-tests.py

diff --git a/test/test-network/conf/85-static-ipv6.network b/test/test-network/conf/85-static-ipv6.network
new file mode 100644 (file)
index 0000000..50322b1
--- /dev/null
@@ -0,0 +1,22 @@
+# SPDX-License-Identifier: LGPL-2.1-or-later
+[Match]
+Name=dummy98
+
+[Network]
+DHCP=no
+IPv6AcceptRA=no
+KeepConfiguration=yes
+
+[Address]
+Address=2001:db8:0:f101::15/64
+PreferredLifetime=forever
+
+[Address]
+Address=2001:db8:1:f101::15/64
+PreferredLifetime=0
+
+[Route]
+Gateway=fe80::f0ca:cc1a
+Source=::/0
+Destination=::/0
+Metric=1
index 26934cc18d2da30f594b22eed98278744d97c288..ee7f6ccd0aa2214b2d06c8a6168d74f73ff8e732 100755 (executable)
@@ -29,6 +29,7 @@ import signal
 import socket
 import subprocess
 import sys
+import tempfile
 import time
 import unittest
 
@@ -4228,6 +4229,62 @@ class NetworkdNetworkTests(unittest.TestCase, Utilities):
         self.assertRegex(output, 'inet 10.1.2.3/16 scope global dummy98')
         self.assertNotRegex(output, 'inet 10.2.3.4/16 scope global dynamic dummy98')
 
+    def check_keep_configuration_on_restart(self):
+        self.wait_online('dummy98:routable')
+        self.wait_online('unmanaged0:routable', setup_state='unmanaged')
+
+        print('### ip -6 address show dev dummy98')
+        output = check_output('ip -6 address show dev dummy98')
+        print(output)
+        self.assertIn('inet6 2001:db8:0:f101::15/64 scope global', output)
+        self.assertIn('inet6 2001:db8:1:f101::15/64 scope global deprecated', output)
+
+        print('### ip -6 address show dev unmanaged0')
+        output = check_output('ip -6 address show dev unmanaged0')
+        print(output)
+        self.assertIn('inet6 2001:db8:9999:f101::15/64 scope global', output)
+
+        print('### ip -6 route show default dev dummy98')
+        output = check_output('ip -6 route show default dev dummy98')
+        print(output)
+        self.assertIn('default via fe80::f0ca:cc1a proto static metric 1 pref medium', output)
+
+    def test_keep_configuration_on_restart(self):
+        # Add an unmanaged interface with an up address
+        call('ip link add unmanaged0 type dummy')
+        call('ip link set unmanaged0 up')
+        call('ip -6 addr add 2001:db8:9999:f101::15/64 dev unmanaged0')
+
+        copy_network_unit('12-dummy.netdev', '85-static-ipv6.network')
+        start_networkd()
+        self.check_keep_configuration_on_restart()
+
+        # Start `ip monitor` with output to a temporary file
+        with tempfile.TemporaryFile(mode='r+', prefix='ip_monitor') as logfile:
+            process = subprocess.Popen(['ip', 'monitor', 'dev', 'dummy98'], stdout=logfile, text=True)
+            restart_networkd()
+            self.check_keep_configuration_on_restart()
+
+            process.send_signal(signal.SIGTERM)
+            process.wait()
+
+            print('### ip monitor dev dummy98 BEGIN')
+
+            # Read the `ip monitor` output looking for network changes
+            logfile.seek(0)
+            for line in logfile:
+                print(line, end="")
+                # Check if a link went down
+                self.assertNotRegex(line, 'unmanaged0: .* state DOWN')
+                self.assertNotRegex(line, 'dummy98: .* state DOWN')
+                # Check if an address was removed
+                self.assertNotRegex(line, '^Deleted .* 2001:db8:')
+                self.assertNotRegex(line, '^Deleted 2001:db8:.*/64')
+                # Check if the default route was removed
+                self.assertNotRegex(line, '^Deleted default via fe80::f0ca:cc1a')
+
+            print('### ip monitor dev dummy98 END')
+
     def check_nexthop(self, manage_foreign_nexthops, first):
         self.wait_online('veth99:routable', 'veth-peer:routable', 'dummy98:routable')