]> review.fuel-infra Code Review - openstack-build/heat-build.git/commitdiff
ReST API: Add Events
authorZane Bitter <zbitter@redhat.com>
Thu, 22 Nov 2012 10:06:57 +0000 (11:06 +0100)
committerZane Bitter <zbitter@redhat.com>
Thu, 22 Nov 2012 13:16:15 +0000 (14:16 +0100)
Change-Id: I716dc2ad1c9294a7a9df27fbb77e32926b1ba307
Signed-off-by: Zane Bitter <zbitter@redhat.com>
docs/api.md
heat/api/openstack/v1/__init__.py
heat/api/openstack/v1/events.py [new file with mode: 0644]
heat/tests/test_api_openstack_v1.py

index 385a6d1cfcda4853d5737a2e7d4229bb36ffc9fa..2fe04da1f36e954260c424cb5324dd2978216df2 100644 (file)
@@ -223,3 +223,66 @@ Parameters:
 * `stack_name` The name of the stack to look up
 * `stack_id` The unique identifier of the stack to look up
 * `resource_name` The name of the resource in the template
+
+List Stack Events
+-----------------
+
+```
+GET /v1/{tenant_id}/stacks/{stack_name}/{stack_id}/events
+```
+
+Parameters:
+
+* `tenant_id` The unique identifier of the tenant or account
+* `stack_name` The name of the stack to look up
+* `stack_id` The unique identifier of the stack to look up
+
+Find Stack Events by Name
+-------------------------
+
+```
+GET /v1/{tenant_id}/stacks/{stack_name}/events
+```
+
+Parameters:
+
+* `stack_name` The name of the stack to look up
+
+Result:
+
+```
+HTTP/1.1 302 Found
+Location: http://heat.example.com:8004/v1/{tenant_id}/stacks/{stack_name}/{stack_id}/events
+```
+
+This is a shortcut to go directly to the list of stack events when only the stack name is known.
+
+
+List Resource Events
+--------------------
+
+```
+GET /v1/{tenant_id}/stacks/{stack_name}/{stack_id}/resources/{resource_name}/events
+```
+
+Parameters:
+
+* `tenant_id` The unique identifier of the tenant or account
+* `stack_name` The name of the stack to look up
+* `stack_id` The unique identifier of the stack to look up
+* `resource_name` The name of the resource in the template
+
+Get Event
+---------
+
+```
+GET /v1/{tenant_id}/stacks/{stack_name}/{stack_id}/resources/{resource_name}/events/{event_id}
+```
+
+Parameters:
+
+* `tenant_id` The unique identifier of the tenant or account
+* `stack_name` The name of the stack to look up
+* `stack_id` The unique identifier of the stack to look up
+* `resource_name` The name of the resource in the template
+* `event_id` The ID of the event
index f95f549d10c111d15e6ac6b48bcff9e762f2ae4c..5600b51ac94c2dacf419ed28a555f039f1c5089f 100644 (file)
@@ -20,6 +20,7 @@ gettext.install('heat', unicode=1)
 
 from heat.api.openstack.v1 import stacks
 from heat.api.openstack.v1 import resources
+from heat.api.openstack.v1 import events
 from heat.common import wsgi
 
 from heat.openstack.common import log as logging
@@ -61,8 +62,10 @@ class API(wsgi.Router):
             stack_mapper.connect("stack_lookup",
                                  "/stacks/{stack_name}",
                                  action="lookup")
+            subpaths = ['resources', 'events']
+            path = "{path:%s}" % '|'.join(subpaths)
             stack_mapper.connect("stack_lookup_subpath",
-                                 "/stacks/{stack_name}/{path:resources}",
+                                 "/stacks/{stack_name}/" + path,
                                  action="lookup",
                                  conditions={'method': 'GET'})
             stack_mapper.connect("stack_show",
@@ -106,4 +109,26 @@ class API(wsgi.Router):
                                action="metadata",
                                conditions={'method': 'GET'})
 
+        # Events
+        events_resource = events.create_resource(conf)
+        with mapper.submapper(controller=events_resource,
+                              path_prefix=stack_path) as ev_mapper:
+
+            # Stack event collection
+            ev_mapper.connect("event_index_stack",
+                              "/events",
+                              action="index",
+                              conditions={'method': 'GET'})
+            # Resource event collection
+            ev_mapper.connect("event_index_resource",
+                              "/resources/{resource_name}/events",
+                              action="index",
+                              conditions={'method': 'GET'})
+
+            # Event data
+            ev_mapper.connect("event_show",
+                              "/resources/{resource_name}/events/{event_id}",
+                              action="show",
+                              conditions={'method': 'GET'})
+
         super(API, self).__init__(mapper)
diff --git a/heat/api/openstack/v1/events.py b/heat/api/openstack/v1/events.py
new file mode 100644 (file)
index 0000000..545d53b
--- /dev/null
@@ -0,0 +1,132 @@
+# vim: tabstop=4 shiftwidth=4 softtabstop=4
+
+#
+#    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.
+
+import itertools
+from webob import exc
+
+from heat.api.openstack.v1 import util
+from heat.common import wsgi
+from heat.engine import api as engine_api
+from heat.engine import identifier
+from heat.engine import rpcapi as engine_rpcapi
+import heat.openstack.common.rpc.common as rpc_common
+from heat.openstack.common.gettextutils import _
+
+
+summary_keys = [
+    engine_api.EVENT_ID,
+    engine_api.EVENT_TIMESTAMP,
+    engine_api.EVENT_RES_NAME,
+    engine_api.EVENT_RES_STATUS,
+    engine_api.EVENT_RES_STATUS_DATA,
+    engine_api.EVENT_RES_PHYSICAL_ID,
+]
+
+
+def format_event(req, event, keys=None):
+    include_key = lambda k: k in keys if keys else True
+
+    def transform(key, value):
+        if not include_key(key):
+            return
+
+        if key == engine_api.EVENT_ID:
+            identity = identifier.EventIdentifier(**value)
+            yield ('id', identity.event_id)
+            yield ('links', [util.make_link(req, identity),
+                             util.make_link(req, identity.resource(),
+                                            'resource'),
+                             util.make_link(req, identity.stack(),
+                                            'stack')])
+        elif (key == engine_api.EVENT_STACK_ID or
+              key == engine_api.EVENT_STACK_NAME):
+            return
+        else:
+            yield (key, value)
+
+    return dict(itertools.chain.from_iterable(
+        transform(k, v) for k, v in event.items()))
+
+
+class EventController(object):
+    """
+    WSGI controller for Events in Heat v1 API
+    Implements the API actions
+    """
+
+    def __init__(self, options):
+        self.options = options
+        self.engine = engine_rpcapi.EngineAPI()
+
+    def _event_list(self, req, identity,
+                    filter_func=lambda e: True, detail=False):
+        try:
+            result = self.engine.list_events(req.context,
+                                             identity)
+        except rpc_common.RemoteError as ex:
+            return util.remote_error(ex)
+
+        if 'events' not in result:
+            raise exc.HTTPInternalServerError()
+        ev_list = result['events']
+
+        keys = None if detail else summary_keys
+
+        return [format_event(req, e, keys) for e in ev_list if filter_func(e)]
+
+    @util.identified_stack
+    def index(self, req, identity, resource_name=None):
+        """
+        Lists summary information for all resources
+        """
+
+        if resource_name is None:
+            events = self._event_list(req, identity)
+        else:
+            res_match = lambda e: e[engine_api.EVENT_RES_NAME] == resource_name
+
+            events = self._event_list(req, identity, res_match)
+            if not events:
+                msg = _('No events found for resource %s') % resource_name
+                raise exc.HTTPNotFound(msg)
+
+        return {'events': events}
+
+    @util.identified_stack
+    def show(self, req, identity, resource_name, event_id):
+        """
+        Gets detailed information for a stack
+        """
+
+        def event_match(ev):
+            identity = identifier.EventIdentifier(**ev[engine_api.EVENT_ID])
+            return (ev[engine_api.EVENT_RES_NAME] == resource_name and
+                    identity.event_id == event_id)
+
+        events = self._event_list(req, identity, event_match, True)
+        if not events:
+            raise exc.HTTPNotFound(_('No event %s found') % event_id)
+
+        return {'event': events[0]}
+
+
+def create_resource(options):
+    """
+    Events resource factory method.
+    """
+    # TODO(zaneb) handle XML based on Content-type/Accepts
+    deserializer = wsgi.JSONRequestDeserializer()
+    serializer = wsgi.JSONResponseSerializer()
+    return wsgi.Resource(EventController(options), deserializer, serializer)
index f94ca97a0b1c4b63ac478a743fae9e51ae32af5a..2e42f59c3a8a10c7885cecc3513ccde46acea971 100644 (file)
@@ -35,6 +35,7 @@ from heat.common.wsgi import Request
 
 import heat.api.openstack.v1.stacks as stacks
 import heat.api.openstack.v1.resources as resources
+import heat.api.openstack.v1.events as events
 
 
 @attr(tag=['unit', 'api-openstack-v1'])
@@ -1021,6 +1022,407 @@ class ResourceControllerTest(ControllerTest, unittest.TestCase):
         self.m.VerifyAll()
 
 
+@attr(tag=['unit', 'api-openstack-v1', 'EventController'])
+@attr(speed='fast')
+class EventControllerTest(ControllerTest, unittest.TestCase):
+    '''
+    Tests the API class which acts as the WSGI controller,
+    the endpoint processing API requests after they are routed
+    '''
+
+    def setUp(self):
+        # Create WSGI controller instance
+        class DummyConfig():
+            bind_port = 8004
+        cfgopts = DummyConfig()
+        self.controller = events.EventController(options=cfgopts)
+
+    def test_resource_index(self):
+        event_id = '42'
+        res_name = 'WikiDatabase'
+        stack_identity = identifier.HeatIdentifier(self.tenant,
+                                                   'wordpress', '6')
+        res_identity = identifier.ResourceIdentifier(resource_name=res_name,
+                                                     **stack_identity)
+        ev_identity = identifier.EventIdentifier(event_id=event_id,
+                                                 **res_identity)
+
+        req = self._get(stack_identity._tenant_path() +
+                        '/resources/' + res_name + '/events')
+
+        engine_resp = {u'events': [
+            {
+                u'stack_name': u'wordpress',
+                u'event_time': u'2012-07-23T13:05:39Z',
+                u'stack_identity': dict(stack_identity),
+                u'logical_resource_id': res_name,
+                u'resource_status_reason': u'state changed',
+                u'event_identity': dict(ev_identity),
+                u'resource_status': u'IN_PROGRESS',
+                u'physical_resource_id': None,
+                u'resource_properties': {u'UserData': u'blah'},
+                u'resource_type': u'AWS::EC2::Instance',
+            },
+            {
+                u'stack_name': u'wordpress',
+                u'event_time': u'2012-07-23T13:05:39Z',
+                u'stack_identity': dict(stack_identity),
+                u'logical_resource_id': 'SomeOtherResource',
+                u'resource_status_reason': u'state changed',
+                u'event_identity': dict(ev_identity),
+                u'resource_status': u'IN_PROGRESS',
+                u'physical_resource_id': None,
+                u'resource_properties': {u'UserData': u'blah'},
+                u'resource_type': u'AWS::EC2::Instance',
+            }
+        ]}
+        self.m.StubOutWithMock(rpc, 'call')
+        rpc.call(req.context, self.topic,
+                 {'method': 'list_events',
+                  'args': {'stack_identity': stack_identity},
+                  'version': self.api_version},
+                 None).AndReturn(engine_resp)
+        self.m.ReplayAll()
+
+        result = self.controller.index(req, tenant_id=self.tenant,
+                                       stack_name=stack_identity.stack_name,
+                                       stack_id=stack_identity.stack_id,
+                                       resource_name=res_name)
+
+        expected = {
+            'events': [
+                {
+                    'id': event_id,
+                    'links': [
+                        {'href': self._url(ev_identity), 'rel': 'self'},
+                        {'href': self._url(res_identity), 'rel': 'resource'},
+                        {'href': self._url(stack_identity), 'rel': 'stack'},
+                    ],
+                    u'logical_resource_id': res_name,
+                    u'resource_status_reason': u'state changed',
+                    u'event_time': u'2012-07-23T13:05:39Z',
+                    u'resource_status': u'IN_PROGRESS',
+                    u'physical_resource_id': None,
+                }
+            ]
+        }
+
+        self.assertEqual(result, expected)
+        self.m.VerifyAll()
+
+    def test_stack_index(self):
+        event_id = '42'
+        res_name = 'WikiDatabase'
+        stack_identity = identifier.HeatIdentifier(self.tenant,
+                                                   'wordpress', '6')
+        res_identity = identifier.ResourceIdentifier(resource_name=res_name,
+                                                     **stack_identity)
+        ev_identity = identifier.EventIdentifier(event_id=event_id,
+                                                 **res_identity)
+
+        req = self._get(stack_identity._tenant_path() + '/events')
+
+        engine_resp = {u'events': [
+            {
+                u'stack_name': u'wordpress',
+                u'event_time': u'2012-07-23T13:05:39Z',
+                u'stack_identity': dict(stack_identity),
+                u'logical_resource_id': res_name,
+                u'resource_status_reason': u'state changed',
+                u'event_identity': dict(ev_identity),
+                u'resource_status': u'IN_PROGRESS',
+                u'physical_resource_id': None,
+                u'resource_properties': {u'UserData': u'blah'},
+                u'resource_type': u'AWS::EC2::Instance',
+            }
+        ]}
+        self.m.StubOutWithMock(rpc, 'call')
+        rpc.call(req.context, self.topic,
+                 {'method': 'list_events',
+                  'args': {'stack_identity': stack_identity},
+                  'version': self.api_version},
+                 None).AndReturn(engine_resp)
+        self.m.ReplayAll()
+
+        result = self.controller.index(req, tenant_id=self.tenant,
+                                       stack_name=stack_identity.stack_name,
+                                       stack_id=stack_identity.stack_id)
+
+        expected = {
+            'events': [
+                {
+                    'id': event_id,
+                    'links': [
+                        {'href': self._url(ev_identity), 'rel': 'self'},
+                        {'href': self._url(res_identity), 'rel': 'resource'},
+                        {'href': self._url(stack_identity), 'rel': 'stack'},
+                    ],
+                    u'logical_resource_id': res_name,
+                    u'resource_status_reason': u'state changed',
+                    u'event_time': u'2012-07-23T13:05:39Z',
+                    u'resource_status': u'IN_PROGRESS',
+                    u'physical_resource_id': None,
+                }
+            ]
+        }
+
+        self.assertEqual(result, expected)
+        self.m.VerifyAll()
+
+    def test_index_stack_nonexist(self):
+        stack_identity = identifier.HeatIdentifier(self.tenant,
+                                                   'wibble', '6')
+
+        req = self._get(stack_identity._tenant_path() + '/events')
+
+        self.m.StubOutWithMock(rpc, 'call')
+        rpc.call(req.context, self.topic,
+                 {'method': 'list_events',
+                  'args': {'stack_identity': stack_identity},
+                  'version': self.api_version},
+                 None).AndRaise(rpc_common.RemoteError("AttributeError"))
+        self.m.ReplayAll()
+
+        self.assertRaises(webob.exc.HTTPNotFound,
+                          self.controller.index,
+                          req, tenant_id=self.tenant,
+                          stack_name=stack_identity.stack_name,
+                          stack_id=stack_identity.stack_id)
+        self.m.VerifyAll()
+
+    def test_index_resource_nonexist(self):
+        event_id = '42'
+        res_name = 'WikiDatabase'
+        stack_identity = identifier.HeatIdentifier(self.tenant,
+                                                   'wordpress', '6')
+        res_identity = identifier.ResourceIdentifier(resource_name=res_name,
+                                                     **stack_identity)
+        ev_identity = identifier.EventIdentifier(event_id=event_id,
+                                                 **res_identity)
+
+        req = self._get(stack_identity._tenant_path() +
+                        '/resources/' + res_name + '/events')
+
+        engine_resp = {u'events': [
+            {
+                u'stack_name': u'wordpress',
+                u'event_time': u'2012-07-23T13:05:39Z',
+                u'stack_identity': dict(stack_identity),
+                u'logical_resource_id': 'SomeOtherResource',
+                u'resource_status_reason': u'state changed',
+                u'event_identity': dict(ev_identity),
+                u'resource_status': u'IN_PROGRESS',
+                u'physical_resource_id': None,
+                u'resource_properties': {u'UserData': u'blah'},
+                u'resource_type': u'AWS::EC2::Instance',
+            }
+        ]}
+        self.m.StubOutWithMock(rpc, 'call')
+        rpc.call(req.context, self.topic,
+                 {'method': 'list_events',
+                  'args': {'stack_identity': stack_identity},
+                  'version': self.api_version},
+                 None).AndReturn(engine_resp)
+        self.m.ReplayAll()
+
+        self.assertRaises(webob.exc.HTTPNotFound,
+                          self.controller.index,
+                          req, tenant_id=self.tenant,
+                          stack_name=stack_identity.stack_name,
+                          stack_id=stack_identity.stack_id,
+                          resource_name=res_name)
+        self.m.VerifyAll()
+
+    def test_show(self):
+        event_id = '42'
+        res_name = 'WikiDatabase'
+        stack_identity = identifier.HeatIdentifier(self.tenant,
+                                                   'wordpress', '6')
+        res_identity = identifier.ResourceIdentifier(resource_name=res_name,
+                                                     **stack_identity)
+        ev1_identity = identifier.EventIdentifier(event_id='41',
+                                                  **res_identity)
+        ev_identity = identifier.EventIdentifier(event_id=event_id,
+                                                 **res_identity)
+
+        req = self._get(stack_identity._tenant_path() +
+                        '/resources/' + res_name + '/events/' + event_id)
+
+        engine_resp = {u'events': [
+            {
+                u'stack_name': u'wordpress',
+                u'event_time': u'2012-07-23T13:05:39Z',
+                u'stack_identity': dict(stack_identity),
+                u'logical_resource_id': res_name,
+                u'resource_status_reason': u'state changed',
+                u'event_identity': dict(ev1_identity),
+                u'resource_status': u'IN_PROGRESS',
+                u'physical_resource_id': None,
+                u'resource_properties': {u'UserData': u'blah'},
+                u'resource_type': u'AWS::EC2::Instance',
+            },
+            {
+                u'stack_name': u'wordpress',
+                u'event_time': u'2012-07-23T13:06:00Z',
+                u'stack_identity': dict(stack_identity),
+                u'logical_resource_id': res_name,
+                u'resource_status_reason': u'state changed',
+                u'event_identity': dict(ev_identity),
+                u'resource_status': u'CREATE_COMPLETE',
+                u'physical_resource_id':
+                    u'a3455d8c-9f88-404d-a85b-5315293e67de',
+                u'resource_properties': {u'UserData': u'blah'},
+                u'resource_type': u'AWS::EC2::Instance',
+            }
+        ]}
+        self.m.StubOutWithMock(rpc, 'call')
+        rpc.call(req.context, self.topic,
+                 {'method': 'list_events',
+                  'args': {'stack_identity': stack_identity},
+                  'version': self.api_version},
+                 None).AndReturn(engine_resp)
+        self.m.ReplayAll()
+
+        result = self.controller.show(req, tenant_id=self.tenant,
+                                      stack_name=stack_identity.stack_name,
+                                      stack_id=stack_identity.stack_id,
+                                      resource_name=res_name,
+                                      event_id=event_id)
+
+        expected = {
+            'event': {
+                'id': event_id,
+                'links': [
+                    {'href': self._url(ev_identity), 'rel': 'self'},
+                    {'href': self._url(res_identity), 'rel': 'resource'},
+                    {'href': self._url(stack_identity), 'rel': 'stack'},
+                ],
+                u'logical_resource_id': res_name,
+                u'resource_status_reason': u'state changed',
+                u'event_time': u'2012-07-23T13:06:00Z',
+                u'resource_status': u'CREATE_COMPLETE',
+                u'physical_resource_id':
+                    u'a3455d8c-9f88-404d-a85b-5315293e67de',
+                u'resource_type': u'AWS::EC2::Instance',
+                u'resource_properties': {u'UserData': u'blah'},
+            }
+        }
+
+        self.assertEqual(result, expected)
+        self.m.VerifyAll()
+
+    def test_show_nonexist(self):
+        event_id = '42'
+        res_name = 'WikiDatabase'
+        stack_identity = identifier.HeatIdentifier(self.tenant,
+                                                   'wordpress', '6')
+        res_identity = identifier.ResourceIdentifier(resource_name=res_name,
+                                                     **stack_identity)
+        ev_identity = identifier.EventIdentifier(event_id='41',
+                                                  **res_identity)
+
+        req = self._get(stack_identity._tenant_path() +
+                        '/resources/' + res_name + '/events/' + event_id)
+
+        engine_resp = {u'events': [
+            {
+                u'stack_name': u'wordpress',
+                u'event_time': u'2012-07-23T13:05:39Z',
+                u'stack_identity': dict(stack_identity),
+                u'logical_resource_id': res_name,
+                u'resource_status_reason': u'state changed',
+                u'event_identity': dict(ev_identity),
+                u'resource_status': u'IN_PROGRESS',
+                u'physical_resource_id': None,
+                u'resource_properties': {u'UserData': u'blah'},
+                u'resource_type': u'AWS::EC2::Instance',
+            }
+        ]}
+        self.m.StubOutWithMock(rpc, 'call')
+        rpc.call(req.context, self.topic,
+                 {'method': 'list_events',
+                  'args': {'stack_identity': stack_identity},
+                  'version': self.api_version},
+                 None).AndReturn(engine_resp)
+        self.m.ReplayAll()
+
+        self.assertRaises(webob.exc.HTTPNotFound,
+                          self.controller.show,
+                          req, tenant_id=self.tenant,
+                          stack_name=stack_identity.stack_name,
+                          stack_id=stack_identity.stack_id,
+                          resource_name=res_name, event_id=event_id)
+        self.m.VerifyAll()
+
+    def test_show_bad_resource(self):
+        event_id = '42'
+        res_name = 'WikiDatabase'
+        stack_identity = identifier.HeatIdentifier(self.tenant,
+                                                   'wordpress', '6')
+        res_identity = identifier.ResourceIdentifier(resource_name=res_name,
+                                                     **stack_identity)
+        ev_identity = identifier.EventIdentifier(event_id='41',
+                                                  **res_identity)
+
+        req = self._get(stack_identity._tenant_path() +
+                        '/resources/' + res_name + '/events/' + event_id)
+
+        engine_resp = {u'events': [
+            {
+                u'stack_name': u'wordpress',
+                u'event_time': u'2012-07-23T13:05:39Z',
+                u'stack_identity': dict(stack_identity),
+                u'logical_resource_id': 'SomeOtherResourceName',
+                u'resource_status_reason': u'state changed',
+                u'event_identity': dict(ev_identity),
+                u'resource_status': u'IN_PROGRESS',
+                u'physical_resource_id': None,
+                u'resource_properties': {u'UserData': u'blah'},
+                u'resource_type': u'AWS::EC2::Instance',
+            }
+        ]}
+        self.m.StubOutWithMock(rpc, 'call')
+        rpc.call(req.context, self.topic,
+                 {'method': 'list_events',
+                  'args': {'stack_identity': stack_identity},
+                  'version': self.api_version},
+                 None).AndReturn(engine_resp)
+        self.m.ReplayAll()
+
+        self.assertRaises(webob.exc.HTTPNotFound,
+                          self.controller.show,
+                          req, tenant_id=self.tenant,
+                          stack_name=stack_identity.stack_name,
+                          stack_id=stack_identity.stack_id,
+                          resource_name=res_name, event_id=event_id)
+        self.m.VerifyAll()
+
+    def test_show_stack_nonexist(self):
+        event_id = '42'
+        res_name = 'WikiDatabase'
+        stack_identity = identifier.HeatIdentifier(self.tenant,
+                                                   'wibble', '6')
+
+        req = self._get(stack_identity._tenant_path() +
+                        '/resources/' + res_name + '/events/' + event_id)
+
+        self.m.StubOutWithMock(rpc, 'call')
+        rpc.call(req.context, self.topic,
+                 {'method': 'list_events',
+                  'args': {'stack_identity': stack_identity},
+                  'version': self.api_version},
+                 None).AndRaise(rpc_common.RemoteError("AttributeError"))
+        self.m.ReplayAll()
+
+        self.assertRaises(webob.exc.HTTPNotFound,
+                          self.controller.show,
+                          req, tenant_id=self.tenant,
+                          stack_name=stack_identity.stack_name,
+                          stack_id=stack_identity.stack_id,
+                          resource_name=res_name, event_id=event_id)
+        self.m.VerifyAll()
+
+
 if __name__ == '__main__':
     sys.argv.append(__file__)
     nose.main()