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:
parent
170781b7c7
commit
0b50be300e
|
@ -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)
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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):
|
||||
|
|
|
@ -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):
|
||||
|
|
|
@ -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 = {}
|
||||
|
||||
|
|
|
@ -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):
|
||||
"""
|
||||
|
|
|
@ -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()
|
||||
|
|
|
@ -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()
|
||||
|
||||
|
|
|
@ -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')
|
||||
|
|
|
@ -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',
|
||||
|
|
|
@ -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)
|
||||
|
|
Loading…
Reference in New Issue