]> git.ipfire.org Git - thirdparty/systemd.git/commitdiff
networkd: support marking links unmanaged 4228/head
authorDavid Michael <david.michael@coreos.com>
Tue, 27 Sep 2016 22:18:14 +0000 (15:18 -0700)
committerDavid Michael <david.michael@coreos.com>
Thu, 1 Dec 2016 22:41:51 +0000 (14:41 -0800)
man/systemd.network.xml
src/network/networkd-link.c
src/network/networkd-network-gperf.gperf
src/network/networkd-network.h
test/networkd-test.py

index 99283813fd46fa5e0ac777a2e5ec0367a1aa3a21..53c49f817fd79771add7afcae5e9c257f546e575 100644 (file)
           the network otherwise.</para>
         </listitem>
       </varlistentry>
+      <varlistentry>
+        <term><varname>Unmanaged=</varname></term>
+        <listitem>
+          <para>A boolean. When <literal>yes</literal>, no attempts are
+          made to bring up or configure matching links, equivalent to
+          when there are no matching network files. Defaults to
+          <literal>no</literal>.</para>
+          <para>This is useful for preventing later matching network
+          files from interfering with certain interfaces that are fully
+          controlled by other applications.</para>
+        </listitem>
+      </varlistentry>
     </variablelist>
   </refsect1>
 
index cb7df633b76211db55f566d6f2e56748a8bad3a4..643578c16457571838f2333f3347b9c9ef9cc2d0 100644 (file)
@@ -2523,6 +2523,9 @@ static int link_initialized_and_synced(sd_netlink *rtnl, sd_netlink_message *m,
                 if (r == -ENOENT) {
                         link_enter_unmanaged(link);
                         return 1;
+                } else if (r == 0 && network->unmanaged) {
+                        link_enter_unmanaged(link);
+                        return 0;
                 } else if (r < 0)
                         return r;
 
index 463f4595c133fdd2b31a268d1b9e0c0df0fce81d..0a5cd763dd74dd20300a711ff3e4394afe846d49 100644 (file)
@@ -29,6 +29,7 @@ Match.Architecture,                     config_parse_net_condition,
 Link.MACAddress,                        config_parse_hwaddr,                            0,                             offsetof(Network, mac)
 Link.MTUBytes,                          config_parse_iec_size,                          0,                             offsetof(Network, mtu)
 Link.ARP,                               config_parse_tristate,                          0,                             offsetof(Network, arp)
+Link.Unmanaged,                         config_parse_bool,                              0,                             offsetof(Network, unmanaged)
 Network.Description,                    config_parse_string,                            0,                             offsetof(Network, description)
 Network.Bridge,                         config_parse_netdev,                            0,                             offsetof(Network, bridge)
 Network.Bond,                           config_parse_netdev,                            0,                             offsetof(Network, bond)
index 4dbc19fc3b6951476adadaa2b589aad70bb959d6..e05dccda294f22d39b572870e7660c8119a3ccd1 100644 (file)
@@ -176,6 +176,7 @@ struct Network {
         struct ether_addr *mac;
         size_t mtu;
         int arp;
+        bool unmanaged;
         uint32_t iaid;
         DUID duid;
 
index 4837c71652a07c75ee4b44ad7b03a20107e15fbe..aed513927509318ff8743ba056ab8ca004315d2a 100755 (executable)
@@ -92,6 +92,45 @@ class NetworkdTestingUtilities:
             dropin.write(contents)
         self.addCleanup(os.remove, dropin_path)
 
+    def assert_link_states(self, **kwargs):
+        """Match networkctl link states to the given ones.
+
+        Each keyword argument should be the name of a network interface
+        with its expected value of the "SETUP" column in output from
+        networkctl.  The interfaces have five seconds to come online
+        before the check is performed.  Every specified interface must
+        be present in the output, and any other interfaces found in the
+        output are ignored.
+
+        A special interface state "managed" is supported, which matches
+        any value in the "SETUP" column other than "unmanaged".
+        """
+        if not kwargs:
+            return
+        interfaces = set(kwargs)
+
+        # Wait for the requested interfaces, but don't fail for them.
+        subprocess.call([NETWORKD_WAIT_ONLINE, '--timeout=5'] +
+                        ['--interface=%s' % iface for iface in kwargs])
+
+        # Validate each link state found in the networkctl output.
+        out = subprocess.check_output(['networkctl', '--no-legend']).rstrip()
+        for line in out.decode('utf-8').split('\n'):
+            fields = line.split()
+            if len(fields) >= 5 and fields[1] in kwargs:
+                iface = fields[1]
+                expected = kwargs[iface]
+                actual = fields[-1]
+                if (actual != expected and
+                        not (expected == 'managed' and actual != 'unmanaged')):
+                    self.fail("Link %s expects state %s, found %s" %
+                              (iface, expected, actual))
+                interfaces.remove(iface)
+
+        # Ensure that all requested interfaces have been covered.
+        if interfaces:
+            self.fail("Missing links in status output: %s" % interfaces)
+
 
 class ClientTestBase(NetworkdTestingUtilities):
     """Provide common methods for testing networkd against servers."""
@@ -720,6 +759,83 @@ DNS=127.0.0.1''')
             raise
 
 
+class UnmanagedClientTest(unittest.TestCase, NetworkdTestingUtilities):
+    """Test if networkd manages the correct interfaces."""
+
+    def setUp(self):
+        """Write .network files to match the named veth devices."""
+        # Define the veth+peer pairs to be created.
+        # Their pairing doesn't actually matter, only their names do.
+        self.veths = {
+            'm1def': 'm0unm',
+            'm1man': 'm1unm',
+        }
+
+        # Define the contents of .network files to be read in order.
+        self.configs = (
+            "[Match]\nName=m1def\n",
+            "[Match]\nName=m1unm\n[Link]\nUnmanaged=yes\n",
+            "[Match]\nName=m1*\n[Link]\nUnmanaged=no\n",
+        )
+
+        # Write out the .network files to be cleaned up automatically.
+        for i, config in enumerate(self.configs):
+            self.write_network("%02d-test.network" % i, config)
+
+    def tearDown(self):
+        """Stop networkd."""
+        subprocess.call(['systemctl', 'stop', 'systemd-networkd'])
+
+    def create_iface(self):
+        """Create temporary veth pairs for interface matching."""
+        for veth, peer in self.veths.items():
+            subprocess.check_call(['ip', 'link', 'add',
+                                   'name', veth, 'type', 'veth',
+                                   'peer', 'name', peer])
+            self.addCleanup(subprocess.call,
+                            ['ip', 'link', 'del', 'dev', peer])
+
+    def test_unmanaged_setting(self):
+        """Verify link states with Unmanaged= settings, hot-plug."""
+        subprocess.check_call(['systemctl', 'start', 'systemd-networkd'])
+        self.create_iface()
+        self.assert_link_states(m1def='managed',
+                                m1man='managed',
+                                m1unm='unmanaged',
+                                m0unm='unmanaged')
+
+    def test_unmanaged_setting_coldplug(self):
+        """Verify link states with Unmanaged= settings, cold-plug."""
+        self.create_iface()
+        subprocess.check_call(['systemctl', 'start', 'systemd-networkd'])
+        self.assert_link_states(m1def='managed',
+                                m1man='managed',
+                                m1unm='unmanaged',
+                                m0unm='unmanaged')
+
+    def test_catchall_config(self):
+        """Verify link states with a catch-all config, hot-plug."""
+        # Don't actually catch ALL interfaces.  It messes up the host.
+        self.write_network('all.network', "[Match]\nName=m[01]???\n")
+        subprocess.check_call(['systemctl', 'start', 'systemd-networkd'])
+        self.create_iface()
+        self.assert_link_states(m1def='managed',
+                                m1man='managed',
+                                m1unm='unmanaged',
+                                m0unm='managed')
+
+    def test_catchall_config_coldplug(self):
+        """Verify link states with a catch-all config, cold-plug."""
+        # Don't actually catch ALL interfaces.  It messes up the host.
+        self.write_network('all.network', "[Match]\nName=m[01]???\n")
+        self.create_iface()
+        subprocess.check_call(['systemctl', 'start', 'systemd-networkd'])
+        self.assert_link_states(m1def='managed',
+                                m1man='managed',
+                                m1unm='unmanaged',
+                                m0unm='managed')
+
+
 if __name__ == '__main__':
     unittest.main(testRunner=unittest.TextTestRunner(stream=sys.stdout,
                                                      verbosity=2))