Merge "Fix no links property in pool list response"
This commit is contained in:
commit
8538f9905a
@ -86,7 +86,7 @@ class TestPools(base.V1_1FunctionalTestBase):
|
||||
# Test existence
|
||||
result = self.client.get('/'+pool_name+'?detailed=true')
|
||||
self.assertEqual(result.status_code, 200)
|
||||
self.assertSchema(result.json(), 'pool-detail')
|
||||
self.assertSchema(result.json(), 'pool_detail')
|
||||
|
||||
@ddt.data(
|
||||
{
|
||||
@ -142,7 +142,7 @@ class TestPools(base.V1_1FunctionalTestBase):
|
||||
|
||||
result = self.client.get()
|
||||
self.assertEqual(result.status_code, 200)
|
||||
self.assertSchema(result.json(), 'pool-list')
|
||||
self.assertSchema(result.json(), 'pool_list')
|
||||
|
||||
@ddt.data(
|
||||
{
|
||||
|
@ -143,6 +143,58 @@ class ResponseSchema(api.Api):
|
||||
'required': ['messages'],
|
||||
'additionalProperties': False
|
||||
},
|
||||
|
||||
'pool_list': {
|
||||
'type': 'object',
|
||||
'properties': {
|
||||
'links': {
|
||||
'type': 'array',
|
||||
'items': {
|
||||
'type': 'object',
|
||||
'properties': {
|
||||
'rel': {
|
||||
'type': 'string'
|
||||
},
|
||||
'href': {
|
||||
'type': 'string',
|
||||
'pattern': '^/v1/pools\?'
|
||||
}
|
||||
},
|
||||
'required': ['rel', 'href'],
|
||||
'additionalProperties': False
|
||||
}
|
||||
},
|
||||
'pools': {
|
||||
'type': 'array',
|
||||
'items': {
|
||||
'type': 'object',
|
||||
'properties': {
|
||||
'href': {
|
||||
'type': 'string',
|
||||
'pattern': '^/v1/'
|
||||
'pools/[a-zA-Z0-9_-]{1,64}$'
|
||||
},
|
||||
'weight': {
|
||||
'type': 'number',
|
||||
'minimum': -1
|
||||
},
|
||||
'uri': {
|
||||
'type': 'string'
|
||||
},
|
||||
'options': {
|
||||
'type': 'object',
|
||||
'additionalProperties': True
|
||||
}
|
||||
},
|
||||
'required': ['href', 'weight', 'uri'],
|
||||
'additionalProperties': False,
|
||||
},
|
||||
}
|
||||
},
|
||||
'required': ['links', 'pools'],
|
||||
'additionalProperties': False
|
||||
},
|
||||
|
||||
'message_list': {
|
||||
'type': 'object',
|
||||
'properties': {
|
||||
|
@ -163,9 +163,26 @@ class ResponseSchema(api.Api):
|
||||
'additionalProperties': False
|
||||
},
|
||||
|
||||
'pool-list': {
|
||||
'pool_list': {
|
||||
'type': 'object',
|
||||
'properties': {
|
||||
'links': {
|
||||
'type': 'array',
|
||||
'items': {
|
||||
'type': 'object',
|
||||
'properties': {
|
||||
'rel': {
|
||||
'type': 'string'
|
||||
},
|
||||
'href': {
|
||||
'type': 'string',
|
||||
'pattern': '^/v1\.1/pools\?'
|
||||
}
|
||||
},
|
||||
'required': ['rel', 'href'],
|
||||
'additionalProperties': False
|
||||
}
|
||||
},
|
||||
'pools': {
|
||||
'type': 'array',
|
||||
'items': {
|
||||
@ -174,7 +191,7 @@ class ResponseSchema(api.Api):
|
||||
'href': {
|
||||
'type': 'string',
|
||||
'pattern': '^/v1\.1/'
|
||||
'pools/[a-zA-Z0-9_-]{1,64}$'
|
||||
'pools/[a-zA-Z0-9_-]{1,64}$'
|
||||
},
|
||||
'weight': {
|
||||
'type': 'number',
|
||||
@ -196,7 +213,7 @@ class ResponseSchema(api.Api):
|
||||
},
|
||||
}
|
||||
},
|
||||
'required': ['pools'],
|
||||
'required': ['links', 'pools'],
|
||||
'additionalProperties': False
|
||||
},
|
||||
|
||||
@ -229,7 +246,7 @@ class ResponseSchema(api.Api):
|
||||
}
|
||||
}
|
||||
},
|
||||
'pool-detail': {
|
||||
'pool_detail': {
|
||||
'type': 'object',
|
||||
'properties': {
|
||||
'uri': {
|
||||
|
@ -60,9 +60,15 @@ class PoolsController(base.PoolsBase):
|
||||
query['n'] = {'$gt': marker}
|
||||
|
||||
cursor = self._col.find(query, fields=_field_spec(detailed),
|
||||
limit=limit)
|
||||
normalizer = functools.partial(_normalize, detailed=detailed)
|
||||
return utils.HookedCursor(cursor, normalizer)
|
||||
limit=limit).sort('n')
|
||||
marker_name = {}
|
||||
|
||||
def normalizer(pool):
|
||||
marker_name['next'] = pool['n']
|
||||
return _normalize(pool, detailed=detailed)
|
||||
|
||||
yield utils.HookedCursor(cursor, normalizer)
|
||||
yield marker_name and marker_name['next']
|
||||
|
||||
@utils.raises_conn_error
|
||||
def get(self, name, detailed=False):
|
||||
|
@ -72,9 +72,10 @@ class DataDriver(storage.DataDriverBase):
|
||||
self._pool_catalog = Catalog(conf, cache, control)
|
||||
|
||||
def is_alive(self):
|
||||
cursor = self._pool_catalog._pools_ctrl.list(limit=0)
|
||||
pools = next(cursor)
|
||||
return all(self._pool_catalog.get_driver(pool['name']).is_alive()
|
||||
for pool in
|
||||
self._pool_catalog._pools_ctrl.list(limit=0))
|
||||
for pool in pools)
|
||||
|
||||
def _health(self):
|
||||
KPI = {}
|
||||
@ -82,15 +83,17 @@ class DataDriver(storage.DataDriverBase):
|
||||
# reachable or not
|
||||
KPI['catalog_reachable'] = self.is_alive()
|
||||
|
||||
cursor = self._pool_catalog._pools_ctrl.list(limit=0)
|
||||
# Messages of each pool
|
||||
for pool in self._pool_catalog._pools_ctrl.list():
|
||||
for pool in next(cursor):
|
||||
driver = self._pool_catalog.get_driver(pool['name'])
|
||||
KPI[pool['name']] = driver._health()
|
||||
|
||||
return KPI
|
||||
|
||||
def gc(self):
|
||||
for pool in self._pool_catalog._pools_ctrl.list():
|
||||
cursor = self._pool_catalog._pools_ctrl.list(limit=0)
|
||||
for pool in next(cursor):
|
||||
driver = self._pool_catalog.get_driver(pool['name'])
|
||||
driver.gc()
|
||||
|
||||
@ -158,7 +161,8 @@ class QueueController(RoutingController):
|
||||
limit=storage.DEFAULT_QUEUES_PER_PAGE, detailed=False):
|
||||
|
||||
def all_pages():
|
||||
for pool in self._pool_catalog._pools_ctrl.list(limit=0):
|
||||
cursor = self._pool_catalog._pools_ctrl.list(limit=0)
|
||||
for pool in next(cursor):
|
||||
yield next(self._pool_catalog.get_driver(pool['name'])
|
||||
.queue_controller.list(
|
||||
project=project,
|
||||
|
@ -54,8 +54,15 @@ class PoolsController(base.PoolsBase):
|
||||
stmt = stmt.limit(limit)
|
||||
cursor = self._conn.execute(stmt)
|
||||
|
||||
normalizer = functools.partial(_normalize, detailed=detailed)
|
||||
return (normalizer(v) for v in cursor)
|
||||
marker_name = {}
|
||||
|
||||
def it():
|
||||
for cur in cursor:
|
||||
marker_name['next'] = cur[0]
|
||||
yield _normalize(cur, detailed=detailed)
|
||||
|
||||
yield it()
|
||||
yield marker_name and marker_name['next']
|
||||
|
||||
@utils.raises_conn_error
|
||||
def get_group(self, group=None, detailed=False):
|
||||
|
@ -60,16 +60,21 @@ class Listing(object):
|
||||
self._ctrl = pools_controller
|
||||
|
||||
def on_get(self, request, response, project_id):
|
||||
"""Returns a pool listing as objects embedded in an array:
|
||||
"""Returns a pool listing as objects embedded in an object:
|
||||
|
||||
::
|
||||
|
||||
[
|
||||
{"href": "", "weight": 100, "uri": ""},
|
||||
...
|
||||
]
|
||||
{
|
||||
"pools": [
|
||||
{"href": "", "weight": 100, "uri": ""},
|
||||
...
|
||||
],
|
||||
"links": [
|
||||
{"href": "", "rel": "next"}
|
||||
]
|
||||
}
|
||||
|
||||
:returns: HTTP | [200, 204]
|
||||
:returns: HTTP | 200
|
||||
"""
|
||||
|
||||
LOG.debug(u'LIST pools')
|
||||
@ -79,14 +84,25 @@ class Listing(object):
|
||||
request.get_param_as_int('limit', store=store)
|
||||
request.get_param_as_bool('detailed', store=store)
|
||||
|
||||
results = {}
|
||||
results['pools'] = list(self._ctrl.list(**store))
|
||||
for entry in results['pools']:
|
||||
entry['href'] = request.path + '/' + entry.pop('name')
|
||||
cursor = self._ctrl.list(**store)
|
||||
|
||||
if not results['pools']:
|
||||
response.status = falcon.HTTP_204
|
||||
return
|
||||
pools = list(next(cursor))
|
||||
|
||||
results = {}
|
||||
|
||||
if pools:
|
||||
store['marker'] = next(cursor)
|
||||
|
||||
for entry in pools:
|
||||
entry['href'] = request.path + '/' + entry.pop('name')
|
||||
|
||||
results['links'] = [
|
||||
{
|
||||
'rel': 'next',
|
||||
'href': request.path + falcon.to_query_str(store)
|
||||
}
|
||||
]
|
||||
results['pools'] = pools
|
||||
|
||||
response.content_location = request.relative_uri
|
||||
response.body = transport_utils.to_json(results)
|
||||
|
@ -62,16 +62,21 @@ class Listing(object):
|
||||
self._ctrl = pools_controller
|
||||
|
||||
def on_get(self, request, response, project_id):
|
||||
"""Returns a pool listing as objects embedded in an array:
|
||||
"""Returns a pool listing as objects embedded in an object:
|
||||
|
||||
::
|
||||
|
||||
[
|
||||
{"href": "", "weight": 100, "uri": ""},
|
||||
...
|
||||
]
|
||||
{
|
||||
"pools": [
|
||||
{"href": "", "weight": 100, "uri": ""},
|
||||
...
|
||||
],
|
||||
"links": [
|
||||
{"href": "", "rel": "next"}
|
||||
]
|
||||
}
|
||||
|
||||
:returns: HTTP | [200, 204]
|
||||
:returns: HTTP | 200
|
||||
"""
|
||||
|
||||
LOG.debug(u'LIST pools')
|
||||
@ -81,15 +86,26 @@ class Listing(object):
|
||||
request.get_param_as_int('limit', store=store)
|
||||
request.get_param_as_bool('detailed', store=store)
|
||||
|
||||
cursor = self._ctrl.list(**store)
|
||||
pools = list(next(cursor))
|
||||
|
||||
results = {}
|
||||
results['pools'] = list(self._ctrl.list(**store))
|
||||
for entry in results['pools']:
|
||||
entry['href'] = request.path + '/' + entry.pop('name')
|
||||
|
||||
if not results['pools']:
|
||||
response.status = falcon.HTTP_204
|
||||
return
|
||||
if pools:
|
||||
store['marker'] = next(cursor)
|
||||
|
||||
for entry in pools:
|
||||
entry['href'] = request.path + '/' + entry.pop('name')
|
||||
|
||||
results['links'] = [
|
||||
{
|
||||
'rel': 'next',
|
||||
'href': request.path + falcon.to_query_str(store)
|
||||
}
|
||||
]
|
||||
results['pools'] = pools
|
||||
|
||||
response.content_location = request.relative_uri
|
||||
response.body = transport_utils.to_json(results)
|
||||
response.status = falcon.HTTP_200
|
||||
|
||||
|
@ -990,7 +990,8 @@ class PoolsControllerTest(ControllerBaseTest):
|
||||
def test_drop_all_leads_to_empty_listing(self):
|
||||
self.pools_controller.drop_all()
|
||||
cursor = self.pools_controller.list()
|
||||
self.assertRaises(StopIteration, next, cursor)
|
||||
pools = next(cursor)
|
||||
self.assertRaises(StopIteration, next, pools)
|
||||
|
||||
def test_listing_simple(self):
|
||||
# NOTE(cpp-cabrera): base entry interferes with listing results
|
||||
@ -1021,7 +1022,15 @@ class PoolsControllerTest(ControllerBaseTest):
|
||||
|
||||
return n, w, u
|
||||
|
||||
res = list(self.pools_controller.list())
|
||||
def get_res(**kwargs):
|
||||
cursor = self.pools_controller.list(**kwargs)
|
||||
res = list(next(cursor))
|
||||
marker = next(cursor)
|
||||
# TODO(jeffrey4l): marker should exist
|
||||
self.assertTrue(marker)
|
||||
return res
|
||||
|
||||
res = get_res()
|
||||
self.assertEqual(len(res), 10)
|
||||
for entry in res:
|
||||
n, w, u = _pool(entry['name'])
|
||||
@ -1029,19 +1038,19 @@ class PoolsControllerTest(ControllerBaseTest):
|
||||
self._pool_expects(entry, n, w, u)
|
||||
self.assertNotIn('options', entry)
|
||||
|
||||
res = list(self.pools_controller.list(limit=5))
|
||||
res = get_res(limit=5)
|
||||
self.assertEqual(len(res), 5)
|
||||
|
||||
res = list(self.pools_controller.list(limit=0))
|
||||
res = get_res(limit=0)
|
||||
self.assertEqual(len(res), 15)
|
||||
|
||||
next_name = marker + 'n'
|
||||
self.pools_controller.create(next_name, 123, '123', options={})
|
||||
res = next(self.pools_controller.list(marker=marker))
|
||||
self._pool_expects(res, next_name, 123, '123')
|
||||
res = get_res(marker=marker)
|
||||
self._pool_expects(res[0], next_name, 123, '123')
|
||||
self.pools_controller.delete(next_name)
|
||||
|
||||
res = list(self.pools_controller.list(detailed=True))
|
||||
res = get_res(detailed=True)
|
||||
self.assertEqual(len(res), 10)
|
||||
for entry in res:
|
||||
n, w, u = _pool(entry['name'])
|
||||
|
@ -18,7 +18,6 @@ import uuid
|
||||
import ddt
|
||||
import falcon
|
||||
|
||||
|
||||
from zaqar.openstack.common import jsonutils
|
||||
from zaqar import tests as testing
|
||||
from zaqar.tests.queues.transport.wsgi import base
|
||||
@ -230,10 +229,13 @@ class PoolsBaseTest(base.V1Base):
|
||||
body=jsonutils.dumps({'weight': 1}))
|
||||
self.assertEqual(self.srmock.status, falcon.HTTP_404)
|
||||
|
||||
def test_empty_listing_returns_204(self):
|
||||
def test_empty_listing(self):
|
||||
self.simulate_delete(self.pool)
|
||||
self.simulate_get(self.url_prefix + '/pools')
|
||||
self.assertEqual(self.srmock.status, falcon.HTTP_204)
|
||||
result = self.simulate_get(self.url_prefix + '/pools')
|
||||
results = jsonutils.loads(result[0])
|
||||
self.assertEqual(self.srmock.status, falcon.HTTP_200)
|
||||
self.assertTrue(len(results['pools']) == 0)
|
||||
self.assertIn('links', results)
|
||||
|
||||
def _listing_test(self, count=10, limit=10,
|
||||
marker=None, detailed=False):
|
||||
@ -242,7 +244,7 @@ class PoolsBaseTest(base.V1Base):
|
||||
self.simulate_delete(self.pool)
|
||||
query = '?limit={0}&detailed={1}'.format(limit, detailed)
|
||||
if marker:
|
||||
query += '&marker={2}'.format(marker)
|
||||
query += '&marker={0}'.format(marker)
|
||||
|
||||
with pools(self, count, self.doc['uri']) as expected:
|
||||
result = self.simulate_get(self.url_prefix + '/pools',
|
||||
@ -251,9 +253,36 @@ class PoolsBaseTest(base.V1Base):
|
||||
results = jsonutils.loads(result[0])
|
||||
self.assertIsInstance(results, dict)
|
||||
self.assertIn('pools', results)
|
||||
self.assertIn('links', results)
|
||||
pool_list = results['pools']
|
||||
|
||||
link = results['links'][0]
|
||||
self.assertEqual('next', link['rel'])
|
||||
href = falcon.uri.parse_query_string(link['href'])
|
||||
self.assertIn('marker', href)
|
||||
self.assertEqual(href['limit'], str(limit))
|
||||
self.assertEqual(href['detailed'], str(detailed).lower())
|
||||
|
||||
next_query_string = ('?marker={marker}&limit={limit}'
|
||||
'&detailed={detailed}').format(**href)
|
||||
next_result = self.simulate_get(link['href'].split('?')[0],
|
||||
query_string=next_query_string)
|
||||
self.assertEqual(self.srmock.status, falcon.HTTP_200)
|
||||
|
||||
next_pool = jsonutils.loads(next_result[0])
|
||||
next_pool_list = next_pool['pools']
|
||||
|
||||
self.assertIn('links', next_pool)
|
||||
if limit < count:
|
||||
self.assertEqual(len(next_pool_list),
|
||||
min(limit, count-limit))
|
||||
else:
|
||||
# NOTE(jeffrey4l): when limit >= count, there will be no
|
||||
# pools in the 2nd page.
|
||||
self.assertTrue(len(next_pool_list) == 0)
|
||||
|
||||
self.assertEqual(len(pool_list), min(limit, count))
|
||||
for s in pool_list:
|
||||
for s in pool_list + next_pool_list:
|
||||
# NOTE(flwang): It can't assumed that both sqlalchemy and
|
||||
# mongodb can return query result with the same order. Just
|
||||
# like the order they're inserted. Actually, sqlalchemy can't
|
||||
|
@ -233,10 +233,13 @@ class PoolsBaseTest(base.V1_1Base):
|
||||
body=jsonutils.dumps({'weight': 1}))
|
||||
self.assertEqual(self.srmock.status, falcon.HTTP_404)
|
||||
|
||||
def test_empty_listing_returns_204(self):
|
||||
def test_empty_listing(self):
|
||||
self.simulate_delete(self.pool)
|
||||
self.simulate_get(self.url_prefix + '/pools')
|
||||
self.assertEqual(self.srmock.status, falcon.HTTP_204)
|
||||
result = self.simulate_get(self.url_prefix + '/pools')
|
||||
results = jsonutils.loads(result[0])
|
||||
self.assertEqual(self.srmock.status, falcon.HTTP_200)
|
||||
self.assertTrue(len(results['pools']) == 0)
|
||||
self.assertIn('links', results)
|
||||
|
||||
def _listing_test(self, count=10, limit=10,
|
||||
marker=None, detailed=False):
|
||||
@ -245,7 +248,7 @@ class PoolsBaseTest(base.V1_1Base):
|
||||
self.simulate_delete(self.pool)
|
||||
query = '?limit={0}&detailed={1}'.format(limit, detailed)
|
||||
if marker:
|
||||
query += '&marker={2}'.format(marker)
|
||||
query += '&marker={0}'.format(marker)
|
||||
|
||||
with pools(self, count, self.doc['uri'], 'my-group') as expected:
|
||||
result = self.simulate_get(self.url_prefix + '/pools',
|
||||
@ -254,9 +257,36 @@ class PoolsBaseTest(base.V1_1Base):
|
||||
results = jsonutils.loads(result[0])
|
||||
self.assertIsInstance(results, dict)
|
||||
self.assertIn('pools', results)
|
||||
self.assertIn('links', results)
|
||||
pool_list = results['pools']
|
||||
|
||||
link = results['links'][0]
|
||||
self.assertEqual('next', link['rel'])
|
||||
href = falcon.uri.parse_query_string(link['href'])
|
||||
self.assertIn('marker', href)
|
||||
self.assertEqual(href['limit'], str(limit))
|
||||
self.assertEqual(href['detailed'], str(detailed).lower())
|
||||
|
||||
next_query_string = ('?marker={marker}&limit={limit}'
|
||||
'&detailed={detailed}').format(**href)
|
||||
next_result = self.simulate_get(link['href'].split('?')[0],
|
||||
query_string=next_query_string)
|
||||
self.assertEqual(self.srmock.status, falcon.HTTP_200)
|
||||
|
||||
next_pool = jsonutils.loads(next_result[0])
|
||||
next_pool_list = next_pool['pools']
|
||||
|
||||
self.assertIn('links', next_pool)
|
||||
if limit < count:
|
||||
self.assertEqual(len(next_pool_list),
|
||||
min(limit, count-limit))
|
||||
else:
|
||||
# NOTE(jeffrey4l): when limit >= count, there will be no
|
||||
# pools in the 2nd page.
|
||||
self.assertTrue(len(next_pool_list) == 0)
|
||||
|
||||
self.assertEqual(len(pool_list), min(limit, count))
|
||||
for s in pool_list:
|
||||
for s in pool_list + next_pool_list:
|
||||
# NOTE(flwang): It can't assumed that both sqlalchemy and
|
||||
# mongodb can return query result with the same order. Just
|
||||
# like the order they're inserted. Actually, sqlalchemy can't
|
||||
|
Loading…
x
Reference in New Issue
Block a user