From 8822100158489d8f7fb5c38d440489df5df62996 Mon Sep 17 00:00:00 2001 From: Joe VLcek Date: Tue, 17 Jul 2012 15:10:10 -0400 Subject: Added RHEVm and vSphere support as source AltCloud --- cloudinit/settings.py | 1 + cloudinit/sources/DataSourceAltCloud.py | 302 ++++++++++++++ tests/unittests/test_datasource/test_altcloud.py | 498 +++++++++++++++++++++++ 3 files changed, 801 insertions(+) create mode 100644 cloudinit/sources/DataSourceAltCloud.py create mode 100644 tests/unittests/test_datasource/test_altcloud.py diff --git a/cloudinit/settings.py b/cloudinit/settings.py index 2083cf60..28340e29 100644 --- a/cloudinit/settings.py +++ b/cloudinit/settings.py @@ -29,6 +29,7 @@ CLOUD_CONFIG = '/etc/cloud/cloud.cfg' # What u get if no config is provided CFG_BUILTIN = { 'datasource_list': [ + 'AltCloud', 'NoCloud', 'ConfigDrive', 'OVF', diff --git a/cloudinit/sources/DataSourceAltCloud.py b/cloudinit/sources/DataSourceAltCloud.py new file mode 100644 index 00000000..121a3c48 --- /dev/null +++ b/cloudinit/sources/DataSourceAltCloud.py @@ -0,0 +1,302 @@ +# vi: ts=4 expandtab +# +# Copyright (C) 2009-2010 Canonical Ltd. +# Copyright (C) 2012 Hewlett-Packard Development Company, L.P. +# Copyright (C) 2012 Yahoo! Inc. +# +# Author: Scott Moser +# Author: Juerg Hafliger +# Author: Joshua Harlow +# +# This program is free software: you can redistribute it and/or modify +# it under the terms of the GNU General Public License version 3, as +# published by the Free Software Foundation. +# +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. +# +# You should have received a copy of the GNU General Public License +# along with this program. If not, see . + +import errno +import os +import os.path +import subprocess + +from cloudinit import log as logging +from cloudinit import sources +from cloudinit import util +from cloudinit.util import ProcessExecutionError + +LOG = logging.getLogger(__name__) + +''' +Needed file paths +''' +CLOUD_INFO_FILE = '/etc/sysconfig/cloud-info' +MEDIA_DIR = '/media/userdata' + +# Deltacloud file name contains deltacloud. Those not using +# Deltacloud but instead instrumenting the injection, could +# drop deltacloud from the file name. +DELTACLOUD_USER_DATA_FILE = MEDIA_DIR + '/deltacloud-user-data.txt' +USER_DATA_FILE = MEDIA_DIR + '/user-data.txt' + +''' +Shell command lists +''' +CMD_DMI_SYSTEM = ['/usr/sbin/dmidecode', '--string', 'system-product-name'] +CMD_PROBE_FLOPPY = ['/sbin/modprobe', 'floppy'] +CMD_MNT_FLOPPY = ['/bin/mount', '/dev/fd0', MEDIA_DIR] +CMD_MNT_CDROM = ['/bin/mount', '/dev/cdrom', MEDIA_DIR] + +META_DATA_NOT_SUPPORTED = { + 'block-device-mapping' : {}, + 'instance-id' : 455, + 'local-hostname' : 'localhost', + 'placement': {}, + } + +class DataSourceAltCloud(sources.DataSource): + def __init__(self, sys_cfg, distro, paths): + sources.DataSource.__init__(self, sys_cfg, distro, paths) + self.dsmode = 'local' + self.seed = None + self.cmdline_id = "ds=nocloud" + self.seed_dir = os.path.join(paths.seed_dir, 'nocloud') + self.supported_seed_starts = ("/", "file://") + + def __str__(self): + mstr = "%s [seed=%s][dsmode=%s]" % (util.obj_name(self), + self.seed, self.dsmode) + return mstr + + def get_cloud_type(self): + ''' + Description: + Get the type for the cloud back end this instance is running on + by examining the string returned by: + dmidecode --string system-product-name + + On VMWare/vSphere dmidecode returns: RHEV Hypervisor + On VMWare/vSphere dmidecode returns: VMware Virtual Platform + + Input: + None + + Returns: + One of the following strings: + 'RHEV', 'VSPHERE' or 'UNKNOWN' + + ''' + + cmd = CMD_DMI_SYSTEM + try: + (cmd_out, _err) = util.subp(cmd) + except ProcessExecutionError, _err: + LOG.debug(('Failed command: %s\n%s') % \ + (' '.join(cmd), _err.message)) + return 'UNKNOWN' + except OSError, _err: + LOG.debug(('Failed command: %s\n%s') % \ + (' '.join(cmd), _err.message)) + return 'UNKNOWN' + + if cmd_out.upper().startswith('RHEV'): + return 'RHEV' + + if cmd_out.upper().startswith('VMWARE'): + return 'VSPHERE' + + return 'UNKNOWN' + + def get_data(self): + ''' + Description: + User Data is passed to the launching instance which + + Cloud providers expose the user data differently. + It is necessary to determine which cloud provider + the current instance is running on to determine + how to access the user data. Images built with + image factory will contain a CLOUD_INFO_FILE which + contains a string identifying the cloud provider. + + Images not built with Imagefactory will try to + determine what the cloud provider is based on system + information. + ''' + + LOG.debug('Invoked get_data()') + + if os.path.exists(CLOUD_INFO_FILE): + try: + cloud_info = open(CLOUD_INFO_FILE) + cloud_type = cloud_info.read().strip().upper() + cloud_info.close() + except: + util.logexc(LOG, 'Unable to access cloud info file.') + return False + else: + cloud_type = self.get_cloud_type() + + LOG.debug('cloud_type: ' + str(cloud_type)) + + if 'RHEV' in cloud_type: + return self.user_data_rhevm() + elif 'VSPHERE' in cloud_type: + return self.user_data_vsphere() + else: + # there was no recognized alternate cloud type. + # suggesting this handler should not be used. + return False + + def user_data_rhevm(self): + ''' + RHEVM specific userdata read + + If on RHEV-M the user data will be contained on the + floppy device in file + To access it: + modprobe floppy + mkdir + mount /dev/fd0 + mount /dev/fd0 # NOTE: -> /dev/ + read / + ''' + + # modprobe floppy + try: + cmd = CMD_PROBE_FLOPPY + (cmd_out, _err) = util.subp(cmd) + except ProcessExecutionError, _err: + util.logexc(LOG, (('Failed command: %s\n%s') % \ + (' '.join(cmd), _err.message))) + return False + except OSError, _err: + util.logexc(LOG, (('Failed command: %s\n%s') % \ + (' '.join(cmd), _err.message))) + return False + + # mkdir dir just in case it isn't already. + try: + os.makedirs(MEDIA_DIR) + except OSError, (errno, strerror): + if errno is not 17: + LOG.debug(('makedirs() failed: %s \nError: %s') % \ + (errno, strerror)) + return False + + # mount /dev/fd0 + try: + cmd = CMD_MNT_FLOPPY + (cmd_out, _err) = util.subp(cmd) + except ProcessExecutionError, _err: + # Ignore failure: already mounted + if 'ALREADY MOUNTED' not in _err.message.upper(): + util.logexc(LOG, (('Failed command: %s\n%s') % \ + (' '.join(cmd), _err.message))) + return False + except OSError, _err: + util.logexc(LOG, (('Failed command: %s\n%s') % \ + (' '.join(cmd), _err.message))) + return False + + # This could be done using "with open()" but that's not available + # in Python 2.4 as used on RHEL5 + # First try DELTACLOUD_USER_DATA_FILE. If that fails then try + # USER_DATA_FILE. + try: + user_data_file = open(DELTACLOUD_USER_DATA_FILE, 'r') + user_data = user_data_file.read().strip() + user_data_file.close() + except: + try: + user_data_file = open(USER_DATA_FILE, 'r') + user_data = user_data_file.read().strip() + user_data_file.close() + except: + util.logexc(LOG, ('Failed accessing RHEVm user data file.')) + try: + user_data_file.close() + except: + pass + return False + + self.userdata_raw = user_data + self.metadata = META_DATA_NOT_SUPPORTED + + return True + + def user_data_vsphere(self): + ''' + VSphere specific userdata read + + If on vSphere the user data will be contained on the + floppy device in file + To access it: + mkdir dir just in case it isn't already. + mount /dev/cdrom # NOTE: -> /dev/cdrom + read / + ''' + + # mkdir dir just in case it isn't already. + try: + os.makedirs(MEDIA_DIR) + except OSError, (errno, strerror): + if errno is not 17: + LOG.debug(('makedirs() failed: %s \nError: %s') % \ + (errno, strerror)) + return False + + # mount /dev/cdrom + try: + cmd = CMD_MNT_CDROM + (cmd_out, _err) = util.subp(cmd) + except ProcessExecutionError, _err: + # Ignore failure: already mounted + if 'ALREADY MOUNTED' not in _err.message.upper(): + LOG.debug(('Failed command: %s\n%s') % \ + (' '.join(cmd), _err.message)) + return False + except OSError, _err: + LOG.debug(('Failed command: %s\n%s') % \ + (' '.join(cmd), _err.message)) + return False + + # This could be done using "with open()" but that's not available + # in Python 2.4 as used on RHEL5 + # First try DELTACLOUD_USER_DATA_FILE. If that fails then try + # USER_DATA_FILE. + try: + user_data_file = open(DELTACLOUD_USER_DATA_FILE, 'r') + user_data = user_data_file.read().strip() + user_data_file.close() + except: + try: + user_data_file = open(USER_DATA_FILE, 'r') + user_data = user_data_file.read().strip() + user_data_file.close() + except: + LOG.debug('Failed accessing vSphere user data file.') + try: + user_data_file.close() + except: + pass + return False + + self.userdata_raw = user_data + self.metadata = META_DATA_NOT_SUPPORTED + return True + +# Used to match classes to dependencies +datasources = [ + (DataSourceAltCloud, (sources.DEP_FILESYSTEM, )), +] + + +# Return a list of data sources that match this set of dependencies +def get_datasource_list(depends): + return sources.list_from_depends(depends, datasources) diff --git a/tests/unittests/test_datasource/test_altcloud.py b/tests/unittests/test_datasource/test_altcloud.py new file mode 100644 index 00000000..d404fab9 --- /dev/null +++ b/tests/unittests/test_datasource/test_altcloud.py @@ -0,0 +1,498 @@ +#! /usr/bin/env python + +import os +import stat +import tempfile + +from shutil import rmtree +from tempfile import mkdtemp +from unittest import TestCase + +from time import sleep + +from cloudinit import helpers + +# Get the cloudinit.sources.DataSourceAltCloud import items needed. +import cloudinit.sources.DataSourceAltCloud +from cloudinit.sources.DataSourceAltCloud import DataSourceAltCloud + +def _write_cloud_info_file(value): + ''' + Populate the CLOUD_INFO_FILE which would be populated + with a cloud backend identifier ImageFactory when building + an image with ImageFactory. + ''' + f = open(cloudinit.sources.DataSourceAltCloud.CLOUD_INFO_FILE, 'w') + f.write(value) + f.close() + os.chmod(cloudinit.sources.DataSourceAltCloud.CLOUD_INFO_FILE, 0664) + +def _remove_cloud_info_file(): + ''' + Remove the test CLOUD_INFO_FILE + ''' + os.remove(cloudinit.sources.DataSourceAltCloud.CLOUD_INFO_FILE) + +def _write_user_data_files(value): + ''' + Populate the DELTACLOUD_USER_DATA_FILE the USER_DATA_FILE + which would be populated with user data. + ''' + f = open(cloudinit.sources.DataSourceAltCloud.DELTACLOUD_USER_DATA_FILE, 'w') + f.write(value) + f.close() + os.chmod(cloudinit.sources.DataSourceAltCloud.DELTACLOUD_USER_DATA_FILE, 0664) + + f = open(cloudinit.sources.DataSourceAltCloud.USER_DATA_FILE, 'w') + f.write(value) + f.close() + os.chmod(cloudinit.sources.DataSourceAltCloud.USER_DATA_FILE, 0664) + +def _remove_user_data_files(): + ''' + Remove the test files: DELTACLOUD_USER_DATA_FILE and + USER_DATA_FILE + ''' + os.remove(cloudinit.sources.DataSourceAltCloud.DELTACLOUD_USER_DATA_FILE) + os.remove(cloudinit.sources.DataSourceAltCloud.USER_DATA_FILE) + +class TestDataSouceAltCloud_get_cloud_type(TestCase): + ''' + Test to exercise method: DataSourceAltCloud.get_cloud_type() + ''' + + def setUp(self): + ''' Set up ''' + self.paths = helpers.Paths({ 'cloud_dir': '/tmp' }) + + def tearDown(self): + # Reset + cloudinit.sources.DataSourceAltCloud.CMD_DMI_SYSTEM = \ + ['dmidecode', '--string', 'system-product-name'] + + def test_get_cloud_type_RHEV(self): + ''' + Test method get_cloud_type() for RHEVm systems. + Forcing dmidecode return to match a RHEVm system: RHEV Hypervisor + ''' + cloudinit.sources.DataSourceAltCloud.CMD_DMI_SYSTEM = \ + ['echo', 'RHEV Hypervisor'] + ds = DataSourceAltCloud({}, None, self.paths) + self.assertEquals('RHEV', \ + ds.get_cloud_type()) + + def test_get_cloud_type_VSPHERE(self): + ''' + Test method get_cloud_type() for vSphere systems. + Forcing dmidecode return to match a vSphere system: RHEV Hypervisor + ''' + cloudinit.sources.DataSourceAltCloud.CMD_DMI_SYSTEM = \ + ['echo', 'VMware Virtual Platform'] + ds = DataSourceAltCloud({}, None, self.paths) + self.assertEquals('VSPHERE', \ + ds.get_cloud_type()) + + def test_get_cloud_type_UNKNOWN(self): + ''' + Test method get_cloud_type() for unknown systems. + Forcing dmidecode return to match an unrecognized return. + ''' + cloudinit.sources.DataSourceAltCloud.CMD_DMI_SYSTEM = \ + ['echo', 'Unrecognized Platform'] + ds = DataSourceAltCloud({}, None, self.paths) + self.assertEquals('UNKNOWN', \ + ds.get_cloud_type()) + + def test_get_cloud_type_exception1(self): + ''' + Test method get_cloud_type() where command dmidecode fails. + ''' + cloudinit.sources.DataSourceAltCloud.CMD_DMI_SYSTEM = \ + ['ls', 'bad command'] + ds = DataSourceAltCloud({}, None, self.paths) + self.assertEquals('UNKNOWN', \ + ds.get_cloud_type()) + + def test_get_cloud_type_exception(self): + ''' + Test method get_cloud_type() where command dmidecode is not available. + ''' + cloudinit.sources.DataSourceAltCloud.CMD_DMI_SYSTEM = \ + ['bad command'] + ds = DataSourceAltCloud({}, None, self.paths) + self.assertEquals('UNKNOWN', \ + ds.get_cloud_type()) + +class TestDataSouceAltCloud_get_data_cloud_info_file(TestCase): + ''' + Test to exercise method: DataSourceAltCloud.get_data() + With a contrived CLOUD_INFO_FILE + ''' + def setUp(self): + ''' Set up ''' + self.paths = helpers.Paths({ 'cloud_dir': '/tmp' }) + cloudinit.sources.DataSourceAltCloud.CLOUD_INFO_FILE = \ + '/tmp/cloudinit_test_etc_sysconfig_cloud-info' + + def tearDown(self): + # Reset + cloudinit.sources.DataSourceAltCloud.CLOUD_INFO_FILE = \ + CLOUD_INFO_FILE = '/etc/sysconfig/cloud-info' + + def test_get_data_RHEV_cloud_file(self): + '''Success Test module get_data() forcing RHEV ''' + + _write_cloud_info_file('RHEV') + ds = DataSourceAltCloud({}, None, self.paths) + ds.user_data_rhevm = lambda : True + self.assertEquals(True, ds.get_data()) + + def test_get_data_VSPHERE_cloud_file(self): + '''Success Test module get_data() forcing VSPHERE ''' + + _write_cloud_info_file('VSPHERE') + ds = DataSourceAltCloud({}, None, self.paths) + ds.user_data_vsphere = lambda : True + self.assertEquals(True, ds.get_data()) + + def test_failure_get_data_RHEV_cloud_file(self): + '''Failure Test module get_data() forcing RHEV ''' + + _write_cloud_info_file('RHEV') + ds = DataSourceAltCloud({}, None, self.paths) + ds.user_data_rhevm = lambda : False + self.assertEquals(False, ds.get_data()) + + def test_failure_get_data_VSPHERE_cloud_file(self): + '''Failure Test module get_data() forcing VSPHERE ''' + + _write_cloud_info_file('VSPHERE') + ds = DataSourceAltCloud({}, None, self.paths) + ds.user_data_vsphere = lambda : False + self.assertEquals(False, ds.get_data()) + + def test_failure_get_data_unrecognized_cloud_file(self): + '''Failure Test module get_data() forcing unrecognized ''' + + _write_cloud_info_file('unrecognized') + ds = DataSourceAltCloud({}, None, self.paths) + self.assertEquals(False, ds.get_data()) + +class TestDataSouceAltCloud_get_data_no_cloud_info_file(TestCase): + ''' + Test to exercise method: DataSourceAltCloud.get_data() + Without a CLOUD_INFO_FILE + ''' + def setUp(self): + ''' Set up ''' + self.paths = helpers.Paths({ 'cloud_dir': '/tmp' }) + cloudinit.sources.DataSourceAltCloud.CLOUD_INFO_FILE = \ + 'no such file' + + def tearDown(self): + # Reset + cloudinit.sources.DataSourceAltCloud.CLOUD_INFO_FILE = \ + CLOUD_INFO_FILE = '/etc/sysconfig/cloud-info' + cloudinit.sources.DataSourceAltCloud.CMD_DMI_SYSTEM = \ + ['dmidecode', '--string', 'system-product-name'] + + def test_get_data_RHEV_cloud_file(self): + '''Test No cloud info file module get_data() forcing RHEV ''' + + cloudinit.sources.DataSourceAltCloud.CMD_DMI_SYSTEM = \ + ['echo', 'RHEV Hypervisor'] + ds = DataSourceAltCloud({}, None, self.paths) + ds.user_data_rhevm = lambda : True + self.assertEquals(True, ds.get_data()) + + def test_get_data_VSPHERE_cloud_file(self): + '''Test No cloud info file module get_data() forcing VSPHERE ''' + + cloudinit.sources.DataSourceAltCloud.CMD_DMI_SYSTEM = \ + ['echo', 'VMware Virtual Platform'] + ds = DataSourceAltCloud({}, None, self.paths) + ds.user_data_vsphere = lambda : True + self.assertEquals(True, ds.get_data()) + + def test_failure_get_data_VSPHERE_cloud_file(self): + '''Test No cloud info file module get_data() forcing unrecognized ''' + + cloudinit.sources.DataSourceAltCloud.CMD_DMI_SYSTEM = \ + ['echo', 'Unrecognized Platform'] + ds = DataSourceAltCloud({}, None, self.paths) + self.assertEquals(False, ds.get_data()) + +class TestDataSouceAltCloud_user_data_rhevm(TestCase): + ''' + Test to exercise method: DataSourceAltCloud.user_data_rhevm() + ''' + def setUp(self): + ''' Set up ''' + self.paths = helpers.Paths({ 'cloud_dir': '/tmp' }) + cloudinit.sources.DataSourceAltCloud.CLOUD_INFO_FILE = \ + '/tmp/cloudinit_test_etc_sysconfig_cloud-info' + cloudinit.sources.DataSourceAltCloud.MEDIA_DIR = \ + '/tmp/cloudinit_test_media' + cloudinit.sources.DataSourceAltCloud.DELTACLOUD_USER_DATA_FILE = \ + cloudinit.sources.DataSourceAltCloud.MEDIA_DIR + \ + '/deltacloud-user-data.txt' + cloudinit.sources.DataSourceAltCloud.USER_DATA_FILE = \ + cloudinit.sources.DataSourceAltCloud.MEDIA_DIR + \ + '/user-data.txt' + + try: + os.mkdir(cloudinit.sources.DataSourceAltCloud.MEDIA_DIR) + except OSError, (errno, strerror): + # Ignore OSError: [Errno 17] File exists: + if errno is not 17: + raise + + _write_user_data_files('test user data') + + def tearDown(self): + # Reset + + _remove_user_data_files() + + cloudinit.sources.DataSourceAltCloud.CLOUD_INFO_FILE = \ + CLOUD_INFO_FILE = '/etc/sysconfig/cloud-info' + cloudinit.sources.DataSourceAltCloud.MEDIA_DIR = '/media' + + cloudinit.sources.DataSourceAltCloud.DELTACLOUD_USER_DATA_FILE = \ + cloudinit.sources.DataSourceAltCloud.MEDIA_DIR + '/deltacloud-user-data.txt' + + cloudinit.sources.DataSourceAltCloud.USER_DATA_FILE = \ + cloudinit.sources.DataSourceAltCloud.MEDIA_DIR + '/user-data.txt' + + cloudinit.sources.DataSourceAltCloud.CMD_PROBE_FLOPPY = \ + ['/sbin/modprobe', 'floppy'] + cloudinit.sources.DataSourceAltCloud.CMD_MNT_FLOPPY = \ + ['/bin/mount', '/dev/fd0', cloudinit.sources.DataSourceAltCloud.MEDIA_DIR] + + def test_user_data_rhevm(self): + '''Test user_data_rhevm() ''' + + cloudinit.sources.DataSourceAltCloud.MEDIA_DIR = \ + '/tmp/cloudinit_test_media' + cloudinit.sources.DataSourceAltCloud.DELTACLOUD_USER_DATA_FILE = \ + cloudinit.sources.DataSourceAltCloud.MEDIA_DIR + '/deltacloud-user-data.txt' + + cloudinit.sources.DataSourceAltCloud.CMD_PROBE_FLOPPY = \ + ['echo', 'modprobe floppy'] + cloudinit.sources.DataSourceAltCloud.CMD_MNT_FLOPPY = \ + ['echo', 'floppy mounted'] + + ds = DataSourceAltCloud({}, None, self.paths) + + self.assertEquals(True, ds.user_data_rhevm()) + + def test_user_data_rhevm_modprobe_fails(self): + '''Test user_data_rhevm() where modprobe fails. ''' + + cloudinit.sources.DataSourceAltCloud.MEDIA_DIR = \ + '/tmp/cloudinit_test_media' + cloudinit.sources.DataSourceAltCloud.DELTACLOUD_USER_DATA_FILE = \ + cloudinit.sources.DataSourceAltCloud.MEDIA_DIR + '/deltacloud-user-data.txt' + + cloudinit.sources.DataSourceAltCloud.CMD_PROBE_FLOPPY = \ + ['ls', 'modprobe floppy'] + cloudinit.sources.DataSourceAltCloud.CMD_MNT_FLOPPY = \ + ['echo', 'floppy mounted'] + + ds = DataSourceAltCloud({}, None, self.paths) + + self.assertEquals(False, ds.user_data_rhevm()) + + def test_user_data_rhevm_no_modprobe_cmd(self): + '''Test user_data_rhevm() with no modprobe command. ''' + + cloudinit.sources.DataSourceAltCloud.MEDIA_DIR = \ + '/tmp/cloudinit_test_media' + cloudinit.sources.DataSourceAltCloud.DELTACLOUD_USER_DATA_FILE = \ + cloudinit.sources.DataSourceAltCloud.MEDIA_DIR + '/deltacloud-user-data.txt' + + cloudinit.sources.DataSourceAltCloud.CMD_PROBE_FLOPPY = \ + ['bad command', 'modprobe floppy'] + cloudinit.sources.DataSourceAltCloud.CMD_MNT_FLOPPY = \ + ['echo', 'floppy mounted'] + + ds = DataSourceAltCloud({}, None, self.paths) + + self.assertEquals(False, ds.user_data_rhevm()) + + def test_user_data_rhevm_mount_fails(self): + '''Test user_data_rhevm() where mount fails. ''' + + cloudinit.sources.DataSourceAltCloud.MEDIA_DIR = \ + '/tmp/cloudinit_test_media' + cloudinit.sources.DataSourceAltCloud.DELTACLOUD_USER_DATA_FILE = \ + cloudinit.sources.DataSourceAltCloud.MEDIA_DIR + '/deltacloud-user-data.txt' + + cloudinit.sources.DataSourceAltCloud.CMD_PROBE_FLOPPY = \ + ['echo', 'modprobe floppy'] + cloudinit.sources.DataSourceAltCloud.CMD_MNT_FLOPPY = \ + ['ls', 'floppy mounted'] + + ds = DataSourceAltCloud({}, None, self.paths) + + self.assertEquals(False, ds.user_data_rhevm()) + + def test_user_data_rhevm_no_user_data_file(self): + '''Test user_data_rhevm() with no user data files.''' + + cloudinit.sources.DataSourceAltCloud.MEDIA_DIR = \ + '/tmp/cloudinit_test_media' + cloudinit.sources.DataSourceAltCloud.DELTACLOUD_USER_DATA_FILE = \ + cloudinit.sources.DataSourceAltCloud.MEDIA_DIR + '/not-user-data.txt' + cloudinit.sources.DataSourceAltCloud.USER_DATA_FILE = \ + cloudinit.sources.DataSourceAltCloud.MEDIA_DIR + '/not-user-data.txt' + + cloudinit.sources.DataSourceAltCloud.CMD_PROBE_FLOPPY = \ + ['echo', 'modprobe floppy'] + cloudinit.sources.DataSourceAltCloud.CMD_MNT_FLOPPY = \ + ['echo', 'floppy mounted'] + + ds = DataSourceAltCloud({}, None, self.paths) + + self.assertEquals(False, ds.user_data_rhevm()) + + cloudinit.sources.DataSourceAltCloud.DELTACLOUD_USER_DATA_FILE = \ + cloudinit.sources.DataSourceAltCloud.MEDIA_DIR + '/deltacloud-user-data.txt' + cloudinit.sources.DataSourceAltCloud.USER_DATA_FILE = \ + cloudinit.sources.DataSourceAltCloud.MEDIA_DIR + '/user-data.txt' + + def test_user_data_rhevm_no_user_data_file(self): + '''Test user_data_rhevm() with no deltacloud user data file.''' + + cloudinit.sources.DataSourceAltCloud.MEDIA_DIR = \ + '/tmp/cloudinit_test_media' + cloudinit.sources.DataSourceAltCloud.DELTACLOUD_USER_DATA_FILE = \ + cloudinit.sources.DataSourceAltCloud.MEDIA_DIR + '/not-user-data.txt' + + cloudinit.sources.DataSourceAltCloud.CMD_PROBE_FLOPPY = \ + ['echo', 'modprobe floppy'] + cloudinit.sources.DataSourceAltCloud.CMD_MNT_FLOPPY = \ + ['echo', 'floppy mounted'] + + ds = DataSourceAltCloud({}, None, self.paths) + + self.assertEquals(True, ds.user_data_rhevm()) + + cloudinit.sources.DataSourceAltCloud.DELTACLOUD_USER_DATA_FILE = \ + cloudinit.sources.DataSourceAltCloud.MEDIA_DIR + '/deltacloud-user-data.txt' + +class TestDataSouceAltCloud_user_data_vsphere(TestCase): + ''' + Test to exercise method: DataSourceAltCloud.user_data_vsphere() + ''' + def setUp(self): + ''' Set up ''' + self.paths = helpers.Paths({ 'cloud_dir': '/tmp' }) + cloudinit.sources.DataSourceAltCloud.CLOUD_INFO_FILE = \ + '/tmp/cloudinit_test_etc_sysconfig_cloud-info' + cloudinit.sources.DataSourceAltCloud.MEDIA_DIR = \ + '/tmp/cloudinit_test_media' + cloudinit.sources.DataSourceAltCloud.DELTACLOUD_USER_DATA_FILE = \ + cloudinit.sources.DataSourceAltCloud.MEDIA_DIR + \ + '/deltacloud-user-data.txt' + cloudinit.sources.DataSourceAltCloud.USER_DATA_FILE = \ + cloudinit.sources.DataSourceAltCloud.MEDIA_DIR + \ + '/user-data.txt' + + try: + os.mkdir(cloudinit.sources.DataSourceAltCloud.MEDIA_DIR) + except OSError, (errno, strerror): + # Ignore OSError: [Errno 17] File exists: + if errno is not 17: + raise + + _write_user_data_files('test user data') + + def tearDown(self): + # Reset + + _remove_user_data_files() + + cloudinit.sources.DataSourceAltCloud.CLOUD_INFO_FILE = \ + CLOUD_INFO_FILE = '/etc/sysconfig/cloud-info' + cloudinit.sources.DataSourceAltCloud.MEDIA_DIR = '/media' + + cloudinit.sources.DataSourceAltCloud.DELTACLOUD_USER_DATA_FILE = \ + cloudinit.sources.DataSourceAltCloud.MEDIA_DIR + '/deltacloud-user-data.txt' + + cloudinit.sources.DataSourceAltCloud.CMD_MNT_CDROM = \ + ['/bin/mount', '/dev/fd0', cloudinit.sources.DataSourceAltCloud.MEDIA_DIR] + + + def test_user_data_vsphere(self): + '''Test user_data_vsphere() ''' + + cloudinit.sources.DataSourceAltCloud.MEDIA_DIR = \ + '/tmp/cloudinit_test_media' + cloudinit.sources.DataSourceAltCloud.DELTACLOUD_USER_DATA_FILE = \ + cloudinit.sources.DataSourceAltCloud.MEDIA_DIR + '/deltacloud-user-data.txt' + + cloudinit.sources.DataSourceAltCloud.CMD_MNT_CDROM = \ + ['echo', 'floppy mounted'] + + ds = DataSourceAltCloud({}, None, self.paths) + + self.assertEquals(True, ds.user_data_vsphere()) + + def test_user_data_vsphere_mount_fails(self): + '''Test user_data_vsphere() where mount fails. ''' + + cloudinit.sources.DataSourceAltCloud.MEDIA_DIR = \ + '/tmp/cloudinit_test_media' + cloudinit.sources.DataSourceAltCloud.DELTACLOUD_USER_DATA_FILE = \ + cloudinit.sources.DataSourceAltCloud.MEDIA_DIR + '/deltacloud-user-data.txt' + + cloudinit.sources.DataSourceAltCloud.CMD_MNT_CDROM = \ + ['ls', 'floppy mounted'] + + ds = DataSourceAltCloud({}, None, self.paths) + + self.assertEquals(False, ds.user_data_vsphere()) + + def test_user_data_vsphere_no_user_data_file(self): + '''Test user_data_vsphere() with no user data files.''' + + cloudinit.sources.DataSourceAltCloud.MEDIA_DIR = \ + '/tmp/cloudinit_test_media' + cloudinit.sources.DataSourceAltCloud.DELTACLOUD_USER_DATA_FILE = \ + cloudinit.sources.DataSourceAltCloud.MEDIA_DIR + '/not-user-data.txt' + cloudinit.sources.DataSourceAltCloud.USER_DATA_FILE = \ + cloudinit.sources.DataSourceAltCloud.MEDIA_DIR + '/not-user-data.txt' + + cloudinit.sources.DataSourceAltCloud.CMD_MNT_CDROM = \ + ['echo', 'floppy mounted'] + + ds = DataSourceAltCloud({}, None, self.paths) + + self.assertEquals(False, ds.user_data_vsphere()) + + cloudinit.sources.DataSourceAltCloud.DELTACLOUD_USER_DATA_FILE = \ + cloudinit.sources.DataSourceAltCloud.MEDIA_DIR + '/deltacloud-user-data.txt' + cloudinit.sources.DataSourceAltCloud.USER_DATA_FILE = \ + cloudinit.sources.DataSourceAltCloud.MEDIA_DIR + '/user-data.txt' + + def test_user_data_vsphere_no_user_data_file(self): + '''Test user_data_vsphere() with no deltacloud user data files.''' + + cloudinit.sources.DataSourceAltCloud.MEDIA_DIR = \ + '/tmp/cloudinit_test_media' + cloudinit.sources.DataSourceAltCloud.DELTACLOUD_USER_DATA_FILE = \ + cloudinit.sources.DataSourceAltCloud.MEDIA_DIR + '/not-user-data.txt' + + cloudinit.sources.DataSourceAltCloud.CMD_MNT_CDROM = \ + ['echo', 'floppy mounted'] + + ds = DataSourceAltCloud({}, None, self.paths) + + self.assertEquals(True, ds.user_data_vsphere()) + + cloudinit.sources.DataSourceAltCloud.DELTACLOUD_USER_DATA_FILE = \ + cloudinit.sources.DataSourceAltCloud.MEDIA_DIR + '/deltacloud-user-data.txt' + +# vi: ts=4 expandtab + -- cgit v1.2.3 From e20958b9d775475dd77c27394d7ccba77a3575b2 Mon Sep 17 00:00:00 2001 From: Joe VLcek Date: Fri, 20 Jul 2012 11:02:06 -0400 Subject: Add retry logic to DataSourceAltCloud --- cloudinit/sources/DataSourceAltCloud.py | 37 +++++++--- tests/unittests/test_datasource/test_altcloud.py | 86 ++++++++++++++++++------ 2 files changed, 93 insertions(+), 30 deletions(-) diff --git a/cloudinit/sources/DataSourceAltCloud.py b/cloudinit/sources/DataSourceAltCloud.py index 121a3c48..53c7d8d9 100644 --- a/cloudinit/sources/DataSourceAltCloud.py +++ b/cloudinit/sources/DataSourceAltCloud.py @@ -7,6 +7,7 @@ # Author: Scott Moser # Author: Juerg Hafliger # Author: Joshua Harlow +# Author: Joe VLcek # # This program is free software: you can redistribute it and/or modify # it under the terms of the GNU General Public License version 3, as @@ -21,6 +22,7 @@ # along with this program. If not, see . import errno +import time import os import os.path import subprocess @@ -52,6 +54,12 @@ CMD_PROBE_FLOPPY = ['/sbin/modprobe', 'floppy'] CMD_MNT_FLOPPY = ['/bin/mount', '/dev/fd0', MEDIA_DIR] CMD_MNT_CDROM = ['/bin/mount', '/dev/cdrom', MEDIA_DIR] +''' +Retry times and sleep secs between each try +''' +RETRY_TIMES = 3 +SLEEP_SECS = 3 + META_DATA_NOT_SUPPORTED = { 'block-device-mapping' : {}, 'instance-id' : 455, @@ -144,14 +152,27 @@ class DataSourceAltCloud(sources.DataSource): LOG.debug('cloud_type: ' + str(cloud_type)) - if 'RHEV' in cloud_type: - return self.user_data_rhevm() - elif 'VSPHERE' in cloud_type: - return self.user_data_vsphere() - else: - # there was no recognized alternate cloud type. - # suggesting this handler should not be used. - return False + # Simple retry logic around user_data_() methods + tries = RETRY_TIMES + sleep_secs = SLEEP_SECS + while tries > 0: + if 'RHEV' in cloud_type: + if self.user_data_rhevm(): + return True + elif 'VSPHERE' in cloud_type: + if self.user_data_vsphere(): + return True + else: + # there was no recognized alternate cloud type. + # suggesting this handler should not be used. + return False + + time.sleep(sleep_secs) + tries -= 1 + sleep_secs *= 3 + + # Retry loop exhausted + return False def user_data_rhevm(self): ''' diff --git a/tests/unittests/test_datasource/test_altcloud.py b/tests/unittests/test_datasource/test_altcloud.py index d404fab9..5c3c8ddf 100644 --- a/tests/unittests/test_datasource/test_altcloud.py +++ b/tests/unittests/test_datasource/test_altcloud.py @@ -64,11 +64,15 @@ class TestDataSouceAltCloud_get_cloud_type(TestCase): def setUp(self): ''' Set up ''' self.paths = helpers.Paths({ 'cloud_dir': '/tmp' }) + cloudinit.sources.DataSourceAltCloud.RETRY_TIMES = 1 + cloudinit.sources.DataSourceAltCloud.SLEEP_SECS = 1 def tearDown(self): # Reset cloudinit.sources.DataSourceAltCloud.CMD_DMI_SYSTEM = \ ['dmidecode', '--string', 'system-product-name'] + cloudinit.sources.DataSourceAltCloud.RETRY_TIMES = 3 + cloudinit.sources.DataSourceAltCloud.SLEEP_SECS = 3 def test_get_cloud_type_RHEV(self): ''' @@ -133,11 +137,15 @@ class TestDataSouceAltCloud_get_data_cloud_info_file(TestCase): self.paths = helpers.Paths({ 'cloud_dir': '/tmp' }) cloudinit.sources.DataSourceAltCloud.CLOUD_INFO_FILE = \ '/tmp/cloudinit_test_etc_sysconfig_cloud-info' + cloudinit.sources.DataSourceAltCloud.RETRY_TIMES = 1 + cloudinit.sources.DataSourceAltCloud.SLEEP_SECS = 1 def tearDown(self): # Reset cloudinit.sources.DataSourceAltCloud.CLOUD_INFO_FILE = \ CLOUD_INFO_FILE = '/etc/sysconfig/cloud-info' + cloudinit.sources.DataSourceAltCloud.RETRY_TIMES = 3 + cloudinit.sources.DataSourceAltCloud.SLEEP_SECS = 3 def test_get_data_RHEV_cloud_file(self): '''Success Test module get_data() forcing RHEV ''' @@ -188,6 +196,8 @@ class TestDataSouceAltCloud_get_data_no_cloud_info_file(TestCase): self.paths = helpers.Paths({ 'cloud_dir': '/tmp' }) cloudinit.sources.DataSourceAltCloud.CLOUD_INFO_FILE = \ 'no such file' + cloudinit.sources.DataSourceAltCloud.RETRY_TIMES = 1 + cloudinit.sources.DataSourceAltCloud.SLEEP_SECS = 1 def tearDown(self): # Reset @@ -195,6 +205,8 @@ class TestDataSouceAltCloud_get_data_no_cloud_info_file(TestCase): CLOUD_INFO_FILE = '/etc/sysconfig/cloud-info' cloudinit.sources.DataSourceAltCloud.CMD_DMI_SYSTEM = \ ['dmidecode', '--string', 'system-product-name'] + cloudinit.sources.DataSourceAltCloud.RETRY_TIMES = 3 + cloudinit.sources.DataSourceAltCloud.SLEEP_SECS = 3 def test_get_data_RHEV_cloud_file(self): '''Test No cloud info file module get_data() forcing RHEV ''' @@ -239,6 +251,8 @@ class TestDataSouceAltCloud_user_data_rhevm(TestCase): cloudinit.sources.DataSourceAltCloud.USER_DATA_FILE = \ cloudinit.sources.DataSourceAltCloud.MEDIA_DIR + \ '/user-data.txt' + cloudinit.sources.DataSourceAltCloud.RETRY_TIMES = 1 + cloudinit.sources.DataSourceAltCloud.SLEEP_SECS = 1 try: os.mkdir(cloudinit.sources.DataSourceAltCloud.MEDIA_DIR) @@ -259,7 +273,8 @@ class TestDataSouceAltCloud_user_data_rhevm(TestCase): cloudinit.sources.DataSourceAltCloud.MEDIA_DIR = '/media' cloudinit.sources.DataSourceAltCloud.DELTACLOUD_USER_DATA_FILE = \ - cloudinit.sources.DataSourceAltCloud.MEDIA_DIR + '/deltacloud-user-data.txt' + cloudinit.sources.DataSourceAltCloud.MEDIA_DIR + \ + '/deltacloud-user-data.txt' cloudinit.sources.DataSourceAltCloud.USER_DATA_FILE = \ cloudinit.sources.DataSourceAltCloud.MEDIA_DIR + '/user-data.txt' @@ -267,7 +282,10 @@ class TestDataSouceAltCloud_user_data_rhevm(TestCase): cloudinit.sources.DataSourceAltCloud.CMD_PROBE_FLOPPY = \ ['/sbin/modprobe', 'floppy'] cloudinit.sources.DataSourceAltCloud.CMD_MNT_FLOPPY = \ - ['/bin/mount', '/dev/fd0', cloudinit.sources.DataSourceAltCloud.MEDIA_DIR] + ['/bin/mount', '/dev/fd0', \ + cloudinit.sources.DataSourceAltCloud.MEDIA_DIR] + cloudinit.sources.DataSourceAltCloud.RETRY_TIMES = 3 + cloudinit.sources.DataSourceAltCloud.SLEEP_SECS = 3 def test_user_data_rhevm(self): '''Test user_data_rhevm() ''' @@ -275,7 +293,8 @@ class TestDataSouceAltCloud_user_data_rhevm(TestCase): cloudinit.sources.DataSourceAltCloud.MEDIA_DIR = \ '/tmp/cloudinit_test_media' cloudinit.sources.DataSourceAltCloud.DELTACLOUD_USER_DATA_FILE = \ - cloudinit.sources.DataSourceAltCloud.MEDIA_DIR + '/deltacloud-user-data.txt' + cloudinit.sources.DataSourceAltCloud.MEDIA_DIR + \ + '/deltacloud-user-data.txt' cloudinit.sources.DataSourceAltCloud.CMD_PROBE_FLOPPY = \ ['echo', 'modprobe floppy'] @@ -292,7 +311,8 @@ class TestDataSouceAltCloud_user_data_rhevm(TestCase): cloudinit.sources.DataSourceAltCloud.MEDIA_DIR = \ '/tmp/cloudinit_test_media' cloudinit.sources.DataSourceAltCloud.DELTACLOUD_USER_DATA_FILE = \ - cloudinit.sources.DataSourceAltCloud.MEDIA_DIR + '/deltacloud-user-data.txt' + cloudinit.sources.DataSourceAltCloud.MEDIA_DIR + \ + '/deltacloud-user-data.txt' cloudinit.sources.DataSourceAltCloud.CMD_PROBE_FLOPPY = \ ['ls', 'modprobe floppy'] @@ -309,7 +329,8 @@ class TestDataSouceAltCloud_user_data_rhevm(TestCase): cloudinit.sources.DataSourceAltCloud.MEDIA_DIR = \ '/tmp/cloudinit_test_media' cloudinit.sources.DataSourceAltCloud.DELTACLOUD_USER_DATA_FILE = \ - cloudinit.sources.DataSourceAltCloud.MEDIA_DIR + '/deltacloud-user-data.txt' + cloudinit.sources.DataSourceAltCloud.MEDIA_DIR + \ + '/deltacloud-user-data.txt' cloudinit.sources.DataSourceAltCloud.CMD_PROBE_FLOPPY = \ ['bad command', 'modprobe floppy'] @@ -326,7 +347,8 @@ class TestDataSouceAltCloud_user_data_rhevm(TestCase): cloudinit.sources.DataSourceAltCloud.MEDIA_DIR = \ '/tmp/cloudinit_test_media' cloudinit.sources.DataSourceAltCloud.DELTACLOUD_USER_DATA_FILE = \ - cloudinit.sources.DataSourceAltCloud.MEDIA_DIR + '/deltacloud-user-data.txt' + cloudinit.sources.DataSourceAltCloud.MEDIA_DIR + \ + '/deltacloud-user-data.txt' cloudinit.sources.DataSourceAltCloud.CMD_PROBE_FLOPPY = \ ['echo', 'modprobe floppy'] @@ -343,9 +365,11 @@ class TestDataSouceAltCloud_user_data_rhevm(TestCase): cloudinit.sources.DataSourceAltCloud.MEDIA_DIR = \ '/tmp/cloudinit_test_media' cloudinit.sources.DataSourceAltCloud.DELTACLOUD_USER_DATA_FILE = \ - cloudinit.sources.DataSourceAltCloud.MEDIA_DIR + '/not-user-data.txt' + cloudinit.sources.DataSourceAltCloud.MEDIA_DIR + \ + '/not-user-data.txt' cloudinit.sources.DataSourceAltCloud.USER_DATA_FILE = \ - cloudinit.sources.DataSourceAltCloud.MEDIA_DIR + '/not-user-data.txt' + cloudinit.sources.DataSourceAltCloud.MEDIA_DIR + \ + '/not-user-data.txt' cloudinit.sources.DataSourceAltCloud.CMD_PROBE_FLOPPY = \ ['echo', 'modprobe floppy'] @@ -357,9 +381,11 @@ class TestDataSouceAltCloud_user_data_rhevm(TestCase): self.assertEquals(False, ds.user_data_rhevm()) cloudinit.sources.DataSourceAltCloud.DELTACLOUD_USER_DATA_FILE = \ - cloudinit.sources.DataSourceAltCloud.MEDIA_DIR + '/deltacloud-user-data.txt' + cloudinit.sources.DataSourceAltCloud.MEDIA_DIR + \ + '/deltacloud-user-data.txt' cloudinit.sources.DataSourceAltCloud.USER_DATA_FILE = \ - cloudinit.sources.DataSourceAltCloud.MEDIA_DIR + '/user-data.txt' + cloudinit.sources.DataSourceAltCloud.MEDIA_DIR + \ + '/user-data.txt' def test_user_data_rhevm_no_user_data_file(self): '''Test user_data_rhevm() with no deltacloud user data file.''' @@ -367,7 +393,8 @@ class TestDataSouceAltCloud_user_data_rhevm(TestCase): cloudinit.sources.DataSourceAltCloud.MEDIA_DIR = \ '/tmp/cloudinit_test_media' cloudinit.sources.DataSourceAltCloud.DELTACLOUD_USER_DATA_FILE = \ - cloudinit.sources.DataSourceAltCloud.MEDIA_DIR + '/not-user-data.txt' + cloudinit.sources.DataSourceAltCloud.MEDIA_DIR + \ + '/not-user-data.txt' cloudinit.sources.DataSourceAltCloud.CMD_PROBE_FLOPPY = \ ['echo', 'modprobe floppy'] @@ -379,7 +406,8 @@ class TestDataSouceAltCloud_user_data_rhevm(TestCase): self.assertEquals(True, ds.user_data_rhevm()) cloudinit.sources.DataSourceAltCloud.DELTACLOUD_USER_DATA_FILE = \ - cloudinit.sources.DataSourceAltCloud.MEDIA_DIR + '/deltacloud-user-data.txt' + cloudinit.sources.DataSourceAltCloud.MEDIA_DIR + \ + '/deltacloud-user-data.txt' class TestDataSouceAltCloud_user_data_vsphere(TestCase): ''' @@ -398,6 +426,8 @@ class TestDataSouceAltCloud_user_data_vsphere(TestCase): cloudinit.sources.DataSourceAltCloud.USER_DATA_FILE = \ cloudinit.sources.DataSourceAltCloud.MEDIA_DIR + \ '/user-data.txt' + cloudinit.sources.DataSourceAltCloud.RETRY_TIMES = 1 + cloudinit.sources.DataSourceAltCloud.SLEEP_SECS = 1 try: os.mkdir(cloudinit.sources.DataSourceAltCloud.MEDIA_DIR) @@ -418,11 +448,15 @@ class TestDataSouceAltCloud_user_data_vsphere(TestCase): cloudinit.sources.DataSourceAltCloud.MEDIA_DIR = '/media' cloudinit.sources.DataSourceAltCloud.DELTACLOUD_USER_DATA_FILE = \ - cloudinit.sources.DataSourceAltCloud.MEDIA_DIR + '/deltacloud-user-data.txt' + cloudinit.sources.DataSourceAltCloud.MEDIA_DIR + \ + '/deltacloud-user-data.txt' cloudinit.sources.DataSourceAltCloud.CMD_MNT_CDROM = \ - ['/bin/mount', '/dev/fd0', cloudinit.sources.DataSourceAltCloud.MEDIA_DIR] + ['/bin/mount', '/dev/fd0', \ + cloudinit.sources.DataSourceAltCloud.MEDIA_DIR] + cloudinit.sources.DataSourceAltCloud.RETRY_TIMES = 3 + cloudinit.sources.DataSourceAltCloud.SLEEP_SECS = 3 def test_user_data_vsphere(self): '''Test user_data_vsphere() ''' @@ -430,7 +464,8 @@ class TestDataSouceAltCloud_user_data_vsphere(TestCase): cloudinit.sources.DataSourceAltCloud.MEDIA_DIR = \ '/tmp/cloudinit_test_media' cloudinit.sources.DataSourceAltCloud.DELTACLOUD_USER_DATA_FILE = \ - cloudinit.sources.DataSourceAltCloud.MEDIA_DIR + '/deltacloud-user-data.txt' + cloudinit.sources.DataSourceAltCloud.MEDIA_DIR + \ + '/deltacloud-user-data.txt' cloudinit.sources.DataSourceAltCloud.CMD_MNT_CDROM = \ ['echo', 'floppy mounted'] @@ -445,7 +480,8 @@ class TestDataSouceAltCloud_user_data_vsphere(TestCase): cloudinit.sources.DataSourceAltCloud.MEDIA_DIR = \ '/tmp/cloudinit_test_media' cloudinit.sources.DataSourceAltCloud.DELTACLOUD_USER_DATA_FILE = \ - cloudinit.sources.DataSourceAltCloud.MEDIA_DIR + '/deltacloud-user-data.txt' + cloudinit.sources.DataSourceAltCloud.MEDIA_DIR + \ + '/deltacloud-user-data.txt' cloudinit.sources.DataSourceAltCloud.CMD_MNT_CDROM = \ ['ls', 'floppy mounted'] @@ -460,9 +496,11 @@ class TestDataSouceAltCloud_user_data_vsphere(TestCase): cloudinit.sources.DataSourceAltCloud.MEDIA_DIR = \ '/tmp/cloudinit_test_media' cloudinit.sources.DataSourceAltCloud.DELTACLOUD_USER_DATA_FILE = \ - cloudinit.sources.DataSourceAltCloud.MEDIA_DIR + '/not-user-data.txt' + cloudinit.sources.DataSourceAltCloud.MEDIA_DIR + \ + '/not-user-data.txt' cloudinit.sources.DataSourceAltCloud.USER_DATA_FILE = \ - cloudinit.sources.DataSourceAltCloud.MEDIA_DIR + '/not-user-data.txt' + cloudinit.sources.DataSourceAltCloud.MEDIA_DIR + \ + '/not-user-data.txt' cloudinit.sources.DataSourceAltCloud.CMD_MNT_CDROM = \ ['echo', 'floppy mounted'] @@ -472,9 +510,11 @@ class TestDataSouceAltCloud_user_data_vsphere(TestCase): self.assertEquals(False, ds.user_data_vsphere()) cloudinit.sources.DataSourceAltCloud.DELTACLOUD_USER_DATA_FILE = \ - cloudinit.sources.DataSourceAltCloud.MEDIA_DIR + '/deltacloud-user-data.txt' + cloudinit.sources.DataSourceAltCloud.MEDIA_DIR + \ + '/deltacloud-user-data.txt' cloudinit.sources.DataSourceAltCloud.USER_DATA_FILE = \ - cloudinit.sources.DataSourceAltCloud.MEDIA_DIR + '/user-data.txt' + cloudinit.sources.DataSourceAltCloud.MEDIA_DIR + \ + '/user-data.txt' def test_user_data_vsphere_no_user_data_file(self): '''Test user_data_vsphere() with no deltacloud user data files.''' @@ -482,7 +522,8 @@ class TestDataSouceAltCloud_user_data_vsphere(TestCase): cloudinit.sources.DataSourceAltCloud.MEDIA_DIR = \ '/tmp/cloudinit_test_media' cloudinit.sources.DataSourceAltCloud.DELTACLOUD_USER_DATA_FILE = \ - cloudinit.sources.DataSourceAltCloud.MEDIA_DIR + '/not-user-data.txt' + cloudinit.sources.DataSourceAltCloud.MEDIA_DIR + \ + '/not-user-data.txt' cloudinit.sources.DataSourceAltCloud.CMD_MNT_CDROM = \ ['echo', 'floppy mounted'] @@ -492,7 +533,8 @@ class TestDataSouceAltCloud_user_data_vsphere(TestCase): self.assertEquals(True, ds.user_data_vsphere()) cloudinit.sources.DataSourceAltCloud.DELTACLOUD_USER_DATA_FILE = \ - cloudinit.sources.DataSourceAltCloud.MEDIA_DIR + '/deltacloud-user-data.txt' + cloudinit.sources.DataSourceAltCloud.MEDIA_DIR + \ + '/deltacloud-user-data.txt' # vi: ts=4 expandtab -- cgit v1.2.3 From 3083335d2c2654e2ef0e41a35d4b1bf11f5d5c90 Mon Sep 17 00:00:00 2001 From: Joe VLcek Date: Mon, 23 Jul 2012 13:48:43 -0400 Subject: pep8, pylint... AltCloud source changes --- cloudinit/sources/DataSourceAltCloud.py | 54 +++++++++++++++------------------ 1 file changed, 25 insertions(+), 29 deletions(-) diff --git a/cloudinit/sources/DataSourceAltCloud.py b/cloudinit/sources/DataSourceAltCloud.py index 53c7d8d9..27a67e6c 100644 --- a/cloudinit/sources/DataSourceAltCloud.py +++ b/cloudinit/sources/DataSourceAltCloud.py @@ -21,11 +21,9 @@ # You should have received a copy of the GNU General Public License # along with this program. If not, see . -import errno import time import os import os.path -import subprocess from cloudinit import log as logging from cloudinit import sources @@ -34,9 +32,7 @@ from cloudinit.util import ProcessExecutionError LOG = logging.getLogger(__name__) -''' -Needed file paths -''' +# Needed file paths CLOUD_INFO_FILE = '/etc/sysconfig/cloud-info' MEDIA_DIR = '/media/userdata' @@ -46,27 +42,24 @@ MEDIA_DIR = '/media/userdata' DELTACLOUD_USER_DATA_FILE = MEDIA_DIR + '/deltacloud-user-data.txt' USER_DATA_FILE = MEDIA_DIR + '/user-data.txt' -''' -Shell command lists -''' -CMD_DMI_SYSTEM = ['/usr/sbin/dmidecode', '--string', 'system-product-name'] +# Shell command lists +CMD_DMI_SYSTEM = ['/usr/sbin/dmidecode', '--string', 'system-product-name'] CMD_PROBE_FLOPPY = ['/sbin/modprobe', 'floppy'] -CMD_MNT_FLOPPY = ['/bin/mount', '/dev/fd0', MEDIA_DIR] -CMD_MNT_CDROM = ['/bin/mount', '/dev/cdrom', MEDIA_DIR] +CMD_MNT_FLOPPY = ['/bin/mount', '/dev/fd0', MEDIA_DIR] +CMD_MNT_CDROM = ['/bin/mount', '/dev/cdrom', MEDIA_DIR] -''' -Retry times and sleep secs between each try -''' +# Retry times and sleep secs between each try RETRY_TIMES = 3 SLEEP_SECS = 3 -META_DATA_NOT_SUPPORTED = { - 'block-device-mapping' : {}, - 'instance-id' : 455, - 'local-hostname' : 'localhost', +META_DATA_NOT_SUPPORTED = { + 'block-device-mapping': {}, + 'instance-id': 455, + 'local-hostname': 'localhost', 'placement': {}, } + class DataSourceAltCloud(sources.DataSource): def __init__(self, sys_cfg, distro, paths): sources.DataSource.__init__(self, sys_cfg, distro, paths) @@ -177,7 +170,7 @@ class DataSourceAltCloud(sources.DataSource): def user_data_rhevm(self): ''' RHEVM specific userdata read - + If on RHEV-M the user data will be contained on the floppy device in file To access it: @@ -187,11 +180,12 @@ class DataSourceAltCloud(sources.DataSource): mount /dev/fd0 # NOTE: -> /dev/ read / ''' - + # modprobe floppy try: cmd = CMD_PROBE_FLOPPY (cmd_out, _err) = util.subp(cmd) + LOG.debug(('Command: %s\nOutput%s') % (' '.join(cmd), cmd_out)) except ProcessExecutionError, _err: util.logexc(LOG, (('Failed command: %s\n%s') % \ (' '.join(cmd), _err.message))) @@ -204,19 +198,20 @@ class DataSourceAltCloud(sources.DataSource): # mkdir dir just in case it isn't already. try: os.makedirs(MEDIA_DIR) - except OSError, (errno, strerror): - if errno is not 17: + except OSError, (_err, strerror): + if _err is not 17: LOG.debug(('makedirs() failed: %s \nError: %s') % \ - (errno, strerror)) + (_err, strerror)) return False # mount /dev/fd0 try: cmd = CMD_MNT_FLOPPY (cmd_out, _err) = util.subp(cmd) + LOG.debug(('Command: %s\nOutput%s') % (' '.join(cmd), cmd_out)) except ProcessExecutionError, _err: # Ignore failure: already mounted - if 'ALREADY MOUNTED' not in _err.message.upper(): + if 'ALREADY MOUNTED' not in str(_err.message).upper(): util.logexc(LOG, (('Failed command: %s\n%s') % \ (' '.join(cmd), _err.message))) return False @@ -254,7 +249,7 @@ class DataSourceAltCloud(sources.DataSource): def user_data_vsphere(self): ''' VSphere specific userdata read - + If on vSphere the user data will be contained on the floppy device in file To access it: @@ -266,19 +261,20 @@ class DataSourceAltCloud(sources.DataSource): # mkdir dir just in case it isn't already. try: os.makedirs(MEDIA_DIR) - except OSError, (errno, strerror): - if errno is not 17: + except OSError, (_err, strerror): + if _err is not 17: LOG.debug(('makedirs() failed: %s \nError: %s') % \ - (errno, strerror)) + (_err, strerror)) return False # mount /dev/cdrom try: cmd = CMD_MNT_CDROM (cmd_out, _err) = util.subp(cmd) + LOG.debug(('Command: %s\nOutput%s') % (' '.join(cmd), cmd_out)) except ProcessExecutionError, _err: # Ignore failure: already mounted - if 'ALREADY MOUNTED' not in _err.message.upper(): + if 'ALREADY MOUNTED' not in str(_err.message).upper(): LOG.debug(('Failed command: %s\n%s') % \ (' '.join(cmd), _err.message)) return False -- cgit v1.2.3 From 5e6ee0444fa7d2acf688b152c7fd632c878e23c0 Mon Sep 17 00:00:00 2001 From: Joe VLcek Date: Tue, 24 Jul 2012 16:36:45 -0400 Subject: Add documentation for RHEVm and vSphere --- doc/examples/cloud-config-datasources.txt | 67 +++++++++++++++++++++++++++++++ 1 file changed, 67 insertions(+) diff --git a/doc/examples/cloud-config-datasources.txt b/doc/examples/cloud-config-datasources.txt index d10dde05..102c3dd7 100644 --- a/doc/examples/cloud-config-datasources.txt +++ b/doc/examples/cloud-config-datasources.txt @@ -14,6 +14,73 @@ datasource: - http://169.254.169.254:80 - http://instance-data:8773 + AltCloud: + Data souce AltCloud will be used to pick up user data on + RHEVm and vSphere. + + RHEVm: + ====== + For REHVm v3.0 the userdata is injected into the VM using floppy + injection via the RHEVm dashboard "Custom Properties". The format + of the Custom Properties entry must be: + "floppyinject=user-data.txt:" + + e.g.: To pass a simple bash script + + % cat simple_script.bash + #!/bin/bash + echo "Hello Joe!" >> /tmp/JJV_Joe_out.txt + + % cat simple_script.bash | base64 + IyEvYmluL2Jhc2gKZWNobyAiSGVsbG8gSm9lISIgPj4gL3RtcC9KSlZfSm9lX291dC50eHQK + + To pass this example script to cloud-init running in a RHEVm v3.0 VM + set the "Custom Properties" when creating the RHEMv v3.0 VM to: + floppyinject=user-data.txt:IyEvYmluL2Jhc2gKZWNobyAiSGVsbG8gSm9lISIgPj4gL3RtcC9KSlZfSm9lX291dC50eHQK + + NOTE: The prefix with file name must be: "floppyinject=user-data.txt:" + + It is also possible to launch a RHEVm v3.0 VM and pass optional user + data to it using the Delta Cloud. + For more inforation on Delta Cloud see: http://deltacloud.apache.org + + vSphere: + ======== + For VMWare's vSphere the userdata is injected into the VM an ISO + via the cdrom. This can be done using the vSphere dashboard + by connecting an ISO image to the CD/DVD drive. + + To pass this example script to cloud-init running in a vSphere VM + set the CD/DVD drive when creating the vSphere VM to point to an + ISO on the data store. + + The ISO must contain the user data: + + For example, to pass the same simple_script.bash to vSphere: + + Create the ISO: + =============== + % mkdir my-iso + + NOTE: The file name on the ISO must be: "user-data.txt" + % cp simple_scirpt.bash my-iso/user-data.txt + + % genisoimage -o user-data.iso -r my-iso + + Verify the ISO: + =============== + % sudo mkdir /media/vsphere_iso + % sudo mount -o loop JoeV_CI_02.iso /media/vsphere_iso + % cat /media/vsphere_iso/user-data.txt + % sudo umount /media/vsphere_iso + + Then, launch the vSphere VM the ISO user-data.iso attached as a CDrom. + + It is also possible to launch a vSphere VM and pass optional user + data to it using the Delta Cloud. + + For more inforation on Delta Cloud see: http://deltacloud.apache.org + MAAS: timeout : 50 max_wait : 120 -- cgit v1.2.3 From 6f2dc0d943d3f3527983b38f20085a6cf071c43e Mon Sep 17 00:00:00 2001 From: Joe VLcek Date: Wed, 8 Aug 2012 14:36:41 -0400 Subject: Address review feedback for: https://code.launchpad.net/~joev-n/cloud-init/altcloud-changes/+merge/116542 --- cloudinit/settings.py | 2 +- cloudinit/sources/DataSourceAltCloud.py | 211 +++++++--------- tests/unittests/test_datasource/test_altcloud.py | 299 +++++++---------------- 3 files changed, 173 insertions(+), 339 deletions(-) diff --git a/cloudinit/settings.py b/cloudinit/settings.py index 28340e29..cdfc31ae 100644 --- a/cloudinit/settings.py +++ b/cloudinit/settings.py @@ -29,9 +29,9 @@ CLOUD_CONFIG = '/etc/cloud/cloud.cfg' # What u get if no config is provided CFG_BUILTIN = { 'datasource_list': [ - 'AltCloud', 'NoCloud', 'ConfigDrive', + 'AltCloud', 'OVF', 'MAAS', 'Ec2', diff --git a/cloudinit/sources/DataSourceAltCloud.py b/cloudinit/sources/DataSourceAltCloud.py index 27a67e6c..0f4cc8e0 100644 --- a/cloudinit/sources/DataSourceAltCloud.py +++ b/cloudinit/sources/DataSourceAltCloud.py @@ -4,9 +4,6 @@ # Copyright (C) 2012 Hewlett-Packard Development Company, L.P. # Copyright (C) 2012 Yahoo! Inc. # -# Author: Scott Moser -# Author: Juerg Hafliger -# Author: Joshua Harlow # Author: Joe VLcek # # This program is free software: you can redistribute it and/or modify @@ -21,9 +18,10 @@ # You should have received a copy of the GNU General Public License # along with this program. If not, see . -import time +import errno import os import os.path +import time from cloudinit import log as logging from cloudinit import sources @@ -34,19 +32,10 @@ LOG = logging.getLogger(__name__) # Needed file paths CLOUD_INFO_FILE = '/etc/sysconfig/cloud-info' -MEDIA_DIR = '/media/userdata' - -# Deltacloud file name contains deltacloud. Those not using -# Deltacloud but instead instrumenting the injection, could -# drop deltacloud from the file name. -DELTACLOUD_USER_DATA_FILE = MEDIA_DIR + '/deltacloud-user-data.txt' -USER_DATA_FILE = MEDIA_DIR + '/user-data.txt' # Shell command lists CMD_DMI_SYSTEM = ['/usr/sbin/dmidecode', '--string', 'system-product-name'] CMD_PROBE_FLOPPY = ['/sbin/modprobe', 'floppy'] -CMD_MNT_FLOPPY = ['/bin/mount', '/dev/fd0', MEDIA_DIR] -CMD_MNT_CDROM = ['/bin/mount', '/dev/cdrom', MEDIA_DIR] # Retry times and sleep secs between each try RETRY_TIMES = 3 @@ -60,18 +49,50 @@ META_DATA_NOT_SUPPORTED = { } +def read_user_data_callback(mount_dir): + ''' + Description: + This callback will be applied by util.mount_cb() on the mounted + file. + + Deltacloud file name contains deltacloud. Those not using + Deltacloud but instead instrumenting the injection, could + drop deltacloud from the file name. + + Input: + mount_dir - Mount directory + + Returns: + User Data + + ''' + + deltacloud_user_data_file = mount_dir + '/deltacloud-user-data.txt' + user_data_file = mount_dir + '/user-data.txt' + + # First try deltacloud_user_data_file. On failure try user_data_file. + try: + with open(deltacloud_user_data_file, 'r') as user_data_f: + user_data = user_data_f.read().strip() + except: + try: + with open(user_data_file, 'r') as user_data_f: + user_data = user_data_f.read().strip() + except: + util.logexc(LOG, ('Failed accessing user data file.')) + return None + + return user_data + + class DataSourceAltCloud(sources.DataSource): def __init__(self, sys_cfg, distro, paths): sources.DataSource.__init__(self, sys_cfg, distro, paths) - self.dsmode = 'local' self.seed = None - self.cmdline_id = "ds=nocloud" - self.seed_dir = os.path.join(paths.seed_dir, 'nocloud') self.supported_seed_starts = ("/", "file://") def __str__(self): - mstr = "%s [seed=%s][dsmode=%s]" % (util.obj_name(self), - self.seed, self.dsmode) + mstr = "%s [seed=%s]" % (util.obj_name(self), self.seed) return mstr def get_cloud_type(self): @@ -117,6 +138,7 @@ class DataSourceAltCloud(sources.DataSource): ''' Description: User Data is passed to the launching instance which + is used to perform instance configuration. Cloud providers expose the user data differently. It is necessary to determine which cloud provider @@ -172,15 +194,19 @@ class DataSourceAltCloud(sources.DataSource): RHEVM specific userdata read If on RHEV-M the user data will be contained on the - floppy device in file + floppy device in file To access it: modprobe floppy - mkdir - mount /dev/fd0 - mount /dev/fd0 # NOTE: -> /dev/ - read / + + Leverage util.mount_cb to: + mkdir + mount /dev/fd0 + The call back passed to util.mount_cb will do: + read / ''' + return_str = None + # modprobe floppy try: cmd = CMD_PROBE_FLOPPY @@ -195,118 +221,59 @@ class DataSourceAltCloud(sources.DataSource): (' '.join(cmd), _err.message))) return False - # mkdir dir just in case it isn't already. - try: - os.makedirs(MEDIA_DIR) - except OSError, (_err, strerror): - if _err is not 17: - LOG.debug(('makedirs() failed: %s \nError: %s') % \ - (_err, strerror)) - return False - - # mount /dev/fd0 - try: - cmd = CMD_MNT_FLOPPY - (cmd_out, _err) = util.subp(cmd) - LOG.debug(('Command: %s\nOutput%s') % (' '.join(cmd), cmd_out)) - except ProcessExecutionError, _err: - # Ignore failure: already mounted - if 'ALREADY MOUNTED' not in str(_err.message).upper(): - util.logexc(LOG, (('Failed command: %s\n%s') % \ - (' '.join(cmd), _err.message))) - return False - except OSError, _err: - util.logexc(LOG, (('Failed command: %s\n%s') % \ - (' '.join(cmd), _err.message))) - return False - - # This could be done using "with open()" but that's not available - # in Python 2.4 as used on RHEL5 - # First try DELTACLOUD_USER_DATA_FILE. If that fails then try - # USER_DATA_FILE. + floppy_dev = '/dev/fd0' try: - user_data_file = open(DELTACLOUD_USER_DATA_FILE, 'r') - user_data = user_data_file.read().strip() - user_data_file.close() - except: - try: - user_data_file = open(USER_DATA_FILE, 'r') - user_data = user_data_file.read().strip() - user_data_file.close() - except: - util.logexc(LOG, ('Failed accessing RHEVm user data file.')) - try: - user_data_file.close() - except: - pass - return False - - self.userdata_raw = user_data + return_str = util.mount_cb(floppy_dev, read_user_data_callback) + except OSError as err: + if err.errno != errno.ENOENT: + raise + except util.MountFailedError: + util.logexc(LOG, ("Failed to mount %s" + " when looking for user data"), floppy_dev) + + self.userdata_raw = return_str self.metadata = META_DATA_NOT_SUPPORTED - return True + if return_str: + return True + else: + return False def user_data_vsphere(self): ''' - VSphere specific userdata read + vSphere specific userdata read If on vSphere the user data will be contained on the - floppy device in file + cdrom device in file To access it: - mkdir dir just in case it isn't already. - mount /dev/cdrom # NOTE: -> /dev/cdrom - read / + Leverage util.mount_cb to: + mkdir + mount /dev/fd0 + The call back passed to util.mount_cb will do: + read / ''' - # mkdir dir just in case it isn't already. - try: - os.makedirs(MEDIA_DIR) - except OSError, (_err, strerror): - if _err is not 17: - LOG.debug(('makedirs() failed: %s \nError: %s') % \ - (_err, strerror)) - return False - - # mount /dev/cdrom - try: - cmd = CMD_MNT_CDROM - (cmd_out, _err) = util.subp(cmd) - LOG.debug(('Command: %s\nOutput%s') % (' '.join(cmd), cmd_out)) - except ProcessExecutionError, _err: - # Ignore failure: already mounted - if 'ALREADY MOUNTED' not in str(_err.message).upper(): - LOG.debug(('Failed command: %s\n%s') % \ - (' '.join(cmd), _err.message)) - return False - except OSError, _err: - LOG.debug(('Failed command: %s\n%s') % \ - (' '.join(cmd), _err.message)) - return False - - # This could be done using "with open()" but that's not available - # in Python 2.4 as used on RHEL5 - # First try DELTACLOUD_USER_DATA_FILE. If that fails then try - # USER_DATA_FILE. - try: - user_data_file = open(DELTACLOUD_USER_DATA_FILE, 'r') - user_data = user_data_file.read().strip() - user_data_file.close() - except: + return_str = None + cdrom_list = util.find_devs_with('LABEL=CDROM') + for cdrom_dev in cdrom_list: try: - user_data_file = open(USER_DATA_FILE, 'r') - user_data = user_data_file.read().strip() - user_data_file.close() - except: - LOG.debug('Failed accessing vSphere user data file.') - try: - user_data_file.close() - except: - pass - return False - - self.userdata_raw = user_data + return_str = util.mount_cb(cdrom_dev, read_user_data_callback) + if return_str: + break + except OSError as err: + if err.errno != errno.ENOENT: + raise + except util.MountFailedError: + util.logexc(LOG, ("Failed to mount %s" + " when looking for user data"), cdrom_dev) + + self.userdata_raw = return_str self.metadata = META_DATA_NOT_SUPPORTED - return True + + if return_str: + return True + else: + return False # Used to match classes to dependencies datasources = [ diff --git a/tests/unittests/test_datasource/test_altcloud.py b/tests/unittests/test_datasource/test_altcloud.py index 5c3c8ddf..27912652 100644 --- a/tests/unittests/test_datasource/test_altcloud.py +++ b/tests/unittests/test_datasource/test_altcloud.py @@ -1,20 +1,14 @@ #! /usr/bin/env python import os -import stat -import tempfile -from shutil import rmtree -from tempfile import mkdtemp from unittest import TestCase - -from time import sleep - from cloudinit import helpers # Get the cloudinit.sources.DataSourceAltCloud import items needed. import cloudinit.sources.DataSourceAltCloud from cloudinit.sources.DataSourceAltCloud import DataSourceAltCloud +from cloudinit.sources.DataSourceAltCloud import read_user_data_callback def _write_cloud_info_file(value): ''' @@ -33,28 +27,47 @@ def _remove_cloud_info_file(): ''' os.remove(cloudinit.sources.DataSourceAltCloud.CLOUD_INFO_FILE) -def _write_user_data_files(value): +def _write_user_data_files(mount_dir, value): ''' - Populate the DELTACLOUD_USER_DATA_FILE the USER_DATA_FILE + Populate the deltacloud_user_data_file the user_data_file which would be populated with user data. ''' - f = open(cloudinit.sources.DataSourceAltCloud.DELTACLOUD_USER_DATA_FILE, 'w') + deltacloud_user_data_file = mount_dir + '/deltacloud-user-data.txt' + user_data_file = mount_dir + '/user-data.txt' + + f = open(deltacloud_user_data_file, 'w') f.write(value) f.close() - os.chmod(cloudinit.sources.DataSourceAltCloud.DELTACLOUD_USER_DATA_FILE, 0664) + os.chmod(deltacloud_user_data_file, 0664) - f = open(cloudinit.sources.DataSourceAltCloud.USER_DATA_FILE, 'w') + f = open(user_data_file, 'w') f.write(value) f.close() - os.chmod(cloudinit.sources.DataSourceAltCloud.USER_DATA_FILE, 0664) + os.chmod(user_data_file, 0664) -def _remove_user_data_files(): +def _remove_user_data_files(mount_dir, + dc_file=True, + non_dc_file=True): ''' - Remove the test files: DELTACLOUD_USER_DATA_FILE and - USER_DATA_FILE + Remove the test files: deltacloud_user_data_file and + user_data_file ''' - os.remove(cloudinit.sources.DataSourceAltCloud.DELTACLOUD_USER_DATA_FILE) - os.remove(cloudinit.sources.DataSourceAltCloud.USER_DATA_FILE) + deltacloud_user_data_file = mount_dir + '/deltacloud-user-data.txt' + user_data_file = mount_dir + '/user-data.txt' + + # Ignore any failures removeing files that are already gone. + if dc_file: + try: + os.remove(deltacloud_user_data_file) + except OSError: + pass + + if non_dc_file: + try: + os.remove(user_data_file) + except OSError: + pass + class TestDataSouceAltCloud_get_cloud_type(TestCase): ''' @@ -143,7 +156,7 @@ class TestDataSouceAltCloud_get_data_cloud_info_file(TestCase): def tearDown(self): # Reset cloudinit.sources.DataSourceAltCloud.CLOUD_INFO_FILE = \ - CLOUD_INFO_FILE = '/etc/sysconfig/cloud-info' + '/etc/sysconfig/cloud-info' cloudinit.sources.DataSourceAltCloud.RETRY_TIMES = 3 cloudinit.sources.DataSourceAltCloud.SLEEP_SECS = 3 @@ -202,7 +215,7 @@ class TestDataSouceAltCloud_get_data_no_cloud_info_file(TestCase): def tearDown(self): # Reset cloudinit.sources.DataSourceAltCloud.CLOUD_INFO_FILE = \ - CLOUD_INFO_FILE = '/etc/sysconfig/cloud-info' + '/etc/sysconfig/cloud-info' cloudinit.sources.DataSourceAltCloud.CMD_DMI_SYSTEM = \ ['dmidecode', '--string', 'system-product-name'] cloudinit.sources.DataSourceAltCloud.RETRY_TIMES = 3 @@ -241,83 +254,50 @@ class TestDataSouceAltCloud_user_data_rhevm(TestCase): def setUp(self): ''' Set up ''' self.paths = helpers.Paths({ 'cloud_dir': '/tmp' }) + self.mount_dir = '/tmp/cloudinit_test_media' + cloudinit.sources.DataSourceAltCloud.CLOUD_INFO_FILE = \ '/tmp/cloudinit_test_etc_sysconfig_cloud-info' - cloudinit.sources.DataSourceAltCloud.MEDIA_DIR = \ - '/tmp/cloudinit_test_media' - cloudinit.sources.DataSourceAltCloud.DELTACLOUD_USER_DATA_FILE = \ - cloudinit.sources.DataSourceAltCloud.MEDIA_DIR + \ - '/deltacloud-user-data.txt' - cloudinit.sources.DataSourceAltCloud.USER_DATA_FILE = \ - cloudinit.sources.DataSourceAltCloud.MEDIA_DIR + \ - '/user-data.txt' cloudinit.sources.DataSourceAltCloud.RETRY_TIMES = 1 cloudinit.sources.DataSourceAltCloud.SLEEP_SECS = 1 try: - os.mkdir(cloudinit.sources.DataSourceAltCloud.MEDIA_DIR) + os.mkdir(self.mount_dir) except OSError, (errno, strerror): # Ignore OSError: [Errno 17] File exists: if errno is not 17: raise - _write_user_data_files('test user data') + _write_user_data_files(self.mount_dir, 'test user data') def tearDown(self): # Reset - _remove_user_data_files() + _remove_user_data_files(self.mount_dir) cloudinit.sources.DataSourceAltCloud.CLOUD_INFO_FILE = \ - CLOUD_INFO_FILE = '/etc/sysconfig/cloud-info' - cloudinit.sources.DataSourceAltCloud.MEDIA_DIR = '/media' - - cloudinit.sources.DataSourceAltCloud.DELTACLOUD_USER_DATA_FILE = \ - cloudinit.sources.DataSourceAltCloud.MEDIA_DIR + \ - '/deltacloud-user-data.txt' - - cloudinit.sources.DataSourceAltCloud.USER_DATA_FILE = \ - cloudinit.sources.DataSourceAltCloud.MEDIA_DIR + '/user-data.txt' + '/etc/sysconfig/cloud-info' cloudinit.sources.DataSourceAltCloud.CMD_PROBE_FLOPPY = \ ['/sbin/modprobe', 'floppy'] - cloudinit.sources.DataSourceAltCloud.CMD_MNT_FLOPPY = \ - ['/bin/mount', '/dev/fd0', \ - cloudinit.sources.DataSourceAltCloud.MEDIA_DIR] cloudinit.sources.DataSourceAltCloud.RETRY_TIMES = 3 cloudinit.sources.DataSourceAltCloud.SLEEP_SECS = 3 def test_user_data_rhevm(self): - '''Test user_data_rhevm() ''' - - cloudinit.sources.DataSourceAltCloud.MEDIA_DIR = \ - '/tmp/cloudinit_test_media' - cloudinit.sources.DataSourceAltCloud.DELTACLOUD_USER_DATA_FILE = \ - cloudinit.sources.DataSourceAltCloud.MEDIA_DIR + \ - '/deltacloud-user-data.txt' + '''Test user_data_rhevm() where mount_cb fails''' cloudinit.sources.DataSourceAltCloud.CMD_PROBE_FLOPPY = \ ['echo', 'modprobe floppy'] - cloudinit.sources.DataSourceAltCloud.CMD_MNT_FLOPPY = \ - ['echo', 'floppy mounted'] ds = DataSourceAltCloud({}, None, self.paths) - self.assertEquals(True, ds.user_data_rhevm()) + self.assertEquals(False, ds.user_data_rhevm()) def test_user_data_rhevm_modprobe_fails(self): '''Test user_data_rhevm() where modprobe fails. ''' - cloudinit.sources.DataSourceAltCloud.MEDIA_DIR = \ - '/tmp/cloudinit_test_media' - cloudinit.sources.DataSourceAltCloud.DELTACLOUD_USER_DATA_FILE = \ - cloudinit.sources.DataSourceAltCloud.MEDIA_DIR + \ - '/deltacloud-user-data.txt' - cloudinit.sources.DataSourceAltCloud.CMD_PROBE_FLOPPY = \ ['ls', 'modprobe floppy'] - cloudinit.sources.DataSourceAltCloud.CMD_MNT_FLOPPY = \ - ['echo', 'floppy mounted'] ds = DataSourceAltCloud({}, None, self.paths) @@ -326,89 +306,13 @@ class TestDataSouceAltCloud_user_data_rhevm(TestCase): def test_user_data_rhevm_no_modprobe_cmd(self): '''Test user_data_rhevm() with no modprobe command. ''' - cloudinit.sources.DataSourceAltCloud.MEDIA_DIR = \ - '/tmp/cloudinit_test_media' - cloudinit.sources.DataSourceAltCloud.DELTACLOUD_USER_DATA_FILE = \ - cloudinit.sources.DataSourceAltCloud.MEDIA_DIR + \ - '/deltacloud-user-data.txt' - cloudinit.sources.DataSourceAltCloud.CMD_PROBE_FLOPPY = \ ['bad command', 'modprobe floppy'] - cloudinit.sources.DataSourceAltCloud.CMD_MNT_FLOPPY = \ - ['echo', 'floppy mounted'] - - ds = DataSourceAltCloud({}, None, self.paths) - - self.assertEquals(False, ds.user_data_rhevm()) - - def test_user_data_rhevm_mount_fails(self): - '''Test user_data_rhevm() where mount fails. ''' - - cloudinit.sources.DataSourceAltCloud.MEDIA_DIR = \ - '/tmp/cloudinit_test_media' - cloudinit.sources.DataSourceAltCloud.DELTACLOUD_USER_DATA_FILE = \ - cloudinit.sources.DataSourceAltCloud.MEDIA_DIR + \ - '/deltacloud-user-data.txt' - - cloudinit.sources.DataSourceAltCloud.CMD_PROBE_FLOPPY = \ - ['echo', 'modprobe floppy'] - cloudinit.sources.DataSourceAltCloud.CMD_MNT_FLOPPY = \ - ['ls', 'floppy mounted'] ds = DataSourceAltCloud({}, None, self.paths) self.assertEquals(False, ds.user_data_rhevm()) - def test_user_data_rhevm_no_user_data_file(self): - '''Test user_data_rhevm() with no user data files.''' - - cloudinit.sources.DataSourceAltCloud.MEDIA_DIR = \ - '/tmp/cloudinit_test_media' - cloudinit.sources.DataSourceAltCloud.DELTACLOUD_USER_DATA_FILE = \ - cloudinit.sources.DataSourceAltCloud.MEDIA_DIR + \ - '/not-user-data.txt' - cloudinit.sources.DataSourceAltCloud.USER_DATA_FILE = \ - cloudinit.sources.DataSourceAltCloud.MEDIA_DIR + \ - '/not-user-data.txt' - - cloudinit.sources.DataSourceAltCloud.CMD_PROBE_FLOPPY = \ - ['echo', 'modprobe floppy'] - cloudinit.sources.DataSourceAltCloud.CMD_MNT_FLOPPY = \ - ['echo', 'floppy mounted'] - - ds = DataSourceAltCloud({}, None, self.paths) - - self.assertEquals(False, ds.user_data_rhevm()) - - cloudinit.sources.DataSourceAltCloud.DELTACLOUD_USER_DATA_FILE = \ - cloudinit.sources.DataSourceAltCloud.MEDIA_DIR + \ - '/deltacloud-user-data.txt' - cloudinit.sources.DataSourceAltCloud.USER_DATA_FILE = \ - cloudinit.sources.DataSourceAltCloud.MEDIA_DIR + \ - '/user-data.txt' - - def test_user_data_rhevm_no_user_data_file(self): - '''Test user_data_rhevm() with no deltacloud user data file.''' - - cloudinit.sources.DataSourceAltCloud.MEDIA_DIR = \ - '/tmp/cloudinit_test_media' - cloudinit.sources.DataSourceAltCloud.DELTACLOUD_USER_DATA_FILE = \ - cloudinit.sources.DataSourceAltCloud.MEDIA_DIR + \ - '/not-user-data.txt' - - cloudinit.sources.DataSourceAltCloud.CMD_PROBE_FLOPPY = \ - ['echo', 'modprobe floppy'] - cloudinit.sources.DataSourceAltCloud.CMD_MNT_FLOPPY = \ - ['echo', 'floppy mounted'] - - ds = DataSourceAltCloud({}, None, self.paths) - - self.assertEquals(True, ds.user_data_rhevm()) - - cloudinit.sources.DataSourceAltCloud.DELTACLOUD_USER_DATA_FILE = \ - cloudinit.sources.DataSourceAltCloud.MEDIA_DIR + \ - '/deltacloud-user-data.txt' - class TestDataSouceAltCloud_user_data_vsphere(TestCase): ''' Test to exercise method: DataSourceAltCloud.user_data_vsphere() @@ -416,125 +320,88 @@ class TestDataSouceAltCloud_user_data_vsphere(TestCase): def setUp(self): ''' Set up ''' self.paths = helpers.Paths({ 'cloud_dir': '/tmp' }) + self.mount_dir = '/tmp/cloudinit_test_media' + cloudinit.sources.DataSourceAltCloud.CLOUD_INFO_FILE = \ '/tmp/cloudinit_test_etc_sysconfig_cloud-info' - cloudinit.sources.DataSourceAltCloud.MEDIA_DIR = \ - '/tmp/cloudinit_test_media' - cloudinit.sources.DataSourceAltCloud.DELTACLOUD_USER_DATA_FILE = \ - cloudinit.sources.DataSourceAltCloud.MEDIA_DIR + \ - '/deltacloud-user-data.txt' - cloudinit.sources.DataSourceAltCloud.USER_DATA_FILE = \ - cloudinit.sources.DataSourceAltCloud.MEDIA_DIR + \ - '/user-data.txt' cloudinit.sources.DataSourceAltCloud.RETRY_TIMES = 1 cloudinit.sources.DataSourceAltCloud.SLEEP_SECS = 1 try: - os.mkdir(cloudinit.sources.DataSourceAltCloud.MEDIA_DIR) + os.mkdir(self.mount_dir) except OSError, (errno, strerror): # Ignore OSError: [Errno 17] File exists: if errno is not 17: raise - _write_user_data_files('test user data') + _write_user_data_files(self.mount_dir, 'test user data') def tearDown(self): # Reset - _remove_user_data_files() + _remove_user_data_files(self.mount_dir) cloudinit.sources.DataSourceAltCloud.CLOUD_INFO_FILE = \ - CLOUD_INFO_FILE = '/etc/sysconfig/cloud-info' - cloudinit.sources.DataSourceAltCloud.MEDIA_DIR = '/media' - - cloudinit.sources.DataSourceAltCloud.DELTACLOUD_USER_DATA_FILE = \ - cloudinit.sources.DataSourceAltCloud.MEDIA_DIR + \ - '/deltacloud-user-data.txt' - - cloudinit.sources.DataSourceAltCloud.CMD_MNT_CDROM = \ - ['/bin/mount', '/dev/fd0', \ - cloudinit.sources.DataSourceAltCloud.MEDIA_DIR] - + '/etc/sysconfig/cloud-info' cloudinit.sources.DataSourceAltCloud.RETRY_TIMES = 3 cloudinit.sources.DataSourceAltCloud.SLEEP_SECS = 3 def test_user_data_vsphere(self): - '''Test user_data_vsphere() ''' + '''Test user_data_vsphere() where mount_cb fails''' cloudinit.sources.DataSourceAltCloud.MEDIA_DIR = \ '/tmp/cloudinit_test_media' - cloudinit.sources.DataSourceAltCloud.DELTACLOUD_USER_DATA_FILE = \ - cloudinit.sources.DataSourceAltCloud.MEDIA_DIR + \ - '/deltacloud-user-data.txt' - - cloudinit.sources.DataSourceAltCloud.CMD_MNT_CDROM = \ - ['echo', 'floppy mounted'] ds = DataSourceAltCloud({}, None, self.paths) - self.assertEquals(True, ds.user_data_vsphere()) - - def test_user_data_vsphere_mount_fails(self): - '''Test user_data_vsphere() where mount fails. ''' - - cloudinit.sources.DataSourceAltCloud.MEDIA_DIR = \ - '/tmp/cloudinit_test_media' - cloudinit.sources.DataSourceAltCloud.DELTACLOUD_USER_DATA_FILE = \ - cloudinit.sources.DataSourceAltCloud.MEDIA_DIR + \ - '/deltacloud-user-data.txt' - - cloudinit.sources.DataSourceAltCloud.CMD_MNT_CDROM = \ - ['ls', 'floppy mounted'] + self.assertEquals(False, ds.user_data_vsphere()) - ds = DataSourceAltCloud({}, None, self.paths) +class TestDataSouceAltCloud_read_user_data_callback(TestCase): + ''' + Test to exercise method: DataSourceAltCloud.read_user_data_callback() + ''' + def setUp(self): + ''' Set up ''' + self.paths = helpers.Paths({ 'cloud_dir': '/tmp' }) + self.mount_dir = '/tmp/cloudinit_test_media' - self.assertEquals(False, ds.user_data_vsphere()) + _write_user_data_files(self.mount_dir, 'test user data') - def test_user_data_vsphere_no_user_data_file(self): - '''Test user_data_vsphere() with no user data files.''' + def tearDown(self): + # Reset - cloudinit.sources.DataSourceAltCloud.MEDIA_DIR = \ - '/tmp/cloudinit_test_media' - cloudinit.sources.DataSourceAltCloud.DELTACLOUD_USER_DATA_FILE = \ - cloudinit.sources.DataSourceAltCloud.MEDIA_DIR + \ - '/not-user-data.txt' - cloudinit.sources.DataSourceAltCloud.USER_DATA_FILE = \ - cloudinit.sources.DataSourceAltCloud.MEDIA_DIR + \ - '/not-user-data.txt' + _remove_user_data_files(self.mount_dir) - cloudinit.sources.DataSourceAltCloud.CMD_MNT_CDROM = \ - ['echo', 'floppy mounted'] + def test_read_user_data_callback_both(self): + '''Test read_user_data_callback() with both files''' - ds = DataSourceAltCloud({}, None, self.paths) + self.assertEquals('test user data', + read_user_data_callback(self.mount_dir)) - self.assertEquals(False, ds.user_data_vsphere()) + def test_read_user_data_callback_dc(self): + '''Test read_user_data_callback() with only DC file''' - cloudinit.sources.DataSourceAltCloud.DELTACLOUD_USER_DATA_FILE = \ - cloudinit.sources.DataSourceAltCloud.MEDIA_DIR + \ - '/deltacloud-user-data.txt' - cloudinit.sources.DataSourceAltCloud.USER_DATA_FILE = \ - cloudinit.sources.DataSourceAltCloud.MEDIA_DIR + \ - '/user-data.txt' + _remove_user_data_files(self.mount_dir, + dc_file=False, + non_dc_file=True) - def test_user_data_vsphere_no_user_data_file(self): - '''Test user_data_vsphere() with no deltacloud user data files.''' + self.assertEquals('test user data', + read_user_data_callback(self.mount_dir)) - cloudinit.sources.DataSourceAltCloud.MEDIA_DIR = \ - '/tmp/cloudinit_test_media' - cloudinit.sources.DataSourceAltCloud.DELTACLOUD_USER_DATA_FILE = \ - cloudinit.sources.DataSourceAltCloud.MEDIA_DIR + \ - '/not-user-data.txt' + def test_read_user_data_callback_non_dc(self): + '''Test read_user_data_callback() with only non-DC file''' - cloudinit.sources.DataSourceAltCloud.CMD_MNT_CDROM = \ - ['echo', 'floppy mounted'] + _remove_user_data_files(self.mount_dir, + dc_file=True, + non_dc_file=False) - ds = DataSourceAltCloud({}, None, self.paths) + self.assertEquals('test user data', + read_user_data_callback(self.mount_dir)) - self.assertEquals(True, ds.user_data_vsphere()) + def test_read_user_data_callback_none(self): + '''Test read_user_data_callback() no files are found''' - cloudinit.sources.DataSourceAltCloud.DELTACLOUD_USER_DATA_FILE = \ - cloudinit.sources.DataSourceAltCloud.MEDIA_DIR + \ - '/deltacloud-user-data.txt' + _remove_user_data_files(self.mount_dir) + self.assertEquals(None, read_user_data_callback(self.mount_dir)) # vi: ts=4 expandtab - -- cgit v1.2.3 From 6205d39ad5fed1bba31e4a97656beb213db251bc Mon Sep 17 00:00:00 2001 From: Joe VLcek Date: Fri, 10 Aug 2012 15:29:42 -0400 Subject: Address review feedback for: https://code.launchpad.net/~joev-n/cloud-init/altcloud-changes/+merge/116542/comments/255564 https://code.launchpad.net/~joev-n/cloud-init/altcloud-changes/+merge/116542/comments/255565 --- cloudinit/sources/DataSourceAltCloud.py | 63 +++--- tests/unittests/test_datasource/test_altcloud.py | 255 ++++++++++++----------- 2 files changed, 169 insertions(+), 149 deletions(-) diff --git a/cloudinit/sources/DataSourceAltCloud.py b/cloudinit/sources/DataSourceAltCloud.py index 0f4cc8e0..69c376a5 100644 --- a/cloudinit/sources/DataSourceAltCloud.py +++ b/cloudinit/sources/DataSourceAltCloud.py @@ -18,10 +18,14 @@ # You should have received a copy of the GNU General Public License # along with this program. If not, see . +''' +This file contains code used to gather the user data passed to an +instance on RHEVm and vSphere. +''' + import errno import os import os.path -import time from cloudinit import log as logging from cloudinit import sources @@ -36,10 +40,7 @@ CLOUD_INFO_FILE = '/etc/sysconfig/cloud-info' # Shell command lists CMD_DMI_SYSTEM = ['/usr/sbin/dmidecode', '--string', 'system-product-name'] CMD_PROBE_FLOPPY = ['/sbin/modprobe', 'floppy'] - -# Retry times and sleep secs between each try -RETRY_TIMES = 3 -SLEEP_SECS = 3 +CMD_UDEVADM_SETTLE = ['/sbin/udevadm', 'settle', '--quiet', '--timeout=5'] META_DATA_NOT_SUPPORTED = { 'block-device-mapping': {}, @@ -167,26 +168,19 @@ class DataSourceAltCloud(sources.DataSource): LOG.debug('cloud_type: ' + str(cloud_type)) - # Simple retry logic around user_data_() methods - tries = RETRY_TIMES - sleep_secs = SLEEP_SECS - while tries > 0: - if 'RHEV' in cloud_type: - if self.user_data_rhevm(): - return True - elif 'VSPHERE' in cloud_type: - if self.user_data_vsphere(): - return True - else: - # there was no recognized alternate cloud type. - # suggesting this handler should not be used. - return False - - time.sleep(sleep_secs) - tries -= 1 - sleep_secs *= 3 + if 'RHEV' in cloud_type: + if self.user_data_rhevm(): + return True + elif 'VSPHERE' in cloud_type: + if self.user_data_vsphere(): + return True + else: + # there was no recognized alternate cloud type + # indicating this handler should not be used. + return False - # Retry loop exhausted + # No user data found + util.logexc(LOG, ('Failed accessing user data.')) return False def user_data_rhevm(self): @@ -222,6 +216,22 @@ class DataSourceAltCloud(sources.DataSource): return False floppy_dev = '/dev/fd0' + + # udevadm settle for floppy device + try: + cmd = CMD_UDEVADM_SETTLE + cmd.append('--exit-if-exists=' + floppy_dev) + (cmd_out, _err) = util.subp(cmd) + LOG.debug(('Command: %s\nOutput%s') % (' '.join(cmd), cmd_out)) + except ProcessExecutionError, _err: + util.logexc(LOG, (('Failed command: %s\n%s') % \ + (' '.join(cmd), _err.message))) + return False + except OSError, _err: + util.logexc(LOG, (('Failed command: %s\n%s') % \ + (' '.join(cmd), _err.message))) + return False + try: return_str = util.mount_cb(floppy_dev, read_user_data_callback) except OSError as err: @@ -276,8 +286,11 @@ class DataSourceAltCloud(sources.DataSource): return False # Used to match classes to dependencies +# Source DataSourceAltCloud does not really depend on networking. +# In the future 'dsmode' like behavior can be added to offer user +# the ability to run before networking. datasources = [ - (DataSourceAltCloud, (sources.DEP_FILESYSTEM, )), + (DataSourceAltCloud, (sources.DEP_FILESYSTEM, sources.DEP_NETWORK)), ] diff --git a/tests/unittests/test_datasource/test_altcloud.py b/tests/unittests/test_datasource/test_altcloud.py index 27912652..b828711c 100644 --- a/tests/unittests/test_datasource/test_altcloud.py +++ b/tests/unittests/test_datasource/test_altcloud.py @@ -1,6 +1,29 @@ -#! /usr/bin/env python +# vi: ts=4 expandtab +# +# Copyright (C) 2009-2010 Canonical Ltd. +# Copyright (C) 2012 Hewlett-Packard Development Company, L.P. +# Copyright (C) 2012 Yahoo! Inc. +# +# Author: Joe VLcek +# +# This program is free software: you can redistribute it and/or modify +# it under the terms of the GNU General Public License version 3, as +# published by the Free Software Foundation. +# +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. +# +# You should have received a copy of the GNU General Public License +# along with this program. If not, see . +''' +This test file exercises the code in sources DataSourceAltCloud.py +''' import os +import shutil +import tempfile from unittest import TestCase from cloudinit import helpers @@ -16,9 +39,9 @@ def _write_cloud_info_file(value): with a cloud backend identifier ImageFactory when building an image with ImageFactory. ''' - f = open(cloudinit.sources.DataSourceAltCloud.CLOUD_INFO_FILE, 'w') - f.write(value) - f.close() + cifile = open(cloudinit.sources.DataSourceAltCloud.CLOUD_INFO_FILE, 'w') + cifile.write(value) + cifile.close() os.chmod(cloudinit.sources.DataSourceAltCloud.CLOUD_INFO_FILE, 0664) def _remove_cloud_info_file(): @@ -35,14 +58,14 @@ def _write_user_data_files(mount_dir, value): deltacloud_user_data_file = mount_dir + '/deltacloud-user-data.txt' user_data_file = mount_dir + '/user-data.txt' - f = open(deltacloud_user_data_file, 'w') - f.write(value) - f.close() + udfile = open(deltacloud_user_data_file, 'w') + udfile.write(value) + udfile.close() os.chmod(deltacloud_user_data_file, 0664) - f = open(user_data_file, 'w') - f.write(value) - f.close() + udfile = open(user_data_file, 'w') + udfile.write(value) + udfile.close() os.chmod(user_data_file, 0664) def _remove_user_data_files(mount_dir, @@ -68,8 +91,7 @@ def _remove_user_data_files(mount_dir, except OSError: pass - -class TestDataSouceAltCloud_get_cloud_type(TestCase): +class TestGetCloudType(TestCase): ''' Test to exercise method: DataSourceAltCloud.get_cloud_type() ''' @@ -77,70 +99,66 @@ class TestDataSouceAltCloud_get_cloud_type(TestCase): def setUp(self): ''' Set up ''' self.paths = helpers.Paths({ 'cloud_dir': '/tmp' }) - cloudinit.sources.DataSourceAltCloud.RETRY_TIMES = 1 - cloudinit.sources.DataSourceAltCloud.SLEEP_SECS = 1 def tearDown(self): # Reset cloudinit.sources.DataSourceAltCloud.CMD_DMI_SYSTEM = \ ['dmidecode', '--string', 'system-product-name'] - cloudinit.sources.DataSourceAltCloud.RETRY_TIMES = 3 - cloudinit.sources.DataSourceAltCloud.SLEEP_SECS = 3 - def test_get_cloud_type_RHEV(self): + def test_rhev(self): ''' Test method get_cloud_type() for RHEVm systems. Forcing dmidecode return to match a RHEVm system: RHEV Hypervisor ''' cloudinit.sources.DataSourceAltCloud.CMD_DMI_SYSTEM = \ ['echo', 'RHEV Hypervisor'] - ds = DataSourceAltCloud({}, None, self.paths) + dsrc = DataSourceAltCloud({}, None, self.paths) self.assertEquals('RHEV', \ - ds.get_cloud_type()) + dsrc.get_cloud_type()) - def test_get_cloud_type_VSPHERE(self): + def test_vsphere(self): ''' Test method get_cloud_type() for vSphere systems. Forcing dmidecode return to match a vSphere system: RHEV Hypervisor ''' cloudinit.sources.DataSourceAltCloud.CMD_DMI_SYSTEM = \ ['echo', 'VMware Virtual Platform'] - ds = DataSourceAltCloud({}, None, self.paths) + dsrc = DataSourceAltCloud({}, None, self.paths) self.assertEquals('VSPHERE', \ - ds.get_cloud_type()) + dsrc.get_cloud_type()) - def test_get_cloud_type_UNKNOWN(self): + def test_unknown(self): ''' Test method get_cloud_type() for unknown systems. Forcing dmidecode return to match an unrecognized return. ''' cloudinit.sources.DataSourceAltCloud.CMD_DMI_SYSTEM = \ ['echo', 'Unrecognized Platform'] - ds = DataSourceAltCloud({}, None, self.paths) + dsrc = DataSourceAltCloud({}, None, self.paths) self.assertEquals('UNKNOWN', \ - ds.get_cloud_type()) + dsrc.get_cloud_type()) - def test_get_cloud_type_exception1(self): + def test_exception1(self): ''' Test method get_cloud_type() where command dmidecode fails. ''' cloudinit.sources.DataSourceAltCloud.CMD_DMI_SYSTEM = \ ['ls', 'bad command'] - ds = DataSourceAltCloud({}, None, self.paths) + dsrc = DataSourceAltCloud({}, None, self.paths) self.assertEquals('UNKNOWN', \ - ds.get_cloud_type()) + dsrc.get_cloud_type()) - def test_get_cloud_type_exception(self): + def test_exception2(self): ''' Test method get_cloud_type() where command dmidecode is not available. ''' cloudinit.sources.DataSourceAltCloud.CMD_DMI_SYSTEM = \ ['bad command'] - ds = DataSourceAltCloud({}, None, self.paths) + dsrc = DataSourceAltCloud({}, None, self.paths) self.assertEquals('UNKNOWN', \ - ds.get_cloud_type()) + dsrc.get_cloud_type()) -class TestDataSouceAltCloud_get_data_cloud_info_file(TestCase): +class TestGetDataCloudInfoFile(TestCase): ''' Test to exercise method: DataSourceAltCloud.get_data() With a contrived CLOUD_INFO_FILE @@ -148,58 +166,62 @@ class TestDataSouceAltCloud_get_data_cloud_info_file(TestCase): def setUp(self): ''' Set up ''' self.paths = helpers.Paths({ 'cloud_dir': '/tmp' }) + self.cloud_info_file = tempfile.mkstemp()[1] cloudinit.sources.DataSourceAltCloud.CLOUD_INFO_FILE = \ - '/tmp/cloudinit_test_etc_sysconfig_cloud-info' - cloudinit.sources.DataSourceAltCloud.RETRY_TIMES = 1 - cloudinit.sources.DataSourceAltCloud.SLEEP_SECS = 1 + self.cloud_info_file def tearDown(self): # Reset + + # Attempt to remove the temp file ignoring errors + try: + os.remove(self.cloud_info_file) + except OSError: + pass + cloudinit.sources.DataSourceAltCloud.CLOUD_INFO_FILE = \ '/etc/sysconfig/cloud-info' - cloudinit.sources.DataSourceAltCloud.RETRY_TIMES = 3 - cloudinit.sources.DataSourceAltCloud.SLEEP_SECS = 3 - def test_get_data_RHEV_cloud_file(self): + def test_rhev(self): '''Success Test module get_data() forcing RHEV ''' _write_cloud_info_file('RHEV') - ds = DataSourceAltCloud({}, None, self.paths) - ds.user_data_rhevm = lambda : True - self.assertEquals(True, ds.get_data()) + dsrc = DataSourceAltCloud({}, None, self.paths) + dsrc.user_data_rhevm = lambda : True + self.assertEquals(True, dsrc.get_data()) - def test_get_data_VSPHERE_cloud_file(self): + def test_vsphere(self): '''Success Test module get_data() forcing VSPHERE ''' _write_cloud_info_file('VSPHERE') - ds = DataSourceAltCloud({}, None, self.paths) - ds.user_data_vsphere = lambda : True - self.assertEquals(True, ds.get_data()) + dsrc = DataSourceAltCloud({}, None, self.paths) + dsrc.user_data_vsphere = lambda : True + self.assertEquals(True, dsrc.get_data()) - def test_failure_get_data_RHEV_cloud_file(self): + def test_fail_rhev(self): '''Failure Test module get_data() forcing RHEV ''' _write_cloud_info_file('RHEV') - ds = DataSourceAltCloud({}, None, self.paths) - ds.user_data_rhevm = lambda : False - self.assertEquals(False, ds.get_data()) + dsrc = DataSourceAltCloud({}, None, self.paths) + dsrc.user_data_rhevm = lambda : False + self.assertEquals(False, dsrc.get_data()) - def test_failure_get_data_VSPHERE_cloud_file(self): + def test_fail_vsphere(self): '''Failure Test module get_data() forcing VSPHERE ''' _write_cloud_info_file('VSPHERE') - ds = DataSourceAltCloud({}, None, self.paths) - ds.user_data_vsphere = lambda : False - self.assertEquals(False, ds.get_data()) + dsrc = DataSourceAltCloud({}, None, self.paths) + dsrc.user_data_vsphere = lambda : False + self.assertEquals(False, dsrc.get_data()) - def test_failure_get_data_unrecognized_cloud_file(self): + def test_unrecognized(self): '''Failure Test module get_data() forcing unrecognized ''' _write_cloud_info_file('unrecognized') - ds = DataSourceAltCloud({}, None, self.paths) - self.assertEquals(False, ds.get_data()) + dsrc = DataSourceAltCloud({}, None, self.paths) + self.assertEquals(False, dsrc.get_data()) -class TestDataSouceAltCloud_get_data_no_cloud_info_file(TestCase): +class TestGetDataNoCloudInfoFile(TestCase): ''' Test to exercise method: DataSourceAltCloud.get_data() Without a CLOUD_INFO_FILE @@ -209,8 +231,6 @@ class TestDataSouceAltCloud_get_data_no_cloud_info_file(TestCase): self.paths = helpers.Paths({ 'cloud_dir': '/tmp' }) cloudinit.sources.DataSourceAltCloud.CLOUD_INFO_FILE = \ 'no such file' - cloudinit.sources.DataSourceAltCloud.RETRY_TIMES = 1 - cloudinit.sources.DataSourceAltCloud.SLEEP_SECS = 1 def tearDown(self): # Reset @@ -218,55 +238,41 @@ class TestDataSouceAltCloud_get_data_no_cloud_info_file(TestCase): '/etc/sysconfig/cloud-info' cloudinit.sources.DataSourceAltCloud.CMD_DMI_SYSTEM = \ ['dmidecode', '--string', 'system-product-name'] - cloudinit.sources.DataSourceAltCloud.RETRY_TIMES = 3 - cloudinit.sources.DataSourceAltCloud.SLEEP_SECS = 3 - def test_get_data_RHEV_cloud_file(self): + def test_rhev_no_cloud_file(self): '''Test No cloud info file module get_data() forcing RHEV ''' cloudinit.sources.DataSourceAltCloud.CMD_DMI_SYSTEM = \ ['echo', 'RHEV Hypervisor'] - ds = DataSourceAltCloud({}, None, self.paths) - ds.user_data_rhevm = lambda : True - self.assertEquals(True, ds.get_data()) + dsrc = DataSourceAltCloud({}, None, self.paths) + dsrc.user_data_rhevm = lambda : True + self.assertEquals(True, dsrc.get_data()) - def test_get_data_VSPHERE_cloud_file(self): + def test_vsphere_no_cloud_file(self): '''Test No cloud info file module get_data() forcing VSPHERE ''' cloudinit.sources.DataSourceAltCloud.CMD_DMI_SYSTEM = \ ['echo', 'VMware Virtual Platform'] - ds = DataSourceAltCloud({}, None, self.paths) - ds.user_data_vsphere = lambda : True - self.assertEquals(True, ds.get_data()) + dsrc = DataSourceAltCloud({}, None, self.paths) + dsrc.user_data_vsphere = lambda : True + self.assertEquals(True, dsrc.get_data()) - def test_failure_get_data_VSPHERE_cloud_file(self): + def test_failure_no_cloud_file(self): '''Test No cloud info file module get_data() forcing unrecognized ''' cloudinit.sources.DataSourceAltCloud.CMD_DMI_SYSTEM = \ ['echo', 'Unrecognized Platform'] - ds = DataSourceAltCloud({}, None, self.paths) - self.assertEquals(False, ds.get_data()) + dsrc = DataSourceAltCloud({}, None, self.paths) + self.assertEquals(False, dsrc.get_data()) -class TestDataSouceAltCloud_user_data_rhevm(TestCase): +class TestUserDataRhevm(TestCase): ''' Test to exercise method: DataSourceAltCloud.user_data_rhevm() ''' def setUp(self): ''' Set up ''' self.paths = helpers.Paths({ 'cloud_dir': '/tmp' }) - self.mount_dir = '/tmp/cloudinit_test_media' - - cloudinit.sources.DataSourceAltCloud.CLOUD_INFO_FILE = \ - '/tmp/cloudinit_test_etc_sysconfig_cloud-info' - cloudinit.sources.DataSourceAltCloud.RETRY_TIMES = 1 - cloudinit.sources.DataSourceAltCloud.SLEEP_SECS = 1 - - try: - os.mkdir(self.mount_dir) - except OSError, (errno, strerror): - # Ignore OSError: [Errno 17] File exists: - if errno is not 17: - raise + self.mount_dir = tempfile.mkdtemp() _write_user_data_files(self.mount_dir, 'test user data') @@ -275,64 +281,55 @@ class TestDataSouceAltCloud_user_data_rhevm(TestCase): _remove_user_data_files(self.mount_dir) + # Attempt to remove the temp dir ignoring errors + try: + shutil.rmtree(self.mount_dir) + except OSError: + pass + cloudinit.sources.DataSourceAltCloud.CLOUD_INFO_FILE = \ '/etc/sysconfig/cloud-info' - cloudinit.sources.DataSourceAltCloud.CMD_PROBE_FLOPPY = \ ['/sbin/modprobe', 'floppy'] - cloudinit.sources.DataSourceAltCloud.RETRY_TIMES = 3 - cloudinit.sources.DataSourceAltCloud.SLEEP_SECS = 3 - def test_user_data_rhevm(self): + def test_mount_cb_fails(self): '''Test user_data_rhevm() where mount_cb fails''' cloudinit.sources.DataSourceAltCloud.CMD_PROBE_FLOPPY = \ ['echo', 'modprobe floppy'] - ds = DataSourceAltCloud({}, None, self.paths) + dsrc = DataSourceAltCloud({}, None, self.paths) - self.assertEquals(False, ds.user_data_rhevm()) + self.assertEquals(False, dsrc.user_data_rhevm()) - def test_user_data_rhevm_modprobe_fails(self): + def test_modprobe_fails(self): '''Test user_data_rhevm() where modprobe fails. ''' cloudinit.sources.DataSourceAltCloud.CMD_PROBE_FLOPPY = \ ['ls', 'modprobe floppy'] - ds = DataSourceAltCloud({}, None, self.paths) + dsrc = DataSourceAltCloud({}, None, self.paths) - self.assertEquals(False, ds.user_data_rhevm()) + self.assertEquals(False, dsrc.user_data_rhevm()) - def test_user_data_rhevm_no_modprobe_cmd(self): + def test_no_modprobe_cmd(self): '''Test user_data_rhevm() with no modprobe command. ''' cloudinit.sources.DataSourceAltCloud.CMD_PROBE_FLOPPY = \ ['bad command', 'modprobe floppy'] - ds = DataSourceAltCloud({}, None, self.paths) + dsrc = DataSourceAltCloud({}, None, self.paths) - self.assertEquals(False, ds.user_data_rhevm()) + self.assertEquals(False, dsrc.user_data_rhevm()) -class TestDataSouceAltCloud_user_data_vsphere(TestCase): +class TestUserDataVsphere(TestCase): ''' Test to exercise method: DataSourceAltCloud.user_data_vsphere() ''' def setUp(self): ''' Set up ''' self.paths = helpers.Paths({ 'cloud_dir': '/tmp' }) - self.mount_dir = '/tmp/cloudinit_test_media' - - cloudinit.sources.DataSourceAltCloud.CLOUD_INFO_FILE = \ - '/tmp/cloudinit_test_etc_sysconfig_cloud-info' - cloudinit.sources.DataSourceAltCloud.RETRY_TIMES = 1 - cloudinit.sources.DataSourceAltCloud.SLEEP_SECS = 1 - - try: - os.mkdir(self.mount_dir) - except OSError, (errno, strerror): - # Ignore OSError: [Errno 17] File exists: - if errno is not 17: - raise + self.mount_dir = tempfile.mkdtemp() _write_user_data_files(self.mount_dir, 'test user data') @@ -341,29 +338,32 @@ class TestDataSouceAltCloud_user_data_vsphere(TestCase): _remove_user_data_files(self.mount_dir) + # Attempt to remove the temp dir ignoring errors + try: + shutil.rmtree(self.mount_dir) + except OSError: + pass + cloudinit.sources.DataSourceAltCloud.CLOUD_INFO_FILE = \ '/etc/sysconfig/cloud-info' - cloudinit.sources.DataSourceAltCloud.RETRY_TIMES = 3 - cloudinit.sources.DataSourceAltCloud.SLEEP_SECS = 3 def test_user_data_vsphere(self): '''Test user_data_vsphere() where mount_cb fails''' - cloudinit.sources.DataSourceAltCloud.MEDIA_DIR = \ - '/tmp/cloudinit_test_media' + cloudinit.sources.DataSourceAltCloud.MEDIA_DIR = self.mount_dir - ds = DataSourceAltCloud({}, None, self.paths) + dsrc = DataSourceAltCloud({}, None, self.paths) - self.assertEquals(False, ds.user_data_vsphere()) + self.assertEquals(False, dsrc.user_data_vsphere()) -class TestDataSouceAltCloud_read_user_data_callback(TestCase): +class TestReadUserDataCallback(TestCase): ''' Test to exercise method: DataSourceAltCloud.read_user_data_callback() ''' def setUp(self): ''' Set up ''' self.paths = helpers.Paths({ 'cloud_dir': '/tmp' }) - self.mount_dir = '/tmp/cloudinit_test_media' + self.mount_dir = tempfile.mkdtemp() _write_user_data_files(self.mount_dir, 'test user data') @@ -372,13 +372,20 @@ class TestDataSouceAltCloud_read_user_data_callback(TestCase): _remove_user_data_files(self.mount_dir) - def test_read_user_data_callback_both(self): + # Attempt to remove the temp dir ignoring errors + try: + shutil.rmtree(self.mount_dir) + except OSError: + pass + + + def test_callback_both(self): '''Test read_user_data_callback() with both files''' self.assertEquals('test user data', read_user_data_callback(self.mount_dir)) - def test_read_user_data_callback_dc(self): + def test_callback_dc(self): '''Test read_user_data_callback() with only DC file''' _remove_user_data_files(self.mount_dir, @@ -388,7 +395,7 @@ class TestDataSouceAltCloud_read_user_data_callback(TestCase): self.assertEquals('test user data', read_user_data_callback(self.mount_dir)) - def test_read_user_data_callback_non_dc(self): + def test_callback_non_dc(self): '''Test read_user_data_callback() with only non-DC file''' _remove_user_data_files(self.mount_dir, @@ -398,7 +405,7 @@ class TestDataSouceAltCloud_read_user_data_callback(TestCase): self.assertEquals('test user data', read_user_data_callback(self.mount_dir)) - def test_read_user_data_callback_none(self): + def test_callback_none(self): '''Test read_user_data_callback() no files are found''' _remove_user_data_files(self.mount_dir) -- cgit v1.2.3 From 213e18a5cd826445c36ac613e7e7459f24cffb4b Mon Sep 17 00:00:00 2001 From: Joe VLcek Date: Mon, 13 Aug 2012 16:41:18 -0400 Subject: add test to excercise udevadm usage. --- tests/unittests/test_datasource/test_altcloud.py | 22 ++++++++++++++++++++++ 1 file changed, 22 insertions(+) diff --git a/tests/unittests/test_datasource/test_altcloud.py b/tests/unittests/test_datasource/test_altcloud.py index b828711c..54e152e9 100644 --- a/tests/unittests/test_datasource/test_altcloud.py +++ b/tests/unittests/test_datasource/test_altcloud.py @@ -291,6 +291,8 @@ class TestUserDataRhevm(TestCase): '/etc/sysconfig/cloud-info' cloudinit.sources.DataSourceAltCloud.CMD_PROBE_FLOPPY = \ ['/sbin/modprobe', 'floppy'] + cloudinit.sources.DataSourceAltCloud.CMD_UDEVADM_SETTLE = \ + ['/sbin/udevadm', 'settle', '--quiet', '--timeout=5'] def test_mount_cb_fails(self): '''Test user_data_rhevm() where mount_cb fails''' @@ -322,6 +324,26 @@ class TestUserDataRhevm(TestCase): self.assertEquals(False, dsrc.user_data_rhevm()) + def test_udevadm_fails(self): + '''Test user_data_rhevm() where udevadm fails. ''' + + cloudinit.sources.DataSourceAltCloud.CMD_UDEVADM_SETTLE = \ + ['ls', 'udevadm floppy'] + + dsrc = DataSourceAltCloud({}, None, self.paths) + + self.assertEquals(False, dsrc.user_data_rhevm()) + + def test_no_udevadm_cmd(self): + '''Test user_data_rhevm() with no udevadm command. ''' + + cloudinit.sources.DataSourceAltCloud.CMD_UDEVADM_SETTLE = \ + ['bad command', 'udevadm floppy'] + + dsrc = DataSourceAltCloud({}, None, self.paths) + + self.assertEquals(False, dsrc.user_data_rhevm()) + class TestUserDataVsphere(TestCase): ''' Test to exercise method: DataSourceAltCloud.user_data_vsphere() -- cgit v1.2.3