diff options
author | James Falcon <james.falcon@canonical.com> | 2022-07-01 10:07:15 -0500 |
---|---|---|
committer | git-ubuntu importer <ubuntu-devel-discuss@lists.ubuntu.com> | 2022-07-01 16:34:09 +0000 |
commit | cff8a8b47acf7048ad08bd121e677fb86e73635b (patch) | |
tree | 011f38ddb27b3df74d78115ebe59be951558edea /tests/unittests/sources | |
parent | 15d691e3b0b32c67b0589665b49e9d2755296d1b (diff) | |
download | cloud-init-git-cff8a8b47acf7048ad08bd121e677fb86e73635b.tar.gz |
22.2-64-g1fcd55d6-0ubuntu1~22.10.1 (patches unapplied)
Imported using git-ubuntu import.
Diffstat (limited to 'tests/unittests/sources')
20 files changed, 1061 insertions, 845 deletions
diff --git a/tests/unittests/sources/test_aliyun.py b/tests/unittests/sources/test_aliyun.py index 8a61d5ee..e628dc02 100644 --- a/tests/unittests/sources/test_aliyun.py +++ b/tests/unittests/sources/test_aliyun.py @@ -149,7 +149,7 @@ class TestAliYunDatasource(test_helpers.HttprettyTestCase): def _test_host_name(self): self.assertEqual( - self.default_metadata["hostname"], self.ds.get_hostname() + self.default_metadata["hostname"], self.ds.get_hostname().hostname ) @mock.patch("cloudinit.sources.DataSourceAliYun._is_aliyun") diff --git a/tests/unittests/sources/test_azure.py b/tests/unittests/sources/test_azure.py index b7dae873..b1edf1f3 100644 --- a/tests/unittests/sources/test_azure.py +++ b/tests/unittests/sources/test_azure.py @@ -12,7 +12,6 @@ from pathlib import Path import httpretty import pytest import requests -import yaml from cloudinit import distros, helpers, subp, url_helper from cloudinit.net import dhcp @@ -23,7 +22,6 @@ from cloudinit.sources.helpers import netlink from cloudinit.util import ( MountFailedError, b64e, - decode_binary, json_dumps, load_file, load_json, @@ -87,6 +85,25 @@ def mock_azure_report_failure_to_fabric(): @pytest.fixture +def mock_device_driver(): + with mock.patch( + MOCKPATH + "device_driver", + autospec=True, + return_value=None, + ) as m: + yield m + + +@pytest.fixture +def mock_generate_fallback_config(): + with mock.patch( + MOCKPATH + "net.generate_fallback_config", + autospec=True, + ) as m: + yield m + + +@pytest.fixture def mock_time(): with mock.patch( MOCKPATH + "time", @@ -122,7 +139,7 @@ def mock_ephemeral_dhcp_v4(): @pytest.fixture def mock_net_dhcp_maybe_perform_dhcp_discovery(): with mock.patch( - "cloudinit.net.dhcp.maybe_perform_dhcp_discovery", + "cloudinit.net.ephemeral.maybe_perform_dhcp_discovery", return_value=[ { "unknown-245": "0a:0b:0c:0d", @@ -140,7 +157,7 @@ def mock_net_dhcp_maybe_perform_dhcp_discovery(): @pytest.fixture def mock_net_dhcp_EphemeralIPv4Network(): with mock.patch( - "cloudinit.net.dhcp.EphemeralIPv4Network", + "cloudinit.net.ephemeral.EphemeralIPv4Network", autospec=True, ) as m: yield m @@ -279,83 +296,101 @@ def patched_markers_dir_path(tmpdir): @pytest.fixture -def patched_reported_ready_marker_path(patched_markers_dir_path): +def patched_reported_ready_marker_path(azure_ds, patched_markers_dir_path): reported_ready_marker = patched_markers_dir_path / "reported_ready" - with mock.patch( - MOCKPATH + "REPORTED_READY_MARKER_FILE", str(reported_ready_marker) + with mock.patch.object( + azure_ds, "_reported_ready_marker_file", str(reported_ready_marker) ): yield reported_ready_marker -def construct_valid_ovf_env( - data=None, pubkeys=None, userdata=None, platform_settings=None +def construct_ovf_env( + *, + custom_data=None, + hostname="test-host", + username="test-user", + password=None, + public_keys=None, + disable_ssh_password_auth=None, + preprovisioned_vm=None, + preprovisioned_vm_type=None, ): - if data is None: - data = {"HostName": "FOOHOST"} - if pubkeys is None: - pubkeys = {} - - content = """<?xml version="1.0" encoding="utf-8"?> -<Environment xmlns="http://schemas.dmtf.org/ovf/environment/1" - xmlns:oe="http://schemas.dmtf.org/ovf/environment/1" - xmlns:wa="http://schemas.microsoft.com/windowsazure" - xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"> - - <wa:ProvisioningSection><wa:Version>1.0</wa:Version> - <LinuxProvisioningConfigurationSet - xmlns="http://schemas.microsoft.com/windowsazure" - xmlns:i="http://www.w3.org/2001/XMLSchema-instance"> - <ConfigurationSetType>LinuxProvisioningConfiguration</ConfigurationSetType> - """ - for key, dval in data.items(): - if isinstance(dval, dict): - val = dict(dval).get("text") - attrs = " " + " ".join( - [ - "%s='%s'" % (k, v) - for k, v in dict(dval).items() - if k != "text" - ] - ) - else: - val = dval - attrs = "" - content += "<%s%s>%s</%s>\n" % (key, attrs, val, key) - - if userdata: - content += "<UserData>%s</UserData>\n" % (b64e(userdata)) - - if pubkeys: - content += "<SSH><PublicKeys>\n" - for fp, path, value in pubkeys: - content += " <PublicKey>" - if fp and path: - content += "<Fingerprint>%s</Fingerprint><Path>%s</Path>" % ( - fp, - path, - ) - if value: - content += "<Value>%s</Value>" % value - content += "</PublicKey>\n" - content += "</PublicKeys></SSH>" - content += """ - </LinuxProvisioningConfigurationSet> - </wa:ProvisioningSection> - <wa:PlatformSettingsSection><wa:Version>1.0</wa:Version> - <PlatformSettings xmlns="http://schemas.microsoft.com/windowsazure" - xmlns:i="http://www.w3.org/2001/XMLSchema-instance"> - <KmsServerHostname>kms.core.windows.net</KmsServerHostname> - <ProvisionGuestAgent>false</ProvisionGuestAgent> - <GuestAgentPackageName i:nil="true" />""" - if platform_settings: - for k, v in platform_settings.items(): - content += "<%s>%s</%s>\n" % (k, v, k) - if "PreprovisionedVMType" not in platform_settings: - content += """<PreprovisionedVMType i:nil="true" />""" - content += """</PlatformSettings></wa:PlatformSettingsSection> -</Environment>""" - - return content + content = [ + '<?xml version="1.0" encoding="utf-8"?>', + '<ns0:Environment xmlns="http://schemas.dmtf.org/ovf/environment/1"', + 'xmlns:ns0="http://schemas.dmtf.org/ovf/environment/1"', + 'xmlns:ns1="http://schemas.microsoft.com/windowsazure"', + 'xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance">', + "<ns1:ProvisioningSection>", + "<ns1:Version>1.0</ns1:Version>", + "<ns1:LinuxProvisioningConfigurationSet>", + "<ns1:ConfigurationSetType>" + "LinuxProvisioningConfiguration" + "</ns1:ConfigurationSetType>", + ] + if hostname is not None: + content.append("<ns1:HostName>%s</ns1:HostName>" % hostname) + if username is not None: + content.append("<ns1:UserName>%s</ns1:UserName>" % username) + if password is not None: + content.append("<ns1:UserPassword>%s</ns1:UserPassword>" % password) + if custom_data is not None: + content.append( + "<ns1:CustomData>%s</ns1:CustomData>" % (b64e(custom_data)) + ) + if disable_ssh_password_auth is not None: + content.append( + "<ns1:DisableSshPasswordAuthentication>%s" + % str(disable_ssh_password_auth).lower() + + "</ns1:DisableSshPasswordAuthentication>" + ) + if public_keys is not None: + content += ["<ns1:SSH>", "<ns1:PublicKeys>"] + for public_key in public_keys: + content.append("<ns1:PublicKey>") + fp = public_key.get("fingerprint") + if fp is not None: + content.append("<ns1:Fingerprint>%s</ns1:Fingerprint>" % fp) + path = public_key.get("path") + if path is not None: + content.append("<ns1:Path>%s</ns1:Path>" % path) + value = public_key.get("value") + if value is not None: + content.append("<ns1:Value>%s</ns1:Value>" % value) + content.append("</ns1:PublicKey>") + content += ["</ns1:PublicKeys>", "</ns1:SSH>"] + content += [ + "</ns1:LinuxProvisioningConfigurationSet>", + "</ns1:ProvisioningSection>", + "<ns1:PlatformSettingsSection>", + "<ns1:Version>1.0</ns1:Version>", + "<ns1:PlatformSettings>", + "<ns1:KmsServerHostname>" + "kms.core.windows.net" + "</ns1:KmsServerHostname>", + "<ns1:ProvisionGuestAgent>false</ns1:ProvisionGuestAgent>", + '<ns1:GuestAgentPackageName xsi:nil="true" />', + ] + if preprovisioned_vm is not None: + content.append( + "<ns1:PreprovisionedVm>%s</ns1:PreprovisionedVm>" + % str(preprovisioned_vm).lower() + ) + + if preprovisioned_vm_type is None: + content.append('<ns1:PreprovisionedVMType xsi:nil="true" />') + else: + content.append( + "<ns1:PreprovisionedVMType>%s</ns1:PreprovisionedVMType>" + % preprovisioned_vm_type + ) + content += [ + "</ns1:PlatformSettings>", + "</ns1:PlatformSettingsSection>", + "</ns0:Environment>", + ] + + return "\n".join(content) NETWORK_METADATA = { @@ -441,7 +476,7 @@ IMDS_NETWORK_METADATA = { EXAMPLE_UUID = "d0df4c54-4ecb-4a4b-9954-5bdf3ed5c3b8" -class TestParseNetworkConfig(CiTestCase): +class TestNetworkConfig: maxDiff = None fallback_config = { @@ -457,11 +492,8 @@ class TestParseNetworkConfig(CiTestCase): ], } - @mock.patch( - "cloudinit.sources.DataSourceAzure.device_driver", return_value=None - ) - def test_single_ipv4_nic_configuration(self, m_driver): - """parse_network_config emits dhcp on single nic with ipv4""" + def test_single_ipv4_nic_configuration(self, azure_ds, mock_device_driver): + """Network config emits dhcp on single nic with ipv4""" expected = { "ethernets": { "eth0": { @@ -474,13 +506,14 @@ class TestParseNetworkConfig(CiTestCase): }, "version": 2, } - self.assertEqual(expected, dsaz.parse_network_config(NETWORK_METADATA)) + azure_ds._metadata_imds = NETWORK_METADATA - @mock.patch( - "cloudinit.sources.DataSourceAzure.device_driver", return_value=None - ) - def test_increases_route_metric_for_non_primary_nics(self, m_driver): - """parse_network_config increases route-metric for each nic""" + assert azure_ds.network_config == expected + + def test_increases_route_metric_for_non_primary_nics( + self, azure_ds, mock_device_driver + ): + """Network config increases route-metric for each nic""" expected = { "ethernets": { "eth0": { @@ -514,70 +547,14 @@ class TestParseNetworkConfig(CiTestCase): third_intf["ipv4"]["subnet"][0]["address"] = "10.0.2.0" third_intf["ipv4"]["ipAddress"][0]["privateIpAddress"] = "10.0.2.6" imds_data["network"]["interface"].append(third_intf) - self.assertEqual(expected, dsaz.parse_network_config(imds_data)) - - @mock.patch( - "cloudinit.sources.DataSourceAzure.device_driver", return_value=None - ) - def test_ipv4_and_ipv6_route_metrics_match_for_nics(self, m_driver): - """parse_network_config emits matching ipv4 and ipv6 route-metrics.""" - expected = { - "ethernets": { - "eth0": { - "addresses": ["10.0.0.5/24", "2001:dead:beef::2/128"], - "dhcp4": True, - "dhcp4-overrides": {"route-metric": 100}, - "dhcp6": True, - "dhcp6-overrides": {"route-metric": 100}, - "match": {"macaddress": "00:0d:3a:04:75:98"}, - "set-name": "eth0", - }, - "eth1": { - "set-name": "eth1", - "match": {"macaddress": "22:0d:3a:04:75:98"}, - "dhcp4": True, - "dhcp6": False, - "dhcp4-overrides": {"route-metric": 200}, - }, - "eth2": { - "set-name": "eth2", - "match": {"macaddress": "33:0d:3a:04:75:98"}, - "dhcp4": True, - "dhcp4-overrides": {"route-metric": 300}, - "dhcp6": True, - "dhcp6-overrides": {"route-metric": 300}, - }, - }, - "version": 2, - } - imds_data = copy.deepcopy(NETWORK_METADATA) - nic1 = imds_data["network"]["interface"][0] - nic1["ipv4"]["ipAddress"].append({"privateIpAddress": "10.0.0.5"}) + azure_ds._metadata_imds = imds_data - nic1["ipv6"] = { - "subnet": [{"address": "2001:dead:beef::16"}], - "ipAddress": [ - {"privateIpAddress": "2001:dead:beef::1"}, - {"privateIpAddress": "2001:dead:beef::2"}, - ], - } - imds_data["network"]["interface"].append(SECONDARY_INTERFACE) - third_intf = copy.deepcopy(SECONDARY_INTERFACE) - third_intf["macAddress"] = third_intf["macAddress"].replace("22", "33") - third_intf["ipv4"]["subnet"][0]["address"] = "10.0.2.0" - third_intf["ipv4"]["ipAddress"][0]["privateIpAddress"] = "10.0.2.6" - third_intf["ipv6"] = { - "subnet": [{"prefix": "64", "address": "2001:dead:beef::2"}], - "ipAddress": [{"privateIpAddress": "2001:dead:beef::1"}], - } - imds_data["network"]["interface"].append(third_intf) - self.assertEqual(expected, dsaz.parse_network_config(imds_data)) + assert azure_ds.network_config == expected - @mock.patch( - "cloudinit.sources.DataSourceAzure.device_driver", return_value=None - ) - def test_ipv4_secondary_ips_will_be_static_addrs(self, m_driver): - """parse_network_config emits primary ipv4 as dhcp others are static""" + def test_ipv4_secondary_ips_will_be_static_addrs( + self, azure_ds, mock_device_driver + ): + """Network config emits primary ipv4 as dhcp others are static""" expected = { "ethernets": { "eth0": { @@ -600,13 +577,14 @@ class TestParseNetworkConfig(CiTestCase): "subnet": [{"prefix": "10", "address": "2001:dead:beef::16"}], "ipAddress": [{"privateIpAddress": "2001:dead:beef::1"}], } - self.assertEqual(expected, dsaz.parse_network_config(imds_data)) + azure_ds._metadata_imds = imds_data - @mock.patch( - "cloudinit.sources.DataSourceAzure.device_driver", return_value=None - ) - def test_ipv6_secondary_ips_will_be_static_cidrs(self, m_driver): - """parse_network_config emits primary ipv6 as dhcp others are static""" + assert azure_ds.network_config == expected + + def test_ipv6_secondary_ips_will_be_static_cidrs( + self, azure_ds, mock_device_driver + ): + """Network config emits primary ipv6 as dhcp others are static""" expected = { "ethernets": { "eth0": { @@ -633,14 +611,13 @@ class TestParseNetworkConfig(CiTestCase): {"privateIpAddress": "2001:dead:beef::2"}, ], } - self.assertEqual(expected, dsaz.parse_network_config(imds_data)) + azure_ds._metadata_imds = imds_data - @mock.patch( - "cloudinit.sources.DataSourceAzure.device_driver", - return_value="hv_netvsc", - ) - def test_match_driver_for_netvsc(self, m_driver): - """parse_network_config emits driver when using netvsc.""" + assert azure_ds.network_config == expected + + def test_match_driver_for_netvsc(self, azure_ds, mock_device_driver): + """Network config emits driver when using netvsc.""" + mock_device_driver.return_value = "hv_netvsc" expected = { "ethernets": { "eth0": { @@ -656,16 +633,31 @@ class TestParseNetworkConfig(CiTestCase): }, "version": 2, } - self.assertEqual(expected, dsaz.parse_network_config(NETWORK_METADATA)) + azure_ds._metadata_imds = NETWORK_METADATA - @mock.patch( - "cloudinit.sources.DataSourceAzure.device_driver", return_value=None - ) - @mock.patch("cloudinit.net.generate_fallback_config") - def test_parse_network_config_uses_fallback_cfg_when_no_network_metadata( - self, m_fallback_config, m_driver + assert azure_ds.network_config == expected + + def test_uses_fallback_cfg_when_apply_network_config_is_false( + self, azure_ds, mock_device_driver, mock_generate_fallback_config + ): + azure_ds.ds_cfg["apply_network_config"] = False + azure_ds._metadata_imds = NETWORK_METADATA + mock_generate_fallback_config.return_value = self.fallback_config + + assert azure_ds.network_config == self.fallback_config + + def test_uses_fallback_cfg_when_imds_metadata_unset( + self, azure_ds, mock_device_driver, mock_generate_fallback_config + ): + azure_ds._metadata_imds = UNSET + mock_generate_fallback_config.return_value = self.fallback_config + + assert azure_ds.network_config == self.fallback_config + + def test_uses_fallback_cfg_when_no_network_metadata( + self, azure_ds, mock_device_driver, mock_generate_fallback_config ): - """parse_network_config generates fallback network config when the + """Network config generates fallback network config when the IMDS instance metadata is corrupted/invalid, such as when network metadata is not present. """ @@ -673,20 +665,15 @@ class TestParseNetworkConfig(CiTestCase): NETWORK_METADATA ) del imds_metadata_missing_network_metadata["network"] - m_fallback_config.return_value = self.fallback_config - self.assertEqual( - self.fallback_config, - dsaz.parse_network_config(imds_metadata_missing_network_metadata), - ) + mock_generate_fallback_config.return_value = self.fallback_config + azure_ds._metadata_imds = imds_metadata_missing_network_metadata - @mock.patch( - "cloudinit.sources.DataSourceAzure.device_driver", return_value=None - ) - @mock.patch("cloudinit.net.generate_fallback_config") - def test_parse_network_config_uses_fallback_cfg_when_no_interface_metadata( - self, m_fallback_config, m_driver + assert azure_ds.network_config == self.fallback_config + + def test_uses_fallback_cfg_when_no_interface_metadata( + self, azure_ds, mock_device_driver, mock_generate_fallback_config ): - """parse_network_config generates fallback network config when the + """Network config generates fallback network config when the IMDS instance metadata is corrupted/invalid, such as when network interface metadata is not present. """ @@ -694,13 +681,10 @@ class TestParseNetworkConfig(CiTestCase): NETWORK_METADATA ) del imds_metadata_missing_interface_metadata["network"]["interface"] - m_fallback_config.return_value = self.fallback_config - self.assertEqual( - self.fallback_config, - dsaz.parse_network_config( - imds_metadata_missing_interface_metadata - ), - ) + mock_generate_fallback_config.return_value = self.fallback_config + azure_ds._metadata_imds = imds_metadata_missing_interface_metadata + + assert azure_ds.network_config == self.fallback_config class TestGetMetadataFromIMDS(HttprettyTestCase): @@ -1201,16 +1185,15 @@ scbus-1 on xpt0 bus 0 ) def test_basic_seed_dir(self): - odata = {"HostName": "myhost", "UserName": "myuser"} data = { - "ovfcontent": construct_valid_ovf_env(data=odata), + "ovfcontent": construct_ovf_env(hostname="myhost"), "sys_cfg": {}, } dsrc = self._get_ds(data) ret = dsrc.get_data() self.assertTrue(ret) self.assertEqual(dsrc.userdata_raw, "") - self.assertEqual(dsrc.metadata["local-hostname"], odata["HostName"]) + self.assertEqual(dsrc.metadata["local-hostname"], "myhost") self.assertTrue( os.path.isfile(os.path.join(self.waagent_d, "ovf-env.xml")) ) @@ -1221,9 +1204,8 @@ scbus-1 on xpt0 bus 0 ) def test_data_dir_without_imds_data(self): - odata = {"HostName": "myhost", "UserName": "myuser"} data = { - "ovfcontent": construct_valid_ovf_env(data=odata), + "ovfcontent": construct_ovf_env(hostname="myhost"), "sys_cfg": {}, } dsrc = self._get_ds( @@ -1240,7 +1222,7 @@ scbus-1 on xpt0 bus 0 self.assertTrue(ret) self.assertEqual(dsrc.userdata_raw, "") - self.assertEqual(dsrc.metadata["local-hostname"], odata["HostName"]) + self.assertEqual(dsrc.metadata["local-hostname"], "myhost") self.assertTrue( os.path.isfile(os.path.join(self.waagent_d, "ovf-env.xml")) ) @@ -1269,9 +1251,10 @@ scbus-1 on xpt0 bus 0 def test_get_data_non_ubuntu_will_not_remove_network_scripts(self): """get_data on non-Ubuntu will not remove ubuntu net scripts.""" - odata = {"HostName": "myhost", "UserName": "myuser"} data = { - "ovfcontent": construct_valid_ovf_env(data=odata), + "ovfcontent": construct_ovf_env( + hostname="myhost", username="myuser" + ), "sys_cfg": {}, } @@ -1282,9 +1265,8 @@ scbus-1 on xpt0 bus 0 def test_get_data_on_ubuntu_will_remove_network_scripts(self): """get_data will remove ubuntu net scripts on Ubuntu distro.""" sys_cfg = {"datasource": {"Azure": {"apply_network_config": True}}} - odata = {"HostName": "myhost", "UserName": "myuser"} data = { - "ovfcontent": construct_valid_ovf_env(data=odata), + "ovfcontent": construct_ovf_env(), "sys_cfg": sys_cfg, } @@ -1295,9 +1277,8 @@ scbus-1 on xpt0 bus 0 def test_get_data_on_ubuntu_will_not_remove_network_scripts_disabled(self): """When apply_network_config false, do not remove scripts on Ubuntu.""" sys_cfg = {"datasource": {"Azure": {"apply_network_config": False}}} - odata = {"HostName": "myhost", "UserName": "myuser"} data = { - "ovfcontent": construct_valid_ovf_env(data=odata), + "ovfcontent": construct_ovf_env(), "sys_cfg": sys_cfg, } @@ -1307,28 +1288,19 @@ scbus-1 on xpt0 bus 0 def test_crawl_metadata_returns_structured_data_and_caches_nothing(self): """Return all structured metadata and cache no class attributes.""" - yaml_cfg = "" - odata = { - "HostName": "myhost", - "UserName": "myuser", - "UserData": {"text": "FOOBAR", "encoding": "plain"}, - "dscfg": {"text": yaml_cfg, "encoding": "plain"}, - } data = { - "ovfcontent": construct_valid_ovf_env(data=odata), + "ovfcontent": construct_ovf_env( + hostname="myhost", username="myuser", custom_data="FOOBAR" + ), "sys_cfg": {}, } dsrc = self._get_ds(data) expected_cfg = { "PreprovisionedVMType": None, "PreprovisionedVm": False, - "datasource": {"Azure": {}}, "system_info": {"default_user": {"name": "myuser"}}, } expected_metadata = { - "azure_data": { - "configurationsettype": "LinuxProvisioningConfiguration" - }, "imds": NETWORK_METADATA, "instance-id": EXAMPLE_UUID, "local-hostname": "myhost", @@ -1346,11 +1318,11 @@ scbus-1 on xpt0 bus 0 list(crawled_metadata["files"].keys()), ["ovf-env.xml"] ) self.assertIn( - b"<HostName>myhost</HostName>", + b"<ns1:HostName>myhost</ns1:HostName>", crawled_metadata["files"]["ovf-env.xml"], ) self.assertEqual(crawled_metadata["metadata"], expected_metadata) - self.assertEqual(crawled_metadata["userdata_raw"], "FOOBAR") + self.assertEqual(crawled_metadata["userdata_raw"], b"FOOBAR") self.assertEqual(dsrc.userdata_raw, None) self.assertEqual(dsrc.metadata, {}) self.assertEqual(dsrc._metadata_imds, UNSET) @@ -1372,9 +1344,7 @@ scbus-1 on xpt0 bus 0 def test_crawl_metadata_call_imds_once_no_reprovision(self): """If reprovisioning, report ready at the end""" - ovfenv = construct_valid_ovf_env( - platform_settings={"PreprovisionedVm": "False"} - ) + ovfenv = construct_ovf_env(preprovisioned_vm=False) data = {"ovfcontent": ovfenv, "sys_cfg": {}} dsrc = self._get_ds(data) @@ -1390,9 +1360,7 @@ scbus-1 on xpt0 bus 0 self, poll_imds_func, m_report_ready, m_write ): """If reprovisioning, imds metadata will be fetched twice""" - ovfenv = construct_valid_ovf_env( - platform_settings={"PreprovisionedVm": "True"} - ) + ovfenv = construct_ovf_env(preprovisioned_vm=True) data = {"ovfcontent": ovfenv, "sys_cfg": {}} dsrc = self._get_ds(data) @@ -1409,9 +1377,7 @@ scbus-1 on xpt0 bus 0 self, poll_imds_func, m_report_ready, m_write ): """If reprovisioning, report ready at the end""" - ovfenv = construct_valid_ovf_env( - platform_settings={"PreprovisionedVm": "True"} - ) + ovfenv = construct_ovf_env(preprovisioned_vm=True) data = {"ovfcontent": ovfenv, "sys_cfg": {}} dsrc = self._get_ds(data) @@ -1432,11 +1398,8 @@ scbus-1 on xpt0 bus 0 self, detect_nics, poll_imds_func, report_ready_func, m_write ): """If reprovisioning, report ready at the end""" - ovfenv = construct_valid_ovf_env( - platform_settings={ - "PreprovisionedVMType": "Savable", - "PreprovisionedVm": "True", - } + ovfenv = construct_ovf_env( + preprovisioned_vm=True, preprovisioned_vm_type="Savable" ) data = {"ovfcontent": ovfenv, "sys_cfg": {}} @@ -1459,9 +1422,7 @@ scbus-1 on xpt0 bus 0 self, m_readurl, m_report_ready, m_media_switch, m_write ): """If reprovisioning, report ready using the obtained lease""" - ovfenv = construct_valid_ovf_env( - platform_settings={"PreprovisionedVm": "True"} - ) + ovfenv = construct_ovf_env(preprovisioned_vm=True) data = {"ovfcontent": ovfenv, "sys_cfg": {}} dsrc = self._get_ds(data) @@ -1476,7 +1437,7 @@ scbus-1 on xpt0 bus 0 self.m_dhcp.return_value.obtain_lease.return_value = lease m_media_switch.return_value = None - reprovision_ovfenv = construct_valid_ovf_env() + reprovision_ovfenv = construct_ovf_env() m_readurl.return_value = url_helper.StringResponse( reprovision_ovfenv.encode("utf-8") ) @@ -1490,7 +1451,7 @@ scbus-1 on xpt0 bus 0 def test_waagent_d_has_0700_perms(self): # we expect /var/lib/waagent to be created 0700 - dsrc = self._get_ds({"ovfcontent": construct_valid_ovf_env()}) + dsrc = self._get_ds({"ovfcontent": construct_ovf_env()}) ret = dsrc.get_data() self.assertTrue(ret) self.assertTrue(os.path.isdir(self.waagent_d)) @@ -1502,9 +1463,8 @@ scbus-1 on xpt0 bus 0 def test_network_config_set_from_imds(self, m_driver): """Datasource.network_config returns IMDS network data.""" sys_cfg = {"datasource": {"Azure": {"apply_network_config": True}}} - odata = {} data = { - "ovfcontent": construct_valid_ovf_env(data=odata), + "ovfcontent": construct_ovf_env(), "sys_cfg": sys_cfg, } expected_network_config = { @@ -1531,9 +1491,8 @@ scbus-1 on xpt0 bus 0 ): """Datasource.network_config adds route-metric to secondary nics.""" sys_cfg = {"datasource": {"Azure": {"apply_network_config": True}}} - odata = {} data = { - "ovfcontent": construct_valid_ovf_env(data=odata), + "ovfcontent": construct_ovf_env(), "sys_cfg": sys_cfg, } expected_network_config = { @@ -1583,9 +1542,8 @@ scbus-1 on xpt0 bus 0 ): """If an IP address is empty then there should no config for it.""" sys_cfg = {"datasource": {"Azure": {"apply_network_config": True}}} - odata = {} data = { - "ovfcontent": construct_valid_ovf_env(data=odata), + "ovfcontent": construct_ovf_env(), "sys_cfg": sys_cfg, } expected_network_config = { @@ -1610,9 +1568,8 @@ scbus-1 on xpt0 bus 0 def test_availability_zone_set_from_imds(self): """Datasource.availability returns IMDS platformFaultDomain.""" sys_cfg = {"datasource": {"Azure": {"apply_network_config": True}}} - odata = {} data = { - "ovfcontent": construct_valid_ovf_env(data=odata), + "ovfcontent": construct_ovf_env(), "sys_cfg": sys_cfg, } dsrc = self._get_ds(data) @@ -1622,9 +1579,8 @@ scbus-1 on xpt0 bus 0 def test_region_set_from_imds(self): """Datasource.region returns IMDS region location.""" sys_cfg = {"datasource": {"Azure": {"apply_network_config": True}}} - odata = {} data = { - "ovfcontent": construct_valid_ovf_env(data=odata), + "ovfcontent": construct_ovf_env(), "sys_cfg": sys_cfg, } dsrc = self._get_ds(data) @@ -1638,7 +1594,7 @@ scbus-1 on xpt0 bus 0 } } data = { - "ovfcontent": construct_valid_ovf_env(data={}), + "ovfcontent": construct_ovf_env(), "sys_cfg": sys_cfg, } @@ -1651,8 +1607,7 @@ scbus-1 on xpt0 bus 0 ) def test_username_used(self): - odata = {"HostName": "myhost", "UserName": "myuser"} - data = {"ovfcontent": construct_valid_ovf_env(data=odata)} + data = {"ovfcontent": construct_ovf_env(username="myuser")} dsrc = self._get_ds(data) ret = dsrc.get_data() @@ -1661,13 +1616,14 @@ scbus-1 on xpt0 bus 0 dsrc.cfg["system_info"]["default_user"]["name"], "myuser" ) + assert "ssh_pwauth" not in dsrc.cfg + def test_password_given(self): - odata = { - "HostName": "myhost", - "UserName": "myuser", - "UserPassword": "mypass", + data = { + "ovfcontent": construct_ovf_env( + username="myuser", password="mypass" + ) } - data = {"ovfcontent": construct_valid_ovf_env(data=odata)} dsrc = self._get_ds(data) ret = dsrc.get_data() @@ -1676,7 +1632,7 @@ scbus-1 on xpt0 bus 0 defuser = dsrc.cfg["system_info"]["default_user"] # default user should be updated username and should not be locked. - self.assertEqual(defuser["name"], odata["UserName"]) + self.assertEqual(defuser["name"], "myuser") self.assertFalse(defuser["lock_passwd"]) # passwd is crypt formated string $id$salt$encrypted # encrypting plaintext with salt value of everything up to final '$' @@ -1684,19 +1640,102 @@ scbus-1 on xpt0 bus 0 pos = defuser["passwd"].rfind("$") + 1 self.assertEqual( defuser["passwd"], - crypt.crypt(odata["UserPassword"], defuser["passwd"][0:pos]), + crypt.crypt("mypass", defuser["passwd"][0:pos]), ) # the same hashed value should also be present in cfg['password'] self.assertEqual(defuser["passwd"], dsrc.cfg["password"]) + assert dsrc.cfg["ssh_pwauth"] is True + + def test_password_with_disable_ssh_pw_auth_true(self): + data = { + "ovfcontent": construct_ovf_env( + username="myuser", + password="mypass", + disable_ssh_password_auth=True, + ) + } + + dsrc = self._get_ds(data) + dsrc.get_data() + + assert dsrc.cfg["ssh_pwauth"] is False + + def test_password_with_disable_ssh_pw_auth_false(self): + data = { + "ovfcontent": construct_ovf_env( + username="myuser", + password="mypass", + disable_ssh_password_auth=False, + ) + } + + dsrc = self._get_ds(data) + dsrc.get_data() + + assert dsrc.cfg["ssh_pwauth"] is True + + def test_password_with_disable_ssh_pw_auth_unspecified(self): + data = { + "ovfcontent": construct_ovf_env( + username="myuser", + password="mypass", + disable_ssh_password_auth=None, + ) + } + + dsrc = self._get_ds(data) + dsrc.get_data() + + assert dsrc.cfg["ssh_pwauth"] is True + + def test_no_password_with_disable_ssh_pw_auth_true(self): + data = { + "ovfcontent": construct_ovf_env( + username="myuser", + disable_ssh_password_auth=True, + ) + } + + dsrc = self._get_ds(data) + dsrc.get_data() + + assert dsrc.cfg["ssh_pwauth"] is False + + def test_no_password_with_disable_ssh_pw_auth_false(self): + data = { + "ovfcontent": construct_ovf_env( + username="myuser", + disable_ssh_password_auth=False, + ) + } + + dsrc = self._get_ds(data) + dsrc.get_data() + + assert dsrc.cfg["ssh_pwauth"] is True + + def test_no_password_with_disable_ssh_pw_auth_unspecified(self): + data = { + "ovfcontent": construct_ovf_env( + username="myuser", + disable_ssh_password_auth=None, + ) + } + + dsrc = self._get_ds(data) + dsrc.get_data() + + assert "ssh_pwauth" not in dsrc.cfg + def test_user_not_locked_if_password_redacted(self): - odata = { - "HostName": "myhost", - "UserName": "myuser", - "UserPassword": dsaz.DEF_PASSWD_REDACTION, + data = { + "ovfcontent": construct_ovf_env( + username="myuser", + password=dsaz.DEF_PASSWD_REDACTION, + ) } - data = {"ovfcontent": construct_valid_ovf_env(data=odata)} dsrc = self._get_ds(data) ret = dsrc.get_data() @@ -1705,24 +1744,13 @@ scbus-1 on xpt0 bus 0 defuser = dsrc.cfg["system_info"]["default_user"] # default user should be updated username and should not be locked. - self.assertEqual(defuser["name"], odata["UserName"]) + self.assertEqual(defuser["name"], "myuser") self.assertIn("lock_passwd", defuser) self.assertFalse(defuser["lock_passwd"]) - def test_userdata_plain(self): - mydata = "FOOBAR" - odata = {"UserData": {"text": mydata, "encoding": "plain"}} - data = {"ovfcontent": construct_valid_ovf_env(data=odata)} - - dsrc = self._get_ds(data) - ret = dsrc.get_data() - self.assertTrue(ret) - self.assertEqual(decode_binary(dsrc.userdata_raw), mydata) - def test_userdata_found(self): mydata = "FOOBAR" - odata = {"UserData": {"text": b64e(mydata), "encoding": "base64"}} - data = {"ovfcontent": construct_valid_ovf_env(data=odata)} + data = {"ovfcontent": construct_ovf_env(custom_data=mydata)} dsrc = self._get_ds(data) ret = dsrc.get_data() @@ -1731,9 +1759,8 @@ scbus-1 on xpt0 bus 0 def test_default_ephemeral_configs_ephemeral_exists(self): # make sure the ephemeral configs are correct if disk present - odata = {} data = { - "ovfcontent": construct_valid_ovf_env(data=odata), + "ovfcontent": construct_ovf_env(), "sys_cfg": {}, } @@ -1761,9 +1788,8 @@ scbus-1 on xpt0 bus 0 def test_default_ephemeral_configs_ephemeral_does_not_exist(self): # make sure the ephemeral configs are correct if disk not present - odata = {} data = { - "ovfcontent": construct_valid_ovf_env(data=odata), + "ovfcontent": construct_ovf_env(), "sys_cfg": {}, } @@ -1783,34 +1809,9 @@ scbus-1 on xpt0 bus 0 assert "disk_setup" not in cfg assert "fs_setup" not in cfg - def test_provide_disk_aliases(self): - # Make sure that user can affect disk aliases - dscfg = {"disk_aliases": {"ephemeral0": "/dev/sdc"}} - odata = { - "HostName": "myhost", - "UserName": "myuser", - "dscfg": {"text": b64e(yaml.dump(dscfg)), "encoding": "base64"}, - } - usercfg = { - "disk_setup": { - "/dev/sdc": {"something": "..."}, - "ephemeral0": False, - } - } - userdata = "#cloud-config" + yaml.dump(usercfg) + "\n" - - ovfcontent = construct_valid_ovf_env(data=odata, userdata=userdata) - data = {"ovfcontent": ovfcontent, "sys_cfg": {}} - - dsrc = self._get_ds(data) - ret = dsrc.get_data() - self.assertTrue(ret) - cfg = dsrc.get_config_obj() - self.assertTrue(cfg) - def test_userdata_arrives(self): userdata = "This is my user-data" - xml = construct_valid_ovf_env(data={}, userdata=userdata) + xml = construct_ovf_env(custom_data=userdata) data = {"ovfcontent": xml} dsrc = self._get_ds(data) dsrc.get_data() @@ -1818,12 +1819,11 @@ scbus-1 on xpt0 bus 0 self.assertEqual(userdata.encode("us-ascii"), dsrc.userdata_raw) def test_password_redacted_in_ovf(self): - odata = { - "HostName": "myhost", - "UserName": "myuser", - "UserPassword": "mypass", + data = { + "ovfcontent": construct_ovf_env( + username="myuser", password="mypass" + ) } - data = {"ovfcontent": construct_valid_ovf_env(data=odata)} dsrc = self._get_ds(data) ret = dsrc.get_data() @@ -1846,7 +1846,7 @@ scbus-1 on xpt0 bus 0 self.assertEqual(dsaz.DEF_PASSWD_REDACTION, elem.text) def test_ovf_env_arrives_in_waagent_dir(self): - xml = construct_valid_ovf_env(data={}, userdata="FOODATA") + xml = construct_ovf_env(custom_data="FOODATA") dsrc = self._get_ds({"ovfcontent": xml}) dsrc.get_data() @@ -1857,18 +1857,18 @@ scbus-1 on xpt0 bus 0 self.xml_equals(xml, load_file(ovf_env_path)) def test_ovf_can_include_unicode(self): - xml = construct_valid_ovf_env(data={}) + xml = construct_ovf_env() xml = "\ufeff{0}".format(xml) dsrc = self._get_ds({"ovfcontent": xml}) dsrc.get_data() def test_dsaz_report_ready_returns_true_when_report_succeeds(self): - dsrc = self._get_ds({"ovfcontent": construct_valid_ovf_env()}) + dsrc = self._get_ds({"ovfcontent": construct_ovf_env()}) assert dsrc._report_ready() == [] @mock.patch(MOCKPATH + "report_diagnostic_event") def test_dsaz_report_ready_failure_reports_telemetry(self, m_report_diag): - dsrc = self._get_ds({"ovfcontent": construct_valid_ovf_env()}) + dsrc = self._get_ds({"ovfcontent": construct_ovf_env()}) self.m_get_metadata_from_fabric.side_effect = Exception("foo") with pytest.raises(Exception): @@ -1883,7 +1883,7 @@ scbus-1 on xpt0 bus 0 ] def test_dsaz_report_failure_returns_true_when_report_succeeds(self): - dsrc = self._get_ds({"ovfcontent": construct_valid_ovf_env()}) + dsrc = self._get_ds({"ovfcontent": construct_ovf_env()}) with mock.patch.object(dsrc, "crawl_metadata") as m_crawl_metadata: # mock crawl metadata failure to cause report failure @@ -1895,7 +1895,7 @@ scbus-1 on xpt0 bus 0 def test_dsaz_report_failure_returns_false_and_does_not_propagate_exc( self, ): - dsrc = self._get_ds({"ovfcontent": construct_valid_ovf_env()}) + dsrc = self._get_ds({"ovfcontent": construct_ovf_env()}) with mock.patch.object( dsrc, "crawl_metadata" @@ -1923,7 +1923,7 @@ scbus-1 on xpt0 bus 0 self.assertEqual(2, self.m_report_failure_to_fabric.call_count) def test_dsaz_report_failure_description_msg(self): - dsrc = self._get_ds({"ovfcontent": construct_valid_ovf_env()}) + dsrc = self._get_ds({"ovfcontent": construct_ovf_env()}) with mock.patch.object(dsrc, "crawl_metadata") as m_crawl_metadata: # mock crawl metadata failure to cause report failure @@ -1936,7 +1936,7 @@ scbus-1 on xpt0 bus 0 ) def test_dsaz_report_failure_no_description_msg(self): - dsrc = self._get_ds({"ovfcontent": construct_valid_ovf_env()}) + dsrc = self._get_ds({"ovfcontent": construct_ovf_env()}) with mock.patch.object(dsrc, "crawl_metadata") as m_crawl_metadata: m_crawl_metadata.side_effect = Exception @@ -1947,7 +1947,7 @@ scbus-1 on xpt0 bus 0 ) def test_dsaz_report_failure_uses_cached_ephemeral_dhcp_ctx_lease(self): - dsrc = self._get_ds({"ovfcontent": construct_valid_ovf_env()}) + dsrc = self._get_ds({"ovfcontent": construct_ovf_env()}) with mock.patch.object( dsrc, "crawl_metadata" @@ -1965,7 +1965,7 @@ scbus-1 on xpt0 bus 0 ) def test_dsaz_report_failure_no_net_uses_new_ephemeral_dhcp_lease(self): - dsrc = self._get_ds({"ovfcontent": construct_valid_ovf_env()}) + dsrc = self._get_ds({"ovfcontent": construct_ovf_env()}) with mock.patch.object(dsrc, "crawl_metadata") as m_crawl_metadata: # mock crawl metadata failure to cause report failure @@ -1988,13 +1988,13 @@ scbus-1 on xpt0 bus 0 def test_exception_fetching_fabric_data_doesnt_propagate(self): """Errors communicating with fabric should warn, but return True.""" - dsrc = self._get_ds({"ovfcontent": construct_valid_ovf_env()}) + dsrc = self._get_ds({"ovfcontent": construct_ovf_env()}) self.m_get_metadata_from_fabric.side_effect = Exception ret = self._get_and_setup(dsrc) self.assertTrue(ret) def test_fabric_data_included_in_metadata(self): - dsrc = self._get_ds({"ovfcontent": construct_valid_ovf_env()}) + dsrc = self._get_ds({"ovfcontent": construct_ovf_env()}) self.m_get_metadata_from_fabric.return_value = ["ssh-key-value"] ret = self._get_and_setup(dsrc) self.assertTrue(ret) @@ -2006,7 +2006,7 @@ scbus-1 on xpt0 bus 0 upper_iid = EXAMPLE_UUID.upper() # lowercase current UUID ds = self._get_ds( - {"ovfcontent": construct_valid_ovf_env()}, instance_id=lower_iid + {"ovfcontent": construct_ovf_env()}, instance_id=lower_iid ) # UPPERCASE previous write_file( @@ -2018,7 +2018,7 @@ scbus-1 on xpt0 bus 0 # UPPERCASE current UUID ds = self._get_ds( - {"ovfcontent": construct_valid_ovf_env()}, instance_id=upper_iid + {"ovfcontent": construct_ovf_env()}, instance_id=upper_iid ) # lowercase previous write_file( @@ -2030,7 +2030,7 @@ scbus-1 on xpt0 bus 0 def test_instance_id_endianness(self): """Return the previous iid when dmi uuid is the byteswapped iid.""" - ds = self._get_ds({"ovfcontent": construct_valid_ovf_env()}) + ds = self._get_ds({"ovfcontent": construct_ovf_env()}) # byte-swapped previous write_file( os.path.join(self.paths.cloud_dir, "data", "instance-id"), @@ -2049,12 +2049,12 @@ scbus-1 on xpt0 bus 0 self.assertEqual(self.instance_id, ds.metadata["instance-id"]) def test_instance_id_from_dmidecode_used(self): - ds = self._get_ds({"ovfcontent": construct_valid_ovf_env()}) + ds = self._get_ds({"ovfcontent": construct_ovf_env()}) ds.get_data() self.assertEqual(self.instance_id, ds.metadata["instance-id"]) def test_instance_id_from_dmidecode_used_for_builtin(self): - ds = self._get_ds({"ovfcontent": construct_valid_ovf_env()}) + ds = self._get_ds({"ovfcontent": construct_ovf_env()}) ds.get_data() self.assertEqual(self.instance_id, ds.metadata["instance-id"]) @@ -2080,126 +2080,12 @@ scbus-1 on xpt0 bus 0 [mock.call("/dev/cd0")], m_check_fbsd_cdrom.call_args_list ) - @mock.patch( - "cloudinit.sources.DataSourceAzure.device_driver", return_value=None - ) - @mock.patch("cloudinit.net.generate_fallback_config") - def test_imds_network_config(self, mock_fallback, m_driver): - """Network config is generated from IMDS network data when present.""" - sys_cfg = {"datasource": {"Azure": {"apply_network_config": True}}} - odata = {"HostName": "myhost", "UserName": "myuser"} - data = { - "ovfcontent": construct_valid_ovf_env(data=odata), - "sys_cfg": sys_cfg, - } - - dsrc = self._get_ds(data) - ret = dsrc.get_data() - self.assertTrue(ret) - - expected_cfg = { - "ethernets": { - "eth0": { - "dhcp4": True, - "dhcp4-overrides": {"route-metric": 100}, - "dhcp6": False, - "match": {"macaddress": "00:0d:3a:04:75:98"}, - "set-name": "eth0", - } - }, - "version": 2, - } - - self.assertEqual(expected_cfg, dsrc.network_config) - mock_fallback.assert_not_called() - - @mock.patch("cloudinit.net.get_interface_mac") - @mock.patch("cloudinit.net.get_devicelist") - @mock.patch("cloudinit.net.device_driver") - @mock.patch("cloudinit.net.generate_fallback_config") - def test_imds_network_ignored_when_apply_network_config_false( - self, mock_fallback, mock_dd, mock_devlist, mock_get_mac - ): - """When apply_network_config is False, use fallback instead of IMDS.""" - sys_cfg = {"datasource": {"Azure": {"apply_network_config": False}}} - odata = {"HostName": "myhost", "UserName": "myuser"} - data = { - "ovfcontent": construct_valid_ovf_env(data=odata), - "sys_cfg": sys_cfg, - } - fallback_config = { - "version": 1, - "config": [ - { - "type": "physical", - "name": "eth0", - "mac_address": "00:11:22:33:44:55", - "params": {"driver": "hv_netsvc"}, - "subnets": [{"type": "dhcp"}], - } - ], - } - mock_fallback.return_value = fallback_config - - mock_devlist.return_value = ["eth0"] - mock_dd.return_value = ["hv_netsvc"] - mock_get_mac.return_value = "00:11:22:33:44:55" - - dsrc = self._get_ds(data) - self.assertTrue(dsrc.get_data()) - self.assertEqual(dsrc.network_config, fallback_config) - - @mock.patch("cloudinit.net.get_interface_mac") - @mock.patch("cloudinit.net.get_devicelist") - @mock.patch("cloudinit.net.device_driver") - @mock.patch("cloudinit.net.generate_fallback_config", autospec=True) - def test_fallback_network_config( - self, mock_fallback, mock_dd, mock_devlist, mock_get_mac - ): - """On absent IMDS network data, generate network fallback config.""" - odata = {"HostName": "myhost", "UserName": "myuser"} - data = { - "ovfcontent": construct_valid_ovf_env(data=odata), - "sys_cfg": {}, - } - - fallback_config = { - "version": 1, - "config": [ - { - "type": "physical", - "name": "eth0", - "mac_address": "00:11:22:33:44:55", - "params": {"driver": "hv_netsvc"}, - "subnets": [{"type": "dhcp"}], - } - ], - } - mock_fallback.return_value = fallback_config - - mock_devlist.return_value = ["eth0"] - mock_dd.return_value = ["hv_netsvc"] - mock_get_mac.return_value = "00:11:22:33:44:55" - - dsrc = self._get_ds(data) - # Represent empty response from network imds - self.m_get_metadata_from_imds.return_value = {} - ret = dsrc.get_data() - self.assertTrue(ret) - - netconfig = dsrc.network_config - self.assertEqual(netconfig, fallback_config) - mock_fallback.assert_called_with( - blacklist_drivers=["mlx4_core", "mlx5_core"], config_driver=True - ) - @mock.patch(MOCKPATH + "net.get_interfaces", autospec=True) def test_blacklist_through_distro(self, m_net_get_interfaces): """Verify Azure DS updates blacklist drivers in the distro's networking object.""" - odata = {"HostName": "myhost", "UserName": "myuser"} data = { - "ovfcontent": construct_valid_ovf_env(data=odata), + "ovfcontent": construct_ovf_env(), "sys_cfg": {}, } @@ -2221,9 +2107,8 @@ scbus-1 on xpt0 bus 0 ) def test_get_public_ssh_keys_with_imds(self, m_parse_certificates): sys_cfg = {"datasource": {"Azure": {"apply_network_config": True}}} - odata = {"HostName": "myhost", "UserName": "myuser"} data = { - "ovfcontent": construct_valid_ovf_env(data=odata), + "ovfcontent": construct_ovf_env(), "sys_cfg": sys_cfg, } dsrc = self._get_ds(data) @@ -2256,9 +2141,8 @@ scbus-1 on xpt0 bus 0 imds_data["compute"]["publicKeys"][0]["keyData"] = "no-openssh-format" m_get_metadata_from_imds.return_value = imds_data sys_cfg = {"datasource": {"Azure": {"apply_network_config": True}}} - odata = {"HostName": "myhost", "UserName": "myuser"} data = { - "ovfcontent": construct_valid_ovf_env(data=odata), + "ovfcontent": construct_ovf_env(), "sys_cfg": sys_cfg, } dsrc = self._get_ds(data) @@ -2272,9 +2156,8 @@ scbus-1 on xpt0 bus 0 def test_get_public_ssh_keys_without_imds(self, m_get_metadata_from_imds): m_get_metadata_from_imds.return_value = dict() sys_cfg = {"datasource": {"Azure": {"apply_network_config": True}}} - odata = {"HostName": "myhost", "UserName": "myuser"} data = { - "ovfcontent": construct_valid_ovf_env(data=odata), + "ovfcontent": construct_ovf_env(), "sys_cfg": sys_cfg, } dsrc = self._get_ds(data) @@ -2295,9 +2178,8 @@ scbus-1 on xpt0 bus 0 m_get_metadata_from_imds.side_effect = get_metadata_from_imds_side_eff sys_cfg = {"datasource": {"Azure": {"apply_network_config": True}}} - odata = {"HostName": "myhost", "UserName": "myuser"} data = { - "ovfcontent": construct_valid_ovf_env(data=odata), + "ovfcontent": construct_ovf_env(), "sys_cfg": sys_cfg, } dsrc = self._get_ds(data) @@ -2326,9 +2208,8 @@ scbus-1 on xpt0 bus 0 ) def test_imds_api_version_wanted_exists(self, m_get_metadata_from_imds): sys_cfg = {"datasource": {"Azure": {"apply_network_config": True}}} - odata = {"HostName": "myhost", "UserName": "myuser"} data = { - "ovfcontent": construct_valid_ovf_env(data=odata), + "ovfcontent": construct_ovf_env(), "sys_cfg": sys_cfg, } dsrc = self._get_ds(data) @@ -2348,9 +2229,8 @@ scbus-1 on xpt0 bus 0 @mock.patch(MOCKPATH + "get_metadata_from_imds") def test_hostname_from_imds(self, m_get_metadata_from_imds): sys_cfg = {"datasource": {"Azure": {"apply_network_config": True}}} - odata = {"HostName": "myhost", "UserName": "myuser"} data = { - "ovfcontent": construct_valid_ovf_env(data=odata), + "ovfcontent": construct_ovf_env(), "sys_cfg": sys_cfg, } imds_data_with_os_profile = copy.deepcopy(NETWORK_METADATA) @@ -2367,9 +2247,8 @@ scbus-1 on xpt0 bus 0 @mock.patch(MOCKPATH + "get_metadata_from_imds") def test_username_from_imds(self, m_get_metadata_from_imds): sys_cfg = {"datasource": {"Azure": {"apply_network_config": True}}} - odata = {"HostName": "myhost", "UserName": "myuser"} data = { - "ovfcontent": construct_valid_ovf_env(data=odata), + "ovfcontent": construct_ovf_env(), "sys_cfg": sys_cfg, } imds_data_with_os_profile = copy.deepcopy(NETWORK_METADATA) @@ -2388,9 +2267,8 @@ scbus-1 on xpt0 bus 0 @mock.patch(MOCKPATH + "get_metadata_from_imds") def test_disable_password_from_imds(self, m_get_metadata_from_imds): sys_cfg = {"datasource": {"Azure": {"apply_network_config": True}}} - odata = {"HostName": "myhost", "UserName": "myuser"} data = { - "ovfcontent": construct_valid_ovf_env(data=odata), + "ovfcontent": construct_ovf_env(), "sys_cfg": sys_cfg, } imds_data_with_os_profile = copy.deepcopy(NETWORK_METADATA) @@ -2407,9 +2285,8 @@ scbus-1 on xpt0 bus 0 @mock.patch(MOCKPATH + "get_metadata_from_imds") def test_userdata_from_imds(self, m_get_metadata_from_imds): sys_cfg = {"datasource": {"Azure": {"apply_network_config": True}}} - odata = {"HostName": "myhost", "UserName": "myuser"} data = { - "ovfcontent": construct_valid_ovf_env(data=odata), + "ovfcontent": construct_ovf_env(), "sys_cfg": sys_cfg, } userdata = "userdataImds" @@ -2431,14 +2308,9 @@ scbus-1 on xpt0 bus 0 self, m_get_metadata_from_imds ): userdataOVF = "userdataOVF" - odata = { - "HostName": "myhost", - "UserName": "myuser", - "UserData": {"text": b64e(userdataOVF), "encoding": "base64"}, - } sys_cfg = {"datasource": {"Azure": {"apply_network_config": True}}} data = { - "ovfcontent": construct_valid_ovf_env(data=odata), + "ovfcontent": construct_ovf_env(custom_data=userdataOVF), "sys_cfg": sys_cfg, } @@ -2487,18 +2359,17 @@ class TestLoadAzureDsDir(CiTestCase): class TestReadAzureOvf(CiTestCase): def test_invalid_xml_raises_non_azure_ds(self): - invalid_xml = "<foo>" + construct_valid_ovf_env(data={}) + invalid_xml = "<foo>" + construct_ovf_env() self.assertRaises( dsaz.BrokenAzureDataSource, dsaz.read_azure_ovf, invalid_xml ) def test_load_with_pubkeys(self): - mypklist = [{"fingerprint": "fp1", "path": "path1", "value": ""}] - pubkeys = [(x["fingerprint"], x["path"], x["value"]) for x in mypklist] - content = construct_valid_ovf_env(pubkeys=pubkeys) + public_keys = [{"fingerprint": "fp1", "path": "path1", "value": ""}] + content = construct_ovf_env(public_keys=public_keys) (_md, _ud, cfg) = dsaz.read_azure_ovf(content) - for mypk in mypklist: - self.assertIn(mypk, cfg["_pubkeys"]) + for pk in public_keys: + self.assertIn(pk, cfg["_pubkeys"]) class TestCanDevBeReformatted(CiTestCase): @@ -2866,9 +2737,7 @@ class TestPreprovisioningReadAzureOvfFlag(CiTestCase): def test_read_azure_ovf_with_true_flag(self): """The read_azure_ovf method should set the PreprovisionedVM cfg flag if the proper setting is present.""" - content = construct_valid_ovf_env( - platform_settings={"PreprovisionedVm": "True"} - ) + content = construct_ovf_env(preprovisioned_vm=True) ret = dsaz.read_azure_ovf(content) cfg = ret[2] self.assertTrue(cfg["PreprovisionedVm"]) @@ -2876,9 +2745,7 @@ class TestPreprovisioningReadAzureOvfFlag(CiTestCase): def test_read_azure_ovf_with_false_flag(self): """The read_azure_ovf method should set the PreprovisionedVM cfg flag to false if the proper setting is false.""" - content = construct_valid_ovf_env( - platform_settings={"PreprovisionedVm": "False"} - ) + content = construct_ovf_env(preprovisioned_vm=False) ret = dsaz.read_azure_ovf(content) cfg = ret[2] self.assertFalse(cfg["PreprovisionedVm"]) @@ -2886,7 +2753,7 @@ class TestPreprovisioningReadAzureOvfFlag(CiTestCase): def test_read_azure_ovf_without_flag(self): """The read_azure_ovf method should not set the PreprovisionedVM cfg flag.""" - content = construct_valid_ovf_env() + content = construct_ovf_env() ret = dsaz.read_azure_ovf(content) cfg = ret[2] self.assertFalse(cfg["PreprovisionedVm"]) @@ -2895,11 +2762,8 @@ class TestPreprovisioningReadAzureOvfFlag(CiTestCase): def test_read_azure_ovf_with_running_type(self): """The read_azure_ovf method should set PreprovisionedVMType cfg flag to Running.""" - content = construct_valid_ovf_env( - platform_settings={ - "PreprovisionedVMType": "Running", - "PreprovisionedVm": "True", - } + content = construct_ovf_env( + preprovisioned_vm=True, preprovisioned_vm_type="Running" ) ret = dsaz.read_azure_ovf(content) cfg = ret[2] @@ -2909,11 +2773,8 @@ class TestPreprovisioningReadAzureOvfFlag(CiTestCase): def test_read_azure_ovf_with_savable_type(self): """The read_azure_ovf method should set PreprovisionedVMType cfg flag to Savable.""" - content = construct_valid_ovf_env( - platform_settings={ - "PreprovisionedVMType": "Savable", - "PreprovisionedVm": "True", - } + content = construct_ovf_env( + preprovisioned_vm=True, preprovisioned_vm_type="Savable" ) ret = dsaz.read_azure_ovf(content) cfg = ret[2] @@ -2997,7 +2858,7 @@ class TestDeterminePPSTypeScenarios: == dsaz.PPSType.UNKNOWN ) assert is_file.mock_calls == [ - mock.call(dsaz.REPORTED_READY_MARKER_FILE) + mock.call(azure_ds._reported_ready_marker_file) ] @@ -3014,10 +2875,7 @@ class TestReprovision(CiTestCase): def test_reprovision_calls__poll_imds(self, _poll_imds, isfile): """_reprovision will poll IMDS.""" isfile.return_value = False - hostname = "myhost" - username = "myuser" - odata = {"HostName": hostname, "UserName": username} - _poll_imds.return_value = construct_valid_ovf_env(data=odata) + _poll_imds.return_value = construct_ovf_env() dsa = dsaz.DataSourceAzure({}, distro=mock.Mock(), paths=self.paths) dsa._reprovision() _poll_imds.assert_called_with() @@ -3053,7 +2911,7 @@ class TestPreprovisioningHotAttachNics(CiTestCase): self.assertEqual(1, m_detach.call_count) self.assertEqual(1, m_writefile.call_count) m_writefile.assert_called_with( - dsaz.REPORTED_READY_MARKER_FILE, mock.ANY + dsa._reported_ready_marker_file, mock.ANY ) @mock.patch(MOCKPATH + "util.write_file", autospec=True) @@ -3231,8 +3089,8 @@ class TestPreprovisioningHotAttachNics(CiTestCase): @mock.patch("cloudinit.net.find_fallback_nic", return_value="eth9") -@mock.patch("cloudinit.net.dhcp.EphemeralIPv4Network") -@mock.patch("cloudinit.net.dhcp.maybe_perform_dhcp_discovery") +@mock.patch("cloudinit.net.ephemeral.EphemeralIPv4Network") +@mock.patch("cloudinit.net.ephemeral.maybe_perform_dhcp_discovery") @mock.patch( "cloudinit.sources.helpers.netlink.wait_for_media_disconnect_connect" ) @@ -3288,7 +3146,9 @@ class TestPreprovisioningPollIMDS(CiTestCase): m_request.side_effect = fake_timeout_once dsa = dsaz.DataSourceAzure({}, distro=mock.Mock(), paths=self.paths) - with mock.patch(MOCKPATH + "REPORTED_READY_MARKER_FILE", report_file): + with mock.patch.object( + dsa, "_reported_ready_marker_file", report_file + ): dsa._poll_imds() assert m_report_ready.mock_calls == [mock.call()] @@ -3316,7 +3176,9 @@ class TestPreprovisioningPollIMDS(CiTestCase): m_isfile.return_value = True dsa = dsaz.DataSourceAzure({}, distro=None, paths=self.paths) dsa._ephemeral_dhcp_ctx = mock.Mock(lease={}) - with mock.patch(MOCKPATH + "REPORTED_READY_MARKER_FILE", report_file): + with mock.patch.object( + dsa, "_reported_ready_marker_file", report_file + ): dsa._poll_imds() self.assertEqual(0, m_dhcp.call_count) self.assertEqual(0, m_media_switch.call_count) @@ -3353,8 +3215,8 @@ class TestPreprovisioningPollIMDS(CiTestCase): report_file = self.tmp_path("report_marker", self.tmp) m_isfile.return_value = True dsa = dsaz.DataSourceAzure({}, distro=None, paths=self.paths) - with mock.patch( - MOCKPATH + "REPORTED_READY_MARKER_FILE", report_file + with mock.patch.object( + dsa, "_reported_ready_marker_file", report_file ), mock.patch.object(dsa, "_ephemeral_dhcp_ctx") as m_dhcp_ctx: m_dhcp_ctx.obtain_lease.return_value = "Dummy lease" dsa._ephemeral_dhcp_ctx = m_dhcp_ctx @@ -3388,7 +3250,9 @@ class TestPreprovisioningPollIMDS(CiTestCase): ] m_media_switch.return_value = None dsa = dsaz.DataSourceAzure({}, distro=mock.Mock(), paths=self.paths) - with mock.patch(MOCKPATH + "REPORTED_READY_MARKER_FILE", report_file): + with mock.patch.object( + dsa, "_reported_ready_marker_file", report_file + ): dsa._poll_imds() self.assertEqual(m_report_ready.call_count, 0) @@ -3416,7 +3280,9 @@ class TestPreprovisioningPollIMDS(CiTestCase): m_media_switch.return_value = None dsa = dsaz.DataSourceAzure({}, distro=None, paths=self.paths) self.assertFalse(os.path.exists(report_file)) - with mock.patch(MOCKPATH + "REPORTED_READY_MARKER_FILE", report_file): + with mock.patch.object( + dsa, "_reported_ready_marker_file", report_file + ): dsa._poll_imds() self.assertEqual(m_report_ready.call_count, 1) self.assertTrue(os.path.exists(report_file)) @@ -3446,7 +3312,9 @@ class TestPreprovisioningPollIMDS(CiTestCase): m_report_ready.side_effect = [Exception("fail")] dsa = dsaz.DataSourceAzure({}, distro=None, paths=self.paths) self.assertFalse(os.path.exists(report_file)) - with mock.patch(MOCKPATH + "REPORTED_READY_MARKER_FILE", report_file): + with mock.patch.object( + dsa, "_reported_ready_marker_file", report_file + ): self.assertRaises(InvalidMetaDataException, dsa._poll_imds) self.assertEqual(m_report_ready.call_count, 1) self.assertFalse(os.path.exists(report_file)) @@ -3458,8 +3326,8 @@ class TestPreprovisioningPollIMDS(CiTestCase): @mock.patch( "cloudinit.sources.helpers.netlink.wait_for_media_disconnect_connect" ) -@mock.patch("cloudinit.net.dhcp.EphemeralIPv4Network", autospec=True) -@mock.patch("cloudinit.net.dhcp.maybe_perform_dhcp_discovery") +@mock.patch("cloudinit.net.ephemeral.EphemeralIPv4Network", autospec=True) +@mock.patch("cloudinit.net.ephemeral.maybe_perform_dhcp_discovery") @mock.patch("requests.Session.request") class TestAzureDataSourcePreprovisioning(CiTestCase): def setUp(self): @@ -3535,8 +3403,7 @@ class TestAzureDataSourcePreprovisioning(CiTestCase): full_url = url.format(host) hostname = "myhost" username = "myuser" - odata = {"HostName": hostname, "UserName": username} - content = construct_valid_ovf_env(data=odata) + content = construct_ovf_env(username=username, hostname=hostname) m_request.return_value = mock.MagicMock( status_code=200, text=content, content=content ) @@ -4203,15 +4070,12 @@ class TestProvisioning: def test_running_pps(self): self.imds_md["extended"]["compute"]["ppsType"] = "Running" - ovf_data = {"HostName": "myhost", "UserName": "myuser"} nl_sock = mock.MagicMock() self.mock_netlink.create_bound_netlink_socket.return_value = nl_sock self.mock_readurl.side_effect = [ mock.MagicMock(contents=json.dumps(self.imds_md).encode()), - mock.MagicMock( - contents=construct_valid_ovf_env(data=ovf_data).encode() - ), + mock.MagicMock(contents=construct_ovf_env().encode()), mock.MagicMock(contents=json.dumps(self.imds_md).encode()), ] self.mock_azure_get_metadata_from_fabric.return_value = [] @@ -4293,7 +4157,6 @@ class TestProvisioning: def test_savable_pps(self): self.imds_md["extended"]["compute"]["ppsType"] = "Savable" - ovf_data = {"HostName": "myhost", "UserName": "myuser"} nl_sock = mock.MagicMock() self.mock_netlink.create_bound_netlink_socket.return_value = nl_sock @@ -4306,9 +4169,7 @@ class TestProvisioning: mock.MagicMock( contents=json.dumps(self.imds_md["network"]).encode() ), - mock.MagicMock( - contents=construct_valid_ovf_env(data=ovf_data).encode() - ), + mock.MagicMock(contents=construct_ovf_env().encode()), mock.MagicMock(contents=json.dumps(self.imds_md).encode()), ] self.mock_azure_get_metadata_from_fabric.return_value = [] @@ -4402,13 +4263,10 @@ class TestProvisioning: def test_recovery_pps(self, pps_type): self.patched_reported_ready_marker_path.write_text("") self.imds_md["extended"]["compute"]["ppsType"] = pps_type - ovf_data = {"HostName": "myhost", "UserName": "myuser"} self.mock_readurl.side_effect = [ mock.MagicMock(contents=json.dumps(self.imds_md).encode()), - mock.MagicMock( - contents=construct_valid_ovf_env(data=ovf_data).encode() - ), + mock.MagicMock(contents=construct_ovf_env().encode()), mock.MagicMock(contents=json.dumps(self.imds_md).encode()), ] self.mock_azure_get_metadata_from_fabric.return_value = [] diff --git a/tests/unittests/sources/test_bigstep.py b/tests/unittests/sources/test_bigstep.py new file mode 100644 index 00000000..148cfa0b --- /dev/null +++ b/tests/unittests/sources/test_bigstep.py @@ -0,0 +1,46 @@ +import json +import os + +import httpretty +import pytest + +from cloudinit import helpers +from cloudinit.sources import DataSourceBigstep as bigstep +from tests.unittests.helpers import mock + +M_PATH = "cloudinit.sources.DataSourceBigstep." + +IMDS_URL = "http://bigstep.com" +METADATA_BODY = json.dumps( + { + "metadata": "metadata", + "vendordata_raw": "vendordata_raw", + "userdata_raw": "userdata_raw", + } +) + + +class TestBigstep: + @httpretty.activate + @pytest.mark.parametrize("custom_paths", [False, True]) + @mock.patch(M_PATH + "util.load_file", return_value=IMDS_URL) + def test_get_data_honor_cloud_dir(self, m_load_file, custom_paths, tmpdir): + httpretty.register_uri(httpretty.GET, IMDS_URL, body=METADATA_BODY) + + paths = {} + url_file = "/var/lib/cloud/data/seed/bigstep/url" + if custom_paths: + paths = { + "cloud_dir": tmpdir.join("cloud"), + "run_dir": tmpdir, + "templates_dir": tmpdir, + } + url_file = os.path.join( + paths["cloud_dir"], "data", "seed", "bigstep", "url" + ) + + ds = bigstep.DataSourceBigstep( + sys_cfg={}, distro=mock.Mock(), paths=helpers.Paths(paths) + ) + assert ds._get_data() + assert [mock.call(url_file)] == m_load_file.call_args_list diff --git a/tests/unittests/sources/test_cloudsigma.py b/tests/unittests/sources/test_cloudsigma.py index 8cd58c96..b92c3723 100644 --- a/tests/unittests/sources/test_cloudsigma.py +++ b/tests/unittests/sources/test_cloudsigma.py @@ -58,12 +58,14 @@ class DataSourceCloudSigmaTest(test_helpers.CiTestCase): def test_get_hostname(self): self.datasource.get_data() - self.assertEqual("test_server", self.datasource.get_hostname()) + self.assertEqual( + "test_server", self.datasource.get_hostname().hostname + ) self.datasource.metadata["name"] = "" - self.assertEqual("65b2fb23", self.datasource.get_hostname()) + self.assertEqual("65b2fb23", self.datasource.get_hostname().hostname) utf8_hostname = b"\xd1\x82\xd0\xb5\xd1\x81\xd1\x82".decode("utf-8") self.datasource.metadata["name"] = utf8_hostname - self.assertEqual("65b2fb23", self.datasource.get_hostname()) + self.assertEqual("65b2fb23", self.datasource.get_hostname().hostname) def test_get_public_ssh_keys(self): self.datasource.get_data() diff --git a/tests/unittests/sources/test_cloudstack.py b/tests/unittests/sources/test_cloudstack.py index f7c69f91..b37400d3 100644 --- a/tests/unittests/sources/test_cloudstack.py +++ b/tests/unittests/sources/test_cloudstack.py @@ -40,6 +40,11 @@ class TestCloudStackPasswordFetching(CiTestCase): get_networkd_server_address, ) ) + get_data_server = mock.MagicMock(return_value=None) + self.patches.enter_context( + mock.patch(mod_name + ".get_data_server", get_data_server) + ) + self.tmp = self.tmp_dir() def _set_password_server_response(self, response_string): diff --git a/tests/unittests/sources/test_digitalocean.py b/tests/unittests/sources/test_digitalocean.py index f3e6224e..47e46c66 100644 --- a/tests/unittests/sources/test_digitalocean.py +++ b/tests/unittests/sources/test_digitalocean.py @@ -178,7 +178,7 @@ class TestDataSourceDigitalOcean(CiTestCase): self.assertEqual(DO_META.get("vendor_data"), ds.get_vendordata_raw()) self.assertEqual(DO_META.get("region"), ds.availability_zone) self.assertEqual(DO_META.get("droplet_id"), ds.get_instance_id()) - self.assertEqual(DO_META.get("hostname"), ds.get_hostname()) + self.assertEqual(DO_META.get("hostname"), ds.get_hostname().hostname) # Single key self.assertEqual( diff --git a/tests/unittests/sources/test_ec2.py b/tests/unittests/sources/test_ec2.py index e5648007..b7476391 100644 --- a/tests/unittests/sources/test_ec2.py +++ b/tests/unittests/sources/test_ec2.py @@ -211,7 +211,7 @@ SECONDARY_IP_METADATA_2018_09_24 = { M_PATH_NET = "cloudinit.sources.DataSourceEc2.net." -TAGS_METADATA_2021_03_23 = { +TAGS_METADATA_2021_03_23: dict = { **DEFAULT_METADATA, "tags": { "instance": { @@ -837,13 +837,14 @@ class TestEc2(test_helpers.HttprettyTestCase): self.logs.getvalue(), ) - @mock.patch("cloudinit.net.dhcp.EphemeralIPv4Network") + @mock.patch("cloudinit.net.ephemeral.EphemeralIPv6Network") + @mock.patch("cloudinit.net.ephemeral.EphemeralIPv4Network") @mock.patch("cloudinit.net.find_fallback_nic") - @mock.patch("cloudinit.net.dhcp.maybe_perform_dhcp_discovery") + @mock.patch("cloudinit.net.ephemeral.maybe_perform_dhcp_discovery") @mock.patch("cloudinit.sources.DataSourceEc2.util.is_FreeBSD") @responses.activate def test_ec2_local_performs_dhcp_on_non_bsd( - self, m_is_bsd, m_dhcp, m_fallback_nic, m_net + self, m_is_bsd, m_dhcp, m_fallback_nic, m_net4, m_net6 ): """Ec2Local returns True for valid platform data on non-BSD with dhcp. @@ -873,7 +874,7 @@ class TestEc2(test_helpers.HttprettyTestCase): ret = ds.get_data() self.assertTrue(ret) m_dhcp.assert_called_once_with("eth9", None) - m_net.assert_called_once_with( + m_net4.assert_called_once_with( broadcast="192.168.2.255", interface="eth9", ip="192.168.2.9", @@ -881,7 +882,7 @@ class TestEc2(test_helpers.HttprettyTestCase): router="192.168.2.1", static_routes=None, ) - self.assertIn("Crawl of metadata service took", self.logs.getvalue()) + self.assertIn("Crawl of metadata service ", self.logs.getvalue()) @responses.activate def test_get_instance_tags(self): diff --git a/tests/unittests/sources/test_gce.py b/tests/unittests/sources/test_gce.py index e030931b..1ce0c6ec 100644 --- a/tests/unittests/sources/test_gce.py +++ b/tests/unittests/sources/test_gce.py @@ -126,7 +126,7 @@ class TestDataSourceGCE(test_helpers.HttprettyTestCase): self.ds.get_data() shostname = GCE_META.get("instance/hostname").split(".")[0] - self.assertEqual(shostname, self.ds.get_hostname()) + self.assertEqual(shostname, self.ds.get_hostname().hostname) self.assertEqual( GCE_META.get("instance/id"), self.ds.get_instance_id() @@ -147,7 +147,7 @@ class TestDataSourceGCE(test_helpers.HttprettyTestCase): ) shostname = GCE_META_PARTIAL.get("instance/hostname").split(".")[0] - self.assertEqual(shostname, self.ds.get_hostname()) + self.assertEqual(shostname, self.ds.get_hostname().hostname) def test_userdata_no_encoding(self): """check that user-data is read.""" diff --git a/tests/unittests/sources/test_hetzner.py b/tests/unittests/sources/test_hetzner.py index f80ed45f..193b7e42 100644 --- a/tests/unittests/sources/test_hetzner.py +++ b/tests/unittests/sources/test_hetzner.py @@ -116,7 +116,7 @@ class TestDataSourceHetzner(CiTestCase): self.assertTrue(m_readmd.called) - self.assertEqual(METADATA.get("hostname"), ds.get_hostname()) + self.assertEqual(METADATA.get("hostname"), ds.get_hostname().hostname) self.assertEqual(METADATA.get("public-keys"), ds.get_public_ssh_keys()) diff --git a/tests/unittests/sources/test_init.py b/tests/unittests/sources/test_init.py index ce8fc970..a42c6a72 100644 --- a/tests/unittests/sources/test_init.py +++ b/tests/unittests/sources/test_init.py @@ -272,9 +272,11 @@ class TestDataSource(CiTestCase): self.assertEqual( "test-subclass-hostname", datasource.metadata["local-hostname"] ) - self.assertEqual("test-subclass-hostname", datasource.get_hostname()) + self.assertEqual( + "test-subclass-hostname", datasource.get_hostname().hostname + ) datasource.metadata["local-hostname"] = "hostname.my.domain.com" - self.assertEqual("hostname", datasource.get_hostname()) + self.assertEqual("hostname", datasource.get_hostname().hostname) def test_get_hostname_with_fqdn_returns_local_hostname_with_domain(self): """Datasource.get_hostname with fqdn set gets qualified hostname.""" @@ -285,7 +287,8 @@ class TestDataSource(CiTestCase): self.assertTrue(datasource.get_data()) datasource.metadata["local-hostname"] = "hostname.my.domain.com" self.assertEqual( - "hostname.my.domain.com", datasource.get_hostname(fqdn=True) + "hostname.my.domain.com", + datasource.get_hostname(fqdn=True).hostname, ) def test_get_hostname_without_metadata_uses_system_hostname(self): @@ -300,10 +303,12 @@ class TestDataSource(CiTestCase): with mock.patch(mock_fqdn) as m_fqdn: m_gethost.return_value = "systemhostname.domain.com" m_fqdn.return_value = None # No maching fqdn in /etc/hosts - self.assertEqual("systemhostname", datasource.get_hostname()) + self.assertEqual( + "systemhostname", datasource.get_hostname().hostname + ) self.assertEqual( "systemhostname.domain.com", - datasource.get_hostname(fqdn=True), + datasource.get_hostname(fqdn=True).hostname, ) def test_get_hostname_without_metadata_returns_none(self): @@ -316,9 +321,13 @@ class TestDataSource(CiTestCase): mock_fqdn = "cloudinit.sources.util.get_fqdn_from_hosts" with mock.patch("cloudinit.sources.util.get_hostname") as m_gethost: with mock.patch(mock_fqdn) as m_fqdn: - self.assertIsNone(datasource.get_hostname(metadata_only=True)) self.assertIsNone( - datasource.get_hostname(fqdn=True, metadata_only=True) + datasource.get_hostname(metadata_only=True).hostname + ) + self.assertIsNone( + datasource.get_hostname( + fqdn=True, metadata_only=True + ).hostname ) self.assertEqual([], m_gethost.call_args_list) self.assertEqual([], m_fqdn.call_args_list) @@ -335,10 +344,12 @@ class TestDataSource(CiTestCase): with mock.patch(mock_fqdn) as m_fqdn: m_gethost.return_value = "systemhostname.domain.com" m_fqdn.return_value = "fqdnhostname.domain.com" - self.assertEqual("fqdnhostname", datasource.get_hostname()) + self.assertEqual( + "fqdnhostname", datasource.get_hostname().hostname + ) self.assertEqual( "fqdnhostname.domain.com", - datasource.get_hostname(fqdn=True), + datasource.get_hostname(fqdn=True).hostname, ) def test_get_data_does_not_write_instance_data_on_failure(self): @@ -750,7 +761,9 @@ class TestDataSource(CiTestCase): """Validate get_hostname signature on all subclasses of DataSource.""" base_args = inspect.getfullargspec(DataSource.get_hostname) # Import all DataSource subclasses so we can inspect them. - modules = util.find_modules(os.path.dirname(os.path.dirname(__file__))) + modules = util.get_modules_from_dir( + os.path.dirname(os.path.dirname(__file__)) + ) for _loc, name in modules.items(): mod_locs, _ = importer.find_module(name, ["cloudinit.sources"], []) if mod_locs: diff --git a/tests/unittests/sources/test_lxd.py b/tests/unittests/sources/test_lxd.py index e11c3746..e60bb71f 100644 --- a/tests/unittests/sources/test_lxd.py +++ b/tests/unittests/sources/test_lxd.py @@ -17,7 +17,7 @@ from cloudinit.sources import InvalidMetaDataException DS_PATH = "cloudinit.sources.DataSourceLXD." -LStatResponse = namedtuple("lstatresponse", "st_mode") +LStatResponse = namedtuple("LStatResponse", "st_mode") NETWORK_V1 = { @@ -34,7 +34,7 @@ NETWORK_V1 = { def _add_network_v1_device(devname) -> dict: """Helper to inject device name into default network v1 config.""" - network_cfg = deepcopy(NETWORK_V1) + network_cfg: dict = deepcopy(NETWORK_V1) network_cfg["config"][0]["name"] = devname return network_cfg @@ -51,14 +51,27 @@ LXD_V1_METADATA = { }, } +LXD_V1_METADATA_NO_NETWORK_CONFIG = { + "meta-data": "instance-id: my-lxc\nlocal-hostname: my-lxc\n\n", + "user-data": "#cloud-config\npackages: [sl]\n", + "vendor-data": "#cloud-config\nruncmd: ['echo vendor-data']\n", + "config": { + "user.user-data": "instance-id: my-lxc\nlocal-hostname: my-lxc\n\n", + "user.vendor-data": "#cloud-config\nruncmd: ['echo vendor-data']\n", + }, +} + -@pytest.fixture def lxd_metadata(): return LXD_V1_METADATA +def lxd_metadata_no_network_config(): + return LXD_V1_METADATA_NO_NETWORK_CONFIG + + @pytest.fixture -def lxd_ds(request, paths, lxd_metadata): +def lxd_ds(request, paths): """ Return an instantiated DataSourceLXD. @@ -69,7 +82,30 @@ def lxd_ds(request, paths, lxd_metadata): (This uses the paths fixture for the required helpers.Paths object) """ with mock.patch(DS_PATH + "is_platform_viable", return_value=True): - with mock.patch(DS_PATH + "read_metadata", return_value=lxd_metadata): + with mock.patch( + DS_PATH + "read_metadata", return_value=lxd_metadata() + ): + yield lxd.DataSourceLXD( + sys_cfg={}, distro=mock.Mock(), paths=paths + ) + + +@pytest.fixture +def lxd_ds_no_network_config(request, paths): + """ + Return an instantiated DataSourceLXD. + + This also performs the mocking required for the default test case: + * ``is_platform_viable`` returns True, + * ``read_metadata`` returns ``LXD_V1_METADATA_NO_NETWORK_CONFIG`` + + (This uses the paths fixture for the required helpers.Paths object) + """ + with mock.patch(DS_PATH + "is_platform_viable", return_value=True): + with mock.patch( + DS_PATH + "read_metadata", + return_value=lxd_metadata_no_network_config(), + ): yield lxd.DataSourceLXD( sys_cfg={}, distro=mock.Mock(), paths=paths ) @@ -142,6 +178,37 @@ class TestDataSourceLXD: assert LXD_V1_METADATA["user-data"] == lxd_ds.userdata_raw assert LXD_V1_METADATA["vendor-data"] == lxd_ds.vendordata_raw + def test_network_config_when_unset(self, lxd_ds): + """network_config is correctly computed when _network_config and + _crawled_metadata are unset. + """ + assert UNSET == lxd_ds._crawled_metadata + assert UNSET == lxd_ds._network_config + assert None is lxd_ds.userdata_raw + # network-config is dumped from YAML + assert NETWORK_V1 == lxd_ds.network_config + assert LXD_V1_METADATA == lxd_ds._crawled_metadata + + def test_network_config_crawled_metadata_no_network_config( + self, lxd_ds_no_network_config + ): + """network_config is correctly computed when _network_config is unset + and _crawled_metadata does not contain network_config. + """ + lxd.generate_fallback_network_config = mock.Mock( + return_value=NETWORK_V1 + ) + assert UNSET == lxd_ds_no_network_config._crawled_metadata + assert UNSET == lxd_ds_no_network_config._network_config + assert None is lxd_ds_no_network_config.userdata_raw + # network-config is dumped from YAML + assert NETWORK_V1 == lxd_ds_no_network_config.network_config + assert ( + LXD_V1_METADATA_NO_NETWORK_CONFIG + == lxd_ds_no_network_config._crawled_metadata + ) + assert 1 == lxd.generate_fallback_network_config.call_count + class TestIsPlatformViable: @pytest.mark.parametrize( diff --git a/tests/unittests/sources/test_opennebula.py b/tests/unittests/sources/test_opennebula.py index e05c4749..af1c45b8 100644 --- a/tests/unittests/sources/test_opennebula.py +++ b/tests/unittests/sources/test_opennebula.py @@ -73,7 +73,7 @@ class TestOpenNebulaDataSource(CiTestCase): orig_find_devs_with = util.find_devs_with try: # dont' try to lookup for CDs - util.find_devs_with = lambda n: [] + util.find_devs_with = lambda n: [] # type: ignore dsrc = self.ds(sys_cfg=self.sys_cfg, distro=None, paths=self.paths) ret = dsrc.get_data() self.assertFalse(ret) @@ -84,7 +84,7 @@ class TestOpenNebulaDataSource(CiTestCase): orig_find_devs_with = util.find_devs_with try: # dont' try to lookup for CDs - util.find_devs_with = lambda n: [] + util.find_devs_with = lambda n: [] # type: ignore populate_dir(self.seed_dir, {"context.sh": INVALID_CONTEXT}) dsrc = self.ds(sys_cfg=self.sys_cfg, distro=None, paths=self.paths) self.assertRaises(ds.BrokenContextDiskDir, dsrc.get_data) @@ -107,7 +107,7 @@ class TestOpenNebulaDataSource(CiTestCase): ] = invalid_user # dont' try to lookup for CDs - util.find_devs_with = lambda n: [] + util.find_devs_with = lambda n: [] # type: ignore populate_context_dir(self.seed_dir, {"KEY1": "val1"}) dsrc = self.ds(sys_cfg=sys_cfg, distro=None, paths=self.paths) self.assertRaises(ds.BrokenContextDiskDir, dsrc.get_data) @@ -118,7 +118,7 @@ class TestOpenNebulaDataSource(CiTestCase): orig_find_devs_with = util.find_devs_with try: # dont' try to lookup for CDs - util.find_devs_with = lambda n: [] + util.find_devs_with = lambda n: [] # type: ignore populate_context_dir(self.seed_dir, {"KEY1": "val1"}) dsrc = self.ds(sys_cfg=self.sys_cfg, distro=None, paths=self.paths) ret = dsrc.get_data() diff --git a/tests/unittests/sources/test_openstack.py b/tests/unittests/sources/test_openstack.py index c111bbcd..f65aab8b 100644 --- a/tests/unittests/sources/test_openstack.py +++ b/tests/unittests/sources/test_openstack.py @@ -39,7 +39,7 @@ USER_DATA = b"#!/bin/sh\necho This is user data\n" VENDOR_DATA = { "magic": "", } -VENDOR_DATA2 = {"static": {}} +VENDOR_DATA2: dict = {"static": {}} OSTACK_META = { "availability_zone": "nova", "files": [ @@ -284,8 +284,10 @@ class TestOpenStackDataSource(test_helpers.HttprettyTestCase): m_dhcp.assert_not_called() @hp.activate - @test_helpers.mock.patch("cloudinit.net.dhcp.EphemeralIPv4Network") - @test_helpers.mock.patch("cloudinit.net.dhcp.maybe_perform_dhcp_discovery") + @test_helpers.mock.patch("cloudinit.net.ephemeral.EphemeralIPv4Network") + @test_helpers.mock.patch( + "cloudinit.net.ephemeral.maybe_perform_dhcp_discovery" + ) def test_local_datasource(self, m_dhcp, m_net): """OpenStackLocal calls EphemeralDHCPNetwork and gets instance data.""" _register_uris(self.VERSION, EC2_FILES, EC2_META, OS_FILES) diff --git a/tests/unittests/sources/test_oracle.py b/tests/unittests/sources/test_oracle.py index b7b16952..7cd55be0 100644 --- a/tests/unittests/sources/test_oracle.py +++ b/tests/unittests/sources/test_oracle.py @@ -3,7 +3,7 @@ import base64 import copy import json -from contextlib import ExitStack +import logging from unittest import mock import pytest @@ -13,6 +13,7 @@ from cloudinit.sources import NetworkConfigSource from cloudinit.sources.DataSourceOracle import OpcMetadata from cloudinit.url_helper import UrlError from tests.unittests import helpers as test_helpers +from tests.unittests.helpers import does_not_raise DS_PATH = "cloudinit.sources.DataSourceOracle" @@ -87,6 +88,25 @@ OPC_V2_METADATA = """\ # Just a small meaningless change to differentiate the two metadatas OPC_V1_METADATA = OPC_V2_METADATA.replace("ocid1.instance", "ocid2.instance") +MAC_ADDR = "00:00:17:02:2b:b1" + +DHCP = { + "name": "eth0", + "type": "physical", + "subnets": [ + { + "broadcast": "192.168.122.255", + "control": "manual", + "gateway": "192.168.122.1", + "dns_search": ["foo.com"], + "type": "dhcp", + "netmask": "255.255.255.0", + "dns_nameservers": ["192.168.122.1"], + } + ], +} +KLIBC_NET_CFG = {"version": 1, "config": [DHCP]} + @pytest.fixture def metadata_version(): @@ -94,15 +114,20 @@ def metadata_version(): @pytest.fixture -def oracle_ds(request, fixture_utils, paths, metadata_version): +def oracle_ds(request, fixture_utils, paths, metadata_version, mocker): """ Return an instantiated DataSourceOracle. - This also performs the mocking required for the default test case: + This also performs the mocking required: * ``_read_system_uuid`` returns something, * ``_is_platform_viable`` returns True, - * ``_is_iscsi_root`` returns True (the simpler code path), - * ``read_opc_metadata`` returns ``OPC_V1_METADATA`` + * ``DataSourceOracle._is_iscsi_root`` returns True by default or what + pytest.mark.is_iscsi gives as first param, + * ``DataSourceOracle._get_iscsi_config`` returns a network cfg if + is_iscsi else an empty network config, + * ``read_opc_metadata`` returns ``OPC_V1_METADATA``, + * ``ephemeral.EphemeralDHCPv4`` and ``net.find_fallback_nic`` mocked to + avoid subp calls (This uses the paths fixture for the required helpers.Paths object, and the fixture_utils fixture for fetching markers.) @@ -110,19 +135,29 @@ def oracle_ds(request, fixture_utils, paths, metadata_version): sys_cfg = fixture_utils.closest_marker_first_arg_or( request, "ds_sys_cfg", mock.MagicMock() ) + is_iscsi = fixture_utils.closest_marker_first_arg_or( + request, "is_iscsi", True + ) metadata = OpcMetadata(metadata_version, json.loads(OPC_V2_METADATA), None) - with mock.patch(DS_PATH + "._read_system_uuid", return_value="someuuid"): - with mock.patch(DS_PATH + "._is_platform_viable", return_value=True): - with mock.patch(DS_PATH + "._is_iscsi_root", return_value=True): - with mock.patch( - DS_PATH + ".read_opc_metadata", - return_value=metadata, - ): - yield oracle.DataSourceOracle( - sys_cfg=sys_cfg, - distro=mock.Mock(), - paths=paths, - ) + + mocker.patch(DS_PATH + ".net.find_fallback_nic") + mocker.patch(DS_PATH + ".ephemeral.EphemeralDHCPv4") + mocker.patch(DS_PATH + "._read_system_uuid", return_value="someuuid") + mocker.patch(DS_PATH + "._is_platform_viable", return_value=True) + mocker.patch(DS_PATH + ".read_opc_metadata", return_value=metadata) + mocker.patch(DS_PATH + ".KlibcOracleNetworkConfigSource") + ds = oracle.DataSourceOracle( + sys_cfg=sys_cfg, + distro=mock.Mock(), + paths=paths, + ) + mocker.patch.object(ds, "_is_iscsi_root", return_value=is_iscsi) + if is_iscsi: + iscsi_config = copy.deepcopy(KLIBC_NET_CFG) + else: + iscsi_config = {"version": 1, "config": []} + mocker.patch.object(ds, "_get_iscsi_config", return_value=iscsi_config) + yield ds class TestDataSourceOracle: @@ -158,28 +193,27 @@ class TestDataSourceOracle: assert oracle_ds.ds_cfg["configure_secondary_nics"] -class TestIsPlatformViable(test_helpers.CiTestCase): - @mock.patch( - DS_PATH + ".dmi.read_dmi_data", return_value=oracle.CHASSIS_ASSET_TAG +class TestIsPlatformViable: + @pytest.mark.parametrize( + "dmi_data, platform_viable", + [ + # System with known chassis tag is viable. + (oracle.CHASSIS_ASSET_TAG, True), + # System without known chassis tag is not viable. + (None, False), + # System with unknown chassis tag is not viable. + ("LetsGoCubs", False), + ], ) - def test_expected_viable(self, m_read_dmi_data): - """System with known chassis tag is viable.""" - self.assertTrue(oracle._is_platform_viable()) - m_read_dmi_data.assert_has_calls([mock.call("chassis-asset-tag")]) - - @mock.patch(DS_PATH + ".dmi.read_dmi_data", return_value=None) - def test_expected_not_viable_dmi_data_none(self, m_read_dmi_data): - """System without known chassis tag is not viable.""" - self.assertFalse(oracle._is_platform_viable()) - m_read_dmi_data.assert_has_calls([mock.call("chassis-asset-tag")]) - - @mock.patch(DS_PATH + ".dmi.read_dmi_data", return_value="LetsGoCubs") - def test_expected_not_viable_other(self, m_read_dmi_data): - """System with unnown chassis tag is not viable.""" - self.assertFalse(oracle._is_platform_viable()) + def test_is_platform_viable(self, dmi_data, platform_viable): + with mock.patch( + DS_PATH + ".dmi.read_dmi_data", return_value=dmi_data + ) as m_read_dmi_data: + assert platform_viable == oracle._is_platform_viable() m_read_dmi_data.assert_has_calls([mock.call("chassis-asset-tag")]) +@pytest.mark.is_iscsi(False) @mock.patch( "cloudinit.net.is_openvswitch_internal_interface", mock.Mock(return_value=False), @@ -190,7 +224,7 @@ class TestNetworkConfigFromOpcImds: # We test this by using in a non-dict to ensure that no dict # operations are used; failure would be seen as exceptions oracle_ds._network_config = object() - oracle_ds._add_network_config_from_opc_imds() + oracle_ds._add_network_config_from_opc_imds(set_primary=False) def test_bare_metal_machine_skipped(self, oracle_ds, caplog): # nicIndex in the first entry indicates a bare metal machine @@ -198,40 +232,47 @@ class TestNetworkConfigFromOpcImds: # We test this by using a non-dict to ensure that no dict # operations are used oracle_ds._network_config = object() - oracle_ds._add_network_config_from_opc_imds() + oracle_ds._add_network_config_from_opc_imds(set_primary=False) assert "bare metal machine" in caplog.text - def test_missing_mac_skipped(self, oracle_ds, caplog): - oracle_ds._vnics_data = json.loads(OPC_VM_SECONDARY_VNIC_RESPONSE) - - oracle_ds._network_config = { - "version": 1, - "config": [{"primary": "nic"}], - } - with mock.patch(DS_PATH + ".get_interfaces_by_mac", return_value={}): - oracle_ds._add_network_config_from_opc_imds() - - assert 1 == len(oracle_ds.network_config["config"]) - assert ( - "Interface with MAC 00:00:17:02:2b:b1 not found; skipping" - in caplog.text - ) - - def test_missing_mac_skipped_v2(self, oracle_ds, caplog): + @pytest.mark.parametrize( + "network_config, network_config_key", + [ + pytest.param( + { + "version": 1, + "config": [{"primary": "nic"}], + }, + "config", + id="v1", + ), + pytest.param( + { + "version": 2, + "ethernets": {"primary": {"nic": {}}}, + }, + "ethernets", + id="v2", + ), + ], + ) + def test_missing_mac_skipped( + self, + oracle_ds, + network_config, + network_config_key, + caplog, + ): oracle_ds._vnics_data = json.loads(OPC_VM_SECONDARY_VNIC_RESPONSE) - - oracle_ds._network_config = { - "version": 2, - "ethernets": {"primary": {"nic": {}}}, - } + oracle_ds._network_config = network_config with mock.patch(DS_PATH + ".get_interfaces_by_mac", return_value={}): - oracle_ds._add_network_config_from_opc_imds() + oracle_ds._add_network_config_from_opc_imds(set_primary=False) - assert 1 == len(oracle_ds.network_config["ethernets"]) + assert 1 == len(oracle_ds._network_config[network_config_key]) assert ( - "Interface with MAC 00:00:17:02:2b:b1 not found; skipping" - in caplog.text + f"Interface with MAC {MAC_ADDR} not found; skipping" in caplog.text ) + assert 1 == caplog.text.count(" not found; skipping") def test_secondary_nic(self, oracle_ds): oracle_ds._vnics_data = json.loads(OPC_VM_SECONDARY_VNIC_RESPONSE) @@ -239,12 +280,12 @@ class TestNetworkConfigFromOpcImds: "version": 1, "config": [{"primary": "nic"}], } - mac_addr, nic_name = "00:00:17:02:2b:b1", "ens3" + mac_addr, nic_name = MAC_ADDR, "ens3" with mock.patch( DS_PATH + ".get_interfaces_by_mac", return_value={mac_addr: nic_name}, ): - oracle_ds._add_network_config_from_opc_imds() + oracle_ds._add_network_config_from_opc_imds(set_primary=False) # The input is mutated assert 2 == len(oracle_ds.network_config["config"]) @@ -266,12 +307,12 @@ class TestNetworkConfigFromOpcImds: "version": 2, "ethernets": {"primary": {"nic": {}}}, } - mac_addr, nic_name = "00:00:17:02:2b:b1", "ens3" + mac_addr, nic_name = MAC_ADDR, "ens3" with mock.patch( DS_PATH + ".get_interfaces_by_mac", return_value={mac_addr: nic_name}, ): - oracle_ds._add_network_config_from_opc_imds() + oracle_ds._add_network_config_from_opc_imds(set_primary=False) # The input is mutated assert 2 == len(oracle_ds.network_config["ethernets"]) @@ -286,77 +327,180 @@ class TestNetworkConfigFromOpcImds: # These values are hard-coded in OPC_VM_SECONDARY_VNIC_RESPONSE assert "10.0.0.231" == secondary_nic_cfg["addresses"][0] + @pytest.mark.parametrize("error_add_network", [None, Exception]) + @pytest.mark.parametrize( + "configure_secondary_nics", + [False, True], + ) + @mock.patch(DS_PATH + "._ensure_netfailover_safe") + def test_network_config_log_errors( + self, + m_ensure_netfailover_safe, + configure_secondary_nics, + error_add_network, + oracle_ds, + caplog, + capsys, + ): + assert not oracle_ds._has_network_config() + oracle_ds.ds_cfg["configure_secondary_nics"] = configure_secondary_nics + with mock.patch.object( + oracle.DataSourceOracle, + "_add_network_config_from_opc_imds", + ) as m_add_network_config_from_opc_imds: + if error_add_network: + m_add_network_config_from_opc_imds.side_effect = ( + error_add_network + ) + oracle_ds.network_config # pylint: disable=pointless-statement # noqa: E501 + assert [ + mock.call(True, False) + == m_add_network_config_from_opc_imds.call_args_list + ] + assert 1 == oracle_ds._is_iscsi_root.call_count + assert 1 == m_ensure_netfailover_safe.call_count + + assert ("", "") == capsys.readouterr() + if not error_add_network: + log_initramfs_index = -1 + else: + log_initramfs_index = -3 + # Primary + assert ( + logging.WARNING, + "Failed to parse IMDS network configuration!", + ) == caplog.record_tuples[-2][1:] + # Secondary + assert ( + logging.DEBUG, + "Failed to parse IMDS network configuration!", + ) == caplog.record_tuples[-1][1:] -class TestNetworkConfigFiltersNetFailover(test_helpers.CiTestCase): - def setUp(self): - super(TestNetworkConfigFiltersNetFailover, self).setUp() - self.add_patch( - DS_PATH + ".get_interfaces_by_mac", "m_get_interfaces_by_mac" - ) - self.add_patch(DS_PATH + ".is_netfail_master", "m_netfail_master") + assert ( + logging.WARNING, + "Could not obtain network configuration from initramfs." + " Falling back to IMDS.", + ) == caplog.record_tuples[log_initramfs_index][1:] - def test_ignore_bogus_network_config(self): - netcfg = {"something": "here"} - passed_netcfg = copy.copy(netcfg) - oracle._ensure_netfailover_safe(passed_netcfg) - self.assertEqual(netcfg, passed_netcfg) - def test_ignore_network_config_unknown_versions(self): - netcfg = {"something": "here", "version": 3} +@mock.patch(DS_PATH + ".get_interfaces_by_mac") +@mock.patch(DS_PATH + ".is_netfail_master") +class TestNetworkConfigFiltersNetFailover: + @pytest.mark.parametrize( + "netcfg", + [ + pytest.param({"something": "here"}, id="bogus"), + pytest.param( + {"something": "here", "version": 3}, id="unknown_version" + ), + ], + ) + def test_ignore_network_config( + self, m_netfail_master, m_get_interfaces_by_mac, netcfg + ): passed_netcfg = copy.copy(netcfg) oracle._ensure_netfailover_safe(passed_netcfg) - self.assertEqual(netcfg, passed_netcfg) + assert netcfg == passed_netcfg - def test_checks_v1_type_physical_interfaces(self): - mac_addr, nic_name = "00:00:17:02:2b:b1", "ens3" - self.m_get_interfaces_by_mac.return_value = { - mac_addr: nic_name, - } - netcfg = { - "version": 1, - "config": [ + @pytest.mark.parametrize( + "nic_name, netcfg, netfail_master_return, call_args_list", + [ + pytest.param( + "ens3", { - "type": "physical", - "name": nic_name, - "mac_address": mac_addr, - "subnets": [{"type": "dhcp4"}], - } - ], - } - passed_netcfg = copy.copy(netcfg) - self.m_netfail_master.return_value = False - oracle._ensure_netfailover_safe(passed_netcfg) - self.assertEqual(netcfg, passed_netcfg) - self.assertEqual( - [mock.call(nic_name)], self.m_netfail_master.call_args_list - ) - - def test_checks_v1_skips_non_phys_interfaces(self): - mac_addr, nic_name = "00:00:17:02:2b:b1", "bond0" - self.m_get_interfaces_by_mac.return_value = { - mac_addr: nic_name, - } - netcfg = { - "version": 1, - "config": [ + "version": 1, + "config": [ + { + "type": "physical", + "name": "ens3", + "mac_address": MAC_ADDR, + "subnets": [{"type": "dhcp4"}], + } + ], + }, + False, + [mock.call("ens3")], + id="checks_v1_type_physical_interfaces", + ), + pytest.param( + "bond0", { - "type": "bond", - "name": nic_name, - "mac_address": mac_addr, - "subnets": [{"type": "dhcp4"}], - } - ], + "version": 1, + "config": [ + { + "type": "bond", + "name": "bond0", + "mac_address": MAC_ADDR, + "subnets": [{"type": "dhcp4"}], + } + ], + }, + None, + [], + id="skips_v1_non_phys_interfaces", + ), + pytest.param( + "ens3", + { + "version": 2, + "ethernets": { + "ens3": { + "dhcp4": True, + "critical": True, + "set-name": "ens3", + "match": {"macaddress": MAC_ADDR}, + } + }, + }, + False, + [mock.call("ens3")], + id="checks_v2_type_ethernet_interfaces", + ), + pytest.param( + "wlps0", + { + "version": 2, + "ethernets": { + "wlps0": { + "dhcp4": True, + "critical": True, + "set-name": "wlps0", + "match": {"macaddress": MAC_ADDR}, + } + }, + }, + None, + [mock.call("wlps0")], + id="skips_v2_non_ethernet_interfaces", + ), + ], + ) + def test__ensure_netfailover_safe( + self, + m_netfail_master, + m_get_interfaces_by_mac, + nic_name, + netcfg, + netfail_master_return, + call_args_list, + ): + m_get_interfaces_by_mac.return_value = { + MAC_ADDR: nic_name, } passed_netcfg = copy.copy(netcfg) + if netfail_master_return is not None: + m_netfail_master.return_value = netfail_master_return oracle._ensure_netfailover_safe(passed_netcfg) - self.assertEqual(netcfg, passed_netcfg) - self.assertEqual(0, self.m_netfail_master.call_count) - - def test_removes_master_mac_property_v1(self): - nic_master, mac_master = "ens3", self.random_string() - nic_other, mac_other = "ens7", self.random_string() - nic_extra, mac_extra = "enp0s1f2", self.random_string() - self.m_get_interfaces_by_mac.return_value = { + assert netcfg == passed_netcfg + assert call_args_list == m_netfail_master.call_args_list + + def test_removes_master_mac_property_v1( + self, m_netfail_master, m_get_interfaces_by_mac + ): + nic_master, mac_master = "ens3", test_helpers.random_string() + nic_other, mac_other = "ens7", test_helpers.random_string() + nic_extra, mac_extra = "enp0s1f2", test_helpers.random_string() + m_get_interfaces_by_mac.return_value = { mac_master: nic_master, mac_other: nic_other, mac_extra: nic_extra, @@ -387,7 +531,7 @@ class TestNetworkConfigFiltersNetFailover(test_helpers.CiTestCase): return True return False - self.m_netfail_master.side_effect = _is_netfail_master + m_netfail_master.side_effect = _is_netfail_master expected_cfg = { "version": 1, "config": [ @@ -405,58 +549,15 @@ class TestNetworkConfigFiltersNetFailover(test_helpers.CiTestCase): ], } oracle._ensure_netfailover_safe(netcfg) - self.assertEqual(expected_cfg, netcfg) - - def test_checks_v2_type_ethernet_interfaces(self): - mac_addr, nic_name = "00:00:17:02:2b:b1", "ens3" - self.m_get_interfaces_by_mac.return_value = { - mac_addr: nic_name, - } - netcfg = { - "version": 2, - "ethernets": { - nic_name: { - "dhcp4": True, - "critical": True, - "set-name": nic_name, - "match": {"macaddress": mac_addr}, - } - }, - } - passed_netcfg = copy.copy(netcfg) - self.m_netfail_master.return_value = False - oracle._ensure_netfailover_safe(passed_netcfg) - self.assertEqual(netcfg, passed_netcfg) - self.assertEqual( - [mock.call(nic_name)], self.m_netfail_master.call_args_list - ) + assert expected_cfg == netcfg - def test_skips_v2_non_ethernet_interfaces(self): - mac_addr, nic_name = "00:00:17:02:2b:b1", "wlps0" - self.m_get_interfaces_by_mac.return_value = { - mac_addr: nic_name, - } - netcfg = { - "version": 2, - "wifis": { - nic_name: { - "dhcp4": True, - "critical": True, - "set-name": nic_name, - "match": {"macaddress": mac_addr}, - } - }, - } - passed_netcfg = copy.copy(netcfg) - oracle._ensure_netfailover_safe(passed_netcfg) - self.assertEqual(netcfg, passed_netcfg) - self.assertEqual(0, self.m_netfail_master.call_count) - - def test_removes_master_mac_property_v2(self): - nic_master, mac_master = "ens3", self.random_string() - nic_other, mac_other = "ens7", self.random_string() - nic_extra, mac_extra = "enp0s1f2", self.random_string() - self.m_get_interfaces_by_mac.return_value = { + def test_removes_master_mac_property_v2( + self, m_netfail_master, m_get_interfaces_by_mac + ): + nic_master, mac_master = "ens3", test_helpers.random_string() + nic_other, mac_other = "ens7", test_helpers.random_string() + nic_extra, mac_extra = "enp0s1f2", test_helpers.random_string() + m_get_interfaces_by_mac.return_value = { mac_master: nic_master, mac_other: nic_other, mac_extra: nic_extra, @@ -487,7 +588,7 @@ class TestNetworkConfigFiltersNetFailover(test_helpers.CiTestCase): return True return False - self.m_netfail_master.side_effect = _is_netfail_master + m_netfail_master.side_effect = _is_netfail_master expected_cfg = { "version": 2, @@ -511,7 +612,7 @@ class TestNetworkConfigFiltersNetFailover(test_helpers.CiTestCase): pprint.pprint(netcfg) print("---- ^^ modified ^^ ---- vv original vv ----") pprint.pprint(expected_cfg) - self.assertEqual(expected_cfg, netcfg) + assert expected_cfg == netcfg def _mock_v2_urls(httpretty): @@ -557,7 +658,6 @@ def _mock_no_v2_urls(httpretty): class TestReadOpcMetadata: # See https://docs.pytest.org/en/stable/example # /parametrize.html#parametrizing-conditional-raising - does_not_raise = ExitStack @mock.patch("cloudinit.url_helper.time.sleep", lambda _: None) @pytest.mark.parametrize( @@ -636,7 +736,29 @@ class TestReadOpcMetadata: with expectation: assert expected_body == oracle.read_opc_metadata().instance_data + # No need to actually wait between retries in the tests + @mock.patch("cloudinit.url_helper.time.sleep", lambda _: None) + def test_fetch_vnics_error(self, caplog): + def mocked_fetch(*args, path="instance", **kwargs): + if path == "vnics": + raise UrlError("cause") + + with mock.patch(DS_PATH + "._fetch", side_effect=mocked_fetch): + opc_metadata = oracle.read_opc_metadata(fetch_vnics_data=True) + assert None is opc_metadata.vnics_data + assert ( + logging.WARNING, + "Failed to fetch IMDS network configuration!", + ) == caplog.record_tuples[-2][1:] + +@pytest.mark.parametrize( + "", + [ + pytest.param(marks=pytest.mark.is_iscsi(True), id="iscsi"), + pytest.param(marks=pytest.mark.is_iscsi(False), id="non-iscsi"), + ], +) class TestCommon_GetDataBehaviour: """This test class tests behaviour common to iSCSI and non-iSCSI root. @@ -649,33 +771,14 @@ class TestCommon_GetDataBehaviour: separate class for that case.) """ - @pytest.fixture(params=[True, False]) - def parameterized_oracle_ds(self, request, oracle_ds): - """oracle_ds parameterized for iSCSI and non-iSCSI root respectively""" - is_iscsi_root = request.param - with ExitStack() as stack: - stack.enter_context( - mock.patch( - DS_PATH + "._is_iscsi_root", return_value=is_iscsi_root - ) - ) - if not is_iscsi_root: - stack.enter_context( - mock.patch(DS_PATH + ".net.find_fallback_nic") - ) - stack.enter_context( - mock.patch(DS_PATH + ".dhcp.EphemeralDHCPv4") - ) - yield oracle_ds - @mock.patch( DS_PATH + "._is_platform_viable", mock.Mock(return_value=False) ) def test_false_if_platform_not_viable( self, - parameterized_oracle_ds, + oracle_ds, ): - assert not parameterized_oracle_ds._get_data() + assert not oracle_ds._get_data() @pytest.mark.parametrize( "keyname,expected_value", @@ -699,10 +802,10 @@ class TestCommon_GetDataBehaviour: self, keyname, expected_value, - parameterized_oracle_ds, + oracle_ds, ): - assert parameterized_oracle_ds._get_data() - assert expected_value == parameterized_oracle_ds.metadata[keyname] + assert oracle_ds._get_data() + assert expected_value == oracle_ds.metadata[keyname] @pytest.mark.parametrize( "attribute_name,expected_value", @@ -722,12 +825,10 @@ class TestCommon_GetDataBehaviour: self, attribute_name, expected_value, - parameterized_oracle_ds, + oracle_ds, ): - assert parameterized_oracle_ds._get_data() - assert expected_value == getattr( - parameterized_oracle_ds, attribute_name - ) + assert oracle_ds._get_data() + assert expected_value == getattr(oracle_ds, attribute_name) @pytest.mark.parametrize( "ssh_keys,expected_value", @@ -746,7 +847,7 @@ class TestCommon_GetDataBehaviour: ], ) def test_public_keys_handled_correctly( - self, ssh_keys, expected_value, parameterized_oracle_ds + self, ssh_keys, expected_value, oracle_ds ): instance_data = json.loads(OPC_V1_METADATA) if ssh_keys is None: @@ -758,14 +859,10 @@ class TestCommon_GetDataBehaviour: DS_PATH + ".read_opc_metadata", mock.Mock(return_value=metadata), ): - assert parameterized_oracle_ds._get_data() - assert ( - expected_value == parameterized_oracle_ds.get_public_ssh_keys() - ) + assert oracle_ds._get_data() + assert expected_value == oracle_ds.get_public_ssh_keys() - def test_missing_user_data_handled_gracefully( - self, parameterized_oracle_ds - ): + def test_missing_user_data_handled_gracefully(self, oracle_ds): instance_data = json.loads(OPC_V1_METADATA) del instance_data["metadata"]["user_data"] metadata = OpcMetadata(None, instance_data, None) @@ -773,13 +870,11 @@ class TestCommon_GetDataBehaviour: DS_PATH + ".read_opc_metadata", mock.Mock(return_value=metadata), ): - assert parameterized_oracle_ds._get_data() + assert oracle_ds._get_data() - assert parameterized_oracle_ds.userdata_raw is None + assert oracle_ds.userdata_raw is None - def test_missing_metadata_handled_gracefully( - self, parameterized_oracle_ds - ): + def test_missing_metadata_handled_gracefully(self, oracle_ds): instance_data = json.loads(OPC_V1_METADATA) del instance_data["metadata"] metadata = OpcMetadata(None, instance_data, None) @@ -787,17 +882,17 @@ class TestCommon_GetDataBehaviour: DS_PATH + ".read_opc_metadata", mock.Mock(return_value=metadata), ): - assert parameterized_oracle_ds._get_data() + assert oracle_ds._get_data() - assert parameterized_oracle_ds.userdata_raw is None - assert [] == parameterized_oracle_ds.get_public_ssh_keys() + assert oracle_ds.userdata_raw is None + assert [] == oracle_ds.get_public_ssh_keys() -@mock.patch(DS_PATH + "._is_iscsi_root", lambda: False) +@pytest.mark.is_iscsi(False) class TestNonIscsiRoot_GetDataBehaviour: - @mock.patch(DS_PATH + ".dhcp.EphemeralDHCPv4") + @mock.patch(DS_PATH + ".ephemeral.EphemeralDHCPv4") @mock.patch(DS_PATH + ".net.find_fallback_nic") - def test_read_opc_metadata_called_with_ephemeral_dhcp( + def test_run_net_files( self, m_find_fallback_nic, m_EphemeralDHCPv4, oracle_ds ): in_context_manager = False @@ -837,74 +932,122 @@ class TestNonIscsiRoot_GetDataBehaviour: ) ] == m_EphemeralDHCPv4.call_args_list + @mock.patch(DS_PATH + ".ephemeral.EphemeralDHCPv4") + @mock.patch(DS_PATH + ".net.find_fallback_nic") + def test_read_opc_metadata_called_with_ephemeral_dhcp( + self, m_find_fallback_nic, m_EphemeralDHCPv4, oracle_ds + ): + in_context_manager = False -@mock.patch(DS_PATH + ".get_interfaces_by_mac", lambda: {}) -@mock.patch(DS_PATH + ".cmdline.read_initramfs_config") -class TestNetworkConfig: - def test_network_config_cached(self, m_read_initramfs_config, oracle_ds): - """.network_config should be cached""" - assert 0 == m_read_initramfs_config.call_count - oracle_ds.network_config # pylint: disable=pointless-statement - assert 1 == m_read_initramfs_config.call_count - oracle_ds.network_config # pylint: disable=pointless-statement - assert 1 == m_read_initramfs_config.call_count + def enter_context_manager(): + nonlocal in_context_manager + in_context_manager = True - def test_network_cmdline(self, m_read_initramfs_config, oracle_ds): - """network_config should prefer initramfs config over fallback""" - ncfg = {"version": 1, "config": [{"a": "b"}]} - m_read_initramfs_config.return_value = copy.deepcopy(ncfg) + def exit_context_manager(*args): + nonlocal in_context_manager + in_context_manager = False - assert ncfg == oracle_ds.network_config - assert 0 == oracle_ds.distro.generate_fallback_config.call_count + m_EphemeralDHCPv4.return_value.__enter__.side_effect = ( + enter_context_manager + ) + m_EphemeralDHCPv4.return_value.__exit__.side_effect = ( + exit_context_manager + ) - def test_network_fallback(self, m_read_initramfs_config, oracle_ds): - """network_config should prefer initramfs config over fallback""" - ncfg = {"version": 1, "config": [{"a": "b"}]} + def assert_in_context_manager(**kwargs): + assert in_context_manager + return mock.MagicMock() - m_read_initramfs_config.return_value = None - oracle_ds.distro.generate_fallback_config.return_value = copy.deepcopy( - ncfg - ) + with mock.patch( + DS_PATH + ".read_opc_metadata", + mock.Mock(side_effect=assert_in_context_manager), + ): + assert oracle_ds._get_data() + + assert [ + mock.call( + iface=m_find_fallback_nic.return_value, + connectivity_url_data={ + "headers": {"Authorization": "Bearer Oracle"}, + "url": "http://169.254.169.254/opc/v2/instance/", + }, + ) + ] == m_EphemeralDHCPv4.call_args_list - assert ncfg == oracle_ds.network_config + +@mock.patch(DS_PATH + ".get_interfaces_by_mac", return_value={}) +class TestNetworkConfig: + def test_network_config_cached(self, m_get_interfaces_by_mac, oracle_ds): + """.network_config should be cached""" + assert 0 == oracle_ds._get_iscsi_config.call_count + oracle_ds.network_config # pylint: disable=pointless-statement + assert 1 == oracle_ds._get_iscsi_config.call_count + oracle_ds.network_config # pylint: disable=pointless-statement + assert 1 == oracle_ds._get_iscsi_config.call_count @pytest.mark.parametrize( - "configure_secondary_nics,expect_secondary_nics", - [(True, True), (False, False), (None, False)], + "configure_secondary_nics,is_iscsi,expected_set_primary", + [ + pytest.param( + True, + True, + [mock.call(False)], + marks=pytest.mark.is_iscsi(True), + ), + pytest.param( + True, + False, + [mock.call(True)], + marks=pytest.mark.is_iscsi(False), + ), + pytest.param(False, True, [], marks=pytest.mark.is_iscsi(True)), + pytest.param( + False, + False, + [mock.call(True)], + marks=pytest.mark.is_iscsi(False), + ), + pytest.param(None, True, [], marks=pytest.mark.is_iscsi(True)), + pytest.param( + None, + False, + [mock.call(True)], + marks=pytest.mark.is_iscsi(False), + ), + ], ) def test_secondary_nic_addition( self, - m_read_initramfs_config, + m_get_interfaces_by_mac, configure_secondary_nics, - expect_secondary_nics, + is_iscsi, + expected_set_primary, oracle_ds, ): """Test that _add_network_config_from_opc_imds is called as expected (configure_secondary_nics=None is used to test the default behaviour.) """ - m_read_initramfs_config.return_value = {"version": 1, "config": []} if configure_secondary_nics is not None: oracle_ds.ds_cfg[ "configure_secondary_nics" ] = configure_secondary_nics - def side_effect(self): - self._network_config["secondary_added"] = mock.sentinel.needle - oracle_ds._vnics_data = "DummyData" with mock.patch.object( - oracle.DataSourceOracle, + oracle_ds, "_add_network_config_from_opc_imds", - new=side_effect, - ): - was_secondary_added = "secondary_added" in oracle_ds.network_config - assert expect_secondary_nics == was_secondary_added + ) as m_add_network_config_from_opc_imds: + oracle_ds.network_config # pylint: disable=pointless-statement + assert ( + expected_set_primary + == m_add_network_config_from_opc_imds.call_args_list + ) def test_secondary_nic_failure_isnt_blocking( self, - m_read_initramfs_config, + m_get_interfaces_by_mac, caplog, oracle_ds, ): @@ -917,15 +1060,88 @@ class TestNetworkConfig: side_effect=Exception(), ): network_config = oracle_ds.network_config - assert network_config == m_read_initramfs_config.return_value - assert "Failed to parse secondary network configuration" in caplog.text + assert network_config == oracle_ds._get_iscsi_config.return_value + assert 2 == caplog.text.count( + "Failed to parse IMDS network configuration" + ) - def test_ds_network_cfg_preferred_over_initramfs(self, _m): + def test_ds_network_cfg_preferred_over_initramfs( + self, m_get_interfaces_by_mac + ): """Ensure that DS net config is preferred over initramfs config""" config_sources = oracle.DataSourceOracle.network_config_sources ds_idx = config_sources.index(NetworkConfigSource.DS) initramfs_idx = config_sources.index(NetworkConfigSource.INITRAMFS) assert ds_idx < initramfs_idx + @pytest.mark.parametrize("set_primary", [True, False]) + def test__add_network_config_from_opc_imds_no_vnics_data( + self, + m_get_interfaces_by_mac, + set_primary, + oracle_ds, + caplog, + ): + assert not oracle_ds._has_network_config() + with mock.patch.object(oracle_ds, "_vnics_data", None): + oracle_ds._add_network_config_from_opc_imds(set_primary) + assert not oracle_ds._has_network_config() + assert ( + logging.WARNING, + "NIC data is UNSET but should not be", + ) == caplog.record_tuples[-1][1:] + + def test_missing_mac_skipped( + self, + m_get_interfaces_by_mac, + oracle_ds, + caplog, + ): + """If no intefaces by mac found, then _network_config not setted and + correct logs. + """ + vnics_data = json.loads(OPC_VM_SECONDARY_VNIC_RESPONSE) + assert not oracle_ds._has_network_config() + with mock.patch.object(oracle_ds, "_vnics_data", vnics_data): + oracle_ds._add_network_config_from_opc_imds(set_primary=True) + assert not oracle_ds._has_network_config() + assert ( + logging.WARNING, + "Interface with MAC 02:00:17:05:d1:db not found; skipping", + ) == caplog.record_tuples[-2][1:] + assert ( + logging.WARNING, + f"Interface with MAC {MAC_ADDR} not found; skipping", + ) == caplog.record_tuples[-1][1:] + + @pytest.mark.parametrize("set_primary", [True, False]) + def test_nics( + self, + m_get_interfaces_by_mac, + set_primary, + oracle_ds, + caplog, + mocker, + ): + """Correct number of configs added""" + vnics_data = json.loads(OPC_VM_SECONDARY_VNIC_RESPONSE) + if set_primary: + assert not oracle_ds._has_network_config() + else: + # Simulate primary config was taken from iscsi + oracle_ds._network_config = copy.deepcopy(KLIBC_NET_CFG) + + mocker.patch( + DS_PATH + ".get_interfaces_by_mac", + return_value={"02:00:17:05:d1:db": "eth_0", MAC_ADDR: "name_1"}, + ) + mocker.patch.object(oracle_ds, "_vnics_data", vnics_data) + + oracle_ds._add_network_config_from_opc_imds(set_primary) + assert 2 == len( + oracle_ds._network_config["config"] + ), "Config not added" + assert "" == caplog.text + # vi: ts=4 expandtab diff --git a/tests/unittests/sources/test_ovf.py b/tests/unittests/sources/test_ovf.py index c2c87f12..1fbd564f 100644 --- a/tests/unittests/sources/test_ovf.py +++ b/tests/unittests/sources/test_ovf.py @@ -13,6 +13,7 @@ from cloudinit import subp, util from cloudinit.helpers import Paths from cloudinit.safeyaml import YAMLError from cloudinit.sources import DataSourceOVF as dsovf +from cloudinit.sources.DataSourceOVF import GuestCustScriptDisabled from cloudinit.sources.helpers.vmware.imc.config_custom_script import ( CustomScriptNotFound, ) @@ -447,7 +448,7 @@ class TestDatasourceOVF(CiTestCase): with mock.patch( MPATH + "set_customization_status", return_value=("msg", b"") ): - with self.assertRaises(RuntimeError) as context: + with self.assertRaises(GuestCustScriptDisabled) as context: wrap_and_call( "cloudinit.sources.DataSourceOVF", { diff --git a/tests/unittests/sources/test_scaleway.py b/tests/unittests/sources/test_scaleway.py index 52bcbc17..64c785d6 100644 --- a/tests/unittests/sources/test_scaleway.py +++ b/tests/unittests/sources/test_scaleway.py @@ -236,7 +236,7 @@ class TestDataSourceScaleway(HttprettyTestCase): ].sort(), ) self.assertEqual( - self.datasource.get_hostname(), + self.datasource.get_hostname().hostname, MetadataResponses.FAKE_METADATA["hostname"], ) self.assertEqual( diff --git a/tests/unittests/sources/test_smartos.py b/tests/unittests/sources/test_smartos.py index 55239c4e..702a67f7 100644 --- a/tests/unittests/sources/test_smartos.py +++ b/tests/unittests/sources/test_smartos.py @@ -23,8 +23,9 @@ import unittest import uuid from binascii import crc32 +import serial + from cloudinit import helpers as c_helpers -from cloudinit import serial from cloudinit.event import EventScope, EventType from cloudinit.sources import DataSourceSmartOS from cloudinit.sources.DataSourceSmartOS import SERIAL_DEVICE, SMARTOS_ENV_KVM @@ -44,14 +45,6 @@ from tests.unittests.helpers import ( skipIf, ) -try: - import serial as _pyserial - - assert _pyserial # avoid pyflakes error F401: import unused - HAS_PYSERIAL = True -except ImportError: - HAS_PYSERIAL = False - DSMOS = "cloudinit.sources.DataSourceSmartOS" SDC_NICS = json.loads( """ @@ -1357,7 +1350,6 @@ class TestNetworkConversion(CiTestCase): os.access(SERIAL_DEVICE, os.W_OK), "Requires write access to " + SERIAL_DEVICE, ) -@unittest.skipUnless(HAS_PYSERIAL is True, "pyserial not available") class TestSerialConcurrency(CiTestCase): """ This class tests locking on an actual serial port, and as such can only diff --git a/tests/unittests/sources/test_upcloud.py b/tests/unittests/sources/test_upcloud.py index e1125b65..317cb638 100644 --- a/tests/unittests/sources/test_upcloud.py +++ b/tests/unittests/sources/test_upcloud.py @@ -216,8 +216,8 @@ class TestUpCloudNetworkSetup(CiTestCase): @mock.patch("cloudinit.sources.helpers.upcloud.read_metadata") @mock.patch("cloudinit.net.find_fallback_nic") - @mock.patch("cloudinit.net.dhcp.maybe_perform_dhcp_discovery") - @mock.patch("cloudinit.net.dhcp.EphemeralIPv4Network") + @mock.patch("cloudinit.net.ephemeral.maybe_perform_dhcp_discovery") + @mock.patch("cloudinit.net.ephemeral.EphemeralIPv4Network") def test_network_configured_metadata( self, m_net, m_dhcp, m_fallback_nic, mock_readmd ): diff --git a/tests/unittests/sources/test_vmware.py b/tests/unittests/sources/test_vmware.py index 3579041a..37a1f259 100644 --- a/tests/unittests/sources/test_vmware.py +++ b/tests/unittests/sources/test_vmware.py @@ -418,7 +418,9 @@ class TestDataSourceVMwareGuestInfo_InvalidPlatform(FilesystemMockingTestCase): def assert_metadata(test_obj, ds, metadata): test_obj.assertEqual(metadata.get("instance-id"), ds.get_instance_id()) - test_obj.assertEqual(metadata.get("local-hostname"), ds.get_hostname()) + test_obj.assertEqual( + metadata.get("local-hostname"), ds.get_hostname().hostname + ) expected_public_keys = metadata.get("public_keys") if not isinstance(expected_public_keys, list): diff --git a/tests/unittests/sources/test_vultr.py b/tests/unittests/sources/test_vultr.py index c8398579..5f2ccd4a 100644 --- a/tests/unittests/sources/test_vultr.py +++ b/tests/unittests/sources/test_vultr.py @@ -344,9 +344,15 @@ class TestDataSourceVultr(CiTestCase): return # Test interface seeking to ensure we are able to find the correct one - @mock.patch("cloudinit.net.dhcp.EphemeralDHCPv4.__init__", ephemeral_init) - @mock.patch("cloudinit.net.dhcp.EphemeralDHCPv4.__enter__", override_enter) - @mock.patch("cloudinit.net.dhcp.EphemeralDHCPv4.__exit__", override_exit) + @mock.patch( + "cloudinit.net.ephemeral.EphemeralDHCPv4.__init__", ephemeral_init + ) + @mock.patch( + "cloudinit.net.ephemeral.EphemeralDHCPv4.__enter__", override_enter + ) + @mock.patch( + "cloudinit.net.ephemeral.EphemeralDHCPv4.__exit__", override_exit + ) @mock.patch("cloudinit.sources.helpers.vultr.check_route") @mock.patch("cloudinit.sources.helpers.vultr.is_vultr") @mock.patch("cloudinit.sources.helpers.vultr.read_metadata") @@ -377,10 +383,15 @@ class TestDataSourceVultr(CiTestCase): # Test route checking sucessful DHCPs @mock.patch("cloudinit.sources.helpers.vultr.check_route", check_route) @mock.patch( - "cloudinit.net.dhcp.EphemeralDHCPv4.__init__", ephemeral_init_always + "cloudinit.net.ephemeral.EphemeralDHCPv4.__init__", + ephemeral_init_always, + ) + @mock.patch( + "cloudinit.net.ephemeral.EphemeralDHCPv4.__enter__", override_enter + ) + @mock.patch( + "cloudinit.net.ephemeral.EphemeralDHCPv4.__exit__", override_exit ) - @mock.patch("cloudinit.net.dhcp.EphemeralDHCPv4.__enter__", override_enter) - @mock.patch("cloudinit.net.dhcp.EphemeralDHCPv4.__exit__", override_exit) @mock.patch("cloudinit.sources.helpers.vultr.get_interface_list") @mock.patch("cloudinit.sources.helpers.vultr.is_vultr") @mock.patch("cloudinit.sources.helpers.vultr.read_metadata") |