Implement events pagination, sorting and filtering

It supports pagination(limit and marker),
sorting(sort_keys and sort_dir) and filtering(filters) of
the results for events-list.

Implements blueprint events-pagination

Change-Id: I1892d0c19d0c6355bb5c88e4a0bc4daa59460b72
This commit is contained in:
huangtianhua 2014-07-03 12:01:08 +08:00
parent 170781b7c7
commit 0b50be300e
11 changed files with 435 additions and 46 deletions

View File

@ -80,10 +80,16 @@ class EventController(object):
self.options = options
self.rpc_client = rpc_client.EngineClient()
def _event_list(self, req, identity,
filter_func=lambda e: True, detail=False):
def _event_list(self, req, identity, filter_func=lambda e: True,
detail=False, filters=None, limit=None, marker=None,
sort_keys=None, sort_dir=None):
events = self.rpc_client.list_events(req.context,
identity)
identity,
filters=filters,
limit=limit,
marker=marker,
sort_keys=sort_keys,
sort_dir=sort_dir)
keys = None if detail else summary_keys
return [format_event(req, e, keys) for e in events if filter_func(e)]
@ -93,13 +99,31 @@ class EventController(object):
"""
Lists summary information for all events
"""
whitelist = {
'limit': 'single',
'marker': 'single',
'sort_dir': 'single',
'sort_keys': 'multi',
}
filter_whitelist = {
'resource_status': 'mixed',
'resource_action': 'mixed',
'resource_name': 'mixed',
'resource_type': 'mixed',
}
params = util.get_allowed_params(req.params, whitelist)
filter_params = util.get_allowed_params(req.params, filter_whitelist)
if not filter_params:
filter_params = None
if resource_name is None:
events = self._event_list(req, identity)
events = self._event_list(req, identity,
filters=filter_params, **params)
else:
res_match = lambda e: e[engine_api.EVENT_RES_NAME] == resource_name
events = self._event_list(req, identity, res_match)
events = self._event_list(req, identity, res_match,
filters=filter_params, **params)
if not events:
msg = _('No events found for resource %s') % resource_name
raise exc.HTTPNotFound(msg)

View File

@ -15,6 +15,7 @@
Stack endpoint for Heat v1 ReST API.
"""
from six.moves.urllib import parse
from webob import exc
from heat.api.openstack.v1 import util
@ -270,6 +271,10 @@ class StackController(object):
if path:
location = '/'.join([location, path])
params = req.params
if params:
location += '?%s' % parse.urlencode(params, True)
raise exc.HTTPFound(location=location)
@util.identified_stack

View File

@ -187,12 +187,24 @@ def event_get_all(context):
return IMPL.event_get_all(context)
def event_get_all_by_tenant(context):
return IMPL.event_get_all_by_tenant(context)
def event_get_all_by_tenant(context, limit=None, marker=None,
sort_keys=None, sort_dir=None, filters=None):
return IMPL.event_get_all_by_tenant(context,
limit=limit,
marker=marker,
sort_keys=sort_keys,
sort_dir=sort_dir,
filters=filters)
def event_get_all_by_stack(context, stack_id):
return IMPL.event_get_all_by_stack(context, stack_id)
def event_get_all_by_stack(context, stack_id, limit=None, marker=None,
sort_keys=None, sort_dir=None, filters=None):
return IMPL.event_get_all_by_stack(context, stack_id,
limit=limit,
marker=marker,
sort_keys=sort_keys,
sort_dir=sort_dir,
filters=filters)
def event_count_all_by_stack(context, stack_id):

View File

@ -527,15 +527,16 @@ def event_get_all(context):
return results
def event_get_all_by_tenant(context):
stacks = soft_delete_aware_query(context, models.Stack).\
filter_by(tenant=context.tenant_id).all()
results = []
for stack in stacks:
results.extend(model_query(context, models.Event).
filter_by(stack_id=stack.id).all())
def event_get_all_by_tenant(context, limit=None, marker=None,
sort_keys=None, sort_dir=None, filters=None):
query = model_query(context, models.Event)
query = db_filters.exact_filter(query, models.Event, filters)
query = query.join(models.Event.stack).\
filter_by(tenant=context.tenant_id).filter_by(deleted_at=None)
filters = None
return results
return _events_filter_and_page_query(context, query, limit, marker,
sort_keys, sort_dir, filters).all()
def _query_all_by_stack(context, stack_id):
@ -544,9 +545,55 @@ def _query_all_by_stack(context, stack_id):
return query
def event_get_all_by_stack(context, stack_id):
return _query_all_by_stack(context, stack_id).order_by(
models.Event.id).all()
def event_get_all_by_stack(context, stack_id, limit=None, marker=None,
sort_keys=None, sort_dir=None, filters=None):
query = _query_all_by_stack(context, stack_id)
return _events_filter_and_page_query(context, query, limit, marker,
sort_keys, sort_dir, filters).all()
def _events_paginate_query(context, query, model, limit=None, sort_keys=None,
marker=None, sort_dir=None):
default_sort_keys = ['created_at']
if not sort_keys:
sort_keys = default_sort_keys
if not sort_dir:
sort_dir = 'desc'
# This assures the order of the stacks will always be the same
# even for sort_key values that are not unique in the database
sort_keys = sort_keys + ['id']
model_marker = None
if marker:
# not to use model_query(context, model).get(marker), because
# user can only see the ID(column 'uuid') and the ID as the marker
model_marker = model_query(context, model).filter_by(uuid=marker).\
first()
try:
query = utils.paginate_query(query, model, limit, sort_keys,
model_marker, sort_dir)
except utils.InvalidSortKey as exc:
raise exception.Invalid(reason=exc.message)
return query
def _events_filter_and_page_query(context, query,
limit=None, marker=None,
sort_keys=None, sort_dir=None,
filters=None):
if filters is None:
filters = {}
allowed_sort_keys = [models.Event.created_at.key,
models.Event.resource_type.key]
whitelisted_sort_keys = _filter_sort_keys(sort_keys, allowed_sort_keys)
query = db_filters.exact_filter(query, models.Event, filters)
return _events_paginate_query(context, query, models.Event, limit,
whitelisted_sort_keys, marker, sort_dir)
def event_count_all_by_stack(context, stack_id):

View File

@ -847,20 +847,37 @@ class EngineService(service.Service):
raise exception.ResourceTypeNotFound(type_name=type_name)
@request_context
def list_events(self, cnxt, stack_identity):
def list_events(self, cnxt, stack_identity, filters=None, limit=None,
marker=None, sort_keys=None, sort_dir=None):
"""
The list_events method lists all events associated with a given stack.
It supports pagination (``limit`` and ``marker``),
sorting (``sort_keys`` and ``sort_dir``) and filtering(filters)
of the results.
:param cnxt: RPC context.
:param stack_identity: Name of the stack you want to get events for.
:param stack_identity: Name of the stack you want to get events for
:param filters: a dict with attribute:value to filter the list
:param limit: the number of events to list (integer or string)
:param marker: the ID of the last event in the previous page
:param sort_keys: an array of fields used to sort the list
:param sort_dir: the direction of the sort ('asc' or 'desc').
"""
if stack_identity is not None:
st = self._get_stack(cnxt, stack_identity, show_deleted=True)
events = db_api.event_get_all_by_stack(cnxt, st.id)
events = db_api.event_get_all_by_stack(cnxt, st.id, limit=limit,
marker=marker,
sort_keys=sort_keys,
sort_dir=sort_dir,
filters=filters)
else:
events = db_api.event_get_all_by_tenant(cnxt)
events = db_api.event_get_all_by_tenant(cnxt, limit=limit,
marker=marker,
sort_keys=sort_keys,
sort_dir=sort_dir,
filters=filters)
stacks = {}

View File

@ -262,15 +262,29 @@ class EngineClient(object):
return self.call(ctxt, self.make_msg('generate_template',
type_name=type_name))
def list_events(self, ctxt, stack_identity):
def list_events(self, ctxt, stack_identity, filters=None, limit=None,
marker=None, sort_keys=None, sort_dir=None,):
"""
The list_events method lists all events associated with a given stack.
It supports pagination (``limit`` and ``marker``),
sorting (``sort_keys`` and ``sort_dir``) and filtering(filters)
of the results.
:param ctxt: RPC context.
:param stack_identity: Name of the stack you want to get events for.
:param stack_identity: Name of the stack you want to get events for
:param filters: a dict with attribute:value to filter the list
:param limit: the number of events to list (integer or string)
:param marker: the ID of the last event in the previous page
:param sort_keys: an array of fields used to sort the list
:param sort_dir: the direction of the sort ('asc' or 'desc').
"""
return self.call(ctxt, self.make_msg('list_events',
stack_identity=stack_identity))
stack_identity=stack_identity,
filters=filters,
limit=limit,
marker=marker,
sort_keys=sort_keys,
sort_dir=sort_dir))
def describe_stack_resource(self, ctxt, stack_identity, resource_name):
"""

View File

@ -1212,12 +1212,15 @@ class CfnStackControllerTest(HeatTestCase):
u'resource_properties': {u'UserData': u'blah'},
u'resource_type': u'AWS::EC2::Instance'}]
kwargs = {'stack_identity': identity,
'limit': None, 'sort_keys': None, 'marker': None,
'sort_dir': None, 'filters': None}
self.m.StubOutWithMock(rpc_client.EngineClient, 'call')
rpc_client.EngineClient.call(
dummy_req.context, ('identify_stack', {'stack_name': stack_name})
).AndReturn(identity)
rpc_client.EngineClient.call(
dummy_req.context, ('list_events', {'stack_identity': identity})
dummy_req.context, ('list_events', kwargs)
).AndReturn(engine_resp)
self.m.ReplayAll()

View File

@ -2164,6 +2164,10 @@ class EventControllerTest(ControllerTest, HeatTestCase):
req = self._get(stack_identity._tenant_path() +
'/resources/' + res_name + '/events')
kwargs = {'stack_identity': stack_identity,
'limit': None, 'sort_keys': None, 'marker': None,
'sort_dir': None, 'filters': None}
engine_resp = [
{
u'stack_name': u'wordpress',
@ -2195,8 +2199,7 @@ class EventControllerTest(ControllerTest, HeatTestCase):
]
self.m.StubOutWithMock(rpc_client.EngineClient, 'call')
rpc_client.EngineClient.call(
req.context,
('list_events', {'stack_identity': stack_identity})
req.context, ('list_events', kwargs)
).AndReturn(engine_resp)
self.m.ReplayAll()
@ -2246,6 +2249,10 @@ class EventControllerTest(ControllerTest, HeatTestCase):
req = self._get(stack_identity._tenant_path() + '/events')
kwargs = {'stack_identity': stack_identity,
'limit': None, 'sort_keys': None, 'marker': None,
'sort_dir': None, 'filters': None}
engine_resp = [
{
u'stack_name': u'wordpress',
@ -2264,7 +2271,7 @@ class EventControllerTest(ControllerTest, HeatTestCase):
self.m.StubOutWithMock(rpc_client.EngineClient, 'call')
rpc_client.EngineClient.call(
req.context,
('list_events', {'stack_identity': stack_identity})
('list_events', kwargs)
).AndReturn(engine_resp)
self.m.ReplayAll()
@ -2301,11 +2308,15 @@ class EventControllerTest(ControllerTest, HeatTestCase):
req = self._get(stack_identity._tenant_path() + '/events')
kwargs = {'stack_identity': stack_identity,
'limit': None, 'sort_keys': None, 'marker': None,
'sort_dir': None, 'filters': None}
error = heat_exc.StackNotFound(stack_name='a')
self.m.StubOutWithMock(rpc_client.EngineClient, 'call')
rpc_client.EngineClient.call(
req.context,
('list_events', {'stack_identity': stack_identity})
('list_events', kwargs)
).AndRaise(to_remote_error(error))
self.m.ReplayAll()
@ -2348,6 +2359,10 @@ class EventControllerTest(ControllerTest, HeatTestCase):
req = self._get(stack_identity._tenant_path() +
'/resources/' + res_name + '/events')
kwargs = {'stack_identity': stack_identity,
'limit': None, 'sort_keys': None, 'marker': None,
'sort_dir': None, 'filters': None}
engine_resp = [
{
u'stack_name': u'wordpress',
@ -2366,7 +2381,7 @@ class EventControllerTest(ControllerTest, HeatTestCase):
self.m.StubOutWithMock(rpc_client.EngineClient, 'call')
rpc_client.EngineClient.call(
req.context,
('list_events', {'stack_identity': stack_identity})
('list_events', kwargs)
).AndReturn(engine_resp)
self.m.ReplayAll()
@ -2378,6 +2393,81 @@ class EventControllerTest(ControllerTest, HeatTestCase):
resource_name=res_name)
self.m.VerifyAll()
@mock.patch.object(rpc_client.EngineClient, 'call')
def test_index_whitelists_pagination_params(self, mock_call, mock_enforce):
self._mock_enforce_setup(mock_enforce, 'index', True)
params = {
'limit': 'fake limit',
'sort_keys': 'fake sort keys',
'marker': 'fake marker',
'sort_dir': 'fake sort dir',
'balrog': 'you shall not pass!'
}
stack_identity = identifier.HeatIdentifier(self.tenant,
'wibble', '6')
req = self._get(stack_identity._tenant_path() + '/events',
params=params)
mock_call.return_value = []
self.controller.index(req, tenant_id=self.tenant,
stack_name=stack_identity.stack_name,
stack_id=stack_identity.stack_id)
rpc_call_args, _ = mock_call.call_args
engine_args = rpc_call_args[1][1]
self.assertEqual(6, len(engine_args))
self.assertIn('limit', engine_args)
self.assertEqual('fake limit', engine_args['limit'])
self.assertIn('sort_keys', engine_args)
self.assertEqual(['fake sort keys'], engine_args['sort_keys'])
self.assertIn('marker', engine_args)
self.assertEqual('fake marker', engine_args['marker'])
self.assertIn('sort_dir', engine_args)
self.assertEqual('fake sort dir', engine_args['sort_dir'])
self.assertIn('filters', engine_args)
self.assertIsNone(engine_args['filters'])
self.assertNotIn('balrog', engine_args)
@mock.patch.object(rpc_client.EngineClient, 'call')
def test_index_whitelist_filter_params(self, mock_call, mock_enforce):
self._mock_enforce_setup(mock_enforce, 'index', True)
params = {
'resource_status': 'COMPLETE',
'resource_action': 'CREATE',
'resource_name': 'my_server',
'resource_type': 'OS::Nova::Server',
'balrog': 'you shall not pass!'
}
stack_identity = identifier.HeatIdentifier(self.tenant,
'wibble', '6')
req = self._get(stack_identity._tenant_path() + '/events',
params=params)
mock_call.return_value = []
self.controller.index(req, tenant_id=self.tenant,
stack_name=stack_identity.stack_name,
stack_id=stack_identity.stack_id)
rpc_call_args, _ = mock_call.call_args
engine_args = rpc_call_args[1][1]
self.assertIn('filters', engine_args)
filters = engine_args['filters']
self.assertEqual(4, len(filters))
self.assertIn('resource_status', filters)
self.assertEqual('COMPLETE', filters['resource_status'])
self.assertIn('resource_action', filters)
self.assertEqual('CREATE', filters['resource_action'])
self.assertIn('resource_name', filters)
self.assertEqual('my_server', filters['resource_name'])
self.assertIn('resource_type', filters)
self.assertEqual('OS::Nova::Server', filters['resource_type'])
self.assertNotIn('balrog', filters)
def test_show_event_id_integer(self, mock_enforce):
self._test_show('42', mock_enforce)
@ -2399,6 +2489,10 @@ class EventControllerTest(ControllerTest, HeatTestCase):
req = self._get(stack_identity._tenant_path() +
'/resources/' + res_name + '/events/' + event_id)
kwargs = {'stack_identity': stack_identity,
'limit': None, 'sort_keys': None, 'marker': None,
'sort_dir': None, 'filters': None}
engine_resp = [
{
u'stack_name': u'wordpress',
@ -2431,7 +2525,7 @@ class EventControllerTest(ControllerTest, HeatTestCase):
self.m.StubOutWithMock(rpc_client.EngineClient, 'call')
rpc_client.EngineClient.call(
req.context,
('list_events', {'stack_identity': stack_identity})
('list_events', kwargs)
).AndReturn(engine_resp)
self.m.ReplayAll()
@ -2485,6 +2579,10 @@ class EventControllerTest(ControllerTest, HeatTestCase):
req = self._get(stack_identity._tenant_path() +
'/resources/' + res_name + '/events/' + event_id)
kwargs = {'stack_identity': stack_identity,
'limit': None, 'sort_keys': None, 'marker': None,
'sort_dir': None, 'filters': None}
engine_resp = [
{
u'stack_name': u'wordpress',
@ -2502,9 +2600,7 @@ class EventControllerTest(ControllerTest, HeatTestCase):
]
self.m.StubOutWithMock(rpc_client.EngineClient, 'call')
rpc_client.EngineClient.call(
req.context,
('list_events', {'stack_identity': stack_identity})
).AndReturn(engine_resp)
req.context, ('list_events', kwargs)).AndReturn(engine_resp)
self.m.ReplayAll()
self.assertRaises(webob.exc.HTTPNotFound,
@ -2529,6 +2625,10 @@ class EventControllerTest(ControllerTest, HeatTestCase):
req = self._get(stack_identity._tenant_path() +
'/resources/' + res_name + '/events/' + event_id)
kwargs = {'stack_identity': stack_identity,
'limit': None, 'sort_keys': None, 'marker': None,
'sort_dir': None, 'filters': None}
engine_resp = [
{
u'stack_name': u'wordpress',
@ -2546,9 +2646,7 @@ class EventControllerTest(ControllerTest, HeatTestCase):
]
self.m.StubOutWithMock(rpc_client.EngineClient, 'call')
rpc_client.EngineClient.call(
req.context,
('list_events', {'stack_identity': stack_identity})
).AndReturn(engine_resp)
req.context, ('list_events', kwargs)).AndReturn(engine_resp)
self.m.ReplayAll()
self.assertRaises(webob.exc.HTTPNotFound,
@ -2569,11 +2667,14 @@ class EventControllerTest(ControllerTest, HeatTestCase):
req = self._get(stack_identity._tenant_path() +
'/resources/' + res_name + '/events/' + event_id)
kwargs = {'stack_identity': stack_identity,
'limit': None, 'sort_keys': None, 'marker': None,
'sort_dir': None, 'filters': None}
error = heat_exc.StackNotFound(stack_name='a')
self.m.StubOutWithMock(rpc_client.EngineClient, 'call')
rpc_client.EngineClient.call(
req.context,
('list_events', {'stack_identity': stack_identity})
req.context, ('list_events', kwargs)
).AndRaise(to_remote_error(error))
self.m.ReplayAll()

View File

@ -1681,6 +1681,48 @@ class StackServiceTest(HeatTestCase):
self.m.VerifyAll()
@mock.patch.object(db_api, 'event_get_all_by_stack')
@mock.patch.object(service.EngineService, '_get_stack')
def test_stack_events_list_passes_marker_and_filters(self,
mock_get_stack,
mock_events_get_all):
limit = object()
marker = object()
sort_keys = object()
sort_dir = object()
filters = object()
s = mock.Mock(id=1)
mock_get_stack.return_value = s
self.eng.list_events(self.ctx, 1, limit=limit,
marker=marker, sort_keys=sort_keys,
sort_dir=sort_dir, filters=filters)
mock_events_get_all.assert_called_once_with(self.ctx,
1,
limit=limit,
sort_keys=sort_keys,
marker=marker,
sort_dir=sort_dir,
filters=filters)
@mock.patch.object(db_api, 'event_get_all_by_tenant')
def test_tenant_events_list_passes_marker_and_filters(
self, mock_tenant_events_get_all):
limit = object()
marker = object()
sort_keys = object()
sort_dir = object()
filters = object()
self.eng.list_events(self.ctx, None, limit=limit,
marker=marker, sort_keys=sort_keys,
sort_dir=sort_dir, filters=filters)
mock_tenant_events_get_all.assert_called_once_with(self.ctx,
limit=limit,
sort_keys=sort_keys,
marker=marker,
sort_dir=sort_dir,
filters=filters)
@stack_context('service_list_all_test_stack')
def test_stack_list_all(self):
self.m.StubOutWithMock(parser.Stack, '_from_db')

View File

@ -149,8 +149,13 @@ class EngineRpcAPITestCase(testtools.TestCase):
self._test_engine_api('generate_template', 'call', type_name="TYPE")
def test_list_events(self):
self._test_engine_api('list_events', 'call',
stack_identity=self.identity)
kwargs = {'stack_identity': self.identity,
'limit': None,
'marker': None,
'sort_keys': None,
'sort_dir': None,
'filters': None}
self._test_engine_api('list_events', 'call', **kwargs)
def test_describe_stack_resource(self):
self._test_engine_api('describe_stack_resource', 'call',

View File

@ -150,6 +150,13 @@ class SqlAlchemyTest(HeatTestCase):
assert mock_paginate_query.called
@mock.patch.object(db_api, '_events_paginate_query')
def test_events_filter_and_page_query(self, mock_events_paginate_query):
query = mock.Mock()
db_api._events_filter_and_page_query(self.ctx, query)
assert mock_events_paginate_query.called
@mock.patch.object(db_api.db_filters, 'exact_filter')
def test_filter_and_page_query_handles_no_filters(self, mock_db_filter):
query = mock.Mock()
@ -157,6 +164,14 @@ class SqlAlchemyTest(HeatTestCase):
mock_db_filter.assert_called_once_with(mock.ANY, mock.ANY, {})
@mock.patch.object(db_api.db_filters, 'exact_filter')
def test_events_filter_and_page_query_handles_no_filters(self,
mock_db_filter):
query = mock.Mock()
db_api._events_filter_and_page_query(self.ctx, query)
mock_db_filter.assert_called_once_with(mock.ANY, mock.ANY, {})
@mock.patch.object(db_api.db_filters, 'exact_filter')
def test_filter_and_page_query_applies_filters(self, mock_db_filter):
query = mock.Mock()
@ -165,6 +180,15 @@ class SqlAlchemyTest(HeatTestCase):
assert mock_db_filter.called
@mock.patch.object(db_api.db_filters, 'exact_filter')
def test_events_filter_and_page_query_applies_filters(self,
mock_db_filter):
query = mock.Mock()
filters = {'foo': 'bar'}
db_api._events_filter_and_page_query(self.ctx, query, filters=filters)
assert mock_db_filter.called
@mock.patch.object(db_api, '_paginate_query')
def test_filter_and_page_query_whitelists_sort_keys(self,
mock_paginate_query):
@ -175,6 +199,17 @@ class SqlAlchemyTest(HeatTestCase):
args, _ = mock_paginate_query.call_args
self.assertIn(['name'], args)
@mock.patch.object(db_api, '_events_paginate_query')
def test_events_filter_and_page_query_whitelists_sort_keys(
self, mock_paginate_query):
query = mock.Mock()
sort_keys = ['created_at', 'foo']
db_api._events_filter_and_page_query(self.ctx, query,
sort_keys=sort_keys)
args, _ = mock_paginate_query.call_args
self.assertIn(['created_at'], args)
@mock.patch.object(db_api.utils, 'paginate_query')
def test_paginate_query_default_sorts_by_created_at_and_id(
self, mock_paginate_query):
@ -535,12 +570,76 @@ class SqlAlchemyTest(HeatTestCase):
events = db_api.event_get_all_by_stack(self.ctx, UUID1)
self.assertEqual(2, len(events))
# test filter by resource_status
filters = {'resource_status': 'COMPLETE'}
events = db_api.event_get_all_by_stack(self.ctx, UUID1,
filters=filters)
self.assertEqual(1, len(events))
self.assertEqual('COMPLETE', events[0].resource_status)
# test filter by resource_action
filters = {'resource_action': 'CREATE'}
events = db_api.event_get_all_by_stack(self.ctx, UUID1,
filters=filters)
self.assertEqual(2, len(events))
self.assertEqual('CREATE', events[0].resource_action)
self.assertEqual('CREATE', events[1].resource_action)
# test filter by resource_type
filters = {'resource_type': 'AWS::EC2::Instance'}
events = db_api.event_get_all_by_stack(self.ctx, UUID1,
filters=filters)
self.assertEqual(2, len(events))
self.assertEqual('AWS::EC2::Instance', events[0].resource_type)
self.assertEqual('AWS::EC2::Instance', events[1].resource_type)
filters = {'resource_type': 'OS::Nova::Server'}
events = db_api.event_get_all_by_stack(self.ctx, UUID1,
filters=filters)
self.assertEqual(0, len(events))
# test limit and marker
events_all = db_api.event_get_all_by_stack(self.ctx, UUID1)
marker = events_all[0].uuid
expected = events_all[1].uuid
events = db_api.event_get_all_by_stack(self.ctx, UUID1,
limit=1, marker=marker)
self.assertEqual(1, len(events))
self.assertEqual(expected, events[0].uuid)
self._mock_delete(self.m)
self.m.ReplayAll()
stack.delete()
events = db_api.event_get_all_by_stack(self.ctx, UUID1)
self.assertEqual(4, len(events))
# test filter by resource_status
filters = {'resource_status': 'COMPLETE'}
events = db_api.event_get_all_by_stack(self.ctx, UUID1,
filters=filters)
self.assertEqual(2, len(events))
self.assertEqual('COMPLETE', events[0].resource_status)
self.assertEqual('COMPLETE', events[1].resource_status)
# test filter by resource_action
filters = {'resource_action': 'DELETE',
'resource_status': 'COMPLETE'}
events = db_api.event_get_all_by_stack(self.ctx, UUID1,
filters=filters)
self.assertEqual(1, len(events))
self.assertEqual('DELETE', events[0].resource_action)
self.assertEqual('COMPLETE', events[0].resource_status)
# test limit and marker
events_all = db_api.event_get_all_by_stack(self.ctx, UUID1)
self.assertEqual(4, len(events_all))
marker = events_all[1].uuid
events2_uuid = events_all[2].uuid
events3_uuid = events_all[3].uuid
events = db_api.event_get_all_by_stack(self.ctx, UUID1,
limit=1, marker=marker)
self.assertEqual(1, len(events))
self.assertEqual(events2_uuid, events[0].uuid)
events = db_api.event_get_all_by_stack(self.ctx, UUID1,
limit=2, marker=marker)
self.assertEqual(2, len(events))
self.assertEqual(events2_uuid, events[0].uuid)
self.assertEqual(events3_uuid, events[1].uuid)
self.m.VerifyAll()
@ -1605,6 +1704,26 @@ class DBAPIEventTest(HeatTestCase):
self.ctx.tenant_id = 'tenant1'
events = db_api.event_get_all_by_tenant(self.ctx)
self.assertEqual(2, len(events))
marker = events[0].uuid
expected = events[1].uuid
events = db_api.event_get_all_by_tenant(self.ctx,
marker=marker)
self.assertEqual(1, len(events))
self.assertEqual(expected, events[0].uuid)
events = db_api.event_get_all_by_tenant(self.ctx, limit=1)
self.assertEqual(1, len(events))
filters = {'resource_name': 'res2'}
events = db_api.event_get_all_by_tenant(self.ctx,
filters=filters)
self.assertEqual(1, len(events))
self.assertEqual('res2', events[0].resource_name)
sort_keys = 'resource_type'
events = db_api.event_get_all_by_tenant(self.ctx,
sort_keys=sort_keys)
self.assertEqual(2, len(events))
self.ctx.tenant_id = 'tenant2'
events = db_api.event_get_all_by_tenant(self.ctx)