Align roles file to become a normalized path
Extend the tripleo client Command class to fetch heat roles data from a role file. That new class method is shared by many derived classes afterwards and used for containers images preparations, containerized overcloud, undercloud and standalone deployments. Apply normalization to the roles_file in plan management and preflight checks as well. Change-Id: I7b35e117b9d12f1e5a51e2ee0465244692d33e33 Signed-off-by: Bogdan Dobrelya <bdobreli@redhat.com>
This commit is contained in:
parent
a33b1499d7
commit
8f1caf7abf
|
@ -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.
|
|
@ -151,8 +151,9 @@ class StandaloneConfig(BaseConfig):
|
||||||
),
|
),
|
||||||
cfg.StrOpt('roles_file',
|
cfg.StrOpt('roles_file',
|
||||||
default=None,
|
default=None,
|
||||||
help=_('Roles file to override for heat. '
|
help=_('Roles file to override for heat. May be an '
|
||||||
'The file path is related to the templates path')
|
'absolute path or the path relative to the '
|
||||||
|
't-h-t templates directory used for deployment')
|
||||||
),
|
),
|
||||||
cfg.BoolOpt('heat_native',
|
cfg.BoolOpt('heat_native',
|
||||||
default=True,
|
default=True,
|
||||||
|
|
|
@ -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):
|
class TestOvercloudNameScenarios(TestWithScenarios):
|
||||||
scenarios = [
|
scenarios = [
|
||||||
('kernel_default',
|
('kernel_default',
|
||||||
|
|
|
@ -1338,8 +1338,7 @@ class TestDeployOvercloud(fakes.TestDeployOvercloud):
|
||||||
mock_create_tempest_deployer_input.assert_called_with()
|
mock_create_tempest_deployer_input.assert_called_with()
|
||||||
|
|
||||||
def test_get_default_role_counts_defaults(self):
|
def test_get_default_role_counts_defaults(self):
|
||||||
parsed_args = mock.Mock()
|
parsed_args = self.check_parser(self.cmd, [], [])
|
||||||
parsed_args.roles_file = None
|
|
||||||
defaults = {
|
defaults = {
|
||||||
'ControllerCount': 1,
|
'ControllerCount': 1,
|
||||||
'ComputeCount': 1,
|
'ComputeCount': 1,
|
||||||
|
@ -1351,11 +1350,8 @@ class TestDeployOvercloud(fakes.TestDeployOvercloud):
|
||||||
defaults,
|
defaults,
|
||||||
self.cmd._get_default_role_counts(parsed_args))
|
self.cmd._get_default_role_counts(parsed_args))
|
||||||
|
|
||||||
@mock.patch("yaml.safe_load")
|
@mock.patch("tripleoclient.utils.fetch_roles_file")
|
||||||
@mock.patch("six.moves.builtins.open")
|
def test_get_default_role_counts_custom_roles(self, mock_roles):
|
||||||
def test_get_default_role_counts_custom_roles(self, mock_open,
|
|
||||||
mock_safe_load):
|
|
||||||
parsed_args = mock.Mock()
|
|
||||||
roles_data = [
|
roles_data = [
|
||||||
{'name': 'ControllerApi', 'CountDefault': 3},
|
{'name': 'ControllerApi', 'CountDefault': 3},
|
||||||
{'name': 'ControllerPcmk', 'CountDefault': 3},
|
{'name': 'ControllerPcmk', 'CountDefault': 3},
|
||||||
|
@ -1363,7 +1359,7 @@ class TestDeployOvercloud(fakes.TestDeployOvercloud):
|
||||||
{'name': 'ObjectStorage', 'CountDefault': 0},
|
{'name': 'ObjectStorage', 'CountDefault': 0},
|
||||||
{'name': 'BlockStorage'}
|
{'name': 'BlockStorage'}
|
||||||
]
|
]
|
||||||
mock_safe_load.return_value = roles_data
|
mock_roles.return_value = roles_data
|
||||||
role_counts = {
|
role_counts = {
|
||||||
'ControllerApiCount': 3,
|
'ControllerApiCount': 3,
|
||||||
'ControllerPcmkCount': 3,
|
'ControllerPcmkCount': 3,
|
||||||
|
@ -1373,7 +1369,7 @@ class TestDeployOvercloud(fakes.TestDeployOvercloud):
|
||||||
}
|
}
|
||||||
self.assertEqual(
|
self.assertEqual(
|
||||||
role_counts,
|
role_counts,
|
||||||
self.cmd._get_default_role_counts(parsed_args))
|
self.cmd._get_default_role_counts(mock.Mock()))
|
||||||
|
|
||||||
@mock.patch(
|
@mock.patch(
|
||||||
'tripleoclient.workflows.plan_management.list_deployment_plans',
|
'tripleoclient.workflows.plan_management.list_deployment_plans',
|
||||||
|
|
|
@ -157,6 +157,7 @@ class TestContainerImagePrepare(TestPluginV1):
|
||||||
append_tag=None,
|
append_tag=None,
|
||||||
)
|
)
|
||||||
|
|
||||||
|
@mock.patch('tripleoclient.utils.fetch_roles_file')
|
||||||
@mock.patch('tripleo_common.image.kolla_builder.'
|
@mock.patch('tripleo_common.image.kolla_builder.'
|
||||||
'container_images_prepare_defaults', create=True)
|
'container_images_prepare_defaults', create=True)
|
||||||
@mock.patch('tripleo_common.image.kolla_builder.'
|
@mock.patch('tripleo_common.image.kolla_builder.'
|
||||||
|
@ -167,7 +168,7 @@ class TestContainerImagePrepare(TestPluginV1):
|
||||||
'build_service_filter')
|
'build_service_filter')
|
||||||
@mock.patch('requests.get')
|
@mock.patch('requests.get')
|
||||||
def test_container_image_prepare(self, mock_get, mock_bsf, pmef, mock_cip,
|
def test_container_image_prepare(self, mock_get, mock_bsf, pmef, mock_cip,
|
||||||
mock_cipd):
|
mock_cipd, mock_roles):
|
||||||
|
|
||||||
temp = tempfile.mkdtemp()
|
temp = tempfile.mkdtemp()
|
||||||
self.addCleanup(shutil.rmtree, temp)
|
self.addCleanup(shutil.rmtree, temp)
|
||||||
|
|
|
@ -22,7 +22,6 @@ import yaml
|
||||||
from heatclient import exc as hc_exc
|
from heatclient import exc as hc_exc
|
||||||
from tripleo_common.image import kolla_builder
|
from tripleo_common.image import kolla_builder
|
||||||
|
|
||||||
from tripleoclient import constants
|
|
||||||
from tripleoclient import exceptions
|
from tripleoclient import exceptions
|
||||||
from tripleoclient.tests.v1.test_plugin import TestPluginV1
|
from tripleoclient.tests.v1.test_plugin import TestPluginV1
|
||||||
|
|
||||||
|
@ -59,53 +58,26 @@ class TestDeployUndercloud(TestPluginV1):
|
||||||
self.orc.stacks.create = mock.MagicMock(
|
self.orc.stacks.create = mock.MagicMock(
|
||||||
return_value={'stack': {'id': 'foo'}})
|
return_value={'stack': {'id': 'foo'}})
|
||||||
|
|
||||||
@mock.patch('os.path.exists', return_value=True)
|
@mock.patch('os.path.exists')
|
||||||
def test_set_roles_file(self, mock_exists):
|
@mock.patch('tripleoclient.utils.fetch_roles_file')
|
||||||
self.cmd._set_roles_file('/foobar.yaml')
|
def test_get_primary_role_name(self, mock_data, mock_exists):
|
||||||
self.assertEqual(self.cmd.roles_file, '/foobar.yaml')
|
parsed_args = mock.Mock()
|
||||||
|
|
||||||
@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_data.return_value = [
|
mock_data.return_value = [
|
||||||
{'name': 'Bar'}, {'name': 'Foo', 'tags': ['primary']}
|
{'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):
|
@mock.patch('tripleoclient.utils.fetch_roles_file', return_value=None)
|
||||||
self.assertEqual(self.cmd._get_primary_role_name(), 'Controller')
|
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.'
|
@mock.patch('tripleoclient.utils.fetch_roles_file')
|
||||||
'_get_roles_data')
|
|
||||||
def test_get_primary_role_name_no_primary(self, mock_data):
|
def test_get_primary_role_name_no_primary(self, mock_data):
|
||||||
|
parsed_args = mock.Mock()
|
||||||
mock_data.return_value = [{'name': 'Bar'}, {'name': 'Foo'}]
|
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('os.path.exists', side_effect=[True, False])
|
||||||
@mock.patch('shutil.copytree')
|
@mock.patch('shutil.copytree')
|
||||||
|
@ -237,6 +209,7 @@ class TestDeployUndercloud(TestPluginV1):
|
||||||
mock.call(env_path='/tmp/thtroot42/notouch.yaml'),
|
mock.call(env_path='/tmp/thtroot42/notouch.yaml'),
|
||||||
mock.call(env_path='../outside.yaml')])
|
mock.call(env_path='../outside.yaml')])
|
||||||
|
|
||||||
|
@mock.patch('tripleoclient.utils.rel_or_abs_path')
|
||||||
@mock.patch('heatclient.common.template_utils.'
|
@mock.patch('heatclient.common.template_utils.'
|
||||||
'process_environment_and_files', return_value=({}, {}),
|
'process_environment_and_files', return_value=({}, {}),
|
||||||
autospec=True)
|
autospec=True)
|
||||||
|
@ -264,7 +237,8 @@ class TestDeployUndercloud(TestPluginV1):
|
||||||
mock_hc_templ_parse,
|
mock_hc_templ_parse,
|
||||||
mock_hc_env_parse,
|
mock_hc_env_parse,
|
||||||
mock_hc_get_templ_cont,
|
mock_hc_get_templ_cont,
|
||||||
mock_hc_process):
|
mock_hc_process,
|
||||||
|
mock_norm_path):
|
||||||
def hc_process(*args, **kwargs):
|
def hc_process(*args, **kwargs):
|
||||||
if 'abs.yaml' in kwargs['env_path']:
|
if 'abs.yaml' in kwargs['env_path']:
|
||||||
raise hc_exc.CommandError
|
raise hc_exc.CommandError
|
||||||
|
@ -408,7 +382,6 @@ class TestDeployUndercloud(TestPluginV1):
|
||||||
found_dropin = True
|
found_dropin = True
|
||||||
self.assertTrue(found_dropin)
|
self.assertTrue(found_dropin)
|
||||||
|
|
||||||
@mock.patch('os.path.isfile')
|
|
||||||
@mock.patch('heatclient.common.template_utils.'
|
@mock.patch('heatclient.common.template_utils.'
|
||||||
'process_environment_and_files', return_value=({}, {}),
|
'process_environment_and_files', return_value=({}, {}),
|
||||||
autospec=True)
|
autospec=True)
|
||||||
|
@ -424,10 +397,12 @@ class TestDeployUndercloud(TestPluginV1):
|
||||||
'_update_passwords_env', autospec=True)
|
'_update_passwords_env', autospec=True)
|
||||||
@mock.patch('tripleoclient.utils.'
|
@mock.patch('tripleoclient.utils.'
|
||||||
'run_command_and_log', autospec=True)
|
'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(
|
def test_setup_heat_environments_default_plan_env(
|
||||||
self, mock_run, mock_update_pass_env, mock_process_hiera,
|
self, mock_prim, mock_run, mock_update_pass_env,
|
||||||
mock_process_multiple_environments, mock_hc_get_templ_cont,
|
mock_process_hiera, mock_process_multiple_environments,
|
||||||
mock_hc_process, mock_os):
|
mock_hc_get_templ_cont, mock_hc_process):
|
||||||
|
|
||||||
tmpdir = self.useFixture(fixtures.TempDir()).path
|
tmpdir = self.useFixture(fixtures.TempDir()).path
|
||||||
tht_from = os.path.join(tmpdir, 'tht-from')
|
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,
|
self._setup_heat_environments(tmpdir, tht_from, plan_env_path,
|
||||||
mock_update_pass_env, mock_run)
|
mock_update_pass_env, mock_run)
|
||||||
|
|
||||||
@mock.patch('os.path.isfile')
|
|
||||||
@mock.patch('heatclient.common.template_utils.'
|
@mock.patch('heatclient.common.template_utils.'
|
||||||
'process_environment_and_files', return_value=({}, {}),
|
'process_environment_and_files', return_value=({}, {}),
|
||||||
autospec=True)
|
autospec=True)
|
||||||
|
@ -455,10 +429,12 @@ class TestDeployUndercloud(TestPluginV1):
|
||||||
'_update_passwords_env', autospec=True)
|
'_update_passwords_env', autospec=True)
|
||||||
@mock.patch('tripleoclient.utils.'
|
@mock.patch('tripleoclient.utils.'
|
||||||
'run_command_and_log', autospec=True)
|
'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(
|
def test_setup_heat_environments_non_default_plan_env(
|
||||||
self, mock_run, mock_update_pass_env, mock_process_hiera,
|
self, mock_prim, mock_run, mock_update_pass_env,
|
||||||
mock_process_multiple_environments, mock_hc_get_templ_cont,
|
mock_process_hiera, mock_process_multiple_environments,
|
||||||
mock_hc_process, mock_os):
|
mock_hc_get_templ_cont, mock_hc_process):
|
||||||
|
|
||||||
tmpdir = self.useFixture(fixtures.TempDir()).path
|
tmpdir = self.useFixture(fixtures.TempDir()).path
|
||||||
tht_from = os.path.join(tmpdir, 'tht-from')
|
tht_from = os.path.join(tmpdir, 'tht-from')
|
||||||
|
@ -502,6 +478,18 @@ class TestDeployUndercloud(TestPluginV1):
|
||||||
mock_update_pass_env.return_value = os.path.join(
|
mock_update_pass_env.return_value = os.path.join(
|
||||||
tht_render, 'passwords.yaml')
|
tht_render, 'passwords.yaml')
|
||||||
mock_run.return_value = 0
|
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
|
# logic handled in _standalone_deploy
|
||||||
self.cmd.output_dir = tht_to
|
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, 'foo.yaml'),
|
||||||
os.path.join(tht_render, 'outside.yaml')]
|
os.path.join(tht_render, 'outside.yaml')]
|
||||||
|
|
||||||
with mock.patch('os.path.isfile'):
|
with mock.patch('os.path.abspath', side_effect=abs_path_stub):
|
||||||
environment = self.cmd._setup_heat_environments(parsed_args)
|
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.'
|
@mock.patch('tripleoclient.v1.tripleo_deploy.Deploy.'
|
||||||
'_create_working_dirs', autospec=True)
|
'_create_working_dirs', autospec=True)
|
||||||
|
@ -578,16 +567,16 @@ class TestDeployUndercloud(TestPluginV1):
|
||||||
'ansible-playbook', '-i', '/tmp/inventory.yaml',
|
'ansible-playbook', '-i', '/tmp/inventory.yaml',
|
||||||
'deploy_steps_playbook.yaml'])
|
'deploy_steps_playbook.yaml'])
|
||||||
|
|
||||||
@mock.patch('tripleoclient.v1.tripleo_deploy.Deploy.'
|
@mock.patch('tripleoclient.utils.fetch_roles_file')
|
||||||
'_get_roles_data')
|
|
||||||
@mock.patch('tripleo_common.image.kolla_builder.'
|
@mock.patch('tripleo_common.image.kolla_builder.'
|
||||||
'container_images_prepare_multi')
|
'container_images_prepare_multi')
|
||||||
def test_prepare_container_images(self, mock_cipm, rolesdata_mock):
|
def test_prepare_container_images(self, mock_cipm, rolesdata_mock):
|
||||||
|
parsed_args = mock.Mock()
|
||||||
env = {'parameter_defaults': {}}
|
env = {'parameter_defaults': {}}
|
||||||
mock_cipm.return_value = {'FooImage': 'foo/bar:baz'}
|
mock_cipm.return_value = {'FooImage': 'foo/bar:baz'}
|
||||||
rolesdata_mock.return_value = [{'name': 'Compute'}]
|
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(
|
mock_cipm.assert_called_once_with(
|
||||||
env,
|
env,
|
||||||
|
|
|
@ -81,9 +81,11 @@ class TestPlanCreationWorkflows(utils.TestCommand):
|
||||||
|
|
||||||
self.workflow.executions.create.assert_not_called()
|
self.workflow.executions.create.assert_not_called()
|
||||||
|
|
||||||
|
@mock.patch('tripleoclient.utils.rel_or_abs_path')
|
||||||
@mock.patch('tripleoclient.workflows.plan_management.tarball',
|
@mock.patch('tripleoclient.workflows.plan_management.tarball',
|
||||||
autospec=True)
|
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": ""}')
|
output = mock.Mock(output='{"result": ""}')
|
||||||
self.workflow.action_executions.create.return_value = output
|
self.workflow.action_executions.create.return_value = output
|
||||||
self.websocket.wait_for_messages.return_value = self.message_success
|
self.websocket.wait_for_messages.return_value = self.message_success
|
||||||
|
@ -106,8 +108,8 @@ class TestPlanCreationWorkflows(utils.TestCommand):
|
||||||
workflow_input={'container': 'test-overcloud',
|
workflow_input={'container': 'test-overcloud',
|
||||||
'generate_passwords': True})
|
'generate_passwords': True})
|
||||||
|
|
||||||
mock_open_context.assert_has_calls(
|
self.assertIn(mock.call('the_roles_file.yaml', '/tht-root/'),
|
||||||
[mock.call('the_roles_file.yaml')])
|
mock_norm_path.call_args_list)
|
||||||
|
|
||||||
self.tripleoclient.object_store.put_object.assert_called_once_with(
|
self.tripleoclient.object_store.put_object.assert_called_once_with(
|
||||||
'test-overcloud', 'roles_data.yaml', mock_open_context())
|
'test-overcloud', 'roles_data.yaml', mock_open_context())
|
||||||
|
|
|
@ -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 error as url_error
|
||||||
from six.moves.urllib import request
|
from six.moves.urllib import request
|
||||||
|
|
||||||
|
from tripleoclient import constants
|
||||||
from tripleoclient import exceptions
|
from tripleoclient import exceptions
|
||||||
|
|
||||||
|
|
||||||
|
@ -1141,17 +1141,27 @@ def build_prepare_env(environment_files, environment_directories):
|
||||||
return env
|
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'''
|
'''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)
|
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))
|
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(
|
raise exceptions.DeploymentError(
|
||||||
"Can't find path %s %s" % (file_path, path))
|
"Can't find path %s %s" % (file_path, path))
|
||||||
return 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):
|
def load_config(osloconf, path):
|
||||||
'''Load oslo config from a file path. '''
|
'''Load oslo config from a file path. '''
|
||||||
log = logging.getLogger(__name__ + ".load_config")
|
log = logging.getLogger(__name__ + ".load_config")
|
||||||
|
|
|
@ -32,6 +32,7 @@ from tripleo_common.image import kolla_builder
|
||||||
|
|
||||||
from tripleoclient import command
|
from tripleoclient import command
|
||||||
from tripleoclient import constants
|
from tripleoclient import constants
|
||||||
|
from tripleoclient import exceptions
|
||||||
from tripleoclient import utils
|
from tripleoclient import utils
|
||||||
|
|
||||||
|
|
||||||
|
@ -198,9 +199,11 @@ class PrepareImageFiles(command.Command):
|
||||||
|
|
||||||
def get_parser(self, prog_name):
|
def get_parser(self, prog_name):
|
||||||
parser = super(PrepareImageFiles, self).get_parser(prog_name)
|
parser = super(PrepareImageFiles, self).get_parser(prog_name)
|
||||||
roles_file = os.path.join(constants.TRIPLEO_HEAT_TEMPLATES,
|
try:
|
||||||
constants.OVERCLOUD_ROLES_FILE)
|
roles_file = utils.rel_or_abs_path(
|
||||||
if not os.path.isfile(roles_file):
|
constants.OVERCLOUD_ROLES_FILE,
|
||||||
|
constants.TRIPLEO_HEAT_TEMPLATES)
|
||||||
|
except exceptions.DeploymentError:
|
||||||
roles_file = None
|
roles_file = None
|
||||||
defaults = kolla_builder.container_images_prepare_defaults()
|
defaults = kolla_builder.container_images_prepare_defaults()
|
||||||
|
|
||||||
|
@ -358,8 +361,11 @@ class PrepareImageFiles(command.Command):
|
||||||
parser.add_argument(
|
parser.add_argument(
|
||||||
'--roles-file', '-r', dest='roles_file',
|
'--roles-file', '-r', dest='roles_file',
|
||||||
default=roles_file,
|
default=roles_file,
|
||||||
help=_('Roles file, overrides the default %s'
|
help=_(
|
||||||
) % constants.OVERCLOUD_ROLES_FILE
|
'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(
|
parser.add_argument(
|
||||||
'--modify-role',
|
'--modify-role',
|
||||||
|
@ -390,10 +396,7 @@ class PrepareImageFiles(command.Command):
|
||||||
def take_action(self, parsed_args):
|
def take_action(self, parsed_args):
|
||||||
self.log.debug("take_action(%s)" % parsed_args)
|
self.log.debug("take_action(%s)" % parsed_args)
|
||||||
|
|
||||||
if parsed_args.roles_file:
|
roles_data = utils.fetch_roles_file(parsed_args.roles_file) or set()
|
||||||
roles_data = yaml.safe_load(open(parsed_args.roles_file).read())
|
|
||||||
else:
|
|
||||||
roles_data = set()
|
|
||||||
|
|
||||||
env = utils.build_prepare_env(
|
env = utils.build_prepare_env(
|
||||||
parsed_args.environment_files,
|
parsed_args.environment_files,
|
||||||
|
@ -546,9 +549,11 @@ class TripleOImagePrepare(command.Command):
|
||||||
|
|
||||||
def get_parser(self, prog_name):
|
def get_parser(self, prog_name):
|
||||||
parser = super(TripleOImagePrepare, self).get_parser(prog_name)
|
parser = super(TripleOImagePrepare, self).get_parser(prog_name)
|
||||||
roles_file = os.path.join(constants.TRIPLEO_HEAT_TEMPLATES,
|
try:
|
||||||
constants.OVERCLOUD_ROLES_FILE)
|
roles_file = utils.rel_or_abs_path(
|
||||||
if not os.path.isfile(roles_file):
|
constants.OVERCLOUD_ROLES_FILE,
|
||||||
|
constants.TRIPLEO_HEAT_TEMPLATES)
|
||||||
|
except exceptions.DeploymentError:
|
||||||
roles_file = None
|
roles_file = None
|
||||||
parser.add_argument(
|
parser.add_argument(
|
||||||
'--environment-file', '-e', metavar='<file path>',
|
'--environment-file', '-e', metavar='<file path>',
|
||||||
|
@ -572,8 +577,11 @@ class TripleOImagePrepare(command.Command):
|
||||||
parser.add_argument(
|
parser.add_argument(
|
||||||
'--roles-file', '-r', dest='roles_file',
|
'--roles-file', '-r', dest='roles_file',
|
||||||
default=roles_file,
|
default=roles_file,
|
||||||
help=_('Roles file to filter images by, overrides the default %s'
|
help=_(
|
||||||
) % constants.OVERCLOUD_ROLES_FILE
|
'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(
|
parser.add_argument(
|
||||||
"--output-env-file",
|
"--output-env-file",
|
||||||
|
@ -610,10 +618,7 @@ class TripleOImagePrepare(command.Command):
|
||||||
raise oscexc.CommandError('--cleanup must be one of: %s' %
|
raise oscexc.CommandError('--cleanup must be one of: %s' %
|
||||||
', '.join(image_uploader.CLEANUP))
|
', '.join(image_uploader.CLEANUP))
|
||||||
|
|
||||||
if parsed_args.roles_file:
|
roles_data = utils.fetch_roles_file(parsed_args.roles_file)
|
||||||
roles_data = yaml.safe_load(open(parsed_args.roles_file).read())
|
|
||||||
else:
|
|
||||||
roles_data = None
|
|
||||||
|
|
||||||
env = utils.build_prepare_env(
|
env = utils.build_prepare_env(
|
||||||
parsed_args.environment_files,
|
parsed_args.environment_files,
|
||||||
|
|
|
@ -550,7 +550,8 @@ class DeployOvercloud(command.Command):
|
||||||
def _get_default_role_counts(self, parsed_args):
|
def _get_default_role_counts(self, parsed_args):
|
||||||
|
|
||||||
if parsed_args.roles_file:
|
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:
|
else:
|
||||||
# Assume default role counts
|
# Assume default role counts
|
||||||
return {
|
return {
|
||||||
|
@ -634,7 +635,8 @@ class DeployOvercloud(command.Command):
|
||||||
parser.add_argument(
|
parser.add_argument(
|
||||||
'--roles-file', '-r', dest='roles_file',
|
'--roles-file', '-r', dest='roles_file',
|
||||||
help=_('Roles file, overrides the default %s in the --templates '
|
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(
|
parser.add_argument(
|
||||||
'--networks-file', '-n', dest='networks_file',
|
'--networks-file', '-n', dest='networks_file',
|
||||||
|
|
|
@ -85,8 +85,6 @@ class Deploy(command.Command):
|
||||||
tht_render = None
|
tht_render = None
|
||||||
output_dir = None
|
output_dir = None
|
||||||
tmp_ansible_dir = None
|
tmp_ansible_dir = None
|
||||||
roles_file = None
|
|
||||||
roles_data = None
|
|
||||||
stack_update_mark = None
|
stack_update_mark = None
|
||||||
stack_action = 'CREATE'
|
stack_action = 'CREATE'
|
||||||
deployment_user = None
|
deployment_user = None
|
||||||
|
@ -123,22 +121,6 @@ class Deploy(command.Command):
|
||||||
else:
|
else:
|
||||||
os.chmod(file_name, mode)
|
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):
|
def _set_stack_action(self, parsed_args):
|
||||||
"""Set the stack action for deployment"""
|
"""Set the stack action for deployment"""
|
||||||
# Prepare the heat stack action we want to start deployment with
|
# 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(
|
_('The heat stack {0} action is {1}').format(
|
||||||
parsed_args.stack, self.stack_action))
|
parsed_args.stack, self.stack_action))
|
||||||
|
|
||||||
def _get_roles_data(self):
|
def _get_primary_role_name(self, parsed_args):
|
||||||
"""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):
|
|
||||||
"""Return the primary role name"""
|
"""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:
|
if not roles_data:
|
||||||
# TODO(aschultz): should this be Undercloud instead?
|
# TODO(aschultz): should this be Undercloud instead?
|
||||||
return 'Controller'
|
return 'Controller'
|
||||||
|
@ -564,11 +533,11 @@ class Deploy(command.Command):
|
||||||
parsed_args.templates, self.tht_render, env_files)
|
parsed_args.templates, self.tht_render, env_files)
|
||||||
|
|
||||||
# generate jinja templates by its work dir location
|
# 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,
|
process_templates = os.path.join(parsed_args.templates,
|
||||||
'tools/process-templates.py')
|
'tools/process-templates.py')
|
||||||
args = ['python', process_templates, '--roles-data',
|
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:
|
if utils.run_command_and_log(self.log, args, cwd=self.tht_render) != 0:
|
||||||
# TODO(aschultz): improve error messaging
|
# TODO(aschultz): improve error messaging
|
||||||
msg = _("Problems generating templates.")
|
msg = _("Problems generating templates.")
|
||||||
|
@ -581,10 +550,10 @@ class Deploy(command.Command):
|
||||||
|
|
||||||
# Include any environments from the plan-environment.yaml
|
# Include any environments from the plan-environment.yaml
|
||||||
plan_env_path = utils.rel_or_abs_path(
|
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:
|
with open(plan_env_path, 'r') as f:
|
||||||
plan_env_data = yaml.safe_load(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', {})]
|
for e in plan_env_data.get('environments', {})]
|
||||||
|
|
||||||
# this will allow the user to overwrite passwords with custom envs
|
# 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(
|
tmp_env.update(self._generate_portmap_parameters(
|
||||||
ip, ip_nw, c_ip, p_ip,
|
ip, ip_nw, c_ip, p_ip,
|
||||||
stack_name=parsed_args.stack,
|
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:
|
with open(maps_file, 'w') as env_file:
|
||||||
yaml.safe_dump({'parameter_defaults': tmp_env}, env_file,
|
yaml.safe_dump({'parameter_defaults': tmp_env}, env_file,
|
||||||
|
@ -653,8 +622,9 @@ class Deploy(command.Command):
|
||||||
|
|
||||||
return environments + user_environments
|
return environments + user_environments
|
||||||
|
|
||||||
def _prepare_container_images(self, env):
|
def _prepare_container_images(self, env, parsed_args):
|
||||||
roles_data = self._get_roles_data()
|
roles_data = utils.fetch_roles_file(parsed_args.roles_file,
|
||||||
|
parsed_args.templates)
|
||||||
image_params = kolla_builder.container_images_prepare_multi(
|
image_params = kolla_builder.container_images_prepare_multi(
|
||||||
env, roles_data, dry_run=True)
|
env, roles_data, dry_run=True)
|
||||||
|
|
||||||
|
@ -678,7 +648,7 @@ class Deploy(command.Command):
|
||||||
environments, self.tht_render, parsed_args.templates,
|
environments, self.tht_render, parsed_args.templates,
|
||||||
cleanup=parsed_args.cleanup)
|
cleanup=parsed_args.cleanup)
|
||||||
|
|
||||||
self._prepare_container_images(env)
|
self._prepare_container_images(env, parsed_args)
|
||||||
|
|
||||||
self.log.debug(_("Getting template contents"))
|
self.log.debug(_("Getting template contents"))
|
||||||
template_path = os.path.join(self.tht_render, 'overcloud.yaml')
|
template_path = os.path.join(self.tht_render, 'overcloud.yaml')
|
||||||
|
@ -805,8 +775,11 @@ class Deploy(command.Command):
|
||||||
)
|
)
|
||||||
parser.add_argument(
|
parser.add_argument(
|
||||||
'--roles-file', '-r', dest='roles_file',
|
'--roles-file', '-r', dest='roles_file',
|
||||||
help=_('Roles file, overrides the default %s in the --templates '
|
help=_(
|
||||||
'directory') % constants.UNDERCLOUD_ROLES_FILE,
|
'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
|
default=constants.UNDERCLOUD_ROLES_FILE
|
||||||
)
|
)
|
||||||
parser.add_argument(
|
parser.add_argument(
|
||||||
|
@ -1038,10 +1011,6 @@ class Deploy(command.Command):
|
||||||
# Set default plan if not specified by user
|
# Set default plan if not specified by user
|
||||||
self._set_default_plan()
|
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
|
rc = 1
|
||||||
try:
|
try:
|
||||||
# NOTE(bogdando): Look for the unique virtual update mark matching
|
# NOTE(bogdando): Look for the unique virtual update mark matching
|
||||||
|
|
|
@ -382,7 +382,9 @@ def _validate_passwords_file():
|
||||||
def _validate_env_files_paths():
|
def _validate_env_files_paths():
|
||||||
"""Verify the non-matching templates path vs env files paths"""
|
"""Verify the non-matching templates path vs env files paths"""
|
||||||
tht_path = CONF.get('templates') or constants.TRIPLEO_HEAT_TEMPLATES
|
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
|
# get the list of jinja templates normally rendered for UC installations
|
||||||
LOG.debug(_("Using roles file {0} from {1}").format(roles_file, tht_path))
|
LOG.debug(_("Using roles file {0} from {1}").format(roles_file, tht_path))
|
||||||
|
|
|
@ -20,6 +20,7 @@ from tripleo_common.utils import tarball
|
||||||
|
|
||||||
from tripleoclient import constants
|
from tripleoclient import constants
|
||||||
from tripleoclient import exceptions
|
from tripleoclient import exceptions
|
||||||
|
from tripleoclient import utils
|
||||||
from tripleoclient.workflows import base
|
from tripleoclient.workflows import base
|
||||||
|
|
||||||
LOG = logging.getLogger(__name__)
|
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
|
# Optional override of the roles_data.yaml file
|
||||||
if roles_file:
|
if roles_file:
|
||||||
_upload_file(swift_client, container_name,
|
_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
|
# Optional override of the network_data.yaml file
|
||||||
if networks_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))
|
"Unable to create plan. {}".format(result))
|
||||||
|
|
||||||
print("Creating plan from template files in: {}".format(tht_root))
|
print("Creating plan from template files in: {}".format(tht_root))
|
||||||
_upload_templates(swift_client, name, tht_root, roles_file, plan_env_file,
|
_upload_templates(swift_client, name, tht_root,
|
||||||
networks_file)
|
utils.rel_or_abs_path(roles_file, tht_root),
|
||||||
|
plan_env_file, networks_file)
|
||||||
|
|
||||||
try:
|
try:
|
||||||
create_deployment_plan(clients, container=name,
|
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
|
swift_client = clients.tripleoclient.object_store
|
||||||
passwords = None
|
passwords = None
|
||||||
keep_file_contents = {}
|
keep_file_contents = {}
|
||||||
|
roles_file = utils.rel_or_abs_path(roles_file, tht_root)
|
||||||
|
|
||||||
if keep_env:
|
if keep_env:
|
||||||
# Dict items are (remote_name, local_name). local_name may be
|
# Dict items are (remote_name, local_name). local_name may be
|
||||||
|
|
Loading…
Reference in New Issue