From 629a35f00b62ebae25e805835cb0d53fbac935a8 Mon Sep 17 00:00:00 2001 From: Emilien Macchi Date: Tue, 17 Dec 2019 10:41:22 -0500 Subject: [PATCH] Implement utils.copy_clouds_yaml copy_clouds_yaml is a new function that will be used to copy /etc/openstack/clouds.yaml which is now generated by Ansible in TripleO Heat Templates: https://review.opendev.org/706850 It also sets the right permissions to only allow the deployment user to read the file which container sensible credentials (cloud admin). We also have to backport: I286c71edf120e11d8e51c792e6078dc60147f026 Depends-On: https://review.opendev.org/#/c/706850/ Change-Id: I49b95bf7a03e39db3c2cbb91b2167c7645b09136 (cherry picked from commit ccd4c7f59f0884b16894018710f9ea5236c257d2) --- .../overcloud_deploy/test_overcloud_deploy.py | 53 ++++++++++++++----- .../tests/v1/tripleo/test_tripleo_deploy.py | 12 +++-- tripleoclient/utils.py | 50 +++++++++++++++++ tripleoclient/v1/overcloud_deploy.py | 24 ++------- tripleoclient/v1/tripleo_deploy.py | 4 ++ 5 files changed, 107 insertions(+), 36 deletions(-) diff --git a/tripleoclient/tests/v1/overcloud_deploy/test_overcloud_deploy.py b/tripleoclient/tests/v1/overcloud_deploy/test_overcloud_deploy.py index e00be249d..c7f48f338 100644 --- a/tripleoclient/tests/v1/overcloud_deploy/test_overcloud_deploy.py +++ b/tripleoclient/tests/v1/overcloud_deploy/test_overcloud_deploy.py @@ -93,6 +93,7 @@ class TestDeployOvercloud(fakes.TestDeployOvercloud): self.cmd._download_missing_files_from_plan = self.real_download_missing shutil.rmtree = self.real_shutil + @mock.patch('tripleoclient.utils.copy_clouds_yaml') @mock.patch('tripleoclient.v1.overcloud_deploy.DeployOvercloud.' '_get_undercloud_host_entry', autospec=True, return_value='192.168.0.1 uc.ctlplane.localhost uc.ctlplane') @@ -111,7 +112,7 @@ class TestDeployOvercloud(fakes.TestDeployOvercloud): mock_create_parameters_env, mock_breakpoints_cleanup, mock_events, mock_stack_network_check, - mock_get_undercloud_host_entry): + mock_get_undercloud_host_entry, mock_copy): fixture = deployment.DeploymentWorkflowFixture() self.useFixture(fixture) clients = self.app.client_manager @@ -188,7 +189,9 @@ class TestDeployOvercloud(fakes.TestDeployOvercloud): template_object=constants.OVERCLOUD_YAML_NAME) mock_create_tempest_deployer_input.assert_called_with() + mock_copy.assert_called_once() + @mock.patch('tripleoclient.utils.copy_clouds_yaml') @mock.patch('tripleoclient.v1.overcloud_deploy.DeployOvercloud.' '_get_undercloud_host_entry', autospec=True, return_value='192.168.0.1 uc.ctlplane.localhost uc.ctlplane') @@ -209,7 +212,7 @@ class TestDeployOvercloud(fakes.TestDeployOvercloud): mock_breakpoints_cleanup, mock_postconfig, mock_invoke_plan_env_wf, - mock_get_undercloud_host_entry): + mock_get_undercloud_host_entry, mock_copy): fixture = deployment.DeploymentWorkflowFixture() self.useFixture(fixture) @@ -308,7 +311,9 @@ class TestDeployOvercloud(fakes.TestDeployOvercloud): env_map = yaml.safe_load(f) self.assertEqual(env_map.get('parameter_defaults'), parameters_env.get('parameter_defaults')) + mock_copy.assert_called_once() + @mock.patch('tripleoclient.utils.copy_clouds_yaml') @mock.patch('tripleoclient.v1.overcloud_deploy.DeployOvercloud.' '_get_undercloud_host_entry', autospec=True, return_value='192.168.0.1 uc.ctlplane.localhost uc.ctlplane') @@ -339,7 +344,7 @@ class TestDeployOvercloud(fakes.TestDeployOvercloud): mock_postconfig, mock_shutil_rmtree, mock_invoke_plan_env_wf, mock_stack_network_check, - mock_get_undercloud_host_entry): + mock_get_undercloud_host_entry, mock_copy): fixture = deployment.DeploymentWorkflowFixture() self.useFixture(fixture) plane_management_fixture = deployment.PlanManagementFixture() @@ -445,7 +450,9 @@ class TestDeployOvercloud(fakes.TestDeployOvercloud): [mock.call('the-plan-environment.yaml')]) clients.tripleoclient.object_store.put_object.assert_called() self.assertTrue(mock_invoke_plan_env_wf.called) + mock_copy.assert_called_once() + @mock.patch('tripleoclient.utils.copy_clouds_yaml') @mock.patch('tripleoclient.v1.overcloud_deploy.DeployOvercloud.' '_get_undercloud_host_entry', autospec=True, return_value='192.168.0.1 uc.ctlplane.localhost uc.ctlplane') @@ -470,7 +477,7 @@ class TestDeployOvercloud(fakes.TestDeployOvercloud): mock_create_parameters_env, mock_validate_args, mock_breakpoints_cleanup, mock_postconfig, mock_deprecated_params, mock_stack_network_check, - mock_get_undercloud_host_entry): + mock_get_undercloud_host_entry, mock_copy): fixture = deployment.DeploymentWorkflowFixture() self.useFixture(fixture) plane_management_fixture = deployment.PlanManagementFixture() @@ -533,7 +540,9 @@ class TestDeployOvercloud(fakes.TestDeployOvercloud): deploy_plan_call = execution_calls[1] deploy_plan_call_input = deploy_plan_call[1]['workflow_input'] self.assertTrue(deploy_plan_call_input['skip_deploy_identifier']) + mock_copy.assert_called_once() + @mock.patch('tripleoclient.utils.copy_clouds_yaml') @mock.patch('tripleoclient.v1.overcloud_deploy.DeployOvercloud.' '_get_undercloud_host_entry', autospec=True, return_value='192.168.0.1 uc.ctlplane.localhost uc.ctlplane') @@ -552,7 +561,8 @@ class TestDeployOvercloud(fakes.TestDeployOvercloud): mock_deploy_postconfig, mock_breakpoints_cleanup, mock_events, mock_stack_network_check, - mock_get_undercloud_host_entry): + mock_get_undercloud_host_entry, + mock_copy): fixture = deployment.DeploymentWorkflowFixture() self.useFixture(fixture) plane_management_fixture = deployment.PlanManagementFixture() @@ -602,6 +612,7 @@ class TestDeployOvercloud(fakes.TestDeployOvercloud): template_object=constants.OVERCLOUD_YAML_NAME) mock_create_tempest_deployer_input.assert_called_with() + mock_copy.assert_called_once() @mock.patch('tripleoclient.v1.overcloud_deploy.DeployOvercloud.' '_deploy_tripleo_heat_templates', autospec=True) @@ -620,6 +631,7 @@ class TestDeployOvercloud(fakes.TestDeployOvercloud): self.cmd.take_action, parsed_args) self.assertFalse(mock_deploy_tht.called) + @mock.patch('tripleoclient.utils.copy_clouds_yaml') @mock.patch('tripleoclient.utils.check_stack_network_matches_env_files') @mock.patch('tripleoclient.v1.overcloud_deploy.DeployOvercloud.' '_deploy_postconfig', autospec=True) @@ -629,7 +641,7 @@ class TestDeployOvercloud(fakes.TestDeployOvercloud): '_heat_deploy', autospec=True) def test_environment_dirs(self, mock_deploy_heat, mock_update_parameters, mock_post_config, - mock_stack_network_check): + mock_stack_network_check, mock_copy): fixture = deployment.DeploymentWorkflowFixture() self.useFixture(fixture) plane_management_fixture = deployment.PlanManagementFixture() @@ -683,6 +695,7 @@ class TestDeployOvercloud(fakes.TestDeployOvercloud): parsed_args = self.check_parser(self.cmd, arglist, verifylist) self.cmd.take_action(parsed_args) + mock_copy.assert_called_once() @mock.patch('tripleoclient.utils.check_stack_network_matches_env_files') @mock.patch('tripleoclient.utils.get_stack', autospec=True) @@ -929,6 +942,7 @@ class TestDeployOvercloud(fakes.TestDeployOvercloud): self.assertFalse(utils_fixture.mock_create_ocrc.called) self.assertFalse(mock_create_tempest_deployer_input.called) + @mock.patch('tripleoclient.utils.copy_clouds_yaml') @mock.patch('tripleoclient.v1.overcloud_deploy.DeployOvercloud.' '_get_undercloud_host_entry', autospec=True, return_value='192.168.0.1 uc.ctlplane.localhost uc.ctlplane') @@ -939,7 +953,7 @@ class TestDeployOvercloud(fakes.TestDeployOvercloud): @mock.patch('shutil.rmtree', autospec=True) def test_answers_file(self, mock_rmtree, mock_tmpdir, mock_heat_deploy, mock_stack_network_check, - mock_get_undercloud_host_entry): + mock_get_undercloud_host_entry, mock_copy): fixture = deployment.DeploymentWorkflowFixture() self.useFixture(fixture) clients = self.app.client_manager @@ -1014,6 +1028,7 @@ class TestDeployOvercloud(fakes.TestDeployOvercloud): self.assertIn('Test2', call_args[8]['resource_registry']) utils_fixture.mock_deploy_tht.assert_called_with() + mock_copy.assert_called_once() @mock.patch('tripleoclient.v1.overcloud_deploy.DeployOvercloud.' '_get_undercloud_host_entry', autospec=True, @@ -1072,6 +1087,7 @@ class TestDeployOvercloud(fakes.TestDeployOvercloud): self.cmd.take_action, parsed_args) + @mock.patch('tripleoclient.utils.copy_clouds_yaml') @mock.patch('tripleoclient.v1.overcloud_deploy.DeployOvercloud.' '_get_undercloud_host_entry', autospec=True, return_value='192.168.0.1 uc.ctlplane.localhost uc.ctlplane') @@ -1097,7 +1113,7 @@ class TestDeployOvercloud(fakes.TestDeployOvercloud): mock_breakpoints_cleanup, mock_deploy_post_config, mock_stack_network_check, - mock_get_undercloud_host_entry): + mock_get_undercloud_host_entry, mock_copy): fixture = deployment.DeploymentWorkflowFixture() self.useFixture(fixture) plane_management_fixture = deployment.PlanManagementFixture() @@ -1182,6 +1198,7 @@ class TestDeployOvercloud(fakes.TestDeployOvercloud): mock_create_tempest_deployer_input.assert_called_with() mock_validate_args.assert_called_once_with(parsed_args) + mock_copy.assert_called_once() @mock.patch('tripleoclient.workflows.parameters.' 'check_deprecated_parameters', autospec=True) @@ -1250,6 +1267,7 @@ class TestDeployOvercloud(fakes.TestDeployOvercloud): self.assertRaises(exceptions.StackInProgress, self.cmd.take_action, parsed_args) + @mock.patch('tripleoclient.utils.copy_clouds_yaml') @mock.patch('tripleoclient.v1.overcloud_deploy.DeployOvercloud.' '_get_undercloud_host_entry', autospec=True, return_value='192.168.0.1 uc.ctlplane.localhost uc.ctlplane') @@ -1258,7 +1276,7 @@ class TestDeployOvercloud(fakes.TestDeployOvercloud): @mock.patch('tripleoclient.v1.overcloud_deploy.DeployOvercloud.' '_deploy_tripleo_heat_templates_tmpdir', autospec=True) def test_deployed_server(self, mock_deploy_tmpdir, mock_overcloudrc, - mock_get_undercloud_host_entry): + mock_get_undercloud_host_entry, mock_copy): fixture = deployment.DeploymentWorkflowFixture() self.useFixture(fixture) utils_fixture = deployment.UtilsOvercloudFixture() @@ -1281,6 +1299,7 @@ class TestDeployOvercloud(fakes.TestDeployOvercloud): self.assertNotCalled(clients.baremetal) self.assertNotCalled(clients.compute) self.assertTrue(utils_fixture.mock_deploy_tht.called) + mock_copy.assert_called_once() @mock.patch('tripleoclient.v1.overcloud_deploy.DeployOvercloud.' '_deploy_tripleo_heat_templates', autospec=True) @@ -1298,6 +1317,7 @@ class TestDeployOvercloud(fakes.TestDeployOvercloud): parsed_args) self.assertFalse(mock_deploy_tmpdir.called) + @mock.patch('tripleoclient.utils.copy_clouds_yaml') @mock.patch('tripleoclient.v1.overcloud_deploy.DeployOvercloud.' '_get_undercloud_host_entry', autospec=True, return_value='192.168.0.1 uc.ctlplane.localhost uc.ctlplane') @@ -1307,7 +1327,7 @@ class TestDeployOvercloud(fakes.TestDeployOvercloud): '_deploy_tripleo_heat_templates_tmpdir', autospec=True) def test_config_download( self, mock_deploy_tmpdir, - mock_overcloudrc, mock_get_undercloud_host_entry): + mock_overcloudrc, mock_get_undercloud_host_entry, mock_copy): fixture = deployment.DeploymentWorkflowFixture() self.useFixture(fixture) utils_fixture = deployment.UtilsOvercloudFixture() @@ -1331,7 +1351,9 @@ class TestDeployOvercloud(fakes.TestDeployOvercloud): self.assertEqual( 'deploying', fixture.mock_set_deployment_status.call_args[0][1]) + mock_copy.assert_called_once() + @mock.patch('tripleoclient.utils.copy_clouds_yaml') @mock.patch('tripleoclient.v1.overcloud_deploy.DeployOvercloud.' '_get_undercloud_host_entry', autospec=True, return_value='192.168.0.1 uc.ctlplane.localhost uc.ctlplane') @@ -1341,7 +1363,7 @@ class TestDeployOvercloud(fakes.TestDeployOvercloud): '_deploy_tripleo_heat_templates_tmpdir', autospec=True) def test_config_download_only( self, mock_deploy_tmpdir, - mock_overcloudrc, mock_get_undercloud_host_entry): + mock_overcloudrc, mock_get_undercloud_host_entry, mock_copy): fixture = deployment.DeploymentWorkflowFixture() self.useFixture(fixture) utils_fixture = deployment.UtilsOvercloudFixture() @@ -1365,6 +1387,7 @@ class TestDeployOvercloud(fakes.TestDeployOvercloud): self.assertEqual( 'deploying', fixture.mock_set_deployment_status.call_args[0][1]) + mock_copy.assert_called_once() @mock.patch('tripleoclient.v1.overcloud_deploy.DeployOvercloud.' '_get_undercloud_host_entry', autospec=True, @@ -1409,6 +1432,7 @@ class TestDeployOvercloud(fakes.TestDeployOvercloud): 'failed', fixture.mock_set_deployment_status.call_args[0][1]) + @mock.patch('tripleoclient.utils.copy_clouds_yaml') @mock.patch('tripleoclient.v1.overcloud_deploy.DeployOvercloud.' '_get_undercloud_host_entry', autospec=True, return_value='192.168.0.1 uc.ctlplane.localhost uc.ctlplane') @@ -1418,7 +1442,7 @@ class TestDeployOvercloud(fakes.TestDeployOvercloud): '_deploy_tripleo_heat_templates_tmpdir', autospec=True) def test_override_ansible_cfg( self, mock_deploy_tmpdir, - mock_overcloudrc, mock_get_undercloud_host_entry): + mock_overcloudrc, mock_get_undercloud_host_entry, mock_copy): fixture = deployment.DeploymentWorkflowFixture() self.useFixture(fixture) utils_fixture = deployment.UtilsOvercloudFixture() @@ -1440,7 +1464,9 @@ class TestDeployOvercloud(fakes.TestDeployOvercloud): self.assertTrue(fixture.mock_config_download.called) self.assertEqual('ansible.cfg', fixture.mock_config_download.call_args[0][8]) + mock_copy.assert_called_once() + @mock.patch('tripleoclient.utils.copy_clouds_yaml') @mock.patch('tripleoclient.v1.overcloud_deploy.DeployOvercloud.' '_get_undercloud_host_entry', autospec=True, return_value='192.168.0.1 uc.ctlplane.localhost uc.ctlplane') @@ -1450,7 +1476,7 @@ class TestDeployOvercloud(fakes.TestDeployOvercloud): '_deploy_tripleo_heat_templates_tmpdir', autospec=True) def test_config_download_timeout( self, mock_deploy_tmpdir, - mock_overcloudrc, mock_get_undercloud_host_entry): + mock_overcloudrc, mock_get_undercloud_host_entry, mock_copy): fixture = deployment.DeploymentWorkflowFixture() self.useFixture(fixture) utils_fixture = deployment.UtilsOvercloudFixture() @@ -1469,6 +1495,7 @@ class TestDeployOvercloud(fakes.TestDeployOvercloud): self.cmd.take_action(parsed_args) fixture.mock_config_download.assert_called() self.assertEqual(240*60, fixture.mock_config_download.call_args[0][9]) + mock_copy.assert_called_once() def test_download_missing_files_from_plan(self): # Restore the real function so we don't accidentally call the mock diff --git a/tripleoclient/tests/v1/tripleo/test_tripleo_deploy.py b/tripleoclient/tests/v1/tripleo/test_tripleo_deploy.py index 1b7a5658f..e24eae476 100644 --- a/tripleoclient/tests/v1/tripleo/test_tripleo_deploy.py +++ b/tripleoclient/tests/v1/tripleo/test_tripleo_deploy.py @@ -887,6 +887,7 @@ class TestDeployUndercloud(TestPluginV1): ) @mock.patch('tripleoclient.utils.reset_cmdline') + @mock.patch('tripleoclient.utils.copy_clouds_yaml') @mock.patch('tripleoclient.v1.tripleo_deploy.Deploy.' '_download_stack_outputs') @mock.patch('tripleo_common.actions.ansible.' @@ -938,7 +939,7 @@ class TestDeployUndercloud(TestPluginV1): mock_tarball, mock_templates_dir, mock_open, mock_os, mock_user, mock_cc, mock_chmod, mock_ac, mock_outputs, - mock_cmdline): + mock_copy, mock_cmdline): mock_slink.side_effect = 'fake-cmd' parsed_args = self.check_parser(self.cmd, ['--local-ip', '127.0.0.1', @@ -975,7 +976,8 @@ class TestDeployUndercloud(TestPluginV1): @mock.patch('tripleoclient.utils.reset_cmdline') @mock.patch('tripleoclient.utils.ansible_symlink') - def test_take_action(self, mock_slink, mock_cmdline): + @mock.patch('tripleoclient.utils.copy_clouds_yaml') + def test_take_action(self, mock_copy, mock_slink, mock_cmdline): mock_slink.side_effect = 'fake-cmd' parsed_args = self.check_parser(self.cmd, ['--local-ip', '127.0.0.1', @@ -985,12 +987,15 @@ class TestDeployUndercloud(TestPluginV1): self.assertRaises(exceptions.DeploymentError, self.cmd.take_action, parsed_args) mock_cmdline.assert_called_once() + mock_copy.assert_called_once() @mock.patch('tripleoclient.utils.reset_cmdline') + @mock.patch('tripleoclient.utils.copy_clouds_yaml') @mock.patch('tripleoclient.v1.tripleo_deploy.Deploy._standalone_deploy', return_value=1) @mock.patch('tripleoclient.utils.ansible_symlink') - def test_take_action_failure(self, mock_slink, mock_deploy, mock_cmdline): + def test_take_action_failure(self, mock_slink, mock_deploy, mock_copy, + mock_cmdline): mock_slink.side_effect = 'fake-cmd' parsed_args = self.check_parser(self.cmd, ['--local-ip', '127.0.0.1', @@ -1001,6 +1006,7 @@ class TestDeployUndercloud(TestPluginV1): self.assertRaises(exceptions.DeploymentError, self.cmd.take_action, parsed_args) mock_cmdline.assert_called_once() + mock_copy.assert_called_once() @mock.patch('os.path.isfile', return_value=False) def test_set_stack_action_default_create(self, mock_isfile): diff --git a/tripleoclient/utils.py b/tripleoclient/utils.py index 2595151fd..7f8a2ebb8 100644 --- a/tripleoclient/utils.py +++ b/tripleoclient/utils.py @@ -2008,3 +2008,53 @@ def safe_write(path, data): 'created. Error: %(msg)') % {'file': path, 'msg': error.message} raise oscexc.CommandError(msg) + + +def copy_clouds_yaml(user): + """Copy clouds.yaml file from /etc/openstack to deployment user's home + + :param user: deployment user + """ + clouds_etc_file = '/etc/openstack/clouds.yaml' + clouds_home_dir = os.path.join('/home', user) + clouds_config_dir = os.path.join(clouds_home_dir, '.config/openstack') + clouds_config_file = os.path.join(clouds_config_dir, 'clouds.yaml') + clouds_user_id = os.stat(clouds_home_dir).st_uid + clouds_group_id = os.stat(clouds_home_dir).st_gid + + # If the file doesn't exist (e.g. on a minion node), we don't need to copy + # /etc/openstack/clouds.yaml to the user directory. + if not os.path.isfile(clouds_etc_file): + return + + if not os.path.exists(clouds_config_dir): + try: + os.makedirs(clouds_config_dir) + except OSError as e: + messages = _("Unable to create credentials directory: " + "{0}, {1}").format(clouds_config_dir, e) + raise OSError(messages) + + # Using 'sudo' here as for the overcloud the deployment command is run + # from regular deployment user. + cp_args = ['sudo', 'cp', clouds_etc_file, clouds_config_dir] + if run_command_and_log(LOG, cp_args) != 0: + msg = _('Error when user %(user)s tried to copy %(src)s to %(dest)s' + ' with sudo') % {'user': user, 'src': clouds_etc_file, + 'dest': clouds_config_dir} + LOG.error(msg) + raise exceptions.DeploymentError(msg) + chmod_args = ['sudo', 'chmod', '0600', clouds_config_file] + if run_command_and_log(LOG, chmod_args) != 0: + msg = _('Error when user %(user)s tried to chmod %(file)s file' + ' with sudo') % {'user': user, 'file': clouds_config_file} + LOG.error(msg) + raise exceptions.DeploymentError(msg) + chown_args = ['sudo', 'chown', '-R', + str(clouds_user_id) + ':' + str(clouds_group_id), + clouds_config_dir] + if run_command_and_log(LOG, chown_args) != 0: + msg = _('Error when user %(user)s tried to chown %(dir)s directory' + ' with sudo') % {'user': user, 'dir': clouds_config_dir} + LOG.error(msg) + raise exceptions.DeploymentError(msg) diff --git a/tripleoclient/v1/overcloud_deploy.py b/tripleoclient/v1/overcloud_deploy.py index ae2a942a3..6669ba2c2 100644 --- a/tripleoclient/v1/overcloud_deploy.py +++ b/tripleoclient/v1/overcloud_deploy.py @@ -20,6 +20,7 @@ import logging import os import os.path from prettytable import PrettyTable +from pwd import getpwuid import re import shutil import six @@ -34,14 +35,6 @@ from osc_lib.i18n import _ from swiftclient.exceptions import ClientException from tripleo_common import update -# FIXME(chkumar246): Once https://review.opendev.org/664568 gets merged -# and new version of tripleo-common gots released, It requires a version -# bump in requirements.txt. -try: - from tripleo_common.utils import clouds_yaml -except ImportError: - from tripleoclient.v1 import mock_clouds_yaml as clouds_yaml - from tripleoclient import command from tripleoclient import constants from tripleoclient import exceptions @@ -1012,18 +1005,9 @@ class DeployOvercloud(command.Command): self.clients, container=stack.stack_name, no_proxy=parsed_args.no_proxy) - # Create overcloud clouds.yaml - cloud_data = deployment.create_cloudsyaml( - self.clients, container=stack.stack_name) - cloud_yaml_dir = os.path.join(constants.CLOUD_HOME_DIR, - constants.CLOUDS_YAML_DIR) - cloud_user_id = os.stat(constants.CLOUD_HOME_DIR).st_uid - cloud_group_id = os.stat(constants.CLOUD_HOME_DIR).st_gid - clouds_yaml.create_clouds_yaml( - cloud=cloud_data, - cloud_yaml_dir=cloud_yaml_dir, - user_id=cloud_user_id, - group_id=cloud_group_id) + # Copy clouds.yaml to the cloud user directory + user = getpwuid(os.stat(constants.CLOUD_HOME_DIR).st_uid).pw_name + utils.copy_clouds_yaml(user) rcpath = utils.write_overcloudrc(stack.stack_name, overcloudrcs) utils.create_tempest_deployer_input() diff --git a/tripleoclient/v1/tripleo_deploy.py b/tripleoclient/v1/tripleo_deploy.py index 3e0986366..e48b293f5 100644 --- a/tripleoclient/v1/tripleo_deploy.py +++ b/tripleoclient/v1/tripleo_deploy.py @@ -1450,6 +1450,10 @@ class Deploy(command.Command): self.log.error(msg) raise exceptions.DeploymentError(msg) finally: + # Copy clouds.yaml from /etc/openstack so credentials can be + # read by the deployment user and not only root. + utils.copy_clouds_yaml(parsed_args.deployment_user) + # send erase sequence to reset the cmdline if paunch/ansible # mangled some escape sequences utils.reset_cmdline()