From a709637a17166cb12e5d94944837bac6451b2851 Mon Sep 17 00:00:00 2001 From: Thomas Herve Date: Tue, 16 Feb 2016 16:29:51 +0100 Subject: [PATCH] Replace SD RPC polling by long RPC call This changes the way SoftwareDeployment updates its internal status. Instead of querying the deployment status in a loop using RPC client, it makes a single call, which returns once the status has been updated. Closes-Bug: #1549219 Change-Id: I484b7c8cb4a4e71817be6bea764f23b68a39b2d4 --- .../openstack/heat/software_deployment.py | 4 ++-- heat/engine/service.py | 7 ++++++- heat/engine/service_software_config.py | 19 ++++++++++++++++++- heat/rpc/client.py | 14 +++++++++++++- .../engine/service/test_service_engine.py | 2 +- .../engine/service/test_software_config.py | 14 ++++++++++++++ .../heat/test_software_deployment.py | 17 ++++++++++------- heat/tests/test_rpc_client.py | 5 +++++ 8 files changed, 69 insertions(+), 13 deletions(-) diff --git a/heat/engine/resources/openstack/heat/software_deployment.py b/heat/engine/resources/openstack/heat/software_deployment.py index 9af8eaf856..6c84e1f0e5 100644 --- a/heat/engine/resources/openstack/heat/software_deployment.py +++ b/heat/engine/resources/openstack/heat/software_deployment.py @@ -287,8 +287,8 @@ class SoftwareDeployment(signal_responder.SignalResponder): return sd def _check_complete(self): - sd = self.rpc_client().show_software_deployment( - self.context, self.resource_id) + sd = self.rpc_client().check_software_deployment( + self.context, self.resource_id, self.stack.time_remaining()) status = sd[rpc_api.SOFTWARE_DEPLOYMENT_STATUS] if status == SoftwareDeployment.COMPLETE: return True diff --git a/heat/engine/service.py b/heat/engine/service.py index 9c03bc7bba..3770191bb4 100644 --- a/heat/engine/service.py +++ b/heat/engine/service.py @@ -294,7 +294,7 @@ class EngineService(service.Service): by the RPC caller. """ - RPC_API_VERSION = '1.26' + RPC_API_VERSION = '1.27' def __init__(self, host, topic): super(EngineService, self).__init__() @@ -2014,6 +2014,11 @@ class EngineService(service.Service): return self.software_config.show_software_deployment( cnxt, deployment_id) + @context.request_context + def check_software_deployment(self, cnxt, deployment_id, timeout): + return self.software_config.check_software_deployment( + cnxt, deployment_id, timeout) + @context.request_context def create_software_deployment(self, cnxt, server_id, config_id, input_values, action, status, diff --git a/heat/engine/service_software_config.py b/heat/engine/service_software_config.py index 7ce1edeb84..cfc578759f 100644 --- a/heat/engine/service_software_config.py +++ b/heat/engine/service_software_config.py @@ -27,6 +27,7 @@ from heat.common.i18n import _ from heat.common.i18n import _LI from heat.db import api as db_api from heat.engine import api +from heat.engine import scheduler from heat.objects import resource as resource_objects from heat.objects import software_config as software_config_object from heat.objects import software_deployment as software_deployment_object @@ -196,7 +197,19 @@ class SoftwareConfigService(service.Service): return software_deployment_object.SoftwareDeployment.get_by_id( cnxt, sd.id) - def show_software_deployment(self, cnxt, deployment_id): + def check_software_deployment(self, cnxt, deployment_id, timeout): + def _check(): + while True: + sd = self._show_software_deployment(cnxt, deployment_id) + if sd.status != rpc_api.SOFTWARE_DEPLOYMENT_IN_PROGRESS: + return + yield + scheduler.TaskRunner(_check)(timeout=timeout) + sd = software_deployment_object.SoftwareDeployment.get_by_id( + cnxt, deployment_id) + return api.format_software_deployment(sd) + + def _show_software_deployment(self, cnxt, deployment_id): sd = software_deployment_object.SoftwareDeployment.get_by_id( cnxt, deployment_id) if sd.status == rpc_api.SOFTWARE_DEPLOYMENT_IN_PROGRESS: @@ -209,6 +222,10 @@ class SoftwareConfigService(service.Service): elif transport == 'ZAQAR_SIGNAL': sd = self._refresh_zaqar_software_deployment( cnxt, sd, input_values.get('deploy_queue_id')) + return sd + + def show_software_deployment(self, cnxt, deployment_id): + sd = self._show_software_deployment(cnxt, deployment_id) return api.format_software_deployment(sd) def create_software_deployment(self, cnxt, server_id, config_id, diff --git a/heat/rpc/client.py b/heat/rpc/client.py index be4ece09dd..e611e98f12 100644 --- a/heat/rpc/client.py +++ b/heat/rpc/client.py @@ -47,6 +47,7 @@ class EngineClient(object): 1.24 - Adds ignorable_errors to validate_template 1.25 - list_stack_resource filter update 1.26 - Add mark_unhealthy + 1.27 - Add check_software_deployment """ BASE_RPC_API_VERSION = '1.0' @@ -60,12 +61,17 @@ class EngineClient(object): def make_msg(method, **kwargs): return method, kwargs - def call(self, ctxt, msg, version=None): + def call(self, ctxt, msg, version=None, timeout=None): method, kwargs = msg + if version is not None: client = self._client.prepare(version=version) else: client = self._client + + if timeout is not None: + client = client.prepare(timeout=timeout) + return client.call(ctxt, method, **kwargs) def cast(self, ctxt, msg, version=None): @@ -681,6 +687,12 @@ class EngineClient(object): return self.call(cnxt, self.make_msg('show_software_deployment', deployment_id=deployment_id)) + def check_software_deployment(self, cnxt, deployment_id, timeout): + return self.call(cnxt, self.make_msg('check_software_deployment', + deployment_id=deployment_id, + timeout=timeout), + timeout=timeout, version='1.27') + def create_software_deployment(self, cnxt, server_id, config_id=None, input_values=None, action='INIT', status='COMPLETE', status_reason='', diff --git a/heat/tests/engine/service/test_service_engine.py b/heat/tests/engine/service/test_service_engine.py index bdc736724f..403581fcdd 100644 --- a/heat/tests/engine/service/test_service_engine.py +++ b/heat/tests/engine/service/test_service_engine.py @@ -40,7 +40,7 @@ class ServiceEngineTest(common.HeatTestCase): def test_make_sure_rpc_version(self): self.assertEqual( - '1.26', + '1.27', service.EngineService.RPC_API_VERSION, ('RPC version is changed, please update this test to new version ' 'and make sure additional test cases are added for RPC APIs ' diff --git a/heat/tests/engine/service/test_software_config.py b/heat/tests/engine/service/test_software_config.py index 1c6d7624cc..84a4fa9834 100644 --- a/heat/tests/engine/service/test_software_config.py +++ b/heat/tests/engine/service/test_software_config.py @@ -311,6 +311,20 @@ class SoftwareConfigServiceTest(common.HeatTestCase): deployment, self.engine.show_software_deployment(self.ctx, deployment_id)) + def test_check_software_deployment(self): + deployment_id = str(uuid.uuid4()) + ex = self.assertRaises(dispatcher.ExpectedException, + self.engine.check_software_deployment, + self.ctx, deployment_id, 10) + self.assertEqual(exception.NotFound, ex.exc_info[0]) + + deployment = self._create_software_deployment() + self.assertIsNotNone(deployment) + deployment_id = deployment['id'] + self.assertEqual( + deployment, + self.engine.check_software_deployment(self.ctx, deployment_id, 10)) + @mock.patch.object(service_software_config.SoftwareConfigService, '_push_metadata_software_deployments') def test_signal_software_deployment(self, pmsd): diff --git a/heat/tests/openstack/heat/test_software_deployment.py b/heat/tests/openstack/heat/test_software_deployment.py index 80004eb5ef..8c238809a7 100644 --- a/heat/tests/openstack/heat/test_software_deployment.py +++ b/heat/tests/openstack/heat/test_software_deployment.py @@ -18,6 +18,8 @@ import uuid import mock import six +from oslo_utils import timeutils + from heat.common import exception as exc from heat.common.i18n import _ from heat.engine.clients.os import nova @@ -168,6 +170,7 @@ class SoftwareDeploymentTest(common.HeatTestCase): stack_user_project_id='65728b74-cfe7-4f17-9c15-11d4f686e591', cache_data=cache_data ) + self.stack.created_time = timeutils.utcnow() self.patchobject(nova.NovaClientPlugin, 'get_server', return_value=mock.MagicMock()) @@ -546,7 +549,7 @@ class SoftwareDeploymentTest(common.HeatTestCase): def test_check_create_complete(self): self._create_stack(self.template) sd = self.mock_deployment() - self.rpc_client.show_software_deployment.return_value = sd + self.rpc_client.check_software_deployment.return_value = sd sd['status'] = self.deployment.COMPLETE self.assertTrue(self.deployment.check_create_complete(sd)) @@ -560,7 +563,7 @@ class SoftwareDeploymentTest(common.HeatTestCase): def test_check_update_complete(self): self._create_stack(self.template) sd = self.mock_deployment() - self.rpc_client.show_software_deployment.return_value = sd + self.rpc_client.check_software_deployment.return_value = sd sd['status'] = self.deployment.COMPLETE self.assertTrue(self.deployment.check_update_complete(sd)) @@ -575,7 +578,7 @@ class SoftwareDeploymentTest(common.HeatTestCase): def test_check_suspend_complete(self): self._create_stack(self.template) sd = self.mock_deployment() - self.rpc_client.show_software_deployment.return_value = sd + self.rpc_client.check_software_deployment.return_value = sd sd['status'] = self.deployment.COMPLETE self.assertTrue(self.deployment.check_suspend_complete(sd)) @@ -590,7 +593,7 @@ class SoftwareDeploymentTest(common.HeatTestCase): def test_check_resume_complete(self): self._create_stack(self.template) sd = self.mock_deployment() - self.rpc_client.show_software_deployment.return_value = sd + self.rpc_client.check_software_deployment.return_value = sd sd['status'] = self.deployment.COMPLETE self.assertTrue(self.deployment.check_resume_complete(sd)) @@ -608,7 +611,7 @@ class SoftwareDeploymentTest(common.HeatTestCase): 'status': self.deployment.FAILED, 'status_reason': 'something wrong' } - self.rpc_client.show_software_deployment.return_value = sd + self.rpc_client.check_software_deployment.return_value = sd err = self.assertRaises( exc.Error, self.deployment.check_create_complete, sd) self.assertEqual( @@ -641,7 +644,7 @@ class SoftwareDeploymentTest(common.HeatTestCase): self.deployment.resource_id = 'c8a19429-7fde-47ea-a42f-40045488226c' - self.rpc_client.show_software_deployment.return_value = sd + self.rpc_client.check_software_deployment.return_value = sd self.rpc_client.update_software_deployment.return_value = sd self.assertEqual(sd, self.deployment.handle_delete()) self.assertEqual({ @@ -748,7 +751,7 @@ class SoftwareDeploymentTest(common.HeatTestCase): derived_sc = self.mock_derived_software_config() sd = self.mock_deployment() - self.rpc_client.show_software_deployment.return_value = sd + self.rpc_client.check_software_deployment.return_value = sd self.deployment.resource_id = 'c8a19429-7fde-47ea-a42f-40045488226c' # first, handle the suspend diff --git a/heat/tests/test_rpc_client.py b/heat/tests/test_rpc_client.py index 9ad61b1b5b..ae52ccba5d 100644 --- a/heat/tests/test_rpc_client.py +++ b/heat/tests/test_rpc_client.py @@ -328,6 +328,11 @@ class EngineRpcAPITestCase(common.HeatTestCase): self._test_engine_api('show_software_deployment', 'call', deployment_id=deployment_id) + def test_check_software_deployment(self): + deployment_id = '86729f02-4648-44d8-af44-d0ec65b6abc9' + self._test_engine_api('check_software_deployment', 'call', + deployment_id=deployment_id, timeout=100) + def test_create_software_deployment(self): self._test_engine_api( 'create_software_deployment', 'call',