mirror of
https://github.com/systemd/systemd-stable.git
synced 2025-01-11 05:17:44 +03:00
networkd: support marking links unmanaged
This commit is contained in:
parent
ec89276c2a
commit
a09dc5467a
@ -232,6 +232,18 @@
|
|||||||
the network otherwise.</para>
|
the network otherwise.</para>
|
||||||
</listitem>
|
</listitem>
|
||||||
</varlistentry>
|
</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>
|
</variablelist>
|
||||||
</refsect1>
|
</refsect1>
|
||||||
|
|
||||||
|
@ -2523,6 +2523,9 @@ static int link_initialized_and_synced(sd_netlink *rtnl, sd_netlink_message *m,
|
|||||||
if (r == -ENOENT) {
|
if (r == -ENOENT) {
|
||||||
link_enter_unmanaged(link);
|
link_enter_unmanaged(link);
|
||||||
return 1;
|
return 1;
|
||||||
|
} else if (r == 0 && network->unmanaged) {
|
||||||
|
link_enter_unmanaged(link);
|
||||||
|
return 0;
|
||||||
} else if (r < 0)
|
} else if (r < 0)
|
||||||
return r;
|
return r;
|
||||||
|
|
||||||
|
@ -29,6 +29,7 @@ Match.Architecture, config_parse_net_condition,
|
|||||||
Link.MACAddress, config_parse_hwaddr, 0, offsetof(Network, mac)
|
Link.MACAddress, config_parse_hwaddr, 0, offsetof(Network, mac)
|
||||||
Link.MTUBytes, config_parse_iec_size, 0, offsetof(Network, mtu)
|
Link.MTUBytes, config_parse_iec_size, 0, offsetof(Network, mtu)
|
||||||
Link.ARP, config_parse_tristate, 0, offsetof(Network, arp)
|
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.Description, config_parse_string, 0, offsetof(Network, description)
|
||||||
Network.Bridge, config_parse_netdev, 0, offsetof(Network, bridge)
|
Network.Bridge, config_parse_netdev, 0, offsetof(Network, bridge)
|
||||||
Network.Bond, config_parse_netdev, 0, offsetof(Network, bond)
|
Network.Bond, config_parse_netdev, 0, offsetof(Network, bond)
|
||||||
|
@ -176,6 +176,7 @@ struct Network {
|
|||||||
struct ether_addr *mac;
|
struct ether_addr *mac;
|
||||||
size_t mtu;
|
size_t mtu;
|
||||||
int arp;
|
int arp;
|
||||||
|
bool unmanaged;
|
||||||
uint32_t iaid;
|
uint32_t iaid;
|
||||||
DUID duid;
|
DUID duid;
|
||||||
|
|
||||||
|
@ -92,6 +92,45 @@ class NetworkdTestingUtilities:
|
|||||||
dropin.write(contents)
|
dropin.write(contents)
|
||||||
self.addCleanup(os.remove, dropin_path)
|
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):
|
class ClientTestBase(NetworkdTestingUtilities):
|
||||||
"""Provide common methods for testing networkd against servers."""
|
"""Provide common methods for testing networkd against servers."""
|
||||||
@ -720,6 +759,83 @@ DNS=127.0.0.1''')
|
|||||||
raise
|
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__':
|
if __name__ == '__main__':
|
||||||
unittest.main(testRunner=unittest.TextTestRunner(stream=sys.stdout,
|
unittest.main(testRunner=unittest.TextTestRunner(stream=sys.stdout,
|
||||||
verbosity=2))
|
verbosity=2))
|
||||||
|
Loading…
Reference in New Issue
Block a user