summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorBen Howard <ben.howard@ubuntu.com>2016-02-08 16:33:07 -0700
committerusd-importer <ubuntu-server@lists.ubuntu.com>2016-02-09 00:59:05 +0000
commita00729ff7421b3661e8b1a1e0fa46393379f2e96 (patch)
tree4563b927e3a57446a4a928a72a92d72c9ad4f6e6
parent53f54030cae2de3d5fa474a61fe51f16c7a07c79 (diff)
downloadvyos-walinuxagent-a00729ff7421b3661e8b1a1e0fa46393379f2e96.tar.gz
vyos-walinuxagent-a00729ff7421b3661e8b1a1e0fa46393379f2e96.zip
Import patches-unapplied version 2.1.3-0ubuntu1 to ubuntu/xenial-proposed
Imported using git-ubuntu import. Changelog parent: 53f54030cae2de3d5fa474a61fe51f16c7a07c79 New changelog entries: * New upstream release (LP: #1543359): - Bug fixes for extension handling - Feature enablement for AzureStack.
-rw-r--r--.gitignore3
-rw-r--r--Changelog7
-rw-r--r--README.md (renamed from README)50
-rw-r--r--azurelinuxagent/agent.py94
-rw-r--r--azurelinuxagent/conf.py115
-rw-r--r--azurelinuxagent/distro/centos/loader.py25
-rw-r--r--azurelinuxagent/distro/coreos/deprovision.py3
-rw-r--r--azurelinuxagent/distro/coreos/distro.py (renamed from azurelinuxagent/distro/coreos/handlerFactory.py)12
-rw-r--r--azurelinuxagent/distro/coreos/osutil.py5
-rw-r--r--azurelinuxagent/distro/debian/distro.py (renamed from azurelinuxagent/distro/coreos/loader.py)13
-rw-r--r--azurelinuxagent/distro/default/daemon.py103
-rw-r--r--azurelinuxagent/distro/default/deprovision.py31
-rw-r--r--azurelinuxagent/distro/default/dhcp.py194
-rw-r--r--azurelinuxagent/distro/default/distro.py51
-rw-r--r--azurelinuxagent/distro/default/env.py41
-rw-r--r--azurelinuxagent/distro/default/extension.py943
-rw-r--r--azurelinuxagent/distro/default/handlerFactory.py40
-rw-r--r--azurelinuxagent/distro/default/init.py22
-rw-r--r--azurelinuxagent/distro/default/monitor.py182
-rw-r--r--azurelinuxagent/distro/default/osutil.py157
-rw-r--r--azurelinuxagent/distro/default/protocolUtil.py243
-rw-r--r--azurelinuxagent/distro/default/provision.py158
-rw-r--r--azurelinuxagent/distro/default/resourceDisk.py21
-rw-r--r--azurelinuxagent/distro/default/run.py71
-rw-r--r--azurelinuxagent/distro/default/scvmm.py11
-rw-r--r--azurelinuxagent/distro/loader.py71
-rw-r--r--azurelinuxagent/distro/oracle/loader.py25
-rw-r--r--azurelinuxagent/distro/redhat/distro.py (renamed from azurelinuxagent/distro/ubuntu/handlerFactory.py)17
-rw-r--r--azurelinuxagent/distro/redhat/loader.py28
-rw-r--r--azurelinuxagent/distro/redhat/osutil.py78
-rw-r--r--azurelinuxagent/distro/suse/distro.py (renamed from azurelinuxagent/distro/default/loader.py)16
-rw-r--r--azurelinuxagent/distro/suse/loader.py29
-rw-r--r--azurelinuxagent/distro/ubuntu/deprovision.py3
-rw-r--r--azurelinuxagent/distro/ubuntu/distro.py55
-rw-r--r--azurelinuxagent/distro/ubuntu/loader.py40
-rw-r--r--azurelinuxagent/distro/ubuntu/osutil.py12
-rw-r--r--azurelinuxagent/distro/ubuntu/provision.py71
-rw-r--r--azurelinuxagent/event.py171
-rw-r--r--azurelinuxagent/exception.py74
-rw-r--r--azurelinuxagent/future.py14
-rw-r--r--azurelinuxagent/handler.py28
-rw-r--r--azurelinuxagent/logger.py8
-rw-r--r--azurelinuxagent/metadata.py6
-rw-r--r--azurelinuxagent/protocol/__init__.py5
-rw-r--r--azurelinuxagent/protocol/metadata.py (renamed from azurelinuxagent/protocol/v2.py)90
-rw-r--r--azurelinuxagent/protocol/ovfenv.py46
-rw-r--r--azurelinuxagent/protocol/protocolFactory.py114
-rw-r--r--azurelinuxagent/protocol/restapi.py (renamed from azurelinuxagent/protocol/common.py)38
-rw-r--r--azurelinuxagent/protocol/wire.py (renamed from azurelinuxagent/protocol/v1.py)196
-rw-r--r--azurelinuxagent/utils/cryptutil.py121
-rw-r--r--azurelinuxagent/utils/fileutil.py7
-rw-r--r--azurelinuxagent/utils/osutil.py27
-rw-r--r--azurelinuxagent/utils/restutil.py22
-rw-r--r--azurelinuxagent/utils/shellutil.py6
-rw-r--r--azurelinuxagent/utils/textutil.py8
-rw-r--r--[-rwxr-xr-x]bin/waagent2.0394
-rw-r--r--config/coreos/waagent.conf91
-rw-r--r--config/suse/waagent.conf3
-rw-r--r--config/ubuntu/waagent.conf3
-rw-r--r--config/waagent.conf18
-rw-r--r--debian/changelog8
-rw-r--r--debian/docs4
-rw-r--r--debian/patches/disable-udev-rules-removal.patch12
-rw-r--r--debian/patches/disable_import_test.patch21
-rw-r--r--debian/patches/disable_rhel_tests.patch13
-rw-r--r--debian/patches/disable_udev_overrides.patch37
-rw-r--r--debian/patches/series3
-rwxr-xr-xdebian/rules4
-rwxr-xr-xsetup.py7
-rw-r--r--tests/data/dhcp (renamed from tests/dhcp)bin328 -> 328 bytes
-rwxr-xr-xtests/data/ext/event.xml1
-rw-r--r--tests/data/ext/sample_ext.zipbin0 -> 878 bytes
-rw-r--r--tests/data/ext/sample_ext/HandlerManifest.json14
-rwxr-xr-xtests/data/ext/sample_ext/sample.py33
-rw-r--r--tests/data/metadata/certificates.json7
-rw-r--r--tests/data/metadata/ext_handler_pkgs.json10
-rw-r--r--tests/data/metadata/ext_handlers.json19
-rw-r--r--tests/data/metadata/ext_handlers_no_ext.json1
-rw-r--r--tests/data/metadata/identity.json4
-rw-r--r--tests/data/ovf-env.xml29
-rw-r--r--tests/data/wire/certs.xml81
-rw-r--r--tests/data/wire/ext_conf.xml46
-rw-r--r--tests/data/wire/ext_conf_no_public.xml46
-rw-r--r--tests/data/wire/ext_conf_no_settings.xml41
-rw-r--r--tests/data/wire/goal_state.xml27
-rw-r--r--tests/data/wire/goal_state_no_ext.xml26
-rw-r--r--tests/data/wire/hosting_env.xml (renamed from tests/test_hostingenv.py)44
-rw-r--r--tests/data/wire/manifest.xml18
-rw-r--r--tests/data/wire/shared_config.xml (renamed from tests/test_sharedconfig.py)39
-rw-r--r--tests/data/wire/sshd_config (renamed from tests/sshd_config)0
-rw-r--r--tests/data/wire/trans_cert19
-rw-r--r--tests/data/wire/trans_prv28
-rw-r--r--tests/data/wire/version_info.xml10
-rw-r--r--tests/distro/__init__.py (renamed from tests/env.py)7
-rw-r--r--tests/distro/test_daemon.py65
-rw-r--r--tests/distro/test_extension.py191
-rw-r--r--tests/distro/test_loader.py (renamed from tests/test_future.py)23
-rw-r--r--tests/distro/test_monitor.py (renamed from tests/test_protocolFactory.py)23
-rw-r--r--tests/distro/test_protocol_util.py89
-rw-r--r--tests/distro/test_provision.py (renamed from tests/test_agent.py)40
-rw-r--r--tests/protocol/__init__.py (renamed from azurelinuxagent/distro/centos/__init__.py)6
-rw-r--r--tests/protocol/mockmetadata.py61
-rw-r--r--tests/protocol/mockwiredata.py101
-rw-r--r--tests/protocol/test_metadata.py48
-rw-r--r--tests/protocol/test_restapi.py (renamed from tests/test_datacontract.py)3
-rw-r--r--tests/protocol/test_wire.py85
-rwxr-xr-xtests/run_all.sh35
-rw-r--r--tests/test.crt17
-rw-r--r--tests/test.prv15
-rw-r--r--tests/test_certificates.py199
-rw-r--r--tests/test_conf.py69
-rw-r--r--tests/test_deprovision.py54
-rw-r--r--tests/test_dhcp.py68
-rw-r--r--tests/test_distroLoader.py42
-rw-r--r--tests/test_envmon.py52
-rw-r--r--tests/test_event.py53
-rw-r--r--tests/test_ext.py223
-rw-r--r--tests/test_extensionsconfig.py159
-rw-r--r--tests/test_goalstate.py71
-rw-r--r--tests/test_import_waagent.py40
-rw-r--r--tests/test_logger.py100
-rw-r--r--tests/test_metadata.py36
-rw-r--r--tests/test_osutil.py174
-rw-r--r--tests/test_ovfxml.py81
-rw-r--r--tests/test_protocol.py72
-rw-r--r--tests/test_redhat.py49
-rw-r--r--tests/test_resourcedisk.py63
-rw-r--r--tests/test_rest_util.py63
-rw-r--r--tests/test_v1.py203
-rw-r--r--tests/test_v2.py120
-rw-r--r--tests/test_version.py53
-rw-r--r--tests/tools.py133
-rw-r--r--tests/utils/__init__.py (renamed from azurelinuxagent/distro/oracle/__init__.py)6
-rw-r--r--tests/utils/test_file_util.py (renamed from tests/test_file_util.py)32
-rw-r--r--tests/utils/test_rest_util.py126
-rw-r--r--tests/utils/test_shell_util.py (renamed from tests/test_shell_util.py)6
-rw-r--r--tests/utils/test_text_util.py (renamed from tests/test_text_util.py)23
137 files changed, 4093 insertions, 4268 deletions
diff --git a/.gitignore b/.gitignore
index db1491b..6860f76 100644
--- a/.gitignore
+++ b/.gitignore
@@ -60,3 +60,6 @@ waagentc
waagentc
bin/waagent2.0c
+
+# rope project
+.ropeproject/
diff --git a/Changelog b/Changelog
index bc79449..8a9488d 100644
--- a/Changelog
+++ b/Changelog
@@ -1,5 +1,12 @@
WALinuxAgent Changelog
|||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
+29 Jan 2016, WALinuxAgent 2.1.3
+ . Fixed endpoint probing for Azure Stack
+ . Multiple fixes for extension handling
+
+07 Dec 2015, WALinuxAgent 2.1.2
+ . Multiple fixes for extension handling and provisioning
+
07 Aug 2015, WALinuxAgent 2.1.1
. Support python3
. Fixed bugs for metadata protocol
diff --git a/README b/README.md
index a713495..f40e311 100644
--- a/README
+++ b/README.md
@@ -1,6 +1,6 @@
-Microsoft Azure Linux Agent README
+## Microsoft Azure Linux Agent README
-INTRODUCTION
+### INTRODUCTION
The Microsoft Azure Linux Agent (waagent) manages Linux & FreeBSD provisioning,
and VM interaction with the Azure Fabric Controller. It provides the following
@@ -39,7 +39,7 @@ functionality for Linux and FreeBSD IaaS deployments:
- VM Extension reference implementation on https://github.com/Azure/azure-linux-extensions
-COMMUNICATION
+### COMMUNICATION
The information flow from the platform to the agent occurs via two channels:
@@ -51,7 +51,7 @@ The information flow from the platform to the agent occurs via two channels:
configuration.
-REQUIREMENTS
+### REQUIREMENTS
The following systems have been tested and are known to work with the Azure
Linux Agent. Please note that this list may differ from the official list
@@ -81,14 +81,14 @@ Waagent depends on some system packages in order to function properly:
* Network tools: ip-route
-INSTALLATION
+### INSTALLATION
Installation via your distribution's package repository is preferred.
You can also customize your own RPM or DEB packages using the configuration
files provided (see debian/README and rpm/README).
For more advanced installation options, such as installing to custom locations
-or prefixes, you can use setuptools to install from source by running:
+or prefixes, you can use ***setuptools*** to install from source by running:
#sudo python setup.py install --register-service
@@ -98,7 +98,7 @@ You can view more installation options by running:
The agent's log file is kept at /var/log/waagent.log.
-UPGRADE
+### UPGRADE
Upgrading via your distribution's package repository is preferred.
@@ -121,7 +121,7 @@ For CoreOS, use:
The agent's log file is kept at /var/log/waagent.log.
-COMMAND LINE OPTIONS
+### COMMAND LINE OPTIONS
Flags:
@@ -133,25 +133,6 @@ Commands:
-help: Lists the supported commands and flags.
- -install: Manual installation of the agent
- * Checks the system for required dependencies
- * Creates the SysV init script (/etc/init.d/waagent), the logrotate
- configuration file (/etc/logrotate.d/waagent) and configures the image
- to run the init script on boot
- * Writes sample configuration file to /etc/waagent.conf
- * Any existing configuration file is moved to /etc/waagent.conf.old
- * Detects kernel version and applies the VNUMA workaround if necessary
- * Moves udev rules that may interfere with networking
- (/lib/udev/rules.d/75-persistent-net-generator.rules,
- /etc/udev/rules.d/70-persistent-net.rules) to /var/lib/waagent/
-
- -uninstall: Unregisters the init script from the system and deletes it.
- Deletes the logrotate configuration and the waagent config file in
- /etc/waagent.conf. Automatic reverting of the VNUMA workaround is not
- supported, please edit the GRUB configuration files by hand to re-enable
- NUMA if required. Restores any moved udev rules that were moved during
- installation.
-
-deprovision: Attempt to clean the system and make it suitable for
re-provisioning. Deletes the following:
* All SSH host keys
@@ -180,15 +161,12 @@ Commands:
-start: Run waagent as a background process
-CONFIGURATION
+### CONFIGURATION
A configuration file (/etc/waagent.conf) controls the actions of
waagent. A sample configuration file is shown below:
-#
-# Azure Linux Agent Configuration
-#
-
+```
Role.StateConsumer=None
Role.ConfigurationConsumer=None
Role.TopologyConsumer=None
@@ -212,6 +190,7 @@ OS.RootDeviceScsiTimeout=300
OS.OpensslPath=None
HttpProxy.Host=None
HttpProxy.Port=None
+```
The various configuration options are described in detail below. Configuration
options are of three types : Boolean, String or Integer. The Boolean
@@ -379,10 +358,12 @@ Type: String Default: None
If set, agent will use proxy server to access internet
-APPENDIX
+### APPENDIX
Sample Role Configuration File:
+```
+
<?xml version="1.0" encoding="utf-8"?> <HostingEnvironmentConfig
version="1.0.0.0" goalStateIncarnation="1">
<StoredCertificates>
@@ -440,9 +421,11 @@ version="1.0.0.0" goalStateIncarnation="1">
disableQuota="false" />
</ResourceReferences>
</HostingEnvironmentConfig>
+```
Sample Role Topology File:
+```
<?xml version="1.0" encoding="utf-8"?> <SharedConfig
version="1.0.0.0" goalStateIncarnation="2">
<Deployment name="a99549a92e38498f98cf2989330cd2f1"
@@ -544,3 +527,4 @@ version="1.0.0.0" goalStateIncarnation="2">
</Instance>
</Instances>
</SharedConfig>
+```
diff --git a/azurelinuxagent/agent.py b/azurelinuxagent/agent.py
index 849a192..93e9c16 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(self, force=False, deluser=False):
+ """
+ Run deprovision command
+ """
+ self.distro.deprovision_handler.run(force=force, deluser=deluser)
-def deprovision(force=False, deluser=False):
+ 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():
"""
- 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(sys.argv[1:])
+ 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..7921e79 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,87 @@ 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_logs_verbose(conf=__conf__):
+ return conf.get_switch("Logs.Verbose", False)
-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_lib_dir(conf=__conf__):
+ return conf.get("Lib.Dir", "/var/lib/waagent")
-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_dvd_mount_point(conf=__conf__):
+ return conf.get("DVD.MountPoint", "/mnt/cdrom/secure")
+
+def get_agent_pid_file_path(conf=__conf__):
+ return conf.get("Pid.File", "/var/run/waagent.pid")
+
+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("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_allow_reset_sys_user(conf=__conf__):
+ return conf.get_switch("Provisioning.AllowResetSysUser", False)
+
+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_decode_customdata(conf=__conf__):
+ return conf.get_switch("Provisioning.DecodeCustomData", False)
+
+def get_execute_customdata(conf=__conf__):
+ return conf.get_switch("Provisioning.ExecuteCustomData", 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.Filesystem", "ext3")
+
+def get_resourcedisk_swap_size_mb(conf=__conf__):
+ return conf.get_int("ResourceDisk.SwapSizeMB", 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 99d3a40..9642579 100644
--- a/azurelinuxagent/distro/coreos/deprovision.py
+++ b/azurelinuxagent/distro/coreos/deprovision.py
@@ -21,6 +21,9 @@ import azurelinuxagent.utils.fileutil as fileutil
from azurelinuxagent.distro.default.deprovision import DeprovisionHandler, DeprovisionAction
class CoreOSDeprovisionHandler(DeprovisionHandler):
+ def __init__(self, distro):
+ self.distro = distro
+
def setup(self, deluser):
warnings, actions = super(CoreOSDeprovisionHandler, self).setup(deluser)
warnings.append("WARNING! /etc/machine-id will be removed.")
diff --git a/azurelinuxagent/distro/coreos/handlerFactory.py b/azurelinuxagent/distro/coreos/distro.py
index 58f476c..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__()
- self.deprovision_handler = CoreOSDeprovisionHandler()
+ super(CoreOSDistro, self).__init__()
+ self.osutil = CoreOSUtil()
+ self.deprovision_handler = CoreOSDeprovisionHandler(self)
diff --git a/azurelinuxagent/distro/coreos/osutil.py b/azurelinuxagent/distro/coreos/osutil.py
index c244311..ffc83e3 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:
@@ -85,9 +85,6 @@ class CoreOSUtil(DefaultOSUtil):
ret= shellutil.run_get_output("pidof systemd-networkd")
return ret[1] if ret[0] == 0 else None
- def decode_customdata(self, data):
- return base64.b64decode(data)
-
def set_ssh_client_alive_interval(self):
#In CoreOS, /etc/sshd_config is mount readonly. Skip the setting
pass
diff --git a/azurelinuxagent/distro/coreos/loader.py b/azurelinuxagent/distro/debian/distro.py
index 802f276..01f4e3e 100644
--- a/azurelinuxagent/distro/coreos/loader.py
+++ b/azurelinuxagent/distro/debian/distro.py
@@ -17,12 +17,11 @@
# Requires Python 2.4+ and Openssl 1.0+
#
+from azurelinuxagent.distro.default.distro import DefaultDistro
+from azurelinuxagent.distro.debian.osutil import DebianOSUtil
-def get_osutil():
- from azurelinuxagent.distro.coreos.osutil import CoreOSUtil
- return CoreOSUtil()
-
-def get_handlers():
- from azurelinuxagent.distro.coreos.handlerFactory import CoreOSHandlerFactory
- return CoreOSHandlerFactory()
+class DebianDistro(DefaultDistro):
+ def __init__(self):
+ super(DebianDistro, self).__init__()
+ self.osutil = DebianOSUtil()
diff --git a/azurelinuxagent/distro/default/daemon.py b/azurelinuxagent/distro/default/daemon.py
new file mode 100644
index 0000000..cf9eb16
--- /dev/null
+++ b/azurelinuxagent/distro/default/daemon.py
@@ -0,0 +1,103 @@
+# 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 time
+import sys
+import traceback
+import azurelinuxagent.conf as conf
+import azurelinuxagent.logger as logger
+from azurelinuxagent.future import ustr
+from azurelinuxagent.event import add_event, WALAEventOperation
+from azurelinuxagent.exception import ProtocolError
+from azurelinuxagent.metadata import AGENT_LONG_NAME, AGENT_VERSION, \
+ DISTRO_NAME, DISTRO_VERSION, \
+ DISTRO_FULL_NAME, PY_VERSION_MAJOR, \
+ PY_VERSION_MINOR, PY_VERSION_MICRO
+import azurelinuxagent.event as event
+import azurelinuxagent.utils.fileutil as fileutil
+
+
+class DaemonHandler(object):
+ def __init__(self, distro):
+ self.distro = distro
+ self.running = True
+
+
+ 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)
+
+ self.check_pid()
+
+ while self.running:
+ try:
+ self.daemon()
+ except Exception as e:
+ err_msg = traceback.format_exc()
+ add_event("WALA", is_success=False, message=ustr(err_msg),
+ op=WALAEventOperation.UnhandledError)
+ logger.info("Sleep 15 seconds and restart daemon")
+ time.sleep(15)
+
+ def check_pid(self):
+ """Check whether daemon is already running"""
+ pid = None
+ pid_file = conf.get_agent_pid_file_path()
+ if os.path.isfile(pid_file):
+ pid = fileutil.read_file(pid_file)
+
+ if pid is not None and os.path.isdir(os.path.join("/proc", pid)):
+ logger.info("Daemon is already running: {0}", pid)
+ sys.exit(0)
+
+ fileutil.write_file(pid_file, ustr(os.getpid()))
+
+ def daemon(self):
+ logger.info("Run daemon")
+ #Create lib dir
+ if not os.path.isdir(conf.get_lib_dir()):
+ fileutil.mkdir(conf.get_lib_dir(), mode=0o700)
+ os.chdir(conf.get_lib_dir())
+
+ if conf.get_detect_scvmm_env():
+ if self.distro.scvmm_handler.run():
+ return
+
+ self.distro.provision_handler.run()
+
+ if conf.get_resourcedisk_format():
+ self.distro.resource_disk_handler.run()
+
+ try:
+ protocol = self.distro.protocol_util.detect_protocol()
+ except ProtocolError as e:
+ logger.error("Failed to detect protocol, exit", e)
+ return
+
+ self.distro.event_handler.run()
+ self.distro.env_handler.run()
+
+ while self.running:
+ #Handle extensions
+ self.distro.ext_handlers_handler.run()
+ time.sleep(25)
+
diff --git a/azurelinuxagent/distro/default/deprovision.py b/azurelinuxagent/distro/default/deprovision.py
index b62c5f6..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,18 +33,20 @@ class DeprovisionAction(object):
self.func(*self.args, **self.kwargs)
class DeprovisionHandler(object):
+ 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
@@ -54,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):
@@ -64,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']
@@ -76,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)
@@ -106,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/distro/default/dhcp.py b/azurelinuxagent/distro/default/dhcp.py
index 4fd23ef..fc439d2 100644
--- a/azurelinuxagent/distro/default/dhcp.py
+++ b/azurelinuxagent/distro/default/dhcp.py
@@ -19,61 +19,106 @@ import os
import socket
import array
import time
+import threading
import azurelinuxagent.logger as logger
-from azurelinuxagent.utils.osutil import OSUTIL
-from azurelinuxagent.exception import AgentNetworkError
+import azurelinuxagent.conf as conf
import azurelinuxagent.utils.fileutil as fileutil
import azurelinuxagent.utils.shellutil as shellutil
-from azurelinuxagent.utils.textutil import *
+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
-WIRE_SERVER_ADDR_FILE_NAME="WireServer"
class DhcpHandler(object):
- def __init__(self):
+ """
+ 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.routes = 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()
+
def wait_for_network(self):
- ipv4 = OSUTIL.get_ip4_addr()
+ """
+ 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)
- OSUTIL.start_network()
- ipv4 = OSUTIL.get_ip4_addr()
-
- def probe(self):
- logger.info("Send dhcp request")
- self.wait_for_network()
- mac_addr = OSUTIL.get_mac_addr()
- req = build_dhcp_request(mac_addr)
- resp = send_dhcp_request(req)
- if resp is None:
- logger.warn("Failed to detect wire server.")
- return
- endpoint, gateway, routes = parse_dhcp_resp(resp)
- self.endpoint = endpoint
- logger.info("Wire server endpoint:{0}", endpoint)
- logger.info("Gateway:{0}", gateway)
- logger.info("Routes:{0}", routes)
- if endpoint is not None:
- path = os.path.join(OSUTIL.get_lib_dir(), WIRE_SERVER_ADDR_FILE_NAME)
- fileutil.write_file(path, endpoint)
- self.gateway = gateway
- self.routes = routes
- self.conf_routes()
-
- def get_endpoint(self):
- return self.endpoint
+ 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")
+ logger.info("Gateway:{0}", self.gateway)
+ 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 _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 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)
+
+ # 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)
+
+ # 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()
+
+ resp = self._send_dhcp_req(req)
+
+ if self.distro.osutil.is_dhcp_enabled():
+ self.distro.osutil.start_dhcp_service()
+
+ 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)
@@ -92,28 +137,25 @@ 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 AgentNetworkError("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 AgentNetworkError("TransactionID in dhcp respones "
- "doesn't match the request")
+ 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 AgentNetworkError("Mac Addr in dhcp respones "
- "doesn't match the request")
+ raise DhcpError("Mac Addr in dhcp respones "
+ "doesn't match the request")
def parse_route(response, option, i, length, bytes_recv):
# http://msdn.microsoft.com/en-us/library/cc227282%28PROT.10%29.aspx
- logger.verb("Routes at offset: {0} with length:{1}",
- hex(i),
- hex(length))
+ logger.verb("Routes at offset: {0} with length:{1}", hex(i), hex(length))
routes = []
if length < 5:
logger.error("Data too small for option:{0}", option)
@@ -169,9 +211,7 @@ def parse_dhcp_resp(response):
if (i + 1) < bytes_recv:
length = str_to_ord(response[i + 1])
logger.verb("DHCP option {0} at offset:{1} with length:{2}",
- hex(option),
- hex(i),
- hex(length))
+ hex(option), hex(i), hex(length))
if option == 255:
logger.verb("DHCP packet ended at offset:{0}", hex(i))
break
@@ -179,69 +219,17 @@ def parse_dhcp_resp(response):
routes = parse_route(response, option, i, length, bytes_recv)
elif option == 3:
gateway = parse_ip_addr(response, option, i, length, bytes_recv)
- logger.verb("Default gateway:{0}, at {1}",
- gateway,
- hex(i))
+ logger.verb("Default gateway:{0}, at {1}", gateway, hex(i))
elif option == 245:
endpoint = parse_ip_addr(response, option, i, length, bytes_recv)
- logger.verb("Azure wire protocol endpoint:{0}, at {1}",
- gateway,
- hex(i))
+ logger.verb("Azure wire protocol endpoint:{0}, at {1}", gateway,
+ hex(i))
else:
logger.verb("Skipping DHCP option:{0} at {1} with length {2}",
- hex(option),
- hex(i),
- hex(length))
+ hex(option), hex(i), hex(length))
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 AgentNetworkError as e:
- logger.warn("Failed to send DHCP request: {0}", e)
- time.sleep(duration)
- return None
-
def socket_send(request):
sock = None
try:
@@ -257,7 +245,7 @@ def socket_send(request):
response = sock.recv(1024)
return response
except IOError as e:
- raise AgentNetworkError("{0}".format(e))
+ raise DhcpError("{0}".format(e))
finally:
if sock is not None:
sock.close()
diff --git a/azurelinuxagent/distro/default/distro.py b/azurelinuxagent/distro/default/distro.py
new file mode 100644
index 0000000..ca0d77e
--- /dev/null
+++ b/azurelinuxagent/distro/default/distro.py
@@ -0,0 +1,51 @@
+# Microsoft Azure Linux Agent
+#
+# Copyright 2014 Microsoft Corporation
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+#
+# Requires Python 2.4+ and Openssl 1.0+
+#
+
+from azurelinuxagent.conf import ConfigurationProvider
+from azurelinuxagent.distro.default.osutil import DefaultOSUtil
+from azurelinuxagent.distro.default.daemon import DaemonHandler
+from azurelinuxagent.distro.default.init import InitHandler
+from azurelinuxagent.distro.default.monitor import MonitorHandler
+from azurelinuxagent.distro.default.dhcp import DhcpHandler
+from azurelinuxagent.distro.default.protocolUtil import ProtocolUtil
+from azurelinuxagent.distro.default.scvmm import ScvmmHandler
+from azurelinuxagent.distro.default.env import EnvHandler
+from azurelinuxagent.distro.default.provision import ProvisionHandler
+from azurelinuxagent.distro.default.resourceDisk import ResourceDiskHandler
+from azurelinuxagent.distro.default.extension import ExtHandlersHandler
+from azurelinuxagent.distro.default.deprovision import DeprovisionHandler
+
+class DefaultDistro(object):
+ """
+ """
+ def __init__(self):
+ self.osutil = DefaultOSUtil()
+ self.protocol_util = ProtocolUtil(self)
+
+ self.init_handler = InitHandler(self)
+ self.daemon_handler = DaemonHandler(self)
+ self.event_handler = MonitorHandler(self)
+ self.dhcp_handler = DhcpHandler(self)
+ self.scvmm_handler = ScvmmHandler(self)
+ self.env_handler = EnvHandler(self)
+ self.provision_handler = ProvisionHandler(self)
+ self.resource_disk_handler = ResourceDiskHandler(self)
+ self.ext_handlers_handler = ExtHandlersHandler(self)
+ self.deprovision_handler = DeprovisionHandler(self)
+
diff --git a/azurelinuxagent/distro/default/env.py b/azurelinuxagent/distro/default/env.py
index 28bf718..7878cff 100644
--- a/azurelinuxagent/distro/default/env.py
+++ b/azurelinuxagent/distro/default/env.py
@@ -23,7 +23,6 @@ import threading
import time
import azurelinuxagent.logger as logger
import azurelinuxagent.conf as conf
-from azurelinuxagent.utils.osutil import OSUTIL
class EnvHandler(object):
"""
@@ -31,35 +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.monitor = EnvMonitor(handlers.dhcp_handler)
-
- def start(self):
- self.monitor.start()
-
- def stop(self):
- self.monitor.stop()
-
-class EnvMonitor(object):
-
- def __init__(self, dhcp_handler):
- self.dhcp_handler = dhcp_handler
+ def __init__(self, distro):
+ self.distro = distro
self.stopped = True
self.hostname = None
self.dhcpid = None
self.server_thread=None
- def start(self):
+ 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.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()
@@ -70,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)
@@ -84,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.osutil.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_handler.conf_routes()
+ self.distro.dhcp_handler.conf_routes()
self.dhcpid = newpid
def stop(self):
diff --git a/azurelinuxagent/distro/default/extension.py b/azurelinuxagent/distro/default/extension.py
index f6c02aa..82cdfed 100644
--- a/azurelinuxagent/distro/default/extension.py
+++ b/azurelinuxagent/distro/default/extension.py
@@ -22,13 +22,16 @@ 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
-import azurelinuxagent.protocol 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, HttpError
+from azurelinuxagent.future import ustr
+from azurelinuxagent.metadata import AGENT_VERSION
+from azurelinuxagent.protocol.restapi import ExtHandlerStatus, ExtensionStatus, \
+ ExtensionSubStatus, Extension, \
+ VMStatus, ExtHandler, \
+ get_properties, set_properties
import azurelinuxagent.utils.fileutil as fileutil
import azurelinuxagent.utils.restutil as restutil
import azurelinuxagent.utils.shellutil as shellutil
@@ -41,15 +44,6 @@ VALID_EXTENSION_STATUS = ['transitioning', 'error', 'success', 'warning']
VALID_HANDLER_STATUS = ['Ready', 'NotReady', "Installing", "Unresponsive"]
-def handler_state_to_status(handler_state):
- if handler_state == "Enabled":
- return "Ready"
- elif handler_state in VALID_HANDLER_STATUS:
- return handler_state
- else:
- return "NotReady"
-
-
def validate_has_key(obj, key, fullname):
if key not in obj:
raise ExtensionError("Missing: {0}".format(fullname))
@@ -64,14 +58,13 @@ def parse_formatted_message(formatted_message):
validate_has_key(formatted_message, 'lang', 'formattedMessage/lang')
validate_has_key(formatted_message, 'message', 'formattedMessage/message')
return formatted_message.get('message')
-
def parse_ext_substatus(substatus):
#Check extension sub status format
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)
@@ -105,333 +98,330 @@ def parse_ext_status(ext_status, data):
for substatus in substatus_list:
ext_status.substatusList.append(parse_ext_substatus(substatus))
-def parse_extension_dirname(dirname):
- """
- Parse installed extension dir name. Sample: ExtensionName-Version/
- """
- seprator = dirname.rfind('-')
- if seprator < 0:
- raise ExtensionError("Invalid extenation dir name")
- return dirname[0:seprator], dirname[seprator + 1:]
-
-def get_installed_version(target_name):
- """
- Return the highest version instance with the same name
- """
- installed_version = None
- lib_dir = OSUTIL.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):
- name, version = parse_extension_dirname(dir_name)
- #Here we need to ensure names are exactly the same.
- if name == target_name:
- if installed_version is None or \
- Version(installed_version) < Version(version):
- installed_version = version
- return installed_version
-
class ExtHandlerState(object):
+ NotInstalled = "NotInstalled"
+ Installed = "Installed"
Enabled = "Enabled"
- Disabled = "Disabled"
- Failed = "Failed"
-
class ExtHandlersHandler(object):
-
- def process(self):
+ def __init__(self, distro):
+ self.distro = distro
+ self.ext_handlers = None
+ self.last_etag = None
+ self.log_report = False
+
+ def run(self):
+ ext_handlers, etag = None, None
try:
- protocol = prot.FACTORY.get_default_protocol()
- ext_handlers = protocol.get_ext_handlers()
- except prot.ProtocolError as e:
- add_event(name="WALA", is_success=False, message = text(e))
+ self.protocol = self.distro.protocol_util.get_protocol()
+ ext_handlers, etag = self.protocol.get_ext_handlers()
+ except ProtocolError as e:
+ add_event(name="WALA", is_success=False, message=ustr(e))
return
-
- vm_status = prot.VMStatus()
+ if self.last_etag is not None and self.last_etag == etag:
+ logger.verb("No change to ext handler config:{0}, skip", etag)
+ self.log_report = False
+ else:
+ logger.info("Handle new ext handler config")
+ self.log_report = True #Log status report success on new config
+ self.handle_ext_handlers(ext_handlers)
+ self.last_etag = etag
+
+ self.report_ext_handlers_status(ext_handlers)
+
+ def handle_ext_handlers(self, ext_handlers):
+ if ext_handlers.extHandlers is None or \
+ len(ext_handlers.extHandlers) == 0:
+ logger.info("No ext handler config found")
+ return
+
+ for ext_handler in ext_handlers.extHandlers:
+ #TODO handle install in sequence, enable in parallel
+ self.handle_ext_handler(ext_handler)
+
+ def handle_ext_handler(self, ext_handler):
+ ext_handler_i = ExtHandlerInstance(ext_handler, self.protocol)
+ try:
+ state = ext_handler.properties.state
+ ext_handler_i.logger.info("Expected handler state: {0}", state)
+ if state == "enabled":
+ self.handle_enable(ext_handler_i)
+ elif state == u"disabled":
+ self.handle_disable(ext_handler_i)
+ elif state == u"uninstall":
+ self.handle_uninstall(ext_handler_i)
+ else:
+ message = u"Unknown ext handler state:{0}".format(state)
+ raise ExtensionError(message)
+ except ExtensionError as e:
+ ext_handler_i.set_handler_status(message=ustr(e), code=-1)
+ ext_handler_i.report_event(message=ustr(e), is_success=False)
+
+ def handle_enable(self, ext_handler_i):
+
+ ext_handler_i.decide_version()
+
+ old_ext_handler_i = ext_handler_i.get_installed_ext_handler()
+ if old_ext_handler_i is not None and \
+ old_ext_handler_i.version_gt(ext_handler_i):
+ raise ExtensionError(u"Downgrade not allowed")
+
+ handler_state = ext_handler_i.get_handler_state()
+ ext_handler_i.logger.info("Current handler state is: {0}", handler_state)
+ if handler_state == ExtHandlerState.NotInstalled:
+ ext_handler_i.set_handler_state(ExtHandlerState.NotInstalled)
+
+ ext_handler_i.download()
+
+ ext_handler_i.update_settings()
+
+ if old_ext_handler_i is None:
+ ext_handler_i.install()
+ elif ext_handler_i.version_gt(old_ext_handler_i):
+ old_ext_handler_i.disable()
+ ext_handler_i.copy_status_files(old_ext_handler_i)
+ ext_handler_i.update()
+ old_ext_handler_i.uninstall()
+ old_ext_handler_i.rm_ext_handler_dir()
+ ext_handler_i.update_with_install()
+ else:
+ ext_handler_i.update_settings()
+
+ ext_handler_i.enable()
+
+ def handle_disable(self, ext_handler_i):
+ handler_state = ext_handler_i.get_handler_state()
+ ext_handler_i.logger.info("Current handler state is: {0}", handler_state)
+ if handler_state == ExtHandlerState.Enabled:
+ ext_handler_i.disable()
+
+ def handle_uninstall(self, ext_handler_i):
+ handler_state = ext_handler_i.get_handler_state()
+ ext_handler_i.logger.info("Current handler state is: {0}", handler_state)
+ if handler_state != ExtHandlerState.NotInstalled:
+ if handler_state == ExtHandlerState.Enabled:
+ ext_handler_i.disable()
+ ext_handler_i.uninstall()
+ ext_handler_i.rm_ext_handler_dir()
+
+ def report_ext_handlers_status(self, ext_handlers):
+ """Go thru handler_state dir, collect and report status"""
+ vm_status = VMStatus()
vm_status.vmAgent.version = AGENT_VERSION
vm_status.vmAgent.status = "Ready"
vm_status.vmAgent.message = "Guest Agent is running"
- if ext_handlers.extHandlers is None or \
- len(ext_handlers.extHandlers) == 0:
- logger.verb("No extensions to handle")
- else:
+ if ext_handlers is not None:
for ext_handler in ext_handlers.extHandlers:
- #TODO handle extension in parallel
try:
- pkg_list = protocol.get_ext_handler_pkgs(ext_handler)
- except prot.ProtocolError as e:
- add_event(name="WALA", is_success=False, message=text(e))
- continue
-
- handler_status = self.process_extension(ext_handler, pkg_list)
- if handler_status is not None:
- vm_status.vmAgent.extensionHandlers.append(handler_status)
-
+ self.report_ext_handler_status(vm_status, ext_handler)
+ except ExtensionError as e:
+ add_event(name="WALA", is_success=False, message=ustr(e))
+
+ logger.verb("Report vm agent status")
+
try:
- logger.verb("Report vm agent status")
- protocol.report_vm_status(vm_status)
- except prot.ProtocolError as e:
- add_event(name="WALA", is_success=False, message = text(e))
-
- def process_extension(self, ext_handler, pkg_list):
- installed_version = get_installed_version(ext_handler.name)
- if installed_version is not None:
- handler = ExtHandlerInstance(ext_handler, pkg_list,
- installed_version, installed=True)
- else:
- handler = ExtHandlerInstance(ext_handler, pkg_list,
- ext_handler.properties.version)
- handler.handle()
+ self.protocol.report_vm_status(vm_status)
+ except ProtocolError as e:
+ message = "Failed to report vm agent status: {0}".format(e)
+ add_event(name="WALA", is_success=False, message=message)
+
+ if self.log_report:
+ logger.info("Successfully reported vm agent status")
+
+
+ def report_ext_handler_status(self, vm_status, ext_handler):
+ ext_handler_i = ExtHandlerInstance(ext_handler, self.protocol)
- if handler.ext_status is not None:
+ handler_status = ext_handler_i.get_handler_status()
+ if handler_status is None:
+ return
+
+ handler_state = ext_handler_i.get_handler_state()
+ if handler_state != ExtHandlerState.NotInstalled:
try:
- protocol = prot.FACTORY.get_default_protocol()
- protocol.report_ext_status(handler.name, handler.ext.name,
- handler.ext_status)
- except prot.ProtocolError as e:
- add_event(name="WALA", is_success=False, message=text(e))
-
- return handler.handler_status
+ active_exts = ext_handler_i.report_ext_status()
+ handler_status.extensions.extend(active_exts)
+ except ExtensionError as e:
+ ext_handler_i.set_handler_status(message=ustr(e), code=-1)
+
+ try:
+ heartbeat = ext_handler_i.collect_heartbeat()
+ if heartbeat is not None:
+ handler_status.status = heartbeat.get('status')
+ except ExtensionError as e:
+ ext_handler_i.set_handler_status(message=ustr(e), code=-1)
+ vm_status.vmAgent.extensionHandlers.append(handler_status)
+
class ExtHandlerInstance(object):
- def __init__(self, ext_handler, pkg_list, curr_version, installed=False):
+ def __init__(self, ext_handler, protocol):
self.ext_handler = ext_handler
- self.name = ext_handler.name
- self.version = ext_handler.properties.version
- self.pkg_list = pkg_list
- self.state = ext_handler.properties.state
- self.update_policy = ext_handler.properties.upgradePolicy
-
- self.curr_version = curr_version
- self.installed = installed
- self.handler_state = None
- self.lib_dir = OSUTIL.get_lib_dir()
-
- self.ext_status = prot.ExtensionStatus()
- self.handler_status = prot.ExtHandlerStatus()
- self.handler_status.name = self.name
- self.handler_status.version = self.curr_version
-
- #Currently, extension settings will have no more than 1 instance
- if len(ext_handler.properties.extensions) > 0:
- self.ext = ext_handler.properties.extensions[0]
- self.handler_status.extensions = [self.ext.name]
- else:
- #When no extension settings, set sequenceNumber to 0
- self.ext = prot.Extension(sequenceNumber=0)
- self.ext_status.sequenceNumber = self.ext.sequenceNumber
+ self.protocol = protocol
+ self.operation = None
+ self.pkg = None
prefix = "[{0}]".format(self.get_full_name())
self.logger = logger.Logger(logger.DEFAULT_LOGGER, prefix)
+
+ try:
+ fileutil.mkdir(self.get_log_dir(), mode=0o744)
+ except IOError as e:
+ self.logger.error(u"Failed to create extension log dir: {0}", e)
- def init_logger(self):
- #Init logger appender for extension
- fileutil.mkdir(self.get_log_dir(), mode=0o644)
log_file = os.path.join(self.get_log_dir(), "CommandExecution.log")
self.logger.add_appender(logger.AppenderType.FILE,
logger.LogLevel.INFO, log_file)
- def handle(self):
- self.init_logger()
- self.logger.verb("Start processing extension handler")
-
- try:
- self.handle_state()
- except ExtensionError as e:
- self.set_state_err(text(e))
- self.report_event(is_success=False, message=text(e))
- self.logger.error("Failed to process extension handler")
- return
-
- try:
- if self.installed:
- self.collect_ext_status()
- self.collect_handler_status()
- except ExtensionError as e:
- self.report_event(is_success=False, message=text(e))
- self.logger.error("Failed to get extension handler status")
- return
-
- self.logger.verb("Finished processing extension handler")
-
- def handle_state(self):
- if self.installed:
- self.handler_state = self.get_state()
-
- self.handler_status.status = handler_state_to_status(self.handler_state)
- self.logger.verb("Handler state: {0}", self.handler_state)
- self.logger.verb("Sequence number: {0}", self.ext.sequenceNumber)
-
- if self.state == 'enabled':
- if self.handler_state == ExtHandlerState.Failed:
- self.logger.verb("Found previous failure, quit handle_enable")
- return
-
- if self.handler_state == ExtHandlerState.Enabled:
- self.logger.verb("Already enabled with sequenceNumber: {0}",
- self.ext.sequenceNumber)
- self.logger.verb("Quit handle_enable")
- return
+ def decide_version(self):
+ """
+ If auto-upgrade, get the largest public extension version under
+ the requested major version family of currently installed plugin version
- try:
- new = self.handle_enable()
- if new is not None:
- #Upgrade happened
- new.set_state(ExtHandlerState.Enabled)
- else:
- self.set_state(ExtHandlerState.Enabled)
+ Else, get the highest hot-fix for requested version,
+ """
+ self.logger.info("Decide which version to use")
+ try:
+ pkg_list = self.protocol.get_ext_handler_pkgs(self.ext_handler)
+ except ProtocolError as e:
+ raise ExtensionError("Failed to get ext handler pkgs", e)
- except ExtensionError as e:
- self.set_state(ExtHandlerState.Failed)
- raise e
- elif self.state == 'disabled':
- if self.handler_state == ExtHandlerState.Failed:
- self.logger.verb("Found previous failure, quit handle_disable")
- return
-
- if self.handler_state == ExtHandlerState.Disabled:
- self.logger.verb("Already disabled with sequenceNumber: {0}",
- self.ext.sequenceNumber)
- self.logger.verb("Quit handle_disable")
- return
+ version = self.ext_handler.properties.version
+ update_policy = self.ext_handler.properties.upgradePolicy
+
+ version_frag = version.split('.')
+ if len(version_frag) < 2:
+ raise ExtensionError("Wrong version format: {0}".format(version))
- try:
- self.handle_disable()
- self.set_state(ExtHandlerState.Disabled)
- except ExtensionError as e:
- self.set_state(ExtHandlerState.Failed)
- raise e
- elif self.state == 'uninstall':
- try:
- self.handle_uninstall()
- except ExtensionError as e:
- self.set_state(ExtHandlerState.Failed)
- raise e
+ version_prefix = None
+ if update_policy is not None and update_policy == 'auto':
+ version_prefix = "{0}.".format(version_frag[0])
else:
- raise ExtensionError("Unknown state:{0}".format(self.state))
-
- def handle_enable(self):
- target_version = self.get_target_version()
- self.logger.info("Target version: {0}", target_version)
- if self.installed:
- if Version(target_version) > Version(self.curr_version):
- return self.upgrade(target_version)
- elif Version(target_version) == Version(self.curr_version):
- self.enable()
- else:
- raise ExtensionError("A newer version is already installed")
- else:
- if Version(target_version) > Version(self.version):
- #This will happen when auto upgrade policy is enabled
- self.logger.info("Auto upgrade to new version:{0}",
- target_version)
- self.curr_version = target_version
- self.download()
- self.init_dir()
- self.install()
- self.enable()
+ version_prefix = "{0}.{1}.".format(version_frag[0], version_frag[1])
+
+ packages = [x for x in pkg_list.versions \
+ if x.version.startswith(version_prefix) or \
+ x.version == version]
+
+ packages = sorted(packages, key=lambda x: Version(x.version),
+ reverse=True)
- def handle_disable(self):
- if not self.installed:
- self.logger.verb("Not installed, quit disable")
- return
+ if len(packages) <= 0:
+ raise ExtensionError("Failed to find and valid extension package")
+ self.pkg = packages[0]
+ self.ext_handler.properties.version = packages[0].version
+ self.logger.info("Use version: {0}", self.pkg.version)
+
+ def version_gt(self, other):
+ self_version = self.ext_handler.properties.version
+ other_version = other.ext_handler.properties.version
+ return Version(self_version) > Version(other_version)
+
+ def get_installed_ext_handler(self):
+ lastest_version = None
+ ext_handler_name = self.ext_handler.name
+
+ for dir_name in os.listdir(conf.get_lib_dir()):
+ path = os.path.join(conf.get_lib_dir(), dir_name)
+ if os.path.isdir(path) and dir_name.startswith(ext_handler_name):
+ seperator = dir_name.rfind('-')
+ if seperator < 0:
+ continue
+ installed_name = dir_name[0: seperator]
+ installed_version = dir_name[seperator + 1:]
+ if installed_name != ext_handler_name:
+ continue
+ if lastest_version is None or \
+ Version(lastest_version) < Version(installed_version):
+ lastest_version = installed_version
- self.disable()
+ if lastest_version is None:
+ return None
+
+ data = get_properties(self.ext_handler)
+ old_ext_handler = ExtHandler()
+ set_properties("ExtHandler", old_ext_handler, data)
+ old_ext_handler.properties.version = lastest_version
+ return ExtHandlerInstance(old_ext_handler, self.protocol)
+
+ def copy_status_files(self, old_ext_handler_i):
+ self.logger.info("Copy status files from old plugin to new")
+ old_ext_dir = old_ext_handler_i.get_base_dir()
+ new_ext_dir = self.get_base_dir()
+
+ old_ext_mrseq_file = os.path.join(old_ext_dir, "mrseq")
+ if os.path.isfile(old_ext_mrseq_file):
+ shutil.copy2(old_ext_mrseq_file, new_ext_dir)
+
+ old_ext_status_dir = old_ext_handler_i.get_status_dir()
+ new_ext_status_dir = self.get_status_dir()
+
+ if os.path.isdir(old_ext_status_dir):
+ for status_file in os.listdir(old_ext_status_dir):
+ status_file = os.path.join(old_ext_status_dir, status_file)
+ if os.path.isfile(status_file):
+ shutil.copy2(status_file, new_ext_status_dir)
+
+ def set_operation(self, op):
+ self.operation = op
- def handle_uninstall(self):
- if not self.installed:
- self.logger.verb("Not installed, quit unistall")
- self.handler_status = None
- self.ext_status = None
- return
- self.disable()
- self.uninstall()
-
- def report_event(self, is_success=True, message=""):
- if self.ext_status is not None:
- if not is_success:
- self.ext_status.status = "error"
- self.ext_status.code = -1
- if self.handler_status is not None:
- self.handler_status.message = message
- if not is_success:
- self.handler_status.status = "NotReady"
- add_event(name=self.name, op=self.ext_status.operation,
- is_success=is_success, message=message)
-
- def set_operation(self, operation):
- if self.ext_status.operation != WALAEventOperation.Upgrade:
- self.ext_status.operation = operation
-
- def upgrade(self, target_version):
- self.logger.info("Upgrade from: {0} to {1}", self.curr_version,
- target_version)
- self.set_operation(WALAEventOperation.Upgrade)
-
- old = self
- new = ExtHandlerInstance(self.ext_handler, self.pkg_list,
- target_version)
- self.logger.info("Download new extension package")
- new.init_logger()
- new.download()
- self.logger.info("Initialize new extension directory")
- new.init_dir()
-
- old.disable()
- self.logger.info("Update new extension")
- new.update()
- old.uninstall()
- man = new.load_manifest()
- if man.is_update_with_install():
- self.logger.info("Install new extension")
- new.install()
- self.logger.info("Enable new extension")
- new.enable()
- return new
+ def report_event(self, message="", is_success=True):
+ version = self.ext_handler.properties.version
+ add_event(name=self.ext_handler.name, version=version, message=message,
+ op=self.operation, is_success=is_success)
def download(self):
self.logger.info("Download extension package")
self.set_operation(WALAEventOperation.Download)
-
- uris = self.get_package_uris()
+ if self.pkg is None:
+ raise ExtensionError("No package uri found")
+
package = None
- for uri in uris:
+ for uri in self.pkg.uris:
try:
- resp = restutil.http_get(uri.uri, chk_proxy=True)
- if resp.status == restutil.httpclient.OK:
- package = resp.read()
- break
- except restutil.HttpError as e:
- self.logger.warn("Failed download extension from: {0}", uri.uri)
-
+ package = self.protocol.download_ext_handler_pkg(uri.uri)
+ except ProtocolError as e:
+ logger.warn("Failed download extension: {0}", e)
+
if package is None:
- raise ExtensionError("Download extension failed")
+ raise ExtensionError("Failed to download extension")
self.logger.info("Unpack extension package")
- pkg_file = os.path.join(self.lib_dir, os.path.basename(uri.uri) + ".zip")
- fileutil.write_file(pkg_file, bytearray(package), asbin=True)
- zipfile.ZipFile(pkg_file).extractall(self.get_base_dir())
+ pkg_file = os.path.join(conf.get_lib_dir(),
+ os.path.basename(uri.uri) + ".zip")
+ try:
+ fileutil.write_file(pkg_file, bytearray(package), asbin=True)
+ zipfile.ZipFile(pkg_file).extractall(self.get_base_dir())
+ except IOError as e:
+ raise ExtensionError(u"Failed to write and unzip plugin", e)
+
chmod = "find {0} -type f | xargs chmod u+x".format(self.get_base_dir())
shellutil.run(chmod)
self.report_event(message="Download succeeded")
- def init_dir(self):
self.logger.info("Initialize extension directory")
#Save HandlerManifest.json
man_file = fileutil.search_file(self.get_base_dir(),
'HandlerManifest.json')
- man = fileutil.read_file(man_file, remove_bom=True)
- fileutil.write_file(self.get_manifest_file(), man)
- #Create status and config dir
- status_dir = self.get_status_dir()
- fileutil.mkdir(status_dir, mode=0o700)
- conf_dir = self.get_conf_dir()
- fileutil.mkdir(conf_dir, mode=0o700)
+ if man_file is None:
+ raise ExtensionError("HandlerManifest.json not found")
- self.make_handler_state_dir()
+ try:
+ man = fileutil.read_file(man_file, remove_bom=True)
+ fileutil.write_file(self.get_manifest_file(), man)
+ except IOError as e:
+ raise ExtensionError(u"Failed to save HandlerManifest.json", e)
+
+ #Create status and config dir
+ try:
+ status_dir = self.get_status_dir()
+ fileutil.mkdir(status_dir, mode=0o700)
+ conf_dir = self.get_conf_dir()
+ fileutil.mkdir(conf_dir, mode=0o700)
+ except IOError as e:
+ raise ExtensionError(u"Failed to create status or config dir", e)
#Save HandlerEnvironment.json
self.create_handler_env()
@@ -442,6 +432,8 @@ class ExtHandlerInstance(object):
man = self.load_manifest()
self.launch_command(man.get_enable_command())
+ self.set_handler_state(ExtHandlerState.Enabled)
+ self.set_handler_status(status="Ready", message="Plugin enabled")
def disable(self):
self.logger.info("Disable extension.")
@@ -449,6 +441,8 @@ class ExtHandlerInstance(object):
man = self.load_manifest()
self.launch_command(man.get_disable_command(), timeout=900)
+ self.set_handler_state(ExtHandlerState.Installed)
+ self.set_handler_status(status="NotReady", message="Plugin disabled")
def install(self):
self.logger.info("Install extension.")
@@ -456,24 +450,31 @@ class ExtHandlerInstance(object):
man = self.load_manifest()
self.launch_command(man.get_install_command(), timeout=900)
- self.installed = True
+ self.set_handler_state(ExtHandlerState.Installed)
def uninstall(self):
self.logger.info("Uninstall extension.")
self.set_operation(WALAEventOperation.UnInstall)
- man = self.load_manifest()
- self.launch_command(man.get_uninstall_command())
-
- self.logger.info("Remove ext handler dir: {0}", self.get_base_dir())
try:
- shutil.rmtree(self.get_base_dir())
+ man = self.load_manifest()
+ self.launch_command(man.get_uninstall_command())
+ except ExtensionError as e:
+ self.report_event(message=ustr(e), is_success=False)
+
+ def rm_ext_handler_dir(self):
+ try:
+ handler_state_dir = self.get_handler_state_dir()
+ if os.path.isdir(handler_state_dir):
+ self.logger.info("Remove ext handler dir: {0}", handler_state_dir)
+ shutil.rmtree(handler_state_dir)
+ base_dir = self.get_base_dir()
+ if os.path.isdir(base_dir):
+ self.logger.info("Remove ext handler dir: {0}", base_dir)
+ shutil.rmtree(base_dir)
except IOError as e:
- raise ExtensionError("Failed to rm ext handler dir: {0}".format(e))
-
- self.installed = False
- self.handler_status = None
- self.ext_status = None
+ message = "Failed to rm ext handler dir: {0}".format(e)
+ self.report_event(message=message, is_success=False)
def update(self):
self.logger.info("Update extension.")
@@ -481,95 +482,82 @@ class ExtHandlerInstance(object):
man = self.load_manifest()
self.launch_command(man.get_update_command(), timeout=900)
-
- def collect_handler_status(self):
- self.logger.verb("Collect extension handler status")
- if self.handler_status is None:
- return
-
- handler_state = self.get_state()
- self.handler_status.status = handler_state_to_status(handler_state)
- self.handler_status.message = self.get_state_err()
+
+ def update_with_install(self):
man = self.load_manifest()
- if man.is_report_heartbeat():
- heartbeat = self.collect_heartbeat()
- if heartbeat is not None:
- self.handler_status.status = heartbeat['status']
+ if man.is_update_with_install():
+ self.install()
+ else:
+ self.logger.info("UpdateWithInstall not set. "
+ "Skip install during upgrade.")
+ self.set_handler_state(ExtHandlerState.Installed)
- def collect_ext_status(self):
+ def get_largest_seq_no(self):
+ seq_no = -1
+ conf_dir = self.get_conf_dir()
+ for item in os.listdir(conf_dir):
+ item_path = os.path.join(conf_dir, item)
+ if os.path.isfile(item_path):
+ try:
+ seperator = item.rfind(".")
+ if seperator > 0 and item[seperator + 1:] == 'settings':
+ curr_seq_no = int(item.split('.')[0])
+ if curr_seq_no > seq_no:
+ seq_no = curr_seq_no
+ except Exception as e:
+ self.logger.verb("Failed to parse file name: {0}", item)
+ continue
+ return seq_no
+
+ def collect_ext_status(self, ext):
self.logger.verb("Collect extension status")
- if self.handler_status is None:
- return
- if self.ext is None:
- return
+ seq_no = self.get_largest_seq_no()
+ if seq_no == -1:
+ return None
+
+ status_dir = self.get_status_dir()
+ ext_status_file = "{0}.status".format(seq_no)
+ ext_status_file = os.path.join(status_dir, ext_status_file)
- ext_status_file = self.get_status_file()
+ ext_status = ExtensionStatus(seq_no=seq_no)
try:
data_str = fileutil.read_file(ext_status_file)
data = json.loads(data_str)
- parse_ext_status(self.ext_status, data)
+ parse_ext_status(ext_status, data)
except IOError as e:
- raise ExtensionError("Failed to get status file: {0}".format(e))
+ ext_status.message = u"Failed to get status file {0}".format(e)
+ ext_status.code = -1
+ ext_status.status = "error"
except ValueError as e:
- raise ExtensionError("Malformed status file: {0}".format(e))
-
- def make_handler_state_dir(self):
- handler_state_dir = self.get_handler_state_dir()
- fileutil.mkdir(handler_state_dir, 0o600)
- if not os.path.exists(handler_state_dir):
- os.makedirs(handler_state_dir)
-
- def get_state(self):
- handler_state_file = self.get_handler_state_file()
- if not os.path.isfile(handler_state_file):
- return None
- try:
- handler_state = fileutil.read_file(handler_state_file)
- if handler_state is not None:
- handler_state = handler_state.rstrip()
- return handler_state
- except IOError as e:
- err = "Failed to get handler state: {0}".format(e)
- add_event(name=self.name, is_success=False, message=err)
-
- def set_state(self, state):
- handler_state_file = self.get_handler_state_file()
- if not os.path.isfile(handler_state_file):
- self.make_handler_state_dir()
- try:
- fileutil.write_file(handler_state_file, state)
- except IOError as e:
- err = "Failed to set handler state: {0}".format(e)
- add_event(name=self.name, is_success=False, message=err)
-
- def get_state_err(self):
- """Get handler error message"""
- handler_state_err_file= self.get_handler_state_err_file()
- if not os.path.isfile(handler_state_err_file):
- return None
- try:
- message = fileutil.read_file(handler_state_err_file)
- return message
- except IOError as e:
- err = "Failed to get handler state message: {0}".format(e)
- add_event(name=self.name, is_success=False, message=err)
-
- def set_state_err(self, message):
- """Set handler error message"""
- handler_state_err_file = self.get_handler_state_err_file()
- if not os.path.isfile(handler_state_err_file):
- self.make_handler_state_dir()
- try:
- fileutil.write_file(handler_state_err_file, message)
- except IOError as e:
- err = "Failed to set handler state message: {0}".format(e)
- add_event(name=self.name, is_success=False, message=err)
+ ext_status.message = u"Malformed status file {0}".format(e)
+ ext_status.code = -1
+ ext_status.status = "error"
+ return ext_status
+
+ def report_ext_status(self):
+ active_exts = []
+ for ext in self.ext_handler.properties.extensions:
+ ext_status = self.collect_ext_status(ext)
+ if ext_status is None:
+ continue
+ try:
+ self.protocol.report_ext_status(self.ext_handler.name, ext.name,
+ ext_status)
+ active_exts.append(ext.name)
+ except ProtocolError as e:
+ self.logger.error(u"Failed to report extension status: {0}", e)
+ return active_exts
+
def collect_heartbeat(self):
- self.logger.info("Collect heart beat")
- heartbeat_file = os.path.join(OSUTIL.get_lib_dir(),
+ man = self.load_manifest()
+ if not man.is_report_heartbeat():
+ return
+ heartbeat_file = os.path.join(conf.get_lib_dir(),
self.get_heartbeat_file())
+
+ self.logger.info("Collect heart beat")
if not os.path.isfile(heartbeat_file):
raise ExtensionError("Failed to get heart beat file")
if not self.is_responsive(heartbeat_file):
@@ -586,15 +574,14 @@ class ExtHandlerInstance(object):
except ValueError as e:
raise ExtensionError("Malformed heartbeat file: {0}".format(e))
return heartbeat
-
+
def is_responsive(self, heartbeat_file):
last_update=int(time.time() - os.stat(heartbeat_file).st_mtime)
return last_update > 600 # not updated for more than 10 min
-
+
def launch_command(self, cmd, timeout=300):
self.logger.info("Launch command:{0}", cmd)
base_dir = self.get_base_dir()
- self.update_settings()
try:
devnull = open(os.devnull, 'w')
child = subprocess.Popen(base_dir + "/" + cmd, shell=True,
@@ -614,6 +601,7 @@ class ExtHandlerInstance(object):
ret = child.wait()
if ret == None or ret != 0:
raise ExtensionError("Non-zero exit code: {0}, {1}".format(ret, cmd))
+
self.report_event(message="Launch command succeeded: {0}".format(cmd))
def load_manifest(self):
@@ -627,26 +615,40 @@ class ExtHandlerInstance(object):
return HandlerManifest(data[0])
+ def update_settings_file(self, settings_file, settings):
+ settings_file = os.path.join(self.get_conf_dir(), settings_file)
+ try:
+ fileutil.write_file(settings_file, settings)
+ except IOError as e:
+ raise ExtensionError(u"Failed to update settings file", e)
+
def update_settings(self):
- if self.ext is None:
- self.logger.verb("Extension has no settings")
+ if self.ext_handler.properties.extensions is None or \
+ len(self.ext_handler.properties.extensions) == 0:
+ #This is the behavior of waagent 2.0.x
+ #The new agent has to be consistent with the old one.
+ self.logger.info("Extension has no settings, write empty 0.settings")
+ self.update_settings_file("0.settings", "")
return
-
- settings = {
- 'publicSettings': self.ext.publicSettings,
- 'protectedSettings': self.ext.privateSettings,
- 'protectedSettingsCertThumbprint': self.ext.certificateThumbprint
- }
- ext_settings = {
- "runtimeSettings":[{
- "handlerSettings": settings
- }]
- }
- fileutil.write_file(self.get_settings_file(), json.dumps(ext_settings))
+
+ for ext in self.ext_handler.properties.extensions:
+ settings = {
+ 'publicSettings': ext.publicSettings,
+ 'protectedSettings': ext.protectedSettings,
+ 'protectedSettingsCertThumbprint': ext.certificateThumbprint
+ }
+ ext_settings = {
+ "runtimeSettings":[{
+ "handlerSettings": settings
+ }]
+ }
+ settings_file = "{0}.settings".format(ext.sequenceNumber)
+ self.logger.info("Update settings file: {0}", settings_file)
+ self.update_settings_file(settings_file, json.dumps(ext_settings))
def create_handler_env(self):
env = [{
- "name": self.name,
+ "name": self.ext_handler.name,
"version" : HANDLER_ENVIRONMENT_VERSION,
"handlerEnvironment" : {
"logFolder" : self.get_log_dir(),
@@ -655,73 +657,91 @@ class ExtHandlerInstance(object):
"heartbeatFile" : self.get_heartbeat_file()
}
}]
- fileutil.write_file(self.get_env_file(),
- json.dumps(env))
-
- def get_target_version(self):
- version = self.version
- update_policy = self.update_policy
- if update_policy is None or update_policy.lower() != 'auto':
- return version
-
- major = version.split('.')[0]
- if major is None:
- raise ExtensionError("Wrong version format: {0}".format(version))
-
- packages = [x for x in self.pkg_list.versions \
- if x.version.startswith(major + ".")]
- packages = sorted(packages, key=lambda x: Version(x.version),
- reverse=True)
- if len(packages) <= 0:
- raise ExtensionError("Can't find version: {0}.*".format(major))
+ try:
+ fileutil.write_file(self.get_env_file(), json.dumps(env))
+ except IOError as e:
+ raise ExtensionError(u"Failed to save handler environment", e)
+
+ def get_handler_state_dir(self):
+ return os.path.join(conf.get_lib_dir(), "handler_state",
+ self.get_full_name())
- return packages[0].version
+ def set_handler_state(self, handler_state):
+ state_dir = self.get_handler_state_dir()
+ if not os.path.exists(state_dir):
+ try:
+ fileutil.mkdir(state_dir, 0o700)
+ except IOError as e:
+ self.logger.error("Failed to create state dir: {0}", e)
+
+ try:
+ state_file = os.path.join(state_dir, "state")
+ fileutil.write_file(state_file, handler_state)
+ except IOError as e:
+ self.logger.error("Failed to set state: {0}", e)
+
+ def get_handler_state(self):
+ state_dir = self.get_handler_state_dir()
+ state_file = os.path.join(state_dir, "state")
+ if not os.path.isfile(state_file):
+ return ExtHandlerState.NotInstalled
- def get_package_uris(self):
- version = self.curr_version
- packages = self.pkg_list.versions
- if packages is None:
- raise ExtensionError("Package uris is None.")
+ try:
+ return fileutil.read_file(state_file)
+ except IOError as e:
+ self.logger.error("Failed to get state: {0}", e)
+ return ExtHandlerState.NotInstalled
+
+ def set_handler_status(self, status="NotReady", message="",
+ code=0):
+ state_dir = self.get_handler_state_dir()
+ if not os.path.exists(state_dir):
+ try:
+ fileutil.mkdir(state_dir, 0o700)
+ except IOError as e:
+ self.logger.error("Failed to create state dir: {0}", e)
+
+ handler_status = ExtHandlerStatus()
+ handler_status.name = self.ext_handler.name
+ handler_status.version = self.ext_handler.properties.version
+ handler_status.message = message
+ handler_status.code = code
+ handler_status.status = status
+ status_file = os.path.join(state_dir, "status")
- for package in packages:
- if Version(package.version) == Version(version):
- return package.uris
+ try:
+ fileutil.write_file(status_file,
+ json.dumps(get_properties(handler_status)))
+ except (IOError, ValueError, ProtocolError) as e:
+ self.logger.error("Failed to save handler status: {0}", e)
+
+ def get_handler_status(self):
+ state_dir = self.get_handler_state_dir()
+ status_file = os.path.join(state_dir, "status")
+ if not os.path.isfile(status_file):
+ return None
+
+ try:
+ data = json.loads(fileutil.read_file(status_file))
+ handler_status = ExtHandlerStatus()
+ set_properties("ExtHandlerStatus", handler_status, data)
+ return handler_status
+ except (IOError, ValueError) as e:
+ self.logger.error("Failed to get handler status: {0}", e)
- raise ExtensionError("Can't get package uris for {0}.".format(version))
-
def get_full_name(self):
- return "{0}-{1}".format(self.name, self.curr_version)
-
+ return "{0}-{1}".format(self.ext_handler.name,
+ self.ext_handler.properties.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")
- def get_status_file(self):
- return os.path.join(self.get_status_dir(),
- "{0}.status".format(self.ext.sequenceNumber))
-
def get_conf_dir(self):
return os.path.join(self.get_base_dir(), 'config')
- def get_settings_file(self):
- return os.path.join(self.get_conf_dir(),
- "{0}.settings".format(self.ext.sequenceNumber))
-
- def get_handler_state_dir(self):
- return os.path.join(OSUTIL.get_lib_dir(), "handler_state",
- self.get_full_name())
-
- def get_handler_state_file(self):
- return os.path.join(self.get_handler_state_dir(),
- '{0}.state'.format(self.ext.sequenceNumber))
-
- def get_handler_state_err_file(self):
- return os.path.join(self.get_handler_state_dir(),
- '{0}.error'.format(self.ext.sequenceNumber))
-
-
def get_heartbeat_file(self):
return os.path.join(self.get_base_dir(), 'heartbeat.log')
@@ -732,8 +752,8 @@ 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,
- self.curr_version)
+ return os.path.join(conf.get_ext_log_dir(), self.ext_handler.name,
+ self.ext_handler.properties.version)
class HandlerEnvironment(object):
def __init__(self, data):
@@ -782,19 +802,16 @@ class HandlerManifest(object):
return self.data['handlerManifest']["disableCommand"]
def is_reboot_after_install(self):
- #TODO handle reboot after install
- if "rebootAfterInstall" not in self.data['handlerManifest']:
- return False
- return self.data['handlerManifest']["rebootAfterInstall"]
+ """
+ Deprecated
+ """
+ return False
def is_report_heartbeat(self):
- if "reportHeartbeat" not in self.data['handlerManifest']:
- return False
- return self.data['handlerManifest']["reportHeartbeat"]
+ return self.data['handlerManifest'].get('reportHeartbeat', False)
def is_update_with_install(self):
- if "updateMode" not in self.data['handlerManifest']:
- return False
- if "updateMode" in self.data:
- return self.data['handlerManifest']["updateMode"].lower() == "updatewithinstall"
- return False
+ update_mode = self.data['handlerManifest'].get('updateMode')
+ if update_mode is None:
+ return True
+ return update_mode.low() == "updatewithinstall"
diff --git a/azurelinuxagent/distro/default/handlerFactory.py b/azurelinuxagent/distro/default/handlerFactory.py
deleted file mode 100644
index dceb2a3..0000000
--- a/azurelinuxagent/distro/default/handlerFactory.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 .init import InitHandler
-from .run import MainHandler
-from .scvmm import ScvmmHandler
-from .dhcp import DhcpHandler
-from .env import EnvHandler
-from .provision import ProvisionHandler
-from .resourceDisk import ResourceDiskHandler
-from .extension import ExtHandlersHandler
-from .deprovision import DeprovisionHandler
-
-class DefaultHandlerFactory(object):
- def __init__(self):
- self.init_handler = InitHandler()
- self.main_handler = MainHandler(self)
- self.scvmm_handler = ScvmmHandler()
- self.dhcp_handler = DhcpHandler()
- self.env_handler = EnvHandler(self)
- self.provision_handler = ProvisionHandler()
- self.resource_disk_handler = ResourceDiskHandler()
- self.ext_handlers_handler = ExtHandlersHandler()
- self.deprovision_handler = DeprovisionHandler()
-
diff --git a/azurelinuxagent/distro/default/init.py b/azurelinuxagent/distro/default/init.py
index db74fef..c703e87 100644
--- a/azurelinuxagent/distro/default/init.py
+++ b/azurelinuxagent/distro/default/init.py
@@ -20,30 +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, verbose):
+ def __init__(self, distro):
+ self.distro = distro
+
+ def run(self, verbose):
#Init stdout log
level = logger.LogLevel.VERBOSE if verbose else logger.LogLevel.INFO
logger.add_logger_appender(logger.AppenderType.STDOUT, level)
#Init config
- conf_file_path = 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 conf.get_logs_verbose()
level = logger.LogLevel.VERBOSE if verbose else logger.LogLevel.INFO
logger.add_logger_appender(logger.AppenderType.FILE, level,
path="/var/log/waagent.log")
logger.add_logger_appender(logger.AppenderType.CONSOLE, level,
path="/dev/console")
- #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(conf.get_lib_dir(), "events")
+ event.init_event_logger(event_dir)
+ event.enable_unhandled_err_dump("WALA")
+
diff --git a/azurelinuxagent/distro/default/monitor.py b/azurelinuxagent/distro/default/monitor.py
new file mode 100644
index 0000000..3b26c9a
--- /dev/null
+++ b/azurelinuxagent/distro/default/monitor.py
@@ -0,0 +1,182 @@
+# 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
+import azurelinuxagent.conf as conf
+from azurelinuxagent.event import WALAEventOperation, add_event
+from azurelinuxagent.exception import EventError, ProtocolError, OSUtilError
+from azurelinuxagent.future import ustr
+from azurelinuxagent.utils.textutil import parse_doc, findall, find, getattrib
+from azurelinuxagent.protocol.restapi import TelemetryEventParam, \
+ TelemetryEventList, \
+ TelemetryEvent, \
+ set_properties, get_properties
+from azurelinuxagent.metadata import DISTRO_NAME, DISTRO_VERSION, \
+ DISTRO_CODE_NAME, AGENT_LONG_VERSION
+
+
+def parse_event(data_str):
+ try:
+ return parse_json_event(data_str)
+ except ValueError:
+ return parse_xml_event(data_str)
+
+def parse_xml_param(param_node):
+ name = getattrib(param_node, "Name")
+ value_str = getattrib(param_node, "Value")
+ attr_type = getattrib(param_node, "T")
+ value = value_str
+ if attr_type == 'mt:uint64':
+ value = int(value_str)
+ elif attr_type == 'mt:bool':
+ value = bool(value_str)
+ elif attr_type == 'mt:float64':
+ value = float(value_str)
+ return TelemetryEventParam(name, value)
+
+def parse_xml_event(data_str):
+ try:
+ xml_doc = parse_doc(data_str)
+ event_id = getattrib(find(xml_doc, "Event"), 'id')
+ provider_id = getattrib(find(xml_doc, "Provider"), 'id')
+ event = TelemetryEvent(event_id, provider_id)
+ param_nodes = findall(xml_doc, 'Param')
+ for param_node in param_nodes:
+ event.parameters.append(parse_xml_param(param_node))
+ return event
+ except Exception as e:
+ raise ValueError(ustr(e))
+
+def parse_json_event(data_str):
+ data = json.loads(data_str)
+ event = TelemetryEvent()
+ set_properties("TelemetryEvent", event, data)
+ return event
+
+
+class MonitorHandler(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_LONG_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()
+ self.sysinfo.append(TelemetryEventParam("VMName",
+ vminfo.vmName))
+ self.sysinfo.append(TelemetryEventParam("TenantName",
+ vminfo.tenantName))
+ self.sysinfo.append(TelemetryEventParam("RoleName",
+ vminfo.roleName))
+ self.sysinfo.append(TelemetryEventParam("RoleInstanceName",
+ vminfo.roleInstanceName))
+ self.sysinfo.append(TelemetryEventParam("ContainerId",
+ vminfo.containerId))
+ 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
+ data_str = evt_file.read().decode("utf-8",'ignore')
+ logger.verb("Processed event file: {0}", evt_file_name)
+ os.remove(evt_file_name)
+ return data_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(conf.get_lib_dir(), "events")
+ 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:
+ event = parse_event(data_str)
+ event.parameters.extend(self.sysinfo)
+ event_list.events.append(event)
+ except (ValueError, ProtocolError) as e:
+ logger.warn("Failed to decode event file: {0}", e)
+ continue
+
+ 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)
+ try:
+ self.collect_and_send_events()
+ except Exception as e:
+ logger.warn("Failed to send events: {0}", e)
+ time.sleep(60)
diff --git a/azurelinuxagent/distro/default/osutil.py b/azurelinuxagent/distro/default/osutil.py
index 00a57cc..18ab2ba 100644
--- a/azurelinuxagent/distro/default/osutil.py
+++ b/azurelinuxagent/distro/default/osutil.py
@@ -25,11 +25,15 @@ import struct
import time
import pwd
import fcntl
+import base64
import azurelinuxagent.logger as logger
-from azurelinuxagent.future import text
+import azurelinuxagent.conf as conf
+from azurelinuxagent.exception import OSUtilError
+from azurelinuxagent.future import ustr
import azurelinuxagent.utils.fileutil as fileutil
import azurelinuxagent.utils.shellutil as shellutil
import azurelinuxagent.utils.textutil as textutil
+from azurelinuxagent.utils.cryptutil import CryptUtil
__RULES_FILES__ = [ "/lib/udev/rules.d/75-persistent-net-generator.rules",
"/etc/udev/rules.d/70-persistent-net.rules" ]
@@ -40,44 +44,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:
@@ -86,6 +60,14 @@ class DefaultOSUtil(object):
return None
def is_sys_user(self, username):
+ """
+ Check whether use is a system user.
+ If reset sys user is allowed in conf, return False
+ Otherwise, check whether UID is less than UID_MIN
+ """
+ if conf.get_allow_reset_sys_user():
+ return False
+
userentry = self.get_userentry(username)
uidmin = None
try:
@@ -104,9 +86,13 @@ class DefaultOSUtil(object):
def useradd(self, username, expiration=None):
"""
- Update password and ssh key for user account.
- New account will be created if not exists.
+ Create user account with 'username'
"""
+ userentry = self.get_userentry(username)
+ if userentry is not None:
+ logger.info("User {0} already exists, skip useradd", username)
+ return
+
if expiration is not None:
cmd = "useradd -m {0} -e {1}".format(username, expiration)
else:
@@ -146,42 +132,21 @@ 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,
- 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)
- 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,
- file_name)
- thumbprint = shellutil.run_get_output(cmd)[1]
- thumbprint = thumbprint.rstrip().split('=')[1].replace(':', '').upper()
- return thumbprint
-
def deploy_ssh_keypair(self, username, keypair):
"""
Deploy id_rsa and id_rsa.pub
@@ -190,13 +155,14 @@ 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))
shutil.copyfile(prv_path, path)
pub_path = path + '.pub'
- pub = self.get_pubkey_from_prv(prv_path)
+ crytputil = CryptUtil(conf.get_openssl_cmd())
+ pub = crytputil.get_pubkey_from_prv(prv_path)
fileutil.write_file(pub_path, pub)
self.set_selinux_context(pub_path, 'unconfined_u:object_r:ssh_home_t:s0')
self.set_selinux_context(path, 'unconfined_u:object_r:ssh_home_t:s0')
@@ -204,8 +170,8 @@ class DefaultOSUtil(object):
os.chmod(pub_path, 0o600)
def openssl_to_openssh(self, input_file, output_file):
- shellutil.run("ssh-keygen -i -m PKCS8 -f {0} >> {1}".format(input_file,
- output_file))
+ cryptutil = CryptUtil(conf.get_openssl_cmd())
+ cryptutil.crt_to_ssh(input_file, output_file)
def deploy_ssh_pubkey(self, username, pubkey):
"""
@@ -215,6 +181,8 @@ class DefaultOSUtil(object):
if path is None:
raise OSUtilError("Publich key path is None")
+ crytputil = CryptUtil(conf.get_openssl_cmd())
+
path = self._norm_path(path)
dir_path = os.path.dirname(path)
fileutil.mkdir(dir_path, mode=0o700, owner=username)
@@ -223,12 +191,12 @@ 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))
pub_path = os.path.join(lib_dir, thumbprint + '.pub')
- pub = self.get_pubkey_from_crt(crt_path)
+ pub = crytputil.get_pubkey_from_crt(crt_path)
fileutil.write_file(pub_path, pub)
self.set_selinux_context(pub_path,
'unconfined_u:object_r:ssh_home_t:s0')
@@ -280,23 +248,21 @@ 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 = fileutil.read_file(conf_file_path).split("\n")
- textutil.set_ssh_config(conf, "ClientAliveInterval", "180")
- fileutil.write_file(conf_file_path, '\n'.join(conf))
+ conf_file_path = conf.get_sshd_conf_file_path()
+ conf_file = fileutil.read_file(conf_file_path).split("\n")
+ textutil.set_ssh_config(conf_file, "ClientAliveInterval", "180")
+ fileutil.write_file(conf_file_path, '\n'.join(conf_file))
logger.info("Configured SSH client probing to keep connections alive.")
def conf_sshd(self, disable_password):
option = "no" if disable_password else "yes"
- conf_file_path = self.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)
- fileutil.write_file(conf_file_path, "\n".join(conf))
+ conf_file_path = conf.get_sshd_conf_file_path()
+ conf_file = fileutil.read_file(conf_file_path).split("\n")
+ textutil.set_ssh_config(conf_file, "PasswordAuthentication", option)
+ textutil.set_ssh_config(conf_file, "ChallengeResponseAuthentication",
+ option)
+ fileutil.write_file(conf_file_path, "\n".join(conf_file))
logger.info("Disabled SSH password-based authentication methods.")
@@ -309,7 +275,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 +298,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.")
@@ -386,17 +352,9 @@ class DefaultOSUtil(object):
shellutil.run("iptables -I INPUT -p udp --dport 68 -j ACCEPT",
chk_err=False)
- def gen_transport_cert(self):
- """
- Create ssl certificate for https communication with endpoint server.
- """
- cmd = ("{0} req -x509 -nodes -subj /CN=LinuxTransport -days 32768 "
- "-newkey rsa:2048 -keyout TransportPrivate.pem "
- "-out TransportCert.pem").format(self.openssl_cmd)
- 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 +365,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)
@@ -603,7 +561,7 @@ class DefaultOSUtil(object):
for vmbus in os.listdir(path):
deviceid = fileutil.read_file(os.path.join(path, vmbus, "device_id"))
guid = deviceid.lstrip('{').split('-')
- if guid[0] == g0 and guid[1] == "000" + text(port_id):
+ if guid[0] == g0 and guid[1] == "000" + ustr(port_id):
for root, dirs, files in os.walk(path + vmbus):
if root.endswith("/block"):
device = dirs[0]
@@ -633,7 +591,7 @@ class DefaultOSUtil(object):
raise OSUtilError("Failed to remove sudoer: {0}".format(e))
def decode_customdata(self, data):
- return data
+ return base64.b64decode(data)
def get_total_mem(self):
cmd = "grep MemTotal /proc/meminfo |awk '{print $2}'"
@@ -649,4 +607,17 @@ class DefaultOSUtil(object):
return int(ret[1])
else:
raise OSUtilError("Failed to get procerssor cores")
+
+ def set_admin_access_to_ip(self, dest_ip):
+ #This allows root to access dest_ip
+ rm_old= "iptables -D OUTPUT -d {0} -j ACCEPT -m owner --uid-owner 0"
+ rule = "iptables -A OUTPUT -d {0} -j ACCEPT -m owner --uid-owner 0"
+ shellutil.run(rm_old.format(dest_ip), chk_err=False)
+ shellutil.run(rule.format(dest_ip))
+
+ #This blocks all other users to access dest_ip
+ rm_old = "iptables -D OUTPUT -d {0} -j DROP"
+ rule = "iptables -A OUTPUT -d {0} -j DROP"
+ shellutil.run(rm_old.format(dest_ip), chk_err=False)
+ shellutil.run(rule.format(dest_ip))
diff --git a/azurelinuxagent/distro/default/protocolUtil.py b/azurelinuxagent/distro/default/protocolUtil.py
new file mode 100644
index 0000000..34466cf
--- /dev/null
+++ b/azurelinuxagent/distro/default/protocolUtil.py
@@ -0,0 +1,243 @@
+# 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, DhcpError
+from azurelinuxagent.future import ustr
+import azurelinuxagent.utils.fileutil as fileutil
+from azurelinuxagent.protocol.ovfenv import OvfEnv
+from azurelinuxagent.protocol.wire import WireProtocol
+from azurelinuxagent.protocol.metadata import MetadataProtocol, METADATA_ENDPOINT
+import azurelinuxagent.utils.shellutil as shellutil
+
+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 = 360
+
+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 = conf.get_dvd_mount_point()
+ ovf_file_path_on_dvd = os.path.join(dvd_mount_point, OVF_FILE_NAME)
+ tag_file_path_on_dvd = os.path.join(dvd_mount_point, TAG_FILE_NAME)
+ try:
+ self.distro.osutil.mount_dvd()
+ ovfxml = fileutil.read_file(ovf_file_path_on_dvd, remove_bom=True)
+ ovfenv = OvfEnv(ovfxml)
+ ovfxml = re.sub("<UserPassword>.*?<", "<UserPassword>*<", 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(ustr(e))
+
+ try:
+ self.distro.osutil.umount_dvd()
+ self.distro.osutil.eject_dvd()
+ except OSUtilError as e:
+ logger.warn(ustr(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(ustr(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(ustr(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")
+ try:
+ self.distro.dhcp_handler.run()
+ except DhcpError as e:
+ raise ProtocolError(ustr(e))
+ endpoint = self.distro.dhcp_handler.endpoint
+
+ try:
+ protocol = WireProtocol(endpoint)
+ protocol.detect()
+ self._set_wireserver_endpoint(endpoint)
+ return protocol
+ except ProtocolError as e:
+ logger.info("WireServer is not responding. Reset endpoint")
+ self.distro.dhcp_handler.endpoint = None
+ raise e
+
+ def _detect_metadata_protocol(self):
+ protocol = MetadataProtocol()
+ protocol.detect()
+
+ #Only allow root access METADATA_ENDPOINT
+ self.distro.osutil.set_admin_access_to_ip(METADATA_ENDPOINT)
+
+ 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":
+ return self._detect_wire_protocol()
+
+ if protocol == "MetadataProtocol":
+ 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":
+ endpoint = self._get_wireserver_endpoint()
+ return WireProtocol(endpoint)
+ elif protocol_name == "MetadataProtocol":
+ return MetadataProtocol()
+ 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", "MetadataProtocol"]
+ 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(conf.get_lib_dir(), TAG_FILE_NAME)
+ if self.protocol is None:
+ protocols = []
+ if os.path.isfile(tag_file_path):
+ protocols.append("MetadataProtocol")
+ else:
+ protocols.append("WireProtocol")
+ self.protocol = self._detect_protocol(protocols)
+ finally:
+ self.lock.release()
+ return self.protocol
+
+ def get_protocol(self):
+ """
+ Get protocol instance based on previous detecting result.
+
+ :returns protocol instance
+ """
+ self.lock.acquire()
+ try:
+ if self.protocol is None:
+ self.protocol = self._get_protocol()
+ 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 424f083..695b82a 100644
--- a/azurelinuxagent/distro/default/provision.py
+++ b/azurelinuxagent/distro/default/provision.py
@@ -21,13 +21,11 @@ Provision handler
import os
import azurelinuxagent.logger as logger
-from azurelinuxagent.future import text
+from azurelinuxagent.future import ustr
import azurelinuxagent.conf as conf
from azurelinuxagent.event import add_event, WALAEventOperation
-from azurelinuxagent.exception import *
-from azurelinuxagent.utils.osutil import OSUTIL, OSUtilError
-import azurelinuxagent.protocol as prot
-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
@@ -35,61 +33,49 @@ CUSTOM_DATA_FILE="CustomData"
class ProvisionHandler(object):
- def process(self):
+ def __init__(self, distro):
+ self.distro = distro
+
+ 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
+ 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.")
- protocol = prot.FACTORY.get_default_protocol()
+ logger.info("Run provision handler.")
+ logger.info("Copy ovf-env.xml.")
+ try:
+ 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
+
+ self.distro.protocol_util.detect_protocol_by_file()
+
+ self.report_not_ready("Provisioning", "Starting")
+
try:
- status = prot.ProvisionStatus(status="NotReady",
- subStatus="Provisioning",
- description="Starting")
- try:
- protocol.report_provision_status(status)
- except prot.ProtocolError as e:
- add_event(name="WALA", is_success=False, message=text(e),
- op=WALAEventOperation.Provision)
-
- self.provision()
+ logger.info("Start provisioning")
+ self.provision(ovfenv)
fileutil.write_file(provisioned, "")
thumbprint = self.reg_ssh_host_key()
-
logger.info("Finished provisioning")
- status = prot.ProvisionStatus(status="Ready")
- status.properties.certificateThumbprint = thumbprint
-
- try:
- protocol.report_provision_status(status)
- except prot.ProtocolError as pe:
- add_event(name="WALA", is_success=False, message=text(pe),
- op=WALAEventOperation.Provision)
-
- add_event(name="WALA", is_success=True, message="",
- op=WALAEventOperation.Provision)
except ProvisionError as e:
logger.error("Provision failed: {0}", e)
- status = prot.ProvisionStatus(status="NotReady",
- subStatus="ProvisioningFailed",
- description= text(e))
- try:
- protocol.report_provision_status(status)
- except prot.ProtocolError as pe:
- add_event(name="WALA", is_success=False, message=text(pe),
- op=WALAEventOperation.Provision)
-
- add_event(name="WALA", is_success=False, message=text(e),
- op=WALAEventOperation.Provision)
+ self.report_not_ready("ProvisioningFailed", ustr(e))
+ self.report_event(ustr(e))
+ return
+ 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))
@@ -105,77 +91,101 @@ class ProvisionHandler(object):
raise ProvisionError(("Failed to generate ssh host key: "
"ret={0}, out= {1}").format(ret[0], ret[1]))
-
- def provision(self):
- logger.info("Copy ovf-env.xml.")
- try:
- ovfenv = ovf.copy_ovf_env()
- except prot.ProtocolError as e:
- raise ProvisionError("Failed to copy ovf-env.xml: {0}".format(e))
-
+ def provision(self, ovfenv):
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()
- fileutil.write_file(os.path.join(lib_dir, CUSTOM_DATA_FILE),
- OSUTIL.decode_customdata(customdata))
+
+ logger.info("Save custom data")
+ lib_dir = conf.get_lib_dir()
+ if conf.get_decode_customdata():
+ customdata= self.distro.osutil.decode_customdata(customdata)
+ customdata_file = os.path.join(lib_dir, CUSTOM_DATA_FILE)
+ fileutil.write_file(customdata_file, customdata)
+
+ if conf.get_execute_customdata():
+ logger.info("Execute custom data")
+ os.chmod(customdata_file, 0o700)
+ shellutil.run(customdata_file)
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(ustr(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(ustr(e))
diff --git a/azurelinuxagent/distro/default/resourceDisk.py b/azurelinuxagent/distro/default/resourceDisk.py
index 734863c..a6c5232 100644
--- a/azurelinuxagent/distro/default/resourceDisk.py
+++ b/azurelinuxagent/distro/default/resourceDisk.py
@@ -21,9 +21,8 @@ import os
import re
import threading
import azurelinuxagent.logger as logger
-from azurelinuxagent.future import text
+from azurelinuxagent.future import ustr
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,6 +40,8 @@ For additional details to please refer to the MSDN documentation at : http://msd
"""
class ResourceDiskHandler(object):
+ def __init__(self, distro):
+ self.distro = distro
def start_activate_resource_disk(self):
disk_thread = threading.Thread(target = self.run)
@@ -48,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:
@@ -68,25 +69,25 @@ class ResourceDiskHandler(object):
return mount_point
except ResourceDiskError as e:
logger.error("Failed to mount resource disk {0}", e)
- add_event(name="WALA", is_success=False, message=text(e),
+ add_event(name="WALA", is_success=False, message=ustr(e),
op=WALAEventOperation.ActivateResourceDisk)
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/run.py b/azurelinuxagent/distro/default/run.py
deleted file mode 100644
index dfd3b03..0000000
--- a/azurelinuxagent/distro/default/run.py
+++ /dev/null
@@ -1,71 +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 time
-import sys
-import azurelinuxagent.logger as logger
-from azurelinuxagent.future import text
-import azurelinuxagent.conf as conf
-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 as prot
-from azurelinuxagent.utils.osutil import OSUTIL
-import azurelinuxagent.utils.fileutil as fileutil
-
-
-class MainHandler(object):
- def __init__(self, handlers):
- self.handlers = handlers
-
- 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)
-
- event.enable_unhandled_err_dump("Azure Linux Agent")
- fileutil.write_file(OSUTIL.get_agent_pid_file_path(), text(os.getpid()))
-
- if conf.get_switch("DetectScvmmEnv", False):
- if self.handlers.scvmm_handler.detect_scvmm_env():
- return
-
- self.handlers.dhcp_handler.probe()
-
- prot.detect_default_protocol()
-
- event.EventMonitor().start()
-
- self.handlers.provision_handler.process()
-
- if conf.get_switch("ResourceDisk.Format", False):
- self.handlers.resource_disk_handler.start_activate_resource_disk()
-
- self.handlers.env_handler.start()
-
- protocol = prot.FACTORY.get_default_protocol()
- while True:
- #Handle extensions
- self.handlers.ext_handlers_handler.process()
- time.sleep(25)
-
diff --git a/azurelinuxagent/distro/default/scvmm.py b/azurelinuxagent/distro/default/scvmm.py
index 680c04b..4d083b4 100644
--- a/azurelinuxagent/distro/default/scvmm.py
+++ b/azurelinuxagent/distro/default/scvmm.py
@@ -20,28 +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, 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..74ea9e7 100644
--- a/azurelinuxagent/distro/loader.py
+++ b/azurelinuxagent/distro/loader.py
@@ -16,31 +16,52 @@
#
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):
- logger.warn("Unable to load distro implemetation for {0}.", DISTRO_NAME)
+def get_distro(distro_name=DISTRO_NAME, distro_version=DISTRO_VERSION,
+ distro_full_name=DISTRO_FULL_NAME):
+ if distro_name == "ubuntu":
+ if Version(distro_version) == Version("12.04") or \
+ Version(distro_version) == Version("12.10"):
+ return Ubuntu12Distro()
+ 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/loader.py b/azurelinuxagent/distro/oracle/loader.py
deleted file mode 100644
index 9dc428f..0000000
--- a/azurelinuxagent/distro/oracle/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/ubuntu/handlerFactory.py b/azurelinuxagent/distro/redhat/distro.py
index 11f7f04..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.deprovision_handler = UbuntuDeprovisionHandler()
+ 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/redhat/osutil.py b/azurelinuxagent/distro/redhat/osutil.py
index 7478867..7f769a5 100644
--- a/azurelinuxagent/distro/redhat/osutil.py
+++ b/azurelinuxagent/distro/redhat/osutil.py
@@ -26,20 +26,19 @@ import struct
import fcntl
import time
import base64
+import azurelinuxagent.conf as conf
import azurelinuxagent.logger as logger
-from azurelinuxagent.future import text, bytebuffer
+from azurelinuxagent.future import ustr, bytebuffer
+from azurelinuxagent.exception import OSUtilError, CryptError
import azurelinuxagent.utils.fileutil as fileutil
import azurelinuxagent.utils.shellutil as shellutil
import azurelinuxagent.utils.textutil as textutil
-from azurelinuxagent.distro.default.osutil import DefaultOSUtil, OSUtilError
+from azurelinuxagent.utils.cryptutil import CryptUtil
+from azurelinuxagent.distro.default.osutil import DefaultOSUtil
class Redhat6xOSUtil(DefaultOSUtil):
def __init__(self):
super(Redhat6xOSUtil, self).__init__()
- self.sshd_conf_file_path = '/etc/ssh/sshd_config'
- self.openssl_cmd = '/usr/bin/openssl'
- self.conf_file_path = '/etc/waagent.conf'
- self.selinux=None
def start_network(self):
return shellutil.run("/sbin/service networking start", chk_err=False)
@@ -58,63 +57,14 @@ class Redhat6xOSUtil(DefaultOSUtil):
def unregister_agent_service(self):
return shellutil.run("chkconfig --del waagent", chk_err=False)
-
- def asn1_to_ssh_rsa(self, pubkey):
- lines = pubkey.split("\n")
- lines = [x for x in lines if not x.startswith("----")]
- base64_encoded = "".join(lines)
- try:
- #TODO remove pyasn1 dependency
- from pyasn1.codec.der import decoder as der_decoder
- der_encoded = base64.b64decode(base64_encoded)
- der_encoded = der_decoder.decode(der_encoded)[0][1]
- key = der_decoder.decode(self.bits_to_bytes(der_encoded))[0]
- n=key[0]
- e=key[1]
- keydata = bytearray()
- keydata.extend(struct.pack('>I', len("ssh-rsa")))
- keydata.extend(b"ssh-rsa")
- keydata.extend(struct.pack('>I', len(self.num_to_bytes(e))))
- keydata.extend(self.num_to_bytes(e))
- keydata.extend(struct.pack('>I', len(self.num_to_bytes(n)) + 1))
- keydata.extend(b"\0")
- keydata.extend(self.num_to_bytes(n))
- keydata_base64 = base64.b64encode(bytebuffer(keydata))
- return text(b"ssh-rsa " + keydata_base64 + b"\n",
- encoding='utf-8')
- except ImportError as e:
- raise OSUtilError("Failed to load pyasn1.codec.der")
-
- def num_to_bytes(self, num):
- """
- Pack number into bytes. Retun as string.
- """
- result = bytearray()
- while num:
- result.append(num & 0xFF)
- num >>= 8
- result.reverse()
- return result
-
- def bits_to_bytes(self, bits):
- """
- Convert an array contains bits, [0,1] to a byte array
- """
- index = 7
- byte_array = bytearray()
- curr = 0
- for bit in bits:
- curr = curr | (bit << index)
- index = index - 1
- if index == -1:
- byte_array.append(curr)
- curr = 0
- index = 7
- return bytes(byte_array)
-
+
def openssl_to_openssh(self, input_file, output_file):
pubkey = fileutil.read_file(input_file)
- ssh_rsa_pubkey = self.asn1_to_ssh_rsa(pubkey)
+ try:
+ cryptutil = CryptUtil(conf.get_openssl_cmd())
+ ssh_rsa_pubkey = cryptutil.asn1_to_ssh(pubkey)
+ except CryptError as e:
+ raise OSUtilError(ustr(e))
fileutil.write_file(output_file, ssh_rsa_pubkey)
#Override
@@ -134,8 +84,7 @@ class Redhat6xOSUtil(DefaultOSUtil):
def set_dhcp_hostname(self, hostname):
ifname = self.get_if_name()
filepath = "/etc/sysconfig/network-scripts/ifcfg-{0}".format(ifname)
- fileutil.update_conf_file(filepath,
- 'DHCP_HOSTNAME',
+ fileutil.update_conf_file(filepath, 'DHCP_HOSTNAME',
'DHCP_HOSTNAME={0}'.format(hostname))
class RedhatOSUtil(Redhat6xOSUtil):
@@ -162,4 +111,5 @@ class RedhatOSUtil(Redhat6xOSUtil):
def unregister_agent_service(self):
return shellutil.run("systemctl disable waagent", chk_err=False)
-
+ def openssl_to_openssh(self, input_file, output_file):
+ DefaultOSUtil.openssl_to_openssh(self, input_file, output_file)
diff --git a/azurelinuxagent/distro/default/loader.py b/azurelinuxagent/distro/suse/distro.py
index 55a51e0..5b39369 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 = SUSE11OSUtil()
+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/deprovision.py b/azurelinuxagent/distro/ubuntu/deprovision.py
index 0c3c4e5..da6e834 100644
--- a/azurelinuxagent/distro/ubuntu/deprovision.py
+++ b/azurelinuxagent/distro/ubuntu/deprovision.py
@@ -33,6 +33,9 @@ def del_resolv():
class UbuntuDeprovisionHandler(DeprovisionHandler):
+ def __init__(self, distro):
+ super(UbuntuDeprovisionHandler, self).__init__(distro)
+
def setup(self, deluser):
warnings, actions = super(UbuntuDeprovisionHandler, self).setup(deluser)
warnings.append("WARNING! Nameserver configuration in "
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 a68fe4d..330e057 100644
--- a/azurelinuxagent/distro/ubuntu/provision.py
+++ b/azurelinuxagent/distro/ubuntu/provision.py
@@ -20,12 +20,11 @@
import os
import time
import azurelinuxagent.logger as logger
-from azurelinuxagent.future import text
+from azurelinuxagent.future import ustr
import azurelinuxagent.conf as conf
-import azurelinuxagent.protocol as prot
+import azurelinuxagent.protocol.ovfenv as ovfenv
from azurelinuxagent.event import add_event, WALAEventOperation
-from azurelinuxagent.exception import *
-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
@@ -34,49 +33,61 @@ from azurelinuxagent.distro.default.provision import ProvisionHandler
On ubuntu image, provision could be disabled.
"""
class UbuntuProvisionHandler(ProvisionHandler):
- def process(self):
+ def __init__(self, distro):
+ self.distro = distro
+
+ def run(self):
#If provision is enabled, run default provision handler
- if conf.get_switch("Provisioning.Enabled", False):
- super(UbuntuProvisionHandler, self).process()
+ if conf.get_provision_enabled():
+ super(UbuntuProvisionHandler, self).run()
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 finish provisioning.")
- protocol = prot.FACTORY.get_default_protocol()
+ logger.info("Waiting cloud-init to copy ovf-env.xml.")
+ self.wait_for_ovfenv()
+
+ protocol = self.distro.protocol_util.detect_protocol()
+ self.report_not_ready("Provisioning", "Starting")
+ logger.info("Sleep 15 seconds to prevent throttling")
+ time.sleep(15) #Sleep to prevent throttling
try:
logger.info("Wait for ssh host key to be generated.")
thumbprint = self.wait_for_ssh_host_key()
fileutil.write_file(provisioned, "")
-
logger.info("Finished provisioning")
- status = prot.ProvisionStatus(status="Ready")
- status.properties.certificateThumbprint = thumbprint
- try:
- protocol.report_provision_status(status)
- except prot.ProtocolError as pe:
- add_event(name="WALA", is_success=False, message=text(pe),
- op=WALAEventOperation.Provision)
-
+
except ProvisionError as e:
logger.error("Provision failed: {0}", e)
- status = prot.ProvisionStatus(status="NotReady",
- subStatus="ProvisioningFailed",
- description= text(e))
- try:
- protocol.report_provision_status(status)
- except prot.ProtocolError as pe:
- add_event(name="WALA", is_success=False, message=text(pe),
- op=WALAEventOperation.Provision)
+ self.report_not_ready("ProvisioningFailed", ustr(e))
+ self.report_event(ustr(e))
+ return
+
+ self.report_ready(thumbprint)
+ self.report_event("Provision succeed", is_success=True)
- add_event(name="WALA", is_success=False, message=text(e),
- op=WALAEventOperation.Provision)
+ def wait_for_ovfenv(self, max_retry=60):
+ """
+ Wait for cloud-init to copy ovf-env.xml file from provision ISO
+ """
+ for retry in range(0, max_retry):
+ try:
+ self.distro.protocol_util.get_ovf_env()
+ return
+ 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):
- kepair_type = conf.get("Provisioning.SshHostKeyPairType", "rsa")
+ """
+ Wait for cloud-init to generate ssh host key
+ """
+ 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 02e8017..f38b242 100644
--- a/azurelinuxagent/event.py
+++ b/azurelinuxagent/event.py
@@ -25,14 +25,15 @@ import datetime
import threading
import platform
import azurelinuxagent.logger as logger
-from azurelinuxagent.future import text
-import azurelinuxagent.protocol as prot
+from azurelinuxagent.exception import EventError, ProtocolError
+from azurelinuxagent.future import ustr
+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"
@@ -47,132 +48,65 @@ class WALAEventOperation:
ActivateResourceDisk="ActivateResourceDisk"
UnhandledError="UnhandledError"
-class EventMonitor(object):
+class EventLogger(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 self.event_dir is None:
+ logger.warn("Event reporter is not initialized.")
+ return
- 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()))
- try:
- protocol = prot.FACTORY.get_default_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()
+ if not os.path.exists(self.event_dir):
+ os.mkdir(self.event_dir)
+ os.chmod(self.event_dir, 0o700)
+ if len(os.listdir(self.event_dir)) > 1000:
+ raise EventError("Too many files under: {0}".format(self.event_dir))
- def collect_event(self, evt_file_name):
+ filename = os.path.join(self.event_dir, ustr(int(time.time()*1000000)))
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)
-
- 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.error("Failed to decode json event file: {0}", e)
- continue
-
- 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
-
+ raise EventError("Failed to write events to file:{0}", e)
+
+ def add_event(self, name, op="", is_success=True, duration=0, version="1.0",
+ message="", evt_type="", is_internal=False):
+ 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))
+
+ data = get_properties(event)
try:
- protocol = prot.FACTORY.get_default_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_logger__ = EventLogger()
def add_event(name, op="", is_success=True, duration=0, version="1.0",
- message="", evt_type="", is_internal=False):
+ message="", evt_type="", is_internal=False,
+ reporter=__event_logger__):
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))
- data = prot.get_properties(event)
- try:
- save_event(json.dumps(data))
- except EventError as e:
- logger.error("{0}", e)
+ 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)
+
+def init_event_logger(event_dir, reporter=__event_logger__):
+ reporter.event_dir = event_dir
def dump_unhandled_err(name):
if hasattr(sys, 'last_type') and hasattr(sys, 'last_value') and \
@@ -184,8 +118,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..7fa5cff 100644
--- a/azurelinuxagent/exception.py
+++ b/azurelinuxagent/exception.py
@@ -24,42 +24,92 @@ class AgentError(Exception):
"""
Base class of agent error.
"""
- def __init__(self, errno, msg):
- msg = "({0}){1}".format(errno, msg)
+ def __init__(self, errno, msg, inner=None):
+ msg = u"({0}){1}".format(errno, msg)
+ if inner is not None:
+ msg = u"{0} \n inner error: {1}".format(msg, inner)
super(AgentError, self).__init__(msg)
class AgentConfigError(AgentError):
"""
When configure file is not found or malformed.
"""
- def __init__(self, msg):
- super(AgentConfigError, self).__init__('000001', msg)
+ def __init__(self, msg=None, inner=None):
+ super(AgentConfigError, self).__init__('000001', msg, inner)
class AgentNetworkError(AgentError):
"""
When network is not avaiable.
"""
- def __init__(self, msg):
- super(AgentNetworkError, self).__init__('000002', msg)
+ def __init__(self, msg=None, inner=None):
+ super(AgentNetworkError, self).__init__('000002', msg, inner)
class ExtensionError(AgentError):
"""
When failed to execute an extension
"""
- def __init__(self, msg):
- super(ExtensionError, self).__init__('000003', msg)
+ def __init__(self, msg=None, inner=None):
+ super(ExtensionError, self).__init__('000003', msg, inner)
class ProvisionError(AgentError):
"""
When provision failed
"""
- def __init__(self, msg):
- super(ProvisionError, self).__init__('000004', msg)
+ def __init__(self, msg=None, inner=None):
+ super(ProvisionError, self).__init__('000004', msg, inner)
class ResourceDiskError(AgentError):
"""
Mount resource disk failed
"""
- def __init__(self, msg):
- super(ResourceDiskError, self).__init__('000005', msg)
+ def __init__(self, msg=None, inner=None):
+ super(ResourceDiskError, self).__init__('000005', msg, inner)
+class DhcpError(AgentError):
+ """
+ Failed to handle dhcp response
+ """
+ def __init__(self, msg=None, inner=None):
+ super(DhcpError, self).__init__('000006', msg, inner)
+
+class OSUtilError(AgentError):
+ """
+ Failed to perform operation to OS configuration
+ """
+ def __init__(self, msg=None, inner=None):
+ super(OSUtilError, self).__init__('000007', msg, inner)
+
+class ProtocolError(AgentError):
+ """
+ Azure protocol error
+ """
+ def __init__(self, msg=None, inner=None):
+ super(ProtocolError, self).__init__('000008', msg, inner)
+
+class ProtocolNotFoundError(ProtocolError):
+ """
+ Azure protocol endpoint not found
+ """
+ def __init__(self, msg=None, inner=None):
+ super(ProtocolNotFoundError, self).__init__(msg, inner)
+
+class HttpError(AgentError):
+ """
+ Http request failure
+ """
+ def __init__(self, msg=None, inner=None):
+ super(HttpError, self).__init__('000009', msg, inner)
+
+class EventError(AgentError):
+ """
+ Event reporting error
+ """
+ def __init__(self, msg=None, inner=None):
+ super(EventError, self).__init__('000010', msg, inner)
+
+class CryptError(AgentError):
+ """
+ Encrypt/Decrypt error
+ """
+ def __init__(self, msg=None, inner=None):
+ super(CryptError, self).__init__('000011', msg, inner)
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/logger.py b/azurelinuxagent/logger.py
index 21c02a6..6c6b406 100644
--- a/azurelinuxagent/logger.py
+++ b/azurelinuxagent/logger.py
@@ -22,7 +22,7 @@ Log utils
"""
import os
import sys
-from azurelinuxagent.future import text
+from azurelinuxagent.future import ustr
from datetime import datetime
class Logger(object):
@@ -49,8 +49,8 @@ class Logger(object):
def log(self, level, msg_format, *args):
#if msg_format is not unicode convert it to unicode
- if type(msg_format) is not text:
- msg_format = text(msg_format, errors="backslashreplace")
+ if type(msg_format) is not ustr:
+ msg_format = ustr(msg_format, errors="backslashreplace")
if len(args) > 0:
msg = msg_format.format(*args)
else:
@@ -63,7 +63,7 @@ class Logger(object):
else:
log_item = u"{0} {1} {2}\n".format(time, level_str, msg)
- log_item = text(log_item.encode('ascii', "backslashreplace"),
+ log_item = ustr(log_item.encode('ascii', "backslashreplace"),
encoding="ascii")
for appender in self.appenders:
appender.write(level, log_item)
diff --git a/azurelinuxagent/metadata.py b/azurelinuxagent/metadata.py
index 5cf4902..34fdcf9 100644
--- a/azurelinuxagent/metadata.py
+++ b/azurelinuxagent/metadata.py
@@ -22,11 +22,11 @@ import re
import platform
import sys
import azurelinuxagent.utils.fileutil as fileutil
-from azurelinuxagent.future import text
+from azurelinuxagent.future import ustr
def get_distro():
if 'FreeBSD' in platform.system():
- release = re.sub('\-.*\Z', '', text(platform.release()))
+ release = re.sub('\-.*\Z', '', ustr(platform.release()))
osinfo = ['freebsd', release, '', 'freebsd']
if 'linux_distribution' in dir(platform):
osinfo = list(platform.linux_distribution(full_distribution_name=0))
@@ -47,7 +47,7 @@ def get_distro():
AGENT_NAME = "WALinuxAgent"
AGENT_LONG_NAME = "Azure Linux Agent"
-AGENT_VERSION = '2.1.2'
+AGENT_VERSION = '2.1.3'
AGENT_LONG_VERSION = "{0}-{1}".format(AGENT_NAME, AGENT_VERSION)
AGENT_DESCRIPTION = """\
The Azure Linux Agent supports the provisioning and running of Linux
diff --git a/azurelinuxagent/protocol/__init__.py b/azurelinuxagent/protocol/__init__.py
index a4572e6..8c1bbdb 100644
--- a/azurelinuxagent/protocol/__init__.py
+++ b/azurelinuxagent/protocol/__init__.py
@@ -16,8 +16,3 @@
#
# Requires Python 2.4+ and Openssl 1.0+
#
-
-from azurelinuxagent.protocol.common import *
-from azurelinuxagent.protocol.protocolFactory import FACTORY, \
- detect_default_protocol
-
diff --git a/azurelinuxagent/protocol/v2.py b/azurelinuxagent/protocol/metadata.py
index 34102b7..8a1656f 100644
--- a/azurelinuxagent/protocol/v2.py
+++ b/azurelinuxagent/protocol/metadata.py
@@ -17,16 +17,30 @@
# Requires Python 2.4+ and Openssl 1.0+
import json
-from azurelinuxagent.future import httpclient, text
+import shutil
+import os
+import time
+from azurelinuxagent.exception import ProtocolError, HttpError
+from azurelinuxagent.future import httpclient, ustr
+import azurelinuxagent.conf as conf
+import azurelinuxagent.logger as logger
import azurelinuxagent.utils.restutil as restutil
-from azurelinuxagent.protocol.common import *
+import azurelinuxagent.utils.textutil as textutil
+import azurelinuxagent.utils.fileutil as fileutil
+from azurelinuxagent.utils.cryptutil import CryptUtil
+from azurelinuxagent.protocol.restapi import *
-ENDPOINT='169.254.169.254'
-#TODO use http for azure pack test
-#ENDPOINT='localhost'
+METADATA_ENDPOINT='169.254.169.254'
APIVERSION='2015-05-01-preview'
BASE_URI = "http://{0}/Microsoft.Compute/{1}?api-version={2}{3}"
+TRANSPORT_PRV_FILE_NAME = "V2TransportPrivate.pem"
+TRANSPORT_CERT_FILE_NAME = "V2TransportCert.pem"
+
+#TODO remote workarround for azure stack
+MAX_PING = 30
+RETRY_PING_INTERVAL = 10
+
def _add_content_type(headers):
if headers is None:
headers = {}
@@ -35,7 +49,7 @@ def _add_content_type(headers):
class MetadataProtocol(Protocol):
- def __init__(self, apiversion=APIVERSION, endpoint=ENDPOINT):
+ def __init__(self, apiversion=APIVERSION, endpoint=METADATA_ENDPOINT):
self.apiversion = apiversion
self.endpoint = endpoint
self.identity_uri = BASE_URI.format(self.endpoint, "identity",
@@ -58,24 +72,25 @@ class MetadataProtocol(Protocol):
def _get_data(self, url, headers=None):
try:
resp = restutil.http_get(url, headers=headers)
- except restutil.HttpError as e:
- raise ProtocolError(text(e))
+ except HttpError as e:
+ raise ProtocolError(ustr(e))
if resp.status != httpclient.OK:
raise ProtocolError("{0} - GET: {1}".format(resp.status, url))
data = resp.read()
+ etag = resp.getheader('ETag')
if data is None:
return None
- data = json.loads(text(data, encoding="utf-8"))
- return data
+ data = json.loads(ustr(data, encoding="utf-8"))
+ return data, etag
def _put_data(self, url, data, headers=None):
headers = _add_content_type(headers)
try:
resp = restutil.http_put(url, json.dumps(data), headers=headers)
- except restutil.HttpError as e:
- raise ProtocolError(text(e))
+ except HttpError as e:
+ raise ProtocolError(ustr(e))
if resp.status != httpclient.OK:
raise ProtocolError("{0} - PUT: {1}".format(resp.status, url))
@@ -83,17 +98,41 @@ class MetadataProtocol(Protocol):
headers = _add_content_type(headers)
try:
resp = restutil.http_post(url, json.dumps(data), headers=headers)
- except restutil.HttpError as e:
- raise ProtocolError(text(e))
+ except HttpError as e:
+ raise ProtocolError(ustr(e))
if resp.status != httpclient.CREATED:
raise ProtocolError("{0} - POST: {1}".format(resp.status, url))
+
+ def _get_trans_cert(self):
+ trans_crt_file = os.path.join(conf.get_lib_dir(),
+ TRANSPORT_CERT_FILE_NAME)
+ if not os.path.isfile(trans_crt_file):
+ raise ProtocolError("{0} is missing.".format(trans_crt_file))
+ content = fileutil.read_file(trans_crt_file)
+ return textutil.get_bytes_from_pem(content)
+
+ def detect(self):
+ self.get_vminfo()
+ trans_prv_file = os.path.join(conf.get_lib_dir(),
+ TRANSPORT_PRV_FILE_NAME)
+ trans_cert_file = os.path.join(conf.get_lib_dir(),
+ TRANSPORT_CERT_FILE_NAME)
+ cryptutil = CryptUtil(conf.get_openssl_cmd())
+ cryptutil.gen_transport_cert(trans_prv_file, trans_cert_file)
+
+ #"Install" the cert and private key to /var/lib/waagent
+ thumbprint = cryptutil.get_thumbprint_from_crt(trans_cert_file)
+ prv_file = os.path.join(conf.get_lib_dir(),
+ "{0}.prv".format(thumbprint))
+ crt_file = os.path.join(conf.get_lib_dir(),
+ "{0}.crt".format(thumbprint))
+ shutil.copyfile(trans_prv_file, prv_file)
+ shutil.copyfile(trans_cert_file, crt_file)
- def initialize(self):
- pass
def get_vminfo(self):
vminfo = VMInfo()
- data = self._get_data(self.identity_uri)
+ data, etag = self._get_data(self.identity_uri)
set_properties("vminfo", vminfo, data)
return vminfo
@@ -102,17 +141,20 @@ class MetadataProtocol(Protocol):
return CertList()
def get_ext_handlers(self):
+ headers = {
+ "x-ms-vmagent-public-x509-cert": self._get_trans_cert()
+ }
ext_list = ExtHandlerList()
- data = self._get_data(self.ext_uri)
+ data, etag = self._get_data(self.ext_uri, headers=headers)
set_properties("extensionHandlers", ext_list.extHandlers, data)
- return ext_list
+ return ext_list, etag
def get_ext_handler_pkgs(self, ext_handler):
ext_handler_pkgs = ExtHandlerPackageList()
data = None
for version_uri in ext_handler.versionUris:
try:
- data = self._get_data(version_uri.uri)
+ data, etag = self._get_data(version_uri.uri)
break
except ProtocolError as e:
logger.warn("Failed to get version uris: {0}", e)
@@ -128,6 +170,14 @@ class MetadataProtocol(Protocol):
def report_vm_status(self, vm_status):
validata_param('vmStatus', vm_status, VMStatus)
data = get_properties(vm_status)
+ #TODO code field is not implemented for metadata protocol yet. Remove it
+ handler_statuses = data['vmAgent']['extensionHandlers']
+ for handler_status in handler_statuses:
+ try:
+ handler_status.pop('code', None)
+ except KeyError:
+ pass
+
self._put_data(self.vm_status_uri, data)
def report_ext_status(self, ext_handler_name, ext_name, ext_status):
diff --git a/azurelinuxagent/protocol/ovfenv.py b/azurelinuxagent/protocol/ovfenv.py
index 9c845ee..de6791c 100644
--- a/azurelinuxagent/protocol/ovfenv.py
+++ b/azurelinuxagent/protocol/ovfenv.py
@@ -17,60 +17,22 @@
# Requires Python 2.4+ and Openssl 1.0+
#
"""
-Copy and parse ovf-env.xml from provisiong ISO and local cache
+Copy and parse ovf-env.xml from provisioning ISO and local cache
"""
import os
import re
+import shutil
import xml.dom.minidom as minidom
import azurelinuxagent.logger as logger
-from azurelinuxagent.future import text
+from azurelinuxagent.exception import ProtocolError
+from azurelinuxagent.future import ustr
import azurelinuxagent.utils.fileutil as fileutil
from azurelinuxagent.utils.textutil import parse_doc, findall, find, findtext
-from azurelinuxagent.utils.osutil import OSUTIL, OSUtilError
-from azurelinuxagent.protocol import ProtocolError
-OVF_FILE_NAME = "ovf-env.xml"
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("<UserPassword>.*?<", "<UserPassword>*<", ovfxml)
- ovf_file_path = os.path.join(OSUTIL.get_lib_dir(), OVF_FILE_NAME)
- fileutil.write_file(ovf_file_path, ovfxml)
- 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/protocolFactory.py b/azurelinuxagent/protocol/protocolFactory.py
deleted file mode 100644
index 0bf6e52..0000000
--- a/azurelinuxagent/protocol/protocolFactory.py
+++ /dev/null
@@ -1,114 +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 threading
-import azurelinuxagent.logger as logger
-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
-
-WIRE_SERVER_ADDR_FILE_NAME = "WireServer"
-
-def get_wire_protocol_endpoint():
- path = os.path.join(OSUTIL.get_lib_dir(), WIRE_SERVER_ADDR_FILE_NAME)
- try:
- endpoint = fileutil.read_file(path)
- except IOError as e:
- raise ProtocolNotFound("Wire server endpoint not found: {0}".format(e))
-
- if endpoint is None:
- raise ProtocolNotFound("Wire server endpoint is None")
-
- return endpoint
-
-def detect_wire_protocol():
- endpoint = get_wire_protocol_endpoint()
-
- OSUTIL.gen_transport_cert()
- protocol = WireProtocol(endpoint)
- protocol.initialize()
- logger.info("Protocol V1 found.")
- return protocol
-
-def detect_metadata_protocol():
- protocol = MetadataProtocol()
- protocol.initialize()
-
- logger.info("Protocol V2 found.")
- return protocol
-
-def detect_available_protocols(prob_funcs=[detect_wire_protocol,
- detect_metadata_protocol]):
- available_protocols = []
- for probe_func in prob_funcs:
- try:
- protocol = probe_func()
- available_protocols.append(protocol)
- except ProtocolNotFound as e:
- logger.info(text(e))
- return available_protocols
-
-def detect_default_protocol():
- logger.info("Detect default protocol.")
- available_protocols = detect_available_protocols()
- return choose_default_protocol(available_protocols)
-
-def choose_default_protocol(protocols):
- if len(protocols) > 0:
- return protocols[0]
- else:
- raise ProtocolNotFound("No available protocol detected.")
-
-def get_wire_protocol():
- endpoint = get_wire_protocol_endpoint()
- return WireProtocol(endpoint)
-
-def get_metadata_protocol():
- return MetadataProtocol()
-
-def get_available_protocols(getters=[get_wire_protocol, get_metadata_protocol]):
- available_protocols = []
- for getter in getters:
- try:
- protocol = getter()
- available_protocols.append(protocol)
- except ProtocolNotFound as e:
- logger.info(text(e))
- return available_protocols
-
-class ProtocolFactory(object):
- def __init__(self):
- self._protocol = None
- self._lock = threading.Lock()
-
- def get_default_protocol(self):
- if self._protocol is None:
- self._lock.acquire()
- if self._protocol is None:
- available_protocols = get_available_protocols()
- self._protocol = choose_default_protocol(available_protocols)
- self._lock.release()
-
- return self._protocol
-
-FACTORY = ProtocolFactory()
diff --git a/azurelinuxagent/protocol/common.py b/azurelinuxagent/protocol/restapi.py
index 367794f..fbd29ed 100644
--- a/azurelinuxagent/protocol/common.py
+++ b/azurelinuxagent/protocol/restapi.py
@@ -22,14 +22,9 @@ import re
import json
import xml.dom.minidom
import azurelinuxagent.logger as logger
-from azurelinuxagent.future import text
-import azurelinuxagent.utils.fileutil as fileutil
-
-class ProtocolError(Exception):
- pass
-
-class ProtocolNotFound(Exception):
- pass
+from azurelinuxagent.exception import ProtocolError, HttpError
+from azurelinuxagent.future import ustr
+import azurelinuxagent.utils.restutil as restutil
def validata_param(name, val, expected_type):
if val is None:
@@ -88,9 +83,14 @@ class DataContractList(list):
Data contract between guest and host
"""
class VMInfo(DataContract):
- def __init__(self, subscriptionId=None, vmName=None):
+ def __init__(self, subscriptionId=None, vmName=None, containerId=None,
+ roleName=None, roleInstanceName=None, tenantName=None):
self.subscriptionId = subscriptionId
self.vmName = vmName
+ self.containerId = containerId
+ self.roleName = roleName
+ self.roleInstanceName = roleInstanceName
+ self.tenantName = tenantName
class Cert(DataContract):
def __init__(self, name=None, thumbprint=None, certificateDataUri=None):
@@ -104,11 +104,11 @@ class CertList(DataContract):
class Extension(DataContract):
def __init__(self, name=None, sequenceNumber=None, publicSettings=None,
- privateSettings=None, certificateThumbprint=None):
+ protectedSettings=None, certificateThumbprint=None):
self.name = name
self.sequenceNumber = sequenceNumber
self.publicSettings = publicSettings
- self.privateSettings = privateSettings
+ self.protectedSettings = protectedSettings
self.certificateThumbprint = certificateThumbprint
class ExtHandlerProperties(DataContract):
@@ -176,12 +176,14 @@ class ExtensionStatus(DataContract):
self.substatusList = DataContractList(ExtensionSubStatus)
class ExtHandlerStatus(DataContract):
- def __init__(self, name=None, version=None, status=None, message=None):
+ def __init__(self, name=None, version=None, status=None, code=0,
+ message=None):
self.name = name
self.version = version
self.status = status
+ self.code = code
self.message = message
- self.extensions = DataContractList(text)
+ self.extensions = DataContractList(ustr)
class VMAgentStatus(DataContract):
def __init__(self, version=None, status=None, message=None):
@@ -211,7 +213,7 @@ class TelemetryEventList(DataContract):
class Protocol(DataContract):
- def initialize(self):
+ def detect(self):
raise NotImplementedError()
def get_vminfo(self):
@@ -226,6 +228,14 @@ class Protocol(DataContract):
def get_ext_handler_pkgs(self, extension):
raise NotImplementedError()
+ def download_ext_handler_pkg(self, uri):
+ try:
+ resp = restutil.http_get(uri, chk_proxy=True)
+ if resp.status == restutil.httpclient.OK:
+ return resp.read()
+ except HttpError as e:
+ raise ProtocolError("Failed to download from: {0}".format(uri), e)
+
def report_provision_status(self, provision_status):
raise NotImplementedError()
diff --git a/azurelinuxagent/protocol/v1.py b/azurelinuxagent/protocol/wire.py
index 92fcc06..7b5ffe8 100644
--- a/azurelinuxagent/protocol/v1.py
+++ b/azurelinuxagent/protocol/wire.py
@@ -22,16 +22,19 @@ import re
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.future import text, httpclient, bytebuffer
+from azurelinuxagent.exception import ProtocolError, HttpError, \
+ ProtocolNotFoundError
+from azurelinuxagent.future import ustr, httpclient, bytebuffer
import azurelinuxagent.utils.restutil as restutil
from azurelinuxagent.utils.textutil import parse_doc, findall, find, findtext, \
- getattrib, gettext, remove_bom
-from azurelinuxagent.utils.osutil import OSUTIL
+ getattrib, gettext, remove_bom, \
+ get_bytes_from_pem
import azurelinuxagent.utils.fileutil as fileutil
import azurelinuxagent.utils.shellutil as shellutil
-from azurelinuxagent.protocol.common import *
+from azurelinuxagent.utils.cryptutil import CryptUtil
+from azurelinuxagent.protocol.restapi import *
VERSION_INFO_URI = "http://{0}/?comp=versions"
GOAL_STATE_URI = "http://{0}/machine/?comp=goalstate"
@@ -53,6 +56,7 @@ TRANSPORT_CERT_FILE_NAME = "TransportCert.pem"
TRANSPORT_PRV_FILE_NAME = "TransportPrivate.pem"
PROTOCOL_VERSION = "2012-11-30"
+ENDPOINT_FINE_NAME = "WireServer"
SHORT_WAITING_INTERVAL = 1 # 1 second
LONG_WAITING_INTERVAL = 15 # 15 seconds
@@ -61,19 +65,37 @@ class WireProtocolResourceGone(ProtocolError):
pass
class WireProtocol(Protocol):
+ """Slim layer to adapte wire protocol data to metadata protocol interface"""
def __init__(self, endpoint):
- self.client = WireClient(endpoint)
+ if endpoint is None:
+ raise ProtocolError("WireProtocl endpoint is None")
+ self.endpoint = endpoint
+ self.client = WireClient(self.endpoint)
- def initialize(self):
+ def detect(self):
self.client.check_wire_protocol_version()
+
+ trans_prv_file = os.path.join(conf.get_lib_dir(),
+ TRANSPORT_PRV_FILE_NAME)
+ trans_cert_file = os.path.join(conf.get_lib_dir(),
+ TRANSPORT_CERT_FILE_NAME)
+ cryptutil = CryptUtil(conf.get_openssl_cmd())
+ cryptutil.gen_transport_cert(trans_prv_file, trans_cert_file)
+
self.client.update_goal_state(forced=True)
def get_vminfo(self):
+ goal_state = self.client.get_goal_state()
hosting_env = self.client.get_hosting_env()
+
vminfo = VMInfo()
vminfo.subscriptionId = None
vminfo.vmName = hosting_env.vm_name
+ vminfo.tenantName = hosting_env.deployment_name
+ vminfo.roleName = hosting_env.role_name
+ vminfo.roleInstanceName = goal_state.role_instance_id
+ vminfo.containerId = goal_state.container_id
return vminfo
def get_certs(self):
@@ -81,12 +103,16 @@ class WireProtocol(Protocol):
return certificates.cert_list
def get_ext_handlers(self):
+ logger.verb("Get extension handler config")
#Update goal state to get latest extensions config
self.client.update_goal_state()
+ goal_state = self.client.get_goal_state()
ext_conf = self.client.get_ext_conf()
- return ext_conf.ext_handlers
+ #In wire protocol, incarnation is equivalent to ETag
+ return ext_conf.ext_handlers, goal_state.incarnation
def get_ext_handler_pkgs(self, ext_handler):
+ logger.verb("Get extension handler package")
goal_state = self.client.get_goal_state()
man = self.client.get_ext_manifest(ext_handler, goal_state)
return man.pkg_list
@@ -134,12 +160,12 @@ def _build_role_properties(container_id, role_instance_id, thumbprint):
return xml
def _build_health_report(incarnation, container_id, role_instance_id,
- status, substatus, description):
+ status, substatus, description):
#Escape '&', '<' and '>'
- description = saxutils.escape(text(description))
+ description = saxutils.escape(ustr(description))
detail = u''
if substatus is not None:
- substatus = saxutils.escape(text(substatus))
+ substatus = saxutils.escape(ustr(substatus))
detail = (u"<Details>"
u"<SubStatus>{0}</SubStatus>"
u"<Description>{1}</Description>"
@@ -228,6 +254,7 @@ def ext_handler_status_to_v1(handler_status, ext_statuses, timestamp):
'handlerVersion' : handler_status.version,
'handlerName' : handler_status.name,
'status' : handler_status.status,
+ 'code': handler_status.code
}
if handler_status.message is not None:
v1_handler_status["formattedMessage"] = {
@@ -303,7 +330,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):
@@ -315,7 +342,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:
@@ -334,10 +361,10 @@ class StatusBlob(object):
data, {
"x-ms-date" : timestamp,
"x-ms-blob-type" : "BlockBlob",
- "Content-Length": text(len(data)),
+ "Content-Length": ustr(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:
@@ -359,10 +386,10 @@ class StatusBlob(object):
"x-ms-date" : timestamp,
"x-ms-blob-type" : "PageBlob",
"Content-Length": "0",
- "x-ms-blob-content-length" : text(page_blob_size),
+ "x-ms-blob-content-length" : ustr(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:
@@ -393,9 +420,9 @@ class StatusBlob(object):
"x-ms-range" : "bytes={0}-{1}".format(start, page_end - 1),
"x-ms-page-write" : "update",
"x-ms-version" : self.__class__.__storage_version__,
- "Content-Length": text(page_end - start)
+ "Content-Length": ustr(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:
@@ -411,13 +438,13 @@ def event_param_to_v1(param):
attr_type = 'mt:uint64'
elif param_type is str:
attr_type = 'mt:wstr'
- elif text(param_type).count("'unicode'") > 0:
+ elif ustr(param_type).count("'unicode'") > 0:
attr_type = 'mt:wstr'
elif param_type is bool:
attr_type = 'mt:bool'
elif param_type is float:
attr_type = 'mt:float64'
- return param_format.format(param.name, saxutils.quoteattr(text(param.value)),
+ return param_format.format(param.name, saxutils.quoteattr(ustr(param.value)),
attr_type)
def event_to_v1(event):
@@ -431,6 +458,7 @@ def event_to_v1(event):
class WireClient(object):
def __init__(self, endpoint):
+ logger.info("Wire server endpoint:{0}", endpoint)
self.endpoint = endpoint
self.goal_state = None
self.updated = None
@@ -448,15 +476,15 @@ class WireClient(object):
"""
now = time.time()
if now - self.last_request < 1:
- logger.info("Last request issued less than 1 second ago")
- logger.info("Sleep {0} second to avoid throttling.",
+ logger.verb("Last request issued less than 1 second ago")
+ logger.verb("Sleep {0} second to avoid throttling.",
SHORT_WAITING_INTERVAL)
time.sleep(SHORT_WAITING_INTERVAL)
self.last_request = now
self.req_count += 1
if self.req_count % 3 == 0:
- logger.info("Sleep {0} second to avoid throttling.",
+ logger.verb("Sleep {0} second to avoid throttling.",
SHORT_WAITING_INTERVAL)
time.sleep(SHORT_WAITING_INTERVAL)
self.req_count = 0
@@ -485,15 +513,15 @@ class WireClient(object):
if data is None:
return None
data = remove_bom(data)
- xml_text = text(data, encoding='utf-8')
+ xml_text = ustr(data, encoding='utf-8')
return xml_text
def fetch_config(self, uri, headers):
try:
resp = self.call_wireserver(restutil.http_get, uri,
headers=headers)
- except restutil.HttpError as e:
- raise ProtocolError(text(e))
+ except HttpError as e:
+ raise ProtocolError(ustr(e))
if(resp.status != httpclient.OK):
raise ProtocolError("{0} - {1}".format(resp.status, uri))
@@ -532,12 +560,13 @@ class WireClient(object):
def fetch_manifest(self, version_uris):
for version_uri in version_uris:
+ logger.verb("Fetch ext handler manifest: {0}", version_uri.uri)
try:
resp = self.call_storage_service(restutil.http_get,
version_uri.uri, None,
chk_proxy=True)
- except restutil.HttpError as e:
- raise ProtocolError(text(e))
+ except HttpError as e:
+ raise ProtocolError(ustr(e))
if resp.status == httpclient.OK:
return self.decode_config(resp.read())
@@ -553,7 +582,7 @@ class WireClient(object):
def update_hosting_env(self, goal_state):
if goal_state.hosting_env_uri is None:
raise ProtocolError("HostingEnvironmentConfig uri is empty")
- local_file = HOSTING_ENV_FILE_NAME
+ local_file = os.path.join(conf.get_lib_dir(), HOSTING_ENV_FILE_NAME)
xml_text = self.fetch_config(goal_state.hosting_env_uri,
self.get_header())
self.save_cache(local_file, xml_text)
@@ -562,7 +591,7 @@ class WireClient(object):
def update_shared_conf(self, goal_state):
if goal_state.shared_conf_uri is None:
raise ProtocolError("SharedConfig uri is empty")
- local_file = SHARED_CONF_FILE_NAME
+ local_file = os.path.join(conf.get_lib_dir(), SHARED_CONF_FILE_NAME)
xml_text = self.fetch_config(goal_state.shared_conf_uri,
self.get_header())
self.save_cache(local_file, xml_text)
@@ -571,7 +600,7 @@ class WireClient(object):
def update_certs(self, goal_state):
if goal_state.certs_uri is None:
return
- local_file = CERTS_FILE_NAME
+ local_file = os.path.join(conf.get_lib_dir(), CERTS_FILE_NAME)
xml_text = self.fetch_config(goal_state.certs_uri,
self.get_header_for_cert())
self.save_cache(local_file, xml_text)
@@ -583,25 +612,18 @@ class WireClient(object):
self.ext_conf = ExtensionsConfig(None)
return
incarnation = goal_state.incarnation
- local_file = EXT_CONF_FILE_NAME.format(incarnation)
+ local_file = os.path.join(conf.get_lib_dir(),
+ EXT_CONF_FILE_NAME.format(incarnation))
xml_text = self.fetch_config(goal_state.ext_uri, self.get_header())
self.save_cache(local_file, xml_text)
self.ext_conf = ExtensionsConfig(xml_text)
- for ext_handler in self.ext_conf.ext_handlers.extHandlers:
- self.update_ext_handler_manifest(ext_handler, goal_state)
-
- def update_ext_handler_manifest(self, ext_handler, goal_state):
- local_file = MANIFEST_FILE_NAME.format(ext_handler.name,
- goal_state.incarnation)
- xml_text = self.fetch_manifest(ext_handler.versionUris)
- self.save_cache(local_file, xml_text)
-
+
def update_goal_state(self, forced=False, max_retry=3):
uri = GOAL_STATE_URI.format(self.endpoint)
xml_text = 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:
@@ -619,7 +641,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)
@@ -636,27 +658,34 @@ class WireClient(object):
def get_goal_state(self):
if(self.goal_state is None):
- incarnation = self.fetch_cache(INCARNATION_FILE_NAME)
- goal_state_file = GOAL_STATE_FILE_NAME.format(incarnation)
+ incarnation_file = os.path.join(conf.get_lib_dir(),
+ INCARNATION_FILE_NAME)
+ incarnation = self.fetch_cache(incarnation_file)
+
+ file_name = GOAL_STATE_FILE_NAME.format(incarnation)
+ goal_state_file = os.path.join(conf.get_lib_dir(), file_name)
xml_text = self.fetch_cache(goal_state_file)
self.goal_state = GoalState(xml_text)
return self.goal_state
def get_hosting_env(self):
if(self.hosting_env is None):
- xml_text = self.fetch_cache(HOSTING_ENV_FILE_NAME)
+ local_file = os.path.join(conf.get_lib_dir(), HOSTING_ENV_FILE_NAME)
+ xml_text = self.fetch_cache(local_file)
self.hosting_env = HostingEnv(xml_text)
return self.hosting_env
def get_shared_conf(self):
if(self.shared_conf is None):
- xml_text = self.fetch_cache(SHARED_CONF_FILE_NAME)
+ local_file = os.path.join(conf.get_lib_dir(), SHARED_CONF_FILE_NAME)
+ xml_text = self.fetch_cache(local_file)
self.shared_conf = SharedConfig(xml_text)
return self.shared_conf
def get_certs(self):
if(self.certs is None):
- xml_text = self.fetch_cache(CERTS_FILE_NAME)
+ local_file = os.path.join(conf.get_lib_dir(), CERTS_FILE_NAME)
+ xml_text = self.fetch_cache(local_file)
self.certs = Certificates(self, xml_text)
if self.certs is None:
return None
@@ -669,14 +698,17 @@ class WireClient(object):
self.ext_conf = ExtensionsConfig(None)
else:
local_file = EXT_CONF_FILE_NAME.format(goal_state.incarnation)
+ local_file = os.path.join(conf.get_lib_dir(), local_file)
xml_text = self.fetch_cache(local_file)
self.ext_conf = ExtensionsConfig(xml_text)
return self.ext_conf
- def get_ext_manifest(self, extension, goal_state):
- local_file = MANIFEST_FILE_NAME.format(extension.name,
- goal_state.incarnation)
- xml_text = self.fetch_cache(local_file)
+ def get_ext_manifest(self, ext_handler, goal_state):
+ local_file = MANIFEST_FILE_NAME.format(ext_handler.name,
+ goal_state.incarnation)
+ local_file = os.path.join(conf.get_lib_dir(), local_file)
+ xml_text = self.fetch_manifest(ext_handler.versionUris)
+ self.save_cache(local_file, xml_text)
return ExtensionManifest(xml_text)
def check_wire_protocol_version(self):
@@ -693,7 +725,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()
@@ -711,7 +743,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:
@@ -732,7 +764,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:
@@ -750,7 +782,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:
@@ -791,11 +823,10 @@ class WireClient(object):
}
def get_header_for_cert(self):
- cert = ""
- content = self.fetch_cache(TRANSPORT_CERT_FILE_NAME)
- for line in content.split('\n'):
- if "CERTIFICATE" not in line:
- cert += line.rstrip()
+ trans_cert_file = os.path.join(conf.get_lib_dir(),
+ TRANSPORT_CERT_FILE_NAME)
+ content = self.fetch_cache(trans_cert_file)
+ cert = get_bytes_from_pem(content)
return {
"x-ms-agent-name":"WALinuxAgent",
"x-ms-version":PROTOCOL_VERSION,
@@ -922,8 +953,6 @@ 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.cert_list = CertList()
self.parse(xml_text)
@@ -935,22 +964,26 @@ class Certificates(object):
data = findtext(xml_doc, "Data")
if data is None:
return
-
+
+ cryptutil = CryptUtil(conf.get_openssl_cmd())
+ p7m_file = os.path.join(conf.get_lib_dir(), P7M_FILE_NAME)
p7m = ("MIME-Version:1.0\n"
"Content-Disposition: attachment; filename=\"{0}\"\n"
"Content-Type: application/x-pkcs7-mime; name=\"{1}\"\n"
"Content-Transfer-Encoding: base64\n"
"\n"
- "{2}").format(P7M_FILE_NAME, P7M_FILE_NAME, data)
+ "{2}").format(p7m_file, p7m_file, data)
- self.client.save_cache(os.path.join(self.lib_dir, P7M_FILE_NAME), p7m)
+ self.client.save_cache(p7m_file, p7m)
+
+ trans_prv_file = os.path.join(conf.get_lib_dir(),
+ TRANSPORT_PRV_FILE_NAME)
+ trans_cert_file = os.path.join(conf.get_lib_dir(),
+ TRANSPORT_CERT_FILE_NAME)
+ pem_file = os.path.join(conf.get_lib_dir(), PEM_FILE_NAME)
#decrypt certificates
- cmd = ("{0} cms -decrypt -in {1} -inkey {2} -recip {3}"
- "| {4} pkcs12 -nodes -password pass: -out {5}"
- "").format(self.openssl_cmd, P7M_FILE_NAME,
- TRANSPORT_PRV_FILE_NAME, TRANSPORT_CERT_FILE_NAME,
- self.openssl_cmd, PEM_FILE_NAME)
- shellutil.run(cmd)
+ cryptutil.decrypt_p7m(p7m_file, trans_prv_file, trans_cert_file,
+ pem_file)
#The parsing process use public key to match prv and crt.
buf = []
@@ -960,7 +993,7 @@ class Certificates(object):
thumbprints = {}
index = 0
v1_cert_list = []
- with open(PEM_FILE_NAME) as pem:
+ with open(pem_file) as pem:
for line in pem.readlines():
buf.append(line)
if re.match(r'[-]+BEGIN.*KEY[-]+', line):
@@ -969,15 +1002,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 = cryptutil.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 = cryptutil.get_pubkey_from_crt(tmp_file)
+ thumbprint = cryptutil.get_thumbprint_from_crt(tmp_file)
thumbprints[pub] = thumbprint
#Rename crt with thumbprint as the file name
crt = "{0}.crt".format(thumbprint)
@@ -985,7 +1018,7 @@ class Certificates(object):
"name":None,
"thumbprint":thumbprint
})
- os.rename(tmp_file, os.path.join(self.lib_dir, crt))
+ os.rename(tmp_file, os.path.join(conf.get_lib_dir(), crt))
buf = []
index += 1
begin_crt = False
@@ -996,7 +1029,7 @@ class Certificates(object):
if thumbprint:
tmp_file = prvs[pubkey]
prv = "{0}.prv".format(thumbprint)
- os.rename(tmp_file, os.path.join(self.lib_dir, prv))
+ os.rename(tmp_file, os.path.join(conf.get_lib_dir(), prv))
for v1_cert in v1_cert_list:
cert = Cert()
@@ -1004,7 +1037,8 @@ class Certificates(object):
self.cert_list.certificates.append(cert)
def write_to_tmp_file(self, index, suffix, buf):
- file_name = os.path.join(self.lib_dir, "{0}.{1}".format(index, suffix))
+ file_name = os.path.join(conf.get_lib_dir(),
+ "{0}.{1}".format(index, suffix))
self.client.save_cache(file_name, "".join(buf))
return file_name
@@ -1090,7 +1124,7 @@ class ExtensionsConfig(object):
ext.name = ext_handler.name
ext.sequenceNumber = seqNo
ext.publicSettings = handler_settings.get("publicSettings")
- ext.privateSettings = handler_settings.get("protectedSettings")
+ ext.protectedSettings = handler_settings.get("protectedSettings")
thumbprint = handler_settings.get("protectedSettingsCertThumbprint")
ext.certificateThumbprint = thumbprint
ext_handler.properties.extensions.append(ext)
diff --git a/azurelinuxagent/utils/cryptutil.py b/azurelinuxagent/utils/cryptutil.py
new file mode 100644
index 0000000..5ee5637
--- /dev/null
+++ b/azurelinuxagent/utils/cryptutil.py
@@ -0,0 +1,121 @@
+# 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 base64
+import struct
+from azurelinuxagent.future import ustr, bytebuffer
+from azurelinuxagent.exception import CryptError
+import azurelinuxagent.utils.shellutil as shellutil
+
+class CryptUtil(object):
+ def __init__(self, openssl_cmd):
+ self.openssl_cmd = openssl_cmd
+
+ def gen_transport_cert(self, prv_file, crt_file):
+ """
+ Create ssl certificate for https communication with endpoint server.
+ """
+ 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)
+ shellutil.run(cmd)
+
+ def get_pubkey_from_prv(self, file_name):
+ cmd = "{0} rsa -in {1} -pubout 2>/dev/null".format(self.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)
+ pub = shellutil.run_get_output(cmd)[1]
+ return pub
+
+ def get_thumbprint_from_crt(self, file_name):
+ cmd="{0} x509 -in {1} -fingerprint -noout".format(self.openssl_cmd,
+ file_name)
+ thumbprint = shellutil.run_get_output(cmd)[1]
+ thumbprint = thumbprint.rstrip().split('=')[1].replace(':', '').upper()
+ return thumbprint
+
+ def decrypt_p7m(self, p7m_file, trans_prv_file, trans_cert_file, pem_file):
+ cmd = ("{0} cms -decrypt -in {1} -inkey {2} -recip {3} "
+ "| {4} pkcs12 -nodes -password pass: -out {5}"
+ "").format(self.openssl_cmd, p7m_file, trans_prv_file,
+ trans_cert_file, self.openssl_cmd, pem_file)
+ shellutil.run(cmd)
+
+ def crt_to_ssh(self, input_file, output_file):
+ shellutil.run("ssh-keygen -i -m PKCS8 -f {0} >> {1}".format(input_file,
+ output_file))
+
+ def asn1_to_ssh(self, pubkey):
+ lines = pubkey.split("\n")
+ lines = [x for x in lines if not x.startswith("----")]
+ base64_encoded = "".join(lines)
+ try:
+ #TODO remove pyasn1 dependency
+ from pyasn1.codec.der import decoder as der_decoder
+ der_encoded = base64.b64decode(base64_encoded)
+ der_encoded = der_decoder.decode(der_encoded)[0][1]
+ key = der_decoder.decode(self.bits_to_bytes(der_encoded))[0]
+ n=key[0]
+ e=key[1]
+ keydata = bytearray()
+ keydata.extend(struct.pack('>I', len("ssh-rsa")))
+ keydata.extend(b"ssh-rsa")
+ keydata.extend(struct.pack('>I', len(self.num_to_bytes(e))))
+ keydata.extend(self.num_to_bytes(e))
+ keydata.extend(struct.pack('>I', len(self.num_to_bytes(n)) + 1))
+ keydata.extend(b"\0")
+ keydata.extend(self.num_to_bytes(n))
+ keydata_base64 = base64.b64encode(bytebuffer(keydata))
+ return ustr(b"ssh-rsa " + keydata_base64 + b"\n",
+ encoding='utf-8')
+ except ImportError as e:
+ raise CryptError("Failed to load pyasn1.codec.der")
+
+ def num_to_bytes(self, num):
+ """
+ Pack number into bytes. Retun as string.
+ """
+ result = bytearray()
+ while num:
+ result.append(num & 0xFF)
+ num >>= 8
+ result.reverse()
+ return result
+
+ def bits_to_bytes(self, bits):
+ """
+ Convert an array contains bits, [0,1] to a byte array
+ """
+ index = 7
+ byte_array = bytearray()
+ curr = 0
+ for bit in bits:
+ curr = curr | (bit << index)
+ index = index - 1
+ if index == -1:
+ byte_array.append(curr)
+ curr = 0
+ index = 7
+ return bytes(byte_array)
+
diff --git a/azurelinuxagent/utils/fileutil.py b/azurelinuxagent/utils/fileutil.py
index 08592bc..5369a7c 100644
--- a/azurelinuxagent/utils/fileutil.py
+++ b/azurelinuxagent/utils/fileutil.py
@@ -27,7 +27,7 @@ import shutil
import pwd
import tempfile
import azurelinuxagent.logger as logger
-from azurelinuxagent.future import text
+from azurelinuxagent.future import ustr
import azurelinuxagent.utils.textutil as textutil
def read_file(filepath, asbin=False, remove_bom=False, encoding='utf-8'):
@@ -46,7 +46,7 @@ def read_file(filepath, asbin=False, remove_bom=False, encoding='utf-8'):
if remove_bom:
#Remove bom on bytes data before it is converted into string.
data = textutil.remove_bom(data)
- data = text(data, encoding=encoding)
+ data = ustr(data, encoding=encoding)
return data
def write_file(filepath, contents, asbin=False, encoding='utf-8', append=False):
@@ -100,6 +100,7 @@ def replace_file(filepath, contents):
return 1
return 0
+
def base_name(path):
head, tail = os.path.split(path)
return tail
@@ -151,7 +152,7 @@ def rm_dirs(*args):
def update_conf_file(path, line_start, val, chk_err=False):
conf = []
if not os.path.isfile(path) and chk_err:
- raise Exception("Can't find config file:{0}".format(path))
+ raise IOError("Can't find config file:{0}".format(path))
conf = read_file(path).split('\n')
conf = [x for x in conf if not x.startswith(line_start)]
conf.append(val)
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..2e8b0be 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,
@@ -61,7 +59,7 @@ def _http_request(method, host, rel_uri, port=None, data=None, secure=False,
if secure:
port = 443 if port is None else port
if proxy_host is not None and proxy_port is not None:
- conn = httpclient.HTTPSConnection(proxy_host, proxy_port)
+ conn = httpclient.HTTPSConnection(proxy_host, proxy_port, timeout=10)
conn.set_tunnel(host, port)
#If proxy is used, full url is needed.
url = "https://{0}:{1}{2}".format(host, port, rel_uri)
@@ -71,7 +69,7 @@ def _http_request(method, host, rel_uri, port=None, data=None, secure=False,
else:
port = 80 if port is None else port
if proxy_host is not None and proxy_port is not None:
- conn = httpclient.HTTPConnection(proxy_host, proxy_port)
+ conn = httpclient.HTTPConnection(proxy_host, proxy_port, timeout=10)
#If proxy is used, full url is needed.
url = "http://{0}:{1}{2}".format(host, port, rel_uri)
else:
@@ -128,8 +126,12 @@ def http_request(method, url, data, headers=None, max_retry=3, chk_proxy=False):
if retry < max_retry - 1:
logger.info("Retry={0}, {1} {2}", retry, method, url)
time.sleep(RETRY_WAITING_INTERVAL)
-
- raise HttpError("HTTP Err: {0} {1}".format(method, url))
+
+ if url is not None and len(url) > 100:
+ url_log = url[0: 100] #In case the url is too long
+ else:
+ url_log = url
+ raise HttpError("HTTP Err: {0} {1}".format(method, url_log))
def http_get(url, headers=None, max_retry=3, chk_proxy=False):
return http_request("GET", url, data=None, headers=headers,
diff --git a/azurelinuxagent/utils/shellutil.py b/azurelinuxagent/utils/shellutil.py
index 372c78a..98871a1 100644
--- a/azurelinuxagent/utils/shellutil.py
+++ b/azurelinuxagent/utils/shellutil.py
@@ -20,7 +20,7 @@
import platform
import os
import subprocess
-from azurelinuxagent.future import text
+from azurelinuxagent.future import ustr
import azurelinuxagent.logger as logger
if not hasattr(subprocess,'check_output'):
@@ -75,9 +75,9 @@ def run_get_output(cmd, chk_err=True, log_cmd=True):
logger.verb(u"run cmd '{0}'", cmd)
try:
output=subprocess.check_output(cmd,stderr=subprocess.STDOUT,shell=True)
- output = text(output, encoding='utf-8', errors="backslashreplace")
+ output = ustr(output, encoding='utf-8', errors="backslashreplace")
except subprocess.CalledProcessError as e :
- output = text(e.output, encoding='utf-8', errors="backslashreplace")
+ output = ustr(e.output, encoding='utf-8', errors="backslashreplace")
if chk_err:
if log_cmd:
logger.error(u"run cmd '{0}' failed", e.cmd)
diff --git a/azurelinuxagent/utils/textutil.py b/azurelinuxagent/utils/textutil.py
index e0f1395..851f98a 100644
--- a/azurelinuxagent/utils/textutil.py
+++ b/azurelinuxagent/utils/textutil.py
@@ -224,5 +224,13 @@ def gen_password_hash(password, crypt_id, salt_len):
salt = "${0}${1}".format(crypt_id, salt)
return crypt.crypt(password, salt)
+def get_bytes_from_pem(pem_str):
+ base64_bytes = ""
+ for line in pem_str.split('\n'):
+ if "----" not in line:
+ base64_bytes += line
+ return base64_bytes
+
+
Version = LooseVersion
diff --git a/bin/waagent2.0 b/bin/waagent2.0
index 94c8ac8..673a04c 100755..100644
--- a/bin/waagent2.0
+++ b/bin/waagent2.0
@@ -1,6 +1,6 @@
#!/usr/bin/env python
#
-# Microsoft Azure Linux Agent
+# Azure Linux Agent
#
# Copyright 2015 Microsoft Corporation
#
@@ -23,6 +23,8 @@
# http://msdn.microsoft.com/en-us/library/cc227259%28PROT.13%29.aspx
#
+import crypt
+import random
import array
import base64
import httplib
@@ -50,6 +52,7 @@ import zipfile
import json
import datetime
import xml.sax.saxutils
+from distutils.version import LooseVersion
if not hasattr(subprocess,'check_output'):
def check_output(*popenargs, **kwargs):
@@ -79,8 +82,8 @@ if not hasattr(subprocess,'check_output'):
subprocess.CalledProcessError=CalledProcessError
GuestAgentName = "WALinuxAgent"
-GuestAgentLongName = "Microsoft Azure Linux Agent"
-GuestAgentVersion = "WALinuxAgent-2.0.15-pre"
+GuestAgentLongName = "Azure Linux Agent"
+GuestAgentVersion = "WALinuxAgent-2.0.16"
ProtocolVersion = "2012-11-30" #WARNING this value is used to confirm the correct fabric protocol.
Config = None
@@ -106,7 +109,7 @@ HandlerStatusToAggStatus = {"installed":"Installing", "enabled":"Ready", "uninta
WaagentConf = """\
#
-# Microsoft Azure Linux Agent Configuration
+# Azure Linux Agent Configuration
#
Role.StateConsumer=None # Specified program is invoked with the argument "Ready" when we report ready status
@@ -126,7 +129,7 @@ ResourceDisk.MountPoint=/mnt/resource #
ResourceDisk.EnableSwap=n # Create and use swapfile on resource disk.
ResourceDisk.SwapSizeMB=0 # Size of the swapfile.
-LBProbeResponder=y # Respond to load balancer probes if requested by Microsoft Azure.
+LBProbeResponder=y # Respond to load balancer probes if requested by Azure.
Logs.Verbose=n # Enable verbose logs
@@ -173,8 +176,8 @@ class AbstractDistro(object):
self.ssh_config_file='/etc/ssh/sshd_config'
self.hostname_file_path='/etc/hostname'
self.dhcp_client_name='dhclient'
- self.requiredDeps = [ 'route', 'shutdown', 'ssh-keygen', 'useradd',
- 'openssl', 'sfdisk', 'fdisk', 'mkfs', 'chpasswd',
+ self.requiredDeps = [ 'route', 'shutdown', 'ssh-keygen', 'useradd', 'usermod',
+ 'openssl', 'sfdisk', 'fdisk', 'mkfs',
'sed', 'grep', 'sudo', 'parted' ]
self.init_script_file='/etc/init.d/waagent'
self.agent_package_name='WALinuxAgent'
@@ -187,6 +190,7 @@ class AbstractDistro(object):
self.sudoers_dir_base = '/etc'
self.waagent_conf_file = WaagentConf
self.shadow_file_mode=0600
+ self.shadow_file_path="/etc/shadow"
self.dhcp_enabled = False
def isSelinuxSystem(self):
@@ -341,8 +345,35 @@ class AbstractDistro(object):
return 0
def changePass(self,user,password):
- return RunSendStdin("chpasswd",(user + ":" + password + "\n"))
+ Log("Change user password")
+ crypt_id = Config.get("Provisioning.PasswordCryptId")
+ if crypt_id is None:
+ crypt_id = "6"
+
+ salt_len = Config.get("Provisioning.PasswordCryptSaltLength")
+ try:
+ salt_len = int(salt_len)
+ if salt_len < 0 or salt_len > 10:
+ salt_len = 10
+ except (ValueError, TypeError):
+ salt_len = 10
+
+ return self.chpasswd(user, password, crypt_id=crypt_id,
+ salt_len=salt_len)
+ def chpasswd(self, username, password, crypt_id=6, salt_len=10):
+ passwd_hash = self.gen_password_hash(password, crypt_id, salt_len)
+ cmd = "usermod -p '{0}' {1}".format(passwd_hash, username)
+ ret, output = RunGetOutput(cmd, log_cmd=False)
+ if ret != 0:
+ return "Failed to set password for {0}: {1}".format(username, output)
+
+ def gen_password_hash(self, password, crypt_id, salt_len):
+ collection = string.ascii_letters + string.digits
+ salt = ''.join(random.choice(collection) for _ in range(salt_len))
+ salt = "${0}${1}".format(crypt_id, salt)
+ return crypt.crypt(password, salt)
+
def load_ata_piix(self):
return WaAgent.TryLoadAtapiix()
@@ -438,8 +469,15 @@ class AbstractDistro(object):
def GetInterfaceName(self):
return GetFirstActiveNetworkInterfaceNonLoopback()[0]
- def RestartInterface(self, iface):
- Run("ifdown " + iface + " && ifup " + iface)
+ def RestartInterface(self, iface, max_retry=3):
+ for retry in range(1, max_retry + 1):
+ ret = Run("ifdown " + iface + " && ifup " + iface)
+ if ret == 0:
+ return
+ Log("Failed to restart interface: {0}, ret={1}".format(iface, ret))
+ if retry < max_retry:
+ Log("Retry restart interface in 5 seconds")
+ time.sleep(5)
def CreateAccount(self,user, password, expiration, thumbprint):
return CreateAccount(user, password, expiration, thumbprint)
@@ -522,6 +560,7 @@ class AbstractDistro(object):
if not os.path.isfile(mountpoint + "/swapfile"):
Run("dd if=/dev/zero of=" + mountpoint + "/swapfile bs=1024 count=" + str(sizeKB))
Run("mkswap " + mountpoint + "/swapfile")
+ Run("chmod 600 " + mountpoint + "/swapfile")
if not Run("swapon " + mountpoint + "/swapfile"):
Log("Enabled " + str(sizeKB) + " KB of swap at " + mountpoint + "/swapfile")
else:
@@ -645,6 +684,13 @@ class AbstractDistro(object):
if ret != 0:
raise Exception("Failed to config ipv4 for {0}: {1}".format(ifName,
output))
+ def setDefaultGateway(self, gateway):
+ Run("/sbin/route add default gw" + gateway, chk_err=False)
+
+ def routeAdd(self, net, mask, gateway):
+ Run("/sbin/route add -net " + net + " netmask " + mask + " gw " + gateway,
+ chk_err=False)
+
############################################################
# GentooDistro
@@ -656,7 +702,7 @@ command=/usr/sbin/waagent
pidfile=/var/run/waagent.pid
command_args=-daemon
command_background=true
-name="Microsoft Azure Linux Agent"
+name="Azure Linux Agent"
depend()
{
@@ -725,7 +771,7 @@ class gentooDistro(AbstractDistro):
suse_init_file = """\
#! /bin/sh
#
-# Microsoft Azure Linux Agent sysV init script
+# Azure Linux Agent sysV init script
#
# Copyright 2013 Microsoft Corporation
# Copyright SUSE LLC
@@ -751,12 +797,12 @@ suse_init_file = """\
# System startup script for the waagent
#
### BEGIN INIT INFO
-# Provides: MicrosoftAzureLinuxAgent
+# Provides: AzureLinuxAgent
# Required-Start: $network sshd
# Required-Stop: $network sshd
# Default-Start: 3 5
# Default-Stop: 0 1 2 6
-# Description: Start the MicrosoftAzureLinuxAgent
+# Description: Start the AzureLinuxAgent
### END INIT INFO
PYTHON=/usr/bin/python
@@ -789,14 +835,14 @@ rc_reset
case "$1" in
start)
- echo -n "Starting MicrosoftAzureLinuxAgent"
+ echo -n "Starting AzureLinuxAgent"
## Start daemon with startproc(8). If this fails
## the echo return value is set appropriate.
startproc -f ${PYTHON} ${WAZD_BIN} -daemon
rc_status -v
;;
stop)
- echo -n "Shutting down MicrosoftAzureLinuxAgent"
+ echo -n "Shutting down AzureLinuxAgent"
## Stop daemon with killproc(8) and if this fails
## set echo the echo return value.
killproc -p ${WAZD_PIDFILE} ${PYTHON} ${WAZD_BIN}
@@ -820,7 +866,7 @@ case "$1" in
rc_status
;;
status)
- echo -n "Checking for service MicrosoftAzureLinuxAgent "
+ echo -n "Checking for service AzureLinuxAgent "
## Check status with checkproc(8), if process is running
## checkproc will return with exit status 0.
@@ -902,17 +948,17 @@ class SuSEDistro(AbstractDistro):
redhat_init_file= """\
#!/bin/bash
#
-# Init file for MicrosoftAzureLinuxAgent.
+# Init file for AzureLinuxAgent.
#
# chkconfig: 2345 60 80
-# description: MicrosoftAzureLinuxAgent
+# description: AzureLinuxAgent
#
# source function library
. /etc/rc.d/init.d/functions
RETVAL=0
-FriendlyName="MicrosoftAzureLinuxAgent"
+FriendlyName="AzureLinuxAgent"
WAZD_BIN=/usr/sbin/waagent
start()
@@ -1014,7 +1060,24 @@ class redhatDistro(AbstractDistro):
else:
return 0
-
+ def checkDependencies(self):
+ """
+ Generic dependency check.
+ Return 1 unless all dependencies are satisfied.
+ """
+ if DistInfo()[1] < '7.0' and self.checkPackageInstalled('NetworkManager'):
+ Error(GuestAgentLongName + " is not compatible with network-manager.")
+ return 1
+ try:
+ m= __import__('pyasn1')
+ except ImportError:
+ Error(GuestAgentLongName + " requires python-pyasn1 for your Linux distribution.")
+ return 1
+ for a in self.requiredDeps:
+ if Run("which " + a + " > /dev/null 2>&1",chk_err=False):
+ Error("Missing required dependency: " + a)
+ return 1
+ return 0
############################################################
# centosDistro
@@ -1028,6 +1091,19 @@ class centosDistro(redhatDistro):
def __init__(self):
super(centosDistro,self).__init__()
+############################################################
+# oracleDistro
+############################################################
+
+class oracleDistro(redhatDistro):
+ """
+ Oracle Distro concrete class
+ Put Oracle specific behavior here...
+ """
+ def __init__(self):
+ super(oracleDistro, self).__init__()
+
+
############################################################
# asianuxDistro
@@ -1150,7 +1226,7 @@ class CoreOSDistro(AbstractDistro):
else:
Log("CreateAccount: " + user + " already exists. Will update password.")
if password != None:
- RunSendStdin("chpasswd", user + ":" + password + "\n")
+ self.changePass(user, password)
try:
if password == None:
SetFileContents("/etc/sudoers.d/waagent", user + " ALL = (ALL) NOPASSWD: ALL\n")
@@ -1194,15 +1270,15 @@ class CoreOSDistro(AbstractDistro):
debian_init_file = """\
#!/bin/sh
### BEGIN INIT INFO
-# Provides: MicrosoftAzureLinuxAgent
+# Provides: AzureLinuxAgent
# Required-Start: $network $syslog
# Required-Stop: $network $syslog
# Should-Start: $network $syslog
# Should-Stop: $network $syslog
# Default-Start: 2 3 4 5
# Default-Stop: 0 1 6
-# Short-Description: MicrosoftAzureLinuxAgent
-# Description: MicrosoftAzureLinuxAgent
+# Short-Description: AzureLinuxAgent
+# Description: AzureLinuxAgent
### END INIT INFO
. /lib/lsb/init-functions
@@ -1213,7 +1289,7 @@ WAZD_PID=/var/run/waagent.pid
case "$1" in
start)
- log_begin_msg "Starting MicrosoftAzureLinuxAgent..."
+ log_begin_msg "Starting AzureLinuxAgent..."
pid=$( pidofproc $WAZD_BIN )
if [ -n "$pid" ] ; then
log_begin_msg "Already running."
@@ -1225,7 +1301,7 @@ case "$1" in
;;
stop)
- log_begin_msg "Stopping MicrosoftAzureLinuxAgent..."
+ log_begin_msg "Stopping AzureLinuxAgent..."
start-stop-daemon --stop --quiet --oknodo --pidfile $WAZD_PID
ret=$?
rm -f $WAZD_PID
@@ -1350,7 +1426,7 @@ class KaliDistro(debianDistro):
# UbuntuDistro
############################################################
ubuntu_upstart_file = """\
-#walinuxagent - start Microsoft Azure agent
+#walinuxagent - start Azure agent
description "walinuxagent"
author "Ben Howard <ben.howard@canonical.com>"
@@ -1474,7 +1550,7 @@ class LinuxMintDistro(UbuntuDistro):
############################################################
fedora_systemd_service = """\
[Unit]
-Description=Microsoft Azure Linux Agent
+Description=Azure Linux Agent
After=network.target
After=sshd.service
ConditionFileIsExecutable=/usr/sbin/waagent
@@ -1599,7 +1675,7 @@ class fedoraDistro(redhatDistro):
############################################################
FreeBSDWaagentConf = """\
#
-# Microsoft Azure Linux Agent Configuration
+# Azure Linux Agent Configuration
#
Role.StateConsumer=None # Specified program is invoked with the argument "Ready" when we report ready status
@@ -1619,7 +1695,7 @@ ResourceDisk.MountPoint=/mnt/resource #
ResourceDisk.EnableSwap=n # Create and use swapfile on resource disk.
ResourceDisk.SwapSizeMB=0 # Size of the swapfile.
-LBProbeResponder=y # Respond to load balancer probes if requested by Microsoft Azure.
+LBProbeResponder=y # Respond to load balancer probes if requested by Azure.
Logs.Verbose=n # Enable verbose logs
@@ -1661,7 +1737,7 @@ __name__='setupmain' #prevent waagent.__main__ from executing
waagent=imp.load_source('waagent','/tmp/waagent')
waagent.LoggerInit('/var/log/waagent.log','/dev/console')
from waagent import RunGetOutput,Run
-Config=waagent.ConfigurationProvider()
+Config=waagent.ConfigurationProvider(None)
format = Config.get("ResourceDisk.Format")
if format == None or format.lower().startswith("n"):
sys.exit(0)
@@ -1764,7 +1840,7 @@ class FreeBSDDistro(AbstractDistro):
return 0
def changePass(self,user,password):
- return RunSendStdin("pw usermod " + user + " -h 0 ",password)
+ return RunSendStdin("pw usermod " + user + " -h 0 ",password, log_cmd=False)
def load_ata_piix(self):
return 0
@@ -2059,7 +2135,13 @@ class FreeBSDDistro(AbstractDistro):
def getTotalMemory(self):
return int(RunGetOutput("sysctl hw.realmem | awk '{print $2}'")[1])/1024
-
+
+ def setDefaultGateway(self, gateway):
+ Run("/sbin/route add default " + gateway, chk_err=False)
+
+ def routeAdd(self, net, mask, gateway):
+ Run("/sbin/route add -net " + net + " " + mask + " " + gateway, chk_err=False)
+
############################################################
# END DISTRO CLASS DEFS
############################################################
@@ -2180,41 +2262,43 @@ def Run(cmd,chk_err=True):
retcode,out=RunGetOutput(cmd,chk_err)
return retcode
-def RunGetOutput(cmd,chk_err=True):
+def RunGetOutput(cmd, chk_err=True, log_cmd=True):
"""
Wrapper for subprocess.check_output.
Execute 'cmd'. Returns return code and STDOUT, trapping expected exceptions.
Reports exceptions to Error if chk_err parameter is True
"""
- LogIfVerbose(cmd)
+ if log_cmd:
+ LogIfVerbose(cmd)
try:
output=subprocess.check_output(cmd,stderr=subprocess.STDOUT,shell=True)
except subprocess.CalledProcessError,e :
- if chk_err :
+ if chk_err and log_cmd:
Error('CalledProcessError. Error Code is ' + str(e.returncode) )
Error('CalledProcessError. Command string was ' + e.cmd )
Error('CalledProcessError. Command result was ' + (e.output[:-1]).decode('latin-1'))
return e.returncode,e.output.decode('latin-1')
return 0,output.decode('latin-1')
-def RunSendStdin(cmd,input,chk_err=True):
+def RunSendStdin(cmd, input, chk_err=True, log_cmd=True):
"""
Wrapper for subprocess.Popen.
Execute 'cmd', sending 'input' to STDIN of 'cmd'.
Returns return code and STDOUT, trapping expected exceptions.
Reports exceptions to Error if chk_err parameter is True
"""
- LogIfVerbose(cmd+input)
+ if log_cmd:
+ LogIfVerbose(cmd+input)
try:
me=subprocess.Popen([cmd], shell=True, stdin=subprocess.PIPE,stderr=subprocess.STDOUT,stdout=subprocess.PIPE)
output=me.communicate(input)
except OSError , e :
- if chk_err :
+ if chk_err and log_cmd:
Error('CalledProcessError. Error Code is ' + str(me.returncode) )
Error('CalledProcessError. Command string was ' + cmd )
Error('CalledProcessError. Command result was ' + output[0].decode('latin-1'))
return 1,output[0].decode('latin-1')
- if me.returncode is not 0 and chk_err is True:
+ if me.returncode is not 0 and chk_err is True and log_cmd:
Error('CalledProcessError. Error Code is ' + str(me.returncode) )
Error('CalledProcessError. Command string was ' + cmd )
Error('CalledProcessError. Command result was ' + output[0].decode('latin-1'))
@@ -2296,7 +2380,7 @@ def CreateAccount(user, password, expiration, thumbprint):
else:
Log("CreateAccount: " + user + " already exists. Will update password.")
if password != None:
- RunSendStdin("chpasswd",(user + ":" + password + "\n"))
+ MyDistro.changePass(user, password)
try:
# for older distros create sudoers.d
if not os.path.isdir('/etc/sudoers.d/'):
@@ -2468,7 +2552,6 @@ class Logger(object):
message = filter(lambda x : x in string.printable, message)
C.write(message.encode('ascii','ignore') + "\n")
except IOError, e:
- print e
pass
def Log(self,message):
@@ -2635,6 +2718,53 @@ def DeviceForIdePort(n):
class HttpResourceGoneError(Exception):
pass
+def DoInstallRHUIRPM():
+ """
+ Install RHUI RPM according to VM region
+ """
+ rhuiRPMinstalled = os.path.exists(LibDir + "/rhuirpminstalled")
+ if rhuiRPMinstalled:
+ return
+ else:
+ SetFileContents(LibDir + "/rhuirpminstalled", "")
+
+ Log("Begin to install RHUI RPM")
+ cmd = "grep '<Location>' /var/lib/waagent/ExtensionsConfig* --no-filename | sed 's/<Location>//g' | sed 's/<\/Location>//g' | sed 's/ //g' | tr 'A-Z' 'a-z' | uniq"
+
+ retcode,out = RunGetOutput(cmd, True)
+ region = out.rstrip("\n")
+
+ #try a few times at most to get the region info
+ retry = 0
+ for i in range(0, 8):
+ if (region != ""):
+ break
+ Log("region info is empty, now wait 15 seconds...")
+ time.sleep(15)
+ retcode,out = RunGetOutput(cmd, True)
+ region = out.rstrip("\n")
+
+ if region == "":
+ Log("could not detect region info, now use the default region: eastus2")
+ region = "eastus2"
+
+ scriptFilePath = "/tmp/install-rhui-rpm.sh"
+
+ if not os.path.exists(scriptFilePath):
+ Error(scriptFilePath + " does not exist, now quit RHUI RPM installation.");
+ return
+ #chmod a+x script file
+ os.chmod(scriptFilePath, 0100)
+ Log("begin to run " + scriptFilePath)
+
+ #execute the downloaded script file
+ retcode,out = RunGetOutput(scriptFilePath, True)
+ if retcode != 0:
+ Error("execute script " + scriptFilePath + " failed, return code: " + str(retcode) + ", now exit RHUI RPM installation.");
+ return
+
+ Log("install RHUI RPM completed")
+
class Util(object):
"""
Http communication class.
@@ -2697,20 +2827,20 @@ class Util(object):
if secure:
port = 443 if port is None else port
if proxyHost is not None and proxyPort is not None:
- conn = httplib.HTTPSConnection(proxyHost, proxyPort)
+ conn = httplib.HTTPSConnection(proxyHost, proxyPort, timeout=10)
conn.set_tunnel(host, port)
#If proxy is used, full url is needed.
path = "https://{0}:{1}{2}".format(host, port, path)
else:
- conn = httplib.HTTPSConnection(host, port)
+ conn = httplib.HTTPSConnection(host, port, timeout=10)
else:
port = 80 if port is None else port
if proxyHost is not None and proxyPort is not None:
- conn = httplib.HTTPConnection(proxyHost, proxyPort)
+ conn = httplib.HTTPConnection(proxyHost, proxyPort, timeout=10)
#If proxy is used, full url is needed.
path = "http://{0}:{1}{2}".format(host, port, path)
else:
- conn = httplib.HTTPConnection(host, port)
+ conn = httplib.HTTPConnection(host, port, timeout=10)
if headers == None:
conn.request(method, path, data)
else:
@@ -2849,8 +2979,12 @@ class Util(object):
"Content-Type": "text/xml; charset=utf-8",
"x-ms-version": ProtocolVersion
}
- return self.HttpPost(url, data=data, headers=headers,
- maxRetry=maxRetry, chkProxy=chkProxy)
+ try:
+ return self.HttpPost(url, data=data, headers=headers,
+ maxRetry=maxRetry, chkProxy=chkProxy)
+ except HttpResourceGoneError as e:
+ Error("Failed to post: {0} {1}".format(url, e))
+ return None
__StorageVersion="2014-02-14"
@@ -2883,6 +3017,8 @@ def PutBlockBlob(url, data):
}, chkProxy=True)
if ret is None:
Error("Failed to upload block blob for status.")
+ return -1
+ return 0
def PutPageBlob(url, data):
restutil = Util()
@@ -2899,7 +3035,7 @@ def PutPageBlob(url, data):
}, chkProxy=True)
if ret is None:
Error("Failed to clean up page blob for status")
- return
+ return -1
if url.index('?') < 0:
url = "{0}?comp=page".format(url)
@@ -2927,8 +3063,9 @@ def PutPageBlob(url, data):
}, chkProxy=True)
if ret is None:
Error("Failed to upload page blob for status")
- return
+ return -1
start = end
+ return 0
def UploadStatusBlob(url, data):
LogIfVerbose("Upload status blob")
@@ -2936,12 +3073,12 @@ def UploadStatusBlob(url, data):
blobType = GetBlobType(url)
if blobType == "BlockBlob":
- PutBlockBlob(url, data)
+ return PutBlockBlob(url, data)
elif blobType == "PageBlob":
- PutPageBlob(url, data)
+ return PutPageBlob(url, data)
else:
Error("Unknown blob type: {0}".format(blobType))
- return None
+ return -1
class TCPHandler(SocketServer.BaseRequestHandler):
"""
@@ -3529,15 +3666,29 @@ class ExtensionsConfig(object):
# if the same plugin exists and the version is newer or
# does not exist then download and unzip the new plugin
plg_dir=None
- for root, dirs, files in os.walk(LibDir):
- for d in dirs:
- if name in d:
- plg_dir=os.path.join(root,d)
- if plg_dir != None:
- break
- if plg_dir != None :
- previous_version=plg_dir.rsplit('-')[-1]
- if plg_dir == None or version > previous_version :
+
+ latest_version_installed = LooseVersion("0.0")
+ for item in os.listdir(LibDir):
+ itemPath = os.path.join(LibDir, item)
+ if os.path.isdir(itemPath) and name in item:
+ try:
+ #Split plugin dir name with '-' to get intalled plugin name and version
+ sperator = item.rfind('-')
+ if sperator < 0:
+ continue
+ installed_plg_name = item[0:sperator]
+ installed_plg_version = LooseVersion(item[sperator + 1:])
+
+ #Check installed plugin name and compare installed version to get the latest version installed
+ if installed_plg_name == name and installed_plg_version > latest_version_installed:
+ plg_dir = itemPath
+ previous_version = str(installed_plg_version)
+ latest_version_installed = installed_plg_version
+ except Exception as e:
+ Warn("Invalid plugin dir name: {0} {1}".format(item, e))
+ continue
+
+ if plg_dir == None or LooseVersion(version) > LooseVersion(previous_version) :
location=p.getAttribute("location")
Log("Downloading plugin manifest: " + name + " from " + location)
SimpleLog(p.plugin_log,"Downloading plugin manifest: " + name + " from " + location)
@@ -3653,7 +3804,7 @@ class ExtensionsConfig(object):
cmd = ''
getcmd='installCommand'
- if plg_dir != None and previous_version != None and version > previous_version :
+ if plg_dir != None and previous_version != None and LooseVersion(version) > LooseVersion(previous_version):
previous_handler=name+'-'+previous_version
if self.GetHandlerState(previous_handler) != 'NotInstalled':
getcmd='updateCommand'
@@ -3666,6 +3817,23 @@ class ExtensionsConfig(object):
self.SetHandlerState(previous_handler, 'Disabled')
Log(name+' version ' + previous_version + ' is disabled')
SimpleLog(p.plugin_log,name+' version ' + previous_version + ' is disabled')
+
+ try:
+ Log("Copy status file from old plugin dir to new")
+ old_plg_dir = plg_dir
+ new_plg_dir = os.path.join(LibDir, "{0}-{1}".format(name, version))
+ old_ext_status_dir = os.path.join(old_plg_dir, "status")
+ new_ext_status_dir = os.path.join(new_plg_dir, "status")
+ if os.path.isdir(old_ext_status_dir):
+ for status_file in os.listdir(old_ext_status_dir):
+ status_file_path = os.path.join(old_ext_status_dir, status_file)
+ if os.path.isfile(status_file_path):
+ shutil.copy2(status_file_path, new_ext_status_dir)
+ mrseq_file = os.path.join(old_plg_dir, "mrseq")
+ if os.path.isfile(mrseq_file):
+ shutil.copy(mrseq_file, new_plg_dir)
+ except Exception as e:
+ Error("Failed to copy status file.")
isupgradeSuccess = True
if getcmd=='updateCommand':
@@ -3687,6 +3855,16 @@ class ExtensionsConfig(object):
self.SetHandlerState(previous_handler, 'NotInstalled')
Log('Uninstall complete'+ previous_handler )
SimpleLog(p.plugin_log,'Uninstall complete'+ name +'-' + previous_version)
+
+ try:
+ #rm old plugin dir
+ if os.path.isdir(plg_dir):
+ shutil.rmtree(plg_dir)
+ Log(name +'-'+ previous_version + ' extension files deleted.')
+ SimpleLog(p.plugin_log,name +'-'+ previous_version + ' extension files deleted.')
+ except Exception as e:
+ Error("Failed to remove old plugin directory")
+
AddExtensionEvent(name,WALAEventOperation.Upgrade,isupgradeSuccess,0,previous_version)
else : # run install
if self.launchCommand(p.plugin_log,name,version,getcmd) == None :
@@ -3902,7 +4080,7 @@ class ExtensionsConfig(object):
incarnation=self.Extensions[0].getAttribute("goalStateIncarnation")
except:
Error('Error parsing ExtensionsConfig. Unable to send status reports')
- return None
+ return -1
status=''
statuses=''
for p in self.Plugins:
@@ -3940,11 +4118,10 @@ class ExtensionsConfig(object):
uri=GetNodeTextData(self.Extensions[0].getElementsByTagName("StatusUploadBlob")[0]).replace('&amp;','&')
except:
Error('Error parsing ExtensionsConfig. Unable to send status reports')
- return None
+ return -1
- UploadStatusBlob(uri, status.encode("utf-8"))
LogIfVerbose('Status report '+status+' sent to ' + uri)
- return True
+ return UploadStatusBlob(uri, status.encode("utf-8"))
def GetCurrentSequenceNumber(self, plugin_base_dir):
"""
@@ -4430,7 +4607,7 @@ class OvfEnv(object):
if len(CDSection) > 0 :
self.CustomData=GetNodeTextData(CDSection[0])
if len(self.CustomData)>0:
- SetFileContents(LibDir + '/CustomData', MyDistro.translateCustomData(self.CustomData))
+ SetFileContents(LibDir + '/CustomData', bytearray(MyDistro.translateCustomData(self.CustomData)))
Log('Wrote ' + LibDir + '/CustomData')
else :
Error('<CustomData> contains no data!')
@@ -4999,7 +5176,16 @@ class Agent(Util):
net = self.IntegerToIpAddressV4String(net)
mask = self.IntegerToIpAddressV4String(mask)
gateway = self.IntegerToIpAddressV4String(gateway)
- Run("/sbin/route add -net " + net + " netmask " + mask + " gw " + gateway,chk_err=False)
+ Log("Route add: net={0}, mask={1}, gateway={2}".format(net, mask, gateway))
+ MyDistro.routeAdd(net, mask, gateway)
+
+ def SetDefaultGateway(self, gateway):
+ """
+ Set default gateway
+ """
+ gateway = self.IntegerToIpAddressV4String(gateway)
+ Log("Set default gateway: {0}".format(gateway))
+ MyDistro.setDefaultGateway(gateway)
def HandleDhcpResponse(self, sendData, receiveBuffer):
"""
@@ -5082,11 +5268,11 @@ class Agent(Util):
gateway = self.UnpackBigEndian(receiveBuffer, i + 2, 4)
IpAddress = self.IntegerToIpAddressV4String(gateway)
if option == 3:
- self.RouteAdd(0, 0, gateway)
+ self.SetDefaultGateway(gateway)
name = "DefaultGateway"
else:
endpoint = IpAddress
- name = "Microsoft Azure wire protocol endpoint"
+ name = "Azure wire protocol endpoint"
LogIfVerbose(name + ": " + IpAddress + " at " + hex(i))
else:
Error("HandleDhcpResponse: Data too small for option " + str(option))
@@ -5098,7 +5284,7 @@ class Agent(Util):
def DoDhcpWork(self):
"""
Discover the wire server via DHCP option 245.
- And workaround incompatibility with Microsoft Azure DHCP servers.
+ And workaround incompatibility with Azure DHCP servers.
"""
ShortSleep = False # Sleep 1 second before retrying DHCP queries.
ifname=None
@@ -5212,7 +5398,7 @@ class Agent(Util):
if not goalStateXml:
Error("UpdateGoalState failed.")
return
- Log("Retrieved GoalState from Microsoft Azure Fabric.")
+ Log("Retrieved GoalState from Azure Fabric.")
self.GoalState = GoalState(self).Parse(goalStateXml)
return self.GoalState
@@ -5503,6 +5689,8 @@ class Agent(Util):
"""
SetFileContents("/var/run/waagent.pid", str(os.getpid()) + "\n")
+ reportHandlerStatusCount = 0
+
# Determine if we are in VMM. Spawn VMM_STARTUP_SCRIPT_NAME if found.
self.SearchForVMMStartup()
ipv4=''
@@ -5524,15 +5712,16 @@ class Agent(Util):
except:
pass
- Log("Probing for Microsoft Azure environment.")
+ Log("Probing for Azure environment.")
self.Endpoint = self.DoDhcpWork()
- if self.Endpoint == None:
- Log("Microsoft Azure environment not detected.")
- while True:
- time.sleep(60)
+ while self.Endpoint == None:
+ Log("Azure environment not detected.")
+ Log("Retry environment detection in 60 seconds")
+ time.sleep(60)
+ self.Endpoint = self.DoDhcpWork()
- Log("Discovered Microsoft Azure endpoint: " + self.Endpoint)
+ Log("Discovered Azure endpoint: " + self.Endpoint)
if not self.CheckVersions():
Error("Agent.CheckVersions failed")
sys.exit(1)
@@ -5647,12 +5836,23 @@ class Agent(Util):
incarnation = self.ReportReady()
# Process our extensions.
if goalState.ExtensionsConfig == None and goalState.ExtensionsConfigXml != None :
+ reportHandlerStatusCount = 0 #Reset count when new goal state comes
goalState.ExtensionsConfig = ExtensionsConfig().Parse(goalState.ExtensionsConfigXml)
# report the status/heartbeat results of extension processing
if goalState.ExtensionsConfig != None :
- goalState.ExtensionsConfig.ReportHandlerStatus()
+ ret = goalState.ExtensionsConfig.ReportHandlerStatus()
+ if ret != 0:
+ Error("Failed to report handler status")
+ elif reportHandlerStatusCount % 1000 == 0:
+ #Agent report handler status every 25 seconds. Reduce the log entries by adding a count
+ Log("Successfully reported handler status")
+ reportHandlerStatusCount += 1
+ global LinuxDistro
+ if LinuxDistro == "redhat":
+ DoInstallRHUIRPM()
+
if not eventMonitor:
eventMonitor = WALAEventMonitor(self.HttpPostWithHeaders)
eventMonitor.StartEventsLoop()
@@ -5987,11 +6187,6 @@ def main():
global LinuxDistro
LinuxDistro=DistInfo()[0]
- #The platform.py lib has issue with detecting oracle linux distribution.
- #Merge the following patch provided by oracle as a temparory fix.
- if os.path.exists("/etc/oracle-release"):
- LinuxDistro="Oracle Linux"
-
global MyDistro
MyDistro=GetMyDistro()
if MyDistro == None :
@@ -6051,18 +6246,21 @@ def main():
sys.exit(Usage())
global modloaded
modloaded = False
- try:
- SwitchCwd()
- Log(GuestAgentLongName + " Version: " + GuestAgentVersion)
- if IsLinux():
- Log("Linux Distribution Detected : " + LinuxDistro)
- global WaAgent
- WaAgent = Agent()
- WaAgent.Run()
- except Exception, e:
- Error(traceback.format_exc())
- Error("Exception: " + str(e))
- sys.exit(1)
+
+ while True:
+ try:
+ SwitchCwd()
+ Log(GuestAgentLongName + " Version: " + GuestAgentVersion)
+ if IsLinux():
+ Log("Linux Distribution Detected : " + LinuxDistro)
+ global WaAgent
+ WaAgent = Agent()
+ WaAgent.Run()
+ except Exception, e:
+ Error(traceback.format_exc())
+ Error("Exception: " + str(e))
+ Log("Restart agent in 15 seconds")
+ time.sleep(15)
if __name__ == '__main__' :
main()
diff --git a/config/coreos/waagent.conf b/config/coreos/waagent.conf
new file mode 100644
index 0000000..5af0baf
--- /dev/null
+++ b/config/coreos/waagent.conf
@@ -0,0 +1,91 @@
+#
+# Microsoft Azure Linux Agent Configuration
+#
+
+# Specified program is invoked with the argument "Ready" when we report ready status
+# to the endpoint server.
+Role.StateConsumer=None
+
+# Specified program is invoked with XML file argument specifying role
+# configuration.
+Role.ConfigurationConsumer=None
+
+# Specified program is invoked with XML file argument specifying role topology.
+Role.TopologyConsumer=None
+
+# Enable instance creation
+Provisioning.Enabled=y
+
+# Password authentication for root account will be unavailable.
+Provisioning.DeleteRootPassword=y
+
+# Generate fresh host key pair.
+Provisioning.RegenerateSshHostKeyPair=y
+
+# Supported values are "rsa", "dsa" and "ecdsa".
+Provisioning.SshHostKeyPairType=rsa
+
+# Monitor host name changes and publish changes via DHCP requests.
+Provisioning.MonitorHostName=y
+
+# Decode CustomData from Base64.
+Provisioning.DecodeCustomData=y
+
+# Execute CustomData after provisioning.
+Provisioning.ExecuteCustomData=n
+
+# Algorithm used by crypt when generating password hash.
+#Provisioning.PasswordCryptId=6
+
+# Length of random salt used when generating password hash.
+#Provisioning.PasswordCryptSaltLength=10
+
+# Allow reset password of sys user
+Provisioning.AllowResetSysUser=n
+
+# Format if unformatted. If 'n', resource disk will not be mounted.
+ResourceDisk.Format=y
+
+# File system on the resource disk
+# Typically ext3 or ext4. FreeBSD images should use 'ufs2' here.
+ResourceDisk.Filesystem=ext4
+
+# Mount point for the resource disk
+ResourceDisk.MountPoint=/mnt/resource
+
+# Create and use swapfile on resource disk.
+ResourceDisk.EnableSwap=n
+
+# Size of the swapfile.
+ResourceDisk.SwapSizeMB=0
+
+# Enable verbose logging (y|n)
+Logs.Verbose=n
+
+# Root device timeout in seconds.
+OS.RootDeviceScsiTimeout=300
+
+# If "None", the system default version is used.
+OS.OpensslPath=None
+
+# If set, agent will use proxy server to access internet
+#HttpProxy.Host=None
+#HttpProxy.Port=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
diff --git a/config/suse/waagent.conf b/config/suse/waagent.conf
index b4f4798..7cd6d37 100644
--- a/config/suse/waagent.conf
+++ b/config/suse/waagent.conf
@@ -40,6 +40,9 @@ Provisioning.ExecuteCustomData=n
# Length of random salt used when generating password hash.
#Provisioning.PasswordCryptSaltLength=10
+# Allow reset password of sys user
+Provisioning.AllowResetSysUser=n
+
# Format if unformatted. If 'n', resource disk will not be mounted.
ResourceDisk.Format=y
diff --git a/config/ubuntu/waagent.conf b/config/ubuntu/waagent.conf
index db29d80..ee60045 100644
--- a/config/ubuntu/waagent.conf
+++ b/config/ubuntu/waagent.conf
@@ -40,6 +40,9 @@ Provisioning.ExecuteCustomData=n
# Length of random salt used when generating password hash.
#Provisioning.PasswordCryptSaltLength=10
+# Allow reset password of sys user
+Provisioning.AllowResetSysUser=n
+
# Format if unformatted. If 'n', resource disk will not be mounted.
ResourceDisk.Format=n
diff --git a/config/waagent.conf b/config/waagent.conf
index 639e723..e808117 100644
--- a/config/waagent.conf
+++ b/config/waagent.conf
@@ -40,6 +40,9 @@ Provisioning.ExecuteCustomData=n
# Length of random salt used when generating password hash.
#Provisioning.PasswordCryptSaltLength=10
+# Allow reset password of sys user
+Provisioning.AllowResetSysUser=n
+
# Format if unformatted. If 'n', resource disk will not be mounted.
ResourceDisk.Format=y
@@ -71,3 +74,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
diff --git a/debian/changelog b/debian/changelog
index 0434259..8c6cf59 100644
--- a/debian/changelog
+++ b/debian/changelog
@@ -1,3 +1,11 @@
+walinuxagent (2.1.3-0ubuntu1) xenial; urgency=medium
+
+ * New upstream release (LP: #1543359):
+ - Bug fixes for extension handling
+ - Feature enablement for AzureStack.
+
+ -- Ben Howard <ben.howard@ubuntu.com> Mon, 08 Feb 2016 16:33:07 -0700
+
walinuxagent (2.1.2-0ubuntu2) xenial; urgency=medium
* Added udev rule to give verbose logging on the serial console.
diff --git a/debian/docs b/debian/docs
index 56b0555..c401623 100644
--- a/debian/docs
+++ b/debian/docs
@@ -1,4 +1,6 @@
debian/99-cloud-init-disable-diskprovisioning.conf
NOTICE
LICENSE-2.0.txt
-README
+README.md
+MANIFEST
+Changelog
diff --git a/debian/patches/disable-udev-rules-removal.patch b/debian/patches/disable-udev-rules-removal.patch
deleted file mode 100644
index 3f3adfb..0000000
--- a/debian/patches/disable-udev-rules-removal.patch
+++ /dev/null
@@ -1,12 +0,0 @@
---- a/azurelinuxagent/distro/ubuntu/osutil.py
-+++ b/azurelinuxagent/distro/ubuntu/osutil.py
-@@ -44,6 +44,9 @@
- def start_agent_service(self):
- return shellutil.run("service walinuxagent start", chk_err=False)
-
-+ def remove_rules_files(self, *args):
-+ return
-+
- class Ubuntu1204OSUtil(Ubuntu14xOSUtil):
- def __init__(self):
- super(Ubuntu1204OSUtil, self).__init__()
diff --git a/debian/patches/disable_import_test.patch b/debian/patches/disable_import_test.patch
index f7e6b64..cf3d167 100644
--- a/debian/patches/disable_import_test.patch
+++ b/debian/patches/disable_import_test.patch
@@ -1,20 +1,3 @@
---- a/tests/test_import_waagent.py
-+++ b/tests/test_import_waagent.py
-@@ -28,13 +28,7 @@
-
- class TestImportWAAgent(unittest.TestCase):
- def test_import_waagent(self):
-- agent_path = os.path.join(tools.parent, 'bin/waagent')
-- if sys.version_info[0] == 2:
-- waagent = imp.load_source('waagent', agent_path)
-- self.assertNotEquals(None, waagent.LoggerInit)
-- else:
-- self.assertRaises(ImportError, imp.load_source, 'waagent',
-- agent_path)
-+ pass
-
- if __name__ == '__main__':
- unittest.main()
--- a/config/waagent.conf
+++ b/config/waagent.conf
@@ -14,13 +14,13 @@
@@ -34,8 +17,8 @@
# Supported values are "rsa", "dsa" and "ecdsa".
Provisioning.SshHostKeyPairType=rsa
-@@ -41,14 +41,14 @@
- #Provisioning.PasswordCryptSaltLength=10
+@@ -44,14 +44,14 @@
+ Provisioning.AllowResetSysUser=n
# Format if unformatted. If 'n', resource disk will not be mounted.
-ResourceDisk.Format=y
diff --git a/debian/patches/disable_rhel_tests.patch b/debian/patches/disable_rhel_tests.patch
deleted file mode 100644
index f815ffe..0000000
--- a/debian/patches/disable_rhel_tests.patch
+++ /dev/null
@@ -1,13 +0,0 @@
---- a/tests/test_redhat.py
-+++ b/tests/test_redhat.py
-@@ -41,9 +41,7 @@
-
- class TestRedhat(unittest.TestCase):
- def test_RsaPublicKeyToSshRsa(self):
-- OSUtil = RedhatOSUtil()
-- ssh_rsa_pubkey = OSUtil.asn1_to_ssh_rsa(test_pubkey)
-- self.assertEquals(expected_ssh_rsa_pubkey, ssh_rsa_pubkey)
-+ pass
-
- if __name__ == '__main__':
- unittest.main()
diff --git a/debian/patches/disable_udev_overrides.patch b/debian/patches/disable_udev_overrides.patch
new file mode 100644
index 0000000..e0bb607
--- /dev/null
+++ b/debian/patches/disable_udev_overrides.patch
@@ -0,0 +1,37 @@
+--- a/bin/waagent2.0
++++ b/bin/waagent2.0
+@@ -95,8 +95,7 @@
+ VMM_STARTUP_SCRIPT_NAME='install'
+ VMM_CONFIG_FILE_NAME='linuxosconfiguration.xml'
+ global RulesFiles
+-RulesFiles = [ "/lib/udev/rules.d/75-persistent-net-generator.rules",
+- "/etc/udev/rules.d/70-persistent-net.rules" ]
++RulesFiles = [ ]
+ VarLibDhcpDirectories = ["/var/lib/dhclient", "/var/lib/dhcpcd", "/var/lib/dhcp"]
+ EtcDhcpClientConfFiles = ["/etc/dhcp/dhclient.conf", "/etc/dhcp3/dhclient.conf"]
+ global LibDir
+--- a/azurelinuxagent/distro/ubuntu/osutil.py
++++ b/azurelinuxagent/distro/ubuntu/osutil.py
+@@ -44,6 +44,12 @@
+ def start_agent_service(self):
+ return shellutil.run("service walinuxagent start", chk_err=False)
+
++ def remove_rules_files(self, rules_files=""):
++ pass
++
++ def restore_rules_files(self, rules_files=""):
++ pass
++
+ class Ubuntu12OSUtil(Ubuntu14OSUtil):
+ def __init__(self):
+ super(Ubuntu12OSUtil, self).__init__()
+@@ -67,9 +73,3 @@
+ def __init__(self):
+ super(UbuntuSnappyOSUtil, self).__init__()
+ self.conf_file_path = '/apps/walinuxagent/current/waagent.conf'
+-
+- def remove_rules_files(self, rules_files=""):
+- pass
+-
+- def restore_rules_files(self, rules_files=""):
+- pass
diff --git a/debian/patches/series b/debian/patches/series
index d32f0ed..05993c8 100644
--- a/debian/patches/series
+++ b/debian/patches/series
@@ -1,4 +1,3 @@
cloud-init-default-cfg.patch
-disable-udev-rules-removal.patch
-disable_rhel_tests.patch
disable_import_test.patch
+disable_udev_overrides.patch
diff --git a/debian/rules b/debian/rules
index 06d0f8c..8ec7cb1 100755
--- a/debian/rules
+++ b/debian/rules
@@ -5,8 +5,8 @@ ORIG_SRC=https://github.com/WindowsAzure/WALinuxAgent
get-packaged-orig-source:
git clone --separate-git-dir=walinuxagent.checkout \
$(ORIG_SRC) orig_source
- git checkout -b tags/$(DEB_VERSION)
- git archive --format=tar.gz WALinuxAgent-$(DEB_VERSION) \
+ git checkout -b tags/v$(DEB_VERSION)
+ git archive --format=tar.gz v$(DEB_VERSION) \
-o walinuxagent_$(DEB_VERSION).orig.tar.gz
rm -rf walinuxagent.checkout
rm -rf orig_source
diff --git a/setup.py b/setup.py
index 0f6230e..c799787 100755
--- a/setup.py
+++ b/setup.py
@@ -22,7 +22,7 @@ from azurelinuxagent.metadata import AGENT_NAME, AGENT_VERSION, \
AGENT_DESCRIPTION, \
DISTRO_NAME, DISTRO_VERSION, DISTRO_FULL_NAME
-import azurelinuxagent.agent as agent
+from azurelinuxagent.agent import Agent
import setuptools
from setuptools import find_packages
from setuptools.command.install import install as _install
@@ -72,7 +72,8 @@ def get_data_files(name, version, fullname):
elif name == 'coreos':
set_bin_files(data_files, dest="/usr/share/oem/bin")
- set_conf_files(data_files, dest="/usr/share/oem")
+ set_conf_files(data_files, dest="/usr/share/oem",
+ src=["config/coreos/waagent.conf"])
set_logrotate_files(data_files)
set_files(data_files, dest="/usr/share/oem",
src="init/coreos/cloud-config.yml")
@@ -149,7 +150,7 @@ class install(_install):
def run(self):
_install.run(self)
if self.register_service:
- agent.register_service()
+ Agent(False).register_service()
setuptools.setup(name=AGENT_NAME,
version=AGENT_VERSION,
diff --git a/tests/dhcp b/tests/data/dhcp
index 8c9d127..8c9d127 100644
--- a/tests/dhcp
+++ b/tests/data/dhcp
Binary files differ
diff --git a/tests/data/ext/event.xml b/tests/data/ext/event.xml
new file mode 100755
index 0000000..436de44
--- /dev/null
+++ b/tests/data/ext/event.xml
@@ -0,0 +1 @@
+<Data><Provider id="69B669B9-4AF8-4C50-BDC4-6006FA76E975"/><Event id="1"/><Param Name="OperationSuccess" Value="True" T="mt:bool" /><Param Name="Processors" Value="0" T="mt:uint64" /><Param Name="OpcodeName" Value="" T="mt:wstr" /><Param Name="Version" Value="1.4.1.0" T="mt:wstr" /><Param Name="RoleName" Value="" T="mt:wstr" /><Param Name="IsInternal" Value="False" T="mt:bool" /><Param Name="RAM" Value="0" T="mt:uint64" /><Param Name="ExecutionMode" Value="IAAS" T="mt:wstr" /><Param Name="RoleInstanceName" Value="" T="mt:wstr" /><Param Name="Name" Value="CustomScript" T="mt:wstr" /><Param Name="Message" Value="(01302)Script is finished.&#10;---stdout---&#10;hello&#10;&#10;---errout---&#10;&#10;" T="mt:wstr" /><Param Name="KeywordName" Value="" T="mt:wstr" /><Param Name="TaskName" Value="" T="mt:wstr" /><Param Name="OSVersion" Value="" T="mt:wstr" /><Param Name="Operation" Value="RunScript" T="mt:wstr" /><Param Name="ContainerId" Value="" T="mt:wstr" /><Param Name="GAVersion" Value="" T="mt:wstr" /><Param Name="TenantName" Value="" T="mt:wstr" /><Param Name="Duration" Value="0" T="mt:uint64" /><Param Name="ExtensionType" Value="" T="mt:wstr" /></Data> \ No newline at end of file
diff --git a/tests/data/ext/sample_ext.zip b/tests/data/ext/sample_ext.zip
new file mode 100644
index 0000000..08cfaf7
--- /dev/null
+++ b/tests/data/ext/sample_ext.zip
Binary files differ
diff --git a/tests/data/ext/sample_ext/HandlerManifest.json b/tests/data/ext/sample_ext/HandlerManifest.json
new file mode 100644
index 0000000..9890d0c
--- /dev/null
+++ b/tests/data/ext/sample_ext/HandlerManifest.json
@@ -0,0 +1,14 @@
+[{
+ "name": "ExampleHandlerLinux",
+ "version": 1.0,
+ "handlerManifest": {
+ "installCommand": "sample.py -install",
+ "uninstallCommand": "sample.py -uninstall",
+ "updateCommand": "sample.py -update",
+ "enableCommand": "sample.py -enable",
+ "disableCommand": "sample.py -disable",
+ "rebootAfterInstall": false,
+ "reportHeartbeat": false
+ }
+}]
+
diff --git a/tests/data/ext/sample_ext/sample.py b/tests/data/ext/sample_ext/sample.py
new file mode 100755
index 0000000..7107ac2
--- /dev/null
+++ b/tests/data/ext/sample_ext/sample.py
@@ -0,0 +1,33 @@
+#!/usr/bin/env python
+
+import os
+
+def get_seq():
+ latest_seq = -1;
+ config_dir = os.path.join(os.getcwd(), "config")
+ if os.path.isdir(config_dir):
+ for item in os.listdir(config_dir):
+ item_path = os.path.join(config_dir, item)
+ if os.path.isfile(item_path):
+ seperator = item.rfind(".")
+ if seperator > 0 and item[seperator + 1:] == "settings":
+ seq = int(item[0: seperator])
+ if seq > latest_seq:
+ latest_seq = seq
+ return latest_seq
+
+
+succeed_status = """
+[{
+ "status": {
+ "status": "success"
+ }
+}]
+"""
+
+if __name__ == "__main__":
+ seq = get_seq()
+ if seq >= 0:
+ status_file = os.path.join(os.getcwd(), "status", "{0}.status".format(seq))
+ with open(status_file, "w+") as status:
+ status.write(succeed_status)
diff --git a/tests/data/metadata/certificates.json b/tests/data/metadata/certificates.json
new file mode 100644
index 0000000..4db7a06
--- /dev/null
+++ b/tests/data/metadata/certificates.json
@@ -0,0 +1,7 @@
+{
+ "certificates":[{
+ "name":"foo",
+ "thumbprint":"bar",
+ "certificateDataUri":"baz"
+ }]
+}
diff --git a/tests/data/metadata/ext_handler_pkgs.json b/tests/data/metadata/ext_handler_pkgs.json
new file mode 100644
index 0000000..869c949
--- /dev/null
+++ b/tests/data/metadata/ext_handler_pkgs.json
@@ -0,0 +1,10 @@
+{
+ "versions": [{
+ "version":"1.3.0.0",
+ "uris":[{
+ "uri":"http://localhost/foo1"
+ },{
+ "uri":"http://localhost/foo2"
+ }]
+ }]
+}
diff --git a/tests/data/metadata/ext_handlers.json b/tests/data/metadata/ext_handlers.json
new file mode 100644
index 0000000..68efc19
--- /dev/null
+++ b/tests/data/metadata/ext_handlers.json
@@ -0,0 +1,19 @@
+[{
+ "name":"foo",
+ "properties":{
+ "version":"1.3.0.0",
+ "upgradePolicy": "manual",
+ "state": "enabled",
+ "extensions":[{
+ "name":"baz",
+ "sequenceNumber":0,
+ "publicSettings":{
+ "commandToExecute": "echo 123",
+ "uris":[]
+ }
+ }]
+ },
+ "versionUris":[{
+ "uri":"http://ext_handler_pkgs/versionUri"
+ }]
+}]
diff --git a/tests/data/metadata/ext_handlers_no_ext.json b/tests/data/metadata/ext_handlers_no_ext.json
new file mode 100644
index 0000000..fe51488
--- /dev/null
+++ b/tests/data/metadata/ext_handlers_no_ext.json
@@ -0,0 +1 @@
+[]
diff --git a/tests/data/metadata/identity.json b/tests/data/metadata/identity.json
new file mode 100644
index 0000000..e6e2273
--- /dev/null
+++ b/tests/data/metadata/identity.json
@@ -0,0 +1,4 @@
+{
+ "vmName":"foo",
+ "subscriptionId":"bar"
+}
diff --git a/tests/data/ovf-env.xml b/tests/data/ovf-env.xml
new file mode 100644
index 0000000..fc94943
--- /dev/null
+++ b/tests/data/ovf-env.xml
@@ -0,0 +1,29 @@
+<?xml version="1.0" encoding="utf-8"?>
+<Environment xmlns="http://schemas.dmtf.org/ovf/environment/1" xmlns:oe="http://schemas.dmtf.org/ovf/environment/1" xmlns:wa="http://schemas.microsoft.com/windowsazure" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance">
+ <wa:ProvisioningSection>
+ <wa:Version>1.0</wa:Version>
+ <LinuxProvisioningConfigurationSet xmlns="http://schemas.microsoft.com/windowsazure" xmlns:i="http://www.w3.org/2001/XMLSchema-instance">
+ <ConfigurationSetType>LinuxProvisioningConfiguration</ConfigurationSetType>
+ <HostName>HostName</HostName>
+ <UserName>UserName</UserName>
+ <UserPassword>UserPassword</UserPassword>
+ <DisableSshPasswordAuthentication>false</DisableSshPasswordAuthentication>
+ <SSH>
+ <PublicKeys>
+ <PublicKey>
+ <Fingerprint>EB0C0AB4B2D5FC35F2F0658D19F44C8283E2DD62</Fingerprint>
+ <Path>$HOME/UserName/.ssh/authorized_keys</Path>
+ <Value>ssh-rsa AAAANOTAREALKEY== foo@bar.local</Value>
+ </PublicKey>
+ </PublicKeys>
+ <KeyPairs>
+ <KeyPair>
+ <Fingerprint>EB0C0AB4B2D5FC35F2F0658D19F44C8283E2DD62</Fingerprint>
+ <Path>$HOME/UserName/.ssh/id_rsa</Path>
+ </KeyPair>
+ </KeyPairs>
+ </SSH>
+ <CustomData>CustomData</CustomData>
+ </LinuxProvisioningConfigurationSet>
+ </wa:ProvisioningSection>
+ </Environment>
diff --git a/tests/data/wire/certs.xml b/tests/data/wire/certs.xml
new file mode 100644
index 0000000..6717c30
--- /dev/null
+++ b/tests/data/wire/certs.xml
@@ -0,0 +1,81 @@
+<?xml version="1.0" encoding="utf-8"?>
+<CertificateFile xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:noNamespaceSchemaLocation="certificates10.xsd">
+ <Version>2012-11-30</Version>
+ <Incarnation>12</Incarnation>
+ <Format>Pkcs7BlobWithPfxContents</Format>
+ <Data>MIINswYJKoZIhvcNAQcDoIINpDCCDaACAQIxggEwMIIBLAIBAoAUvyL+x6GkZXog
+QNfsXRZAdD9lc7IwDQYJKoZIhvcNAQEBBQAEggEArhMPepD/RqwdPcHEVqvrdZid
+72vXrOCuacRBhwlCGrNlg8oI+vbqmT6CSv6thDpet31ALUzsI4uQHq1EVfV1+pXy
+NlYD1CKhBCoJxs2fSPU4rc8fv0qs5JAjnbtW7lhnrqFrXYcyBYjpURKfa9qMYBmj
+NdijN+1T4E5qjxPr7zK5Dalp7Cgp9P2diH4Nax2nixotfek3MrEFBaiiegDd+7tE
+ux685GWYPqB5Fn4OsDkkYOdb0OE2qzLRrnlCIiBCt8VubWH3kMEmSCxBwSJupmQ8
+sxCWk+sBPQ9gJSt2sIqfx/61F8Lpu6WzP+ZOnMLTUn2wLU/d1FN85HXmnQALzTCC
+DGUGCSqGSIb3DQEHATAUBggqhkiG9w0DBwQIbEcBfddWPv+AggxAAOAt/kCXiffe
+GeJG0P2K9Q18XZS6Rz7Xcz+Kp2PVgqHKRpPjjmB2ufsRO0pM4z/qkHTOdpfacB4h
+gz912D9U04hC8mt0fqGNTvRNAFVFLsmo7KXc/a8vfZNrGWEnYn7y1WfP52pqA/Ei
+SNFf0NVtMyqg5Gx+hZ/NpWAE5vcmRRdoYyWeg13lhlW96QUxf/W7vY/D5KpAGACI
+ok79/XI4eJkbq3Dps0oO/difNcvdkE74EU/GPuL68yR0CdzzafbLxzV+B43TBRgP
+jH1hCdRqaspjAaZL5LGfp1QUM8HZIKHuTze/+4dWzS1XR3/ix9q/2QFI7YCuXpuE
+un3AFYXE4QX/6kcPklZwh9FqjSie3I5HtC1vczqYVjqT4oHrs8ktkZ7oAzeXaXTF
+k6+JQNNa/IyJw24I1MR77q7HlHSSfhXX5cFjVCd/+SiA4HJQjJgeIuXZ+dXmSPdL
+9xLbDbtppifFyNaXdlSzcsvepKy0WLF49RmbL7Bnd46ce/gdQ6Midwi2MTnUtapu
+tHmu/iJtaUpwXXC0B93PHfAk7Y3SgeY4tl/gKzn9/x5SPAcHiNRtOsNBU8ZThzos
+Wh41xMLZavmX8Yfm/XWtl4eU6xfhcRAbJQx7E1ymGEt7xGqyPV7hjqhoB9i3oR5N
+itxHgf1+jw/cr7hob+Trd1hFqZO6ePMyWpqUg97G2ThJvWx6cv+KRtTlVA6/r/UH
+gRGBArJKBlLpXO6dAHFztT3Y6DFThrus4RItcfA8rltfQcRm8d0nPb4lCa5kRbCx
+iudq3djWtTIe64sfk8jsc6ahWYSovM+NmhbpxEUbZVWLVEcHAYOeMbKgXSu5sxNO
+JZNeFdzZqDRRY9fGjYNS7DdNOmrMmWKH+KXuMCItpNZsZS/3W7QxAo3ugYLdUylU
+Zg8H/BjUGZCGn1rEBAuQX78m0SZ1xHlgHSwJIOmxOJUDHLPHtThfbELY9ec14yi5
+so1aQwhhfhPvF+xuXBrVeTAfhFNYkf2uxcEp7+tgFAc5W0QfT9SBn5vSvIxv+dT4
+7B2Pg1l/zjdsM74g58lmRJeDoz4psAq+Uk7n3ImBhIku9qX632Q1hanjC8D4xM4W
+sI/W0ADCuAbY7LmwMpAMdrGg//SJUnBftlom7C9VA3EVf8Eo+OZH9hze+gIgUq+E
+iEUL5M4vOHK2ttsYrSkAt8MZzjQiTlDr1yzcg8fDIrqEAi5arjTPz0n2s0NFptNW
+lRD+Xz6pCXrnRgR8YSWpxvq3EWSJbZkSEk/eOmah22sFnnBZpDqn9+UArAznXrRi
+nYK9w38aMGPKM39ymG8kcbY7jmDZlRgGs2ab0Fdj1jl3CRo5IUatkOJwCEMd/tkB
+eXLQ8hspJhpFnVNReX0oithVZir+j36epk9Yn8d1l+YlKmuynjunKl9fhmoq5Q6i
+DFzdYpqBV+x9nVhnmPfGyrOkXvGL0X6vmXAEif/4JoOW4IZpyXjgn+VoCJUoae5J
+Djl45Bcc2Phrn4HW4Gg/+pIwTFqqZZ2jFrznNdgeIxTGjBrVsyJUeO3BHI0mVLaq
+jtjhTshYCI7mXOis9W3ic0RwE8rgdDXOYKHhLVw9c4094P/43utSVXE7UzbEhhLE
+Ngb4H5UGrQmPTNbq40tMUMUCej3zIKuVOvamzeE0IwLhkjNrvKhCG1EUhX4uoJKu
+DQ++3KVIVeYSv3+78Jfw9F3usAXxX1ICU74/La5DUNjU7DVodLDvCAy5y1jxP3Ic
+If6m7aBYVjFSQAcD8PZPeIEl9W4ZnbwyBfSDd11P2a8JcZ7N99GiiH3yS1QgJnAO
+g9XAgjT4Gcn7k4lHPHLULgijfiDSvt94Ga4/hse0F0akeZslVN/bygyib7x7Lzmq
+JkepRianrvKHbatuxvcajt/d+dxCnr32Q1qCEc5fcgDsjvviRL2tKR0qhuYjn1zR
+Vk/fRtYOmlaGBVzUXcjLRAg3gC9+Gy8KvXIDrnHxD+9Ob+DUP9fgbKqMeOzKcCK8
+NSfSQ+tQjBYD5Ku4zAPUQJoRGgx43vXzcl2Z2i3E2otpoH82Kx8S9WlVEUlTtBjQ
+QIGM5aR0QUNt8z34t2KWRA8SpP54VzBmEPdwLnzna+PkrGKsKiHVn4K+HfjDp1uW
+xyO8VjrolAOYosTPXMpNp2u/FoFxaAPTa/TvmKc0kQ3ED9/sGLS2twDnEccvHP+9
+zzrnzzN3T2CWuXveDpuyuAty3EoAid1nuC86WakSaAZoa8H2QoRgsrkkBCq+K/yl
+4FO9wuP+ksZoVq3mEDQ9qv6H4JJEWurfkws3OqrA5gENcLmSUkZie4oqAxeOD4Hh
+Zx4ckG5egQYr0PnOd2r7ZbIizv3MKT4RBrfOzrE6cvm9bJEzNWXdDyIxZ/kuoLA6
+zX7gGLdGhg7dqzKqnGtopLAsyM1b/utRtWxOTGO9K9lRxyX82oCVT9Yw0DwwA+cH
+Gutg1w7JHrIAYEtY0ezHgxhqMGuuTyJMX9Vr0D+9DdMeBK7hVOeSnxkaQ0f9HvF6
+0XI/2OTIoBSCBpUXjpgsYt7m7n2rFJGJmtqgLAosCAkacHnHLwX0EnzBw3sdDU6Q
+jFXUWIDd5xUsNkFDCbspLMFs22hjNI6f/GREwd23Q4ujF8pUIcxcfbs2myjbK45s
+tsn/jrkxmKRgwCIeN/H7CM+4GXSkEGLWbiGCxWzWt9wW1F4M7NW9nho3D1Pi2LBL
+1ByTmjfo/9u9haWrp53enDLJJbcaslfe+zvo3J70Nnzu3m3oJ3dmUxgJIstG10g3
+lhpUm1ynvx04IFkYJ3kr/QHG/xGS+yh/pMZlwcUSpjEgYFmjFHU4A1Ng4LGI4lnw
+5wisay4J884xmDgGfK0sdVQyW5rExIg63yYXp2GskRdDdwvWlFUzPzGgCNXQU96A
+ljZfjs2u4IiVCC3uVsNbGqCeSdAl9HC5xKuPNbw5yTxPkeRL1ouSdkBy7rvdFaFf
+dMPw6sBRNW8ZFInlgOncR3+xT/rZxru87LCq+3hRN3kw3hvFldrW2QzZSksO759b
+pJEP+4fxuG96Wq25fRmzHzE0bdJ+2qF3fp/hy4oRi+eVPa0vHdtkymE4OUFWftb6
++P++JVOzZ4ZxYA8zyUoJb0YCaxL+Jp/QqiUiH8WZVmYZmswqR48sUUKr7TIvpNbY
+6jEH6F7KiZCoWfKH12tUC69iRYx3UT/4Bmsgi3S4yUxfieYRMIwihtpP4i0O+OjB
+/DPbb13qj8ZSfXJ+jmF2SRFfFG+2T7NJqm09JvT9UcslVd+vpUySNe9UAlpcvNGZ
+2+j180ZU7YAgpwdVwdvqiJxkeVtAsIeqAvIXMFm1PDe7FJB0BiSVZdihB6cjnKBI
+dv7Lc1tI2sQe7QSfk+gtionLrEnto+aXF5uVM5LMKi3gLElz7oXEIhn54OeEciB1
+cEmyX3Kb4HMRDMHyJxqJXwxm88RgC6RekoPvstu+AfX/NgSpRj5beaj9XkweJT3H
+rKWhkjq4Ghsn1LoodxluMMHd61m47JyoqIP9PBKoW+Na0VUKIVHw9e9YeW0nY1Zi
+5qFA/pHPAt9AbEilRay6NEm8P7TTlNo216amc8byPXanoNrqBYZQHhZ93A4yl6jy
+RdpYskMivT+Sh1nhZAioKqqTZ3HiFR8hFGspAt5gJc4WLYevmxSicGa6AMyhrkvG
+rvOSdjY6JY/NkxtcgeycBX5MLF7uDbhUeqittvmlcrVN6+V+2HIbCCrvtow9pcX9
+EkaaNttj5M0RzjQxogCG+S5TkhCy04YvKIkaGJFi8xO3icdlxgOrKD8lhtbf4UpR
+cDuytl70JD95mSUWL53UYjeRf9OsLRJMHQOpS02japkMwCb/ngMCQuUXA8hGkBZL
+Xw7RwwPuM1Lx8edMXn5C0E8UK5e0QmI/dVIl2aglXk2oBMBJbnyrbfUPm462SG6u
+ke4gQKFmVy2rKICqSkh2DMr0NzeYEUjZ6KbmQcV7sKiFxQ0/ROk8eqkYYxGWUWJv
+ylPF1OTLH0AIbGlFPLQO4lMPh05yznZTac4tmowADSHY9RCxad1BjBeine2pj48D
+u36OnnuQIsedxt5YC+h1bs+mIvwMVsnMLidse38M/RayCDitEBvL0KeG3vWYzaAL
+h0FCZGOW0ilVk8tTF5+XWtsQEp1PpclvkcBMkU3DtBUnlmPSKNfJT0iRr2T0sVW1
+h+249Wj0Bw==
+</Data>
+</CertificateFile>
diff --git a/tests/data/wire/ext_conf.xml b/tests/data/wire/ext_conf.xml
new file mode 100644
index 0000000..725271d
--- /dev/null
+++ b/tests/data/wire/ext_conf.xml
@@ -0,0 +1,46 @@
+<Extensions version="1.0.0.0" goalStateIncarnation="9"><GuestAgentExtension xmlns:i="http://www.w3.org/2001/XMLSchema-instance">
+ <GAFamilies>
+ <GAFamily>
+ <Name>Win8</Name>
+ <Uris>
+ <Uri>http://rdfepirv2hknprdstr03.blob.core.windows.net/bfd5c281a7dc4e4b84381eb0b47e3aaf/Microsoft.WindowsAzure.GuestAgent_Win8_asiaeast_manifest.xml</Uri>
+ <Uri>http://rdfepirv2hknprdstr04.blob.core.windows.net/bfd5c281a7dc4e4b84381eb0b47e3aaf/Microsoft.WindowsAzure.GuestAgent_Win8_asiaeast_manifest.xml</Uri>
+ <Uri>http://rdfepirv2hknprdstr05.blob.core.windows.net/bfd5c281a7dc4e4b84381eb0b47e3aaf/Microsoft.WindowsAzure.GuestAgent_Win8_asiaeast_manifest.xml</Uri>
+ <Uri>http://rdfepirv2hknprdstr06.blob.core.windows.net/bfd5c281a7dc4e4b84381eb0b47e3aaf/Microsoft.WindowsAzure.GuestAgent_Win8_asiaeast_manifest.xml</Uri>
+ <Uri>http://rdfepirv2hknprdstr07.blob.core.windows.net/bfd5c281a7dc4e4b84381eb0b47e3aaf/Microsoft.WindowsAzure.GuestAgent_Win8_asiaeast_manifest.xml</Uri>
+ <Uri>http://rdfepirv2hknprdstr08.blob.core.windows.net/bfd5c281a7dc4e4b84381eb0b47e3aaf/Microsoft.WindowsAzure.GuestAgent_Win8_asiaeast_manifest.xml</Uri>
+ <Uri>http://rdfepirv2hknprdstr09.blob.core.windows.net/bfd5c281a7dc4e4b84381eb0b47e3aaf/Microsoft.WindowsAzure.GuestAgent_Win8_asiaeast_manifest.xml</Uri>
+ <Uri>http://rdfepirv2hknprdstr10.blob.core.windows.net/bfd5c281a7dc4e4b84381eb0b47e3aaf/Microsoft.WindowsAzure.GuestAgent_Win8_asiaeast_manifest.xml</Uri>
+ <Uri>http://rdfepirv2hknprdstr11.blob.core.windows.net/bfd5c281a7dc4e4b84381eb0b47e3aaf/Microsoft.WindowsAzure.GuestAgent_Win8_asiaeast_manifest.xml</Uri>
+ <Uri>http://rdfepirv2hknprdstr12.blob.core.windows.net/bfd5c281a7dc4e4b84381eb0b47e3aaf/Microsoft.WindowsAzure.GuestAgent_Win8_asiaeast_manifest.xml</Uri>
+ <Uri>http://zrdfepirv2hk2prdstr01.blob.core.windows.net/bfd5c281a7dc4e4b84381eb0b47e3aaf/Microsoft.WindowsAzure.GuestAgent_Win8_asiaeast_manifest.xml</Uri>
+ </Uris>
+ </GAFamily>
+ <GAFamily>
+ <Name>Win7</Name>
+ <Uris>
+ <Uri>http://rdfepirv2hknprdstr03.blob.core.windows.net/bfd5c281a7dc4e4b84381eb0b47e3aaf/Microsoft.WindowsAzure.GuestAgent_Win7_asiaeast_manifest.xml</Uri>
+ <Uri>http://rdfepirv2hknprdstr04.blob.core.windows.net/bfd5c281a7dc4e4b84381eb0b47e3aaf/Microsoft.WindowsAzure.GuestAgent_Win7_asiaeast_manifest.xml</Uri>
+ <Uri>http://rdfepirv2hknprdstr05.blob.core.windows.net/bfd5c281a7dc4e4b84381eb0b47e3aaf/Microsoft.WindowsAzure.GuestAgent_Win7_asiaeast_manifest.xml</Uri>
+ <Uri>http://rdfepirv2hknprdstr06.blob.core.windows.net/bfd5c281a7dc4e4b84381eb0b47e3aaf/Microsoft.WindowsAzure.GuestAgent_Win7_asiaeast_manifest.xml</Uri>
+ <Uri>http://rdfepirv2hknprdstr07.blob.core.windows.net/bfd5c281a7dc4e4b84381eb0b47e3aaf/Microsoft.WindowsAzure.GuestAgent_Win7_asiaeast_manifest.xml</Uri>
+ <Uri>http://rdfepirv2hknprdstr08.blob.core.windows.net/bfd5c281a7dc4e4b84381eb0b47e3aaf/Microsoft.WindowsAzure.GuestAgent_Win7_asiaeast_manifest.xml</Uri>
+ <Uri>http://rdfepirv2hknprdstr09.blob.core.windows.net/bfd5c281a7dc4e4b84381eb0b47e3aaf/Microsoft.WindowsAzure.GuestAgent_Win7_asiaeast_manifest.xml</Uri>
+ <Uri>http://rdfepirv2hknprdstr10.blob.core.windows.net/bfd5c281a7dc4e4b84381eb0b47e3aaf/Microsoft.WindowsAzure.GuestAgent_Win7_asiaeast_manifest.xml</Uri>
+ <Uri>http://rdfepirv2hknprdstr11.blob.core.windows.net/bfd5c281a7dc4e4b84381eb0b47e3aaf/Microsoft.WindowsAzure.GuestAgent_Win7_asiaeast_manifest.xml</Uri>
+ <Uri>http://rdfepirv2hknprdstr12.blob.core.windows.net/bfd5c281a7dc4e4b84381eb0b47e3aaf/Microsoft.WindowsAzure.GuestAgent_Win7_asiaeast_manifest.xml</Uri>
+ <Uri>http://zrdfepirv2hk2prdstr01.blob.core.windows.net/bfd5c281a7dc4e4b84381eb0b47e3aaf/Microsoft.WindowsAzure.GuestAgent_Win7_asiaeast_manifest.xml</Uri>
+ </Uris>
+ </GAFamily>
+ </GAFamilies>
+</GuestAgentExtension>
+<Plugins>
+ <Plugin name="OSTCExtensions.ExampleHandlerLinux" version="1.0" location="http://rdfepirv2hknprdstr03.blob.core.windows.net/b01058962be54ceca550a390fa5ff064/Microsoft.OSTCExtensions_ExampleHandlerLinux_asiaeast_manifest.xml" config="" state="enabled" autoUpgrade="false" failoverlocation="http://rdfepirv2hknprdstr04.blob.core.windows.net/b01058962be54ceca550a390fa5ff064/Microsoft.OSTCExtensions_ExampleHandlerLinux_asiaeast_manifest.xml" runAsStartupTask="false" isJson="true" />
+</Plugins>
+<PluginSettings>
+ <Plugin name="OSTCExtensions.ExampleHandlerLinux" version="1.0">
+ <RuntimeSettings seqNo="0">{"runtimeSettings":[{"handlerSettings":{"protectedSettingsCertThumbprint":"4037FBF5F1F3014F99B5D6C7799E9B20E6871CB3","protectedSettings":"MIICWgYJK","publicSettings":{"foo":"bar"}}}]}</RuntimeSettings>
+ </Plugin>
+</PluginSettings>
+<StatusUploadBlob>https://yuezhatest.blob.core.windows.net/vhds/test-cs12.test-cs12.test-cs12.status?sr=b&amp;sp=rw&amp;se=9999-01-01&amp;sk=key1&amp;sv=2014-02-14&amp;sig=hfRh7gzUE7sUtYwke78IOlZOrTRCYvkec4hGZ9zZzXo%3D</StatusUploadBlob></Extensions>
+
diff --git a/tests/data/wire/ext_conf_no_public.xml b/tests/data/wire/ext_conf_no_public.xml
new file mode 100644
index 0000000..abbde80
--- /dev/null
+++ b/tests/data/wire/ext_conf_no_public.xml
@@ -0,0 +1,46 @@
+<Extensions version="1.0.0.0" goalStateIncarnation="9"><GuestAgentExtension xmlns:i="http://www.w3.org/2001/XMLSchema-instance">
+ <GAFamilies>
+ <GAFamily>
+ <Name>Win8</Name>
+ <Uris>
+ <Uri>http://rdfepirv2hknprdstr03.blob.core.windows.net/bfd5c281a7dc4e4b84381eb0b47e3aaf/Microsoft.WindowsAzure.GuestAgent_Win8_asiaeast_manifest.xml</Uri>
+ <Uri>http://rdfepirv2hknprdstr04.blob.core.windows.net/bfd5c281a7dc4e4b84381eb0b47e3aaf/Microsoft.WindowsAzure.GuestAgent_Win8_asiaeast_manifest.xml</Uri>
+ <Uri>http://rdfepirv2hknprdstr05.blob.core.windows.net/bfd5c281a7dc4e4b84381eb0b47e3aaf/Microsoft.WindowsAzure.GuestAgent_Win8_asiaeast_manifest.xml</Uri>
+ <Uri>http://rdfepirv2hknprdstr06.blob.core.windows.net/bfd5c281a7dc4e4b84381eb0b47e3aaf/Microsoft.WindowsAzure.GuestAgent_Win8_asiaeast_manifest.xml</Uri>
+ <Uri>http://rdfepirv2hknprdstr07.blob.core.windows.net/bfd5c281a7dc4e4b84381eb0b47e3aaf/Microsoft.WindowsAzure.GuestAgent_Win8_asiaeast_manifest.xml</Uri>
+ <Uri>http://rdfepirv2hknprdstr08.blob.core.windows.net/bfd5c281a7dc4e4b84381eb0b47e3aaf/Microsoft.WindowsAzure.GuestAgent_Win8_asiaeast_manifest.xml</Uri>
+ <Uri>http://rdfepirv2hknprdstr09.blob.core.windows.net/bfd5c281a7dc4e4b84381eb0b47e3aaf/Microsoft.WindowsAzure.GuestAgent_Win8_asiaeast_manifest.xml</Uri>
+ <Uri>http://rdfepirv2hknprdstr10.blob.core.windows.net/bfd5c281a7dc4e4b84381eb0b47e3aaf/Microsoft.WindowsAzure.GuestAgent_Win8_asiaeast_manifest.xml</Uri>
+ <Uri>http://rdfepirv2hknprdstr11.blob.core.windows.net/bfd5c281a7dc4e4b84381eb0b47e3aaf/Microsoft.WindowsAzure.GuestAgent_Win8_asiaeast_manifest.xml</Uri>
+ <Uri>http://rdfepirv2hknprdstr12.blob.core.windows.net/bfd5c281a7dc4e4b84381eb0b47e3aaf/Microsoft.WindowsAzure.GuestAgent_Win8_asiaeast_manifest.xml</Uri>
+ <Uri>http://zrdfepirv2hk2prdstr01.blob.core.windows.net/bfd5c281a7dc4e4b84381eb0b47e3aaf/Microsoft.WindowsAzure.GuestAgent_Win8_asiaeast_manifest.xml</Uri>
+ </Uris>
+ </GAFamily>
+ <GAFamily>
+ <Name>Win7</Name>
+ <Uris>
+ <Uri>http://rdfepirv2hknprdstr03.blob.core.windows.net/bfd5c281a7dc4e4b84381eb0b47e3aaf/Microsoft.WindowsAzure.GuestAgent_Win7_asiaeast_manifest.xml</Uri>
+ <Uri>http://rdfepirv2hknprdstr04.blob.core.windows.net/bfd5c281a7dc4e4b84381eb0b47e3aaf/Microsoft.WindowsAzure.GuestAgent_Win7_asiaeast_manifest.xml</Uri>
+ <Uri>http://rdfepirv2hknprdstr05.blob.core.windows.net/bfd5c281a7dc4e4b84381eb0b47e3aaf/Microsoft.WindowsAzure.GuestAgent_Win7_asiaeast_manifest.xml</Uri>
+ <Uri>http://rdfepirv2hknprdstr06.blob.core.windows.net/bfd5c281a7dc4e4b84381eb0b47e3aaf/Microsoft.WindowsAzure.GuestAgent_Win7_asiaeast_manifest.xml</Uri>
+ <Uri>http://rdfepirv2hknprdstr07.blob.core.windows.net/bfd5c281a7dc4e4b84381eb0b47e3aaf/Microsoft.WindowsAzure.GuestAgent_Win7_asiaeast_manifest.xml</Uri>
+ <Uri>http://rdfepirv2hknprdstr08.blob.core.windows.net/bfd5c281a7dc4e4b84381eb0b47e3aaf/Microsoft.WindowsAzure.GuestAgent_Win7_asiaeast_manifest.xml</Uri>
+ <Uri>http://rdfepirv2hknprdstr09.blob.core.windows.net/bfd5c281a7dc4e4b84381eb0b47e3aaf/Microsoft.WindowsAzure.GuestAgent_Win7_asiaeast_manifest.xml</Uri>
+ <Uri>http://rdfepirv2hknprdstr10.blob.core.windows.net/bfd5c281a7dc4e4b84381eb0b47e3aaf/Microsoft.WindowsAzure.GuestAgent_Win7_asiaeast_manifest.xml</Uri>
+ <Uri>http://rdfepirv2hknprdstr11.blob.core.windows.net/bfd5c281a7dc4e4b84381eb0b47e3aaf/Microsoft.WindowsAzure.GuestAgent_Win7_asiaeast_manifest.xml</Uri>
+ <Uri>http://rdfepirv2hknprdstr12.blob.core.windows.net/bfd5c281a7dc4e4b84381eb0b47e3aaf/Microsoft.WindowsAzure.GuestAgent_Win7_asiaeast_manifest.xml</Uri>
+ <Uri>http://zrdfepirv2hk2prdstr01.blob.core.windows.net/bfd5c281a7dc4e4b84381eb0b47e3aaf/Microsoft.WindowsAzure.GuestAgent_Win7_asiaeast_manifest.xml</Uri>
+ </Uris>
+ </GAFamily>
+ </GAFamilies>
+</GuestAgentExtension>
+<Plugins>
+ <Plugin name="OSTCExtensions.ExampleHandlerLinux" version="1.0" location="http://rdfepirv2hknprdstr03.blob.core.windows.net/b01058962be54ceca550a390fa5ff064/Microsoft.OSTCExtensions_ExampleHandlerLinux_asiaeast_manifest.xml" config="" state="enabled" autoUpgrade="false" failoverlocation="http://rdfepirv2hknprdstr04.blob.core.windows.net/b01058962be54ceca550a390fa5ff064/Microsoft.OSTCExtensions_ExampleHandlerLinux_asiaeast_manifest.xml" runAsStartupTask="false" isJson="true" />
+</Plugins>
+<PluginSettings>
+ <Plugin name="OSTCExtensions.ExampleHandlerLinux" version="1.0">
+ <RuntimeSettings seqNo="0">{"runtimeSettings":[{"handlerSettings":{"protectedSettingsCertThumbprint":"4037FBF5F1F3014F99B5D6C7799E9B20E6871CB3","protectedSettings":"MIICWgYJK"}}]}</RuntimeSettings>
+ </Plugin>
+</PluginSettings>
+<StatusUploadBlob>https://yuezhatest.blob.core.windows.net/vhds/test-cs12.test-cs12.test-cs12.status?sr=b&amp;sp=rw&amp;se=9999-01-01&amp;sk=key1&amp;sv=2014-02-14&amp;sig=hfRh7gzUE7sUtYwke78IOlZOrTRCYvkec4hGZ9zZzXo%3D</StatusUploadBlob></Extensions>
+
diff --git a/tests/data/wire/ext_conf_no_settings.xml b/tests/data/wire/ext_conf_no_settings.xml
new file mode 100644
index 0000000..df76d54
--- /dev/null
+++ b/tests/data/wire/ext_conf_no_settings.xml
@@ -0,0 +1,41 @@
+<Extensions version="1.0.0.0" goalStateIncarnation="9"><GuestAgentExtension xmlns:i="http://www.w3.org/2001/XMLSchema-instance">
+ <GAFamilies>
+ <GAFamily>
+ <Name>Win8</Name>
+ <Uris>
+ <Uri>http://rdfepirv2hknprdstr03.blob.core.windows.net/bfd5c281a7dc4e4b84381eb0b47e3aaf/Microsoft.WindowsAzure.GuestAgent_Win8_asiaeast_manifest.xml</Uri>
+ <Uri>http://rdfepirv2hknprdstr04.blob.core.windows.net/bfd5c281a7dc4e4b84381eb0b47e3aaf/Microsoft.WindowsAzure.GuestAgent_Win8_asiaeast_manifest.xml</Uri>
+ <Uri>http://rdfepirv2hknprdstr05.blob.core.windows.net/bfd5c281a7dc4e4b84381eb0b47e3aaf/Microsoft.WindowsAzure.GuestAgent_Win8_asiaeast_manifest.xml</Uri>
+ <Uri>http://rdfepirv2hknprdstr06.blob.core.windows.net/bfd5c281a7dc4e4b84381eb0b47e3aaf/Microsoft.WindowsAzure.GuestAgent_Win8_asiaeast_manifest.xml</Uri>
+ <Uri>http://rdfepirv2hknprdstr07.blob.core.windows.net/bfd5c281a7dc4e4b84381eb0b47e3aaf/Microsoft.WindowsAzure.GuestAgent_Win8_asiaeast_manifest.xml</Uri>
+ <Uri>http://rdfepirv2hknprdstr08.blob.core.windows.net/bfd5c281a7dc4e4b84381eb0b47e3aaf/Microsoft.WindowsAzure.GuestAgent_Win8_asiaeast_manifest.xml</Uri>
+ <Uri>http://rdfepirv2hknprdstr09.blob.core.windows.net/bfd5c281a7dc4e4b84381eb0b47e3aaf/Microsoft.WindowsAzure.GuestAgent_Win8_asiaeast_manifest.xml</Uri>
+ <Uri>http://rdfepirv2hknprdstr10.blob.core.windows.net/bfd5c281a7dc4e4b84381eb0b47e3aaf/Microsoft.WindowsAzure.GuestAgent_Win8_asiaeast_manifest.xml</Uri>
+ <Uri>http://rdfepirv2hknprdstr11.blob.core.windows.net/bfd5c281a7dc4e4b84381eb0b47e3aaf/Microsoft.WindowsAzure.GuestAgent_Win8_asiaeast_manifest.xml</Uri>
+ <Uri>http://rdfepirv2hknprdstr12.blob.core.windows.net/bfd5c281a7dc4e4b84381eb0b47e3aaf/Microsoft.WindowsAzure.GuestAgent_Win8_asiaeast_manifest.xml</Uri>
+ <Uri>http://zrdfepirv2hk2prdstr01.blob.core.windows.net/bfd5c281a7dc4e4b84381eb0b47e3aaf/Microsoft.WindowsAzure.GuestAgent_Win8_asiaeast_manifest.xml</Uri>
+ </Uris>
+ </GAFamily>
+ <GAFamily>
+ <Name>Win7</Name>
+ <Uris>
+ <Uri>http://rdfepirv2hknprdstr03.blob.core.windows.net/bfd5c281a7dc4e4b84381eb0b47e3aaf/Microsoft.WindowsAzure.GuestAgent_Win7_asiaeast_manifest.xml</Uri>
+ <Uri>http://rdfepirv2hknprdstr04.blob.core.windows.net/bfd5c281a7dc4e4b84381eb0b47e3aaf/Microsoft.WindowsAzure.GuestAgent_Win7_asiaeast_manifest.xml</Uri>
+ <Uri>http://rdfepirv2hknprdstr05.blob.core.windows.net/bfd5c281a7dc4e4b84381eb0b47e3aaf/Microsoft.WindowsAzure.GuestAgent_Win7_asiaeast_manifest.xml</Uri>
+ <Uri>http://rdfepirv2hknprdstr06.blob.core.windows.net/bfd5c281a7dc4e4b84381eb0b47e3aaf/Microsoft.WindowsAzure.GuestAgent_Win7_asiaeast_manifest.xml</Uri>
+ <Uri>http://rdfepirv2hknprdstr07.blob.core.windows.net/bfd5c281a7dc4e4b84381eb0b47e3aaf/Microsoft.WindowsAzure.GuestAgent_Win7_asiaeast_manifest.xml</Uri>
+ <Uri>http://rdfepirv2hknprdstr08.blob.core.windows.net/bfd5c281a7dc4e4b84381eb0b47e3aaf/Microsoft.WindowsAzure.GuestAgent_Win7_asiaeast_manifest.xml</Uri>
+ <Uri>http://rdfepirv2hknprdstr09.blob.core.windows.net/bfd5c281a7dc4e4b84381eb0b47e3aaf/Microsoft.WindowsAzure.GuestAgent_Win7_asiaeast_manifest.xml</Uri>
+ <Uri>http://rdfepirv2hknprdstr10.blob.core.windows.net/bfd5c281a7dc4e4b84381eb0b47e3aaf/Microsoft.WindowsAzure.GuestAgent_Win7_asiaeast_manifest.xml</Uri>
+ <Uri>http://rdfepirv2hknprdstr11.blob.core.windows.net/bfd5c281a7dc4e4b84381eb0b47e3aaf/Microsoft.WindowsAzure.GuestAgent_Win7_asiaeast_manifest.xml</Uri>
+ <Uri>http://rdfepirv2hknprdstr12.blob.core.windows.net/bfd5c281a7dc4e4b84381eb0b47e3aaf/Microsoft.WindowsAzure.GuestAgent_Win7_asiaeast_manifest.xml</Uri>
+ <Uri>http://zrdfepirv2hk2prdstr01.blob.core.windows.net/bfd5c281a7dc4e4b84381eb0b47e3aaf/Microsoft.WindowsAzure.GuestAgent_Win7_asiaeast_manifest.xml</Uri>
+ </Uris>
+ </GAFamily>
+ </GAFamilies>
+</GuestAgentExtension>
+<Plugins>
+ <Plugin name="OSTCExtensions.ExampleHandlerLinux" version="1.0" location="http://rdfepirv2hknprdstr03.blob.core.windows.net/b01058962be54ceca550a390fa5ff064/Microsoft.OSTCExtensions_ExampleHandlerLinux_asiaeast_manifest.xml" config="" state="enabled" autoUpgrade="false" failoverlocation="http://rdfepirv2hknprdstr04.blob.core.windows.net/b01058962be54ceca550a390fa5ff064/Microsoft.OSTCExtensions_ExampleHandlerLinux_asiaeast_manifest.xml" runAsStartupTask="false" isJson="true" />
+</Plugins>
+<StatusUploadBlob>https://yuezhatest.blob.core.windows.net/vhds/test-cs12.test-cs12.test-cs12.status?sr=b&amp;sp=rw&amp;se=9999-01-01&amp;sk=key1&amp;sv=2014-02-14&amp;sig=hfRh7gzUE7sUtYwke78IOlZOrTRCYvkec4hGZ9zZzXo%3D</StatusUploadBlob></Extensions>
+
diff --git a/tests/data/wire/goal_state.xml b/tests/data/wire/goal_state.xml
new file mode 100644
index 0000000..960444b
--- /dev/null
+++ b/tests/data/wire/goal_state.xml
@@ -0,0 +1,27 @@
+<?xml version="1.0" encoding="utf-8"?>
+<GoalState xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:noNamespaceSchemaLocation="goalstate10.xsd">
+ <Version>2010-12-15</Version>
+ <Incarnation>1</Incarnation>
+ <Machine>
+ <ExpectedState>Started</ExpectedState>
+ <LBProbePorts>
+ <Port>16001</Port>
+ </LBProbePorts>
+ </Machine>
+ <Container>
+ <ContainerId>c6d5526c-5ac2-4200-b6e2-56f2b70c5ab2</ContainerId>
+ <RoleInstanceList>
+ <RoleInstance>
+ <InstanceId>MachineRole_IN_0</InstanceId>
+ <State>Started</State>
+ <Configuration>
+ <HostingEnvironmentConfig>http://hostingenvuri/</HostingEnvironmentConfig>
+ <SharedConfig>http://sharedconfiguri/</SharedConfig>
+ <Certificates>http://certificatesuri/</Certificates>
+ <ExtensionsConfig>http://extensionsconfiguri/</ExtensionsConfig>
+ <FullConfig>http://fullconfiguri/</FullConfig>
+ </Configuration>
+ </RoleInstance>
+ </RoleInstanceList>
+ </Container>
+ </GoalState>
diff --git a/tests/data/wire/goal_state_no_ext.xml b/tests/data/wire/goal_state_no_ext.xml
new file mode 100644
index 0000000..0b4f566
--- /dev/null
+++ b/tests/data/wire/goal_state_no_ext.xml
@@ -0,0 +1,26 @@
+<?xml version="1.0" encoding="utf-8"?>
+<GoalState xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:noNamespaceSchemaLocation="goalstate10.xsd">
+ <Version>2010-12-15</Version>
+ <Incarnation>1</Incarnation>
+ <Machine>
+ <ExpectedState>Started</ExpectedState>
+ <LBProbePorts>
+ <Port>16001</Port>
+ </LBProbePorts>
+ </Machine>
+ <Container>
+ <ContainerId>c6d5526c-5ac2-4200-b6e2-56f2b70c5ab2</ContainerId>
+ <RoleInstanceList>
+ <RoleInstance>
+ <InstanceId>MachineRole_IN_0</InstanceId>
+ <State>Started</State>
+ <Configuration>
+ <HostingEnvironmentConfig>http://hostingenvuri/</HostingEnvironmentConfig>
+ <SharedConfig>http://sharedconfiguri/</SharedConfig>
+ <Certificates>http://certificatesuri/</Certificates>
+ <FullConfig>http://fullconfiguri/</FullConfig>
+ </Configuration>
+ </RoleInstance>
+ </RoleInstanceList>
+ </Container>
+ </GoalState>
diff --git a/tests/test_hostingenv.py b/tests/data/wire/hosting_env.xml
index 3d2ce73..f763a25 100644
--- a/tests/test_hostingenv.py
+++ b/tests/data/wire/hosting_env.xml
@@ -1,32 +1,4 @@
-# Copyright 2014 Microsoft Corporation
-#
-# Licensed under the Apache License, Version 2.0 (the "License");
-# you may not use this file except in compliance with the License.
-# You may obtain a copy of the License at
-#
-# http://www.apache.org/licenses/LICENSE-2.0
-#
-# Unless required by applicable law or agreed to in writing, software
-# distributed under the License is distributed on an "AS IS" BASIS,
-# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-# See the License for the specific language governing permissions and
-# limitations under the License.
-#
-# Requires Python 2.4+ and Openssl 1.0+
-#
-# Implements parts of RFC 2131, 1541, 1497 and
-# http://msdn.microsoft.com/en-us/library/cc227282%28PROT.10%29.aspx
-# http://msdn.microsoft.com/en-us/library/cc227259%28PROT.13%29.aspx
-
-import tests.env
-import tests.tools as tools
-import uuid
-import unittest
-import os
-import azurelinuxagent.protocol.v1 as v1
-
-hosting_env_sample=u"""
- <HostingEnvironmentConfig version="1.0.0.0" goalStateIncarnation="1">
+<HostingEnvironmentConfig version="1.0.0.0" goalStateIncarnation="1">
<StoredCertificates>
<StoredCertificate name="Stored0Microsoft.WindowsAzure.Plugins.RemoteAccess.PasswordEncryption" certificateId="sha1:C093FA5CD3AAE057CB7C4E04532B2E16E07C26CA" storeName="My" configurationLevel="System" />
</StoredCertificates>
@@ -50,17 +22,3 @@ hosting_env_sample=u"""
<Resource name="DiagnosticStore" type="directory" request="Microsoft.Cis.Fabric.Controller.Descriptions.ServiceDescription.Data.Policy" sticky="true" size="1" path="db00a7755a5e4e8a8fe4b19bc3b330c3.MachineRole.DiagnosticStore\" disableQuota="false" />
</ResourceReferences>
</HostingEnvironmentConfig>
-"""
-
-class TestHostingEvn(unittest.TestCase):
- def test_hosting_env(self):
- hosting_env = v1.HostingEnv(hosting_env_sample)
- self.assertNotEquals(None, hosting_env)
- self.assertEquals("MachineRole_IN_0", hosting_env.vm_name)
- self.assertEquals("MachineRole", hosting_env.role_name)
- self.assertEquals("db00a7755a5e4e8a8fe4b19bc3b330c3",
- hosting_env.deployment_name)
-
-
-if __name__ == '__main__':
- unittest.main()
diff --git a/tests/data/wire/manifest.xml b/tests/data/wire/manifest.xml
new file mode 100644
index 0000000..943755a
--- /dev/null
+++ b/tests/data/wire/manifest.xml
@@ -0,0 +1,18 @@
+<?xml version="1.0" encoding="utf-8"?>
+<PluginVersionManifest xmlns:i="http://www.w3.org/2001/XMLSchema-instance">
+ <Plugins>
+ <Plugin>
+ <Version>1.0</Version>
+ <Uris>
+ <Uri>http://foo.bar/zar/OSTCExtensions.ExampleHandlerLinux</Uri>
+ </Uris>
+ </Plugin>
+ <Plugin>
+ <Version>1.1</Version>
+ <Uris>
+ <Uri>http://foo.bar/zar/OSTCExtensions.ExampleHandlerLinux</Uri>
+ </Uris>
+ </Plugin>
+ </Plugins>
+</PluginVersionManifest>
+
diff --git a/tests/test_sharedconfig.py b/tests/data/wire/shared_config.xml
index f480253..7c6ae14 100644
--- a/tests/test_sharedconfig.py
+++ b/tests/data/wire/shared_config.xml
@@ -1,33 +1,4 @@
-# Copyright 2014 Microsoft Corporation
-#
-# Licensed under the Apache License, Version 2.0 (the "License");
-# you may not use this file except in compliance with the License.
-# You may obtain a copy of the License at
-#
-# http://www.apache.org/licenses/LICENSE-2.0
-#
-# Unless required by applicable law or agreed to in writing, software
-# distributed under the License is distributed on an "AS IS" BASIS,
-# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-# See the License for the specific language governing permissions and
-# limitations under the License.
-#
-# Requires Python 2.4+ and Openssl 1.0+
-#
-# Implements parts of RFC 2131, 1541, 1497 and
-# http://msdn.microsoft.com/en-us/library/cc227282%28PROT.10%29.aspx
-# http://msdn.microsoft.com/en-us/library/cc227259%28PROT.13%29.aspx
-
-import tests.env
-import tests.tools as tools
-import uuid
-import unittest
-import os
-import azurelinuxagent.protocol.v1 as v1
-
-shared_config_sample=u"""
-
- <SharedConfig version="1.0.0.0" goalStateIncarnation="1">
+<SharedConfig version="1.0.0.0" goalStateIncarnation="1">
<Deployment name="db00a7755a5e4e8a8fe4b19bc3b330c3" guid="{ce5a036f-5c93-40e7-8adf-2613631008ab}" incarnation="2">
<Service name="MyVMRoleService" guid="{00000000-0000-0000-0000-000000000000}" />
<ServiceInstance name="db00a7755a5e4e8a8fe4b19bc3b330c3.1" guid="{d113f4d7-9ead-4e73-b715-b724b5b7842c}" />
@@ -69,11 +40,3 @@ shared_config_sample=u"""
</Instance>
</Instances>
</SharedConfig>
-"""
-
-class TestSharedConfig(unittest.TestCase):
- def test_sharedconfig(self):
- shared_conf = v1.SharedConfig(shared_config_sample)
-
-if __name__ == '__main__':
- unittest.main()
diff --git a/tests/sshd_config b/tests/data/wire/sshd_config
index 77fb290..77fb290 100644
--- a/tests/sshd_config
+++ b/tests/data/wire/sshd_config
diff --git a/tests/data/wire/trans_cert b/tests/data/wire/trans_cert
new file mode 100644
index 0000000..d560ae2
--- /dev/null
+++ b/tests/data/wire/trans_cert
@@ -0,0 +1,19 @@
+-----BEGIN CERTIFICATE-----
+MIIDBzCCAe+gAwIBAgIJANujJuVt5eC8MA0GCSqGSIb3DQEBCwUAMBkxFzAVBgNV
+BAMMDkxpbnV4VHJhbnNwb3J0MCAXDTE0MTAyNDA3MjgwN1oYDzIxMDQwNzEyMDcy
+ODA3WjAZMRcwFQYDVQQDDA5MaW51eFRyYW5zcG9ydDCCASIwDQYJKoZIhvcNAQEB
+BQADggEPADCCAQoCggEBANPcJAkd6V5NeogSKjIeTXOWC5xzKTyuJPt4YZMVSosU
+0lI6a0wHp+g2fP22zrVswW+QJz6AVWojIEqLQup3WyCXZTv8RUblHnIjkvX/+J/G
+aLmz0G5JzZIpELL2C8IfQLH2IiPlK9LOQH00W74WFcK3QqcJ6Kw8GcVaeSXT1r7X
+QcGMqEjcWJkpKLoMJv3LMufE+JMdbXDUGY+Ps7Zicu8KXvBPaKVsc6H2jrqBS8et
+jXbzLyrezTUDz45rmyRJzCO5Sk2pohuYg73wUykAUPVxd7L8WnSyqz1v4zrObqnw
+BAyor67JR/hjTBfjFOvd8qFGonfiv2Vnz9XsYFTZsXECAwEAAaNQME4wHQYDVR0O
+BBYEFL8i/sehpGV6IEDX7F0WQHQ/ZXOyMB8GA1UdIwQYMBaAFL8i/sehpGV6IEDX
+7F0WQHQ/ZXOyMAwGA1UdEwQFMAMBAf8wDQYJKoZIhvcNAQELBQADggEBAMPLrimT
+Gptu5pLRHPT8OFRN+skNSkepYaUaJuq6cSKxLumSYkD8++rohu+1+a7t1YNjjNSJ
+8ohRAynRJ7aRqwBmyX2OPLRpOfyRZwR0rcFfAMORm/jOE6WBdqgYD2L2b+tZplGt
+/QqgQzebaekXh/032FK4c74Zg5r3R3tfNSUMG6nLauWzYHbQ5SCdkuQwV0ehGqh5
+VF1AOdmz4CC2237BNznDFQhkeU0LrqqAoE/hv5ih7klJKZdS88rOYEnVJsFFJb0g
+qaycXjOm5Khgl4hKrd+DBD/qj4IVVzsmdpFli72k6WLBHGOXusUGo/3isci2iAIt
+DsfY6XGSEIhZnA4=
+-----END CERTIFICATE-----
diff --git a/tests/data/wire/trans_prv b/tests/data/wire/trans_prv
new file mode 100644
index 0000000..063cf15
--- /dev/null
+++ b/tests/data/wire/trans_prv
@@ -0,0 +1,28 @@
+-----BEGIN PRIVATE KEY-----
+MIIEvgIBADANBgkqhkiG9w0BAQEFAASCBKgwggSkAgEAAoIBAQDT3CQJHeleTXqI
+EioyHk1zlguccyk8riT7eGGTFUqLFNJSOmtMB6foNnz9ts61bMFvkCc+gFVqIyBK
+i0Lqd1sgl2U7/EVG5R5yI5L1//ifxmi5s9BuSc2SKRCy9gvCH0Cx9iIj5SvSzkB9
+NFu+FhXCt0KnCeisPBnFWnkl09a+10HBjKhI3FiZKSi6DCb9yzLnxPiTHW1w1BmP
+j7O2YnLvCl7wT2ilbHOh9o66gUvHrY128y8q3s01A8+Oa5skScwjuUpNqaIbmIO9
+8FMpAFD1cXey/Fp0sqs9b+M6zm6p8AQMqK+uyUf4Y0wX4xTr3fKhRqJ34r9lZ8/V
+7GBU2bFxAgMBAAECggEBAM4hsfog3VAAyIieS+npq+gbhH6bWfMNaTQ3g5CNNbMu
+9hhFeOJHzKnWYjSlamgBQhAfTN+2E+Up+iAtcVUZ/lMumrQLlwgMo1vgmvu5Kxmh
+/YE5oEG+k0JzrCjD1trwd4zvc3ZDYyk/vmVTzTOc311N248UyArUiyqHBbq1a4rP
+tJhCLn2c4S7flXGF0MDVGZyV9V7J8N8leq/dRGMB027Li21T+B4mPHXa6b8tpRPL
+4vc8sHoUJDa2/+mFDJ2XbZfmlgd3MmIPlRn1VWoW7mxgT/AObsPl7LuQx7+t80Wx
+hIMjuKUHRACQSLwHxJ3SQRFWp4xbztnXSRXYuHTscLUCgYEA//Uu0qIm/FgC45yG
+nXtoax4+7UXhxrsWDEkbtL6RQ0TSTiwaaI6RSQcjrKDVSo/xo4ZySTYcRgp5GKlI
+CrWyNM+UnIzTNbZOtvSIAfjxYxMsq1vwpTlOB5/g+cMukeGg39yUlrjVNoFpv4i6
+9t4yYuEaF4Vww0FDd2nNKhhW648CgYEA0+UYH6TKu03zDXqFpwf4DP2VoSo8OgfQ
+eN93lpFNyjrfzvxDZkGF+7M/ebyYuI6hFplVMu6BpgpFP7UVJpW0Hn/sXkTq7F1Q
+rTJTtkTp2+uxQVP/PzSOqK0Twi5ifkfoEOkPkNNtTiXzwCW6Qmmcvln2u893pyR5
+gqo5BHR7Ev8CgYAb7bXpN9ZHLJdMHLU3k9Kl9YvqOfjTxXA3cPa79xtEmsrTys4q
+4HuL22KSII6Fb0VvkWkBAg19uwDRpw78VC0YxBm0J02Yi8b1AaOhi3dTVzFFlWeh
+r6oK/PAAcMKxGkyCgMAZ3hstsltGkfXMoBwhW+yL6nyOYZ2p9vpzAGrjkwKBgQDF
+0huzbyXVt/AxpTEhv07U0enfjI6tnp4COp5q8zyskEph8yD5VjK/yZh5DpmFs6Kw
+dnYUFpbzbKM51tToMNr3nnYNjEnGYVfwWgvNHok1x9S0KLcjSu3ki7DmmGdbfcYq
+A2uEyd5CFyx5Nr+tQOwUyeiPbiFG6caHNmQExLoiAQKBgFPy9H8///xsadYmZ18k
+r77R2CvU7ArxlLfp9dr19aGYKvHvnpsY6EuChkWfy8Xjqn3ogzgrHz/rn3mlGUpK
+vbtwtsknAHtTbotXJwfaBZv2RGgGRr3DzNo6ll2Aez0lNblZFXq132h7+y5iLvar
+4euORaD/fuM4UPlR5mN+bypU
+-----END PRIVATE KEY-----
diff --git a/tests/data/wire/version_info.xml b/tests/data/wire/version_info.xml
new file mode 100644
index 0000000..b4d0a3f
--- /dev/null
+++ b/tests/data/wire/version_info.xml
@@ -0,0 +1,10 @@
+<?xml version="1.0" encoding="utf-8"?>
+<Versions>
+ <Preferred>
+ <Version>2012-11-30</Version>
+ </Preferred>
+ <Supported>
+ <Version>2010-12-15</Version>
+ <Version>2010-28-10</Version>
+ </Supported>
+</Versions>
diff --git a/tests/env.py b/tests/distro/__init__.py
index 5bc6eb8..9bdb27e 100644
--- a/tests/env.py
+++ b/tests/distro/__init__.py
@@ -17,10 +17,3 @@
# Implements parts of RFC 2131, 1541, 1497 and
# http://msdn.microsoft.com/en-us/library/cc227282%28PROT.10%29.aspx
# http://msdn.microsoft.com/en-us/library/cc227259%28PROT.13%29.aspx
-
-import os
-import sys
-
-test_root = os.path.dirname(os.path.abspath(__file__))
-project_root = os.path.dirname(test_root)
-sys.path.insert(0, project_root)
diff --git a/tests/distro/test_daemon.py b/tests/distro/test_daemon.py
new file mode 100644
index 0000000..9d3c45b
--- /dev/null
+++ b/tests/distro/test_daemon.py
@@ -0,0 +1,65 @@
+# Copyright 2014 Microsoft Corporation
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+#
+# Requires Python 2.4+ and Openssl 1.0+
+#
+# Implements parts of RFC 2131, 1541, 1497 and
+# http://msdn.microsoft.com/en-us/library/cc227282%28PROT.10%29.aspx
+# http://msdn.microsoft.com/en-us/library/cc227259%28PROT.13%29.aspx
+
+from tests.tools import *
+from azurelinuxagent.distro.loader import get_distro
+from azurelinuxagent.exception import *
+from azurelinuxagent.distro.default.daemon import *
+
+class MockDaemonCall(object):
+ def __init__(self, daemon_handler, count):
+ self.daemon_handler = daemon_handler
+ self.count = count
+
+ def __call__(self, *args, **kw):
+ self.count = self.count - 1
+ #Stop daemon after restarting for n times
+ if self.count <= 0:
+ self.daemon_handler.running = False
+ raise Exception("Mock unhandled exception")
+
+@patch("time.sleep")
+class TestDaemon(AgentTestCase):
+ def test_daemon_restart(self, mock_sleep):
+ distro = get_distro()
+ mock_daemon = Mock(side_effect=MockDaemonCall(distro.daemon_handler, 2))
+ distro.daemon_handler.daemon = mock_daemon
+ distro.daemon_handler.check_pid = Mock()
+ distro.daemon_handler.run()
+
+ mock_sleep.assert_any_call(15)
+ self.assertEquals(2, distro.daemon_handler.daemon.call_count)
+
+ @patch("azurelinuxagent.distro.default.daemon.conf")
+ @patch("azurelinuxagent.distro.default.daemon.sys.exit")
+ def test_check_pid(self, mock_exit, mock_conf, mock_sleep):
+ distro = get_distro()
+ mock_pid_file = os.path.join(self.tmp_dir, "pid")
+ mock_conf.get_agent_pid_file_path = Mock(return_value=mock_pid_file)
+
+ distro.daemon_handler.check_pid()
+ self.assertTrue(os.path.isfile(mock_pid_file))
+
+ distro.daemon_handler.check_pid()
+ mock_exit.assert_any_call(0)
+
+if __name__ == '__main__':
+ unittest.main()
+
diff --git a/tests/distro/test_extension.py b/tests/distro/test_extension.py
new file mode 100644
index 0000000..d0b631f
--- /dev/null
+++ b/tests/distro/test_extension.py
@@ -0,0 +1,191 @@
+# Copyright 2014 Microsoft Corporation
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+#
+# Requires Python 2.4+ and Openssl 1.0+
+#
+# Implements parts of RFC 2131, 1541, 1497 and
+# http://msdn.microsoft.com/en-us/library/cc227282%28PROT.10%29.aspx
+# http://msdn.microsoft.com/en-us/library/cc227259%28PROT.13%29.aspx
+
+from tests.tools import *
+from tests.protocol.mockwiredata import *
+from azurelinuxagent.exception import *
+from azurelinuxagent.distro.loader import get_distro
+from azurelinuxagent.protocol.restapi import get_properties
+from azurelinuxagent.protocol.wire import WireProtocol
+
+@patch("time.sleep")
+@patch("azurelinuxagent.protocol.wire.CryptUtil")
+@patch("azurelinuxagent.utils.restutil.http_get")
+class TestExtension(AgentTestCase):
+
+ def _assert_handler_status(self, report_vm_status, expected_status,
+ expected_ext_count, version):
+ self.assertTrue(report_vm_status.called)
+ args, kw = report_vm_status.call_args
+ vm_status = args[0]
+ self.assertNotEquals(0, len(vm_status.vmAgent.extensionHandlers))
+ handler_status = vm_status.vmAgent.extensionHandlers[0]
+ self.assertEquals(expected_status, handler_status.status)
+ self.assertEquals("OSTCExtensions.ExampleHandlerLinux",
+ handler_status.name)
+ self.assertEquals(version, handler_status.version)
+ self.assertEquals(expected_ext_count, len(handler_status.extensions))
+
+ def _assert_no_handler_status(self, report_vm_status):
+ self.assertTrue(report_vm_status.called)
+ args, kw = report_vm_status.call_args
+ vm_status = args[0]
+ self.assertEquals(0, len(vm_status.vmAgent.extensionHandlers))
+
+ def _create_mock(self, test_data, mock_http_get, MockCryptUtil, _):
+ """Test enable/disable/unistall of an extension"""
+ distro = get_distro()
+
+ #Mock protocol to return test data
+ mock_http_get.side_effect = test_data.mock_http_get
+ MockCryptUtil.side_effect = test_data.mock_crypt_util
+
+ protocol = WireProtocol("foo.bar")
+ protocol.detect()
+ protocol.report_ext_status = MagicMock()
+ protocol.report_vm_status = MagicMock()
+ distro.protocol_util.get_protocol = Mock(return_value=protocol)
+
+ return distro, protocol
+
+ def test_ext_handler(self, *args):
+ test_data = WireProtocolData(DATA_FILE)
+ distro, protocol = self._create_mock(test_data, *args)
+
+ #Test enable scenario.
+ distro.ext_handlers_handler.run()
+ self._assert_handler_status(protocol.report_vm_status, "Ready", 1, "1.0")
+ self._assert_ext_status(protocol.report_ext_status, "success", 0)
+
+ #Test goal state not changed
+ distro.ext_handlers_handler.run()
+ self._assert_handler_status(protocol.report_vm_status, "Ready", 1, "1.0")
+
+ #Test goal state changed
+ test_data.goal_state = test_data.goal_state.replace("<Incarnation>1<",
+ "<Incarnation>2<")
+ test_data.ext_conf = test_data.ext_conf.replace("seqNo=\"0\"",
+ "seqNo=\"1\"")
+ distro.ext_handlers_handler.run()
+ self._assert_handler_status(protocol.report_vm_status, "Ready", 1, "1.0")
+ self._assert_ext_status(protocol.report_ext_status, "success", 1)
+
+ #Test upgrade
+ test_data.goal_state = test_data.goal_state.replace("<Incarnation>2<",
+ "<Incarnation>3<")
+ test_data.ext_conf = test_data.ext_conf.replace("1.0", "1.1")
+ test_data.ext_conf = test_data.ext_conf.replace("seqNo=\"1\"",
+ "seqNo=\"2\"")
+ distro.ext_handlers_handler.run()
+ self._assert_handler_status(protocol.report_vm_status, "Ready", 1, "1.1")
+ self._assert_ext_status(protocol.report_ext_status, "success", 2)
+
+ #Test disable
+ test_data.goal_state = test_data.goal_state.replace("<Incarnation>3<",
+ "<Incarnation>4<")
+ test_data.ext_conf = test_data.ext_conf.replace("enabled", "disabled")
+ distro.ext_handlers_handler.run()
+ self._assert_handler_status(protocol.report_vm_status, "NotReady",
+ 1, "1.1")
+
+ #Test uninstall
+ test_data.goal_state = test_data.goal_state.replace("<Incarnation>4<",
+ "<Incarnation>5<")
+ test_data.ext_conf = test_data.ext_conf.replace("disabled", "uninstall")
+ distro.ext_handlers_handler.run()
+ self._assert_no_handler_status(protocol.report_vm_status)
+
+ #Test uninstall again!
+ test_data.goal_state = test_data.goal_state.replace("<Incarnation>5<",
+ "<Incarnation>6<")
+ distro.ext_handlers_handler.run()
+ self._assert_no_handler_status(protocol.report_vm_status)
+
+ def test_ext_handler_no_settings(self, *args):
+ test_data = WireProtocolData(DATA_FILE_EXT_NO_SETTINGS)
+ distro, protocol = self._create_mock(test_data, *args)
+
+ distro.ext_handlers_handler.run()
+ self._assert_handler_status(protocol.report_vm_status, "Ready", 0, "1.0")
+
+ def test_ext_handler_no_public_settings(self, *args):
+ test_data = WireProtocolData(DATA_FILE_EXT_NO_PUBLIC)
+ distro, protocol = self._create_mock(test_data, *args)
+
+ distro.ext_handlers_handler.run()
+ self._assert_handler_status(protocol.report_vm_status, "Ready", 1, "1.0")
+
+ def test_ext_handler_no_ext(self, *args):
+ test_data = WireProtocolData(DATA_FILE_NO_EXT)
+ distro, protocol = self._create_mock(test_data, *args)
+
+ #Assert no extension handler status
+ distro.ext_handlers_handler.run()
+ self._assert_no_handler_status(protocol.report_vm_status)
+
+ @patch('azurelinuxagent.distro.default.extension.add_event')
+ def test_ext_handler_download_failure(self, mock_add_event, *args):
+ test_data = WireProtocolData(DATA_FILE)
+ distro, protocol = self._create_mock(test_data, *args)
+ protocol.download_ext_handler_pkg = Mock(side_effect=ProtocolError)
+
+ distro.ext_handlers_handler.run()
+ args, kw = mock_add_event.call_args
+ self.assertEquals(False, kw['is_success'])
+ self.assertEquals("OSTCExtensions.ExampleHandlerLinux", kw['name'])
+ self.assertEquals("Download", kw['op'])
+
+ @patch('azurelinuxagent.distro.default.extension.fileutil')
+ def test_ext_handler_io_error(self, mock_fileutil, *args):
+ test_data = WireProtocolData(DATA_FILE)
+ distro, protocol = self._create_mock(test_data, *args)
+
+ mock_fileutil.write_file.return_value = IOError("Mock IO Error")
+ distro.ext_handlers_handler.run()
+
+ def _assert_ext_status(self, report_ext_status, expected_status,
+ expected_seq_no):
+ self.assertTrue(report_ext_status.called)
+ args, kw = report_ext_status.call_args
+ ext_status = args[-1]
+ self.assertEquals(expected_status, ext_status.status)
+ self.assertEquals(expected_seq_no, ext_status.sequenceNumber)
+
+ def test_ext_handler_no_reporting_status(self, *args):
+ test_data = WireProtocolData(DATA_FILE)
+ distro, protocol = self._create_mock(test_data, *args)
+ distro.ext_handlers_handler.run()
+ self._assert_handler_status(protocol.report_vm_status, "Ready", 1, "1.0")
+
+ #Remove status file and re-run collecting extension status
+ status_file = os.path.join(self.tmp_dir,
+ "OSTCExtensions.ExampleHandlerLinux-1.0",
+ "status", "0.status")
+ self.assertTrue(os.path.isfile(status_file))
+ os.remove(status_file)
+
+ distro.ext_handlers_handler.run()
+ self._assert_handler_status(protocol.report_vm_status, "Ready", 1, "1.0")
+ self._assert_ext_status(protocol.report_ext_status, "error", 0)
+
+
+if __name__ == '__main__':
+ unittest.main()
+
diff --git a/tests/test_future.py b/tests/distro/test_loader.py
index 20edc93..94ca913 100644
--- a/tests/test_future.py
+++ b/tests/distro/test_loader.py
@@ -18,20 +18,19 @@
# http://msdn.microsoft.com/en-us/library/cc227282%28PROT.10%29.aspx
# http://msdn.microsoft.com/en-us/library/cc227259%28PROT.13%29.aspx
-import tests.env
from tests.tools import *
-import uuid
-import unittest
-import os
-import shutil
-import time
-import azurelinuxagent.future as future
+from azurelinuxagent.distro.loader import get_distro
+from azurelinuxagent.distro.default.distro import DefaultDistro
-class TestFuture(unittest.TestCase):
- def test_future_pkgs(self):
- future.httpclient
- future.urlparse
- future.text(b"asdf", encoding="utf-8")
+class TestDistroLoader(AgentTestCase):
+
+ @distros()
+ def test_distro_loader(self, *distro_args):
+ distro = get_distro(*distro_args)
+ self.assertNotEquals(None, distro)
+ self.assertNotEquals(DefaultDistro, type(distro))
+
if __name__ == '__main__':
unittest.main()
+
diff --git a/tests/test_protocolFactory.py b/tests/distro/test_monitor.py
index 9928b88..1dd7740 100644
--- a/tests/test_protocolFactory.py
+++ b/tests/distro/test_monitor.py
@@ -18,20 +18,15 @@
# http://msdn.microsoft.com/en-us/library/cc227282%28PROT.10%29.aspx
# http://msdn.microsoft.com/en-us/library/cc227259%28PROT.13%29.aspx
-import tests.env
from tests.tools import *
-import uuid
-import unittest
-import os
-import azurelinuxagent.protocol as protocol
-import azurelinuxagent.protocol.protocolFactory as protocolFactory
+from azurelinuxagent.exception import *
+from azurelinuxagent.distro.default.monitor import *
-class TestWireProtocolEndpoint(unittest.TestCase):
- def test_get_available_protocols(self):
- mockGetV1 = MockFunc(retval="Mock protocol")
- protocols = protocolFactory.get_available_protocols([mockGetV1])
- self.assertNotEquals(None, protocols)
- self.assertNotEquals(0, len(protocols))
+class TestMonitor(AgentTestCase):
+ def test_parse_xml_event(self):
+ data_str = load_data('ext/event.xml')
+ event = parse_xml_event(data_str)
+ self.assertNotEquals(None, event)
+ self.assertNotEquals(0, event.parameters)
+ self.assertNotEquals(None, event.parameters[0])
-if __name__ == '__main__':
- unittest.main()
diff --git a/tests/distro/test_protocol_util.py b/tests/distro/test_protocol_util.py
new file mode 100644
index 0000000..61339f3
--- /dev/null
+++ b/tests/distro/test_protocol_util.py
@@ -0,0 +1,89 @@
+# Copyright 2014 Microsoft Corporation
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+#
+# Requires Python 2.4+ and Openssl 1.0+
+#
+# Implements parts of RFC 2131, 1541, 1497 and
+# http://msdn.microsoft.com/en-us/library/cc227282%28PROT.10%29.aspx
+# http://msdn.microsoft.com/en-us/library/cc227259%28PROT.13%29.aspx
+
+from tests.tools import *
+from azurelinuxagent.distro.loader import get_distro
+from azurelinuxagent.exception import *
+from azurelinuxagent.distro.default.protocolUtil import *
+
+@patch("time.sleep")
+class TestProtocolUtil(AgentTestCase):
+
+ @distros()
+ @patch("azurelinuxagent.distro.default.protocolUtil.MetadataProtocol")
+ @patch("azurelinuxagent.distro.default.protocolUtil.WireProtocol")
+ def test_detect_protocol(self, distro_name, distro_version, distro_full_name,
+ WireProtocol, MetadataProtocol, _, *distro_args):
+
+ WireProtocol.return_value = MagicMock()
+ MetadataProtocol.return_value = MagicMock()
+
+ distro = get_distro(distro_name, distro_version, distro_full_name)
+ distro.dhcp_handler = MagicMock()
+ distro.dhcp_handler.endpoint = "foo.bar"
+
+ #Test wire protocol is available
+ protocol = distro.protocol_util.detect_protocol()
+ self.assertEquals(WireProtocol.return_value, protocol)
+
+ #Test wire protocol is not available
+ distro.protocol_util.protocol = None
+ WireProtocol.side_effect = ProtocolError()
+
+ protocol = distro.protocol_util.detect_protocol()
+ self.assertEquals(MetadataProtocol.return_value, protocol)
+
+ #Test no protocol is available
+ distro.protocol_util.protocol = None
+ WireProtocol.side_effect = ProtocolError()
+ MetadataProtocol.side_effect = ProtocolError()
+ self.assertRaises(ProtocolError, distro.protocol_util.detect_protocol)
+
+ @distros()
+ def test_detect_protocol_by_file(self, distro_name, distro_version,
+ distro_full_name, _):
+ distro = get_distro(distro_name, distro_version, distro_full_name)
+ protocol_util = distro.protocol_util
+
+ protocol_util._detect_wire_protocol = Mock()
+ protocol_util._detect_metadata_protocol = Mock()
+
+ tag_file = os.path.join(self.tmp_dir, TAG_FILE_NAME)
+
+ #Test tag file doesn't exist
+ protocol_util.detect_protocol_by_file()
+ protocol_util._detect_wire_protocol.assert_any_call()
+ protocol_util._detect_metadata_protocol.assert_not_called()
+
+ #Test tag file exists
+ protocol_util.protocol = None
+ protocol_util._detect_wire_protocol.reset_mock()
+ protocol_util._detect_metadata_protocol.reset_mock()
+ with open(tag_file, "w+") as tag_fd:
+ tag_fd.write("")
+
+ protocol_util.detect_protocol_by_file()
+ protocol_util._detect_metadata_protocol.assert_any_call()
+ protocol_util._detect_wire_protocol.assert_not_called()
+
+
+if __name__ == '__main__':
+ unittest.main()
+
diff --git a/tests/test_agent.py b/tests/distro/test_provision.py
index 721b9f0..60249ce 100644
--- a/tests/test_agent.py
+++ b/tests/distro/test_provision.py
@@ -18,28 +18,30 @@
# http://msdn.microsoft.com/en-us/library/cc227282%28PROT.10%29.aspx
# http://msdn.microsoft.com/en-us/library/cc227259%28PROT.13%29.aspx
-import tests.env
-import tests.tools as tools
-import uuid
-import unittest
-import os
-import json
+from tests.tools import *
+from azurelinuxagent.distro.loader import get_distro
+from azurelinuxagent.distro.default.protocolUtil import *
import azurelinuxagent.utils.fileutil as fileutil
-import azurelinuxagent.agent as agent
-class TestAgent(unittest.TestCase):
- def test_parse_args(self):
- cmd, force, verbose = agent.parse_args(["deprovision+user",
- "-force",
- "/verbose"])
- self.assertEquals("deprovision+user", cmd)
- self.assertTrue(force)
- self.assertTrue(verbose)
+class TestProvision(AgentTestCase):
- cmd, force, verbose = agent.parse_args(["wrong cmd"])
- self.assertEquals("help", cmd)
- self.assertFalse(force)
- self.assertFalse(verbose)
+ @distros("redhat")
+ def test_provision(self, distro_name, distro_version, distro_full_name):
+ distro = get_distro(distro_name, distro_version, distro_full_name)
+ distro.osutil = MagicMock()
+ distro.osutil.decode_customdata = Mock(return_value="")
+
+ distro.protocol_util.detect_protocol_by_file = MagicMock()
+ distro.protocol_util.get_protocol = MagicMock()
+ conf.get_dvd_mount_point = Mock(return_value=self.tmp_dir)
+
+ ovfenv_file = os.path.join(self.tmp_dir, OVF_FILE_NAME)
+ ovfenv_data = load_data("ovf-env.xml")
+ fileutil.write_file(ovfenv_file, ovfenv_data)
+
+ handler = distro.provision_handler
+ handler.run()
if __name__ == '__main__':
unittest.main()
+
diff --git a/azurelinuxagent/distro/centos/__init__.py b/tests/protocol/__init__.py
index d9b82f5..9bdb27e 100644
--- a/azurelinuxagent/distro/centos/__init__.py
+++ b/tests/protocol/__init__.py
@@ -1,5 +1,3 @@
-# Microsoft Azure Linux Agent
-#
# Copyright 2014 Microsoft Corporation
#
# Licensed under the Apache License, Version 2.0 (the "License");
@@ -16,4 +14,6 @@
#
# Requires Python 2.4+ and Openssl 1.0+
#
-
+# Implements parts of RFC 2131, 1541, 1497 and
+# http://msdn.microsoft.com/en-us/library/cc227282%28PROT.10%29.aspx
+# http://msdn.microsoft.com/en-us/library/cc227259%28PROT.13%29.aspx
diff --git a/tests/protocol/mockmetadata.py b/tests/protocol/mockmetadata.py
new file mode 100644
index 0000000..0f7b568
--- /dev/null
+++ b/tests/protocol/mockmetadata.py
@@ -0,0 +1,61 @@
+# Copyright 2014 Microsoft Corporation
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+#
+# Requires Python 2.4+ and Openssl 1.0+
+#
+# Implements parts of RFC 2131, 1541, 1497 and
+# http://msdn.microsoft.com/en-us/library/cc227282%28PROT.10%29.aspx
+# http://msdn.microsoft.com/en-us/library/cc227259%28PROT.13%29.aspx
+
+from tests.tools import *
+from azurelinuxagent.future import httpclient
+from azurelinuxagent.utils.cryptutil import CryptUtil
+
+DATA_FILE = {
+ "identity": "metadata/identity.json",
+ "certificates": "metadata/certificates.json",
+ "ext_handlers": "metadata/ext_handlers.json",
+ "ext_handler_pkgs": "metadata/ext_handler_pkgs.json",
+}
+
+DATA_FILE_NO_EXT = DATA_FILE.copy()
+DATA_FILE_NO_EXT["ext_handlers"] = "metadata/ext_handlers_no_ext.json"
+
+class MetadataProtocolData(object):
+ def __init__(self, data_files):
+ self.identity = load_data(data_files.get("identity"))
+ self.certificates = load_data(data_files.get("certificates"))
+ self.ext_handlers = load_data(data_files.get("ext_handlers"))
+ self.ext_handler_pkgs = load_data(data_files.get("ext_handler_pkgs"))
+
+ def mock_http_get(self, url, *args, **kwargs):
+ content = None
+ if url.count(u"identity?") > 0:
+ content = self.identity
+ elif url.count(u"certificates") > 0:
+ content = self.certificates
+ elif url.count(u"extensionHandlers") > 0:
+ content = self.ext_handlers
+ elif url.count(u"versionUri") > 0:
+ content = self.ext_handler_pkgs
+ else:
+ raise Exception("Bad url {0}".format(url))
+ resp = MagicMock()
+ resp.status = httpclient.OK
+ if content is None:
+ resp.read = Mock(return_value=None)
+ else:
+ resp.read = Mock(return_value=content.encode("utf-8"))
+ return resp
+
diff --git a/tests/protocol/mockwiredata.py b/tests/protocol/mockwiredata.py
new file mode 100644
index 0000000..6ffd19c
--- /dev/null
+++ b/tests/protocol/mockwiredata.py
@@ -0,0 +1,101 @@
+# Copyright 2014 Microsoft Corporation
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+#
+# Requires Python 2.4+ and Openssl 1.0+
+#
+# Implements parts of RFC 2131, 1541, 1497 and
+# http://msdn.microsoft.com/en-us/library/cc227282%28PROT.10%29.aspx
+# http://msdn.microsoft.com/en-us/library/cc227259%28PROT.13%29.aspx
+
+from tests.tools import *
+from azurelinuxagent.future import httpclient
+from azurelinuxagent.utils.cryptutil import CryptUtil
+
+DATA_FILE = {
+ "version_info": "wire/version_info.xml",
+ "goal_state": "wire/goal_state.xml",
+ "hosting_env": "wire/hosting_env.xml",
+ "shared_config": "wire/shared_config.xml",
+ "certs": "wire/certs.xml",
+ "ext_conf": "wire/ext_conf.xml",
+ "manifest": "wire/manifest.xml",
+ "trans_prv": "wire/trans_prv",
+ "trans_cert": "wire/trans_cert",
+ "test_ext": "ext/sample_ext.zip"
+}
+
+DATA_FILE_NO_EXT = DATA_FILE.copy()
+DATA_FILE_NO_EXT["goal_state"] = "wire/goal_state_no_ext.xml"
+
+DATA_FILE_EXT_NO_SETTINGS = DATA_FILE.copy()
+DATA_FILE_EXT_NO_SETTINGS["ext_conf"] = "wire/ext_conf_no_settings.xml"
+
+DATA_FILE_EXT_NO_PUBLIC = DATA_FILE.copy()
+DATA_FILE_EXT_NO_PUBLIC["ext_conf"] = "wire/ext_conf_no_public.xml"
+
+class WireProtocolData(object):
+ def __init__(self, data_files=DATA_FILE):
+ self.version_info = load_data(data_files.get("version_info"))
+ self.goal_state = load_data(data_files.get("goal_state"))
+ self.hosting_env = load_data(data_files.get("hosting_env"))
+ self.shared_config = load_data(data_files.get("shared_config"))
+ self.certs = load_data(data_files.get("certs"))
+ self.ext_conf = load_data(data_files.get("ext_conf"))
+ self.manifest = load_data(data_files.get("manifest"))
+ self.trans_prv = load_data(data_files.get("trans_prv"))
+ self.trans_cert = load_data(data_files.get("trans_cert"))
+ self.ext = load_bin_data(data_files.get("test_ext"))
+
+ def mock_http_get(self, url, *args, **kwargs):
+ content = None
+ if "versions" in url:
+ content = self.version_info
+ elif "goalstate" in url:
+ content = self.goal_state
+ elif "hostingenvuri" in url:
+ content = self.hosting_env
+ elif "sharedconfiguri" in url:
+ content = self.shared_config
+ elif "certificatesuri" in url:
+ content = self.certs
+ elif "extensionsconfiguri" in url:
+ content = self.ext_conf
+ elif "manifest.xml" in url:
+ content = self.manifest
+ elif "ExampleHandlerLinux" in url:
+ content = self.ext
+ resp = MagicMock()
+ resp.status = httpclient.OK
+ resp.read = Mock(return_value=content)
+ return resp
+ else:
+ raise Exception("Bad url {0}".format(url))
+ resp = MagicMock()
+ resp.status = httpclient.OK
+ resp.read = Mock(return_value=content.encode("utf-8"))
+ return resp
+
+ def mock_crypt_util(self, *args, **kw):
+ #Partially patch instance method of class CryptUtil
+ cryptutil = CryptUtil(*args, **kw)
+ cryptutil.gen_transport_cert = Mock(side_effect=self.mock_gen_trans_cert)
+ return cryptutil
+
+ def mock_gen_trans_cert(self, trans_prv_file, trans_cert_file):
+ with open(trans_prv_file, 'w+') as prv_file:
+ prv_file.write(self.trans_prv)
+
+ with open(trans_cert_file, 'w+') as cert_file:
+ cert_file.write(self.trans_cert)
+
diff --git a/tests/protocol/test_metadata.py b/tests/protocol/test_metadata.py
new file mode 100644
index 0000000..fca1a82
--- /dev/null
+++ b/tests/protocol/test_metadata.py
@@ -0,0 +1,48 @@
+# Copyright 2014 Microsoft Corporation
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+#
+# Requires Python 2.4+ and Openssl 1.0+
+#
+# Implements parts of RFC 2131, 1541, 1497 and
+# http://msdn.microsoft.com/en-us/library/cc227282%28PROT.10%29.aspx
+# http://msdn.microsoft.com/en-us/library/cc227259%28PROT.13%29.aspx
+
+from tests.tools import *
+from tests.protocol.mockmetadata import *
+from azurelinuxagent.utils.restutil import httpclient
+from azurelinuxagent.protocol.metadata import MetadataProtocol
+
+@patch("time.sleep")
+@patch("azurelinuxagent.protocol.metadata.restutil")
+class TestWireProtocolGetters(AgentTestCase):
+ def _test_getters(self, test_data, mock_restutil ,_):
+ mock_restutil.http_get.side_effect = test_data.mock_http_get
+
+ protocol = MetadataProtocol()
+ protocol.detect()
+ protocol.get_vminfo()
+ protocol.get_certs()
+ ext_handlers, etag= protocol.get_ext_handlers()
+ for ext_handler in ext_handlers.extHandlers:
+ protocol.get_ext_handler_pkgs(ext_handler)
+
+ def test_getters(self, *args):
+ test_data = MetadataProtocolData(DATA_FILE)
+ self._test_getters(test_data, *args)
+
+ def test_getters_no(self, *args):
+ test_data = MetadataProtocolData(DATA_FILE_NO_EXT)
+ self._test_getters(test_data, *args)
+
+
diff --git a/tests/test_datacontract.py b/tests/protocol/test_restapi.py
index 4d4edd7..656ecc6 100644
--- a/tests/test_datacontract.py
+++ b/tests/protocol/test_restapi.py
@@ -18,14 +18,13 @@
# http://msdn.microsoft.com/en-us/library/cc227282%28PROT.10%29.aspx
# http://msdn.microsoft.com/en-us/library/cc227259%28PROT.13%29.aspx
-import tests.env
from tests.tools import *
import uuid
import unittest
import os
import shutil
import time
-from azurelinuxagent.protocol.common import *
+from azurelinuxagent.protocol.restapi import *
class SampleDataContract(DataContract):
def __init__(self):
diff --git a/tests/protocol/test_wire.py b/tests/protocol/test_wire.py
new file mode 100644
index 0000000..4c38c13
--- /dev/null
+++ b/tests/protocol/test_wire.py
@@ -0,0 +1,85 @@
+# Copyright 2014 Microsoft Corporation
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+#
+# Requires Python 2.4+ and Openssl 1.0+
+#
+# Implements parts of RFC 2131, 1541, 1497 and
+# http://msdn.microsoft.com/en-us/library/cc227282%28PROT.10%29.aspx
+# http://msdn.microsoft.com/en-us/library/cc227259%28PROT.13%29.aspx
+
+from tests.tools import *
+from tests.protocol.mockwiredata import *
+import uuid
+import unittest
+import os
+import time
+from azurelinuxagent.utils.restutil import httpclient
+from azurelinuxagent.utils.cryptutil import CryptUtil
+from azurelinuxagent.protocol.restapi import *
+from azurelinuxagent.protocol.wire import WireClient, WireProtocol, \
+ TRANSPORT_PRV_FILE_NAME, \
+ TRANSPORT_CERT_FILE_NAME
+
+data_with_bom = b'\xef\xbb\xbfhehe'
+
+@patch("time.sleep")
+@patch("azurelinuxagent.protocol.wire.CryptUtil")
+@patch("azurelinuxagent.protocol.wire.restutil")
+class TestWireProtocolGetters(AgentTestCase):
+
+ def _test_getters(self, test_data, mock_restutil, MockCryptUtil, _):
+ mock_restutil.http_get.side_effect = test_data.mock_http_get
+ MockCryptUtil.side_effect = test_data.mock_crypt_util
+
+ protocol = WireProtocol("foo.bar")
+ protocol.detect()
+ protocol.get_vminfo()
+ protocol.get_certs()
+ ext_handlers, etag = protocol.get_ext_handlers()
+ for ext_handler in ext_handlers.extHandlers:
+ protocol.get_ext_handler_pkgs(ext_handler)
+
+ crt1 = os.path.join(self.tmp_dir,
+ '33B0ABCE4673538650971C10F7D7397E71561F35.crt')
+ crt2 = os.path.join(self.tmp_dir,
+ '4037FBF5F1F3014F99B5D6C7799E9B20E6871CB3.crt')
+ prv2 = os.path.join(self.tmp_dir,
+ '4037FBF5F1F3014F99B5D6C7799E9B20E6871CB3.prv')
+
+ self.assertTrue(os.path.isfile(crt1))
+ self.assertTrue(os.path.isfile(crt2))
+ self.assertTrue(os.path.isfile(prv2))
+
+ def test_getters(self, *args):
+ """Normal case"""
+ test_data = WireProtocolData(DATA_FILE)
+ self._test_getters(test_data, *args)
+
+ def test_getters_no_ext(self, *args):
+ """Provision with agent is not checked"""
+ test_data = WireProtocolData(DATA_FILE_NO_EXT)
+ self._test_getters(test_data, *args)
+
+ def test_getters_ext_no_settings(self, *args):
+ """Extensions without any settings"""
+ test_data = WireProtocolData(DATA_FILE_EXT_NO_SETTINGS)
+ self._test_getters(test_data, *args)
+
+ def test_getters_ext_no_public(self, *args):
+ """Extensions without any public settings"""
+ test_data = WireProtocolData(DATA_FILE_EXT_NO_PUBLIC)
+ self._test_getters(test_data, *args)
+
+if __name__ == '__main__':
+ unittest.main()
diff --git a/tests/run_all.sh b/tests/run_all.sh
deleted file mode 100755
index 9e88c46..0000000
--- a/tests/run_all.sh
+++ /dev/null
@@ -1,35 +0,0 @@
-#!/bin/bash
-#
-# This script is used to set up a test env for extensions
-#
-# 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.
-#
-
-script=$(dirname $0)
-root=$script
-cd $root
-root=`pwd`
-
-echo "Run unit test:"
-tests=`ls test_*.py | sed -e 's/\.py//'`
-for test in $tests ; do
- echo $test
-done
-
-if [ "$1" == "-c" ] ; then
- echo $tests | xargs coverage run -m unittest
-else
- echo $tests | xargs python -m unittest
-fi
diff --git a/tests/test.crt b/tests/test.crt
deleted file mode 100644
index e87c5f9..0000000
--- a/tests/test.crt
+++ /dev/null
@@ -1,17 +0,0 @@
-Bag Attributes: <Empty Attributes>
-subject=/C=ab/ST=ab/L=ab/O=ab/OU=ab/CN=ab/emailAddress=ab
-issuer=/C=ab/ST=ab/L=ab/O=ab/OU=ab/CN=ab/emailAddress=ab
------BEGIN CERTIFICATE-----
-MIICOTCCAaICCQD7F0nb+GtpcTANBgkqhkiG9w0BAQsFADBhMQswCQYDVQQGEwJh
-YjELMAkGA1UECAwCYWIxCzAJBgNVBAcMAmFiMQswCQYDVQQKDAJhYjELMAkGA1UE
-CwwCYWIxCzAJBgNVBAMMAmFiMREwDwYJKoZIhvcNAQkBFgJhYjAeFw0xNDA4MDUw
-ODIwNDZaFw0xNTA4MDUwODIwNDZaMGExCzAJBgNVBAYTAmFiMQswCQYDVQQIDAJh
-YjELMAkGA1UEBwwCYWIxCzAJBgNVBAoMAmFiMQswCQYDVQQLDAJhYjELMAkGA1UE
-AwwCYWIxETAPBgkqhkiG9w0BCQEWAmFiMIGfMA0GCSqGSIb3DQEBAQUAA4GNADCB
-iQKBgQC4Vugyj4uAKGYHW/D1eAg1DmLAv01e+9I0zIi8HzJxP87MXmS8EdG5SEzR
-N6tfQQie76JBSTYI4ngTaVCKx5dVT93LiWxLV193Q3vs/HtwwH1fLq0rAKUhREQ6
-+CsRGNyeVfJkNsxAvNvQkectnYuOtcDxX5n/25eWAofobxVbSQIDAQABMA0GCSqG
-SIb3DQEBCwUAA4GBAF20gkq/DeUSXkZA+jjmmbCPioB3KL63GpoTXfP65d6yU4xZ
-TlMoLkqGKe3WoXmhjaTOssulgDAGA24IeWy/u7luH+oHdZEmEufFhj4M7tQ1pAhN
-CT8JCL2dI3F76HD6ZutTOkwRar3PYk5q7RsSJdAemtnwVpgp+RBMtbmct7MQ
------END CERTIFICATE-----
diff --git a/tests/test.prv b/tests/test.prv
deleted file mode 100644
index 9d6ab87..0000000
--- a/tests/test.prv
+++ /dev/null
@@ -1,15 +0,0 @@
------BEGIN RSA PRIVATE KEY-----
-MIICXAIBAAKBgQC4Vugyj4uAKGYHW/D1eAg1DmLAv01e+9I0zIi8HzJxP87MXmS8
-EdG5SEzRN6tfQQie76JBSTYI4ngTaVCKx5dVT93LiWxLV193Q3vs/HtwwH1fLq0r
-AKUhREQ6+CsRGNyeVfJkNsxAvNvQkectnYuOtcDxX5n/25eWAofobxVbSQIDAQAB
-AoGAIakE506c238E+m0Id9o+LWn+EFIeT6zN+oQqp6dOr61GFr1ZyZm7YQjZtg5j
-RZZ7e4Iob6Fts3ufD3RYl67QbBzRwsKwI7sAmzdCmqkopY2H6xv421cEGjkqZIJV
-2Xyp9Idji6GfUB6+t1SZDOssbZx3SUkyim0hixK2HCJT4u0CQQDw6rNLZwEmwuhY
-z1jSERyeTtIcRJ47+Y79tX2xmkyKxZ2Kf28V3Fw/6biCIlmuvxHNhlLijimOME7/
-rkqDiscnAkEAw+FpkM96xLlDCqNL2AcNxVnmNyO0Boxw0AKrogfcnDh6S3rD5tZQ
-IdcIAsEYNjhEJ+/hVCByIUArC885PTzQDwJBAMaDfm3ZWHeKD05uvG+MLhq8NCGa
-4Q/mWU7xZ7sau4t1vpTK4MwQoesAOUrx5xg41QCXeGC6Z7+ESvQft8Kgbe0CQAkS
-OExPf3T6y2MDuvBvKzEXf7TP/3dKK7NGXGJtkMbfSrKSJd5b0GwwxBs0jAV+x5E9
-56Z4tjBaA2RRnWn7lfsCQA5SWuDMtlOzyWir09fparnnRL1JFvOwDAHTE0iwS8dO
-UFHIIw4nqqUYuHb+r/eyRzVtokJ9bSPZOjtTWSVL4W4=
------END RSA PRIVATE KEY-----
diff --git a/tests/test_certificates.py b/tests/test_certificates.py
deleted file mode 100644
index 18f28c4..0000000
--- a/tests/test_certificates.py
+++ /dev/null
@@ -1,199 +0,0 @@
-# Copyright 2014 Microsoft Corporation
-#
-# Licensed under the Apache License, Version 2.0 (the "License");
-# you may not use this file except in compliance with the License.
-# You may obtain a copy of the License at
-#
-# http://www.apache.org/licenses/LICENSE-2.0
-#
-# Unless required by applicable law or agreed to in writing, software
-# distributed under the License is distributed on an "AS IS" BASIS,
-# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-# See the License for the specific language governing permissions and
-# limitations under the License.
-#
-# Requires Python 2.4+ and Openssl 1.0+
-#
-# Implements parts of RFC 2131, 1541, 1497 and
-# http://msdn.microsoft.com/en-us/library/cc227282%28PROT.10%29.aspx
-# http://msdn.microsoft.com/en-us/library/cc227259%28PROT.13%29.aspx
-
-import tests.env
-from .tools import *
-import uuid
-import unittest
-import os
-import json
-import azurelinuxagent.utils.fileutil as fileutil
-import azurelinuxagent.protocol.v1 as v1
-
-certs_sample=u"""\
-<?xml version="1.0" encoding="utf-8"?>
-<CertificateFile xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:noNamespaceSchemaLocation="certificates10.xsd">
- <Version>2012-11-30</Version>
- <Incarnation>12</Incarnation>
- <Format>Pkcs7BlobWithPfxContents</Format>
- <Data>MIINswYJKoZIhvcNAQcDoIINpDCCDaACAQIxggEwMIIBLAIBAoAUvyL+x6GkZXog
-QNfsXRZAdD9lc7IwDQYJKoZIhvcNAQEBBQAEggEArhMPepD/RqwdPcHEVqvrdZid
-72vXrOCuacRBhwlCGrNlg8oI+vbqmT6CSv6thDpet31ALUzsI4uQHq1EVfV1+pXy
-NlYD1CKhBCoJxs2fSPU4rc8fv0qs5JAjnbtW7lhnrqFrXYcyBYjpURKfa9qMYBmj
-NdijN+1T4E5qjxPr7zK5Dalp7Cgp9P2diH4Nax2nixotfek3MrEFBaiiegDd+7tE
-ux685GWYPqB5Fn4OsDkkYOdb0OE2qzLRrnlCIiBCt8VubWH3kMEmSCxBwSJupmQ8
-sxCWk+sBPQ9gJSt2sIqfx/61F8Lpu6WzP+ZOnMLTUn2wLU/d1FN85HXmnQALzTCC
-DGUGCSqGSIb3DQEHATAUBggqhkiG9w0DBwQIbEcBfddWPv+AggxAAOAt/kCXiffe
-GeJG0P2K9Q18XZS6Rz7Xcz+Kp2PVgqHKRpPjjmB2ufsRO0pM4z/qkHTOdpfacB4h
-gz912D9U04hC8mt0fqGNTvRNAFVFLsmo7KXc/a8vfZNrGWEnYn7y1WfP52pqA/Ei
-SNFf0NVtMyqg5Gx+hZ/NpWAE5vcmRRdoYyWeg13lhlW96QUxf/W7vY/D5KpAGACI
-ok79/XI4eJkbq3Dps0oO/difNcvdkE74EU/GPuL68yR0CdzzafbLxzV+B43TBRgP
-jH1hCdRqaspjAaZL5LGfp1QUM8HZIKHuTze/+4dWzS1XR3/ix9q/2QFI7YCuXpuE
-un3AFYXE4QX/6kcPklZwh9FqjSie3I5HtC1vczqYVjqT4oHrs8ktkZ7oAzeXaXTF
-k6+JQNNa/IyJw24I1MR77q7HlHSSfhXX5cFjVCd/+SiA4HJQjJgeIuXZ+dXmSPdL
-9xLbDbtppifFyNaXdlSzcsvepKy0WLF49RmbL7Bnd46ce/gdQ6Midwi2MTnUtapu
-tHmu/iJtaUpwXXC0B93PHfAk7Y3SgeY4tl/gKzn9/x5SPAcHiNRtOsNBU8ZThzos
-Wh41xMLZavmX8Yfm/XWtl4eU6xfhcRAbJQx7E1ymGEt7xGqyPV7hjqhoB9i3oR5N
-itxHgf1+jw/cr7hob+Trd1hFqZO6ePMyWpqUg97G2ThJvWx6cv+KRtTlVA6/r/UH
-gRGBArJKBlLpXO6dAHFztT3Y6DFThrus4RItcfA8rltfQcRm8d0nPb4lCa5kRbCx
-iudq3djWtTIe64sfk8jsc6ahWYSovM+NmhbpxEUbZVWLVEcHAYOeMbKgXSu5sxNO
-JZNeFdzZqDRRY9fGjYNS7DdNOmrMmWKH+KXuMCItpNZsZS/3W7QxAo3ugYLdUylU
-Zg8H/BjUGZCGn1rEBAuQX78m0SZ1xHlgHSwJIOmxOJUDHLPHtThfbELY9ec14yi5
-so1aQwhhfhPvF+xuXBrVeTAfhFNYkf2uxcEp7+tgFAc5W0QfT9SBn5vSvIxv+dT4
-7B2Pg1l/zjdsM74g58lmRJeDoz4psAq+Uk7n3ImBhIku9qX632Q1hanjC8D4xM4W
-sI/W0ADCuAbY7LmwMpAMdrGg//SJUnBftlom7C9VA3EVf8Eo+OZH9hze+gIgUq+E
-iEUL5M4vOHK2ttsYrSkAt8MZzjQiTlDr1yzcg8fDIrqEAi5arjTPz0n2s0NFptNW
-lRD+Xz6pCXrnRgR8YSWpxvq3EWSJbZkSEk/eOmah22sFnnBZpDqn9+UArAznXrRi
-nYK9w38aMGPKM39ymG8kcbY7jmDZlRgGs2ab0Fdj1jl3CRo5IUatkOJwCEMd/tkB
-eXLQ8hspJhpFnVNReX0oithVZir+j36epk9Yn8d1l+YlKmuynjunKl9fhmoq5Q6i
-DFzdYpqBV+x9nVhnmPfGyrOkXvGL0X6vmXAEif/4JoOW4IZpyXjgn+VoCJUoae5J
-Djl45Bcc2Phrn4HW4Gg/+pIwTFqqZZ2jFrznNdgeIxTGjBrVsyJUeO3BHI0mVLaq
-jtjhTshYCI7mXOis9W3ic0RwE8rgdDXOYKHhLVw9c4094P/43utSVXE7UzbEhhLE
-Ngb4H5UGrQmPTNbq40tMUMUCej3zIKuVOvamzeE0IwLhkjNrvKhCG1EUhX4uoJKu
-DQ++3KVIVeYSv3+78Jfw9F3usAXxX1ICU74/La5DUNjU7DVodLDvCAy5y1jxP3Ic
-If6m7aBYVjFSQAcD8PZPeIEl9W4ZnbwyBfSDd11P2a8JcZ7N99GiiH3yS1QgJnAO
-g9XAgjT4Gcn7k4lHPHLULgijfiDSvt94Ga4/hse0F0akeZslVN/bygyib7x7Lzmq
-JkepRianrvKHbatuxvcajt/d+dxCnr32Q1qCEc5fcgDsjvviRL2tKR0qhuYjn1zR
-Vk/fRtYOmlaGBVzUXcjLRAg3gC9+Gy8KvXIDrnHxD+9Ob+DUP9fgbKqMeOzKcCK8
-NSfSQ+tQjBYD5Ku4zAPUQJoRGgx43vXzcl2Z2i3E2otpoH82Kx8S9WlVEUlTtBjQ
-QIGM5aR0QUNt8z34t2KWRA8SpP54VzBmEPdwLnzna+PkrGKsKiHVn4K+HfjDp1uW
-xyO8VjrolAOYosTPXMpNp2u/FoFxaAPTa/TvmKc0kQ3ED9/sGLS2twDnEccvHP+9
-zzrnzzN3T2CWuXveDpuyuAty3EoAid1nuC86WakSaAZoa8H2QoRgsrkkBCq+K/yl
-4FO9wuP+ksZoVq3mEDQ9qv6H4JJEWurfkws3OqrA5gENcLmSUkZie4oqAxeOD4Hh
-Zx4ckG5egQYr0PnOd2r7ZbIizv3MKT4RBrfOzrE6cvm9bJEzNWXdDyIxZ/kuoLA6
-zX7gGLdGhg7dqzKqnGtopLAsyM1b/utRtWxOTGO9K9lRxyX82oCVT9Yw0DwwA+cH
-Gutg1w7JHrIAYEtY0ezHgxhqMGuuTyJMX9Vr0D+9DdMeBK7hVOeSnxkaQ0f9HvF6
-0XI/2OTIoBSCBpUXjpgsYt7m7n2rFJGJmtqgLAosCAkacHnHLwX0EnzBw3sdDU6Q
-jFXUWIDd5xUsNkFDCbspLMFs22hjNI6f/GREwd23Q4ujF8pUIcxcfbs2myjbK45s
-tsn/jrkxmKRgwCIeN/H7CM+4GXSkEGLWbiGCxWzWt9wW1F4M7NW9nho3D1Pi2LBL
-1ByTmjfo/9u9haWrp53enDLJJbcaslfe+zvo3J70Nnzu3m3oJ3dmUxgJIstG10g3
-lhpUm1ynvx04IFkYJ3kr/QHG/xGS+yh/pMZlwcUSpjEgYFmjFHU4A1Ng4LGI4lnw
-5wisay4J884xmDgGfK0sdVQyW5rExIg63yYXp2GskRdDdwvWlFUzPzGgCNXQU96A
-ljZfjs2u4IiVCC3uVsNbGqCeSdAl9HC5xKuPNbw5yTxPkeRL1ouSdkBy7rvdFaFf
-dMPw6sBRNW8ZFInlgOncR3+xT/rZxru87LCq+3hRN3kw3hvFldrW2QzZSksO759b
-pJEP+4fxuG96Wq25fRmzHzE0bdJ+2qF3fp/hy4oRi+eVPa0vHdtkymE4OUFWftb6
-+P++JVOzZ4ZxYA8zyUoJb0YCaxL+Jp/QqiUiH8WZVmYZmswqR48sUUKr7TIvpNbY
-6jEH6F7KiZCoWfKH12tUC69iRYx3UT/4Bmsgi3S4yUxfieYRMIwihtpP4i0O+OjB
-/DPbb13qj8ZSfXJ+jmF2SRFfFG+2T7NJqm09JvT9UcslVd+vpUySNe9UAlpcvNGZ
-2+j180ZU7YAgpwdVwdvqiJxkeVtAsIeqAvIXMFm1PDe7FJB0BiSVZdihB6cjnKBI
-dv7Lc1tI2sQe7QSfk+gtionLrEnto+aXF5uVM5LMKi3gLElz7oXEIhn54OeEciB1
-cEmyX3Kb4HMRDMHyJxqJXwxm88RgC6RekoPvstu+AfX/NgSpRj5beaj9XkweJT3H
-rKWhkjq4Ghsn1LoodxluMMHd61m47JyoqIP9PBKoW+Na0VUKIVHw9e9YeW0nY1Zi
-5qFA/pHPAt9AbEilRay6NEm8P7TTlNo216amc8byPXanoNrqBYZQHhZ93A4yl6jy
-RdpYskMivT+Sh1nhZAioKqqTZ3HiFR8hFGspAt5gJc4WLYevmxSicGa6AMyhrkvG
-rvOSdjY6JY/NkxtcgeycBX5MLF7uDbhUeqittvmlcrVN6+V+2HIbCCrvtow9pcX9
-EkaaNttj5M0RzjQxogCG+S5TkhCy04YvKIkaGJFi8xO3icdlxgOrKD8lhtbf4UpR
-cDuytl70JD95mSUWL53UYjeRf9OsLRJMHQOpS02japkMwCb/ngMCQuUXA8hGkBZL
-Xw7RwwPuM1Lx8edMXn5C0E8UK5e0QmI/dVIl2aglXk2oBMBJbnyrbfUPm462SG6u
-ke4gQKFmVy2rKICqSkh2DMr0NzeYEUjZ6KbmQcV7sKiFxQ0/ROk8eqkYYxGWUWJv
-ylPF1OTLH0AIbGlFPLQO4lMPh05yznZTac4tmowADSHY9RCxad1BjBeine2pj48D
-u36OnnuQIsedxt5YC+h1bs+mIvwMVsnMLidse38M/RayCDitEBvL0KeG3vWYzaAL
-h0FCZGOW0ilVk8tTF5+XWtsQEp1PpclvkcBMkU3DtBUnlmPSKNfJT0iRr2T0sVW1
-h+249Wj0Bw==
-</Data>
-</CertificateFile>
-"""
-
-transport_cert=u"""\
------BEGIN CERTIFICATE-----
-MIIDBzCCAe+gAwIBAgIJANujJuVt5eC8MA0GCSqGSIb3DQEBCwUAMBkxFzAVBgNV
-BAMMDkxpbnV4VHJhbnNwb3J0MCAXDTE0MTAyNDA3MjgwN1oYDzIxMDQwNzEyMDcy
-ODA3WjAZMRcwFQYDVQQDDA5MaW51eFRyYW5zcG9ydDCCASIwDQYJKoZIhvcNAQEB
-BQADggEPADCCAQoCggEBANPcJAkd6V5NeogSKjIeTXOWC5xzKTyuJPt4YZMVSosU
-0lI6a0wHp+g2fP22zrVswW+QJz6AVWojIEqLQup3WyCXZTv8RUblHnIjkvX/+J/G
-aLmz0G5JzZIpELL2C8IfQLH2IiPlK9LOQH00W74WFcK3QqcJ6Kw8GcVaeSXT1r7X
-QcGMqEjcWJkpKLoMJv3LMufE+JMdbXDUGY+Ps7Zicu8KXvBPaKVsc6H2jrqBS8et
-jXbzLyrezTUDz45rmyRJzCO5Sk2pohuYg73wUykAUPVxd7L8WnSyqz1v4zrObqnw
-BAyor67JR/hjTBfjFOvd8qFGonfiv2Vnz9XsYFTZsXECAwEAAaNQME4wHQYDVR0O
-BBYEFL8i/sehpGV6IEDX7F0WQHQ/ZXOyMB8GA1UdIwQYMBaAFL8i/sehpGV6IEDX
-7F0WQHQ/ZXOyMAwGA1UdEwQFMAMBAf8wDQYJKoZIhvcNAQELBQADggEBAMPLrimT
-Gptu5pLRHPT8OFRN+skNSkepYaUaJuq6cSKxLumSYkD8++rohu+1+a7t1YNjjNSJ
-8ohRAynRJ7aRqwBmyX2OPLRpOfyRZwR0rcFfAMORm/jOE6WBdqgYD2L2b+tZplGt
-/QqgQzebaekXh/032FK4c74Zg5r3R3tfNSUMG6nLauWzYHbQ5SCdkuQwV0ehGqh5
-VF1AOdmz4CC2237BNznDFQhkeU0LrqqAoE/hv5ih7klJKZdS88rOYEnVJsFFJb0g
-qaycXjOm5Khgl4hKrd+DBD/qj4IVVzsmdpFli72k6WLBHGOXusUGo/3isci2iAIt
-DsfY6XGSEIhZnA4=
------END CERTIFICATE-----
-"""
-
-transport_private=u"""\
------BEGIN PRIVATE KEY-----
-MIIEvgIBADANBgkqhkiG9w0BAQEFAASCBKgwggSkAgEAAoIBAQDT3CQJHeleTXqI
-EioyHk1zlguccyk8riT7eGGTFUqLFNJSOmtMB6foNnz9ts61bMFvkCc+gFVqIyBK
-i0Lqd1sgl2U7/EVG5R5yI5L1//ifxmi5s9BuSc2SKRCy9gvCH0Cx9iIj5SvSzkB9
-NFu+FhXCt0KnCeisPBnFWnkl09a+10HBjKhI3FiZKSi6DCb9yzLnxPiTHW1w1BmP
-j7O2YnLvCl7wT2ilbHOh9o66gUvHrY128y8q3s01A8+Oa5skScwjuUpNqaIbmIO9
-8FMpAFD1cXey/Fp0sqs9b+M6zm6p8AQMqK+uyUf4Y0wX4xTr3fKhRqJ34r9lZ8/V
-7GBU2bFxAgMBAAECggEBAM4hsfog3VAAyIieS+npq+gbhH6bWfMNaTQ3g5CNNbMu
-9hhFeOJHzKnWYjSlamgBQhAfTN+2E+Up+iAtcVUZ/lMumrQLlwgMo1vgmvu5Kxmh
-/YE5oEG+k0JzrCjD1trwd4zvc3ZDYyk/vmVTzTOc311N248UyArUiyqHBbq1a4rP
-tJhCLn2c4S7flXGF0MDVGZyV9V7J8N8leq/dRGMB027Li21T+B4mPHXa6b8tpRPL
-4vc8sHoUJDa2/+mFDJ2XbZfmlgd3MmIPlRn1VWoW7mxgT/AObsPl7LuQx7+t80Wx
-hIMjuKUHRACQSLwHxJ3SQRFWp4xbztnXSRXYuHTscLUCgYEA//Uu0qIm/FgC45yG
-nXtoax4+7UXhxrsWDEkbtL6RQ0TSTiwaaI6RSQcjrKDVSo/xo4ZySTYcRgp5GKlI
-CrWyNM+UnIzTNbZOtvSIAfjxYxMsq1vwpTlOB5/g+cMukeGg39yUlrjVNoFpv4i6
-9t4yYuEaF4Vww0FDd2nNKhhW648CgYEA0+UYH6TKu03zDXqFpwf4DP2VoSo8OgfQ
-eN93lpFNyjrfzvxDZkGF+7M/ebyYuI6hFplVMu6BpgpFP7UVJpW0Hn/sXkTq7F1Q
-rTJTtkTp2+uxQVP/PzSOqK0Twi5ifkfoEOkPkNNtTiXzwCW6Qmmcvln2u893pyR5
-gqo5BHR7Ev8CgYAb7bXpN9ZHLJdMHLU3k9Kl9YvqOfjTxXA3cPa79xtEmsrTys4q
-4HuL22KSII6Fb0VvkWkBAg19uwDRpw78VC0YxBm0J02Yi8b1AaOhi3dTVzFFlWeh
-r6oK/PAAcMKxGkyCgMAZ3hstsltGkfXMoBwhW+yL6nyOYZ2p9vpzAGrjkwKBgQDF
-0huzbyXVt/AxpTEhv07U0enfjI6tnp4COp5q8zyskEph8yD5VjK/yZh5DpmFs6Kw
-dnYUFpbzbKM51tToMNr3nnYNjEnGYVfwWgvNHok1x9S0KLcjSu3ki7DmmGdbfcYq
-A2uEyd5CFyx5Nr+tQOwUyeiPbiFG6caHNmQExLoiAQKBgFPy9H8///xsadYmZ18k
-r77R2CvU7ArxlLfp9dr19aGYKvHvnpsY6EuChkWfy8Xjqn3ogzgrHz/rn3mlGUpK
-vbtwtsknAHtTbotXJwfaBZv2RGgGRr3DzNo6ll2Aez0lNblZFXq132h7+y5iLvar
-4euORaD/fuM4UPlR5mN+bypU
------END PRIVATE KEY-----
-"""
-
-def MockGetOpensslCmd():
- return 'openssl'
-
-class TestCertificates(unittest.TestCase):
-
- def test_certificates(self):
- crt1 = '/tmp/33B0ABCE4673538650971C10F7D7397E71561F35.crt'
- crt2 = '/tmp/4037FBF5F1F3014F99B5D6C7799E9B20E6871CB3.crt'
- prv2 = '/tmp/4037FBF5F1F3014F99B5D6C7799E9B20E6871CB3.prv'
- os.chdir('/tmp')
- if os.path.isfile(crt1):
- os.remove(crt1)
- if os.path.isfile(crt2):
- os.remove(crt2)
- if os.path.isfile(prv2):
- os.remove(prv2)
- fileutil.write_file(os.path.join('/tmp', "TransportCert.pem"),
- transport_cert)
- fileutil.write_file(os.path.join('/tmp', "TransportPrivate.pem"),
- transport_private)
- client = v1.WireClient("http://foo.bar")
- config = v1.Certificates(client, certs_sample)
- self.assertNotEquals(None, config)
- self.assertTrue(os.path.isfile(crt1))
- self.assertTrue(os.path.isfile(crt2))
- self.assertTrue(os.path.isfile(prv2))
-
- self.assertNotEquals(0, len(config.cert_list.certificates))
- cert = config.cert_list.certificates[0]
- self.assertNotEquals(None, cert.thumbprint)
-
-if __name__ == '__main__':
- unittest.main()
diff --git a/tests/test_conf.py b/tests/test_conf.py
deleted file mode 100644
index 204e4f5..0000000
--- a/tests/test_conf.py
+++ /dev/null
@@ -1,69 +0,0 @@
-# Copyright 2014 Microsoft Corporation
-#
-# Licensed under the Apache License, Version 2.0 (the "License");
-# you may not use this file except in compliance with the License.
-# You may obtain a copy of the License at
-#
-# http://www.apache.org/licenses/LICENSE-2.0
-#
-# Unless required by applicable law or agreed to in writing, software
-# distributed under the License is distributed on an "AS IS" BASIS,
-# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-# See the License for the specific language governing permissions and
-# limitations under the License.
-#
-# Requires Python 2.4+ and Openssl 1.0+
-#
-# Implements parts of RFC 2131, 1541, 1497 and
-# http://msdn.microsoft.com/en-us/library/cc227282%28PROT.10%29.aspx
-# http://msdn.microsoft.com/en-us/library/cc227259%28PROT.13%29.aspx
-
-import os
-import tests.env
-import uuid
-import unittest
-import tests.tools as tools
-import azurelinuxagent.utils.fileutil as fileutil
-import azurelinuxagent.conf as conf
-from azurelinuxagent.exception import *
-
-TestConf="""\
-#
-# This is comment
-#
-foo.bar.switch=y
-foo.bar.switch2=n
-foo.bar.str=foobar
-foo.bar.int=300
-
-"""
-
-class TestConfiguration(unittest.TestCase):
- def test_parse_conf(self):
- config = conf.ConfigurationProvider()
- config.load(TestConf)
- self.assertEquals(True, config.get_switch("foo.bar.switch"))
- self.assertEquals(False, config.get_switch("foo.bar.switch2"))
- self.assertEquals(False, config.get_switch("foo.bar.switch3"))
- self.assertEquals(True, config.get_switch("foo.bar.switch4", True))
- self.assertEquals("foobar", config.get("foo.bar.str"))
- self.assertEquals("foobar1", config.get("foo.bar.str1", "foobar1"))
- self.assertEquals(300, config.get_int("foo.bar.int"))
- self.assertEquals(-1, config.get_int("foo.bar.int2"))
- self.assertEquals(-1, config.get_int("foo.bar.str"))
-
- def test_parse_malformed_conf(self):
- config = conf.ConfigurationProvider()
- self.assertRaises(AgentConfigError, config.load, None)
-
- def test_load_conf_file(self):
- with open('/tmp/test_conf', 'w') as F:
- F.write(TestConf)
- F.close()
-
- config = conf.ConfigurationProvider()
- conf.load_conf('/tmp/test_conf', conf=config)
- self.assertEquals(True, config.get_switch("foo.bar.switch"), False)
-
-if __name__ == '__main__':
- unittest.main()
diff --git a/tests/test_deprovision.py b/tests/test_deprovision.py
deleted file mode 100644
index 8bad6b9..0000000
--- a/tests/test_deprovision.py
+++ /dev/null
@@ -1,54 +0,0 @@
-# Copyright 2014 Microsoft Corporation
-#
-# Licensed under the Apache License, Version 2.0 (the "License");
-# you may not use this file except in compliance with the License.
-# You may obtain a copy of the License at
-#
-# http://www.apache.org/licenses/LICENSE-2.0
-#
-# Unless required by applicable law or agreed to in writing, software
-# distributed under the License is distributed on an "AS IS" BASIS,
-# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-# See the License for the specific language governing permissions and
-# limitations under the License.
-#
-# Requires Python 2.4+ and Openssl 1.0+
-#
-# Implements parts of RFC 2131, 1541, 1497 and
-# http://msdn.microsoft.com/en-us/library/cc227282%28PROT.10%29.aspx
-# http://msdn.microsoft.com/en-us/library/cc227259%28PROT.13%29.aspx
-
-import tests.env
-from tests.tools import *
-import unittest
-import azurelinuxagent.distro.default.deprovision as deprovision_handler
-
-def MockAction(param):
- #print param
- pass
-
-def MockSetup(self, deluser):
- warnings = ["Print warning to console"]
- actions = [
- deprovision_handler.DeprovisionAction(MockAction, ['Take action'])
- ]
- return warnings, actions
-
-class TestDeprovisionHandler(unittest.TestCase):
- def test_setup(self):
- handler = deprovision_handler.DeprovisionHandler()
- warnings, actions = handler.setup(False)
- self.assertNotEquals(None, warnings)
- self.assertNotEquals(0, len(warnings))
- self.assertNotEquals(None, actions)
- self.assertNotEquals(0, len(actions))
- self.assertEquals(deprovision_handler.DeprovisionAction, type(actions[0]))
-
-
- @mock(deprovision_handler.DeprovisionHandler, 'setup', MockSetup)
- def test_deprovision(self):
- handler = deprovision_handler.DeprovisionHandler()
- handler.deprovision(force=True)
-
-if __name__ == '__main__':
- unittest.main()
diff --git a/tests/test_dhcp.py b/tests/test_dhcp.py
deleted file mode 100644
index 2206325..0000000
--- a/tests/test_dhcp.py
+++ /dev/null
@@ -1,68 +0,0 @@
-# Copyright 2014 Microsoft Corporation
-#
-# Licensed under the Apache License, Version 2.0 (the "License");
-# you may not use this file except in compliance with the License.
-# You may obtain a copy of the License at
-#
-# http://www.apache.org/licenses/LICENSE-2.0
-#
-# Unless required by applicable law or agreed to in writing, software
-# distributed under the License is distributed on an "AS IS" BASIS,
-# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-# See the License for the specific language governing permissions and
-# limitations under the License.
-#
-# Requires Python 2.4+ and Openssl 1.0+
-#
-# Implements parts of RFC 2131, 1541, 1497 and
-# http://msdn.microsoft.com/en-us/library/cc227282%28PROT.10%29.aspx
-# http://msdn.microsoft.com/en-us/library/cc227259%28PROT.13%29.aspx
-
-import tests.env as env
-from tests.tools import *
-import uuid
-import unittest
-import os
-import json
-import azurelinuxagent.utils.fileutil as fileutil
-import azurelinuxagent.distro.default.dhcp as dhcp_handler
-
-SampleDhcpResponse = None
-with open(os.path.join(env.test_root, "dhcp"), 'rb') as F:
- SampleDhcpResponse = F.read()
-
-mock_socket_send = MockFunc('socket_send', SampleDhcpResponse)
-mock_gen_trans_id = MockFunc('gen_trans_id', "\xC6\xAA\xD1\x5D")
-mock_get_mac_addr = MockFunc('get_mac_addr', "\x00\x15\x5D\x38\xAA\x38")
-mock_send_dhcp_failed = MockFunc(retval=None)
-
-class TestdhcpHandler(unittest.TestCase):
-
- def test_build_dhcp_req(self):
- req = dhcp_handler.build_dhcp_request(mock_get_mac_addr())
- self.assertNotEquals(None, req)
-
- @mock(dhcp_handler, "gen_trans_id", mock_gen_trans_id)
- @mock(dhcp_handler, "socket_send", mock_socket_send)
- def test_send_dhcp_req(self):
- req = dhcp_handler.build_dhcp_request(mock_get_mac_addr())
- resp = dhcp_handler.send_dhcp_request(req)
- self.assertNotEquals(None, resp)
-
- @mock(dhcp_handler, "send_dhcp_request", mock_send_dhcp_failed)
- def test_send_dhcp_failed(self):
- dhcp = dhcp_handler.DhcpHandler()
- dhcp.probe()
-
- @mock(dhcp_handler, "socket_send", mock_socket_send)
- @mock(dhcp_handler, "gen_trans_id", mock_gen_trans_id)
- @mock(dhcp_handler.OSUTIL, "get_mac_addr", mock_get_mac_addr)
- @mock(dhcp_handler.fileutil, "write_file", MockFunc())
- def test_handle_dhcp(self):
- dh = dhcp_handler.DhcpHandler()
- dh.probe()
- self.assertEquals("10.62.144.1", dh.gateway)
- self.assertEquals("10.62.144.140", dh.endpoint)
-
-if __name__ == '__main__':
- unittest.main()
diff --git a/tests/test_distroLoader.py b/tests/test_distroLoader.py
deleted file mode 100644
index 16987c5..0000000
--- a/tests/test_distroLoader.py
+++ /dev/null
@@ -1,42 +0,0 @@
-# Copyright 2014 Microsoft Corporation
-#
-# Licensed under the Apache License, Version 2.0 (the "License");
-# you may not use this file except in compliance with the License.
-# You may obtain a copy of the License at
-#
-# http://www.apache.org/licenses/LICENSE-2.0
-#
-# Unless required by applicable law or agreed to in writing, software
-# distributed under the License is distributed on an "AS IS" BASIS,
-# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-# See the License for the specific language governing permissions and
-# limitations under the License.
-#
-# Requires Python 2.4+ and Openssl 1.0+
-#
-# Implements parts of RFC 2131, 1541, 1497 and
-# http://msdn.microsoft.com/en-us/library/cc227282%28PROT.10%29.aspx
-# http://msdn.microsoft.com/en-us/library/cc227259%28PROT.13%29.aspx
-
-import tests.env
-from tests.tools import *
-import unittest
-from azurelinuxagent.utils.osutil import OSUTIL, OSUtilError
-from azurelinuxagent.handler import HANDLERS
-import azurelinuxagent.distro.default.osutil as osutil
-
-class TestDistroLoader(unittest.TestCase):
- def test_loader(self):
- self.assertNotEquals(osutil.DefaultOSUtil, type(OSUTIL))
- self.assertNotEquals(None, HANDLERS.init_handler)
- self.assertNotEquals(None, HANDLERS.main_handler)
- self.assertNotEquals(None, HANDLERS.scvmm_handler)
- self.assertNotEquals(None, HANDLERS.dhcp_handler)
- self.assertNotEquals(None, HANDLERS.env_handler)
- self.assertNotEquals(None, HANDLERS.provision_handler)
- self.assertNotEquals(None, HANDLERS.resource_disk_handler)
- self.assertNotEquals(None, HANDLERS.env_handler)
- self.assertNotEquals(None, HANDLERS.deprovision_handler)
-
-if __name__ == '__main__':
- unittest.main()
diff --git a/tests/test_envmon.py b/tests/test_envmon.py
deleted file mode 100644
index 74b61ee..0000000
--- a/tests/test_envmon.py
+++ /dev/null
@@ -1,52 +0,0 @@
-# Copyright 2014 Microsoft Corporation
-#
-# Licensed under the Apache License, Version 2.0 (the "License");
-# you may not use this file except in compliance with the License.
-# You may obtain a copy of the License at
-#
-# http://www.apache.org/licenses/LICENSE-2.0
-#
-# Unless required by applicable law or agreed to in writing, software
-# distributed under the License is distributed on an "AS IS" BASIS,
-# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-# See the License for the specific language governing permissions and
-# limitations under the License.
-#
-# Requires Python 2.4+ and Openssl 1.0+
-#
-# Implements parts of RFC 2131, 1541, 1497 and
-# http://msdn.microsoft.com/en-us/library/cc227282%28PROT.10%29.aspx
-# http://msdn.microsoft.com/en-us/library/cc227259%28PROT.13%29.aspx
-
-import tests.env
-from tests.tools import *
-import unittest
-import time
-from azurelinuxagent.future import text
-from azurelinuxagent.utils.osutil import OSUTIL
-from azurelinuxagent.distro.default.env import EnvMonitor
-
-class MockDhcpHandler(object):
- def conf_routes(self):
- pass
-
-def mock_get_dhcp_pid():
- return "1234"
-
-def mock_dhcp_pid_change():
- return text(time.time())
-
-class TestEnvMonitor(unittest.TestCase):
-
- @mock(OSUTIL, 'get_dhcp_pid', mock_get_dhcp_pid)
- def test_dhcp_pid_not_change(self):
- monitor = EnvMonitor(MockDhcpHandler())
- monitor.handle_dhclient_restart()
-
- @mock(OSUTIL, 'get_dhcp_pid', mock_dhcp_pid_change)
- def test_dhcp_pid_change(self):
- monitor = EnvMonitor(MockDhcpHandler())
- monitor.handle_dhclient_restart()
-
-if __name__ == '__main__':
- unittest.main()
diff --git a/tests/test_event.py b/tests/test_event.py
deleted file mode 100644
index fcf67c9..0000000
--- a/tests/test_event.py
+++ /dev/null
@@ -1,53 +0,0 @@
-# Copyright 2014 Microsoft Corporation
-#
-# Licensed under the Apache License, Version 2.0 (the "License");
-# you may not use this file except in compliance with the License.
-# You may obtain a copy of the License at
-#
-# http://www.apache.org/licenses/LICENSE-2.0
-#
-# Unless required by applicable law or agreed to in writing, software
-# distributed under the License is distributed on an "AS IS" BASIS,
-# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-# See the License for the specific language governing permissions and
-# limitations under the License.
-#
-# Requires Python 2.4+ and Openssl 1.0+
-#
-# Implements parts of RFC 2131, 1541, 1497 and
-# http://msdn.microsoft.com/en-us/library/cc227282%28PROT.10%29.aspx
-# http://msdn.microsoft.com/en-us/library/cc227259%28PROT.13%29.aspx
-
-import tests.env
-from .tools import *
-import uuid
-import unittest
-import os
-import shutil
-import azurelinuxagent.utils.fileutil as fileutil
-import azurelinuxagent.event as evt
-import azurelinuxagent.protocol as prot
-
-class MockProtocol(object):
- def get_vminfo(self):
- return prot.VMInfo(subscriptionId='foo', vmName='bar')
- def report_event(self, data): pass
-
-class TestEvent(unittest.TestCase):
- def test_save(self):
- if not os.path.exists("/tmp/events"):
- os.mkdir("/tmp/events")
- evt.add_event("Test", "Test", True)
- eventsFile = os.listdir("/tmp/events")
- self.assertNotEquals(0, len(eventsFile))
- shutil.rmtree("/tmp/events")
-
- @mock(evt.prot.FACTORY, 'get_default_protocol',
- MockFunc(retval=MockProtocol()))
- def test_init_sys_info(self):
- monitor = evt.EventMonitor()
- monitor.init_sysinfo()
- self.assertNotEquals(0, len(monitor.sysinfo))
-
-if __name__ == '__main__':
- unittest.main()
diff --git a/tests/test_ext.py b/tests/test_ext.py
deleted file mode 100644
index a68a851..0000000
--- a/tests/test_ext.py
+++ /dev/null
@@ -1,223 +0,0 @@
-# Copyright 2014 Microsoft Corporation
-#
-# Licensed under the Apache License, Version 2.0 (the "License");
-# you may not use this file except in compliance with the License.
-# You may obtain a copy of the License at
-#
-# http://www.apache.org/licenses/LICENSE-2.0
-#
-# Unless required by applicable law or agreed to in writing, software
-# distributed under the License is distributed on an "AS IS" BASIS,
-# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-# See the License for the specific language governing permissions and
-# limitations under the License.
-#
-# Requires Python 2.4+ and Openssl 1.0+
-#
-# Implements parts of RFC 2131, 1541, 1497 and
-# http://msdn.microsoft.com/en-us/library/cc227282%28PROT.10%29.aspx
-# http://msdn.microsoft.com/en-us/library/cc227259%28PROT.13%29.aspx
-
-import tests.env
-from tests.tools import *
-import uuid
-import unittest
-import os
-import json
-import azurelinuxagent.logger as logger
-from azurelinuxagent.utils.osutil import OSUTIL
-import azurelinuxagent.utils.fileutil as fileutil
-import azurelinuxagent.protocol as prot
-import azurelinuxagent.distro.default.extension as ext
-
-ext_sample_json = {
- "name":"TestExt",
- "properties":{
- "version":"2.0",
- "state":"enabled",
- "upgradePolicy":"auto",
- "extensions":[{
- "sequenceNumber": 0,
- "publicSettings": "",
- "protectedSettings": "",
- "certificateThumbprint": ""
- }],
- "versionUris":[{
- "version":"2.1",
- "uris":["http://foo.bar"]
- },{
- "version":"2.0",
- "uris":["http://foo.bar"]
- }]
- }
-}
-ext_sample = prot.ExtHandler()
-prot.set_properties("extensions", ext_sample, ext_sample_json)
-
-pkd_list_sample_str={
- "versions": [{
- "version": "2.0",
- "uris":[{
- "uri":"http://foo.bar"
- }]
- },{
- "version": "2.1",
- "uris":[{
- "uri":"http://foo.bar"
- }]
- }]
-}
-pkg_list_sample = prot.ExtHandlerPackageList()
-prot.set_properties("packages", pkg_list_sample, pkd_list_sample_str)
-
-manifest_sample_str = {
- "handlerManifest":{
- "installCommand": "echo 'install'",
- "uninstallCommand": "echo 'uninstall'",
- "updateCommand": "echo 'update'",
- "enableCommand": "echo 'enable'",
- "disableCommand": "echo 'disable'",
- }
-}
-manifest_sample = ext.HandlerManifest(manifest_sample_str)
-
-ext_status_sample="""
-[{
- "version": 1.0,
- "timestampUTC": "2015-11-12T06:59:48Z",
- "status": {
- "name": "<Handler workload name>",
- "operation": "<name of the operation being performed>",
- "configurationAppliedTime": "2015-11-12T06:59:48Z",
- "status": "error",
- "code": 0,
- "formattedMessage": {
- "lang": "en-US",
- "message": "formatted user message"
- },
- "substatus": [{
- "name": "<Handler workload subcomponent name>",
- "status": "error",
- "code": 0 ,
- "formattedMessage": {
- "lang": "lang[-locale]",
- "message": "formatted user message"
- }
- },{
- "status": "error"
- }]
- }
-}]
-"""
-
-ext_status_sample_min="""
-[{
- "version": 1.0,
- "timestampUTC": "2015-11-12T06:59:48Z",
- "status": {
- "status": "error"
- }
-}]
-"""
-
-def mock_load_manifest(self):
- return manifest_sample
-
-mock_launch_command = MockFunc()
-mock_set_state = MockFunc()
-
-def mock_download(self):
- fileutil.mkdir(self.get_base_dir())
- fileutil.write_file(self.get_manifest_file(), json.dumps(manifest_sample_str))
-
-#logger.LoggerInit("/dev/null", "/dev/stdout")
-class TestExtensions(unittest.TestCase):
-
- def test_load_ext(self):
- libDir = OSUTIL.get_lib_dir()
- test_ext1 = os.path.join(libDir, 'TestExt-1.0')
- test_ext2 = os.path.join(libDir, 'TestExt-2.0')
- test_ext2 = os.path.join(libDir, 'TestExt-2.1')
- for path in [test_ext1, test_ext2]:
- if not os.path.isdir(path):
- os.mkdir(path)
- test_ext = ext.get_installed_version('TestExt')
- self.assertEqual('2.1', test_ext)
-
- def test_getters(self):
- test_ext = ext.ExtHandlerInstance(ext_sample, pkg_list_sample,
- ext_sample.properties.version, False)
- self.assertEqual("/tmp/TestExt-2.0", test_ext.get_base_dir())
- self.assertEqual("/tmp/TestExt-2.0/status", test_ext.get_status_dir())
- self.assertEqual("/tmp/TestExt-2.0/status/0.status",
- test_ext.get_status_file())
- self.assertEqual("/tmp/handler_state/TestExt-2.0/0.state",
- test_ext.get_handler_state_file())
- self.assertEqual("/tmp/handler_state/TestExt-2.0/0.error",
- test_ext.get_handler_state_err_file())
- self.assertEqual("/tmp/TestExt-2.0/config", test_ext.get_conf_dir())
- self.assertEqual("/tmp/TestExt-2.0/config/0.settings",
- test_ext.get_settings_file())
- self.assertEqual("/tmp/TestExt-2.0/heartbeat.log",
- test_ext.get_heartbeat_file())
- self.assertEqual("/tmp/TestExt-2.0/HandlerManifest.json",
- test_ext.get_manifest_file())
- self.assertEqual("/tmp/TestExt-2.0/HandlerEnvironment.json",
- test_ext.get_env_file())
- self.assertEqual("/tmp/log/TestExt/2.0", test_ext.get_log_dir())
-
- test_ext = ext.ExtHandlerInstance(ext_sample, pkg_list_sample,
- "2.1", False)
- self.assertEqual("/tmp/TestExt-2.1", test_ext.get_base_dir())
- self.assertEqual("2.1", test_ext.get_target_version())
-
- @mock(ext.ExtHandlerInstance, 'load_manifest', mock_load_manifest)
- @mock(ext.ExtHandlerInstance, 'launch_command', mock_launch_command)
- @mock(ext.ExtHandlerInstance, 'set_state', mock_set_state)
- def test_handle_uninstall(self):
- mock_launch_command.args = None
- mock_set_state.args = None
- test_ext = ext.ExtHandlerInstance(ext_sample, pkg_list_sample,
- ext_sample.properties.version, False)
- if not os.path.isdir(test_ext.get_base_dir()):
- os.makedirs(test_ext.get_base_dir())
- test_ext.handle_uninstall()
- self.assertEqual(None, mock_launch_command.args)
- self.assertEqual(None, mock_set_state.args)
-
- test_ext = ext.ExtHandlerInstance(ext_sample, pkg_list_sample,
- ext_sample.properties.version, True)
- if not os.path.isdir(test_ext.get_base_dir()):
- os.makedirs(test_ext.get_base_dir())
- test_ext.handle_uninstall()
- self.assertEqual(manifest_sample.get_uninstall_command(),
- mock_launch_command.args[0])
-
- @mock(ext.ExtHandlerInstance, 'upgrade', MockFunc())
- @mock(ext.ExtHandlerInstance, 'enable', MockFunc())
- @mock(ext.ExtHandlerInstance, 'download', MockFunc())
- @mock(ext.ExtHandlerInstance, 'init_dir', MockFunc())
- @mock(ext.ExtHandlerInstance, 'install', MockFunc())
- def test_handle_enable(self):
- #Test enable
- test_ext = ext.ExtHandlerInstance(ext_sample, pkg_list_sample,
- ext_sample.properties.version, False)
- test_ext.handle_enable()
-
- #Test upgrade
- test_ext = ext.ExtHandlerInstance(ext_sample, pkg_list_sample,
- "2.0" , True)
- test_ext.handle_enable()
-
- def test_status_convert(self):
- data = json.loads(ext_status_sample)
- ext_status = prot.ExtensionStatus()
- ext.parse_ext_status(ext_status, data)
-
- data = json.loads(ext_status_sample_min)
- ext_status = prot.ExtensionStatus()
- ext.parse_ext_status(ext_status, data)
-
-
-if __name__ == '__main__':
- unittest.main()
diff --git a/tests/test_extensionsconfig.py b/tests/test_extensionsconfig.py
deleted file mode 100644
index 505c73d..0000000
--- a/tests/test_extensionsconfig.py
+++ /dev/null
@@ -1,159 +0,0 @@
-# Copyright 2014 Microsoft Corporation
-#
-# Licensed under the Apache License, Version 2.0 (the "License");
-# you may not use this file except in compliance with the License.
-# You may obtain a copy of the License at
-#
-# http://www.apache.org/licenses/LICENSE-2.0
-#
-# Unless required by applicable law or agreed to in writing, software
-# distributed under the License is distributed on an "AS IS" BASIS,
-# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-# See the License for the specific language governing permissions and
-# limitations under the License.
-#
-# Requires Python 2.4+ and Openssl 1.0+
-#
-# Implements parts of RFC 2131, 1541, 1497 and
-# http://msdn.microsoft.com/en-us/library/cc227282%28PROT.10%29.aspx
-# http://msdn.microsoft.com/en-us/library/cc227259%28PROT.13%29.aspx
-
-import tests.env
-import tests.tools as tools
-import uuid
-import unittest
-import os
-import json
-import azurelinuxagent.protocol.v1 as v1
-
-ext_conf_sample=u"""\
-<Extensions version="1.0.0.0" goalStateIncarnation="9"><GuestAgentExtension xmlns:i="http://www.w3.org/2001/XMLSchema-instance">
- <GAFamilies>
- <GAFamily>
- <Name>Win8</Name>
- <Uris>
- <Uri>http://rdfepirv2hknprdstr03.blob.core.windows.net/bfd5c281a7dc4e4b84381eb0b47e3aaf/Microsoft.WindowsAzure.GuestAgent_Win8_asiaeast_manifest.xml</Uri>
- <Uri>http://rdfepirv2hknprdstr04.blob.core.windows.net/bfd5c281a7dc4e4b84381eb0b47e3aaf/Microsoft.WindowsAzure.GuestAgent_Win8_asiaeast_manifest.xml</Uri>
- <Uri>http://rdfepirv2hknprdstr05.blob.core.windows.net/bfd5c281a7dc4e4b84381eb0b47e3aaf/Microsoft.WindowsAzure.GuestAgent_Win8_asiaeast_manifest.xml</Uri>
- <Uri>http://rdfepirv2hknprdstr06.blob.core.windows.net/bfd5c281a7dc4e4b84381eb0b47e3aaf/Microsoft.WindowsAzure.GuestAgent_Win8_asiaeast_manifest.xml</Uri>
- <Uri>http://rdfepirv2hknprdstr07.blob.core.windows.net/bfd5c281a7dc4e4b84381eb0b47e3aaf/Microsoft.WindowsAzure.GuestAgent_Win8_asiaeast_manifest.xml</Uri>
- <Uri>http://rdfepirv2hknprdstr08.blob.core.windows.net/bfd5c281a7dc4e4b84381eb0b47e3aaf/Microsoft.WindowsAzure.GuestAgent_Win8_asiaeast_manifest.xml</Uri>
- <Uri>http://rdfepirv2hknprdstr09.blob.core.windows.net/bfd5c281a7dc4e4b84381eb0b47e3aaf/Microsoft.WindowsAzure.GuestAgent_Win8_asiaeast_manifest.xml</Uri>
- <Uri>http://rdfepirv2hknprdstr10.blob.core.windows.net/bfd5c281a7dc4e4b84381eb0b47e3aaf/Microsoft.WindowsAzure.GuestAgent_Win8_asiaeast_manifest.xml</Uri>
- <Uri>http://rdfepirv2hknprdstr11.blob.core.windows.net/bfd5c281a7dc4e4b84381eb0b47e3aaf/Microsoft.WindowsAzure.GuestAgent_Win8_asiaeast_manifest.xml</Uri>
- <Uri>http://rdfepirv2hknprdstr12.blob.core.windows.net/bfd5c281a7dc4e4b84381eb0b47e3aaf/Microsoft.WindowsAzure.GuestAgent_Win8_asiaeast_manifest.xml</Uri>
- <Uri>http://zrdfepirv2hk2prdstr01.blob.core.windows.net/bfd5c281a7dc4e4b84381eb0b47e3aaf/Microsoft.WindowsAzure.GuestAgent_Win8_asiaeast_manifest.xml</Uri>
- </Uris>
- </GAFamily>
- <GAFamily>
- <Name>Win7</Name>
- <Uris>
- <Uri>http://rdfepirv2hknprdstr03.blob.core.windows.net/bfd5c281a7dc4e4b84381eb0b47e3aaf/Microsoft.WindowsAzure.GuestAgent_Win7_asiaeast_manifest.xml</Uri>
- <Uri>http://rdfepirv2hknprdstr04.blob.core.windows.net/bfd5c281a7dc4e4b84381eb0b47e3aaf/Microsoft.WindowsAzure.GuestAgent_Win7_asiaeast_manifest.xml</Uri>
- <Uri>http://rdfepirv2hknprdstr05.blob.core.windows.net/bfd5c281a7dc4e4b84381eb0b47e3aaf/Microsoft.WindowsAzure.GuestAgent_Win7_asiaeast_manifest.xml</Uri>
- <Uri>http://rdfepirv2hknprdstr06.blob.core.windows.net/bfd5c281a7dc4e4b84381eb0b47e3aaf/Microsoft.WindowsAzure.GuestAgent_Win7_asiaeast_manifest.xml</Uri>
- <Uri>http://rdfepirv2hknprdstr07.blob.core.windows.net/bfd5c281a7dc4e4b84381eb0b47e3aaf/Microsoft.WindowsAzure.GuestAgent_Win7_asiaeast_manifest.xml</Uri>
- <Uri>http://rdfepirv2hknprdstr08.blob.core.windows.net/bfd5c281a7dc4e4b84381eb0b47e3aaf/Microsoft.WindowsAzure.GuestAgent_Win7_asiaeast_manifest.xml</Uri>
- <Uri>http://rdfepirv2hknprdstr09.blob.core.windows.net/bfd5c281a7dc4e4b84381eb0b47e3aaf/Microsoft.WindowsAzure.GuestAgent_Win7_asiaeast_manifest.xml</Uri>
- <Uri>http://rdfepirv2hknprdstr10.blob.core.windows.net/bfd5c281a7dc4e4b84381eb0b47e3aaf/Microsoft.WindowsAzure.GuestAgent_Win7_asiaeast_manifest.xml</Uri>
- <Uri>http://rdfepirv2hknprdstr11.blob.core.windows.net/bfd5c281a7dc4e4b84381eb0b47e3aaf/Microsoft.WindowsAzure.GuestAgent_Win7_asiaeast_manifest.xml</Uri>
- <Uri>http://rdfepirv2hknprdstr12.blob.core.windows.net/bfd5c281a7dc4e4b84381eb0b47e3aaf/Microsoft.WindowsAzure.GuestAgent_Win7_asiaeast_manifest.xml</Uri>
- <Uri>http://zrdfepirv2hk2prdstr01.blob.core.windows.net/bfd5c281a7dc4e4b84381eb0b47e3aaf/Microsoft.WindowsAzure.GuestAgent_Win7_asiaeast_manifest.xml</Uri>
- </Uris>
- </GAFamily>
- </GAFamilies>
-</GuestAgentExtension>
-<Plugins>
- <Plugin name="OSTCExtensions.ExampleHandlerLinux" version="1.4" location="http://rdfepirv2hknprdstr03.blob.core.windows.net/b01058962be54ceca550a390fa5ff064/Microsoft.OSTCExtensions_CustomScriptForLinuxTest_asiaeast_manifest.xml" config="" state="enabled" autoUpgrade="true" failoverlocation="http://rdfepirv2hknprdstr04.blob.core.windows.net/b01058962be54ceca550a390fa5ff064/Microsoft.OSTCExtensions_CustomScriptForLinuxTest_asiaeast_manifest.xml" runAsStartupTask="false" isJson="true" />
-</Plugins>
-<PluginSettings>
- <Plugin name="OSTCExtensions.ExampleHandlerLinux" version="1.4">
- <RuntimeSettings seqNo="6">{"runtimeSettings":[{"handlerSettings":{"protectedSettingsCertThumbprint":"4037FBF5F1F3014F99B5D6C7799E9B20E6871CB3","protectedSettings":"MIICWgYJK","publicSettings":{"foo":"bar"}}}]}</RuntimeSettings>
- </Plugin>
-</PluginSettings>
-<StatusUploadBlob>https://yuezhatest.blob.core.windows.net/vhds/test-cs12.test-cs12.test-cs12.status?sr=b&amp;sp=rw&amp;se=9999-01-01&amp;sk=key1&amp;sv=2014-02-14&amp;sig=hfRh7gzUE7sUtYwke78IOlZOrTRCYvkec4hGZ9zZzXo%3D</StatusUploadBlob></Extensions>
-"""
-
-manifest_sample=u"""\
-<?xml version="1.0" encoding="utf-8"?>
-<PluginVersionManifest xmlns:i="http://www.w3.org/2001/XMLSchema-instance">
- <Plugins>
- <Plugin>
- <Version>1.0</Version>
- <Uris>
- <Uri>http://blahblah</Uri>
- </Uris>
- </Plugin>
- <Plugin>
- <Version>1.1</Version>
- <Uris>
- <Uri>http://blahblah</Uri>
- </Uris>
- </Plugin>
- </Plugins>
- <InternalPlugins>
- <Plugin>
- <Version>1.2</Version>
- <Uris>
- <Uri>http://blahblah</Uri>
- </Uris>
- </Plugin>
-</InternalPlugins>
-</PluginVersionManifest>
-"""
-
-EmptySettings=u"""\
-<Extensions>
- <Plugins>
- <Plugin name="OSTCExtensions.ExampleHandlerLinux" version="1.4" location="http://rdfepirv2hknprdstr03.blob.core.windows.net/b01058962be54ceca550a390fa5ff064/Microsoft.OSTCExtensions_CustomScriptForLinuxTest_asiaeast_manifest.xml" config="" state="enabled" autoUpgrade="true" failoverlocation="http://rdfepirv2hknprdstr04.blob.core.windows.net/b01058962be54ceca550a390fa5ff064/Microsoft.OSTCExtensions_CustomScriptForLinuxTest_asiaeast_manifest.xml" runAsStartupTask="false" isJson="true" />
- </Plugins>
- <StatusUploadBlob>https://yuezhatest.blob.core.windows.net/vhds/test-cs12.test-cs12.test-cs12.status?sr=b&amp;sp=rw&amp;se=9999-01-01&amp;sk=key1&amp;sv=2014-02-14&amp;sig=hfRh7gzUE7sUtYwke78IOlZOrTRCYvkec4hGZ9zZzXo%3D</StatusUploadBlob>
-</Extensions>
-"""
-
-EmptyPublicSettings=u"""\
-<Extensions>
- <Plugins>
- <Plugin name="OSTCExtensions.ExampleHandlerLinux" version="1.4" location="http://rdfepirv2hknprdstr03.blob.core.windows.net/b01058962be54ceca550a390fa5ff064/Microsoft.OSTCExtensions_CustomScriptForLinuxTest_asiaeast_manifest.xml" config="" state="enabled" autoUpgrade="true" failoverlocation="http://rdfepirv2hknprdstr04.blob.core.windows.net/b01058962be54ceca550a390fa5ff064/Microsoft.OSTCExtensions_CustomScriptForLinuxTest_asiaeast_manifest.xml" runAsStartupTask="false" isJson="true" />
- </Plugins>
- <PluginSettings>
- <Plugin name="OSTCExtensions.ExampleHandlerLinux" version="1.4">
- <RuntimeSettings seqNo="6">{"runtimeSettings":[{"handlerSettings":{"protectedSettingsCertThumbprint":"4037FBF5F1F3014F99B5D6C7799E9B20E6871CB3","protectedSettings":"MIICWgYJK"}}]}</RuntimeSettings>
- </Plugin>
- </PluginSettings>
- <StatusUploadBlob>https://yuezhatest.blob.core.windows.net/vhds/test-cs12.test-cs12.test-cs12.status?sr=b&amp;sp=rw&amp;se=9999-01-01&amp;sk=key1&amp;sv=2014-02-14&amp;sig=hfRh7gzUE7sUtYwke78IOlZOrTRCYvkec4hGZ9zZzXo%3D</StatusUploadBlob>
-</Extensions>
-"""
-
-class TestExtensionsConfig(unittest.TestCase):
- def test_extensions_config(self):
- config = v1.ExtensionsConfig(ext_conf_sample)
- extensions = config.ext_handlers.extHandlers
- self.assertNotEquals(None, extensions)
- self.assertEquals(1, len(extensions))
- self.assertNotEquals(None, extensions[0])
- extension = extensions[0]
- self.assertEquals("OSTCExtensions.ExampleHandlerLinux",
- extension.name)
- self.assertEquals("1.4", extension.properties.version)
- self.assertEquals('auto', extension.properties.upgradePolicy)
- self.assertEquals("enabled", extension.properties.state)
- settings = extension.properties.extensions[0]
- self.assertEquals("4037FBF5F1F3014F99B5D6C7799E9B20E6871CB3",
- settings.certificateThumbprint)
- self.assertEquals("MIICWgYJK", settings.privateSettings)
- self.assertEquals(json.loads('{"foo":"bar"}'),
- settings.publicSettings)
-
- man = v1.ExtensionManifest(manifest_sample)
- self.assertNotEquals(None, man.pkg_list)
- self.assertEquals(3, len(man.pkg_list.versions))
-
- def test_empty_settings(self):
- config = v1.ExtensionsConfig(EmptySettings)
-
- def test_empty_public_settings(self):
- config = v1.ExtensionsConfig(EmptyPublicSettings)
-
-if __name__ == '__main__':
- unittest.main()
diff --git a/tests/test_goalstate.py b/tests/test_goalstate.py
deleted file mode 100644
index a18ce8d..0000000
--- a/tests/test_goalstate.py
+++ /dev/null
@@ -1,71 +0,0 @@
-# Copyright 2014 Microsoft Corporation
-#
-# Licensed under the Apache License, Version 2.0 (the "License");
-# you may not use this file except in compliance with the License.
-# You may obtain a copy of the License at
-#
-# http://www.apache.org/licenses/LICENSE-2.0
-#
-# Unless required by applicable law or agreed to in writing, software
-# distributed under the License is distributed on an "AS IS" BASIS,
-# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-# See the License for the specific language governing permissions and
-# limitations under the License.
-#
-# Requires Python 2.4+ and Openssl 1.0+
-#
-# Implements parts of RFC 2131, 1541, 1497 and
-# http://msdn.microsoft.com/en-us/library/cc227282%28PROT.10%29.aspx
-# http://msdn.microsoft.com/en-us/library/cc227259%28PROT.13%29.aspx
-
-import tests.env
-import tests.tools as tools
-import uuid
-import unittest
-import os
-import test
-import azurelinuxagent.protocol.v1 as v1
-
-goal_state_sample=u"""\
-<?xml version="1.0" encoding="utf-8"?>
-<GoalState xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:noNamespaceSchemaLocation="goalstate10.xsd">
- <Version>2010-12-15</Version>
- <Incarnation>1</Incarnation>
- <Machine>
- <ExpectedState>Started</ExpectedState>
- <LBProbePorts>
- <Port>16001</Port>
- </LBProbePorts>
- </Machine>
- <Container>
- <ContainerId>c6d5526c-5ac2-4200-b6e2-56f2b70c5ab2</ContainerId>
- <RoleInstanceList>
- <RoleInstance>
- <InstanceId>MachineRole_IN_0</InstanceId>
- <State>Started</State>
- <Configuration>
- <HostingEnvironmentConfig>http://hostingenvuri/</HostingEnvironmentConfig>
- <SharedConfig>http://sharedconfiguri/</SharedConfig>
- <ExtensionsConfig>http://extensionsconfiguri/</ExtensionsConfig>
- <FullConfig>http://fullconfiguri/</FullConfig>
- </Configuration>
- </RoleInstance>
- </RoleInstanceList>
- </Container>
- </GoalState>
-"""
-
-class TestGoalState(unittest.TestCase):
- def test_goal_state(self):
- goal_state = v1.GoalState(goal_state_sample)
- self.assertEquals('1', goal_state.incarnation)
- self.assertNotEquals(None, goal_state.expected_state)
- self.assertNotEquals(None, goal_state.hosting_env_uri)
- self.assertNotEquals(None, goal_state.shared_conf_uri)
- self.assertEquals(None, goal_state.certs_uri)
- self.assertNotEquals(None, goal_state.ext_uri)
- self.assertNotEquals(None, goal_state.role_instance_id)
- self.assertNotEquals(None, goal_state.container_id)
-
-if __name__ == '__main__':
- unittest.main()
diff --git a/tests/test_import_waagent.py b/tests/test_import_waagent.py
deleted file mode 100644
index ec3f923..0000000
--- a/tests/test_import_waagent.py
+++ /dev/null
@@ -1,40 +0,0 @@
-# Copyright 2014 Microsoft Corporation
-#
-# Licensed under the Apache License, Version 2.0 (the "License");
-# you may not use this file except in compliance with the License.
-# You may obtain a copy of the License at
-#
-# http://www.apache.org/licenses/LICENSE-2.0
-#
-# Unless required by applicable law or agreed to in writing, software
-# distributed under the License is distributed on an "AS IS" BASIS,
-# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-# See the License for the specific language governing permissions and
-# limitations under the License.
-#
-# Requires Python 2.4+ and Openssl 1.0+
-#
-# Implements parts of RFC 2131, 1541, 1497 and
-# http://msdn.microsoft.com/en-us/library/cc227282%28PROT.10%29.aspx
-# http://msdn.microsoft.com/en-us/library/cc227259%28PROT.13%29.aspx
-
-import tests.env
-import tests.tools as tools
-import os
-import imp
-import sys
-import uuid
-import unittest
-
-class TestImportWAAgent(unittest.TestCase):
- def test_import_waagent(self):
- agent_path = os.path.join(tools.parent, 'bin/waagent')
- if sys.version_info[0] == 2:
- waagent = imp.load_source('waagent', agent_path)
- self.assertNotEquals(None, waagent.LoggerInit)
- else:
- self.assertRaises(ImportError, imp.load_source, 'waagent',
- agent_path)
-
-if __name__ == '__main__':
- unittest.main()
diff --git a/tests/test_logger.py b/tests/test_logger.py
deleted file mode 100644
index 20e9259..0000000
--- a/tests/test_logger.py
+++ /dev/null
@@ -1,100 +0,0 @@
-# -*- coding: utf-8 -*-
-# Copyright 2014 Microsoft Corporation
-#
-# Licensed under the Apache License, Version 2.0 (the "License");
-# you may not use this file except in compliance with the License.
-# You may obtain a copy of the License at
-#
-# http://www.apache.org/licenses/LICENSE-2.0
-#
-# Unless required by applicable law or agreed to in writing, software
-# distributed under the License is distributed on an "AS IS" BASIS,
-# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-# See the License for the specific language governing permissions and
-# limitations under the License.
-#
-# Requires Python 2.4+ and Openssl 1.0+
-#
-# Implements parts of RFC 2131, 1541, 1497 and
-# http://msdn.microsoft.com/en-us/library/cc227282%28PROT.10%29.aspx
-# http://msdn.microsoft.com/en-us/library/cc227259%28PROT.13%29.aspx
-
-import tests.env
-import tests.tools as tools
-import uuid
-import unittest
-import azurelinuxagent.logger as logger
-from azurelinuxagent.future import text
-
-class TestLogger(unittest.TestCase):
-
- def test_no_appender(self):
- #The logger won't throw exception even if no appender.
- _logger = logger.Logger()
- _logger.verb("Assert no exception")
- _logger.info("Assert no exception")
- _logger.warn("Assert no exception")
- _logger.error("Assert no exception")
-
- def test_logger_format(self):
- _logger = logger.Logger()
- _logger.info("This is an exception {0}", Exception("Test"))
- _logger.info("This is an number {0}", 0)
- _logger.info("This is an boolean {0}", True)
- _logger.verb("{0}")
- _logger.verb("{0} {1}", 0, 1)
- _logger.info("{0} {1}", 0, 1)
- _logger.warn("{0} {1}", 0, 1)
- _logger.error("{0} {1}", 0, 1)
- _logger.add_appender(logger.AppenderType.STDOUT,
- logger.LogLevel.INFO, None)
- _logger.info(u"啊哈this is a utf-8 {0}", u'呵呵')
-
- def test_file_appender(self):
- _logger = logger.Logger()
- _logger.add_appender(logger.AppenderType.FILE,
- logger.LogLevel.INFO,
- '/tmp/testlog')
-
- msg = text(uuid.uuid4())
- _logger.info("Test logger: {0}", msg)
- self.assertTrue(tools.simple_file_grep('/tmp/testlog', msg))
-
- msg = text(uuid.uuid4())
- _logger.verb("Verbose should not be logged: {0}", msg)
- self.assertFalse(tools.simple_file_grep('/tmp/testlog', msg))
-
-
- def test_concole_appender(self):
- _logger = logger.Logger()
- _logger.add_appender(logger.AppenderType.CONSOLE,
- logger.LogLevel.VERBOSE,
- '/tmp/testlog')
-
- msg = text(uuid.uuid4())
- _logger.info("Test logger: {0}", msg)
- self.assertTrue(tools.simple_file_grep('/tmp/testlog', msg))
-
- msg = text(uuid.uuid4())
- _logger.verb("Test logger: {0}", msg)
- self.assertFalse(tools.simple_file_grep('/tmp/testlog', msg))
-
-
- def test_log_to_non_exists_dev(self):
- _logger = logger.Logger()
- _logger.add_appender(logger.AppenderType.CONSOLE,
- logger.LogLevel.INFO,
- '/dev/nonexists')
- _logger.info("something")
-
- def test_log_to_non_exists_file(self):
- _logger = logger.Logger()
- _logger.add_appender(logger.AppenderType.FILE,
- logger.LogLevel.INFO,
- '/tmp/nonexists')
- _logger.info("something")
-
-
-
-if __name__ == '__main__':
- unittest.main()
diff --git a/tests/test_metadata.py b/tests/test_metadata.py
deleted file mode 100644
index 8c34acc..0000000
--- a/tests/test_metadata.py
+++ /dev/null
@@ -1,36 +0,0 @@
-# Copyright 2014 Microsoft Corporation
-#
-# Licensed under the Apache License, Version 2.0 (the "License");
-# you may not use this file except in compliance with the License.
-# You may obtain a copy of the License at
-#
-# http://www.apache.org/licenses/LICENSE-2.0
-#
-# Unless required by applicable law or agreed to in writing, software
-# distributed under the License is distributed on an "AS IS" BASIS,
-# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-# See the License for the specific language governing permissions and
-# limitations under the License.
-#
-# Requires Python 2.4+ and Openssl 1.0+
-#
-# Implements parts of RFC 2131, 1541, 1497 and
-# http://msdn.microsoft.com/en-us/library/cc227282%28PROT.10%29.aspx
-# http://msdn.microsoft.com/en-us/library/cc227259%28PROT.13%29.aspx
-
-import tests.env
-from tests.tools import *
-import unittest
-from azurelinuxagent.metadata import AGENT_NAME, AGENT_VERSION, \
- DISTRO_NAME, DISTRO_VERSION, DISTRO_CODE_NAME, \
- DISTRO_FULL_NAME
-
-class TestOSInfo(unittest.TestCase):
- def test_curr_os_info(self):
- self.assertNotEquals(None, DISTRO_NAME)
- self.assertNotEquals(None, DISTRO_VERSION)
- self.assertNotEquals(None, DISTRO_CODE_NAME)
- self.assertNotEquals(None, DISTRO_FULL_NAME)
-
-if __name__ == '__main__':
- unittest.main()
diff --git a/tests/test_osutil.py b/tests/test_osutil.py
deleted file mode 100644
index 95b8e17..0000000
--- a/tests/test_osutil.py
+++ /dev/null
@@ -1,174 +0,0 @@
-# Copyright 2014 Microsoft Corporation
-#
-# Licensed under the Apache License, Version 2.0 (the "License");
-# you may not use this file except in compliance with the License.
-# You may obtain a copy of the License at
-#
-# http://www.apache.org/licenses/LICENSE-2.0
-#
-# Unless required by applicable law or agreed to in writing, software
-# distributed under the License is distributed on an "AS IS" BASIS,
-# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-# See the License for the specific language governing permissions and
-# limitations under the License.
-#
-# Requires Python 2.4+ and Openssl 1.0+
-#
-# Implements parts of RFC 2131, 1541, 1497 and
-# http://msdn.microsoft.com/en-us/library/cc227282%28PROT.10%29.aspx
-# http://msdn.microsoft.com/en-us/library/cc227259%28PROT.13%29.aspx
-
-import tests.env as env
-from tests.tools import *
-import uuid
-import unittest
-import os
-import shutil
-import time
-import azurelinuxagent.utils.fileutil as fileutil
-import azurelinuxagent.utils.shellutil as shellutil
-import azurelinuxagent.conf as conf
-from azurelinuxagent.utils.osutil import OSUTIL, OSUtilError
-import test
-
-class TestOSUtil(unittest.TestCase):
- def test_current_distro(self):
- self.assertNotEquals(None, OSUTIL)
-
-mount_list_sample="""\
-/dev/sda1 on / type ext4 (rw)
-proc on /proc type proc (rw)
-sysfs on /sys type sysfs (rw)
-devpts on /dev/pts type devpts (rw,gid=5,mode=620)
-tmpfs on /dev/shm type tmpfs (rw,rootcontext="system_u:object_r:tmpfs_t:s0")
-none on /proc/sys/fs/binfmt_misc type binfmt_misc (rw)
-/dev/sdb1 on /mnt/resource type ext4 (rw)
-"""
-
-class TestCurrOS(unittest.TestCase):
-#class TestCurrOS(object):
- def test_get_paths(self):
- self.assertNotEquals(None, OSUTIL.get_home())
- self.assertNotEquals(None, OSUTIL.get_lib_dir())
- self.assertNotEquals(None, OSUTIL.get_agent_pid_file_path())
- self.assertNotEquals(None, OSUTIL.get_conf_file_path())
- self.assertNotEquals(None, OSUTIL.get_dvd_mount_point())
- self.assertNotEquals(None, OSUTIL.get_ovf_env_file_path_on_dvd())
-
- @mock(fileutil, 'write_file', MockFunc())
- @mock(fileutil, 'append_file', MockFunc())
- @mock(fileutil, 'chmod', MockFunc())
- @mock(fileutil, 'read_file', MockFunc(retval=''))
- @mock(shellutil, 'run', MockFunc())
- @mock(shellutil, 'run_get_output', MockFunc(retval=[0, '']))
- def test_update_user_account(self):
- OSUTIL.useradd('foo')
- OSUTIL.chpasswd('foo', 'bar')
- OSUTIL.del_account('foo')
-
- @mock(fileutil, 'read_file', MockFunc(retval='root::::'))
- @mock(fileutil, 'write_file', MockFunc())
- def test_delete_root_password(self):
- OSUTIL.del_root_password()
- self.assertEquals('root:*LOCK*:14600::::::',
- fileutil.write_file.args[1])
-
- def test_cert_operation(self):
- if os.path.isfile('/tmp/test.prv'):
- os.remove('/tmp/test.prv')
- shutil.copyfile(os.path.join(env.test_root, 'test.prv'),
- '/tmp/test.prv')
- if os.path.isfile('/tmp/test.crt'):
- os.remove('/tmp/test.crt')
- shutil.copyfile(os.path.join(env.test_root, 'test.crt'),
- '/tmp/test.crt')
- pub1 = OSUTIL.get_pubkey_from_prv('/tmp/test.prv')
- pub2 = OSUTIL.get_pubkey_from_crt('/tmp/test.crt')
- self.assertEquals(pub1, pub2)
- thumbprint = OSUTIL.get_thumbprint_from_crt('/tmp/test.crt')
- self.assertEquals('33B0ABCE4673538650971C10F7D7397E71561F35', thumbprint)
-
- def test_selinux(self):
- if not OSUTIL.is_selinux_system():
- return
- isrunning = OSUTIL.is_selinux_enforcing()
- if not OSUTIL.is_selinux_enforcing():
- OSUTIL.set_selinux_enforce(0)
- self.assertEquals(False, OSUTIL.is_selinux_enforcing())
- OSUTIL.set_selinux_enforce(1)
- self.assertEquals(True, OSUTIL.is_selinux_enforcing())
- if os.path.isfile('/tmp/abc'):
- os.remove('/tmp/abc')
- fileutil.write_file('/tmp/abc', '')
- OSUTIL.set_selinux_context('/tmp/abc','unconfined_u:object_r:ssh_home_t:s')
- OSUTIL.set_selinux_enforce(1 if isrunning else 0)
-
- @mock(shellutil, 'run_get_output', MockFunc(retval=[0, '']))
- @mock(fileutil, 'write_file', MockFunc())
- def test_network_operation(self):
- OSUTIL.start_network()
- OSUTIL.allow_dhcp_broadcast()
- OSUTIL.gen_transport_cert()
- mac = OSUTIL.get_mac_addr()
- self.assertNotEquals(None, mac)
- OSUTIL.is_missing_default_route()
- OSUTIL.set_route_for_dhcp_broadcast('api')
- OSUTIL.remove_route_for_dhcp_broadcast('api')
- OSUTIL.route_add('', '', '')
- OSUTIL.get_dhcp_pid()
- OSUTIL.set_hostname('api')
- OSUTIL.publish_hostname('api')
-
- @mock(OSUTIL, 'get_home', MockFunc(retval='/tmp/home'))
- @mock(OSUTIL, 'get_pubkey_from_prv', MockFunc(retval=''))
- @mock(fileutil, 'chowner', MockFunc())
- def test_deploy_key(self):
- if os.path.isdir('/tmp/home'):
- shutil.rmtree('/tmp/home')
- fileutil.write_file('/tmp/foo.prv', '')
- OSUTIL.deploy_ssh_keypair("foo", ('$HOME/.ssh/id_rsa', 'foo'))
- OSUTIL.deploy_ssh_pubkey("foo", ('$HOME/.ssh/authorized_keys', None,
- 'ssh-rsa asdf'))
- OSUTIL.deploy_ssh_pubkey("foo", ('$HOME/.ssh/authorized_keys', 'foo',
- 'ssh-rsa asdf'))
- self.assertRaises(OSUtilError, OSUTIL.deploy_ssh_pubkey, "foo",
- ('$HOME/.ssh/authorized_keys', 'foo','hehe-rsa asdf'))
- self.assertTrue(os.path.isfile('/tmp/home/.ssh/id_rsa'))
- self.assertTrue(os.path.isfile('/tmp/home/.ssh/id_rsa.pub'))
- self.assertTrue(os.path.isfile('/tmp/home/.ssh/authorized_keys'))
-
- @mock(shellutil, 'run_get_output', MockFunc(retval=[0, '']))
- @mock(OSUTIL, 'get_sshd_conf_file_path', MockFunc(retval='/tmp/sshd_config'))
- def test_ssh_operation(self):
- shellutil.run_get_output.retval=[0,
- '2048 f1:fe:14:66:9d:46:9a:60:8b:8c:'
- '80:43:39:1c:20:9e root@api (RSA)']
- sshd_conf = OSUTIL.get_sshd_conf_file_path()
- self.assertEquals('/tmp/sshd_config', sshd_conf)
- if os.path.isfile(sshd_conf):
- os.remove(sshd_conf)
- shutil.copyfile(os.path.join(env.test_root, 'sshd_config'), sshd_conf)
- OSUTIL.set_ssh_client_alive_interval()
- OSUTIL.conf_sshd(True)
- self.assertTrue(simple_file_grep(sshd_conf,
- 'PasswordAuthentication no'))
- self.assertTrue(simple_file_grep(sshd_conf,
- 'ChallengeResponseAuthentication no'))
- self.assertTrue(simple_file_grep(sshd_conf,
- 'ClientAliveInterval 180'))
-
- @mock(shellutil, 'run_get_output', MockFunc(retval=[0, '']))
- @mock(OSUTIL, 'get_dvd_device', MockFunc(retval=[0, 'abc']))
- @mock(OSUTIL, 'get_mount_point', MockFunc(retval='/tmp/cdrom'))
- def test_mount(self):
- OSUTIL.mount_dvd()
- OSUTIL.umount_dvd()
- mount_point = OSUTIL.get_mount_point(mount_list_sample, '/dev/sda')
- self.assertNotEquals(None, mount_point)
-
- def test_getdvd(self):
- fileutil.write_file("/tmp/sr0", '')
- OSUTIL.get_dvd_device(dev_dir='/tmp')
-
-if __name__ == '__main__':
- unittest.main()
diff --git a/tests/test_ovfxml.py b/tests/test_ovfxml.py
deleted file mode 100644
index 7b6990b..0000000
--- a/tests/test_ovfxml.py
+++ /dev/null
@@ -1,81 +0,0 @@
-# Copyright 2014 Microsoft Corporation
-#
-# Licensed under the Apache License, Version 2.0 (the "License");
-# you may not use this file except in compliance with the License.
-# You may obtain a copy of the License at
-#
-# http://www.apache.org/licenses/LICENSE-2.0
-#
-# Unless required by applicable law or agreed to in writing, software
-# distributed under the License is distributed on an "AS IS" BASIS,
-# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-# See the License for the specific language governing permissions and
-# limitations under the License.
-#
-# Requires Python 2.4+ and Openssl 1.0+
-#
-# Implements parts of RFC 2131, 1541, 1497 and
-# http://msdn.microsoft.com/en-us/library/cc227282%28PROT.10%29.aspx
-# http://msdn.microsoft.com/en-us/library/cc227259%28PROT.13%29.aspx
-
-import tests.env
-import tests.tools as tools
-import uuid
-import unittest
-import os
-import json
-from azurelinuxagent.future import text
-import azurelinuxagent.protocol.ovfenv as ovfenv
-
-ExtensionsConfigSample="""\
-<?xml version="1.0" encoding="utf-8"?>
- <Environment xmlns="http://schemas.dmtf.org/ovf/environment/1" xmlns:oe="http://schemas.dmtf.org/ovf/environment/1" xmlns:wa="http://schemas.microsoft.com/windowsazure" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance">
- <wa:ProvisioningSection>
- <wa:Version>1.0</wa:Version>
- <LinuxProvisioningConfigurationSet xmlns="http://schemas.microsoft.com/windowsazure" xmlns:i="http://www.w3.org/2001/XMLSchema-instance">
- <ConfigurationSetType>LinuxProvisioningConfiguration</ConfigurationSetType>
- <HostName>HostName</HostName>
- <UserName>UserName</UserName>
- <UserPassword>UserPassword</UserPassword>
- <DisableSshPasswordAuthentication>false</DisableSshPasswordAuthentication>
- <SSH>
- <PublicKeys>
- <PublicKey>
- <Fingerprint>EB0C0AB4B2D5FC35F2F0658D19F44C8283E2DD62</Fingerprint>
- <Path>$HOME/UserName/.ssh/authorized_keys</Path>
- <Value>ssh-rsa AAAANOTAREALKEY== foo@bar.local</Value>
- </PublicKey>
- </PublicKeys>
- <KeyPairs>
- <KeyPair>
- <Fingerprint>EB0C0AB4B2D5FC35F2F0658D19F44C8283E2DD62</Fingerprint>
- <Path>$HOME/UserName/.ssh/id_rsa</Path>
- </KeyPair>
- </KeyPairs>
- </SSH>
- <CustomData>CustomData</CustomData>
- </LinuxProvisioningConfigurationSet>
- </wa:ProvisioningSection>
- </Environment>
-"""
-
-class TestOvf(unittest.TestCase):
- def test_ovf(self):
- config = ovfenv.OvfEnv(ExtensionsConfigSample)
- self.assertEquals("HostName", config.hostname)
- self.assertEquals("UserName", config.username)
- self.assertEquals("UserPassword", config.user_password)
- self.assertEquals(False, config.disable_ssh_password_auth)
- self.assertEquals("CustomData", config.customdata)
- self.assertNotEquals(None, config.ssh_pubkeys)
- self.assertEquals(1, len(config.ssh_pubkeys))
- pubkey = config.ssh_pubkeys[0]
- path, fingerprint, value = pubkey
- self.assertEquals(path, "$HOME/UserName/.ssh/authorized_keys")
- self.assertEquals(fingerprint, "EB0C0AB4B2D5FC35F2F0658D19F44C8283E2DD62"),
- self.assertEquals(value, "ssh-rsa AAAANOTAREALKEY== foo@bar.local")
- self.assertNotEquals(None, config.ssh_keypairs)
- self.assertEquals(1, len(config.ssh_keypairs))
-
-if __name__ == '__main__':
- unittest.main()
diff --git a/tests/test_protocol.py b/tests/test_protocol.py
deleted file mode 100644
index de74443..0000000
--- a/tests/test_protocol.py
+++ /dev/null
@@ -1,72 +0,0 @@
-# Copyright 2014 Microsoft Corporation
-#
-# Licensed under the Apache License, Version 2.0 (the "License");
-# you may not use this file except in compliance with the License.
-# You may obtain a copy of the License at
-#
-# http://www.apache.org/licenses/LICENSE-2.0
-#
-# Unless required by applicable law or agreed to in writing, software
-# distributed under the License is distributed on an "AS IS" BASIS,
-# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-# See the License for the specific language governing permissions and
-# limitations under the License.
-#
-# Requires Python 2.4+ and Openssl 1.0+
-#
-# Implements parts of RFC 2131, 1541, 1497 and
-# http://msdn.microsoft.com/en-us/library/cc227282%28PROT.10%29.aspx
-# http://msdn.microsoft.com/en-us/library/cc227259%28PROT.13%29.aspx
-
-import tests.env
-from tests.tools import *
-import uuid
-import unittest
-import os
-import time
-import json
-from azurelinuxagent.protocol.common import *
-
-extensionDataStr = """
-{
- "vmAgent": {
- "agentVersion": "2.4.1198.689",
- "status": "Ready",
- "message": "GuestAgent is running and accepting new configurations.",
- "extensionHandlers": [{
- "name": "Microsoft.Compute.CustomScript",
- "version": "1.0.0.0",
- "status": "Ready",
- "message": "Plugin enabled (name: Microsoft.Compute.CustomScript, version: 1.0.0.0).",
- "extensions": []
- }]
- }
-}
-"""
-
-class TestProtocolContract(unittest.TestCase):
- def test_get_properties(self):
- data = get_properties(VMInfo())
- data = get_properties(Cert())
- data = get_properties(ExtHandlerPackageList())
- data = get_properties(VMStatus())
- data = get_properties(TelemetryEventList())
- data = get_properties(ExtHandler(name="hehe"))
- self.assertTrue("name" in data)
- self.assertTrue("properties" in data)
- self.assertEquals(dict, type(data["properties"]))
- self.assertTrue("versionUris" in data)
-
- def test_set_properties(self):
- data = json.loads(extensionDataStr)
- obj = VMStatus()
- set_properties("vmStatus", obj, data)
- self.assertNotEquals(None, obj.vmAgent)
- self.assertEquals(VMAgentStatus, type(obj.vmAgent))
- self.assertNotEquals(None, obj.vmAgent.status)
- self.assertNotEquals(None, obj.vmAgent.extensionHandlers)
- self.assertEquals(DataContractList, type(obj.vmAgent.extensionHandlers))
-
-if __name__ == '__main__':
- unittest.main()
-
diff --git a/tests/test_redhat.py b/tests/test_redhat.py
deleted file mode 100644
index d9ea4ec..0000000
--- a/tests/test_redhat.py
+++ /dev/null
@@ -1,49 +0,0 @@
-# Copyright 2014 Microsoft Corporation
-#
-# Licensed under the Apache License, Version 2.0 (the "License");
-# you may not use this file except in compliance with the License.
-# You may obtain a copy of the License at
-#
-# http://www.apache.org/licenses/LICENSE-2.0
-#
-# Unless required by applicable law or agreed to in writing, software
-# distributed under the License is distributed on an "AS IS" BASIS,
-# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-# See the License for the specific language governing permissions and
-# limitations under the License.
-#
-# Requires Python 2.4+ and Openssl 1.0+
-#
-# Implements parts of RFC 2131, 1541, 1497 and
-# http://msdn.microsoft.com/en-us/library/cc227282%28PROT.10%29.aspx
-# http://msdn.microsoft.com/en-us/library/cc227259%28PROT.13%29.aspx
-
-import tests.env
-from tests.tools import *
-import unittest
-from azurelinuxagent.distro.redhat.osutil import RedhatOSUtil
-
-test_pubkey="""\
------BEGIN PUBLIC KEY-----
-MIIBIDANBgkqhkiG9w0BAQEFAAOCAQ0AMIIBCAKCAQEA2wo22vf1N8NWE+5lLfit
-T7uzkfwqdw0IAoHZ0l2BtP0ajy6f835HCR3w3zLWw5ut7Xvyo26x1OMOzjo5lqtM
-h8iyQwfHtWf6Cekxfkf+6Pca99bNuDgwRopOTOyoVgwDzJB0+slpn/sJjeGbhxJl
-ToT8tNPLrBmnnpaMZLMIANcPQtTRCQcV/ycv+/omKXFB+zULYkN8v22o5mysoCuQ
-fzXiJP3Mlnf+V2XMl1WAJylhOJif04K8j+G8oF5ECBIQiph4ZLQS1yTYlozPXU8k
-8vB6A5+UiOGxBnOQYnp42cS5d4qSQ8LORCRGXrCj4DCP+lvkUDLUHx2WN+1ivZkO
-fQIBIw==
------END PUBLIC KEY-----
-"""
-
-expected_ssh_rsa_pubkey="""\
-ssh-rsa AAAAB3NzaC1yc2EAAAABIwAAAQEA2wo22vf1N8NWE+5lLfitT7uzkfwqdw0IAoHZ0l2BtP0ajy6f835HCR3w3zLWw5ut7Xvyo26x1OMOzjo5lqtMh8iyQwfHtWf6Cekxfkf+6Pca99bNuDgwRopOTOyoVgwDzJB0+slpn/sJjeGbhxJlToT8tNPLrBmnnpaMZLMIANcPQtTRCQcV/ycv+/omKXFB+zULYkN8v22o5mysoCuQfzXiJP3Mlnf+V2XMl1WAJylhOJif04K8j+G8oF5ECBIQiph4ZLQS1yTYlozPXU8k8vB6A5+UiOGxBnOQYnp42cS5d4qSQ8LORCRGXrCj4DCP+lvkUDLUHx2WN+1ivZkOfQ==
-"""
-
-class TestRedhat(unittest.TestCase):
- def test_RsaPublicKeyToSshRsa(self):
- OSUtil = RedhatOSUtil()
- ssh_rsa_pubkey = OSUtil.asn1_to_ssh_rsa(test_pubkey)
- self.assertEquals(expected_ssh_rsa_pubkey, ssh_rsa_pubkey)
-
-if __name__ == '__main__':
- unittest.main()
diff --git a/tests/test_resourcedisk.py b/tests/test_resourcedisk.py
deleted file mode 100644
index de54fd3..0000000
--- a/tests/test_resourcedisk.py
+++ /dev/null
@@ -1,63 +0,0 @@
-# Copyright 2014 Microsoft Corporation
-#
-# Licensed under the Apache License, Version 2.0 (the "License");
-# you may not use this file except in compliance with the License.
-# You may obtain a copy of the License at
-#
-# http://www.apache.org/licenses/LICENSE-2.0
-#
-# Unless required by applicable law or agreed to in writing, software
-# distributed under the License is distributed on an "AS IS" BASIS,
-# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-# See the License for the specific language governing permissions and
-# limitations under the License.
-#
-# Requires Python 2.4+ and Openssl 1.0+
-#
-# Implements parts of RFC 2131, 1541, 1497 and
-# http://msdn.microsoft.com/en-us/library/cc227282%28PROT.10%29.aspx
-# http://msdn.microsoft.com/en-us/library/cc227259%28PROT.13%29.aspx
-
-import tests.env
-from tests.tools import *
-import unittest
-import azurelinuxagent.distro.default.resourceDisk as rdh
-import azurelinuxagent.logger as logger
-from azurelinuxagent.utils.osutil import OSUTIL
-
-#logger.LoggerInit("/dev/null", "/dev/stdout")
-
-gpt_output_sample="""
-Model: Msft Virtual Disk (scsi)
-Disk /dev/sda: 32.2GB
-Sector size (logical/physical): 512B/4096B
-Partition Table: gpt
-
-Number Start End Size Type File system Flags
- 1 2097kB 29.4GB 29.4GB primary ext4 boot
- 2 2097kB 29.4GB 29.4GB primary ext4 boot
-"""
-
-class TestResourceDisk(unittest.TestCase):
-
- @mock(rdh.OSUTIL, 'device_for_ide_port', MockFunc(retval='foo'))
- @mock(rdh.shellutil, 'run_get_output', MockFunc(retval=(0, gpt_output_sample)))
- @mock(rdh.shellutil, 'run', MockFunc(retval=0))
- def test_mountGPT(self):
- handler = rdh.ResourceDiskHandler()
- handler.mount_resource_disk('/tmp/foo', 'ext4')
-
- @mock(rdh.OSUTIL, 'device_for_ide_port', MockFunc(retval='foo'))
- @mock(rdh.shellutil, 'run_get_output', MockFunc(retval=(0, "")))
- @mock(rdh.shellutil, 'run', MockFunc(retval=0))
- def test_mountMBR(self):
- handler = rdh.ResourceDiskHandler()
- handler.mount_resource_disk('/tmp/foo', 'ext4')
-
- @mock(rdh.shellutil, 'run', MockFunc(retval=0))
- def test_createSwapSpace(self):
- handler = rdh.ResourceDiskHandler()
- handler.create_swap_space('/tmp/foo', 512)
-
-if __name__ == '__main__':
- unittest.main()
diff --git a/tests/test_rest_util.py b/tests/test_rest_util.py
deleted file mode 100644
index d07a1df..0000000
--- a/tests/test_rest_util.py
+++ /dev/null
@@ -1,63 +0,0 @@
-# Copyright 2014 Microsoft Corporation
-#
-# Licensed under the Apache License, Version 2.0 (the "License");
-# you may not use this file except in compliance with the License.
-# You may obtain a copy of the License at
-#
-# http://www.apache.org/licenses/LICENSE-2.0
-#
-# Unless required by applicable law or agreed to in writing, software
-# distributed under the License is distributed on an "AS IS" BASIS,
-# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-# See the License for the specific language governing permissions and
-# limitations under the License.
-#
-# Requires Python 2.4+ and Openssl 1.0+
-#
-# Implements parts of RFC 2131, 1541, 1497 and
-# http://msdn.microsoft.com/en-us/library/cc227282%28PROT.10%29.aspx
-# http://msdn.microsoft.com/en-us/library/cc227259%28PROT.13%29.aspx
-
-import tests.env
-from tests.tools import *
-import uuid
-import unittest
-import os
-import azurelinuxagent.utils.restutil as restutil
-from azurelinuxagent.future import text
-import test
-import socket
-import azurelinuxagent.logger as logger
-
-class MockResponse(object):
- def __init__(self, status=restutil.httpclient.OK):
- self.status = status
-
- def getheaders(self):
- pass
-
-class TestHttpOperations(unittest.TestCase):
-
- def test_parse_url(self):
- host, port, secure, rel_uri = restutil._parse_url("http://abc.def/ghi#hash?jkl=mn")
- self.assertEquals("abc.def", host)
- self.assertEquals("/ghi#hash?jkl=mn", rel_uri)
-
- host, port, secure, rel_uri = restutil._parse_url("http://abc.def/")
- self.assertEquals("abc.def", host)
- self.assertEquals("/", rel_uri)
- self.assertEquals(False, secure)
-
- host, port, secure, rel_uri = restutil._parse_url("https://abc.def/ghi?jkl=mn")
- self.assertEquals(True, secure)
-
- host, port, secure, rel_uri = restutil._parse_url("http://abc.def:80/")
- self.assertEquals("abc.def", host)
-
- @mock(restutil.httpclient.HTTPConnection, 'request', MockFunc())
- @mock(restutil.httpclient.HTTPConnection, 'getresponse', MockFunc(retval=MockResponse()))
- def test_http_request(self):
- restutil.http_get("https://httpbin.org/get")
-
-if __name__ == '__main__':
- unittest.main()
diff --git a/tests/test_v1.py b/tests/test_v1.py
deleted file mode 100644
index 5a1b36b..0000000
--- a/tests/test_v1.py
+++ /dev/null
@@ -1,203 +0,0 @@
-# Copyright 2014 Microsoft Corporation
-#
-# Licensed under the Apache License, Version 2.0 (the "License");
-# you may not use this file except in compliance with the License.
-# You may obtain a copy of the License at
-#
-# http://www.apache.org/licenses/LICENSE-2.0
-#
-# Unless required by applicable law or agreed to in writing, software
-# distributed under the License is distributed on an "AS IS" BASIS,
-# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-# See the License for the specific language governing permissions and
-# limitations under the License.
-#
-# Requires Python 2.4+ and Openssl 1.0+
-#
-# Implements parts of RFC 2131, 1541, 1497 and
-# http://msdn.microsoft.com/en-us/library/cc227282%28PROT.10%29.aspx
-# http://msdn.microsoft.com/en-us/library/cc227259%28PROT.13%29.aspx
-
-import tests.env
-import tests.tools as tools
-from tests.tools import *
-import uuid
-import unittest
-import os
-import time
-from azurelinuxagent.utils.restutil import httpclient
-import azurelinuxagent.logger as logger
-import azurelinuxagent.protocol.v1 as v1
-from tests.test_version import VersionInfoSample
-from tests.test_goalstate import goal_state_sample
-from tests.test_hostingenv import hosting_env_sample
-from tests.test_sharedconfig import shared_config_sample
-from tests.test_certificates import certs_sample, transport_cert
-from tests.test_extensionsconfig import ext_conf_sample, manifest_sample
-
-def mock_fetch_config(self, url, headers=None, chk_proxy=False):
- content = None
- if "versions" in url:
- content = VersionInfoSample
- elif "goalstate" in url:
- content = goal_state_sample
- elif "hostingenvuri" in url:
- content = hosting_env_sample
- elif "sharedconfiguri" in url:
- content = shared_config_sample
- elif "certificatesuri" in url:
- content = certs_sample
- elif "extensionsconfiguri" in url:
- content = ext_conf_sample
- elif "manifest.xml" in url:
- content = manifest_sample
- else:
- raise Exception("Bad url {0}".format(url))
- return content
-
-def mock_fetch_manifest(self, uris):
- return manifest_sample
-
-def mock_fetch_cache(self, file_path):
- content = None
- if "Incarnation" in file_path:
- content = 1
- elif "GoalState" in file_path:
- content = goal_state_sample
- elif "HostingEnvironmentConfig" in file_path:
- content = hosting_env_sample
- elif "SharedConfig" in file_path:
- content = shared_config_sample
- elif "Certificates" in file_path:
- content = certs_sample
- elif "TransportCert" in file_path:
- content = transport_cert
- elif "ExtensionsConfig" in file_path:
- content = ext_conf_sample
- elif "manifest" in file_path:
- content = manifest_sample
- else:
- raise Exception("Bad filepath {0}".format(file_path))
- return content
-
-data_with_bom = b'\xef\xbb\xbfhehe'
-
-class MockResp(object):
- def __init__(self, status=v1.httpclient.OK, data=None):
- self.status = status
- self.data = data
-
- def read(self):
- return self.data
-
-def mock_403():
- return MockResp(status = v1.httpclient.FORBIDDEN)
-
-def mock_410():
- return MockResp(status = v1.httpclient.GONE)
-
-def mock_503():
- return MockResp(status = v1.httpclient.SERVICE_UNAVAILABLE)
-
-class TestWireClint(unittest.TestCase):
-
- @mock(v1.restutil, 'http_get', MockFunc(retval=MockResp(data=data_with_bom)))
- def test_fetch_uri_with_bom(self):
- client = v1.WireClient("http://foo.bar/")
- client.fetch_config("http://foo.bar", None)
-
- @mock(v1.WireClient, 'fetch_cache', mock_fetch_cache)
- def test_get(self):
- os.chdir('/tmp')
- client = v1.WireClient("foobar")
- goalState = client.get_goal_state()
- self.assertNotEquals(None, goalState)
- hostingEnv = client.get_hosting_env()
- self.assertNotEquals(None, hostingEnv)
- sharedConfig = client.get_shared_conf()
- self.assertNotEquals(None, sharedConfig)
- extensionsConfig = client.get_ext_conf()
- self.assertNotEquals(None, extensionsConfig)
-
-
- @mock(v1.WireClient, 'fetch_cache', mock_fetch_cache)
- def test_get_head_for_cert(self):
- client = v1.WireClient("foobar")
- header = client.get_header_for_cert()
- self.assertNotEquals(None, header)
-
- @mock(v1.WireClient, 'get_header_for_cert', MockFunc())
- @mock(v1.WireClient, 'fetch_config', mock_fetch_config)
- @mock(v1.WireClient, 'fetch_manifest', mock_fetch_manifest)
- @mock(v1.fileutil, 'write_file', MockFunc())
- def test_update_goal_state(self):
- client = v1.WireClient("foobar")
- client.update_goal_state()
- goal_state = client.get_goal_state()
- self.assertNotEquals(None, goal_state)
- hosting_env = client.get_hosting_env()
- self.assertNotEquals(None, hosting_env)
- shared_config = client.get_shared_conf()
- self.assertNotEquals(None, shared_config)
- ext_conf = client.get_ext_conf()
- self.assertNotEquals(None, ext_conf)
-
- @mock(v1.time, "sleep", MockFunc())
- def test_call_wireserver(self):
- client = v1.WireClient("foobar")
- self.assertRaises(v1.ProtocolError, client.call_wireserver, mock_403)
- self.assertRaises(v1.WireProtocolResourceGone, client.call_wireserver,
- mock_410)
-
- @mock(v1.time, "sleep", MockFunc())
- def test_call_storage_service(self):
- client = v1.WireClient("foobar")
- self.assertRaises(v1.ProtocolError, client.call_storage_service,
- mock_503)
-
-
-class TestStatusBlob(unittest.TestCase):
- def testToJson(self):
- vm_status = v1.VMStatus()
- status_blob = v1.StatusBlob(v1.WireClient("http://foo.bar/"))
- status_blob.set_vm_status(vm_status)
- self.assertNotEquals(None, status_blob.to_json())
-
- @mock(v1.restutil, 'http_put', MockFunc(retval=MockResp(httpclient.CREATED)))
- @mock(v1.restutil, 'http_head', MockFunc(retval=MockResp(httpclient.OK)))
- def test_put_page_blob(self):
- vm_status = v1.VMStatus()
- status_blob = v1.StatusBlob(v1.WireClient("http://foo.bar/"))
- status_blob.set_vm_status(vm_status)
- data = 'a' * 100
- status_blob.put_page_blob("http://foo.bar", data)
-
-class TestConvert(unittest.TestCase):
- def test_status(self):
- vm_status = v1.VMStatus()
- handler_status = v1.ExtHandlerStatus(name="foo")
-
- ext_statuses = {}
-
- ext_name="bar"
- ext_status = v1.ExtensionStatus()
- handler_status.extensions.append(ext_name)
- ext_statuses[ext_name] = ext_status
-
- substatus = v1.ExtensionSubStatus()
- ext_status.substatusList.append(substatus)
-
- vm_status.vmAgent.extensionHandlers.append(handler_status)
- v1_status = v1.vm_status_to_v1(vm_status, ext_statuses)
- print(v1_status)
-
- def test_param(self):
- param = v1.TelemetryEventParam()
- event = v1.TelemetryEvent()
- event.parameters.append(param)
-
- v1.event_to_v1(event)
-
-if __name__ == '__main__':
- unittest.main()
-
diff --git a/tests/test_v2.py b/tests/test_v2.py
deleted file mode 100644
index c4a0b4d..0000000
--- a/tests/test_v2.py
+++ /dev/null
@@ -1,120 +0,0 @@
-# Copyright 2014 Microsoft Corporation
-#
-# Licensed under the Apache License, Version 2.0 (the "License");
-# you may not use this file except in compliance with the License.
-# You may obtain a copy of the License at
-#
-# http://www.apache.org/licenses/LICENSE-2.0
-#
-# Unless required by applicable law or agreed to in writing, software
-# distributed under the License is distributed on an "AS IS" BASIS,
-# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-# See the License for the specific language governing permissions and
-# limitations under the License.
-#
-# Requires Python 2.4+ and Openssl 1.0+
-#
-# Implements parts of RFC 2131, 1541, 1497 and
-# http://msdn.microsoft.com/en-us/library/cc227282%28PROT.10%29.aspx
-# http://msdn.microsoft.com/en-us/library/cc227259%28PROT.13%29.aspx
-
-import tests.env
-from tests.tools import *
-import unittest
-import json
-import azurelinuxagent.protocol.v2 as v2
-
-SAMPLE_IDENTITY=u"""{
- "vmName":"foo",
- "subscriptionId":"bar"
-}"""
-
-SAMPLE_CERTS=u"""{
- "certificates":[{
- "name":"foo",
- "thumbprint":"bar",
- "certificateDataUri":"baz"
- }]
-}"""
-
-SAMPLE_EXT_HANDLER=u"""[{
- "name":"foo",
- "properties":{
- "version":"bar",
- "upgradePolicy": "manual",
- "state": "enabled",
- "extensions":[{
- "name":"baz",
- "sequenceNumber":0,
- "publicSettings":{
- "commandToExecute": "echo 123",
- "uris":[]
- }
- }]
- },
- "versionUris":[{
- "uri":"versionUri.foo"
- }]
-}]"""
-
-SAMPLE_EXT_HANDLER_PKGS=u"""{
- "versions": [{
- "version":"foo",
- "uris":[{
- "uri":"bar"
- },{
- "uri":"baz"
- }]
- }]
-}"""
-
-def mock_get_data(self, url, headers=None):
- data = u"{}"
- if url.count(u"identity") > 0:
- data = SAMPLE_IDENTITY
- elif url.count(u"certificates") > 0:
- data = SAMPLE_CERTS
- elif url.count(u"extensionHandlers") > 0:
- data = SAMPLE_EXT_HANDLER
- elif url.count(u"versionUri") > 0:
- data = SAMPLE_EXT_HANDLER_PKGS
- return json.loads(data)
-
-class TestMetadataProtocol(unittest.TestCase):
- @mock(v2.MetadataProtocol, '_get_data', mock_get_data)
- def test_getters(self):
- protocol = v2.MetadataProtocol()
- vminfo = protocol.get_vminfo()
- self.assertNotEquals(None, vminfo)
- self.assertNotEquals(None, vminfo.vmName)
- self.assertNotEquals(None, vminfo.subscriptionId)
-
- protocol.get_certs()
-
- ext_handers = protocol.get_ext_handlers()
- self.assertNotEquals(None, ext_handers)
- self.assertNotEquals(None, ext_handers.extHandlers)
- self.assertNotEquals(0, len(ext_handers.extHandlers))
-
- ext_hander = ext_handers.extHandlers[0]
- self.assertNotEquals(None, ext_hander)
- self.assertNotEquals(0, len(ext_hander.properties.extensions))
-
- ext = ext_hander.properties.extensions[0]
- self.assertNotEquals(None, ext)
- self.assertNotEquals(None, ext.publicSettings)
- self.assertEquals("echo 123", ext.publicSettings.get('commandToExecute'))
-
- packages = protocol.get_ext_handler_pkgs(ext_handers.extHandlers[0])
- self.assertNotEquals(None, packages)
-
- @mock(v2.MetadataProtocol, '_put_data', MockFunc())
- def test_reporters(self):
- protocol = v2.MetadataProtocol()
- protocol.report_provision_status(v2.ProvisionStatus())
- protocol.report_vm_status(v2.VMStatus())
- protocol.report_ext_status("foo", "baz", v2.ExtensionStatus())
- protocol.report_event(v2.TelemetryEventList())
-
-if __name__ == '__main__':
- unittest.main()
diff --git a/tests/test_version.py b/tests/test_version.py
deleted file mode 100644
index 72d9599..0000000
--- a/tests/test_version.py
+++ /dev/null
@@ -1,53 +0,0 @@
-# Copyright 2014 Microsoft Corporation
-#
-# Licensed under the Apache License, Version 2.0 (the "License");
-# you may not use this file except in compliance with the License.
-# You may obtain a copy of the License at
-#
-# http://www.apache.org/licenses/LICENSE-2.0
-#
-# Unless required by applicable law or agreed to in writing, software
-# distributed under the License is distributed on an "AS IS" BASIS,
-# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-# See the License for the specific language governing permissions and
-# limitations under the License.
-#
-# Requires Python 2.4+ and Openssl 1.0+
-#
-# Implements parts of RFC 2131, 1541, 1497 and
-# http://msdn.microsoft.com/en-us/library/cc227282%28PROT.10%29.aspx
-# http://msdn.microsoft.com/en-us/library/cc227259%28PROT.13%29.aspx
-
-import tests.env
-import tests.tools as tools
-import uuid
-import unittest
-import os
-import json
-import azurelinuxagent.protocol.v1 as v1
-from azurelinuxagent.future import text
-
-VersionInfoSample=u"""\
-<?xml version="1.0" encoding="utf-8"?>
-<Versions>
- <Preferred>
- <Version>2012-11-30</Version>
- </Preferred>
- <Supported>
- <Version>2010-12-15</Version>
- <Version>2010-28-10</Version>
- </Supported>
-</Versions>
-"""
-
-class TestVersionInfo(unittest.TestCase):
- def test_version_info(self):
- config = v1.VersionInfo(VersionInfoSample)
- self.assertEquals("2012-11-30", config.get_preferred())
- self.assertNotEquals(None, config.get_supported())
- self.assertEquals(2, len(config.get_supported()))
- self.assertEquals("2010-12-15", config.get_supported()[0])
- self.assertEquals("2010-28-10", config.get_supported()[1])
-
-if __name__ == '__main__':
- unittest.main()
diff --git a/tests/tools.py b/tests/tools.py
index 392f395..672c60b 100644
--- a/tests/tools.py
+++ b/tests/tools.py
@@ -18,48 +18,107 @@
# http://msdn.microsoft.com/en-us/library/cc227282%28PROT.10%29.aspx
# http://msdn.microsoft.com/en-us/library/cc227259%28PROT.13%29.aspx
+"""
+Define util functions for unit test
+"""
+import re
import os
import sys
+import unittest
+import shutil
+import json
+import tempfile
from functools import wraps
-from azurelinuxagent.utils.osutil import OSUTIL
-
-parent = os.path.dirname(os.path.dirname(os.path.abspath(__file__)))
-sys.path.append(parent)
-
-def simple_file_grep(file_path, search_str):
- for line in open(file_path):
- if search_str in line:
- return line
-
-def mock(target, name, mock):
- def decorator(func):
- @wraps(func)
- def wrapper(*args, **kwargs):
- origin = getattr(target, name)
- setattr(target, name, mock)
- try:
- result = func(*args, **kwargs)
- except:
- raise
- finally:
- setattr(target, name, origin)
- return result
- return wrapper
- return decorator
+import azurelinuxagent.conf as conf
+import azurelinuxagent.logger as logger
+import azurelinuxagent.event as event
+
+#Import mock module for Python2 and Python3
+try:
+ from unittest.mock import Mock, patch, MagicMock
+except ImportError:
+ from mock import Mock, patch, MagicMock
+
+test_dir = os.path.dirname(os.path.abspath(__file__))
+data_dir = os.path.join(test_dir, "data")
+
+debug = False
+if os.environ.get('DEBUG') == '1':
+ debug = True
+
+#Enable verbose logger to stdout
+if debug:
+ logger.add_logger_appender(logger.AppenderType.STDOUT,
+ logger.LogLevel.VERBOSE)
+
+class AgentTestCase(unittest.TestCase):
+ def setUp(self):
+ prefix = "{0}_".format(self.__class__.__name__)
+ self.tmp_dir = tempfile.mkdtemp(prefix=prefix)
+ conf.get_lib_dir = Mock(return_value=self.tmp_dir)
+ ext_log_dir = os.path.join(self.tmp_dir, "azure")
+ conf.get_ext_log_dir = Mock(return_value=ext_log_dir)
+
+ def tearDown(self):
+ if not debug and self.tmp_dir is not None:
+ shutil.rmtree(self.tmp_dir)
+
+def load_data(name):
+ """Load test data"""
+ path = os.path.join(data_dir, name)
+ with open(path, "r") as data_file:
+ return data_file.read()
-class MockFunc(object):
- def __init__(self, name='', retval=None):
- self.name = name
- self.retval = retval
+def load_bin_data(name):
+ """Load test bin data"""
+ path = os.path.join(data_dir, name)
+ with open(path, "rb") as data_file:
+ return data_file.read()
- def __call__(*args, **kwargs):
- self = args[0]
- self.args = args[1:]
- self.kwargs = kwargs
- return self.retval
+
+supported_distro = [
+ ["ubuntu", "12.04", ""],
+ ["ubuntu", "14.04", ""],
+ ["ubuntu", "14.10", ""],
+ ["ubuntu", "15.10", ""],
+ ["ubuntu", "15.10", "Snappy Ubuntu Core"],
+
+ ["coreos", "", ""],
+
+ ["suse", "12", "SUSE Linux Enterprise Server"],
+ ["suse", "13.2", "openSUSE"],
+ ["suse", "11", "SUSE Linux Enterprise Server"],
+ ["suse", "13.1", "openSUSE"],
+
+ ["debian", "6.0", ""],
+
+ ["redhat", "6.5", ""],
+ ["redhat", "7.0", ""],
+
+]
+
+def distros(distro_name=".*", distro_version=".*", distro_full_name=".*"):
+ """Run test on multiple distros"""
+ def decorator(test_method):
+ @wraps(test_method)
+ def wrapper(self, *args, **kwargs):
+ for distro in supported_distro:
+ if re.match(distro_name, distro[0]) and \
+ re.match(distro_version, distro[1]) and \
+ re.match(distro_full_name, distro[2]):
+ if debug:
+ logger.info("Run {0} on {1}", test_method.__name__,
+ distro)
+ new_args = []
+ new_args.extend(args)
+ new_args.extend(distro)
+ test_method(self, *new_args, **kwargs)
+ #Call tearDown and setUp to create seprated environment for
+ #distro testing
+ self.tearDown()
+ self.setUp()
+ return wrapper
+ return decorator
-#Mock osutil so that the test of other part will be os unrelated
-OSUTIL.get_lib_dir = MockFunc(retval='/tmp')
-OSUTIL.get_ext_log_dir = MockFunc(retval='/tmp/log')
diff --git a/azurelinuxagent/distro/oracle/__init__.py b/tests/utils/__init__.py
index d9b82f5..9bdb27e 100644
--- a/azurelinuxagent/distro/oracle/__init__.py
+++ b/tests/utils/__init__.py
@@ -1,5 +1,3 @@
-# Microsoft Azure Linux Agent
-#
# Copyright 2014 Microsoft Corporation
#
# Licensed under the Apache License, Version 2.0 (the "License");
@@ -16,4 +14,6 @@
#
# Requires Python 2.4+ and Openssl 1.0+
#
-
+# Implements parts of RFC 2131, 1541, 1497 and
+# http://msdn.microsoft.com/en-us/library/cc227282%28PROT.10%29.aspx
+# http://msdn.microsoft.com/en-us/library/cc227259%28PROT.13%29.aspx
diff --git a/tests/test_file_util.py b/tests/utils/test_file_util.py
index 1eeb784..bf7c638 100644
--- a/tests/test_file_util.py
+++ b/tests/utils/test_file_util.py
@@ -18,48 +18,48 @@
# http://msdn.microsoft.com/en-us/library/cc227282%28PROT.10%29.aspx
# http://msdn.microsoft.com/en-us/library/cc227259%28PROT.13%29.aspx
-import tests.env
-import tests.tools as tools
+from tests.tools import *
import uuid
import unittest
import os
import sys
-from azurelinuxagent.future import text
+from azurelinuxagent.future import ustr
import azurelinuxagent.utils.fileutil as fileutil
-class TestFileOperations(unittest.TestCase):
+class TestFileOperations(AgentTestCase):
def test_read_write_file(self):
- test_file='/tmp/test_file'
- content = text(uuid.uuid4())
+ test_file=os.path.join(self.tmp_dir, 'test_file')
+ content = ustr(uuid.uuid4())
fileutil.write_file(test_file, content)
- self.assertTrue(tools.simple_file_grep(test_file, content))
- content_read = fileutil.read_file('/tmp/test_file')
+ content_read = fileutil.read_file(test_file)
self.assertEquals(content, content_read)
os.remove(test_file)
def test_rw_utf8_file(self):
- test_file='/tmp/test_file3'
- content = "\u6211"
+ test_file=os.path.join(self.tmp_dir, 'test_file')
+ content = u"\u6211"
fileutil.write_file(test_file, content, encoding="utf-8")
- self.assertTrue(tools.simple_file_grep(test_file, content))
- content_read = fileutil.read_file('/tmp/test_file3')
+ content_read = fileutil.read_file(test_file)
self.assertEquals(content, content_read)
os.remove(test_file)
def test_remove_bom(self):
- test_file= '/tmp/test_file4'
+ test_file=os.path.join(self.tmp_dir, 'test_file')
data = b'\xef\xbb\xbfhehe'
fileutil.write_file(test_file, data, asbin=True)
data = fileutil.read_file(test_file, remove_bom=True)
self.assertNotEquals(0xbb, ord(data[0]))
def test_append_file(self):
- test_file='/tmp/test_file2'
- content = text(uuid.uuid4())
+ test_file=os.path.join(self.tmp_dir, 'test_file')
+ content = ustr(uuid.uuid4())
fileutil.append_file(test_file, content)
- self.assertTrue(tools.simple_file_grep(test_file, content))
+
+ content_read = fileutil.read_file(test_file)
+ self.assertEquals(content, content_read)
+
os.remove(test_file)
def test_get_last_path_element(self):
diff --git a/tests/utils/test_rest_util.py b/tests/utils/test_rest_util.py
new file mode 100644
index 0000000..bd22c55
--- /dev/null
+++ b/tests/utils/test_rest_util.py
@@ -0,0 +1,126 @@
+# Copyright 2014 Microsoft Corporation
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+#
+# Requires Python 2.4+ and Openssl 1.0+
+#
+# Implements parts of RFC 2131, 1541, 1497 and
+# http://msdn.microsoft.com/en-us/library/cc227282%28PROT.10%29.aspx
+# http://msdn.microsoft.com/en-us/library/cc227259%28PROT.13%29.aspx
+
+from tests.tools import AgentTestCase, patch, Mock, MagicMock
+import uuid
+import unittest
+import os
+import azurelinuxagent.utils.restutil as restutil
+from azurelinuxagent.future import ustr, httpclient
+import azurelinuxagent.logger as logger
+
+class TestHttpOperations(AgentTestCase):
+
+ def test_parse_url(self):
+ test_uri = "http://abc.def/ghi#hash?jkl=mn"
+ host, port, secure, rel_uri = restutil._parse_url(test_uri)
+ self.assertEquals("abc.def", host)
+ self.assertEquals("/ghi#hash?jkl=mn", rel_uri)
+
+ test_uri = "http://abc.def/"
+ host, port, secure, rel_uri = restutil._parse_url(test_uri)
+ self.assertEquals("abc.def", host)
+ self.assertEquals("/", rel_uri)
+ self.assertEquals(False, secure)
+
+ test_uri = "https://abc.def/ghi?jkl=mn"
+ host, port, secure, rel_uri = restutil._parse_url(test_uri)
+ self.assertEquals(True, secure)
+
+ test_uri = "http://abc.def:80/"
+ host, port, secure, rel_uri = restutil._parse_url(test_uri)
+ self.assertEquals("abc.def", host)
+
+ host, port, secure, rel_uri = restutil._parse_url("")
+ self.assertEquals(None, host)
+ self.assertEquals(rel_uri, "")
+
+ host, port, secure, rel_uri = restutil._parse_url("None")
+ self.assertEquals(None, host)
+ self.assertEquals(rel_uri, "None")
+
+
+ @patch("azurelinuxagent.future.httpclient.HTTPSConnection")
+ @patch("azurelinuxagent.future.httpclient.HTTPConnection")
+ def test_http_request(self, HTTPConnection, HTTPSConnection):
+ mock_httpconn = MagicMock()
+ mock_httpresp = MagicMock()
+ mock_httpconn.getresponse = Mock(return_value=mock_httpresp)
+ HTTPConnection.return_value = mock_httpconn
+ HTTPSConnection.return_value = mock_httpconn
+
+ mock_httpresp.read = Mock(return_value="_(:3| <)_")
+
+ #Test http get
+ resp = restutil._http_request("GET", "foo", "bar")
+ self.assertNotEquals(None, resp)
+ self.assertEquals("_(:3| <)_", resp.read())
+
+ #Test https get
+ resp = restutil._http_request("GET", "foo", "bar", secure=True)
+ self.assertNotEquals(None, resp)
+ self.assertEquals("_(:3| <)_", resp.read())
+
+ #Test http get with proxy
+ mock_httpresp.read = Mock(return_value="_(:3| <)_")
+ resp = restutil._http_request("GET", "foo", "bar", proxy_host="foo.bar",
+ proxy_port=23333)
+ self.assertNotEquals(None, resp)
+ self.assertEquals("_(:3| <)_", resp.read())
+
+ #Test https get
+ resp = restutil._http_request("GET", "foo", "bar", secure=True)
+ self.assertNotEquals(None, resp)
+ self.assertEquals("_(:3| <)_", resp.read())
+
+ #Test https get with proxy
+ mock_httpresp.read = Mock(return_value="_(:3| <)_")
+ resp = restutil._http_request("GET", "foo", "bar", proxy_host="foo.bar",
+ proxy_port=23333, secure=True)
+ self.assertNotEquals(None, resp)
+ self.assertEquals("_(:3| <)_", resp.read())
+
+ @patch("time.sleep")
+ @patch("azurelinuxagent.utils.restutil._http_request")
+ def test_http_request_with_retry(self, _http_request, sleep):
+ mock_httpresp = MagicMock()
+ mock_httpresp.read = Mock(return_value="hehe")
+ _http_request.return_value = mock_httpresp
+
+ #Test http get
+ resp = restutil.http_get("http://foo.bar")
+ self.assertEquals("hehe", resp.read())
+
+ #Test https get
+ resp = restutil.http_get("https://foo.bar")
+ self.assertEquals("hehe", resp.read())
+
+ #Test http failure
+ _http_request.side_effect = httpclient.HTTPException("Http failure")
+ self.assertRaises(restutil.HttpError, restutil.http_get, "http://foo.bar")
+
+ #Test http failure
+ _http_request.side_effect = IOError("IO failure")
+ self.assertRaises(restutil.HttpError, restutil.http_get, "http://foo.bar")
+
+if __name__ == '__main__':
+ unittest.main()
+if __name__ == '__main__':
+ unittest.main()
diff --git a/tests/test_shell_util.py b/tests/utils/test_shell_util.py
index 9f84c6d..aa89121 100644
--- a/tests/test_shell_util.py
+++ b/tests/utils/test_shell_util.py
@@ -19,16 +19,14 @@
# http://msdn.microsoft.com/en-us/library/cc227282%28PROT.10%29.aspx
# http://msdn.microsoft.com/en-us/library/cc227259%28PROT.13%29.aspx
-import tests.env
-import tests.tools as tools
+from tests.tools import *
import uuid
import unittest
import os
import azurelinuxagent.utils.shellutil as shellutil
import test
-from azurelinuxagent.future import text
-class TestrunCmd(unittest.TestCase):
+class TestrunCmd(AgentTestCase):
def test_run_get_output(self):
output = shellutil.run_get_output(u"ls /")
self.assertNotEquals(None, output)
diff --git a/tests/test_text_util.py b/tests/utils/test_text_util.py
index 5c0016c..0e8cc7d 100644
--- a/tests/test_text_util.py
+++ b/tests/utils/test_text_util.py
@@ -18,16 +18,15 @@
# http://msdn.microsoft.com/en-us/library/cc227282%28PROT.10%29.aspx
# http://msdn.microsoft.com/en-us/library/cc227259%28PROT.13%29.aspx
-import tests.env
from tests.tools import *
import uuid
import unittest
import os
-from azurelinuxagent.future import text
+from azurelinuxagent.future import ustr
import azurelinuxagent.utils.textutil as textutil
from azurelinuxagent.utils.textutil import Version
-class TestTextUtil(unittest.TestCase):
+class TestTextUtil(AgentTestCase):
def test_get_password_hash(self):
password_hash = textutil.gen_password_hash("asdf", 6, 10)
self.assertNotEquals(None, password_hash)
@@ -36,7 +35,7 @@ class TestTextUtil(unittest.TestCase):
def test_remove_bom(self):
#Test bom could be removed
- data = text(b'\xef\xbb\xbfhehe', encoding='utf-8')
+ data = ustr(b'\xef\xbb\xbfhehe', encoding='utf-8')
data = textutil.remove_bom(data)
self.assertNotEquals(0xbb, data[0])
@@ -45,7 +44,7 @@ class TestTextUtil(unittest.TestCase):
data = textutil.remove_bom(data)
self.assertEquals(u"h", data[0])
- def test_version_compare(self) :
+ def test_version_compare(self):
self.assertTrue(Version("1.0") < Version("1.1"))
self.assertTrue(Version("1.9") < Version("1.10"))
self.assertTrue(Version("1.9.9") < Version("1.10.0"))
@@ -61,6 +60,20 @@ class TestTextUtil(unittest.TestCase):
self.assertTrue(Version("1.9") < "1.10")
self.assertTrue("1.9" < Version("1.10"))
+
+ def test_get_bytes_from_pem(self):
+ content = ("-----BEGIN CERTIFICATE-----\n"
+ "certificate\n"
+ "-----END CERTIFICATE----\n")
+ base64_bytes = textutil.get_bytes_from_pem(content)
+ self.assertEquals("certificate", base64_bytes)
+
+
+ content = ("-----BEGIN PRIVATE KEY-----\n"
+ "private key\n"
+ "-----END PRIVATE Key-----\n")
+ base64_bytes = textutil.get_bytes_from_pem(content)
+ self.assertEquals("private key", base64_bytes)
if __name__ == '__main__':
unittest.main()