summaryrefslogtreecommitdiff
path: root/azurelinuxagent/common
diff options
context:
space:
mode:
Diffstat (limited to 'azurelinuxagent/common')
-rw-r--r--azurelinuxagent/common/dhcp.py13
-rw-r--r--azurelinuxagent/common/event.py23
-rw-r--r--azurelinuxagent/common/osutil/default.py23
-rw-r--r--azurelinuxagent/common/osutil/factory.py3
-rw-r--r--azurelinuxagent/common/protocol/hostplugin.py246
-rw-r--r--azurelinuxagent/common/protocol/wire.py112
-rw-r--r--azurelinuxagent/common/utils/restutil.py2
-rw-r--r--azurelinuxagent/common/version.py5
8 files changed, 301 insertions, 126 deletions
diff --git a/azurelinuxagent/common/dhcp.py b/azurelinuxagent/common/dhcp.py
index d5c90cb..66346b5 100644
--- a/azurelinuxagent/common/dhcp.py
+++ b/azurelinuxagent/common/dhcp.py
@@ -84,7 +84,7 @@ class DhcpHandler(object):
:return: True if a route to KNOWN_WIRESERVER_IP exists.
"""
route_exists = False
- logger.info("test for route to {0}".format(KNOWN_WIRESERVER_IP))
+ logger.info("Test for route to {0}".format(KNOWN_WIRESERVER_IP))
try:
route_file = '/proc/net/route'
if os.path.exists(route_file) and \
@@ -95,13 +95,12 @@ class DhcpHandler(object):
self.gateway = None
self.routes = None
route_exists = True
- logger.info("route to {0} exists".format(KNOWN_WIRESERVER_IP))
+ logger.info("Route to {0} exists".format(KNOWN_WIRESERVER_IP))
else:
- logger.warn(
- "no route exists to {0}".format(KNOWN_WIRESERVER_IP))
+ logger.warn("No route exists to {0}".format(KNOWN_WIRESERVER_IP))
except Exception as e:
logger.error(
- "could not determine whether route exists to {0}: {1}".format(
+ "Could not determine whether route exists to {0}: {1}".format(
KNOWN_WIRESERVER_IP, e))
return route_exists
@@ -118,12 +117,12 @@ class DhcpHandler(object):
exists = False
- logger.info("checking for dhcp lease cache")
+ logger.info("Checking for dhcp lease cache")
cached_endpoint = self.osutil.get_dhcp_lease_endpoint()
if cached_endpoint is not None:
self.endpoint = cached_endpoint
exists = True
- logger.info("cache exists [{0}]".format(exists))
+ logger.info("Cache exists [{0}]".format(exists))
return exists
def conf_routes(self):
diff --git a/azurelinuxagent/common/event.py b/azurelinuxagent/common/event.py
index 9265820..ce79adf 100644
--- a/azurelinuxagent/common/event.py
+++ b/azurelinuxagent/common/event.py
@@ -46,6 +46,7 @@ class WALAEventOperation:
Install = "Install"
InitializeHostPlugin = "InitializeHostPlugin"
Provision = "Provision"
+ ReportStatus = "ReportStatus"
Restart = "Restart"
UnhandledError = "UnhandledError"
UnInstall = "UnInstall"
@@ -65,8 +66,17 @@ class EventLogger(object):
if not os.path.exists(self.event_dir):
os.mkdir(self.event_dir)
os.chmod(self.event_dir, 0o700)
- if len(os.listdir(self.event_dir)) > 1000:
- raise EventError("Too many files under: {0}".format(self.event_dir))
+
+ existing_events = os.listdir(self.event_dir)
+ if len(existing_events) >= 1000:
+ existing_events.sort()
+ oldest_files = existing_events[:-999]
+ logger.warn("Too many files under: {0}, removing oldest".format(self.event_dir))
+ try:
+ for f in oldest_files:
+ os.remove(os.path.join(self.event_dir, f))
+ except IOError as e:
+ raise EventError(e)
filename = os.path.join(self.event_dir,
ustr(int(time.time() * 1000000)))
@@ -101,6 +111,15 @@ class EventLogger(object):
__event_logger__ = EventLogger()
+def report_event(op, is_success=True, message=''):
+ from azurelinuxagent.common.version import AGENT_NAME, CURRENT_VERSION
+ add_event(AGENT_NAME,
+ version=CURRENT_VERSION,
+ is_success=is_success,
+ message=message,
+ op=op)
+
+
def add_event(name, op="", is_success=True, duration=0, version=CURRENT_VERSION,
message="", evt_type="", is_internal=False,
reporter=__event_logger__):
diff --git a/azurelinuxagent/common/osutil/default.py b/azurelinuxagent/common/osutil/default.py
index 4cd379b..59d5985 100644
--- a/azurelinuxagent/common/osutil/default.py
+++ b/azurelinuxagent/common/osutil/default.py
@@ -18,6 +18,7 @@
import multiprocessing
import os
+import platform
import re
import shutil
import socket
@@ -420,7 +421,12 @@ class DefaultOSUtil(object):
"""
iface=''
expected=16 # how many devices should I expect...
- struct_size=40 # for 64bit the size is 40 bytes
+
+ # for 64bit the size is 40 bytes
+ # for 32bit the size is 32 bytes
+ python_arc = platform.architecture()[0]
+ struct_size = 32 if python_arc == '32bit' else 40
+
sock = socket.socket(socket.AF_INET,
socket.SOCK_DGRAM,
socket.IPPROTO_UDP)
@@ -440,11 +446,11 @@ class DefaultOSUtil(object):
if len(iface) == 0 or self.is_loopback(iface) or iface != primary:
# test the next one
if len(iface) != 0 and not self.disable_route_warning:
- logger.info('interface [{0}] skipped'.format(iface))
+ logger.info('Interface [{0}] skipped'.format(iface))
continue
else:
# use this one
- logger.info('interface [{0}] selected'.format(iface))
+ logger.info('Interface [{0}] selected'.format(iface))
break
return iface.decode('latin-1'), socket.inet_ntoa(sock[i+20:i+24])
@@ -473,7 +479,7 @@ class DefaultOSUtil(object):
primary_metric = None
if not self.disable_route_warning:
- logger.info("examine /proc/net/route for primary interface")
+ logger.info("Examine /proc/net/route for primary interface")
with open('/proc/net/route') as routing_table:
idx = 0
for header in filter(lambda h: len(h) > 0, routing_table.readline().strip(" \n").split("\t")):
@@ -500,12 +506,13 @@ class DefaultOSUtil(object):
if not self.disable_route_warning:
with open('/proc/net/route') as routing_table_fh:
routing_table_text = routing_table_fh.read()
- logger.error('could not determine primary interface, '
- 'please ensure /proc/net/route is correct:\n'
- '{0}'.format(routing_table_text))
+ logger.warn('Could not determine primary interface, '
+ 'please ensure /proc/net/route is correct')
+ logger.warn('Contents of /proc/net/route:\n{0}'.format(routing_table_text))
+ logger.warn('Primary interface examination will retry silently')
self.disable_route_warning = True
else:
- logger.info('primary interface is [{0}]'.format(primary))
+ logger.info('Primary interface is [{0}]'.format(primary))
self.disable_route_warning = False
return primary
diff --git a/azurelinuxagent/common/osutil/factory.py b/azurelinuxagent/common/osutil/factory.py
index acd7f6e..eee9f97 100644
--- a/azurelinuxagent/common/osutil/factory.py
+++ b/azurelinuxagent/common/osutil/factory.py
@@ -76,6 +76,9 @@ def get_osutil(distro_name=DISTRO_NAME,
else:
return RedhatOSUtil()
+ elif distro_name == "euleros":
+ return RedhatOSUtil()
+
elif distro_name == "freebsd":
return FreeBSDOSUtil()
diff --git a/azurelinuxagent/common/protocol/hostplugin.py b/azurelinuxagent/common/protocol/hostplugin.py
index bdae56e..70bf8b4 100644
--- a/azurelinuxagent/common/protocol/hostplugin.py
+++ b/azurelinuxagent/common/protocol/hostplugin.py
@@ -16,8 +16,17 @@
#
# Requires Python 2.4+ and Openssl 1.0+
#
-from azurelinuxagent.common.protocol.wire import *
+
+import base64
+import json
+
+from azurelinuxagent.common import logger
+from azurelinuxagent.common.exception import ProtocolError, HttpError
+from azurelinuxagent.common.future import ustr, httpclient
+from azurelinuxagent.common.utils import restutil
from azurelinuxagent.common.utils import textutil
+from azurelinuxagent.common.utils.textutil import remove_bom
+from azurelinuxagent.common.version import PY_VERSION_MAJOR
HOST_PLUGIN_PORT = 32526
URI_FORMAT_GET_API_VERSIONS = "http://{0}:{1}/versions"
@@ -30,12 +39,15 @@ HEADER_VERSION = "x-ms-version"
HEADER_HOST_CONFIG_NAME = "x-ms-host-config-name"
HEADER_ARTIFACT_LOCATION = "x-ms-artifact-location"
HEADER_ARTIFACT_MANIFEST_LOCATION = "x-ms-artifact-manifest-location"
+MAXIMUM_PAGEBLOB_PAGE_SIZE = 4 * 1024 * 1024 # Max page size: 4MB
class HostPluginProtocol(object):
+ _is_default_channel = False
+
def __init__(self, endpoint, container_id, role_config_name):
if endpoint is None:
- raise ProtocolError("Host plugin endpoint not provided")
+ raise ProtocolError("HostGAPlugin: Endpoint not provided")
self.is_initialized = False
self.is_available = False
self.api_versions = None
@@ -45,45 +57,51 @@ class HostPluginProtocol(object):
self.role_config_name = role_config_name
self.manifest_uri = None
+ @staticmethod
+ def is_default_channel():
+ return HostPluginProtocol._is_default_channel
+
+ @staticmethod
+ def set_default_channel(is_default):
+ HostPluginProtocol._is_default_channel = is_default
+
def ensure_initialized(self):
if not self.is_initialized:
self.api_versions = self.get_api_versions()
self.is_available = API_VERSION in self.api_versions
self.is_initialized = True
-
- from azurelinuxagent.common.event import add_event, WALAEventOperation
- add_event(name="WALA",
- op=WALAEventOperation.InitializeHostPlugin,
- is_success=self.is_available)
+ from azurelinuxagent.common.event import WALAEventOperation, report_event
+ report_event(WALAEventOperation.InitializeHostPlugin,
+ is_success=self.is_available)
return self.is_available
def get_api_versions(self):
url = URI_FORMAT_GET_API_VERSIONS.format(self.endpoint,
HOST_PLUGIN_PORT)
- logger.verbose("getting API versions at [{0}]".format(url))
+ logger.verbose("HostGAPlugin: Getting API versions at [{0}]".format(
+ url))
return_val = []
try:
headers = {HEADER_CONTAINER_ID: self.container_id}
response = restutil.http_get(url, headers)
if response.status != httpclient.OK:
logger.error(
- "get API versions returned status code [{0}]".format(
- response.status))
+ "HostGAPlugin: Failed Get API versions: {0}".format(
+ self.read_response_error(response)))
else:
return_val = ustr(remove_bom(response.read()), encoding='utf-8')
except HttpError as e:
- logger.error("get API versions failed with [{0}]".format(e))
+ logger.error("HostGAPlugin: Exception Get API versions: {0}".format(e))
return return_val
def get_artifact_request(self, artifact_url, artifact_manifest_url=None):
if not self.ensure_initialized():
- logger.error("host plugin channel is not available")
- return None, None
+ raise ProtocolError("HostGAPlugin: Host plugin channel is not available")
+
if textutil.is_str_none_or_whitespace(artifact_url):
- logger.error("no extension artifact url was provided")
- return None, None
+ raise ProtocolError("HostGAPlugin: No extension artifact url was provided")
url = URI_FORMAT_GET_EXTENSION_ARTIFACT.format(self.endpoint,
HOST_PLUGIN_PORT)
@@ -97,45 +115,6 @@ class HostPluginProtocol(object):
return url, headers
- def put_vm_status(self, status_blob, sas_url, config_blob_type=None):
- """
- Try to upload the VM status via the host plugin /status channel
- :param sas_url: the blob SAS url to pass to the host plugin
- :param config_blob_type: the blob type from the extension config
- :type status_blob: StatusBlob
- """
- if not self.ensure_initialized():
- logger.error("host plugin channel is not available")
- return
- if status_blob is None or status_blob.vm_status is None:
- logger.error("no status data was provided")
- return
- try:
- url = URI_FORMAT_PUT_VM_STATUS.format(self.endpoint, HOST_PLUGIN_PORT)
- logger.verbose("Posting VM status to host plugin")
- status = textutil.b64encode(status_blob.data)
- blob_type = status_blob.type if status_blob.type else config_blob_type
- headers = {HEADER_VERSION: API_VERSION,
- "Content-type": "application/json",
- HEADER_CONTAINER_ID: self.container_id,
- HEADER_HOST_CONFIG_NAME: self.role_config_name}
- blob_headers = [{'headerName': 'x-ms-version',
- 'headerValue': status_blob.__storage_version__},
- {'headerName': 'x-ms-blob-type',
- 'headerValue': blob_type}]
- data = json.dumps({'requestUri': sas_url, 'headers': blob_headers,
- 'content': status}, sort_keys=True)
- response = restutil.http_put(url, data=data, headers=headers)
- if response.status != httpclient.OK:
- logger.warn("PUT {0} [{1}: {2}]",
- url,
- response.status,
- response.reason)
- else:
- logger.verbose("Successfully uploaded status to host plugin")
- except Exception as e:
- logger.error("Put VM status failed [{0}]", e)
-
def put_vm_log(self, content):
"""
Try to upload the given content to the host plugin
@@ -147,13 +126,13 @@ class HostPluginProtocol(object):
:return:
"""
if not self.ensure_initialized():
- logger.error("host plugin channel is not available")
- return
+ raise ProtocolError("HostGAPlugin: Host plugin channel is not available")
+
if content is None \
or self.container_id is None \
or self.deployment_id is None:
logger.error(
- "invalid arguments passed: "
+ "HostGAPlugin: Invalid arguments passed: "
"[{0}], [{1}], [{2}]".format(
content,
self.container_id,
@@ -163,11 +142,158 @@ class HostPluginProtocol(object):
headers = {"x-ms-vmagentlog-deploymentid": self.deployment_id,
"x-ms-vmagentlog-containerid": self.container_id}
- logger.info("put VM log at [{0}]".format(url))
+ logger.info("HostGAPlugin: Put VM log to [{0}]".format(url))
try:
response = restutil.http_put(url, content, headers)
if response.status != httpclient.OK:
- logger.error("put log returned status code [{0}]".format(
+ logger.error("HostGAPlugin: Put log failed: Code {0}".format(
response.status))
except HttpError as e:
- logger.error("put log failed with [{0}]".format(e))
+ logger.error("HostGAPlugin: Put log exception: {0}".format(e))
+
+ def put_vm_status(self, status_blob, sas_url, config_blob_type=None):
+ """
+ Try to upload the VM status via the host plugin /status channel
+ :param sas_url: the blob SAS url to pass to the host plugin
+ :param config_blob_type: the blob type from the extension config
+ :type status_blob: StatusBlob
+ """
+ if not self.ensure_initialized():
+ raise ProtocolError("HostGAPlugin: HostGAPlugin is not available")
+
+ if status_blob is None or status_blob.vm_status is None:
+ raise ProtocolError("HostGAPlugin: Status blob was not provided")
+
+ logger.verbose("HostGAPlugin: Posting VM status")
+ try:
+ blob_type = status_blob.type if status_blob.type else config_blob_type
+
+ if blob_type == "BlockBlob":
+ self._put_block_blob_status(sas_url, status_blob)
+ else:
+ self._put_page_blob_status(sas_url, status_blob)
+
+ if not HostPluginProtocol.is_default_channel():
+ logger.info("HostGAPlugin: Setting host plugin as default channel")
+ HostPluginProtocol.set_default_channel(True)
+ except Exception as e:
+ message = "HostGAPlugin: Exception Put VM status: {0}".format(e)
+ logger.error(message)
+ from azurelinuxagent.common.event import WALAEventOperation, report_event
+ report_event(op=WALAEventOperation.ReportStatus,
+ is_success=False,
+ message=message)
+ logger.warn("HostGAPlugin: resetting default channel")
+ HostPluginProtocol.set_default_channel(False)
+
+ def _put_block_blob_status(self, sas_url, status_blob):
+ url = URI_FORMAT_PUT_VM_STATUS.format(self.endpoint, HOST_PLUGIN_PORT)
+
+ response = restutil.http_put(url,
+ data=self._build_status_data(
+ sas_url,
+ status_blob.get_block_blob_headers(len(status_blob.data)),
+ bytearray(status_blob.data, encoding='utf-8')),
+ headers=self._build_status_headers())
+
+ if response.status != httpclient.OK:
+ raise HttpError("HostGAPlugin: Put BlockBlob failed: {0}".format(
+ self.read_response_error(response)))
+ else:
+ logger.verbose("HostGAPlugin: Put BlockBlob status succeeded")
+
+ def _put_page_blob_status(self, sas_url, status_blob):
+ url = URI_FORMAT_PUT_VM_STATUS.format(self.endpoint, HOST_PLUGIN_PORT)
+
+ # Convert the status into a blank-padded string whose length is modulo 512
+ status = bytearray(status_blob.data, encoding='utf-8')
+ status_size = int((len(status) + 511) / 512) * 512
+ status = bytearray(status_blob.data.ljust(status_size), encoding='utf-8')
+
+ # First, initialize an empty blob
+ response = restutil.http_put(url,
+ data=self._build_status_data(
+ sas_url,
+ status_blob.get_page_blob_create_headers(status_size)),
+ headers=self._build_status_headers())
+
+ if response.status != httpclient.OK:
+ raise HttpError(
+ "HostGAPlugin: Failed PageBlob clean-up: {0}".format(
+ self.read_response_error(response)))
+ else:
+ logger.verbose("HostGAPlugin: PageBlob clean-up succeeded")
+
+ # Then, upload the blob in pages
+ if sas_url.count("?") <= 0:
+ sas_url = "{0}?comp=page".format(sas_url)
+ else:
+ sas_url = "{0}&comp=page".format(sas_url)
+
+ start = 0
+ end = 0
+ while start < len(status):
+ # Create the next page
+ end = start + min(len(status) - start, MAXIMUM_PAGEBLOB_PAGE_SIZE)
+ page_size = int((end - start + 511) / 512) * 512
+ buf = bytearray(page_size)
+ buf[0: end - start] = status[start: end]
+
+ # Send the page
+ response = restutil.http_put(url,
+ data=self._build_status_data(
+ sas_url,
+ status_blob.get_page_blob_page_headers(start, end),
+ buf),
+ headers=self._build_status_headers())
+
+ if response.status != httpclient.OK:
+ raise HttpError(
+ "HostGAPlugin Error: Put PageBlob bytes [{0},{1}]: " \
+ "{2}".format(
+ start, end, self.read_response_error(response)))
+
+ # Advance to the next page (if any)
+ start = end
+
+ def _build_status_data(self, sas_url, blob_headers, content=None):
+ headers = []
+ for name in iter(blob_headers.keys()):
+ headers.append({
+ 'headerName': name,
+ 'headerValue': blob_headers[name]
+ })
+
+ data = {
+ 'requestUri': sas_url,
+ 'headers': headers
+ }
+ if not content is None:
+ data['content'] = self._base64_encode(content)
+ return json.dumps(data, sort_keys=True)
+
+ def _build_status_headers(self):
+ return {
+ HEADER_VERSION: API_VERSION,
+ "Content-type": "application/json",
+ HEADER_CONTAINER_ID: self.container_id,
+ HEADER_HOST_CONFIG_NAME: self.role_config_name
+ }
+
+ def _base64_encode(self, data):
+ s = base64.b64encode(bytes(data))
+ if PY_VERSION_MAJOR > 2:
+ return s.decode('utf-8')
+ return s
+
+ @staticmethod
+ def read_response_error(response):
+ if response is None:
+ return ''
+ body = remove_bom(response.read())
+ if PY_VERSION_MAJOR < 3 and body is not None:
+ body = ustr(body, encoding='utf-8')
+ return "{0}, {1}, {2}".format(
+ response.status,
+ response.reason,
+ body)
diff --git a/azurelinuxagent/common/protocol/wire.py b/azurelinuxagent/common/protocol/wire.py
index 71c3e37..265e2dd 100644
--- a/azurelinuxagent/common/protocol/wire.py
+++ b/azurelinuxagent/common/protocol/wire.py
@@ -370,11 +370,14 @@ class StatusBlob(object):
__storage_version__ = "2014-02-14"
+ def prepare(self, blob_type):
+ logger.verbose("Prepare status blob")
+ self.data = self.to_json()
+ self.type = blob_type
+
def upload(self, url):
# TODO upload extension only if content has changed
- logger.verbose("Upload status blob")
upload_successful = False
- self.data = self.to_json()
self.type = self.get_blob_type(url)
try:
if self.type == "BlockBlob":
@@ -384,7 +387,12 @@ class StatusBlob(object):
else:
raise ProtocolError("Unknown blob type: {0}".format(self.type))
except HttpError as e:
- logger.warn("Initial upload failed [{0}]".format(e))
+ message = "Initial upload failed [{0}]".format(e)
+ logger.warn(message)
+ from azurelinuxagent.common.event import WALAEventOperation, report_event
+ report_event(op=WALAEventOperation.ReportStatus,
+ is_success=False,
+ message=message)
else:
logger.verbose("Uploading status blob succeeded")
upload_successful = True
@@ -411,48 +419,54 @@ class StatusBlob(object):
logger.verbose("Blob type: [{0}]", blob_type)
return blob_type
+ def get_block_blob_headers(self, blob_size):
+ return {
+ "Content-Length": ustr(blob_size),
+ "x-ms-blob-type": "BlockBlob",
+ "x-ms-date": time.strftime("%Y-%m-%dT%H:%M:%SZ", time.gmtime()),
+ "x-ms-version": self.__class__.__storage_version__
+ }
+
def put_block_blob(self, url, data):
logger.verbose("Put block blob")
- timestamp = time.strftime("%Y-%m-%dT%H:%M:%SZ", time.gmtime())
- resp = self.client.call_storage_service(
- restutil.http_put,
- url,
- data,
- {
- "x-ms-date": timestamp,
- "x-ms-blob-type": "BlockBlob",
- "Content-Length": ustr(len(data)),
- "x-ms-version": self.__class__.__storage_version__
- })
+ headers = self.get_block_blob_headers(len(data))
+ resp = self.client.call_storage_service(restutil.http_put, url, data, headers)
if resp.status != httpclient.CREATED:
raise UploadError(
"Failed to upload block blob: {0}".format(resp.status))
+ def get_page_blob_create_headers(self, blob_size):
+ return {
+ "Content-Length": "0",
+ "x-ms-blob-content-length": ustr(blob_size),
+ "x-ms-blob-type": "PageBlob",
+ "x-ms-date": time.strftime("%Y-%m-%dT%H:%M:%SZ", time.gmtime()),
+ "x-ms-version": self.__class__.__storage_version__
+ }
+
+ def get_page_blob_page_headers(self, start, end):
+ return {
+ "Content-Length": ustr(end - start),
+ "x-ms-date": time.strftime("%Y-%m-%dT%H:%M:%SZ", time.gmtime()),
+ "x-ms-range": "bytes={0}-{1}".format(start, end - 1),
+ "x-ms-page-write": "update",
+ "x-ms-version": self.__class__.__storage_version__
+ }
+
def put_page_blob(self, url, data):
logger.verbose("Put page blob")
- # Convert string into bytes
+ # Convert string into bytes and align to 512 bytes
data = bytearray(data, encoding='utf-8')
- timestamp = time.strftime("%Y-%m-%dT%H:%M:%SZ", time.gmtime())
-
- # Align to 512 bytes
page_blob_size = int((len(data) + 511) / 512) * 512
- resp = self.client.call_storage_service(
- restutil.http_put,
- url,
- "",
- {
- "x-ms-date": timestamp,
- "x-ms-blob-type": "PageBlob",
- "Content-Length": "0",
- "x-ms-blob-content-length": ustr(page_blob_size),
- "x-ms-version": self.__class__.__storage_version__
- })
+
+ headers = self.get_page_blob_create_headers(page_blob_size)
+ resp = self.client.call_storage_service(restutil.http_put, url, "", headers)
if resp.status != httpclient.CREATED:
raise UploadError(
"Failed to clean up page blob: {0}".format(resp.status))
- if url.count("?") < 0:
+ if url.count("?") <= 0:
url = "{0}?comp=page".format(url)
else:
url = "{0}&comp=page".format(url)
@@ -469,17 +483,12 @@ class StatusBlob(object):
buf_size = page_end - start
buf = bytearray(buf_size)
buf[0: content_size] = data[start: end]
+ headers = self.get_page_blob_page_headers(start, page_end)
resp = self.client.call_storage_service(
restutil.http_put,
url,
bytebuffer(buf),
- {
- "x-ms-date": timestamp,
- "x-ms-range": "bytes={0}-{1}".format(start, page_end - 1),
- "x-ms-page-write": "update",
- "x-ms-version": self.__class__.__storage_version__,
- "Content-Length": ustr(page_end - start)
- })
+ headers)
if resp is None or resp.status != httpclient.CREATED:
raise UploadError(
"Failed to upload page blob: {0}".format(resp.status))
@@ -634,9 +643,14 @@ class WireClient(object):
def fetch_manifest(self, version_uris):
logger.verbose("Fetch manifest")
for version in version_uris:
- response = self.fetch(version.uri)
+ response = None
+ if not HostPluginProtocol.is_default_channel():
+ response = self.fetch(version.uri)
if not response:
- logger.verbose("Manifest could not be downloaded, falling back to host plugin")
+ if HostPluginProtocol.is_default_channel():
+ logger.verbose("Using host plugin as default channel")
+ else:
+ logger.verbose("Manifest could not be downloaded, falling back to host plugin")
host = self.get_host_plugin()
uri, headers = host.get_artifact_request(version.uri)
response = self.fetch(uri, headers)
@@ -648,6 +662,9 @@ class WireClient(object):
else:
host.manifest_uri = version.uri
logger.verbose("Manifest downloaded successfully from host plugin")
+ if not HostPluginProtocol.is_default_channel():
+ logger.info("Setting host plugin as default channel")
+ HostPluginProtocol.set_default_channel(True)
if response:
return response
raise ProtocolError("Failed to fetch manifest from all sources")
@@ -663,12 +680,11 @@ class WireClient(object):
if resp.status == httpclient.OK:
return_value = self.decode_config(resp.read())
else:
- logger.warn("Could not fetch {0} [{1}: {2}]",
+ logger.warn("Could not fetch {0} [{1}]",
uri,
- resp.status,
- resp.reason)
+ HostPluginProtocol.read_response_error(resp))
except (HttpError, ProtocolError) as e:
- logger.verbose("Fetch failed from [{0}]", uri)
+ logger.verbose("Fetch failed from [{0}]: {1}", uri, e)
return return_value
def update_hosting_env(self, goal_state):
@@ -839,10 +855,12 @@ class WireClient(object):
if ext_conf.status_upload_blob is not None:
uploaded = False
try:
- uploaded = self.status_blob.upload(ext_conf.status_upload_blob)
- self.report_blob_type(self.status_blob.type,
- ext_conf.status_upload_blob_type)
- except (HttpError, ProtocolError) as e:
+ self.status_blob.prepare(ext_conf.status_upload_blob_type)
+ if not HostPluginProtocol.is_default_channel():
+ uploaded = self.status_blob.upload(ext_conf.status_upload_blob)
+ self.report_blob_type(self.status_blob.type,
+ ext_conf.status_upload_blob_type)
+ except (HttpError, ProtocolError):
# errors have already been logged
pass
if not uploaded:
diff --git a/azurelinuxagent/common/utils/restutil.py b/azurelinuxagent/common/utils/restutil.py
index 7197370..49d2d68 100644
--- a/azurelinuxagent/common/utils/restutil.py
+++ b/azurelinuxagent/common/utils/restutil.py
@@ -28,7 +28,7 @@ from azurelinuxagent.common.future import httpclient, urlparse
REST api util functions
"""
-RETRY_WAITING_INTERVAL = 10
+RETRY_WAITING_INTERVAL = 3
secure_warning = True
diff --git a/azurelinuxagent/common/version.py b/azurelinuxagent/common/version.py
index 30b751c..8a81974 100644
--- a/azurelinuxagent/common/version.py
+++ b/azurelinuxagent/common/version.py
@@ -76,6 +76,9 @@ def get_distro():
osinfo[2] = "oracle"
osinfo[3] = "Oracle Linux"
+ if os.path.exists("/etc/euleros-release"):
+ osinfo[0] = "euleros"
+
# The platform.py lib has issue with detecting BIG-IP linux distribution.
# Merge the following patch provided by F5.
if os.path.exists("/shared/vadc"):
@@ -88,7 +91,7 @@ def get_distro():
AGENT_NAME = "WALinuxAgent"
AGENT_LONG_NAME = "Azure Linux Agent"
-AGENT_VERSION = '2.2.6'
+AGENT_VERSION = '2.2.9'
AGENT_LONG_VERSION = "{0}-{1}".format(AGENT_NAME, AGENT_VERSION)
AGENT_DESCRIPTION = """\
The Azure Linux Agent supports the provisioning and running of Linux