diff --git a/azurelinuxagent/agent.py b/azurelinuxagent/agent.py index 93e9c16..8b5c8f2 100644 --- a/azurelinuxagent/agent.py +++ b/azurelinuxagent/agent.py @@ -25,41 +25,90 @@ import os import sys import re import subprocess -from azurelinuxagent.metadata import AGENT_NAME, AGENT_LONG_VERSION, \ +import azurelinuxagent.common.logger as logger +import azurelinuxagent.common.event as event +import azurelinuxagent.common.conf as conf +from azurelinuxagent.common.version import AGENT_NAME, AGENT_LONG_VERSION, \ DISTRO_NAME, DISTRO_VERSION, \ PY_VERSION_MAJOR, PY_VERSION_MINOR, \ PY_VERSION_MICRO - -from azurelinuxagent.distro.loader import get_distro +from azurelinuxagent.common.osutil import get_osutil class Agent(object): def __init__(self, verbose): """ Initialize agent running environment. """ - self.distro = get_distro(); - self.distro.init_handler.run(verbose) + self.osutil = get_osutil() + #Init stdout log + level = logger.LogLevel.VERBOSE if verbose else logger.LogLevel.INFO + logger.add_logger_appender(logger.AppenderType.STDOUT, level) + + #Init config + conf_file_path = self.osutil.get_agent_conf_file_path() + conf.load_conf_from_file(conf_file_path) + + #Init log + verbose = verbose or conf.get_logs_verbose() + level = logger.LogLevel.VERBOSE if verbose else logger.LogLevel.INFO + logger.add_logger_appender(logger.AppenderType.FILE, level, + path="/var/log/waagent.log") + logger.add_logger_appender(logger.AppenderType.CONSOLE, level, + path="/dev/console") + + #Init event reporter + event_dir = os.path.join(conf.get_lib_dir(), "events") + event.init_event_logger(event_dir) + event.enable_unhandled_err_dump("WALA") def daemon(self): """ Run agent daemon """ - self.distro.daemon_handler.run() + from azurelinuxagent.daemon import get_daemon_handler + daemon_handler = get_daemon_handler() + daemon_handler.run() + + def provision(self): + """ + Run provision command + """ + from azurelinuxagent.pa.provision import get_provision_handler + provision_handler = get_provision_handler() + provision_handler.run() def deprovision(self, force=False, deluser=False): """ Run deprovision command """ - self.distro.deprovision_handler.run(force=force, deluser=deluser) + from azurelinuxagent.pa.deprovision import get_deprovision_handler + deprovision_handler = get_deprovision_handler() + deprovision_handler.run(force=force, deluser=deluser) def register_service(self): """ Register agent as a service """ print("Register {0} service".format(AGENT_NAME)) - self.distro.osutil.register_agent_service() + self.osutil.register_agent_service() print("Start {0} service".format(AGENT_NAME)) - self.distro.osutil.start_agent_service() + self.osutil.start_agent_service() + + def update(self): + """ + Run extension handlers handler + """ + from azurelinuxagent.ga.update import get_update_handler + update_handler = get_update_handler() + update_handler.run() + + def run_exthandlers(self): + """ + Run extension handlers handler + """ + from azurelinuxagent.ga.exthandlers import get_exthandlers_handler + exthandlers_handler = get_exthandlers_handler() + exthandlers_handler.run() def main(): """ @@ -74,15 +123,24 @@ def main(): elif command == "start": start() else: - agent = Agent(verbose) - if command == "deprovision+user": - agent.deprovision(force, deluser=True) - elif command == "deprovision": - agent.deprovision(force, deluser=False) - elif command == "register-service": - agent.register_service() - elif command == "daemon": - agent.daemon() + try: + agent = Agent(verbose) + if command == "deprovision+user": + agent.deprovision(force, deluser=True) + elif command == "provision": + agent.provision() + elif command == "deprovision": + agent.deprovision(force, deluser=False) + elif command == "register-service": + agent.register_service() + elif command == "daemon": + agent.daemon() + elif command == "update": + agent.update() + elif command == "run-exthandlers": + agent.run_exthandlers() + except Exception as e: + logger.error(u"Failed to run '{0}': {1}", command, e) def parse_args(sys_args): """ @@ -102,6 +160,10 @@ def parse_args(sys_args): cmd = "start" elif re.match("^([-/]*)register-service", a): cmd = "register-service" + elif re.match("^([-/]*)update", a): + cmd = "update" + elif re.match("^([-/]*)run-exthandlers", a): + cmd = "run-exthandlers" elif re.match("^([-/]*)version", a): cmd = "version" elif re.match("^([-/]*)verbose", a): @@ -129,7 +191,8 @@ def usage(): """ print("") print((("usage: {0} [-verbose] [-force] [-help]" - "-deprovision[+user]|-register-service|-version|-daemon|-start]" + "-deprovision[+user]|-register-service|-version|-daemon|-start|" + "-update|-run-exthandlers]" "").format(sys.argv[0]))) print("") diff --git a/azurelinuxagent/distro/__init__.py b/azurelinuxagent/common/__init__.py similarity index 95% rename from azurelinuxagent/distro/__init__.py rename to azurelinuxagent/common/__init__.py index d9b82f5..1ea2f38 100644 --- a/azurelinuxagent/distro/__init__.py +++ b/azurelinuxagent/common/__init__.py @@ -1,5 +1,3 @@ -# Microsoft Azure Linux Agent -# # Copyright 2014 Microsoft Corporation # # Licensed under the Apache License, Version 2.0 (the "License"); diff --git a/azurelinuxagent/conf.py b/azurelinuxagent/common/conf.py similarity index 94% rename from azurelinuxagent/conf.py rename to azurelinuxagent/common/conf.py index 1ac65cb..22b5d74 100644 --- a/azurelinuxagent/conf.py +++ b/azurelinuxagent/common/conf.py @@ -21,8 +21,8 @@ Module conf loads and parses configuration file """ import os -import azurelinuxagent.utils.fileutil as fileutil -from azurelinuxagent.exception import AgentConfigError +import azurelinuxagent.common.utils.fileutil as fileutil +from azurelinuxagent.common.exception import AgentConfigError class ConfigurationProvider(object): """ @@ -167,3 +167,9 @@ def get_resourcedisk_filesystem(conf=__conf__): def get_resourcedisk_swap_size_mb(conf=__conf__): return conf.get_int("ResourceDisk.SwapSizeMB", 0) +def get_autoupdate_gafamily(conf=__conf__): + return conf.get("AutoUpdate.GAFamily", "Prod") + +def get_autoupdate_enabled(conf=__conf__): + return conf.get_switch("AutoUpdate.Enabled", True) + diff --git a/azurelinuxagent/distro/default/dhcp.py b/azurelinuxagent/common/dhcp.py similarity index 87% rename from azurelinuxagent/distro/default/dhcp.py rename to azurelinuxagent/common/dhcp.py index 1c20960..9b4da90 100644 --- a/azurelinuxagent/distro/default/dhcp.py +++ b/azurelinuxagent/common/dhcp.py @@ -1,5 +1,3 @@ -# Microsoft Azure Linux Agent -# # Copyright 2014 Microsoft Corporation # # Licensed under the Apache License, Version 2.0 (the "License"); @@ -15,29 +13,34 @@ # limitations under the License. # # Requires Python 2.4+ and Openssl 1.0+ + import os import socket import array import time import threading -import azurelinuxagent.logger as logger -import azurelinuxagent.conf as conf -import azurelinuxagent.utils.fileutil as fileutil -import azurelinuxagent.utils.shellutil as shellutil -from azurelinuxagent.utils.textutil import hex_dump, hex_dump2, hex_dump3, \ - compare_bytes, str_to_ord, \ - unpack_big_endian, \ - unpack_little_endian, \ - int_to_ip4_addr -from azurelinuxagent.exception import DhcpError +import azurelinuxagent.common.logger as logger +import azurelinuxagent.common.conf as conf +import azurelinuxagent.common.utils.fileutil as fileutil +import azurelinuxagent.common.utils.shellutil as shellutil +from azurelinuxagent.common.utils.textutil import hex_dump, hex_dump2, \ + hex_dump3, \ + compare_bytes, str_to_ord, \ + unpack_big_endian, \ + unpack_little_endian, \ + int_to_ip4_addr +from azurelinuxagent.common.exception import DhcpError +from azurelinuxagent.common.osutil import get_osutil +def get_dhcp_handler(): + return DhcpHandler() class DhcpHandler(object): """ Azure use DHCP option 245 to pass endpoint ip to VMs. """ - def __init__(self, distro): - self.distro = distro + def __init__(self): + self.osutil = get_osutil() self.endpoint = None self.gateway = None self.routes = None @@ -56,13 +59,13 @@ class DhcpHandler(object): """ Wait for network stack to be initialized. """ - ipv4 = self.distro.osutil.get_ip4_addr() + ipv4 = self.osutil.get_ip4_addr() while ipv4 == '' or ipv4 == '0.0.0.0': logger.info("Waiting for network.") time.sleep(10) logger.info("Try to start network interface.") - self.distro.osutil.start_network() - ipv4 = self.distro.osutil.get_ip4_addr() + self.osutil.start_network() + ipv4 = self.osutil.get_ip4_addr() def conf_routes(self): logger.info("Configure routes") @@ -70,16 +73,16 @@ class DhcpHandler(object): logger.info("Routes:{0}", self.routes) #Add default gateway if self.gateway is not None: - self.distro.osutil.route_add(0 , 0, self.gateway) + self.osutil.route_add(0 , 0, self.gateway) if self.routes is not None: for route in self.routes: - self.distro.osutil.route_add(route[0], route[1], route[2]) + self.osutil.route_add(route[0], route[1], route[2]) def _send_dhcp_req(self, request): __waiting_duration__ = [0, 10, 30, 60, 60] for duration in __waiting_duration__: try: - self.distro.osutil.allow_dhcp_broadcast() + self.osutil.allow_dhcp_broadcast() response = socket_send(request) validate_dhcp_resp(request, response) return response @@ -95,7 +98,8 @@ class DhcpHandler(object): Stop dhcp service if necessary """ logger.info("Send dhcp request") - mac_addr = self.distro.osutil.get_mac_addr() + mac_addr = self.osutil.get_mac_addr() + req = build_dhcp_request(mac_addr) # Do unicast first, then fallback to broadcast if fails. req = build_dhcp_request(mac_addr, self._request_broadcast) @@ -103,23 +107,23 @@ class DhcpHandler(object): self._request_broadcast = True # Temporary allow broadcast for dhcp. Remove the route when done. - missing_default_route = self.distro.osutil.is_missing_default_route() - ifname = self.distro.osutil.get_if_name() + missing_default_route = self.osutil.is_missing_default_route() + ifname = self.osutil.get_if_name() if missing_default_route: - self.distro.osutil.set_route_for_dhcp_broadcast(ifname) + self.osutil.set_route_for_dhcp_broadcast(ifname) # In some distros, dhcp service needs to be shutdown before agent probe # endpoint through dhcp. - if self.distro.osutil.is_dhcp_enabled(): - self.distro.osutil.stop_dhcp_service() + if self.osutil.is_dhcp_enabled(): + self.osutil.stop_dhcp_service() resp = self._send_dhcp_req(req) - if self.distro.osutil.is_dhcp_enabled(): - self.distro.osutil.start_dhcp_service() + if self.osutil.is_dhcp_enabled(): + self.osutil.start_dhcp_service() if missing_default_route: - self.distro.osutil.remove_route_for_dhcp_broadcast(ifname) + self.osutil.remove_route_for_dhcp_broadcast(ifname) if resp is None: raise DhcpError("Failed to receive dhcp response.") diff --git a/azurelinuxagent/event.py b/azurelinuxagent/common/event.py similarity index 93% rename from azurelinuxagent/event.py rename to azurelinuxagent/common/event.py index f38b242..2ab2ef2 100644 --- a/azurelinuxagent/event.py +++ b/azurelinuxagent/common/event.py @@ -24,14 +24,14 @@ import time import datetime import threading import platform -import azurelinuxagent.logger as logger -from azurelinuxagent.exception import EventError, ProtocolError -from azurelinuxagent.future import ustr -from azurelinuxagent.protocol.restapi import TelemetryEventParam, \ +import azurelinuxagent.common.logger as logger +from azurelinuxagent.common.exception import EventError, ProtocolError +from azurelinuxagent.common.future import ustr +from azurelinuxagent.common.protocol.restapi import TelemetryEventParam, \ TelemetryEventList, \ TelemetryEvent, \ set_properties, get_properties -from azurelinuxagent.metadata import DISTRO_NAME, DISTRO_VERSION, \ +from azurelinuxagent.common.version import DISTRO_NAME, DISTRO_VERSION, \ DISTRO_CODE_NAME, AGENT_VERSION diff --git a/azurelinuxagent/exception.py b/azurelinuxagent/common/exception.py similarity index 94% rename from azurelinuxagent/exception.py rename to azurelinuxagent/common/exception.py index 7fa5cff..457490c 100644 --- a/azurelinuxagent/exception.py +++ b/azurelinuxagent/common/exception.py @@ -113,3 +113,11 @@ class CryptError(AgentError): """ def __init__(self, msg=None, inner=None): super(CryptError, self).__init__('000011', msg, inner) + +class UpdateError(AgentError): + """ + Update Guest Agent error + """ + def __init__(self, msg=None, inner=None): + super(UpdateError, self).__init__('000012', msg, inner) + diff --git a/azurelinuxagent/future.py b/azurelinuxagent/common/future.py similarity index 100% rename from azurelinuxagent/future.py rename to azurelinuxagent/common/future.py diff --git a/azurelinuxagent/logger.py b/azurelinuxagent/common/logger.py similarity index 95% rename from azurelinuxagent/logger.py rename to azurelinuxagent/common/logger.py index 52cb55f..bd4916b 100644 --- a/azurelinuxagent/logger.py +++ b/azurelinuxagent/common/logger.py @@ -14,15 +14,12 @@ # # Requires Python 2.4+ and openssl_bin 1.0+ # -# Implements parts of RFC 2131, 1541, 1497 and -# http://msdn.microsoft.com/en-us/library/cc227282%28PROT.10%29.aspx -# http://msdn.microsoft.com/en-us/library/cc227259%28PROT.13%29.aspx """ Log utils """ import os import sys -from azurelinuxagent.future import ustr +from azurelinuxagent.common.future import ustr from datetime import datetime class Logger(object): diff --git a/azurelinuxagent/common/osutil/__init__.py b/azurelinuxagent/common/osutil/__init__.py new file mode 100644 index 0000000..3b5ba3b --- /dev/null +++ b/azurelinuxagent/common/osutil/__init__.py @@ -0,0 +1,18 @@ +# Copyright 2014 Microsoft Corporation +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +# +# Requires Python 2.4+ and Openssl 1.0+ +# + +from azurelinuxagent.common.osutil.factory import get_osutil diff --git a/azurelinuxagent/distro/coreos/osutil.py b/azurelinuxagent/common/osutil/coreos.py similarity index 90% rename from azurelinuxagent/distro/coreos/osutil.py rename to azurelinuxagent/common/osutil/coreos.py index b174d04..e26fd97 100644 --- a/azurelinuxagent/distro/coreos/osutil.py +++ b/azurelinuxagent/common/osutil/coreos.py @@ -26,11 +26,11 @@ import struct import fcntl import time import base64 -import azurelinuxagent.logger as logger -import azurelinuxagent.utils.fileutil as fileutil -import azurelinuxagent.utils.shellutil as shellutil -import azurelinuxagent.utils.textutil as textutil -from azurelinuxagent.distro.default.osutil import DefaultOSUtil +import azurelinuxagent.common.logger as logger +import azurelinuxagent.common.utils.fileutil as fileutil +import azurelinuxagent.common.utils.shellutil as shellutil +import azurelinuxagent.common.utils.textutil as textutil +from azurelinuxagent.common.osutil.default import DefaultOSUtil class CoreOSUtil(DefaultOSUtil): def __init__(self): diff --git a/azurelinuxagent/distro/debian/osutil.py b/azurelinuxagent/common/osutil/debian.py similarity index 80% rename from azurelinuxagent/distro/debian/osutil.py rename to azurelinuxagent/common/osutil/debian.py index a40c1de..f455572 100644 --- a/azurelinuxagent/distro/debian/osutil.py +++ b/azurelinuxagent/common/osutil/debian.py @@ -26,11 +26,11 @@ import struct import fcntl import time import base64 -import azurelinuxagent.logger as logger -import azurelinuxagent.utils.fileutil as fileutil -import azurelinuxagent.utils.shellutil as shellutil -import azurelinuxagent.utils.textutil as textutil -from azurelinuxagent.distro.default.osutil import DefaultOSUtil +import azurelinuxagent.common.logger as logger +import azurelinuxagent.common.utils.fileutil as fileutil +import azurelinuxagent.common.utils.shellutil as shellutil +import azurelinuxagent.common.utils.textutil as textutil +from azurelinuxagent.common.osutil.default import DefaultOSUtil class DebianOSUtil(DefaultOSUtil): def __init__(self): diff --git a/azurelinuxagent/distro/default/osutil.py b/azurelinuxagent/common/osutil/default.py similarity index 98% rename from azurelinuxagent/distro/default/osutil.py rename to azurelinuxagent/common/osutil/default.py index 523202b..796d92d 100644 --- a/azurelinuxagent/distro/default/osutil.py +++ b/azurelinuxagent/common/osutil/default.py @@ -26,14 +26,14 @@ import time import pwd import fcntl import base64 -import azurelinuxagent.logger as logger -import azurelinuxagent.conf as conf -from azurelinuxagent.exception import OSUtilError -from azurelinuxagent.future import ustr -import azurelinuxagent.utils.fileutil as fileutil -import azurelinuxagent.utils.shellutil as shellutil -import azurelinuxagent.utils.textutil as textutil -from azurelinuxagent.utils.cryptutil import CryptUtil +import azurelinuxagent.common.logger as logger +import azurelinuxagent.common.conf as conf +from azurelinuxagent.common.exception import OSUtilError +from azurelinuxagent.common.future import ustr +import azurelinuxagent.common.utils.fileutil as fileutil +import azurelinuxagent.common.utils.shellutil as shellutil +import azurelinuxagent.common.utils.textutil as textutil +from azurelinuxagent.common.utils.cryptutil import CryptUtil __RULES_FILES__ = [ "/lib/udev/rules.d/75-persistent-net-generator.rules", "/etc/udev/rules.d/70-persistent-net.rules" ] diff --git a/azurelinuxagent/distro/loader.py b/azurelinuxagent/common/osutil/factory.py similarity index 57% rename from azurelinuxagent/distro/loader.py rename to azurelinuxagent/common/osutil/factory.py index f6800b3..5e8ae6e 100644 --- a/azurelinuxagent/distro/loader.py +++ b/azurelinuxagent/common/osutil/factory.py @@ -15,56 +15,55 @@ # Requires Python 2.4+ and Openssl 1.0+ # -import azurelinuxagent.logger as logger -from azurelinuxagent.utils.textutil import Version -from azurelinuxagent.metadata import DISTRO_NAME, DISTRO_VERSION, \ +import azurelinuxagent.common.logger as logger +from azurelinuxagent.common.utils.textutil import Version +from azurelinuxagent.common.version import DISTRO_NAME, DISTRO_VERSION, \ DISTRO_FULL_NAME -from azurelinuxagent.distro.default.distro import DefaultDistro -from azurelinuxagent.distro.ubuntu.distro import UbuntuDistro, \ - Ubuntu14Distro, \ - Ubuntu12Distro, \ - UbuntuSnappyDistro -from azurelinuxagent.distro.redhat.distro import RedhatDistro, Redhat6xDistro -from azurelinuxagent.distro.coreos.distro import CoreOSDistro -from azurelinuxagent.distro.suse.distro import SUSE11Distro, SUSEDistro -from azurelinuxagent.distro.debian.distro import DebianDistro -from azurelinuxagent.distro.freebsd.distro import FreeBSDDistro -def get_distro(distro_name=DISTRO_NAME, distro_version=DISTRO_VERSION, +from .default import DefaultOSUtil +from .coreos import CoreOSUtil +from .debian import DebianOSUtil +from .freebsd import FreeBSDOSUtil +from .redhat import RedhatOSUtil, Redhat6xOSUtil +from .suse import SUSEOSUtil, SUSE11OSUtil +from .ubuntu import UbuntuOSUtil, Ubuntu12OSUtil, Ubuntu14OSUtil, \ + UbuntuSnappyOSUtil + +def get_osutil(distro_name=DISTRO_NAME, distro_version=DISTRO_VERSION, distro_full_name=DISTRO_FULL_NAME): if distro_name == "ubuntu": if Version(distro_version) == Version("12.04") or \ Version(distro_version) == Version("12.10"): - return Ubuntu12Distro() + return Ubuntu12OSUtil() elif Version(distro_version) == Version("14.04") or \ Version(distro_version) == Version("14.10"): - return Ubuntu14Distro() + return Ubuntu14OSUtil() elif distro_full_name == "Snappy Ubuntu Core": - return UbuntuSnappyDistro() + return UbuntuSnappyOSUtil() else: - return UbuntuDistro() + return UbuntuOSUtil() if distro_name == "coreos": - return CoreOSDistro() + return CoreOSUtil() if distro_name == "suse": if distro_full_name=='SUSE Linux Enterprise Server' and \ Version(distro_version) < Version('12') or \ distro_full_name == 'openSUSE' and \ Version(distro_version) < Version('13.2'): - return SUSE11Distro() + return SUSE11OSUtil() else: - return SUSEDistro() + return SUSEOSUtil() elif distro_name == "debian": - return DebianDistro() + return DebianOSUtil() elif distro_name == "redhat" or distro_name == "centos" or \ distro_name == "oracle": if Version(distro_version) < Version("7"): - return Redhat6xDistro() + return Redhat6xOSUtil() else: - return RedhatDistro() + return RedhatOSUtil() elif distro_name == "freebsd": - return FreeBSDDistro() + return FreeBSDOSUtil() else: logger.warn("Unable to load distro implemetation for {0}.", distro_name) logger.warn("Use default distro implemetation instead.") - return DefaultDistro() + return DefaultOSUtil() diff --git a/azurelinuxagent/distro/freebsd/osutil.py b/azurelinuxagent/common/osutil/freebsd.py similarity index 96% rename from azurelinuxagent/distro/freebsd/osutil.py rename to azurelinuxagent/common/osutil/freebsd.py index 3dfb6fc..3d2febd 100644 --- a/azurelinuxagent/distro/freebsd/osutil.py +++ b/azurelinuxagent/common/osutil/freebsd.py @@ -16,11 +16,11 @@ # # Requires Python 2.4+ and Openssl 1.0+ -import azurelinuxagent.utils.shellutil as shellutil -import azurelinuxagent.utils.textutil as textutil -import azurelinuxagent.logger as logger -from azurelinuxagent.distro.default.osutil import DefaultOSUtil -from azurelinuxagent.exception import OSUtilError +import azurelinuxagent.common.utils.shellutil as shellutil +import azurelinuxagent.common.utils.textutil as textutil +import azurelinuxagent.common.logger as logger +from azurelinuxagent.common.exception import OSUtilError +from azurelinuxagent.common.osutil.default import DefaultOSUtil class FreeBSDOSUtil(DefaultOSUtil): diff --git a/azurelinuxagent/distro/redhat/osutil.py b/azurelinuxagent/common/osutil/redhat.py similarity index 87% rename from azurelinuxagent/distro/redhat/osutil.py rename to azurelinuxagent/common/osutil/redhat.py index 7f769a5..1001e7e 100644 --- a/azurelinuxagent/distro/redhat/osutil.py +++ b/azurelinuxagent/common/osutil/redhat.py @@ -26,15 +26,15 @@ import struct import fcntl import time import base64 -import azurelinuxagent.conf as conf -import azurelinuxagent.logger as logger -from azurelinuxagent.future import ustr, bytebuffer -from azurelinuxagent.exception import OSUtilError, CryptError -import azurelinuxagent.utils.fileutil as fileutil -import azurelinuxagent.utils.shellutil as shellutil -import azurelinuxagent.utils.textutil as textutil -from azurelinuxagent.utils.cryptutil import CryptUtil -from azurelinuxagent.distro.default.osutil import DefaultOSUtil +import azurelinuxagent.common.conf as conf +import azurelinuxagent.common.logger as logger +from azurelinuxagent.common.future import ustr, bytebuffer +from azurelinuxagent.common.exception import OSUtilError, CryptError +import azurelinuxagent.common.utils.fileutil as fileutil +import azurelinuxagent.common.utils.shellutil as shellutil +import azurelinuxagent.common.utils.textutil as textutil +from azurelinuxagent.common.utils.cryptutil import CryptUtil +from azurelinuxagent.common.osutil.default import DefaultOSUtil class Redhat6xOSUtil(DefaultOSUtil): def __init__(self): diff --git a/azurelinuxagent/distro/suse/osutil.py b/azurelinuxagent/common/osutil/suse.py similarity index 89% rename from azurelinuxagent/distro/suse/osutil.py rename to azurelinuxagent/common/osutil/suse.py index 8d6f5bf..f0ed0c0 100644 --- a/azurelinuxagent/distro/suse/osutil.py +++ b/azurelinuxagent/common/osutil/suse.py @@ -25,12 +25,12 @@ import array import struct import fcntl import time -import azurelinuxagent.logger as logger -import azurelinuxagent.utils.fileutil as fileutil -import azurelinuxagent.utils.shellutil as shellutil -import azurelinuxagent.utils.textutil as textutil -from azurelinuxagent.metadata import DISTRO_NAME, DISTRO_VERSION, DISTRO_FULL_NAME -from azurelinuxagent.distro.default.osutil import DefaultOSUtil +import azurelinuxagent.common.logger as logger +import azurelinuxagent.common.utils.fileutil as fileutil +import azurelinuxagent.common.utils.shellutil as shellutil +import azurelinuxagent.common.utils.textutil as textutil +from azurelinuxagent.common.version import DISTRO_NAME, DISTRO_VERSION, DISTRO_FULL_NAME +from azurelinuxagent.common.osutil.default import DefaultOSUtil class SUSE11OSUtil(DefaultOSUtil): def __init__(self): diff --git a/azurelinuxagent/distro/ubuntu/osutil.py b/azurelinuxagent/common/osutil/ubuntu.py similarity index 88% rename from azurelinuxagent/distro/ubuntu/osutil.py rename to azurelinuxagent/common/osutil/ubuntu.py index cc4b8ef..3cf669a 100644 --- a/azurelinuxagent/distro/ubuntu/osutil.py +++ b/azurelinuxagent/common/osutil/ubuntu.py @@ -25,11 +25,11 @@ import array import struct import fcntl import time -import azurelinuxagent.logger as logger -import azurelinuxagent.utils.fileutil as fileutil -import azurelinuxagent.utils.shellutil as shellutil -import azurelinuxagent.utils.textutil as textutil -from azurelinuxagent.distro.default.osutil import DefaultOSUtil +import azurelinuxagent.common.logger as logger +import azurelinuxagent.common.utils.fileutil as fileutil +import azurelinuxagent.common.utils.shellutil as shellutil +import azurelinuxagent.common.utils.textutil as textutil +from azurelinuxagent.common.osutil.default import DefaultOSUtil class Ubuntu14OSUtil(DefaultOSUtil): def __init__(self): diff --git a/azurelinuxagent/distro/debian/loader.py b/azurelinuxagent/common/protocol/__init__.py similarity index 76% rename from azurelinuxagent/distro/debian/loader.py rename to azurelinuxagent/common/protocol/__init__.py index cc0c06f..fb7c273 100644 --- a/azurelinuxagent/distro/debian/loader.py +++ b/azurelinuxagent/common/protocol/__init__.py @@ -1,5 +1,3 @@ -# Microsoft Azure Linux Agent -# # Copyright 2014 Microsoft Corporation # # Licensed under the Apache License, Version 2.0 (the "License"); @@ -17,8 +15,7 @@ # Requires Python 2.4+ and Openssl 1.0+ # - -def get_osutil(): - from azurelinuxagent.distro.debian.osutil import DebianOSUtil - return DebianOSUtil() +from azurelinuxagent.common.protocol.util import get_protocol_util, \ + OVF_FILE_NAME, \ + TAG_FILE_NAME diff --git a/azurelinuxagent/protocol/metadata.py b/azurelinuxagent/common/protocol/metadata.py similarity index 81% rename from azurelinuxagent/protocol/metadata.py rename to azurelinuxagent/common/protocol/metadata.py index 8a1656f..38d7645 100644 --- a/azurelinuxagent/protocol/metadata.py +++ b/azurelinuxagent/common/protocol/metadata.py @@ -20,15 +20,15 @@ import json import shutil import os import time -from azurelinuxagent.exception import ProtocolError, HttpError -from azurelinuxagent.future import httpclient, ustr -import azurelinuxagent.conf as conf -import azurelinuxagent.logger as logger -import azurelinuxagent.utils.restutil as restutil -import azurelinuxagent.utils.textutil as textutil -import azurelinuxagent.utils.fileutil as fileutil -from azurelinuxagent.utils.cryptutil import CryptUtil -from azurelinuxagent.protocol.restapi import * +from azurelinuxagent.common.exception import ProtocolError, HttpError +from azurelinuxagent.common.future import httpclient, ustr +import azurelinuxagent.common.conf as conf +import azurelinuxagent.common.logger as logger +import azurelinuxagent.common.utils.restutil as restutil +import azurelinuxagent.common.utils.textutil as textutil +import azurelinuxagent.common.utils.fileutil as fileutil +from azurelinuxagent.common.utils.cryptutil import CryptUtil +from azurelinuxagent.common.protocol.restapi import * METADATA_ENDPOINT='169.254.169.254' APIVERSION='2015-05-01-preview' @@ -58,6 +58,8 @@ class MetadataProtocol(Protocol): self.apiversion, "&$expand=*") self.ext_uri = BASE_URI.format(self.endpoint, "extensionHandlers", self.apiversion, "&$expand=*") + self.vmagent_uri = BASE_URI.format(self.endpoint, "vmAgentVersions", + self.apiversion, "&$expand=*") self.provision_status_uri = BASE_URI.format(self.endpoint, "provisioningStatus", self.apiversion, "") @@ -140,6 +142,29 @@ class MetadataProtocol(Protocol): #TODO download and save certs return CertList() + def get_vmagent_manifests(self): + manifests = VMAgentManifestList() + data = self._get_data(self.vmagent_uri) + set_properties("vmAgentManifests", manifests.vmAgentManifests, data) + return manifests + + def get_vmagent_pkgs(self, vmagent_manifest): + #Agent package is the same with extension handler + vmagent_pkgs = ExtHandlerPackageList() + data = None + for manifest_uri in vmagent_manifest.versionsManifestUris: + try: + data = self._get_data(manifest_uri.uri) + break + except ProtocolError as e: + logger.warn("Failed to get vmagent versions: {0}", e) + logger.info("Retry getting vmagent versions") + if data is None: + raise ProtocolError(("Failed to get versions for vm agent: {0}" + "").format(vmagent_manifest.family)) + set_properties("vmAgentVersions", vmagent_pkgs, data) + return vmagent_pkgs + def get_ext_handlers(self): headers = { "x-ms-vmagent-public-x509-cert": self._get_trans_cert() diff --git a/azurelinuxagent/protocol/ovfenv.py b/azurelinuxagent/common/protocol/ovfenv.py similarity index 93% rename from azurelinuxagent/protocol/ovfenv.py rename to azurelinuxagent/common/protocol/ovfenv.py index de6791c..47571b2 100644 --- a/azurelinuxagent/protocol/ovfenv.py +++ b/azurelinuxagent/common/protocol/ovfenv.py @@ -23,11 +23,11 @@ import os import re import shutil import xml.dom.minidom as minidom -import azurelinuxagent.logger as logger -from azurelinuxagent.exception import ProtocolError -from azurelinuxagent.future import ustr -import azurelinuxagent.utils.fileutil as fileutil -from azurelinuxagent.utils.textutil import parse_doc, findall, find, findtext +import azurelinuxagent.common.logger as logger +from azurelinuxagent.common.exception import ProtocolError +from azurelinuxagent.common.future import ustr +import azurelinuxagent.common.utils.fileutil as fileutil +from azurelinuxagent.common.utils.textutil import parse_doc, findall, find, findtext OVF_VERSION = "1.0" OVF_NAME_SPACE = "http://schemas.dmtf.org/ovf/environment/1" diff --git a/azurelinuxagent/protocol/restapi.py b/azurelinuxagent/common/protocol/restapi.py similarity index 90% rename from azurelinuxagent/protocol/restapi.py rename to azurelinuxagent/common/protocol/restapi.py index 2778dc4..c1ca64e 100644 --- a/azurelinuxagent/protocol/restapi.py +++ b/azurelinuxagent/common/protocol/restapi.py @@ -21,10 +21,10 @@ import copy import re import json import xml.dom.minidom -import azurelinuxagent.logger as logger -from azurelinuxagent.exception import ProtocolError, HttpError -from azurelinuxagent.future import ustr -import azurelinuxagent.utils.restutil as restutil +import azurelinuxagent.common.logger as logger +from azurelinuxagent.common.exception import ProtocolError, HttpError +from azurelinuxagent.common.future import ustr +import azurelinuxagent.common.utils.restutil as restutil def validata_param(name, val, expected_type): if val is None: @@ -102,6 +102,20 @@ class CertList(DataContract): def __init__(self): self.certificates = DataContractList(Cert) +#TODO: confirm vmagent manifest schema +class VMAgentManifestUri(DataContract): + def __init__(self, uri=None): + self.uri = uri + +class VMAgentManifest(DataContract): + def __init__(self, family=None): + self.family = family + self.versionsManifestUris = DataContractList(VMAgentManifestUri) + +class VMAgentManifestList(DataContract): + def __init__(self): + self.vmAgentManifests = DataContractList(VMAgentManifest) + class Extension(DataContract): def __init__(self, name=None, sequenceNumber=None, publicSettings=None, protectedSettings=None, certificateThumbprint=None): @@ -224,6 +238,12 @@ class Protocol(DataContract): def get_certs(self): raise NotImplementedError() + def get_vmagent_manifests(self): + raise NotImplementedError() + + def get_vmagent_pkgs(self): + raise NotImplementedError() + def get_ext_handlers(self): raise NotImplementedError() diff --git a/azurelinuxagent/distro/default/protocolUtil.py b/azurelinuxagent/common/protocol/util.py similarity index 67% rename from azurelinuxagent/distro/default/protocolUtil.py rename to azurelinuxagent/common/protocol/util.py index 34466cf..98adeed 100644 --- a/azurelinuxagent/distro/default/protocolUtil.py +++ b/azurelinuxagent/common/protocol/util.py @@ -21,16 +21,19 @@ import re import shutil import time import threading -import azurelinuxagent.conf as conf -import azurelinuxagent.logger as logger -from azurelinuxagent.exception import ProtocolError, OSUtilError, \ +import azurelinuxagent.common.conf as conf +import azurelinuxagent.common.logger as logger +from azurelinuxagent.common.exception import ProtocolError, OSUtilError, \ ProtocolNotFoundError, DhcpError -from azurelinuxagent.future import ustr -import azurelinuxagent.utils.fileutil as fileutil -from azurelinuxagent.protocol.ovfenv import OvfEnv -from azurelinuxagent.protocol.wire import WireProtocol -from azurelinuxagent.protocol.metadata import MetadataProtocol, METADATA_ENDPOINT -import azurelinuxagent.utils.shellutil as shellutil +from azurelinuxagent.common.future import ustr +import azurelinuxagent.common.utils.fileutil as fileutil +from azurelinuxagent.common.osutil import get_osutil +from azurelinuxagent.common.dhcp import get_dhcp_handler +from azurelinuxagent.common.protocol.ovfenv import OvfEnv +from azurelinuxagent.common.protocol.wire import WireProtocol +from azurelinuxagent.common.protocol.metadata import MetadataProtocol, \ + METADATA_ENDPOINT +import azurelinuxagent.common.utils.shellutil as shellutil OVF_FILE_NAME = "ovf-env.xml" @@ -46,15 +49,19 @@ PROBE_INTERVAL = 10 ENDPOINT_FILE_NAME = "WireServerEndpoint" +def get_protocol_util(): + return ProtocolUtil() + class ProtocolUtil(object): """ ProtocolUtil handles initialization for protocol instance. 2 protocol types are invoked, wire protocol and metadata protocols. """ - def __init__(self, distro): - self.distro = distro - self.protocol = None + def __init__(self): self.lock = threading.Lock() + self.protocol = None + self.osutil = get_osutil() + self.dhcp_handler = get_dhcp_handler() def copy_ovf_env(self): """ @@ -65,7 +72,7 @@ class ProtocolUtil(object): ovf_file_path_on_dvd = os.path.join(dvd_mount_point, OVF_FILE_NAME) tag_file_path_on_dvd = os.path.join(dvd_mount_point, TAG_FILE_NAME) try: - self.distro.osutil.mount_dvd() + self.osutil.mount_dvd() ovfxml = fileutil.read_file(ovf_file_path_on_dvd, remove_bom=True) ovfenv = OvfEnv(ovfxml) ovfxml = re.sub(".*?<", "*<", ovfxml) @@ -81,8 +88,8 @@ class ProtocolUtil(object): raise ProtocolError(ustr(e)) try: - self.distro.osutil.umount_dvd() - self.distro.osutil.eject_dvd() + self.osutil.umount_dvd() + self.osutil.eject_dvd() except OSUtilError as e: logger.warn(ustr(e)) @@ -114,23 +121,24 @@ class ProtocolUtil(object): raise OSUtilError(ustr(e)) def _detect_wire_protocol(self): - endpoint = self.distro.dhcp_handler.endpoint + endpoint = self.dhcp_handler.endpoint if endpoint is None: logger.info("WireServer endpoint is not found. Rerun dhcp handler") try: - self.distro.dhcp_handler.run() + self.dhcp_handler.run() except DhcpError as e: raise ProtocolError(ustr(e)) - endpoint = self.distro.dhcp_handler.endpoint + endpoint = self.dhcp_handler.endpoint try: protocol = WireProtocol(endpoint) protocol.detect() self._set_wireserver_endpoint(endpoint) + self.save_protocol("WireProtocol") return protocol except ProtocolError as e: logger.info("WireServer is not responding. Reset endpoint") - self.distro.dhcp_handler.endpoint = None + self.dhcp_handler.endpoint = None raise e def _detect_metadata_protocol(self): @@ -138,7 +146,9 @@ class ProtocolUtil(object): protocol.detect() #Only allow root access METADATA_ENDPOINT - self.distro.osutil.set_admin_access_to_ip(METADATA_ENDPOINT) + self.osutil.set_admin_access_to_ip(METADATA_ENDPOINT) + + self.save_protocol("MetadataProtocol") return protocol @@ -146,9 +156,8 @@ class ProtocolUtil(object): """ Probe protocol endpoints in turn. """ - protocol_file_path = os.path.join(conf.get_lib_dir(), PROTOCOL_FILE_NAME) - if os.path.isfile(protocol_file_path): - os.remove(protocol_file_path) + self.clear_protocol() + for retry in range(0, MAX_RETRY): for protocol in protocols: try: @@ -174,7 +183,7 @@ class ProtocolUtil(object): protocol_file_path = os.path.join(conf.get_lib_dir(), PROTOCOL_FILE_NAME) if not os.path.isfile(protocol_file_path): - raise ProtocolError("No protocl found") + raise ProtocolNotFoundError("No protocol found") protocol_name = fileutil.read_file(protocol_file_path) if protocol_name == "WireProtocol": @@ -186,23 +195,61 @@ class ProtocolUtil(object): raise ProtocolNotFoundError(("Unknown protocol: {0}" "").format(protocol_name)) - def detect_protocol(self): + def save_protocol(self, protocol_name): + """ + Save protocol endpoint + """ + protocol_file_path = os.path.join(conf.get_lib_dir(), PROTOCOL_FILE_NAME) + try: + fileutil.write_file(protocol_file_path, protocol_name) + except IOError as e: + logger.error("Failed to save protocol endpoint: {0}", e) + + + def clear_protocol(self): + """ + Cleanup previous saved endpoint. + """ + logger.info("Clean protocol") + self.protocol = None + protocol_file_path = os.path.join(conf.get_lib_dir(), PROTOCOL_FILE_NAME) + if not os.path.isfile(protocol_file_path): + return + + try: + os.remove(protocol_file_path) + except IOError as e: + logger.error("Failed to clear protocol endpoint: {0}", e) + + def get_protocol(self): """ Detect protocol by endpoints :returns: protocol instance """ - logger.info("Detect protocol endpoints") - protocols = ["WireProtocol", "MetadataProtocol"] self.lock.acquire() + try: - if self.protocol is None: - self.protocol = self._detect_protocol(protocols) + if self.protocol is not None: + return self.protocol + + try: + self.protocol = self._get_protocol() + return self.protocol + except ProtocolNotFoundError: + pass + + logger.info("Detect protocol endpoints") + protocols = ["WireProtocol", "MetadataProtocol"] + self.protocol = self._detect_protocol(protocols) + return self.protocol + finally: self.lock.release() - def detect_protocol_by_file(self): + + def get_protocol_by_file(self): """ Detect protocol by tag file. @@ -211,33 +258,27 @@ class ProtocolUtil(object): :returns: protocol instance """ - logger.info("Detect protocol by file") self.lock.acquire() + try: + if self.protocol is not None: + return self.protocol + + try: + self.protocol = self._get_protocol() + return self.protocol + except ProtocolNotFoundError: + pass + + logger.info("Detect protocol by file") tag_file_path = os.path.join(conf.get_lib_dir(), TAG_FILE_NAME) - if self.protocol is None: - protocols = [] - if os.path.isfile(tag_file_path): - protocols.append("MetadataProtocol") - else: - protocols.append("WireProtocol") - self.protocol = self._detect_protocol(protocols) - finally: - self.lock.release() - return self.protocol - - def get_protocol(self): - """ - Get protocol instance based on previous detecting result. - - :returns protocol instance - """ - self.lock.acquire() - try: - if self.protocol is None: - self.protocol = self._get_protocol() + protocols = [] + if os.path.isfile(tag_file_path): + protocols.append("MetadataProtocol") + else: + protocols.append("WireProtocol") + self.protocol = self._detect_protocol(protocols) return self.protocol + finally: self.lock.release() - return self.protocol - diff --git a/azurelinuxagent/protocol/wire.py b/azurelinuxagent/common/protocol/wire.py similarity index 95% rename from azurelinuxagent/protocol/wire.py rename to azurelinuxagent/common/protocol/wire.py index b9c93cf..815fb6b 100644 --- a/azurelinuxagent/protocol/wire.py +++ b/azurelinuxagent/common/protocol/wire.py @@ -22,19 +22,19 @@ import re import time import traceback import xml.sax.saxutils as saxutils -import azurelinuxagent.conf as conf -import azurelinuxagent.logger as logger -from azurelinuxagent.exception import ProtocolError, HttpError, \ +import azurelinuxagent.common.conf as conf +import azurelinuxagent.common.logger as logger +from azurelinuxagent.common.exception import ProtocolError, HttpError, \ ProtocolNotFoundError -from azurelinuxagent.future import ustr, httpclient, bytebuffer -import azurelinuxagent.utils.restutil as restutil -from azurelinuxagent.utils.textutil import parse_doc, findall, find, findtext, \ +from azurelinuxagent.common.future import ustr, httpclient, bytebuffer +import azurelinuxagent.common.utils.restutil as restutil +from azurelinuxagent.common.utils.textutil import parse_doc, findall, find, findtext, \ getattrib, gettext, remove_bom, \ get_bytes_from_pem -import azurelinuxagent.utils.fileutil as fileutil -import azurelinuxagent.utils.shellutil as shellutil -from azurelinuxagent.utils.cryptutil import CryptUtil -from azurelinuxagent.protocol.restapi import * +import azurelinuxagent.common.utils.fileutil as fileutil +import azurelinuxagent.common.utils.shellutil as shellutil +from azurelinuxagent.common.utils.cryptutil import CryptUtil +from azurelinuxagent.common.protocol.restapi import * VERSION_INFO_URI = "http://{0}/?comp=versions" GOAL_STATE_URI = "http://{0}/machine/?comp=goalstate" @@ -102,6 +102,18 @@ class WireProtocol(Protocol): certificates = self.client.get_certs() return certificates.cert_list + def get_vmagent_manifests(self): + #Update goal state to get latest extensions config + self.client.update_goal_state() + goal_state = self.client.get_goal_state() + ext_conf = self.client.get_ext_conf() + return ext_conf.vmagent_manifests, goal_state.incarnation + + def get_vmagent_pkgs(self, vmagent_manifest): + goal_state = self.client.get_goal_state() + man = self.client.get_gafamily_manifest(vmagent_manifest, goal_state) + return man.pkg_list + def get_ext_handlers(self): logger.verb("Get extension handler config") #Update goal state to get latest extensions config @@ -711,6 +723,14 @@ class WireClient(object): self.save_cache(local_file, xml_text) return ExtensionManifest(xml_text) + def get_gafamily_manifest(self, vmagent_manifest, goal_state): + local_file = MANIFEST_FILE_NAME.format(vmagent_manifest.family, + goal_state.incarnation) + local_file = os.path.join(conf.get_lib_dir(), local_file) + xml_text = self.fetch_manifest(vmagent_manifest.versionsManifestUris) + fileutil.write_file(local_file, xml_text) + return ExtensionManifest(xml_text) + def check_wire_protocol_version(self): uri = VERSION_INFO_URI.format(self.endpoint) version_info_xml = self.fetch_config(uri, None) @@ -1052,6 +1072,7 @@ class ExtensionsConfig(object): def __init__(self, xml_text): logger.verb("Load ExtensionsConfig.xml") self.ext_handlers = ExtHandlerList() + self.vmagent_manifests = VMAgentManifestList() self.status_upload_blob = None if xml_text is not None: self.parse(xml_text) @@ -1061,6 +1082,21 @@ class ExtensionsConfig(object): Write configuration to file ExtensionsConfig.xml. """ xml_doc = parse_doc(xml_text) + + ga_families_list = find(xml_doc, "GAFamilies") + ga_families = findall(ga_families_list, "GAFamily") + + for ga_family in ga_families: + family = findtext(ga_family, "Name") + uris_list = find(ga_family, "Uris") + uris = findall(uris_list, "Uri") + manifest = VMAgentManifest() + manifest.family = family + for uri in uris: + manifestUri = VMAgentManifestUri(uri=gettext(uri)) + manifest.versionsManifestUris.append(manifestUri) + self.vmagent_manifests.vmAgentManifests.append(manifest) + plugins_list = find(xml_doc, "Plugins") plugins = findall(plugins_list, "Plugin") plugin_settings_list = find(xml_doc, "PluginSettings") diff --git a/azurelinuxagent/distro/freebsd/__init__.py b/azurelinuxagent/common/utils/__init__.py similarity index 95% rename from azurelinuxagent/distro/freebsd/__init__.py rename to azurelinuxagent/common/utils/__init__.py index d9b82f5..1ea2f38 100644 --- a/azurelinuxagent/distro/freebsd/__init__.py +++ b/azurelinuxagent/common/utils/__init__.py @@ -1,5 +1,3 @@ -# Microsoft Azure Linux Agent -# # Copyright 2014 Microsoft Corporation # # Licensed under the Apache License, Version 2.0 (the "License"); diff --git a/azurelinuxagent/utils/cryptutil.py b/azurelinuxagent/common/utils/cryptutil.py similarity index 96% rename from azurelinuxagent/utils/cryptutil.py rename to azurelinuxagent/common/utils/cryptutil.py index 5ee5637..b35bda0 100644 --- a/azurelinuxagent/utils/cryptutil.py +++ b/azurelinuxagent/common/utils/cryptutil.py @@ -19,9 +19,9 @@ import base64 import struct -from azurelinuxagent.future import ustr, bytebuffer -from azurelinuxagent.exception import CryptError -import azurelinuxagent.utils.shellutil as shellutil +from azurelinuxagent.common.future import ustr, bytebuffer +from azurelinuxagent.common.exception import CryptError +import azurelinuxagent.common.utils.shellutil as shellutil class CryptUtil(object): def __init__(self, openssl_cmd): diff --git a/azurelinuxagent/utils/fileutil.py b/azurelinuxagent/common/utils/fileutil.py similarity index 96% rename from azurelinuxagent/utils/fileutil.py rename to azurelinuxagent/common/utils/fileutil.py index 82fd973..24842d0 100644 --- a/azurelinuxagent/utils/fileutil.py +++ b/azurelinuxagent/common/utils/fileutil.py @@ -26,9 +26,9 @@ import re import shutil import pwd import tempfile -import azurelinuxagent.logger as logger -from azurelinuxagent.future import ustr -import azurelinuxagent.utils.textutil as textutil +import azurelinuxagent.common.logger as logger +from azurelinuxagent.common.future import ustr +import azurelinuxagent.common.utils.textutil as textutil def read_file(filepath, asbin=False, remove_bom=False, encoding='utf-8'): """ diff --git a/azurelinuxagent/utils/restutil.py b/azurelinuxagent/common/utils/restutil.py similarity index 96% rename from azurelinuxagent/utils/restutil.py rename to azurelinuxagent/common/utils/restutil.py index 2e8b0be..71c88c1 100644 --- a/azurelinuxagent/utils/restutil.py +++ b/azurelinuxagent/common/utils/restutil.py @@ -21,10 +21,10 @@ import time import platform import os import subprocess -import azurelinuxagent.conf as conf -import azurelinuxagent.logger as logger -from azurelinuxagent.exception import HttpError -from azurelinuxagent.future import httpclient, urlparse +import azurelinuxagent.common.conf as conf +import azurelinuxagent.common.logger as logger +from azurelinuxagent.common.exception import HttpError +from azurelinuxagent.common.future import httpclient, urlparse """ REST api util functions diff --git a/azurelinuxagent/utils/shellutil.py b/azurelinuxagent/common/utils/shellutil.py similarity index 97% rename from azurelinuxagent/utils/shellutil.py rename to azurelinuxagent/common/utils/shellutil.py index 98871a1..8632758 100644 --- a/azurelinuxagent/utils/shellutil.py +++ b/azurelinuxagent/common/utils/shellutil.py @@ -20,8 +20,8 @@ import platform import os import subprocess -from azurelinuxagent.future import ustr -import azurelinuxagent.logger as logger +from azurelinuxagent.common.future import ustr +import azurelinuxagent.common.logger as logger if not hasattr(subprocess,'check_output'): def check_output(*popenargs, **kwargs): diff --git a/azurelinuxagent/utils/textutil.py b/azurelinuxagent/common/utils/textutil.py similarity index 100% rename from azurelinuxagent/utils/textutil.py rename to azurelinuxagent/common/utils/textutil.py diff --git a/azurelinuxagent/metadata.py b/azurelinuxagent/common/version.py similarity index 95% rename from azurelinuxagent/metadata.py rename to azurelinuxagent/common/version.py index 42ccc68..5d49655 100644 --- a/azurelinuxagent/metadata.py +++ b/azurelinuxagent/common/version.py @@ -1,5 +1,3 @@ -# Microsoft Azure Linux Agent -# # Copyright 2014 Microsoft Corporation # # Licensed under the Apache License, Version 2.0 (the "License"); @@ -21,8 +19,8 @@ import os import re import platform import sys -import azurelinuxagent.utils.fileutil as fileutil -from azurelinuxagent.future import ustr +import azurelinuxagent.common.utils.fileutil as fileutil +from azurelinuxagent.common.future import ustr def get_distro(): if 'FreeBSD' in platform.system(): diff --git a/azurelinuxagent/daemon/__init__.py b/azurelinuxagent/daemon/__init__.py new file mode 100644 index 0000000..979e01b --- /dev/null +++ b/azurelinuxagent/daemon/__init__.py @@ -0,0 +1,18 @@ +# Copyright 2014 Microsoft Corporation +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +# +# Requires Python 2.4+ and Openssl 1.0+ +# + +from azurelinuxagent.daemon.main import get_daemon_handler diff --git a/azurelinuxagent/distro/default/env.py b/azurelinuxagent/daemon/env.py similarity index 77% rename from azurelinuxagent/distro/default/env.py rename to azurelinuxagent/daemon/env.py index be47eaf..9d18026 100644 --- a/azurelinuxagent/distro/default/env.py +++ b/azurelinuxagent/daemon/env.py @@ -21,8 +21,13 @@ import os import socket import threading import time -import azurelinuxagent.logger as logger -import azurelinuxagent.conf as conf +import azurelinuxagent.common.logger as logger +import azurelinuxagent.common.conf as conf +from azurelinuxagent.common.osutil import get_osutil +from azurelinuxagent.common.dhcp import get_dhcp_handler + +def get_env_handler(): + return EnvHandler() class EnvHandler(object): """ @@ -32,8 +37,9 @@ class EnvHandler(object): Monitor scsi disk. If new scsi disk found, set timeout """ - def __init__(self, distro): - self.distro = distro + def __init__(self): + self.osutil = get_osutil() + self.dhcp_handler = get_dhcp_handler() self.stopped = True self.hostname = None self.dhcpid = None @@ -46,9 +52,9 @@ class EnvHandler(object): self.stopped = False logger.info("Start env monitor service.") - self.distro.dhcp_handler.conf_routes() + self.dhcp_handler.conf_routes() self.hostname = socket.gethostname() - self.dhcpid = self.distro.osutil.get_dhcp_pid() + self.dhcpid = self.osutil.get_dhcp_pid() self.server_thread = threading.Thread(target = self.monitor) self.server_thread.setDaemon(True) self.server_thread.start() @@ -59,10 +65,10 @@ class EnvHandler(object): If dhcp clinet process re-start has occurred, reset routes. """ while not self.stopped: - self.distro.osutil.remove_rules_files() + self.osutil.remove_rules_files() timeout = conf.get_root_device_scsi_timeout() if timeout is not None: - self.distro.osutil.set_scsi_disks_timeout(timeout) + self.osutil.set_scsi_disks_timeout(timeout) if conf.get_monitor_hostname(): self.handle_hostname_update() self.handle_dhclient_restart() @@ -73,25 +79,25 @@ class EnvHandler(object): if curr_hostname != self.hostname: logger.info("EnvMonitor: Detected host name change: {0} -> {1}", self.hostname, curr_hostname) - self.distro.osutil.set_hostname(curr_hostname) - self.distro.osutil.publish_hostname(curr_hostname) + self.osutil.set_hostname(curr_hostname) + self.osutil.publish_hostname(curr_hostname) self.hostname = curr_hostname def handle_dhclient_restart(self): if self.dhcpid is None: logger.warn("Dhcp client is not running. ") - self.dhcpid = self.distro.osutil.get_dhcp_pid() + self.dhcpid = self.osutil.get_dhcp_pid() return #The dhcp process hasn't changed since last check - if self.distro.osutil.check_pid_alive(self.dhcpid.strip()): + if self.osutil.check_pid_alive(self.dhcpid.strip()): return - newpid = self.distro.osutil.get_dhcp_pid() + newpid = self.osutil.get_dhcp_pid() if newpid is not None and newpid != self.dhcpid: logger.info("EnvMonitor: Detected dhcp client restart. " "Restoring routing table.") - self.distro.dhcp_handler.conf_routes() + self.dhcp_handler.conf_routes() self.dhcpid = newpid def stop(self): diff --git a/azurelinuxagent/distro/default/daemon.py b/azurelinuxagent/daemon/main.py similarity index 57% rename from azurelinuxagent/distro/default/daemon.py rename to azurelinuxagent/daemon/main.py index aa7f4e6..3539732 100644 --- a/azurelinuxagent/distro/default/daemon.py +++ b/azurelinuxagent/daemon/main.py @@ -21,24 +21,43 @@ import os import time import sys import traceback -import azurelinuxagent.conf as conf -import azurelinuxagent.logger as logger -from azurelinuxagent.future import ustr -from azurelinuxagent.event import add_event, WALAEventOperation -from azurelinuxagent.exception import ProtocolError -from azurelinuxagent.metadata import AGENT_LONG_NAME, AGENT_VERSION, \ +import azurelinuxagent.common.conf as conf +import azurelinuxagent.common.logger as logger +from azurelinuxagent.common.future import ustr +from azurelinuxagent.common.event import add_event, WALAEventOperation +from azurelinuxagent.common.exception import ProtocolError +from azurelinuxagent.common.version import AGENT_LONG_NAME, AGENT_VERSION, \ DISTRO_NAME, DISTRO_VERSION, \ DISTRO_FULL_NAME, PY_VERSION_MAJOR, \ PY_VERSION_MINOR, PY_VERSION_MICRO -import azurelinuxagent.event as event -import azurelinuxagent.utils.fileutil as fileutil +import azurelinuxagent.common.event as event +import azurelinuxagent.common.utils.fileutil as fileutil +from azurelinuxagent.common.osutil import get_osutil +from azurelinuxagent.common.protocol import get_protocol_util +from azurelinuxagent.daemon.scvmm import get_scvmm_handler +from azurelinuxagent.daemon.resourcedisk import get_resourcedisk_handler +from azurelinuxagent.daemon.monitor import get_monitor_handler +from azurelinuxagent.daemon.env import get_env_handler +from azurelinuxagent.pa.provision import get_provision_handler +from azurelinuxagent.ga.update import get_update_handler +def get_daemon_handler(): + return DaemonHandler() class DaemonHandler(object): - def __init__(self, distro): - self.distro = distro + """ + Main thread of daemon. It will invoke other threads to do actual work + """ + def __init__(self): self.running = True - + self.osutil = get_osutil() + self.protocol_util = get_protocol_util() + self.scvmm_handler = get_scvmm_handler() + self.resourcedisk_handler = get_resourcedisk_handler() + self.monitor_handler = get_monitor_handler() + self.env_handler = get_env_handler() + self.provision_handler = get_provision_handler() + self.update_handler = get_update_handler() def run(self): logger.info("{0} Version:{1}", AGENT_LONG_NAME, AGENT_VERSION) @@ -58,6 +77,7 @@ class DaemonHandler(object): logger.info("Sleep 15 seconds and restart daemon") time.sleep(15) + def check_pid(self): """Check whether daemon is already running""" pid = None @@ -65,7 +85,7 @@ class DaemonHandler(object): if os.path.isfile(pid_file): pid = fileutil.read_file(pid_file) - if self.distro.osutil.check_pid_alive(pid): + if self.osutil.check_pid_alive(pid): logger.info("Daemon is already running: {0}", pid) sys.exit(0) @@ -79,25 +99,19 @@ class DaemonHandler(object): os.chdir(conf.get_lib_dir()) if conf.get_detect_scvmm_env(): - if self.distro.scvmm_handler.run(): - return - - self.distro.provision_handler.run() + self.scvmm_handler.run() if conf.get_resourcedisk_format(): - self.distro.resource_disk_handler.run() - - try: - protocol = self.distro.protocol_util.detect_protocol() - except ProtocolError as e: - logger.error("Failed to detect protocol, exit", e) - return + self.resourcedisk_handler.run() - self.distro.event_handler.run() - self.distro.env_handler.run() + self.protocol_util.clear_protocol() + + self.provision_handler.run() + + self.monitor_handler.run() + + self.env_handler.run() while self.running: - #Handle extensions - self.distro.ext_handlers_handler.run() + self.update_handler.run() time.sleep(25) - diff --git a/azurelinuxagent/distro/default/monitor.py b/azurelinuxagent/daemon/monitor.py similarity index 84% rename from azurelinuxagent/distro/default/monitor.py rename to azurelinuxagent/daemon/monitor.py index 3b26c9a..40782fe 100644 --- a/azurelinuxagent/distro/default/monitor.py +++ b/azurelinuxagent/daemon/monitor.py @@ -24,19 +24,23 @@ import time import datetime import threading import platform -import azurelinuxagent.logger as logger -import azurelinuxagent.conf as conf -from azurelinuxagent.event import WALAEventOperation, add_event -from azurelinuxagent.exception import EventError, ProtocolError, OSUtilError -from azurelinuxagent.future import ustr -from azurelinuxagent.utils.textutil import parse_doc, findall, find, getattrib -from azurelinuxagent.protocol.restapi import TelemetryEventParam, \ +import azurelinuxagent.common.logger as logger +import azurelinuxagent.common.conf as conf +from azurelinuxagent.common.event import WALAEventOperation, add_event +from azurelinuxagent.common.exception import EventError, ProtocolError, \ + OSUtilError +from azurelinuxagent.common.future import ustr +from azurelinuxagent.common.utils.textutil import parse_doc, findall, find, \ + getattrib +from azurelinuxagent.common.protocol.restapi import TelemetryEventParam, \ TelemetryEventList, \ TelemetryEvent, \ set_properties, get_properties -from azurelinuxagent.metadata import DISTRO_NAME, DISTRO_VERSION, \ +from azurelinuxagent.common.version import DISTRO_NAME, DISTRO_VERSION, \ DISTRO_CODE_NAME, AGENT_LONG_VERSION +from azurelinuxagent.common.osutil import get_osutil +from azurelinuxagent.common.protocol import get_protocol_util def parse_event(data_str): try: @@ -76,10 +80,13 @@ def parse_json_event(data_str): set_properties("TelemetryEvent", event, data) return event +def get_monitor_handler(): + return MonitorHandler() class MonitorHandler(object): - def __init__(self, distro): - self.distro = distro + def __init__(self): + self.osutil = get_osutil() + self.protocol_util = get_protocol_util() self.sysinfo = [] def run(self): @@ -99,15 +106,15 @@ class MonitorHandler(object): self.sysinfo.append(TelemetryEventParam("GAVersion", AGENT_LONG_VERSION)) try: - ram = self.distro.osutil.get_total_mem() - processors = self.distro.osutil.get_processor_cores() + ram = self.osutil.get_total_mem() + processors = self.osutil.get_processor_cores() self.sysinfo.append(TelemetryEventParam("RAM", ram)) self.sysinfo.append(TelemetryEventParam("Processors", processors)) except OSUtilError as e: logger.warn("Failed to get system info: {0}", e) try: - protocol = self.distro.protocol_util.get_protocol() + protocol = self.protocol_util.get_protocol() vminfo = protocol.get_vminfo() self.sysinfo.append(TelemetryEventParam("VMName", vminfo.vmName)) @@ -161,7 +168,7 @@ class MonitorHandler(object): return try: - protocol = self.distro.protocol_util.get_protocol() + protocol = self.protocol_util.get_protocol() protocol.report_event(event_list) except ProtocolError as e: logger.error("{0}", e) diff --git a/azurelinuxagent/daemon/resourcedisk/__init__.py b/azurelinuxagent/daemon/resourcedisk/__init__.py new file mode 100644 index 0000000..021cecd --- /dev/null +++ b/azurelinuxagent/daemon/resourcedisk/__init__.py @@ -0,0 +1,20 @@ +# Microsoft Azure Linux Agent +# +# Copyright 2014 Microsoft Corporation +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +# +# Requires Python 2.4+ and Openssl 1.0+ +# + +from azurelinuxagent.daemon.resourcedisk.factory import get_resourcedisk_handler diff --git a/azurelinuxagent/distro/default/resourceDisk.py b/azurelinuxagent/daemon/resourcedisk/default.py similarity index 91% rename from azurelinuxagent/distro/default/resourceDisk.py rename to azurelinuxagent/daemon/resourcedisk/default.py index a6c5232..d435e30 100644 --- a/azurelinuxagent/distro/default/resourceDisk.py +++ b/azurelinuxagent/daemon/resourcedisk/default.py @@ -1,5 +1,3 @@ -# Microsoft Azure Linux Agent -# # Copyright 2014 Microsoft Corporation # # Licensed under the Apache License, Version 2.0 (the "License"); @@ -20,13 +18,14 @@ import os import re import threading -import azurelinuxagent.logger as logger -from azurelinuxagent.future import ustr -import azurelinuxagent.conf as conf -from azurelinuxagent.event import add_event, WALAEventOperation -import azurelinuxagent.utils.fileutil as fileutil -import azurelinuxagent.utils.shellutil as shellutil -from azurelinuxagent.exception import ResourceDiskError +import azurelinuxagent.common.logger as logger +from azurelinuxagent.common.future import ustr +import azurelinuxagent.common.conf as conf +from azurelinuxagent.common.event import add_event, WALAEventOperation +import azurelinuxagent.common.utils.fileutil as fileutil +import azurelinuxagent.common.utils.shellutil as shellutil +from azurelinuxagent.common.exception import ResourceDiskError +from azurelinuxagent.common.osutil import get_osutil DATALOSS_WARNING_FILE_NAME="DATALOSS_WARNING_README.txt" DATA_LOSS_WARNING="""\ @@ -40,8 +39,8 @@ For additional details to please refer to the MSDN documentation at : http://msd """ class ResourceDiskHandler(object): - def __init__(self, distro): - self.distro = distro + def __init__(self): + self.osutil = get_osutil() def start_activate_resource_disk(self): disk_thread = threading.Thread(target = self.run) @@ -81,13 +80,13 @@ class ResourceDiskHandler(object): logger.error("Failed to enable swap {0}", e) def mount_resource_disk(self, mount_point, fs): - device = self.distro.osutil.device_for_ide_port(1) + device = self.osutil.device_for_ide_port(1) if device is None: raise ResourceDiskError("unable to detect disk topology") device = "/dev/" + device mountlist = shellutil.run_get_output("mount")[1] - existing = self.distro.osutil.get_mount_point(mountlist, device) + existing = self.osutil.get_mount_point(mountlist, device) if(existing): logger.info("Resource disk {0}1 is already mounted", device) diff --git a/azurelinuxagent/daemon/resourcedisk/factory.py b/azurelinuxagent/daemon/resourcedisk/factory.py new file mode 100644 index 0000000..76e5a23 --- /dev/null +++ b/azurelinuxagent/daemon/resourcedisk/factory.py @@ -0,0 +1,33 @@ +# Copyright 2014 Microsoft Corporation +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +# +# Requires Python 2.4+ and Openssl 1.0+ +# + +import azurelinuxagent.common.logger as logger +from azurelinuxagent.common.utils.textutil import Version +from azurelinuxagent.common.version import DISTRO_NAME, \ + DISTRO_VERSION, \ + DISTRO_FULL_NAME +from .default import ResourceDiskHandler +from .freebsd import FreeBSDResourceDiskHandler + +def get_resourcedisk_handler(distro_name=DISTRO_NAME, + distro_version=DISTRO_VERSION, + distro_full_name=DISTRO_FULL_NAME): + if distro_name == "freebsd": + return FreeBSDResourceDiskHandler() + + return ResourceDiskHandler() + diff --git a/azurelinuxagent/distro/freebsd/resourceDisk.py b/azurelinuxagent/daemon/resourcedisk/freebsd.py similarity index 90% rename from azurelinuxagent/distro/freebsd/resourceDisk.py rename to azurelinuxagent/daemon/resourcedisk/freebsd.py index aefdff1..36a3ac9 100644 --- a/azurelinuxagent/distro/freebsd/resourceDisk.py +++ b/azurelinuxagent/daemon/resourcedisk/freebsd.py @@ -16,12 +16,11 @@ # # Requires Python 2.4+ and Openssl 1.0+ # -import azurelinuxagent.logger as logger -import azurelinuxagent.utils.fileutil as fileutil -import azurelinuxagent.utils.shellutil as shellutil -from azurelinuxagent.distro.default.resourceDisk import ResourceDiskHandler -from azurelinuxagent.exception import ResourceDiskError - +import azurelinuxagent.common.logger as logger +import azurelinuxagent.common.utils.fileutil as fileutil +import azurelinuxagent.common.utils.shellutil as shellutil +from azurelinuxagent.common.exception import ResourceDiskError +from azurelinuxagent.daemon.resourcedisk.default import ResourceDiskHandler class FreeBSDResourceDiskHandler(ResourceDiskHandler): """ @@ -35,8 +34,8 @@ class FreeBSDResourceDiskHandler(ResourceDiskHandler): 1. MBR: The resource disk partition is /dev/da1s1 2. GPT: The resource disk partition is /dev/da1p2, /dev/da1p1 is for reserved usage. """ - def __init__(self, distro): - super(FreeBSDResourceDiskHandler, self).__init__(distro) + def __init__(self): + super(FreeBSDResourceDiskHandler, self).__init__() @staticmethod def parse_gpart_list(data): @@ -95,7 +94,7 @@ class FreeBSDResourceDiskHandler(ResourceDiskHandler): # 3. Mount partition mount_list = shellutil.run_get_output("mount")[1] - existing = self.distro.osutil.get_mount_point(mount_list, partition) + existing = self.osutil.get_mount_point(mount_list, partition) if existing: logger.info("Resource disk {0} is already mounted", partition) diff --git a/azurelinuxagent/distro/default/scvmm.py b/azurelinuxagent/daemon/scvmm.py similarity index 64% rename from azurelinuxagent/distro/default/scvmm.py rename to azurelinuxagent/daemon/scvmm.py index 4d083b4..0df1845 100644 --- a/azurelinuxagent/distro/default/scvmm.py +++ b/azurelinuxagent/daemon/scvmm.py @@ -18,31 +18,44 @@ # import os +import sys import subprocess -import azurelinuxagent.logger as logger +import azurelinuxagent.common.logger as logger +import azurelinuxagent.common.conf as conf +from azurelinuxagent.common.osutil import get_osutil VMM_CONF_FILE_NAME = "linuxosconfiguration.xml" VMM_STARTUP_SCRIPT_NAME= "install" +def get_scvmm_handler(): + return ScvmmHandler() + class ScvmmHandler(object): - def __init__(self, distro): - self.distro = distro + def __init__(self): + self.osutil = get_osutil() def detect_scvmm_env(self): logger.info("Detecting Microsoft System Center VMM Environment") - self.distro.osutil.mount_dvd(max_retry=1, chk_err=False) - mount_point = self.distro.osutil.get_dvd_mount_point() + self.osutil.mount_dvd(max_retry=1, chk_err=False) + mount_point = conf.get_dvd_mount_point() found = os.path.isfile(os.path.join(mount_point, VMM_CONF_FILE_NAME)) if found: self.start_scvmm_agent() else: - self.distro.osutil.umount_dvd(chk_err=False) + self.osutil.umount_dvd(chk_err=False) return found def start_scvmm_agent(self): logger.info("Starting Microsoft System Center VMM Initialization " "Process") - mount_point = self.distro.osutil.get_dvd_mount_point() + mount_point = conf.get_dvd_mount_point() startup_script = os.path.join(mount_point, VMM_STARTUP_SCRIPT_NAME) - subprocess.Popen(["/bin/bash", startup_script, "-p " + mount_point]) - + devnull = open(os.devnull, 'w') + subprocess.Popen(["/bin/bash", startup_script, "-p " + mount_point], + stdout=devnull, stderr=devnull) + + def run(self): + if self.detect_scvmm_env(): + self.start_scvmm_agent() + logger.info("Exiting") + sys.exit(0) diff --git a/azurelinuxagent/distro/coreos/distro.py b/azurelinuxagent/distro/coreos/distro.py deleted file mode 100644 index 04c7bff..0000000 --- a/azurelinuxagent/distro/coreos/distro.py +++ /dev/null @@ -1,29 +0,0 @@ -# Microsoft Azure Linux Agent -# -# Copyright 2014 Microsoft Corporation -# -# Licensed under the Apache License, Version 2.0 (the "License"); -# you may not use this file except in compliance with the License. -# You may obtain a copy of the License at -# -# http://www.apache.org/licenses/LICENSE-2.0 -# -# Unless required by applicable law or agreed to in writing, software -# distributed under the License is distributed on an "AS IS" BASIS, -# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -# See the License for the specific language governing permissions and -# limitations under the License. -# -# Requires Python 2.4+ and Openssl 1.0+ -# - -from azurelinuxagent.distro.default.distro import DefaultDistro -from azurelinuxagent.distro.coreos.osutil import CoreOSUtil -from azurelinuxagent.distro.coreos.deprovision import CoreOSDeprovisionHandler - -class CoreOSDistro(DefaultDistro): - def __init__(self): - super(CoreOSDistro, self).__init__() - self.osutil = CoreOSUtil() - self.deprovision_handler = CoreOSDeprovisionHandler(self) - diff --git a/azurelinuxagent/distro/debian/distro.py b/azurelinuxagent/distro/debian/distro.py deleted file mode 100644 index 01f4e3e..0000000 --- a/azurelinuxagent/distro/debian/distro.py +++ /dev/null @@ -1,27 +0,0 @@ -# Microsoft Azure Linux Agent -# -# Copyright 2014 Microsoft Corporation -# -# Licensed under the Apache License, Version 2.0 (the "License"); -# you may not use this file except in compliance with the License. -# You may obtain a copy of the License at -# -# http://www.apache.org/licenses/LICENSE-2.0 -# -# Unless required by applicable law or agreed to in writing, software -# distributed under the License is distributed on an "AS IS" BASIS, -# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -# See the License for the specific language governing permissions and -# limitations under the License. -# -# Requires Python 2.4+ and Openssl 1.0+ -# - -from azurelinuxagent.distro.default.distro import DefaultDistro -from azurelinuxagent.distro.debian.osutil import DebianOSUtil - -class DebianDistro(DefaultDistro): - def __init__(self): - super(DebianDistro, self).__init__() - self.osutil = DebianOSUtil() - diff --git a/azurelinuxagent/distro/default/distro.py b/azurelinuxagent/distro/default/distro.py deleted file mode 100644 index ca0d77e..0000000 --- a/azurelinuxagent/distro/default/distro.py +++ /dev/null @@ -1,51 +0,0 @@ -# Microsoft Azure Linux Agent -# -# Copyright 2014 Microsoft Corporation -# -# Licensed under the Apache License, Version 2.0 (the "License"); -# you may not use this file except in compliance with the License. -# You may obtain a copy of the License at -# -# http://www.apache.org/licenses/LICENSE-2.0 -# -# Unless required by applicable law or agreed to in writing, software -# distributed under the License is distributed on an "AS IS" BASIS, -# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -# See the License for the specific language governing permissions and -# limitations under the License. -# -# Requires Python 2.4+ and Openssl 1.0+ -# - -from azurelinuxagent.conf import ConfigurationProvider -from azurelinuxagent.distro.default.osutil import DefaultOSUtil -from azurelinuxagent.distro.default.daemon import DaemonHandler -from azurelinuxagent.distro.default.init import InitHandler -from azurelinuxagent.distro.default.monitor import MonitorHandler -from azurelinuxagent.distro.default.dhcp import DhcpHandler -from azurelinuxagent.distro.default.protocolUtil import ProtocolUtil -from azurelinuxagent.distro.default.scvmm import ScvmmHandler -from azurelinuxagent.distro.default.env import EnvHandler -from azurelinuxagent.distro.default.provision import ProvisionHandler -from azurelinuxagent.distro.default.resourceDisk import ResourceDiskHandler -from azurelinuxagent.distro.default.extension import ExtHandlersHandler -from azurelinuxagent.distro.default.deprovision import DeprovisionHandler - -class DefaultDistro(object): - """ - """ - def __init__(self): - self.osutil = DefaultOSUtil() - self.protocol_util = ProtocolUtil(self) - - self.init_handler = InitHandler(self) - self.daemon_handler = DaemonHandler(self) - self.event_handler = MonitorHandler(self) - self.dhcp_handler = DhcpHandler(self) - self.scvmm_handler = ScvmmHandler(self) - self.env_handler = EnvHandler(self) - self.provision_handler = ProvisionHandler(self) - self.resource_disk_handler = ResourceDiskHandler(self) - self.ext_handlers_handler = ExtHandlersHandler(self) - self.deprovision_handler = DeprovisionHandler(self) - diff --git a/azurelinuxagent/distro/default/init.py b/azurelinuxagent/distro/default/init.py deleted file mode 100644 index c703e87..0000000 --- a/azurelinuxagent/distro/default/init.py +++ /dev/null @@ -1,53 +0,0 @@ -# Microsoft Azure Linux Agent -# -# Copyright 2014 Microsoft Corporation -# -# Licensed under the Apache License, Version 2.0 (the "License"); -# you may not use this file except in compliance with the License. -# You may obtain a copy of the License at -# -# http://www.apache.org/licenses/LICENSE-2.0 -# -# Unless required by applicable law or agreed to in writing, software -# distributed under the License is distributed on an "AS IS" BASIS, -# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -# See the License for the specific language governing permissions and -# limitations under the License. -# -# Requires Python 2.4+ and Openssl 1.0+ -# - -import os -import azurelinuxagent.conf as conf -import azurelinuxagent.logger as logger -import azurelinuxagent.event as event - - -class InitHandler(object): - def __init__(self, distro): - self.distro = distro - - def run(self, verbose): - #Init stdout log - level = logger.LogLevel.VERBOSE if verbose else logger.LogLevel.INFO - logger.add_logger_appender(logger.AppenderType.STDOUT, level) - - #Init config - conf_file_path = self.distro.osutil.get_agent_conf_file_path() - conf.load_conf_from_file(conf_file_path) - - #Init log - verbose = verbose or conf.get_logs_verbose() - level = logger.LogLevel.VERBOSE if verbose else logger.LogLevel.INFO - logger.add_logger_appender(logger.AppenderType.FILE, level, - path="/var/log/waagent.log") - logger.add_logger_appender(logger.AppenderType.CONSOLE, level, - path="/dev/console") - - #Init event reporter - event_dir = os.path.join(conf.get_lib_dir(), "events") - event.init_event_logger(event_dir) - event.enable_unhandled_err_dump("WALA") - - - diff --git a/azurelinuxagent/distro/freebsd/distro.py b/azurelinuxagent/distro/freebsd/distro.py deleted file mode 100644 index a889b53..0000000 --- a/azurelinuxagent/distro/freebsd/distro.py +++ /dev/null @@ -1,29 +0,0 @@ -# Microsoft Azure Linux Agent -# -# Copyright 2014 Microsoft Corporation -# -# Licensed under the Apache License, Version 2.0 (the "License"); -# you may not use this file except in compliance with the License. -# You may obtain a copy of the License at -# -# http://www.apache.org/licenses/LICENSE-2.0 -# -# Unless required by applicable law or agreed to in writing, software -# distributed under the License is distributed on an "AS IS" BASIS, -# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -# See the License for the specific language governing permissions and -# limitations under the License. -# -# Requires Python 2.4+ and Openssl 1.0+ -# - -from azurelinuxagent.distro.default.distro import DefaultDistro -from azurelinuxagent.distro.freebsd.resourceDisk import FreeBSDResourceDiskHandler -from azurelinuxagent.distro.freebsd.osutil import FreeBSDOSUtil - - -class FreeBSDDistro(DefaultDistro): - def __init__(self): - super(FreeBSDDistro, self).__init__() - self.osutil = FreeBSDOSUtil() - self.resource_disk_handler = FreeBSDResourceDiskHandler(self) diff --git a/azurelinuxagent/distro/redhat/__init__.py b/azurelinuxagent/distro/redhat/__init__.py deleted file mode 100644 index d9b82f5..0000000 --- a/azurelinuxagent/distro/redhat/__init__.py +++ /dev/null @@ -1,19 +0,0 @@ -# Microsoft Azure Linux Agent -# -# Copyright 2014 Microsoft Corporation -# -# Licensed under the Apache License, Version 2.0 (the "License"); -# you may not use this file except in compliance with the License. -# You may obtain a copy of the License at -# -# http://www.apache.org/licenses/LICENSE-2.0 -# -# Unless required by applicable law or agreed to in writing, software -# distributed under the License is distributed on an "AS IS" BASIS, -# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -# See the License for the specific language governing permissions and -# limitations under the License. -# -# Requires Python 2.4+ and Openssl 1.0+ -# - diff --git a/azurelinuxagent/distro/redhat/distro.py b/azurelinuxagent/distro/redhat/distro.py deleted file mode 100644 index c9278b8..0000000 --- a/azurelinuxagent/distro/redhat/distro.py +++ /dev/null @@ -1,31 +0,0 @@ -# Microsoft Azure Linux Agent -# -# Copyright 2014 Microsoft Corporation -# -# Licensed under the Apache License, Version 2.0 (the "License"); -# you may not use this file except in compliance with the License. -# You may obtain a copy of the License at -# -# http://www.apache.org/licenses/LICENSE-2.0 -# -# Unless required by applicable law or agreed to in writing, software -# distributed under the License is distributed on an "AS IS" BASIS, -# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -# See the License for the specific language governing permissions and -# limitations under the License. -# -# Requires Python 2.4+ and Openssl 1.0+ -# - -from azurelinuxagent.distro.default.distro import DefaultDistro -from azurelinuxagent.distro.redhat.osutil import RedhatOSUtil, Redhat6xOSUtil - -class Redhat6xDistro(DefaultDistro): - def __init__(self): - super(Redhat6xDistro, self).__init__() - self.osutil = Redhat6xOSUtil() - -class RedhatDistro(DefaultDistro): - def __init__(self): - super(RedhatDistro, self).__init__() - self.osutil = RedhatOSUtil() diff --git a/azurelinuxagent/distro/suse/__init__.py b/azurelinuxagent/distro/suse/__init__.py deleted file mode 100644 index d9b82f5..0000000 --- a/azurelinuxagent/distro/suse/__init__.py +++ /dev/null @@ -1,19 +0,0 @@ -# Microsoft Azure Linux Agent -# -# Copyright 2014 Microsoft Corporation -# -# Licensed under the Apache License, Version 2.0 (the "License"); -# you may not use this file except in compliance with the License. -# You may obtain a copy of the License at -# -# http://www.apache.org/licenses/LICENSE-2.0 -# -# Unless required by applicable law or agreed to in writing, software -# distributed under the License is distributed on an "AS IS" BASIS, -# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -# See the License for the specific language governing permissions and -# limitations under the License. -# -# Requires Python 2.4+ and Openssl 1.0+ -# - diff --git a/azurelinuxagent/distro/ubuntu/__init__.py b/azurelinuxagent/distro/ubuntu/__init__.py deleted file mode 100644 index d9b82f5..0000000 --- a/azurelinuxagent/distro/ubuntu/__init__.py +++ /dev/null @@ -1,19 +0,0 @@ -# Microsoft Azure Linux Agent -# -# Copyright 2014 Microsoft Corporation -# -# Licensed under the Apache License, Version 2.0 (the "License"); -# you may not use this file except in compliance with the License. -# You may obtain a copy of the License at -# -# http://www.apache.org/licenses/LICENSE-2.0 -# -# Unless required by applicable law or agreed to in writing, software -# distributed under the License is distributed on an "AS IS" BASIS, -# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -# See the License for the specific language governing permissions and -# limitations under the License. -# -# Requires Python 2.4+ and Openssl 1.0+ -# - diff --git a/azurelinuxagent/distro/ubuntu/distro.py b/azurelinuxagent/distro/ubuntu/distro.py deleted file mode 100644 index f380f6c..0000000 --- a/azurelinuxagent/distro/ubuntu/distro.py +++ /dev/null @@ -1,55 +0,0 @@ -# Microsoft Azure Linux Agent -# -# Copyright 2014 Microsoft Corporation -# -# Licensed under the Apache License, Version 2.0 (the "License"); -# you may not use this file except in compliance with the License. -# You may obtain a copy of the License at -# -# http://www.apache.org/licenses/LICENSE-2.0 -# -# Unless required by applicable law or agreed to in writing, software -# distributed under the License is distributed on an "AS IS" BASIS, -# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -# See the License for the specific language governing permissions and -# limitations under the License. -# -# Requires Python 2.4+ and Openssl 1.0+ -# - -from azurelinuxagent.distro.default.distro import DefaultDistro -from azurelinuxagent.distro.ubuntu.osutil import Ubuntu14OSUtil, \ - Ubuntu12OSUtil, \ - UbuntuOSUtil, \ - UbuntuSnappyOSUtil - -from azurelinuxagent.distro.ubuntu.provision import UbuntuProvisionHandler -from azurelinuxagent.distro.ubuntu.deprovision import UbuntuDeprovisionHandler - -class UbuntuDistro(DefaultDistro): - def __init__(self): - super(UbuntuDistro, self).__init__() - self.osutil = UbuntuOSUtil() - self.provision_handler = UbuntuProvisionHandler(self) - self.deprovision_handler = UbuntuDeprovisionHandler(self) - -class Ubuntu12Distro(DefaultDistro): - def __init__(self): - super(Ubuntu12Distro, self).__init__() - self.osutil = Ubuntu12OSUtil() - self.provision_handler = UbuntuProvisionHandler(self) - self.deprovision_handler = UbuntuDeprovisionHandler(self) - -class Ubuntu14Distro(DefaultDistro): - def __init__(self): - super(Ubuntu14Distro, self).__init__() - self.osutil = Ubuntu14OSUtil() - self.provision_handler = UbuntuProvisionHandler(self) - self.deprovision_handler = UbuntuDeprovisionHandler(self) - -class UbuntuSnappyDistro(DefaultDistro): - def __init__(self): - super(UbuntuSnappyDistro, self).__init__() - self.osutil = UbuntuSnappyOSUtil() - self.provision_handler = UbuntuProvisionHandler(self) - self.deprovision_handler = UbuntuDeprovisionHandler(self) diff --git a/azurelinuxagent/distro/debian/__init__.py b/azurelinuxagent/ga/__init__.py similarity index 95% rename from azurelinuxagent/distro/debian/__init__.py rename to azurelinuxagent/ga/__init__.py index d9b82f5..1ea2f38 100644 --- a/azurelinuxagent/distro/debian/__init__.py +++ b/azurelinuxagent/ga/__init__.py @@ -1,5 +1,3 @@ -# Microsoft Azure Linux Agent -# # Copyright 2014 Microsoft Corporation # # Licensed under the Apache License, Version 2.0 (the "License"); diff --git a/azurelinuxagent/distro/default/extension.py b/azurelinuxagent/ga/exthandlers.py similarity index 95% rename from azurelinuxagent/distro/default/extension.py rename to azurelinuxagent/ga/exthandlers.py index 262b01b..8f91253 100644 --- a/azurelinuxagent/distro/default/extension.py +++ b/azurelinuxagent/ga/exthandlers.py @@ -22,20 +22,24 @@ import time import json import subprocess import shutil -import azurelinuxagent.conf as conf -import azurelinuxagent.logger as logger -from azurelinuxagent.event import add_event, WALAEventOperation -from azurelinuxagent.exception import ExtensionError, ProtocolError, HttpError -from azurelinuxagent.future import ustr -from azurelinuxagent.metadata import AGENT_VERSION -from azurelinuxagent.protocol.restapi import ExtHandlerStatus, ExtensionStatus, \ - ExtensionSubStatus, Extension, \ - VMStatus, ExtHandler, \ - get_properties, set_properties -import azurelinuxagent.utils.fileutil as fileutil -import azurelinuxagent.utils.restutil as restutil -import azurelinuxagent.utils.shellutil as shellutil -from azurelinuxagent.utils.textutil import Version +import azurelinuxagent.common.conf as conf +import azurelinuxagent.common.logger as logger +from azurelinuxagent.common.event import add_event, WALAEventOperation +from azurelinuxagent.common.exception import ExtensionError, ProtocolError, HttpError +from azurelinuxagent.common.future import ustr +from azurelinuxagent.common.version import AGENT_VERSION +from azurelinuxagent.common.protocol.restapi import ExtHandlerStatus, \ + ExtensionStatus, \ + ExtensionSubStatus, \ + Extension, \ + VMStatus, ExtHandler, \ + get_properties, \ + set_properties +import azurelinuxagent.common.utils.fileutil as fileutil +import azurelinuxagent.common.utils.restutil as restutil +import azurelinuxagent.common.utils.shellutil as shellutil +from azurelinuxagent.common.utils.textutil import Version +from azurelinuxagent.common.protocol import get_protocol_util #HandlerEnvironment.json schema version HANDLER_ENVIRONMENT_VERSION = 1.0 @@ -103,9 +107,12 @@ class ExtHandlerState(object): Installed = "Installed" Enabled = "Enabled" +def get_exthandlers_handler(): + return ExtHandlersHandler() + class ExtHandlersHandler(object): - def __init__(self, distro): - self.distro = distro + def __init__(self): + self.protocol_util = get_protocol_util() self.ext_handlers = None self.last_etag = None self.log_report = False @@ -113,7 +120,7 @@ class ExtHandlersHandler(object): def run(self): ext_handlers, etag = None, None try: - self.protocol = self.distro.protocol_util.get_protocol() + self.protocol = self.protocol_util.get_protocol() ext_handlers, etag = self.protocol.get_ext_handlers() except ProtocolError as e: add_event(name="WALA", is_success=False, message=ustr(e)) diff --git a/azurelinuxagent/ga/update.py b/azurelinuxagent/ga/update.py new file mode 100644 index 0000000..4aa66c4 --- /dev/null +++ b/azurelinuxagent/ga/update.py @@ -0,0 +1,278 @@ +# Windows Azure Linux Agent +# +# Copyright 2014 Microsoft Corporation +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +# +# Requires Python 2.4+ and Openssl 1.0+ +# +import os +import json +import time +import subprocess +import signal +import sys +import zipfile +from azurelinuxagent.common.exception import UpdateError, ProtocolError +import azurelinuxagent.common.logger as logger +import azurelinuxagent.common.conf as conf +from azurelinuxagent.common.event import add_event +from azurelinuxagent.common.future import ustr +import azurelinuxagent.common.utils.fileutil as fileutil +import azurelinuxagent.common.utils.restutil as restutil +from azurelinuxagent.common.version import AGENT_VERSION +from azurelinuxagent.common.utils.textutil import Version +from azurelinuxagent.common.osutil import get_osutil +from azurelinuxagent.common.protocol import get_protocol_util + +def get_update_handler(): + return UpdateHandler() + +GA_DIR = 'GuestAgent' # Dir name for guest agent bin and data +GA_ERR = 'error' # File name for guest agent error record +MAX_FAILURE = 3 # Max failure allowed for guest agent before blacklisted +RETAIN_INTERVAL = 24 * 60 * 60 # Retain interval for black list + +""" +Handles self update logic +""" +class UpdateHandler(object): + + def __init__(self): + self.osutil = get_osutil() + self.protocol_util = get_protocol_util() + self.last_etag = None + self.agents = [] + self.error_record = {} + + def run(self): + """ + 1. If self-update is enabled, check for new versions. + otherwise, use current agent + 2. Invoke run-exthandlers task + """ + self.mk_ga_dir() + + self.load_error_record() + + updated = self.check_for_update() + if not updated: + return + + latest_agent = None + + if conf.get_autoupdate_enabled(): + logger.info("Auto update enabled") + latest_agent = self.get_latest_agent() + + try: + self.run_extensions(latest_agent) + except UpdateError as e: + add_event(u"WALA", is_success=False, message=ustr(e)) + if latest_agent is not None: + latest_agent.err.mark_failure() + + self.save_error_record() + + def mk_ga_dir(self): + dir_path = os.path.join(conf.get_lib_dir(), GA_DIR) + fileutil.mkdir(dir_path, mode=0o700) + + def get_latest_agent(self): + available_agent = [agent for agent in self.agents \ + if not agent.err.is_blacklisted()] + + return available_agent[0] if len(available_agent) >= 1 else None + + def run_extensions(self, latest_agent): + agent_bin = sys.argv[0] + + if latest_agent is not None: + logger.info(u"The latest guest agent version is : {0}", + latest_agent.version) + agent_bin = latest_agent.get_agent_bin() + + if not latest_agent.is_downloaded(): + try: + latest_agent.download() + except Exception as e: + raise UpdateError(u"Download failed", e) + + devnull = open(os.devnull, 'w') + try: + child = subprocess.Popen([agent_bin, 'run-exthandlers'], + stdout=devnull, stderr=devnull) + except Exception as e: + raise UpdateError(u"Failed to launch task 'run-exthandlers'", e) + + ret = child.wait() + if ret == None or ret != 0: + msg = u"Task 'run-exthandlers' returns none-zero code: {0}".format(1) + raise UpdateError(msg) + + def check_for_update(self): + """Get latest version not in black list""" + self.agents = [] + try: + protocol = self.protocol_util.get_protocol() + manifest_list, etag = protocol.get_vmagent_manifests() + except ProtocolError as e: + add_event(u"WALA", is_success=False, message=ustr(e)) + return False + + if self.last_etag is not None and self.last_etag == etag: + logger.verb("No change to ext handler config:{0}, skip", etag) + return False + + logger.info("Check for update") + + family = conf.get_autoupdate_gafamily() + manifests = [manifest for manifest in manifest_list.vmAgentManifests \ + if manifest.family == family] + if len(manifests) == 0: + message = u"No avaiable guest agent found for: {0}".format(family) + add_event(u"WALA", message=message) + + try: + pkg_list = protocol.get_vmagent_pkgs(manifests[0]) + except ProtocolError as e: + message= u"Failed to get GA package list: {0}".format(e) + add_event("WALA", is_success=False, message=message) + return + + #Only considering versions that is larger than current + pkgs = [pkg for pkg in pkg_list.versions \ + if Version(pkg.version) > Version(AGENT_VERSION)] + + pkgs = sorted(pkgs, key=lambda pkg : Version(pkg.version), reverse=True) + + for pkg in pkgs: + ga_err = self.error_record.get(pkg.version) + if ga_err is None: + ga_err = GuestAgentError(version=pkg.version) + agent = GuestAgent(pkg, ga_err) + self.agents.append(agent) + + #Update error record list. + #Only keep the records for available agent versions + self.error_record = {} + for agent in self.agents: + self.error_record[agent.version] = agent.err + return True + + def load_error_record(self): + self.error_record = {} + file_path = os.path.join(conf.get_lib_dir(), GA_DIR, GA_ERR) + if not os.path.isfile(file_path): + return + + try: + error_data_list = json.loads(fileutil.read_file(file_path)) + for error_data in error_data_list: + ga_err = GuestAgentError() + ga_err.from_dict(error_data) + ga_err.clear_old_failure() + self.error_record[ga_err.version] = ga_err + except (IOError, ValueError) as e: + message = u"Failed to load GA error record: {0}".format(e) + add_event(u"WALA", is_success=False, message=message) + + def save_error_record(self): + error_data_list = [] + for err in self.error_record.values(): + error_data = err.to_dict() + error_data_list.append(error_data) + + file_path = os.path.join(conf.get_lib_dir(), GA_DIR, GA_ERR) + try: + fileutil.write_file(file_path, json.dumps(error_data_list)) + except (IOError, ValueError) as e: + message = u"Failed to save GA error record: {0}".format(e) + add_event(u"WALA", is_success=False, message=message) + +class GuestAgent(object): + def __init__(self, pkg, err): + self.version = pkg.version + self.pkg = pkg + self.err = err + + def get_agent_bin(self): + file_name = "WALinuxAgent-{0}.egg".format(self.version) + return os.path.join(self.get_agent_dir(), file_name) + + def get_agent_dir(self): + dir_name = "WALinuxAgent-{0}".format(self.version) + return os.path.join(conf.get_lib_dir(), GA_DIR, dir_name) + + def get_agent_pkg_file(self): + pkg_file_name = "WALinuxAgent-{0}.zip".format(self.version) + return os.path.join(conf.get_lib_dir(), GA_DIR, pkg_file_name) + + def is_downloaded(self): + return os.path.isfile(self.get_agent_bin()) + + def download(self): + logger.info(u"Download guest agent: {0}", self.version) + add_event(u"WALA", message="Start downloading guest agent package") + package = None + + for uri in self.pkg.uris: + try: + resp = restutil.http_get(uri.uri, chk_proxy=True) + if resp.status == restutil.httpclient.OK: + package = resp.read() + break + except restutil.HttpError as e: + logger.warn("Failed download guest agent from: {0}", uri.uri) + + if package is None: + raise UpdateError("Failed to download guest agent package") + + logger.info("Unpack guest agent package") + pkg_file = self.get_agent_pkg_file() + fileutil.write_file(pkg_file, bytearray(package), asbin=True) + zipfile.ZipFile(pkg_file).extractall(self.get_agent_dir()) + + add_event(name="WALA", message="Download guest agent package succeeded") + +class GuestAgentError(object): + def __init__(self, version=None, last_failure=0, failure_count=0): + self.version = version + self.last_failure = last_failure + self.failure_count = failure_count + + def mark_failure(self): + self.last_failure = time.time() + self.failure_count += 1 + + def clear_old_failure(self): + """Clear failure recored""" + if self.last_failure < (time.time() - RETAIN_INTERVAL): + self.last_failure = 0 + self.failure_count = 0 + + def is_blacklisted(self): + return self.failure_count >= MAX_FAILURE + + def from_dict(self, data): + self.version = data.get(u"version") + self.last_failure = data.get(u"last_failure", 0) + self.failure_count = data.get(u"failure_count", 0) + + def to_dict(self): + data = { + u"version": self.version, + u"last_failure": self.last_failure, + u"failure_count": self.failure_count, + } + return data diff --git a/azurelinuxagent/distro/default/__init__.py b/azurelinuxagent/pa/__init__.py similarity index 95% rename from azurelinuxagent/distro/default/__init__.py rename to azurelinuxagent/pa/__init__.py index d9b82f5..1ea2f38 100644 --- a/azurelinuxagent/distro/default/__init__.py +++ b/azurelinuxagent/pa/__init__.py @@ -1,5 +1,3 @@ -# Microsoft Azure Linux Agent -# # Copyright 2014 Microsoft Corporation # # Licensed under the Apache License, Version 2.0 (the "License"); diff --git a/azurelinuxagent/pa/deprovision/__init__.py b/azurelinuxagent/pa/deprovision/__init__.py new file mode 100644 index 0000000..de77168 --- /dev/null +++ b/azurelinuxagent/pa/deprovision/__init__.py @@ -0,0 +1,20 @@ +# Copyright 2014 Microsoft Corporation +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +# +# Requires Python 2.4+ and Openssl 1.0+ +# + +from azurelinuxagent.pa.deprovision.factory import get_deprovision_handler + +__all__ = ["get_deprovision_handler"] diff --git a/azurelinuxagent/distro/coreos/deprovision.py b/azurelinuxagent/pa/deprovision/coreos.py similarity index 78% rename from azurelinuxagent/distro/coreos/deprovision.py rename to azurelinuxagent/pa/deprovision/coreos.py index 9642579..079a913 100644 --- a/azurelinuxagent/distro/coreos/deprovision.py +++ b/azurelinuxagent/pa/deprovision/coreos.py @@ -17,12 +17,13 @@ # Requires Python 2.4+ and Openssl 1.0+ # -import azurelinuxagent.utils.fileutil as fileutil -from azurelinuxagent.distro.default.deprovision import DeprovisionHandler, DeprovisionAction +import azurelinuxagent.common.utils.fileutil as fileutil +from azurelinuxagent.pa.deprovision.default import DeprovisionHandler, \ + DeprovisionAction class CoreOSDeprovisionHandler(DeprovisionHandler): - def __init__(self, distro): - self.distro = distro + def __init__(self): + super(CoreOSDeprovisionHandler, self).__init__() def setup(self, deluser): warnings, actions = super(CoreOSDeprovisionHandler, self).setup(deluser) diff --git a/azurelinuxagent/distro/default/deprovision.py b/azurelinuxagent/pa/deprovision/default.py similarity index 81% rename from azurelinuxagent/distro/default/deprovision.py rename to azurelinuxagent/pa/deprovision/default.py index e349272..b570c31 100644 --- a/azurelinuxagent/distro/default/deprovision.py +++ b/azurelinuxagent/pa/deprovision/default.py @@ -17,11 +17,13 @@ # Requires Python 2.4+ and Openssl 1.0+ # -import azurelinuxagent.conf as conf -from azurelinuxagent.exception import ProtocolError -from azurelinuxagent.future import read_input -import azurelinuxagent.utils.fileutil as fileutil -import azurelinuxagent.utils.shellutil as shellutil +import azurelinuxagent.common.conf as conf +from azurelinuxagent.common.exception import ProtocolError +from azurelinuxagent.common.future import read_input +import azurelinuxagent.common.utils.fileutil as fileutil +import azurelinuxagent.common.utils.shellutil as shellutil +from azurelinuxagent.common.osutil import get_osutil +from azurelinuxagent.common.protocol import get_protocol_util class DeprovisionAction(object): def __init__(self, func, args=[], kwargs={}): @@ -33,19 +35,20 @@ class DeprovisionAction(object): self.func(*self.args, **self.kwargs) class DeprovisionHandler(object): - def __init__(self, distro): - self.distro = distro + def __init__(self): + self.osutil = get_osutil() + self.protocol_util = get_protocol_util() def del_root_password(self, warnings, actions): warnings.append("WARNING! root password will be disabled. " "You will not be able to login as root.") - actions.append(DeprovisionAction(self.distro.osutil.del_root_password)) + actions.append(DeprovisionAction(self.osutil.del_root_password)) def del_user(self, warnings, actions): try: - ovfenv = self.distro.protocol_util.get_ovf_env() + ovfenv = self.protocol_util.get_ovf_env() except ProtocolError: warnings.append("WARNING! ovf-env.xml is not found.") warnings.append("WARNING! Skip delete user.") @@ -54,7 +57,7 @@ class DeprovisionHandler(object): username = ovfenv.username warnings.append(("WARNING! {0} account and entire home directory " "will be deleted.").format(username)) - actions.append(DeprovisionAction(self.distro.osutil.del_account, + actions.append(DeprovisionAction(self.osutil.del_account, [username])) @@ -65,7 +68,7 @@ class DeprovisionHandler(object): def stop_agent_service(self, warnings, actions): warnings.append("WARNING! The waagent service will be stopped.") - actions.append(DeprovisionAction(self.distro.osutil.stop_agent_service)) + actions.append(DeprovisionAction(self.osutil.stop_agent_service)) def del_files(self, warnings, actions): files_to_del = ['/root/.bash_history', '/var/log/waagent.log'] @@ -85,9 +88,9 @@ class DeprovisionHandler(object): def reset_hostname(self, warnings, actions): localhost = ["localhost.localdomain"] - actions.append(DeprovisionAction(self.distro.osutil.set_hostname, + actions.append(DeprovisionAction(self.osutil.set_hostname, localhost)) - actions.append(DeprovisionAction(self.distro.osutil.set_dhcp_hostname, + actions.append(DeprovisionAction(self.osutil.set_dhcp_hostname, localhost)) def setup(self, deluser): diff --git a/azurelinuxagent/pa/deprovision/factory.py b/azurelinuxagent/pa/deprovision/factory.py new file mode 100644 index 0000000..dd01633 --- /dev/null +++ b/azurelinuxagent/pa/deprovision/factory.py @@ -0,0 +1,36 @@ +# Copyright 2014 Microsoft Corporation +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +# +# Requires Python 2.4+ and Openssl 1.0+ +# + +import azurelinuxagent.common.logger as logger +from azurelinuxagent.common.utils.textutil import Version +from azurelinuxagent.common.version import DISTRO_NAME, DISTRO_VERSION, \ + DISTRO_FULL_NAME + +from .default import DeprovisionHandler +from .coreos import CoreOSDeprovisionHandler +from .ubuntu import UbuntuDeprovisionHandler + +def get_deprovision_handler(distro_name=DISTRO_NAME, + distro_version=DISTRO_VERSION, + distro_full_name=DISTRO_FULL_NAME): + if distro_name == "ubuntu": + return UbuntuDeprovisionHandler() + if distro_name == "coreos": + return CoreOSDeprovisionHandler() + + return DeprovisionHandler() + diff --git a/azurelinuxagent/distro/ubuntu/deprovision.py b/azurelinuxagent/pa/deprovision/ubuntu.py similarity index 82% rename from azurelinuxagent/distro/ubuntu/deprovision.py rename to azurelinuxagent/pa/deprovision/ubuntu.py index da6e834..14f90de 100644 --- a/azurelinuxagent/distro/ubuntu/deprovision.py +++ b/azurelinuxagent/pa/deprovision/ubuntu.py @@ -18,9 +18,10 @@ # import os -import azurelinuxagent.logger as logger -import azurelinuxagent.utils.fileutil as fileutil -from azurelinuxagent.distro.default.deprovision import DeprovisionHandler, DeprovisionAction +import azurelinuxagent.common.logger as logger +import azurelinuxagent.common.utils.fileutil as fileutil +from azurelinuxagent.pa.deprovision.default import DeprovisionHandler, \ + DeprovisionAction def del_resolv(): if os.path.realpath('/etc/resolv.conf') != '/run/resolvconf/resolv.conf': @@ -33,8 +34,8 @@ def del_resolv(): class UbuntuDeprovisionHandler(DeprovisionHandler): - def __init__(self, distro): - super(UbuntuDeprovisionHandler, self).__init__(distro) + def __init__(self): + super(UbuntuDeprovisionHandler, self).__init__() def setup(self, deluser): warnings, actions = super(UbuntuDeprovisionHandler, self).setup(deluser) diff --git a/azurelinuxagent/pa/provision/__init__.py b/azurelinuxagent/pa/provision/__init__.py new file mode 100644 index 0000000..05f75ae --- /dev/null +++ b/azurelinuxagent/pa/provision/__init__.py @@ -0,0 +1,18 @@ +# Copyright 2014 Microsoft Corporation +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +# +# Requires Python 2.4+ and Openssl 1.0+ +# + +from azurelinuxagent.pa.provision.factory import get_provision_handler diff --git a/azurelinuxagent/distro/default/provision.py b/azurelinuxagent/pa/provision/default.py similarity index 75% rename from azurelinuxagent/distro/default/provision.py rename to azurelinuxagent/pa/provision/default.py index ae2951d..b07c147 100644 --- a/azurelinuxagent/distro/default/provision.py +++ b/azurelinuxagent/pa/provision/default.py @@ -20,21 +20,25 @@ Provision handler """ import os -import azurelinuxagent.logger as logger -from azurelinuxagent.future import ustr -import azurelinuxagent.conf as conf -from azurelinuxagent.event import add_event, WALAEventOperation -from azurelinuxagent.exception import ProvisionError, ProtocolError, OSUtilError -from azurelinuxagent.protocol.restapi import ProvisionStatus -import azurelinuxagent.utils.shellutil as shellutil -import azurelinuxagent.utils.fileutil as fileutil +import azurelinuxagent.common.logger as logger +from azurelinuxagent.common.future import ustr +import azurelinuxagent.common.conf as conf +from azurelinuxagent.common.event import add_event, WALAEventOperation +from azurelinuxagent.common.exception import ProvisionError, ProtocolError, \ + OSUtilError +from azurelinuxagent.common.protocol.restapi import ProvisionStatus +import azurelinuxagent.common.utils.shellutil as shellutil +import azurelinuxagent.common.utils.fileutil as fileutil +from azurelinuxagent.common.osutil import get_osutil +from azurelinuxagent.common.protocol import get_protocol_util CUSTOM_DATA_FILE="CustomData" class ProvisionHandler(object): - def __init__(self, distro): - self.distro = distro + def __init__(self): + self.osutil = get_osutil() + self.protocol_util = get_protocol_util() def run(self): #If provision is not enabled, return @@ -49,12 +53,12 @@ class ProvisionHandler(object): logger.info("Run provision handler.") logger.info("Copy ovf-env.xml.") try: - ovfenv = self.distro.protocol_util.copy_ovf_env() + ovfenv = self.protocol_util.copy_ovf_env() except ProtocolError as e: self.report_event("Failed to copy ovf-env.xml: {0}".format(e)) return - self.distro.protocol_util.detect_protocol_by_file() + self.protocol_util.get_protocol_by_file() self.report_not_ready("Provisioning", "Starting") @@ -95,50 +99,50 @@ class ProvisionHandler(object): logger.info("Handle ovf-env.xml.") try: logger.info("Set host name.") - self.distro.osutil.set_hostname(ovfenv.hostname) + self.osutil.set_hostname(ovfenv.hostname) logger.info("Publish host name.") - self.distro.osutil.publish_hostname(ovfenv.hostname) + self.osutil.publish_hostname(ovfenv.hostname) self.config_user_account(ovfenv) self.save_customdata(ovfenv) if conf.get_delete_root_password(): - self.distro.osutil.del_root_password() + self.osutil.del_root_password() except OSUtilError as e: raise ProvisionError("Failed to handle ovf-env.xml: {0}".format(e)) def config_user_account(self, ovfenv): logger.info("Create user account if not exists") - self.distro.osutil.useradd(ovfenv.username) + self.osutil.useradd(ovfenv.username) if ovfenv.user_password is not None: logger.info("Set user password.") crypt_id = conf.get_password_cryptid() salt_len = conf.get_password_crypt_salt_len() - self.distro.osutil.chpasswd(ovfenv.username, ovfenv.user_password, + self.osutil.chpasswd(ovfenv.username, ovfenv.user_password, crypt_id=crypt_id, salt_len=salt_len) logger.info("Configure sudoer") - self.distro.osutil.conf_sudoer(ovfenv.username, nopasswd=ovfenv.user_password is None) + self.osutil.conf_sudoer(ovfenv.username, nopasswd=ovfenv.user_password is None) logger.info("Configure sshd") - self.distro.osutil.conf_sshd(ovfenv.disable_ssh_password_auth) + self.osutil.conf_sshd(ovfenv.disable_ssh_password_auth) #Disable selinux temporary - sel = self.distro.osutil.is_selinux_enforcing() + sel = self.osutil.is_selinux_enforcing() if sel: - self.distro.osutil.set_selinux_enforce(0) + self.osutil.set_selinux_enforce(0) self.deploy_ssh_pubkeys(ovfenv) self.deploy_ssh_keypairs(ovfenv) if sel: - self.distro.osutil.set_selinux_enforce(1) + self.osutil.set_selinux_enforce(1) - self.distro.osutil.restart_ssh_service() + self.osutil.restart_ssh_service() def save_customdata(self, ovfenv): customdata = ovfenv.customdata @@ -148,7 +152,8 @@ class ProvisionHandler(object): logger.info("Save custom data") lib_dir = conf.get_lib_dir() if conf.get_decode_customdata(): - customdata= self.distro.osutil.decode_customdata(customdata) + customdata= self.osutil.decode_customdata(customdata) + customdata_file = os.path.join(lib_dir, CUSTOM_DATA_FILE) fileutil.write_file(customdata_file, customdata) @@ -160,12 +165,12 @@ class ProvisionHandler(object): def deploy_ssh_pubkeys(self, ovfenv): for pubkey in ovfenv.ssh_pubkeys: logger.info("Deploy ssh public key.") - self.distro.osutil.deploy_ssh_pubkey(ovfenv.username, pubkey) + self.osutil.deploy_ssh_pubkey(ovfenv.username, pubkey) def deploy_ssh_keypairs(self, ovfenv): for keypair in ovfenv.ssh_keypairs: logger.info("Deploy ssh key pairs.") - self.distro.osutil.deploy_ssh_keypair(ovfenv.username, keypair) + self.osutil.deploy_ssh_keypair(ovfenv.username, keypair) def report_event(self, message, is_success=False): add_event(name="WALA", message=message, is_success=is_success, @@ -175,7 +180,7 @@ class ProvisionHandler(object): status = ProvisionStatus(status="NotReady", subStatus=sub_status, description=description) try: - protocol = self.distro.protocol_util.get_protocol() + protocol = self.protocol_util.get_protocol() protocol.report_provision_status(status) except ProtocolError as e: self.report_event(ustr(e)) @@ -184,7 +189,7 @@ class ProvisionHandler(object): status = ProvisionStatus(status="Ready") status.properties.certificateThumbprint = thumbprint try: - protocol = self.distro.protocol_util.get_protocol() + protocol = self.protocol_util.get_protocol() protocol.report_provision_status(status) except ProtocolError as e: self.report_event(ustr(e)) diff --git a/azurelinuxagent/distro/suse/distro.py b/azurelinuxagent/pa/provision/factory.py similarity index 51% rename from azurelinuxagent/distro/suse/distro.py rename to azurelinuxagent/pa/provision/factory.py index 5b39369..9bbe35c 100644 --- a/azurelinuxagent/distro/suse/distro.py +++ b/azurelinuxagent/pa/provision/factory.py @@ -1,5 +1,3 @@ -# Microsoft Azure Linux Agent -# # Copyright 2014 Microsoft Corporation # # Licensed under the Apache License, Version 2.0 (the "License"); @@ -17,16 +15,18 @@ # Requires Python 2.4+ and Openssl 1.0+ # -from azurelinuxagent.distro.default.distro import DefaultDistro -from azurelinuxagent.distro.suse.osutil import SUSE11OSUtil, SUSEOSUtil +import azurelinuxagent.common.logger as logger +from azurelinuxagent.common.utils.textutil import Version +from azurelinuxagent.common.version import DISTRO_NAME, DISTRO_VERSION, \ + DISTRO_FULL_NAME +from .default import ProvisionHandler +from .ubuntu import UbuntuProvisionHandler -class SUSE11Distro(DefaultDistro): - def __init__(self): - super(SUSE11Distro, self).__init__() - self.osutil = SUSE11OSUtil() +def get_provision_handler(distro_name=DISTRO_NAME, + distro_version=DISTRO_VERSION, + distro_full_name=DISTRO_FULL_NAME): + if distro_name == "ubuntu": + return UbuntuProvisionHandler() -class SUSEDistro(DefaultDistro): - def __init__(self): - super(SUSEDistro, self).__init__() - self.osutil = SUSEOSUtil() + return ProvisionHandler() diff --git a/azurelinuxagent/distro/ubuntu/provision.py b/azurelinuxagent/pa/provision/ubuntu.py similarity index 81% rename from azurelinuxagent/distro/ubuntu/provision.py rename to azurelinuxagent/pa/provision/ubuntu.py index 330e057..b6098c4 100644 --- a/azurelinuxagent/distro/ubuntu/provision.py +++ b/azurelinuxagent/pa/provision/ubuntu.py @@ -19,22 +19,22 @@ import os import time -import azurelinuxagent.logger as logger -from azurelinuxagent.future import ustr -import azurelinuxagent.conf as conf -import azurelinuxagent.protocol.ovfenv as ovfenv -from azurelinuxagent.event import add_event, WALAEventOperation -from azurelinuxagent.exception import ProvisionError, ProtocolError -import azurelinuxagent.utils.shellutil as shellutil -import azurelinuxagent.utils.fileutil as fileutil -from azurelinuxagent.distro.default.provision import ProvisionHandler +import azurelinuxagent.common.logger as logger +from azurelinuxagent.common.future import ustr +import azurelinuxagent.common.conf as conf +import azurelinuxagent.common.protocol.ovfenv as ovfenv +from azurelinuxagent.common.event import add_event, WALAEventOperation +from azurelinuxagent.common.exception import ProvisionError, ProtocolError +import azurelinuxagent.common.utils.shellutil as shellutil +import azurelinuxagent.common.utils.fileutil as fileutil +from azurelinuxagent.pa.provision.default import ProvisionHandler """ On ubuntu image, provision could be disabled. """ class UbuntuProvisionHandler(ProvisionHandler): - def __init__(self, distro): - self.distro = distro + def __init__(self): + super(UbuntuProvisionHandler, self).__init__() def run(self): #If provision is enabled, run default provision handler @@ -50,7 +50,7 @@ class UbuntuProvisionHandler(ProvisionHandler): logger.info("Waiting cloud-init to copy ovf-env.xml.") self.wait_for_ovfenv() - protocol = self.distro.protocol_util.detect_protocol() + protocol = self.protocol_util.get_protocol() self.report_not_ready("Provisioning", "Starting") logger.info("Sleep 15 seconds to prevent throttling") time.sleep(15) #Sleep to prevent throttling @@ -75,7 +75,7 @@ class UbuntuProvisionHandler(ProvisionHandler): """ for retry in range(0, max_retry): try: - self.distro.protocol_util.get_ovf_env() + self.protocol_util.get_ovf_env() return except ProtocolError: if retry < max_retry - 1: diff --git a/azurelinuxagent/utils/__init__.py b/azurelinuxagent/utils/__init__.py deleted file mode 100644 index d9b82f5..0000000 --- a/azurelinuxagent/utils/__init__.py +++ /dev/null @@ -1,19 +0,0 @@ -# Microsoft Azure Linux Agent -# -# Copyright 2014 Microsoft Corporation -# -# Licensed under the Apache License, Version 2.0 (the "License"); -# you may not use this file except in compliance with the License. -# You may obtain a copy of the License at -# -# http://www.apache.org/licenses/LICENSE-2.0 -# -# Unless required by applicable law or agreed to in writing, software -# distributed under the License is distributed on an "AS IS" BASIS, -# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -# See the License for the specific language governing permissions and -# limitations under the License. -# -# Requires Python 2.4+ and Openssl 1.0+ -# - diff --git a/setup.py b/setup.py index 0cfecff..0f42ba6 100755 --- a/setup.py +++ b/setup.py @@ -18,11 +18,11 @@ # import os -from azurelinuxagent.metadata import AGENT_NAME, AGENT_VERSION, \ +from azurelinuxagent.common.version import AGENT_NAME, AGENT_VERSION, \ AGENT_DESCRIPTION, \ DISTRO_NAME, DISTRO_VERSION, DISTRO_FULL_NAME -from azurelinuxagent.agent import Agent +from azurelinuxagent.common.osutil import get_osutil import setuptools from setuptools import find_packages from setuptools.command.install import install as _install @@ -158,7 +158,7 @@ class install(_install): def run(self): _install.run(self) if self.register_service: - Agent(False).register_service() + get_osutil().register_agent_service() setuptools.setup(name=AGENT_NAME, version=AGENT_VERSION, diff --git a/tests/__init__.py b/tests/__init__.py index 9bdb27e..2ef4c16 100644 --- a/tests/__init__.py +++ b/tests/__init__.py @@ -14,6 +14,3 @@ # # Requires Python 2.4+ and Openssl 1.0+ # -# Implements parts of RFC 2131, 1541, 1497 and -# http://msdn.microsoft.com/en-us/library/cc227282%28PROT.10%29.aspx -# http://msdn.microsoft.com/en-us/library/cc227259%28PROT.13%29.aspx diff --git a/azurelinuxagent/protocol/__init__.py b/tests/daemon/__init__.py similarity index 95% rename from azurelinuxagent/protocol/__init__.py rename to tests/daemon/__init__.py index 8c1bbdb..2ef4c16 100644 --- a/azurelinuxagent/protocol/__init__.py +++ b/tests/daemon/__init__.py @@ -1,5 +1,3 @@ -# Microsoft Azure Linux Agent -# # Copyright 2014 Microsoft Corporation # # Licensed under the Apache License, Version 2.0 (the "License"); diff --git a/tests/distro/test_daemon.py b/tests/daemon/test_daemon.py similarity index 63% rename from tests/distro/test_daemon.py rename to tests/daemon/test_daemon.py index 9d3c45b..263af49 100644 --- a/tests/distro/test_daemon.py +++ b/tests/daemon/test_daemon.py @@ -14,14 +14,10 @@ # # Requires Python 2.4+ and Openssl 1.0+ # -# Implements parts of RFC 2131, 1541, 1497 and -# http://msdn.microsoft.com/en-us/library/cc227282%28PROT.10%29.aspx -# http://msdn.microsoft.com/en-us/library/cc227259%28PROT.13%29.aspx from tests.tools import * -from azurelinuxagent.distro.loader import get_distro -from azurelinuxagent.exception import * -from azurelinuxagent.distro.default.daemon import * +from azurelinuxagent.common.exception import * +from azurelinuxagent.daemon import * class MockDaemonCall(object): def __init__(self, daemon_handler, count): @@ -38,26 +34,30 @@ class MockDaemonCall(object): @patch("time.sleep") class TestDaemon(AgentTestCase): def test_daemon_restart(self, mock_sleep): - distro = get_distro() - mock_daemon = Mock(side_effect=MockDaemonCall(distro.daemon_handler, 2)) - distro.daemon_handler.daemon = mock_daemon - distro.daemon_handler.check_pid = Mock() - distro.daemon_handler.run() + #Mock daemon function + daemon_handler = get_daemon_handler() + mock_daemon = Mock(side_effect=MockDaemonCall(daemon_handler, 2)) + daemon_handler.daemon = mock_daemon + + daemon_handler.check_pid = Mock() + + daemon_handler.run() mock_sleep.assert_any_call(15) - self.assertEquals(2, distro.daemon_handler.daemon.call_count) + self.assertEquals(2, daemon_handler.daemon.call_count) - @patch("azurelinuxagent.distro.default.daemon.conf") - @patch("azurelinuxagent.distro.default.daemon.sys.exit") + @patch("azurelinuxagent.daemon.main.conf") + @patch("azurelinuxagent.daemon.main.sys.exit") def test_check_pid(self, mock_exit, mock_conf, mock_sleep): - distro = get_distro() + daemon_handler = get_daemon_handler() + mock_pid_file = os.path.join(self.tmp_dir, "pid") mock_conf.get_agent_pid_file_path = Mock(return_value=mock_pid_file) - distro.daemon_handler.check_pid() + daemon_handler.check_pid() self.assertTrue(os.path.isfile(mock_pid_file)) - distro.daemon_handler.check_pid() + daemon_handler.check_pid() mock_exit.assert_any_call(0) if __name__ == '__main__': diff --git a/tests/distro/test_monitor.py b/tests/daemon/test_monitor.py similarity index 77% rename from tests/distro/test_monitor.py rename to tests/daemon/test_monitor.py index 1dd7740..e037dc0 100644 --- a/tests/distro/test_monitor.py +++ b/tests/daemon/test_monitor.py @@ -14,13 +14,10 @@ # # Requires Python 2.4+ and Openssl 1.0+ # -# Implements parts of RFC 2131, 1541, 1497 and -# http://msdn.microsoft.com/en-us/library/cc227282%28PROT.10%29.aspx -# http://msdn.microsoft.com/en-us/library/cc227259%28PROT.13%29.aspx from tests.tools import * -from azurelinuxagent.exception import * -from azurelinuxagent.distro.default.monitor import * +from azurelinuxagent.common.exception import * +from azurelinuxagent.daemon.monitor import * class TestMonitor(AgentTestCase): def test_parse_xml_event(self): diff --git a/tests/data/ga/WALinuxAgent-2.1.5.rc0.zip b/tests/data/ga/WALinuxAgent-2.1.5.rc0.zip new file mode 100644 index 0000000..fcf158d Binary files /dev/null and b/tests/data/ga/WALinuxAgent-2.1.5.rc0.zip differ diff --git a/tests/data/wire/ext_conf.xml b/tests/data/wire/ext_conf.xml index aded2dd..0b7c528 100644 --- a/tests/data/wire/ext_conf.xml +++ b/tests/data/wire/ext_conf.xml @@ -1,36 +1,16 @@ - Win8 + Prod - http://rdfepirv2hknprdstr03.blob.core.windows.net/bfd5c281a7dc4e4b84381eb0b47e3aaf/Microsoft.WindowsAzure.GuestAgent_Win8_asiaeast_manifest.xml - http://rdfepirv2hknprdstr04.blob.core.windows.net/bfd5c281a7dc4e4b84381eb0b47e3aaf/Microsoft.WindowsAzure.GuestAgent_Win8_asiaeast_manifest.xml - http://rdfepirv2hknprdstr05.blob.core.windows.net/bfd5c281a7dc4e4b84381eb0b47e3aaf/Microsoft.WindowsAzure.GuestAgent_Win8_asiaeast_manifest.xml - http://rdfepirv2hknprdstr06.blob.core.windows.net/bfd5c281a7dc4e4b84381eb0b47e3aaf/Microsoft.WindowsAzure.GuestAgent_Win8_asiaeast_manifest.xml - http://rdfepirv2hknprdstr07.blob.core.windows.net/bfd5c281a7dc4e4b84381eb0b47e3aaf/Microsoft.WindowsAzure.GuestAgent_Win8_asiaeast_manifest.xml - http://rdfepirv2hknprdstr08.blob.core.windows.net/bfd5c281a7dc4e4b84381eb0b47e3aaf/Microsoft.WindowsAzure.GuestAgent_Win8_asiaeast_manifest.xml - http://rdfepirv2hknprdstr09.blob.core.windows.net/bfd5c281a7dc4e4b84381eb0b47e3aaf/Microsoft.WindowsAzure.GuestAgent_Win8_asiaeast_manifest.xml - http://rdfepirv2hknprdstr10.blob.core.windows.net/bfd5c281a7dc4e4b84381eb0b47e3aaf/Microsoft.WindowsAzure.GuestAgent_Win8_asiaeast_manifest.xml - http://rdfepirv2hknprdstr11.blob.core.windows.net/bfd5c281a7dc4e4b84381eb0b47e3aaf/Microsoft.WindowsAzure.GuestAgent_Win8_asiaeast_manifest.xml - http://rdfepirv2hknprdstr12.blob.core.windows.net/bfd5c281a7dc4e4b84381eb0b47e3aaf/Microsoft.WindowsAzure.GuestAgent_Win8_asiaeast_manifest.xml - http://zrdfepirv2hk2prdstr01.blob.core.windows.net/bfd5c281a7dc4e4b84381eb0b47e3aaf/Microsoft.WindowsAzure.GuestAgent_Win8_asiaeast_manifest.xml + http://manifest_of_ga.xml - Win7 + Test - http://rdfepirv2hknprdstr03.blob.core.windows.net/bfd5c281a7dc4e4b84381eb0b47e3aaf/Microsoft.WindowsAzure.GuestAgent_Win7_asiaeast_manifest.xml - http://rdfepirv2hknprdstr04.blob.core.windows.net/bfd5c281a7dc4e4b84381eb0b47e3aaf/Microsoft.WindowsAzure.GuestAgent_Win7_asiaeast_manifest.xml - http://rdfepirv2hknprdstr05.blob.core.windows.net/bfd5c281a7dc4e4b84381eb0b47e3aaf/Microsoft.WindowsAzure.GuestAgent_Win7_asiaeast_manifest.xml - http://rdfepirv2hknprdstr06.blob.core.windows.net/bfd5c281a7dc4e4b84381eb0b47e3aaf/Microsoft.WindowsAzure.GuestAgent_Win7_asiaeast_manifest.xml - http://rdfepirv2hknprdstr07.blob.core.windows.net/bfd5c281a7dc4e4b84381eb0b47e3aaf/Microsoft.WindowsAzure.GuestAgent_Win7_asiaeast_manifest.xml - http://rdfepirv2hknprdstr08.blob.core.windows.net/bfd5c281a7dc4e4b84381eb0b47e3aaf/Microsoft.WindowsAzure.GuestAgent_Win7_asiaeast_manifest.xml - http://rdfepirv2hknprdstr09.blob.core.windows.net/bfd5c281a7dc4e4b84381eb0b47e3aaf/Microsoft.WindowsAzure.GuestAgent_Win7_asiaeast_manifest.xml - http://rdfepirv2hknprdstr10.blob.core.windows.net/bfd5c281a7dc4e4b84381eb0b47e3aaf/Microsoft.WindowsAzure.GuestAgent_Win7_asiaeast_manifest.xml - http://rdfepirv2hknprdstr11.blob.core.windows.net/bfd5c281a7dc4e4b84381eb0b47e3aaf/Microsoft.WindowsAzure.GuestAgent_Win7_asiaeast_manifest.xml - http://rdfepirv2hknprdstr12.blob.core.windows.net/bfd5c281a7dc4e4b84381eb0b47e3aaf/Microsoft.WindowsAzure.GuestAgent_Win7_asiaeast_manifest.xml - http://zrdfepirv2hk2prdstr01.blob.core.windows.net/bfd5c281a7dc4e4b84381eb0b47e3aaf/Microsoft.WindowsAzure.GuestAgent_Win7_asiaeast_manifest.xml - + http://manifest_of_ga.xml + diff --git a/tests/data/wire/ga_manifest.xml b/tests/data/wire/ga_manifest.xml new file mode 100644 index 0000000..f43daf5 --- /dev/null +++ b/tests/data/wire/ga_manifest.xml @@ -0,0 +1,48 @@ + + + + + 1.0.0 + + http://foo.bar/zar/OSTCExtensions.WALinuxAgent__1.0.0 + + + + 1.1.0 + + http://foo.bar/zar/OSTCExtensions.WALinuxAgent__1.1.0 + + + + 2.0.0http://host/OSTCExtensions.WALinuxAgent__2.0.0 + + + 2.1.0http://host/OSTCExtensions.WALinuxAgent__2.1.0 + + + 2.1.1http://host/OSTCExtensions.WALinuxAgent__2.1.1 + + + 2.2.0http://host/OSTCExtensions.WALinuxAgent__2.2.0 + + + 3.0http://host/OSTCExtensions.WALinuxAgent__3.0 + + + 3.1http://host/OSTCExtensions.WALinuxAgent__3.1 + + + 4.0.0.0http://host/OSTCExtensions.WALinuxAgent__3.0 + + + 4.0.0.1http://host/OSTCExtensions.WALinuxAgent__3.1 + + + 4.1.0.0http://host/OSTCExtensions.WALinuxAgent__3.1 + + + 99999.0.0.0http://host/OSTCExtensions.WALinuxAgent__99999.0.0.0 + + + + diff --git a/tests/distro/__init__.py b/tests/distro/__init__.py deleted file mode 100644 index 9bdb27e..0000000 --- a/tests/distro/__init__.py +++ /dev/null @@ -1,19 +0,0 @@ -# Copyright 2014 Microsoft Corporation -# -# Licensed under the Apache License, Version 2.0 (the "License"); -# you may not use this file except in compliance with the License. -# You may obtain a copy of the License at -# -# http://www.apache.org/licenses/LICENSE-2.0 -# -# Unless required by applicable law or agreed to in writing, software -# distributed under the License is distributed on an "AS IS" BASIS, -# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -# See the License for the specific language governing permissions and -# limitations under the License. -# -# Requires Python 2.4+ and Openssl 1.0+ -# -# Implements parts of RFC 2131, 1541, 1497 and -# http://msdn.microsoft.com/en-us/library/cc227282%28PROT.10%29.aspx -# http://msdn.microsoft.com/en-us/library/cc227259%28PROT.13%29.aspx diff --git a/tests/distro/test_loader.py b/tests/distro/test_loader.py deleted file mode 100644 index 94ca913..0000000 --- a/tests/distro/test_loader.py +++ /dev/null @@ -1,36 +0,0 @@ -# Copyright 2014 Microsoft Corporation -# -# Licensed under the Apache License, Version 2.0 (the "License"); -# you may not use this file except in compliance with the License. -# You may obtain a copy of the License at -# -# http://www.apache.org/licenses/LICENSE-2.0 -# -# Unless required by applicable law or agreed to in writing, software -# distributed under the License is distributed on an "AS IS" BASIS, -# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -# See the License for the specific language governing permissions and -# limitations under the License. -# -# Requires Python 2.4+ and Openssl 1.0+ -# -# Implements parts of RFC 2131, 1541, 1497 and -# http://msdn.microsoft.com/en-us/library/cc227282%28PROT.10%29.aspx -# http://msdn.microsoft.com/en-us/library/cc227259%28PROT.13%29.aspx - -from tests.tools import * -from azurelinuxagent.distro.loader import get_distro -from azurelinuxagent.distro.default.distro import DefaultDistro - -class TestDistroLoader(AgentTestCase): - - @distros() - def test_distro_loader(self, *distro_args): - distro = get_distro(*distro_args) - self.assertNotEquals(None, distro) - self.assertNotEquals(DefaultDistro, type(distro)) - - -if __name__ == '__main__': - unittest.main() - diff --git a/azurelinuxagent/distro/coreos/__init__.py b/tests/ga/__init__.py similarity index 95% rename from azurelinuxagent/distro/coreos/__init__.py rename to tests/ga/__init__.py index 8c1bbdb..2ef4c16 100644 --- a/azurelinuxagent/distro/coreos/__init__.py +++ b/tests/ga/__init__.py @@ -1,5 +1,3 @@ -# Microsoft Azure Linux Agent -# # Copyright 2014 Microsoft Corporation # # Licensed under the Apache License, Version 2.0 (the "License"); diff --git a/tests/distro/test_extension.py b/tests/ga/test_extension.py similarity index 86% rename from tests/distro/test_extension.py rename to tests/ga/test_extension.py index e54cbf5..061c0ee 100644 --- a/tests/distro/test_extension.py +++ b/tests/ga/test_extension.py @@ -14,19 +14,16 @@ # # Requires Python 2.4+ and Openssl 1.0+ # -# Implements parts of RFC 2131, 1541, 1497 and -# http://msdn.microsoft.com/en-us/library/cc227282%28PROT.10%29.aspx -# http://msdn.microsoft.com/en-us/library/cc227259%28PROT.13%29.aspx from tests.protocol.mockwiredata import * -from azurelinuxagent.exception import * -from azurelinuxagent.distro.loader import get_distro -from azurelinuxagent.protocol.wire import WireProtocol -from azurelinuxagent.distro.default.extension import ExtHandlerInstance +from azurelinuxagent.common.exception import * +from azurelinuxagent.common.protocol import get_protocol_util +from azurelinuxagent.ga.exthandlers import * +from azurelinuxagent.common.protocol.wire import WireProtocol @patch("time.sleep") -@patch("azurelinuxagent.protocol.wire.CryptUtil") -@patch("azurelinuxagent.utils.restutil.http_get") +@patch("azurelinuxagent.common.protocol.wire.CryptUtil") +@patch("azurelinuxagent.common.utils.restutil.http_get") class TestExtension(AgentTestCase): def _assert_handler_status(self, report_vm_status, expected_status, @@ -50,31 +47,32 @@ class TestExtension(AgentTestCase): def _create_mock(self, test_data, mock_http_get, MockCryptUtil, _): """Test enable/disable/unistall of an extension""" - distro = get_distro() - + handler = get_exthandlers_handler() + #Mock protocol to return test data mock_http_get.side_effect = test_data.mock_http_get MockCryptUtil.side_effect = test_data.mock_crypt_util - + protocol = WireProtocol("foo.bar") protocol.detect() protocol.report_ext_status = MagicMock() protocol.report_vm_status = MagicMock() - distro.protocol_util.get_protocol = Mock(return_value=protocol) + + handler.protocol_util.get_protocol = Mock(return_value=protocol) - return distro, protocol + return handler, protocol def test_ext_handler(self, *args): test_data = WireProtocolData(DATA_FILE) - distro, protocol = self._create_mock(test_data, *args) + exthandlers_handler, protocol = self._create_mock(test_data, *args) #Test enable scenario. - distro.ext_handlers_handler.run() + exthandlers_handler.run() self._assert_handler_status(protocol.report_vm_status, "Ready", 1, "1.0.0") self._assert_ext_status(protocol.report_ext_status, "success", 0) #Test goal state not changed - distro.ext_handlers_handler.run() + exthandlers_handler.run() self._assert_handler_status(protocol.report_vm_status, "Ready", 1, "1.0.0") #Test goal state changed @@ -82,7 +80,7 @@ class TestExtension(AgentTestCase): "2<") test_data.ext_conf = test_data.ext_conf.replace("seqNo=\"0\"", "seqNo=\"1\"") - distro.ext_handlers_handler.run() + exthandlers_handler.run() self._assert_handler_status(protocol.report_vm_status, "Ready", 1, "1.0.0") self._assert_ext_status(protocol.report_ext_status, "success", 1) @@ -92,7 +90,7 @@ class TestExtension(AgentTestCase): test_data.ext_conf = test_data.ext_conf.replace("1.0.0", "1.1.0") test_data.ext_conf = test_data.ext_conf.replace("seqNo=\"1\"", "seqNo=\"2\"") - distro.ext_handlers_handler.run() + exthandlers_handler.run() self._assert_handler_status(protocol.report_vm_status, "Ready", 1, "1.1.0") self._assert_ext_status(protocol.report_ext_status, "success", 2) @@ -100,7 +98,7 @@ class TestExtension(AgentTestCase): test_data.goal_state = test_data.goal_state.replace("3<", "4<") test_data.ext_conf = test_data.ext_conf.replace("enabled", "disabled") - distro.ext_handlers_handler.run() + exthandlers_handler.run() self._assert_handler_status(protocol.report_vm_status, "NotReady", 1, "1.1.0") @@ -108,56 +106,56 @@ class TestExtension(AgentTestCase): test_data.goal_state = test_data.goal_state.replace("4<", "5<") test_data.ext_conf = test_data.ext_conf.replace("disabled", "uninstall") - distro.ext_handlers_handler.run() + exthandlers_handler.run() self._assert_no_handler_status(protocol.report_vm_status) #Test uninstall again! test_data.goal_state = test_data.goal_state.replace("5<", "6<") - distro.ext_handlers_handler.run() + exthandlers_handler.run() self._assert_no_handler_status(protocol.report_vm_status) def test_ext_handler_no_settings(self, *args): test_data = WireProtocolData(DATA_FILE_EXT_NO_SETTINGS) - distro, protocol = self._create_mock(test_data, *args) + exthandlers_handler, protocol = self._create_mock(test_data, *args) - distro.ext_handlers_handler.run() + exthandlers_handler.run() self._assert_handler_status(protocol.report_vm_status, "Ready", 0, "1.0.0") def test_ext_handler_no_public_settings(self, *args): test_data = WireProtocolData(DATA_FILE_EXT_NO_PUBLIC) - distro, protocol = self._create_mock(test_data, *args) + exthandlers_handler, protocol = self._create_mock(test_data, *args) - distro.ext_handlers_handler.run() + exthandlers_handler.run() self._assert_handler_status(protocol.report_vm_status, "Ready", 1, "1.0.0") def test_ext_handler_no_ext(self, *args): test_data = WireProtocolData(DATA_FILE_NO_EXT) - distro, protocol = self._create_mock(test_data, *args) + exthandlers_handler, protocol = self._create_mock(test_data, *args) #Assert no extension handler status - distro.ext_handlers_handler.run() + exthandlers_handler.run() self._assert_no_handler_status(protocol.report_vm_status) - @patch('azurelinuxagent.distro.default.extension.add_event') + @patch('azurelinuxagent.ga.exthandlers.add_event') def test_ext_handler_download_failure(self, mock_add_event, *args): test_data = WireProtocolData(DATA_FILE) - distro, protocol = self._create_mock(test_data, *args) + exthandlers_handler, protocol = self._create_mock(test_data, *args) protocol.download_ext_handler_pkg = Mock(side_effect=ProtocolError) - distro.ext_handlers_handler.run() + exthandlers_handler.run() args, kw = mock_add_event.call_args self.assertEquals(False, kw['is_success']) self.assertEquals("OSTCExtensions.ExampleHandlerLinux", kw['name']) self.assertEquals("Download", kw['op']) - @patch('azurelinuxagent.distro.default.extension.fileutil') + @patch('azurelinuxagent.ga.exthandlers.fileutil') def test_ext_handler_io_error(self, mock_fileutil, *args): test_data = WireProtocolData(DATA_FILE) - distro, protocol = self._create_mock(test_data, *args) + exthandlers_handler, protocol = self._create_mock(test_data, *args) mock_fileutil.write_file.return_value = IOError("Mock IO Error") - distro.ext_handlers_handler.run() + exthandlers_handler.run() def _assert_ext_status(self, report_ext_status, expected_status, expected_seq_no): @@ -169,8 +167,8 @@ class TestExtension(AgentTestCase): def test_ext_handler_no_reporting_status(self, *args): test_data = WireProtocolData(DATA_FILE) - distro, protocol = self._create_mock(test_data, *args) - distro.ext_handlers_handler.run() + exthandlers_handler, protocol = self._create_mock(test_data, *args) + exthandlers_handler.run() self._assert_handler_status(protocol.report_vm_status, "Ready", 1, "1.0.0") #Remove status file and re-run collecting extension status @@ -180,7 +178,7 @@ class TestExtension(AgentTestCase): self.assertTrue(os.path.isfile(status_file)) os.remove(status_file) - distro.ext_handlers_handler.run() + exthandlers_handler.run() self._assert_handler_status(protocol.report_vm_status, "Ready", 1, "1.0.0") self._assert_ext_status(protocol.report_ext_status, "error", 0) diff --git a/tests/ga/test_update.py b/tests/ga/test_update.py new file mode 100644 index 0000000..b382b32 --- /dev/null +++ b/tests/ga/test_update.py @@ -0,0 +1,136 @@ +# Copyright 2014 Microsoft Corporation +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +# +# Requires Python 2.4+ and Openssl 1.0+ +# + +from tests.tools import * +import azurelinuxagent.common.utils.fileutil as fileutil +from azurelinuxagent.common.protocol.wire import * +from azurelinuxagent.common.exception import UpdateError +from azurelinuxagent.ga.update import * +from tests.protocol.mockwiredata import * + +class TestUpdate(AgentTestCase): + def test_error_record(self): + update_handler = get_update_handler() + update_handler.mk_ga_dir() + + #Save error record + err = GuestAgentError(version="1.0") + update_handler.error_record[err.version] = err + update_handler.save_error_record() + + #Load error record + update_handler = get_update_handler() + update_handler.load_error_record() + self.assertNotEquals(None, update_handler.error_record.get("1.0")) + + #Mark failure and save again + err = update_handler.error_record.get("1.0") + err.mark_failure() + update_handler.save_error_record() + + #Re-load the error record and check + update_handler = get_update_handler() + update_handler.load_error_record() + self.assertNotEquals(None, update_handler.error_record.get("1.0")) + err = update_handler.error_record.get("1.0") + self.assertNotEquals(0, err.failure_count) + self.assertNotEquals(0, err.last_failure) + + @patch("time.sleep") + @patch("azurelinuxagent.common.protocol.wire.CryptUtil") + @patch("azurelinuxagent.common.utils.restutil.http_get") + def test_check_update(self, mock_http_get, MockCryptUtil, _): + update_handler = get_update_handler() + + test_data = WireProtocolData(DATA_FILE) + mock_http_get.side_effect = test_data.mock_http_get + MockCryptUtil.side_effect = test_data.mock_crypt_util + + protocol = WireProtocol("foo.bar") + protocol.detect() + update_handler.protocol_util.get_protocol = Mock(return_value=protocol) + + update_handler.check_for_update() + self.assertNotEquals(0, update_handler.agents) + + latest_agent = update_handler.get_latest_agent() + self.assertNotEquals(None, latest_agent) + self.assertEquals("99999.0.0.0", latest_agent.version) + #Only should consider versions >= current version + self.assertFalse("1.0.0" in update_handler.error_record) + + def test_run_extension(self): + update_handler = get_update_handler() + + #Create a mock guest agent instance + test_script = os.path.join(self.tmp_dir, "mock_success") + pkg = ExtHandlerPackage(version="1.0") + latest_agent = GuestAgent(pkg, GuestAgentError(version=pkg.version)) + latest_agent.get_agent_bin = Mock(return_value=test_script) + + #Create a test script to mock invoking run-extensions success + fileutil.write_file(test_script, "#!/bin/bash\nexit 0") + fileutil.chmod(test_script, 0o700) + update_handler.run_extensions(latest_agent) + + #Create a test script to mock invoking run-extensions failure + fileutil.write_file(test_script, "#!/bin/bash\nexit 1") + self.assertRaises(UpdateError, update_handler.run_extensions, + latest_agent) + +class TestGuestAgent(AgentTestCase): + @patch("azurelinuxagent.ga.update.restutil.http_get") + def test_download(self, mock_http_get): + update_handler = get_update_handler() + update_handler.mk_ga_dir() + + ga_pkg = load_bin_data("ga/WALinuxAgent-2.1.5.rc0.zip") + ga_pkg_resp = MagicMock() + ga_pkg_resp.status = restutil.httpclient.OK + ga_pkg_resp.read = Mock(return_value=ga_pkg) + mock_http_get.return_value= ga_pkg_resp + + pkg = ExtHandlerPackage(version="2.1.5.rc0") + pkg.uris.append(ExtHandlerPackageUri()) + agent = GuestAgent(pkg, GuestAgentError(version=pkg.version)) + agent.download() + self.assertTrue(agent.is_downloaded()) + +class TestGuestAgentError(AgentTestCase): + def test_mark_failure(self): + err = GuestAgentError() + + self.assertFalse(err.is_blacklisted()) + + for i in range(0, MAX_FAILURE): + err.mark_failure() + + #Assume agent failed >= MAX_FAILURE, it should be blacklisted + self.assertTrue(err.is_blacklisted()) + self.assertEqual(MAX_FAILURE, err.failure_count) + + #Clear old failure won't clear recent failure + err.clear_old_failure() + self.assertTrue(err.is_blacklisted()) + + #Unless we set the failure to earlier than (now - RETAIN_INTERVAL) + err.last_failure -= RETAIN_INTERVAL * 2 + err.clear_old_failure() + self.assertFalse(err.is_blacklisted()) + +if __name__ == '__main__': + unittest.main() diff --git a/tests/pa/__init__.py b/tests/pa/__init__.py new file mode 100644 index 0000000..2ef4c16 --- /dev/null +++ b/tests/pa/__init__.py @@ -0,0 +1,16 @@ +# Copyright 2014 Microsoft Corporation +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +# +# Requires Python 2.4+ and Openssl 1.0+ +# diff --git a/tests/distro/test_provision.py b/tests/pa/test_provision.py similarity index 58% rename from tests/distro/test_provision.py rename to tests/pa/test_provision.py index 60249ce..6508017 100644 --- a/tests/distro/test_provision.py +++ b/tests/pa/test_provision.py @@ -14,33 +14,33 @@ # # Requires Python 2.4+ and Openssl 1.0+ # -# Implements parts of RFC 2131, 1541, 1497 and -# http://msdn.microsoft.com/en-us/library/cc227282%28PROT.10%29.aspx -# http://msdn.microsoft.com/en-us/library/cc227259%28PROT.13%29.aspx from tests.tools import * -from azurelinuxagent.distro.loader import get_distro -from azurelinuxagent.distro.default.protocolUtil import * -import azurelinuxagent.utils.fileutil as fileutil +import azurelinuxagent.common.conf as conf +from azurelinuxagent.common.protocol import OVF_FILE_NAME +import azurelinuxagent.common.utils.fileutil as fileutil +from azurelinuxagent.pa.provision import get_provision_handler class TestProvision(AgentTestCase): - + @distros("redhat") def test_provision(self, distro_name, distro_version, distro_full_name): - distro = get_distro(distro_name, distro_version, distro_full_name) - distro.osutil = MagicMock() - distro.osutil.decode_customdata = Mock(return_value="") - - distro.protocol_util.detect_protocol_by_file = MagicMock() - distro.protocol_util.get_protocol = MagicMock() + provision_handler = get_provision_handler(distro_name, distro_version, + distro_full_name) + mock_osutil = MagicMock() + mock_osutil.decode_customdata = Mock(return_value="") + + provision_handler.osutil = mock_osutil + provision_handler.protocol_util.osutil = mock_osutil + provision_handler.protocol_util.get_protocol_by_file = MagicMock() + provision_handler.protocol_util.get_protocol = MagicMock() + conf.get_dvd_mount_point = Mock(return_value=self.tmp_dir) - ovfenv_file = os.path.join(self.tmp_dir, OVF_FILE_NAME) ovfenv_data = load_data("ovf-env.xml") fileutil.write_file(ovfenv_file, ovfenv_data) - handler = distro.provision_handler - handler.run() + provision_handler.run() if __name__ == '__main__': unittest.main() diff --git a/tests/protocol/__init__.py b/tests/protocol/__init__.py index 9bdb27e..2ef4c16 100644 --- a/tests/protocol/__init__.py +++ b/tests/protocol/__init__.py @@ -14,6 +14,3 @@ # # Requires Python 2.4+ and Openssl 1.0+ # -# Implements parts of RFC 2131, 1541, 1497 and -# http://msdn.microsoft.com/en-us/library/cc227282%28PROT.10%29.aspx -# http://msdn.microsoft.com/en-us/library/cc227259%28PROT.13%29.aspx diff --git a/tests/protocol/mockmetadata.py b/tests/protocol/mockmetadata.py index 0f7b568..dce3367 100644 --- a/tests/protocol/mockmetadata.py +++ b/tests/protocol/mockmetadata.py @@ -14,13 +14,10 @@ # # Requires Python 2.4+ and Openssl 1.0+ # -# Implements parts of RFC 2131, 1541, 1497 and -# http://msdn.microsoft.com/en-us/library/cc227282%28PROT.10%29.aspx -# http://msdn.microsoft.com/en-us/library/cc227259%28PROT.13%29.aspx from tests.tools import * -from azurelinuxagent.future import httpclient -from azurelinuxagent.utils.cryptutil import CryptUtil +from azurelinuxagent.common.future import httpclient +from azurelinuxagent.common.utils.cryptutil import CryptUtil DATA_FILE = { "identity": "metadata/identity.json", diff --git a/tests/protocol/mockwiredata.py b/tests/protocol/mockwiredata.py index 2bfb0e9..c789de5 100644 --- a/tests/protocol/mockwiredata.py +++ b/tests/protocol/mockwiredata.py @@ -14,13 +14,10 @@ # # Requires Python 2.4+ and Openssl 1.0+ # -# Implements parts of RFC 2131, 1541, 1497 and -# http://msdn.microsoft.com/en-us/library/cc227282%28PROT.10%29.aspx -# http://msdn.microsoft.com/en-us/library/cc227259%28PROT.13%29.aspx from tests.tools import * -from azurelinuxagent.future import httpclient -from azurelinuxagent.utils.cryptutil import CryptUtil +from azurelinuxagent.common.future import httpclient +from azurelinuxagent.common.utils.cryptutil import CryptUtil DATA_FILE = { "version_info": "wire/version_info.xml", @@ -30,6 +27,7 @@ DATA_FILE = { "certs": "wire/certs.xml", "ext_conf": "wire/ext_conf.xml", "manifest": "wire/manifest.xml", + "ga_manifest" : "wire/ga_manifest.xml", "trans_prv": "wire/trans_prv", "trans_cert": "wire/trans_cert", "test_ext": "ext/sample_ext.zip" @@ -62,6 +60,7 @@ class WireProtocolData(object): self.certs = load_data(data_files.get("certs")) self.ext_conf = load_data(data_files.get("ext_conf")) self.manifest = load_data(data_files.get("manifest")) + self.ga_manifest = load_data(data_files.get("ga_manifest")) self.trans_prv = load_data(data_files.get("trans_prv")) self.trans_cert = load_data(data_files.get("trans_cert")) self.ext = load_bin_data(data_files.get("test_ext")) @@ -82,6 +81,8 @@ class WireProtocolData(object): content = self.ext_conf elif "manifest.xml" in url: content = self.manifest + elif "manifest_of_ga.xml" in url: + content = self.ga_manifest elif "ExampleHandlerLinux" in url: content = self.ext resp = MagicMock() diff --git a/tests/protocol/test_metadata.py b/tests/protocol/test_metadata.py index fca1a82..3fcddbb 100644 --- a/tests/protocol/test_metadata.py +++ b/tests/protocol/test_metadata.py @@ -14,17 +14,14 @@ # # Requires Python 2.4+ and Openssl 1.0+ # -# Implements parts of RFC 2131, 1541, 1497 and -# http://msdn.microsoft.com/en-us/library/cc227282%28PROT.10%29.aspx -# http://msdn.microsoft.com/en-us/library/cc227259%28PROT.13%29.aspx from tests.tools import * from tests.protocol.mockmetadata import * -from azurelinuxagent.utils.restutil import httpclient -from azurelinuxagent.protocol.metadata import MetadataProtocol +from azurelinuxagent.common.utils.restutil import httpclient +from azurelinuxagent.common.protocol.metadata import MetadataProtocol @patch("time.sleep") -@patch("azurelinuxagent.protocol.metadata.restutil") +@patch("azurelinuxagent.common.protocol.metadata.restutil") class TestWireProtocolGetters(AgentTestCase): def _test_getters(self, test_data, mock_restutil ,_): mock_restutil.http_get.side_effect = test_data.mock_http_get diff --git a/tests/distro/test_protocol_util.py b/tests/protocol/test_protocol_util.py similarity index 52% rename from tests/distro/test_protocol_util.py rename to tests/protocol/test_protocol_util.py index 61339f3..cb9a06f 100644 --- a/tests/distro/test_protocol_util.py +++ b/tests/protocol/test_protocol_util.py @@ -14,72 +14,64 @@ # # Requires Python 2.4+ and Openssl 1.0+ # -# Implements parts of RFC 2131, 1541, 1497 and -# http://msdn.microsoft.com/en-us/library/cc227282%28PROT.10%29.aspx -# http://msdn.microsoft.com/en-us/library/cc227259%28PROT.13%29.aspx from tests.tools import * -from azurelinuxagent.distro.loader import get_distro -from azurelinuxagent.exception import * -from azurelinuxagent.distro.default.protocolUtil import * +from azurelinuxagent.common.exception import * +from azurelinuxagent.common.protocol import get_protocol_util, \ + TAG_FILE_NAME @patch("time.sleep") class TestProtocolUtil(AgentTestCase): - @distros() - @patch("azurelinuxagent.distro.default.protocolUtil.MetadataProtocol") - @patch("azurelinuxagent.distro.default.protocolUtil.WireProtocol") - def test_detect_protocol(self, distro_name, distro_version, distro_full_name, - WireProtocol, MetadataProtocol, _, *distro_args): - + @patch("azurelinuxagent.common.protocol.util.MetadataProtocol") + @patch("azurelinuxagent.common.protocol.util.WireProtocol") + def test_detect_protocol(self, WireProtocol, MetadataProtocol, _): WireProtocol.return_value = MagicMock() MetadataProtocol.return_value = MagicMock() + + protocol_util = get_protocol_util() - distro = get_distro(distro_name, distro_version, distro_full_name) - distro.dhcp_handler = MagicMock() - distro.dhcp_handler.endpoint = "foo.bar" + protocol_util.dhcp_handler = MagicMock() + protocol_util.dhcp_handler.endpoint = "foo.bar" #Test wire protocol is available - protocol = distro.protocol_util.detect_protocol() + protocol = protocol_util.get_protocol() self.assertEquals(WireProtocol.return_value, protocol) #Test wire protocol is not available - distro.protocol_util.protocol = None - WireProtocol.side_effect = ProtocolError() + protocol_util.clear_protocol() + WireProtocol.return_value.detect.side_effect = ProtocolError() - protocol = distro.protocol_util.detect_protocol() + protocol = protocol_util.get_protocol() self.assertEquals(MetadataProtocol.return_value, protocol) #Test no protocol is available - distro.protocol_util.protocol = None - WireProtocol.side_effect = ProtocolError() - MetadataProtocol.side_effect = ProtocolError() - self.assertRaises(ProtocolError, distro.protocol_util.detect_protocol) + protocol_util.clear_protocol() + WireProtocol.return_value.detect.side_effect = ProtocolError() - @distros() - def test_detect_protocol_by_file(self, distro_name, distro_version, - distro_full_name, _): - distro = get_distro(distro_name, distro_version, distro_full_name) - protocol_util = distro.protocol_util + MetadataProtocol.return_value.detect.side_effect = ProtocolError() + self.assertRaises(ProtocolError, protocol_util.get_protocol) + def test_detect_protocol_by_file(self, _): + protocol_util = get_protocol_util() protocol_util._detect_wire_protocol = Mock() protocol_util._detect_metadata_protocol = Mock() tag_file = os.path.join(self.tmp_dir, TAG_FILE_NAME) #Test tag file doesn't exist - protocol_util.detect_protocol_by_file() + protocol_util.get_protocol_by_file() protocol_util._detect_wire_protocol.assert_any_call() protocol_util._detect_metadata_protocol.assert_not_called() #Test tag file exists - protocol_util.protocol = None + protocol_util.clear_protocol() protocol_util._detect_wire_protocol.reset_mock() protocol_util._detect_metadata_protocol.reset_mock() with open(tag_file, "w+") as tag_fd: tag_fd.write("") - protocol_util.detect_protocol_by_file() + protocol_util.get_protocol_by_file() protocol_util._detect_metadata_protocol.assert_any_call() protocol_util._detect_wire_protocol.assert_not_called() diff --git a/tests/protocol/test_restapi.py b/tests/protocol/test_restapi.py index 656ecc6..e4b65c9 100644 --- a/tests/protocol/test_restapi.py +++ b/tests/protocol/test_restapi.py @@ -14,9 +14,6 @@ # # Requires Python 2.4+ and Openssl 1.0+ # -# Implements parts of RFC 2131, 1541, 1497 and -# http://msdn.microsoft.com/en-us/library/cc227282%28PROT.10%29.aspx -# http://msdn.microsoft.com/en-us/library/cc227259%28PROT.13%29.aspx from tests.tools import * import uuid @@ -24,7 +21,7 @@ import unittest import os import shutil import time -from azurelinuxagent.protocol.restapi import * +from azurelinuxagent.common.protocol.restapi import * class SampleDataContract(DataContract): def __init__(self): diff --git a/tests/protocol/test_wire.py b/tests/protocol/test_wire.py index 4c38c13..bd3acaf 100644 --- a/tests/protocol/test_wire.py +++ b/tests/protocol/test_wire.py @@ -14,9 +14,6 @@ # # Requires Python 2.4+ and Openssl 1.0+ # -# Implements parts of RFC 2131, 1541, 1497 and -# http://msdn.microsoft.com/en-us/library/cc227282%28PROT.10%29.aspx -# http://msdn.microsoft.com/en-us/library/cc227259%28PROT.13%29.aspx from tests.tools import * from tests.protocol.mockwiredata import * @@ -24,18 +21,18 @@ import uuid import unittest import os import time -from azurelinuxagent.utils.restutil import httpclient -from azurelinuxagent.utils.cryptutil import CryptUtil -from azurelinuxagent.protocol.restapi import * -from azurelinuxagent.protocol.wire import WireClient, WireProtocol, \ +from azurelinuxagent.common.utils.restutil import httpclient +from azurelinuxagent.common.utils.cryptutil import CryptUtil +from azurelinuxagent.common.protocol.restapi import * +from azurelinuxagent.common.protocol.wire import WireClient, WireProtocol, \ TRANSPORT_PRV_FILE_NAME, \ TRANSPORT_CERT_FILE_NAME data_with_bom = b'\xef\xbb\xbfhehe' @patch("time.sleep") -@patch("azurelinuxagent.protocol.wire.CryptUtil") -@patch("azurelinuxagent.protocol.wire.restutil") +@patch("azurelinuxagent.common.protocol.wire.CryptUtil") +@patch("azurelinuxagent.common.protocol.wire.restutil") class TestWireProtocolGetters(AgentTestCase): def _test_getters(self, test_data, mock_restutil, MockCryptUtil, _): diff --git a/tests/test_import.py b/tests/test_import.py new file mode 100644 index 0000000..0412411 --- /dev/null +++ b/tests/test_import.py @@ -0,0 +1,26 @@ +from tests.tools import * +import azurelinuxagent.common.osutil as osutil +import azurelinuxagent.common.dhcp as dhcp +import azurelinuxagent.common.protocol as protocol +import azurelinuxagent.pa.provision as provision +import azurelinuxagent.pa.deprovision as deprovision +import azurelinuxagent.daemon as daemon +import azurelinuxagent.daemon.resourcedisk as resourcedisk +import azurelinuxagent.daemon.scvmm as scvmm +import azurelinuxagent.daemon.monitor as monitor +import azurelinuxagent.ga.update as update +import azurelinuxagent.ga.exthandlers as exthandlers + +class TestImportHandler(AgentTestCase): + def test_get_handler(self): + osutil.get_osutil() + protocol.get_protocol_util() + dhcp.get_dhcp_handler() + provision.get_provision_handler() + deprovision.get_deprovision_handler() + daemon.get_daemon_handler() + resourcedisk.get_resourcedisk_handler() + scvmm.get_scvmm_handler() + monitor.get_monitor_handler() + update.get_update_handler() + exthandlers.get_exthandlers_handler() diff --git a/tests/tools.py b/tests/tools.py index 672c60b..aef641e 100644 --- a/tests/tools.py +++ b/tests/tools.py @@ -14,9 +14,6 @@ # # Requires Python 2.4+ and Openssl 1.0+ # -# Implements parts of RFC 2131, 1541, 1497 and -# http://msdn.microsoft.com/en-us/library/cc227282%28PROT.10%29.aspx -# http://msdn.microsoft.com/en-us/library/cc227259%28PROT.13%29.aspx """ Define util functions for unit test @@ -30,9 +27,9 @@ import shutil import json import tempfile from functools import wraps -import azurelinuxagent.conf as conf -import azurelinuxagent.logger as logger -import azurelinuxagent.event as event +import azurelinuxagent.common.conf as conf +import azurelinuxagent.common.logger as logger +import azurelinuxagent.common.event as event #Import mock module for Python2 and Python3 try: diff --git a/tests/utils/__init__.py b/tests/utils/__init__.py index 9bdb27e..2ef4c16 100644 --- a/tests/utils/__init__.py +++ b/tests/utils/__init__.py @@ -14,6 +14,3 @@ # # Requires Python 2.4+ and Openssl 1.0+ # -# Implements parts of RFC 2131, 1541, 1497 and -# http://msdn.microsoft.com/en-us/library/cc227282%28PROT.10%29.aspx -# http://msdn.microsoft.com/en-us/library/cc227259%28PROT.13%29.aspx diff --git a/tests/utils/test_file_util.py b/tests/utils/test_file_util.py index bf7c638..9a5479e 100644 --- a/tests/utils/test_file_util.py +++ b/tests/utils/test_file_util.py @@ -14,17 +14,14 @@ # # Requires Python 2.4+ and Openssl 1.0+ # -# Implements parts of RFC 2131, 1541, 1497 and -# http://msdn.microsoft.com/en-us/library/cc227282%28PROT.10%29.aspx -# http://msdn.microsoft.com/en-us/library/cc227259%28PROT.13%29.aspx from tests.tools import * import uuid import unittest import os import sys -from azurelinuxagent.future import ustr -import azurelinuxagent.utils.fileutil as fileutil +from azurelinuxagent.common.future import ustr +import azurelinuxagent.common.utils.fileutil as fileutil class TestFileOperations(AgentTestCase): def test_read_write_file(self): diff --git a/tests/utils/test_rest_util.py b/tests/utils/test_rest_util.py index bd22c55..874e527 100644 --- a/tests/utils/test_rest_util.py +++ b/tests/utils/test_rest_util.py @@ -14,17 +14,14 @@ # # Requires Python 2.4+ and Openssl 1.0+ # -# Implements parts of RFC 2131, 1541, 1497 and -# http://msdn.microsoft.com/en-us/library/cc227282%28PROT.10%29.aspx -# http://msdn.microsoft.com/en-us/library/cc227259%28PROT.13%29.aspx from tests.tools import AgentTestCase, patch, Mock, MagicMock import uuid import unittest import os -import azurelinuxagent.utils.restutil as restutil -from azurelinuxagent.future import ustr, httpclient -import azurelinuxagent.logger as logger +import azurelinuxagent.common.utils.restutil as restutil +from azurelinuxagent.common.future import ustr, httpclient +import azurelinuxagent.common.logger as logger class TestHttpOperations(AgentTestCase): @@ -57,8 +54,8 @@ class TestHttpOperations(AgentTestCase): self.assertEquals(rel_uri, "None") - @patch("azurelinuxagent.future.httpclient.HTTPSConnection") - @patch("azurelinuxagent.future.httpclient.HTTPConnection") + @patch("azurelinuxagent.common.future.httpclient.HTTPSConnection") + @patch("azurelinuxagent.common.future.httpclient.HTTPConnection") def test_http_request(self, HTTPConnection, HTTPSConnection): mock_httpconn = MagicMock() mock_httpresp = MagicMock() @@ -98,7 +95,7 @@ class TestHttpOperations(AgentTestCase): self.assertEquals("_(:3| <)_", resp.read()) @patch("time.sleep") - @patch("azurelinuxagent.utils.restutil._http_request") + @patch("azurelinuxagent.common.utils.restutil._http_request") def test_http_request_with_retry(self, _http_request, sleep): mock_httpresp = MagicMock() mock_httpresp.read = Mock(return_value="hehe") diff --git a/tests/utils/test_shell_util.py b/tests/utils/test_shell_util.py index aa89121..156a50f 100644 --- a/tests/utils/test_shell_util.py +++ b/tests/utils/test_shell_util.py @@ -15,15 +15,12 @@ # # Requires Python 2.4+ and Openssl 1.0+ # -# Implements parts of RFC 2131, 1541, 1497 and -# http://msdn.microsoft.com/en-us/library/cc227282%28PROT.10%29.aspx -# http://msdn.microsoft.com/en-us/library/cc227259%28PROT.13%29.aspx from tests.tools import * import uuid import unittest import os -import azurelinuxagent.utils.shellutil as shellutil +import azurelinuxagent.common.utils.shellutil as shellutil import test class TestrunCmd(AgentTestCase): diff --git a/tests/utils/test_text_util.py b/tests/utils/test_text_util.py index 0e8cc7d..9ac0707 100644 --- a/tests/utils/test_text_util.py +++ b/tests/utils/test_text_util.py @@ -14,17 +14,14 @@ # # Requires Python 2.4+ and Openssl 1.0+ # -# Implements parts of RFC 2131, 1541, 1497 and -# http://msdn.microsoft.com/en-us/library/cc227282%28PROT.10%29.aspx -# http://msdn.microsoft.com/en-us/library/cc227259%28PROT.13%29.aspx from tests.tools import * import uuid import unittest import os -from azurelinuxagent.future import ustr -import azurelinuxagent.utils.textutil as textutil -from azurelinuxagent.utils.textutil import Version +from azurelinuxagent.common.future import ustr +import azurelinuxagent.common.utils.textutil as textutil +from azurelinuxagent.common.utils.textutil import Version class TestTextUtil(AgentTestCase): def test_get_password_hash(self):