def fetch_vm_settings()

in azurelinuxagent/common/protocol/hostplugin.py [0:0]


    def fetch_vm_settings(self, force_update=False):
        """
        Queries the vmSettings from the HostGAPlugin and returns an (ExtensionsGoalState, bool) tuple with the vmSettings and
        a boolean indicating if they are an updated (True) or a cached value (False).

        Raises
            * VmSettingsNotSupported if the HostGAPlugin does not support the vmSettings API
            * VmSettingsSupportStopped if the HostGAPlugin stopped supporting the vmSettings API
            * VmSettingsParseError if the HostGAPlugin returned invalid vmSettings (e.g. syntax error)
            * ResourceGoneError if the container ID and roleconfig name need to be refreshed
            * ProtocolError if the request fails for any other reason (e.g. not supported, time out, server error)
        """
        def raise_not_supported():
            try:
                if self._supports_vm_settings:
                    # The most recent goal state was delivered using FastTrack, and suddenly the HostGAPlugin does not support the vmSettings API anymore.
                    # This can happen if, for example, the VM is migrated across host nodes that are running different versions of the HostGAPlugin.
                    logger.warn("The HostGAPlugin stopped supporting the vmSettings API. If there is a pending FastTrack goal state, it will not be executed.")
                    add_event(op=WALAEventOperation.VmSettings, message="[VmSettingsSupportStopped] HostGAPlugin: {0}".format(self._version), is_success=False, log_event=False)
                    raise VmSettingsSupportStopped(self._fast_track_timestamp)
                else:
                    logger.info("HostGAPlugin {0} does not support the vmSettings API. Will not use FastTrack.", self._version)
                    add_event(op=WALAEventOperation.VmSettings, message="[VmSettingsNotSupported] HostGAPlugin: {0}".format(self._version), is_success=True)
                    raise VmSettingsNotSupported()
            finally:
                self._supports_vm_settings = False
                self._supports_vm_settings_next_check = datetime.datetime.now() + datetime.timedelta(hours=6)  # check again in 6 hours

        def format_message(msg):
            return "GET vmSettings [correlation ID: {0} eTag: {1}]: {2}".format(correlation_id, etag, msg)

        try:
            # Raise if VmSettings are not supported, but check again periodically since the HostGAPlugin could have been updated since the last check
            # Note that self._host_plugin_supports_vm_settings can be None, so we need to compare against False
            if not self._supports_vm_settings and self._supports_vm_settings_next_check > datetime.datetime.now():
                # Raise VmSettingsNotSupported directly instead of using raise_not_supported() to avoid resetting the timestamp for the next check
                raise VmSettingsNotSupported()

            etag = None if force_update or self._cached_vm_settings is None else self._cached_vm_settings.etag
            correlation_id = str(uuid.uuid4())

            self._vm_settings_error_reporter.report_request()

            url, headers = self.get_vm_settings_request(correlation_id)
            if etag is not None:
                headers['if-none-match'] = etag
            response = restutil.http_get(url, headers=headers, use_proxy=False, max_retry=1, return_raw_response=True)

            if response.status == httpclient.GONE:
                raise ResourceGoneError()

            if response.status == httpclient.NOT_FOUND:  # the HostGAPlugin does not support FastTrack
                raise_not_supported()

            if response.status == httpclient.NOT_MODIFIED:  # The goal state hasn't changed, return the current instance
                return self._cached_vm_settings, False

            if response.status != httpclient.OK:
                error_description = restutil.read_response_error(response)
                # For historical reasons the HostGAPlugin returns 502 (BAD_GATEWAY) for internal errors instead of using
                # 500 (INTERNAL_SERVER_ERROR). We add a short prefix to the error message in the hope that it will help
                # clear any confusion produced by the poor choice of status code.
                if response.status == httpclient.BAD_GATEWAY:
                    error_description = "[Internal error in HostGAPlugin] {0}".format(error_description)
                error_description = format_message(error_description)

                if 400 <= response.status <= 499:
                    self._vm_settings_error_reporter.report_error(error_description, _VmSettingsError.ClientError)
                elif 500 <= response.status <= 599:
                    self._vm_settings_error_reporter.report_error(error_description, _VmSettingsError.ServerError)
                else:
                    self._vm_settings_error_reporter.report_error(error_description, _VmSettingsError.HttpError)

                raise ProtocolError(error_description)

            for h in response.getheaders():
                if h[0].lower() == 'etag':
                    response_etag = h[1]
                    break
            else:  # since the vmSettings were updated, the response must include an etag
                message = format_message("The vmSettings response does not include an Etag header")
                raise ProtocolError(message)

            response_content = ustr(response.read(), encoding='utf-8')

            vm_settings = ExtensionsGoalStateFactory.create_from_vm_settings(response_etag, response_content, correlation_id)

            # log the HostGAPlugin version
            if vm_settings.host_ga_plugin_version != self._version:
                self._version = vm_settings.host_ga_plugin_version
                message = "HostGAPlugin version: {0}".format(vm_settings.host_ga_plugin_version)
                logger.info(message)
                add_event(op=WALAEventOperation.HostPlugin, message=message, is_success=True)

            # Don't support HostGAPlugin versions older than 133
            if vm_settings.host_ga_plugin_version < FlexibleVersion("1.0.8.133"):
                raise_not_supported()

            self._supports_vm_settings = True
            self._cached_vm_settings = vm_settings

            if vm_settings.source == GoalStateSource.FastTrack:
                self._fast_track_timestamp = vm_settings.created_on_timestamp
                self._save_fast_track_state(vm_settings.created_on_timestamp)
            else:
                self.clear_fast_track_state()

            return vm_settings, True

        except (ProtocolError, ResourceGoneError, VmSettingsNotSupported, VmSettingsParseError):
            raise
        except Exception as exception:
            if isinstance(exception, IOError) and "timed out" in ustr(exception):
                message = format_message("Timeout")
                self._vm_settings_error_reporter.report_error(message, _VmSettingsError.Timeout)
            else:
                message = format_message("Request failed: {0}".format(textutil.format_exception(exception)))
                self._vm_settings_error_reporter.report_error(message, _VmSettingsError.RequestFailed)
            raise ProtocolError(message)
        finally:
            self._vm_settings_error_reporter.report_summary()