--- /dev/null
+# vim: tabstop=4 shiftwidth=4 softtabstop=4
+
+# Copyright 2012, Red Hat, Inc.
+#
+# Licensed under the Apache License, Version 2.0 (the "License"); you may
+# not use this file except in compliance with the License. You may obtain
+# a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
+# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
+# License for the specific language governing permissions and limitations
+# under the License.
+
+"""
+Client side of the heat engine RPC API.
+"""
+
+from heat.openstack.common import cfg
+from heat.openstack.common import exception
+from heat.openstack.common import rpc
+from heat.openstack.common.rpc import common as rpc_common
+import heat.openstack.common.rpc.proxy
+
+
+FLAGS = cfg.CONF
+
+
+def _engine_topic(topic, ctxt, host):
+ '''Get the topic to use for a message.
+
+ :param topic: the base topic
+ :param ctxt: request context
+ :param host: explicit host to send the message to.
+
+ :returns: A topic string
+ '''
+ if not host:
+ host = cfg.CONF.host
+ return rpc.queue_get_for(ctxt, topic, host)
+
+
+class EngineAPI(heat.openstack.common.rpc.proxy.RpcProxy):
+ '''Client side of the heat engine rpc API.
+
+ API version history:
+
+ 1.0 - Initial version.
+ '''
+
+ BASE_RPC_API_VERSION = '1.0'
+
+ def __init__(self):
+ super(EngineAPI, self).__init__(
+ topic=FLAGS.engine_topic,
+ default_version=self.BASE_RPC_API_VERSION)
+
+ def show_stack(self, ctxt, stack_name, params):
+ """
+ The show_stack method returns the attributes of one stack.
+
+ :param ctxt: RPC context.
+ :param stack_name: Name of the stack you want to see,
+ or None to see all
+ :param params: Dict of http request parameters passed in from API side.
+ """
+ return self.call(ctxt, self.make_msg('show_stack',
+ stack_name=stack_name, params=params),
+ topic=_engine_topic(self.topic, ctxt, None))
+
+ def create_stack(self, ctxt, stack_name, template, params, args):
+ """
+ The create_stack method creates a new stack using the template
+ provided.
+ Note that at this stage the template has already been fetched from the
+ heat-api process if using a template-url.
+
+ :param ctxt: RPC context.
+ :param stack_name: Name of the stack you want to create.
+ :param template: Template of stack you want to create.
+ :param params: Stack Input Params
+ :param args: Request parameters/args passed from API
+ """
+ return self.call(ctxt,
+ self.make_msg('create_stack', stack_name=stack_name,
+ template=template, params=params, args=args),
+ topic=_engine_topic(self.topic, ctxt, None))
+
+ def update_stack(self, ctxt, stack_name, template, params, args):
+ """
+ The update_stack method updates an existing stack based on the
+ provided template and parameters.
+ Note that at this stage the template has already been fetched from the
+ heat-api process if using a template-url.
+
+ :param ctxt: RPC context.
+ :param stack_name: Name of the stack you want to create.
+ :param template: Template of stack you want to create.
+ :param params: Stack Input Params
+ :param args: Request parameters/args passed from API
+ """
+ return self.call(ctxt, self.make_msg('update_stack',
+ stack_name=stack_name,
+ template=template, params=params, args=args),
+ topic=_engine_topic(self.topic, ctxt, None))
+
+ def validate_template(self, ctxt, template, params):
+ """
+ The validate_template method uses the stack parser to check
+ the validity of a template.
+
+ :param ctxt: RPC context.
+ :param template: Template of stack you want to create.
+ :param params: Params passed from API.
+ """
+ return self.call(ctxt, self.make_msg('validate_template',
+ template=template, params=params),
+ topic=_engine_topic(self.topic, ctxt, None))
+
+ def get_template(self, ctxt, stack_name, params):
+ """
+ Get the template.
+
+ :param ctxt: RPC context.
+ :param stack_name: Name of the stack you want to see.
+ :param params: Dict of http request parameters passed in from API side.
+ """
+ return self.call(ctxt, self.make_msg('get_template',
+ stack_name=stack_name, params=params),
+ topic=_engine_topic(self.topic, ctxt, None))
+
+ def delete_stack(self, ctxt, stack_name, params, cast=True):
+ """
+ The delete_stack method deletes a given stack.
+
+ :param ctxt: RPC context.
+ :param stack_name: Name of the stack you want to delete.
+ :param params: Params passed from API.
+ """
+ rpc_method = self.cast if cast else self.call
+ return rpc_method(ctxt, self.make_msg('delete_stack',
+ stack_name=stack_name, params=params),
+ topic=_engine_topic(self.topic, ctxt, None))
+
+ def list_events(self, ctxt, stack_name, params):
+ """
+ The list_events method lists all events associated with a given stack.
+
+ :param ctxt: RPC context.
+ :param stack_name: Name of the stack you want to get events for.
+ :param params: Params passed from API.
+ """
+ return self.call(ctxt, self.make_msg('list_events',
+ stack_name=stack_name, params=params),
+ topic=_engine_topic(self.topic, ctxt, None))
+
+ def describe_stack_resource(self, ctxt, stack_name, resource_name):
+ return self.call(ctxt, self.make_msg('describe_stack_resource',
+ stack_name=stack_name, resource_name=resource_name),
+ topic=_engine_topic(self.topic, ctxt, None))
+
+ def describe_stack_resources(self, ctxt, stack_name,
+ physical_resource_id, logical_resource_id):
+ return self.call(ctxt, self.make_msg('describe_stack_resources',
+ stack_name=stack_name,
+ physical_resource_id=physical_resource_id,
+ logical_resource_id=logical_resource_id),
+ topic=_engine_topic(self.topic, ctxt, None))
+
+ def list_stack_resources(self, ctxt, stack_name):
+ return self.call(ctxt, self.make_msg('list_stack_resources',
+ stack_name=stack_name),
+ topic=_engine_topic(self.topic, ctxt, None))
+
+ def metadata_list_stacks(self, ctxt):
+ """
+ Return the names of the stacks registered with Heat.
+ """
+ return self.call(ctxt, self.make_msg('metadata_list_stacks'),
+ topic=_engine_topic(self.topic, ctxt, None))
+
+ def metadata_list_resources(self, ctxt, stack_name):
+ """
+ Return the resource IDs of the given stack.
+ """
+ return self.call(ctxt, self.make_msg('metadata_list_resources',
+ stack_name=stack_name),
+ topic=_engine_topic(self.topic, ctxt, None))
+
+ def metadata_get_resource(self, ctxt, stack_name, resource_name):
+ """
+ Get the metadata for the given resource.
+ """
+ return self.call(ctxt, self.make_msg('metadata_get_resource',
+ stack_name=stack_name, resource_name=resource_name),
+ topic=_engine_topic(self.topic, ctxt, None))
+
+ def metadata_update(self, ctxt, stack_id, resource_name, metadata):
+ """
+ Update the metadata for the given resource.
+ """
+ return self.call(ctxt, self.make_msg('metadata_update',
+ stack_id=stack_id,
+ resource_name=resource_name, metadata=metadata),
+ topic=_engine_topic(self.topic, ctxt, None))
+
+ def event_create(self, ctxt, event):
+ return self.call(ctxt, self.make_msg('event_create',
+ event=event),
+ topic=_engine_topic(self.topic, ctxt, None))
+
+ def create_watch_data(self, ctxt, watch_name, stats_data):
+ '''
+ This could be used by CloudWatch and WaitConditions
+ and treat HA service events like any other CloudWatch.
+ '''
+ return self.call(ctxt, self.make_msg('create_watch_data',
+ watch_name=watch_name, stats_data=stats_data),
+ topic=_engine_topic(self.topic, ctxt, None))
--- /dev/null
+# vim: tabstop=4 shiftwidth=4 softtabstop=4
+
+# Copyright 2012, Red Hat, Inc.
+#
+# Licensed under the Apache License, Version 2.0 (the "License"); you may
+# not use this file except in compliance with the License. You may obtain
+# a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
+# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
+# License for the specific language governing permissions and limitations
+# under the License.
+
+"""
+Unit Tests for heat.engine.rpcapi
+"""
+
+
+import stubout
+import unittest
+
+from heat.common import config
+from heat.common import context
+from heat.engine import rpcapi as engine_rpcapi
+from heat.openstack.common import cfg
+from heat.openstack.common import rpc
+
+
+class EngineRpcAPITestCase(unittest.TestCase):
+
+ def setUp(self):
+ self.context = context.get_admin_context()
+ config.register_engine_opts()
+ cfg.CONF.set_default('rpc_backend',
+ 'heat.openstack.common.rpc.impl_fake')
+ cfg.CONF.set_default('verbose', True)
+ cfg.CONF.set_default('engine_topic', 'engine')
+ cfg.CONF.set_default('host', 'host')
+
+ self.stubs = stubout.StubOutForTesting()
+ super(EngineRpcAPITestCase, self).setUp()
+
+ def tearDown(self):
+ super(EngineRpcAPITestCase, self).tearDown()
+
+ def _test_engine_api(self, method, rpc_method, **kwargs):
+ ctxt = context.RequestContext('fake_user', 'fake_project')
+ if 'rpcapi_class' in kwargs:
+ rpcapi_class = kwargs['rpcapi_class']
+ del kwargs['rpcapi_class']
+ else:
+ rpcapi_class = engine_rpcapi.EngineAPI
+ rpcapi = rpcapi_class()
+ expected_retval = 'foo' if method == 'call' else None
+
+ expected_version = kwargs.pop('version', rpcapi.BASE_RPC_API_VERSION)
+ expected_msg = rpcapi.make_msg(method, **kwargs)
+
+ expected_msg['version'] = expected_version
+ expected_topic = '%s.%s' % (cfg.CONF.engine_topic, cfg.CONF.host)
+
+ cast_and_call = ['delete_stack']
+ if rpc_method == 'call' and method in cast_and_call:
+ kwargs['cast'] = False
+
+ self.fake_args = None
+ self.fake_kwargs = None
+
+ def _fake_rpc_method(*args, **kwargs):
+ self.fake_args = args
+ self.fake_kwargs = kwargs
+ if expected_retval:
+ return expected_retval
+
+ self.stubs.Set(rpc, rpc_method, _fake_rpc_method)
+
+ retval = getattr(rpcapi, method)(ctxt, **kwargs)
+
+ self.assertEqual(retval, expected_retval)
+ expected_args = [ctxt, expected_topic, expected_msg]
+ for arg, expected_arg in zip(self.fake_args, expected_args):
+ self.assertEqual(arg, expected_arg)
+
+ def test_show_stack(self):
+ self._test_engine_api('show_stack', 'call', stack_name='wordpress',
+ params={'Action': 'ListStacks'})
+
+ def test_create_stack(self):
+ self._test_engine_api('create_stack', 'call', stack_name='wordpress',
+ template={u'Foo': u'bar'},
+ params={u'InstanceType': u'm1.xlarge'},
+ args={'timeout_mins': u'30'})
+
+ def test_update_stack(self):
+ self._test_engine_api('update_stack', 'call', stack_name='wordpress',
+ template={u'Foo': u'bar'},
+ params={u'InstanceType': u'm1.xlarge'},
+ args={})
+
+ def test_validate_template(self):
+ self._test_engine_api('validate_template', 'call',
+ template={u'Foo': u'bar'}, params={})
+
+ def test_get_template(self):
+ self._test_engine_api('get_template', 'call',
+ stack_name='wordpress', params={})
+
+ def test_delete_stack_cast(self):
+ self._test_engine_api('delete_stack', 'cast',
+ stack_name='wordpress', params={})
+
+ def test_delete_stack_call(self):
+ self._test_engine_api('delete_stack', 'call',
+ stack_name='wordpress', params={})
+
+ def test_list_events(self):
+ self._test_engine_api('list_events', 'call',
+ stack_name='wordpress', params={})
+
+ def test_describe_stack_resource(self):
+ self._test_engine_api('describe_stack_resource', 'call',
+ stack_name='wordpress',
+ resource_name='LogicalResourceId')
+
+ def test_describe_stack_resources(self):
+ self._test_engine_api('describe_stack_resources', 'call',
+ stack_name='wordpress',
+ physical_resource_id=u'404d-a85b-5315293e67de',
+ logical_resource_id=u'WikiDatabase')
+
+ def test_list_stack_resources(self):
+ self._test_engine_api('list_stack_resources', 'call',
+ stack_name='wordpress')
+
+ def test_metadata_list_stacks(self):
+ self._test_engine_api('metadata_list_stacks', 'call')
+
+ def test_metadata_list_resources(self):
+ self._test_engine_api('metadata_list_resources', 'call',
+ stack_name='wordpress')
+
+ def test_metadata_get_resource(self):
+ self._test_engine_api('metadata_get_resource', 'call',
+ stack_name='wordpress',
+ resource_name='LogicalResourceId')
+
+ def test_metadata_update(self):
+ self._test_engine_api('metadata_update', 'call',
+ stack_id=6,
+ resource_name='LogicalResourceId',
+ metadata={u'wordpress': []})
+
+ def test_event_create(self):
+ self._test_engine_api('event_create', 'call',
+ event={
+ 'stack': 'wordpress',
+ 'resource': 'LogicalResourceId',
+ 'message': 'Foo',
+ 'reason': 'Bar'})
+
+ def test_create_watch_data(self):
+ self._test_engine_api('create_watch_data', 'call',
+ watch_name='watch1',
+ stats_data={})