diff --git a/playbooks/inventory/dynamic_inventory.py b/playbooks/inventory/dynamic_inventory.py index 0d81cf2b8a..67577ea9fe 100755 --- a/playbooks/inventory/dynamic_inventory.py +++ b/playbooks/inventory/dynamic_inventory.py @@ -826,24 +826,29 @@ def _ensure_inventory_uptodate(inventory, container_skel): :param inventory: ``dict`` Living inventory of containers and hosts """ - for key, value in inventory['_meta']['hostvars'].iteritems(): - if 'container_name' not in value: - value['container_name'] = key + host_vars = inventory['_meta']['hostvars'] + for hostname, _vars in host_vars.items(): + if 'container_name' not in _vars: + _vars['container_name'] = hostname for rh in REQUIRED_HOSTVARS: - if rh not in value: - value[rh] = None + if rh not in _vars: + _vars[rh] = None if rh == 'container_networks': - value[rh] = {} + _vars[rh] = {} - for key, value in container_skel.iteritems(): - item = inventory.get(key) + # For each of the various properties in the container skeleton, + # copy them into the host's properties dictionary + for container_type, type_vars in container_skel.items(): + item = inventory.get(container_type) + # Note: this creates an implicit dependency on skel_setup which + # adds the hosts entries. hosts = item.get('hosts') if hosts: for host in hosts: - container = inventory['_meta']['hostvars'][host] - if 'properties' in value: - container['properties'] = value['properties'] + container = host_vars[host] + if 'properties' in type_vars: + container['properties'] = type_vars['properties'] def _parse_global_variables(user_cidr, inventory, user_defined_config): diff --git a/tests/test_inventory.py b/tests/test_inventory.py index 4364940793..3798284a12 100644 --- a/tests/test_inventory.py +++ b/tests/test_inventory.py @@ -1,6 +1,7 @@ #!/usr/bin/env python import collections +import copy import json import os from os import path @@ -669,5 +670,63 @@ class TestMultipleRuns(unittest.TestCase): # this test. cleanup() + +class TestEnsureInventoryUptoDate(unittest.TestCase): + def setUp(self): + self.env = di.load_environment(TARGET_DIR) + # Copy because we manipulate the structure in each test; + # not copying would modify the global var in the target code + self.inv = copy.deepcopy(di.INVENTORY_SKEL) + # Since we're not running skel_setup, add necessary keys + self.host_vars = self.inv['_meta']['hostvars'] + + # The _ensure_inventory_uptodate function depends on values inserted + # by the skel_setup function + di.skel_setup(self.env, self.inv) + + def test_missing_required_host_vars(self): + self.host_vars['host1'] = {} + + di._ensure_inventory_uptodate(self.inv, self.env['container_skel']) + + for required_key in di.REQUIRED_HOSTVARS: + self.assertIn(required_key, self.host_vars['host1']) + + def test_missing_container_name(self): + self.host_vars['host1'] = {} + + di._ensure_inventory_uptodate(self.inv, self.env['container_skel']) + + self.assertIn('container_name', self.host_vars['host1']) + self.assertEqual(self.host_vars['host1']['container_name'], 'host1') + + def test_inserting_container_networks_is_dict(self): + self.host_vars['host1'] = {} + + di._ensure_inventory_uptodate(self.inv, self.env['container_skel']) + + self.assertIsInstance(self.host_vars['host1']['container_networks'], + dict) + + def test_populating_inventory_info(self): + skel = self.env['container_skel'] + + di._ensure_inventory_uptodate(self.inv, skel) + + for container_type, type_vars in skel.items(): + hosts = self.inv[container_type]['hosts'] + if hosts: + for host in hosts: + host_var_entries = self.inv['_meta']['hostvars'][host] + if 'properties' in type_vars: + self.assertEqual(host_var_entries['properties'], + type_vars['properties']) + + def tearDown(self): + self.env = None + self.host_vars = None + self.inv = None + + if __name__ == '__main__': unittest.main()