diff --git a/releasenotes/notes/roles_file-8adea5990682504d.yaml b/releasenotes/notes/roles_file-8adea5990682504d.yaml new file mode 100644 index 000000000..9c6896042 --- /dev/null +++ b/releasenotes/notes/roles_file-8adea5990682504d.yaml @@ -0,0 +1,6 @@ +--- +other: + - | + The roles data file may take either an absolute path or the path relative + to the tripleo heat templates directory. This is now applicable for all + of the commands involving roles data files. diff --git a/tripleoclient/config/standalone.py b/tripleoclient/config/standalone.py index 07e1fd289..51b989a06 100644 --- a/tripleoclient/config/standalone.py +++ b/tripleoclient/config/standalone.py @@ -151,8 +151,9 @@ class StandaloneConfig(BaseConfig): ), cfg.StrOpt('roles_file', default=None, - help=_('Roles file to override for heat. ' - 'The file path is related to the templates path') + help=_('Roles file to override for heat. May be an ' + 'absolute path or the path relative to the ' + 't-h-t templates directory used for deployment') ), cfg.BoolOpt('heat_native', default=True, diff --git a/tripleoclient/tests/test_utils.py b/tripleoclient/tests/test_utils.py index 8779ba9ad..f45af7f9d 100644 --- a/tripleoclient/tests/test_utils.py +++ b/tripleoclient/tests/test_utils.py @@ -770,6 +770,33 @@ class GetTripleoAnsibleInventory(TestCase): ) +class TestNormalizeFilePath(TestCase): + + @mock.patch('os.path.isfile', return_value=True) + def test_norm_path_abs(self, mock_exists): + self.assertEqual( + utils.rel_or_abs_path('/foobar.yaml', '/tmp'), + '/foobar.yaml') + + @mock.patch('os.path.isfile', side_effect=[False, True]) + def test_norm_path_rel(self, mock_exists): + self.assertEqual( + utils.rel_or_abs_path('baz/foobar.yaml', '/bar'), + '/bar/baz/foobar.yaml') + + +class TestFetchRolesFile(TestCase): + + @mock.patch('os.path.exists', return_value=True) + def test_fetch_roles_file(self, mock_exists): + with tempfile.NamedTemporaryFile(mode='w') as roles_file: + yaml.dump([{'name': 'Foobar'}], roles_file) + with mock.patch('tripleoclient.utils.rel_or_abs_path') as mock_rf: + mock_rf.return_value = roles_file.name + self.assertEqual(utils.fetch_roles_file(roles_file.name), + [{'name': 'Foobar'}]) + + class TestOvercloudNameScenarios(TestWithScenarios): scenarios = [ ('kernel_default', diff --git a/tripleoclient/tests/v1/overcloud_deploy/test_overcloud_deploy.py b/tripleoclient/tests/v1/overcloud_deploy/test_overcloud_deploy.py index 6969ca535..e9b769e21 100644 --- a/tripleoclient/tests/v1/overcloud_deploy/test_overcloud_deploy.py +++ b/tripleoclient/tests/v1/overcloud_deploy/test_overcloud_deploy.py @@ -1338,8 +1338,7 @@ class TestDeployOvercloud(fakes.TestDeployOvercloud): mock_create_tempest_deployer_input.assert_called_with() def test_get_default_role_counts_defaults(self): - parsed_args = mock.Mock() - parsed_args.roles_file = None + parsed_args = self.check_parser(self.cmd, [], []) defaults = { 'ControllerCount': 1, 'ComputeCount': 1, @@ -1351,11 +1350,8 @@ class TestDeployOvercloud(fakes.TestDeployOvercloud): defaults, self.cmd._get_default_role_counts(parsed_args)) - @mock.patch("yaml.safe_load") - @mock.patch("six.moves.builtins.open") - def test_get_default_role_counts_custom_roles(self, mock_open, - mock_safe_load): - parsed_args = mock.Mock() + @mock.patch("tripleoclient.utils.fetch_roles_file") + def test_get_default_role_counts_custom_roles(self, mock_roles): roles_data = [ {'name': 'ControllerApi', 'CountDefault': 3}, {'name': 'ControllerPcmk', 'CountDefault': 3}, @@ -1363,7 +1359,7 @@ class TestDeployOvercloud(fakes.TestDeployOvercloud): {'name': 'ObjectStorage', 'CountDefault': 0}, {'name': 'BlockStorage'} ] - mock_safe_load.return_value = roles_data + mock_roles.return_value = roles_data role_counts = { 'ControllerApiCount': 3, 'ControllerPcmkCount': 3, @@ -1373,7 +1369,7 @@ class TestDeployOvercloud(fakes.TestDeployOvercloud): } self.assertEqual( role_counts, - self.cmd._get_default_role_counts(parsed_args)) + self.cmd._get_default_role_counts(mock.Mock())) @mock.patch( 'tripleoclient.workflows.plan_management.list_deployment_plans', diff --git a/tripleoclient/tests/v1/test_container_image.py b/tripleoclient/tests/v1/test_container_image.py index ab6cf8ff1..c5867ea8b 100644 --- a/tripleoclient/tests/v1/test_container_image.py +++ b/tripleoclient/tests/v1/test_container_image.py @@ -157,6 +157,7 @@ class TestContainerImagePrepare(TestPluginV1): append_tag=None, ) + @mock.patch('tripleoclient.utils.fetch_roles_file') @mock.patch('tripleo_common.image.kolla_builder.' 'container_images_prepare_defaults', create=True) @mock.patch('tripleo_common.image.kolla_builder.' @@ -167,7 +168,7 @@ class TestContainerImagePrepare(TestPluginV1): 'build_service_filter') @mock.patch('requests.get') def test_container_image_prepare(self, mock_get, mock_bsf, pmef, mock_cip, - mock_cipd): + mock_cipd, mock_roles): temp = tempfile.mkdtemp() self.addCleanup(shutil.rmtree, temp) diff --git a/tripleoclient/tests/v1/tripleo/test_tripleo_deploy.py b/tripleoclient/tests/v1/tripleo/test_tripleo_deploy.py index fd13ae25d..3bf2a5b2f 100644 --- a/tripleoclient/tests/v1/tripleo/test_tripleo_deploy.py +++ b/tripleoclient/tests/v1/tripleo/test_tripleo_deploy.py @@ -22,7 +22,6 @@ import yaml from heatclient import exc as hc_exc from tripleo_common.image import kolla_builder -from tripleoclient import constants from tripleoclient import exceptions from tripleoclient.tests.v1.test_plugin import TestPluginV1 @@ -59,53 +58,26 @@ class TestDeployUndercloud(TestPluginV1): self.orc.stacks.create = mock.MagicMock( return_value={'stack': {'id': 'foo'}}) - @mock.patch('os.path.exists', return_value=True) - def test_set_roles_file(self, mock_exists): - self.cmd._set_roles_file('/foobar.yaml') - self.assertEqual(self.cmd.roles_file, '/foobar.yaml') - - @mock.patch('os.path.exists', return_value=False) - def test_set_roles_file_relative(self, mock_exists): - self.cmd._set_roles_file('foobar.yaml', - constants.TRIPLEO_HEAT_TEMPLATES) - self.assertEqual(self.cmd.roles_file, - '/usr/share/openstack-tripleo-heat-templates/' - 'foobar.yaml') - - def test_get_roles_data(self): - self.cmd.roles_data = [{'name': 'Foobar'}] - self.assertEqual(self.cmd._get_roles_data(), - [{'name': 'Foobar'}]) - - def test_get_roles_data_from_file(self): - with tempfile.NamedTemporaryFile(mode='w') as roles_file: - yaml.dump([{'name': 'Foobar'}], roles_file) - self.cmd.roles_file = roles_file.name - self.assertEqual(self.cmd._get_roles_data(), - [{'name': 'Foobar'}]) - - @mock.patch('os.path.exists', return_value=False) - def test_get_roles_data_missing(self, mock_exists): - self.cmd.roles_file = '/tmp/foo.yaml' - self.cmd.roles_data = None - self.assertEqual(self.cmd._get_roles_data(), None) - - @mock.patch('tripleoclient.v1.tripleo_deploy.Deploy.' - '_get_roles_data') - def test_get_primary_role_name(self, mock_data): + @mock.patch('os.path.exists') + @mock.patch('tripleoclient.utils.fetch_roles_file') + def test_get_primary_role_name(self, mock_data, mock_exists): + parsed_args = mock.Mock() mock_data.return_value = [ {'name': 'Bar'}, {'name': 'Foo', 'tags': ['primary']} ] - self.assertEqual(self.cmd._get_primary_role_name(), 'Foo') + self.assertEqual(self.cmd._get_primary_role_name(parsed_args), 'Foo') - def test_get_primary_role_name_none_defined(self): - self.assertEqual(self.cmd._get_primary_role_name(), 'Controller') + @mock.patch('tripleoclient.utils.fetch_roles_file', return_value=None) + def test_get_primary_role_name_none_defined(self, mock_data): + parsed_args = self.check_parser(self.cmd, [], []) + self.assertEqual(self.cmd._get_primary_role_name(parsed_args), + 'Controller') - @mock.patch('tripleoclient.v1.tripleo_deploy.Deploy.' - '_get_roles_data') + @mock.patch('tripleoclient.utils.fetch_roles_file') def test_get_primary_role_name_no_primary(self, mock_data): + parsed_args = mock.Mock() mock_data.return_value = [{'name': 'Bar'}, {'name': 'Foo'}] - self.assertEqual(self.cmd._get_primary_role_name(), 'Bar') + self.assertEqual(self.cmd._get_primary_role_name(parsed_args), 'Bar') @mock.patch('os.path.exists', side_effect=[True, False]) @mock.patch('shutil.copytree') @@ -237,6 +209,7 @@ class TestDeployUndercloud(TestPluginV1): mock.call(env_path='/tmp/thtroot42/notouch.yaml'), mock.call(env_path='../outside.yaml')]) + @mock.patch('tripleoclient.utils.rel_or_abs_path') @mock.patch('heatclient.common.template_utils.' 'process_environment_and_files', return_value=({}, {}), autospec=True) @@ -264,7 +237,8 @@ class TestDeployUndercloud(TestPluginV1): mock_hc_templ_parse, mock_hc_env_parse, mock_hc_get_templ_cont, - mock_hc_process): + mock_hc_process, + mock_norm_path): def hc_process(*args, **kwargs): if 'abs.yaml' in kwargs['env_path']: raise hc_exc.CommandError @@ -408,7 +382,6 @@ class TestDeployUndercloud(TestPluginV1): found_dropin = True self.assertTrue(found_dropin) - @mock.patch('os.path.isfile') @mock.patch('heatclient.common.template_utils.' 'process_environment_and_files', return_value=({}, {}), autospec=True) @@ -424,10 +397,12 @@ class TestDeployUndercloud(TestPluginV1): '_update_passwords_env', autospec=True) @mock.patch('tripleoclient.utils.' 'run_command_and_log', autospec=True) + @mock.patch('tripleoclient.v1.tripleo_deploy.Deploy.' + '_get_primary_role_name', autospec=True) def test_setup_heat_environments_default_plan_env( - self, mock_run, mock_update_pass_env, mock_process_hiera, - mock_process_multiple_environments, mock_hc_get_templ_cont, - mock_hc_process, mock_os): + self, mock_prim, mock_run, mock_update_pass_env, + mock_process_hiera, mock_process_multiple_environments, + mock_hc_get_templ_cont, mock_hc_process): tmpdir = self.useFixture(fixtures.TempDir()).path tht_from = os.path.join(tmpdir, 'tht-from') @@ -439,7 +414,6 @@ class TestDeployUndercloud(TestPluginV1): self._setup_heat_environments(tmpdir, tht_from, plan_env_path, mock_update_pass_env, mock_run) - @mock.patch('os.path.isfile') @mock.patch('heatclient.common.template_utils.' 'process_environment_and_files', return_value=({}, {}), autospec=True) @@ -455,10 +429,12 @@ class TestDeployUndercloud(TestPluginV1): '_update_passwords_env', autospec=True) @mock.patch('tripleoclient.utils.' 'run_command_and_log', autospec=True) + @mock.patch('tripleoclient.v1.tripleo_deploy.Deploy.' + '_get_primary_role_name', autospec=True) def test_setup_heat_environments_non_default_plan_env( - self, mock_run, mock_update_pass_env, mock_process_hiera, - mock_process_multiple_environments, mock_hc_get_templ_cont, - mock_hc_process, mock_os): + self, mock_prim, mock_run, mock_update_pass_env, + mock_process_hiera, mock_process_multiple_environments, + mock_hc_get_templ_cont, mock_hc_process): tmpdir = self.useFixture(fixtures.TempDir()).path tht_from = os.path.join(tmpdir, 'tht-from') @@ -502,6 +478,18 @@ class TestDeployUndercloud(TestPluginV1): mock_update_pass_env.return_value = os.path.join( tht_render, 'passwords.yaml') mock_run.return_value = 0 + original_abs = os.path.abspath + + # Stub abspath for default plan and envs to return the tht_render base + def abs_path_stub(*args, **kwargs): + if 'plan-environment.yaml' in args: + return plan_env_path + elif 'notenv.yaml' in args: + return os.path.join(tht_render, 'notenv.yaml') + elif 'env.yaml' in args: + return os.path.join(tht_render, 'env.yaml') + else: + return original_abs(*args, **kwargs) # logic handled in _standalone_deploy self.cmd.output_dir = tht_to @@ -535,10 +523,11 @@ class TestDeployUndercloud(TestPluginV1): os.path.join(tht_render, 'foo.yaml'), os.path.join(tht_render, 'outside.yaml')] - with mock.patch('os.path.isfile'): - environment = self.cmd._setup_heat_environments(parsed_args) + with mock.patch('os.path.abspath', side_effect=abs_path_stub): + with mock.patch('os.path.isfile'): + environment = self.cmd._setup_heat_environments(parsed_args) - self.assertEqual(expected_env, environment) + self.assertEqual(expected_env, environment) @mock.patch('tripleoclient.v1.tripleo_deploy.Deploy.' '_create_working_dirs', autospec=True) @@ -578,16 +567,16 @@ class TestDeployUndercloud(TestPluginV1): 'ansible-playbook', '-i', '/tmp/inventory.yaml', 'deploy_steps_playbook.yaml']) - @mock.patch('tripleoclient.v1.tripleo_deploy.Deploy.' - '_get_roles_data') + @mock.patch('tripleoclient.utils.fetch_roles_file') @mock.patch('tripleo_common.image.kolla_builder.' 'container_images_prepare_multi') def test_prepare_container_images(self, mock_cipm, rolesdata_mock): + parsed_args = mock.Mock() env = {'parameter_defaults': {}} mock_cipm.return_value = {'FooImage': 'foo/bar:baz'} rolesdata_mock.return_value = [{'name': 'Compute'}] - self.cmd._prepare_container_images(env) + self.cmd._prepare_container_images(env, parsed_args) mock_cipm.assert_called_once_with( env, diff --git a/tripleoclient/tests/workflows/test_plan_management.py b/tripleoclient/tests/workflows/test_plan_management.py index 3a38f0472..c6fd5f532 100644 --- a/tripleoclient/tests/workflows/test_plan_management.py +++ b/tripleoclient/tests/workflows/test_plan_management.py @@ -81,9 +81,11 @@ class TestPlanCreationWorkflows(utils.TestCommand): self.workflow.executions.create.assert_not_called() + @mock.patch('tripleoclient.utils.rel_or_abs_path') @mock.patch('tripleoclient.workflows.plan_management.tarball', autospec=True) - def test_create_plan_from_templates_roles_data(self, mock_tarball): + def test_create_plan_from_templates_roles_data(self, mock_tarball, + mock_norm_path): output = mock.Mock(output='{"result": ""}') self.workflow.action_executions.create.return_value = output self.websocket.wait_for_messages.return_value = self.message_success @@ -106,8 +108,8 @@ class TestPlanCreationWorkflows(utils.TestCommand): workflow_input={'container': 'test-overcloud', 'generate_passwords': True}) - mock_open_context.assert_has_calls( - [mock.call('the_roles_file.yaml')]) + self.assertIn(mock.call('the_roles_file.yaml', '/tht-root/'), + mock_norm_path.call_args_list) self.tripleoclient.object_store.put_object.assert_called_once_with( 'test-overcloud', 'roles_data.yaml', mock_open_context()) diff --git a/tripleoclient/utils.py b/tripleoclient/utils.py index 2d5bbaca4..c982ab758 100644 --- a/tripleoclient/utils.py +++ b/tripleoclient/utils.py @@ -45,7 +45,7 @@ from heatclient import exc as hc_exc from six.moves.urllib import error as url_error from six.moves.urllib import request - +from tripleoclient import constants from tripleoclient import exceptions @@ -1141,17 +1141,27 @@ def build_prepare_env(environment_files, environment_directories): return env -def rel_or_abs_path(tht_root, file_path): +def rel_or_abs_path(file_path, tht_root): '''Find a file, either absolute path or relative to the t-h-t dir''' + if not file_path: + return None path = os.path.abspath(file_path) - if not os.path.exists(path): + if not os.path.isfile(path): path = os.path.abspath(os.path.join(tht_root, file_path)) - if not os.path.exists(path): + if not os.path.isfile(path): raise exceptions.DeploymentError( "Can't find path %s %s" % (file_path, path)) return path +def fetch_roles_file(roles_file, tht_path=constants.TRIPLEO_HEAT_TEMPLATES): + '''Fetch t-h-t roles data fromm roles_file abs path or rel to tht_path.''' + if not roles_file: + return None + with open(rel_or_abs_path(roles_file, tht_path)) as f: + return yaml.safe_load(f) + + def load_config(osloconf, path): '''Load oslo config from a file path. ''' log = logging.getLogger(__name__ + ".load_config") diff --git a/tripleoclient/v1/container_image.py b/tripleoclient/v1/container_image.py index e58eb5337..57abdb741 100644 --- a/tripleoclient/v1/container_image.py +++ b/tripleoclient/v1/container_image.py @@ -32,6 +32,7 @@ from tripleo_common.image import kolla_builder from tripleoclient import command from tripleoclient import constants +from tripleoclient import exceptions from tripleoclient import utils @@ -198,9 +199,11 @@ class PrepareImageFiles(command.Command): def get_parser(self, prog_name): parser = super(PrepareImageFiles, self).get_parser(prog_name) - roles_file = os.path.join(constants.TRIPLEO_HEAT_TEMPLATES, - constants.OVERCLOUD_ROLES_FILE) - if not os.path.isfile(roles_file): + try: + roles_file = utils.rel_or_abs_path( + constants.OVERCLOUD_ROLES_FILE, + constants.TRIPLEO_HEAT_TEMPLATES) + except exceptions.DeploymentError: roles_file = None defaults = kolla_builder.container_images_prepare_defaults() @@ -358,8 +361,11 @@ class PrepareImageFiles(command.Command): parser.add_argument( '--roles-file', '-r', dest='roles_file', default=roles_file, - help=_('Roles file, overrides the default %s' - ) % constants.OVERCLOUD_ROLES_FILE + help=_( + 'Roles file, overrides the default %s in the t-h-t templates ' + 'directory used for deployment. May be an ' + 'absolute path or the path relative to the templates dir.' + ) % constants.OVERCLOUD_ROLES_FILE ) parser.add_argument( '--modify-role', @@ -390,10 +396,7 @@ class PrepareImageFiles(command.Command): def take_action(self, parsed_args): self.log.debug("take_action(%s)" % parsed_args) - if parsed_args.roles_file: - roles_data = yaml.safe_load(open(parsed_args.roles_file).read()) - else: - roles_data = set() + roles_data = utils.fetch_roles_file(parsed_args.roles_file) or set() env = utils.build_prepare_env( parsed_args.environment_files, @@ -546,9 +549,11 @@ class TripleOImagePrepare(command.Command): def get_parser(self, prog_name): parser = super(TripleOImagePrepare, self).get_parser(prog_name) - roles_file = os.path.join(constants.TRIPLEO_HEAT_TEMPLATES, - constants.OVERCLOUD_ROLES_FILE) - if not os.path.isfile(roles_file): + try: + roles_file = utils.rel_or_abs_path( + constants.OVERCLOUD_ROLES_FILE, + constants.TRIPLEO_HEAT_TEMPLATES) + except exceptions.DeploymentError: roles_file = None parser.add_argument( '--environment-file', '-e', metavar='', @@ -572,8 +577,11 @@ class TripleOImagePrepare(command.Command): parser.add_argument( '--roles-file', '-r', dest='roles_file', default=roles_file, - help=_('Roles file to filter images by, overrides the default %s' - ) % constants.OVERCLOUD_ROLES_FILE + help=_( + 'Roles file, overrides the default %s in the t-h-t templates ' + 'directory used for deployment. May be an ' + 'absolute path or the path relative to the templates dir.' + ) % constants.OVERCLOUD_ROLES_FILE ) parser.add_argument( "--output-env-file", @@ -610,10 +618,7 @@ class TripleOImagePrepare(command.Command): raise oscexc.CommandError('--cleanup must be one of: %s' % ', '.join(image_uploader.CLEANUP)) - if parsed_args.roles_file: - roles_data = yaml.safe_load(open(parsed_args.roles_file).read()) - else: - roles_data = None + roles_data = utils.fetch_roles_file(parsed_args.roles_file) env = utils.build_prepare_env( parsed_args.environment_files, diff --git a/tripleoclient/v1/overcloud_deploy.py b/tripleoclient/v1/overcloud_deploy.py index 2ec7fb9d4..1c2a1a74b 100644 --- a/tripleoclient/v1/overcloud_deploy.py +++ b/tripleoclient/v1/overcloud_deploy.py @@ -550,7 +550,8 @@ class DeployOvercloud(command.Command): def _get_default_role_counts(self, parsed_args): if parsed_args.roles_file: - roles_data = yaml.safe_load(open(parsed_args.roles_file).read()) + roles_data = utils.fetch_roles_file(parsed_args.roles_file, + parsed_args.templates) else: # Assume default role counts return { @@ -634,7 +635,8 @@ class DeployOvercloud(command.Command): parser.add_argument( '--roles-file', '-r', dest='roles_file', help=_('Roles file, overrides the default %s in the --templates ' - 'directory') % constants.OVERCLOUD_ROLES_FILE + 'directory. May be an absolute path or the path relative ' + ' to --templates') % constants.OVERCLOUD_ROLES_FILE ) parser.add_argument( '--networks-file', '-n', dest='networks_file', diff --git a/tripleoclient/v1/tripleo_deploy.py b/tripleoclient/v1/tripleo_deploy.py index 7ba8c4a5c..b5789478a 100644 --- a/tripleoclient/v1/tripleo_deploy.py +++ b/tripleoclient/v1/tripleo_deploy.py @@ -85,8 +85,6 @@ class Deploy(command.Command): tht_render = None output_dir = None tmp_ansible_dir = None - roles_file = None - roles_data = None stack_update_mark = None stack_action = 'CREATE' deployment_user = None @@ -123,22 +121,6 @@ class Deploy(command.Command): else: os.chmod(file_name, mode) - def _set_roles_file(self, file_name=None, templates_dir=None): - """Set the roles file for the deployment - - If the file_name is a full path, it will be used. If the file name - passed in is not a full path, we will join it with the templates - dir and use that instead. - - :param file_name: (String) role file name to use, can be relative to - templates directory - :param templates_dir: - """ - if os.path.exists(file_name): - self.roles_file = file_name - else: - self.roles_file = os.path.join(templates_dir, file_name) - def _set_stack_action(self, parsed_args): """Set the stack action for deployment""" # Prepare the heat stack action we want to start deployment with @@ -151,23 +133,10 @@ class Deploy(command.Command): _('The heat stack {0} action is {1}').format( parsed_args.stack, self.stack_action)) - def _get_roles_data(self): - """Load the roles data for deployment""" - # only load once - if self.roles_data: - return self.roles_data - - if self.roles_file and os.path.exists(self.roles_file): - with open(self.roles_file) as f: - self.roles_data = yaml.safe_load(f) - elif self.roles_file: - self.log.warning("roles_data '%s' is not found" % self.roles_file) - - return self.roles_data - - def _get_primary_role_name(self): + def _get_primary_role_name(self, parsed_args): """Return the primary role name""" - roles_data = self._get_roles_data() + roles_data = utils.fetch_roles_file(parsed_args.roles_file, + parsed_args.templates) if not roles_data: # TODO(aschultz): should this be Undercloud instead? return 'Controller' @@ -564,11 +533,11 @@ class Deploy(command.Command): parsed_args.templates, self.tht_render, env_files) # generate jinja templates by its work dir location - self.log.debug(_("Using roles file %s") % self.roles_file) + self.log.debug(_("Using roles file %s") % parsed_args.roles_file) process_templates = os.path.join(parsed_args.templates, 'tools/process-templates.py') args = ['python', process_templates, '--roles-data', - self.roles_file, '--output-dir', self.tht_render] + parsed_args.roles_file, '--output-dir', self.tht_render] if utils.run_command_and_log(self.log, args, cwd=self.tht_render) != 0: # TODO(aschultz): improve error messaging msg = _("Problems generating templates.") @@ -581,10 +550,10 @@ class Deploy(command.Command): # Include any environments from the plan-environment.yaml plan_env_path = utils.rel_or_abs_path( - self.tht_render, parsed_args.plan_environment_file) + parsed_args.plan_environment_file, self.tht_render) with open(plan_env_path, 'r') as f: plan_env_data = yaml.safe_load(f) - environments = [utils.rel_or_abs_path(self.tht_render, e.get('path')) + environments = [utils.rel_or_abs_path(e.get('path'), self.tht_render) for e in plan_env_data.get('environments', {})] # this will allow the user to overwrite passwords with custom envs @@ -626,7 +595,7 @@ class Deploy(command.Command): tmp_env.update(self._generate_portmap_parameters( ip, ip_nw, c_ip, p_ip, stack_name=parsed_args.stack, - role_name=self._get_primary_role_name())) + role_name=self._get_primary_role_name(parsed_args))) with open(maps_file, 'w') as env_file: yaml.safe_dump({'parameter_defaults': tmp_env}, env_file, @@ -653,8 +622,9 @@ class Deploy(command.Command): return environments + user_environments - def _prepare_container_images(self, env): - roles_data = self._get_roles_data() + def _prepare_container_images(self, env, parsed_args): + roles_data = utils.fetch_roles_file(parsed_args.roles_file, + parsed_args.templates) image_params = kolla_builder.container_images_prepare_multi( env, roles_data, dry_run=True) @@ -678,7 +648,7 @@ class Deploy(command.Command): environments, self.tht_render, parsed_args.templates, cleanup=parsed_args.cleanup) - self._prepare_container_images(env) + self._prepare_container_images(env, parsed_args) self.log.debug(_("Getting template contents")) template_path = os.path.join(self.tht_render, 'overcloud.yaml') @@ -805,8 +775,11 @@ class Deploy(command.Command): ) parser.add_argument( '--roles-file', '-r', dest='roles_file', - help=_('Roles file, overrides the default %s in the --templates ' - 'directory') % constants.UNDERCLOUD_ROLES_FILE, + help=_( + 'Roles file, overrides the default %s in the t-h-t templates ' + 'directory used for deployment. May be an ' + 'absolute path or the path relative to the templates dir.' + ) % constants.UNDERCLOUD_ROLES_FILE, default=constants.UNDERCLOUD_ROLES_FILE ) parser.add_argument( @@ -1038,10 +1011,6 @@ class Deploy(command.Command): # Set default plan if not specified by user self._set_default_plan() - # configure our roles data - self._set_roles_file(parsed_args.roles_file, self.tht_render) - self._get_roles_data() - rc = 1 try: # NOTE(bogdando): Look for the unique virtual update mark matching diff --git a/tripleoclient/v1/undercloud_preflight.py b/tripleoclient/v1/undercloud_preflight.py index fed5abedf..e0687d1e8 100644 --- a/tripleoclient/v1/undercloud_preflight.py +++ b/tripleoclient/v1/undercloud_preflight.py @@ -382,7 +382,9 @@ def _validate_passwords_file(): def _validate_env_files_paths(): """Verify the non-matching templates path vs env files paths""" tht_path = CONF.get('templates') or constants.TRIPLEO_HEAT_TEMPLATES - roles_file = CONF.get('roles_file') or constants.UNDERCLOUD_ROLES_FILE + roles_file = utils.rel_or_abs_path( + CONF.get('roles_file') or constants.UNDERCLOUD_ROLES_FILE, + tht_path) # get the list of jinja templates normally rendered for UC installations LOG.debug(_("Using roles file {0} from {1}").format(roles_file, tht_path)) diff --git a/tripleoclient/workflows/plan_management.py b/tripleoclient/workflows/plan_management.py index 5fadb0014..eb91beff6 100644 --- a/tripleoclient/workflows/plan_management.py +++ b/tripleoclient/workflows/plan_management.py @@ -20,6 +20,7 @@ from tripleo_common.utils import tarball from tripleoclient import constants from tripleoclient import exceptions +from tripleoclient import utils from tripleoclient.workflows import base LOG = logging.getLogger(__name__) @@ -42,7 +43,8 @@ def _upload_templates(swift_client, container_name, tht_root, roles_file=None, # Optional override of the roles_data.yaml file if roles_file: _upload_file(swift_client, container_name, - constants.OVERCLOUD_ROLES_FILE, roles_file) + constants.OVERCLOUD_ROLES_FILE, + utils.rel_or_abs_path(roles_file, tht_root)) # Optional override of the network_data.yaml file if networks_file: @@ -157,8 +159,9 @@ def create_plan_from_templates(clients, name, tht_root, roles_file=None, "Unable to create plan. {}".format(result)) print("Creating plan from template files in: {}".format(tht_root)) - _upload_templates(swift_client, name, tht_root, roles_file, plan_env_file, - networks_file) + _upload_templates(swift_client, name, tht_root, + utils.rel_or_abs_path(roles_file, tht_root), + plan_env_file, networks_file) try: create_deployment_plan(clients, container=name, @@ -174,6 +177,7 @@ def update_plan_from_templates(clients, name, tht_root, roles_file=None, swift_client = clients.tripleoclient.object_store passwords = None keep_file_contents = {} + roles_file = utils.rel_or_abs_path(roles_file, tht_root) if keep_env: # Dict items are (remote_name, local_name). local_name may be