diff options
-rw-r--r-- | cloudinit/sources/DataSourceOpenNebula.py | 78 | ||||
-rw-r--r-- | tests/unittests/test_datasource/test_opennebula.py | 95 |
2 files changed, 132 insertions, 41 deletions
diff --git a/cloudinit/sources/DataSourceOpenNebula.py b/cloudinit/sources/DataSourceOpenNebula.py index a1995aca..1b419cfd 100644 --- a/cloudinit/sources/DataSourceOpenNebula.py +++ b/cloudinit/sources/DataSourceOpenNebula.py @@ -35,6 +35,7 @@ LOG = logging.getLogger(__name__) DEFAULT_IID = "iid-dsopennebula" DEFAULT_MODE = 'net' +DEFAULT_PARSEUSER = 'nobody' CONTEXT_DISK_FILES = ["context.sh"] VALID_DSMODES = ("local", "net", "disabled") @@ -51,33 +52,35 @@ class DataSourceOpenNebula(sources.DataSource): return "%s [seed=%s][dsmode=%s]" % (root, self.seed, self.dsmode) def get_data(self): - defaults = { - "instance-id": DEFAULT_IID, #TODO:???? - "DSMODE": self.dsmode - } - + defaults = {"instance-id": DEFAULT_IID} + results = None seed = None - results = {} - # first try to read local seed_dir - if os.path.isdir(self.seed_dir): + # decide parseuser for context.sh shell reader + parseuser = DEFAULT_PARSEUSER + if self.ds_cfg.get('parseuser'): + parseuser = self.ds_cfg.get('parseuser') + + candidates = [self.seed_dir] + candidates.extend(find_candidate_devs()) + for cdev in candidates: try: - results = read_context_disk_dir(self.seed_dir) - seed = self.seed_dir + if os.path.isdir(self.seed_dir): + results = read_context_disk_dir(cdev, asuser=parseuser) + elif cdev.startswith("/dev"): + results = util.mount_cb(cdev, read_context_disk_dir, + data=parseuser) except NonContextDiskDir: - util.logexc(LOG, "Failed reading context from %s", - self.seed_dir) + continue + except BrokenContextDiskDir as exc: + raise exc + except util.MountFailedError: + LOG.warn("%s was not mountable" % cdev) - if not seed: - # then try to detect and mount candidate devices and - # read contextualization if present - for dev in find_candidate_devs(): - try: - results = util.mount_cb(dev, read_context_disk_dir) - seed = dev - break - except (NonContextDiskDir, util.MountFailedError): - pass + if results: + seed = cdev + LOG.debug("found datasource in %s", cdev) + break if not seed: return False @@ -95,7 +98,7 @@ class DataSourceOpenNebula(sources.DataSource): # decide dsmode if user_dsmode: dsmode = user_dsmode - elif self.ds_cfg.get('dsmode'): #TODO: fakt se k tomu nekdy dostane? + elif self.ds_cfg.get('dsmode'): dsmode = self.ds_cfg.get('dsmode') else: dsmode = DEFAULT_MODE @@ -137,6 +140,10 @@ class NonContextDiskDir(Exception): pass +class BrokenContextDiskDir(Exception): + pass + + class OpenNebulaNetwork(object): REG_DEV_MAC = re.compile( r'^\d+: (eth\d+):.*?link\/ether (..:..:..:..:..:..) ?', @@ -306,7 +313,8 @@ def parse_shell_config(content, keylist=None, bash=None, asuser=None): cmd.extend(bash) sp = subprocess.Popen(cmd, stdin=subprocess.PIPE, - stdout=subprocess.PIPE) + stdout=subprocess.PIPE, + stderr=subprocess.PIPE) (output, error) = sp.communicate(input=bcmd) if sp.returncode != 0: @@ -340,7 +348,7 @@ def parse_shell_config(content, keylist=None, bash=None, asuser=None): return ret -def read_context_disk_dir(source_dir): +def read_context_disk_dir(source_dir, asuser=None): """ read_context_disk_dir(source_dir): read source_dir and return a tuple with metadata dict and user-data @@ -355,23 +363,31 @@ def read_context_disk_dir(source_dir): if not found: raise NonContextDiskDir("%s: %s" % (source_dir, "no files found")) - results = {'userdata': None, 'metadata': {}} context = {} + results = {'userdata': None, 'metadata': {}} if "context.sh" in found: try: with open(os.path.join(source_dir, 'context.sh'), 'r') as f: - content = f.read().strip() - if content: - context = parse_shell_config(content) + content = f.read().strip() f.close() - except (IOError, Exception) as e: + + # don't pass empty context script + # to shell parser + non_empty = re.match(r'.*?^\s*([^# ]+)', content, + re.MULTILINE | re.DOTALL) + + if non_empty: + context = parse_shell_config(content, asuser=asuser) + except IOError as e: raise NonContextDiskDir("Error reading context.sh: %s" % (e)) + except Exception as e: + raise BrokenContextDiskDir("Error processing context.sh: %s" % (e)) else: raise NonContextDiskDir("Missing context.sh") if not context: - raise NonContextDiskDir("No context variables found") + return results results['metadata'] = context diff --git a/tests/unittests/test_datasource/test_opennebula.py b/tests/unittests/test_datasource/test_opennebula.py index f544e145..752638b6 100644 --- a/tests/unittests/test_datasource/test_opennebula.py +++ b/tests/unittests/test_datasource/test_opennebula.py @@ -1,4 +1,5 @@ from cloudinit.sources import DataSourceOpenNebula as ds +from cloudinit import helpers from cloudinit import util from mocker import MockerTestCase from tests.unittests.helpers import populate_dir @@ -20,6 +21,8 @@ TEST_VARS = { 'VAR12': '$', # expect $ } +INVALID_PARSEUSER = 'cloud-init-mocker-opennebula-invalid' +INVALID_CONTEXT = ';' USER_DATA = '#cloud-config\napt_upgrade: true' SSH_KEY = 'ssh-rsa AAAAB3NzaC1....sIkJhq8wdX+4I3A4cYbYP ubuntu@server-460-%i' HOSTNAME = 'foo.example.com' @@ -38,21 +41,93 @@ class TestOpenNebulaDataSource(MockerTestCase): def setUp(self): super(TestOpenNebulaDataSource, self).setUp() self.tmp = self.makeDir() + self.paths = helpers.Paths({'cloud_dir': self.tmp}) + + # defaults for few tests + self.ds = ds.DataSourceOpenNebula + self.seed_dir = os.path.join(self.paths.seed_dir, "opennebula") + self.sys_cfg = {'datasource': {'OpenNebula': {'dsmode': 'local'}}} + + def test_get_data_non_contextdisk(self): + try: + # dont' try to lookup for CDs + orig_find_devs_with = util.find_devs_with + util.find_devs_with = lambda n: [] + + dsrc = self.ds(sys_cfg=self.sys_cfg, distro=None, paths=self.paths) + ret = dsrc.get_data() + self.assertFalse(ret) + finally: + util.find_devs_with = orig_find_devs_with + + def test_get_data_broken_contextdisk(self): + try: + # dont' try to lookup for CDs + orig_find_devs_with = util.find_devs_with + util.find_devs_with = lambda n: [] + + populate_dir(self.seed_dir, {'context.sh': INVALID_CONTEXT}) + dsrc = self.ds(sys_cfg=self.sys_cfg, distro=None, paths=self.paths) + self.assertRaises(ds.BrokenContextDiskDir, dsrc.get_data) + finally: + util.find_devs_with = orig_find_devs_with + + def test_get_data_invalid_identity(self): + try: + # dont' try to lookup for CDs + orig_find_devs_with = util.find_devs_with + util.find_devs_with = lambda n: [] + + sys_cfg = self.sys_cfg + sys_cfg['datasource']['OpenNebula']['parseuser'] = \ + INVALID_PARSEUSER + + populate_context_dir(self.seed_dir, {'KEY1': 'val1'}) + dsrc = self.ds(sys_cfg=sys_cfg, distro=None, paths=self.paths) + self.assertRaises(ds.BrokenContextDiskDir, dsrc.get_data) + finally: + util.find_devs_with = orig_find_devs_with + + def test_get_data(self): + try: + # dont' try to lookup for CDs + orig_find_devs_with = util.find_devs_with + util.find_devs_with = lambda n: [] + populate_context_dir(self.seed_dir, {'KEY1': 'val1'}) + dsrc = self.ds(sys_cfg=self.sys_cfg, distro=None, paths=self.paths) + ret = dsrc.get_data() + self.assertTrue(ret) + finally: + util.find_devs_with = orig_find_devs_with def test_seed_dir_non_contextdisk(self): - my_d = os.path.join(self.tmp, 'non-contextdisk') - self.assertRaises(ds.NonContextDiskDir, ds.read_context_disk_dir, my_d) + self.assertRaises(ds.NonContextDiskDir, ds.read_context_disk_dir, + self.seed_dir) + + def test_seed_dir_empty1_context(self): + populate_dir(self.seed_dir, {'context.sh': ''}) + results = ds.read_context_disk_dir(self.seed_dir) + + self.assertEqual(results['userdata'], None) + self.assertEqual(results['metadata'], {}) + + def test_seed_dir_empty2_context(self): + populate_context_dir(self.seed_dir, {}) + results = ds.read_context_disk_dir(self.seed_dir) + + self.assertEqual(results['userdata'], None) + self.assertEqual(results['metadata'], {}) + + def test_seed_dir_broken_context(self): + populate_dir(self.seed_dir, {'context.sh': INVALID_CONTEXT}) - def test_seed_dir_bad_context(self): - my_d = os.path.join(self.tmp, 'bad-context') - os.mkdir(my_d) - open(os.path.join(my_d, "context.sh"), "w").close() - self.assertRaises(ds.NonContextDiskDir, ds.read_context_disk_dir, my_d) + self.assertRaises(ds.BrokenContextDiskDir, + ds.read_context_disk_dir, + self.seed_dir) def test_context_parser(self): - my_d = os.path.join(self.tmp, 'context-parser') - populate_context_dir(my_d, TEST_VARS) - results = ds.read_context_disk_dir(my_d) + populate_context_dir(self.seed_dir, TEST_VARS) + results = ds.read_context_disk_dir(self.seed_dir) self.assertTrue('metadata' in results) self.assertEqual(TEST_VARS, results['metadata']) |