Reference:fa53c7f4089c7502a8012e17a0d6269f0efc474ebb414c78669cbd94dd576d817e94be0450a1faff
195 lines
8.0 KiB
Diff
195 lines
8.0 KiB
Diff
From 19300c5cc6161949e28026876e9fe407d647b2c0 Mon Sep 17 00:00:00 2001
|
|
From: Chris Patterson <cpatterson@microsoft.com>
|
|
Date: Fri, 4 Feb 2022 14:17:38 -0500
|
|
Subject: [PATCH 1/8] sources/azure: fix metadata check in
|
|
_check_if_nic_is_primary() (#1232)
|
|
|
|
Reference:https://github.com/canonical/cloud-init/commit/6d817e94beb404d3917bf973bcb728aa6cc22ffe
|
|
Conflict:change tests/unittests/test_datasource/test_azure.py not tests/unittests/sources/test_azure.py
|
|
|
|
Currently _check_if_nic_is_primary() checks for imds_md is None,
|
|
but imds_md is returned as an empty dictionary on error fetching
|
|
metdata.
|
|
|
|
Fix this check and the tests that are incorrectly vetting IMDS
|
|
polling code.
|
|
|
|
Additionally, use response.contents instead of str(response) when
|
|
loding the JSON data returned from readurl. This helps simplify the
|
|
mocking and avoids an unncessary conversion.
|
|
|
|
Signed-off-by: Chris Patterson <cpatterson@microsoft.com>
|
|
---
|
|
cloudinit/sources/DataSourceAzure.py | 6 +-
|
|
tests/unittests/test_datasource/test_azure.py | 82 ++++++-------------
|
|
2 files changed, 27 insertions(+), 61 deletions(-)
|
|
|
|
diff --git a/cloudinit/sources/DataSourceAzure.py b/cloudinit/sources/DataSourceAzure.py
|
|
index 93493fa..f1e6642 100755
|
|
--- a/cloudinit/sources/DataSourceAzure.py
|
|
+++ b/cloudinit/sources/DataSourceAzure.py
|
|
@@ -1078,10 +1078,10 @@ class DataSourceAzure(sources.DataSource):
|
|
"primary nic.", ifname, e)
|
|
finally:
|
|
# If we are not the primary nic, then clean the dhcp context.
|
|
- if imds_md is None:
|
|
+ if not imds_md:
|
|
dhcp_ctx.clean_network()
|
|
|
|
- if imds_md is not None:
|
|
+ if imds_md:
|
|
# Only primary NIC will get a response from IMDS.
|
|
LOG.info("%s is the primary nic", ifname)
|
|
is_primary = True
|
|
@@ -2337,7 +2337,7 @@ def _get_metadata_from_imds(
|
|
json_decode_error = ValueError
|
|
|
|
try:
|
|
- return util.load_json(str(response))
|
|
+ return util.load_json(response.contents)
|
|
except json_decode_error as e:
|
|
report_diagnostic_event(
|
|
'Ignoring non-json IMDS instance metadata response: %s. '
|
|
diff --git a/tests/unittests/test_datasource/test_azure.py b/tests/unittests/test_datasource/test_azure.py
|
|
index cbc9665..62e657b 100644
|
|
--- a/tests/unittests/test_datasource/test_azure.py
|
|
+++ b/tests/unittests/test_datasource/test_azure.py
|
|
@@ -2853,16 +2853,6 @@ class TestPreprovisioningHotAttachNics(CiTestCase):
|
|
'interface': 'eth9', 'fixed-address': '192.168.2.9',
|
|
'routers': '192.168.2.1', 'subnet-mask': '255.255.255.0',
|
|
'unknown-245': '624c3620'}
|
|
- m_attach_call_count = 0
|
|
-
|
|
- def nic_attach_ret(nl_sock, nics_found):
|
|
- nonlocal m_attach_call_count
|
|
- m_attach_call_count = m_attach_call_count + 1
|
|
- if m_attach_call_count == 1:
|
|
- return "eth0"
|
|
- elif m_attach_call_count == 2:
|
|
- return "eth1"
|
|
- raise RuntimeError("Must have found primary nic by now.")
|
|
|
|
# Simulate two NICs by adding the same one twice.
|
|
md = {
|
|
@@ -2872,17 +2862,15 @@ class TestPreprovisioningHotAttachNics(CiTestCase):
|
|
]
|
|
}
|
|
|
|
- def network_metadata_ret(ifname, retries, type, exc_cb, infinite):
|
|
- if ifname == "eth0":
|
|
- return md
|
|
- raise requests.Timeout('Fake connection timeout')
|
|
-
|
|
m_isfile.return_value = True
|
|
- m_attach.side_effect = nic_attach_ret
|
|
+ m_attach.side_effect = [
|
|
+ "eth0",
|
|
+ "eth1",
|
|
+ ]
|
|
dhcp_ctx = mock.MagicMock(lease=lease)
|
|
dhcp_ctx.obtain_lease.return_value = lease
|
|
m_dhcpv4.return_value = dhcp_ctx
|
|
- m_imds.side_effect = network_metadata_ret
|
|
+ m_imds.side_effect = [md]
|
|
m_fallback_if.return_value = None
|
|
|
|
dsa._wait_for_all_nics_ready()
|
|
@@ -2894,10 +2882,11 @@ class TestPreprovisioningHotAttachNics(CiTestCase):
|
|
self.assertEqual(1, m_imds.call_count)
|
|
self.assertEqual(2, m_link_up.call_count)
|
|
|
|
- @mock.patch(MOCKPATH + 'DataSourceAzure.get_imds_data_with_api_fallback')
|
|
+ @mock.patch("cloudinit.url_helper.time.sleep", autospec=True)
|
|
+ @mock.patch("requests.Session.request", autospec=True)
|
|
@mock.patch(MOCKPATH + 'EphemeralDHCPv4')
|
|
def test_check_if_nic_is_primary_retries_on_failures(
|
|
- self, m_dhcpv4, m_imds):
|
|
+ self, m_dhcpv4, m_request, m_sleep):
|
|
"""Retry polling for network metadata on all failures except timeout
|
|
and network unreachable errors"""
|
|
dsa = dsaz.DataSourceAzure({}, distro=None, paths=self.paths)
|
|
@@ -2906,8 +2895,6 @@ class TestPreprovisioningHotAttachNics(CiTestCase):
|
|
'routers': '192.168.2.1', 'subnet-mask': '255.255.255.0',
|
|
'unknown-245': '624c3620'}
|
|
|
|
- eth0Retries = []
|
|
- eth1Retries = []
|
|
# Simulate two NICs by adding the same one twice.
|
|
md = {
|
|
"interface": [
|
|
@@ -2916,55 +2903,34 @@ class TestPreprovisioningHotAttachNics(CiTestCase):
|
|
]
|
|
}
|
|
|
|
- def network_metadata_ret(ifname, retries, type, exc_cb, infinite):
|
|
- nonlocal eth0Retries, eth1Retries
|
|
-
|
|
- # Simulate readurl functionality with retries and
|
|
- # exception callbacks so that the callback logic can be
|
|
- # validated.
|
|
- if ifname == "eth0":
|
|
- cause = requests.HTTPError()
|
|
- for _ in range(0, 15):
|
|
- error = url_helper.UrlError(cause=cause, code=410)
|
|
- eth0Retries.append(exc_cb("No goal state.", error))
|
|
- else:
|
|
- for _ in range(0, 10):
|
|
- # We are expected to retry for a certain period for both
|
|
- # timeout errors and network unreachable errors.
|
|
- if _ < 5:
|
|
- cause = requests.Timeout('Fake connection timeout')
|
|
- else:
|
|
- cause = requests.ConnectionError('Network Unreachable')
|
|
- error = url_helper.UrlError(cause=cause)
|
|
- eth1Retries.append(exc_cb("Connection timeout", error))
|
|
- # Should stop retrying after 10 retries
|
|
- eth1Retries.append(exc_cb("Connection timeout", error))
|
|
- raise cause
|
|
- return md
|
|
-
|
|
- m_imds.side_effect = network_metadata_ret
|
|
-
|
|
dhcp_ctx = mock.MagicMock(lease=lease)
|
|
dhcp_ctx.obtain_lease.return_value = lease
|
|
m_dhcpv4.return_value = dhcp_ctx
|
|
|
|
+ m_req = mock.Mock(content=json.dumps(md))
|
|
+ m_request.side_effect = [
|
|
+ requests.Timeout("Fake connection timeout"),
|
|
+ requests.ConnectionError("Fake Network Unreachable"),
|
|
+ m_req,
|
|
+ ]
|
|
+
|
|
is_primary, expected_nic_count = dsa._check_if_nic_is_primary("eth0")
|
|
self.assertEqual(True, is_primary)
|
|
self.assertEqual(2, expected_nic_count)
|
|
+ assert len(m_request.mock_calls) == 3
|
|
|
|
- # All Eth0 errors are non-timeout errors. So we should have been
|
|
- # retrying indefinitely until success.
|
|
- for i in eth0Retries:
|
|
- self.assertTrue(i)
|
|
+ # Re-run tests to verify max retries.
|
|
+ m_request.reset_mock()
|
|
+ m_request.side_effect = [
|
|
+ requests.Timeout("Fake connection timeout")
|
|
+ ] * 6 + [requests.ConnectionError("Fake Network Unreachable")] * 6
|
|
+
|
|
+ dsa = dsaz.DataSourceAzure({}, distro=None, paths=self.paths)
|
|
|
|
is_primary, expected_nic_count = dsa._check_if_nic_is_primary("eth1")
|
|
self.assertEqual(False, is_primary)
|
|
|
|
- # All Eth1 errors are timeout errors. Retry happens for a max of 10 and
|
|
- # then we should have moved on assuming it is not the primary nic.
|
|
- for i in range(0, 10):
|
|
- self.assertTrue(eth1Retries[i])
|
|
- self.assertFalse(eth1Retries[10])
|
|
+ assert len(m_request.mock_calls) == 11
|
|
|
|
@mock.patch('cloudinit.distros.networking.LinuxNetworking.try_set_link_up')
|
|
def test_wait_for_link_up_returns_if_already_up(
|
|
--
|
|
2.40.0
|
|
|