summaryrefslogtreecommitdiff
path: root/azurelinuxagent
diff options
context:
space:
mode:
authorBen Howard <ben.howard@ubuntu.com>2015-12-07 16:48:51 -0700
committerusd-importer <ubuntu-server@lists.ubuntu.com>2015-12-08 16:10:11 +0000
commit83432149e212155469b1e9f06eb0095121377356 (patch)
tree9029a2e7836dce3025d5c5a6527d74bbf8d799e5 /azurelinuxagent
parent04946cba49f19c0b6b876bccdbb36d47334af002 (diff)
downloadvyos-walinuxagent-83432149e212155469b1e9f06eb0095121377356.tar.gz
vyos-walinuxagent-83432149e212155469b1e9f06eb0095121377356.zip
Import patches-unapplied version 2.1.2-0ubuntu1 to ubuntu/xenial-proposed
Imported using git-ubuntu import. Changelog parent: 04946cba49f19c0b6b876bccdbb36d47334af002 New changelog entries: * New upstream release (LP: #1523715): - Bug fixes for Ubuntu 15.10 on Azure - Enablement for Azure Stack - Dropped patch for systemd job as upstream now includes it.
Diffstat (limited to 'azurelinuxagent')
-rw-r--r--azurelinuxagent/agent.py2
-rw-r--r--azurelinuxagent/conf.py2
-rw-r--r--azurelinuxagent/distro/__init__.py2
-rw-r--r--azurelinuxagent/distro/centos/__init__.py2
-rw-r--r--azurelinuxagent/distro/centos/loader.py2
-rw-r--r--azurelinuxagent/distro/coreos/__init__.py2
-rw-r--r--azurelinuxagent/distro/coreos/deprovision.py2
-rw-r--r--azurelinuxagent/distro/coreos/handlerFactory.py2
-rw-r--r--azurelinuxagent/distro/coreos/loader.py2
-rw-r--r--azurelinuxagent/distro/coreos/osutil.py12
-rw-r--r--azurelinuxagent/distro/debian/__init__.py2
-rw-r--r--azurelinuxagent/distro/debian/loader.py2
-rw-r--r--azurelinuxagent/distro/default/__init__.py2
-rw-r--r--azurelinuxagent/distro/default/deprovision.py13
-rw-r--r--azurelinuxagent/distro/default/dhcp.py2
-rw-r--r--azurelinuxagent/distro/default/env.py2
-rw-r--r--azurelinuxagent/distro/default/extension.py611
-rw-r--r--azurelinuxagent/distro/default/handlerFactory.py6
-rw-r--r--azurelinuxagent/distro/default/init.py2
-rw-r--r--azurelinuxagent/distro/default/loader.py2
-rw-r--r--azurelinuxagent/distro/default/osutil.py23
-rw-r--r--azurelinuxagent/distro/default/provision.py38
-rw-r--r--azurelinuxagent/distro/default/resourceDisk.py2
-rw-r--r--azurelinuxagent/distro/default/run.py21
-rw-r--r--azurelinuxagent/distro/default/scvmm.py2
-rw-r--r--azurelinuxagent/distro/loader.py2
-rw-r--r--azurelinuxagent/distro/oracle/__init__.py2
-rw-r--r--azurelinuxagent/distro/oracle/loader.py2
-rw-r--r--azurelinuxagent/distro/redhat/__init__.py2
-rw-r--r--azurelinuxagent/distro/redhat/loader.py2
-rw-r--r--azurelinuxagent/distro/redhat/osutil.py27
-rw-r--r--azurelinuxagent/distro/suse/__init__.py2
-rw-r--r--azurelinuxagent/distro/suse/loader.py2
-rw-r--r--azurelinuxagent/distro/suse/osutil.py20
-rw-r--r--azurelinuxagent/distro/ubuntu/__init__.py2
-rw-r--r--azurelinuxagent/distro/ubuntu/deprovision.py2
-rw-r--r--azurelinuxagent/distro/ubuntu/handlerFactory.py2
-rw-r--r--azurelinuxagent/distro/ubuntu/loader.py10
-rw-r--r--azurelinuxagent/distro/ubuntu/osutil.py10
-rw-r--r--azurelinuxagent/distro/ubuntu/provision.py21
-rw-r--r--azurelinuxagent/event.py11
-rw-r--r--azurelinuxagent/exception.py2
-rw-r--r--azurelinuxagent/future.py2
-rw-r--r--azurelinuxagent/handler.py2
-rw-r--r--azurelinuxagent/logger.py15
-rw-r--r--azurelinuxagent/metadata.py27
-rw-r--r--azurelinuxagent/protocol/__init__.py2
-rw-r--r--azurelinuxagent/protocol/common.py167
-rw-r--r--azurelinuxagent/protocol/ovfenv.py11
-rw-r--r--azurelinuxagent/protocol/protocolFactory.py2
-rw-r--r--azurelinuxagent/protocol/v1.py667
-rw-r--r--azurelinuxagent/protocol/v2.py97
-rw-r--r--azurelinuxagent/utils/__init__.py2
-rw-r--r--azurelinuxagent/utils/fileutil.py2
-rw-r--r--azurelinuxagent/utils/osutil.py2
-rw-r--r--azurelinuxagent/utils/restutil.py6
-rw-r--r--azurelinuxagent/utils/shellutil.py22
-rw-r--r--azurelinuxagent/utils/textutil.py14
58 files changed, 1165 insertions, 756 deletions
diff --git a/azurelinuxagent/agent.py b/azurelinuxagent/agent.py
index 5e61a6c..849a192 100644
--- a/azurelinuxagent/agent.py
+++ b/azurelinuxagent/agent.py
@@ -1,4 +1,4 @@
-# Windows Azure Linux Agent
+# Microsoft Azure Linux Agent
#
# Copyright 2014 Microsoft Corporation
#
diff --git a/azurelinuxagent/conf.py b/azurelinuxagent/conf.py
index 3185d99..2b0eb01 100644
--- a/azurelinuxagent/conf.py
+++ b/azurelinuxagent/conf.py
@@ -1,4 +1,4 @@
-# Windows Azure Linux Agent
+# Microsoft Azure Linux Agent
#
# Copyright 2014 Microsoft Corporation
#
diff --git a/azurelinuxagent/distro/__init__.py b/azurelinuxagent/distro/__init__.py
index 4b2b9e1..d9b82f5 100644
--- a/azurelinuxagent/distro/__init__.py
+++ b/azurelinuxagent/distro/__init__.py
@@ -1,4 +1,4 @@
-# Windows Azure Linux Agent
+# Microsoft Azure Linux Agent
#
# Copyright 2014 Microsoft Corporation
#
diff --git a/azurelinuxagent/distro/centos/__init__.py b/azurelinuxagent/distro/centos/__init__.py
index 4b2b9e1..d9b82f5 100644
--- a/azurelinuxagent/distro/centos/__init__.py
+++ b/azurelinuxagent/distro/centos/__init__.py
@@ -1,4 +1,4 @@
-# Windows Azure Linux Agent
+# Microsoft Azure Linux Agent
#
# Copyright 2014 Microsoft Corporation
#
diff --git a/azurelinuxagent/distro/centos/loader.py b/azurelinuxagent/distro/centos/loader.py
index 379f027..9dc428f 100644
--- a/azurelinuxagent/distro/centos/loader.py
+++ b/azurelinuxagent/distro/centos/loader.py
@@ -1,4 +1,4 @@
-# Windows Azure Linux Agent
+# Microsoft Azure Linux Agent
#
# Copyright 2014 Microsoft Corporation
#
diff --git a/azurelinuxagent/distro/coreos/__init__.py b/azurelinuxagent/distro/coreos/__init__.py
index 7a4980e..8c1bbdb 100644
--- a/azurelinuxagent/distro/coreos/__init__.py
+++ b/azurelinuxagent/distro/coreos/__init__.py
@@ -1,4 +1,4 @@
-# Windows Azure Linux Agent
+# Microsoft Azure Linux Agent
#
# Copyright 2014 Microsoft Corporation
#
diff --git a/azurelinuxagent/distro/coreos/deprovision.py b/azurelinuxagent/distro/coreos/deprovision.py
index f0ff604..99d3a40 100644
--- a/azurelinuxagent/distro/coreos/deprovision.py
+++ b/azurelinuxagent/distro/coreos/deprovision.py
@@ -1,4 +1,4 @@
-# Windows Azure Linux Agent
+# Microsoft Azure Linux Agent
#
# Copyright 2014 Microsoft Corporation
#
diff --git a/azurelinuxagent/distro/coreos/handlerFactory.py b/azurelinuxagent/distro/coreos/handlerFactory.py
index f0490e8..58f476c 100644
--- a/azurelinuxagent/distro/coreos/handlerFactory.py
+++ b/azurelinuxagent/distro/coreos/handlerFactory.py
@@ -1,4 +1,4 @@
-# Windows Azure Linux Agent
+# Microsoft Azure Linux Agent
#
# Copyright 2014 Microsoft Corporation
#
diff --git a/azurelinuxagent/distro/coreos/loader.py b/azurelinuxagent/distro/coreos/loader.py
index ec009ef..802f276 100644
--- a/azurelinuxagent/distro/coreos/loader.py
+++ b/azurelinuxagent/distro/coreos/loader.py
@@ -1,4 +1,4 @@
-# Windows Azure Linux Agent
+# Microsoft Azure Linux Agent
#
# Copyright 2014 Microsoft Corporation
#
diff --git a/azurelinuxagent/distro/coreos/osutil.py b/azurelinuxagent/distro/coreos/osutil.py
index 6dfba64..c244311 100644
--- a/azurelinuxagent/distro/coreos/osutil.py
+++ b/azurelinuxagent/distro/coreos/osutil.py
@@ -37,7 +37,7 @@ class CoreOSUtil(DefaultOSUtil):
super(CoreOSUtil, self).__init__()
self.waagent_path='/usr/share/oem/bin/waagent'
self.python_path='/usr/share/oem/python/bin'
- self.conf_path = '/usr/share/oem/waagent.conf'
+ self.conf_file_path = '/usr/share/oem/waagent.conf'
if 'PATH' in os.environ:
path = "{0}:{1}".format(os.environ['PATH'], self.python_path)
else:
@@ -55,7 +55,7 @@ class CoreOSUtil(DefaultOSUtil):
#User 'core' is not a sysuser
if username == 'core':
return False
- return super(CoreOSUtil, self).IsSysUser(username)
+ return super(CoreOSUtil, self).is_sys_user(username)
def is_dhcp_enabled(self):
return True
@@ -88,3 +88,11 @@ class CoreOSUtil(DefaultOSUtil):
def decode_customdata(self, data):
return base64.b64decode(data)
+ def set_ssh_client_alive_interval(self):
+ #In CoreOS, /etc/sshd_config is mount readonly. Skip the setting
+ pass
+
+ def conf_sshd(self, disable_password):
+ #In CoreOS, /etc/sshd_config is mount readonly. Skip the setting
+ pass
+
diff --git a/azurelinuxagent/distro/debian/__init__.py b/azurelinuxagent/distro/debian/__init__.py
index 4b2b9e1..d9b82f5 100644
--- a/azurelinuxagent/distro/debian/__init__.py
+++ b/azurelinuxagent/distro/debian/__init__.py
@@ -1,4 +1,4 @@
-# Windows Azure Linux Agent
+# Microsoft Azure Linux Agent
#
# Copyright 2014 Microsoft Corporation
#
diff --git a/azurelinuxagent/distro/debian/loader.py b/azurelinuxagent/distro/debian/loader.py
index 0787758..cc0c06f 100644
--- a/azurelinuxagent/distro/debian/loader.py
+++ b/azurelinuxagent/distro/debian/loader.py
@@ -1,4 +1,4 @@
-# Windows Azure Linux Agent
+# Microsoft Azure Linux Agent
#
# Copyright 2014 Microsoft Corporation
#
diff --git a/azurelinuxagent/distro/default/__init__.py b/azurelinuxagent/distro/default/__init__.py
index 4b2b9e1..d9b82f5 100644
--- a/azurelinuxagent/distro/default/__init__.py
+++ b/azurelinuxagent/distro/default/__init__.py
@@ -1,4 +1,4 @@
-# Windows Azure Linux Agent
+# Microsoft Azure Linux Agent
#
# Copyright 2014 Microsoft Corporation
#
diff --git a/azurelinuxagent/distro/default/deprovision.py b/azurelinuxagent/distro/default/deprovision.py
index 231f4eb..b62c5f6 100644
--- a/azurelinuxagent/distro/default/deprovision.py
+++ b/azurelinuxagent/distro/default/deprovision.py
@@ -1,4 +1,4 @@
-# Windows Azure Linux Agent
+# Microsoft Azure Linux Agent
#
# Copyright 2014 Microsoft Corporation
#
@@ -19,6 +19,7 @@
import azurelinuxagent.conf as conf
from azurelinuxagent.utils.osutil import OSUTIL
+from azurelinuxagent.future import read_input
import azurelinuxagent.protocol as prot
import azurelinuxagent.protocol.ovfenv as ovf
import azurelinuxagent.utils.fileutil as fileutil
@@ -58,8 +59,6 @@ class DeprovisionHandler(object):
def regen_ssh_host_key(self, warnings, actions):
warnings.append("WARNING! All SSH host key pairs will be deleted.")
- actions.append(DeprovisionAction(OSUTIL.set_hostname,
- ['localhost.localdomain']))
actions.append(DeprovisionAction(shellutil.run,
['rm -f /etc/ssh/ssh_host_*key*']))
@@ -80,6 +79,11 @@ class DeprovisionHandler(object):
dirs_to_del = [OSUTIL.get_lib_dir()]
actions.append(DeprovisionAction(fileutil.rm_dirs, dirs_to_del))
+ def reset_hostname(self, warnings, actions):
+ localhost = ["localhost.localdomain"]
+ actions.append(DeprovisionAction(OSUTIL.set_hostname, localhost))
+ actions.append(DeprovisionAction(OSUTIL.set_dhcp_hostname, localhost))
+
def setup(self, deluser):
warnings = []
actions = []
@@ -89,6 +93,7 @@ class DeprovisionHandler(object):
self.regen_ssh_host_key(warnings, actions)
self.del_dhcp_lease(warnings, actions)
+ self.reset_hostname(warnings, actions)
if conf.get_switch("Provisioning.DeleteRootPassword", False):
self.del_root_password(warnings, actions)
@@ -107,7 +112,7 @@ class DeprovisionHandler(object):
print(warning)
if not force:
- confirm = input("Do you want to proceed (y/n)")
+ confirm = read_input("Do you want to proceed (y/n)")
if not confirm.lower().startswith('y'):
return
diff --git a/azurelinuxagent/distro/default/dhcp.py b/azurelinuxagent/distro/default/dhcp.py
index 574ebd4..4fd23ef 100644
--- a/azurelinuxagent/distro/default/dhcp.py
+++ b/azurelinuxagent/distro/default/dhcp.py
@@ -1,4 +1,4 @@
-# Windows Azure Linux Agent
+# Microsoft Azure Linux Agent
#
# Copyright 2014 Microsoft Corporation
#
diff --git a/azurelinuxagent/distro/default/env.py b/azurelinuxagent/distro/default/env.py
index 6a67113..28bf718 100644
--- a/azurelinuxagent/distro/default/env.py
+++ b/azurelinuxagent/distro/default/env.py
@@ -1,4 +1,4 @@
-# Windows Azure Linux Agent
+# Microsoft Azure Linux Agent
#
# Copyright 2014 Microsoft Corporation
#
diff --git a/azurelinuxagent/distro/default/extension.py b/azurelinuxagent/distro/default/extension.py
index 58ba84e..f6c02aa 100644
--- a/azurelinuxagent/distro/default/extension.py
+++ b/azurelinuxagent/distro/default/extension.py
@@ -1,4 +1,4 @@
-# Windows Azure Linux Agent
+# Microsoft Azure Linux Agent
#
# Copyright 2014 Microsoft Corporation
#
@@ -21,18 +21,35 @@ import zipfile
import time
import json
import subprocess
+import shutil
import azurelinuxagent.logger as logger
from azurelinuxagent.future import text
from azurelinuxagent.utils.osutil import OSUTIL
import azurelinuxagent.protocol as prot
+from azurelinuxagent.metadata import AGENT_VERSION
from azurelinuxagent.event import add_event, WALAEventOperation
from azurelinuxagent.exception import ExtensionError
import azurelinuxagent.utils.fileutil as fileutil
import azurelinuxagent.utils.restutil as restutil
import azurelinuxagent.utils.shellutil as shellutil
+from azurelinuxagent.utils.textutil import Version
+
+#HandlerEnvironment.json schema version
+HANDLER_ENVIRONMENT_VERSION = 1.0
VALID_EXTENSION_STATUS = ['transitioning', 'error', 'success', 'warning']
+VALID_HANDLER_STATUS = ['Ready', 'NotReady', "Installing", "Unresponsive"]
+
+def handler_state_to_status(handler_state):
+ if handler_state == "Enabled":
+ return "Ready"
+ elif handler_state in VALID_HANDLER_STATUS:
+ return handler_state
+ else:
+ return "NotReady"
+
+
def validate_has_key(obj, key, fullname):
if key not in obj:
raise ExtensionError("Missing: {0}".format(fullname))
@@ -41,103 +58,52 @@ def validate_in_range(val, valid_range, name):
if val not in valid_range:
raise ExtensionError("Invalid {0}: {1}".format(name, val))
-def try_get(dictionary, key, default=None):
- try:
- return dictionary[key]
- except KeyError:
- return default
+def parse_formatted_message(formatted_message):
+ if formatted_message is None:
+ return None
+ validate_has_key(formatted_message, 'lang', 'formattedMessage/lang')
+ validate_has_key(formatted_message, 'message', 'formattedMessage/message')
+ return formatted_message.get('message')
+
-def extension_sub_status_to_v2(substatus):
+def parse_ext_substatus(substatus):
#Check extension sub status format
- validate_has_key(substatus, 'name', 'substatus/name')
validate_has_key(substatus, 'status', 'substatus/status')
- validate_has_key(substatus, 'code', 'substatus/code')
- validate_has_key(substatus, 'formattedMessage', 'substatus/formattedMessage')
- validate_has_key(substatus['formattedMessage'], 'lang',
- 'substatus/formattedMessage/lang')
- validate_has_key(substatus['formattedMessage'], 'message',
- 'substatus/formattedMessage/message')
-
validate_in_range(substatus['status'], VALID_EXTENSION_STATUS,
'substatus/status')
status = prot.ExtensionSubStatus()
- status.name = try_get(substatus, 'name')
- status.status = try_get(substatus, 'status')
- status.code = try_get(substatus, 'code')
- status.message = try_get(substatus['formattedMessage'], 'message')
+ status.name = substatus.get('name')
+ status.status = substatus.get('status')
+ status.code = substatus.get('code', 0)
+ formatted_message = substatus.get('formattedMessage')
+ status.message = parse_formatted_message(formatted_message)
return status
-def ext_status_to_v2(ext_status, seq_no):
+def parse_ext_status(ext_status, data):
+ if data is None or len(data) is None:
+ return
+ #Currently, only the first status will be reported
+ data = data[0]
#Check extension status format
- validate_has_key(ext_status, 'status', 'status')
- validate_has_key(ext_status['status'], 'status', 'status/status')
- validate_has_key(ext_status['status'], 'operation', 'status/operation')
- validate_has_key(ext_status['status'], 'code', 'status/code')
- validate_has_key(ext_status['status'], 'name', 'status/name')
- validate_has_key(ext_status['status'], 'formattedMessage',
- 'status/formattedMessage')
- validate_has_key(ext_status['status']['formattedMessage'], 'lang',
- 'status/formattedMessage/lang')
- validate_has_key(ext_status['status']['formattedMessage'], 'message',
- 'status/formattedMessage/message')
-
- validate_in_range(ext_status['status']['status'], VALID_EXTENSION_STATUS,
+ validate_has_key(data, 'status', 'status')
+ status_data = data['status']
+ validate_has_key(status_data, 'status', 'status/status')
+
+ validate_in_range(status_data['status'], VALID_EXTENSION_STATUS,
'status/status')
- status = prot.ExtensionStatus()
- status.name = try_get(ext_status['status'], 'name')
- status.configurationAppliedTime = try_get(ext_status['status'],
- 'configurationAppliedTime')
- status.operation = try_get(ext_status['status'], 'operation')
- status.status = try_get(ext_status['status'], 'status')
- status.code = try_get(ext_status['status'], 'code')
- status.message = try_get(ext_status['status']['formattedMessage'], 'message')
- status.sequenceNumber = seq_no
-
- substatus_list = try_get(ext_status['status'], 'substatus', [])
+ applied_time = status_data.get('configurationAppliedTime')
+ ext_status.configurationAppliedTime = applied_time
+ ext_status.operation = status_data.get('operation')
+ ext_status.status = status_data.get('status')
+ ext_status.code = status_data.get('code', 0)
+ formatted_message = status_data.get('formattedMessage')
+ ext_status.message = parse_formatted_message(formatted_message)
+ substatus_list = status_data.get('substatus')
+ if substatus_list is None:
+ return
for substatus in substatus_list:
- status.substatusList.extend(extension_sub_status_to_v2(substatus))
- return status
-
-class ExtensionsHandler(object):
-
- def process(self):
- protocol = prot.FACTORY.get_default_protocol()
- ext_list = protocol.get_extensions()
-
- h_status_list = []
- for extension in ext_list.extensions:
- #TODO handle extension in parallel
- pkg_list = protocol.get_extension_pkgs(extension)
- h_status = self.process_extension(extension, pkg_list)
- h_status_list.append(h_status)
-
- return h_status_list
-
- def process_extension(self, extension, pkg_list):
- installed_version = get_installed_version(extension.name)
- if installed_version is not None:
- ext = ExtensionInstance(extension, pkg_list,
- installed_version, installed=True)
- else:
- ext = ExtensionInstance(extension, pkg_list,
- extension.properties.version)
- try:
- ext.init_logger()
- ext.handle()
- status = ext.collect_handler_status()
- except ExtensionError as e:
- logger.error("Failed to handle extension: {0}-{1}\n {2}",
- ext.get_name(), ext.get_version(), e)
- add_event(name=ext.get_name(), is_success=False,
- op=ext.get_curr_op(), message = text(e))
- ext_status = prot.ExtensionStatus(status='error', code='-1',
- operation = ext.get_curr_op(),
- message = text(e),
- seq_no = ext.get_seq_no())
- status = ext.create_handler_status(ext_status)
- status.status = "Ready"
- return status
+ ext_status.substatusList.append(parse_ext_substatus(substatus))
def parse_extension_dirname(dirname):
"""
@@ -160,67 +126,204 @@ def get_installed_version(target_name):
name, version = parse_extension_dirname(dir_name)
#Here we need to ensure names are exactly the same.
if name == target_name:
- if installed_version is None or installed_version < version:
+ if installed_version is None or \
+ Version(installed_version) < Version(version):
installed_version = version
return installed_version
-class ExtensionInstance(object):
- def __init__(self, extension, pkg_list, curr_version, installed=False):
- self.extension = extension
+class ExtHandlerState(object):
+ Enabled = "Enabled"
+ Disabled = "Disabled"
+ Failed = "Failed"
+
+
+class ExtHandlersHandler(object):
+
+ def process(self):
+ try:
+ protocol = prot.FACTORY.get_default_protocol()
+ ext_handlers = protocol.get_ext_handlers()
+ except prot.ProtocolError as e:
+ add_event(name="WALA", is_success=False, message = text(e))
+ return
+
+
+ vm_status = prot.VMStatus()
+ vm_status.vmAgent.version = AGENT_VERSION
+ vm_status.vmAgent.status = "Ready"
+ vm_status.vmAgent.message = "Guest Agent is running"
+
+ if ext_handlers.extHandlers is None or \
+ len(ext_handlers.extHandlers) == 0:
+ logger.verb("No extensions to handle")
+ else:
+ for ext_handler in ext_handlers.extHandlers:
+ #TODO handle extension in parallel
+ try:
+ pkg_list = protocol.get_ext_handler_pkgs(ext_handler)
+ except prot.ProtocolError as e:
+ add_event(name="WALA", is_success=False, message=text(e))
+ continue
+
+ handler_status = self.process_extension(ext_handler, pkg_list)
+ if handler_status is not None:
+ vm_status.vmAgent.extensionHandlers.append(handler_status)
+
+ try:
+ logger.verb("Report vm agent status")
+ protocol.report_vm_status(vm_status)
+ except prot.ProtocolError as e:
+ add_event(name="WALA", is_success=False, message = text(e))
+
+ def process_extension(self, ext_handler, pkg_list):
+ installed_version = get_installed_version(ext_handler.name)
+ if installed_version is not None:
+ handler = ExtHandlerInstance(ext_handler, pkg_list,
+ installed_version, installed=True)
+ else:
+ handler = ExtHandlerInstance(ext_handler, pkg_list,
+ ext_handler.properties.version)
+ handler.handle()
+
+ if handler.ext_status is not None:
+ try:
+ protocol = prot.FACTORY.get_default_protocol()
+ protocol.report_ext_status(handler.name, handler.ext.name,
+ handler.ext_status)
+ except prot.ProtocolError as e:
+ add_event(name="WALA", is_success=False, message=text(e))
+
+ return handler.handler_status
+
+class ExtHandlerInstance(object):
+ def __init__(self, ext_handler, pkg_list, curr_version, installed=False):
+ self.ext_handler = ext_handler
+ self.name = ext_handler.name
+ self.version = ext_handler.properties.version
self.pkg_list = pkg_list
+ self.state = ext_handler.properties.state
+ self.update_policy = ext_handler.properties.upgradePolicy
+
self.curr_version = curr_version
- self.lib_dir = OSUTIL.get_lib_dir()
self.installed = installed
- self.settings = None
-
- #Extension will have no more than 1 settings instance
- if len(extension.properties.extensions) > 0:
- self.settings = extension.properties.extensions[0]
- self.enabled = False
- self.curr_op = None
+ self.handler_state = None
+ self.lib_dir = OSUTIL.get_lib_dir()
+
+ self.ext_status = prot.ExtensionStatus()
+ self.handler_status = prot.ExtHandlerStatus()
+ self.handler_status.name = self.name
+ self.handler_status.version = self.curr_version
+
+ #Currently, extension settings will have no more than 1 instance
+ if len(ext_handler.properties.extensions) > 0:
+ self.ext = ext_handler.properties.extensions[0]
+ self.handler_status.extensions = [self.ext.name]
+ else:
+ #When no extension settings, set sequenceNumber to 0
+ self.ext = prot.Extension(sequenceNumber=0)
+ self.ext_status.sequenceNumber = self.ext.sequenceNumber
prefix = "[{0}]".format(self.get_full_name())
self.logger = logger.Logger(logger.DEFAULT_LOGGER, prefix)
def init_logger(self):
#Init logger appender for extension
- fileutil.mkdir(self.get_log_dir(), mode=0o700)
+ fileutil.mkdir(self.get_log_dir(), mode=0o644)
log_file = os.path.join(self.get_log_dir(), "CommandExecution.log")
self.logger.add_appender(logger.AppenderType.FILE,
- logger.LogLevel.INFO, log_file)
+ logger.LogLevel.INFO, log_file)
def handle(self):
- self.logger.info("Process extension settings:")
- self.logger.info(" Name: {0}", self.get_name())
- self.logger.info(" Version: {0}", self.get_version())
+ self.init_logger()
+ self.logger.verb("Start processing extension handler")
+
+ try:
+ self.handle_state()
+ except ExtensionError as e:
+ self.set_state_err(text(e))
+ self.report_event(is_success=False, message=text(e))
+ self.logger.error("Failed to process extension handler")
+ return
+ try:
+ if self.installed:
+ self.collect_ext_status()
+ self.collect_handler_status()
+ except ExtensionError as e:
+ self.report_event(is_success=False, message=text(e))
+ self.logger.error("Failed to get extension handler status")
+ return
+
+ self.logger.verb("Finished processing extension handler")
+
+ def handle_state(self):
if self.installed:
- self.logger.info("Installed version:{0}", self.curr_version)
- h_status = self.get_handler_status()
- self.enabled = (h_status == "Ready")
-
- state = self.get_state()
- if state == 'enabled':
- self.handle_enable()
- elif state == 'disabled':
- self.handle_disable()
- elif state == 'uninstall':
- self.handle_disable()
- self.handle_uninstall()
+ self.handler_state = self.get_state()
+
+ self.handler_status.status = handler_state_to_status(self.handler_state)
+ self.logger.verb("Handler state: {0}", self.handler_state)
+ self.logger.verb("Sequence number: {0}", self.ext.sequenceNumber)
+
+ if self.state == 'enabled':
+ if self.handler_state == ExtHandlerState.Failed:
+ self.logger.verb("Found previous failure, quit handle_enable")
+ return
+
+ if self.handler_state == ExtHandlerState.Enabled:
+ self.logger.verb("Already enabled with sequenceNumber: {0}",
+ self.ext.sequenceNumber)
+ self.logger.verb("Quit handle_enable")
+ return
+
+ try:
+ new = self.handle_enable()
+ if new is not None:
+ #Upgrade happened
+ new.set_state(ExtHandlerState.Enabled)
+ else:
+ self.set_state(ExtHandlerState.Enabled)
+
+ except ExtensionError as e:
+ self.set_state(ExtHandlerState.Failed)
+ raise e
+ elif self.state == 'disabled':
+ if self.handler_state == ExtHandlerState.Failed:
+ self.logger.verb("Found previous failure, quit handle_disable")
+ return
+
+ if self.handler_state == ExtHandlerState.Disabled:
+ self.logger.verb("Already disabled with sequenceNumber: {0}",
+ self.ext.sequenceNumber)
+ self.logger.verb("Quit handle_disable")
+ return
+
+ try:
+ self.handle_disable()
+ self.set_state(ExtHandlerState.Disabled)
+ except ExtensionError as e:
+ self.set_state(ExtHandlerState.Failed)
+ raise e
+ elif self.state == 'uninstall':
+ try:
+ self.handle_uninstall()
+ except ExtensionError as e:
+ self.set_state(ExtHandlerState.Failed)
+ raise e
else:
- raise ExtensionError("Unknown extension state:{0}".format(state))
+ raise ExtensionError("Unknown state:{0}".format(self.state))
def handle_enable(self):
target_version = self.get_target_version()
+ self.logger.info("Target version: {0}", target_version)
if self.installed:
- if target_version > self.curr_version:
- self.upgrade(target_version)
- elif target_version == self.curr_version:
+ if Version(target_version) > Version(self.curr_version):
+ return self.upgrade(target_version)
+ elif Version(target_version) == Version(self.curr_version):
self.enable()
else:
- raise ExtensionError("A newer version has already been installed")
+ raise ExtensionError("A newer version is already installed")
else:
- if target_version > self.get_version():
+ if Version(target_version) > Version(self.version):
#This will happen when auto upgrade policy is enabled
self.logger.info("Auto upgrade to new version:{0}",
target_version)
@@ -231,21 +334,45 @@ class ExtensionInstance(object):
self.enable()
def handle_disable(self):
- if not self.installed or not self.enabled:
+ if not self.installed:
+ self.logger.verb("Not installed, quit disable")
return
+
self.disable()
def handle_uninstall(self):
if not self.installed:
+ self.logger.verb("Not installed, quit unistall")
+ self.handler_status = None
+ self.ext_status = None
return
+ self.disable()
self.uninstall()
+ def report_event(self, is_success=True, message=""):
+ if self.ext_status is not None:
+ if not is_success:
+ self.ext_status.status = "error"
+ self.ext_status.code = -1
+ if self.handler_status is not None:
+ self.handler_status.message = message
+ if not is_success:
+ self.handler_status.status = "NotReady"
+ add_event(name=self.name, op=self.ext_status.operation,
+ is_success=is_success, message=message)
+
+ def set_operation(self, operation):
+ if self.ext_status.operation != WALAEventOperation.Upgrade:
+ self.ext_status.operation = operation
+
def upgrade(self, target_version):
self.logger.info("Upgrade from: {0} to {1}", self.curr_version,
target_version)
- self.curr_op=WALAEventOperation.Upgrade
+ self.set_operation(WALAEventOperation.Upgrade)
+
old = self
- new = ExtensionInstance(self.extension, self.pkg_list, target_version)
+ new = ExtHandlerInstance(self.ext_handler, self.pkg_list,
+ target_version)
self.logger.info("Download new extension package")
new.init_logger()
new.download()
@@ -262,19 +389,20 @@ class ExtensionInstance(object):
new.install()
self.logger.info("Enable new extension")
new.enable()
- add_event(name=self.get_name(), is_success=True,
- op=self.curr_op, message="")
+ return new
def download(self):
self.logger.info("Download extension package")
- self.curr_op=WALAEventOperation.Download
+ self.set_operation(WALAEventOperation.Download)
+
uris = self.get_package_uris()
package = None
for uri in uris:
try:
resp = restutil.http_get(uri.uri, chk_proxy=True)
- package = resp.read()
- break
+ if resp.status == restutil.httpclient.OK:
+ package = resp.read()
+ break
except restutil.HttpError as e:
self.logger.warn("Failed download extension from: {0}", uri.uri)
@@ -287,14 +415,13 @@ class ExtensionInstance(object):
zipfile.ZipFile(pkg_file).extractall(self.get_base_dir())
chmod = "find {0} -type f | xargs chmod u+x".format(self.get_base_dir())
shellutil.run(chmod)
- add_event(name=self.get_name(), is_success=True,
- op=self.curr_op, message="")
+ self.report_event(message="Download succeeded")
def init_dir(self):
self.logger.info("Initialize extension directory")
#Save HandlerManifest.json
man_file = fileutil.search_file(self.get_base_dir(),
- 'HandlerManifest.json')
+ 'HandlerManifest.json')
man = fileutil.read_file(man_file, remove_bom=True)
fileutil.write_file(self.get_manifest_file(), man)
@@ -303,106 +430,141 @@ class ExtensionInstance(object):
fileutil.mkdir(status_dir, mode=0o700)
conf_dir = self.get_conf_dir()
fileutil.mkdir(conf_dir, mode=0o700)
-
- #Init handler state to uninstall
- self.set_handler_status("NotReady")
+
+ self.make_handler_state_dir()
#Save HandlerEnvironment.json
self.create_handler_env()
def enable(self):
self.logger.info("Enable extension.")
- self.curr_op=WALAEventOperation.Enable
+ self.set_operation(WALAEventOperation.Enable)
+
man = self.load_manifest()
self.launch_command(man.get_enable_command())
- self.set_handler_status("Ready")
- add_event(name=self.get_name(), is_success=True,
- op=self.curr_op, message="")
def disable(self):
self.logger.info("Disable extension.")
- self.curr_op=WALAEventOperation.Disable
+ self.set_operation(WALAEventOperation.Disable)
+
man = self.load_manifest()
self.launch_command(man.get_disable_command(), timeout=900)
- self.set_handler_status("Ready")
- add_event(name=self.get_name(), is_success=True,
- op=self.curr_op, message="")
def install(self):
self.logger.info("Install extension.")
- self.curr_op=WALAEventOperation.Install
+ self.set_operation(WALAEventOperation.Install)
+
man = self.load_manifest()
- self.set_handler_status("Installing")
self.launch_command(man.get_install_command(), timeout=900)
- self.set_handler_status("Ready")
- add_event(name=self.get_name(), is_success=True,
- op=self.curr_op, message="")
+ self.installed = True
def uninstall(self):
self.logger.info("Uninstall extension.")
- self.curr_op=WALAEventOperation.UnInstall
+ self.set_operation(WALAEventOperation.UnInstall)
+
man = self.load_manifest()
self.launch_command(man.get_uninstall_command())
- self.set_handler_status("NotReady")
- add_event(name=self.get_name(), is_success=True,
- op=self.curr_op, message="")
+
+ self.logger.info("Remove ext handler dir: {0}", self.get_base_dir())
+ try:
+ shutil.rmtree(self.get_base_dir())
+ except IOError as e:
+ raise ExtensionError("Failed to rm ext handler dir: {0}".format(e))
+
+ self.installed = False
+ self.handler_status = None
+ self.ext_status = None
def update(self):
self.logger.info("Update extension.")
- self.curr_op=WALAEventOperation.Update
+ self.set_operation(WALAEventOperation.Update)
+
man = self.load_manifest()
self.launch_command(man.get_update_command(), timeout=900)
- add_event(name=self.get_name(), is_success=True,
- op=self.curr_op, message="")
-
- def create_handler_status(self, ext_status, heartbeat=None):
- status = prot.ExtensionHandlerStatus()
- status.handlerName = self.get_name()
- status.handlerVersion = self.get_version()
- status.status = self.get_handler_status()
- status.extensionStatusList.append(ext_status)
- return status
def collect_handler_status(self):
+ self.logger.verb("Collect extension handler status")
+ if self.handler_status is None:
+ return
+
+ handler_state = self.get_state()
+ self.handler_status.status = handler_state_to_status(handler_state)
+ self.handler_status.message = self.get_state_err()
man = self.load_manifest()
- heartbeat=None
if man.is_report_heartbeat():
heartbeat = self.collect_heartbeat()
- ext_status = self.collect_extension_status()
- status= self.create_handler_status(ext_status, heartbeat)
- status.status = self.get_handler_status()
- if heartbeat is not None:
- status.status = heartbeat['status']
- status.extensionStatusList.append(ext_status)
- return status
-
- def collect_extension_status(self):
+ if heartbeat is not None:
+ self.handler_status.status = heartbeat['status']
+
+ def collect_ext_status(self):
+ self.logger.verb("Collect extension status")
+ if self.handler_status is None:
+ return
+
+ if self.ext is None:
+ return
+
ext_status_file = self.get_status_file()
try:
- ext_status_str = fileutil.read_file(ext_status_file)
- ext_status = json.loads(ext_status_str)
+ data_str = fileutil.read_file(ext_status_file)
+ data = json.loads(data_str)
+ parse_ext_status(self.ext_status, data)
except IOError as e:
raise ExtensionError("Failed to get status file: {0}".format(e))
except ValueError as e:
raise ExtensionError("Malformed status file: {0}".format(e))
- return ext_status_to_v2(ext_status[0],
- self.settings.sequenceNumber)
+
+ def make_handler_state_dir(self):
+ handler_state_dir = self.get_handler_state_dir()
+ fileutil.mkdir(handler_state_dir, 0o600)
+ if not os.path.exists(handler_state_dir):
+ os.makedirs(handler_state_dir)
- def get_handler_status(self):
- h_status = "uninstalled"
- h_status_file = self.get_handler_state_file()
+ def get_state(self):
+ handler_state_file = self.get_handler_state_file()
+ if not os.path.isfile(handler_state_file):
+ return None
try:
- h_status = fileutil.read_file(h_status_file)
- return h_status
+ handler_state = fileutil.read_file(handler_state_file)
+ if handler_state is not None:
+ handler_state = handler_state.rstrip()
+ return handler_state
except IOError as e:
- raise ExtensionError("Failed to get handler status: {0}".format(e))
+ err = "Failed to get handler state: {0}".format(e)
+ add_event(name=self.name, is_success=False, message=err)
- def set_handler_status(self, status):
- h_status_file = self.get_handler_state_file()
+ def set_state(self, state):
+ handler_state_file = self.get_handler_state_file()
+ if not os.path.isfile(handler_state_file):
+ self.make_handler_state_dir()
+ try:
+ fileutil.write_file(handler_state_file, state)
+ except IOError as e:
+ err = "Failed to set handler state: {0}".format(e)
+ add_event(name=self.name, is_success=False, message=err)
+
+ def get_state_err(self):
+ """Get handler error message"""
+ handler_state_err_file= self.get_handler_state_err_file()
+ if not os.path.isfile(handler_state_err_file):
+ return None
try:
- fileutil.write_file(h_status_file, status)
+ message = fileutil.read_file(handler_state_err_file)
+ return message
except IOError as e:
- raise ExtensionError("Failed to set handler status: {0}".format(e))
+ err = "Failed to get handler state message: {0}".format(e)
+ add_event(name=self.name, is_success=False, message=err)
+
+ def set_state_err(self, message):
+ """Set handler error message"""
+ handler_state_err_file = self.get_handler_state_err_file()
+ if not os.path.isfile(handler_state_err_file):
+ self.make_handler_state_dir()
+ try:
+ fileutil.write_file(handler_state_err_file, message)
+ except IOError as e:
+ err = "Failed to set handler state message: {0}".format(e)
+ add_event(name=self.name, is_success=False, message=err)
def collect_heartbeat(self):
self.logger.info("Collect heart beat")
@@ -426,7 +588,7 @@ class ExtensionInstance(object):
return heartbeat
def is_responsive(self, heartbeat_file):
- last_update=int(time.time()-os.stat(heartbeat_file).st_mtime)
+ last_update=int(time.time() - os.stat(heartbeat_file).st_mtime)
return last_update > 600 # not updated for more than 10 min
def launch_command(self, cmd, timeout=300):
@@ -452,6 +614,7 @@ class ExtensionInstance(object):
ret = child.wait()
if ret == None or ret != 0:
raise ExtensionError("Non-zero exit code: {0}, {1}".format(ret, cmd))
+ self.report_event(message="Launch command succeeded: {0}".format(cmd))
def load_manifest(self):
man_file = self.get_manifest_file()
@@ -464,16 +627,15 @@ class ExtensionInstance(object):
return HandlerManifest(data[0])
-
def update_settings(self):
- if self.settings is None:
- self.logger.verbose("Extension has no settings")
+ if self.ext is None:
+ self.logger.verb("Extension has no settings")
return
settings = {
- 'publicSettings': self.settings.publicSettings,
- 'protectedSettings': self.settings.privateSettings,
- 'protectedSettingsCertThumbprint': self.settings.certificateThumbprint
+ 'publicSettings': self.ext.publicSettings,
+ 'protectedSettings': self.ext.privateSettings,
+ 'protectedSettingsCertThumbprint': self.ext.certificateThumbprint
}
ext_settings = {
"runtimeSettings":[{
@@ -482,13 +644,10 @@ class ExtensionInstance(object):
}
fileutil.write_file(self.get_settings_file(), json.dumps(ext_settings))
- latest = os.path.join(self.get_conf_dir(), "latest")
- fileutil.write_file(latest, self.settings.sequenceNumber)
-
def create_handler_env(self):
env = [{
- "name": self.get_name(),
- "version" : self.get_version(),
+ "name": self.name,
+ "version" : HANDLER_ENVIRONMENT_VERSION,
"handlerEnvironment" : {
"logFolder" : self.get_log_dir(),
"configFolder" : self.get_conf_dir(),
@@ -500,8 +659,8 @@ class ExtensionInstance(object):
json.dumps(env))
def get_target_version(self):
- version = self.get_version()
- update_policy = self.get_upgrade_policy()
+ version = self.version
+ update_policy = self.update_policy
if update_policy is None or update_policy.lower() != 'auto':
return version
@@ -509,45 +668,29 @@ class ExtensionInstance(object):
if major is None:
raise ExtensionError("Wrong version format: {0}".format(version))
- packages = [x for x in self.pkg_list.versions if x.version.startswith(major + ".")]
- packages = sorted(packages, key=lambda x: x.version, reverse=True)
+ packages = [x for x in self.pkg_list.versions \
+ if x.version.startswith(major + ".")]
+ packages = sorted(packages, key=lambda x: Version(x.version),
+ reverse=True)
if len(packages) <= 0:
raise ExtensionError("Can't find version: {0}.*".format(major))
return packages[0].version
def get_package_uris(self):
- version = self.get_version()
+ version = self.curr_version
packages = self.pkg_list.versions
if packages is None:
raise ExtensionError("Package uris is None.")
for package in packages:
- if package.version == version:
+ if Version(package.version) == Version(version):
return package.uris
raise ExtensionError("Can't get package uris for {0}.".format(version))
-
- def get_curr_op(self):
- return self.curr_op
-
- def get_name(self):
- return self.extension.name
-
- def get_version(self):
- return self.extension.properties.version
-
- def get_state(self):
- return self.extension.properties.state
-
- def get_seq_no(self):
- return self.settings.sequenceNumber
-
- def get_upgrade_policy(self):
- return self.extension.properties.upgradePolicy
-
+
def get_full_name(self):
- return "{0}-{1}".format(self.get_name(), self.curr_version)
+ return "{0}-{1}".format(self.name, self.curr_version)
def get_base_dir(self):
return os.path.join(OSUTIL.get_lib_dir(), self.get_full_name())
@@ -557,17 +700,27 @@ class ExtensionInstance(object):
def get_status_file(self):
return os.path.join(self.get_status_dir(),
- "{0}.status".format(self.settings.sequenceNumber))
+ "{0}.status".format(self.ext.sequenceNumber))
def get_conf_dir(self):
return os.path.join(self.get_base_dir(), 'config')
def get_settings_file(self):
return os.path.join(self.get_conf_dir(),
- "{0}.settings".format(self.settings.sequenceNumber))
+ "{0}.settings".format(self.ext.sequenceNumber))
+
+ def get_handler_state_dir(self):
+ return os.path.join(OSUTIL.get_lib_dir(), "handler_state",
+ self.get_full_name())
def get_handler_state_file(self):
- return os.path.join(self.get_conf_dir(), 'HandlerState')
+ return os.path.join(self.get_handler_state_dir(),
+ '{0}.state'.format(self.ext.sequenceNumber))
+
+ def get_handler_state_err_file(self):
+ return os.path.join(self.get_handler_state_dir(),
+ '{0}.error'.format(self.ext.sequenceNumber))
+
def get_heartbeat_file(self):
return os.path.join(self.get_base_dir(), 'heartbeat.log')
@@ -579,7 +732,7 @@ class ExtensionInstance(object):
return os.path.join(self.get_base_dir(), 'HandlerEnvironment.json')
def get_log_dir(self):
- return os.path.join(OSUTIL.get_ext_log_dir(), self.get_name(),
+ return os.path.join(OSUTIL.get_ext_log_dir(), self.name,
self.curr_version)
class HandlerEnvironment(object):
diff --git a/azurelinuxagent/distro/default/handlerFactory.py b/azurelinuxagent/distro/default/handlerFactory.py
index 98b2380..dceb2a3 100644
--- a/azurelinuxagent/distro/default/handlerFactory.py
+++ b/azurelinuxagent/distro/default/handlerFactory.py
@@ -1,4 +1,4 @@
-# Windows Azure Linux Agent
+# Microsoft Azure Linux Agent
#
# Copyright 2014 Microsoft Corporation
#
@@ -23,7 +23,7 @@ from .dhcp import DhcpHandler
from .env import EnvHandler
from .provision import ProvisionHandler
from .resourceDisk import ResourceDiskHandler
-from .extension import ExtensionsHandler
+from .extension import ExtHandlersHandler
from .deprovision import DeprovisionHandler
class DefaultHandlerFactory(object):
@@ -35,6 +35,6 @@ class DefaultHandlerFactory(object):
self.env_handler = EnvHandler(self)
self.provision_handler = ProvisionHandler()
self.resource_disk_handler = ResourceDiskHandler()
- self.extension_handler = ExtensionsHandler()
+ self.ext_handlers_handler = ExtHandlersHandler()
self.deprovision_handler = DeprovisionHandler()
diff --git a/azurelinuxagent/distro/default/init.py b/azurelinuxagent/distro/default/init.py
index 337fdea..db74fef 100644
--- a/azurelinuxagent/distro/default/init.py
+++ b/azurelinuxagent/distro/default/init.py
@@ -1,4 +1,4 @@
-# Windows Azure Linux Agent
+# Microsoft Azure Linux Agent
#
# Copyright 2014 Microsoft Corporation
#
diff --git a/azurelinuxagent/distro/default/loader.py b/azurelinuxagent/distro/default/loader.py
index d7dbe87..55a51e0 100644
--- a/azurelinuxagent/distro/default/loader.py
+++ b/azurelinuxagent/distro/default/loader.py
@@ -1,4 +1,4 @@
-# Windows Azure Linux Agent
+# Microsoft Azure Linux Agent
#
# Copyright 2014 Microsoft Corporation
#
diff --git a/azurelinuxagent/distro/default/osutil.py b/azurelinuxagent/distro/default/osutil.py
index 8e3fb77..00a57cc 100644
--- a/azurelinuxagent/distro/default/osutil.py
+++ b/azurelinuxagent/distro/default/osutil.py
@@ -117,22 +117,16 @@ class DefaultOSUtil(object):
"retcode:{1}, "
"output:{2}").format(username, retcode, out))
- def chpasswd(self, username, password, use_salt=True, salt_type=6,
- salt_len=10):
+ def chpasswd(self, username, password, crypt_id=6, salt_len=10):
if self.is_sys_user(username):
raise OSUtilError(("User {0} is a system user. "
"Will not set passwd.").format(username))
- passwd_hash = textutil.gen_password_hash(password, use_salt, salt_type,
- salt_len)
- try:
- passwd_content = fileutil.read_file(self.passwd_file_path)
- passwd = passwd_content.split("\n")
- new_passwd = [x for x in passwd if not x.startswith(username)]
- new_passwd.append("{0}:{1}:14600::::::".format(username, passwd_hash))
- fileutil.write_file(self.passwd_file_path, "\n".join(new_passwd))
- except IOError as e:
+ passwd_hash = textutil.gen_password_hash(password, crypt_id, salt_len)
+ cmd = "usermod -p '{0}' {1}".format(passwd_hash, username)
+ ret, output = shellutil.run_get_output(cmd, log_cmd=False)
+ if ret != 0:
raise OSUtilError(("Failed to set password for {0}: {1}"
- "").format(username, e))
+ "").format(username, output))
def conf_sudoer(self, username, nopasswd):
# for older distros create sudoers.d
@@ -344,9 +338,10 @@ class DefaultOSUtil(object):
raise OSUtilError("Failed to umount dvd.")
def eject_dvd(self, chk_err=True):
- retcode = shellutil.run("eject")
+ dvd = self.get_dvd_device()
+ retcode = shellutil.run("eject {0}".format(dvd))
if chk_err and retcode != 0:
- raise OSUtilError("Failed to eject dvd")
+ raise OSUtilError("Failed to eject dvd: ret={0}".format(retcode))
def load_atappix_mod(self):
if self.is_atapiix_mod_loaded():
diff --git a/azurelinuxagent/distro/default/provision.py b/azurelinuxagent/distro/default/provision.py
index 1e9c459..424f083 100644
--- a/azurelinuxagent/distro/default/provision.py
+++ b/azurelinuxagent/distro/default/provision.py
@@ -49,8 +49,13 @@ class ProvisionHandler(object):
protocol = prot.FACTORY.get_default_protocol()
try:
status = prot.ProvisionStatus(status="NotReady",
- subStatus="Provision started")
- protocol.report_provision_status(status)
+ subStatus="Provisioning",
+ description="Starting")
+ try:
+ protocol.report_provision_status(status)
+ except prot.ProtocolError as e:
+ add_event(name="WALA", is_success=False, message=text(e),
+ op=WALAEventOperation.Provision)
self.provision()
fileutil.write_file(provisioned, "")
@@ -59,17 +64,28 @@ class ProvisionHandler(object):
logger.info("Finished provisioning")
status = prot.ProvisionStatus(status="Ready")
status.properties.certificateThumbprint = thumbprint
- protocol.report_provision_status(status)
+
+ try:
+ protocol.report_provision_status(status)
+ except prot.ProtocolError as pe:
+ add_event(name="WALA", is_success=False, message=text(pe),
+ op=WALAEventOperation.Provision)
add_event(name="WALA", is_success=True, message="",
- op=WALAEventOperation.Provision)
+ op=WALAEventOperation.Provision)
except ProvisionError as e:
logger.error("Provision failed: {0}", e)
status = prot.ProvisionStatus(status="NotReady",
- subStatus= text(e))
- protocol.report_provision_status(status)
+ subStatus="ProvisioningFailed",
+ description= text(e))
+ try:
+ protocol.report_provision_status(status)
+ except prot.ProtocolError as pe:
+ add_event(name="WALA", is_success=False, message=text(pe),
+ op=WALAEventOperation.Provision)
+
add_event(name="WALA", is_success=False, message=text(e),
- op=WALAEventOperation.Provision)
+ op=WALAEventOperation.Provision)
def reg_ssh_host_key(self):
keypair_type = conf.get("Provisioning.SshHostKeyPairType", "rsa")
@@ -120,10 +136,10 @@ class ProvisionHandler(object):
if ovfenv.user_password is not None:
logger.info("Set user password.")
- use_salt = conf.get_switch("Provision.UseSalt", True)
- salt_type = conf.get_switch("Provision.SaltType", 6)
- OSUTIL.chpasswd(ovfenv.username, ovfenv.user_password,
- use_salt,salt_type)
+ crypt_id = conf.get("Provision.PasswordCryptId", "6")
+ salt_len = conf.get_int("Provision.PasswordCryptSaltLength", 10)
+ OSUTIL.chpasswd(ovfenv.username, ovfenv.user_password,
+ crypt_id=crypt_id, salt_len=salt_len)
logger.info("Configure sudoer")
OSUTIL.conf_sudoer(ovfenv.username, ovfenv.user_password is None)
diff --git a/azurelinuxagent/distro/default/resourceDisk.py b/azurelinuxagent/distro/default/resourceDisk.py
index d4ef1c9..734863c 100644
--- a/azurelinuxagent/distro/default/resourceDisk.py
+++ b/azurelinuxagent/distro/default/resourceDisk.py
@@ -1,4 +1,4 @@
-# Windows Azure Linux Agent
+# Microsoft Azure Linux Agent
#
# Copyright 2014 Microsoft Corporation
#
diff --git a/azurelinuxagent/distro/default/run.py b/azurelinuxagent/distro/default/run.py
index 13880b4..dfd3b03 100644
--- a/azurelinuxagent/distro/default/run.py
+++ b/azurelinuxagent/distro/default/run.py
@@ -1,4 +1,4 @@
-# Windows Azure Linux Agent
+# Microsoft Azure Linux Agent
#
# Copyright 2014 Microsoft Corporation
#
@@ -27,8 +27,8 @@ from azurelinuxagent.metadata import AGENT_LONG_NAME, AGENT_VERSION, \
DISTRO_NAME, DISTRO_VERSION, \
DISTRO_FULL_NAME, PY_VERSION_MAJOR, \
PY_VERSION_MINOR, PY_VERSION_MICRO
-import azurelinuxagent.protocol as prot
import azurelinuxagent.event as event
+import azurelinuxagent.protocol as prot
from azurelinuxagent.utils.osutil import OSUTIL
import azurelinuxagent.utils.fileutil as fileutil
@@ -65,22 +65,7 @@ class MainHandler(object):
protocol = prot.FACTORY.get_default_protocol()
while True:
-
#Handle extensions
- h_status_list = self.handlers.extension_handler.process()
-
- #Report status
- vm_status = prot.VMStatus()
- vm_status.vmAgent.agentVersion = AGENT_LONG_NAME
- vm_status.vmAgent.status = "Ready"
- vm_status.vmAgent.message = "Guest Agent is running"
- for h_status in h_status_list:
- vm_status.extensionHandlers.append(h_status)
- try:
- logger.info("Report vm status")
- protocol.report_status(vm_status)
- except prot.ProtocolError as e:
- logger.error("Failed to report vm status: {0}", e)
-
+ self.handlers.ext_handlers_handler.process()
time.sleep(25)
diff --git a/azurelinuxagent/distro/default/scvmm.py b/azurelinuxagent/distro/default/scvmm.py
index 18fad4b..680c04b 100644
--- a/azurelinuxagent/distro/default/scvmm.py
+++ b/azurelinuxagent/distro/default/scvmm.py
@@ -1,4 +1,4 @@
-# Windows Azure Linux Agent
+# Microsoft Azure Linux Agent
#
# Copyright 2014 Microsoft Corporation
#
diff --git a/azurelinuxagent/distro/loader.py b/azurelinuxagent/distro/loader.py
index 0060a7f..375abd2 100644
--- a/azurelinuxagent/distro/loader.py
+++ b/azurelinuxagent/distro/loader.py
@@ -25,7 +25,7 @@ def get_distro_loader():
logger.verb("Loading distro implemetation from: {0}", DISTRO_NAME)
pkg_name = "azurelinuxagent.distro.{0}.loader".format(DISTRO_NAME)
return __import__(pkg_name, fromlist="loader")
- except ImportError as e:
+ except (ImportError, ValueError):
logger.warn("Unable to load distro implemetation for {0}.", DISTRO_NAME)
logger.warn("Use default distro implemetation instead.")
return default_loader
diff --git a/azurelinuxagent/distro/oracle/__init__.py b/azurelinuxagent/distro/oracle/__init__.py
index 4b2b9e1..d9b82f5 100644
--- a/azurelinuxagent/distro/oracle/__init__.py
+++ b/azurelinuxagent/distro/oracle/__init__.py
@@ -1,4 +1,4 @@
-# Windows Azure Linux Agent
+# Microsoft Azure Linux Agent
#
# Copyright 2014 Microsoft Corporation
#
diff --git a/azurelinuxagent/distro/oracle/loader.py b/azurelinuxagent/distro/oracle/loader.py
index 379f027..9dc428f 100644
--- a/azurelinuxagent/distro/oracle/loader.py
+++ b/azurelinuxagent/distro/oracle/loader.py
@@ -1,4 +1,4 @@
-# Windows Azure Linux Agent
+# Microsoft Azure Linux Agent
#
# Copyright 2014 Microsoft Corporation
#
diff --git a/azurelinuxagent/distro/redhat/__init__.py b/azurelinuxagent/distro/redhat/__init__.py
index 4b2b9e1..d9b82f5 100644
--- a/azurelinuxagent/distro/redhat/__init__.py
+++ b/azurelinuxagent/distro/redhat/__init__.py
@@ -1,4 +1,4 @@
-# Windows Azure Linux Agent
+# Microsoft Azure Linux Agent
#
# Copyright 2014 Microsoft Corporation
#
diff --git a/azurelinuxagent/distro/redhat/loader.py b/azurelinuxagent/distro/redhat/loader.py
index 911e74d..8d3c75b 100644
--- a/azurelinuxagent/distro/redhat/loader.py
+++ b/azurelinuxagent/distro/redhat/loader.py
@@ -1,4 +1,4 @@
-# Windows Azure Linux Agent
+# Microsoft Azure Linux Agent
#
# Copyright 2014 Microsoft Corporation
#
diff --git a/azurelinuxagent/distro/redhat/osutil.py b/azurelinuxagent/distro/redhat/osutil.py
index c6c3016..7478867 100644
--- a/azurelinuxagent/distro/redhat/osutil.py
+++ b/azurelinuxagent/distro/redhat/osutil.py
@@ -122,15 +122,14 @@ class Redhat6xOSUtil(DefaultOSUtil):
ret= shellutil.run_get_output("pidof dhclient")
return ret[1] if ret[0] == 0 else None
-class RedhatOSUtil(Redhat6xOSUtil):
- def __init__(self):
- super(RedhatOSUtil, self).__init__()
-
def set_hostname(self, hostname):
- super(RedhatOSUtil, self).set_hostname(hostname)
+ """
+ Set /etc/sysconfig/network
+ """
fileutil.update_conf_file('/etc/sysconfig/network',
'HOSTNAME',
'HOSTNAME={0}'.format(hostname))
+ shellutil.run("hostname {0}".format(hostname), chk_err=False)
def set_dhcp_hostname(self, hostname):
ifname = self.get_if_name()
@@ -139,6 +138,24 @@ class RedhatOSUtil(Redhat6xOSUtil):
'DHCP_HOSTNAME',
'DHCP_HOSTNAME={0}'.format(hostname))
+class RedhatOSUtil(Redhat6xOSUtil):
+ def __init__(self):
+ super(RedhatOSUtil, self).__init__()
+
+ def set_hostname(self, hostname):
+ """
+ Set /etc/hostname
+ Unlike redhat 6.x, redhat 7.x will set hostname to /etc/hostname
+ """
+ DefaultOSUtil.set_hostname(self, hostname)
+
+ def publish_hostname(self, hostname):
+ """
+ Restart NetworkManager first before publishing hostname
+ """
+ shellutil.run("service NetworkManager restart")
+ super(RedhatOSUtil, self).publish_hostname(hostname)
+
def register_agent_service(self):
return shellutil.run("systemctl enable waagent", chk_err=False)
diff --git a/azurelinuxagent/distro/suse/__init__.py b/azurelinuxagent/distro/suse/__init__.py
index 4b2b9e1..d9b82f5 100644
--- a/azurelinuxagent/distro/suse/__init__.py
+++ b/azurelinuxagent/distro/suse/__init__.py
@@ -1,4 +1,4 @@
-# Windows Azure Linux Agent
+# Microsoft Azure Linux Agent
#
# Copyright 2014 Microsoft Corporation
#
diff --git a/azurelinuxagent/distro/suse/loader.py b/azurelinuxagent/distro/suse/loader.py
index e38aa17..b01384b 100644
--- a/azurelinuxagent/distro/suse/loader.py
+++ b/azurelinuxagent/distro/suse/loader.py
@@ -1,4 +1,4 @@
-# Windows Azure Linux Agent
+# Microsoft Azure Linux Agent
#
# Copyright 2014 Microsoft Corporation
#
diff --git a/azurelinuxagent/distro/suse/osutil.py b/azurelinuxagent/distro/suse/osutil.py
index 870e0b7..8d6f5bf 100644
--- a/azurelinuxagent/distro/suse/osutil.py
+++ b/azurelinuxagent/distro/suse/osutil.py
@@ -79,6 +79,26 @@ class SUSEOSUtil(SUSE11OSUtil):
super(SUSEOSUtil, self).__init__()
self.dhclient_name = 'wickedd-dhcp4'
+ def stop_dhcp_service(self):
+ cmd = "systemctl stop {0}".format(self.dhclient_name)
+ return shellutil.run(cmd, chk_err=False)
+
+ def start_dhcp_service(self):
+ cmd = "systemctl start {0}".format(self.dhclient_name)
+ return shellutil.run(cmd, chk_err=False)
+
+ def start_network(self) :
+ return shellutil.run("systemctl start network", chk_err=False)
+
+ def restart_ssh_service(self):
+ return shellutil.run("systemctl restart sshd", chk_err=False)
+
+ def stop_agent_service(self):
+ return shellutil.run("systemctl stop waagent", chk_err=False)
+
+ def start_agent_service(self):
+ return shellutil.run("systemctl start waagent", chk_err=False)
+
def register_agent_service(self):
return shellutil.run("systemctl enable waagent", chk_err=False)
diff --git a/azurelinuxagent/distro/ubuntu/__init__.py b/azurelinuxagent/distro/ubuntu/__init__.py
index 4b2b9e1..d9b82f5 100644
--- a/azurelinuxagent/distro/ubuntu/__init__.py
+++ b/azurelinuxagent/distro/ubuntu/__init__.py
@@ -1,4 +1,4 @@
-# Windows Azure Linux Agent
+# Microsoft Azure Linux Agent
#
# Copyright 2014 Microsoft Corporation
#
diff --git a/azurelinuxagent/distro/ubuntu/deprovision.py b/azurelinuxagent/distro/ubuntu/deprovision.py
index 10fa123..0c3c4e5 100644
--- a/azurelinuxagent/distro/ubuntu/deprovision.py
+++ b/azurelinuxagent/distro/ubuntu/deprovision.py
@@ -1,4 +1,4 @@
-# Windows Azure Linux Agent
+# Microsoft Azure Linux Agent
#
# Copyright 2014 Microsoft Corporation
#
diff --git a/azurelinuxagent/distro/ubuntu/handlerFactory.py b/azurelinuxagent/distro/ubuntu/handlerFactory.py
index c8d0906..11f7f04 100644
--- a/azurelinuxagent/distro/ubuntu/handlerFactory.py
+++ b/azurelinuxagent/distro/ubuntu/handlerFactory.py
@@ -1,4 +1,4 @@
-# Windows Azure Linux Agent
+# Microsoft Azure Linux Agent
#
# Copyright 2014 Microsoft Corporation
#
diff --git a/azurelinuxagent/distro/ubuntu/loader.py b/azurelinuxagent/distro/ubuntu/loader.py
index 26db4fa..3fe2239 100644
--- a/azurelinuxagent/distro/ubuntu/loader.py
+++ b/azurelinuxagent/distro/ubuntu/loader.py
@@ -1,4 +1,4 @@
-# Windows Azure Linux Agent
+# Microsoft Azure Linux Agent
#
# Copyright 2014 Microsoft Corporation
#
@@ -17,16 +17,20 @@
# Requires Python 2.4+ and Openssl 1.0+
#
-from azurelinuxagent.metadata import DISTRO_NAME, DISTRO_VERSION
+from azurelinuxagent.metadata import DISTRO_NAME, DISTRO_VERSION, DISTRO_FULL_NAME
def get_osutil():
from azurelinuxagent.distro.ubuntu.osutil import Ubuntu1204OSUtil, \
UbuntuOSUtil, \
- Ubuntu14xOSUtil
+ Ubuntu14xOSUtil, \
+ UbuntuSnappyOSUtil
+
if DISTRO_VERSION == "12.04":
return Ubuntu1204OSUtil()
elif DISTRO_VERSION == "14.04" or DISTRO_VERSION == "14.10":
return Ubuntu14xOSUtil()
+ elif DISTRO_FULL_NAME == "Snappy Ubuntu Core":
+ return UbuntuSnappyOSUtil()
else:
return UbuntuOSUtil()
diff --git a/azurelinuxagent/distro/ubuntu/osutil.py b/azurelinuxagent/distro/ubuntu/osutil.py
index 1e51c2a..adf7660 100644
--- a/azurelinuxagent/distro/ubuntu/osutil.py
+++ b/azurelinuxagent/distro/ubuntu/osutil.py
@@ -63,3 +63,13 @@ class UbuntuOSUtil(Ubuntu14xOSUtil):
def unregister_agent_service(self):
return shellutil.run("systemctl mask walinuxagent", chk_err=False)
+class UbuntuSnappyOSUtil(Ubuntu14xOSUtil):
+ def __init__(self):
+ super(UbuntuSnappyOSUtil, self).__init__()
+ self.conf_file_path = '/apps/walinuxagent/current/waagent.conf'
+
+ def remove_rules_files(self, rules_files=""):
+ pass
+
+ def restore_rules_files(self, rules_files=""):
+ pass
diff --git a/azurelinuxagent/distro/ubuntu/provision.py b/azurelinuxagent/distro/ubuntu/provision.py
index 7551074..a68fe4d 100644
--- a/azurelinuxagent/distro/ubuntu/provision.py
+++ b/azurelinuxagent/distro/ubuntu/provision.py
@@ -1,4 +1,4 @@
-# Windows Azure Linux Agent
+# Microsoft Azure Linux Agent
#
# Copyright 2014 Microsoft Corporation
#
@@ -23,6 +23,7 @@ import azurelinuxagent.logger as logger
from azurelinuxagent.future import text
import azurelinuxagent.conf as conf
import azurelinuxagent.protocol as prot
+from azurelinuxagent.event import add_event, WALAEventOperation
from azurelinuxagent.exception import *
from azurelinuxagent.utils.osutil import OSUTIL
import azurelinuxagent.utils.shellutil as shellutil
@@ -54,11 +55,25 @@ class UbuntuProvisionHandler(ProvisionHandler):
logger.info("Finished provisioning")
status = prot.ProvisionStatus(status="Ready")
status.properties.certificateThumbprint = thumbprint
- protocol.report_provision_status(status)
+ try:
+ protocol.report_provision_status(status)
+ except prot.ProtocolError as pe:
+ add_event(name="WALA", is_success=False, message=text(pe),
+ op=WALAEventOperation.Provision)
except ProvisionError as e:
logger.error("Provision failed: {0}", e)
- protocol.report_provision_status(status="NotReady", subStatus=text(e))
+ status = prot.ProvisionStatus(status="NotReady",
+ subStatus="ProvisioningFailed",
+ description= text(e))
+ try:
+ protocol.report_provision_status(status)
+ except prot.ProtocolError as pe:
+ add_event(name="WALA", is_success=False, message=text(pe),
+ op=WALAEventOperation.Provision)
+
+ add_event(name="WALA", is_success=False, message=text(e),
+ op=WALAEventOperation.Provision)
def wait_for_ssh_host_key(self, max_retry=60):
kepair_type = conf.get("Provisioning.SshHostKeyPairType", "rsa")
diff --git a/azurelinuxagent/event.py b/azurelinuxagent/event.py
index f866a22..02e8017 100644
--- a/azurelinuxagent/event.py
+++ b/azurelinuxagent/event.py
@@ -82,9 +82,11 @@ class EventMonitor(object):
def collect_event(self, evt_file_name):
try:
+ logger.verb("Found event file: {0}", evt_file_name)
with open(evt_file_name, "rb") as evt_file:
#if fail to open or delete the file, throw exception
json_str = evt_file.read().decode("utf-8",'ignore')
+ logger.verb("Processed event file: {0}", evt_file_name)
os.remove(evt_file_name)
return json_str
except IOError as e:
@@ -107,11 +109,11 @@ class EventMonitor(object):
data = json.loads(data_str)
except ValueError as e:
logger.verb(data_str)
- logger.error("Failed to decode json event file{0}", e)
+ logger.error("Failed to decode json event file: {0}", e)
continue
event = prot.TelemetryEvent()
- prot.set_properties(event, data)
+ prot.set_properties("event", event, data)
event.parameters.extend(self.sysinfo)
event_list.events.append(event)
if len(event_list.events) == 0:
@@ -151,8 +153,10 @@ def save_event(data):
except IOError as e:
raise EventError("Failed to write events to file:{0}", e)
-def add_event(name, op, is_success, duration=0, version="1.0",
+def add_event(name, op="", is_success=True, duration=0, version="1.0",
message="", evt_type="", is_internal=False):
+ log = logger.info if is_success else logger.error
+ log("Event: name={0}, op={1}, message={2}", name, op, message)
event = prot.TelemetryEvent(1, "69B669B9-4AF8-4C50-BDC4-6006FA76E975")
event.parameters.append(prot.TelemetryEventParam('Name', name))
event.parameters.append(prot.TelemetryEventParam('Version', version))
@@ -179,7 +183,6 @@ def dump_unhandled_err(name):
error = traceback.format_exception(last_type, last_value,
last_traceback)
message= "".join(error)
- logger.error(message)
add_event(name, is_success=False, message=message,
op=WALAEventOperation.UnhandledError)
diff --git a/azurelinuxagent/exception.py b/azurelinuxagent/exception.py
index 7c31394..d7d9b0a 100644
--- a/azurelinuxagent/exception.py
+++ b/azurelinuxagent/exception.py
@@ -1,4 +1,4 @@
-# Windows Azure Linux Agent
+# Microsoft Azure Linux Agent
#
# Copyright 2014 Microsoft Corporation
#
diff --git a/azurelinuxagent/future.py b/azurelinuxagent/future.py
index 8186fcf..8451345 100644
--- a/azurelinuxagent/future.py
+++ b/azurelinuxagent/future.py
@@ -9,11 +9,13 @@ if sys.version_info[0]== 3:
from urllib.parse import urlparse
text = str
bytebuffer = memoryview
+ read_input = input
elif sys.version_info[0] == 2:
import httplib as httpclient
from urlparse import urlparse
text = unicode
bytebuffer = buffer
+ read_input = raw_input
else:
raise ImportError("Unknown python version:{0}".format(sys.version_info))
diff --git a/azurelinuxagent/handler.py b/azurelinuxagent/handler.py
index c180112..538ee30 100644
--- a/azurelinuxagent/handler.py
+++ b/azurelinuxagent/handler.py
@@ -1,4 +1,4 @@
-# Windows Azure Linux Agent
+# Microsoft Azure Linux Agent
#
# Copyright 2014 Microsoft Corporation
#
diff --git a/azurelinuxagent/logger.py b/azurelinuxagent/logger.py
index 126d6bc..21c02a6 100644
--- a/azurelinuxagent/logger.py
+++ b/azurelinuxagent/logger.py
@@ -20,10 +20,9 @@
"""
Log utils
"""
-
+import os
import sys
from azurelinuxagent.future import text
-import azurelinuxagent.utils.textutil as textutil
from datetime import datetime
class Logger(object):
@@ -36,7 +35,7 @@ class Logger(object):
self.appenders.extend(logger.appenders)
self.prefix = prefix
- def verbose(self, msg_format, *args):
+ def verb(self, msg_format, *args):
self.log(LogLevel.VERBOSE, msg_format, *args)
def info(self, msg_format, *args):
@@ -49,6 +48,9 @@ class Logger(object):
self.log(LogLevel.ERROR, msg_format, *args)
def log(self, level, msg_format, *args):
+ #if msg_format is not unicode convert it to unicode
+ if type(msg_format) is not text:
+ msg_format = text(msg_format, errors="backslashreplace")
if len(args) > 0:
msg = msg_format.format(*args)
else:
@@ -60,7 +62,9 @@ class Logger(object):
msg)
else:
log_item = u"{0} {1} {2}\n".format(time, level_str, msg)
- log_item = text(log_item.encode("ascii", "backslashreplace"), encoding='ascii')
+
+ log_item = text(log_item.encode('ascii', "backslashreplace"),
+ encoding="ascii")
for appender in self.appenders:
appender.write(level, log_item)
@@ -107,7 +111,6 @@ class StdoutAppender(object):
except IOError:
pass
-
#Initialize logger instance
DEFAULT_LOGGER = Logger()
@@ -132,7 +135,7 @@ def add_logger_appender(appender_type, level=LogLevel.INFO, path=None):
DEFAULT_LOGGER.add_appender(appender_type, level, path)
def verb(msg_format, *args):
- DEFAULT_LOGGER.verbose(msg_format, *args)
+ DEFAULT_LOGGER.verb(msg_format, *args)
def info(msg_format, *args):
DEFAULT_LOGGER.info(msg_format, *args)
diff --git a/azurelinuxagent/metadata.py b/azurelinuxagent/metadata.py
index 83d4676..5cf4902 100644
--- a/azurelinuxagent/metadata.py
+++ b/azurelinuxagent/metadata.py
@@ -1,4 +1,4 @@
-# Windows Azure Linux Agent
+# Microsoft Azure Linux Agent
#
# Copyright 2014 Microsoft Corporation
#
@@ -21,6 +21,7 @@ import os
import re
import platform
import sys
+import azurelinuxagent.utils.fileutil as fileutil
from azurelinuxagent.future import text
def get_distro():
@@ -46,7 +47,7 @@ def get_distro():
AGENT_NAME = "WALinuxAgent"
AGENT_LONG_NAME = "Azure Linux Agent"
-AGENT_VERSION = '2.1.1'
+AGENT_VERSION = '2.1.2'
AGENT_LONG_VERSION = "{0}-{1}".format(AGENT_NAME, AGENT_VERSION)
AGENT_DESCRIPTION = """\
The Azure Linux Agent supports the provisioning and running of Linux
@@ -70,24 +71,12 @@ PY_VERSION_MICRO = sys.version_info[2]
Add this walk arround for detecting Snappy Ubuntu Core temporarily, until ubuntu
fixed this bug: https://bugs.launchpad.net/snappy/+bug/1481086
"""
-def which(program):
- # Return path of program for execution if found in path
- def is_exe(fpath):
- return os.path.isfile(fpath) and os.access(fpath, os.X_OK)
- _fpath, _ = os.path.split(program)
- if _fpath:
- if is_exe(program):
- return program
- else:
- for path in os.environ.get("PATH", "").split(os.pathsep):
- path = path.strip('"')
- exe_file = os.path.join(path, program)
- if is_exe(exe_file):
- return exe_file
- return None
-
def is_snappy():
- return which("snappy")
+ if os.path.exists("/etc/motd"):
+ motd = fileutil.read_file("/etc/motd")
+ if "snappy" in motd:
+ return True
+ return False
if is_snappy():
DISTRO_FULL_NAME = "Snappy Ubuntu Core"
diff --git a/azurelinuxagent/protocol/__init__.py b/azurelinuxagent/protocol/__init__.py
index 65d8a5d..a4572e6 100644
--- a/azurelinuxagent/protocol/__init__.py
+++ b/azurelinuxagent/protocol/__init__.py
@@ -1,4 +1,4 @@
-# Windows Azure Linux Agent
+# Microsoft Azure Linux Agent
#
# Copyright 2014 Microsoft Corporation
#
diff --git a/azurelinuxagent/protocol/common.py b/azurelinuxagent/protocol/common.py
index 77247ab..367794f 100644
--- a/azurelinuxagent/protocol/common.py
+++ b/azurelinuxagent/protocol/common.py
@@ -1,4 +1,4 @@
-# Windows Azure Linux Agent
+# Microsoft Azure Linux Agent
#
# Copyright 2014 Microsoft Corporation
#
@@ -22,6 +22,7 @@ import re
import json
import xml.dom.minidom
import azurelinuxagent.logger as logger
+from azurelinuxagent.future import text
import azurelinuxagent.utils.fileutil as fileutil
class ProtocolError(Exception):
@@ -32,51 +33,49 @@ class ProtocolNotFound(Exception):
def validata_param(name, val, expected_type):
if val is None:
- raise ProtocolError("Param {0} is None".format(name))
+ raise ProtocolError("{0} is None".format(name))
if not isinstance(val, expected_type):
- raise ProtocolError("Param {0} type should be {1}".format(name,
- expected_type))
-
-def set_properties(obj, data):
- validata_param("obj", obj, DataContract)
- validata_param("data", data, dict)
-
- props = vars(obj)
- for name, val in list(props.items()):
- try:
- new_val = data[name]
- except KeyError:
- continue
-
- if isinstance(new_val, dict):
- set_properties(val, new_val)
- elif isinstance(new_val, list):
- validata_param("list", val, DataContractList)
- for data_item in new_val:
- item = val.item_cls()
- set_properties(item, data_item)
- val.append(item)
- else:
- setattr(obj, name, new_val)
+ raise ProtocolError(("{0} type should be {1} not {2}"
+ "").format(name, expected_type, type(val)))
+
+def set_properties(name, obj, data):
+ if isinstance(obj, DataContract):
+ validata_param("Property '{0}'".format(name), data, dict)
+ for prob_name, prob_val in data.items():
+ prob_full_name = "{0}.{1}".format(name, prob_name)
+ try:
+ prob = getattr(obj, prob_name)
+ except AttributeError:
+ logger.warn("Unknown property: {0}", prob_full_name)
+ continue
+ prob = set_properties(prob_full_name, prob, prob_val)
+ setattr(obj, prob_name, prob)
+ return obj
+ elif isinstance(obj, DataContractList):
+ validata_param("List '{0}'".format(name), data, list)
+ for item_data in data:
+ item = obj.item_cls()
+ item = set_properties(name, item, item_data)
+ obj.append(item)
+ return obj
+ else:
+ return data
def get_properties(obj):
- validata_param("obj", obj, DataContract)
-
- data = {}
- props = vars(obj)
- for name, val in list(props.items()):
- if isinstance(val, DataContract):
- data[name] = get_properties(val)
- elif isinstance(val, DataContractList):
- if len(val) == 0:
- continue
- data[name] = []
- for item in val:
- date_item = get_properties(item)
- data[name].append(date_item)
- elif val is not None:
- data[name] = val
- return data
+ if isinstance(obj, DataContract):
+ data = {}
+ props = vars(obj)
+ for prob_name, prob in list(props.items()):
+ data[prob_name] = get_properties(prob)
+ return data
+ elif isinstance(obj, DataContractList):
+ data = []
+ for item in obj:
+ item_data = get_properties(item)
+ data.append(item_data)
+ return data
+ else:
+ return obj
class DataContract(object):
pass
@@ -85,6 +84,9 @@ class DataContractList(list):
def __init__(self, item_cls):
self.item_cls = item_cls
+"""
+Data contract between guest and host
+"""
class VMInfo(DataContract):
def __init__(self, subscriptionId=None, vmName=None):
self.subscriptionId = subscriptionId
@@ -100,7 +102,7 @@ class CertList(DataContract):
def __init__(self):
self.certificates = DataContractList(Cert)
-class ExtensionSettings(DataContract):
+class Extension(DataContract):
def __init__(self, name=None, sequenceNumber=None, publicSettings=None,
privateSettings=None, certificateThumbprint=None):
self.name = name
@@ -109,47 +111,39 @@ class ExtensionSettings(DataContract):
self.privateSettings = privateSettings
self.certificateThumbprint = certificateThumbprint
-class ExtensionProperties(DataContract):
+class ExtHandlerProperties(DataContract):
def __init__(self):
self.version = None
self.upgradePolicy = None
self.state = None
- self.extensions = DataContractList(ExtensionSettings)
+ self.extensions = DataContractList(Extension)
-class ExtensionVersionUri(DataContract):
+class ExtHandlerVersionUri(DataContract):
def __init__(self):
self.uri = None
-class Extension(DataContract):
+class ExtHandler(DataContract):
def __init__(self, name=None):
self.name = name
- self.properties = ExtensionProperties()
- self.version_uris = DataContractList(ExtensionVersionUri)
+ self.properties = ExtHandlerProperties()
+ self.versionUris = DataContractList(ExtHandlerVersionUri)
-class ExtensionList(DataContract):
+class ExtHandlerList(DataContract):
def __init__(self):
- self.extensions = DataContractList(Extension)
+ self.extHandlers = DataContractList(ExtHandler)
-class ExtensionPackageUri(DataContract):
+class ExtHandlerPackageUri(DataContract):
def __init__(self, uri=None):
self.uri = uri
-class ExtensionPackage(DataContract):
+class ExtHandlerPackage(DataContract):
def __init__(self, version = None):
self.version = version
- self.uris = DataContractList(ExtensionPackageUri)
+ self.uris = DataContractList(ExtHandlerPackageUri)
-class ExtensionPackageList(DataContract):
+class ExtHandlerPackageList(DataContract):
def __init__(self):
- self.versions = DataContractList(ExtensionPackage)
-
-class InstanceMetadata(DataContract):
- def __init__(self, deploymentName=None, roleName=None, roleInstanceId=None,
- containerId=None):
- self.deploymentName = deploymentName
- self.roleName = roleName
- self.roleInstanceId = roleInstanceId
- self.containerId = containerId
+ self.versions = DataContractList(ExtHandlerPackage)
class VMProperties(DataContract):
def __init__(self, certificateThumbprint=None):
@@ -163,12 +157,6 @@ class ProvisionStatus(DataContract):
self.description = description
self.properties = VMProperties()
-class VMAgentStatus(DataContract):
- def __init__(self, agentVersion=None, status=None, message=None):
- self.agentVersion = agentVersion
- self.status = status
- self.message = message
-
class ExtensionSubStatus(DataContract):
def __init__(self, name=None, status=None, code=None, message=None):
self.name = name
@@ -177,30 +165,34 @@ class ExtensionSubStatus(DataContract):
self.message = message
class ExtensionStatus(DataContract):
- def __init__(self, name=None, configurationAppliedTime=None, operation=None,
- status=None, code=None, message=None, seq_no=None):
- self.name = name
+ def __init__(self, configurationAppliedTime=None, operation=None,
+ status=None, seq_no=None, code=None, message=None):
self.configurationAppliedTime = configurationAppliedTime
self.operation = operation
self.status = status
+ self.sequenceNumber = seq_no
self.code = code
self.message = message
- self.sequenceNumber = seq_no
self.substatusList = DataContractList(ExtensionSubStatus)
-class ExtensionHandlerStatus(DataContract):
- def __init__(self, handlerName=None, handlerVersion=None, status=None,
- message=None):
- self.handlerName = handlerName
- self.handlerVersion = handlerVersion
+class ExtHandlerStatus(DataContract):
+ def __init__(self, name=None, version=None, status=None, message=None):
+ self.name = name
+ self.version = version
self.status = status
self.message = message
- self.extensionStatusList = DataContractList(ExtensionStatus)
+ self.extensions = DataContractList(text)
+
+class VMAgentStatus(DataContract):
+ def __init__(self, version=None, status=None, message=None):
+ self.version = version
+ self.status = status
+ self.message = message
+ self.extensionHandlers = DataContractList(ExtHandlerStatus)
class VMStatus(DataContract):
def __init__(self):
self.vmAgent = VMAgentStatus()
- self.extensionHandlers = DataContractList(ExtensionHandlerStatus)
class TelemetryEventParam(DataContract):
def __init__(self, name=None, value=None):
@@ -228,16 +220,19 @@ class Protocol(DataContract):
def get_certs(self):
raise NotImplementedError()
- def get_extensions(self):
+ def get_ext_handlers(self):
+ raise NotImplementedError()
+
+ def get_ext_handler_pkgs(self, extension):
raise NotImplementedError()
- def get_extension_pkgs(self, extension):
+ def report_provision_status(self, provision_status):
raise NotImplementedError()
- def report_provision_status(self, status):
+ def report_vm_status(self, vm_status):
raise NotImplementedError()
- def report_status(self, status):
+ def report_ext_status(self, ext_handler_name, ext_name, ext_status):
raise NotImplementedError()
def report_event(self, event):
diff --git a/azurelinuxagent/protocol/ovfenv.py b/azurelinuxagent/protocol/ovfenv.py
index 2e0411d..9c845ee 100644
--- a/azurelinuxagent/protocol/ovfenv.py
+++ b/azurelinuxagent/protocol/ovfenv.py
@@ -1,4 +1,4 @@
-# Windows Azure Linux Agent
+# Microsoft Azure Linux Agent
#
# Copyright 2014 Microsoft Corporation
#
@@ -58,12 +58,17 @@ def copy_ovf_env():
ovfxml = re.sub("<UserPassword>.*?<", "<UserPassword>*<", ovfxml)
ovf_file_path = os.path.join(OSUTIL.get_lib_dir(), OVF_FILE_NAME)
fileutil.write_file(ovf_file_path, ovfxml)
- OSUTIL.umount_dvd()
- OSUTIL.eject_dvd()
except IOError as e:
raise ProtocolError(text(e))
except OSUtilError as e:
raise ProtocolError(text(e))
+
+ try:
+ OSUTIL.umount_dvd()
+ OSUTIL.eject_dvd()
+ except OSUtilError as e:
+ logger.warn(text(e))
+
return ovfenv
def _validate_ovf(val, msg):
diff --git a/azurelinuxagent/protocol/protocolFactory.py b/azurelinuxagent/protocol/protocolFactory.py
index d2ca201..0bf6e52 100644
--- a/azurelinuxagent/protocol/protocolFactory.py
+++ b/azurelinuxagent/protocol/protocolFactory.py
@@ -1,4 +1,4 @@
-# Windows Azure Linux Agent
+# Microsoft Azure Linux Agent
#
# Copyright 2014 Microsoft Corporation
#
diff --git a/azurelinuxagent/protocol/v1.py b/azurelinuxagent/protocol/v1.py
index 54a80b6..92fcc06 100644
--- a/azurelinuxagent/protocol/v1.py
+++ b/azurelinuxagent/protocol/v1.py
@@ -1,4 +1,4 @@
-# Windows Azure Linux Agent
+# Microsoft Azure Linux Agent
#
# Copyright 2014 Microsoft Corporation
#
@@ -24,7 +24,7 @@ import traceback
import xml.sax.saxutils as saxutils
import xml.etree.ElementTree as ET
import azurelinuxagent.logger as logger
-from azurelinuxagent.future import text, httpclient
+from azurelinuxagent.future import text, httpclient, bytebuffer
import azurelinuxagent.utils.restutil as restutil
from azurelinuxagent.utils.textutil import parse_doc, findall, find, findtext, \
getattrib, gettext, remove_bom
@@ -54,6 +54,9 @@ TRANSPORT_PRV_FILE_NAME = "TransportPrivate.pem"
PROTOCOL_VERSION = "2012-11-30"
+SHORT_WAITING_INTERVAL = 1 # 1 second
+LONG_WAITING_INTERVAL = 15 # 15 seconds
+
class WireProtocolResourceGone(ProtocolError):
pass
@@ -77,113 +80,89 @@ class WireProtocol(Protocol):
certificates = self.client.get_certs()
return certificates.cert_list
- def get_extensions(self):
+ def get_ext_handlers(self):
#Update goal state to get latest extensions config
self.client.update_goal_state()
ext_conf = self.client.get_ext_conf()
- return ext_conf.ext_list
+ return ext_conf.ext_handlers
- def get_extension_pkgs(self, extension):
+ def get_ext_handler_pkgs(self, ext_handler):
goal_state = self.client.get_goal_state()
- man = self.client.get_ext_manifest(extension, goal_state)
+ man = self.client.get_ext_manifest(ext_handler, goal_state)
return man.pkg_list
- def report_provision_status(self, provisionStatus):
- validata_param("provisionStatus", provisionStatus, ProvisionStatus)
-
- if provisionStatus.status is not None:
- self.client.report_health(provisionStatus.status,
- provisionStatus.subStatus,
- provisionStatus.description)
- if provisionStatus.properties.certificateThumbprint is not None:
- thumbprint = provisionStatus.properties.certificateThumbprint
+ def report_provision_status(self, provision_status):
+ validata_param("provision_status", provision_status, ProvisionStatus)
+
+ if provision_status.status is not None:
+ self.client.report_health(provision_status.status,
+ provision_status.subStatus,
+ provision_status.description)
+ if provision_status.properties.certificateThumbprint is not None:
+ thumbprint = provision_status.properties.certificateThumbprint
self.client.report_role_prop(thumbprint)
- def report_status(self, vmStatus):
- validata_param("vmStatus", vmStatus, VMStatus)
- self.client.upload_status_blob(vmStatus)
+ def report_vm_status(self, vm_status):
+ validata_param("vm_status", vm_status, VMStatus)
+ self.client.status_blob.set_vm_status(vm_status)
+ self.client.upload_status_blob()
+
+ def report_ext_status(self, ext_handler_name, ext_name, ext_status):
+ validata_param("ext_status", ext_status, ExtensionStatus)
+ self.client.status_blob.set_ext_status(ext_handler_name, ext_status)
def report_event(self, events):
validata_param("events", events, TelemetryEventList)
self.client.report_event(events)
-def _fetch_cache(local_file):
- if not os.path.isfile(local_file):
- raise ProtocolError("{0} is missing.".format(local_file))
- return fileutil.read_file(local_file)
-
-def _fetch_uri(uri, headers, chk_proxy=False):
- try:
- resp = restutil.http_get(uri, headers, chk_proxy=chk_proxy)
- except restutil.HttpError as e:
- raise ProtocolError(text(e))
-
- if(resp.status == httpclient.GONE):
- raise WireProtocolResourceGone(uri)
- if(resp.status != httpclient.OK):
- raise ProtocolError("{0} - {1}".format(resp.status, uri))
- data = resp.read()
- if data is None:
- return None
- data = remove_bom(data)
- xml_text = text(data, encoding='utf-8')
- return xml_text
-
-def _fetch_manifest(version_uris):
- for version_uri in version_uris:
- try:
- xml_text = _fetch_uri(version_uri.uri, None, chk_proxy=True)
- return xml_text
- except IOError as e:
- logger.warn("Failed to fetch ExtensionManifest: {0}, {1}", e,
- version_uri.uri)
- raise ProtocolError("Failed to fetch ExtensionManifest from all sources")
-
def _build_role_properties(container_id, role_instance_id, thumbprint):
- xml = ("<?xml version=\"1.0\" encoding=\"utf-8\"?>"
- "<RoleProperties>"
- "<Container>"
- "<ContainerId>{0}</ContainerId>"
- "<RoleInstances>"
- "<RoleInstance>"
- "<Id>{1}</Id>"
- "<Properties>"
- "<Property name=\"CertificateThumbprint\" value=\"{2}\" />"
- "</Properties>"
- "</RoleInstance>"
- "</RoleInstances>"
- "</Container>"
- "</RoleProperties>"
- "").format(container_id, role_instance_id, thumbprint)
+ xml = (u"<?xml version=\"1.0\" encoding=\"utf-8\"?>"
+ u"<RoleProperties>"
+ u"<Container>"
+ u"<ContainerId>{0}</ContainerId>"
+ u"<RoleInstances>"
+ u"<RoleInstance>"
+ u"<Id>{1}</Id>"
+ u"<Properties>"
+ u"<Property name=\"CertificateThumbprint\" value=\"{2}\" />"
+ u"</Properties>"
+ u"</RoleInstance>"
+ u"</RoleInstances>"
+ u"</Container>"
+ u"</RoleProperties>"
+ u"").format(container_id, role_instance_id, thumbprint)
return xml
def _build_health_report(incarnation, container_id, role_instance_id,
status, substatus, description):
- detail = ''
+ #Escape '&', '<' and '>'
+ description = saxutils.escape(text(description))
+ detail = u''
if substatus is not None:
- detail = ("<Details>"
- "<SubStatus>{0}</SubStatus>"
- "<Description>{1}</Description>"
- "</Details>").format(substatus, description)
- xml = ("<?xml version=\"1.0\" encoding=\"utf-8\"?>"
- "<Health "
- "xmlns:xsi=\"http://www.w3.org/2001/XMLSchema-instance\""
- " xmlns:xsd=\"http://www.w3.org/2001/XMLSchema\">"
- "<GoalStateIncarnation>{0}</GoalStateIncarnation>"
- "<Container>"
- "<ContainerId>{1}</ContainerId>"
- "<RoleInstanceList>"
- "<Role>"
- "<InstanceId>{2}</InstanceId>"
- "<Health>"
- "<State>{3}</State>"
- "{4}"
- "</Health>"
- "</Role>"
- "</RoleInstanceList>"
- "</Container>"
- "</Health>"
- "").format(incarnation,
+ substatus = saxutils.escape(text(substatus))
+ detail = (u"<Details>"
+ u"<SubStatus>{0}</SubStatus>"
+ u"<Description>{1}</Description>"
+ u"</Details>").format(substatus, description)
+ xml = (u"<?xml version=\"1.0\" encoding=\"utf-8\"?>"
+ u"<Health "
+ u"xmlns:xsi=\"http://www.w3.org/2001/XMLSchema-instance\""
+ u" xmlns:xsd=\"http://www.w3.org/2001/XMLSchema\">"
+ u"<GoalStateIncarnation>{0}</GoalStateIncarnation>"
+ u"<Container>"
+ u"<ContainerId>{1}</ContainerId>"
+ u"<RoleInstanceList>"
+ u"<Role>"
+ u"<InstanceId>{2}</InstanceId>"
+ u"<Health>"
+ u"<State>{3}</State>"
+ u"{4}"
+ u"</Health>"
+ u"</Role>"
+ u"</RoleInstanceList>"
+ u"</Container>"
+ u"</Health>"
+ u"").format(incarnation,
container_id,
role_instance_id,
status,
@@ -193,19 +172,19 @@ def _build_health_report(incarnation, container_id, role_instance_id,
"""
Convert VMStatus object to status blob format
"""
-def guest_agent_status_to_v1(ga_status):
+def ga_status_to_v1(ga_status):
formatted_msg = {
'lang' : 'en-US',
'message' : ga_status.message
}
v1_ga_status = {
- 'version' : ga_status.agentVersion,
+ 'version' : ga_status.version,
'status' : ga_status.status,
'formattedMessage' : formatted_msg
}
return v1_ga_status
-def extension_substatus_to_v1(sub_status_list):
+def ext_substatus_to_v1(sub_status_list):
status_list = []
for substatus in sub_status_list:
status = {
@@ -220,14 +199,14 @@ def extension_substatus_to_v1(sub_status_list):
status_list.append(status)
return status_list
-def extension_handler_status_to_v1(handler_status, timestamp):
- if handler_status is None or len(handler_status.extensionStatusList) == 0:
- return
- ext_status = handler_status.extensionStatusList[0]
- sub_status = extension_substatus_to_v1(ext_status.substatusList)
- ext_in_status = {
+def ext_status_to_v1(ext_name, ext_status):
+ if ext_status is None:
+ return None
+ timestamp = time.strftime("%Y-%m-%dT%H:%M:%SZ", time.gmtime())
+ v1_sub_status = ext_substatus_to_v1(ext_status.substatusList)
+ v1_ext_status = {
"status":{
- "name": ext_status.name,
+ "name": ext_name,
"configurationAppliedTime": ext_status.configurationAppliedTime,
"operation": ext_status.operation,
"status": ext_status.status,
@@ -237,33 +216,47 @@ def extension_handler_status_to_v1(handler_status, timestamp):
"message": ext_status.message
}
},
+ "version": 1.0,
"timestampUTC": timestamp
}
-
- if len(sub_status) != 0:
- ext_in_status['substatus'] = sub_status
-
+ if len(v1_sub_status) != 0:
+ v1_ext_status['substatus'] = v1_sub_status
+ return v1_ext_status
+
+def ext_handler_status_to_v1(handler_status, ext_statuses, timestamp):
v1_handler_status = {
- 'handlerVersion' : handler_status.handlerVersion,
- 'handlerName' : handler_status.handlerName,
+ 'handlerVersion' : handler_status.version,
+ 'handlerName' : handler_status.name,
'status' : handler_status.status,
- 'runtimeSettingsStatus' : {
- 'settingsStatus' : ext_in_status,
- 'sequenceNumber' : ext_status.sequenceNumber
- }
}
- return v1_handler_status
+ if handler_status.message is not None:
+ v1_handler_status["formattedMessage"] = {
+ "lang":"en-US",
+ "message": handler_status.message
+ }
+ if len(handler_status.extensions) > 0:
+ #Currently, no more than one extension per handler
+ ext_name = handler_status.extensions[0]
+ ext_status = ext_statuses.get(ext_name)
+ v1_ext_status = ext_status_to_v1(ext_name, ext_status)
+ if ext_status is not None and v1_ext_status is not None:
+ v1_handler_status["runtimeSettingsStatus"] = {
+ 'settingsStatus' : v1_ext_status,
+ 'sequenceNumber' : ext_status.sequenceNumber
+ }
+ return v1_handler_status
-def vm_status_to_v1(vm_status):
+def vm_status_to_v1(vm_status, ext_statuses):
timestamp = time.strftime("%Y-%m-%dT%H:%M:%SZ", time.gmtime())
- v1_ga_status = guest_agent_status_to_v1(vm_status.vmAgent)
+ v1_ga_status = ga_status_to_v1(vm_status.vmAgent)
v1_handler_status_list = []
- for handler_status in vm_status.extensionHandlers:
- v1_handler_status = extension_handler_status_to_v1(handler_status,
- timestamp)
- v1_handler_status_list.append(v1_handler_status)
+ for handler_status in vm_status.vmAgent.extensionHandlers:
+ v1_handler_status = ext_handler_status_to_v1(handler_status,
+ ext_statuses, timestamp)
+ if v1_handler_status is not None:
+ v1_handler_status_list.append(v1_handler_status)
v1_agg_status = {
'guestAgentStatus': v1_ga_status,
@@ -278,35 +271,53 @@ def vm_status_to_v1(vm_status):
class StatusBlob(object):
- def __init__(self, vm_status):
- self.vm_status = vm_status
+ def __init__(self, client):
+ self.vm_status = None
+ self.ext_statuses = {}
+ self.client = client
+ def set_vm_status(self, vm_status):
+ validata_param("vmAgent", vm_status, VMStatus)
+ self.vm_status = vm_status
+
+ def set_ext_status(self, ext_handler_name, ext_status):
+ validata_param("extensionStatus", ext_status, ExtensionStatus)
+ self.ext_statuses[ext_handler_name]= ext_status
+
def to_json(self):
- report = vm_status_to_v1(self.vm_status)
+ report = vm_status_to_v1(self.vm_status, self.ext_statuses)
return json.dumps(report)
__storage_version__ = "2014-02-14"
def upload(self, url):
- logger.info("Upload status blob")
+ #TODO upload extension only if content has changed
+ logger.verb("Upload status blob")
blob_type = self.get_blob_type(url)
data = self.to_json()
- if blob_type == "BlockBlob":
- self.put_block_blob(url, data)
- elif blob_type == "PageBlob":
- self.put_page_blob(url, data)
- else:
- raise ProtocolError("Unknown blob type: {0}".format(blob_type))
+ try:
+ if blob_type == "BlockBlob":
+ self.put_block_blob(url, data)
+ elif blob_type == "PageBlob":
+ self.put_page_blob(url, data)
+ else:
+ raise ProtocolError("Unknown blob type: {0}".format(blob_type))
+ except restutil.HttpError as e:
+ raise ProtocolError("Failed to upload status blob: {0}".format(e))
def get_blob_type(self, url):
#Check blob type
logger.verb("Check blob type.")
timestamp = time.strftime("%Y-%m-%dT%H:%M:%SZ", time.gmtime())
- resp = restutil.http_head(url, {
- "x-ms-date" : timestamp,
- 'x-ms-version' : self.__class__.__storage_version__
- })
+ try:
+ resp = self.client.call_storage_service(restutil.http_head, url, {
+ "x-ms-date" : timestamp,
+ 'x-ms-version' : self.__class__.__storage_version__
+ })
+ except restutil.HttpError as e:
+ raise ProtocolError((u"Failed to get status blob type: {0}"
+ u"").format(e))
if resp is None or resp.status != httpclient.OK:
raise ProtocolError(("Failed to get status blob type: {0}"
"").format(resp.status))
@@ -318,33 +329,47 @@ class StatusBlob(object):
def put_block_blob(self, url, data):
logger.verb("Upload block blob")
timestamp = time.strftime("%Y-%m-%dT%H:%M:%SZ", time.gmtime())
- resp = restutil.http_put(url, data, {
- "x-ms-date" : timestamp,
- "x-ms-blob-type" : "BlockBlob",
- "Content-Length": text(len(data)),
- "x-ms-version" : self.__class__.__storage_version__
- })
- if resp is None or resp.status != httpclient.CREATED:
+ try:
+ resp = self.client.call_storage_service(restutil.http_put, url,
+ data, {
+ "x-ms-date" : timestamp,
+ "x-ms-blob-type" : "BlockBlob",
+ "Content-Length": text(len(data)),
+ "x-ms-version" : self.__class__.__storage_version__
+ })
+ except restutil.HttpError as e:
+ raise ProtocolError((u"Failed to upload block blob: {0}"
+ u"").format(e))
+ if resp.status != httpclient.CREATED:
raise ProtocolError(("Failed to upload block blob: {0}"
"").format(resp.status))
def put_page_blob(self, url, data):
logger.verb("Replace old page blob")
+
+ #Convert string into 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 = ((len(data) + 511) / 512) * 512
- resp = restutil.http_put(url, "", {
- "x-ms-date" : timestamp,
- "x-ms-blob-type" : "PageBlob",
- "Content-Length": "0",
- "x-ms-blob-content-length" : text(page_blob_size),
- "x-ms-version" : self.__class__.__storage_version__
- })
- if resp is None or resp.status != httpclient.CREATED:
+ page_blob_size = int((len(data) + 511) / 512) * 512
+ try:
+ 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" : text(page_blob_size),
+ "x-ms-version" : self.__class__.__storage_version__
+ })
+ except restutil.HttpError as e:
+ raise ProtocolError((u"Failed to clean up page blob: {0}"
+ u"").format(e))
+ if resp.status != httpclient.CREATED:
raise ProtocolError(("Failed to clean up page blob: {0}"
"").format(resp.status))
- if '?' in url < 0:
+ if url.count("?") < 0:
url = "{0}?comp=page".format(url)
else:
url = "{0}&comp=page".format(url)
@@ -359,15 +384,20 @@ class StatusBlob(object):
#Align to 512 bytes
page_end = int((end + 511) / 512) * 512
buf_size = page_end - start
- buf = bytearray(source=data[start:end], encoding="utf-8")
- #TODO buffer is not defined in python3, however we need this to make httplib to work on python 2.6
- resp = restutil.http_put(url, 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": text(page_end - start)
- })
+ buf = bytearray(buf_size)
+ buf[0: content_size] = data[start: end]
+ try:
+ 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": text(page_end - start)
+ })
+ except restutil.HttpError as e:
+ raise ProtocolError((u"Failed to upload page blob: {0}"
+ u"").format(e))
if resp is None or resp.status != httpclient.CREATED:
raise ProtocolError(("Failed to upload page blob: {0}"
"").format(resp.status))
@@ -408,59 +438,176 @@ class WireClient(object):
self.shared_conf = None
self.certs = None
self.ext_conf = None
+ self.last_request = 0
self.req_count = 0
+ self.status_blob = StatusBlob(self)
+
+ def prevent_throttling(self):
+ """
+ Try to avoid throttling of wire server
+ """
+ now = time.time()
+ if now - self.last_request < 1:
+ logger.info("Last request issued less than 1 second ago")
+ logger.info("Sleep {0} second to avoid throttling.",
+ SHORT_WAITING_INTERVAL)
+ time.sleep(SHORT_WAITING_INTERVAL)
+ self.last_request = now
+
+ self.req_count += 1
+ if self.req_count % 3 == 0:
+ logger.info("Sleep {0} second to avoid throttling.",
+ SHORT_WAITING_INTERVAL)
+ time.sleep(SHORT_WAITING_INTERVAL)
+ self.req_count = 0
+
+ def call_wireserver(self, http_req, *args, **kwargs):
+ """
+ Call wire server. Handle throttling(403) and Resource Gone(410)
+ """
+ self.prevent_throttling()
+ for retry in range(0, 3):
+ resp = http_req(*args, **kwargs)
+ if resp.status == httpclient.FORBIDDEN:
+ logger.warn("Sending too much request to wire server")
+ logger.info("Sleep {0} second to avoid throttling.",
+ LONG_WAITING_INTERVAL)
+ time.sleep(LONG_WAITING_INTERVAL)
+ elif resp.status == httpclient.GONE:
+ msg = args[0] if len(args) > 0 else ""
+ raise WireProtocolResourceGone(msg)
+ else:
+ return resp
+ raise ProtocolError(("Calling wire server failed: {0}"
+ "").format(resp.status))
+
+ def decode_config(self, data):
+ if data is None:
+ return None
+ data = remove_bom(data)
+ xml_text = text(data, encoding='utf-8')
+ return xml_text
+
+ def fetch_config(self, uri, headers):
+ try:
+ resp = self.call_wireserver(restutil.http_get, uri,
+ headers=headers)
+ except restutil.HttpError as e:
+ raise ProtocolError(text(e))
+
+ if(resp.status != httpclient.OK):
+ raise ProtocolError("{0} - {1}".format(resp.status, uri))
+
+ return self.decode_config(resp.read())
+
+ def fetch_cache(self, local_file):
+ if not os.path.isfile(local_file):
+ raise ProtocolError("{0} is missing.".format(local_file))
+ try:
+ return fileutil.read_file(local_file)
+ except IOError as e:
+ raise ProtocolError("Failed to read cache: {0}".format(e))
+
+ def save_cache(self, local_file, data):
+ try:
+ fileutil.write_file(local_file, data)
+ except IOError as e:
+ raise ProtocolError("Failed to write cache: {0}".format(e))
+
+ def call_storage_service(self, http_req, *args, **kwargs):
+ """
+ Call storage service, handle SERVICE_UNAVAILABLE(503)
+ """
+ for retry in range(0, 3):
+ resp = http_req(*args, **kwargs)
+ if resp.status == httpclient.SERVICE_UNAVAILABLE:
+ logger.warn("Storage service is not avaible temporaryly")
+ logger.info("Will retry later, in {0} seconds",
+ LONG_WAITING_INTERVAL)
+ time.sleep(LONG_WAITING_INTERVAL)
+ else:
+ return resp
+ raise ProtocolError(("Calling storage endpoint failed: {0}"
+ "").format(resp.status))
+
+ def fetch_manifest(self, version_uris):
+ for version_uri in version_uris:
+ try:
+ resp = self.call_storage_service(restutil.http_get,
+ version_uri.uri, None,
+ chk_proxy=True)
+ except restutil.HttpError as e:
+ raise ProtocolError(text(e))
+
+ if resp.status == httpclient.OK:
+ return self.decode_config(resp.read())
+ logger.warn("Failed to fetch ExtensionManifest: {0}, {1}",
+ resp.status, version_uri.uri)
+ logger.info("Will retry later, in {0} seconds",
+ LONG_WAITING_INTERVAL)
+ time.sleep(LONG_WAITING_INTERVAL)
+ raise ProtocolError(("Failed to fetch ExtensionManifest from "
+ "all sources"))
+
def update_hosting_env(self, goal_state):
if goal_state.hosting_env_uri is None:
raise ProtocolError("HostingEnvironmentConfig uri is empty")
local_file = HOSTING_ENV_FILE_NAME
- xml_text = _fetch_uri(goal_state.hosting_env_uri, self.get_header())
- fileutil.write_file(local_file, xml_text)
+ xml_text = self.fetch_config(goal_state.hosting_env_uri,
+ self.get_header())
+ self.save_cache(local_file, xml_text)
self.hosting_env = HostingEnv(xml_text)
def update_shared_conf(self, goal_state):
if goal_state.shared_conf_uri is None:
raise ProtocolError("SharedConfig uri is empty")
local_file = SHARED_CONF_FILE_NAME
- xml_text = _fetch_uri(goal_state.shared_conf_uri, self.get_header())
- fileutil.write_file(local_file, xml_text)
+ xml_text = self.fetch_config(goal_state.shared_conf_uri,
+ self.get_header())
+ self.save_cache(local_file, xml_text)
self.shared_conf = SharedConfig(xml_text)
def update_certs(self, goal_state):
if goal_state.certs_uri is None:
return
local_file = CERTS_FILE_NAME
- xml_text = _fetch_uri(goal_state.certs_uri, self.get_header_for_cert())
- fileutil.write_file(local_file, xml_text)
- self.certs = Certificates(xml_text)
+ xml_text = self.fetch_config(goal_state.certs_uri,
+ self.get_header_for_cert())
+ self.save_cache(local_file, xml_text)
+ self.certs = Certificates(self, xml_text)
def update_ext_conf(self, goal_state):
if goal_state.ext_uri is None:
- raise ProtocolError("ExtensionsConfig uri is empty")
+ logger.info("ExtensionsConfig.xml uri is empty")
+ self.ext_conf = ExtensionsConfig(None)
+ return
incarnation = goal_state.incarnation
local_file = EXT_CONF_FILE_NAME.format(incarnation)
- xml_text = _fetch_uri(goal_state.ext_uri,
- self.get_header())
- fileutil.write_file(local_file, xml_text)
+ xml_text = self.fetch_config(goal_state.ext_uri, self.get_header())
+ self.save_cache(local_file, xml_text)
self.ext_conf = ExtensionsConfig(xml_text)
- for extension in self.ext_conf.ext_list.extensions:
- self.update_ext_manifest(extension, goal_state)
+ for ext_handler in self.ext_conf.ext_handlers.extHandlers:
+ self.update_ext_handler_manifest(ext_handler, goal_state)
- def update_ext_manifest(self, extension, goal_state):
- local_file = MANIFEST_FILE_NAME.format(extension.name,
- goal_state.incarnation)
- xml_text = _fetch_manifest(extension.version_uris)
- fileutil.write_file(local_file, xml_text)
+ def update_ext_handler_manifest(self, ext_handler, goal_state):
+ local_file = MANIFEST_FILE_NAME.format(ext_handler.name,
+ goal_state.incarnation)
+ xml_text = self.fetch_manifest(ext_handler.versionUris)
+ self.save_cache(local_file, xml_text)
def update_goal_state(self, forced=False, max_retry=3):
uri = GOAL_STATE_URI.format(self.endpoint)
- xml_text = _fetch_uri(uri, self.get_header())
+ xml_text = self.fetch_config(uri, self.get_header())
goal_state = GoalState(xml_text)
+ incarnation_file = os.path.join(OSUTIL.get_lib_dir(),
+ INCARNATION_FILE_NAME)
+
if not forced:
last_incarnation = None
- if(os.path.isfile(INCARNATION_FILE_NAME)):
- last_incarnation = fileutil.read_file(INCARNATION_FILE_NAME)
+ if(os.path.isfile(incarnation_file)):
+ last_incarnation = fileutil.read_file(incarnation_file)
new_incarnation = goal_state.incarnation
if last_incarnation is not None and \
last_incarnation == new_incarnation:
@@ -471,10 +618,10 @@ class WireClient(object):
for retry in range(0, max_retry):
try:
self.goal_state = goal_state
- goal_state_file = GOAL_STATE_FILE_NAME.format(goal_state.incarnation)
- fileutil.write_file(goal_state_file, xml_text)
- fileutil.write_file(INCARNATION_FILE_NAME,
- goal_state.incarnation)
+ file_name = GOAL_STATE_FILE_NAME.format(goal_state.incarnation)
+ goal_state_file = os.path.join(OSUTIL.get_lib_dir(), file_name)
+ self.save_cache(goal_state_file, xml_text)
+ self.save_cache(incarnation_file, goal_state.incarnation)
self.update_hosting_env(goal_state)
self.update_shared_conf(goal_state)
self.update_certs(goal_state)
@@ -482,35 +629,35 @@ class WireClient(object):
return
except WireProtocolResourceGone:
logger.info("Incarnation is out of date. Update goalstate.")
- xml_text = _fetch_uri(GOAL_STATE_URI, self.get_header())
+ xml_text = self.fetch_config(uri, self.get_header())
goal_state = GoalState(xml_text)
raise ProtocolError("Exceeded max retry updating goal state")
def get_goal_state(self):
if(self.goal_state is None):
- incarnation = _fetch_cache(INCARNATION_FILE_NAME)
+ incarnation = self.fetch_cache(INCARNATION_FILE_NAME)
goal_state_file = GOAL_STATE_FILE_NAME.format(incarnation)
- xml_text = _fetch_cache(goal_state_file)
+ xml_text = self.fetch_cache(goal_state_file)
self.goal_state = GoalState(xml_text)
return self.goal_state
def get_hosting_env(self):
if(self.hosting_env is None):
- xml_text = _fetch_cache(HOSTING_ENV_FILE_NAME)
+ xml_text = self.fetch_cache(HOSTING_ENV_FILE_NAME)
self.hosting_env = HostingEnv(xml_text)
return self.hosting_env
def get_shared_conf(self):
if(self.shared_conf is None):
- xml_text = _fetch_cache(SHARED_CONF_FILE_NAME)
+ xml_text = self.fetch_cache(SHARED_CONF_FILE_NAME)
self.shared_conf = SharedConfig(xml_text)
return self.shared_conf
def get_certs(self):
if(self.certs is None):
- xml_text = _fetch_cache(Certificates)
- self.certs = Certificates(xml_text)
+ xml_text = self.fetch_cache(CERTS_FILE_NAME)
+ self.certs = Certificates(self, xml_text)
if self.certs is None:
return None
return self.certs
@@ -518,20 +665,23 @@ class WireClient(object):
def get_ext_conf(self):
if(self.ext_conf is None):
goal_state = self.get_goal_state()
- local_file = EXT_CONF_FILE_NAME.format(goal_state.incarnation)
- xml_text = _fetch_cache(local_file)
- self.ext_conf = ExtensionsConfig(xml_text)
+ if goal_state.ext_uri is None:
+ self.ext_conf = ExtensionsConfig(None)
+ else:
+ local_file = EXT_CONF_FILE_NAME.format(goal_state.incarnation)
+ xml_text = self.fetch_cache(local_file)
+ self.ext_conf = ExtensionsConfig(xml_text)
return self.ext_conf
def get_ext_manifest(self, extension, goal_state):
local_file = MANIFEST_FILE_NAME.format(extension.name,
goal_state.incarnation)
- xml_text = _fetch_cache(local_file)
+ xml_text = self.fetch_cache(local_file)
return ExtensionManifest(xml_text)
def check_wire_protocol_version(self):
uri = VERSION_INFO_URI.format(self.endpoint)
- version_info_xml = _fetch_uri(uri, None)
+ version_info_xml = self.fetch_config(uri, None)
version_info = VersionInfo(version_info_xml)
preferred = version_info.get_preferred()
@@ -544,42 +694,50 @@ class WireClient(object):
error = ("Agent supported wire protocol version: {0} was not "
"advised by Fabric.").format(PROTOCOL_VERSION)
raise ProtocolNotFound(error)
-
- def upload_status_blob(self, vm_status):
+
+ def upload_status_blob(self):
ext_conf = self.get_ext_conf()
- status_blob = StatusBlob(vm_status)
- status_blob.upload(ext_conf.status_upload_blob)
+ if ext_conf.status_upload_blob is not None:
+ self.status_blob.upload(ext_conf.status_upload_blob)
def report_role_prop(self, thumbprint):
goal_state = self.get_goal_state()
role_prop = _build_role_properties(goal_state.container_id,
goal_state.role_instance_id,
thumbprint)
+ role_prop = role_prop.encode("utf-8")
role_prop_uri = ROLE_PROP_URI.format(self.endpoint)
- ret = restutil.http_post(role_prop_uri,
- role_prop,
- headers=self.get_header_for_xml_content())
-
+ headers = self.get_header_for_xml_content()
+ try:
+ resp = self.call_wireserver(restutil.http_post, role_prop_uri,
+ role_prop, headers = headers)
+ except restutil.HttpError as e:
+ raise ProtocolError((u"Failed to send role properties: {0}"
+ u"").format(e))
+ if resp.status != httpclient.ACCEPTED:
+ raise ProtocolError((u"Failed to send role properties: {0}"
+ u", {1}").format(resp.status, resp.read()))
def report_health(self, status, substatus, description):
goal_state = self.get_goal_state()
health_report = _build_health_report(goal_state.incarnation,
- goal_state.container_id,
- goal_state.role_instance_id,
- status,
- substatus,
- description)
+ goal_state.container_id,
+ goal_state.role_instance_id,
+ status,
+ substatus,
+ description)
+ health_report = health_report.encode("utf-8")
health_report_uri = HEALTH_REPORT_URI.format(self.endpoint)
headers = self.get_header_for_xml_content()
- resp = restutil.http_post(health_report_uri,
- health_report,
- headers=headers)
- def prevent_throttling(self):
- self.req_count += 1
- if self.req_count % 3 == 0:
- logger.info("Sleep 15 before sending event to avoid throttling.")
- self.req_count = 0
- time.sleep(15)
+ try:
+ resp = self.call_wireserver(restutil.http_post, health_report_uri,
+ health_report, headers = headers)
+ except restutil.HttpError as e:
+ raise ProtocolError((u"Failed to send provision status: {0}"
+ u"").format(e))
+ if resp.status != httpclient.OK:
+ raise ProtocolError((u"Failed to send provision status: {0}"
+ u", {1}").format(resp.status, resp.read()))
def send_event(self, provider_id, event_str):
uri = TELEMETRY_URI.format(self.endpoint)
@@ -590,9 +748,8 @@ class WireClient(object):
'</TelemetryData>')
data = data_format.format(provider_id, event_str)
try:
- self.prevent_throttling()
header = self.get_header_for_xml_content()
- resp = restutil.http_post(uri, data, header)
+ resp = self.call_wireserver(restutil.http_post, uri, data, header)
except restutil.HttpError as e:
raise ProtocolError("Failed to send events:{0}".format(e))
@@ -635,7 +792,7 @@ class WireClient(object):
def get_header_for_cert(self):
cert = ""
- content = _fetch_cache(TRANSPORT_CERT_FILE_NAME)
+ content = self.fetch_cache(TRANSPORT_CERT_FILE_NAME)
for line in content.split('\n'):
if "CERTIFICATE" not in line:
cert += line.rstrip()
@@ -762,10 +919,9 @@ class Certificates(object):
"""
Object containing certificates of host and provisioned user.
"""
- def __init__(self, xml_text=None):
- if xml_text is None:
- raise ValueError("Certificates.xml is None")
+ def __init__(self, client, xml_text):
logger.verb("Load Certificates.xml")
+ self.client = client
self.lib_dir = OSUTIL.get_lib_dir()
self.openssl_cmd = OSUTIL.get_openssl_cmd()
self.cert_list = CertList()
@@ -787,7 +943,7 @@ class Certificates(object):
"\n"
"{2}").format(P7M_FILE_NAME, P7M_FILE_NAME, data)
- fileutil.write_file(os.path.join(self.lib_dir, P7M_FILE_NAME), p7m)
+ self.client.save_cache(os.path.join(self.lib_dir, P7M_FILE_NAME), p7m)
#decrypt certificates
cmd = ("{0} cms -decrypt -in {1} -inkey {2} -recip {3}"
"| {4} pkcs12 -nodes -password pass: -out {5}"
@@ -844,13 +1000,12 @@ class Certificates(object):
for v1_cert in v1_cert_list:
cert = Cert()
- set_properties(cert, v1_cert)
+ set_properties("certs", cert, v1_cert)
self.cert_list.certificates.append(cert)
def write_to_tmp_file(self, index, suffix, buf):
file_name = os.path.join(self.lib_dir, "{0}.{1}".format(index, suffix))
- with open(file_name, 'w') as tmp:
- tmp.writelines(buf)
+ self.client.save_cache(file_name, "".join(buf))
return file_name
@@ -861,12 +1016,11 @@ class ExtensionsConfig(object):
"""
def __init__(self, xml_text):
- if xml_text is None:
- raise ValueError("ExtensionsConfig is None")
logger.verb("Load ExtensionsConfig.xml")
- self.ext_list = ExtensionList()
+ self.ext_handlers = ExtHandlerList()
self.status_upload_blob = None
- self.parse(xml_text)
+ if xml_text is not None:
+ self.parse(xml_text)
def parse(self, xml_text):
"""
@@ -879,38 +1033,38 @@ class ExtensionsConfig(object):
plugin_settings = findall(plugin_settings_list, "Plugin")
for plugin in plugins:
- ext = self.parse_ext(plugin)
- self.ext_list.extensions.append(ext)
- self.parse_ext_settings(ext, plugin_settings)
+ ext_handler = self.parse_plugin(plugin)
+ self.ext_handlers.extHandlers.append(ext_handler)
+ self.parse_plugin_settings(ext_handler, plugin_settings)
self.status_upload_blob = findtext(xml_doc, "StatusUploadBlob")
- def parse_ext(self, plugin):
- ext = Extension()
- ext.name = getattrib(plugin, "name")
- ext.properties.version = getattrib(plugin, "version")
- ext.properties.state = getattrib(plugin, "state")
+ def parse_plugin(self, plugin):
+ ext_handler = ExtHandler()
+ ext_handler.name = getattrib(plugin, "name")
+ ext_handler.properties.version = getattrib(plugin, "version")
+ ext_handler.properties.state = getattrib(plugin, "state")
auto_upgrade = getattrib(plugin, "autoUpgrade")
if auto_upgrade is not None and auto_upgrade.lower() == "true":
- ext.properties.upgradePolicy = "auto"
+ ext_handler.properties.upgradePolicy = "auto"
else:
- ext.properties.upgradePolicy = "manual"
+ ext_handler.properties.upgradePolicy = "manual"
location = getattrib(plugin, "location")
failover_location = getattrib(plugin, "failoverlocation")
for uri in [location, failover_location]:
- version_uri = ExtensionVersionUri()
+ version_uri = ExtHandlerVersionUri()
version_uri.uri = uri
- ext.version_uris.append(version_uri)
- return ext
+ ext_handler.versionUris.append(version_uri)
+ return ext_handler
- def parse_ext_settings(self, ext, plugin_settings):
+ def parse_plugin_settings(self, ext_handler, plugin_settings):
if plugin_settings is None:
return
- name = ext.name
- version = ext.properties.version
+ name = ext_handler.name
+ version = ext_handler.properties.version
settings = [x for x in plugin_settings \
if getattrib(x, "name") == name and \
getattrib(x ,"version") == version]
@@ -930,20 +1084,23 @@ class ExtensionsConfig(object):
for plugin_settings_list in runtime_settings["runtimeSettings"]:
handler_settings = plugin_settings_list["handlerSettings"]
- ext_settings = ExtensionSettings()
- ext_settings.sequenceNumber = seqNo
- ext_settings.publicSettings = handler_settings.get("publicSettings", None)
- ext_settings.privateSettings = handler_settings.get("protectedSettings", None)
- thumbprint = handler_settings.get("protectedSettingsCertThumbprint", None)
- ext_settings.certificateThumbprint = thumbprint
- ext.properties.extensions.append(ext_settings)
+ ext = Extension()
+ #There is no "extension name" in wire protocol.
+ #Put
+ ext.name = ext_handler.name
+ ext.sequenceNumber = seqNo
+ ext.publicSettings = handler_settings.get("publicSettings")
+ ext.privateSettings = handler_settings.get("protectedSettings")
+ thumbprint = handler_settings.get("protectedSettingsCertThumbprint")
+ ext.certificateThumbprint = thumbprint
+ ext_handler.properties.extensions.append(ext)
class ExtensionManifest(object):
def __init__(self, xml_text):
if xml_text is None:
raise ValueError("ExtensionManifest is None")
logger.verb("Load ExtensionManifest.xml")
- self.pkg_list = ExtensionPackageList()
+ self.pkg_list = ExtHandlerPackageList()
self.parse(xml_text)
def parse(self, xml_text):
@@ -954,10 +1111,10 @@ class ExtensionManifest(object):
uris = find(package, "Uris")
uri_list = findall(uris, "Uri")
uri_list = [gettext(x) for x in uri_list]
- package = ExtensionPackage()
+ package = ExtHandlerPackage()
package.version = version
for uri in uri_list:
- pkg_uri = ExtensionPackageUri()
+ pkg_uri = ExtHandlerVersionUri()
pkg_uri.uri = uri
package.uris.append(pkg_uri)
self.pkg_list.versions.append(package)
diff --git a/azurelinuxagent/protocol/v2.py b/azurelinuxagent/protocol/v2.py
index d7c9143..34102b7 100644
--- a/azurelinuxagent/protocol/v2.py
+++ b/azurelinuxagent/protocol/v2.py
@@ -1,4 +1,4 @@
-# Windows Azure Linux Agent
+# Microsoft Azure Linux Agent
#
# Copyright 2014 Microsoft Corporation
#
@@ -25,7 +25,7 @@ ENDPOINT='169.254.169.254'
#TODO use http for azure pack test
#ENDPOINT='localhost'
APIVERSION='2015-05-01-preview'
-BASE_URI = "http://{0}/Microsoft.Compute/{1}?api-version={{{2}}}{3}"
+BASE_URI = "http://{0}/Microsoft.Compute/{1}?api-version={2}{3}"
def _add_content_type(headers):
if headers is None:
@@ -47,12 +47,15 @@ class MetadataProtocol(Protocol):
self.provision_status_uri = BASE_URI.format(self.endpoint,
"provisioningStatus",
self.apiversion, "")
- self.status_uri = BASE_URI.format(self.endpoint, "status",
- self.apiversion, "")
+ self.vm_status_uri = BASE_URI.format(self.endpoint, "status/vmagent",
+ self.apiversion, "")
+ self.ext_status_uri = BASE_URI.format(self.endpoint,
+ "status/extensions/{0}",
+ self.apiversion, "")
self.event_uri = BASE_URI.format(self.endpoint, "status/telemetry",
self.apiversion, "")
- def _get_data(self, data_type, url, headers=None):
+ def _get_data(self, url, headers=None):
try:
resp = restutil.http_get(url, headers=headers)
except restutil.HttpError as e:
@@ -60,20 +63,15 @@ class MetadataProtocol(Protocol):
if resp.status != httpclient.OK:
raise ProtocolError("{0} - GET: {1}".format(resp.status, url))
- try:
- data = resp.read()
- if data is None:
- return None
- data = json.loads(text(data, encoding="utf-8"))
- except ValueError as e:
- raise ProtocolError(text(e))
- obj = data_type()
- set_properties(obj, data)
- return obj
- def _put_data(self, url, obj, headers=None):
+ data = resp.read()
+ if data is None:
+ return None
+ data = json.loads(text(data, encoding="utf-8"))
+ return data
+
+ def _put_data(self, url, data, headers=None):
headers = _add_content_type(headers)
- data = get_properties(obj)
try:
resp = restutil.http_put(url, json.dumps(data), headers=headers)
except restutil.HttpError as e:
@@ -81,9 +79,8 @@ class MetadataProtocol(Protocol):
if resp.status != httpclient.OK:
raise ProtocolError("{0} - PUT: {1}".format(resp.status, url))
- def _post_data(self, url, obj, headers=None):
+ def _post_data(self, url, data, headers=None):
headers = _add_content_type(headers)
- data = get_properties(obj)
try:
resp = restutil.http_post(url, json.dumps(data), headers=headers)
except restutil.HttpError as e:
@@ -95,28 +92,54 @@ class MetadataProtocol(Protocol):
pass
def get_vminfo(self):
- return self._get_data(VMInfo, self.identity_uri)
+ vminfo = VMInfo()
+ data = self._get_data(self.identity_uri)
+ set_properties("vminfo", vminfo, data)
+ return vminfo
def get_certs(self):
- #TODO walk arround for azure pack test
+ #TODO download and save certs
return CertList()
- certs = self._get_data(CertList, self.cert_uri)
- #TODO download pfx and convert to pem
- return certs
-
- def get_extensions(self):
- return self._get_data(ExtensionList, self.ext_uri)
-
- def report_provision_status(self, status):
- validata_param('status', status, ProvisionStatus)
- self._put_data(self.provision_status_uri, status)
-
- def report_status(self, status):
- validata_param('status', status, VMStatus)
- self._put_data(self.status_uri, status)
+ def get_ext_handlers(self):
+ ext_list = ExtHandlerList()
+ data = self._get_data(self.ext_uri)
+ set_properties("extensionHandlers", ext_list.extHandlers, data)
+ return ext_list
+
+ def get_ext_handler_pkgs(self, ext_handler):
+ ext_handler_pkgs = ExtHandlerPackageList()
+ data = None
+ for version_uri in ext_handler.versionUris:
+ try:
+ data = self._get_data(version_uri.uri)
+ break
+ except ProtocolError as e:
+ logger.warn("Failed to get version uris: {0}", e)
+ logger.info("Retry getting version uris")
+ set_properties("extensionPackages", ext_handler_pkgs, data)
+ return ext_handler_pkgs
+
+ def report_provision_status(self, provision_status):
+ validata_param('provisionStatus', provision_status, ProvisionStatus)
+ data = get_properties(provision_status)
+ self._put_data(self.provision_status_uri, data)
+
+ def report_vm_status(self, vm_status):
+ validata_param('vmStatus', vm_status, VMStatus)
+ data = get_properties(vm_status)
+ self._put_data(self.vm_status_uri, data)
+
+ def report_ext_status(self, ext_handler_name, ext_name, ext_status):
+ validata_param('extensionStatus', ext_status, ExtensionStatus)
+ data = get_properties(ext_status)
+ uri = self.ext_status_uri.format(ext_name)
+ self._put_data(uri, data)
def report_event(self, events):
- validata_param('events', events, TelemetryEventList)
- self._post_data(self.event_uri, events)
+ #TODO disable telemetry for azure stack test
+ #validata_param('events', events, TelemetryEventList)
+ #data = get_properties(events)
+ #self._post_data(self.event_uri, data)
+ pass
diff --git a/azurelinuxagent/utils/__init__.py b/azurelinuxagent/utils/__init__.py
index 4b2b9e1..d9b82f5 100644
--- a/azurelinuxagent/utils/__init__.py
+++ b/azurelinuxagent/utils/__init__.py
@@ -1,4 +1,4 @@
-# Windows Azure Linux Agent
+# Microsoft Azure Linux Agent
#
# Copyright 2014 Microsoft Corporation
#
diff --git a/azurelinuxagent/utils/fileutil.py b/azurelinuxagent/utils/fileutil.py
index 5e7fecf..08592bc 100644
--- a/azurelinuxagent/utils/fileutil.py
+++ b/azurelinuxagent/utils/fileutil.py
@@ -1,4 +1,4 @@
-# Windows Azure Linux Agent
+# Microsoft Azure Linux Agent
#
# Copyright 2014 Microsoft Corporation
#
diff --git a/azurelinuxagent/utils/osutil.py b/azurelinuxagent/utils/osutil.py
index 756400c..9de47e7 100644
--- a/azurelinuxagent/utils/osutil.py
+++ b/azurelinuxagent/utils/osutil.py
@@ -1,4 +1,4 @@
-# Windows Azure Linux Agent
+# Microsoft Azure Linux Agent
#
# Copyright 2014 Microsoft Corporation
#
diff --git a/azurelinuxagent/utils/restutil.py b/azurelinuxagent/utils/restutil.py
index 1015f71..2acfa57 100644
--- a/azurelinuxagent/utils/restutil.py
+++ b/azurelinuxagent/utils/restutil.py
@@ -1,4 +1,4 @@
-# Windows Azure Linux Agent
+# Microsoft Azure Linux Agent
#
# Copyright 2014 Microsoft Corporation
#
@@ -66,7 +66,7 @@ def _http_request(method, host, rel_uri, port=None, data=None, secure=False,
#If proxy is used, full url is needed.
url = "https://{0}:{1}{2}".format(host, port, rel_uri)
else:
- conn = httpclient.HTTPSConnection(host, port)
+ conn = httpclient.HTTPSConnection(host, port, timeout=10)
url = rel_uri
else:
port = 80 if port is None else port
@@ -75,7 +75,7 @@ def _http_request(method, host, rel_uri, port=None, data=None, secure=False,
#If proxy is used, full url is needed.
url = "http://{0}:{1}{2}".format(host, port, rel_uri)
else:
- conn = httpclient.HTTPConnection(host, port)
+ conn = httpclient.HTTPConnection(host, port, timeout=10)
url = rel_uri
if headers == None:
conn.request(method, url, data)
diff --git a/azurelinuxagent/utils/shellutil.py b/azurelinuxagent/utils/shellutil.py
index f4305d9..372c78a 100644
--- a/azurelinuxagent/utils/shellutil.py
+++ b/azurelinuxagent/utils/shellutil.py
@@ -1,4 +1,4 @@
-# Windows Azure Linux Agent
+# Microsoft Azure Linux Agent
#
# Copyright 2014 Microsoft Corporation
#
@@ -65,21 +65,25 @@ def run(cmd, chk_err=True):
retcode,out=run_get_output(cmd,chk_err)
return retcode
-def run_get_output(cmd, chk_err=True):
+def run_get_output(cmd, chk_err=True, log_cmd=True):
"""
Wrapper for subprocess.check_output.
Execute 'cmd'. Returns return code and STDOUT, trapping expected exceptions.
Reports exceptions to Error if chk_err parameter is True
"""
- logger.verb("run cmd '{0}'", cmd)
+ if log_cmd:
+ logger.verb(u"run cmd '{0}'", cmd)
try:
output=subprocess.check_output(cmd,stderr=subprocess.STDOUT,shell=True)
+ output = text(output, encoding='utf-8', errors="backslashreplace")
except subprocess.CalledProcessError as e :
- if chk_err :
- logger.error("run cmd '{0}' failed", e.cmd)
- logger.error("Error Code:{0}", e.returncode)
- logger.error("Result:{0}", e.output[:-1].decode('latin-1'))
- return e.returncode, e.output.decode('latin-1')
- return 0, text(output, encoding="utf-8")
+ output = text(e.output, encoding='utf-8', errors="backslashreplace")
+ if chk_err:
+ if log_cmd:
+ logger.error(u"run cmd '{0}' failed", e.cmd)
+ logger.error(u"Error Code:{0}", e.returncode)
+ logger.error(u"Result:{0}", output)
+ return e.returncode, output
+ return 0, output
#End shell command util functions
diff --git a/azurelinuxagent/utils/textutil.py b/azurelinuxagent/utils/textutil.py
index 2e66b0e..e0f1395 100644
--- a/azurelinuxagent/utils/textutil.py
+++ b/azurelinuxagent/utils/textutil.py
@@ -1,4 +1,4 @@
-# Windows Azure Linux Agent
+# Microsoft Azure Linux Agent
#
# Copyright 2014 Microsoft Corporation
#
@@ -22,6 +22,7 @@ import string
import struct
import xml.dom.minidom as minidom
import sys
+from distutils.version import LooseVersion
def parse_doc(xml_text):
"""
@@ -217,12 +218,11 @@ def remove_bom(c):
c = c[3:]
return c
-def gen_password_hash(password, use_salt, salt_type, salt_len):
- salt="$6$"
- if use_salt:
- collection = string.ascii_letters + string.digits
- salt = ''.join(random.choice(collection) for _ in range(salt_len))
- salt = "${0}${1}".format(salt_type, salt)
+def gen_password_hash(password, crypt_id, salt_len):
+ collection = string.ascii_letters + string.digits
+ salt = ''.join(random.choice(collection) for _ in range(salt_len))
+ salt = "${0}${1}".format(crypt_id, salt)
return crypt.crypt(password, salt)
+Version = LooseVersion