networkd: support marking links unmanaged

This commit is contained in:
David Michael 2016-09-27 15:18:14 -07:00
parent ec89276c2a
commit a09dc5467a
5 changed files with 133 additions and 0 deletions

View File

@ -232,6 +232,18 @@
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>

View 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;

View 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)

View File

@ -176,6 +176,7 @@ struct Network {
struct ether_addr *mac;
size_t mtu;
int arp;
bool unmanaged;
uint32_t iaid;
DUID duid;

View File

@ -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))