def _download_ansible_playbooks(self, client, stack_name, output_dir): stack_config = config.Config(client) print('** Downloading undercloud ansible.. **') # python output buffering is making this seem to take forever.. sys.stdout.flush() stack_config.download_config('undercloud', output_dir) # Sadly the above writes the ansible config to a new directory each # time. This finds the newest new entry. # The use of tmpdirs is going away, but we need to remain # backwards compatible for now to pass the gate. lp:17252118 def find_ansible_dir(): tmpdirs = glob.glob('%s/tripleo-*-config' % output_dir) if not tmpdirs: return output_dir else: return max(tmpdirs, key=os.path.getctime) ansible_dir = find_ansible_dir() inventory = TripleoInventory(hclient=client, plan_name=stack_name, ansible_ssh_user='******') inv_path = os.path.join(ansible_dir, 'inventory.yaml') extra_vars = {'Undercloud': {'ansible_connection': 'local'}} inventory.write_static_inventory(inv_path, extra_vars) print('** Downloaded undercloud ansible to %s **' % ansible_dir) sys.stdout.flush() return ansible_dir
def _download_ansible_playbooks(self, client, stack_name, tripleo_role_name='Standalone'): stack_config = config.Config(client) self._create_working_dirs() self.log.warning( _('** Downloading {0} ansible.. **').format(stack_name)) # python output buffering is making this seem to take forever.. sys.stdout.flush() stack_config.write_config(stack_config.fetch_config(stack_name), stack_name, self.tmp_ansible_dir) inventory = TripleoInventory(hclient=client, plan_name=stack_name, ansible_ssh_user='******') inv_path = os.path.join(self.tmp_ansible_dir, 'inventory.yaml') extra_vars = {tripleo_role_name: {'ansible_connection': 'local'}} inventory.write_static_inventory(inv_path, extra_vars) self.log.info( _('** Downloaded {0} ansible to {1} **').format( stack_name, self.tmp_ansible_dir)) sys.stdout.flush() return self.tmp_ansible_dir
def run(self, context): inventory_path = os.path.join(self.work_dir, 'tripleo-ansible-inventory.yaml') inventory = TripleoInventory( session=self.get_session(context, 'heat'), hclient=self.get_orchestration_client(context), auth_url=context.security.auth_uri, cacert=context.security.auth_cacert, project_name=context.security.project_name, username=context.security.user_name, ansible_ssh_user=self.ansible_ssh_user, plan_name=self.plan_name) inventory.write_static_inventory(inventory_path) return inventory_path
class TestInventory(base.TestCase): def setUp(self): super(TestInventory, self).setUp() self.outputs_data = {'outputs': [ {'output_key': 'EnabledServices', 'output_value': { 'Controller': ['sa', 'sb'], 'Compute': ['sd', 'se'], 'CustomRole': ['sg', 'sh']}}, {'output_key': 'KeystoneURL', 'output_value': 'xyz://keystone'}, {'output_key': 'ServerIdData', 'output_value': { 'server_ids': { 'Controller': ['a', 'b', 'c'], 'Compute': ['d'], 'CustomRole': ['e']}, 'bootstrap_server_id': 'a'}}, {'output_key': 'RoleNetHostnameMap', 'output_value': { 'Controller': { 'ctlplane': ['c-0.ctlplane.localdomain', 'c-1.ctlplane.localdomain', 'c-2.ctlplane.localdomain']}, 'Compute': { 'ctlplane': ['cp-0.ctlplane.localdomain']}, 'CustomRole': { 'ctlplane': ['cs-0.ctlplane.localdomain']}}}, {'output_key': 'RoleNetIpMap', 'output_value': { 'Controller': { 'ctlplane': ['x.x.x.1', 'x.x.x.2', 'x.x.x.3']}, 'Compute': { 'ctlplane': ['y.y.y.1']}, 'CustomRole': { 'ctlplane': ['z.z.z.1']}}}, {'output_key': 'VipMap', 'output_value': { 'ctlplane': 'x.x.x.4', 'redis': 'x.x.x.6'}}, {'output_key': 'RoleData', 'output_value': { 'Controller': {'config_settings': 'foo1'}, 'Compute': {'config_settings': 'foo2'}, 'CustomRole': {'config_settings': 'foo3'}}}]} self.plan_name = 'overcloud' self.hclient = MagicMock() self.hclient.stacks.environment.return_value = { 'parameter_defaults': {'AdminPassword': '******', 'ContainerCli': 'podman'}} self.mock_stack = MagicMock() self.mock_stack.outputs = self.outputs_data['outputs'] self.hclient.stacks.get.return_value = self.mock_stack self.session = MagicMock() self.session.get_token.return_value = 'atoken' self.session.get_endpoint.return_value = 'anendpoint' self.outputs = StackOutputs('overcloud', self.hclient) self.inventory = TripleoInventory( session=self.session, hclient=self.hclient, plan_name=self.plan_name, auth_url='xyz://keystone.local', cacert='acacert', project_name='admin', username='******', ansible_ssh_user='******') self.inventory.stack_outputs = self.outputs def test_get_roles_by_service(self): services = TripleoInventory.get_roles_by_service( MOCK_ENABLED_SERVICES) expected = { 'kernel': ['BlockStorage', 'CephStorage', 'Compute', 'Controller', 'ObjectStorage'], 'swift_storage': ['ObjectStorage'], 'tripleo_packages': ['BlockStorage', 'CephStorage', 'Compute', 'Controller', 'ObjectStorage'], 'keystone': ['Controller'], 'nova_compute': ['Compute'], 'cinder_volume': ['BlockStorage'], } self.assertDictEqual(services, expected) def test_outputs_are_empty_if_stack_doesnt_exist(self): self.hclient.stacks.get.side_effect = HTTPNotFound('not found') stack_outputs = StackOutputs('no-plan', self.hclient) self.assertEqual(list(stack_outputs), []) def test_outputs_valid_key_calls_api(self): expected = 'xyz://keystone' self.hclient.stacks.output_show.return_value = dict(output=dict( output_value=expected)) self.assertEqual(expected, self.outputs['KeystoneURL']) # This should also support the get method self.assertEqual(expected, self.outputs.get('KeystoneURL')) self.assertTrue(self.hclient.called_once_with('overcloud', 'KeystoneURL')) def test_outputs_invalid_key_raises_keyerror(self): self.assertRaises(KeyError, lambda: self.outputs['Invalid']) def test_outputs_get_method_returns_default(self): default = 'default value' self.assertEqual(default, self.outputs.get('Invalid', default)) def test_outputs_iterating_returns_list_of_output_keys(self): self.assertEqual( {'EnabledServices', 'KeystoneURL', 'ServerIdData', 'RoleNetHostnameMap', 'RoleNetIpMap', 'VipMap', 'RoleData'}, set([o for o in self.outputs])) def test_inventory_list(self): self.inventory.undercloud_connection = 'local' self._inventory_list(self.inventory) def _inventory_list(self, inventory): ansible_ssh_user = '******' expected = { 'Compute': { 'hosts': ['cp-0'], 'vars': {'ansible_ssh_user': ansible_ssh_user, 'bootstrap_server_id': 'a', 'tripleo_role_name': 'Compute'}}, 'Controller': { 'hosts': ['c-0', 'c-1', 'c-2'], 'vars': {'ansible_ssh_user': ansible_ssh_user, 'bootstrap_server_id': 'a', 'tripleo_role_name': 'Controller'}}, 'CustomRole': { 'hosts': ['cs-0'], 'vars': {'ansible_ssh_user': ansible_ssh_user, 'bootstrap_server_id': 'a', 'tripleo_role_name': 'CustomRole'}}, 'overcloud': { 'children': ['Compute', 'Controller', 'CustomRole'], 'vars': { 'container_cli': 'podman', 'ctlplane_vip': 'x.x.x.4', 'redis_vip': 'x.x.x.6'}}, 'Undercloud': { 'hosts': ['undercloud'], 'vars': {'ansible_connection': 'local', 'ansible_host': 'localhost', 'ansible_python_interpreter': sys.executable, 'ansible_remote_tmp': '/tmp/ansible-${USER}', 'auth_url': 'xyz://keystone.local', 'cacert': 'acacert', 'os_auth_token': 'atoken', 'overcloud_keystone_url': 'xyz://keystone', 'overcloud_admin_password': '******', 'plan': 'overcloud', 'project_name': 'admin', 'undercloud_service_list': [ 'openstack-nova-compute', 'openstack-heat-engine', 'openstack-ironic-conductor', 'openstack-swift-container', 'openstack-swift-object', 'openstack-mistral-engine'], 'undercloud_swift_url': 'anendpoint', 'username': '******'}}} inv_list = inventory.list() for k in expected: self.assertEqual(expected[k], inv_list[k]) def test_ansible_ssh_user(self): self._try_alternative_args( ansible_ssh_user='******', undercloud_connection='ssh', session=self.session,) def test_no_session(self): self._try_alternative_args( ansible_ssh_user='******', undercloud_connection='ssh', session=None) def _try_alternative_args(self, ansible_ssh_user, session, undercloud_connection): key_file = '/var/lib/mistral/.ssh/%s-key' % ansible_ssh_user self.inventory = TripleoInventory( session=session, hclient=self.hclient, plan_name=self.plan_name, auth_url='xyz://keystone.local', project_name='admin', username='******', cacert='acacert', ansible_ssh_user=ansible_ssh_user, undercloud_connection=undercloud_connection, undercloud_key_file=key_file, ansible_python_interpreter='foo') self.inventory.stack_outputs = self.outputs expected = { 'Compute': { 'hosts': ['cp-0'], 'vars': {'ansible_python_interpreter': 'foo', 'ansible_ssh_user': ansible_ssh_user, 'bootstrap_server_id': 'a', 'tripleo_role_name': 'Compute'}}, 'Controller': { 'hosts': ['c-0', 'c-1', 'c-2'], 'vars': {'ansible_python_interpreter': 'foo', 'ansible_ssh_user': ansible_ssh_user, 'bootstrap_server_id': 'a', 'tripleo_role_name': 'Controller'}}, 'CustomRole': { 'hosts': ['cs-0'], 'vars': {'ansible_python_interpreter': 'foo', 'ansible_ssh_user': ansible_ssh_user, 'bootstrap_server_id': 'a', 'tripleo_role_name': 'CustomRole'}}, 'overcloud': { 'children': ['Compute', 'Controller', 'CustomRole'], 'vars': { 'container_cli': 'podman', 'ctlplane_vip': 'x.x.x.4', 'redis_vip': 'x.x.x.6'}}, 'Undercloud': { 'hosts': ['undercloud'], 'vars': {'ansible_connection': 'ssh', 'ansible_ssh_private_key_file': key_file, 'ansible_ssh_user': '******', 'ansible_host': 'localhost', 'ansible_python_interpreter': 'foo', 'ansible_remote_tmp': '/tmp/ansible-${USER}', 'auth_url': 'xyz://keystone.local', 'cacert': 'acacert', 'os_auth_token': 'atoken' if session else None, 'overcloud_keystone_url': 'xyz://keystone', 'overcloud_admin_password': '******', 'plan': 'overcloud', 'project_name': 'admin', 'undercloud_service_list': [ 'openstack-nova-compute', 'openstack-heat-engine', 'openstack-ironic-conductor', 'openstack-swift-container', 'openstack-swift-object', 'openstack-mistral-engine'], 'undercloud_swift_url': 'anendpoint' if session else None, 'username': '******'}}} inv_list = self.inventory.list() for k in expected: self.assertEqual(expected[k], inv_list[k]) def test_inventory_write_static(self): self.inventory.undercloud_connection = 'local' self._inventory_write_static() def test_inventory_write_static_extra_vars(self): self.inventory.undercloud_connection = 'local' extra_vars = {'Undercloud': {'anextravar': 123}} self._inventory_write_static(extra_vars=extra_vars) def _inventory_write_static(self, extra_vars=None): tmp_dir = self.useFixture(fixtures.TempDir()).path inv_path = os.path.join(tmp_dir, "inventory.yaml") self.inventory.write_static_inventory(inv_path, extra_vars) ansible_ssh_user = '******' expected = { 'Compute': { 'hosts': { 'cp-0': { 'ansible_host': 'y.y.y.1', 'ctlplane_ip': 'y.y.y.1', 'deploy_server_id': 'd', 'enabled_networks': ['ctlplane']}}, 'vars': {'ansible_ssh_user': ansible_ssh_user, 'bootstrap_server_id': 'a', 'tripleo_role_name': 'Compute'}}, 'Controller': { 'hosts': { 'c-0': { 'ansible_host': 'x.x.x.1', 'ctlplane_ip': 'x.x.x.1', 'deploy_server_id': 'a', 'enabled_networks': ['ctlplane']}, 'c-1': { 'ansible_host': 'x.x.x.2', 'ctlplane_ip': 'x.x.x.2', 'deploy_server_id': 'b', 'enabled_networks': ['ctlplane']}, 'c-2': { 'ansible_host': 'x.x.x.3', 'ctlplane_ip': 'x.x.x.3', 'deploy_server_id': 'c', 'enabled_networks': ['ctlplane']}}, 'vars': {'ansible_ssh_user': ansible_ssh_user, 'bootstrap_server_id': 'a', 'tripleo_role_name': 'Controller'}}, 'CustomRole': { 'hosts': { 'cs-0': { 'ansible_host': 'z.z.z.1', 'ctlplane_ip': 'z.z.z.1', 'deploy_server_id': 'e', 'enabled_networks': ['ctlplane']}}, 'vars': {'ansible_ssh_user': ansible_ssh_user, 'bootstrap_server_id': 'a', 'tripleo_role_name': 'CustomRole'}}, 'overcloud': {'children': {'Compute': {}, 'Controller': {}, 'CustomRole': {}}, 'vars': {'container_cli': 'podman', 'ctlplane_vip': 'x.x.x.4', 'redis_vip': 'x.x.x.6'}}, 'sa': {'children': {'Controller': {}}, 'vars': {'ansible_ssh_user': '******'}}, 'sb': {'children': {'Controller': {}}, 'vars': {'ansible_ssh_user': '******'}}, 'sd': {'children': {'Compute': {}}, 'vars': {'ansible_ssh_user': '******'}}, 'se': {'children': {'Compute': {}}, 'vars': {'ansible_ssh_user': '******'}}, 'sg': {'children': {'CustomRole': {}}, 'vars': {'ansible_ssh_user': '******'}}, 'sh': {'children': {'CustomRole': {}}, 'vars': {'ansible_ssh_user': '******'}}, 'Undercloud': {'hosts': {'undercloud': {}}, 'vars': {'ansible_connection': 'local', 'ansible_host': 'localhost', 'ansible_python_interpreter': sys.executable, 'ansible_remote_tmp': '/tmp/ansible-${USER}', 'auth_url': 'xyz://keystone.local', 'cacert': 'acacert', 'os_auth_token': 'atoken', 'overcloud_admin_password': '******', 'overcloud_keystone_url': 'xyz://keystone', 'plan': 'overcloud', 'project_name': 'admin', 'undercloud_service_list': [ 'openstack-nova-compute', 'openstack-heat-engine', 'openstack-ironic-conductor', 'openstack-swift-container', 'openstack-swift-object', 'openstack-mistral-engine'], 'undercloud_swift_url': 'anendpoint', 'username': '******'}}} if extra_vars: expected['Undercloud']['vars']['anextravar'] = 123 with open(inv_path, 'r') as f: loaded_inv = yaml.safe_load(f) self.assertEqual(expected, loaded_inv)
class TestInventory(base.TestCase): def setUp(self): super(TestInventory, self).setUp() self.outputs_data = {'outputs': [ {'output_key': 'EnabledServices', 'output_value': { 'Controller': ['sa', 'sb'], 'Compute': ['sd', 'se', 'ceph_client'], 'CustomRole': ['sg', 'sh']}}, {'output_key': 'KeystoneURL', 'output_value': 'xyz://keystone'}, {'output_key': 'ServerIdData', 'output_value': { 'server_ids': { 'Controller': ['a', 'b', 'c'], 'Compute': ['d'], 'CustomRole': ['e']}, 'bootstrap_server_id': 'a'}}, {'output_key': 'RoleNetHostnameMap', 'output_value': { 'Controller': { 'ctlplane': ['c-0.ctlplane.localdomain', 'c-1.ctlplane.localdomain', 'c-2.ctlplane.localdomain'], 'internal_api': ['c-0.internal_api.localdomain', 'c-1.internal_api.localdomain', 'c-2.internal_api.localdomain']}, 'Compute': { 'ctlplane': ['cp-0.ctlplane.localdomain']}, 'CustomRole': { 'ctlplane': ['cs-0.ctlplane.localdomain']}}}, {'output_key': 'RoleNetIpMap', 'output_value': { 'Controller': { 'ctlplane': ['x.x.x.1', 'x.x.x.2', 'x.x.x.3'], 'internal_api': ['x.x.x.4', 'x.x.x.5', 'x.x.x.6']}, 'Compute': { 'ctlplane': ['y.y.y.1']}, 'CustomRole': { 'ctlplane': ['z.z.z.1']}}}, {'output_key': 'VipMap', 'output_value': { 'ctlplane': 'x.x.x.4', 'redis': 'x.x.x.6'}}, {'output_key': 'RoleData', 'output_value': { 'Controller': {'config_settings': 'foo1'}, 'Compute': {'config_settings': 'foo2'}, 'CustomRole': {'config_settings': 'foo3'}}}]} self.plan_name = 'overcloud' self.hclient = MagicMock() self.hclient.stacks.environment.return_value = { 'parameter_defaults': {'AdminPassword': '******', 'ContainerCli': 'podman'}} self.mock_stack = MagicMock() self.mock_stack.outputs = self.outputs_data['outputs'] self.hclient.stacks.get.return_value = self.mock_stack self.session = MagicMock() self.session.get_token.return_value = 'atoken' self.session.get_endpoint.return_value = 'anendpoint' self.outputs = StackOutputs(self.mock_stack) self.inventory = TripleoInventory( session=self.session, hclient=self.hclient, plan_name=self.plan_name, auth_url='xyz://keystone.local', cacert='acacert', project_name='admin', username='******', ansible_ssh_user='******') self.inventory.stack_outputs = self.outputs def test_get_roles_by_service(self): services = TripleoInventory.get_roles_by_service( MOCK_ENABLED_SERVICES) expected = { 'kernel': ['BlockStorage', 'CephStorage', 'Compute', 'Controller', 'ObjectStorage'], 'swift_storage': ['ObjectStorage'], 'tripleo_packages': ['BlockStorage', 'CephStorage', 'Compute', 'Controller', 'ObjectStorage'], 'keystone': ['Controller'], 'nova_compute': ['Compute'], 'cinder_volume': ['BlockStorage'], 'ceph_client': ['Compute'], } self.assertDictEqual(services, expected) def test_stack_not_found(self): self.hclient.stacks.get.side_effect = HTTPNotFound('not found') self.assertEqual(None, self.inventory._get_stack()) def test_outputs_valid_key_calls_api(self): expected = 'xyz://keystone' self.hclient.stacks.output_show.return_value = dict(output=dict( output_value=expected)) self.assertEqual(expected, self.outputs['KeystoneURL']) # This should also support the get method self.assertEqual(expected, self.outputs.get('KeystoneURL')) self.assertTrue(self.hclient.called_once_with('overcloud', 'KeystoneURL')) def test_no_ips(self): for output in self.outputs_data['outputs']: if output['output_key'] == 'RoleNetIpMap': output['output_value'] = dict(Controller=dict(ctlplane=[])) self.assertRaises(Exception, self.inventory.list) def test_outputs_invalid_key_raises_keyerror(self): self.assertRaises(KeyError, lambda: self.outputs['Invalid']) def test_outputs_get_method_returns_default(self): default = 'default value' self.assertEqual(default, self.outputs.get('Invalid', default)) def test_outputs_iterating_returns_list_of_output_keys(self): self.assertEqual( {'EnabledServices', 'KeystoneURL', 'ServerIdData', 'RoleNetHostnameMap', 'RoleNetIpMap', 'VipMap', 'RoleData'}, set([o for o in self.outputs])) def test_inventory_list(self): self.inventory.undercloud_connection = 'local' self._inventory_list(self.inventory) def _inventory_list(self, inventory): ansible_ssh_user = '******' expected = { 'Compute': { 'hosts': ['cp-0'], 'vars': {'ansible_ssh_user': ansible_ssh_user, 'bootstrap_server_id': 'a', 'serial': 1, 'tripleo_role_name': 'Compute', 'tripleo_role_networks': ['ctlplane']}}, 'Controller': { 'hosts': ['c-0', 'c-1', 'c-2'], 'vars': {'ansible_ssh_user': ansible_ssh_user, 'bootstrap_server_id': 'a', 'serial': 1, 'tripleo_role_name': 'Controller', 'tripleo_role_networks': ['ctlplane', 'internal_api']}}, 'CustomRole': { 'hosts': ['cs-0'], 'vars': {'ansible_ssh_user': ansible_ssh_user, 'bootstrap_server_id': 'a', 'serial': 1, 'tripleo_role_name': 'CustomRole', 'tripleo_role_networks': ['ctlplane']}}, 'overcloud': { 'children': ['Compute', 'Controller', 'CustomRole'], 'vars': { 'container_cli': 'podman', 'ctlplane_vip': 'x.x.x.4', 'redis_vip': 'x.x.x.6'}}, 'Undercloud': { 'hosts': ['undercloud'], 'vars': {'ansible_connection': 'local', 'ansible_host': 'localhost', 'ansible_python_interpreter': sys.executable, 'ansible_remote_tmp': '/tmp/ansible-${USER}', 'auth_url': 'xyz://keystone.local', 'cacert': 'acacert', 'os_auth_token': 'atoken', 'overcloud_keystone_url': 'xyz://keystone', 'overcloud_admin_password': '******', 'plan': 'overcloud', 'project_name': 'admin', 'undercloud_service_list': [ 'tripleo_nova_compute', 'tripleo_heat_engine', 'tripleo_ironic_conductor', 'tripleo_swift_container_server', 'tripleo_swift_object_server', 'tripleo_mistral_engine'], 'undercloud_swift_url': 'anendpoint', 'username': '******'}}} inv_list = inventory.list() for k in expected: self.assertEqual(expected[k], inv_list[k]) def test_ansible_ssh_user(self): self._try_alternative_args( ansible_ssh_user='******', undercloud_connection='ssh', session=self.session,) def test_no_session(self): self._try_alternative_args( ansible_ssh_user='******', undercloud_connection='ssh', session=None) def _try_alternative_args(self, ansible_ssh_user, session, undercloud_connection): key_file = '/var/lib/mistral/.ssh/%s-key' % ansible_ssh_user self.inventory = TripleoInventory( session=session, hclient=self.hclient, plan_name=self.plan_name, auth_url='xyz://keystone.local', project_name='admin', username='******', cacert='acacert', ansible_ssh_user=ansible_ssh_user, undercloud_connection=undercloud_connection, undercloud_key_file=key_file, ansible_python_interpreter='foo') self.inventory.stack_outputs = self.outputs expected = { 'Compute': { 'hosts': ['cp-0'], 'vars': {'ansible_python_interpreter': 'foo', 'ansible_ssh_user': ansible_ssh_user, 'bootstrap_server_id': 'a', 'serial': 1, 'tripleo_role_name': 'Compute', 'tripleo_role_networks': ['ctlplane']}}, 'Controller': { 'hosts': ['c-0', 'c-1', 'c-2'], 'vars': {'ansible_python_interpreter': 'foo', 'ansible_ssh_user': ansible_ssh_user, 'bootstrap_server_id': 'a', 'serial': 1, 'tripleo_role_name': 'Controller', 'tripleo_role_networks': ['ctlplane', 'internal_api']}}, 'CustomRole': { 'hosts': ['cs-0'], 'vars': {'ansible_python_interpreter': 'foo', 'ansible_ssh_user': ansible_ssh_user, 'bootstrap_server_id': 'a', 'serial': 1, 'tripleo_role_name': 'CustomRole', 'tripleo_role_networks': ['ctlplane']}}, 'overcloud': { 'children': ['Compute', 'Controller', 'CustomRole'], 'vars': { 'container_cli': 'podman', 'ctlplane_vip': 'x.x.x.4', 'redis_vip': 'x.x.x.6'}}, 'sa': {'children': ['Controller'], 'vars': {'ansible_ssh_user': '******', 'ansible_python_interpreter': 'foo'}}, 'sb': {'children': ['Controller'], 'vars': {'ansible_ssh_user': '******', 'ansible_python_interpreter': 'foo'}}, 'sd': {'children': ['Compute'], 'vars': {'ansible_ssh_user': '******', 'ansible_python_interpreter': 'foo'}}, 'se': {'children': ['Compute'], 'vars': {'ansible_ssh_user': '******', 'ansible_python_interpreter': 'foo'}}, 'ceph_client': {'children': ['Compute'], 'vars': {'ansible_ssh_user': '******', 'ansible_python_interpreter': 'foo'}}, 'clients': {'children': ['Compute'], 'vars': {'ansible_ssh_user': '******', 'ansible_python_interpreter': 'foo'}}, 'sg': {'children': ['CustomRole'], 'vars': {'ansible_ssh_user': '******', 'ansible_python_interpreter': 'foo'}}, 'sh': {'children': ['CustomRole'], 'vars': {'ansible_ssh_user': '******', 'ansible_python_interpreter': 'foo'}}, 'Undercloud': { 'hosts': ['undercloud'], 'vars': {'ansible_connection': 'ssh', 'ansible_ssh_private_key_file': key_file, 'ansible_ssh_user': '******', 'ansible_host': 'localhost', 'ansible_python_interpreter': 'foo', 'ansible_remote_tmp': '/tmp/ansible-${USER}', 'auth_url': 'xyz://keystone.local', 'cacert': 'acacert', 'os_auth_token': 'atoken' if session else None, 'overcloud_keystone_url': 'xyz://keystone', 'overcloud_admin_password': '******', 'plan': 'overcloud', 'project_name': 'admin', 'undercloud_service_list': [ 'tripleo_nova_compute', 'tripleo_heat_engine', 'tripleo_ironic_conductor', 'tripleo_swift_container_server', 'tripleo_swift_object_server', 'tripleo_mistral_engine'], 'undercloud_swift_url': 'anendpoint' if session else None, 'username': '******'}}} inv_list = self.inventory.list() for k in expected: self.assertEqual(expected[k], inv_list[k]) def test_inventory_write_static(self): self.inventory.undercloud_connection = 'local' self._inventory_write_static() def test_inventory_write_static_extra_vars(self): self.inventory.undercloud_connection = 'local' extra_vars = {'Undercloud': {'anextravar': 123}} self._inventory_write_static(extra_vars=extra_vars) def _inventory_write_static(self, extra_vars=None): tmp_dir = self.useFixture(fixtures.TempDir()).path inv_path = os.path.join(tmp_dir, "inventory.yaml") self.inventory.write_static_inventory(inv_path, extra_vars) ansible_ssh_user = '******' expected = { 'Compute': { 'hosts': { 'cp-0': { 'ansible_host': 'y.y.y.1', 'ctlplane_ip': 'y.y.y.1', 'deploy_server_id': 'd', 'ctlplane_hostname': 'cp-0.ctlplane.localdomain'}}, 'vars': {'ansible_ssh_user': ansible_ssh_user, 'bootstrap_server_id': 'a', 'serial': 1, 'tripleo_role_name': 'Compute', 'tripleo_role_networks': ['ctlplane']}}, 'Controller': { 'hosts': { 'c-0': { 'ansible_host': 'x.x.x.1', 'ctlplane_ip': 'x.x.x.1', 'deploy_server_id': 'a', 'ctlplane_hostname': 'c-0.ctlplane.localdomain', 'internal_api_hostname': 'c-0.internal_api.localdomain', 'internal_api_ip': 'x.x.x.4'}, 'c-1': { 'ansible_host': 'x.x.x.2', 'ctlplane_ip': 'x.x.x.2', 'deploy_server_id': 'b', 'ctlplane_hostname': 'c-1.ctlplane.localdomain', 'internal_api_hostname': 'c-1.internal_api.localdomain', 'internal_api_ip': 'x.x.x.5'}, 'c-2': { 'ansible_host': 'x.x.x.3', 'ctlplane_ip': 'x.x.x.3', 'deploy_server_id': 'c', 'ctlplane_hostname': 'c-2.ctlplane.localdomain', 'internal_api_hostname': 'c-2.internal_api.localdomain', 'internal_api_ip': 'x.x.x.6'}}, 'vars': {'ansible_ssh_user': ansible_ssh_user, 'bootstrap_server_id': 'a', 'serial': 1, 'tripleo_role_name': 'Controller', 'tripleo_role_networks': ['ctlplane', 'internal_api']}}, 'CustomRole': { 'hosts': { 'cs-0': { 'ansible_host': 'z.z.z.1', 'ctlplane_ip': 'z.z.z.1', 'deploy_server_id': 'e', 'ctlplane_hostname': 'cs-0.ctlplane.localdomain'}}, 'vars': {'ansible_ssh_user': ansible_ssh_user, 'bootstrap_server_id': 'a', 'serial': 1, 'tripleo_role_name': 'CustomRole', 'tripleo_role_networks': ['ctlplane']}}, 'overcloud': {'children': {'Compute': {}, 'Controller': {}, 'CustomRole': {}}, 'vars': {'container_cli': 'podman', 'ctlplane_vip': 'x.x.x.4', 'redis_vip': 'x.x.x.6'}}, 'sa': {'children': {'Controller': {}}, 'vars': {'ansible_ssh_user': '******'}}, 'sb': {'children': {'Controller': {}}, 'vars': {'ansible_ssh_user': '******'}}, 'sd': {'children': {'Compute': {}}, 'vars': {'ansible_ssh_user': '******'}}, 'se': {'children': {'Compute': {}}, 'vars': {'ansible_ssh_user': '******'}}, 'ceph_client': {'children': {'Compute': {}}, 'vars': {'ansible_ssh_user': '******'}}, 'clients': {'children': {'Compute': {}}, 'vars': {'ansible_ssh_user': '******'}}, 'sg': {'children': {'CustomRole': {}}, 'vars': {'ansible_ssh_user': '******'}}, 'sh': {'children': {'CustomRole': {}}, 'vars': {'ansible_ssh_user': '******'}}, 'Undercloud': {'hosts': {'undercloud': {}}, 'vars': {'ansible_connection': 'local', 'ansible_host': 'localhost', 'ansible_python_interpreter': sys.executable, 'ansible_remote_tmp': '/tmp/ansible-${USER}', 'auth_url': 'xyz://keystone.local', 'cacert': 'acacert', 'os_auth_token': 'atoken', 'overcloud_admin_password': '******', 'overcloud_keystone_url': 'xyz://keystone', 'plan': 'overcloud', 'project_name': 'admin', 'undercloud_service_list': [ 'tripleo_nova_compute', 'tripleo_heat_engine', 'tripleo_ironic_conductor', 'tripleo_swift_container_server', 'tripleo_swift_object_server', 'tripleo_mistral_engine'], 'undercloud_swift_url': 'anendpoint', 'username': '******'}}} if extra_vars: expected['Undercloud']['vars']['anextravar'] = 123 with open(inv_path, 'r') as f: loaded_inv = yaml.safe_load(f) self.assertEqual(expected, loaded_inv)
class TestInventory(base.TestCase): def setUp(self): super(TestInventory, self).setUp() self.outputs_data = { 'outputs': [{ 'output_key': 'EnabledServices', 'output_value': { 'Controller': ['sa', 'sb'], 'Compute': ['sd', 'se'], 'CustomRole': ['sg', 'sh'] } }, { 'output_key': 'KeystoneURL', 'output_value': 'xyz://keystone' }, { 'output_key': 'ServerIdData', 'output_value': { 'server_ids': { 'Controller': ['a', 'b', 'c'], 'Compute': ['d'], 'CustomRole': ['e'] }, 'bootstrap_server_id': 'a' } }, { 'output_key': 'RoleNetHostnameMap', 'output_value': { 'Controller': { 'ctlplane': [ 'c-0.ctlplane.localdomain', 'c-1.ctlplane.localdomain', 'c-2.ctlplane.localdomain' ] }, 'Compute': { 'ctlplane': ['cp-0.ctlplane.localdomain'] }, 'CustomRole': { 'ctlplane': ['cs-0.ctlplane.localdomain'] } } }, { 'output_key': 'RoleNetIpMap', 'output_value': { 'Controller': { 'ctlplane': ['x.x.x.1', 'x.x.x.2', 'x.x.x.3'] }, 'Compute': { 'ctlplane': ['y.y.y.1'] }, 'CustomRole': { 'ctlplane': ['z.z.z.1'] } } }, { 'output_key': 'VipMap', 'output_value': { 'ctlplane': 'x.x.x.4', 'redis': 'x.x.x.6' } }, { 'output_key': 'RoleData', 'output_value': { 'Controller': { 'config_settings': 'foo1' }, 'Compute': { 'config_settings': 'foo2' }, 'CustomRole': { 'config_settings': 'foo3' } } }] } self.plan_name = 'overcloud' self.hclient = MagicMock() self.hclient.stacks.environment.return_value = { 'parameter_defaults': { 'AdminPassword': '******' } } self.mock_stack = MagicMock() self.mock_stack.outputs = self.outputs_data['outputs'] self.hclient.stacks.get.return_value = self.mock_stack self.session = MagicMock() self.session.get_token.return_value = 'atoken' self.session.get_endpoint.return_value = 'anendpoint' self.outputs = StackOutputs('overcloud', self.hclient) self.inventory = TripleoInventory(session=self.session, hclient=self.hclient, plan_name=self.plan_name, auth_url='xyz://keystone.local', cacert='acacert', project_name='admin', username='******', ansible_ssh_user='******') self.inventory.stack_outputs = self.outputs def test_get_roles_by_service(self): services = TripleoInventory.get_roles_by_service(MOCK_ENABLED_SERVICES) expected = { 'kernel': [ 'BlockStorage', 'CephStorage', 'Compute', 'Controller', 'ObjectStorage' ], 'swift_storage': ['ObjectStorage'], 'tripleo_packages': [ 'BlockStorage', 'CephStorage', 'Compute', 'Controller', 'ObjectStorage' ], 'keystone': ['Controller'], 'nova_compute': ['Compute'], 'cinder_volume': ['BlockStorage'], } self.assertDictEqual(services, expected) def test_outputs_are_empty_if_stack_doesnt_exist(self): self.hclient.stacks.get.side_effect = HTTPNotFound('not found') stack_outputs = StackOutputs('no-plan', self.hclient) self.assertEqual(list(stack_outputs), []) def test_outputs_valid_key_calls_api(self): expected = 'xyz://keystone' self.hclient.stacks.output_show.return_value = dict(output=dict( output_value=expected)) self.assertEqual(expected, self.outputs['KeystoneURL']) # This should also support the get method self.assertEqual(expected, self.outputs.get('KeystoneURL')) self.assertTrue( self.hclient.called_once_with('overcloud', 'KeystoneURL')) def test_outputs_invalid_key_raises_keyerror(self): self.assertRaises(KeyError, lambda: self.outputs['Invalid']) def test_outputs_get_method_returns_default(self): default = 'default value' self.assertEqual(default, self.outputs.get('Invalid', default)) def test_outputs_iterating_returns_list_of_output_keys(self): self.assertEqual( { 'EnabledServices', 'KeystoneURL', 'ServerIdData', 'RoleNetHostnameMap', 'RoleNetIpMap', 'VipMap', 'RoleData' }, set([o for o in self.outputs])) def test_inventory_list(self): self._inventory_list(self.inventory) def test_inventory_list_backwards_compat_configs(self): # FIXME(shardy) backwards compatibility until we switch # tripleo-validations to pass the individual values configs = MagicMock() configs.plan = self.plan_name configs.auth_url = 'xyz://keystone.local' configs.cacert = 'acacert' configs.project_name = 'admin' configs.username = '******' configs.ansible_ssh_user = '******' inventory = TripleoInventory(configs, self.session, self.hclient) self._inventory_list(inventory) def _inventory_list(self, inventory): expected = { 'c-0': { 'hosts': ['x.x.x.1'], 'vars': { 'deploy_server_id': 'a', 'ctlplane_ip': 'x.x.x.1', 'enabled_networks': ['ctlplane'] } }, 'c-1': { 'hosts': ['x.x.x.2'], 'vars': { 'deploy_server_id': 'b', 'ctlplane_ip': 'x.x.x.2', 'enabled_networks': ['ctlplane'] } }, 'c-2': { 'hosts': ['x.x.x.3'], 'vars': { 'deploy_server_id': 'c', 'ctlplane_ip': 'x.x.x.3', 'enabled_networks': ['ctlplane'] } }, 'Compute': { 'children': ['cp-0'], 'vars': { 'ansible_ssh_user': '******', 'bootstrap_server_id': 'a', 'role_data_config_settings': 'foo2', 'role_name': 'Compute' } }, 'Controller': { 'children': ['c-0', 'c-1', 'c-2'], 'vars': { 'ansible_ssh_user': '******', 'bootstrap_server_id': 'a', 'role_data_config_settings': 'foo1', 'role_name': 'Controller' } }, 'cp-0': { 'hosts': ['y.y.y.1'], 'vars': { 'deploy_server_id': 'd', 'ctlplane_ip': 'y.y.y.1', 'enabled_networks': ['ctlplane'] } }, 'cs-0': { 'hosts': ['z.z.z.1'], 'vars': { 'deploy_server_id': 'e', 'ctlplane_ip': 'z.z.z.1', 'enabled_networks': ['ctlplane'] } }, 'CustomRole': { 'children': ['cs-0'], 'vars': { 'ansible_ssh_user': '******', 'bootstrap_server_id': 'a', 'role_data_config_settings': 'foo3', 'role_name': 'CustomRole' } }, 'overcloud': { 'children': ['Compute', 'Controller', 'CustomRole'], 'vars': { 'ctlplane_vip': 'x.x.x.4', 'redis_vip': 'x.x.x.6' } }, 'undercloud': { 'hosts': ['localhost'], 'vars': { 'ansible_connection': 'local', 'auth_url': 'xyz://keystone.local', 'cacert': 'acacert', 'os_auth_token': 'atoken', 'overcloud_keystone_url': 'xyz://keystone', 'overcloud_admin_password': '******', 'plan': 'overcloud', 'project_name': 'admin', 'undercloud_service_list': [ 'openstack-nova-compute', 'openstack-heat-engine', 'openstack-ironic-conductor', 'openstack-swift-container', 'openstack-swift-object', 'openstack-mistral-engine' ], 'undercloud_swift_url': 'anendpoint', 'username': '******' } } } inv_list = inventory.list() for k in expected: self.assertEqual(expected[k], inv_list[k]) def test_ansible_ssh_user(self): self._try_alternative_args( ansible_ssh_user='******', session=self.session, ) def test_no_session(self): self._try_alternative_args(ansible_ssh_user='******', session=None) def _try_alternative_args(self, ansible_ssh_user, session): self.inventory = TripleoInventory(session=session, hclient=self.hclient, plan_name=self.plan_name, auth_url='xyz://keystone.local', project_name='admin', username='******', cacert='acacert', ansible_ssh_user=ansible_ssh_user) self.inventory.stack_outputs = self.outputs expected = { 'c-0': { 'hosts': ['x.x.x.1'], 'vars': { 'deploy_server_id': 'a', 'ctlplane_ip': 'x.x.x.1', 'enabled_networks': ['ctlplane'] } }, 'c-1': { 'hosts': ['x.x.x.2'], 'vars': { 'deploy_server_id': 'b', 'ctlplane_ip': 'x.x.x.2', 'enabled_networks': ['ctlplane'] } }, 'c-2': { 'hosts': ['x.x.x.3'], 'vars': { 'deploy_server_id': 'c', 'ctlplane_ip': 'x.x.x.3', 'enabled_networks': ['ctlplane'] } }, 'Compute': { 'children': ['cp-0'], 'vars': { 'ansible_ssh_user': ansible_ssh_user, 'bootstrap_server_id': 'a', 'role_data_config_settings': 'foo2', 'role_name': 'Compute' } }, 'Controller': { 'children': ['c-0', 'c-1', 'c-2'], 'vars': { 'ansible_ssh_user': ansible_ssh_user, 'bootstrap_server_id': 'a', 'role_data_config_settings': 'foo1', 'role_name': 'Controller' } }, 'cp-0': { 'hosts': ['y.y.y.1'], 'vars': { 'deploy_server_id': 'd', 'ctlplane_ip': 'y.y.y.1', 'enabled_networks': ['ctlplane'] } }, 'cs-0': { 'hosts': ['z.z.z.1'], 'vars': { 'deploy_server_id': 'e', 'ctlplane_ip': 'z.z.z.1', 'enabled_networks': ['ctlplane'] } }, 'CustomRole': { 'children': ['cs-0'], 'vars': { 'ansible_ssh_user': ansible_ssh_user, 'bootstrap_server_id': 'a', 'role_data_config_settings': 'foo3', 'role_name': 'CustomRole' } }, 'overcloud': { 'children': ['Compute', 'Controller', 'CustomRole'], 'vars': { 'ctlplane_vip': 'x.x.x.4', 'redis_vip': 'x.x.x.6' } }, 'undercloud': { 'hosts': ['localhost'], 'vars': { 'ansible_connection': 'local', 'auth_url': 'xyz://keystone.local', 'cacert': 'acacert', 'os_auth_token': 'atoken' if session else None, 'overcloud_keystone_url': 'xyz://keystone', 'overcloud_admin_password': '******', 'plan': 'overcloud', 'project_name': 'admin', 'undercloud_service_list': [ 'openstack-nova-compute', 'openstack-heat-engine', 'openstack-ironic-conductor', 'openstack-swift-container', 'openstack-swift-object', 'openstack-mistral-engine' ], 'undercloud_swift_url': 'anendpoint' if session else None, 'username': '******' } } } inv_list = self.inventory.list() for k in expected: self.assertEqual(expected[k], inv_list[k]) def test_inventory_write_static(self): self._inventory_write_static() def test_inventory_write_static_extra_vars(self): extra_vars = {'undercloud': {'anextravar': 123}} self._inventory_write_static(extra_vars=extra_vars) def _inventory_write_static(self, extra_vars=None): tmp_dir = self.useFixture(fixtures.TempDir()).path inv_path = os.path.join(tmp_dir, "inventory.yaml") self.inventory.write_static_inventory(inv_path, extra_vars) expected = { 'Compute': { 'children': { 'cp-0': {} }, 'vars': { 'ansible_ssh_user': '******', 'bootstrap_server_id': 'a', 'role_data_config_settings': 'foo2', 'role_name': 'Compute' } }, 'Controller': { 'children': { 'c-0': {}, 'c-1': {}, 'c-2': {} }, 'vars': { 'ansible_ssh_user': '******', 'bootstrap_server_id': 'a', 'role_data_config_settings': 'foo1', 'role_name': 'Controller' } }, 'CustomRole': { 'children': { 'cs-0': {} }, 'vars': { 'ansible_ssh_user': '******', 'bootstrap_server_id': 'a', 'role_data_config_settings': 'foo3', 'role_name': 'CustomRole' } }, '_meta': { 'hostvars': {} }, 'c-0': { 'hosts': { 'x.x.x.1': {} }, 'vars': { 'ctlplane_ip': 'x.x.x.1', 'deploy_server_id': 'a', 'enabled_networks': ['ctlplane'] } }, 'c-1': { 'hosts': { 'x.x.x.2': {} }, 'vars': { 'ctlplane_ip': 'x.x.x.2', 'deploy_server_id': 'b', 'enabled_networks': ['ctlplane'] } }, 'c-2': { 'hosts': { 'x.x.x.3': {} }, 'vars': { 'ctlplane_ip': 'x.x.x.3', 'deploy_server_id': 'c', 'enabled_networks': ['ctlplane'] } }, 'cp-0': { 'hosts': { 'y.y.y.1': {} }, 'vars': { 'ctlplane_ip': 'y.y.y.1', 'deploy_server_id': 'd', 'enabled_networks': ['ctlplane'] } }, 'cs-0': { 'hosts': { 'z.z.z.1': {} }, 'vars': { 'ctlplane_ip': 'z.z.z.1', 'deploy_server_id': 'e', 'enabled_networks': ['ctlplane'] } }, 'overcloud': { 'children': { 'Compute': {}, 'Controller': {}, 'CustomRole': {} }, 'vars': { 'ctlplane_vip': 'x.x.x.4', 'redis_vip': 'x.x.x.6' } }, 'sa': { 'children': { 'Controller': {} }, 'vars': { 'ansible_ssh_user': '******' } }, 'sb': { 'children': { 'Controller': {} }, 'vars': { 'ansible_ssh_user': '******' } }, 'sd': { 'children': { 'Compute': {} }, 'vars': { 'ansible_ssh_user': '******' } }, 'se': { 'children': { 'Compute': {} }, 'vars': { 'ansible_ssh_user': '******' } }, 'sg': { 'children': { 'CustomRole': {} }, 'vars': { 'ansible_ssh_user': '******' } }, 'sh': { 'children': { 'CustomRole': {} }, 'vars': { 'ansible_ssh_user': '******' } }, 'undercloud': { 'hosts': { 'localhost': {} }, 'vars': { 'ansible_connection': 'local', 'auth_url': 'xyz://keystone.local', 'cacert': 'acacert', 'os_auth_token': 'atoken', 'overcloud_admin_password': '******', 'overcloud_keystone_url': 'xyz://keystone', 'plan': 'overcloud', 'project_name': 'admin', 'undercloud_service_list': [ 'openstack-nova-compute', 'openstack-heat-engine', 'openstack-ironic-conductor', 'openstack-swift-container', 'openstack-swift-object', 'openstack-mistral-engine' ], 'undercloud_swift_url': 'anendpoint', 'username': '******' } } } if extra_vars: expected['undercloud']['vars']['anextravar'] = 123 with open(inv_path, 'r') as f: loaded_inv = yaml.safe_load(f) self.assertEqual(expected, loaded_inv)