summaryrefslogtreecommitdiff
path: root/azurelinuxagent/distro
diff options
context:
space:
mode:
Diffstat (limited to 'azurelinuxagent/distro')
-rw-r--r--azurelinuxagent/distro/centos/__init__.py19
-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/loader.py28
-rw-r--r--azurelinuxagent/distro/coreos/osutil.py5
-rw-r--r--azurelinuxagent/distro/debian/distro.py (renamed from azurelinuxagent/distro/oracle/loader.py)10
-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/__init__.py19
-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
34 files changed, 1562 insertions, 1247 deletions
diff --git a/azurelinuxagent/distro/centos/__init__.py b/azurelinuxagent/distro/centos/__init__.py
deleted file mode 100644
index d9b82f5..0000000
--- a/azurelinuxagent/distro/centos/__init__.py
+++ /dev/null
@@ -1,19 +0,0 @@
-# Microsoft Azure Linux Agent
-#
-# Copyright 2014 Microsoft Corporation
-#
-# Licensed under the Apache License, Version 2.0 (the "License");
-# you may not use this file except in compliance with the License.
-# You may obtain a copy of the License at
-#
-# http://www.apache.org/licenses/LICENSE-2.0
-#
-# Unless required by applicable law or agreed to in writing, software
-# distributed under the License is distributed on an "AS IS" BASIS,
-# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-# See the License for the specific language governing permissions and
-# limitations under the License.
-#
-# Requires Python 2.4+ and Openssl 1.0+
-#
-
diff --git a/azurelinuxagent/distro/centos/loader.py b/azurelinuxagent/distro/centos/loader.py
deleted file mode 100644
index 9dc428f..0000000
--- a/azurelinuxagent/distro/centos/loader.py
+++ /dev/null
@@ -1,25 +0,0 @@
-# Microsoft Azure Linux Agent
-#
-# Copyright 2014 Microsoft Corporation
-#
-# Licensed under the Apache License, Version 2.0 (the "License");
-# you may not use this file except in compliance with the License.
-# You may obtain a copy of the License at
-#
-# http://www.apache.org/licenses/LICENSE-2.0
-#
-# Unless required by applicable law or agreed to in writing, software
-# distributed under the License is distributed on an "AS IS" BASIS,
-# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-# See the License for the specific language governing permissions and
-# limitations under the License.
-#
-# Requires Python 2.4+ and Openssl 1.0+
-#
-
-from azurelinuxagent.metadata import DISTRO_NAME, DISTRO_VERSION
-import azurelinuxagent.distro.redhat.loader as redhat
-
-def get_osutil():
- return redhat.get_osutil()
-
diff --git a/azurelinuxagent/distro/coreos/deprovision.py b/azurelinuxagent/distro/coreos/deprovision.py
index 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/loader.py b/azurelinuxagent/distro/coreos/loader.py
deleted file mode 100644
index 802f276..0000000
--- a/azurelinuxagent/distro/coreos/loader.py
+++ /dev/null
@@ -1,28 +0,0 @@
-# Microsoft Azure Linux Agent
-#
-# Copyright 2014 Microsoft Corporation
-#
-# Licensed under the Apache License, Version 2.0 (the "License");
-# you may not use this file except in compliance with the License.
-# You may obtain a copy of the License at
-#
-# http://www.apache.org/licenses/LICENSE-2.0
-#
-# Unless required by applicable law or agreed to in writing, software
-# distributed under the License is distributed on an "AS IS" BASIS,
-# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-# See the License for the specific language governing permissions and
-# limitations under the License.
-#
-# Requires Python 2.4+ and Openssl 1.0+
-#
-
-
-def get_osutil():
- from azurelinuxagent.distro.coreos.osutil import CoreOSUtil
- return CoreOSUtil()
-
-def get_handlers():
- from azurelinuxagent.distro.coreos.handlerFactory import CoreOSHandlerFactory
- return CoreOSHandlerFactory()
-
diff --git a/azurelinuxagent/distro/coreos/osutil.py b/azurelinuxagent/distro/coreos/osutil.py
index c244311..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/oracle/loader.py b/azurelinuxagent/distro/debian/distro.py
index 9dc428f..01f4e3e 100644
--- a/azurelinuxagent/distro/oracle/loader.py
+++ b/azurelinuxagent/distro/debian/distro.py
@@ -17,9 +17,11 @@
# Requires Python 2.4+ and Openssl 1.0+
#
-from azurelinuxagent.metadata import DISTRO_NAME, DISTRO_VERSION
-import azurelinuxagent.distro.redhat.loader as redhat
+from azurelinuxagent.distro.default.distro import DefaultDistro
+from azurelinuxagent.distro.debian.osutil import DebianOSUtil
-def get_osutil():
- return redhat.get_osutil()
+class DebianDistro(DefaultDistro):
+ def __init__(self):
+ super(DebianDistro, self).__init__()
+ self.osutil = DebianOSUtil()
diff --git a/azurelinuxagent/distro/default/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/__init__.py b/azurelinuxagent/distro/oracle/__init__.py
deleted file mode 100644
index d9b82f5..0000000
--- a/azurelinuxagent/distro/oracle/__init__.py
+++ /dev/null
@@ -1,19 +0,0 @@
-# Microsoft Azure Linux Agent
-#
-# Copyright 2014 Microsoft Corporation
-#
-# Licensed under the Apache License, Version 2.0 (the "License");
-# you may not use this file except in compliance with the License.
-# You may obtain a copy of the License at
-#
-# http://www.apache.org/licenses/LICENSE-2.0
-#
-# Unless required by applicable law or agreed to in writing, software
-# distributed under the License is distributed on an "AS IS" BASIS,
-# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-# See the License for the specific language governing permissions and
-# limitations under the License.
-#
-# Requires Python 2.4+ and Openssl 1.0+
-#
-
diff --git a/azurelinuxagent/distro/ubuntu/handlerFactory.py b/azurelinuxagent/distro/redhat/distro.py
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):