summaryrefslogtreecommitdiff
path: root/azurelinuxagent/distro/default
diff options
context:
space:
mode:
Diffstat (limited to 'azurelinuxagent/distro/default')
-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
13 files changed, 440 insertions, 286 deletions
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
#