On Volume list only retrieve needed data from DB
Currently when there is no limit set on a volume list query we retrieve all volumes and then limit them locally using osapi_max_limit. Similar thing happens when we are using the marker for next pages, we get all volumes from that marker until the last volume and then limit it locally. We should be limiting it on the DB side so we only retrieve the data we are actually going to return to the API caller. This patch always limits the data retrieved from the DB and for the offset to keep working as it was before we need to do the offset on the DB side as well. For reference some tests were performed: On a deployment with 60,000 volumes, 370,000 volume_metadata items and 240,000 volume_glance_metadata items in cinder db. Before the patch this will use nearly 10G memory. With the patch we will just use about 500M. Co-Authored-By: wangxiyuan <wangxiyuan@huawei.com> Closes-bug:#1483165i Change-Id: Ie903e546074fe118299e8e1acfb9c88c8a10d78c
This commit is contained in:
parent
7e84f149e8
commit
28879f9a78
@ -69,42 +69,64 @@ def validate_key_names(key_names_list):
|
|||||||
return True
|
return True
|
||||||
|
|
||||||
|
|
||||||
def get_pagination_params(request):
|
def get_pagination_params(params, max_limit=CONF.osapi_max_limit):
|
||||||
"""Return marker, limit tuple from request.
|
"""Return marker, limit, offset tuple from request.
|
||||||
|
|
||||||
:param request: `wsgi.Request` possibly containing 'marker' and 'limit'
|
|
||||||
GET variables. 'marker' is the id of the last element
|
|
||||||
the client has seen, and 'limit' is the maximum number
|
|
||||||
of items to return. If 'limit' is not specified, 0, or
|
|
||||||
> max_limit, we default to max_limit. Negative values
|
|
||||||
for either marker or limit will cause
|
|
||||||
exc.HTTPBadRequest() exceptions to be raised.
|
|
||||||
|
|
||||||
|
:param params: `wsgi.Request`'s GET dictionary, possibly containing
|
||||||
|
'marker', 'limit', and 'offset' variables. 'marker' is the
|
||||||
|
id of the last element the client has seen, 'limit' is the
|
||||||
|
maximum number of items to return and 'offset' is the number
|
||||||
|
of items to skip from the marker or from the first element.
|
||||||
|
If 'limit' is not specified, or > max_limit, we default to
|
||||||
|
max_limit. Negative values for either offset or limit will
|
||||||
|
cause exc.HTTPBadRequest() exceptions to be raised. If no
|
||||||
|
offset is present we'll default to 0 and if no marker is
|
||||||
|
present we'll default to None.
|
||||||
|
:max_limit: Max value 'limit' return value can take
|
||||||
|
:returns: Tuple (marker, limit, offset)
|
||||||
"""
|
"""
|
||||||
params = {}
|
limit = _get_limit_param(params, max_limit)
|
||||||
if 'limit' in request.GET:
|
marker = _get_marker_param(params)
|
||||||
params['limit'] = _get_limit_param(request)
|
offset = _get_offset_param(params)
|
||||||
if 'marker' in request.GET:
|
return marker, limit, offset
|
||||||
params['marker'] = _get_marker_param(request)
|
|
||||||
return params
|
|
||||||
|
|
||||||
|
|
||||||
def _get_limit_param(request):
|
def _get_limit_param(params, max_limit=CONF.osapi_max_limit):
|
||||||
"""Extract integer limit from request or fail."""
|
"""Extract integer limit from request's dictionary or fail.
|
||||||
|
|
||||||
|
Defaults to max_limit if not present and returns max_limit if present
|
||||||
|
'limit' is greater than max_limit.
|
||||||
|
"""
|
||||||
try:
|
try:
|
||||||
limit = int(request.GET['limit'])
|
limit = int(params.pop('limit', max_limit))
|
||||||
except ValueError:
|
except ValueError:
|
||||||
msg = _('limit param must be an integer')
|
msg = _('limit param must be an integer')
|
||||||
raise webob.exc.HTTPBadRequest(explanation=msg)
|
raise webob.exc.HTTPBadRequest(explanation=msg)
|
||||||
if limit < 0:
|
if limit < 0:
|
||||||
msg = _('limit param must be positive')
|
msg = _('limit param must be positive')
|
||||||
raise webob.exc.HTTPBadRequest(explanation=msg)
|
raise webob.exc.HTTPBadRequest(explanation=msg)
|
||||||
|
limit = min(limit, max_limit)
|
||||||
return limit
|
return limit
|
||||||
|
|
||||||
|
|
||||||
def _get_marker_param(request):
|
def _get_marker_param(params):
|
||||||
"""Extract marker id from request or fail."""
|
"""Extract marker id from request's dictionary (defaults to None)."""
|
||||||
return request.GET['marker']
|
return params.pop('marker', None)
|
||||||
|
|
||||||
|
|
||||||
|
def _get_offset_param(params):
|
||||||
|
"""Extract offset id from request's dictionary (defaults to 0) or fail."""
|
||||||
|
try:
|
||||||
|
offset = int(params.pop('offset', 0))
|
||||||
|
except ValueError:
|
||||||
|
msg = _('offset param must be an integer')
|
||||||
|
raise webob.exc.HTTPBadRequest(explanation=msg)
|
||||||
|
|
||||||
|
if offset < 0:
|
||||||
|
msg = _('offset param must be positive')
|
||||||
|
raise webob.exc.HTTPBadRequest(explanation=msg)
|
||||||
|
|
||||||
|
return offset
|
||||||
|
|
||||||
|
|
||||||
def limited(items, request, max_limit=CONF.osapi_max_limit):
|
def limited(items, request, max_limit=CONF.osapi_max_limit):
|
||||||
@ -119,39 +141,16 @@ def limited(items, request, max_limit=CONF.osapi_max_limit):
|
|||||||
will cause exc.HTTPBadRequest() exceptions to be raised.
|
will cause exc.HTTPBadRequest() exceptions to be raised.
|
||||||
:kwarg max_limit: The maximum number of items to return from 'items'
|
:kwarg max_limit: The maximum number of items to return from 'items'
|
||||||
"""
|
"""
|
||||||
try:
|
marker, limit, offset = get_pagination_params(request.GET.copy(),
|
||||||
offset = int(request.GET.get('offset', 0))
|
max_limit)
|
||||||
except ValueError:
|
range_end = offset + (limit or max_limit)
|
||||||
msg = _('offset param must be an integer')
|
|
||||||
raise webob.exc.HTTPBadRequest(explanation=msg)
|
|
||||||
|
|
||||||
try:
|
|
||||||
limit = int(request.GET.get('limit', max_limit))
|
|
||||||
except ValueError:
|
|
||||||
msg = _('limit param must be an integer')
|
|
||||||
raise webob.exc.HTTPBadRequest(explanation=msg)
|
|
||||||
|
|
||||||
if limit < 0:
|
|
||||||
msg = _('limit param must be positive')
|
|
||||||
raise webob.exc.HTTPBadRequest(explanation=msg)
|
|
||||||
|
|
||||||
if offset < 0:
|
|
||||||
msg = _('offset param must be positive')
|
|
||||||
raise webob.exc.HTTPBadRequest(explanation=msg)
|
|
||||||
|
|
||||||
limit = min(max_limit, limit or max_limit)
|
|
||||||
range_end = offset + limit
|
|
||||||
return items[offset:range_end]
|
return items[offset:range_end]
|
||||||
|
|
||||||
|
|
||||||
def limited_by_marker(items, request, max_limit=CONF.osapi_max_limit):
|
def limited_by_marker(items, request, max_limit=CONF.osapi_max_limit):
|
||||||
"""Return a slice of items according to the requested marker and limit."""
|
"""Return a slice of items according to the requested marker and limit."""
|
||||||
params = get_pagination_params(request)
|
marker, limit, __ = get_pagination_params(request.GET.copy(), max_limit)
|
||||||
|
|
||||||
limit = params.get('limit', max_limit)
|
|
||||||
marker = params.get('marker')
|
|
||||||
|
|
||||||
limit = min(max_limit, limit)
|
|
||||||
start_index = 0
|
start_index = 0
|
||||||
if marker:
|
if marker:
|
||||||
start_index = -1
|
start_index = -1
|
||||||
@ -289,19 +288,19 @@ class ViewBuilder(object):
|
|||||||
str(identifier))
|
str(identifier))
|
||||||
|
|
||||||
def _get_collection_links(self, request, items, collection_name,
|
def _get_collection_links(self, request, items, collection_name,
|
||||||
item_count, id_key="uuid"):
|
item_count=None, id_key="uuid"):
|
||||||
"""Retrieve 'next' link, if applicable.
|
"""Retrieve 'next' link, if applicable.
|
||||||
|
|
||||||
The next link is included if:
|
The next link is included if we are returning as many items as we can,
|
||||||
1) 'limit' param is specified and equal to or less than the
|
given the restrictions of limit optional request parameter and
|
||||||
number of items.
|
osapi_max_limit configuration parameter as long as we are returning
|
||||||
2) 'limit' param is specified and both the limit and the number
|
some elements.
|
||||||
of items are greater than CONF.osapi_max_limit, even if limit
|
|
||||||
is greater than the number of items.
|
So we return next link if:
|
||||||
3) 'limit' param is NOT specified and the number of items is
|
|
||||||
greater than CONF.osapi_max_limit.
|
1) 'limit' param is specified and equal to the number of items.
|
||||||
Notes: The case limit equals to 0 or CONF.osapi_max_limit equals to 0
|
2) 'limit' param is NOT specified and the number of items is
|
||||||
is not included in the above conditions.
|
equal to CONF.osapi_max_limit.
|
||||||
|
|
||||||
:param request: API request
|
:param request: API request
|
||||||
:param items: List of collection items
|
:param items: List of collection items
|
||||||
@ -313,25 +312,12 @@ class ViewBuilder(object):
|
|||||||
to generate the next link marker for a pagination query
|
to generate the next link marker for a pagination query
|
||||||
:returns links
|
:returns links
|
||||||
"""
|
"""
|
||||||
limit = request.params.get("limit", None)
|
item_count = item_count or len(items)
|
||||||
if limit is None or int(limit) > CONF.osapi_max_limit:
|
limit = _get_limit_param(request.GET.copy())
|
||||||
# If limit is not set in the request or greater than
|
if len(items) and limit <= item_count:
|
||||||
# osapi_max_limit, len(items) < item_count means the items
|
|
||||||
# are limited by osapi_max_limit and we need to generate the
|
|
||||||
# next link. Otherwise, all the items will be returned in
|
|
||||||
# the response, no next link is generated.
|
|
||||||
if len(items) < item_count:
|
|
||||||
return self._generate_next_link(items, id_key, request,
|
|
||||||
collection_name)
|
|
||||||
else:
|
|
||||||
# If limit is set in the request and not more than
|
|
||||||
# osapi_max_limit, int(limit) == len(items) means it is possible
|
|
||||||
# that the DB still have more items left. In this case,
|
|
||||||
# we generate the next link.
|
|
||||||
limit = int(limit)
|
|
||||||
if limit and limit == len(items):
|
|
||||||
return self._generate_next_link(items, id_key, request,
|
return self._generate_next_link(items, id_key, request,
|
||||||
collection_name)
|
collection_name)
|
||||||
|
|
||||||
return []
|
return []
|
||||||
|
|
||||||
def _generate_next_link(self, items, id_key, request,
|
def _generate_next_link(self, items, id_key, request,
|
||||||
|
@ -30,12 +30,12 @@ class ViewBuilder(common.ViewBuilder):
|
|||||||
"""Initialize view builder."""
|
"""Initialize view builder."""
|
||||||
super(ViewBuilder, self).__init__()
|
super(ViewBuilder, self).__init__()
|
||||||
|
|
||||||
def summary_list(self, request, volumes, volume_count):
|
def summary_list(self, request, volumes, volume_count=None):
|
||||||
"""Show a list of volumes without many details."""
|
"""Show a list of volumes without many details."""
|
||||||
return self._list_view(self.summary, request, volumes,
|
return self._list_view(self.summary, request, volumes,
|
||||||
volume_count)
|
volume_count)
|
||||||
|
|
||||||
def detail_list(self, request, volumes, volume_count):
|
def detail_list(self, request, volumes, volume_count=None):
|
||||||
"""Detailed view of a list of volumes."""
|
"""Detailed view of a list of volumes."""
|
||||||
return self._list_view(self.detail, request, volumes,
|
return self._list_view(self.detail, request, volumes,
|
||||||
volume_count,
|
volume_count,
|
||||||
|
@ -227,10 +227,8 @@ class VolumeController(wsgi.Controller):
|
|||||||
context = req.environ['cinder.context']
|
context = req.environ['cinder.context']
|
||||||
|
|
||||||
params = req.params.copy()
|
params = req.params.copy()
|
||||||
marker = params.pop('marker', None)
|
marker, limit, offset = common.get_pagination_params(params)
|
||||||
limit = params.pop('limit', None)
|
|
||||||
sort_keys, sort_dirs = common.get_sort_params(params)
|
sort_keys, sort_dirs = common.get_sort_params(params)
|
||||||
params.pop('offset', None)
|
|
||||||
filters = params
|
filters = params
|
||||||
|
|
||||||
utils.remove_invalid_filter_options(context,
|
utils.remove_invalid_filter_options(context,
|
||||||
@ -255,23 +253,20 @@ class VolumeController(wsgi.Controller):
|
|||||||
sort_keys=sort_keys,
|
sort_keys=sort_keys,
|
||||||
sort_dirs=sort_dirs,
|
sort_dirs=sort_dirs,
|
||||||
filters=filters,
|
filters=filters,
|
||||||
viewable_admin_meta=True)
|
viewable_admin_meta=True,
|
||||||
|
offset=offset)
|
||||||
|
|
||||||
volumes = [dict(vol) for vol in volumes]
|
volumes = [dict(vol) for vol in volumes]
|
||||||
|
|
||||||
for volume in volumes:
|
for volume in volumes:
|
||||||
utils.add_visible_admin_metadata(volume)
|
utils.add_visible_admin_metadata(volume)
|
||||||
|
|
||||||
limited_list = common.limited(volumes, req)
|
req.cache_db_volumes(volumes)
|
||||||
volume_count = len(volumes)
|
|
||||||
req.cache_db_volumes(limited_list)
|
|
||||||
|
|
||||||
if is_detail:
|
if is_detail:
|
||||||
volumes = self._view_builder.detail_list(req, limited_list,
|
volumes = self._view_builder.detail_list(req, volumes)
|
||||||
volume_count)
|
|
||||||
else:
|
else:
|
||||||
volumes = self._view_builder.summary_list(req, limited_list,
|
volumes = self._view_builder.summary_list(req, volumes)
|
||||||
volume_count)
|
|
||||||
return volumes
|
return volumes
|
||||||
|
|
||||||
def _image_uuid_from_ref(self, image_ref, context):
|
def _image_uuid_from_ref(self, image_ref, context):
|
||||||
|
@ -31,7 +31,7 @@ LOG = logging.getLogger(__name__)
|
|||||||
|
|
||||||
# copied from glance/db/sqlalchemy/api.py
|
# copied from glance/db/sqlalchemy/api.py
|
||||||
def paginate_query(query, model, limit, sort_keys, marker=None,
|
def paginate_query(query, model, limit, sort_keys, marker=None,
|
||||||
sort_dir=None, sort_dirs=None):
|
sort_dir=None, sort_dirs=None, offset=None):
|
||||||
"""Returns a query with sorting / pagination criteria added.
|
"""Returns a query with sorting / pagination criteria added.
|
||||||
|
|
||||||
Pagination works by requiring a unique sort_key, specified by sort_keys.
|
Pagination works by requiring a unique sort_key, specified by sort_keys.
|
||||||
@ -125,4 +125,7 @@ def paginate_query(query, model, limit, sort_keys, marker=None,
|
|||||||
if limit is not None:
|
if limit is not None:
|
||||||
query = query.limit(limit)
|
query = query.limit(limit)
|
||||||
|
|
||||||
|
if offset:
|
||||||
|
query = query.offset(offset)
|
||||||
|
|
||||||
return query
|
return query
|
||||||
|
@ -206,10 +206,11 @@ def volume_get(context, volume_id):
|
|||||||
|
|
||||||
|
|
||||||
def volume_get_all(context, marker, limit, sort_keys=None, sort_dirs=None,
|
def volume_get_all(context, marker, limit, sort_keys=None, sort_dirs=None,
|
||||||
filters=None):
|
filters=None, offset=None):
|
||||||
"""Get all volumes."""
|
"""Get all volumes."""
|
||||||
return IMPL.volume_get_all(context, marker, limit, sort_keys=sort_keys,
|
return IMPL.volume_get_all(context, marker, limit, sort_keys=sort_keys,
|
||||||
sort_dirs=sort_dirs, filters=filters)
|
sort_dirs=sort_dirs, filters=filters,
|
||||||
|
offset=offset)
|
||||||
|
|
||||||
|
|
||||||
def volume_get_all_by_host(context, host, filters=None):
|
def volume_get_all_by_host(context, host, filters=None):
|
||||||
@ -223,12 +224,14 @@ def volume_get_all_by_group(context, group_id, filters=None):
|
|||||||
|
|
||||||
|
|
||||||
def volume_get_all_by_project(context, project_id, marker, limit,
|
def volume_get_all_by_project(context, project_id, marker, limit,
|
||||||
sort_keys=None, sort_dirs=None, filters=None):
|
sort_keys=None, sort_dirs=None, filters=None,
|
||||||
|
offset=None):
|
||||||
"""Get all volumes belonging to a project."""
|
"""Get all volumes belonging to a project."""
|
||||||
return IMPL.volume_get_all_by_project(context, project_id, marker, limit,
|
return IMPL.volume_get_all_by_project(context, project_id, marker, limit,
|
||||||
sort_keys=sort_keys,
|
sort_keys=sort_keys,
|
||||||
sort_dirs=sort_dirs,
|
sort_dirs=sort_dirs,
|
||||||
filters=filters)
|
filters=filters,
|
||||||
|
offset=offset)
|
||||||
|
|
||||||
|
|
||||||
def volume_get_iscsi_target_num(context, volume_id):
|
def volume_get_iscsi_target_num(context, volume_id):
|
||||||
|
@ -1339,7 +1339,7 @@ def volume_get(context, volume_id):
|
|||||||
|
|
||||||
@require_admin_context
|
@require_admin_context
|
||||||
def volume_get_all(context, marker, limit, sort_keys=None, sort_dirs=None,
|
def volume_get_all(context, marker, limit, sort_keys=None, sort_dirs=None,
|
||||||
filters=None):
|
filters=None, offset=None):
|
||||||
"""Retrieves all volumes.
|
"""Retrieves all volumes.
|
||||||
|
|
||||||
If no sort parameters are specified then the returned volumes are sorted
|
If no sort parameters are specified then the returned volumes are sorted
|
||||||
@ -1364,7 +1364,7 @@ def volume_get_all(context, marker, limit, sort_keys=None, sort_dirs=None,
|
|||||||
with session.begin():
|
with session.begin():
|
||||||
# Generate the query
|
# Generate the query
|
||||||
query = _generate_paginate_query(context, session, marker, limit,
|
query = _generate_paginate_query(context, session, marker, limit,
|
||||||
sort_keys, sort_dirs, filters)
|
sort_keys, sort_dirs, filters, offset)
|
||||||
# No volumes would match, return empty list
|
# No volumes would match, return empty list
|
||||||
if query is None:
|
if query is None:
|
||||||
return []
|
return []
|
||||||
@ -1429,7 +1429,8 @@ def volume_get_all_by_group(context, group_id, filters=None):
|
|||||||
|
|
||||||
@require_context
|
@require_context
|
||||||
def volume_get_all_by_project(context, project_id, marker, limit,
|
def volume_get_all_by_project(context, project_id, marker, limit,
|
||||||
sort_keys=None, sort_dirs=None, filters=None):
|
sort_keys=None, sort_dirs=None, filters=None,
|
||||||
|
offset=None):
|
||||||
"""Retrieves all volumes in a project.
|
"""Retrieves all volumes in a project.
|
||||||
|
|
||||||
If no sort parameters are specified then the returned volumes are sorted
|
If no sort parameters are specified then the returned volumes are sorted
|
||||||
@ -1459,7 +1460,7 @@ def volume_get_all_by_project(context, project_id, marker, limit,
|
|||||||
filters['project_id'] = project_id
|
filters['project_id'] = project_id
|
||||||
# Generate the query
|
# Generate the query
|
||||||
query = _generate_paginate_query(context, session, marker, limit,
|
query = _generate_paginate_query(context, session, marker, limit,
|
||||||
sort_keys, sort_dirs, filters)
|
sort_keys, sort_dirs, filters, offset)
|
||||||
# No volumes would match, return empty list
|
# No volumes would match, return empty list
|
||||||
if query is None:
|
if query is None:
|
||||||
return []
|
return []
|
||||||
@ -1467,7 +1468,7 @@ def volume_get_all_by_project(context, project_id, marker, limit,
|
|||||||
|
|
||||||
|
|
||||||
def _generate_paginate_query(context, session, marker, limit, sort_keys,
|
def _generate_paginate_query(context, session, marker, limit, sort_keys,
|
||||||
sort_dirs, filters):
|
sort_dirs, filters, offset=None):
|
||||||
"""Generate the query to include the filters and the paginate options.
|
"""Generate the query to include the filters and the paginate options.
|
||||||
|
|
||||||
Returns a query with sorting / pagination criteria added or None
|
Returns a query with sorting / pagination criteria added or None
|
||||||
@ -1486,6 +1487,7 @@ def _generate_paginate_query(context, session, marker, limit, sort_keys,
|
|||||||
or sets cause an 'IN' operation, while exact matching
|
or sets cause an 'IN' operation, while exact matching
|
||||||
is used for other values, see _process_volume_filters
|
is used for other values, see _process_volume_filters
|
||||||
function for more information
|
function for more information
|
||||||
|
:param offset: number of items to skip
|
||||||
:returns: updated query or None
|
:returns: updated query or None
|
||||||
"""
|
"""
|
||||||
sort_keys, sort_dirs = process_sort_params(sort_keys,
|
sort_keys, sort_dirs = process_sort_params(sort_keys,
|
||||||
@ -1505,7 +1507,8 @@ def _generate_paginate_query(context, session, marker, limit, sort_keys,
|
|||||||
return sqlalchemyutils.paginate_query(query, models.Volume, limit,
|
return sqlalchemyutils.paginate_query(query, models.Volume, limit,
|
||||||
sort_keys,
|
sort_keys,
|
||||||
marker=marker_volume,
|
marker=marker_volume,
|
||||||
sort_dirs=sort_dirs)
|
sort_dirs=sort_dirs,
|
||||||
|
offset=offset)
|
||||||
|
|
||||||
|
|
||||||
def _process_volume_filters(query, filters):
|
def _process_volume_filters(query, filters):
|
||||||
|
@ -22,12 +22,15 @@ from testtools import matchers
|
|||||||
import webob
|
import webob
|
||||||
import webob.exc
|
import webob.exc
|
||||||
|
|
||||||
|
from oslo_config import cfg
|
||||||
|
|
||||||
from cinder.api import common
|
from cinder.api import common
|
||||||
from cinder import test
|
from cinder import test
|
||||||
|
|
||||||
|
|
||||||
NS = "{http://docs.openstack.org/compute/api/v1.1}"
|
NS = "{http://docs.openstack.org/compute/api/v1.1}"
|
||||||
ATOMNS = "{http://www.w3.org/2005/Atom}"
|
ATOMNS = "{http://www.w3.org/2005/Atom}"
|
||||||
|
CONF = cfg.CONF
|
||||||
|
|
||||||
|
|
||||||
class LimiterTest(test.TestCase):
|
class LimiterTest(test.TestCase):
|
||||||
@ -172,37 +175,45 @@ class PaginationParamsTest(test.TestCase):
|
|||||||
"""Test nonnumerical limit param."""
|
"""Test nonnumerical limit param."""
|
||||||
req = webob.Request.blank('/?limit=hello')
|
req = webob.Request.blank('/?limit=hello')
|
||||||
self.assertRaises(
|
self.assertRaises(
|
||||||
webob.exc.HTTPBadRequest, common.get_pagination_params, req)
|
webob.exc.HTTPBadRequest, common.get_pagination_params,
|
||||||
|
req.GET.copy())
|
||||||
|
|
||||||
def test_no_params(self):
|
def test_no_params(self):
|
||||||
"""Test no params."""
|
"""Test no params."""
|
||||||
req = webob.Request.blank('/')
|
req = webob.Request.blank('/')
|
||||||
self.assertEqual({}, common.get_pagination_params(req))
|
expected = (None, CONF.osapi_max_limit, 0)
|
||||||
|
self.assertEqual(expected,
|
||||||
|
common.get_pagination_params(req.GET.copy()))
|
||||||
|
|
||||||
def test_valid_marker(self):
|
def test_valid_marker(self):
|
||||||
"""Test valid marker param."""
|
"""Test valid marker param."""
|
||||||
req = webob.Request.blank(
|
marker = '263abb28-1de6-412f-b00b-f0ee0c4333c2'
|
||||||
'/?marker=263abb28-1de6-412f-b00b-f0ee0c4333c2')
|
req = webob.Request.blank('/?marker=' + marker)
|
||||||
self.assertEqual({'marker': '263abb28-1de6-412f-b00b-f0ee0c4333c2'},
|
expected = (marker, CONF.osapi_max_limit, 0)
|
||||||
common.get_pagination_params(req))
|
self.assertEqual(expected,
|
||||||
|
common.get_pagination_params(req.GET.copy()))
|
||||||
|
|
||||||
def test_valid_limit(self):
|
def test_valid_limit(self):
|
||||||
"""Test valid limit param."""
|
"""Test valid limit param."""
|
||||||
req = webob.Request.blank('/?limit=10')
|
req = webob.Request.blank('/?limit=10')
|
||||||
self.assertEqual({'limit': 10}, common.get_pagination_params(req))
|
expected = (None, 10, 0)
|
||||||
|
self.assertEqual(expected,
|
||||||
|
common.get_pagination_params(req.GET.copy()))
|
||||||
|
|
||||||
def test_invalid_limit(self):
|
def test_invalid_limit(self):
|
||||||
"""Test invalid limit param."""
|
"""Test invalid limit param."""
|
||||||
req = webob.Request.blank('/?limit=-2')
|
req = webob.Request.blank('/?limit=-2')
|
||||||
self.assertRaises(
|
self.assertRaises(
|
||||||
webob.exc.HTTPBadRequest, common.get_pagination_params, req)
|
webob.exc.HTTPBadRequest, common.get_pagination_params,
|
||||||
|
req.GET.copy())
|
||||||
|
|
||||||
def test_valid_limit_and_marker(self):
|
def test_valid_limit_and_marker(self):
|
||||||
"""Test valid limit and marker parameters."""
|
"""Test valid limit and marker parameters."""
|
||||||
marker = '263abb28-1de6-412f-b00b-f0ee0c4333c2'
|
marker = '263abb28-1de6-412f-b00b-f0ee0c4333c2'
|
||||||
req = webob.Request.blank('/?limit=20&marker=%s' % marker)
|
req = webob.Request.blank('/?limit=20&marker=%s' % marker)
|
||||||
self.assertEqual({'marker': marker, 'limit': 20},
|
expected = (marker, 20, 0)
|
||||||
common.get_pagination_params(req))
|
self.assertEqual(expected,
|
||||||
|
common.get_pagination_params(req.GET.copy()))
|
||||||
|
|
||||||
|
|
||||||
class SortParamUtilsTest(test.TestCase):
|
class SortParamUtilsTest(test.TestCase):
|
||||||
@ -354,22 +365,32 @@ class MiscFunctionsTest(test.TestCase):
|
|||||||
class TestCollectionLinks(test.TestCase):
|
class TestCollectionLinks(test.TestCase):
|
||||||
"""Tests the _get_collection_links method."""
|
"""Tests the _get_collection_links method."""
|
||||||
|
|
||||||
def _validate_next_link(self, href_link_mock, item_count,
|
def _validate_next_link(self, item_count, osapi_max_limit, limit,
|
||||||
osapi_max_limit, limit, should_link_exist):
|
should_link_exist):
|
||||||
req = mock.MagicMock()
|
req = webob.Request.blank('/?limit=%s' % limit if limit else '/')
|
||||||
href_link_mock.return_value = [{"rel": "next",
|
link_return = [{"rel": "next", "href": "fake_link"}]
|
||||||
"href": "fake_link"}]
|
|
||||||
self.flags(osapi_max_limit=osapi_max_limit)
|
self.flags(osapi_max_limit=osapi_max_limit)
|
||||||
if limit is None:
|
if limit is None:
|
||||||
params = mock.PropertyMock(return_value=dict())
|
|
||||||
limited_list_size = min(item_count, osapi_max_limit)
|
limited_list_size = min(item_count, osapi_max_limit)
|
||||||
else:
|
else:
|
||||||
params = mock.PropertyMock(return_value=dict(limit=limit))
|
limited_list_size = min(item_count, osapi_max_limit, limit)
|
||||||
limited_list_size = min(item_count, osapi_max_limit,
|
|
||||||
limit)
|
|
||||||
limited_list = [{"uuid": str(i)} for i in range(limited_list_size)]
|
limited_list = [{"uuid": str(i)} for i in range(limited_list_size)]
|
||||||
type(req).params = params
|
|
||||||
builder = common.ViewBuilder()
|
builder = common.ViewBuilder()
|
||||||
|
|
||||||
|
def get_pagination_params(params, max_limit=CONF.osapi_max_limit,
|
||||||
|
original_call=common.get_pagination_params):
|
||||||
|
return original_call(params, max_limit)
|
||||||
|
|
||||||
|
def _get_limit_param(params, max_limit=CONF.osapi_max_limit,
|
||||||
|
original_call=common._get_limit_param):
|
||||||
|
return original_call(params, max_limit)
|
||||||
|
|
||||||
|
with mock.patch.object(common, 'get_pagination_params',
|
||||||
|
get_pagination_params), \
|
||||||
|
mock.patch.object(common, '_get_limit_param',
|
||||||
|
_get_limit_param), \
|
||||||
|
mock.patch.object(common.ViewBuilder, '_generate_next_link',
|
||||||
|
return_value=link_return) as href_link_mock:
|
||||||
results = builder._get_collection_links(req, limited_list,
|
results = builder._get_collection_links(req, limited_list,
|
||||||
mock.sentinel.coll_key,
|
mock.sentinel.coll_key,
|
||||||
item_count, "uuid")
|
item_count, "uuid")
|
||||||
@ -382,171 +403,133 @@ class TestCollectionLinks(test.TestCase):
|
|||||||
self.assertFalse(href_link_mock.called)
|
self.assertFalse(href_link_mock.called)
|
||||||
self.assertThat(results, matchers.HasLength(0))
|
self.assertThat(results, matchers.HasLength(0))
|
||||||
|
|
||||||
@mock.patch('cinder.api.common.ViewBuilder._generate_next_link')
|
def test_items_equals_osapi_max_no_limit(self):
|
||||||
def test_items_equals_osapi_max_no_limit(self, href_link_mock):
|
|
||||||
item_count = 5
|
item_count = 5
|
||||||
osapi_max_limit = 5
|
osapi_max_limit = 5
|
||||||
limit = None
|
limit = None
|
||||||
should_link_exist = False
|
should_link_exist = True
|
||||||
self._validate_next_link(href_link_mock, item_count,
|
self._validate_next_link(item_count, osapi_max_limit, limit,
|
||||||
osapi_max_limit,
|
should_link_exist)
|
||||||
limit, should_link_exist)
|
|
||||||
|
|
||||||
@mock.patch('cinder.api.common.ViewBuilder._generate_next_link')
|
def test_items_equals_osapi_max_greater_than_limit(self):
|
||||||
def test_items_equals_osapi_max_greater_than_limit(self,
|
|
||||||
href_link_mock):
|
|
||||||
item_count = 5
|
item_count = 5
|
||||||
osapi_max_limit = 5
|
osapi_max_limit = 5
|
||||||
limit = 4
|
limit = 4
|
||||||
should_link_exist = True
|
should_link_exist = True
|
||||||
self._validate_next_link(href_link_mock, item_count,
|
self._validate_next_link(item_count, osapi_max_limit, limit,
|
||||||
osapi_max_limit,
|
should_link_exist)
|
||||||
limit, should_link_exist)
|
|
||||||
|
|
||||||
@mock.patch('cinder.api.common.ViewBuilder._generate_next_link')
|
def test_items_equals_osapi_max_equals_limit(self):
|
||||||
def test_items_equals_osapi_max_equals_limit(self, href_link_mock):
|
|
||||||
item_count = 5
|
item_count = 5
|
||||||
osapi_max_limit = 5
|
osapi_max_limit = 5
|
||||||
limit = 5
|
limit = 5
|
||||||
should_link_exist = True
|
should_link_exist = True
|
||||||
self._validate_next_link(href_link_mock, item_count,
|
self._validate_next_link(item_count, osapi_max_limit, limit,
|
||||||
osapi_max_limit,
|
should_link_exist)
|
||||||
limit, should_link_exist)
|
|
||||||
|
|
||||||
@mock.patch('cinder.api.common.ViewBuilder._generate_next_link')
|
def test_items_equals_osapi_max_less_than_limit(self):
|
||||||
def test_items_equals_osapi_max_less_than_limit(self, href_link_mock):
|
|
||||||
item_count = 5
|
item_count = 5
|
||||||
osapi_max_limit = 5
|
osapi_max_limit = 5
|
||||||
limit = 6
|
limit = 6
|
||||||
should_link_exist = False
|
should_link_exist = True
|
||||||
self._validate_next_link(href_link_mock, item_count,
|
self._validate_next_link(item_count, osapi_max_limit, limit,
|
||||||
osapi_max_limit,
|
should_link_exist)
|
||||||
limit, should_link_exist)
|
|
||||||
|
|
||||||
@mock.patch('cinder.api.common.ViewBuilder._generate_next_link')
|
def test_items_less_than_osapi_max_no_limit(self):
|
||||||
def test_items_less_than_osapi_max_no_limit(self, href_link_mock):
|
|
||||||
item_count = 5
|
item_count = 5
|
||||||
osapi_max_limit = 7
|
osapi_max_limit = 7
|
||||||
limit = None
|
limit = None
|
||||||
should_link_exist = False
|
should_link_exist = False
|
||||||
self._validate_next_link(href_link_mock, item_count,
|
self._validate_next_link(item_count, osapi_max_limit, limit,
|
||||||
osapi_max_limit,
|
should_link_exist)
|
||||||
limit, should_link_exist)
|
|
||||||
|
|
||||||
@mock.patch('cinder.api.common.ViewBuilder._generate_next_link')
|
def test_limit_less_than_items_less_than_osapi_max(self):
|
||||||
def test_limit_less_than_items_less_than_osapi_max(self, href_link_mock):
|
|
||||||
item_count = 5
|
item_count = 5
|
||||||
osapi_max_limit = 7
|
osapi_max_limit = 7
|
||||||
limit = 4
|
limit = 4
|
||||||
should_link_exist = True
|
should_link_exist = True
|
||||||
self._validate_next_link(href_link_mock, item_count,
|
self._validate_next_link(item_count, osapi_max_limit, limit,
|
||||||
osapi_max_limit,
|
should_link_exist)
|
||||||
limit, should_link_exist)
|
|
||||||
|
|
||||||
@mock.patch('cinder.api.common.ViewBuilder._generate_next_link')
|
def test_limit_equals_items_less_than_osapi_max(self):
|
||||||
def test_limit_equals_items_less_than_osapi_max(self, href_link_mock):
|
|
||||||
item_count = 5
|
item_count = 5
|
||||||
osapi_max_limit = 7
|
osapi_max_limit = 7
|
||||||
limit = 5
|
limit = 5
|
||||||
should_link_exist = True
|
should_link_exist = True
|
||||||
self._validate_next_link(href_link_mock, item_count,
|
self._validate_next_link(item_count, osapi_max_limit, limit,
|
||||||
osapi_max_limit,
|
should_link_exist)
|
||||||
limit, should_link_exist)
|
|
||||||
|
|
||||||
@mock.patch('cinder.api.common.ViewBuilder._generate_next_link')
|
def test_items_less_than_limit_less_than_osapi_max(self):
|
||||||
def test_items_less_than_limit_less_than_osapi_max(self, href_link_mock):
|
|
||||||
item_count = 5
|
item_count = 5
|
||||||
osapi_max_limit = 7
|
osapi_max_limit = 7
|
||||||
limit = 6
|
limit = 6
|
||||||
should_link_exist = False
|
should_link_exist = False
|
||||||
self._validate_next_link(href_link_mock, item_count,
|
self._validate_next_link(item_count, osapi_max_limit, limit,
|
||||||
osapi_max_limit,
|
should_link_exist)
|
||||||
limit, should_link_exist)
|
|
||||||
|
|
||||||
@mock.patch('cinder.api.common.ViewBuilder._generate_next_link')
|
def test_items_less_than_osapi_max_equals_limit(self):
|
||||||
def test_items_less_than_osapi_max_equals_limit(self, href_link_mock):
|
|
||||||
item_count = 5
|
item_count = 5
|
||||||
osapi_max_limit = 7
|
osapi_max_limit = 7
|
||||||
limit = 7
|
limit = 7
|
||||||
should_link_exist = False
|
should_link_exist = False
|
||||||
self._validate_next_link(href_link_mock, item_count,
|
self._validate_next_link(item_count, osapi_max_limit, limit,
|
||||||
osapi_max_limit,
|
should_link_exist)
|
||||||
limit, should_link_exist)
|
|
||||||
|
|
||||||
@mock.patch('cinder.api.common.ViewBuilder._generate_next_link')
|
def test_items_less_than_osapi_max_less_than_limit(self):
|
||||||
def test_items_less_than_osapi_max_less_than_limit(self, href_link_mock):
|
|
||||||
item_count = 5
|
item_count = 5
|
||||||
osapi_max_limit = 7
|
osapi_max_limit = 7
|
||||||
limit = 8
|
limit = 8
|
||||||
should_link_exist = False
|
should_link_exist = False
|
||||||
self._validate_next_link(href_link_mock, item_count,
|
self._validate_next_link(item_count, osapi_max_limit, limit,
|
||||||
osapi_max_limit,
|
should_link_exist)
|
||||||
limit, should_link_exist)
|
|
||||||
|
|
||||||
@mock.patch('cinder.api.common.ViewBuilder._generate_next_link')
|
def test_items_greater_than_osapi_max_no_limit(self):
|
||||||
def test_items_greater_than_osapi_max_no_limit(self, href_link_mock):
|
|
||||||
item_count = 5
|
item_count = 5
|
||||||
osapi_max_limit = 3
|
osapi_max_limit = 3
|
||||||
limit = None
|
limit = None
|
||||||
should_link_exist = True
|
should_link_exist = True
|
||||||
self._validate_next_link(href_link_mock, item_count,
|
self._validate_next_link(item_count, osapi_max_limit, limit,
|
||||||
osapi_max_limit,
|
should_link_exist)
|
||||||
limit, should_link_exist)
|
|
||||||
|
|
||||||
@mock.patch('cinder.api.common.ViewBuilder._generate_next_link')
|
def test_limit_less_than_items_greater_than_osapi_max(self):
|
||||||
def test_limit_less_than_items_greater_than_osapi_max(self,
|
|
||||||
href_link_mock):
|
|
||||||
item_count = 5
|
item_count = 5
|
||||||
osapi_max_limit = 3
|
osapi_max_limit = 3
|
||||||
limit = 2
|
limit = 2
|
||||||
should_link_exist = True
|
should_link_exist = True
|
||||||
self._validate_next_link(href_link_mock, item_count,
|
self._validate_next_link(item_count, osapi_max_limit, limit,
|
||||||
osapi_max_limit,
|
should_link_exist)
|
||||||
limit, should_link_exist)
|
|
||||||
|
|
||||||
@mock.patch('cinder.api.common.ViewBuilder._generate_next_link')
|
def test_items_greater_than_osapi_max_equals_limit(self):
|
||||||
def test_items_greater_than_osapi_max_equals_limit(self,
|
|
||||||
href_link_mock):
|
|
||||||
item_count = 5
|
item_count = 5
|
||||||
osapi_max_limit = 3
|
osapi_max_limit = 3
|
||||||
limit = 3
|
limit = 3
|
||||||
should_link_exist = True
|
should_link_exist = True
|
||||||
self._validate_next_link(href_link_mock, item_count,
|
self._validate_next_link(item_count, osapi_max_limit, limit,
|
||||||
osapi_max_limit,
|
should_link_exist)
|
||||||
limit, should_link_exist)
|
|
||||||
|
|
||||||
@mock.patch('cinder.api.common.ViewBuilder._generate_next_link')
|
def test_items_greater_than_limit_greater_than_osapi_max(self):
|
||||||
def test_items_greater_than_limit_greater_than_osapi_max(self,
|
|
||||||
href_link_mock):
|
|
||||||
item_count = 5
|
item_count = 5
|
||||||
osapi_max_limit = 3
|
osapi_max_limit = 3
|
||||||
limit = 4
|
limit = 4
|
||||||
should_link_exist = True
|
should_link_exist = True
|
||||||
self._validate_next_link(href_link_mock, item_count,
|
self._validate_next_link(item_count, osapi_max_limit, limit,
|
||||||
osapi_max_limit,
|
should_link_exist)
|
||||||
limit, should_link_exist)
|
|
||||||
|
|
||||||
@mock.patch('cinder.api.common.ViewBuilder._generate_next_link')
|
def test_items_equals_limit_greater_than_osapi_max(self):
|
||||||
def test_items_equals_limit_greater_than_osapi_max(self,
|
|
||||||
href_link_mock):
|
|
||||||
item_count = 5
|
item_count = 5
|
||||||
osapi_max_limit = 3
|
osapi_max_limit = 3
|
||||||
limit = 5
|
limit = 5
|
||||||
should_link_exist = True
|
should_link_exist = True
|
||||||
self._validate_next_link(href_link_mock, item_count,
|
self._validate_next_link(item_count, osapi_max_limit, limit,
|
||||||
osapi_max_limit,
|
should_link_exist)
|
||||||
limit, should_link_exist)
|
|
||||||
|
|
||||||
@mock.patch('cinder.api.common.ViewBuilder._generate_next_link')
|
def test_limit_greater_than_items_greater_than_osapi_max(self):
|
||||||
def test_limit_greater_than_items_greater_than_osapi_max(self,
|
|
||||||
href_link_mock):
|
|
||||||
item_count = 5
|
item_count = 5
|
||||||
osapi_max_limit = 3
|
osapi_max_limit = 3
|
||||||
limit = 6
|
limit = 6
|
||||||
should_link_exist = True
|
should_link_exist = True
|
||||||
self._validate_next_link(href_link_mock, item_count,
|
self._validate_next_link(item_count, osapi_max_limit, limit,
|
||||||
osapi_max_limit,
|
should_link_exist)
|
||||||
limit, should_link_exist)
|
|
||||||
|
|
||||||
|
|
||||||
class LinkPrefixTest(test.TestCase):
|
class LinkPrefixTest(test.TestCase):
|
||||||
|
@ -620,7 +620,8 @@ class VolumeApiTest(test.TestCase):
|
|||||||
def stub_volume_get_all_by_project(context, project_id, marker,
|
def stub_volume_get_all_by_project(context, project_id, marker,
|
||||||
limit, sort_keys=None,
|
limit, sort_keys=None,
|
||||||
sort_dirs=None, filters=None,
|
sort_dirs=None, filters=None,
|
||||||
viewable_admin_meta=False):
|
viewable_admin_meta=False,
|
||||||
|
offset=None):
|
||||||
return [
|
return [
|
||||||
stubs.stub_volume(1, display_name='vol1'),
|
stubs.stub_volume(1, display_name='vol1'),
|
||||||
stubs.stub_volume(2, display_name='vol2'),
|
stubs.stub_volume(2, display_name='vol2'),
|
||||||
|
@ -139,7 +139,7 @@ def stub_volume_get_db(context, volume_id):
|
|||||||
|
|
||||||
def stub_volume_get_all(context, search_opts=None, marker=None, limit=None,
|
def stub_volume_get_all(context, search_opts=None, marker=None, limit=None,
|
||||||
sort_keys=None, sort_dirs=None, filters=None,
|
sort_keys=None, sort_dirs=None, filters=None,
|
||||||
viewable_admin_meta=False):
|
viewable_admin_meta=False, offset=None):
|
||||||
return [stub_volume(100, project_id='fake'),
|
return [stub_volume(100, project_id='fake'),
|
||||||
stub_volume(101, project_id='superfake'),
|
stub_volume(101, project_id='superfake'),
|
||||||
stub_volume(102, project_id='superduperfake')]
|
stub_volume(102, project_id='superduperfake')]
|
||||||
@ -148,7 +148,7 @@ def stub_volume_get_all(context, search_opts=None, marker=None, limit=None,
|
|||||||
def stub_volume_get_all_by_project(self, context, marker, limit,
|
def stub_volume_get_all_by_project(self, context, marker, limit,
|
||||||
sort_keys=None, sort_dirs=None,
|
sort_keys=None, sort_dirs=None,
|
||||||
filters=None,
|
filters=None,
|
||||||
viewable_admin_meta=False):
|
viewable_admin_meta=False, offset=None):
|
||||||
filters = filters or {}
|
filters = filters or {}
|
||||||
return [stub_volume_get(self, context, '1')]
|
return [stub_volume_get(self, context, '1')]
|
||||||
|
|
||||||
|
@ -25,6 +25,7 @@ from six.moves import range
|
|||||||
from six.moves import urllib
|
from six.moves import urllib
|
||||||
import webob
|
import webob
|
||||||
|
|
||||||
|
from cinder.api import common
|
||||||
from cinder.api import extensions
|
from cinder.api import extensions
|
||||||
from cinder.api.v2 import volumes
|
from cinder.api.v2 import volumes
|
||||||
from cinder import consistencygroup as consistencygroupAPI
|
from cinder import consistencygroup as consistencygroupAPI
|
||||||
@ -36,6 +37,7 @@ from cinder.tests.unit.api import fakes
|
|||||||
from cinder.tests.unit.api.v2 import stubs
|
from cinder.tests.unit.api.v2 import stubs
|
||||||
from cinder.tests.unit import fake_notifier
|
from cinder.tests.unit import fake_notifier
|
||||||
from cinder.tests.unit.image import fake as fake_image
|
from cinder.tests.unit.image import fake as fake_image
|
||||||
|
from cinder.tests.unit import utils
|
||||||
from cinder.volume import api as volume_api
|
from cinder.volume import api as volume_api
|
||||||
|
|
||||||
CONF = cfg.CONF
|
CONF = cfg.CONF
|
||||||
@ -60,6 +62,7 @@ class VolumeApiTest(test.TestCase):
|
|||||||
self.stubs.Set(db, 'service_get_all_by_topic',
|
self.stubs.Set(db, 'service_get_all_by_topic',
|
||||||
stubs.stub_service_get_all_by_topic)
|
stubs.stub_service_get_all_by_topic)
|
||||||
self.maxDiff = None
|
self.maxDiff = None
|
||||||
|
self.ctxt = context.RequestContext('admin', 'fakeproject', True)
|
||||||
|
|
||||||
def test_volume_create(self):
|
def test_volume_create(self):
|
||||||
self.stubs.Set(volume_api.API, 'get', stubs.stub_volume_get)
|
self.stubs.Set(volume_api.API, 'get', stubs.stub_volume_get)
|
||||||
@ -731,7 +734,8 @@ class VolumeApiTest(test.TestCase):
|
|||||||
def stub_volume_get_all_by_project(context, project_id, marker, limit,
|
def stub_volume_get_all_by_project(context, project_id, marker, limit,
|
||||||
sort_keys=None, sort_dirs=None,
|
sort_keys=None, sort_dirs=None,
|
||||||
filters=None,
|
filters=None,
|
||||||
viewable_admin_meta=False):
|
viewable_admin_meta=False,
|
||||||
|
offset=0):
|
||||||
return [
|
return [
|
||||||
stubs.stub_volume(1, display_name='vol1'),
|
stubs.stub_volume(1, display_name='vol1'),
|
||||||
stubs.stub_volume(2, display_name='vol2'),
|
stubs.stub_volume(2, display_name='vol2'),
|
||||||
@ -776,13 +780,13 @@ class VolumeApiTest(test.TestCase):
|
|||||||
|
|
||||||
def test_volume_index_limit_negative(self):
|
def test_volume_index_limit_negative(self):
|
||||||
req = fakes.HTTPRequest.blank('/v2/volumes?limit=-1')
|
req = fakes.HTTPRequest.blank('/v2/volumes?limit=-1')
|
||||||
self.assertRaises(exception.Invalid,
|
self.assertRaises(webob.exc.HTTPBadRequest,
|
||||||
self.controller.index,
|
self.controller.index,
|
||||||
req)
|
req)
|
||||||
|
|
||||||
def test_volume_index_limit_non_int(self):
|
def test_volume_index_limit_non_int(self):
|
||||||
req = fakes.HTTPRequest.blank('/v2/volumes?limit=a')
|
req = fakes.HTTPRequest.blank('/v2/volumes?limit=a')
|
||||||
self.assertRaises(exception.Invalid,
|
self.assertRaises(webob.exc.HTTPBadRequest,
|
||||||
self.controller.index,
|
self.controller.index,
|
||||||
req)
|
req)
|
||||||
|
|
||||||
@ -797,32 +801,29 @@ class VolumeApiTest(test.TestCase):
|
|||||||
self.assertEqual(1, len(volumes))
|
self.assertEqual(1, len(volumes))
|
||||||
self.assertEqual('1', volumes[0]['id'])
|
self.assertEqual('1', volumes[0]['id'])
|
||||||
|
|
||||||
def test_volume_index_limit_offset(self):
|
def _create_db_volumes(self, num_volumes):
|
||||||
def stub_volume_get_all_by_project(context, project_id, marker, limit,
|
volumes = [utils.create_volume(self.ctxt, display_name='vol%s' % i)
|
||||||
sort_keys=None, sort_dirs=None,
|
for i in range(num_volumes)]
|
||||||
filters=None,
|
for vol in volumes:
|
||||||
viewable_admin_meta=False):
|
self.addCleanup(db.volume_destroy, self.ctxt, vol.id)
|
||||||
return [
|
volumes.reverse()
|
||||||
stubs.stub_volume(1, display_name='vol1'),
|
return volumes
|
||||||
stubs.stub_volume(2, display_name='vol2'),
|
|
||||||
]
|
|
||||||
self.stubs.Set(db, 'volume_get_all_by_project',
|
|
||||||
stub_volume_get_all_by_project)
|
|
||||||
self.stubs.Set(volume_api.API, 'get', stubs.stub_volume_get)
|
|
||||||
|
|
||||||
|
def test_volume_index_limit_offset(self):
|
||||||
|
created_volumes = self._create_db_volumes(2)
|
||||||
req = fakes.HTTPRequest.blank('/v2/volumes?limit=2&offset=1')
|
req = fakes.HTTPRequest.blank('/v2/volumes?limit=2&offset=1')
|
||||||
res_dict = self.controller.index(req)
|
res_dict = self.controller.index(req)
|
||||||
volumes = res_dict['volumes']
|
volumes = res_dict['volumes']
|
||||||
self.assertEqual(1, len(volumes))
|
self.assertEqual(1, len(volumes))
|
||||||
self.assertEqual(2, volumes[0]['id'])
|
self.assertEqual(created_volumes[1].id, volumes[0]['id'])
|
||||||
|
|
||||||
req = fakes.HTTPRequest.blank('/v2/volumes?limit=-1&offset=1')
|
req = fakes.HTTPRequest.blank('/v2/volumes?limit=-1&offset=1')
|
||||||
self.assertRaises(exception.InvalidInput,
|
self.assertRaises(webob.exc.HTTPBadRequest,
|
||||||
self.controller.index,
|
self.controller.index,
|
||||||
req)
|
req)
|
||||||
|
|
||||||
req = fakes.HTTPRequest.blank('/v2/volumes?limit=a&offset=1')
|
req = fakes.HTTPRequest.blank('/v2/volumes?limit=a&offset=1')
|
||||||
self.assertRaises(exception.InvalidInput,
|
self.assertRaises(webob.exc.HTTPBadRequest,
|
||||||
self.controller.index,
|
self.controller.index,
|
||||||
req)
|
req)
|
||||||
|
|
||||||
@ -830,7 +831,8 @@ class VolumeApiTest(test.TestCase):
|
|||||||
def stub_volume_get_all_by_project(context, project_id, marker, limit,
|
def stub_volume_get_all_by_project(context, project_id, marker, limit,
|
||||||
sort_keys=None, sort_dirs=None,
|
sort_keys=None, sort_dirs=None,
|
||||||
filters=None,
|
filters=None,
|
||||||
viewable_admin_meta=False):
|
viewable_admin_meta=False,
|
||||||
|
offset=0):
|
||||||
return [
|
return [
|
||||||
stubs.stub_volume(1, display_name='vol1'),
|
stubs.stub_volume(1, display_name='vol1'),
|
||||||
stubs.stub_volume(2, display_name='vol2'),
|
stubs.stub_volume(2, display_name='vol2'),
|
||||||
@ -867,13 +869,13 @@ class VolumeApiTest(test.TestCase):
|
|||||||
|
|
||||||
def test_volume_detail_limit_negative(self):
|
def test_volume_detail_limit_negative(self):
|
||||||
req = fakes.HTTPRequest.blank('/v2/volumes/detail?limit=-1')
|
req = fakes.HTTPRequest.blank('/v2/volumes/detail?limit=-1')
|
||||||
self.assertRaises(exception.Invalid,
|
self.assertRaises(webob.exc.HTTPBadRequest,
|
||||||
self.controller.detail,
|
self.controller.detail,
|
||||||
req)
|
req)
|
||||||
|
|
||||||
def test_volume_detail_limit_non_int(self):
|
def test_volume_detail_limit_non_int(self):
|
||||||
req = fakes.HTTPRequest.blank('/v2/volumes/detail?limit=a')
|
req = fakes.HTTPRequest.blank('/v2/volumes/detail?limit=a')
|
||||||
self.assertRaises(exception.Invalid,
|
self.assertRaises(webob.exc.HTTPBadRequest,
|
||||||
self.controller.detail,
|
self.controller.detail,
|
||||||
req)
|
req)
|
||||||
|
|
||||||
@ -889,38 +891,27 @@ class VolumeApiTest(test.TestCase):
|
|||||||
self.assertEqual('1', volumes[0]['id'])
|
self.assertEqual('1', volumes[0]['id'])
|
||||||
|
|
||||||
def test_volume_detail_limit_offset(self):
|
def test_volume_detail_limit_offset(self):
|
||||||
def stub_volume_get_all_by_project(context, project_id, marker, limit,
|
created_volumes = self._create_db_volumes(2)
|
||||||
sort_keys=None, sort_dirs=None,
|
|
||||||
filters=None,
|
|
||||||
viewable_admin_meta=False):
|
|
||||||
return [
|
|
||||||
stubs.stub_volume(1, display_name='vol1'),
|
|
||||||
stubs.stub_volume(2, display_name='vol2'),
|
|
||||||
]
|
|
||||||
self.stubs.Set(db, 'volume_get_all_by_project',
|
|
||||||
stub_volume_get_all_by_project)
|
|
||||||
self.stubs.Set(volume_api.API, 'get', stubs.stub_volume_get)
|
|
||||||
|
|
||||||
req = fakes.HTTPRequest.blank('/v2/volumes/detail?limit=2&offset=1')
|
req = fakes.HTTPRequest.blank('/v2/volumes/detail?limit=2&offset=1')
|
||||||
res_dict = self.controller.detail(req)
|
res_dict = self.controller.detail(req)
|
||||||
volumes = res_dict['volumes']
|
volumes = res_dict['volumes']
|
||||||
self.assertEqual(1, len(volumes))
|
self.assertEqual(1, len(volumes))
|
||||||
self.assertEqual(2, volumes[0]['id'])
|
self.assertEqual(created_volumes[1].id, volumes[0]['id'])
|
||||||
|
|
||||||
req = fakes.HTTPRequest.blank('/v2/volumes/detail?limit=2&offset=1',
|
req = fakes.HTTPRequest.blank('/v2/volumes/detail?limit=2&offset=1',
|
||||||
use_admin_context=True)
|
use_admin_context=True)
|
||||||
res_dict = self.controller.detail(req)
|
res_dict = self.controller.detail(req)
|
||||||
volumes = res_dict['volumes']
|
volumes = res_dict['volumes']
|
||||||
self.assertEqual(1, len(volumes))
|
self.assertEqual(1, len(volumes))
|
||||||
self.assertEqual(2, volumes[0]['id'])
|
self.assertEqual(created_volumes[1].id, volumes[0]['id'])
|
||||||
|
|
||||||
req = fakes.HTTPRequest.blank('/v2/volumes/detail?limit=-1&offset=1')
|
req = fakes.HTTPRequest.blank('/v2/volumes/detail?limit=-1&offset=1')
|
||||||
self.assertRaises(exception.InvalidInput,
|
self.assertRaises(webob.exc.HTTPBadRequest,
|
||||||
self.controller.detail,
|
self.controller.detail,
|
||||||
req)
|
req)
|
||||||
|
|
||||||
req = fakes.HTTPRequest.blank('/v2/volumes/detail?limit=a&offset=1')
|
req = fakes.HTTPRequest.blank('/v2/volumes/detail?limit=a&offset=1')
|
||||||
self.assertRaises(exception.InvalidInput,
|
self.assertRaises(webob.exc.HTTPBadRequest,
|
||||||
self.controller.detail,
|
self.controller.detail,
|
||||||
req)
|
req)
|
||||||
|
|
||||||
@ -933,84 +924,71 @@ class VolumeApiTest(test.TestCase):
|
|||||||
expected = {'volumes': []}
|
expected = {'volumes': []}
|
||||||
self.assertEqual(expected, res_dict)
|
self.assertEqual(expected, res_dict)
|
||||||
|
|
||||||
def test_volume_default_limit(self):
|
def _validate_next_link(self, detailed, item_count, osapi_max_limit, limit,
|
||||||
self.stubs.Set(volume_api.API, 'get', stubs.stub_volume_get)
|
should_link_exist):
|
||||||
|
keys_fns = (('volumes', self.controller.index),
|
||||||
|
('volumes/detail', self.controller.detail))
|
||||||
|
key, fn = keys_fns[detailed]
|
||||||
|
|
||||||
def _verify_links(links, url_key):
|
req_string = '/v2/%s?all_tenants=1' % key
|
||||||
"""Verify next link and url."""
|
if limit:
|
||||||
self.assertEqual('next', links[0]['rel'])
|
req_string += '&limit=%s' % limit
|
||||||
href_parts = urllib.parse.urlparse(links[0]['href'])
|
req = fakes.HTTPRequest.blank(req_string, use_admin_context=True)
|
||||||
self.assertEqual('/v2/fakeproject/%s' % key, href_parts.path)
|
|
||||||
|
link_return = [{"rel": "next", "href": "fake_link"}]
|
||||||
|
self.flags(osapi_max_limit=osapi_max_limit)
|
||||||
|
|
||||||
|
def get_pagination_params(params, max_limit=CONF.osapi_max_limit,
|
||||||
|
original_call=common.get_pagination_params):
|
||||||
|
return original_call(params, max_limit)
|
||||||
|
|
||||||
|
def _get_limit_param(params, max_limit=CONF.osapi_max_limit,
|
||||||
|
original_call=common._get_limit_param):
|
||||||
|
return original_call(params, max_limit)
|
||||||
|
|
||||||
|
with mock.patch.object(common, 'get_pagination_params',
|
||||||
|
get_pagination_params), \
|
||||||
|
mock.patch.object(common, '_get_limit_param',
|
||||||
|
_get_limit_param), \
|
||||||
|
mock.patch.object(common.ViewBuilder, '_generate_next_link',
|
||||||
|
return_value=link_return):
|
||||||
|
res_dict = fn(req)
|
||||||
|
self.assertEqual(item_count, len(res_dict['volumes']))
|
||||||
|
self.assertEqual(should_link_exist, 'volumes_links' in res_dict)
|
||||||
|
|
||||||
|
def test_volume_default_limit(self):
|
||||||
|
self.stubs.UnsetAll()
|
||||||
|
self._create_db_volumes(3)
|
||||||
|
|
||||||
# Verify both the index and detail queries
|
# Verify both the index and detail queries
|
||||||
api_keys = ['volumes', 'volumes/detail']
|
for detailed in (True, False):
|
||||||
fns = [self.controller.index, self.controller.detail]
|
|
||||||
|
|
||||||
# Number of volumes equals the max, next link not included
|
|
||||||
def stub_volume_get_all(context, marker, limit,
|
|
||||||
sort_keys=None, sort_dirs=None,
|
|
||||||
filters=None,
|
|
||||||
viewable_admin_meta=False):
|
|
||||||
vols = [stubs.stub_volume(i)
|
|
||||||
for i in range(CONF.osapi_max_limit)]
|
|
||||||
if limit is None or limit >= len(vols):
|
|
||||||
return vols
|
|
||||||
return vols[:limit]
|
|
||||||
self.stubs.Set(db, 'volume_get_all', stub_volume_get_all)
|
|
||||||
for key, fn in zip(api_keys, fns):
|
|
||||||
req = fakes.HTTPRequest.blank('/v2/%s?all_tenants=1' % key,
|
|
||||||
use_admin_context=True)
|
|
||||||
res_dict = fn(req)
|
|
||||||
self.assertEqual(len(res_dict['volumes']), CONF.osapi_max_limit)
|
|
||||||
self.assertFalse('volumes_links' in res_dict)
|
|
||||||
|
|
||||||
# Number of volumes less than max, do not include
|
# Number of volumes less than max, do not include
|
||||||
def stub_volume_get_all2(context, marker, limit,
|
self._validate_next_link(detailed, item_count=3, osapi_max_limit=4,
|
||||||
sort_keys=None, sort_dirs=None,
|
limit=None, should_link_exist=False)
|
||||||
filters=None,
|
|
||||||
viewable_admin_meta=False):
|
# Number of volumes equals the max, next link will be included
|
||||||
vols = [stubs.stub_volume(i)
|
self._validate_next_link(detailed, item_count=3, osapi_max_limit=3,
|
||||||
for i in range(100)]
|
limit=None, should_link_exist=True)
|
||||||
if limit is None or limit >= len(vols):
|
|
||||||
return vols
|
|
||||||
return vols[:limit]
|
|
||||||
self.stubs.Set(db, 'volume_get_all', stub_volume_get_all2)
|
|
||||||
for key, fn in zip(api_keys, fns):
|
|
||||||
req = fakes.HTTPRequest.blank('/v2/%s?all_tenants=1' % key,
|
|
||||||
use_admin_context=True)
|
|
||||||
res_dict = fn(req)
|
|
||||||
self.assertEqual(100, len(res_dict['volumes']))
|
|
||||||
self.assertFalse('volumes_links' in res_dict)
|
|
||||||
|
|
||||||
# Number of volumes more than the max, include next link
|
# Number of volumes more than the max, include next link
|
||||||
def stub_volume_get_all3(context, marker, limit,
|
self._validate_next_link(detailed, item_count=2, osapi_max_limit=2,
|
||||||
sort_keys=None, sort_dirs=None,
|
limit=None, should_link_exist=True)
|
||||||
filters=None,
|
|
||||||
viewable_admin_meta=False):
|
# Limit lower than max but doesn't limit, no next link
|
||||||
vols = [stubs.stub_volume(i)
|
self._validate_next_link(detailed, item_count=3, osapi_max_limit=5,
|
||||||
for i in range(CONF.osapi_max_limit + 100)]
|
limit=4, should_link_exist=False)
|
||||||
if limit is None or limit >= len(vols):
|
|
||||||
return vols
|
# Limit lower than max and limits, we have next link
|
||||||
return vols[:limit]
|
self._validate_next_link(detailed, item_count=2, osapi_max_limit=4,
|
||||||
self.stubs.Set(db, 'volume_get_all', stub_volume_get_all3)
|
limit=2, should_link_exist=True)
|
||||||
for key, fn in zip(api_keys, fns):
|
|
||||||
req = fakes.HTTPRequest.blank('/v2/%s?all_tenants=1' % key,
|
# Limit higher than max and max limits, we have next link
|
||||||
use_admin_context=True)
|
self._validate_next_link(detailed, item_count=2, osapi_max_limit=2,
|
||||||
res_dict = fn(req)
|
limit=4, should_link_exist=True)
|
||||||
self.assertEqual(CONF.osapi_max_limit, len(res_dict['volumes']))
|
|
||||||
volumes_links = res_dict['volumes_links']
|
# Limit higher than max but none of them limiting, no next link
|
||||||
_verify_links(volumes_links, key)
|
self._validate_next_link(detailed, item_count=3, osapi_max_limit=4,
|
||||||
# Pass a limit that is greater than the max and the total number of
|
limit=5, should_link_exist=False)
|
||||||
# volumes, ensure only the maximum is returned and that the next
|
|
||||||
# link is present.
|
|
||||||
for key, fn in zip(api_keys, fns):
|
|
||||||
req = fakes.HTTPRequest.blank('/v2/%s?all_tenants=1&limit=%d'
|
|
||||||
% (key, CONF.osapi_max_limit * 2),
|
|
||||||
use_admin_context=True)
|
|
||||||
res_dict = fn(req)
|
|
||||||
self.assertEqual(CONF.osapi_max_limit, len(res_dict['volumes']))
|
|
||||||
volumes_links = res_dict['volumes_links']
|
|
||||||
_verify_links(volumes_links, key)
|
|
||||||
|
|
||||||
def test_volume_list_default_filters(self):
|
def test_volume_list_default_filters(self):
|
||||||
"""Tests that the default filters from volume.api.API.get_all are set.
|
"""Tests that the default filters from volume.api.API.get_all are set.
|
||||||
@ -1027,7 +1005,8 @@ class VolumeApiTest(test.TestCase):
|
|||||||
def stub_volume_get_all_by_project(context, project_id, marker, limit,
|
def stub_volume_get_all_by_project(context, project_id, marker, limit,
|
||||||
sort_keys=None, sort_dirs=None,
|
sort_keys=None, sort_dirs=None,
|
||||||
filters=None,
|
filters=None,
|
||||||
viewable_admin_meta=False):
|
viewable_admin_meta=False,
|
||||||
|
offset=0):
|
||||||
self.assertEqual(True, filters['no_migration_targets'])
|
self.assertEqual(True, filters['no_migration_targets'])
|
||||||
self.assertFalse('all_tenants' in filters)
|
self.assertFalse('all_tenants' in filters)
|
||||||
return [stubs.stub_volume(1, display_name='vol1')]
|
return [stubs.stub_volume(1, display_name='vol1')]
|
||||||
@ -1035,7 +1014,7 @@ class VolumeApiTest(test.TestCase):
|
|||||||
def stub_volume_get_all(context, marker, limit,
|
def stub_volume_get_all(context, marker, limit,
|
||||||
sort_keys=None, sort_dirs=None,
|
sort_keys=None, sort_dirs=None,
|
||||||
filters=None,
|
filters=None,
|
||||||
viewable_admin_meta=False):
|
viewable_admin_meta=False, offset=0):
|
||||||
return []
|
return []
|
||||||
self.stubs.Set(db, 'volume_get_all_by_project',
|
self.stubs.Set(db, 'volume_get_all_by_project',
|
||||||
stub_volume_get_all_by_project)
|
stub_volume_get_all_by_project)
|
||||||
@ -1053,14 +1032,15 @@ class VolumeApiTest(test.TestCase):
|
|||||||
def stub_volume_get_all_by_project2(context, project_id, marker, limit,
|
def stub_volume_get_all_by_project2(context, project_id, marker, limit,
|
||||||
sort_keys=None, sort_dirs=None,
|
sort_keys=None, sort_dirs=None,
|
||||||
filters=None,
|
filters=None,
|
||||||
viewable_admin_meta=False):
|
viewable_admin_meta=False,
|
||||||
|
offset=0):
|
||||||
self.assertFalse('no_migration_targets' in filters)
|
self.assertFalse('no_migration_targets' in filters)
|
||||||
return [stubs.stub_volume(1, display_name='vol2')]
|
return [stubs.stub_volume(1, display_name='vol2')]
|
||||||
|
|
||||||
def stub_volume_get_all2(context, marker, limit,
|
def stub_volume_get_all2(context, marker, limit,
|
||||||
sort_keys=None, sort_dirs=None,
|
sort_keys=None, sort_dirs=None,
|
||||||
filters=None,
|
filters=None,
|
||||||
viewable_admin_meta=False):
|
viewable_admin_meta=False, offset=0):
|
||||||
return []
|
return []
|
||||||
self.stubs.Set(db, 'volume_get_all_by_project',
|
self.stubs.Set(db, 'volume_get_all_by_project',
|
||||||
stub_volume_get_all_by_project2)
|
stub_volume_get_all_by_project2)
|
||||||
@ -1076,13 +1056,14 @@ class VolumeApiTest(test.TestCase):
|
|||||||
def stub_volume_get_all_by_project3(context, project_id, marker, limit,
|
def stub_volume_get_all_by_project3(context, project_id, marker, limit,
|
||||||
sort_keys=None, sort_dirs=None,
|
sort_keys=None, sort_dirs=None,
|
||||||
filters=None,
|
filters=None,
|
||||||
viewable_admin_meta=False):
|
viewable_admin_meta=False,
|
||||||
|
offset=0):
|
||||||
return []
|
return []
|
||||||
|
|
||||||
def stub_volume_get_all3(context, marker, limit,
|
def stub_volume_get_all3(context, marker, limit,
|
||||||
sort_keys=None, sort_dirs=None,
|
sort_keys=None, sort_dirs=None,
|
||||||
filters=None,
|
filters=None,
|
||||||
viewable_admin_meta=False):
|
viewable_admin_meta=False, offset=0):
|
||||||
self.assertFalse('no_migration_targets' in filters)
|
self.assertFalse('no_migration_targets' in filters)
|
||||||
self.assertFalse('all_tenants' in filters)
|
self.assertFalse('all_tenants' in filters)
|
||||||
return [stubs.stub_volume(1, display_name='vol3')]
|
return [stubs.stub_volume(1, display_name='vol3')]
|
||||||
@ -1279,10 +1260,10 @@ class VolumeApiTest(test.TestCase):
|
|||||||
self.controller._view_builder.detail_list = mock.Mock()
|
self.controller._view_builder.detail_list = mock.Mock()
|
||||||
self.controller._get_volumes(req, True)
|
self.controller._get_volumes(req, True)
|
||||||
get_all.assert_called_once_with(
|
get_all.assert_called_once_with(
|
||||||
context, None, None,
|
context, None, CONF.osapi_max_limit,
|
||||||
sort_keys=['created_at'], sort_dirs=['desc'],
|
sort_keys=['created_at'], sort_dirs=['desc'],
|
||||||
filters={'display_name': 'Volume-573108026'},
|
filters={'display_name': 'Volume-573108026'},
|
||||||
viewable_admin_meta=True)
|
viewable_admin_meta=True, offset=0)
|
||||||
|
|
||||||
@mock.patch('cinder.volume.api.API.get_all')
|
@mock.patch('cinder.volume.api.API.get_all')
|
||||||
def test_get_volumes_filter_with_list(self, get_all):
|
def test_get_volumes_filter_with_list(self, get_all):
|
||||||
@ -1293,9 +1274,10 @@ class VolumeApiTest(test.TestCase):
|
|||||||
self.controller._view_builder.detail_list = mock.Mock()
|
self.controller._view_builder.detail_list = mock.Mock()
|
||||||
self.controller._get_volumes(req, True)
|
self.controller._get_volumes(req, True)
|
||||||
get_all.assert_called_once_with(
|
get_all.assert_called_once_with(
|
||||||
context, None, None,
|
context, None, CONF.osapi_max_limit,
|
||||||
sort_keys=['created_at'], sort_dirs=['desc'],
|
sort_keys=['created_at'], sort_dirs=['desc'],
|
||||||
filters={'id': ['1', '2', '3']}, viewable_admin_meta=True)
|
filters={'id': ['1', '2', '3']}, viewable_admin_meta=True,
|
||||||
|
offset=0)
|
||||||
|
|
||||||
@mock.patch('cinder.volume.api.API.get_all')
|
@mock.patch('cinder.volume.api.API.get_all')
|
||||||
def test_get_volumes_filter_with_expression(self, get_all):
|
def test_get_volumes_filter_with_expression(self, get_all):
|
||||||
@ -1306,9 +1288,9 @@ class VolumeApiTest(test.TestCase):
|
|||||||
self.controller._view_builder.detail_list = mock.Mock()
|
self.controller._view_builder.detail_list = mock.Mock()
|
||||||
self.controller._get_volumes(req, True)
|
self.controller._get_volumes(req, True)
|
||||||
get_all.assert_called_once_with(
|
get_all.assert_called_once_with(
|
||||||
context, None, None,
|
context, None, CONF.osapi_max_limit,
|
||||||
sort_keys=['created_at'], sort_dirs=['desc'],
|
sort_keys=['created_at'], sort_dirs=['desc'],
|
||||||
filters={'display_name': 'd-'}, viewable_admin_meta=True)
|
filters={'display_name': 'd-'}, viewable_admin_meta=True, offset=0)
|
||||||
|
|
||||||
@mock.patch('cinder.volume.api.API.get_all')
|
@mock.patch('cinder.volume.api.API.get_all')
|
||||||
def test_get_volumes_filter_with_status(self, get_all):
|
def test_get_volumes_filter_with_status(self, get_all):
|
||||||
@ -1319,9 +1301,10 @@ class VolumeApiTest(test.TestCase):
|
|||||||
self.controller._view_builder.detail_list = mock.Mock()
|
self.controller._view_builder.detail_list = mock.Mock()
|
||||||
self.controller._get_volumes(req, True)
|
self.controller._get_volumes(req, True)
|
||||||
get_all.assert_called_once_with(
|
get_all.assert_called_once_with(
|
||||||
ctxt, None, None,
|
ctxt, None, CONF.osapi_max_limit,
|
||||||
sort_keys=['created_at'], sort_dirs=['desc'],
|
sort_keys=['created_at'], sort_dirs=['desc'],
|
||||||
filters={'status': 'available'}, viewable_admin_meta=True)
|
filters={'status': 'available'}, viewable_admin_meta=True,
|
||||||
|
offset=0)
|
||||||
|
|
||||||
@mock.patch('cinder.volume.api.API.get_all')
|
@mock.patch('cinder.volume.api.API.get_all')
|
||||||
def test_get_volumes_filter_with_metadata(self, get_all):
|
def test_get_volumes_filter_with_metadata(self, get_all):
|
||||||
@ -1332,10 +1315,10 @@ class VolumeApiTest(test.TestCase):
|
|||||||
self.controller._view_builder.detail_list = mock.Mock()
|
self.controller._view_builder.detail_list = mock.Mock()
|
||||||
self.controller._get_volumes(req, True)
|
self.controller._get_volumes(req, True)
|
||||||
get_all.assert_called_once_with(
|
get_all.assert_called_once_with(
|
||||||
ctxt, None, None,
|
ctxt, None, CONF.osapi_max_limit,
|
||||||
sort_keys=['created_at'], sort_dirs=['desc'],
|
sort_keys=['created_at'], sort_dirs=['desc'],
|
||||||
filters={'metadata': {'fake_key': 'fake_value'}},
|
filters={'metadata': {'fake_key': 'fake_value'}},
|
||||||
viewable_admin_meta=True)
|
viewable_admin_meta=True, offset=0)
|
||||||
|
|
||||||
@mock.patch('cinder.volume.api.API.get_all')
|
@mock.patch('cinder.volume.api.API.get_all')
|
||||||
def test_get_volumes_filter_with_availability_zone(self, get_all):
|
def test_get_volumes_filter_with_availability_zone(self, get_all):
|
||||||
@ -1346,9 +1329,10 @@ class VolumeApiTest(test.TestCase):
|
|||||||
self.controller._view_builder.detail_list = mock.Mock()
|
self.controller._view_builder.detail_list = mock.Mock()
|
||||||
self.controller._get_volumes(req, True)
|
self.controller._get_volumes(req, True)
|
||||||
get_all.assert_called_once_with(
|
get_all.assert_called_once_with(
|
||||||
ctxt, None, None,
|
ctxt, None, CONF.osapi_max_limit,
|
||||||
sort_keys=['created_at'], sort_dirs=['desc'],
|
sort_keys=['created_at'], sort_dirs=['desc'],
|
||||||
filters={'availability_zone': 'nova'}, viewable_admin_meta=True)
|
filters={'availability_zone': 'nova'}, viewable_admin_meta=True,
|
||||||
|
offset=0)
|
||||||
|
|
||||||
@mock.patch('cinder.volume.api.API.get_all')
|
@mock.patch('cinder.volume.api.API.get_all')
|
||||||
def test_get_volumes_filter_with_invalid_filter(self, get_all):
|
def test_get_volumes_filter_with_invalid_filter(self, get_all):
|
||||||
@ -1360,9 +1344,10 @@ class VolumeApiTest(test.TestCase):
|
|||||||
self.controller._view_builder.detail_list = mock.Mock()
|
self.controller._view_builder.detail_list = mock.Mock()
|
||||||
self.controller._get_volumes(req, True)
|
self.controller._get_volumes(req, True)
|
||||||
get_all.assert_called_once_with(
|
get_all.assert_called_once_with(
|
||||||
ctxt, None, None,
|
ctxt, None, CONF.osapi_max_limit,
|
||||||
sort_keys=['created_at'], sort_dirs=['desc'],
|
sort_keys=['created_at'], sort_dirs=['desc'],
|
||||||
filters={'availability_zone': 'nova'}, viewable_admin_meta=True)
|
filters={'availability_zone': 'nova'}, viewable_admin_meta=True,
|
||||||
|
offset=0)
|
||||||
|
|
||||||
@mock.patch('cinder.volume.api.API.get_all')
|
@mock.patch('cinder.volume.api.API.get_all')
|
||||||
def test_get_volumes_sort_by_name(self, get_all):
|
def test_get_volumes_sort_by_name(self, get_all):
|
||||||
@ -1375,9 +1360,9 @@ class VolumeApiTest(test.TestCase):
|
|||||||
self.controller._view_builder.detail_list = mock.Mock()
|
self.controller._view_builder.detail_list = mock.Mock()
|
||||||
self.controller._get_volumes(req, True)
|
self.controller._get_volumes(req, True)
|
||||||
get_all.assert_called_once_with(
|
get_all.assert_called_once_with(
|
||||||
ctxt, None, None,
|
ctxt, None, CONF.osapi_max_limit,
|
||||||
sort_dirs=['desc'], viewable_admin_meta=True,
|
sort_dirs=['desc'], viewable_admin_meta=True,
|
||||||
sort_keys=['display_name'], filters={})
|
sort_keys=['display_name'], filters={}, offset=0)
|
||||||
|
|
||||||
def test_get_volume_filter_options_using_config(self):
|
def test_get_volume_filter_options_using_config(self):
|
||||||
self.override_config('query_volume_filters', ['name', 'status',
|
self.override_config('query_volume_filters', ['name', 'status',
|
||||||
|
@ -437,7 +437,8 @@ class API(base.Base):
|
|||||||
return b
|
return b
|
||||||
|
|
||||||
def get_all(self, context, marker=None, limit=None, sort_keys=None,
|
def get_all(self, context, marker=None, limit=None, sort_keys=None,
|
||||||
sort_dirs=None, filters=None, viewable_admin_meta=False):
|
sort_dirs=None, filters=None, viewable_admin_meta=False,
|
||||||
|
offset=None):
|
||||||
check_policy(context, 'get_all')
|
check_policy(context, 'get_all')
|
||||||
|
|
||||||
if filters is None:
|
if filters is None:
|
||||||
@ -471,7 +472,8 @@ class API(base.Base):
|
|||||||
volumes = self.db.volume_get_all(context, marker, limit,
|
volumes = self.db.volume_get_all(context, marker, limit,
|
||||||
sort_keys=sort_keys,
|
sort_keys=sort_keys,
|
||||||
sort_dirs=sort_dirs,
|
sort_dirs=sort_dirs,
|
||||||
filters=filters)
|
filters=filters,
|
||||||
|
offset=offset)
|
||||||
else:
|
else:
|
||||||
if viewable_admin_meta:
|
if viewable_admin_meta:
|
||||||
context = context.elevated()
|
context = context.elevated()
|
||||||
@ -480,7 +482,8 @@ class API(base.Base):
|
|||||||
marker, limit,
|
marker, limit,
|
||||||
sort_keys=sort_keys,
|
sort_keys=sort_keys,
|
||||||
sort_dirs=sort_dirs,
|
sort_dirs=sort_dirs,
|
||||||
filters=filters)
|
filters=filters,
|
||||||
|
offset=offset)
|
||||||
|
|
||||||
LOG.info(_LI("Get all volumes completed successfully."))
|
LOG.info(_LI("Get all volumes completed successfully."))
|
||||||
return volumes
|
return volumes
|
||||||
|
Loading…
Reference in New Issue
Block a user