diff --git a/azurelinuxagent/distro/default/daemon.py b/azurelinuxagent/distro/default/daemon.py index aa7f4e6f32..744baf6fb7 100644 --- a/azurelinuxagent/distro/default/daemon.py +++ b/azurelinuxagent/distro/default/daemon.py @@ -79,11 +79,13 @@ def daemon(self): os.chdir(conf.get_lib_dir()) if conf.get_detect_scvmm_env(): - if self.distro.scvmm_handler.run(): - return + if self.distro.scvmm_handler.detect_scvmm_env(): + # scvmm install script is executed after detection + time.sleep(5) + sys.exit(0) self.distro.provision_handler.run() - + if conf.get_resourcedisk_format(): self.distro.resource_disk_handler.run() @@ -92,10 +94,10 @@ def daemon(self): 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() diff --git a/azurelinuxagent/distro/default/osutil.py b/azurelinuxagent/distro/default/osutil.py index 523202b7d8..06475b4fa7 100644 --- a/azurelinuxagent/distro/default/osutil.py +++ b/azurelinuxagent/distro/default/osutil.py @@ -277,15 +277,17 @@ def conf_sshd(self, disable_password): def get_dvd_device(self, dev_dir='/dev'): - patten=r'(sr[0-9]|hd[c-z]|cdrom[0-9]|cd[0-9])' - for dvd in [re.match(patten, dev) for dev in os.listdir(dev_dir)]: + pattern=r'(sr[0-9]|hd[c-z]|cdrom[0-9]|cd[0-9])' + for dvd in [re.match(pattern, dev) for dev in os.listdir(dev_dir)]: if dvd is not None: return "/dev/{0}".format(dvd.group(0)) raise OSUtilError("Failed to get dvd device") - def mount_dvd(self, max_retry=6, chk_err=True): - dvd = self.get_dvd_device() - mount_point = conf.get_dvd_mount_point() + def mount_dvd(self, max_retry=6, chk_err=True, dvd=None, mount_point=None): + if dvd is None: + dvd = self.get_dvd_device() + if mount_point is None: + 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 @@ -307,8 +309,9 @@ def mount_dvd(self, max_retry=6, chk_err=True): if chk_err: raise OSUtilError("Failed to mount dvd.") - def umount_dvd(self, chk_err=True): - mount_point = conf.get_dvd_mount_point() + def umount_dvd(self, chk_err=True, mount_point=None): + if mount_point is None: + 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.") @@ -319,7 +322,13 @@ def eject_dvd(self, chk_err=True): if chk_err and retcode != 0: raise OSUtilError("Failed to eject dvd: ret={0}".format(retcode)) - def load_atappix_mod(self): + def try_load_atapiix_mod(self): + try: + self.load_atapiix_mod() + except: + logger.warn("could not load ATAPI driver") + + def load_atapiix_mod(self): if self.is_atapiix_mod_loaded(): return ret, kern_version = shellutil.run_get_output("uname -r") diff --git a/azurelinuxagent/distro/default/scvmm.py b/azurelinuxagent/distro/default/scvmm.py index 4d083b4541..85277b192d 100644 --- a/azurelinuxagent/distro/default/scvmm.py +++ b/azurelinuxagent/distro/default/scvmm.py @@ -17,32 +17,55 @@ # Requires Python 2.4+ and Openssl 1.0+ # +import re import os import subprocess import azurelinuxagent.logger as logger +import azurelinuxagent.conf as conf VMM_CONF_FILE_NAME = "linuxosconfiguration.xml" -VMM_STARTUP_SCRIPT_NAME= "install" +VMM_STARTUP_SCRIPT_NAME = "install" class ScvmmHandler(object): def __init__(self, distro): self.distro = distro - def detect_scvmm_env(self): + def detect_scvmm_env(self, dev_dir='/dev/'): logger.info("Detecting Microsoft System Center VMM Environment") - self.distro.osutil.mount_dvd(max_retry=1, chk_err=False) - mount_point = self.distro.osutil.get_dvd_mount_point() - found = os.path.isfile(os.path.join(mount_point, VMM_CONF_FILE_NAME)) - if found: - self.start_scvmm_agent() - else: - self.distro.osutil.umount_dvd(chk_err=False) - return found + found = False + + # try to load the ATAPI driver, continue on failure + self.distro.osutil.try_load_atapiix_mod() - def start_scvmm_agent(self): - logger.info("Starting Microsoft System Center VMM Initialization " - "Process") - 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]) + # cycle through all available /dev/sr*|hd*|cdrom*|cd* looking for the scvmm configuration file + mount_point = conf.get_dvd_mount_point() + for dvds in [re.match(r'(sr[0-9]|hd[c-z]|cdrom[0-9]?|cd[0-9]+)', dev) for dev in os.listdir(dev_dir)]: + if dvds is None: + continue + dvd = dev_dir + dvds.group(0) + self.distro.osutil.mount_dvd(max_retry=1, chk_err=False, dvd=dvd, mount_point=mount_point) + conf_file_full = os.path.join(mount_point, VMM_CONF_FILE_NAME) + found = os.path.isfile(conf_file_full) + if found: + logger.info("Found VMM configuration at {0}".format(conf_file_full)) + install_script_full = os.path.join(mount_point, VMM_STARTUP_SCRIPT_NAME) + if os.path.exists(install_script_full): + logger.info("Found VMM install script at {0}".format(install_script_full)) + self.start_scvmm_agent(mount_point=mount_point, startup_script=install_script_full) + break + else: + logger.warn("Microsoft System Center VMM install script was not found at {0}".format(install_script_full)) + else: + self.distro.osutil.umount_dvd(chk_err=False, mount_point=mount_point) + + return found + def start_scvmm_agent(self, mount_point=None, startup_script=None): + logger.info("Starting VMM Initialization Process") + if mount_point is None: + mount_point = conf.get_dvd_mount_point() + if startup_script is None: + startup_script = os.path.join(mount_point, VMM_STARTUP_SCRIPT_NAME) + script_commands = ["/bin/bash", startup_script, "-p " + mount_point] + logger.info("Starting VMM install process with {0}".format(script_commands)) + subprocess.Popen(script_commands) diff --git a/tests/distro/test_scvmm.py b/tests/distro/test_scvmm.py new file mode 100644 index 0000000000..c8b5795099 --- /dev/null +++ b/tests/distro/test_scvmm.py @@ -0,0 +1,85 @@ +# Copyright 2014 Microsoft Corporation +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +# +# Requires Python 2.4+ and Openssl 1.0+ +# +# Implements parts of RFC 2131, 1541, 1497 and +# http://msdn.microsoft.com/en-us/library/cc227282%28PROT.10%29.aspx +# http://msdn.microsoft.com/en-us/library/cc227259%28PROT.13%29.aspx + +import azurelinuxagent.distro.default.scvmm as scvmm +import azurelinuxagent.distro.default.osutil as osutil +import mock +from tests.tools import * +from azurelinuxagent.distro.loader import get_distro +from azurelinuxagent.distro.default.protocolUtil import * + + +class TestSCVMM(AgentTestCase): + def test_scvmm_detection_with_file(self): + # setup + conf.get_dvd_mount_point = Mock(return_value=self.tmp_dir) + conf.get_detect_scvmm_env = Mock(return_value=True) + scvmm_file = os.path.join(self.tmp_dir, scvmm.VMM_CONF_FILE_NAME) + fileutil.write_file(scvmm_file, "") + + patch = mock.patch.object(scvmm.ScvmmHandler, 'start_scvmm_agent').start() + + # execute + get_distro().daemon_handler.daemon() + + # assert + patch.assert_called() + + # cleanup + os.remove(scvmm_file) + + + def test_scvmm_detection_with_multiple_cdroms(self): + # setup + conf.get_dvd_mount_point = Mock(return_value=self.tmp_dir) + conf.get_detect_scvmm_env = Mock(return_value=True) + + patch_mount = mock.patch.object(osutil.DefaultOSUtil, 'mount_dvd').start() + + # execute + with patch('os.listdir', return_value=["sr0", "sr1", "sr2"]): + scvmm.ScvmmHandler(get_distro()).detect_scvmm_env() + + # assert + assert patch_mount.call_count == 3 + assert patch_mount.call_args_list[0][1]['dvd'] == '/dev/sr0' + assert patch_mount.call_args_list[1][1]['dvd'] == '/dev/sr1' + assert patch_mount.call_args_list[2][1]['dvd'] == '/dev/sr2' + + + def test_scvmm_detection_without_file(self): + # setup + conf.get_dvd_mount_point = Mock(return_value=self.tmp_dir) + conf.get_detect_scvmm_env = Mock(return_value=True) + scvmm_file = os.path.join(self.tmp_dir, scvmm.VMM_CONF_FILE_NAME) + if os.path.exists(scvmm_file): + os.remove(scvmm_file) + + patch_start = mock.patch.object(scvmm.ScvmmHandler, 'start_scvmm_agent').start() + + # execute + scvmm.ScvmmHandler(get_distro()).detect_scvmm_env() + + # assert + patch_start.assert_not_called() + + +if __name__ == '__main__': + unittest.main()