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:
I585abc3e6a3b9b8ae9183e0b5170df2e39301e17

It also sets the right permissions to only allow the deployment user to
read the file which container sensible credentials (cloud admin).

Note that I286c71edf120e11d8e51c792e6078dc60147f026 will be rebased so
the revert can properly happen with the right dependencies with
tripleo-common.

Depends-On: https://review.opendev.org/#/c/696390/
Change-Id: I49b95bf7a03e39db3c2cbb91b2167c7645b09136
This commit is contained in:
Emilien Macchi 2019-12-17 10:41:22 -05:00
parent 87c4c66f39
commit ccd4c7f59f
5 changed files with 107 additions and 39 deletions

View File

@ -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)
@ -306,8 +309,10 @@ 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('os.chdir')
@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')
@ -338,8 +343,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_chdir):
mock_get_undercloud_host_entry, mock_copy, mock_chdir):
fixture = deployment.DeploymentWorkflowFixture()
self.useFixture(fixture)
plane_management_fixture = deployment.PlanManagementFixture()
@ -445,8 +449,10 @@ 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('os.chdir')
@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')
@ -471,8 +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_chdir):
mock_get_undercloud_host_entry, mock_copy, mock_chdir):
fixture = deployment.DeploymentWorkflowFixture()
self.useFixture(fixture)
plane_management_fixture = deployment.PlanManagementFixture()
@ -533,7 +538,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 +559,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 +610,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 +629,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 +639,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 +693,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 +940,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 +951,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 +1026,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 +1085,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 +1111,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 +1196,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)
@ -1234,6 +1249,7 @@ class TestDeployOvercloud(fakes.TestDeployOvercloud):
self.assertFalse(mock_deploy_and_wait.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')
@ -1242,7 +1258,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()
@ -1265,6 +1281,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)
@ -1282,6 +1299,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')
@ -1291,7 +1309,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()
@ -1315,7 +1333,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')
@ -1325,7 +1345,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()
@ -1349,6 +1369,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,
@ -1393,6 +1414,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')
@ -1402,7 +1424,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()
@ -1424,7 +1446,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')
@ -1434,7 +1458,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()
@ -1453,6 +1477,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

View File

@ -872,6 +872,7 @@ class TestDeployUndercloud(TestPluginV1):
@mock.patch('os.path.exists')
@mock.patch('os.chdir')
@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.'
@ -919,8 +920,8 @@ class TestDeployUndercloud(TestPluginV1):
mock_cleanupdirs, mock_tarball,
mock_templates_dir, mock_open, mock_os,
mock_user, mock_cc, mock_chmod, mock_ac,
mock_outputs, mock_cmdline, mock_chdir,
mock_file_exists, mock_run,
mock_outputs, mock_copy, mock_cmdline,
mock_chdir, mock_file_exists, mock_run,
mock_run_prepare):
parsed_args = self.check_parser(self.cmd,
['--local-ip', '127.0.0.1',
@ -955,7 +956,8 @@ class TestDeployUndercloud(TestPluginV1):
self.assertEqual(mock_killheat.call_count, 2)
@mock.patch('tripleoclient.utils.reset_cmdline')
def test_take_action(self, mock_cmdline):
@mock.patch('tripleoclient.utils.copy_clouds_yaml')
def test_take_action(self, mock_copy, mock_cmdline):
parsed_args = self.check_parser(self.cmd,
['--local-ip', '127.0.0.1',
'--templates', '/tmp/thtroot',
@ -963,11 +965,13 @@ class TestDeployUndercloud(TestPluginV1):
'--output-dir', '/my'], [])
self.assertRaises(exceptions.DeploymentError,
self.cmd.take_action, parsed_args)
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)
def test_take_action_failure(self, mock_deploy, mock_cmdline):
def test_take_action_failure(self, mock_deploy, mock_copy, mock_cmdline):
parsed_args = self.check_parser(self.cmd,
['--local-ip', '127.0.0.1',
'--templates', '/tmp/thtroot',
@ -976,6 +980,7 @@ class TestDeployUndercloud(TestPluginV1):
'--standalone'], [])
self.assertRaises(exceptions.DeploymentError,
self.cmd.take_action, parsed_args)
mock_copy.assert_called_once()
@mock.patch('os.path.isfile', return_value=False)
def test_set_stack_action_default_create(self, mock_isfile):

View File

@ -2224,3 +2224,53 @@ def safe_write(path, data):
'The output file {file} can not be created. Error: {msg}'
).format(file=path, msg=str(error))
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)

View File

@ -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
@ -33,14 +34,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
@ -986,18 +979,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()

View File

@ -1435,6 +1435,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()