diff --git a/heat/engine/clients/os/zaqar.py b/heat/engine/clients/os/zaqar.py index 3798174f86..665042e348 100644 --- a/heat/engine/clients/os/zaqar.py +++ b/heat/engine/clients/os/zaqar.py @@ -28,6 +28,8 @@ class ZaqarClientPlugin(client_plugin.ClientPlugin): exceptions_module = zaqar_errors service_types = ['messaging'] + DEFAULT_TTL = 3600 + def _create(self): return self.create_for_tenant(self.context.tenant_id) diff --git a/heat/engine/resources/openstack/heat/software_deployment.py b/heat/engine/resources/openstack/heat/software_deployment.py index 6a84ac2fd2..79d7258998 100644 --- a/heat/engine/resources/openstack/heat/software_deployment.py +++ b/heat/engine/resources/openstack/heat/software_deployment.py @@ -97,20 +97,24 @@ class SoftwareDeployment(signal_responder.SignalResponder): DEPLOY_RESOURCE_NAME, DEPLOY_AUTH_URL, DEPLOY_USERNAME, DEPLOY_PASSWORD, DEPLOY_PROJECT_ID, DEPLOY_USER_ID, - DEPLOY_SIGNAL_VERB, DEPLOY_SIGNAL_TRANSPORT + DEPLOY_SIGNAL_VERB, DEPLOY_SIGNAL_TRANSPORT, + DEPLOY_QUEUE_ID ) = ( 'deploy_server_id', 'deploy_action', 'deploy_signal_id', 'deploy_stack_id', 'deploy_resource_name', 'deploy_auth_url', 'deploy_username', 'deploy_password', 'deploy_project_id', 'deploy_user_id', - 'deploy_signal_verb', 'deploy_signal_transport' + 'deploy_signal_verb', 'deploy_signal_transport', + 'deploy_queue_id' ) SIGNAL_TRANSPORTS = ( - CFN_SIGNAL, TEMP_URL_SIGNAL, HEAT_SIGNAL, NO_SIGNAL + CFN_SIGNAL, TEMP_URL_SIGNAL, HEAT_SIGNAL, NO_SIGNAL, + ZAQAR_SIGNAL ) = ( - 'CFN_SIGNAL', 'TEMP_URL_SIGNAL', 'HEAT_SIGNAL', 'NO_SIGNAL' + 'CFN_SIGNAL', 'TEMP_URL_SIGNAL', 'HEAT_SIGNAL', 'NO_SIGNAL', + 'ZAQAR_SIGNAL' ) properties_schema = { @@ -199,6 +203,10 @@ class SoftwareDeployment(signal_responder.SignalResponder): return self.properties[ self.SIGNAL_TRANSPORT] == self.TEMP_URL_SIGNAL + def _signal_transport_zaqar(self): + return self.properties.get( + self.SIGNAL_TRANSPORT) == self.ZAQAR_SIGNAL + def _build_properties(self, properties, config_id, action): props = { 'config_id': config_id, @@ -326,6 +334,17 @@ class SoftwareDeployment(signal_responder.SignalResponder): container, object_name, '') return put_url + def _get_queue_id(self): + queue_id = self.data().get('signal_queue_id') + if queue_id: + return queue_id + + queue_id = self.physical_resource_name() + zaqar = self.client('zaqar') + zaqar.queue(queue_id).ensure_exists() + self.data_set('signal_queue_id', queue_id) + return queue_id + def _delete_temp_url(self): object_name = self.data().get('signal_object_name') if not object_name: @@ -342,6 +361,17 @@ class SoftwareDeployment(signal_responder.SignalResponder): self.data_delete('signal_object_name') self.data_delete('signal_temp_url') + def _delete_queue(self): + queue_id = self.data().get('signal_queue_id') + if not queue_id: + return + zaqar = self.client('zaqar') + try: + zaqar.queue(queue_id).delete() + except Exception as ex: + self.client_plugin('zaqar').ignore_not_found(ex) + self.data_delete('signal_queue_id') + def _build_derived_inputs(self, action, source): scl = sc.SoftwareConfig inputs = copy.deepcopy(source.get(scl.INPUTS)) or [] @@ -419,7 +449,7 @@ class SoftwareDeployment(signal_responder.SignalResponder): scl.TYPE: 'String', 'value': 'PUT' }) - elif self._signal_transport_heat(): + elif self._signal_transport_heat() or self._signal_transport_zaqar(): inputs.extend([{ scl.NAME: self.DEPLOY_AUTH_URL, scl.DESCRIPTION: _('URL for API authentication'), @@ -446,6 +476,14 @@ class SoftwareDeployment(signal_responder.SignalResponder): scl.TYPE: 'String', 'value': self.stack.stack_user_project_id }]) + if self._signal_transport_zaqar(): + inputs.append({ + scl.NAME: self.DEPLOY_QUEUE_ID, + scl.DESCRIPTION: _('ID of queue to use for signaling ' + 'output values'), + scl.TYPE: 'String', + 'value': self._get_queue_id() + }) return inputs @@ -453,7 +491,7 @@ class SoftwareDeployment(signal_responder.SignalResponder): if self._signal_transport_cfn(): self._create_user() self._create_keypair() - if self._signal_transport_heat(): + if self._signal_transport_heat() or self._signal_transport_zaqar(): self.password = uuid.uuid4().hex self._create_user() return self._handle_action(self.CREATE) @@ -505,6 +543,8 @@ class SoftwareDeployment(signal_responder.SignalResponder): self._delete_user() elif self._signal_transport_temp_url(): self._delete_temp_url() + elif self._signal_transport_zaqar(): + self._delete_queue() derived_config_id = None if self.resource_id is not None: diff --git a/heat/engine/resources/openstack/nova/server.py b/heat/engine/resources/openstack/nova/server.py index ae5fafa197..8f00c7c1aa 100644 --- a/heat/engine/resources/openstack/nova/server.py +++ b/heat/engine/resources/openstack/nova/server.py @@ -109,9 +109,9 @@ class Server(stack_user.StackUser): ) _SOFTWARE_CONFIG_TRANSPORTS = ( - POLL_SERVER_CFN, POLL_SERVER_HEAT, POLL_TEMP_URL + POLL_SERVER_CFN, POLL_SERVER_HEAT, POLL_TEMP_URL, ZAQAR_MESSAGE ) = ( - 'POLL_SERVER_CFN', 'POLL_SERVER_HEAT', 'POLL_TEMP_URL' + 'POLL_SERVER_CFN', 'POLL_SERVER_HEAT', 'POLL_TEMP_URL', 'ZAQAR_MESSAGE' ) ATTRIBUTES = ( @@ -530,6 +530,21 @@ class Server(stack_user.StackUser): 'stack_id': self.stack.identifier().stack_path(), 'resource_name': self.name} } + if self.transport_zaqar_message(): + queue_id = self.physical_resource_name() + self.data_set('metadata_queue_id', queue_id) + zaqar_plugin = self.client_plugin('zaqar') + zaqar = zaqar_plugin.create_for_tenant( + self.stack.stack_user_project_id) + queue = zaqar.queue(queue_id) + queue.post({'body': meta, 'ttl': zaqar_plugin.DEFAULT_TTL}) + meta['os-collect-config'] = {'zaqar': { + 'user_id': self._get_user_id(), + 'password': self.password, + 'auth_url': self.context.auth_url, + 'project_id': self.stack.stack_user_project_id, + 'queue_id': queue_id} + } elif self.transport_poll_server_cfn(): meta['os-collect-config'] = {'cfn': { 'metadata_url': '%s/v1/' % cfg.CONF.heat_metadata_server_url, @@ -577,7 +592,8 @@ class Server(stack_user.StackUser): self._create_user() self._create_keypair() - elif self.transport_poll_server_heat(): + elif (self.transport_poll_server_heat() or + self.transport_zaqar_message()): self.password = uuid.uuid4().hex self._create_user() @@ -621,6 +637,10 @@ class Server(stack_user.StackUser): return self.properties[ self.SOFTWARE_CONFIG_TRANSPORT] == self.POLL_TEMP_URL + def transport_zaqar_message(self): + return self.properties.get( + self.SOFTWARE_CONFIG_TRANSPORT) == self.ZAQAR_MESSAGE + def get_software_config(self, ud_content): try: sc = self.rpc_client().show_software_config( @@ -1331,6 +1351,19 @@ class Server(stack_user.StackUser): except Exception as ex: self.client_plugin('swift').ignore_not_found(ex) + def _delete_queue(self): + queue_id = self.data().get('metadata_queue_id') + if not queue_id: + return + client_plugin = self.client_plugin('zaqar') + zaqar = client_plugin.create_for_tenant( + self.stack.stack_user_project_id) + try: + zaqar.queue(queue_id).delete() + except Exception as ex: + client_plugin.ignore_not_found(ex) + self.data_delete('metadata_queue_id') + def handle_delete(self): if self.resource_id is None: @@ -1339,6 +1372,7 @@ class Server(stack_user.StackUser): if self.user_data_software_config(): self._delete_user() self._delete_temp_url() + self._delete_queue() try: self.client().servers.delete(self.resource_id) diff --git a/heat/engine/service_software_config.py b/heat/engine/service_software_config.py index 21a4d900be..2d499f520e 100644 --- a/heat/engine/service_software_config.py +++ b/heat/engine/service_software_config.py @@ -81,7 +81,7 @@ class SoftwareConfigService(service.Service): result = [api.format_software_config(sd.config) for sd in all_sd_s] return result - def _push_metadata_software_deployments(self, cnxt, server_id): + def _push_metadata_software_deployments(self, cnxt, server_id, sd): rs = (resource_object.Resource. get_by_physical_resource_id(cnxt, server_id)) if not rs: @@ -92,15 +92,24 @@ class SoftwareConfigService(service.Service): rs.update_and_save({'rsrc_metadata': md}) metadata_put_url = None + metadata_queue_id = None for rd in rs.data: if rd.key == 'metadata_put_url': metadata_put_url = rd.value break + elif rd.key == 'metadata_queue_id': + metadata_queue_id = rd.value + break if metadata_put_url: json_md = jsonutils.dumps(md) requests.put(metadata_put_url, json_md) + elif metadata_queue_id: + zaqar_plugin = cnxt.clients.client_plugin('zaqar') + zaqar = zaqar_plugin.create_for_tenant(sd.stack_user_project_id) + queue = zaqar.queue(metadata_queue_id) + queue.post({'body': md, 'ttl': zaqar_plugin.DEFAULT_TTL}) - def _refresh_software_deployment(self, cnxt, sd, deploy_signal_id): + def _refresh_swift_software_deployment(self, cnxt, sd, deploy_signal_id): container, object_name = urlparse.urlparse( deploy_signal_id).path.split('/')[-2:] swift_plugin = cnxt.clients.client_plugin('swift') @@ -146,6 +155,19 @@ class SoftwareConfigService(service.Service): return software_deployment_object.SoftwareDeployment.get_by_id( cnxt, sd.id) + def _refresh_zaqar_software_deployment(self, cnxt, sd, deploy_queue_id): + zaqar_plugin = cnxt.clients.client_plugin('zaqar') + zaqar = zaqar_plugin.create_for_tenant(sd.stack_user_project_id) + queue = zaqar.queue(deploy_queue_id) + + messages = list(queue.pop()) + if messages: + self.signal_software_deployment( + cnxt, sd.id, messages[0].body, None) + + return software_deployment_object.SoftwareDeployment.get_by_id( + cnxt, sd.id) + def show_software_deployment(self, cnxt, deployment_id): sd = software_deployment_object.SoftwareDeployment.get_by_id( cnxt, deployment_id) @@ -154,8 +176,11 @@ class SoftwareConfigService(service.Service): input_values = dict((i['name'], i['value']) for i in c['inputs']) transport = input_values.get('deploy_signal_transport') if transport == 'TEMP_URL_SIGNAL': - sd = self._refresh_software_deployment( + sd = self._refresh_swift_software_deployment( cnxt, sd, input_values.get('deploy_signal_id')) + elif transport == 'ZAQAR_SIGNAL': + sd = self._refresh_zaqar_software_deployment( + cnxt, sd, input_values.get('deploy_queue_id')) return api.format_software_deployment(sd) def create_software_deployment(self, cnxt, server_id, config_id, @@ -171,7 +196,7 @@ class SoftwareConfigService(service.Service): 'action': action, 'status': status, 'status_reason': status_reason}) - self._push_metadata_software_deployments(cnxt, server_id) + self._push_metadata_software_deployments(cnxt, server_id, sd) return api.format_software_deployment(sd) def signal_software_deployment(self, cnxt, deployment_id, details, @@ -262,7 +287,7 @@ class SoftwareConfigService(service.Service): # only push metadata if this update resulted in the config_id # changing, since metadata is just a list of configs if config_id: - self._push_metadata_software_deployments(cnxt, sd.server_id) + self._push_metadata_software_deployments(cnxt, sd.server_id, sd) return api.format_software_deployment(sd) diff --git a/heat/tests/engine/test_software_config.py b/heat/tests/engine/test_software_config.py index a10ede91a3..c7cfc74a78 100644 --- a/heat/tests/engine/test_software_config.py +++ b/heat/tests/engine/test_software_config.py @@ -23,6 +23,7 @@ import six from heat.common import exception from heat.common import template_format from heat.engine.clients.os import swift +from heat.engine.clients.os import zaqar from heat.engine import service from heat.engine import service_software_config from heat.objects import resource as resource_objects @@ -386,9 +387,9 @@ class SoftwareConfigServiceTest(common.HeatTestCase): self.assertEqual(kwargs['input_values'], deployment['input_values']) @mock.patch.object(service_software_config.SoftwareConfigService, - '_refresh_software_deployment') + '_refresh_swift_software_deployment') def test_show_software_deployment_refresh( - self, _refresh_software_deployment): + self, _refresh_swift_software_deployment): temp_url = ('http://192.0.2.1/v1/AUTH_a/b/c' '?temp_url_sig=ctemp_url_expires=1234') config = self._create_software_config(inputs=[ @@ -409,13 +410,13 @@ class SoftwareConfigServiceTest(common.HeatTestCase): deployment_id = deployment['id'] sd = software_deployment_object.SoftwareDeployment.get_by_id( self.ctx, deployment_id) - _refresh_software_deployment.return_value = sd + _refresh_swift_software_deployment.return_value = sd self.assertEqual( deployment, self.engine.show_software_deployment(self.ctx, deployment_id)) self.assertEqual( (self.ctx, sd, temp_url), - _refresh_software_deployment.call_args[0]) + _refresh_swift_software_deployment.call_args[0]) def test_update_software_deployment_new_config(self): @@ -459,7 +460,10 @@ class SoftwareConfigServiceTest(common.HeatTestCase): self.assertIsNotNone(updated) self.assertEqual('DEPLOY', updated['action']) self.assertEqual('WAITING', updated['status']) - mock_push.assert_called_once_with(self.ctx, server_id) + + sd = software_deployment_object.SoftwareDeployment.get_by_id( + self.ctx, deployment_id) + mock_push.assert_called_once_with(self.ctx, server_id, sd) def test_update_software_deployment_fields(self): @@ -529,7 +533,7 @@ class SoftwareConfigServiceTest(common.HeatTestCase): } self.engine.software_config._push_metadata_software_deployments( - self.ctx, '1234') + self.ctx, '1234', None) rs.update_and_save.assert_called_once_with( {'rsrc_metadata': result_metadata}) put.side_effect = Exception('Unexpected requests.put') @@ -558,17 +562,56 @@ class SoftwareConfigServiceTest(common.HeatTestCase): } self.engine.software_config._push_metadata_software_deployments( - self.ctx, '1234') + self.ctx, '1234', None) rs.update_and_save.assert_called_once_with( {'rsrc_metadata': result_metadata}) put.assert_called_once_with( 'http://192.168.2.2/foo/bar', json.dumps(result_metadata)) + @mock.patch.object(service_software_config.SoftwareConfigService, + 'metadata_software_deployments') + @mock.patch.object(service_software_config.resource_object.Resource, + 'get_by_physical_resource_id') + @mock.patch.object(zaqar.ZaqarClientPlugin, 'create_for_tenant') + def test_push_metadata_software_deployments_queue( + self, plugin, res_get, md_sd): + rs = mock.Mock() + rs.rsrc_metadata = {'original': 'metadata'} + rd = mock.Mock() + rd.key = 'metadata_queue_id' + rd.value = '6789' + rs.data = [rd] + res_get.return_value = rs + sd = mock.Mock() + sd.stack_user_project_id = 'project1' + queue = mock.Mock() + zaqar_client = mock.Mock() + plugin.return_value = zaqar_client + zaqar_client.queue.return_value = queue + + deployments = {'deploy': 'this'} + md_sd.return_value = deployments + + result_metadata = { + 'original': 'metadata', + 'deployments': {'deploy': 'this'} + } + + self.engine.software_config._push_metadata_software_deployments( + self.ctx, '1234', sd) + rs.update_and_save.assert_called_once_with( + {'rsrc_metadata': result_metadata}) + + plugin.assert_called_once_with('project1') + zaqar_client.queue.assert_called_once_with('6789') + queue.post.assert_called_once_with( + {'body': result_metadata, 'ttl': 3600}) + @mock.patch.object(service_software_config.SoftwareConfigService, 'signal_software_deployment') @mock.patch.object(swift.SwiftClientPlugin, '_create') - def test_refresh_software_deployment(self, scc, ssd): + def test_refresh_swift_software_deployment(self, scc, ssd): temp_url = ('http://192.0.2.1/v1/AUTH_a/b/c' '?temp_url_sig=ctemp_url_expires=1234') container = 'b' @@ -617,7 +660,7 @@ class SoftwareConfigServiceTest(common.HeatTestCase): self.assertEqual( sd, - self.engine.software_config._refresh_software_deployment( + self.engine.software_config._refresh_swift_software_deployment( self.ctx, sd, temp_url)) sc.head_object.assert_called_once_with(container, object_name) # no call to get_object or signal_last_modified @@ -629,7 +672,7 @@ class SoftwareConfigServiceTest(common.HeatTestCase): 'Ouch', http_status=409) self.assertRaises( swift_exc.ClientException, - self.engine.software_config._refresh_software_deployment, + self.engine.software_config._refresh_swift_software_deployment, self.ctx, sd, temp_url) @@ -639,7 +682,7 @@ class SoftwareConfigServiceTest(common.HeatTestCase): sc.head_object.side_effect = None # first poll populates data signal_last_modified - self.engine.software_config._refresh_software_deployment( + self.engine.software_config._refresh_swift_software_deployment( self.ctx, sd, temp_url) sc.head_object.assert_called_with(container, object_name) sc.get_object.assert_called_once_with(container, object_name) @@ -653,7 +696,7 @@ class SoftwareConfigServiceTest(common.HeatTestCase): sd = software_deployment_object.SoftwareDeployment.get_by_id( self.ctx, deployment_id) self.assertEqual(then, sd.updated_at) - self.engine.software_config._refresh_software_deployment( + self.engine.software_config._refresh_swift_software_deployment( self.ctx, sd, temp_url) sc.get_object.assert_called_once_with(container, object_name) # signal_software_deployment has not been called again @@ -664,7 +707,7 @@ class SoftwareConfigServiceTest(common.HeatTestCase): headers['last-modified'] = last_modified_2 sc.head_object.return_value = headers sc.get_object.return_value = (headers, '{"bar": "baz"}') - self.engine.software_config._refresh_software_deployment( + self.engine.software_config._refresh_swift_software_deployment( self.ctx, sd, temp_url) # two calls to signal_software_deployment, for then and now @@ -677,6 +720,40 @@ class SoftwareConfigServiceTest(common.HeatTestCase): self.ctx, deployment_id, {'updated_at': now}) sd = software_deployment_object.SoftwareDeployment.get_by_id( self.ctx, deployment_id) - self.engine.software_config._refresh_software_deployment( + self.engine.software_config._refresh_swift_software_deployment( self.ctx, sd, temp_url) self.assertEqual(2, len(ssd.mock_calls)) + + @mock.patch.object(service_software_config.SoftwareConfigService, + 'signal_software_deployment') + @mock.patch.object(zaqar.ZaqarClientPlugin, 'create_for_tenant') + def test_refresh_zaqar_software_deployment(self, plugin, ssd): + config = self._create_software_config(inputs=[ + { + 'name': 'deploy_signal_transport', + 'type': 'String', + 'value': 'ZAQAR_SIGNAL' + }, { + 'name': 'deploy_queue_id', + 'type': 'String', + 'value': '6789' + } + ]) + + queue = mock.Mock() + zaqar_client = mock.Mock() + plugin.return_value = zaqar_client + zaqar_client.queue.return_value = queue + queue.pop.return_value = [mock.Mock(body='ok')] + + deployment = self._create_software_deployment( + status='IN_PROGRESS', config_id=config['id']) + + deployment_id = deployment['id'] + self.assertEqual( + deployment, + self.engine.show_software_deployment(self.ctx, deployment_id)) + + zaqar_client.queue.assert_called_once_with('6789') + queue.pop.assert_called_once() + ssd.assert_called_once_with(self.ctx, deployment_id, 'ok', None) diff --git a/heat/tests/test_server.py b/heat/tests/test_server.py index 19224c69a2..8525c46b0f 100644 --- a/heat/tests/test_server.py +++ b/heat/tests/test_server.py @@ -27,6 +27,7 @@ from heat.engine.clients.os import glance from heat.engine.clients.os import neutron from heat.engine.clients.os import nova from heat.engine.clients.os import swift +from heat.engine.clients.os import zaqar from heat.engine import environment from heat.engine import resource from heat.engine.resources.openstack.nova import server as servers @@ -832,6 +833,75 @@ class ServersTest(common.HeatTestCase): self.m.VerifyAll() + def test_server_create_software_config_zaqar(self): + return_server = self.fc.servers.list()[1] + stack_name = 'software_config_s' + (tmpl, stack) = self._setup_test_stack(stack_name) + + props = tmpl.t['Resources']['WebServer']['Properties'] + props['user_data_format'] = 'SOFTWARE_CONFIG' + props['software_config_transport'] = 'ZAQAR_MESSAGE' + + resource_defns = tmpl.resource_definitions(stack) + server = servers.Server('WebServer', + resource_defns['WebServer'], stack) + + ncp = self.patchobject(nova.NovaClientPlugin, '_create') + zcc = self.patchobject(zaqar.ZaqarClientPlugin, 'create_for_tenant') + zc = mock.Mock() + + ncp.return_value = self.fc + zcc.return_value = zc + queue = mock.Mock() + zc.queue.return_value = queue + self._mock_get_image_id_success('F17-x86_64-gold', 744) + + self.m.StubOutWithMock(self.fc.servers, 'create') + self.fc.servers.create( + image=744, flavor=3, key_name='test', + name=utils.PhysName(stack_name, server.name), + security_groups=[], + userdata=mox.IgnoreArg(), scheduler_hints=None, + meta=None, nics=None, availability_zone=None, + block_device_mapping=None, block_device_mapping_v2=None, + config_drive=None, disk_config=None, reservation_id=None, + files={}, admin_pass=None).AndReturn( + return_server) + + self.m.ReplayAll() + scheduler.TaskRunner(server.create)() + + metadata_queue_id = server.data().get('metadata_queue_id') + md = server.metadata_get() + queue_id = md['os-collect-config']['zaqar']['queue_id'] + self.assertEqual(queue_id, metadata_queue_id) + + self.assertEqual({ + 'os-collect-config': { + 'zaqar': { + 'user_id': '1234', + 'password': server.password, + 'auth_url': 'http://server.test:5000/v2.0', + 'project_id': '8888', + 'queue_id': queue_id + } + }, + 'deployments': [] + }, server.metadata_get()) + + zc.queue.assert_called_once_with(queue_id) + queue.post.assert_called_once_with( + {'body': server.metadata_get(), 'ttl': 3600}) + + zc.queue.reset_mock() + + server._delete_queue() + + zc.queue.assert_called_once_with(queue_id) + zc.queue.delete.assert_called_once() + + self.m.VerifyAll() + @mock.patch.object(nova.NovaClientPlugin, '_create') def test_server_create_default_admin_pass(self, mock_client): return_server = self.fc.servers.list()[1] diff --git a/heat/tests/test_software_deployment.py b/heat/tests/test_software_deployment.py index 5229aab93f..92a1159af5 100644 --- a/heat/tests/test_software_deployment.py +++ b/heat/tests/test_software_deployment.py @@ -22,6 +22,7 @@ from heat.common import exception as exc from heat.common.i18n import _ from heat.engine.clients.os import nova from heat.engine.clients.os import swift +from heat.engine.clients.os import zaqar from heat.engine.resources.openstack.heat import software_deployment as sd from heat.engine import rsrc_defn from heat.engine import stack as parser @@ -100,6 +101,22 @@ class SoftwareDeploymentTest(common.HeatTestCase): } } + template_zaqar_signal = { + 'HeatTemplateFormatVersion': '2012-12-12', + 'Resources': { + 'deployment_mysql': { + 'Type': 'OS::Heat::SoftwareDeployment', + 'Properties': { + 'server': '9f1f0e00-05d2-4ca5-8602-95021f19c9d0', + 'config': '48e8ade1-9196-42d5-89a2-f709fde42632', + 'input_values': {'foo': 'bar', 'bink': 'bonk'}, + 'signal_transport': 'ZAQAR_SIGNAL', + 'name': '00_run_me_first' + } + } + } + } + template_delete_suspend_resume = { 'HeatTemplateFormatVersion': '2012-12-12', 'Resources': { @@ -1042,6 +1059,69 @@ class SoftwareDeploymentTest(common.HeatTestCase): for action in ('CREATE', 'UPDATE'): self.assertIsNotNone(self.deployment._handle_action(action)) + def test_get_zaqar_queue(self): + dep_data = {} + + zc = mock.MagicMock() + zcc = self.patch( + 'heat.engine.clients.os.zaqar.ZaqarClientPlugin._create') + zcc.return_value = zc + + self._create_stack(self.template_zaqar_signal) + + def data_set(key, value, redact=False): + dep_data[key] = value + + self.deployment.data_set = data_set + self.deployment.data = mock.Mock(return_value=dep_data) + + self.deployment.id = 23 + self.deployment.uuid = str(uuid.uuid4()) + self.deployment.action = self.deployment.CREATE + + queue_id = self.deployment._get_queue_id() + self.assertEqual(2, len(zc.queue.mock_calls)) + self.assertEqual(queue_id, zc.queue.mock_calls[0][1][0]) + self.assertEqual(queue_id, dep_data['signal_queue_id']) + + self.assertEqual(queue_id, self.deployment._get_queue_id()) + + def test_delete_zaqar_queue(self): + queue_id = str(uuid.uuid4()) + dep_data = { + 'signal_queue_id': queue_id + } + self._create_stack(self.template_zaqar_signal) + + self.deployment.data_delete = mock.MagicMock() + self.deployment.data = mock.Mock(return_value=dep_data) + + zc = mock.MagicMock() + zcc = self.patch( + 'heat.engine.clients.os.zaqar.ZaqarClientPlugin._create') + zcc.return_value = zc + + self.deployment.id = 23 + self.deployment.uuid = str(uuid.uuid4()) + self.deployment._delete_queue() + zc.queue.assert_called_once_with(queue_id) + zc.queue.delete.assert_called_once() + self.assertEqual( + [mock.call('signal_queue_id')], + self.deployment.data_delete.mock_calls) + + zaqar_exc = zaqar.ZaqarClientPlugin.exceptions_module + zc.queue.delete.side_effect = zaqar_exc.ResourceNotFound() + self.deployment._delete_queue() + self.assertEqual( + [mock.call('signal_queue_id'), mock.call('signal_queue_id')], + self.deployment.data_delete.mock_calls) + + dep_data.pop('signal_queue_id') + self.deployment.physical_resource_name = mock.Mock() + self.deployment._delete_queue() + self.assertEqual(2, len(self.deployment.data_delete.mock_calls)) + class SoftwareDeploymentGroupTest(common.HeatTestCase):