diff options
-rw-r--r-- | src/nm.c | 162 | ||||
-rw-r--r-- | tests/generator/base.py | 13 | ||||
-rw-r--r-- | tests/generator/test_auth.py | 18 | ||||
-rw-r--r-- | tests/generator/test_bonds.py | 16 | ||||
-rw-r--r-- | tests/generator/test_bridges.py | 37 | ||||
-rw-r--r-- | tests/generator/test_common.py | 48 | ||||
-rw-r--r-- | tests/generator/test_ethernets.py | 69 | ||||
-rw-r--r-- | tests/generator/test_modems.py | 30 | ||||
-rw-r--r-- | tests/generator/test_passthrough.py | 28 | ||||
-rw-r--r-- | tests/generator/test_vlans.py | 28 | ||||
-rw-r--r-- | tests/generator/test_wifis.py | 34 |
11 files changed, 244 insertions, 239 deletions
@@ -26,58 +26,15 @@ #include <glib/gprintf.h> #include <uuid.h> +#include "names.h" #include "netplan.h" #include "nm.h" #include "parse.h" #include "parse-globals.h" +#include "parse-nm.h" #include "util.h" #include "util-internal.h" #include "validation.h" -#include "parse-nm.h" - -GString* udev_rules; - -/** - * Append NM device specifier of @def to @s. - */ -static void -g_string_append_netdef_match(GString* s, const NetplanNetDefinition* def) -{ - g_assert(!def->match.driver || def->set_name); - if (def->match.mac || def->match.original_name || def->set_name || def->type >= NETPLAN_DEF_TYPE_VIRTUAL) { - if (def->match.mac) { - g_string_append_printf(s, "mac:%s,", def->match.mac); - } - /* MAC could change, e.g. for bond slaves. Ignore by interface-name as well */ - if (def->match.original_name || def->set_name || def->type >= NETPLAN_DEF_TYPE_VIRTUAL) { - /* we always have the renamed name here */ - g_string_append_printf(s, "interface-name:%s,", - (def->type >= NETPLAN_DEF_TYPE_VIRTUAL) ? def->id - : (def->set_name ?: def->match.original_name)); - } - } else { - /* no matches → match all devices of that type */ - switch (def->type) { - case NETPLAN_DEF_TYPE_ETHERNET: - g_string_append(s, "type:ethernet,"); - break; - /* This cannot be reached with just NM and networkd backends, as - * networkd does not support wifi and thus we'll never blacklist a - * wifi device from NM. This would become relevant with another - * wifi-supporting backend, but until then this just spoils 100% - * code coverage. - case NETPLAN_DEF_TYPE_WIFI: - g_string_append(s, "type:wifi"); - break; - */ - - // LCOV_EXCL_START - default: - g_assert_not_reached(); - // LCOV_EXCL_STOP - } - } -} /** * Infer if this is a modem netdef of type GSM. @@ -959,49 +916,108 @@ netplan_netdef_write_nm( return no_error; } -static void -nd_append_non_nm_ids(gpointer data, gpointer str) -{ - const NetplanNetDefinition* nd = data; - - if (nd->backend != NETPLAN_BACKEND_NM) { - if (nd->match.driver) { - /* TODO: NetworkManager supports (non-globbing) "driver:..." matching nowadays */ - /* NM cannot match on drivers, so ignore these via udev rules */ - if (!udev_rules) - udev_rules = g_string_new(NULL); - g_string_append_printf(udev_rules, "ACTION==\"add|change\", SUBSYSTEM==\"net\", ENV{ID_NET_DRIVER}==\"%s\", ENV{NM_UNMANAGED}=\"1\"\n", nd->match.driver); - } else { - g_string_append_netdef_match((GString*) str, nd); - } - } -} - gboolean netplan_state_finish_nm_write( const NetplanState* np_state, const char* rootdir, GError** error) { - GString *s = NULL; - gsize len; + GString* udev_rules = g_string_new(NULL); + GString *nm_conf = g_string_new(NULL); if (netplan_state_get_netdefs_size(np_state) == 0) return TRUE; // LCOV_EXCL_LINE as generate.c already deals with it. /* Set all devices not managed by us to unmanaged, so that NM does not - * auto-connect and interferes */ - s = g_string_new("[keyfile]\n# devices managed by networkd\nunmanaged-devices+="); - len = s->len; - g_list_foreach(np_state->netdefs_ordered, nd_append_non_nm_ids, s); - if (s->len > len) - g_string_free_to_file(s, rootdir, "run/NetworkManager/conf.d/netplan.conf", NULL); + * auto-connect and interferes. + * Also, mark all devices managed by us explicitly, so it won't get in + * conflict with the system's udev rules that might ignore some devices + * in containers via usr/lib/udev/rules.d/85-nm-unmanaged-devices.rules */ + GList* iter = np_state->netdefs_ordered; + while (iter) { + const NetplanNetDefinition* nd = iter->data; + const gchar* nm_type; + GString *tmp = NULL; + guint unmanaged = nd->backend == NETPLAN_BACKEND_NM ? 0 : 1; + + /* Special case: manage or ignore any device of given type on empty "match: {}" stanza */ + if (nd->has_match && !nd->match.driver && !nd->match.mac && !nd->match.original_name) { + nm_type = type_str(nd); + g_assert(nm_type); + g_string_append_printf(nm_conf, "[device-netplan.%s.%s]\nmatch-device=type:%s\n" + "managed=%d\n\n", netplan_def_type_name(nd->type), + nd->id, nm_type, !unmanaged); + } + /* Normal case: manage or ignore devices by specific udev rules */ + else { + const gchar *prefix = "SUBSYSTEM==\"net\", ACTION==\"add|change|move\","; + const gchar *suffix = nd->backend == NETPLAN_BACKEND_NM ? " ENV{NM_UNMANAGED}=\"0\"\n" : " ENV{NM_UNMANAGED}=\"1\"\n"; + g_string_append_printf(udev_rules, "# netplan: network.%s.%s (on NetworkManager %s)\n", + netplan_def_type_name(nd->type), nd->id, + unmanaged ? "deny-list" : "allow-list"); + /* Match by explicit interface name, if possible */ + if (nd->set_name) { + // simple case: explicit new interface name + g_string_append_printf(udev_rules, "%s ENV{ID_NET_NAME}==\"%s\",%s", prefix, nd->set_name, suffix); + } else if (!nd->has_match) { + // simple case: explicit netplan ID is interface name + g_string_append_printf(udev_rules, "%s ENV{ID_NET_NAME}==\"%s\",%s", prefix, nd->id, suffix); + } + /* Also, match by explicit (new) MAC, if available */ + if (nd->set_mac) { + tmp = g_string_new(nd->set_mac); + g_string_append_printf(udev_rules, "%s ATTR{address}==\"%s\",%s", prefix, g_string_ascii_down(tmp)->str, suffix); + g_string_free(tmp, TRUE); + } + /* Finally, add a full match, using all rules & globs available + * from the "match" stanza (e.g. original_name/mac/drivers) + * This will match the "old" interface (i.e. original MAC and/or + * interface name) if it got changed */ + if (nd->has_match && (nd->match.original_name || nd->match.mac || nd->match.driver)) { + // match on original name glob + // TODO: maybe support matching on multiple name globs in the future (like drivers) + g_string_append(udev_rules, prefix); + if (nd->match.original_name) + g_string_append_printf(udev_rules, " ENV{ID_NET_NAME}==\"%s\",", nd->match.original_name); + + // match on (explicit) MAC address. Yes this would be unique on its own, but we + // keep it within the "full match" to make the logic more comprehensible. + if (nd->match.mac) { + tmp = g_string_new(nd->match.mac); + g_string_append_printf(udev_rules, " ATTR{address}==\"%s\",", g_string_ascii_down(tmp)->str); + g_string_free(tmp, TRUE); + } + + // match on (multiple) driver globs + if (nd->match.driver) { + gchar *drivers = NULL; + if (strchr(nd->match.driver, '\t')) { + gchar **split = g_strsplit(nd->match.driver, "\t", -1); + drivers = g_strjoinv("|", split); + g_strfreev(split); + } else + drivers = g_strdup(nd->match.driver); + g_string_append_printf(udev_rules, " ENV{ID_NET_DRIVER}==\"%s\",", drivers); + g_free(drivers); + } + g_string_append(udev_rules, suffix); + } + } + iter = iter->next; + } + + /* write generated NetworkManager drop-in config */ + if (nm_conf->len > 0) + g_string_free_to_file(nm_conf, rootdir, "run/NetworkManager/conf.d/netplan.conf", NULL); else - g_string_free(s, TRUE); + g_string_free(nm_conf, TRUE); /* write generated udev rules */ - if (udev_rules) + if (udev_rules->len > 0) g_string_free_to_file(udev_rules, rootdir, "run/udev/rules.d/90-netplan.rules", NULL); + else + g_string_free(udev_rules, TRUE); + return TRUE; } diff --git a/tests/generator/base.py b/tests/generator/base.py index 689c4e0..f0407c9 100644 --- a/tests/generator/base.py +++ b/tests/generator/base.py @@ -92,6 +92,12 @@ Wants=network.target Type=simple ExecStart=/sbin/wpa_supplicant -c /run/netplan/wpa-%(iface)s.conf -i%(iface)s -D%(drivers)s ''' +NM_MANAGED = 'SUBSYSTEM=="net", ACTION=="add|change|move", ENV{ID_NET_NAME}=="%s", ENV{NM_UNMANAGED}="0"\n' +NM_UNMANAGED = 'SUBSYSTEM=="net", ACTION=="add|change|move", ENV{ID_NET_NAME}=="%s", ENV{NM_UNMANAGED}="1"\n' +NM_MANAGED_MAC = 'SUBSYSTEM=="net", ACTION=="add|change|move", ATTR{address}=="%s", ENV{NM_UNMANAGED}="0"\n' +NM_UNMANAGED_MAC = 'SUBSYSTEM=="net", ACTION=="add|change|move", ATTR{address}=="%s", ENV{NM_UNMANAGED}="1"\n' +NM_MANAGED_DRIVER = 'SUBSYSTEM=="net", ACTION=="add|change|move", ENV{ID_NET_DRIVER}=="%s", ENV{NM_UNMANAGED}="0"\n' +NM_UNMANAGED_DRIVER = 'SUBSYSTEM=="net", ACTION=="add|change|move", ENV{ID_NET_DRIVER}=="%s", ENV{NM_UNMANAGED}="1"\n' class NetplanV2Normalizer(): @@ -432,7 +438,12 @@ class TestBase(unittest.TestCase): self.assertFalse(os.path.exists(rule_path)) return with open(rule_path) as f: - self.assertEqual(f.read(), contents) + lines = [] + for line in f.readlines(): + # ignore any comment in udev rules.d file + if not line.startswith('#'): + lines.append(line) + self.assertEqual(''.join(lines), contents) def assert_ovs(self, file_contents_map): systemd_dir = os.path.join(self.workdir.name, 'run', 'systemd', 'system') diff --git a/tests/generator/test_auth.py b/tests/generator/test_auth.py index 3d20109..e8fd023 100644 --- a/tests/generator/test_auth.py +++ b/tests/generator/test_auth.py @@ -19,7 +19,7 @@ import os import stat -from .base import TestBase, ND_DHCP4, ND_WIFI_DHCP4, SD_WPA +from .base import TestBase, ND_DHCP4, ND_WIFI_DHCP4, SD_WPA, NM_MANAGED, NM_UNMANAGED class TestNetworkd(TestBase): @@ -83,10 +83,8 @@ class TestNetworkd(TestBase): ''') self.assert_networkd({'wl0.network': ND_WIFI_DHCP4 % 'wl0'}) - self.assert_nm(None, '''[keyfile] -# devices managed by networkd -unmanaged-devices+=interface-name:wl0,''') - self.assert_nm_udev(None) + self.assert_nm(None) + self.assert_nm_udev(NM_UNMANAGED % 'wl0') # generates wpa config and enables wpasupplicant unit with open(os.path.join(self.workdir.name, 'run/netplan/wpa-wl0.conf')) as f: @@ -201,10 +199,8 @@ network={ ''') self.assert_networkd({'eth0.network': ND_DHCP4 % 'eth0'}) - self.assert_nm(None, '''[keyfile] -# devices managed by networkd -unmanaged-devices+=interface-name:eth0,''') - self.assert_nm_udev(None) + self.assert_nm(None) + self.assert_nm_udev(NM_UNMANAGED % 'eth0') # generates wpa config and enables wpasupplicant unit with open(os.path.join(self.workdir.name, 'run/netplan/wpa-eth0.conf')) as f: @@ -458,7 +454,7 @@ method=ignore ssid=peer2peer mode=adhoc '''}) - self.assert_nm_udev(None) + self.assert_nm_udev(NM_MANAGED % 'wl0') def test_auth_wired(self): self.generate('''network: @@ -502,7 +498,7 @@ private-key=/etc/ssl/cust-key.pem private-key-password=d3cryptPr1v4t3K3y '''}) self.assert_networkd({}) - self.assert_nm_udev(None) + self.assert_nm_udev(NM_MANAGED % 'eth0') class TestConfigErrors(TestBase): diff --git a/tests/generator/test_bonds.py b/tests/generator/test_bonds.py index fea475e..ee3f53c 100644 --- a/tests/generator/test_bonds.py +++ b/tests/generator/test_bonds.py @@ -16,7 +16,7 @@ # You should have received a copy of the GNU General Public License # along with this program. If not, see <http://www.gnu.org/licenses/>. -from .base import TestBase +from .base import TestBase, NM_MANAGED, NM_UNMANAGED class TestNetworkd(TestBase): @@ -79,10 +79,8 @@ ConfigureWithoutCarrier=yes RouteMetric=100 UseMTU=true '''}) - self.assert_nm(None, '''[keyfile] -# devices managed by networkd -unmanaged-devices+=interface-name:bn0,''') - self.assert_nm_udev(None) + self.assert_nm(None) + self.assert_nm_udev(NM_UNMANAGED % 'bn0') def test_bond_components(self): self.generate('''network: @@ -460,7 +458,7 @@ method=auto method=ignore '''}) self.assert_networkd({}) - self.assert_nm_udev(None) + self.assert_nm_udev(NM_MANAGED % 'eno1' + NM_MANAGED % 'enp2s1' + NM_MANAGED % 'bn0') def test_bond_empty_params(self): self.generate('''network: @@ -521,7 +519,7 @@ method=auto method=ignore '''}) self.assert_networkd({}) - self.assert_nm_udev(None) + self.assert_nm_udev(NM_MANAGED % 'eno1' + NM_MANAGED % 'enp2s1' + NM_MANAGED % 'bn0') def test_bond_with_params(self): self.generate('''network: @@ -625,7 +623,7 @@ method=auto method=ignore '''}) self.assert_networkd({}) - self.assert_nm_udev(None) + self.assert_nm_udev(NM_MANAGED % 'eno1' + NM_MANAGED % 'enp2s1' + NM_MANAGED % 'bn0') def test_bond_primary_slave(self): self.generate('''network: @@ -692,7 +690,7 @@ method=auto method=ignore '''}) self.assert_networkd({}) - self.assert_nm_udev(None) + self.assert_nm_udev(NM_MANAGED % 'eno1' + NM_MANAGED % 'enp2s1' + NM_MANAGED % 'bn0') class TestConfigErrors(TestBase): diff --git a/tests/generator/test_bridges.py b/tests/generator/test_bridges.py index 7898cbe..102d592 100644 --- a/tests/generator/test_bridges.py +++ b/tests/generator/test_bridges.py @@ -19,7 +19,7 @@ import os import unittest -from .base import TestBase +from .base import TestBase, NM_UNMANAGED, NM_MANAGED class TestNetworkd(TestBase): @@ -108,10 +108,8 @@ ConfigureWithoutCarrier=yes RouteMetric=100 UseMTU=true '''}) - self.assert_nm(None, '''[keyfile] -# devices managed by networkd -unmanaged-devices+=interface-name:br0,''') - self.assert_nm_udev(None) + self.assert_nm(None) + self.assert_nm_udev(NM_UNMANAGED % 'br0') def test_bridge_type_renderer(self): self.generate('''network: @@ -135,10 +133,8 @@ ConfigureWithoutCarrier=yes RouteMetric=100 UseMTU=true '''}) - self.assert_nm(None, '''[keyfile] -# devices managed by networkd -unmanaged-devices+=interface-name:br0,''') - self.assert_nm_udev(None) + self.assert_nm(None) + self.assert_nm_udev(NM_UNMANAGED % 'br0') def test_bridge_def_renderer(self): self.generate('''network: @@ -165,10 +161,8 @@ ConfigureWithoutCarrier=yes RouteMetric=100 UseMTU=true '''}) - self.assert_nm(None, '''[keyfile] -# devices managed by networkd -unmanaged-devices+=interface-name:br0,''') - self.assert_nm_udev(None) + self.assert_nm(None) + self.assert_nm_udev(NM_UNMANAGED % 'br0') def test_bridge_forward_declaration(self): self.generate('''network: @@ -215,9 +209,8 @@ UseMTU=true mybr: interfaces: [ethbr] dhcp4: yes''') - self.assert_nm(None, '''[keyfile] -# devices managed by networkd -unmanaged-devices+=interface-name:eth42,interface-name:eth43,interface-name:mybr,''') + self.assert_nm(None) + self.assert_nm_udev(NM_UNMANAGED % 'eth42' + NM_UNMANAGED % 'eth43' + NM_UNMANAGED % 'mybr') def test_bridge_components(self): self.generate('''network: @@ -322,7 +315,7 @@ method=auto method=ignore '''}) self.assert_networkd({}) - self.assert_nm_udev(None) + self.assert_nm_udev(NM_MANAGED % 'br0') def test_bridge_type_renderer(self): self.generate('''network: @@ -345,7 +338,7 @@ method=auto method=ignore '''}) self.assert_networkd({}) - self.assert_nm_udev(None) + self.assert_nm_udev(NM_MANAGED % 'br0') def test_bridge_set_mac(self): self.generate('''network: @@ -395,7 +388,7 @@ address1=1.2.3.4/12 method=ignore '''}) self.assert_networkd({}) - self.assert_nm_udev(None) + self.assert_nm_udev(NM_MANAGED % 'br0') def test_bridge_forward_declaration(self): self.generate('''network: @@ -456,7 +449,7 @@ method=auto method=ignore '''}) self.assert_networkd({}) - self.assert_nm_udev(None) + self.assert_nm_udev(NM_MANAGED % 'br0' + NM_MANAGED % 'eno1' + NM_MANAGED % 'enp2s1') def test_bridge_components(self): self.generate('''network: @@ -516,7 +509,7 @@ method=auto method=ignore '''}) self.assert_networkd({}) - self.assert_nm_udev(None) + self.assert_nm_udev(NM_MANAGED % 'eno1' + NM_MANAGED % 'enp2s1' + NM_MANAGED % 'br0') def test_bridge_params(self): self.generate('''network: @@ -599,7 +592,7 @@ method=auto method=ignore '''}) self.assert_networkd({}) - self.assert_nm_udev(None) + self.assert_nm_udev(NM_MANAGED % 'eno1' + NM_MANAGED % 'enp2s1' + NM_MANAGED % 'br0') class TestNetplanYAMLv2(TestBase): diff --git a/tests/generator/test_common.py b/tests/generator/test_common.py index f4bd7f4..18467ef 100644 --- a/tests/generator/test_common.py +++ b/tests/generator/test_common.py @@ -19,7 +19,7 @@ import os import textwrap -from .base import TestBase, ND_DHCP4, ND_DHCP6, ND_DHCPYES, ND_EMPTY +from .base import TestBase, ND_DHCP4, ND_DHCP6, ND_DHCPYES, ND_EMPTY, NM_MANAGED, NM_UNMANAGED class TestNetworkd(TestBase): @@ -184,10 +184,8 @@ Bond=bond0 dhcp4: true''') self.assert_networkd({'eth0.network': ND_DHCP4 % 'eth0'}) - self.assert_nm(None, '''[keyfile] -# devices managed by networkd -unmanaged-devices+=interface-name:eth0,''') - self.assert_nm_udev(None) + self.assert_nm(None) + self.assert_nm_udev(NM_UNMANAGED % 'eth0') # should not allow NM to manage everything self.assertFalse(os.path.exists(self.nm_enable_all_conf)) @@ -201,12 +199,10 @@ unmanaged-devices+=interface-name:eth0,''') dhcp4: true''') self.assert_networkd({'eth0.network': ND_DHCP4 % 'eth0'}) - self.assert_nm(None, '''[keyfile] -# devices managed by networkd -unmanaged-devices+=interface-name:eth0,''') + self.assert_nm(None) # should allow NM to manage everything else self.assertTrue(os.path.exists(self.nm_enable_all_conf)) - self.assert_nm_udev(None) + self.assert_nm_udev(NM_UNMANAGED % 'eth0') def test_eth_def_renderer(self): self.generate('''network: @@ -220,10 +216,8 @@ unmanaged-devices+=interface-name:eth0,''') self.assert_networkd({'eth0.network': ND_DHCP4 % 'eth0'}) self.assert_networkd_udev(None) - self.assert_nm(None, '''[keyfile] -# devices managed by networkd -unmanaged-devices+=interface-name:eth0,''') - self.assert_nm_udev(None) + self.assert_nm(None) + self.assert_nm_udev(NM_UNMANAGED % 'eth0') def test_eth_dhcp6(self): self.generate('''network: @@ -931,7 +925,7 @@ method=link-local method=ignore '''}) self.assert_networkd({}) - self.assert_nm_udev(None) + self.assert_nm_udev(NM_MANAGED % 'eth0') def test_ipv6_mtu(self): self.generate(textwrap.dedent(""" @@ -966,7 +960,7 @@ method=auto method=ignore '''}) self.assert_networkd({}) - self.assert_nm_udev(None) + self.assert_nm_udev(NM_MANAGED % 'eth0') def test_eth_type_renderer(self): self.generate('''network: @@ -992,7 +986,7 @@ method=auto method=ignore '''}) self.assert_networkd({}) - self.assert_nm_udev(None) + self.assert_nm_udev(NM_MANAGED % 'eth0') def test_eth_def_renderer(self): self.generate('''network: @@ -1018,7 +1012,7 @@ method=link-local method=ignore '''}) self.assert_networkd({}) - self.assert_nm_udev(None) + self.assert_nm_udev(NM_MANAGED % 'eth0') def test_global_renderer_only(self): self.generate(None, confs={'01-default-nm.yaml': 'network: {version: 2, renderer: NetworkManager}'}) @@ -1193,7 +1187,7 @@ address1=2001:FFfe::1/64 ip6-privacy=0 '''}) self.assert_networkd({}) - self.assert_nm_udev(None) + self.assert_nm_udev(NM_MANAGED % 'engreen') def test_eth_manual_addresses_dhcp(self): self.generate('''network: @@ -1545,10 +1539,8 @@ class TestMerging(TestBase): confs={'backend': 'network:\n renderer: networkd'}) self.assert_networkd({'engreen.network': ND_DHCP4 % 'engreen'}) - self.assert_nm(None, '''[keyfile] -# devices managed by networkd -unmanaged-devices+=interface-name:engreen,''') - self.assert_nm_udev(None) + self.assert_nm(None) + self.assert_nm_udev(NM_UNMANAGED % 'engreen') def test_add_def(self): self.generate('''network: @@ -1568,10 +1560,8 @@ unmanaged-devices+=interface-name:engreen,''') # releases, so we can't depend on the exact order. # TODO: (cyphermox) turn this into an "assert_in_nm()" function. if "CODECOV_TOKEN" not in os.environ: # pragma: nocover - self.assert_nm(None, '''[keyfile] -# devices managed by networkd -unmanaged-devices+=interface-name:engreen,interface-name:enblue,''') - self.assert_nm_udev(None) + self.assert_nm(None) + self.assert_nm_udev(NM_UNMANAGED % 'engreen' + NM_UNMANAGED % 'enblue') def test_change_def(self): self.generate('''network: @@ -1608,10 +1598,8 @@ unmanaged-devices+=interface-name:engreen,interface-name:enblue,''') engreen: {dhcp4: true}''') self.assert_networkd({'engreen.network': ND_DHCP4 % 'engreen'}) - self.assert_nm(None, '''[keyfile] -# devices managed by networkd -unmanaged-devices+=interface-name:engreen,''') - self.assert_nm_udev(None) + self.assert_nm(None) + self.assert_nm_udev(NM_UNMANAGED % 'engreen') def test_ref(self): self.generate('''network: diff --git a/tests/generator/test_ethernets.py b/tests/generator/test_ethernets.py index e81941b..4d3f7d0 100644 --- a/tests/generator/test_ethernets.py +++ b/tests/generator/test_ethernets.py @@ -18,7 +18,9 @@ import os -from .base import TestBase, ND_DHCP4, UDEV_MAC_RULE, UDEV_NO_MAC_RULE, UDEV_SRIOV_RULE +from .base import TestBase, ND_DHCP4, UDEV_MAC_RULE, UDEV_NO_MAC_RULE, UDEV_SRIOV_RULE, \ + NM_MANAGED, NM_UNMANAGED, NM_MANAGED_MAC, NM_UNMANAGED_MAC, \ + NM_MANAGED_DRIVER, NM_UNMANAGED_DRIVER class TestNetworkd(TestBase): @@ -39,10 +41,8 @@ Name=eth0 LinkLocalAddressing=ipv6 '''}) self.assert_networkd_udev(None) - self.assert_nm(None, '''[keyfile] -# devices managed by networkd -unmanaged-devices+=interface-name:eth0,''') - self.assert_nm_udev(None) + self.assert_nm(None) + self.assert_nm_udev(NM_UNMANAGED % 'eth0') # should not allow NM to manage everything self.assertFalse(os.path.exists(self.nm_enable_all_conf)) @@ -140,8 +140,8 @@ LinkLocalAddressing=ipv6 '''}) self.assert_networkd_udev({'def1.rules': (UDEV_NO_MAC_RULE % ('ixgbe', 'lom1'))}) # NM cannot match by driver, so blacklisting needs to happen via udev - self.assert_nm(None, None) - self.assert_nm_udev('ACTION=="add|change", SUBSYSTEM=="net", ENV{ID_NET_DRIVER}=="ixgbe", ENV{NM_UNMANAGED}="1"\n') + self.assert_nm(None) + self.assert_nm_udev(NM_UNMANAGED % 'lom1' + NM_UNMANAGED_DRIVER % 'ixgbe') def test_eth_match_by_mac_rename(self): self.generate('''network: @@ -161,10 +161,8 @@ Name=lom1 LinkLocalAddressing=ipv6 '''}) self.assert_networkd_udev({'def1.rules': (UDEV_MAC_RULE % ('?*', '11:22:33:44:55:66', 'lom1'))}) - self.assert_nm(None, '''[keyfile] -# devices managed by networkd -unmanaged-devices+=mac:11:22:33:44:55:66,interface-name:lom1,''') - self.assert_nm_udev(None) + self.assert_nm(None) + self.assert_nm_udev(NM_UNMANAGED % 'lom1' + NM_UNMANAGED_MAC % '11:22:33:44:55:66') def test_eth_implicit_name_match_dhcp4(self): self.generate('''network: @@ -197,7 +195,7 @@ RouteMetric=100 UseMTU=true '''}) self.assert_networkd_udev(None) - self.assert_nm_udev('ACTION=="add|change", SUBSYSTEM=="net", ENV{ID_NET_DRIVER}=="ixgbe", ENV{NM_UNMANAGED}="1"\n') + self.assert_nm_udev(NM_UNMANAGED_DRIVER % 'ixgbe') def test_eth_match_name(self): self.generate('''network: @@ -210,10 +208,8 @@ UseMTU=true self.assert_networkd({'def1.network': ND_DHCP4 % 'green'}) self.assert_networkd_udev(None) - self.assert_nm(None, '''[keyfile] -# devices managed by networkd -unmanaged-devices+=interface-name:green,''') - self.assert_nm_udev(None) + self.assert_nm(None) + self.assert_nm_udev(NM_UNMANAGED % 'green') def test_eth_set_mac(self): self.generate('''network: @@ -247,9 +243,8 @@ unmanaged-devices+=interface-name:green,''') # The udev rules engine does support renaming by name self.assert_networkd_udev(None) - self.assert_nm(None, '''[keyfile] -# devices managed by networkd -unmanaged-devices+=interface-name:blue,''') + self.assert_nm(None) + self.assert_nm_udev(NM_UNMANAGED % 'blue' + NM_UNMANAGED % 'green') def test_eth_match_all_names(self): self.generate('''network: @@ -261,10 +256,8 @@ unmanaged-devices+=interface-name:blue,''') self.assert_networkd({'def1.network': ND_DHCP4 % '*'}) self.assert_networkd_udev(None) - self.assert_nm(None, '''[keyfile] -# devices managed by networkd -unmanaged-devices+=interface-name:*,''') - self.assert_nm_udev(None) + self.assert_nm(None) + self.assert_nm_udev(NM_UNMANAGED % '*') def test_eth_match_all(self): self.generate('''network: @@ -277,9 +270,9 @@ unmanaged-devices+=interface-name:*,''') self.assert_networkd({'def1.network': '[Match]\n\n[Network]\nDHCP=ipv4\nLinkLocalAddressing=ipv6\n\n' '[DHCP]\nRouteMetric=100\nUseMTU=true\n'}) self.assert_networkd_udev(None) - self.assert_nm(None, '''[keyfile] -# devices managed by networkd -unmanaged-devices+=type:ethernet,''') + self.assert_nm(None, '''[device-netplan.ethernets.def1] +match-device=type:ethernet +managed=0\n\n''') self.assert_nm_udev(None) def test_match_multiple(self): @@ -303,9 +296,9 @@ LinkLocalAddressing=ipv6 RouteMetric=100 UseMTU=true '''}) - self.assert_nm(None, '''[keyfile] -# devices managed by networkd -unmanaged-devices+=mac:00:11:22:33:44:55,interface-name:en1s*,''') + self.assert_nm(None) + self.assert_nm_udev('SUBSYSTEM=="net", ACTION=="add|change|move", ENV{ID_NET_NAME}=="en1s*", ' + 'ATTR{address}=="00:11:22:33:44:55", ENV{NM_UNMANAGED}="1"\n') class TestNetworkManager(TestBase): @@ -335,7 +328,7 @@ method=ignore # should allow NM to manage everything else self.assertTrue(os.path.exists(self.nm_enable_all_conf)) self.assert_networkd({'eth0.link': '[Match]\nOriginalName=eth0\n\n[Link]\nWakeOnLan=magic\n'}) - self.assert_nm_udev(None) + self.assert_nm_udev(NM_MANAGED % 'eth0') def test_eth_mtu(self): self.generate('''network: @@ -547,7 +540,7 @@ method=link-local [ipv6] method=ignore '''}) - self.assert_nm_udev(None) + self.assert_nm_udev(NM_MANAGED % 'lom1' + NM_MANAGED_DRIVER % 'ixgbe') def test_eth_match_by_mac_rename(self): self.generate('''network: @@ -575,7 +568,7 @@ method=link-local [ipv6] method=ignore '''}) - self.assert_nm_udev(None) + self.assert_nm_udev(NM_MANAGED % 'lom1' + NM_MANAGED_MAC % '11:22:33:44:55:66') def test_eth_implicit_name_match_dhcp4(self): self.generate('''network: @@ -652,7 +645,7 @@ method=auto method=ignore '''}) self.assert_networkd({}) - self.assert_nm_udev(None) + self.assert_nm_udev(NM_MANAGED % 'green') def test_eth_match_name_rename(self): self.generate('''network: @@ -685,7 +678,7 @@ method=ignore '''}) # ... while udev renames it self.assert_networkd({'def1.link': '[Match]\nOriginalName=green\n\n[Link]\nName=blue\nWakeOnLan=off\n'}) - self.assert_nm_udev(None) + self.assert_nm_udev(NM_MANAGED % 'blue' + NM_MANAGED % 'green') def test_eth_match_name_glob(self): self.generate('''network: @@ -735,7 +728,10 @@ method=auto [ipv6] method=ignore -'''}) +'''}, '''[device-netplan.ethernets.def1] +match-device=type:ethernet +managed=1\n\n''') + self.assert_nm_udev(None) self.assert_networkd({}) def test_match_multiple(self): @@ -764,7 +760,8 @@ method=auto method=ignore '''}) self.assert_networkd({}) - self.assert_nm_udev(None) + self.assert_nm_udev('SUBSYSTEM=="net", ACTION=="add|change|move", ENV{ID_NET_NAME}=="engreen", ' + 'ATTR{address}=="00:11:22:33:44:55", ENV{NM_UNMANAGED}="0"\n') def test_offload(self): self.generate('''network: diff --git a/tests/generator/test_modems.py b/tests/generator/test_modems.py index acffe87..b66144e 100644 --- a/tests/generator/test_modems.py +++ b/tests/generator/test_modems.py @@ -16,7 +16,7 @@ # You should have received a copy of the GNU General Public License # along with this program. If not, see <http://www.gnu.org/licenses/>. -from .base import TestBase +from .base import TestBase, NM_MANAGED class TestNetworkd(TestBase): @@ -67,7 +67,7 @@ method=link-local method=ignore '''}) self.assert_networkd({}) - self.assert_nm_udev(None) + self.assert_nm_udev(NM_MANAGED % 'mobilephone') def test_gsm_auto_config(self): self.generate('''network: @@ -91,7 +91,7 @@ method=link-local method=ignore '''}) self.assert_networkd({}) - self.assert_nm_udev(None) + self.assert_nm_udev(NM_MANAGED % 'mobilephone') def test_gsm_auto_config_implicit(self): self.generate('''network: @@ -120,7 +120,7 @@ method=link-local method=ignore '''}) self.assert_networkd({}) - self.assert_nm_udev(None) + self.assert_nm_udev(NM_MANAGED % 'mobilephone') def test_gsm_apn(self): self.generate('''network: @@ -144,7 +144,7 @@ method=link-local method=ignore '''}) self.assert_networkd({}) - self.assert_nm_udev(None) + self.assert_nm_udev(NM_MANAGED % 'mobilephone') def test_gsm_apn_username_password(self): self.generate('''network: @@ -172,7 +172,7 @@ method=link-local method=ignore '''}) self.assert_networkd({}) - self.assert_nm_udev(None) + self.assert_nm_udev(NM_MANAGED % 'mobilephone') def test_gsm_device_id(self): self.generate('''network: @@ -197,7 +197,7 @@ method=link-local method=ignore '''}) self.assert_networkd({}) - self.assert_nm_udev(None) + self.assert_nm_udev(NM_MANAGED % 'mobilephone') def test_gsm_network_id(self): self.generate('''network: @@ -222,7 +222,7 @@ method=link-local method=ignore '''}) self.assert_networkd({}) - self.assert_nm_udev(None) + self.assert_nm_udev(NM_MANAGED % 'mobilephone') def test_gsm_pin(self): self.generate('''network: @@ -247,7 +247,7 @@ method=link-local method=ignore '''}) self.assert_networkd({}) - self.assert_nm_udev(None) + self.assert_nm_udev(NM_MANAGED % 'mobilephone') def test_gsm_sim_id(self): self.generate('''network: @@ -272,7 +272,7 @@ method=link-local method=ignore '''}) self.assert_networkd({}) - self.assert_nm_udev(None) + self.assert_nm_udev(NM_MANAGED % 'mobilephone') def test_gsm_sim_operator_id(self): self.generate('''network: @@ -297,7 +297,7 @@ method=link-local method=ignore '''}) self.assert_networkd({}) - self.assert_nm_udev(None) + self.assert_nm_udev(NM_MANAGED % 'mobilephone') def test_gsm_example(self): self.generate('''network: @@ -339,7 +339,7 @@ method=link-local method=ignore '''}) self.assert_networkd({}) - self.assert_nm_udev(None) + self.assert_nm_udev(NM_MANAGED % 'cdc-wdm1') def test_modem_nm_integration(self): self.generate('''network: @@ -366,7 +366,7 @@ method=link-local method=ignore '''}) self.assert_networkd({}) - self.assert_nm_udev(None) + self.assert_nm_udev(NM_MANAGED % 'mobilephone') def test_modem_nm_integration_gsm_cdma(self): self.generate('''network: @@ -421,6 +421,8 @@ method=auto [ipv6] #Netplan: passthrough override method=auto -'''}) +'''}, '''[device-netplan.modems.NM-a08c5805-7cf5-43f7-afb9-12cb30f6eca3] +match-device=type:gsm +managed=1\n\n''') self.assert_networkd({}) self.assert_nm_udev(None) diff --git a/tests/generator/test_passthrough.py b/tests/generator/test_passthrough.py index 8d03c92..4af80d6 100644 --- a/tests/generator/test_passthrough.py +++ b/tests/generator/test_passthrough.py @@ -56,7 +56,9 @@ method=link-local [ipv6] method=ignore -'''}) +'''}, '''[device-netplan.ethernets.NM-87749f1d-334f-40b2-98d4-55db58965f5f] +match-device=type:ethernet +managed=1\n\n''') def test_passthrough_wifi(self): self.generate('''network: @@ -107,7 +109,9 @@ method=ignore ssid=OTHER-SSID mode=infrastructure hidden=true -'''}) +'''}, '''[device-netplan.wifis.NM-87749f1d-334f-40b2-98d4-55db58965f5f] +match-device=type:wifi +managed=1\n\n''') def test_passthrough_type_nm_devices(self): self.generate('''network: @@ -132,7 +136,9 @@ method=link-local [ipv6] method=ignore -'''}) +'''}, '''[device-netplan.nm-devices.NM-87749f1d-334f-40b2-98d4-55db58965f5f] +match-device=type:dummy +managed=1\n\n''') def test_passthrough_dotted_group(self): self.generate('''network: @@ -159,7 +165,9 @@ method=ignore [wireguard-peer.some-key] #Netplan: passthrough setting endpoint=1.2.3.4 -'''}) +'''}, '''[device-netplan.nm-devices.dotted-group-test] +match-device=type:wireguard +managed=1\n\n''') def test_passthrough_dotted_key(self): self.generate('''network: @@ -193,7 +201,9 @@ qdisc.root=something qdisc.fff1=:abc #Netplan: passthrough setting filters.test=test -'''}) +'''}, '''[device-netplan.ethernets.dotted-key-test] +match-device=type:ethernet +managed=1\n\n''') def test_passthrough_unsupported_setting(self): self.generate('''network: @@ -221,7 +231,9 @@ method=ignore ssid=SOME-SSID #Netplan: passthrough override mode=mesh -'''}) +'''}, '''[device-netplan.wifis.test] +match-device=type:wifi +managed=1\n\n''') def test_passthrough_empty_group(self): self.generate('''network: @@ -247,7 +259,9 @@ method=link-local method=ignore [proxy] -'''}) +'''}, '''[device-netplan.ethernets.test] +match-device=type:ethernet +managed=1\n\n''') def test_passthrough_interface_rename_existing_id(self): self.generate('''network: diff --git a/tests/generator/test_vlans.py b/tests/generator/test_vlans.py index f728d35..012295e 100644 --- a/tests/generator/test_vlans.py +++ b/tests/generator/test_vlans.py @@ -20,7 +20,8 @@ import os import re import unittest -from .base import TestBase, ND_VLAN, ND_EMPTY, ND_WITHIP, ND_DHCP6_WOCARRIER +from .base import TestBase, ND_VLAN, ND_EMPTY, ND_WITHIP, ND_DHCP6_WOCARRIER, \ + NM_MANAGED, NM_UNMANAGED, NM_MANAGED_MAC, NM_UNMANAGED_MAC class TestNetworkd(TestBase): @@ -66,10 +67,9 @@ Id=3 .replace('[Network]', '[Link]\nMACAddress=aa:bb:cc:dd:ee:11\n\n[Network]'), 'engreen.network': (ND_DHCP6_WOCARRIER % 'engreen')}) - self.assert_nm(None, '''[keyfile] -# devices managed by networkd -unmanaged-devices+=interface-name:en1,interface-name:enblue,interface-name:enred,interface-name:engreen,''') - self.assert_nm_udev(None) + self.assert_nm(None) + self.assert_nm_udev(NM_UNMANAGED % 'en1' + NM_UNMANAGED % 'enblue' + NM_UNMANAGED % 'enred' + + NM_UNMANAGED_MAC % 'aa:bb:cc:dd:ee:11' + NM_UNMANAGED % 'engreen') def test_vlan_sriov(self): # we need to make sure renderer: sriov vlans are not saved as part of @@ -95,10 +95,8 @@ VLAN=engreen 'engreen.netdev': ND_VLAN % ('engreen', 2), 'engreen.network': (ND_DHCP6_WOCARRIER % 'engreen')}) - self.assert_nm(None, '''[keyfile] -# devices managed by networkd -unmanaged-devices+=interface-name:en1,interface-name:enblue,interface-name:engreen,''') - self.assert_nm_udev(None) + self.assert_nm(None) + self.assert_nm_udev(NM_UNMANAGED % 'en1' + NM_UNMANAGED % 'enblue' + NM_UNMANAGED % 'engreen') # see LP: #1888726 def test_vlan_parent_match(self): @@ -137,10 +135,8 @@ MTUBytes=9000 'vlan20.network': ND_EMPTY % ('vlan20', 'ipv6'), 'vlan20.netdev': ND_VLAN % ('vlan20', 20)}) - self.assert_nm(None, '''[keyfile] -# devices managed by networkd -unmanaged-devices+=mac:11:22:33:44:55:66,interface-name:lan,interface-name:vlan20,''') - self.assert_nm_udev(None) + self.assert_nm(None) + self.assert_nm_udev(NM_UNMANAGED % 'lan' + NM_UNMANAGED_MAC % '11:22:33:44:55:66' + NM_UNMANAGED % 'vlan20') class TestNetworkManager(TestBase): @@ -205,7 +201,7 @@ method=link-local method=auto ip6-privacy=0 '''}) - self.assert_nm_udev(None) + self.assert_nm_udev(NM_MANAGED % 'en1' + NM_MANAGED % 'enblue' + NM_MANAGED % 'engreen') def test_vlan_parent_match(self): self.generate('''network: @@ -256,7 +252,7 @@ method=auto [ipv6] method=ignore ''' % uuid}) - self.assert_nm_udev(None) + self.assert_nm_udev(NM_MANAGED_MAC % '11:22:33:44:55:66' + NM_MANAGED % 'engreen') def test_vlan_sriov(self): # we need to make sure renderer: sriov vlans are not saved as part of @@ -305,4 +301,4 @@ method=link-local method=auto ip6-privacy=0 '''}) - self.assert_nm_udev(None) + self.assert_nm_udev(NM_MANAGED % 'en1' + NM_MANAGED % 'enblue' + NM_MANAGED % 'engreen') diff --git a/tests/generator/test_wifis.py b/tests/generator/test_wifis.py index 1a4ead2..bcc4cea 100644 --- a/tests/generator/test_wifis.py +++ b/tests/generator/test_wifis.py @@ -19,7 +19,7 @@ import os import stat -from .base import TestBase, ND_WIFI_DHCP4, SD_WPA +from .base import TestBase, ND_WIFI_DHCP4, SD_WPA, NM_MANAGED, NM_UNMANAGED class TestNetworkd(TestBase): @@ -57,10 +57,8 @@ class TestNetworkd(TestBase): dhcp4: yes''') self.assert_networkd({'wl0.network': ND_WIFI_DHCP4 % 'wl0'}) - self.assert_nm(None, '''[keyfile] -# devices managed by networkd -unmanaged-devices+=interface-name:wl0,''') - self.assert_nm_udev(None) + self.assert_nm(None) + self.assert_nm_udev(NM_UNMANAGED % 'wl0') # generates wpa config and enables wpasupplicant unit with open(os.path.join(self.workdir.name, 'run/netplan/wpa-wl0.conf')) as f: @@ -240,10 +238,8 @@ RouteMetric=600 UseMTU=true '''}) - self.assert_nm(None, '''[keyfile] -# devices managed by networkd -unmanaged-devices+=interface-name:wl0,''') - self.assert_nm_udev(None) + self.assert_nm(None) + self.assert_nm_udev(NM_UNMANAGED % 'wl0') def test_wifi_match(self): err = self.generate('''network: @@ -292,10 +288,8 @@ Name=wl0 [Network] LinkLocalAddressing=ipv6 '''}) - self.assert_nm(None, '''[keyfile] -# devices managed by networkd -unmanaged-devices+=interface-name:wl0,''') - self.assert_nm_udev(None) + self.assert_nm(None) + self.assert_nm_udev(NM_UNMANAGED % 'wl0') # generates wpa config and enables wpasupplicant unit with open(os.path.join(self.workdir.name, 'run/netplan/wpa-wl0.conf')) as f: @@ -328,10 +322,8 @@ Name=wl0 [Network] LinkLocalAddressing=ipv6 '''}) - self.assert_nm(None, '''[keyfile] -# devices managed by networkd -unmanaged-devices+=interface-name:wl0,''') - self.assert_nm_udev(None) + self.assert_nm(None) + self.assert_nm_udev(NM_UNMANAGED % 'wl0') # generates wpa config and enables wpasupplicant unit with open(os.path.join(self.workdir.name, 'run/netplan/wpa-wl0.conf')) as f: @@ -495,7 +487,7 @@ mode=infrastructure band=a '''}) self.assert_networkd({}) - self.assert_nm_udev(None) + self.assert_nm_udev(NM_MANAGED % 'wl0') def test_wifi_match_mac(self): self.generate('''network: @@ -547,7 +539,9 @@ method=ignore [wifi] ssid=workplace mode=infrastructure -'''}) +'''}, '''[device-netplan.wifis.all] +match-device=type:wifi +managed=1\n\n''') def test_wifi_ap(self): self.generate('''network: @@ -580,7 +574,7 @@ key-mgmt=wpa-psk psk=s0s3cret '''}) self.assert_networkd({}) - self.assert_nm_udev(None) + self.assert_nm_udev(NM_MANAGED % 'wl0') def test_wifi_adhoc(self): self.generate('''network: |