# Licensed under the Apache License, Version 2.0 (the "License"); you may # not use this file except in compliance with the License. You may obtain # a copy of the License at # # http://www.apache.org/licenses/LICENSE-2.0 # # Unless required by applicable law or agreed to in writing, software # distributed under the License is distributed on an "AS IS" BASIS, WITHOUT # WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the # License for the specific language governing permissions and limitations # under the License. import datetime import fixtures import os from unittest import mock from unittest.mock import patch from unittest.mock import call import uuid import warnings import yaml from tripleo_common import constants from tripleo_common.tests import base from tripleo_common.tests.fake_config import fakes from tripleo_common.utils import config as ooo_config from tripleo_common.utils.safe_import import git RESOURCES_YAML_CONTENTS = """heat_template_version: 2016-04-08 resources: Controller: type: OS::Heat::ResourceGroup NotRoleContoller: type: OS::Dummy::DummyGroup """ class TestConfig(base.TestCase): def setUp(self): super(TestConfig, self).setUp() @patch.object(ooo_config.Config, 'initialize_git_repo') @patch.object(ooo_config.shutil, 'copyfile') @patch.object(ooo_config.Config, '_mkdir') @patch.object(ooo_config.Config, '_open_file') @patch.object(ooo_config.shutil, 'rmtree') def test_overcloud_config_generate_config(self, mock_rmtree, mock_open, mock_mkdir, mock_copyfile, mock_git_init): config_type_list = ['config_settings', 'global_config_settings', 'logging_sources', 'monitoring_subscriptions', 'service_config_settings', 'service_metadata_settings', 'service_names', 'upgrade_batch_tasks', 'upgrade_tasks', 'external_deploy_steps_tasks'] heat = mock.MagicMock() heat.stacks.get.return_value = fakes.create_tht_stack() self.config = ooo_config.Config(heat) self.config.fetch_config('overcloud') fake_role = list(self.config.stack_outputs.get('RoleData')) self.config.download_config('overcloud', '/tmp/tht', config_type_list) mock_git_init.assert_called_once_with('/tmp/tht') expected_mkdir_calls = [call('/tmp/tht/%s' % r) for r in fake_role] mock_mkdir.assert_has_calls(expected_mkdir_calls, any_order=True) mock_mkdir.assert_called() expected_calls = [] for config in config_type_list: if 'external' in config: for step in range(constants.DEFAULT_STEPS_MAX): expected_calls += [call('/tmp/tht/%s_step%s.yaml' % (config, step))] for role in fake_role: if 'external' in config: continue elif config == 'step_config': expected_calls += [call('/tmp/tht/%s/%s.pp' % (role, config))] elif config == 'param_config': expected_calls += [call('/tmp/tht/%s/%s.json' % (role, config))] else: expected_calls += [call('/tmp/tht/%s/%s.yaml' % (role, config))] mock_open.assert_has_calls(expected_calls, any_order=True) @patch.object(ooo_config.Config, 'initialize_git_repo') @patch.object(ooo_config.shutil, 'copyfile') @patch.object(ooo_config.Config, '_mkdir') @patch.object(ooo_config.Config, '_open_file') @patch.object(ooo_config.shutil, 'rmtree') def test_overcloud_config_one_config_type(self, mock_rmtree, mock_open, mock_mkdir, mock_copyfile, mock_git_init): expected_config_type = 'config_settings' heat = mock.MagicMock() heat.stacks.get.return_value = fakes.create_tht_stack() self.config = ooo_config.Config(heat) self.config.fetch_config('overcloud') fake_role = list(self.config.stack_outputs.get('RoleData')) self.config.download_config('overcloud', '/tmp/tht', ['config_settings']) expected_mkdir_calls = [call('/tmp/tht/%s' % r) for r in fake_role] expected_calls = [call('/tmp/tht/%s/%s.yaml' % (r, expected_config_type)) for r in fake_role] mock_mkdir.assert_has_calls(expected_mkdir_calls, any_order=True) mock_mkdir.assert_called() mock_open.assert_has_calls(expected_calls, any_order=True) mock_git_init.assert_called_once_with('/tmp/tht') @patch.object(ooo_config.git, 'Repo') @mock.patch('os.mkdir') @mock.patch('six.moves.builtins.open') @patch.object(ooo_config.shutil, 'rmtree') def test_overcloud_config_wrong_config_type(self, mock_rmtree, mock_open, mock_mkdir, mock_repo): args = {'name': 'overcloud', 'config_dir': '/tmp/tht', 'config_type': ['bad_config']} heat = mock.MagicMock() heat.stacks.get.return_value = fakes.create_tht_stack() self.config = ooo_config.Config(heat) self.assertRaises( KeyError, self.config.download_config, *args) def test_overcloud_config_upgrade_tasks(self): heat = mock.MagicMock() heat.stacks.get.return_value = fakes.create_tht_stack() self.config = ooo_config.Config(heat) self.config.fetch_config('overcloud') self.tmp_dir = self.useFixture(fixtures.TempDir()).path fake_role = list(self.config.stack_outputs.get('RoleData')) expected_tasks = {'FakeController': {0: [], 1: [{'name': 'Stop fake service', 'service': 'name=fake ' 'state=stopped', 'when': 'step|int == 1'}], 2: [], 3: [], 4: [], 5: []}, 'FakeCompute': {0: [], 1: [{'name': 'Stop fake service', 'service': 'name=fake ' 'state=stopped', 'when': ['nova_api_enabled.rc' ' == 0', False, 'httpd_enabled.rc' ' != 0', 'step|int == 1']}], 2: [{'name': 'Stop nova-compute ' 'service', 'service': 'name=openstack-' 'nova-compute state=stopped', 'when': ['nova_compute_' 'enabled.rc == 0', 'step|int == 2', 'existing', 'list']}], 3: [], 4: [], 5: []}} for role in fake_role: filedir = os.path.join(self.tmp_dir, role) os.makedirs(filedir) for step in range(constants.DEFAULT_STEPS_MAX): filepath = os.path.join(filedir, "upgrade_tasks_step%s.yaml" % step) playbook_tasks = self.config._write_tasks_per_step( self.config.stack_outputs.get('RoleData')[role] ['upgrade_tasks'], filepath, step) self.assertTrue(os.path.isfile(filepath)) self.assertEqual(expected_tasks[role][step], playbook_tasks) def test_get_server_names(self): heat = mock.MagicMock() self.config = ooo_config.Config(heat) self.config.stack_outputs = { 'RoleNetHostnameMap': { 'Controller': { 'ctlplane': [ 'c0.ctlplane.localdomain', 'c1.ctlplane.localdomain', 'c2.ctlplane.localdomain']}}, 'ServerIdData': { 'server_ids': { 'Controller': [ '8269f736', '2af0a373', 'c8479674']}}} server_names = self.config.get_server_names() expected = {'2af0a373': 'c1', '8269f736': 'c0', 'c8479674': 'c2'} self.assertEqual(expected, server_names) def test_get_role_config(self): heat = mock.MagicMock() self.config = ooo_config.Config(heat) self.config.stack_outputs = {'RoleConfig': None} role_config = self.config.get_role_config() self.assertEqual({}, role_config) def test_get_deployment_data(self): heat = mock.MagicMock() self.config = ooo_config.Config(heat) stack = 'overcloud' first = mock.MagicMock() first.creation_time = datetime.datetime.now() - datetime.timedelta(2) second = mock.MagicMock() second.creation_time = datetime.datetime.now() - datetime.timedelta(1) third = mock.MagicMock() third.creation_time = datetime.datetime.now() # Set return_value in a nonsorted order, as we expect the function to # sort, so that's what we want to test heat.resources.list.return_value = [second, third, first] deployment_data = self.config.get_deployment_data(stack) self.assertTrue(heat.resources.list.called) self.assertEqual( heat.resources.list.call_args, mock.call(stack, filters=dict(name=constants.TRIPLEO_DEPLOYMENT_RESOURCE), nested_depth=constants.NESTED_DEPTH, with_detail=True)) self.assertEqual(deployment_data, [first, second, third]) def _get_config_data(self, datafile): config_data_path = os.path.join( os.path.dirname(os.path.realpath(__file__)), 'data', datafile) with open(config_data_path) as fin: config_data = yaml.safe_load(fin.read()) deployment_data = [] for deployment in config_data['deployments']: deployment_mock = mock.MagicMock() deployment_mock.id = deployment['deployment'] deployment_mock.attributes = dict( value=dict(server=deployment['server'], deployment=deployment['deployment'], config=deployment['config'], name=deployment['name'])) deployment_data.append(deployment_mock) configs = config_data['configs'] return deployment_data, configs def _get_deployment_id(self, deployment): return deployment.attributes['value']['deployment'] def _get_config_dict(self, deployment_id): deployment = list(filter( lambda d: d.id == deployment_id, self.deployments))[0] config = self.configs[deployment.attributes['value']['config']].copy() config['inputs'] = [] config['inputs'].append(dict( name='deploy_server_id', value=deployment.attributes['value']['server'])) return config def _get_yaml_file(self, file_name): file_path = os.path.join( os.path.dirname(os.path.realpath(__file__)), 'data', file_name) with open(file_path) as fin: return yaml.safe_load(fin.read()) @patch.object(ooo_config.Config, 'initialize_git_repo') @patch('tripleo_common.utils.config.Config.get_deployment_resource_id') @patch('tripleo_common.utils.config.Config.get_config_dict') @patch('tripleo_common.utils.config.Config.get_deployment_data') def test_config_download(self, mock_deployment_data, mock_config_dict, mock_deployment_resource_id, mock_git_init): heat = mock.MagicMock() self.config = ooo_config.Config(heat) stack = mock.MagicMock() heat.stacks.get.return_value = stack stack.outputs = [ {'output_key': 'RoleNetHostnameMap', 'output_value': { 'Controller': { 'ctlplane': [ 'overcloud-controller-0.ctlplane.localdomain']}, 'Compute': { 'ctlplane': [ 'overcloud-novacompute-0.ctlplane.localdomain', 'overcloud-novacompute-1.ctlplane.localdomain', 'overcloud-novacompute-2.ctlplane.localdomain']}}}, {'output_key': 'ServerIdData', 'output_value': { 'server_ids': { 'Controller': [ '00b3a5e1-5e8e-4b55-878b-2fa2271f15ad'], 'Compute': [ 'a7db3010-a51f-4ae0-a791-2364d629d20d', '8b07cd31-3083-4b88-a433-955f72039e2c', '169b46f8-1965-4d90-a7de-f36fb4a830fe']}}}, {'output_key': 'HostnameNetworkConfigMap', 'output_value': {}}, {'output_key': 'AnsibleHostVarsMap', 'output_value': { 'Controller': { 'overcloud-controller-0': { 'uuid': 0, 'my_var': 'foo'}}, 'Compute': { 'overcloud-novacompute-0': { 'uuid': 1}, 'overcloud-novacompute-1': { 'uuid': 2}, 'overcloud-novacompute-2': { 'uuid': 3}}}}, {'output_key': 'RoleData', 'output_value': { 'Controller': { 'ansible_group_vars': { 'chrony_host': 'localhost', 'chrony_acl': 'none', }}}}, {'output_key': 'RoleGroupVars', 'output_value': { 'Controller': { 'any_errors_fatal': True, 'chrony_host': '192.168.2.1', 'chrony_foo': 'bar', 'max_fail_percentage': 15}, 'Compute': { 'any_errors_fatal': True, 'max_fail_percentage': 15}, }}] deployment_data, configs = \ self._get_config_data('config_data.yaml') self.configs = configs self.deployments = deployment_data mock_deployment_data.return_value = deployment_data mock_deployment_resource_id.side_effect = self._get_deployment_id mock_config_dict.side_effect = self._get_config_dict self.tmp_dir = self.useFixture(fixtures.TempDir()).path tmp_path = self.config.download_config(stack, self.tmp_dir) mock_git_init.assert_called_once_with(self.tmp_dir) for f in ['Controller', 'Compute', ]: with open(os.path.join(tmp_path, 'group_vars', f)) as fin: self.assertEqual( self._get_yaml_file(f), yaml.safe_load(fin.read())) for f in ['overcloud-controller-0', 'overcloud-novacompute-0', 'overcloud-novacompute-1', 'overcloud-novacompute-2']: with open(os.path.join(tmp_path, 'host_vars', f)) as fin: self.assertEqual( self._get_yaml_file(os.path.join('host_vars', f)), yaml.safe_load(fin.read())) for d in ['ControllerHostEntryDeployment', 'NetworkDeployment', 'MyExtraConfigPost', 'MyPostConfig']: with open(os.path.join(tmp_path, 'Controller', 'overcloud-controller-0', d)) as fin: self.assertEqual( yaml.safe_load(fin.read()), self._get_yaml_file(os.path.join( 'overcloud-controller-0', d))) for d in ['ComputeHostEntryDeployment', 'NetworkDeployment', 'MyExtraConfigPost']: with open(os.path.join(tmp_path, 'Compute', 'overcloud-novacompute-0', d)) as fin: self.assertEqual( yaml.safe_load(fin.read()), self._get_yaml_file(os.path.join( 'overcloud-novacompute-0', d))) for d in ['ComputeHostEntryDeployment', 'NetworkDeployment', 'MyExtraConfigPost']: with open(os.path.join(tmp_path, 'Compute', 'overcloud-novacompute-1', d)) as fin: self.assertEqual( yaml.safe_load(fin.read()), self._get_yaml_file(os.path.join( 'overcloud-novacompute-1', d))) for d in ['ComputeHostEntryDeployment', 'NetworkDeployment', 'MyExtraConfigPost', 'AnsibleDeployment']: with open(os.path.join(tmp_path, 'Compute', 'overcloud-novacompute-2', d)) as fin: self.assertEqual( yaml.safe_load(fin.read()), self._get_yaml_file(os.path.join( 'overcloud-novacompute-2', d))) @patch.object(ooo_config.Config, 'initialize_git_repo') @patch('tripleo_common.utils.config.Config.get_deployment_resource_id') @patch('tripleo_common.utils.config.Config.get_config_dict') @patch('tripleo_common.utils.config.Config.get_deployment_data') def test_config_download_os_apply_config( self, mock_deployment_data, mock_config_dict, mock_deployment_resource_id, mock_git_init): heat = mock.MagicMock() self.config = ooo_config.Config(heat) stack = mock.MagicMock() heat.stacks.get.return_value = stack heat.resources.get.return_value = mock.MagicMock() stack.outputs = [ {'output_key': 'RoleNetHostnameMap', 'output_value': { 'Controller': { 'ctlplane': [ 'overcloud-controller-0.ctlplane.localdomain']}, 'Compute': { 'ctlplane': [ 'overcloud-novacompute-0.ctlplane.localdomain', 'overcloud-novacompute-1.ctlplane.localdomain', 'overcloud-novacompute-2.ctlplane.localdomain']}}}, {'output_key': 'ServerIdData', 'output_value': { 'server_ids': { 'Controller': [ '00b3a5e1-5e8e-4b55-878b-2fa2271f15ad'], 'Compute': [ 'a7db3010-a51f-4ae0-a791-2364d629d20d', '8b07cd31-3083-4b88-a433-955f72039e2c', '169b46f8-1965-4d90-a7de-f36fb4a830fe']}}}, {'output_key': 'HostnameNetworkConfigMap', 'output_value': {}}, {'output_key': 'RoleGroupVars', 'output_value': { 'Controller': { 'any_errors_fatal': 'yes', 'max_fail_percentage': 15}, 'Compute': { 'any_errors_fatal': 'yes', 'max_fail_percentage': 15}, }}] deployment_data, configs = \ self._get_config_data('config_data.yaml') # Add a group:os-apply-config config and deployment config_uuid = str(uuid.uuid4()) configs[config_uuid] = dict( id=config_uuid, config=dict(a='a'), group='os-apply-config', outputs=[]) deployment_uuid = str(uuid.uuid4()) deployment_mock = mock.MagicMock() deployment_mock.id = deployment_uuid deployment_mock.attributes = dict( value=dict(server='00b3a5e1-5e8e-4b55-878b-2fa2271f15ad', deployment=deployment_uuid, config=config_uuid, name='OsApplyConfigDeployment')) deployment_data.append(deployment_mock) self.configs = configs self.deployments = deployment_data mock_deployment_data.return_value = deployment_data mock_config_dict.side_effect = self._get_config_dict mock_deployment_resource_id.side_effect = self._get_deployment_id self.tmp_dir = self.useFixture(fixtures.TempDir()).path with warnings.catch_warnings(record=True) as w: self.config.download_config(stack, self.tmp_dir) mock_git_init.assert_called_once_with(self.tmp_dir) # check that we got at least one of the warnings that we expected # to throw self.assertGreaterEqual(len(w), 1) self.assertGreaterEqual(len([x for x in w if issubclass(x.category, DeprecationWarning)]), 1) self.assertGreaterEqual(len([x for x in w if "group:os-apply-config" in str(x.message)]), 1) @patch.object(ooo_config.Config, 'initialize_git_repo') @patch('tripleo_common.utils.config.Config.get_deployment_resource_id') @patch('tripleo_common.utils.config.Config.get_deployment_data') def test_config_download_no_deployment_name( self, mock_deployment_data, mock_deployment_resource_id, mock_git_init): heat = mock.MagicMock() self.config = ooo_config.Config(heat) stack = mock.MagicMock() heat.stacks.get.return_value = stack heat.resources.get.return_value = mock.MagicMock() deployment_data, _ = self._get_config_data('config_data.yaml') # Delete the name of the first deployment and his parent. del deployment_data[0].attributes['value']['name'] deployment_data[0].parent_resource = None self.deployments = deployment_data mock_deployment_data.return_value = deployment_data mock_deployment_resource_id.side_effect = self._get_deployment_id self.tmp_dir = self.useFixture(fixtures.TempDir()).path self.assertRaises(ValueError, self.config.download_config, stack, self.tmp_dir) mock_git_init.assert_called_once_with(self.tmp_dir) @patch.object(ooo_config.Config, 'initialize_git_repo') @patch('tripleo_common.utils.config.Config.get_deployment_resource_id') @patch('tripleo_common.utils.config.Config.get_deployment_data') def test_config_download_warn_grandparent_resource_name( self, mock_deployment_data, mock_deployment_resource_id, mock_git_init): heat = mock.MagicMock() self.config = ooo_config.Config(heat) stack = mock.MagicMock() heat.stacks.get.return_value = stack heat.resources.get.return_value = mock.MagicMock() deployment_data, _ = self._get_config_data('config_data.yaml') # Set the name of the deployment to an integer to trigger looking up # the grandparent resource name deployment_data[0].attributes['value']['name'] = 1 self.deployments = deployment_data mock_deployment_data.return_value = deployment_data mock_deployment_resource_id.side_effect = self._get_deployment_id self.tmp_dir = self.useFixture(fixtures.TempDir()).path with warnings.catch_warnings(record=True) as w: self.assertRaises(ValueError, self.config.download_config, stack, self.tmp_dir) self.assertGreaterEqual(len(w), 1) self.assertGreaterEqual(len([x for x in w if "grandparent" in str(x.message)]), 1) mock_git_init.assert_called_once_with(self.tmp_dir) @patch.object(ooo_config.Config, 'initialize_git_repo') @patch('tripleo_common.utils.config.Config.get_deployment_resource_id') @patch('tripleo_common.utils.config.Config.get_config_dict') @patch('tripleo_common.utils.config.Config.get_deployment_data') def test_config_download_no_deployment_uuid(self, mock_deployment_data, mock_config_dict, mock_deployment_resource_id, mock_git_init): heat = mock.MagicMock() self.config = ooo_config.Config(heat) stack = mock.MagicMock() heat.stacks.get.return_value = stack heat.resources.get.return_value = mock.MagicMock() stack.outputs = [ {'output_key': 'RoleNetHostnameMap', 'output_value': { 'Controller': { 'ctlplane': [ 'overcloud-controller-0.ctlplane.localdomain']}, 'Compute': { 'ctlplane': [ 'overcloud-novacompute-0.ctlplane.localdomain', 'overcloud-novacompute-1.ctlplane.localdomain', 'overcloud-novacompute-2.ctlplane.localdomain']}}}, {'output_key': 'ServerIdData', 'output_value': { 'server_ids': { 'Controller': [ '00b3a5e1-5e8e-4b55-878b-2fa2271f15ad'], 'Compute': [ 'a7db3010-a51f-4ae0-a791-2364d629d20d', '8b07cd31-3083-4b88-a433-955f72039e2c', '169b46f8-1965-4d90-a7de-f36fb4a830fe']}}}, {'output_key': 'HostnameNetworkConfigMap', 'output_value': {}}, {'output_key': 'RoleGroupVars', 'output_value': { 'Controller': { 'any_errors_fatal': 'yes', 'max_fail_percentage': 15}, 'Compute': { 'any_errors_fatal': 'yes', 'max_fail_percentage': 15}, }}] deployment_data, configs = self._get_config_data('config_data.yaml') # Set the deployment to TripleOSoftwareDeployment for the first # deployment deployment_data[0].attributes['value']['deployment'] = \ 'TripleOSoftwareDeployment' # Set the physical_resource_id as '' for the second deployment deployment_data[1].attributes['value']['deployment'] = '' self.configs = configs self.deployments = deployment_data mock_deployment_data.return_value = deployment_data mock_config_dict.side_effect = self._get_config_dict mock_deployment_resource_id.side_effect = self._get_deployment_id self.tmp_dir = self.useFixture(fixtures.TempDir()).path with warnings.catch_warnings(record=True) as w: self.config.download_config(stack, self.tmp_dir) assert "Skipping deployment" in str(w[-1].message) assert "Skipping deployment" in str(w[-2].message) @patch.object(ooo_config.Config, 'initialize_git_repo') @patch.object(ooo_config.git, 'Repo') @patch.object(ooo_config.shutil, 'copyfile') @patch.object(ooo_config.Config, '_mkdir') @patch.object(ooo_config.Config, '_open_file') @patch.object(ooo_config.shutil, 'rmtree') @patch.object(ooo_config.os.path, 'exists') def test_overcloud_config_dont_preserve_config(self, mock_os_path_exists, mock_rmtree, mock_open, mock_mkdir, mock_copyfile, mock_repo, mock_git_init): config_type_list = ['config_settings', 'global_config_settings', 'logging_sources', 'monitoring_subscriptions', 'service_config_settings', 'service_metadata_settings', 'service_names', 'upgrade_batch_tasks', 'upgrade_tasks', 'external_deploy_tasks'] mock_os_path_exists.get.return_value = True heat = mock.MagicMock() heat.stacks.get.return_value = fakes.create_tht_stack() self.config = ooo_config.Config(heat) self.config.fetch_config('overcloud') fake_role = list(self.config.stack_outputs.get('RoleData')) self.config.download_config('overcloud', '/tmp/tht', config_type_list, False) mock_git_init.assert_called_once_with('/tmp/tht') expected_rmtree_calls = [call('/tmp/tht')] mock_rmtree.assert_has_calls(expected_rmtree_calls) expected_mkdir_calls = [call('/tmp/tht/%s' % r) for r in fake_role] mock_mkdir.assert_has_calls(expected_mkdir_calls, any_order=True) mock_mkdir.assert_called() expected_calls = [] for config in config_type_list: for role in fake_role: if 'external' in config: continue elif config == 'step_config': expected_calls += [call('/tmp/tht/%s/%s.pp' % (role, config))] elif config == 'param_config': expected_calls += [call('/tmp/tht/%s/%s.json' % (role, config))] else: expected_calls += [call('/tmp/tht/%s/%s.yaml' % (role, config))] mock_open.assert_has_calls(expected_calls, any_order=True) @patch.object(ooo_config.shutil, 'rmtree') @patch.object(ooo_config.os.path, 'exists') def test_create_config_dir(self, mock_os_path_exists, mock_rmtree): mock_os_path_exists.get.return_value = True heat = mock.MagicMock() heat.stacks.get.return_value = fakes.create_tht_stack() self.config = ooo_config.Config(heat) self.config.create_config_dir('/tmp/tht', False) expected_rmtree_calls = [call('/tmp/tht')] mock_rmtree.assert_has_calls(expected_rmtree_calls) def test_initialize_git_repo(self): heat = mock.MagicMock() heat.stacks.get.return_value = fakes.create_tht_stack() self.config = ooo_config.Config(heat) self.tmp_dir = self.useFixture(fixtures.TempDir()).path repo = self.config.initialize_git_repo(self.tmp_dir) self.assertIsInstance(repo, git.Repo) @patch('tripleo_common.utils.config.Config.get_config_dict') @patch('tripleo_common.utils.config.Config.get_deployment_data') def test_write_config(self, mock_deployment_data, mock_config_dict): heat = mock.MagicMock() self.config = ooo_config.Config(heat) stack = mock.MagicMock() heat.stacks.get.return_value = stack stack.outputs = [ {'output_key': 'RoleNetHostnameMap', 'output_value': { 'Controller': { 'ctlplane': [ 'overcloud-controller-0.ctlplane.localdomain']}, 'Compute': { 'ctlplane': [ 'overcloud-novacompute-0.ctlplane.localdomain', 'overcloud-novacompute-1.ctlplane.localdomain', 'overcloud-novacompute-2.ctlplane.localdomain']}}}, {'output_key': 'ServerIdData', 'output_value': { 'server_ids': { 'Controller': [ '00b3a5e1-5e8e-4b55-878b-2fa2271f15ad'], 'Compute': [ 'a7db3010-a51f-4ae0-a791-2364d629d20d', '8b07cd31-3083-4b88-a433-955f72039e2c', '169b46f8-1965-4d90-a7de-f36fb4a830fe']}}}, {'output_key': 'RoleData', 'output_value': { 'Controller': { 'ansible_group_vars': { 'chrony_host': 'localhost', 'chrony_acl': 'none', }}}}, {'output_key': 'RoleGroupVars', 'output_value': { 'Controller': { 'any_errors_fatal': True, 'chrony_host': '192.168.2.1', 'chrony_foo': 'bar', 'max_fail_percentage': 15}, 'Compute': { 'any_errors_fatal': True, 'max_fail_percentage': 15}}}, {'output_key': 'HostnameNetworkConfigMap', 'output_value': {}} ] deployment_data, configs = \ self._get_config_data('config_data.yaml') self.configs = configs self.deployments = deployment_data stack_data = self.config.fetch_config('overcloud') mock_deployment_data.return_value = deployment_data mock_config_dict.side_effect = self._get_config_dict config_dir = self.useFixture(fixtures.TempDir()).path self.config.write_config(stack_data, 'overcloud', config_dir) for f in ['Controller', 'Compute', ]: with open(os.path.join(config_dir, 'group_vars', f)) as fin: self.assertEqual( yaml.safe_load(fin.read()), self._get_yaml_file(f)) for d in ['ControllerHostEntryDeployment', 'NetworkDeployment', 'MyExtraConfigPost', 'MyPostConfig']: with open(os.path.join(config_dir, 'Controller', 'overcloud-controller-0', d)) as fin: self.assertEqual( yaml.safe_load(fin.read()), self._get_yaml_file(os.path.join( 'overcloud-controller-0', d))) for d in ['ComputeHostEntryDeployment', 'NetworkDeployment', 'MyExtraConfigPost']: with open(os.path.join(config_dir, 'Compute', 'overcloud-novacompute-0', d)) as fin: self.assertEqual( yaml.safe_load(fin.read()), self._get_yaml_file(os.path.join( 'overcloud-novacompute-0', d))) for d in ['ComputeHostEntryDeployment', 'NetworkDeployment', 'MyExtraConfigPost']: with open(os.path.join(config_dir, 'Compute', 'overcloud-novacompute-1', d)) as fin: self.assertEqual( yaml.safe_load(fin.read()), self._get_yaml_file(os.path.join( 'overcloud-novacompute-1', d))) for d in ['ComputeHostEntryDeployment', 'NetworkDeployment', 'MyExtraConfigPost', 'AnsibleDeployment']: with open(os.path.join(config_dir, 'Compute', 'overcloud-novacompute-2', d)) as fin: self.assertEqual( yaml.safe_load(fin.read()), self._get_yaml_file(os.path.join( 'overcloud-novacompute-2', d))) @patch('tripleo_common.utils.config.Config.get_config_dict') @patch('tripleo_common.utils.config.Config.get_deployment_data') @patch.object(ooo_config.yaml, 'safe_load') def test_validate_config(self, mock_yaml, mock_deployment_data, mock_config_dict): stack_config = """ Controller: ctlplane: overcloud-controller-0.ctlplane.localdomain Compute: ctlplane: overcloud-novacompute-0.ctlplane.localdomain overcloud-novacompute-1.ctlplane.localdomain overcloud-novacompute-2.ctlplane.localdomain """ yaml_file = '/tmp/testfile.yaml' heat = mock.MagicMock() heat.stacks.get.return_value = fakes.create_tht_stack() self.config = ooo_config.Config(heat) self.config.validate_config(stack_config, yaml_file) expected_yaml_safe_load_calls = [call(stack_config)] mock_yaml.assert_has_calls(expected_yaml_safe_load_calls) @patch('tripleo_common.utils.config.Config.get_config_dict') @patch('tripleo_common.utils.config.Config.get_deployment_data') def test_validate_config_invalid_yaml(self, mock_deployment_data, mock_config_dict): # Use invalid YAML to assert that we properly handle the exception stack_config = """ Controller: ctlplane: overcloud-controller-0.ctlplane.localdomain Compute: ctlplane: overcloud-novacompute-0.ctlplane.localdomain overcloud-novacompute-1.ctlplane.localdomain overcloud-novacompute-2.ctlplane.localdomain """ yaml_file = '/tmp/testfile.yaml' heat = mock.MagicMock() heat.stacks.get.return_value = fakes.create_tht_stack() self.config = ooo_config.Config(heat) self.assertRaises(yaml.scanner.ScannerError, self.config.validate_config, stack_config, yaml_file) @patch('tripleo_common.utils.config.Config.get_network_config_data') def test_render_network_config_empty_dict(self, mock_get_network_config_data): heat = mock.MagicMock() heat.stacks.get.return_value = fakes.create_tht_stack() config_mock = mock.MagicMock() config_mock.config = {} heat.software_configs.get.return_value = config_mock self.config = ooo_config.Config(heat) stack = mock.Mock() server_roles = dict(Controller='controller') mock_get_network_config_data.return_value = dict(Controller='config') config_dir = '/tmp/tht' self.config.render_network_config(stack, config_dir, server_roles) @patch.object(ooo_config.Config, '_open_file') @patch('tripleo_common.utils.config.Config.get_network_config_data') def test_render_network_config(self, mock_get_network_config_data, mock_open): heat = mock.MagicMock() heat.stacks.get.return_value = fakes.create_tht_stack() config_mock = mock.MagicMock() config_mock.config = 'some config' heat.software_configs.get.return_value = config_mock self.config = ooo_config.Config(heat) stack = mock.Mock() server_roles = dict(node1='Controller') mock_get_network_config_data.return_value = dict(node1='config', node2='config') config_dir = '/tmp/tht' self.config.render_network_config(stack, config_dir, server_roles) self.assertEqual(2, mock_open.call_count) self.assertEqual('/tmp/tht/Controller/node1/NetworkConfig', mock_open.call_args_list[0][0][0]) self.assertEqual('/tmp/tht/Controller/NetworkConfig', mock_open.call_args_list[1][0][0]) class OvercloudConfigTest(base.TestCase): def setUp(self,): super(OvercloudConfigTest, self).setUp() self.plan = 'overcloud' self.delete_after = 3600 self.config_container = 'config-overcloud' # setup swift self.template_files = ( 'some-name.yaml', 'some-other-name.yaml', 'yet-some-other-name.yaml', 'finally-another-name.yaml' ) self.swift = mock.MagicMock() self.swift.get_container.return_value = ( {'x-container-meta-usage-tripleo': 'plan'}, [ { 'name': tf, 'last_modified': '2018-11-05' } for tf in self.template_files ] ) self.swift.get_object.return_value = ({}, RESOURCES_YAML_CONTENTS) @mock.patch('tripleo_common.utils.swift.delete_container') @mock.patch('tripleo_common.utils.swift.download_container') @mock.patch('tripleo_common.utils.config.Config.download_config') @mock.patch('tripleo_common.utils.tarball.create_tarball') def test_get_overcloud_config(self, mock_create_tarball, mock_config, mock_swift_download, mock_swift_delete): heat = mock.MagicMock() heat.stacks.get.return_value = mock.MagicMock( stack_name='stack', id='stack_id') mock_config.return_value = '/tmp/fake-path' ooo_config.get_overcloud_config( self.swift, heat, self.plan, self.config_container, '/tmp/fake-path') mock_swift_download.assert_called_once_with(self.swift, self.config_container, '/tmp/fake-path') mock_swift_delete.assert_called_once_with(self.swift, self.config_container) self.assertEqual(2, self.swift.put_object.call_count) self.assertEqual(mock.call( 'config-overcloud', 'config-overcloud.tar.gz', self.swift.put_object.call_args_list[1][0][2]), # closed file call self.swift.put_object.call_args_list[1]) mock_create_tarball.assert_called_once() self.assertEqual(dict(excludes=['.tox', '*.pyc', '*.pyo']), mock_create_tarball.call_args[1]) @mock.patch('tripleo_common.utils.config.os.unlink') @mock.patch('tripleo_common.utils.config.os.path.exists') @mock.patch('tripleo_common.utils.config.os.symlink') @mock.patch('tripleo_common.utils.swift.download_container') @mock.patch('tempfile.mkdtemp') def test_download_config(self, mock_mkdtemp, mock_swiftutils, mock_os_symlink, mock_os_path_exists, mock_os_unlink): mock_mkdtemp.return_value = '/tmp/tripleo-foo-config' ooo_config.download_overcloud_config(self.swift, self.config_container) mock_swiftutils.assert_called_once_with(self.swift, self.config_container, '/tmp/tripleo-foo-config') @mock.patch('tripleo_common.utils.config.os.path.exists') @mock.patch('tripleo_common.utils.config.os.symlink') @mock.patch('tripleo_common.utils.swift.download_container') @mock.patch('tempfile.mkdtemp') def test_download_create_latest_symlink( self, mock_mkdtemp, mock_swiftutils, mock_os_symlink, mock_os_path_exists): mock_mkdtemp.return_value = '/var/lib/mistral/uuid' mock_os_path_exists.return_value = False ooo_config.download_overcloud_config(self.swift, self.config_container) mock_swiftutils.assert_called_once_with(self.swift, self.config_container, mock_mkdtemp()) mock_os_symlink.assert_called_once_with( '/var/lib/mistral/uuid', os.path.join(os.path.dirname('/var/lib/mistral/uuid'), 'config-download-latest')) @mock.patch('tripleo_common.utils.config.os.unlink') @mock.patch('tripleo_common.utils.config.os.path.exists') @mock.patch('tripleo_common.utils.config.os.symlink') @mock.patch('tripleo_common.utils.swift.download_container') @mock.patch('tempfile.mkdtemp') def test_download_update_latest_symlink( self, mock_mkdtemp, mock_swiftutils, mock_os_symlink, mock_os_path_exists, mock_os_unlink): mock_mkdtemp.return_value = '/var/lib/mistral/uuid' mock_os_path_exists.return_value = True ooo_config.download_overcloud_config(self.swift, self.config_container) mock_swiftutils.assert_called_once_with(self.swift, self.config_container, mock_mkdtemp()) mock_os_symlink.assert_called_once_with( '/var/lib/mistral/uuid', os.path.join(os.path.dirname('/var/lib/mistral/uuid'), 'config-download-latest')) mock_os_unlink.assert_called_once_with( '/var/lib/mistral/config-download-latest')