From f3acb69ffce3c630df5b3051779a7af21b96af47 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 --- ...ip-deploy-identifier-f7eb0d3ff5126f62.yaml | 10 ++ .../overcloud_deploy/test_overcloud_deploy.py | 112 ++++++++++++++++-- tripleoclient/tests/v1/test_overcloud_plan.py | 3 +- tripleoclient/v1/overcloud_deploy.py | 34 ++++-- tripleoclient/workflows/deployment.py | 4 +- 5 files changed, 143 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 b85d02282..3f29dacd1 100644 --- a/tripleoclient/tests/v1/overcloud_deploy/test_overcloud_deploy.py +++ b/tripleoclient/tests/v1/overcloud_deploy/test_overcloud_deploy.py @@ -285,6 +285,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) @@ -445,7 +540,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': { @@ -506,7 +602,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): # Should be no breakpoint cleanup because utils.get_stack = None assertEqual( {'parameter_defaults': {}, @@ -741,13 +838,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) @@ -757,7 +854,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) @@ -768,7 +866,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)) @@ -1120,7 +1218,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 d45768e1c..a9a93c22f 100644 --- a/tripleoclient/tests/v1/test_overcloud_plan.py +++ b/tripleoclient/tests/v1/test_overcloud_plan.py @@ -356,6 +356,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 00e296781..cae9dde20 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""" self.log.debug("Getting template contents from plan %s" % stack_name) @@ -227,10 +224,12 @@ class DeployOvercloud(command.Command): stack_name, env, moved_files, tht_root) if not update_plan_only: - deployment.deploy_and_wait(self.log, self.clients, stack, - stack_name, self.app_args.verbose_level, - timeout=timeout, - run_validations=run_validations) + deployment.deploy_and_wait( + self.log, self.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'): @@ -439,19 +438,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) @@ -743,6 +743,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 53c0f2963..7ec4be719 100644 --- a/tripleoclient/workflows/deployment.py +++ b/tripleoclient/workflows/deployment.py @@ -46,12 +46,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()), }