From c68305a91791e28031df8b9ebd33bfe7ffd7e75d Mon Sep 17 00:00:00 2001 From: James Falcon Date: Tue, 4 Jul 2023 07:00:22 -0500 Subject: [PATCH] Fix network v2 metric rendering (#4220) Reference:https://github.com/canonical/cloud-init/commit/c68305a91791e28031df8b9ebd33bfe7ffd7e75d Conflict:(1)change 'small' to 'small_v1' (2)do not change TestNetworkManagerRendering. (3)do not add "NM_CONTROLLED=no" in test because of cloud-init-20.4-nm-controlled.patch. (4)format diffs. Metric info was not being included in v2-based routes. Fixes GH-4217 --- cloudinit/net/network_state.py | 4 +- tests/unittests/test_net.py | 232 ++++++++++++++++++++++++++++++--- 2 files changed, 216 insertions(+), 20 deletions(-) diff --git a/cloudinit/net/network_state.py b/cloudinit/net/network_state.py index 4862bf9..ac44304 100644 --- a/cloudinit/net/network_state.py +++ b/cloudinit/net/network_state.py @@ -187,7 +187,6 @@ class NetworkState(object): class NetworkStateInterpreter(metaclass=CommandHandlerMeta): - initial_network_state = { 'interfaces': {}, 'routes': [], @@ -582,7 +581,6 @@ class NetworkStateInterpreter(metaclass=CommandHandlerMeta): self._handle_bond_bridge(command, cmd_type='bond') def handle_bridges(self, command): - ''' v2_command = { br0: { @@ -815,7 +813,7 @@ class NetworkStateInterpreter(metaclass=CommandHandlerMeta): routes = [] for route in cfg.get('routes', []): routes.append(_normalize_route( - {'destination': route.get('to'), 'gateway': route.get('via')})) + {'destination': route.get('to'), 'gateway': route.get('via'), "metric": route.get("metric"),})) # v2 routes are bound to the interface, in v1 we add them under # the first subnet since there isn't an equivalent interface level. diff --git a/tests/unittests/test_net.py b/tests/unittests/test_net.py index 7cde102..764e1c7 100644 --- a/tests/unittests/test_net.py +++ b/tests/unittests/test_net.py @@ -818,7 +818,7 @@ iface eth1 inet static """.lstrip() NETWORK_CONFIGS = { - 'small': { + 'small_v1': { 'expected_networkd_eth99': textwrap.dedent("""\ [Match] Name=eth99 @@ -959,6 +959,164 @@ NETWORK_CONFIGS = { - wark.maas """), }, + # We test a separate set of configs here because v2 doesn't support + # generic nameservers, so that aspect needs to be modified + "small_v2": { + "expected_networkd_eth99": textwrap.dedent( + """\ + [Match] + Name=eth99 + MACAddress=c0:d6:9f:2c:e8:80 + [Address] + Address=192.168.21.3/24 + [Network] + DHCP=ipv4 + Domains=barley.maas sach.maas + DNS=8.8.8.8 8.8.4.4 + [Route] + Gateway=65.61.151.37 + Destination=0.0.0.0/0 + Metric=10000 + """ + ).rstrip(" "), + "expected_networkd_eth1": textwrap.dedent( + """\ + [Match] + Name=eth1 + MACAddress=cf:d6:af:48:e8:80 + [Network] + DHCP=no + """ + ).rstrip(" "), + "expected_eni": textwrap.dedent( + """\ + auto lo + iface lo inet loopback + dns-nameservers 8.8.8.8 8.8.4.4 + dns-search wark.maas + iface eth1 inet manual + auto eth99 + iface eth99 inet dhcp + # control-alias eth99 + iface eth99 inet static + address 192.168.21.3/24 + dns-nameservers 8.8.8.8 8.8.4.4 + dns-search barley.maas sach.maas + post-up route add default gw 65.61.151.37 metric 10000 || true + pre-down route del default gw 65.61.151.37 metric 10000 || true + """ + ).rstrip(" "), + "expected_sysconfig_opensuse": { + "ifcfg-eth1": textwrap.dedent( + """\ + BOOTPROTO=static + LLADDR=cf:d6:af:48:e8:80 + STARTMODE=auto""" + ), + "ifcfg-eth99": textwrap.dedent( + """\ + BOOTPROTO=dhcp4 + LLADDR=c0:d6:9f:2c:e8:80 + IPADDR=192.168.21.3 + NETMASK=255.255.255.0 + STARTMODE=auto""" + ), + }, + "expected_sysconfig_rhel": { + "ifcfg-eth1": textwrap.dedent( + """\ + BOOTPROTO=none + DEVICE=eth1 + HWADDR=cf:d6:af:48:e8:80 + ONBOOT=yes + TYPE=Ethernet + USERCTL=no""" + ), + "ifcfg-eth99": textwrap.dedent( + """\ + BOOTPROTO=dhcp + DEFROUTE=yes + DEVICE=eth99 + DHCLIENT_SET_DEFAULT_ROUTE=yes + DNS1=8.8.8.8 + DNS2=8.8.4.4 + DOMAIN="barley.maas sach.maas" + GATEWAY=65.61.151.37 + HWADDR=c0:d6:9f:2c:e8:80 + IPADDR=192.168.21.3 + NETMASK=255.255.255.0 + METRIC=10000 + ONBOOT=yes + TYPE=Ethernet + USERCTL=no""" + ), + }, + "expected_network_manager": { + "cloud-init-eth1.nmconnection": textwrap.dedent( + """\ + # Generated by cloud-init. Changes will be lost. + [connection] + id=cloud-init eth1 + uuid=3c50eb47-7260-5a6d-801d-bd4f587d6b58 + autoconnect-priority=120 + type=ethernet + [user] + org.freedesktop.NetworkManager.origin=cloud-init + [ethernet] + mac-address=CF:D6:AF:48:E8:80 + """ + ), + "cloud-init-eth99.nmconnection": textwrap.dedent( + """\ + # Generated by cloud-init. Changes will be lost. + [connection] + id=cloud-init eth99 + uuid=b1b88000-1f03-5360-8377-1a2205efffb4 + autoconnect-priority=120 + type=ethernet + [user] + org.freedesktop.NetworkManager.origin=cloud-init + [ethernet] + mac-address=C0:D6:9F:2C:E8:80 + [ipv4] + method=auto + may-fail=false + route1=0.0.0.0/0,65.61.151.37 + address1=192.168.21.3/24 + dns=8.8.8.8;8.8.4.4; + dns-search=barley.maas;sach.maas; + """ + ), + }, + "yaml": textwrap.dedent( + """ + version: 2 + ethernets: + eth1: + match: + macaddress: cf:d6:af:48:e8:80 + set-name: eth1 + eth99: + addresses: + - 192.168.21.3/24 + dhcp4: true + match: + macaddress: c0:d6:9f:2c:e8:80 + nameservers: + addresses: + - 8.8.8.8 + - 8.8.4.4 + search: + - barley.maas + - sach.maas + routes: + - metric: 10000 + to: 0.0.0.0/0 + via: 65.61.151.37 + set-name: eth99 + """ + ), + }, 'v4_and_v6': { 'expected_networkd': textwrap.dedent("""\ [Match] @@ -2965,7 +3123,6 @@ iface eth1 inet dhcp mock.Mock(return_value=False) ) class TestRhelSysConfigRendering(CiTestCase): - with_logs = True nm_cfg_file = "/etc/NetworkManager/NetworkManager.conf" @@ -3286,8 +3443,14 @@ USERCTL=no 'WARNING: Network config: ignoring eth0.101 device-level mtu', self.logs.getvalue()) - def test_small_config(self): - entry = NETWORK_CONFIGS['small'] + def test_small_config_v1(self): + entry = NETWORK_CONFIGS["small_v1"] + found = self._render_and_read(network_config=yaml.load(entry["yaml"])) + self._compare_files_to_expected(entry[self.expected_name], found) + self._assert_headers(found) + + def test_small_config_v2(self): + entry = NETWORK_CONFIGS["small_v2"] found = self._render_and_read(network_config=yaml.load(entry['yaml'])) self._compare_files_to_expected(entry[self.expected_name], found) self._assert_headers(found) @@ -3429,7 +3592,7 @@ USERCTL=no self.assertTrue(os.path.exists(nm_cfg)) # render and read - entry = NETWORK_CONFIGS['small'] + entry = NETWORK_CONFIGS['small_v1'] found = self._render_and_read(network_config=yaml.load(entry['yaml']), dir=render_dir) self._compare_files_to_expected(entry[self.expected_name], found) @@ -3450,7 +3613,7 @@ USERCTL=no util.write_file(nm_cfg, '# test_check_ifcfg_rh\n[main]\nplugins=foo\n') # render and read - entry = NETWORK_CONFIGS['small'] + entry = NETWORK_CONFIGS['small_v1'] found = self._render_and_read(network_config=yaml.load(entry['yaml']), dir=render_dir) self._compare_files_to_expected(entry[self.expected_name], found) @@ -3476,7 +3639,7 @@ USERCTL=no self.assertTrue(os.path.exists(nm_cfg)) # render and read - entry = NETWORK_CONFIGS['small'] + entry = NETWORK_CONFIGS['small_v1'] found = self._render_and_read(network_config=yaml.load(entry['yaml']), dir=render_dir) self._compare_files_to_expected(entry[self.expected_name], found) @@ -3644,7 +3807,6 @@ USERCTL=no mock.Mock(return_value=False) ) class TestOpenSuseSysConfigRendering(CiTestCase): - with_logs = True scripts_dir = '/etc/sysconfig/network' @@ -3916,8 +4078,14 @@ STARTMODE=auto 'WARNING: Network config: ignoring eth0.101 device-level mtu', self.logs.getvalue()) - def test_small_config(self): - entry = NETWORK_CONFIGS['small'] + def test_small_config_v1(self): + entry = NETWORK_CONFIGS["small_v1"] + found = self._render_and_read(network_config=yaml.load(entry["yaml"])) + self._compare_files_to_expected(entry[self.expected_name], found) + self._assert_headers(found) + + def test_small_config_v2(self): + entry = NETWORK_CONFIGS["small_v1"] found = self._render_and_read(network_config=yaml.load(entry['yaml'])) self._compare_files_to_expected(entry[self.expected_name], found) self._assert_headers(found) @@ -4539,7 +4707,6 @@ class TestReadInitramfsConfig(CiTestCase): class TestNetplanRoundTrip(CiTestCase): - NETPLAN_INFO_OUT = textwrap.dedent(""" netplan.io: features: @@ -4596,7 +4763,7 @@ class TestNetplanRoundTrip(CiTestCase): files['/etc/netplan/50-cloud-init.yaml'].splitlines()) def testsimple_render_small_netplan(self): - entry = NETWORK_CONFIGS['small'] + entry = NETWORK_CONFIGS['small_v1'] files = self._render_and_read(network_config=yaml.load(entry['yaml'])) self.assertEqual( entry['expected_netplan'].splitlines(), @@ -4781,8 +4948,17 @@ class TestEniRoundTrip(CiTestCase): entry['expected_eni'].splitlines(), files['/etc/network/interfaces'].splitlines()) - def testsimple_render_small(self): - entry = NETWORK_CONFIGS['small'] + def testsimple_render_small_v1(self): + entry = NETWORK_CONFIGS["small_v1"] + files = self._render_and_read(network_config=yaml.load(entry["yaml"])) + self.assertEqual( + entry["expected_eni"].splitlines(), + files["/etc/network/interfaces"].splitlines(), + ) + + @pytest.mark.xfail(reason="GH-4219") + def testsimple_render_small_v2(self): + entry = NETWORK_CONFIGS["small_v2"] files = self._render_and_read(network_config=yaml.load(entry['yaml'])) self.assertEqual( entry['expected_eni'].splitlines(), @@ -5105,10 +5281,33 @@ class TestNetworkdRoundTrip(CiTestCase): return dir2dict(dir) @mock.patch("cloudinit.net.util.chownbyname", return_value=True) - def testsimple_render_small_networkd(self, m_chown): + def testsimple_render_small_networkd_v1(self, m_chown): + nwk_fn1 = "/etc/systemd/network/10-cloud-init-eth99.network" + nwk_fn2 = "/etc/systemd/network/10-cloud-init-eth1.network" + entry = NETWORK_CONFIGS["small_v1"] + files = self._render_and_read(network_config=yaml.load(entry["yaml"])) + + actual = files[nwk_fn1].splitlines() + actual = self.create_conf_dict(actual) + + expected = entry["expected_networkd_eth99"].splitlines() + expected = self.create_conf_dict(expected) + + self.compare_dicts(actual, expected) + + actual = files[nwk_fn2].splitlines() + actual = self.create_conf_dict(actual) + + expected = entry["expected_networkd_eth1"].splitlines() + expected = self.create_conf_dict(expected) + + self.compare_dicts(actual, expected) + + @mock.patch("cloudinit.net.util.chownbyname", return_value=True) + def testsimple_render_small_networkd_v2(self, m_chown): nwk_fn1 = '/etc/systemd/network/10-cloud-init-eth99.network' nwk_fn2 = '/etc/systemd/network/10-cloud-init-eth1.network' - entry = NETWORK_CONFIGS['small'] + entry = NETWORK_CONFIGS['small_v2'] files = self._render_and_read(network_config=yaml.load(entry['yaml'])) actual = files[nwk_fn1].splitlines() @@ -5735,7 +5934,6 @@ class TestInterfacesSorting(CiTestCase): mock.Mock(return_value=False) ) class TestGetIBHwaddrsByInterface(CiTestCase): - _ib_addr = '80:00:00:28:fe:80:00:00:00:00:00:00:00:11:22:03:00:33:44:56' _ib_addr_eth_format = '00:11:22:33:44:56' _data = {'devices': ['enp0s1', 'enp0s2', 'bond1', 'bridge1', -- 2.33.0