Add collection count to stack list
Add an optional parameter ``with_count`` to the stack_list API to allow the inclusion of the collection count in the body of the response When passing the parameter, the response looks like: { 'stacks': [...], 'count': 42 } blueprint: count-stacks Change-Id: I560cfff9d327db9b325e7cf4786bd2fcd71d47b2
This commit is contained in:
parent
e1c2b622fa
commit
8143c80213
|
@ -167,10 +167,22 @@ class StackController(object):
|
||||||
}
|
}
|
||||||
params = util.get_allowed_params(req.params, whitelist)
|
params = util.get_allowed_params(req.params, whitelist)
|
||||||
filter_params = util.get_allowed_params(req.params, filter_whitelist)
|
filter_params = util.get_allowed_params(req.params, filter_whitelist)
|
||||||
stacks = self.engine.list_stacks(req.context, filters=filter_params,
|
|
||||||
|
stacks = self.engine.list_stacks(req.context,
|
||||||
|
filters=filter_params,
|
||||||
**params)
|
**params)
|
||||||
|
|
||||||
return stacks_view.collection(req, stacks)
|
count = None
|
||||||
|
if req.params.get('with_count'):
|
||||||
|
try:
|
||||||
|
# Check if engine has been updated to a version with
|
||||||
|
# support to count_stacks before trying to use it.
|
||||||
|
count = self.engine.count_stacks(req.context,
|
||||||
|
filters=filter_params)
|
||||||
|
except AttributeError as exc:
|
||||||
|
logger.warning("Old Engine Version: %s" % str(exc))
|
||||||
|
|
||||||
|
return stacks_view.collection(req, stacks=stacks, count=count)
|
||||||
|
|
||||||
@util.tenant_local
|
@util.tenant_local
|
||||||
def detail(self, req):
|
def detail(self, req):
|
||||||
|
|
|
@ -57,12 +57,14 @@ def format_stack(req, stack, keys=None):
|
||||||
transform(k, v) for k, v in stack.items()))
|
transform(k, v) for k, v in stack.items()))
|
||||||
|
|
||||||
|
|
||||||
def collection(req, stacks):
|
def collection(req, stacks, count=None):
|
||||||
formatted_stacks = [format_stack(req, s, basic_keys) for s in stacks]
|
formatted_stacks = [format_stack(req, s, basic_keys) for s in stacks]
|
||||||
|
|
||||||
result = {'stacks': formatted_stacks}
|
result = {'stacks': formatted_stacks}
|
||||||
links = views_common.get_collection_links(req, formatted_stacks)
|
links = views_common.get_collection_links(req, formatted_stacks)
|
||||||
if links:
|
if links:
|
||||||
result['links'] = links
|
result['links'] = links
|
||||||
|
if count is not None:
|
||||||
|
result['count'] = count
|
||||||
|
|
||||||
return result
|
return result
|
||||||
|
|
|
@ -129,8 +129,8 @@ def stack_get_all_by_tenant(context, limit=None, sort_keys=None,
|
||||||
marker, sort_dir, filters)
|
marker, sort_dir, filters)
|
||||||
|
|
||||||
|
|
||||||
def stack_count_all_by_tenant(context):
|
def stack_count_all_by_tenant(context, filters=None):
|
||||||
return IMPL.stack_count_all_by_tenant(context)
|
return IMPL.stack_count_all_by_tenant(context, filters=filters)
|
||||||
|
|
||||||
|
|
||||||
def stack_create(context, values):
|
def stack_create(context, values):
|
||||||
|
|
|
@ -336,8 +336,10 @@ def stack_get_all_by_tenant(context, limit=None, sort_keys=None, marker=None,
|
||||||
marker, sort_dir).all()
|
marker, sort_dir).all()
|
||||||
|
|
||||||
|
|
||||||
def stack_count_all_by_tenant(context):
|
def stack_count_all_by_tenant(context, filters=None):
|
||||||
return _query_stack_get_all_by_tenant(context).count()
|
query = _query_stack_get_all_by_tenant(context)
|
||||||
|
query = db_filters.exact_filter(query, models.Stack, filters)
|
||||||
|
return query.count()
|
||||||
|
|
||||||
|
|
||||||
def stack_create(context, values):
|
def stack_create(context, values):
|
||||||
|
|
|
@ -30,6 +30,8 @@ def exact_filter(query, model, filters):
|
||||||
"""
|
"""
|
||||||
|
|
||||||
filter_dict = {}
|
filter_dict = {}
|
||||||
|
if filters is None:
|
||||||
|
filters = {}
|
||||||
|
|
||||||
for key, value in filters.iteritems():
|
for key, value in filters.iteritems():
|
||||||
if isinstance(value, (list, tuple, set, frozenset)):
|
if isinstance(value, (list, tuple, set, frozenset)):
|
||||||
|
|
|
@ -235,6 +235,16 @@ class EngineService(service.Service):
|
||||||
sort_dir, filters) or []
|
sort_dir, filters) or []
|
||||||
return list(format_stack_details(stacks))
|
return list(format_stack_details(stacks))
|
||||||
|
|
||||||
|
@request_context
|
||||||
|
def count_stacks(self, cnxt, filters=None):
|
||||||
|
"""
|
||||||
|
Return the number of stacks that match the given filters
|
||||||
|
:param ctxt: RPC context.
|
||||||
|
:param filters: a dict of ATTR:VALUE to match agains stacks
|
||||||
|
:returns: a integer representing the number of matched stacks
|
||||||
|
"""
|
||||||
|
return db_api.stack_count_all_by_tenant(cnxt, filters=filters)
|
||||||
|
|
||||||
def _validate_deferred_auth_context(self, cnxt, stack):
|
def _validate_deferred_auth_context(self, cnxt, stack):
|
||||||
if cfg.CONF.deferred_auth_method != 'password':
|
if cfg.CONF.deferred_auth_method != 'password':
|
||||||
return
|
return
|
||||||
|
|
|
@ -69,6 +69,16 @@ class EngineClient(heat.openstack.common.rpc.proxy.RpcProxy):
|
||||||
sort_keys=sort_keys, marker=marker,
|
sort_keys=sort_keys, marker=marker,
|
||||||
sort_dir=sort_dir, filters=filters))
|
sort_dir=sort_dir, filters=filters))
|
||||||
|
|
||||||
|
def count_stacks(self, ctxt, filters=None):
|
||||||
|
"""
|
||||||
|
Return the number of stacks that match the given filters
|
||||||
|
:param ctxt: RPC context.
|
||||||
|
:param filters: a dict of ATTR:VALUE to match agains stacks
|
||||||
|
:returns: a integer representing the number of matched stacks
|
||||||
|
"""
|
||||||
|
return self.call(ctxt, self.make_msg('count_stacks',
|
||||||
|
filters=filters))
|
||||||
|
|
||||||
def show_stack(self, ctxt, stack_identity):
|
def show_stack(self, ctxt, stack_identity):
|
||||||
"""
|
"""
|
||||||
Return detailed information about one or all stacks.
|
Return detailed information about one or all stacks.
|
||||||
|
|
|
@ -26,6 +26,7 @@ from heat.common.wsgi import Request
|
||||||
from heat.common import urlfetch
|
from heat.common import urlfetch
|
||||||
from heat.openstack.common.rpc import common as rpc_common
|
from heat.openstack.common.rpc import common as rpc_common
|
||||||
from heat.rpc import api as rpc_api
|
from heat.rpc import api as rpc_api
|
||||||
|
from heat.rpc import client as rpc_client
|
||||||
from heat.tests.common import HeatTestCase
|
from heat.tests.common import HeatTestCase
|
||||||
|
|
||||||
import heat.api.openstack.v1 as api_v1
|
import heat.api.openstack.v1 as api_v1
|
||||||
|
@ -371,6 +372,7 @@ class StackControllerTest(ControllerTest, HeatTestCase):
|
||||||
self.assertIn('sort_keys', engine_args)
|
self.assertIn('sort_keys', engine_args)
|
||||||
self.assertIn('marker', engine_args)
|
self.assertIn('marker', engine_args)
|
||||||
self.assertIn('sort_dir', engine_args)
|
self.assertIn('sort_dir', engine_args)
|
||||||
|
self.assertIn('filters', engine_args)
|
||||||
self.assertNotIn('balrog', engine_args)
|
self.assertNotIn('balrog', engine_args)
|
||||||
|
|
||||||
@mock.patch.object(rpc, 'call')
|
@mock.patch.object(rpc, 'call')
|
||||||
|
@ -395,6 +397,41 @@ class StackControllerTest(ControllerTest, HeatTestCase):
|
||||||
self.assertIn('name', filters)
|
self.assertIn('name', filters)
|
||||||
self.assertNotIn('balrog', filters)
|
self.assertNotIn('balrog', filters)
|
||||||
|
|
||||||
|
def test_index_returns_stack_count_if_with_count_is_truthy(self):
|
||||||
|
params = {'with_count': 'True'}
|
||||||
|
req = self._get('/stacks', params=params)
|
||||||
|
engine = self.controller.engine
|
||||||
|
|
||||||
|
engine.list_stacks = mock.Mock(return_value=[])
|
||||||
|
engine.count_stacks = mock.Mock(return_value=0)
|
||||||
|
|
||||||
|
result = self.controller.index(req, tenant_id='fake_tenant_id')
|
||||||
|
self.assertEqual(0, result['count'])
|
||||||
|
|
||||||
|
def test_index_doesnt_return_stack_count_if_with_count_is_falsy(self):
|
||||||
|
params = {'with_count': ''}
|
||||||
|
req = self._get('/stacks', params=params)
|
||||||
|
engine = self.controller.engine
|
||||||
|
|
||||||
|
engine.list_stacks = mock.Mock(return_value=[])
|
||||||
|
engine.count_stacks = mock.Mock()
|
||||||
|
|
||||||
|
result = self.controller.index(req, tenant_id='fake_tenant_id')
|
||||||
|
self.assertNotIn('count', result)
|
||||||
|
assert not engine.count_stacks.called
|
||||||
|
|
||||||
|
@mock.patch.object(rpc_client.EngineClient, 'count_stacks')
|
||||||
|
def test_index_doesnt_break_with_old_engine(self, mock_count_stacks):
|
||||||
|
params = {'with_count': 'Truthy'}
|
||||||
|
req = self._get('/stacks', params=params)
|
||||||
|
engine = self.controller.engine
|
||||||
|
|
||||||
|
engine.list_stacks = mock.Mock(return_value=[])
|
||||||
|
mock_count_stacks.side_effect = AttributeError("Should not exist")
|
||||||
|
|
||||||
|
result = self.controller.index(req, tenant_id='fake_tenant_id')
|
||||||
|
self.assertNotIn('count', result)
|
||||||
|
|
||||||
@mock.patch.object(rpc, 'call')
|
@mock.patch.object(rpc, 'call')
|
||||||
def test_detail(self, mock_call):
|
def test_detail(self, mock_call):
|
||||||
req = self._get('/stacks/detail')
|
req = self._get('/stacks/detail')
|
||||||
|
|
|
@ -134,3 +134,25 @@ class TestStacksViewBuilder(HeatTestCase):
|
||||||
mock_get_collection_links.return_value = None
|
mock_get_collection_links.return_value = None
|
||||||
stack_view = stacks_view.collection(self.request, stacks)
|
stack_view = stacks_view.collection(self.request, stacks)
|
||||||
self.assertNotIn('links', stack_view)
|
self.assertNotIn('links', stack_view)
|
||||||
|
|
||||||
|
@mock.patch.object(stacks_view.views_common, 'get_collection_links')
|
||||||
|
def test_append_collection_count(self, mock_get_collection_links):
|
||||||
|
stacks = [self.stack1]
|
||||||
|
count = 1
|
||||||
|
stack_view = stacks_view.collection(self.request, stacks, count)
|
||||||
|
self.assertIn('count', stack_view)
|
||||||
|
self.assertEqual(1, stack_view['count'])
|
||||||
|
|
||||||
|
@mock.patch.object(stacks_view.views_common, 'get_collection_links')
|
||||||
|
def test_doesnt_append_collection_count(self, mock_get_collection_links):
|
||||||
|
stacks = [self.stack1]
|
||||||
|
stack_view = stacks_view.collection(self.request, stacks)
|
||||||
|
self.assertNotIn('count', stack_view)
|
||||||
|
|
||||||
|
@mock.patch.object(stacks_view.views_common, 'get_collection_links')
|
||||||
|
def test_appends_collection_count_of_zero(self, mock_get_collection_links):
|
||||||
|
stacks = [self.stack1]
|
||||||
|
count = 0
|
||||||
|
stack_view = stacks_view.collection(self.request, stacks, count)
|
||||||
|
self.assertIn('count', stack_view)
|
||||||
|
self.assertEqual(0, stack_view['count'])
|
||||||
|
|
|
@ -219,8 +219,8 @@ class SqlAlchemyTest(HeatTestCase):
|
||||||
self.assertEqual(1, len(st_db))
|
self.assertEqual(1, len(st_db))
|
||||||
|
|
||||||
def test_stack_get_all_by_tenant_and_filters(self):
|
def test_stack_get_all_by_tenant_and_filters(self):
|
||||||
stack1 = self._setup_test_stack('foo', UUIDs[0])
|
stack1 = self._setup_test_stack('foo', UUID1)
|
||||||
stack2 = self._setup_test_stack('bar', UUIDs[1])
|
stack2 = self._setup_test_stack('bar', UUID2)
|
||||||
stacks = [stack1, stack2]
|
stacks = [stack1, stack2]
|
||||||
|
|
||||||
filters = {'name': 'foo'}
|
filters = {'name': 'foo'}
|
||||||
|
@ -231,8 +231,8 @@ class SqlAlchemyTest(HeatTestCase):
|
||||||
self.assertEqual('foo', results[0]['name'])
|
self.assertEqual('foo', results[0]['name'])
|
||||||
|
|
||||||
def test_stack_get_all_by_tenant_filter_matches_in_list(self):
|
def test_stack_get_all_by_tenant_filter_matches_in_list(self):
|
||||||
stack1 = self._setup_test_stack('foo', UUIDs[0])
|
stack1 = self._setup_test_stack('foo', UUID1)
|
||||||
stack2 = self._setup_test_stack('bar', UUIDs[1])
|
stack2 = self._setup_test_stack('bar', UUID2)
|
||||||
stacks = [stack1, stack2]
|
stacks = [stack1, stack2]
|
||||||
|
|
||||||
filters = {'name': ['bar', 'quux']}
|
filters = {'name': ['bar', 'quux']}
|
||||||
|
@ -243,8 +243,8 @@ class SqlAlchemyTest(HeatTestCase):
|
||||||
self.assertEqual('bar', results[0]['name'])
|
self.assertEqual('bar', results[0]['name'])
|
||||||
|
|
||||||
def test_stack_get_all_by_tenant_returns_all_if_no_filters(self):
|
def test_stack_get_all_by_tenant_returns_all_if_no_filters(self):
|
||||||
stack1 = self._setup_test_stack('foo', UUIDs[0])
|
stack1 = self._setup_test_stack('foo', UUID1)
|
||||||
stack2 = self._setup_test_stack('bar', UUIDs[1])
|
stack2 = self._setup_test_stack('bar', UUID2)
|
||||||
stacks = [stack1, stack2]
|
stacks = [stack1, stack2]
|
||||||
|
|
||||||
filters = None
|
filters = None
|
||||||
|
@ -328,6 +328,15 @@ class SqlAlchemyTest(HeatTestCase):
|
||||||
st_db = db_api.stack_count_all_by_tenant(self.ctx)
|
st_db = db_api.stack_count_all_by_tenant(self.ctx)
|
||||||
self.assertEqual(1, st_db)
|
self.assertEqual(1, st_db)
|
||||||
|
|
||||||
|
def test_stack_count_all_by_tenant_with_filters(self):
|
||||||
|
stack1 = self._setup_test_stack('foo', UUID1)
|
||||||
|
stack2 = self._setup_test_stack('bar', UUID2)
|
||||||
|
stack3 = self._setup_test_stack('bar', UUID3)
|
||||||
|
filters = {'name': 'bar'}
|
||||||
|
|
||||||
|
st_db = db_api.stack_count_all_by_tenant(self.ctx, filters=filters)
|
||||||
|
self.assertEqual(2, st_db)
|
||||||
|
|
||||||
def test_event_get_all_by_stack(self):
|
def test_event_get_all_by_stack(self):
|
||||||
stack = self._setup_test_stack('stack', UUID1)[1]
|
stack = self._setup_test_stack('stack', UUID1)[1]
|
||||||
|
|
||||||
|
|
Loading…
Reference in New Issue