diff --git a/azurelinuxagent/agent.py b/azurelinuxagent/agent.py index 849a192..5785268 100644 --- a/azurelinuxagent/agent.py +++ b/azurelinuxagent/agent.py @@ -29,27 +29,60 @@ from azurelinuxagent.metadata import AGENT_NAME, AGENT_LONG_VERSION, \ DISTRO_NAME, DISTRO_VERSION, \ PY_VERSION_MAJOR, PY_VERSION_MINOR, \ PY_VERSION_MICRO -from azurelinuxagent.utils.osutil import OSUTIL -from azurelinuxagent.handler import HANDLERS +from azurelinuxagent.distro.loader import get_distro -def init(verbose): - """ - Initialize agent running environment. - """ - HANDLERS.init_handler.init(verbose) +class Agent(object): + def __init__(self, verbose): + """ + Initialize agent running environment. + """ + self.distro = get_distro(); + self.distro.init_handler.run(verbose) -def run(): - """ - Run agent daemon - """ - HANDLERS.main_handler.run() + def daemon(self): + """ + Run agent daemon + """ + self.distro.daemon_handler.run() -def deprovision(force=False, deluser=False): + def deprovision(self, force=False, deluser=False): + """ + Run deprovision command + """ + self.distro.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() + print("Start {0} service".format(AGENT_NAME)) + self.distro.osutil.start_agent_service() + +def main(args): """ - Run deprovision command + Parse command line arguments, exit with usage() on error. + Invoke different methods according to different command """ - HANDLERS.deprovision_handler.deprovision(force=force, deluser=deluser) + command, force, verbose = parse_args(args) + if command == "version": + version() + elif command == "help": + usage() + 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() def parse_args(sys_args): """ @@ -108,34 +141,3 @@ def start(): devnull = open(os.devnull, 'w') subprocess.Popen([sys.argv[0], '-daemon'], stdout=devnull, stderr=devnull) -def register_service(): - """ - Register agent as a service - """ - print("Register {0} service".format(AGENT_NAME)) - OSUTIL.register_agent_service() - print("Start {0} service".format(AGENT_NAME)) - OSUTIL.start_agent_service() - -def main(): - """ - Parse command line arguments, exit with usage() on error. - Invoke different methods according to different command - """ - command, force, verbose = parse_args(sys.argv[1:]) - if command == "version": - version() - elif command == "help": - usage() - else: - init(verbose) - if command == "deprovision+user": - deprovision(force, deluser=True) - elif command == "deprovision": - deprovision(force, deluser=False) - elif command == "start": - start() - elif command == "register-service": - register_service() - elif command == "daemon": - run() diff --git a/azurelinuxagent/conf.py b/azurelinuxagent/conf.py index 2b0eb01..416d9fd 100644 --- a/azurelinuxagent/conf.py +++ b/azurelinuxagent/conf.py @@ -43,11 +43,11 @@ class ConfigurationProvider(object): else: self.values[parts[0]] = None - def get(self, key, default_val=None): + def get(self, key, default_val): val = self.values.get(key) return val if val is not None else default_val - def get_switch(self, key, default_val=False): + def get_switch(self, key, default_val): val = self.values.get(key) if val is not None and val.lower() == 'y': return True @@ -55,7 +55,7 @@ class ConfigurationProvider(object): return False return default_val - def get_int(self, key, default_val=-1): + def get_int(self, key, default_val): try: return int(self.values.get(key)) except TypeError: @@ -64,9 +64,9 @@ class ConfigurationProvider(object): return default_val -__config__ = ConfigurationProvider() +__conf__ = ConfigurationProvider() -def load_conf(conf_file_path, conf=__config__): +def load_conf_from_file(conf_file_path, conf=__conf__): """ Load conf file from: conf_file_path """ @@ -80,30 +80,75 @@ def load_conf(conf_file_path, conf=__config__): raise AgentConfigError(("Failed to load conf file:{0}, {1}" "").format(conf_file_path, err)) -def get(key, default_val=None, conf=__config__): - """ - Get option value by key, return default_val if not found - """ - if conf is not None: - return conf.get(key, default_val) - else: - return default_val +def get_lib_dir(conf=__conf__): + return conf.get("Lib.Dir", "/var/lib/waagent") -def get_switch(key, default_val=None, conf=__config__): - """ - Get bool option value by key, return default_val if not found - """ - if conf is not None: - return conf.get_switch(key, default_val) - else: - return default_val +def get_dvd_mount_point(conf=__conf__): + return conf.get("DVD.MountPoint", "/mnt/cdrom/secure") -def get_int(key, default_val=None, conf=__config__): - """ - Get int option value by key, return default_val if not found - """ - if conf is not None: - return conf.get_int(key, default_val) - else: - return default_val +def get_agent_pid_file_path(conf=__conf__): + return conf.get("Pid.File", "/mnt/cdrom/secure") + +def get_ext_log_dir(conf=__conf__): + return conf.get("Extension.LogDir", "/var/log/azure") + +def get_openssl_cmd(conf=__conf__): + return conf.get("OS.OpensslPath", "/usr/bin/openssl") + +def get_home_dir(conf=__conf__): + return conf.get("OS.HomeDir", "/home") + +def get_passwd_file_path(conf=__conf__): + return conf.get("OS.PasswordPath", "/etc/shadow") + +def get_sshd_conf_file_path(conf=__conf__): + return conf.get("OS.SshdConfigPath", "/etc/ssh/sshd_config") + +def get_root_device_scsi_timeout(conf=__conf__): + return conf.get_int("OS.RootDeviceScsiTimeout", None) + +def get_ssh_host_keypair_type(conf=__conf__): + return conf.get("Provisioning.SshHostKeyPairType", "rsa") + +def get_provision_enabled(conf=__conf__): + return conf.get_switch("Provisioning.Enabled", True) + +def get_regenerate_ssh_host_key(conf=__conf__): + return conf.get_switch("Provisioning.RegenerateSshHostKeyPair", False) + +def get_delete_root_password(conf=__conf__): + return conf.get_switch("Provisioning.DeleteRootPassword", False) + +def get_password_cryptid(conf=__conf__): + return conf.get("Provisioning.PasswordCryptId", "6") + +def get_password_crypt_salt_len(conf=__conf__): + return conf.get_int("Provisioning.PasswordCryptSaltLength", 10) + +def get_monitor_hostname(conf=__conf__): + return conf.get_switch("Provisioning.MonitorHostName", False) + +def get_httpproxy_host(conf=__conf__): + return conf.get("HttpProxy.Host", None) + +def get_httpproxy_port(conf=__conf__): + return conf.get("HttpProxy.Port", None) + +def get_detect_scvmm_env(conf=__conf__): + return conf.get_switch("DetectScvmmEnv", False) + +def get_resourcedisk_format(conf=__conf__): + return conf.get_switch("ResourceDisk.Format", False) + +def get_resourcedisk_enable_swap(conf=__conf__): + return conf.get_switch("ResourceDisk.EnableSwap", False) + +def get_resourcedisk_mountpoint(conf=__conf__): + return conf.get("ResourceDisk.MountPoint", "/mnt/resource") + +def get_resourcedisk_filesystem(conf=__conf__): + return conf.get("ResourceDisk.MountPoint", "/mnt/resource") + +def get_resourcedisk_swap_size_mb(conf=__conf__): + return conf.get_int("ResourceDisk.SwapSizeMB", 0) diff --git a/azurelinuxagent/distro/centos/__init__.py b/azurelinuxagent/distro/centos/__init__.py deleted file mode 100644 index d9b82f5..0000000 --- a/azurelinuxagent/distro/centos/__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/centos/loader.py b/azurelinuxagent/distro/centos/loader.py deleted file mode 100644 index 9dc428f..0000000 --- a/azurelinuxagent/distro/centos/loader.py +++ /dev/null @@ -1,25 +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.metadata import DISTRO_NAME, DISTRO_VERSION -import azurelinuxagent.distro.redhat.loader as redhat - -def get_osutil(): - return redhat.get_osutil() - diff --git a/azurelinuxagent/distro/coreos/deprovision.py b/azurelinuxagent/distro/coreos/deprovision.py index 1dc5218..9642579 100644 --- a/azurelinuxagent/distro/coreos/deprovision.py +++ b/azurelinuxagent/distro/coreos/deprovision.py @@ -21,8 +21,8 @@ import azurelinuxagent.utils.fileutil as fileutil from azurelinuxagent.distro.default.deprovision import DeprovisionHandler, DeprovisionAction class CoreOSDeprovisionHandler(DeprovisionHandler): - def __init__(self, handlers): - self.handlers = handlers + def __init__(self, distro): + self.distro = distro def setup(self, deluser): warnings, actions = super(CoreOSDeprovisionHandler, self).setup(deluser) diff --git a/azurelinuxagent/distro/coreos/handlerFactory.py b/azurelinuxagent/distro/coreos/distro.py similarity index 70% rename from azurelinuxagent/distro/coreos/handlerFactory.py rename to azurelinuxagent/distro/coreos/distro.py index 01a2afb..04c7bff 100644 --- a/azurelinuxagent/distro/coreos/handlerFactory.py +++ b/azurelinuxagent/distro/coreos/distro.py @@ -17,11 +17,13 @@ # Requires Python 2.4+ and Openssl 1.0+ # -from .deprovision import CoreOSDeprovisionHandler -from azurelinuxagent.distro.default.handlerFactory import DefaultHandlerFactory +from azurelinuxagent.distro.default.distro import DefaultDistro +from azurelinuxagent.distro.coreos.osutil import CoreOSUtil +from azurelinuxagent.distro.coreos.deprovision import CoreOSDeprovisionHandler -class CoreOSHandlerFactory(DefaultHandlerFactory): +class CoreOSDistro(DefaultDistro): def __init__(self): - super(CoreOSHandlerFactory, self).__init__() + super(CoreOSDistro, self).__init__() + self.osutil = CoreOSUtil() self.deprovision_handler = CoreOSDeprovisionHandler(self) diff --git a/azurelinuxagent/distro/coreos/loader.py b/azurelinuxagent/distro/coreos/loader.py deleted file mode 100644 index 802f276..0000000 --- a/azurelinuxagent/distro/coreos/loader.py +++ /dev/null @@ -1,28 +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+ -# - - -def get_osutil(): - from azurelinuxagent.distro.coreos.osutil import CoreOSUtil - return CoreOSUtil() - -def get_handlers(): - from azurelinuxagent.distro.coreos.handlerFactory import CoreOSHandlerFactory - return CoreOSHandlerFactory() - diff --git a/azurelinuxagent/distro/coreos/osutil.py b/azurelinuxagent/distro/coreos/osutil.py index c244311..c830817 100644 --- a/azurelinuxagent/distro/coreos/osutil.py +++ b/azurelinuxagent/distro/coreos/osutil.py @@ -35,9 +35,9 @@ from azurelinuxagent.distro.default.osutil import DefaultOSUtil class CoreOSUtil(DefaultOSUtil): def __init__(self): super(CoreOSUtil, self).__init__() + self.agent_conf_file_path = '/usr/share/oem/waagent.conf' self.waagent_path='/usr/share/oem/bin/waagent' self.python_path='/usr/share/oem/python/bin' - self.conf_file_path = '/usr/share/oem/waagent.conf' if 'PATH' in os.environ: path = "{0}:{1}".format(os.environ['PATH'], self.python_path) else: diff --git a/azurelinuxagent/distro/oracle/loader.py b/azurelinuxagent/distro/debian/distro.py similarity index 71% rename from azurelinuxagent/distro/oracle/loader.py rename to azurelinuxagent/distro/debian/distro.py index 9dc428f..01f4e3e 100644 --- a/azurelinuxagent/distro/oracle/loader.py +++ b/azurelinuxagent/distro/debian/distro.py @@ -17,9 +17,11 @@ # Requires Python 2.4+ and Openssl 1.0+ # -from azurelinuxagent.metadata import DISTRO_NAME, DISTRO_VERSION -import azurelinuxagent.distro.redhat.loader as redhat +from azurelinuxagent.distro.default.distro import DefaultDistro +from azurelinuxagent.distro.debian.osutil import DebianOSUtil -def get_osutil(): - return redhat.get_osutil() +class DebianDistro(DefaultDistro): + def __init__(self): + super(DebianDistro, self).__init__() + self.osutil = DebianOSUtil() diff --git a/azurelinuxagent/distro/default/run.py b/azurelinuxagent/distro/default/daemon.py similarity index 59% rename from azurelinuxagent/distro/default/run.py rename to azurelinuxagent/distro/default/daemon.py index cbb47c6..b8b2321 100644 --- a/azurelinuxagent/distro/default/run.py +++ b/azurelinuxagent/distro/default/daemon.py @@ -20,51 +20,57 @@ import os import time import sys +import azurelinuxagent.conf as conf import azurelinuxagent.logger as logger from azurelinuxagent.future import text -import azurelinuxagent.conf as conf +from azurelinuxagent.exception import ProtocolError from azurelinuxagent.metadata import AGENT_LONG_NAME, AGENT_VERSION, \ DISTRO_NAME, DISTRO_VERSION, \ DISTRO_FULL_NAME, PY_VERSION_MAJOR, \ PY_VERSION_MINOR, PY_VERSION_MICRO import azurelinuxagent.event as event -import azurelinuxagent.protocol.dhcp as dhcp -from azurelinuxagent.protocol.factory import PROT_FACTORY -from azurelinuxagent.utils.osutil import OSUTIL import azurelinuxagent.utils.fileutil as fileutil -class MainHandler(object): - def __init__(self, handlers): - self.handlers = handlers +class DaemonHandler(object): + def __init__(self, distro): + self.distro = distro def run(self): logger.info("{0} Version:{1}", AGENT_LONG_NAME, AGENT_VERSION) logger.info("OS: {0} {1}", DISTRO_NAME, DISTRO_VERSION) logger.info("Python: {0}.{1}.{2}", PY_VERSION_MAJOR, PY_VERSION_MINOR, PY_VERSION_MICRO) + + #Create lib dir + if not os.path.isdir(self.distro.conf.get_lib_dir()): + fileutil.mkdir(self.distro.conf.get_lib_dir(), mode=0o700) + os.chdir(self.distro.conf.get_lib_dir()) - event.enable_unhandled_err_dump(AGENT_LONG_NAME) - fileutil.write_file(OSUTIL.get_agent_pid_file_path(), text(os.getpid())) + #TODO check running daemon + fileutil.write_file(self.distro.osutil.get_agent_pid_file_path(), + text(os.getpid())) - if conf.get_switch("DetectScvmmEnv", False): - if self.handlers.scvmm_handler.detect_scvmm_env(): + if conf.get_detect_scvmm_env(): + if self.distro.scvmm_handler.run(): return + + self.distro.provision_handler.run() - PROT_FACTORY.wait_for_network() + if conf.get_resourcedisk_format(): + self.distro.resource_disk_handler.run() - self.handlers.provision_handler.process() + try: + protocol = self.distro.protocol_util.detect_protocol() + except ProtocolError as e: + logger.error("Failed to detect protocol, exit", e) + return + + self.distro.event_handler.run() + self.distro.env_handler.run() - PROT_FACTORY.detect_protocol() - - if conf.get_switch("ResourceDisk.Format", False): - self.handlers.resource_disk_handler.start_activate_resource_disk() - - event.EventMonitor().start() - self.handlers.env_handler.start() - while True: #Handle extensions - self.handlers.ext_handlers_handler.process() + self.distro.ext_distro_handler.run() time.sleep(25) diff --git a/azurelinuxagent/distro/default/deprovision.py b/azurelinuxagent/distro/default/deprovision.py index ab3e9ac..4db4cdc 100644 --- a/azurelinuxagent/distro/default/deprovision.py +++ b/azurelinuxagent/distro/default/deprovision.py @@ -18,10 +18,8 @@ # import azurelinuxagent.conf as conf -from azurelinuxagent.utils.osutil import OSUTIL +from azurelinuxagent.exception import ProtocolError from azurelinuxagent.future import read_input -import azurelinuxagent.protocol as prot -import azurelinuxagent.protocol.ovfenv as ovf import azurelinuxagent.utils.fileutil as fileutil import azurelinuxagent.utils.shellutil as shellutil @@ -35,20 +33,20 @@ class DeprovisionAction(object): self.func(*self.args, **self.kwargs) class DeprovisionHandler(object): - def __init__(self, handlers): - self.handlers = handlers + def __init__(self, distro): + self.distro = distro 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(OSUTIL.del_root_password)) + actions.append(DeprovisionAction(self.distro.osutil.del_root_password)) def del_user(self, warnings, actions): try: - ovfenv = ovf.get_ovf_env() - except prot.ProtocolError: + ovfenv = self.distro.protocol_util.get_ovf_env() + except ProtocolError: warnings.append("WARNING! ovf-env.xml is not found.") warnings.append("WARNING! Skip delete user.") return @@ -56,7 +54,8 @@ class DeprovisionHandler(object): username = ovfenv.username warnings.append(("WARNING! {0} account and entire home directory " "will be deleted.").format(username)) - actions.append(DeprovisionAction(OSUTIL.del_account, [username])) + actions.append(DeprovisionAction(self.distro.osutil.del_account, + [username])) def regen_ssh_host_key(self, warnings, actions): @@ -66,7 +65,7 @@ class DeprovisionHandler(object): def stop_agent_service(self, warnings, actions): warnings.append("WARNING! The waagent service will be stopped.") - actions.append(DeprovisionAction(OSUTIL.stop_agent_service)) + actions.append(DeprovisionAction(self.distro.osutil.stop_agent_service)) def del_files(self, warnings, actions): files_to_del = ['/root/.bash_history', '/var/log/waagent.log'] @@ -78,26 +77,28 @@ class DeprovisionHandler(object): actions.append(DeprovisionAction(fileutil.rm_dirs, dirs_to_del)) def del_lib_dir(self, warnings, actions): - dirs_to_del = [OSUTIL.get_lib_dir()] + dirs_to_del = [conf.get_lib_dir()] actions.append(DeprovisionAction(fileutil.rm_dirs, dirs_to_del)) def reset_hostname(self, warnings, actions): localhost = ["localhost.localdomain"] - actions.append(DeprovisionAction(OSUTIL.set_hostname, localhost)) - actions.append(DeprovisionAction(OSUTIL.set_dhcp_hostname, localhost)) + actions.append(DeprovisionAction(self.distro.osutil.set_hostname, + localhost)) + actions.append(DeprovisionAction(self.distro.osutil.set_dhcp_hostname, + localhost)) def setup(self, deluser): warnings = [] actions = [] self.stop_agent_service(warnings, actions) - if conf.get_switch("Provisioning.RegenerateSshHostkey", False): + if conf.get_regenerate_ssh_host_key(): self.regen_ssh_host_key(warnings, actions) self.del_dhcp_lease(warnings, actions) self.reset_hostname(warnings, actions) - if conf.get_switch("Provisioning.DeleteRootPassword", False): + if conf.get_delete_root_password(): self.del_root_password(warnings, actions) self.del_lib_dir(warnings, actions) @@ -108,7 +109,7 @@ class DeprovisionHandler(object): return warnings, actions - def deprovision(self, force=False, deluser=False): + def run(self, force=False, deluser=False): warnings, actions = self.setup(deluser) for warning in warnings: print(warning) diff --git a/azurelinuxagent/protocol/dhcp.py b/azurelinuxagent/distro/default/dhcp.py similarity index 71% rename from azurelinuxagent/protocol/dhcp.py rename to azurelinuxagent/distro/default/dhcp.py index b544389..19ad0b4 100644 --- a/azurelinuxagent/protocol/dhcp.py +++ b/azurelinuxagent/distro/default/dhcp.py @@ -21,20 +21,49 @@ import array import time import threading import azurelinuxagent.logger as logger -from azurelinuxagent.utils.osutil import OSUTIL +import azurelinuxagent.conf as conf import azurelinuxagent.utils.fileutil as fileutil import azurelinuxagent.utils.shellutil as shellutil -from azurelinuxagent.utils.textutil import * -from azurelinuxagent.protocol.common import ProtocolError +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 -DHCP_FILE_NAME = "DHCP" - -class DhcpResponse(object): - def __init__(self, resp): - endpoint, gateway, routes = parse_dhcp_resp(resp) - self.endpoint = endpoint - self.gateway = gateway - self.routes = routes + +class DhcpHandler(object): + """ + Azure use DHCP option 245 to pass endpoint ip to VMs. + """ + def __init__(self, distro): + self.distro = distro + self.endpoint = None + self.gateway = None + self.route = None + + def run(self): + """ + Send dhcp request + Configure default gateway and routes + Save wire server endpoint if found + """ + self.send_dhcp_req() + self.conf_routes() + if self.endpoint is not None: + self.distro.osutil.set_wireserver_endpoint(self.endpoint) + + def wait_for_network(self): + """ + Wait for network stack to be initialized. + """ + ipv4 = self.distro.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() def conf_routes(self): logger.info("Configure routes") @@ -42,56 +71,56 @@ class DhcpResponse(object): logger.info("Routes:{0}", self.routes) #Add default gateway if self.gateway is not None: - OSUTIL.route_add(0 , 0, self.gateway) + self.distro.osutil.route_add(0 , 0, self.gateway) if self.routes is not None: for route in self.routes: - OSUTIL.route_add(route[0], route[1], route[2]) + self.distro.osutil.route_add(route[0], route[1], route[2]) -def _load_dhcp_resp(): - dhcp_file_path = os.path.join(OSUTIL.get_lib_dir(), DHCP_FILE_NAME) - resp = fileutil.read_file(dhcp_file_path, asbin=True) - return DhcpResponse(resp) + 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() + response = socket_send(request) + validate_dhcp_resp(request, response) + return response + except DhcpError as e: + logger.warn("Failed to send DHCP request: {0}", e) + time.sleep(duration) + return None -def _fetch_dhcp_resp(): - logger.info("Send dhcp request") - mac_addr = OSUTIL.get_mac_addr() - req = build_dhcp_request(mac_addr) - resp = send_dhcp_request(req) - if resp is None: - raise ProtocolError("Failed to receive dhcp response.") - dhcp_file_path = os.path.join(OSUTIL.get_lib_dir(), DHCP_FILE_NAME) - try: - fileutil.write_file(dhcp_file_path, resp, asbin=True) - except IOError as e: - logger.warn("Failed to save dhcp response: {0}", e) - return DhcpResponse(resp) + def send_dhcp_req(self): + """ + Build dhcp request with mac addr + Configure route to allow dhcp traffic + Stop dhcp service if necessary + """ + logger.info("Send dhcp request") + mac_addr = self.distro.osutil.get_mac_addr() + req = build_dhcp_request(mac_addr) -class DhcpClient(object): - def __init__(self): - self._resp = None - self._lock = threading.Lock() + # 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() + if missing_default_route: + self.distro.osutil.set_route_for_dhcp_broadcast(ifname) - def get_dhcp_resp(self): - self._lock.acquire() - try: - if self._resp is None: - try: - self._resp = _load_dhcp_resp() - except IOError: - self._resp = _fetch_dhcp_resp() - return self._resp - finally: - self._lock.release() + # 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() - def fetch_dhcp_resp(self): - self._lock.acquire() - try: - self._resp = _fetch_dhcp_resp() - return self._resp - finally: - self._lock.release() + resp = self._send_dhcp_req(req) + + if self.distro.osutil.is_dhcp_enabled(): + self.distro.osutil.start_dhcp_service() -DHCPCLIENT = DhcpClient() + if missing_default_route: + self.distro.osutil.remove_route_for_dhcp_broadcast(ifname) + + if resp is None: + raise DhcpError("Failed to receive dhcp response.") + self.endpoint, self.gateway, self.routes = parse_dhcp_resp(resp) def validate_dhcp_resp(request, response): bytes_recv = len(response) @@ -110,20 +139,20 @@ def validate_dhcp_resp(request, response): logger.verb("Cookie not match:\nsend={0},\nreceive={1}", hex_dump3(request, 0xEC, 4), hex_dump3(response, 0xEC, 4)) - raise ProtocolError("Cookie in dhcp respones doesn't match the request") + raise DhcpError("Cookie in dhcp respones doesn't match the request") if not compare_bytes(request, response, 4, 4): logger.verb("TransactionID not match:\nsend={0},\nreceive={1}", hex_dump3(request, 4, 4), hex_dump3(response, 4, 4)) - raise ProtocolError("TransactionID in dhcp respones " + raise DhcpError("TransactionID in dhcp respones " "doesn't match the request") if not compare_bytes(request, response, 0x1C, 6): logger.verb("Mac Address not match:\nsend={0},\nreceive={1}", hex_dump3(request, 0x1C, 6), hex_dump3(response, 0x1C, 6)) - raise ProtocolError("Mac Addr in dhcp respones " + raise DhcpError("Mac Addr in dhcp respones " "doesn't match the request") def parse_route(response, option, i, length, bytes_recv): @@ -203,53 +232,6 @@ def parse_dhcp_resp(response): i += length + 2 return endpoint, gateway, routes - -def allow_dhcp_broadcast(func): - """ - Temporary allow broadcase for dhcp. Remove the route when done. - """ - def wrapper(*args, **kwargs): - missing_default_route = OSUTIL.is_missing_default_route() - ifname = OSUTIL.get_if_name() - if missing_default_route: - OSUTIL.set_route_for_dhcp_broadcast(ifname) - result = func(*args, **kwargs) - if missing_default_route: - OSUTIL.remove_route_for_dhcp_broadcast(ifname) - return result - return wrapper - -def disable_dhcp_service(func): - """ - In some distros, dhcp service needs to be shutdown before agent probe - endpoint through dhcp. - """ - def wrapper(*args, **kwargs): - if OSUTIL.is_dhcp_enabled(): - OSUTIL.stop_dhcp_service() - result = func(*args, **kwargs) - OSUTIL.start_dhcp_service() - return result - else: - return func(*args, **kwargs) - return wrapper - - -@allow_dhcp_broadcast -@disable_dhcp_service -def send_dhcp_request(request): - __waiting_duration__ = [0, 10, 30, 60, 60] - for duration in __waiting_duration__: - try: - OSUTIL.allow_dhcp_broadcast() - response = socket_send(request) - validate_dhcp_resp(request, response) - return response - except ProtocolError as e: - logger.warn("Failed to send DHCP request: {0}", e) - time.sleep(duration) - return None - def socket_send(request): sock = None try: @@ -265,7 +247,7 @@ def socket_send(request): response = sock.recv(1024) return response except IOError as e: - raise ProtocolError("{0}".format(e)) + raise DhcpError("{0}".format(e)) finally: if sock is not None: sock.close() diff --git a/azurelinuxagent/distro/default/handlerFactory.py b/azurelinuxagent/distro/default/distro.py similarity index 70% rename from azurelinuxagent/distro/default/handlerFactory.py rename to azurelinuxagent/distro/default/distro.py index 55a48f1..881373a 100644 --- a/azurelinuxagent/distro/default/handlerFactory.py +++ b/azurelinuxagent/distro/default/distro.py @@ -16,8 +16,14 @@ # # 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.run import MainHandler +from azurelinuxagent.distro.default.event import EventHandler +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 @@ -25,10 +31,17 @@ from azurelinuxagent.distro.default.resourceDisk import ResourceDiskHandler from azurelinuxagent.distro.default.extension import ExtHandlersHandler from azurelinuxagent.distro.default.deprovision import DeprovisionHandler -class DefaultHandlerFactory(object): +class DefaultDistro(object): + """ + """ def __init__(self): + self.osutil = DefaultOSUtil() + self.protocol_util = ProtocolUtil(self) + self.init_handler = InitHandler(self) - self.main_handler = MainHandler(self) + self.daemon_handler = DaemonHandler(self) + self.event_handler = EventHandler(self) + self.dhcp_handler = DhcpHandler(self) self.scvmm_handler = ScvmmHandler(self) self.env_handler = EnvHandler(self) self.provision_handler = ProvisionHandler(self) diff --git a/azurelinuxagent/distro/default/env.py b/azurelinuxagent/distro/default/env.py index 7f3ce06..094cc2a 100644 --- a/azurelinuxagent/distro/default/env.py +++ b/azurelinuxagent/distro/default/env.py @@ -23,8 +23,6 @@ import threading import time import azurelinuxagent.logger as logger import azurelinuxagent.conf as conf -import azurelinuxagent.protocol.dhcp as dhcp -from azurelinuxagent.utils.osutil import OSUTIL class EnvHandler(object): """ @@ -32,37 +30,25 @@ class EnvHandler(object): If dhcp clinet process re-start has occurred, reset routes, dhcp with fabric. Monitor scsi disk. - If new scsi disk found, set + If new scsi disk found, set timeout """ - def __init__(self, handlers): - self.handlers = handlers - self.monitor = EnvMonitor() - - def start(self): - self.monitor.start() - - def stop(self): - self.monitor.stop() - -class EnvMonitor(object): - - def __init__(self): + def __init__(self, distro): + self.distro = distro self.stopped = True self.hostname = None self.dhcpid = None self.server_thread=None - def start(self): - self.dhcp_resp = dhcp.DHCPCLIENT.get_dhcp_resp() + def run(self): if not self.stopped: logger.info("Stop existing env monitor service.") self.stop() self.stopped = False logger.info("Start env monitor service.") - self.dhcp_resp.conf_routes() + self.distro.dhcp_handler.conf_routes() self.hostname = socket.gethostname() - self.dhcpid = OSUTIL.get_dhcp_pid() + self.dhcpid = self.distro.osutil.get_dhcp_pid() self.server_thread = threading.Thread(target = self.monitor) self.server_thread.setDaemon(True) self.server_thread.start() @@ -73,11 +59,11 @@ class EnvMonitor(object): If dhcp clinet process re-start has occurred, reset routes. """ while not self.stopped: - OSUTIL.remove_rules_files() - timeout = conf.get("OS.RootDeviceScsiTimeout", None) + self.distro.osutil.remove_rules_files() + timeout = conf.get_root_device_scsi_timeout() if timeout is not None: - OSUTIL.set_scsi_disks_timeout(timeout) - if conf.get_switch("Provisioning.MonitorHostName", False): + self.distro.osutil.set_scsi_disks_timeout(timeout) + if conf.get_monitor_hostname(): self.handle_hostname_update() self.handle_dhclient_restart() time.sleep(5) @@ -87,25 +73,25 @@ class EnvMonitor(object): if curr_hostname != self.hostname: logger.info("EnvMonitor: Detected host name change: {0} -> {1}", self.hostname, curr_hostname) - OSUTIL.set_hostname(curr_hostname) - OSUTIL.publish_hostname(curr_hostname) + self.distro.osutil.set_hostname(curr_hostname) + self.distro.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 = OSUTIL.get_dhcp_pid() + self.dhcpid = self.distro.dhcp_handler.get_dhcp_pid() return #The dhcp process hasn't changed since last check if os.path.isdir(os.path.join('/proc', self.dhcpid.strip())): return - newpid = OSUTIL.get_dhcp_pid() + newpid = self.distro.osutil.get_dhcp_pid() if newpid is not None and newpid != self.dhcpid: logger.info("EnvMonitor: Detected dhcp client restart. " "Restoring routing table.") - self.dhcp_resp.conf_routes() + self.distro.dhcp_handler.conf_routes() self.dhcpid = newpid def stop(self): diff --git a/azurelinuxagent/distro/default/event.py b/azurelinuxagent/distro/default/event.py new file mode 100644 index 0000000..700ea15 --- /dev/null +++ b/azurelinuxagent/distro/default/event.py @@ -0,0 +1,133 @@ +# 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 sys +import traceback +import atexit +import json +import time +import datetime +import threading +import platform +import azurelinuxagent.logger as logger +from azurelinuxagent.event import WALAEventOperation, add_event +from azurelinuxagent.exception import EventError, ProtocolError, OSUtilError +from azurelinuxagent.future import text +from azurelinuxagent.protocol.restapi import TelemetryEventParam, \ + TelemetryEventList, \ + TelemetryEvent, \ + set_properties, get_properties +from azurelinuxagent.metadata import DISTRO_NAME, DISTRO_VERSION, \ + DISTRO_CODE_NAME, AGENT_VERSION + + + +class EventHandler(object): + def __init__(self, distro): + self.distro = distro + self.sysinfo = [] + + def run(self): + event_thread = threading.Thread(target = self.daemon) + event_thread.setDaemon(True) + event_thread.start() + + def init_sysinfo(self): + osversion = "{0}:{1}-{2}-{3}:{4}".format(platform.system(), + DISTRO_NAME, + DISTRO_VERSION, + DISTRO_CODE_NAME, + platform.release()) + + + self.sysinfo.append(TelemetryEventParam("OSVersion", osversion)) + self.sysinfo.append(TelemetryEventParam("GAVersion", AGENT_VERSION)) + + try: + ram = self.distro.osutil.get_total_mem() + processors = self.distro.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() + vminfo = protocol.get_vminfo() + #TODO add more system info + self.sysinfo.append(TelemetryEventParam("VMName", vminfo.vmName )) + except ProtocolError as e: + logger.warn("Failed to get system info: {0}", e) + + def collect_event(self, evt_file_name): + try: + logger.verb("Found event file: {0}", evt_file_name) + with open(evt_file_name, "rb") as evt_file: + #if fail to open or delete the file, throw exception + json_str = evt_file.read().decode("utf-8",'ignore') + logger.verb("Processed event file: {0}", evt_file_name) + os.remove(evt_file_name) + return json_str + except IOError as e: + msg = "Failed to process {0}, {1}".format(evt_file_name, e) + raise EventError(msg) + + def collect_and_send_events(self): + event_list = TelemetryEventList() + event_dir = os.path.join(self.distro.osutil.get_lib_dir(), "event") + event_files = os.listdir(event_dir) + for event_file in event_files: + if not event_file.endswith(".tld"): + continue + event_file_path = os.path.join(event_dir, event_file) + try: + data_str = self.collect_event(event_file_path) + except EventError as e: + logger.error("{0}", e) + continue + try: + data = json.loads(data_str) + except ValueError as e: + logger.verb(data_str) + logger.verb("Failed to decode json event file: {0}", e) + continue + + event = TelemetryEvent() + set_properties("event", event, data) + event.parameters.extend(self.sysinfo) + event_list.events.append(event) + if len(event_list.events) == 0: + return + + try: + protocol = self.distro.protocol_util.get_protocol() + protocol.report_event(event_list) + except ProtocolError as e: + logger.error("{0}", e) + + def daemon(self): + self.init_sysinfo() + last_heartbeat = datetime.datetime.min + period = datetime.timedelta(hours = 12) + while(True): + if (datetime.datetime.now()-last_heartbeat) > period: + last_heartbeat = datetime.datetime.now() + add_event(op=WALAEventOperation.HeartBeat, name="WALA", + is_success=True) + self.collect_and_send_events() + time.sleep(60) diff --git a/azurelinuxagent/distro/default/extension.py b/azurelinuxagent/distro/default/extension.py index c82498f..c05f54b 100644 --- a/azurelinuxagent/distro/default/extension.py +++ b/azurelinuxagent/distro/default/extension.py @@ -22,14 +22,15 @@ import time import json import subprocess import shutil +import azurelinuxagent.conf as conf import azurelinuxagent.logger as logger -from azurelinuxagent.future import text -from azurelinuxagent.utils.osutil import OSUTIL -from azurelinuxagent.protocol.factory import PROT_FACTORY -import azurelinuxagent.protocol.common as prot -from azurelinuxagent.metadata import AGENT_VERSION from azurelinuxagent.event import add_event, WALAEventOperation -from azurelinuxagent.exception import ExtensionError +from azurelinuxagent.exception import ExtensionError, ProtocolError +from azurelinuxagent.future import text +from azurelinuxagent.metadata import AGENT_VERSION +from azurelinuxagent.protocol.restapi import ExtHandlerStatus, ExtensionStatus, \ + ExtensionSubStatus, Extension, \ + VMStatus import azurelinuxagent.utils.fileutil as fileutil import azurelinuxagent.utils.restutil as restutil import azurelinuxagent.utils.shellutil as shellutil @@ -72,7 +73,7 @@ def parse_ext_substatus(substatus): validate_has_key(substatus, 'status', 'substatus/status') validate_in_range(substatus['status'], VALID_EXTENSION_STATUS, 'substatus/status') - status = prot.ExtensionSubStatus() + status = ExtensionSubStatus() status.name = substatus.get('name') status.status = substatus.get('status') status.code = substatus.get('code', 0) @@ -120,7 +121,7 @@ def get_installed_version(target_name): Return the highest version instance with the same name """ installed_version = None - lib_dir = OSUTIL.get_lib_dir() + lib_dir = conf.get_lib_dir() for dir_name in os.listdir(lib_dir): path = os.path.join(lib_dir, dir_name) if os.path.isdir(path) and dir_name.startswith(target_name): @@ -139,19 +140,18 @@ class ExtHandlerState(object): class ExtHandlersHandler(object): - def __init__(self, handlers): - self.handlers = handlers + def __init__(self, distro): + self.distro = distro - def process(self): + def run(self): try: - protocol = PROT_FACTORY.get_protocol() + protocol = self.distro.protocol_util.get_protocol() ext_handlers = protocol.get_ext_handlers() - except prot.ProtocolError as e: + except ProtocolError as e: add_event(name="WALA", is_success=False, message = text(e)) return - - vm_status = prot.VMStatus() + vm_status = VMStatus() vm_status.vmAgent.version = AGENT_VERSION vm_status.vmAgent.status = "Ready" vm_status.vmAgent.message = "Guest Agent is running" @@ -164,7 +164,7 @@ class ExtHandlersHandler(object): #TODO handle extension in parallel try: pkg_list = protocol.get_ext_handler_pkgs(ext_handler) - except prot.ProtocolError as e: + except ProtocolError as e: add_event(name="WALA", is_success=False, message=text(e)) continue @@ -175,14 +175,14 @@ class ExtHandlersHandler(object): try: logger.verb("Report vm agent status") protocol.report_vm_status(vm_status) - except prot.ProtocolError as e: + except ProtocolError as e: add_event(name="WALA", is_success=False, message = text(e)) def process_extension(self, ext_handler, pkg_list): installed_version = get_installed_version(ext_handler.name) if installed_version is not None: - handler = ExtHandlerInstance(ext_handler, pkg_list, - installed_version, installed=True) + handler = ExtHandlerInstance(ext_handler, pkg_list, installed_version, + installed=True) else: handler = ExtHandlerInstance(ext_handler, pkg_list, ext_handler.properties.version) @@ -190,16 +190,17 @@ class ExtHandlersHandler(object): if handler.ext_status is not None: try: - protocol = PROT_FACTORY.get_protocol() + protocol = self.distro.protocol_util.get_protocol() protocol.report_ext_status(handler.name, handler.ext.name, handler.ext_status) - except prot.ProtocolError as e: + except ProtocolError as e: add_event(name="WALA", is_success=False, message=text(e)) return handler.handler_status class ExtHandlerInstance(object): - def __init__(self, ext_handler, pkg_list, curr_version, installed=False): + def __init__(self, ext_handler, pkg_list, curr_version, + installed=False): self.ext_handler = ext_handler self.name = ext_handler.name self.version = ext_handler.properties.version @@ -210,10 +211,10 @@ class ExtHandlerInstance(object): self.curr_version = curr_version self.installed = installed self.handler_state = None - self.lib_dir = OSUTIL.get_lib_dir() + self.lib_dir = conf.get_lib_dir() - self.ext_status = prot.ExtensionStatus() - self.handler_status = prot.ExtHandlerStatus() + self.ext_status = ExtensionStatus() + self.handler_status = ExtHandlerStatus() self.handler_status.name = self.name self.handler_status.version = self.curr_version @@ -223,7 +224,7 @@ class ExtHandlerInstance(object): self.handler_status.extensions = [self.ext.name] else: #When no extension settings, set sequenceNumber to 0 - self.ext = prot.Extension(sequenceNumber=0) + self.ext = Extension(sequenceNumber=0) self.ext_status.sequenceNumber = self.ext.sequenceNumber prefix = "[{0}]".format(self.get_full_name()) @@ -571,7 +572,7 @@ class ExtHandlerInstance(object): def collect_heartbeat(self): self.logger.info("Collect heart beat") - heartbeat_file = os.path.join(OSUTIL.get_lib_dir(), + heartbeat_file = os.path.join(conf.get_lib_dir(), self.get_heartbeat_file()) if not os.path.isfile(heartbeat_file): raise ExtensionError("Failed to get heart beat file") @@ -696,7 +697,7 @@ class ExtHandlerInstance(object): return "{0}-{1}".format(self.name, self.curr_version) def get_base_dir(self): - return os.path.join(OSUTIL.get_lib_dir(), self.get_full_name()) + return os.path.join(conf.get_lib_dir(), self.get_full_name()) def get_status_dir(self): return os.path.join(self.get_base_dir(), "status") @@ -713,7 +714,7 @@ class ExtHandlerInstance(object): "{0}.settings".format(self.ext.sequenceNumber)) def get_handler_state_dir(self): - return os.path.join(OSUTIL.get_lib_dir(), "handler_state", + return os.path.join(conf.get_lib_dir(), "handler_state", self.get_full_name()) def get_handler_state_file(self): @@ -735,7 +736,7 @@ class ExtHandlerInstance(object): return os.path.join(self.get_base_dir(), 'HandlerEnvironment.json') def get_log_dir(self): - return os.path.join(OSUTIL.get_ext_log_dir(), self.name, + return os.path.join(conf.get_ext_log_dir(), self.name, self.curr_version) class HandlerEnvironment(object): diff --git a/azurelinuxagent/distro/default/init.py b/azurelinuxagent/distro/default/init.py index 3c6671c..9b48e82 100644 --- a/azurelinuxagent/distro/default/init.py +++ b/azurelinuxagent/distro/default/init.py @@ -20,33 +20,34 @@ import os import azurelinuxagent.conf as conf import azurelinuxagent.logger as logger -from azurelinuxagent.utils.osutil import OSUTIL -import azurelinuxagent.utils.fileutil as fileutil +import azurelinuxagent.event as event class InitHandler(object): - def __init__(self, handlers): - self.handlers = handlers + def __init__(self, distro): + self.distro = distro - def init(self, verbose): + 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 = OSUTIL.get_conf_file_path() - conf.load_conf(conf_file_path) + 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_switch("Logs.Verbose", False) + verbose = verbose or self.distro.conf.get_switch("Logs.Verbose", False) 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") - #Create lib dir - fileutil.mkdir(OSUTIL.get_lib_dir(), mode=0o700) - os.chdir(OSUTIL.get_lib_dir()) + #Init event reporter + event_dir = os.path.join(self.distro.conf.get_lib_dir(), "events") + event.init_event_reporter(event_dir) + event.enable_unhandled_err_dump("WALA") + diff --git a/azurelinuxagent/distro/default/osutil.py b/azurelinuxagent/distro/default/osutil.py index a165f19..87459b0 100644 --- a/azurelinuxagent/distro/default/osutil.py +++ b/azurelinuxagent/distro/default/osutil.py @@ -26,6 +26,8 @@ import time import pwd import fcntl import azurelinuxagent.logger as logger +import azurelinuxagent.conf as conf +from azurelinuxagent.exception import OSUtilError from azurelinuxagent.future import text import azurelinuxagent.utils.fileutil as fileutil import azurelinuxagent.utils.shellutil as shellutil @@ -40,44 +42,14 @@ for all distros. Each concrete distro classes could overwrite default behavior if needed. """ -class OSUtilError(Exception): - pass - class DefaultOSUtil(object): def __init__(self): - self.lib_dir = "/var/lib/waagent" - self.ext_log_dir = "/var/log/azure" - self.dvd_mount_point = "/mnt/cdrom/secure" - self.ovf_env_file_path = "/mnt/cdrom/secure/ovf-env.xml" - self.agent_pid_file_path = "/var/run/waagent.pid" - self.passwd_file_path = "/etc/shadow" - self.home = '/home' - self.sshd_conf_file_path = '/etc/ssh/sshd_config' - self.openssl_cmd = '/usr/bin/openssl' - self.conf_file_path = '/etc/waagent.conf' + self.agent_conf_file_path = '/etc/waagent.conf' self.selinux=None - def get_lib_dir(self): - return self.lib_dir - - def get_ext_log_dir(self): - return self.ext_log_dir - - def get_dvd_mount_point(self): - return self.dvd_mount_point - - def get_conf_file_path(self): - return self.conf_file_path - - def get_ovf_env_file_path_on_dvd(self): - return self.ovf_env_file_path - - def get_agent_pid_file_path(self): - return self.agent_pid_file_path - - def get_openssl_cmd(self): - return self.openssl_cmd + def get_agent_conf_file_path(self): + return self.agent_conf_file_path def get_userentry(self, username): try: @@ -146,37 +118,37 @@ class DefaultOSUtil(object): def del_root_password(self): try: - passwd_content = fileutil.read_file(self.passwd_file_path) + passwd_file_path = conf.get_passwd_file_path() + passwd_content = fileutil.read_file(passwd_file_path) passwd = passwd_content.split('\n') new_passwd = [x for x in passwd if not x.startswith("root:")] new_passwd.insert(0, "root:*LOCK*:14600::::::") - fileutil.write_file(self.passwd_file_path, "\n".join(new_passwd)) + fileutil.write_file(passwd_file_path, "\n".join(new_passwd)) except IOError as e: raise OSUtilError("Failed to delete root password:{0}".format(e)) - def get_home(self): - return self.home - def get_pubkey_from_prv(self, file_name): - cmd = "{0} rsa -in {1} -pubout 2>/dev/null".format(self.openssl_cmd, + openssl_cmd = conf.get_openssl_cmd() + cmd = "{0} rsa -in {1} -pubout 2>/dev/null".format(openssl_cmd, file_name) pub = shellutil.run_get_output(cmd)[1] return pub def get_pubkey_from_crt(self, file_name): - cmd = "{0} x509 -in {1} -pubkey -noout".format(self.openssl_cmd, - file_name) + openssl_cmd = conf.get_openssl_cmd() + cmd = "{0} x509 -in {1} -pubkey -noout".format(openssl_cmd, file_name) pub = shellutil.run_get_output(cmd)[1] return pub def _norm_path(self, filepath): - home = self.get_home() + home = conf.get_home_dir() # Expand HOME variable if present in path path = os.path.normpath(filepath.replace("$HOME", home)) return path def get_thumbprint_from_crt(self, file_name): - cmd="{0} x509 -in {1} -fingerprint -noout".format(self.openssl_cmd, + openssl_cmd = conf.get_openssl_cmd() + cmd="{0} x509 -in {1} -fingerprint -noout".format(openssl_cmd, file_name) thumbprint = shellutil.run_get_output(cmd)[1] thumbprint = thumbprint.rstrip().split('=')[1].replace(':', '').upper() @@ -190,7 +162,7 @@ class DefaultOSUtil(object): path = self._norm_path(path) dir_path = os.path.dirname(path) fileutil.mkdir(dir_path, mode=0o700, owner=username) - lib_dir = self.get_lib_dir() + lib_dir = conf.get_lib_dir() prv_path = os.path.join(lib_dir, thumbprint + '.prv') if not os.path.isfile(prv_path): raise OSUtilError("Can't find {0}.prv".format(thumbprint)) @@ -223,7 +195,7 @@ class DefaultOSUtil(object): raise OSUtilError("Bad public key: {0}".format(value)) fileutil.write_file(path, value) elif thumbprint is not None: - lib_dir = self.get_lib_dir() + lib_dir = conf.get_lib_dir() crt_path = os.path.join(lib_dir, thumbprint + '.crt') if not os.path.isfile(crt_path): raise OSUtilError("Can't find {0}.crt".format(thumbprint)) @@ -280,11 +252,8 @@ class DefaultOSUtil(object): if self.is_selinux_system(): return shellutil.run('chcon ' + con + ' ' + path) - def get_sshd_conf_file_path(self): - return self.sshd_conf_file_path - def set_ssh_client_alive_interval(self): - conf_file_path = self.get_sshd_conf_file_path() + conf_file_path = conf.get_sshd_conf_file_path() conf = fileutil.read_file(conf_file_path).split("\n") textutil.set_ssh_config(conf, "ClientAliveInterval", "180") fileutil.write_file(conf_file_path, '\n'.join(conf)) @@ -292,7 +261,7 @@ class DefaultOSUtil(object): def conf_sshd(self, disable_password): option = "no" if disable_password else "yes" - conf_file_path = self.get_sshd_conf_file_path() + conf_file_path = conf.get_sshd_conf_file_path() conf = fileutil.read_file(conf_file_path).split("\n") textutil.set_ssh_config(conf, "PasswordAuthentication", option) textutil.set_ssh_config(conf, "ChallengeResponseAuthentication", option) @@ -309,7 +278,7 @@ class DefaultOSUtil(object): def mount_dvd(self, max_retry=6, chk_err=True): dvd = self.get_dvd_device() - mount_point = self.get_dvd_mount_point() + mount_point = conf.get_dvd_mount_point() mountlist = shellutil.run_get_output("mount")[1] existing = self.get_mount_point(mountlist, dvd) if existing is not None: #Already mounted @@ -332,7 +301,7 @@ class DefaultOSUtil(object): raise OSUtilError("Failed to mount dvd.") def umount_dvd(self, chk_err=True): - mount_point = self.get_dvd_mount_point() + mount_point = conf.get_dvd_mount_point() retcode = self.umount(mount_point, chk_err=chk_err) if chk_err and retcode != 0: raise OSUtilError("Failed to umount dvd.") @@ -390,13 +359,14 @@ class DefaultOSUtil(object): """ Create ssl certificate for https communication with endpoint server. """ + openssl_cmd = conf.get_openssl_cmd() cmd = ("{0} req -x509 -nodes -subj /CN=LinuxTransport -days 32768 " "-newkey rsa:2048 -keyout {1} " - "-out {2}").format(self.openssl_cmd, prv_file, crt_file) + "-out {2}").format(openssl_cmd, prv_file, crt_file) shellutil.run(cmd) def remove_rules_files(self, rules_files=__RULES_FILES__): - lib_dir = self.get_lib_dir() + lib_dir = conf.get_lib_dir() for src in rules_files: file_name = fileutil.base_name(src) dest = os.path.join(lib_dir, file_name) @@ -407,7 +377,7 @@ class DefaultOSUtil(object): shutil.move(src, dest) def restore_rules_files(self, rules_files=__RULES_FILES__): - lib_dir = self.get_lib_dir() + lib_dir = conf.get_lib_dir() for dest in rules_files: filename = fileutil.base_name(dest) src = os.path.join(lib_dir, filename) @@ -649,4 +619,5 @@ class DefaultOSUtil(object): return int(ret[1]) else: raise OSUtilError("Failed to get procerssor cores") + diff --git a/azurelinuxagent/distro/default/protocolUtil.py b/azurelinuxagent/distro/default/protocolUtil.py new file mode 100644 index 0000000..5a25dbb --- /dev/null +++ b/azurelinuxagent/distro/default/protocolUtil.py @@ -0,0 +1,236 @@ +# 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 re +import shutil +import time +import threading +import azurelinuxagent.conf as conf +import azurelinuxagent.logger as logger +from azurelinuxagent.exception import ProtocolError, OSUtilError, \ + ProtocolNotFoundError +from azurelinuxagent.future import text +import azurelinuxagent.utils.fileutil as fileutil +from azurelinuxagent.protocol.ovfenv import OvfEnv +from azurelinuxagent.protocol.wire import WireProtocol +from azurelinuxagent.protocol.metadata import MetadataProtocol + +OVF_FILE_NAME = "ovf-env.xml" + +#Tag file to indicate usage of metadata protocol +TAG_FILE_NAME = "useMetadataEndpoint.tag" + +PROTOCOL_FILE_NAME = "Protocol" + +#MAX retry times for protocol probing +MAX_RETRY = 60 + +PROBE_INTERVAL = 10 + +ENDPOINT_FILE_NAME = "WireServerEndpoint" + +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 + self.lock = threading.Lock() + + def copy_ovf_env(self): + """ + Copy ovf env file from dvd to hard disk. + Remove password before save it to the disk + """ + dvd_mount_point = self.distro.osutil.get_dvd_mount_point() + ovf_file_path_on_dvd = self.distro.osutil.get_ovf_env_file_path_on_dvd() + tag_file_path_on_dvd = os.path.join(dvd_mount_point, TAG_FILE_NAME) + try: + self.distro.osutil.mount_dvd() + ovfxml = fileutil.read_file(ovf_file_path_on_dvd, remove_bom=True) + ovfenv = OvfEnv(ovfxml) + ovfxml = re.sub(".*?<", "*<", ovfxml) + ovf_file_path = os.path.join(conf.get_lib_dir(), OVF_FILE_NAME) + fileutil.write_file(ovf_file_path, ovfxml) + + if os.path.isfile(tag_file_path_on_dvd): + logger.info("Found {0} in provisioning ISO", TAG_FILE_NAME) + tag_file_path = os.path.join(conf.get_lib_dir(), TAG_FILE_NAME) + shutil.copyfile(tag_file_path_on_dvd, tag_file_path) + + except (OSUtilError, IOError) as e: + raise ProtocolError(text(e)) + + try: + self.distro.osutil.umount_dvd() + self.distro.osutil.eject_dvd() + except OSUtilError as e: + logger.warn(text(e)) + + return ovfenv + + def get_ovf_env(self): + """ + Load saved ovf-env.xml + """ + ovf_file_path = os.path.join(conf.get_lib_dir(), OVF_FILE_NAME) + if os.path.isfile(ovf_file_path): + xml_text = fileutil.read_file(ovf_file_path) + return OvfEnv(xml_text) + else: + raise ProtocolError("ovf-env.xml is missing.") + + def _get_wireserver_endpoint(self): + try: + file_path = os.path.join(conf.get_lib_dir(), ENDPOINT_FILE_NAME) + return fileutil.read_file(file_path) + except IOError as e: + raise OSUtilError(text(e)) + + def _set_wireserver_endpoint(self, endpoint): + try: + file_path = os.path.join(conf.get_lib_dir(), ENDPOINT_FILE_NAME) + fileutil.write_file(file_path, endpoint) + except IOError as e: + raise OSUtilError(text(e)) + + def _detect_wire_protocol(self): + endpoint = self.distro.dhcp_handler.endpoint + if endpoint is None: + logger.info("WireServer endpoint is not found. Rerun dhcp handler") + self.distro.dhcp_handler.run() + endpoint = self.distro.dhcp_handler.endpoint + + try: + protocol = WireProtocol(self.distro.osutil, endpoint) + protocol.detect() + self._set_wireserver_endpoint(endpoint) + return protocol + except ProtocolError as e: + logger.info("WireServer is not responding. Rerun dhcp handler") + self.distro.dhcp_handler.run() + raise e + + def _detect_metadata_protocol(self): + protocol = MetadataProtocol(self.distro.osutil) + protocol.detect() + return protocol + + def _detect_protocol(self, protocols): + """ + 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) + for retry in range(0, MAX_RETRY): + for protocol in protocols: + try: + if protocol == WireProtocol.__name__: + return self._detect_wire_protocol() + + if protocol == MetadataProtocol.__name__: + return self._detect_metadata_protocol + + except ProtocolError as e: + logger.info("Protocol endpoint not found: {0}, {1}", + protocol, e) + + if retry < MAX_RETRY -1: + logger.info("Retry detect protocols: retry={0}", retry) + time.sleep(PROBE_INTERVAL) + raise ProtocolNotFoundError("No protocol found.") + + def _get_protocol(self): + """ + Get protocol instance based on previous detecting result. + """ + 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") + + protocol_name = fileutil.read_file(protocol_file_path) + if protocol_name == WireProtocol.__name__: + endpoint = self._get_wireserver_endpoint() + return WireProtocol(self.distro.osutil, endpoint) + elif protocol_name == MetadataProtocol.__name__: + return MetadataProtocol(self.distro.osutil) + else: + raise ProtocolNotFoundError(("Unknown protocol: {0}" + "").format(protocol_name)) + + def detect_protocol(self): + """ + Detect protocol by endpoints + + :returns: protocol instance + """ + logger.info("Detect protocol endpoints") + protocols = [WireProtocol.__name__, MetadataProtocol.__name__] + self.lock.acquire() + try: + if self.protocol is None: + self.protocol = self._detect_protocol(protocols) + return self.protocol + finally: + self.lock.release() + + def detect_protocol_by_file(self): + """ + Detect protocol by tag file. + + If a file "useMetadataEndpoint.tag" is found on provision iso, + metedata protocol will be used. No need to probe for wire protocol + + :returns: protocol instance + """ + logger.info("Detect protocol by file") + self.lock.acquire() + try: + tag_file_path = os.path.join(self.distro.conf.get_lib_dir(), + TAG_FILE_NAME) + if self.protocol is None: + protocols = [] + if os.path.isfile(tag_file_path): + protocols.append(MetadataProtocol.__name__) + else: + protocols.append(WireProtocol.__name__) + 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() + return self.protocol + finally: + self.lock.release() + return self.protocol + diff --git a/azurelinuxagent/distro/default/provision.py b/azurelinuxagent/distro/default/provision.py index 67fede1..523ab17 100644 --- a/azurelinuxagent/distro/default/provision.py +++ b/azurelinuxagent/distro/default/provision.py @@ -24,11 +24,8 @@ import azurelinuxagent.logger as logger from azurelinuxagent.future import text import azurelinuxagent.conf as conf from azurelinuxagent.event import add_event, WALAEventOperation -from azurelinuxagent.exception import ProvisionError -from azurelinuxagent.utils.osutil import OSUTIL, OSUtilError -from azurelinuxagent.protocol.factory import PROT_FACTORY, ProvisionStatus, \ - ProtocolError -import azurelinuxagent.protocol.ovfenv as ovf +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 @@ -36,49 +33,30 @@ CUSTOM_DATA_FILE="CustomData" class ProvisionHandler(object): - def __init__(self, handlers): - self.handlers = handlers + def __init__(self, distro): + self.distro = distro - def report_event(self, message, is_success=False): - add_event(name="WALA", message=message, is_success=is_success, - op=WALAEventOperation.Provision) - - def report_not_ready(self, protocol, sub_status, description): - status = ProvisionStatus(status="NotReady", subStatus=sub_status, - description=description) - try: - protocol.report_provision_status(status) - except ProtocolError as e: - self.report_event(text(e)) - - def report_ready(self, protocol, thumbprint=None): - status = ProvisionStatus(status="Ready") - status.properties.certificateThumbprint = thumbprint - try: - protocol.report_provision_status(status) - except ProtocolError as e: - self.report_event(text(e)) - - def process(self): + def run(self): #If provision is not enabled, return - if not conf.get_switch("Provisioning.Enabled", True): + if not conf.get_provision_enabled(): logger.info("Provisioning is disabled. Skip.") return - provisioned = os.path.join(OSUTIL.get_lib_dir(), "provisioned") + provisioned = os.path.join(conf.get_lib_dir(), "provisioned") if os.path.isfile(provisioned): return logger.info("Run provision handler.") logger.info("Copy ovf-env.xml.") try: - ovfenv = ovf.copy_ovf_env() + ovfenv = self.distro.protocol_util.copy_ovf_env() except ProtocolError as e: self.report_event("Failed to copy ovf-env.xml: {0}".format(e)) return - protocol = PROT_FACTORY.detect_protocol_by_file() - self.report_not_ready(protocol, "Provisioning", "Starting") + self.distro.protocol_util.detect_protocol_by_file() + + self.report_not_ready("Provisioning", "Starting") try: logger.info("Start provisioning") @@ -88,16 +66,16 @@ class ProvisionHandler(object): logger.info("Finished provisioning") except ProvisionError as e: logger.error("Provision failed: {0}", e) - self.report_not_ready(protocol, "ProvisioningFailed", text(e)) + self.report_not_ready("ProvisioningFailed", text(e)) self.report_event(text(e)) return - self.report_ready(protocol, thumbprint) + self.report_ready(thumbprint) self.report_event("Provision succeed", is_success=True) def reg_ssh_host_key(self): - keypair_type = conf.get("Provisioning.SshHostKeyPairType", "rsa") - if conf.get_switch("Provisioning.RegenerateSshHostKeyPair"): + keypair_type = conf.get_ssh_host_keypair_type() + if conf.get_regenerate_ssh_host_key(): shellutil.run("rm -f /etc/ssh/ssh_host_*key*") shellutil.run(("ssh-keygen -N '' -t {0} -f /etc/ssh/ssh_host_{1}_key" "").format(keypair_type, keypair_type)) @@ -117,66 +95,89 @@ class ProvisionHandler(object): logger.info("Handle ovf-env.xml.") try: logger.info("Set host name.") - OSUTIL.set_hostname(ovfenv.hostname) + self.distro.osutil.set_hostname(ovfenv.hostname) logger.info("Publish host name.") - OSUTIL.publish_hostname(ovfenv.hostname) + self.distro.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() - if conf.get_switch("Provisioning.DeleteRootPassword"): - 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") - OSUTIL.useradd(ovfenv.username) + self.distro.osutil.useradd(ovfenv.username) if ovfenv.user_password is not None: logger.info("Set user password.") - crypt_id = conf.get("Provision.PasswordCryptId", "6") - salt_len = conf.get_int("Provision.PasswordCryptSaltLength", 10) - OSUTIL.chpasswd(ovfenv.username, ovfenv.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, crypt_id=crypt_id, salt_len=salt_len) logger.info("Configure sudoer") - OSUTIL.conf_sudoer(ovfenv.username, ovfenv.user_password is None) + self.distro.osutil.conf_sudoer(ovfenv.username, ovfenv.user_password is None) logger.info("Configure sshd") - OSUTIL.conf_sshd(ovfenv.disable_ssh_password_auth) + self.distro.osutil.conf_sshd(ovfenv.disable_ssh_password_auth) #Disable selinux temporary - sel = OSUTIL.is_selinux_enforcing() + sel = self.distro.osutil.is_selinux_enforcing() if sel: - OSUTIL.set_selinux_enforce(0) + self.distro.osutil.set_selinux_enforce(0) self.deploy_ssh_pubkeys(ovfenv) self.deploy_ssh_keypairs(ovfenv) if sel: - OSUTIL.set_selinux_enforce(1) + self.distro.osutil.set_selinux_enforce(1) - OSUTIL.restart_ssh_service() + self.distro.osutil.restart_ssh_service() def save_customdata(self, ovfenv): logger.info("Save custom data") customdata = ovfenv.customdata if customdata is None: return - lib_dir = OSUTIL.get_lib_dir() + lib_dir = conf.get_lib_dir() fileutil.write_file(os.path.join(lib_dir, CUSTOM_DATA_FILE), - OSUTIL.decode_customdata(customdata)) + self.distro.osutil.decode_customdata(customdata)) def deploy_ssh_pubkeys(self, ovfenv): for pubkey in ovfenv.ssh_pubkeys: logger.info("Deploy ssh public key.") - OSUTIL.deploy_ssh_pubkey(ovfenv.username, pubkey) + self.distro.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.") - OSUTIL.deploy_ssh_keypair(ovfenv.username, keypair) + self.distro.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, + op=WALAEventOperation.Provision) + + def report_not_ready(self, sub_status, description): + status = ProvisionStatus(status="NotReady", subStatus=sub_status, + description=description) + try: + protocol = self.distro.protocol_util.get_protocol() + protocol.report_provision_status(status) + except ProtocolError as e: + self.report_event(text(e)) + + def report_ready(self, thumbprint=None): + status = ProvisionStatus(status="Ready") + status.properties.certificateThumbprint = thumbprint + try: + protocol = self.distro.protocol_util.get_protocol() + protocol.report_provision_status(status) + except ProtocolError as e: + self.report_event(text(e)) diff --git a/azurelinuxagent/distro/default/resourceDisk.py b/azurelinuxagent/distro/default/resourceDisk.py index ff548d3..c014d14 100644 --- a/azurelinuxagent/distro/default/resourceDisk.py +++ b/azurelinuxagent/distro/default/resourceDisk.py @@ -23,7 +23,6 @@ import threading import azurelinuxagent.logger as logger from azurelinuxagent.future import text import azurelinuxagent.conf as conf -from azurelinuxagent.utils.osutil import OSUTIL from azurelinuxagent.event import add_event, WALAEventOperation import azurelinuxagent.utils.fileutil as fileutil import azurelinuxagent.utils.shellutil as shellutil @@ -41,8 +40,8 @@ For additional details to please refer to the MSDN documentation at : http://msd """ class ResourceDiskHandler(object): - def __init__(self, handlers): - self.handlers = handlers + def __init__(self, distro): + self.distro = distro def start_activate_resource_disk(self): disk_thread = threading.Thread(target = self.run) @@ -50,17 +49,17 @@ class ResourceDiskHandler(object): def run(self): mount_point = None - if conf.get_switch("ResourceDisk.Format", False): + if conf.get_resourcedisk_format(): mount_point = self.activate_resource_disk() if mount_point is not None and \ - conf.get_switch("ResourceDisk.EnableSwap", False): + conf.get_resourcedisk_enable_swap(): self.enable_swap(mount_point) def activate_resource_disk(self): logger.info("Activate resource disk") try: - mount_point = conf.get("ResourceDisk.MountPoint", "/mnt/resource") - fs = conf.get("ResourceDisk.Filesystem", "ext3") + mount_point = conf.get_resourcedisk_mountpoint() + fs = conf.get_resourcedisk_filesystem() mount_point = self.mount_resource_disk(mount_point, fs) warning_file = os.path.join(mount_point, DATALOSS_WARNING_FILE_NAME) try: @@ -76,19 +75,19 @@ class ResourceDiskHandler(object): def enable_swap(self, mount_point): logger.info("Enable swap") try: - size_mb = conf.get_int("ResourceDisk.SwapSizeMB", 0) + size_mb = conf.get_resourcedisk_swap_size_mb() self.create_swap_space(mount_point, size_mb) except ResourceDiskError as e: logger.error("Failed to enable swap {0}", e) def mount_resource_disk(self, mount_point, fs): - device = OSUTIL.device_for_ide_port(1) + device = self.distro.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 = OSUTIL.get_mount_point(mountlist, device) + existing = self.distro.osutil.get_mount_point(mountlist, device) if(existing): logger.info("Resource disk {0}1 is already mounted", device) diff --git a/azurelinuxagent/distro/default/scvmm.py b/azurelinuxagent/distro/default/scvmm.py index 21e2906..4d083b4 100644 --- a/azurelinuxagent/distro/default/scvmm.py +++ b/azurelinuxagent/distro/default/scvmm.py @@ -20,30 +20,29 @@ import os import subprocess import azurelinuxagent.logger as logger -from azurelinuxagent.utils.osutil import OSUTIL VMM_CONF_FILE_NAME = "linuxosconfiguration.xml" VMM_STARTUP_SCRIPT_NAME= "install" class ScvmmHandler(object): - def __init__(self, handlers): - self.handlers = handlers + def __init__(self, distro): + self.distro = distro def detect_scvmm_env(self): logger.info("Detecting Microsoft System Center VMM Environment") - OSUTIL.mount_dvd(max_retry=1, chk_err=False) - mount_point = OSUTIL.get_dvd_mount_point() + self.distro.osutil.mount_dvd(max_retry=1, chk_err=False) + mount_point = self.distro.osutil.get_dvd_mount_point() found = os.path.isfile(os.path.join(mount_point, VMM_CONF_FILE_NAME)) if found: self.start_scvmm_agent() else: - OSUTIL.umount_dvd(chk_err=False) + self.distro.osutil.umount_dvd(chk_err=False) return found def start_scvmm_agent(self): logger.info("Starting Microsoft System Center VMM Initialization " "Process") - mount_point = OSUTIL.get_dvd_mount_point() + mount_point = self.distro.osutil.get_dvd_mount_point() startup_script = os.path.join(mount_point, VMM_STARTUP_SCRIPT_NAME) subprocess.Popen(["/bin/bash", startup_script, "-p " + mount_point]) diff --git a/azurelinuxagent/distro/loader.py b/azurelinuxagent/distro/loader.py index 375abd2..d6bb252 100644 --- a/azurelinuxagent/distro/loader.py +++ b/azurelinuxagent/distro/loader.py @@ -16,31 +16,51 @@ # import azurelinuxagent.logger as logger -from azurelinuxagent.metadata import DISTRO_NAME -import azurelinuxagent.distro.default.loader as default_loader +from azurelinuxagent.utils.textutil import Version +from azurelinuxagent.metadata 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 - -def get_distro_loader(): - try: - logger.verb("Loading distro implemetation from: {0}", DISTRO_NAME) - pkg_name = "azurelinuxagent.distro.{0}.loader".format(DISTRO_NAME) - return __import__(pkg_name, fromlist="loader") - except (ImportError, ValueError): +def get_distro(): + if DISTRO_NAME == "ubuntu": + if Version(DISTRO_VERSION) == Version("12.04") or \ + Version(DISTRO_VERSION) == Version("12.10"): + return Ubuntu12Distro() + elif Version(DISTRO_VERSION) == Version("14.04") or \ + Version(DISTRO_VERSION) == Version("14.10"): + return Ubuntu14Distro() + elif DISTRO_FULL_NAME == "Snappy Ubuntu Core": + return UbuntuSnappyDistro() + else: + return UbuntuDistro() + if DISTRO_NAME == "coreos": + return CoreOSDistro() + 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() + else: + return SUSEDistro() + elif DISTRO_NAME == "debian": + return DebianDistro() + elif DISTRO_NAME == "redhat" or DISTRO_NAME == "centos" or \ + DISTRO_NAME == "oracle": + if Version(DISTRO_VERSION) < Version(7): + return Redhat6xDistro() + else: + return RedhatDistro() + else: logger.warn("Unable to load distro implemetation for {0}.", DISTRO_NAME) logger.warn("Use default distro implemetation instead.") - return default_loader - -DISTRO_LOADER = get_distro_loader() - -def get_osutil(): - try: - return DISTRO_LOADER.get_osutil() - except AttributeError: - return default_loader.get_osutil() - -def get_handlers(): - try: - return DISTRO_LOADER.get_handlers() - except AttributeError: - return default_loader.get_handlers() + return DefaultDistro() diff --git a/azurelinuxagent/distro/oracle/__init__.py b/azurelinuxagent/distro/oracle/__init__.py deleted file mode 100644 index d9b82f5..0000000 --- a/azurelinuxagent/distro/oracle/__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/handlerFactory.py b/azurelinuxagent/distro/redhat/distro.py similarity index 59% rename from azurelinuxagent/distro/ubuntu/handlerFactory.py rename to azurelinuxagent/distro/redhat/distro.py index 9446594..2f128d7 100644 --- a/azurelinuxagent/distro/ubuntu/handlerFactory.py +++ b/azurelinuxagent/distro/redhat/distro.py @@ -17,13 +17,16 @@ # Requires Python 2.4+ and Openssl 1.0+ # -from azurelinuxagent.distro.ubuntu.provision import UbuntuProvisionHandler -from azurelinuxagent.distro.ubuntu.deprovision import UbuntuDeprovisionHandler -from azurelinuxagent.distro.default.handlerFactory import DefaultHandlerFactory +from azurelinuxagent.distro.default.distro import DefaultDistro +from azurelinuxagent.distro.redhat.osutil import RedhatOSUtil, Redhat6xOSUtil +from azurelinuxagent.distro.coreos.deprovision import CoreOSDeprovisionHandler -class UbuntuHandlerFactory(DefaultHandlerFactory): +class Redhat6xDistro(DefaultDistro): def __init__(self): - super(UbuntuHandlerFactory, self).__init__() - self.provision_handler = UbuntuProvisionHandler(self) - self.deprovision_handler = UbuntuDeprovisionHandler(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/redhat/loader.py b/azurelinuxagent/distro/redhat/loader.py deleted file mode 100644 index 8d3c75b..0000000 --- a/azurelinuxagent/distro/redhat/loader.py +++ /dev/null @@ -1,28 +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.metadata import DISTRO_NAME, DISTRO_VERSION - -def get_osutil(): - from azurelinuxagent.distro.redhat.osutil import Redhat6xOSUtil, RedhatOSUtil - if DISTRO_VERSION < "7": - return Redhat6xOSUtil() - else: - return RedhatOSUtil() - diff --git a/azurelinuxagent/distro/default/loader.py b/azurelinuxagent/distro/suse/distro.py similarity index 61% rename from azurelinuxagent/distro/default/loader.py rename to azurelinuxagent/distro/suse/distro.py index 55a51e0..d05057e 100644 --- a/azurelinuxagent/distro/default/loader.py +++ b/azurelinuxagent/distro/suse/distro.py @@ -17,12 +17,16 @@ # Requires Python 2.4+ and Openssl 1.0+ # -def get_osutil(): - from azurelinuxagent.distro.default.osutil import DefaultOSUtil - return DefaultOSUtil() +from azurelinuxagent.distro.default.distro import DefaultDistro +from azurelinuxagent.distro.suse.osutil import SUSE11OSUtil, SUSEOSUtil -def get_handlers(): - from azurelinuxagent.distro.default.handlerFactory import DefaultHandlerFactory - return DefaultHandlerFactory() +class SUSE11Distro(DefaultDistro): + def __init__(self): + super(SUSE11Distro, self).__init__() + self.osutil = SUSE11Distro() +class SUSEDistro(DefaultDistro): + def __init__(self): + super(SUSEDistro, self).__init__() + self.osutil = SUSEOSUtil() diff --git a/azurelinuxagent/distro/suse/loader.py b/azurelinuxagent/distro/suse/loader.py deleted file mode 100644 index b01384b..0000000 --- a/azurelinuxagent/distro/suse/loader.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.metadata import DISTRO_NAME, DISTRO_VERSION, DISTRO_FULL_NAME - -def get_osutil(): - from azurelinuxagent.distro.suse.osutil import SUSE11OSUtil, SUSEOSUtil - if DISTRO_FULL_NAME=='SUSE Linux Enterprise Server' and DISTRO_VERSION < '12' \ - or DISTRO_FULL_NAME == 'openSUSE' and DISTRO_VERSION < '13.2': - return SUSE11OSUtil() - else: - return SUSEOSUtil() - diff --git a/azurelinuxagent/distro/ubuntu/distro.py b/azurelinuxagent/distro/ubuntu/distro.py new file mode 100644 index 0000000..f380f6c --- /dev/null +++ b/azurelinuxagent/distro/ubuntu/distro.py @@ -0,0 +1,55 @@ +# 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/ubuntu/loader.py b/azurelinuxagent/distro/ubuntu/loader.py deleted file mode 100644 index 3fe2239..0000000 --- a/azurelinuxagent/distro/ubuntu/loader.py +++ /dev/null @@ -1,40 +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.metadata import DISTRO_NAME, DISTRO_VERSION, DISTRO_FULL_NAME - -def get_osutil(): - from azurelinuxagent.distro.ubuntu.osutil import Ubuntu1204OSUtil, \ - UbuntuOSUtil, \ - Ubuntu14xOSUtil, \ - UbuntuSnappyOSUtil - - if DISTRO_VERSION == "12.04": - return Ubuntu1204OSUtil() - elif DISTRO_VERSION == "14.04" or DISTRO_VERSION == "14.10": - return Ubuntu14xOSUtil() - elif DISTRO_FULL_NAME == "Snappy Ubuntu Core": - return UbuntuSnappyOSUtil() - else: - return UbuntuOSUtil() - -def get_handlers(): - from azurelinuxagent.distro.ubuntu.handlerFactory import UbuntuHandlerFactory - return UbuntuHandlerFactory() - diff --git a/azurelinuxagent/distro/ubuntu/osutil.py b/azurelinuxagent/distro/ubuntu/osutil.py index adf7660..cc4b8ef 100644 --- a/azurelinuxagent/distro/ubuntu/osutil.py +++ b/azurelinuxagent/distro/ubuntu/osutil.py @@ -31,9 +31,9 @@ import azurelinuxagent.utils.shellutil as shellutil import azurelinuxagent.utils.textutil as textutil from azurelinuxagent.distro.default.osutil import DefaultOSUtil -class Ubuntu14xOSUtil(DefaultOSUtil): +class Ubuntu14OSUtil(DefaultOSUtil): def __init__(self): - super(Ubuntu14xOSUtil, self).__init__() + super(Ubuntu14OSUtil, self).__init__() def start_network(self): return shellutil.run("service networking start", chk_err=False) @@ -44,16 +44,16 @@ class Ubuntu14xOSUtil(DefaultOSUtil): def start_agent_service(self): return shellutil.run("service walinuxagent start", chk_err=False) -class Ubuntu1204OSUtil(Ubuntu14xOSUtil): +class Ubuntu12OSUtil(Ubuntu14OSUtil): def __init__(self): - super(Ubuntu1204OSUtil, self).__init__() + super(Ubuntu12OSUtil, self).__init__() #Override def get_dhcp_pid(self): ret= shellutil.run_get_output("pidof dhclient3") return ret[1] if ret[0] == 0 else None -class UbuntuOSUtil(Ubuntu14xOSUtil): +class UbuntuOSUtil(Ubuntu14OSUtil): def __init__(self): super(UbuntuOSUtil, self).__init__() @@ -63,7 +63,7 @@ class UbuntuOSUtil(Ubuntu14xOSUtil): def unregister_agent_service(self): return shellutil.run("systemctl mask walinuxagent", chk_err=False) -class UbuntuSnappyOSUtil(Ubuntu14xOSUtil): +class UbuntuSnappyOSUtil(Ubuntu14OSUtil): def __init__(self): super(UbuntuSnappyOSUtil, self).__init__() self.conf_file_path = '/apps/walinuxagent/current/waagent.conf' diff --git a/azurelinuxagent/distro/ubuntu/provision.py b/azurelinuxagent/distro/ubuntu/provision.py index 3456df7..37d2e44 100644 --- a/azurelinuxagent/distro/ubuntu/provision.py +++ b/azurelinuxagent/distro/ubuntu/provision.py @@ -22,11 +22,9 @@ import time import azurelinuxagent.logger as logger from azurelinuxagent.future import text import azurelinuxagent.conf as conf -from azurelinuxagent.protocol.factory import PROT_FACTORY -import azurelinuxagent.protocol.ovfenv as ovf +import azurelinuxagent.protocol.ovfenv as ovfenv from azurelinuxagent.event import add_event, WALAEventOperation -from azurelinuxagent.exception import ProvisionError -from azurelinuxagent.utils.osutil import OSUTIL +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 @@ -35,25 +33,25 @@ from azurelinuxagent.distro.default.provision import ProvisionHandler On ubuntu image, provision could be disabled. """ class UbuntuProvisionHandler(ProvisionHandler): - def __init__(self, handlers): - self.handlers = handlers + def __init__(self, distro): + self.distro = distro def process(self): #If provision is enabled, run default provision handler - if conf.get_switch("Provisioning.Enabled", False): + if conf.get_provision_enabled(): super(UbuntuProvisionHandler, self).process() return logger.info("run Ubuntu provision handler") - provisioned = os.path.join(OSUTIL.get_lib_dir(), "provisioned") + provisioned = os.path.join(conf.get_lib_dir(), "provisioned") if os.path.isfile(provisioned): return logger.info("Waiting cloud-init to copy ovf-env.xml.") self.wait_for_ovfenv() - protocol = PROT_FACTORY.detect_protocol_by_file() - self.report_not_ready(protocol, "Provisioning", "Starting") + protocol = self.distro.protocol_util.detect_protocol_by_file() + self.report_not_ready("Provisioning", "Starting") logger.info("Sleep 15 seconds to prevent throttling") time.sleep(15) #Sleep to prevent throttling try: @@ -64,31 +62,32 @@ class UbuntuProvisionHandler(ProvisionHandler): except ProvisionError as e: logger.error("Provision failed: {0}", e) - self.report_not_ready(protocol, "ProvisioningFailed", text(e)) + self.report_not_ready("ProvisioningFailed", text(e)) self.report_event(text(e)) return - self.report_ready(protocol, thumbprint) + self.report_ready(thumbprint) self.report_event("Provision succeed", is_success=True) def wait_for_ovfenv(self, max_retry=60): """ Wait for cloud-init to copy ovf-env.xml file from provision ISO """ - ovf_file_path = os.path.join(OSUTIL.get_lib_dir(), ovf.OVF_FILE_NAME) for retry in range(0, max_retry): - if os.path.isfile(ovf_file_path): + try: + self.distro.protocol_util.get_ovf_env() return - if retry < max_retry - 1: - logger.info("Wait for cloud-init to copy ovf-env.xml") - time.sleep(5) + except ProtocolError: + if retry < max_retry - 1: + logger.info("Wait for cloud-init to copy ovf-env.xml") + time.sleep(5) raise ProvisionError("ovf-env.xml is not copied") def wait_for_ssh_host_key(self, max_retry=60): """ Wait for cloud-init to generate ssh host key """ - kepair_type = conf.get("Provisioning.SshHostKeyPairType", "rsa") + kepair_type = conf.get_ssh_host_keypair_type() path = '/etc/ssh/ssh_host_{0}_key'.format(kepair_type) for retry in range(0, max_retry): if os.path.isfile(path): diff --git a/azurelinuxagent/event.py b/azurelinuxagent/event.py index af783f6..3d4e11c 100644 --- a/azurelinuxagent/event.py +++ b/azurelinuxagent/event.py @@ -25,15 +25,15 @@ import datetime import threading import platform import azurelinuxagent.logger as logger +from azurelinuxagent.exception import EventError, ProtocolError from azurelinuxagent.future import text -import azurelinuxagent.protocol as prot -from azurelinuxagent.protocol.factory import PROT_FACTORY +from azurelinuxagent.protocol.restapi import TelemetryEventParam, \ + TelemetryEventList, \ + TelemetryEvent, \ + set_properties, get_properties from azurelinuxagent.metadata import DISTRO_NAME, DISTRO_VERSION, \ DISTRO_CODE_NAME, AGENT_VERSION -from azurelinuxagent.utils.osutil import OSUTIL -class EventError(Exception): - pass class WALAEventOperation: HeartBeat="HeartBeat" @@ -48,132 +48,60 @@ class WALAEventOperation: ActivateResourceDisk="ActivateResourceDisk" UnhandledError="UnhandledError" -class EventMonitor(object): +class EventReporter(object): def __init__(self): - self.sysinfo = [] - self.event_dir = os.path.join(OSUTIL.get_lib_dir(), "events") + self.event_dir = None - def init_sysinfo(self): - osversion = "{0}:{1}-{2}-{3}:{4}".format(platform.system(), - DISTRO_NAME, - DISTRO_VERSION, - DISTRO_CODE_NAME, - platform.release()) + def save_event(self, data): + if not os.path.exists(self.event_dir): + os.mkdir(self.event_dir) + os.chmod(self.event_dir, 0o700) + if len(os.listdir(self.event_dir)) > 1000: + raise EventError("Too many files under: {0}", self.event_dir) - self.sysinfo.append(prot.TelemetryEventParam("OSVersion", osversion)) - self.sysinfo.append(prot.TelemetryEventParam("GAVersion", - AGENT_VERSION)) - self.sysinfo.append(prot.TelemetryEventParam("RAM", - OSUTIL.get_total_mem())) - self.sysinfo.append(prot.TelemetryEventParam("Processors", - OSUTIL.get_processor_cores())) + filename = os.path.join(self.event_dir, text(int(time.time()*1000000))) try: - protocol = PROT_FACTORY.get_protocol() - vminfo = protocol.get_vminfo() - self.sysinfo.append(prot.TelemetryEventParam("VMName", - vminfo.vmName)) - #TODO add other system info like, subscription id, etc. - except prot.ProtocolError as e: - logger.warn("Failed to get vm info: {0}", e) - - def start(self): - event_thread = threading.Thread(target = self.run) - event_thread.setDaemon(True) - event_thread.start() - - def collect_event(self, evt_file_name): - try: - logger.verb("Found event file: {0}", evt_file_name) - with open(evt_file_name, "rb") as evt_file: - #if fail to open or delete the file, throw exception - json_str = evt_file.read().decode("utf-8",'ignore') - logger.verb("Processed event file: {0}", evt_file_name) - os.remove(evt_file_name) - return json_str + with open(filename+".tmp",'wb+') as hfile: + hfile.write(data.encode("utf-8")) + os.rename(filename+".tmp", filename+".tld") except IOError as e: - msg = "Failed to process {0}, {1}".format(evt_file_name, e) - raise EventError(msg) + raise EventError("Failed to write events to file:{0}", e) - def collect_and_send_events(self): - event_list = prot.TelemetryEventList() - event_files = os.listdir(self.event_dir) - for event_file in event_files: - if not event_file.endswith(".tld"): - continue - event_file_path = os.path.join(self.event_dir, event_file) - try: - data_str = self.collect_event(event_file_path) - except EventError as e: - logger.error("{0}", e) - continue - try: - data = json.loads(data_str) - except ValueError as e: - logger.verb(data_str) - logger.verb("Failed to decode json event file: {0}", e) - continue + def add_event(self, name, op="", is_success=True, duration=0, version="1.0", + message="", evt_type="", is_internal=False): + log = logger.info if is_success else logger.error + log("Event: name={0}, op={1}, message={2}", name, op, message) + event = TelemetryEvent(1, "69B669B9-4AF8-4C50-BDC4-6006FA76E975") + event.parameters.append(TelemetryEventParam('Name', name)) + event.parameters.append(TelemetryEventParam('Version', version)) + event.parameters.append(TelemetryEventParam('IsInternal', is_internal)) + event.parameters.append(TelemetryEventParam('Operation', op)) + event.parameters.append(TelemetryEventParam('OperationSuccess', + is_success)) + event.parameters.append(TelemetryEventParam('Message', message)) + event.parameters.append(TelemetryEventParam('Duration', duration)) + event.parameters.append(TelemetryEventParam('ExtensionType', evt_type)) - event = prot.TelemetryEvent() - prot.set_properties("event", event, data) - event.parameters.extend(self.sysinfo) - event_list.events.append(event) - if len(event_list.events) == 0: - return - + data = get_properties(event) try: - protocol = PROT_FACTORY.get_protocol() - protocol.report_event(event_list) - except prot.ProtocolError as e: + self.save_event(json.dumps(data)) + except EventError as e: logger.error("{0}", e) - def run(self): - self.init_sysinfo() - last_heartbeat = datetime.datetime.min - period = datetime.timedelta(hours = 12) - while(True): - if (datetime.datetime.now()-last_heartbeat) > period: - last_heartbeat = datetime.datetime.now() - add_event(op=WALAEventOperation.HeartBeat, - name="WALA",is_success=True) - self.collect_and_send_events() - time.sleep(60) - -def save_event(data): - event_dir = os.path.join(OSUTIL.get_lib_dir(), 'events') - if not os.path.exists(event_dir): - os.mkdir(event_dir) - os.chmod(event_dir,0o700) - if len(os.listdir(event_dir)) > 1000: - raise EventError("Too many files under: {0}", event_dir) - - filename = os.path.join(event_dir, text(int(time.time()*1000000))) - try: - with open(filename+".tmp",'wb+') as hfile: - hfile.write(data.encode("utf-8")) - os.rename(filename+".tmp", filename+".tld") - except IOError as e: - raise EventError("Failed to write events to file:{0}", e) +__event_reporter__ = EventReporter() def add_event(name, op="", is_success=True, duration=0, version="1.0", - message="", evt_type="", is_internal=False): - log = logger.info if is_success else logger.error - log("Event: name={0}, op={1}, message={2}", name, op, message) - event = prot.TelemetryEvent(1, "69B669B9-4AF8-4C50-BDC4-6006FA76E975") - event.parameters.append(prot.TelemetryEventParam('Name', name)) - event.parameters.append(prot.TelemetryEventParam('Version', version)) - event.parameters.append(prot.TelemetryEventParam('IsInternal', is_internal)) - event.parameters.append(prot.TelemetryEventParam('Operation', op)) - event.parameters.append(prot.TelemetryEventParam('OperationSuccess', - is_success)) - event.parameters.append(prot.TelemetryEventParam('Message', message)) - event.parameters.append(prot.TelemetryEventParam('Duration', duration)) - event.parameters.append(prot.TelemetryEventParam('ExtensionType', evt_type)) + message="", evt_type="", is_internal=False, + reporter=__event_reporter__): + if reporter.event_dir is None: + logger.warn("Event reporter is not initialized.") + return + reporter.add_event(name, op=op, is_success=is_success, duration=duration, + version=version, message=message, evt_type=evt_type, + is_internal=is_internal) - data = prot.get_properties(event) - try: - save_event(json.dumps(data)) - except EventError as e: - logger.error("{0}", e) +def init_event_reporter(event_dir, reporter=__event_reporter__): + reporter.event_dir = event_dir def dump_unhandled_err(name): if hasattr(sys, 'last_type') and hasattr(sys, 'last_value') and \ @@ -185,8 +113,7 @@ def dump_unhandled_err(name): last_traceback) message= "".join(error) add_event(name, is_success=False, message=message, - op=WALAEventOperation.UnhandledError) + op=WALAEventOperation.UnhandledError) def enable_unhandled_err_dump(name): atexit.register(dump_unhandled_err, name) - diff --git a/azurelinuxagent/exception.py b/azurelinuxagent/exception.py index d7d9b0a..6f5e222 100644 --- a/azurelinuxagent/exception.py +++ b/azurelinuxagent/exception.py @@ -63,3 +63,44 @@ class ResourceDiskError(AgentError): def __init__(self, msg): super(ResourceDiskError, self).__init__('000005', msg) +class DhcpError(AgentError): + """ + Failed to handle dhcp response + """ + def __init__(self, msg): + super(DhcpError, self).__init__('000006', msg) + +class OSUtilError(AgentError): + """ + Failed to perform operation to OS configuration + """ + def __init__(self, msg): + super(OSUtilError, self).__init__('000007', msg) + +class ProtocolError(AgentError): + """ + Azure protocol error + """ + def __init__(self, msg): + super(ProtocolError, self).__init__('000008', msg) + +class ProtocolNotFoundError(ProtocolError): + """ + Azure protocol endpoint not found + """ + def __init__(self, msg): + super(ProtocolNotFoundError, self).__init__(msg) + +class HttpError(AgentError): + """ + Http request failure + """ + def __init__(self, msg): + super(HttpError, self).__init__('000009', msg) + +class EventError(AgentError): + """ + Event reporting error + """ + def __init__(self, msg): + super(EventError, self).__init__('000010', msg) diff --git a/azurelinuxagent/future.py b/azurelinuxagent/future.py index 8451345..8509732 100644 --- a/azurelinuxagent/future.py +++ b/azurelinuxagent/future.py @@ -7,15 +7,25 @@ Add alies for python2 and python3 libs and fucntions. if sys.version_info[0]== 3: import http.client as httpclient from urllib.parse import urlparse - text = str + + """Rename Python3 str to ustr""" + ustr = str + bytebuffer = memoryview + read_input = input + elif sys.version_info[0] == 2: import httplib as httpclient from urlparse import urlparse - text = unicode + + """Rename Python2 unicode to ustr""" + ustr = unicode + bytebuffer = buffer + read_input = raw_input + else: raise ImportError("Unknown python version:{0}".format(sys.version_info)) diff --git a/azurelinuxagent/handler.py b/azurelinuxagent/handler.py deleted file mode 100644 index 538ee30..0000000 --- a/azurelinuxagent/handler.py +++ /dev/null @@ -1,28 +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+ -# - -""" -Handler handles different tasks like, provisioning, deprovisioning etc. -The handlers could be extended for different distros. The default -implementation is under azurelinuxagent.distros.default -""" -import azurelinuxagent.distro.loader as loader - -HANDLERS = loader.get_handlers() - diff --git a/azurelinuxagent/protocol/__init__.py b/azurelinuxagent/protocol/__init__.py index b605ff7..8c1bbdb 100644 --- a/azurelinuxagent/protocol/__init__.py +++ b/azurelinuxagent/protocol/__init__.py @@ -16,5 +16,3 @@ # # Requires Python 2.4+ and Openssl 1.0+ # - -from azurelinuxagent.protocol.common import * diff --git a/azurelinuxagent/protocol/factory.py b/azurelinuxagent/protocol/factory.py deleted file mode 100644 index 0db385a..0000000 --- a/azurelinuxagent/protocol/factory.py +++ /dev/null @@ -1,126 +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 traceback -import time -import threading -import azurelinuxagent.logger as logger -from azurelinuxagent.exception import * -from azurelinuxagent.future import text -import azurelinuxagent.utils.fileutil as fileutil -from azurelinuxagent.utils.osutil import OSUTIL -from azurelinuxagent.protocol.common import * -from azurelinuxagent.protocol.v1 import WireProtocol -from azurelinuxagent.protocol.v2 import MetadataProtocol -from azurelinuxagent.protocol.ovfenv import TAG_FILE_NAME - -PROTOCOL_FILE_NAME = "Protocol" -MAX_RETRY = 60 - - -def _detect_protocol(protocols=[WireProtocol, MetadataProtocol]): - protocol_file_path = os.path.join(OSUTIL.get_lib_dir(), PROTOCOL_FILE_NAME) - if os.path.isfile(protocol_file_path): - os.remove(protocol_file_path) - for retry in range(0, MAX_RETRY): - for protocol_cls in protocols: - try: - logger.info("Detecting protocol: {0}", protocol_cls.__name__) - protocol = protocol_cls() - protocol.initialize() - logger.info("Found protocol: {0}", protocol_cls.__name__) - fileutil.write_file(protocol_file_path, protocol_cls.__name__) - return protocol - except ProtocolError as e: - logger.info("Protocol endpoint not found: {0}, {1}", - protocol_cls.__name__, e) - if retry < MAX_RETRY -1: - logger.info("Retry detect protocols: retry={0}", retry) - time.sleep(10) - raise ProtocolNotFound("No protocol found.") - -def _get_protocol(): - protocol_file_path = os.path.join(OSUTIL.get_lib_dir(), - PROTOCOL_FILE_NAME) - if not os.path.isfile(protocol_file_path): - raise ProtocolError("No protocl found") - - protocol_name = fileutil.read_file(protocol_file_path) - if protocol_name == WireProtocol.__name__: - protoc0l = WireProtocol() - else: - protocol = MetadataProtocol() - protocol.reinitialize() - return protocol - -class ProtocolFactory(object): - def __init__(self): - self.protocol = None - self.lock = threading.Lock() - - def detect_protocol(self): - logger.info("Detect protocol endpoints") - self.lock.acquire() - try: - if self.protocol is None: - self.protocol = _detect_protocol() - return self.protocol - finally: - self.lock.release() - - def detect_protocol_by_file(self): - logger.info("Detect protocol by file") - self.lock.acquire() - try: - tag_file_path = os.path.join(OSUTIL.get_lib_dir(), TAG_FILE_NAME) - if self.protocol is None: - if os.path.isfile(tag_file_path): - protocol = _detect_protocol(protocols=[MetadataProtocol]) - else: - protocol = _detect_protocol(protocols=[WireProtocol]) - self.protocol = protocol - return self.protocol - finally: - self.lock.release() - - def get_protocol(self): - """ - Get protocol detected - """ - self.lock.acquire() - try: - if self.protocol is None: - self.protocol = _get_protocol() - return self.protocol - finally: - self.lock.release() - return self.protocol - - def wait_for_network(self): - """ - Wait for network stack to be initialized - """ - ipv4 = OSUTIL.get_ip4_addr() - while ipv4 == '' or ipv4 == '0.0.0.0': - logger.info("Waiting for network.") - time.sleep(10) - OSUTIL.start_network() - ipv4 = OSUTIL.get_ip4_addr() - -PROT_FACTORY = ProtocolFactory() diff --git a/azurelinuxagent/protocol/v2.py b/azurelinuxagent/protocol/metadata.py similarity index 89% rename from azurelinuxagent/protocol/v2.py rename to azurelinuxagent/protocol/metadata.py index 3e95042..43632f7 100644 --- a/azurelinuxagent/protocol/v2.py +++ b/azurelinuxagent/protocol/metadata.py @@ -20,11 +20,13 @@ import json import shutil import os import time -from azurelinuxagent.utils.osutil import OSUTIL +from azurelinuxagent.exception import ProtocolError, HttpError from azurelinuxagent.future import httpclient, text +import azurelinuxagent.conf as conf +import azurelinuxagent.logger as logger import azurelinuxagent.utils.restutil as restutil import azurelinuxagent.utils.textutil as textutil -from azurelinuxagent.protocol.common import * +from azurelinuxagent.protocol.restapi import * ENDPOINT='169.254.169.254' #TODO use http for azure pack test @@ -47,7 +49,8 @@ def _add_content_type(headers): class MetadataProtocol(Protocol): - def __init__(self, apiversion=APIVERSION, endpoint=ENDPOINT): + def __init__(self, osutil, apiversion=APIVERSION, endpoint=ENDPOINT): + self.osutil = osutil self.apiversion = apiversion self.endpoint = endpoint self.identity_uri = BASE_URI.format(self.endpoint, "identity", @@ -70,7 +73,7 @@ class MetadataProtocol(Protocol): def _get_data(self, url, headers=None): try: resp = restutil.http_get(url, headers=headers) - except restutil.HttpError as e: + except HttpError as e: raise ProtocolError(text(e)) if resp.status != httpclient.OK: @@ -86,7 +89,7 @@ class MetadataProtocol(Protocol): headers = _add_content_type(headers) try: resp = restutil.http_put(url, json.dumps(data), headers=headers) - except restutil.HttpError as e: + except HttpError as e: raise ProtocolError(text(e)) if resp.status != httpclient.OK: raise ProtocolError("{0} - PUT: {1}".format(resp.status, url)) @@ -95,7 +98,7 @@ class MetadataProtocol(Protocol): headers = _add_content_type(headers) try: resp = restutil.http_post(url, json.dumps(data), headers=headers) - except restutil.HttpError as e: + except HttpError as e: raise ProtocolError(text(e)) if resp.status != httpclient.CREATED: raise ProtocolError("{0} - POST: {1}".format(resp.status, url)) @@ -107,25 +110,22 @@ class MetadataProtocol(Protocol): content = fileutil.read_file(file_name) return textutil.get_bytes_from_pem(content) - def initialize(self): + def detect(self): self.get_vminfo() - trans_prv_file = os.path.join(OSUTIL.get_lib_dir(), + trans_prv_file = os.path.join(conf.get_lib_dir(), TRANSPORT_PRV_FILE_NAME) - trans_crt_file = os.path.join(OSUTIL.get_lib_dir(), + trans_crt_file = os.path.join(conf.get_lib_dir(), TRANSPORT_CERT_FILE_NAME) - OSUTIL.gen_transport_cert(trans_prv_file, trans_crt_file) + self.osutil.gen_transport_cert(trans_prv_file, trans_crt_file) #"Install" the cert and private key to /var/lib/waagent - thumbprint = OSUTIL.get_thumbprint_from_crt(trans_crt_file) - prv_file = os.path.join(OSUTIL.get_lib_dir(), + thumbprint = self.osutil.get_thumbprint_from_crt(trans_crt_file) + prv_file = os.path.join(conf.get_lib_dir(), "{0}.prv".format(thumbprint)) - crt_file = os.path.join(OSUTIL.get_lib_dir(), + crt_file = os.path.join(conf.get_lib_dir(), "{0}.crt".format(thumbprint)) shutil.copyfile(trans_prv_file, prv_file) shutil.copyfile(trans_crt_file, crt_file) - - def reinitialize(self): - pass def get_vminfo(self): vminfo = VMInfo() diff --git a/azurelinuxagent/protocol/ovfenv.py b/azurelinuxagent/protocol/ovfenv.py index afd9a23..9681fe9 100644 --- a/azurelinuxagent/protocol/ovfenv.py +++ b/azurelinuxagent/protocol/ovfenv.py @@ -24,67 +24,15 @@ import re import shutil import xml.dom.minidom as minidom import azurelinuxagent.logger as logger +from azurelinuxagent.exception import ProtocolError from azurelinuxagent.future import text import azurelinuxagent.utils.fileutil as fileutil from azurelinuxagent.utils.textutil import parse_doc, findall, find, findtext -from azurelinuxagent.utils.osutil import OSUTIL, OSUtilError -from azurelinuxagent.protocol.common import ProtocolError - -OVF_FILE_NAME = "ovf-env.xml" - -#Tag file to indicate usage of metadata protocol -TAG_FILE_NAME = "useMetadataEndpoint.tag" OVF_VERSION = "1.0" OVF_NAME_SPACE = "http://schemas.dmtf.org/ovf/environment/1" WA_NAME_SPACE = "http://schemas.microsoft.com/windowsazure" -def get_ovf_env(): - """ - Load saved ovf-env.xml - """ - ovf_file_path = os.path.join(OSUTIL.get_lib_dir(), OVF_FILE_NAME) - if os.path.isfile(ovf_file_path): - xml_text = fileutil.read_file(ovf_file_path) - return OvfEnv(xml_text) - else: - raise ProtocolError("ovf-env.xml is missing.") - -def copy_ovf_env(): - """ - Copy ovf env file from dvd to hard disk. - Remove password before save it to the disk - """ - try: - OSUTIL.mount_dvd() - - ovf_file_path_on_dvd = OSUTIL.get_ovf_env_file_path_on_dvd() - ovfxml = fileutil.read_file(ovf_file_path_on_dvd, remove_bom=True) - ovfenv = OvfEnv(ovfxml) - ovfxml = re.sub(".*?<", "*<", ovfxml) - ovf_file_path = os.path.join(OSUTIL.get_lib_dir(), OVF_FILE_NAME) - fileutil.write_file(ovf_file_path, ovfxml) - - tag_file_path_on_dvd = os.path.join(OSUTIL.get_dvd_mount_point(), - TAG_FILE_NAME) - if os.path.isfile(tag_file_path_on_dvd): - logger.info("Found {0} in provisioning ISO", TAG_FILE_NAME) - tag_file_path = os.path.join(OSUTIL.get_lib_dir(), TAG_FILE_NAME) - shutil.copyfile(tag_file_path_on_dvd, tag_file_path) - - except IOError as e: - raise ProtocolError(text(e)) - except OSUtilError as e: - raise ProtocolError(text(e)) - - try: - OSUTIL.umount_dvd() - OSUTIL.eject_dvd() - except OSUtilError as e: - logger.warn(text(e)) - - return ovfenv - def _validate_ovf(val, msg): if val is None: raise ProtocolError("Failed to parse OVF XML: {0}".format(msg)) diff --git a/azurelinuxagent/protocol/common.py b/azurelinuxagent/protocol/restapi.py similarity index 97% rename from azurelinuxagent/protocol/common.py rename to azurelinuxagent/protocol/restapi.py index 0c57f13..ed310d5 100644 --- a/azurelinuxagent/protocol/common.py +++ b/azurelinuxagent/protocol/restapi.py @@ -22,15 +22,10 @@ import re import json import xml.dom.minidom import azurelinuxagent.logger as logger +from azurelinuxagent.exception import ProtocolError from azurelinuxagent.future import text import azurelinuxagent.utils.fileutil as fileutil -class ProtocolError(Exception): - pass - -class ProtocolNotFound(Exception): - pass - def validata_param(name, val, expected_type): if val is None: raise ProtocolError("{0} is None".format(name)) @@ -211,10 +206,7 @@ class TelemetryEventList(DataContract): class Protocol(DataContract): - def initialize(self): - raise NotImplementedError() - - def reinitialize(self): + def detect(self): raise NotImplementedError() def get_vminfo(self): diff --git a/azurelinuxagent/protocol/v1.py b/azurelinuxagent/protocol/wire.py similarity index 96% rename from azurelinuxagent/protocol/v1.py rename to azurelinuxagent/protocol/wire.py index 27052ae..80a911f 100644 --- a/azurelinuxagent/protocol/v1.py +++ b/azurelinuxagent/protocol/wire.py @@ -23,17 +23,18 @@ import time import traceback import xml.sax.saxutils as saxutils import xml.etree.ElementTree as ET +import azurelinuxagent.conf as conf import azurelinuxagent.logger as logger +from azurelinuxagent.exception import ProtocolError, HttpError, \ + ProtocolNotFoundError from azurelinuxagent.future import text, httpclient, bytebuffer import azurelinuxagent.utils.restutil as restutil import azurelinuxagent.utils.textutil as textutil from azurelinuxagent.utils.textutil import parse_doc, findall, find, findtext, \ getattrib, gettext, remove_bom -from azurelinuxagent.utils.osutil import OSUTIL import azurelinuxagent.utils.fileutil as fileutil import azurelinuxagent.utils.shellutil as shellutil -from azurelinuxagent.protocol.common import * -import azurelinuxagent.protocol.dhcp as dhcp +from azurelinuxagent.protocol.restapi import * VERSION_INFO_URI = "http://{0}/?comp=versions" GOAL_STATE_URI = "http://{0}/machine/?comp=goalstate" @@ -65,21 +66,17 @@ class WireProtocolResourceGone(ProtocolError): class WireProtocol(Protocol): - def __init__(self): - pass + def __init__(self, osutil, endpoint): + self.osutil = osutil + self.endpoint = endpoint + self.client = WireClient(self.endpoint) - def initialize(self): - dhcp_resp = dhcp.DHCPCLIENT.fetch_dhcp_resp() - self.client = WireClient(dhcp_resp.endpoint) + def detect(self): self.client.check_wire_protocol_version() - OSUTIL.gen_transport_cert(TRANSPORT_PRV_FILE_NAME, + self.osutil.gen_transport_cert(TRANSPORT_PRV_FILE_NAME, TRANSPORT_CERT_FILE_NAME) self.client.update_goal_state(forced=True) - def reinitialize(self): - dhcp_resp = dhcp.DHCPCLIENT.get_dhcp_resp() - self.client = WireClient(dhcp_resp.endpoint) - def get_vminfo(self): hosting_env = self.client.get_hosting_env() vminfo = VMInfo() @@ -314,7 +311,7 @@ class StatusBlob(object): self.put_page_blob(url, data) else: raise ProtocolError("Unknown blob type: {0}".format(blob_type)) - except restutil.HttpError as e: + except HttpError as e: raise ProtocolError("Failed to upload status blob: {0}".format(e)) def get_blob_type(self, url): @@ -326,7 +323,7 @@ class StatusBlob(object): "x-ms-date" : timestamp, 'x-ms-version' : self.__class__.__storage_version__ }) - except restutil.HttpError as e: + except HttpError as e: raise ProtocolError((u"Failed to get status blob type: {0}" u"").format(e)) if resp is None or resp.status != httpclient.OK: @@ -348,7 +345,7 @@ class StatusBlob(object): "Content-Length": text(len(data)), "x-ms-version" : self.__class__.__storage_version__ }) - except restutil.HttpError as e: + except HttpError as e: raise ProtocolError((u"Failed to upload block blob: {0}" u"").format(e)) if resp.status != httpclient.CREATED: @@ -373,7 +370,7 @@ class StatusBlob(object): "x-ms-blob-content-length" : text(page_blob_size), "x-ms-version" : self.__class__.__storage_version__ }) - except restutil.HttpError as e: + except HttpError as e: raise ProtocolError((u"Failed to clean up page blob: {0}" u"").format(e)) if resp.status != httpclient.CREATED: @@ -406,7 +403,7 @@ class StatusBlob(object): "x-ms-version" : self.__class__.__storage_version__, "Content-Length": text(page_end - start) }) - except restutil.HttpError as e: + except HttpError as e: raise ProtocolError((u"Failed to upload page blob: {0}" u"").format(e)) if resp is None or resp.status != httpclient.CREATED: @@ -506,7 +503,7 @@ class WireClient(object): try: resp = self.call_wireserver(restutil.http_get, uri, headers=headers) - except restutil.HttpError as e: + except HttpError as e: raise ProtocolError(text(e)) if(resp.status != httpclient.OK): @@ -550,7 +547,7 @@ class WireClient(object): resp = self.call_storage_service(restutil.http_get, version_uri.uri, None, chk_proxy=True) - except restutil.HttpError as e: + except HttpError as e: raise ProtocolError(text(e)) if resp.status == httpclient.OK: @@ -615,7 +612,7 @@ class WireClient(object): xml_text = self.fetch_config(uri, self.get_header()) goal_state = GoalState(xml_text) - incarnation_file = os.path.join(OSUTIL.get_lib_dir(), + incarnation_file = os.path.join(conf.get_lib_dir(), INCARNATION_FILE_NAME) if not forced: @@ -633,7 +630,7 @@ class WireClient(object): try: self.goal_state = goal_state file_name = GOAL_STATE_FILE_NAME.format(goal_state.incarnation) - goal_state_file = os.path.join(OSUTIL.get_lib_dir(), file_name) + goal_state_file = os.path.join(conf.get_lib_dir(), file_name) self.save_cache(goal_state_file, xml_text) self.save_cache(incarnation_file, goal_state.incarnation) self.update_hosting_env(goal_state) @@ -707,7 +704,7 @@ class WireClient(object): else: error = ("Agent supported wire protocol version: {0} was not " "advised by Fabric.").format(PROTOCOL_VERSION) - raise ProtocolNotFound(error) + raise ProtocolNotFoundError(error) def upload_status_blob(self): ext_conf = self.get_ext_conf() @@ -725,7 +722,7 @@ class WireClient(object): try: resp = self.call_wireserver(restutil.http_post, role_prop_uri, role_prop, headers = headers) - except restutil.HttpError as e: + except HttpError as e: raise ProtocolError((u"Failed to send role properties: {0}" u"").format(e)) if resp.status != httpclient.ACCEPTED: @@ -746,7 +743,7 @@ class WireClient(object): try: resp = self.call_wireserver(restutil.http_post, health_report_uri, health_report, headers = headers) - except restutil.HttpError as e: + except HttpError as e: raise ProtocolError((u"Failed to send provision status: {0}" u"").format(e)) if resp.status != httpclient.OK: @@ -764,7 +761,7 @@ class WireClient(object): try: header = self.get_header_for_xml_content() resp = self.call_wireserver(restutil.http_post, uri, data, header) - except restutil.HttpError as e: + except HttpError as e: raise ProtocolError("Failed to send events:{0}".format(e)) if resp.status != httpclient.OK: @@ -933,8 +930,8 @@ class Certificates(object): def __init__(self, client, xml_text): logger.verb("Load Certificates.xml") self.client = client - self.lib_dir = OSUTIL.get_lib_dir() - self.openssl_cmd = OSUTIL.get_openssl_cmd() + self.lib_dir = conf.get_lib_dir() + self.openssl_cmd = self.client.osutil.get_openssl_cmd() self.cert_list = CertList() self.parse(xml_text) @@ -980,15 +977,15 @@ class Certificates(object): begin_crt = True elif re.match(r'[-]+END.*KEY[-]+', line): tmp_file = self.write_to_tmp_file(index, 'prv', buf) - pub = OSUTIL.get_pubkey_from_prv(tmp_file) + pub = self.client.osutil.get_pubkey_from_prv(tmp_file) prvs[pub] = tmp_file buf = [] index += 1 begin_prv = False elif re.match(r'[-]+END.*CERTIFICATE[-]+', line): tmp_file = self.write_to_tmp_file(index, 'crt', buf) - pub = OSUTIL.get_pubkey_from_crt(tmp_file) - thumbprint = OSUTIL.get_thumbprint_from_crt(tmp_file) + pub = self.client.osutil.get_pubkey_from_crt(tmp_file) + thumbprint = self.client.osutil.get_thumbprint_from_crt(tmp_file) thumbprints[pub] = thumbprint #Rename crt with thumbprint as the file name crt = "{0}.crt".format(thumbprint) diff --git a/azurelinuxagent/utils/osutil.py b/azurelinuxagent/utils/osutil.py deleted file mode 100644 index 9de47e7..0000000 --- a/azurelinuxagent/utils/osutil.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+ -# - -""" -Load OSUtil implementation from azurelinuxagent.distro -""" -from azurelinuxagent.distro.default.osutil import OSUtilError -import azurelinuxagent.distro.loader as loader - -OSUTIL = loader.get_osutil() - diff --git a/azurelinuxagent/utils/restutil.py b/azurelinuxagent/utils/restutil.py index 2acfa57..5d7159c 100644 --- a/azurelinuxagent/utils/restutil.py +++ b/azurelinuxagent/utils/restutil.py @@ -21,8 +21,9 @@ import time import platform import os import subprocess -import azurelinuxagent.logger as logger import azurelinuxagent.conf as conf +import azurelinuxagent.logger as logger +from azurelinuxagent.exception import HttpError from azurelinuxagent.future import httpclient, urlparse """ @@ -31,9 +32,6 @@ REST api util functions RETRY_WAITING_INTERVAL = 10 -class HttpError(Exception): - pass - def _parse_url(url): o = urlparse(url) rel_uri = o.path @@ -51,8 +49,8 @@ def get_http_proxy(): Get http_proxy and https_proxy from environment variables. Username and password is not supported now. """ - host = conf.get("HttpProxy.Host", None) - port = conf.get("HttpProxy.Port", None) + host = conf.get_httpproxy_host() + port = conf.get_httpproxy_port() return (host, port) def _http_request(method, host, rel_uri, port=None, data=None, secure=False, diff --git a/config/waagent.conf b/config/waagent.conf index 639e723..98b481c 100644 --- a/config/waagent.conf +++ b/config/waagent.conf @@ -71,3 +71,18 @@ OS.OpensslPath=None # Detect Scvmm environment, default is n # DetectScvmmEnv=n + +# +# Lib.Dir=/var/lib/waagent + +# +# DVD.MountPoint=/mnt/cdrom/secure + +# +# Pid.File=/var/run/waagent.pid + +# +# Extension.LogDir=/var/log/azure + +# +# Home.Dir=/home