From 1b877698ce76f858d6d9d0dc629261bf21aa89f9 Mon Sep 17 00:00:00 2001 From: James Slagle Date: Fri, 31 Mar 2017 10:24:32 -0400 Subject: [PATCH] Add --skip-deploy-identifier Add a new cli argument, --skip-deploy-identifier. The argument will eventually be passed to the tripleo-common DeployStackAction action to disable setting a unique value for the DeployIdentifier parameter, which means the SoftwareDeployment resources in the templates will only be triggered if there is an actual change to their configuration. This argument can be used to avoid always applying configuration, such as during node scale out. Removes the setting of DeployIdentifier completely from tripleoclient, as that is now handled by the tripleo-common action. Depends-On: Idb901a841846fec33d189b3c95f669b0380498ce Change-Id: I294eabe84e070367981534030b5927062f00c628 Closes-Bug: #1688387 (cherry picked from commit f3acb69ffce3c630df5b3051779a7af21b96af47) --- ...ip-deploy-identifier-f7eb0d3ff5126f62.yaml | 10 ++ .../overcloud_deploy/test_overcloud_deploy.py | 113 ++++++++++++++++-- tripleoclient/tests/v1/test_overcloud_plan.py | 3 +- tripleoclient/v1/overcloud_deploy.py | 34 ++++-- tripleoclient/workflows/deployment.py | 4 +- 5 files changed, 144 insertions(+), 20 deletions(-) create mode 100644 releasenotes/notes/skip-deploy-identifier-f7eb0d3ff5126f62.yaml diff --git a/releasenotes/notes/skip-deploy-identifier-f7eb0d3ff5126f62.yaml b/releasenotes/notes/skip-deploy-identifier-f7eb0d3ff5126f62.yaml new file mode 100644 index 000000000..bd34ae4db --- /dev/null +++ b/releasenotes/notes/skip-deploy-identifier-f7eb0d3ff5126f62.yaml @@ -0,0 +1,10 @@ +--- +features: + - Add a new cli argument, --skip-deploy-identifier. The argument will disable + setting a unique value for the DeployIdentifier parameter, which means the + SoftwareDeployment resources in the templates will only be triggered if + there is an actual change to their configuration. This argument can be used + to avoid always applying configuration, such as during node scale out. + This option should be used with Caution, and only if there is confidence + that the software configuration does not need to be run, such as when + scaling out certain roles. diff --git a/tripleoclient/tests/v1/overcloud_deploy/test_overcloud_deploy.py b/tripleoclient/tests/v1/overcloud_deploy/test_overcloud_deploy.py index b4263af27..3aa10cc16 100644 --- a/tripleoclient/tests/v1/overcloud_deploy/test_overcloud_deploy.py +++ b/tripleoclient/tests/v1/overcloud_deploy/test_overcloud_deploy.py @@ -299,6 +299,101 @@ class TestDeployOvercloud(fakes.TestDeployOvercloud): mock_tarball.tarball_extract_to_swift_container.assert_called_with( clients.tripleoclient.object_store, mock.ANY, 'overcloud') + @mock.patch('tripleoclient.utils.get_overcloud_endpoint', autospec=True) + @mock.patch('tripleoclient.v1.overcloud_deploy.DeployOvercloud.' + '_deploy_postconfig', autospec=True) + @mock.patch('tripleoclient.workflows.plan_management.tarball', + autospec=True) + @mock.patch('tripleo_common.update.add_breakpoints_cleanup_into_env', + autospec=True) + @mock.patch('tripleoclient.v1.overcloud_deploy.DeployOvercloud.' + '_validate_args') + @mock.patch('tripleoclient.v1.overcloud_deploy.DeployOvercloud.' + '_create_parameters_env', autospec=True) + @mock.patch('tripleoclient.utils.create_tempest_deployer_input', + autospec=True) + @mock.patch('tripleoclient.utils.write_overcloudrc', autospec=True) + @mock.patch('tripleoclient.utils.remove_known_hosts', autospec=True) + @mock.patch('tripleoclient.utils.wait_for_stack_ready', + autospec=True) + @mock.patch('heatclient.common.template_utils.get_template_contents', + autospec=True) + @mock.patch('tripleoclient.utils.check_hypervisor_stats', + autospec=True) + @mock.patch('uuid.uuid1', autospec=True) + @mock.patch('time.time', autospec=True) + @mock.patch('shutil.rmtree', autospec=True) + @mock.patch('shutil.copytree', autospec=True) + @mock.patch('tempfile.mkdtemp', autospec=True) + def test_tht_deploy_skip_deploy_identifier( + self, mock_tmpdir, mock_copy, mock_rm, mock_time, + mock_uuid1, + mock_check_hypervisor_stats, + mock_get_template_contents, + wait_for_stack_ready_mock, + mock_remove_known_hosts, + mock_write_overcloudrc, + mock_create_tempest_deployer_input, + mock_create_parameters_env, mock_validate_args, + mock_breakpoints_cleanup, mock_tarball, + mock_postconfig, mock_get_overcloud_endpoint): + + arglist = ['--templates', '--skip-deploy-identifier'] + verifylist = [ + ('templates', '/usr/share/openstack-tripleo-heat-templates/'), + ('skip_deploy_identifier', True) + ] + + mock_tmpdir.return_value = "/tmp/tht" + mock_uuid1.return_value = "uuid" + mock_time.return_value = 123456789 + + clients = self.app.client_manager + orchestration_client = clients.orchestration + mock_stack = fakes.create_tht_stack() + orchestration_client.stacks.get.side_effect = [None, mock.Mock()] + workflow_client = clients.workflow_engine + workflow_client.environments.get.return_value = mock.MagicMock( + variables={'environments': []}) + workflow_client.action_executions.create.return_value = mock.MagicMock( + output='{"result":[]}') + + def _orch_clt_create(**kwargs): + orchestration_client.stacks.get.return_value = mock_stack + + orchestration_client.stacks.create.side_effect = _orch_clt_create + + mock_check_hypervisor_stats.return_value = { + 'count': 4, + 'memory_mb': 4096, + 'vcpus': 8, + } + clients.network.api.find_attr.return_value = { + "id": "network id" + } + mock_get_template_contents.return_value = [{}, "template"] + wait_for_stack_ready_mock.return_value = True + + parsed_args = self.check_parser(self.cmd, arglist, verifylist) + + baremetal = clients.baremetal + baremetal.node.list.return_value = range(10) + + testcase = self + + def _custom_create_params_env(self, parameters): + testcase.assertTrue('DeployIdentifier' not in parameters) + parameter_defaults = {"parameter_defaults": parameters} + return parameter_defaults + + mock_create_parameters_env.side_effect = _custom_create_params_env + + self.cmd.take_action(parsed_args) + execution_calls = workflow_client.executions.create.call_args_list + 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.patch('tripleoclient.workflows.plan_management.tarball', autospec=True) @mock.patch("heatclient.common.event_utils.get_events", autospec=True) @@ -464,7 +559,8 @@ class TestDeployOvercloud(fakes.TestDeployOvercloud): def _fake_heat_deploy(self, stack, stack_name, template_path, parameters, environments, timeout, tht_root, - env, update_plan_only, run_validations): + env, update_plan_only, run_validations, + skip_deploy_identifier): assertEqual( {'parameter_defaults': {}, 'resource_registry': {'Test': u'OS::Heat::None'}}, env) @@ -520,7 +616,9 @@ class TestDeployOvercloud(fakes.TestDeployOvercloud): def _fake_heat_deploy(self, stack, stack_name, template_path, parameters, environments, timeout, tht_root, - env, update_plan_only, run_validations): + env, update_plan_only, run_validations, + skip_deploy_identifier): + # Should be no breakpoint cleanup because utils.get_stack = None assertEqual( {'parameter_defaults': {}, 'resource_registry': {'Test': u'OS::Heat::None'}}, env) @@ -760,13 +858,13 @@ class TestDeployOvercloud(fakes.TestDeployOvercloud): self, mock_heat_deploy_func): result = self.cmd._try_overcloud_deploy_with_compat_yaml( '/fake/path', {}, 'overcloud', {}, ['~/overcloud-env.json'], 1, - {}, False, True) + {}, False, True, False) # If it returns None it succeeded self.assertIsNone(result) mock_heat_deploy_func.assert_called_once_with( self.cmd, {}, 'overcloud', '/fake/path/' + constants.OVERCLOUD_YAML_NAME, {}, - ['~/overcloud-env.json'], 1, '/fake/path', {}, False, True) + ['~/overcloud-env.json'], 1, '/fake/path', {}, False, True, False) @mock.patch('tripleoclient.v1.overcloud_deploy.DeployOvercloud.' '_heat_deploy', autospec=True) @@ -776,7 +874,8 @@ class TestDeployOvercloud(fakes.TestDeployOvercloud): self.assertRaises(ValueError, self.cmd._try_overcloud_deploy_with_compat_yaml, '/fake/path', mock.ANY, mock.ANY, mock.ANY, - mock.ANY, mock.ANY, mock.ANY, mock.ANY, mock.ANY) + mock.ANY, mock.ANY, mock.ANY, mock.ANY, mock.ANY, + mock.ANY) @mock.patch('tripleoclient.v1.overcloud_deploy.DeployOvercloud.' '_heat_deploy', autospec=True) @@ -787,7 +886,7 @@ class TestDeployOvercloud(fakes.TestDeployOvercloud): try: self.cmd._try_overcloud_deploy_with_compat_yaml( '/fake/path', mock.ANY, mock.ANY, mock.ANY, - mock.ANY, mock.ANY, mock.ANY, mock.ANY, mock.ANY) + mock.ANY, mock.ANY, mock.ANY, mock.ANY, mock.ANY, mock.ANY) except ValueError as value_error: self.assertIn('/fake/path', str(value_error)) @@ -1256,7 +1355,7 @@ class TestDeployOvercloud(fakes.TestDeployOvercloud): mock_get_template_contents.return_value = [{}, {}] self.cmd._heat_deploy(mock_stack, 'mock_stack', '/tmp', {}, - {}, 1, '/tmp', {}, True, False) + {}, 1, '/tmp', {}, True, False, False) self.assertFalse(mock_deploy_and_wait.called) diff --git a/tripleoclient/tests/v1/test_overcloud_plan.py b/tripleoclient/tests/v1/test_overcloud_plan.py index 7da58aea4..38f7dbefb 100644 --- a/tripleoclient/tests/v1/test_overcloud_plan.py +++ b/tripleoclient/tests/v1/test_overcloud_plan.py @@ -315,6 +315,7 @@ class TestOvercloudDeployPlan(utils.TestCommand): workflow_input={ 'container': 'overcast', 'run_validations': True, - 'queue_name': 'UUID4' + 'queue_name': 'UUID4', + 'skip_deploy_identifier': False } ) diff --git a/tripleoclient/v1/overcloud_deploy.py b/tripleoclient/v1/overcloud_deploy.py index 9ecca9f59..da964a60e 100644 --- a/tripleoclient/v1/overcloud_deploy.py +++ b/tripleoclient/v1/overcloud_deploy.py @@ -23,7 +23,6 @@ import re import shutil import six import tempfile -import time import uuid import yaml @@ -68,8 +67,6 @@ class DeployOvercloud(command.Command): stack_is_new = stack is None - timestamp = int(time.time()) - parameters['DeployIdentifier'] = timestamp parameters['UpdateIdentifier'] = '' parameters['StackAction'] = 'CREATE' if stack_is_new else 'UPDATE' @@ -199,7 +196,7 @@ class DeployOvercloud(command.Command): def _heat_deploy(self, stack, stack_name, template_path, parameters, env_files, timeout, tht_root, env, update_plan_only, - run_validations): + run_validations, skip_deploy_identifier): """Verify the Baremetal nodes are available and do a stack update""" clients = self.app.client_manager @@ -246,10 +243,12 @@ class DeployOvercloud(command.Command): workflow_client) if not update_plan_only: - deployment.deploy_and_wait(self.log, clients, stack, stack_name, - self.app_args.verbose_level, - timeout=timeout, - run_validations=run_validations) + deployment.deploy_and_wait( + self.log, clients, stack, stack_name, + self.app_args.verbose_level, + timeout=timeout, + run_validations=run_validations, + skip_deploy_identifier=skip_deploy_identifier) def _load_environment_directories(self, directories): if os.environ.get('TRIPLEO_ENVIRONMENT_DIRECTORY'): @@ -456,19 +455,20 @@ class DeployOvercloud(command.Command): self._try_overcloud_deploy_with_compat_yaml( tht_root, stack, parsed_args.stack, parameters, env_files, parsed_args.timeout, env, parsed_args.update_plan_only, - parsed_args.run_validations) + parsed_args.run_validations, parsed_args.skip_deploy_identifier) def _try_overcloud_deploy_with_compat_yaml(self, tht_root, stack, stack_name, parameters, env_files, timeout, env, update_plan_only, - run_validations): + run_validations, + skip_deploy_identifier): overcloud_yaml = os.path.join(tht_root, constants.OVERCLOUD_YAML_NAME) try: self._heat_deploy(stack, stack_name, overcloud_yaml, parameters, env_files, timeout, tht_root, env, update_plan_only, - run_validations) + run_validations, skip_deploy_identifier) except ClientException as e: messages = 'Failed to deploy: %s' % str(e) raise ValueError(messages) @@ -1014,6 +1014,18 @@ class DeployOvercloud(command.Command): default=False, help=_('Force the overcloud post-deployment configuration.') ) + parser.add_argument( + '--skip-deploy-identifier', + action='store_true', + default=False, + help=_('Skip generation of a unique identifier for the ' + 'DeployIdentifier parameter. The software configuration ' + 'deployment steps will only be triggered if there is an ' + 'actual change to the configuration. This option should ' + 'be used with Caution, and only if there is confidence ' + 'that the software configuration does not need to be ' + 'run, such as when scaling out certain roles.') + ) reg_group = parser.add_argument_group('Registration Parameters') reg_group.add_argument( '--rhel-reg', diff --git a/tripleoclient/workflows/deployment.py b/tripleoclient/workflows/deployment.py index 67053252b..ab5db03e3 100644 --- a/tripleoclient/workflows/deployment.py +++ b/tripleoclient/workflows/deployment.py @@ -44,12 +44,14 @@ def deploy(clients, **workflow_input): def deploy_and_wait(log, clients, stack, plan_name, verbose_level, - timeout=None, run_validations=False): + timeout=None, run_validations=False, + skip_deploy_identifier=False): """Start the deploy and wait for it to finish""" workflow_input = { "container": plan_name, "run_validations": run_validations, + "skip_deploy_identifier": skip_deploy_identifier, "queue_name": str(uuid.uuid4()), }