From 2ece71923a37a5e1107c80f091a1cc620943fbf2 Mon Sep 17 00:00:00 2001 From: Anh Vo Date: Fri, 23 Apr 2021 10:18:05 -0400 Subject: [PATCH 4/7] Azure: eject the provisioning iso before reporting ready (#861) RH-Author: Eduardo Otubo RH-MergeRequest: 18: Add support for userdata on Azure from IMDS RH-Commit: [4/7] 63e379a4406530c0c15c733f8eee35421079508b (otubo/cloud-init-src) RH-Bugzilla: 2042351 RH-Acked-by: Miroslav Rezanina RH-Acked-by: Emanuele Giuseppe Esposito Due to hyper-v implementations, iso ejection is more efficient if performed from within the guest. The code will attempt to perform a best-effort ejection. Failure during ejection will not prevent reporting ready from happening. If iso ejection is successful, later iso ejection from the platform will be a no-op. In the event the iso ejection from the guest fails, iso ejection will still happen at the platform level. --- cloudinit/sources/DataSourceAzure.py | 22 +++++++++++++++--- cloudinit/sources/helpers/azure.py | 23 ++++++++++++++++--- .../test_datasource/test_azure_helper.py | 13 +++++++++-- 3 files changed, 50 insertions(+), 8 deletions(-) diff --git a/cloudinit/sources/DataSourceAzure.py b/cloudinit/sources/DataSourceAzure.py index 020b7006..39e67c4f 100755 --- a/cloudinit/sources/DataSourceAzure.py +++ b/cloudinit/sources/DataSourceAzure.py @@ -332,6 +332,7 @@ class DataSourceAzure(sources.DataSource): dsname = 'Azure' _negotiated = False _metadata_imds = sources.UNSET + _ci_pkl_version = 1 def __init__(self, sys_cfg, distro, paths): sources.DataSource.__init__(self, sys_cfg, distro, paths) @@ -346,8 +347,13 @@ class DataSourceAzure(sources.DataSource): # Regenerate network config new_instance boot and every boot self.update_events['network'].add(EventType.BOOT) self._ephemeral_dhcp_ctx = None - self.failed_desired_api_version = False + self.iso_dev = None + + def _unpickle(self, ci_pkl_version: int) -> None: + super()._unpickle(ci_pkl_version) + if "iso_dev" not in self.__dict__: + self.iso_dev = None def __str__(self): root = sources.DataSource.__str__(self) @@ -459,6 +465,13 @@ class DataSourceAzure(sources.DataSource): '%s was not mountable' % cdev, logger_func=LOG.warning) continue + report_diagnostic_event("Found provisioning metadata in %s" % cdev, + logger_func=LOG.debug) + + # save the iso device for ejection before reporting ready + if cdev.startswith("/dev"): + self.iso_dev = cdev + perform_reprovision = reprovision or self._should_reprovision(ret) perform_reprovision_after_nic_attach = ( reprovision_after_nic_attach or @@ -1226,7 +1239,9 @@ class DataSourceAzure(sources.DataSource): @return: The success status of sending the ready signal. """ try: - get_metadata_from_fabric(None, lease['unknown-245']) + get_metadata_from_fabric(fallback_lease_file=None, + dhcp_opts=lease['unknown-245'], + iso_dev=self.iso_dev) return True except Exception as e: report_diagnostic_event( @@ -1332,7 +1347,8 @@ class DataSourceAzure(sources.DataSource): metadata_func = partial(get_metadata_from_fabric, fallback_lease_file=self. dhclient_lease_file, - pubkey_info=pubkey_info) + pubkey_info=pubkey_info, + iso_dev=self.iso_dev) LOG.debug("negotiating with fabric via agent command %s", self.ds_cfg['agent_command']) diff --git a/cloudinit/sources/helpers/azure.py b/cloudinit/sources/helpers/azure.py index 03e7156b..ad476076 100755 --- a/cloudinit/sources/helpers/azure.py +++ b/cloudinit/sources/helpers/azure.py @@ -865,7 +865,19 @@ class WALinuxAgentShim: return endpoint_ip_address @azure_ds_telemetry_reporter - def register_with_azure_and_fetch_data(self, pubkey_info=None) -> dict: + def eject_iso(self, iso_dev) -> None: + try: + LOG.debug("Ejecting the provisioning iso") + subp.subp(['eject', iso_dev]) + except Exception as e: + report_diagnostic_event( + "Failed ejecting the provisioning iso: %s" % e, + logger_func=LOG.debug) + + @azure_ds_telemetry_reporter + def register_with_azure_and_fetch_data(self, + pubkey_info=None, + iso_dev=None) -> dict: """Gets the VM's GoalState from Azure, uses the GoalState information to report ready/send the ready signal/provisioning complete signal to Azure, and then uses pubkey_info to filter and obtain the user's @@ -891,6 +903,10 @@ class WALinuxAgentShim: ssh_keys = self._get_user_pubkeys(goal_state, pubkey_info) health_reporter = GoalStateHealthReporter( goal_state, self.azure_endpoint_client, self.endpoint) + + if iso_dev is not None: + self.eject_iso(iso_dev) + health_reporter.send_ready_signal() return {'public-keys': ssh_keys} @@ -1046,11 +1062,12 @@ class WALinuxAgentShim: @azure_ds_telemetry_reporter def get_metadata_from_fabric(fallback_lease_file=None, dhcp_opts=None, - pubkey_info=None): + pubkey_info=None, iso_dev=None): shim = WALinuxAgentShim(fallback_lease_file=fallback_lease_file, dhcp_options=dhcp_opts) try: - return shim.register_with_azure_and_fetch_data(pubkey_info=pubkey_info) + return shim.register_with_azure_and_fetch_data( + pubkey_info=pubkey_info, iso_dev=iso_dev) finally: shim.clean_up() diff --git a/tests/unittests/test_datasource/test_azure_helper.py b/tests/unittests/test_datasource/test_azure_helper.py index 63482c6c..552c7905 100644 --- a/tests/unittests/test_datasource/test_azure_helper.py +++ b/tests/unittests/test_datasource/test_azure_helper.py @@ -1009,6 +1009,14 @@ class TestWALinuxAgentShim(CiTestCase): self.GoalState.return_value.container_id = self.test_container_id self.GoalState.return_value.instance_id = self.test_instance_id + def test_eject_iso_is_called(self): + shim = wa_shim() + with mock.patch.object( + shim, 'eject_iso', autospec=True + ) as m_eject_iso: + shim.register_with_azure_and_fetch_data(iso_dev="/dev/sr0") + m_eject_iso.assert_called_once_with("/dev/sr0") + def test_http_client_does_not_use_certificate_for_report_ready(self): shim = wa_shim() shim.register_with_azure_and_fetch_data() @@ -1283,13 +1291,14 @@ class TestGetMetadataGoalStateXMLAndReportReadyToFabric(CiTestCase): def test_calls_shim_register_with_azure_and_fetch_data(self): m_pubkey_info = mock.MagicMock() - azure_helper.get_metadata_from_fabric(pubkey_info=m_pubkey_info) + azure_helper.get_metadata_from_fabric( + pubkey_info=m_pubkey_info, iso_dev="/dev/sr0") self.assertEqual( 1, self.m_shim.return_value .register_with_azure_and_fetch_data.call_count) self.assertEqual( - mock.call(pubkey_info=m_pubkey_info), + mock.call(iso_dev="/dev/sr0", pubkey_info=m_pubkey_info), self.m_shim.return_value .register_with_azure_and_fetch_data.call_args) -- 2.27.0