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()