From 199e4ac322f815c774895b0b20479264dec8853f Mon Sep 17 00:00:00 2001 From: rabi Date: Mon, 10 Jul 2017 16:40:12 +0530 Subject: [PATCH] Implement handle__cancel for SoftwareDeployment This implements handle__cancel for SoftwareDeployment to update the deployment to failed, when the resource fails due to scheduler timeout or some other external reason. Change-Id: Ibaa551d35e96f34cf950811a5b5a05e1cda6c364 Closes-Bug: #1585815 --- .../openstack/heat/software_deployment.py | 32 +++++++++++++++++++ .../heat/test_software_deployment.py | 23 +++++++++++++ heat_integrationtests/common/test.py | 6 ++-- .../functional/test_software_config.py | 30 +++++++++++++++-- 4 files changed, 87 insertions(+), 4 deletions(-) diff --git a/heat/engine/resources/openstack/heat/software_deployment.py b/heat/engine/resources/openstack/heat/software_deployment.py index a0294fe4ee..56d6c72d10 100644 --- a/heat/engine/resources/openstack/heat/software_deployment.py +++ b/heat/engine/resources/openstack/heat/software_deployment.py @@ -507,6 +507,38 @@ class SoftwareDeployment(signal_responder.SignalResponder): self.context, self.resource_id, details, timeutils.utcnow().isoformat()) + def _handle_cancel(self): + if self.resource_id is None: + return + + sd = self.rpc_client().show_software_deployment( + self.context, self.resource_id) + + if sd is None: + return + + status = sd[rpc_api.SOFTWARE_DEPLOYMENT_STATUS] + if status == SoftwareDeployment.IN_PROGRESS: + self.rpc_client().update_software_deployment( + self.context, self.resource_id, + status=SoftwareDeployment.FAILED, + status_reason=_('Deployment cancelled.')) + + def handle_create_cancel(self, cookie): + self._handle_cancel() + + def handle_update_cancel(self, cookie): + self._handle_cancel() + + def handle_delete_cancel(self, cookie): + self._handle_cancel() + + def handle_suspend_cancel(self, cookie): + self._handle_cancel() + + def handle_resume_cancel(self, cookie): + self._handle_cancel() + def get_attribute(self, key, *path): """Resource attributes map to deployment outputs values.""" sd = self.rpc_client().show_software_deployment( diff --git a/heat/tests/openstack/heat/test_software_deployment.py b/heat/tests/openstack/heat/test_software_deployment.py index 5e2800f8c9..9d071ea2a0 100644 --- a/heat/tests/openstack/heat/test_software_deployment.py +++ b/heat/tests/openstack/heat/test_software_deployment.py @@ -685,6 +685,29 @@ class SoftwareDeploymentTest(common.HeatTestCase): self.assertEqual( 'Deployment to server failed: something wrong', six.text_type(err)) + def test_handle_create_cancel(self): + self._create_stack(self.template) + mock_sd = self.mock_deployment() + self.rpc_client.show_software_deployment.return_value = mock_sd + self.deployment.resource_id = 'c8a19429-7fde-47ea-a42f-40045488226c' + + # status in_progress + mock_sd['status'] = self.deployment.IN_PROGRESS + self.deployment.handle_create_cancel(None) + self.assertEqual( + 'FAILED', + self.rpc_client.update_software_deployment.call_args[1]['status']) + + # status failed + mock_sd['status'] = self.deployment.FAILED + self.deployment.handle_create_cancel(None) + + # deployment not created + mock_sd = None + self.deployment.handle_create_cancel(None) + self.assertEqual(1, + self.rpc_client.update_software_deployment.call_count) + def test_handle_delete(self): self._create_stack(self.template) mock_sd = self.mock_deployment() diff --git a/heat_integrationtests/common/test.py b/heat_integrationtests/common/test.py index 9944bd16d0..0eedbe386f 100644 --- a/heat_integrationtests/common/test.py +++ b/heat_integrationtests/common/test.py @@ -551,12 +551,13 @@ class HeatIntegrationTest(testscenarios.WithScenarios, parameters=None, environment=None, tags=None, expected_status='CREATE_COMPLETE', disable_rollback=True, enable_cleanup=True, - environment_files=None): + environment_files=None, timeout=None): name = stack_name or self._stack_rand_name() templ = template or self.template templ_files = files or {} params = parameters or {} env = environment or {} + timeout_mins = timeout or self.conf.build_timeout self.client.stacks.create( stack_name=name, template=templ, @@ -565,7 +566,8 @@ class HeatIntegrationTest(testscenarios.WithScenarios, parameters=params, environment=env, tags=tags, - environment_files=environment_files + environment_files=environment_files, + timeout_mins=timeout_mins ) if expected_status not in ['ROLLBACK_COMPLETE'] and enable_cleanup: self.addCleanup(self._stack_delete, name) diff --git a/heat_integrationtests/functional/test_software_config.py b/heat_integrationtests/functional/test_software_config.py index 8a2e193848..8c1cd53f90 100644 --- a/heat_integrationtests/functional/test_software_config.py +++ b/heat_integrationtests/functional/test_software_config.py @@ -22,6 +22,7 @@ import yaml from oslo_utils import timeutils from heat_integrationtests.common import exceptions +from heat_integrationtests.common import test from heat_integrationtests.functional import functional_base @@ -101,6 +102,30 @@ properties: for config_stack in config_stacks: self._wait_for_stack_status(config_stack, 'CREATE_COMPLETE') + def test_deployments_timeout_failed(self): + parms = {'flavor': self.conf.minimal_instance_type, + 'network': self.conf.fixed_network_name, + 'image': self.conf.minimal_image_ref} + stack_identifier = self.stack_create( + parameters=parms, + template=self.server_template, + enable_cleanup=self.enable_cleanup) + server_stack = self.client.stacks.get(stack_identifier) + server = server_stack.outputs[0]['output_value'] + config_stack = self.deploy_config(server, 3, 1) + self._wait_for_stack_status(config_stack, 'CREATE_FAILED') + kwargs = {'server_id': server} + + def check_deployment_status(): + sd_list = self.client.software_deployments.list(**kwargs) + for sd in sd_list: + if sd.status != 'FAILED': + return False + return True + + self.assertTrue(test.call_until_true( + 20, 0, check_deployment_status)) + def deploy_many_configs(self, stack, server, config_stacks, stack_count, deploys_per_stack, deploy_count_start): @@ -112,7 +137,7 @@ properties: self.wait_for_deploy_metadata_set(stack, new_count) return new_count - def deploy_config(self, server, deploy_count): + def deploy_config(self, server, deploy_count, timeout=None): parms = {'server': server} template = yaml.safe_load(self.config_template) resources = template['resources'] @@ -123,7 +148,8 @@ properties: parameters=parms, template=template, enable_cleanup=self.enable_cleanup, - expected_status=None) + expected_status=None, + timeout=timeout) def wait_for_deploy_metadata_set(self, stack, deploy_count): build_timeout = self.conf.build_timeout