azurelinuxagent/common/protocol/extensions_goal_state.py (141 lines of code) (raw):

# Microsoft Azure Linux Agent # # Copyright 2020 Microsoft Corporation # # Licensed under the Apache License, Version 2.0 (the "License"); # you may not use this file except in compliance with the License. # You may obtain a copy of the License at # # http://www.apache.org/licenses/LICENSE-2.0 # # Unless required by applicable law or agreed to in writing, software # distributed under the License is distributed on an "AS IS" BASIS, # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. # See the License for the specific language governing permissions and # limitations under the License. # # Requires Python 2.6+ and Openssl 1.0+ import datetime from azurelinuxagent.common import logger from azurelinuxagent.common.AgentGlobals import AgentGlobals from azurelinuxagent.common.exception import AgentError from azurelinuxagent.common.utils import textutil class GoalStateChannel(object): WireServer = "WireServer" HostGAPlugin = "HostGAPlugin" Empty = "Empty" class GoalStateSource(object): Fabric = "Fabric" FastTrack = "FastTrack" Empty = "Empty" class VmSettingsParseError(AgentError): """ Error raised when the VmSettings are malformed """ def __init__(self, message, etag, vm_settings_text, inner=None): super(VmSettingsParseError, self).__init__(message, inner) self.etag = etag self.vm_settings_text = vm_settings_text class ExtensionsGoalState(object): """ ExtensionsGoalState represents the extensions information in the goal state; that information can originate from ExtensionsConfig when the goal state is retrieved from the WireServe or from vmSettings when it is retrieved from the HostGAPlugin. NOTE: This is an abstract class. The corresponding concrete classes can be instantiated using the ExtensionsGoalStateFactory. """ def __init__(self): self._is_outdated = False @property def id(self): """ Returns a string that includes the incarnation number if the ExtensionsGoalState was created from ExtensionsConfig, or the etag if it was created from vmSettings. """ raise NotImplementedError() @property def is_outdated(self): """ A goal state can be outdated if, for example, the VM Agent is using Fast Track and support for it stops (e.g. the VM is migrated to a node with an older version of the HostGAPlugin) and now the Agent is fetching goal states via the WireServer. """ return self._is_outdated @is_outdated.setter def is_outdated(self, value): self._is_outdated = value @property def svd_sequence_number(self): raise NotImplementedError() @property def activity_id(self): raise NotImplementedError() @property def correlation_id(self): raise NotImplementedError() @property def created_on_timestamp(self): raise NotImplementedError() @property def channel(self): """ Whether the goal state was retrieved from the WireServer or the HostGAPlugin """ raise NotImplementedError() @property def source(self): """ Whether the goal state originated from Fabric or Fast Track """ raise NotImplementedError() @property def status_upload_blob(self): raise NotImplementedError() @property def status_upload_blob_type(self): raise NotImplementedError() def _set_status_upload_blob_type(self, value): raise NotImplementedError() @property def required_features(self): raise NotImplementedError() @property def on_hold(self): raise NotImplementedError() @property def agent_families(self): raise NotImplementedError() @property def extensions(self): raise NotImplementedError() def get_redacted_text(self): """ Returns the raw text (either the ExtensionsConfig or the vmSettings) with any confidential data removed, or an empty string for empty goal states. """ raise NotImplementedError() def _do_common_validations(self): """ Does validations common to vmSettings and ExtensionsConfig """ if self.status_upload_blob_type not in ["BlockBlob", "PageBlob"]: logger.info("Status Blob type '{0}' is not valid, assuming BlockBlob", self.status_upload_blob) self._set_status_upload_blob_type("BlockBlob") @staticmethod def _ticks_to_utc_timestamp(ticks_string): """ Takes 'ticks', a string indicating the number of ticks since midnight 0001-01-01 00:00:00, and returns a UTC timestamp (every tick is 1/10000000 of a second). """ minimum = datetime.datetime(1900, 1, 1, 0, 0) # min value accepted by datetime.strftime() as_date_time = minimum if ticks_string not in (None, ""): try: as_date_time = datetime.datetime.min + datetime.timedelta(seconds=float(ticks_string) / 10 ** 7) except Exception as exception: logger.verbose("Can't parse ticks: {0}", textutil.format_exception(exception)) as_date_time = max(as_date_time, minimum) return as_date_time.strftime(logger.Logger.LogTimeFormatInUTC) @staticmethod def _string_to_id(id_string): """ Takes 'id', a string indicating an ID, and returns a null GUID if the string is None or empty; otherwise return 'id' unchanged """ if id_string in (None, ""): return AgentGlobals.GUID_ZERO return id_string class EmptyExtensionsGoalState(ExtensionsGoalState): def __init__(self, incarnation): super(EmptyExtensionsGoalState, self).__init__() self._id = "incarnation_{0}".format(incarnation) self._incarnation = incarnation @property def id(self): return self._id @property def incarnation(self): return self._incarnation @property def svd_sequence_number(self): return self._incarnation @property def activity_id(self): return AgentGlobals.GUID_ZERO @property def correlation_id(self): return AgentGlobals.GUID_ZERO @property def created_on_timestamp(self): return datetime.datetime.min @property def channel(self): return GoalStateChannel.Empty @property def source(self): return GoalStateSource.Empty @property def status_upload_blob(self): return None @property def status_upload_blob_type(self): return None def _set_status_upload_blob_type(self, value): raise TypeError("EmptyExtensionsGoalState is immutable; cannot change the value of the status upload blob") @property def required_features(self): return [] @property def on_hold(self): return False @property def agent_families(self): return [] @property def extensions(self): return [] def get_redacted_text(self): return ''