Merge ~asakkurr/cloud-init:azure_report_ready into cloud-init:master

Proposed by Aswin Rajamannar
Status: Merged
Approved by: Chad Smith
Approved revision: ccd13677c9659b493452872228f39bda666ea0b6
Merge reported by: Server Team CI bot
Merged at revision: not available
Proposed branch: ~asakkurr/cloud-init:azure_report_ready
Merge into: cloud-init:master
Diff against target: 164 lines (+74/-9)
3 files modified
cloudinit/sources/DataSourceAzure.py (+12/-3)
cloudinit/url_helper.py (+11/-6)
tests/unittests/test_datasource/test_azure.py (+51/-0)
Reviewer Review Type Date Requested Status
Server Team CI bot continuous-integration Approve
Paul Meyer (community) Approve
Review via email: mp+355805@code.launchpad.net

Commit message

azure: report ready to fabric after reprovision and reduce logging

When reusing a preprovisioned VM, report ready to Azure fabric as soon as
we get the reprovision data and the goal state so that we are not delayed
by the cloud-init stage switch, saving 2-3 seconds. Also reduce logging
when polling IMDS for reprovision data.

LP: #1799594

Description of the change

When reusing a preprovisioned VM, report ready to Azure fabric as soon as we get the reprovision data and the goal state so that we are not delayed by the cloud-init stage switch, saving 2-3 seconds. Also reduce logging when polling IMDS for reprovision data.

To post a comment you must log in.
Revision history for this message
Paul Meyer (paul-meyer) wrote :

LGTM, but if you want to shave off some more time, it might be time to stop depending on get_metadata_from_fabric to report ready, because that does a bunch more (retrieve goalstate and maybe certificates).
cloudinit.helpers.azure._report_ready(...) needs only the goalstate incarnation, container_id and instance_id. I bet we already have those from the data we read from IMDS?

Revision history for this message
Aswin Rajamannar (asakkurr) wrote :

> LGTM, but if you want to shave off some more time, it might be time to stop
> depending on get_metadata_from_fabric to report ready, because that does a
> bunch more (retrieve goalstate and maybe certificates).
> cloudinit.helpers.azure._report_ready(...) needs only the goalstate
> incarnation, container_id and instance_id. I bet we already have those from
> the data we read from IMDS?

IMDS gives us just the ovf-env.xml content. We still need to depend on the wireserver to get the goalstate info. I guess that can be done by the waagent as well, but I think it makes sense to report ready after getting the goalstate info once the vm gets reused, so that it serves as an indication that we have everything needed to proceed further.

Revision history for this message
Paul Meyer (paul-meyer) :
review: Approve
Revision history for this message
Chad Smith (chad.smith) wrote :

Thanks for this, and tuning down the noise in readurl where possible.

Revision history for this message
Server Team CI bot (server-team-bot) wrote :

PASSED: Continuous integration, rev:ccd13677c9659b493452872228f39bda666ea0b6
https://jenkins.ubuntu.com/server/job/cloud-init-ci/422/
Executed test runs:
    SUCCESS: Checkout
    SUCCESS: Unit & Style Tests
    SUCCESS: Ubuntu LTS: Build
    SUCCESS: Ubuntu LTS: Integration
    IN_PROGRESS: Declarative: Post Actions

Click here to trigger a rebuild:
https://jenkins.ubuntu.com/server/job/cloud-init-ci/422/rebuild

review: Approve (continuous-integration)

Preview Diff

[H/L] Next/Prev Comment, [J/K] Next/Prev File, [N/P] Next/Prev Hunk
1diff --git a/cloudinit/sources/DataSourceAzure.py b/cloudinit/sources/DataSourceAzure.py
2index d0358e9..8642915 100644
3--- a/cloudinit/sources/DataSourceAzure.py
4+++ b/cloudinit/sources/DataSourceAzure.py
5@@ -267,6 +267,7 @@ class DataSourceAzure(sources.DataSource):
6 dsname = 'Azure'
7 _negotiated = False
8 _metadata_imds = sources.UNSET
9+ lease_info = None
10
11 def __init__(self, sys_cfg, distro, paths):
12 sources.DataSource.__init__(self, sys_cfg, distro, paths)
13@@ -406,8 +407,10 @@ class DataSourceAzure(sources.DataSource):
14 LOG.warning("%s was not mountable", cdev)
15 continue
16
17+ should_report_ready_after_reprovision = False
18 if reprovision or self._should_reprovision(ret):
19 ret = self._reprovision()
20+ should_report_ready_after_reprovision = True
21 imds_md = get_metadata_from_imds(
22 self.fallback_interface, retries=3)
23 (md, userdata_raw, cfg, files) = ret
24@@ -434,6 +437,11 @@ class DataSourceAzure(sources.DataSource):
25 crawled_data['metadata']['random_seed'] = seed
26 crawled_data['metadata']['instance-id'] = util.read_dmi_data(
27 'system-uuid')
28+
29+ if should_report_ready_after_reprovision:
30+ LOG.info("Reporting ready to Azure after getting ReprovisionData")
31+ self._report_ready(lease=self.lease_info)
32+
33 return crawled_data
34
35 def _is_platform_viable(self):
36@@ -522,6 +530,7 @@ class DataSourceAzure(sources.DataSource):
37 while True:
38 try:
39 with EphemeralDHCPv4() as lease:
40+ self.lease_info = lease
41 if report_ready:
42 path = REPORTED_READY_MARKER_FILE
43 LOG.info(
44@@ -531,13 +540,13 @@ class DataSourceAzure(sources.DataSource):
45 self._report_ready(lease=lease)
46 report_ready = False
47 return readurl(url, timeout=1, headers=headers,
48- exception_cb=exc_cb, infinite=True).contents
49+ exception_cb=exc_cb, infinite=True,
50+ log_req_resp=False).contents
51 except UrlError:
52 pass
53
54 def _report_ready(self, lease):
55- """Tells the fabric provisioning has completed
56- before we go into our polling loop."""
57+ """Tells the fabric provisioning has completed """
58 try:
59 get_metadata_from_fabric(None, lease['unknown-245'])
60 except Exception:
61diff --git a/cloudinit/url_helper.py b/cloudinit/url_helper.py
62index 8067979..cf57dbd 100644
63--- a/cloudinit/url_helper.py
64+++ b/cloudinit/url_helper.py
65@@ -199,7 +199,7 @@ def _get_ssl_args(url, ssl_details):
66 def readurl(url, data=None, timeout=None, retries=0, sec_between=1,
67 headers=None, headers_cb=None, ssl_details=None,
68 check_status=True, allow_redirects=True, exception_cb=None,
69- session=None, infinite=False):
70+ session=None, infinite=False, log_req_resp=True):
71 url = _cleanurl(url)
72 req_args = {
73 'url': url,
74@@ -256,9 +256,11 @@ def readurl(url, data=None, timeout=None, retries=0, sec_between=1,
75 continue
76 filtered_req_args[k] = v
77 try:
78- LOG.debug("[%s/%s] open '%s' with %s configuration", i,
79- "infinite" if infinite else manual_tries, url,
80- filtered_req_args)
81+
82+ if log_req_resp:
83+ LOG.debug("[%s/%s] open '%s' with %s configuration", i,
84+ "infinite" if infinite else manual_tries, url,
85+ filtered_req_args)
86
87 if session is None:
88 session = requests.Session()
89@@ -294,8 +296,11 @@ def readurl(url, data=None, timeout=None, retries=0, sec_between=1,
90 break
91 if (infinite and sec_between > 0) or \
92 (i + 1 < manual_tries and sec_between > 0):
93- LOG.debug("Please wait %s seconds while we wait to try again",
94- sec_between)
95+
96+ if log_req_resp:
97+ LOG.debug(
98+ "Please wait %s seconds while we wait to try again",
99+ sec_between)
100 time.sleep(sec_between)
101 if excps:
102 raise excps[-1]
103diff --git a/tests/unittests/test_datasource/test_azure.py b/tests/unittests/test_datasource/test_azure.py
104index cd6e7e7..4c5c6c1 100644
105--- a/tests/unittests/test_datasource/test_azure.py
106+++ b/tests/unittests/test_datasource/test_azure.py
107@@ -513,6 +513,57 @@ fdescfs /dev/fd fdescfs rw 0 0
108 dsrc.crawl_metadata()
109 self.assertEqual(str(cm.exception), error_msg)
110
111+ @mock.patch('cloudinit.sources.DataSourceAzure.util.write_file')
112+ @mock.patch(
113+ 'cloudinit.sources.DataSourceAzure.DataSourceAzure._report_ready')
114+ @mock.patch('cloudinit.sources.DataSourceAzure.DataSourceAzure._poll_imds')
115+ def test_crawl_metadata_on_reprovision_reports_ready(
116+ self, poll_imds_func,
117+ report_ready_func,
118+ m_write):
119+ """If reprovisioning, report ready at the end"""
120+ ovfenv = construct_valid_ovf_env(
121+ platform_settings={"PreprovisionedVm": "True"})
122+
123+ data = {'ovfcontent': ovfenv,
124+ 'sys_cfg': {}}
125+ dsrc = self._get_ds(data)
126+ poll_imds_func.return_value = ovfenv
127+ dsrc.crawl_metadata()
128+ self.assertEqual(1, report_ready_func.call_count)
129+
130+ @mock.patch('cloudinit.sources.DataSourceAzure.util.write_file')
131+ @mock.patch(
132+ 'cloudinit.sources.DataSourceAzure.DataSourceAzure._report_ready')
133+ @mock.patch('cloudinit.net.dhcp.EphemeralIPv4Network')
134+ @mock.patch('cloudinit.net.dhcp.maybe_perform_dhcp_discovery')
135+ @mock.patch('cloudinit.sources.DataSourceAzure.readurl')
136+ def test_crawl_metadata_on_reprovision_reports_ready_using_lease(
137+ self, m_readurl, m_dhcp,
138+ m_net, report_ready_func,
139+ m_write):
140+ """If reprovisioning, report ready using the obtained lease"""
141+ ovfenv = construct_valid_ovf_env(
142+ platform_settings={"PreprovisionedVm": "True"})
143+
144+ data = {'ovfcontent': ovfenv,
145+ 'sys_cfg': {}}
146+ dsrc = self._get_ds(data)
147+
148+ lease = {
149+ 'interface': 'eth9', 'fixed-address': '192.168.2.9',
150+ 'routers': '192.168.2.1', 'subnet-mask': '255.255.255.0',
151+ 'unknown-245': '624c3620'}
152+ m_dhcp.return_value = [lease]
153+
154+ reprovision_ovfenv = construct_valid_ovf_env()
155+ m_readurl.return_value = url_helper.StringResponse(
156+ reprovision_ovfenv.encode('utf-8'))
157+
158+ dsrc.crawl_metadata()
159+ self.assertEqual(2, report_ready_func.call_count)
160+ report_ready_func.assert_called_with(lease=lease)
161+
162 def test_waagent_d_has_0700_perms(self):
163 # we expect /var/lib/waagent to be created 0700
164 dsrc = self._get_ds({'ovfcontent': construct_valid_ovf_env()})

Subscribers

People subscribed via source and target branches