diff --git a/nova/api/openstack/common.py b/nova/api/openstack/common.py index 30c18b5c7551..471643448c7b 100644 --- a/nova/api/openstack/common.py +++ b/nova/api/openstack/common.py @@ -150,23 +150,25 @@ def get_pagination_params(request): """ params = {} if 'limit' in request.GET: - params['limit'] = _get_limit_param(request) + params['limit'] = _get_int_param(request, 'limit') + if 'page_size' in request.GET: + params['page_size'] = _get_int_param(request, 'page_size') if 'marker' in request.GET: params['marker'] = _get_marker_param(request) return params -def _get_limit_param(request): - """Extract integer limit from request or fail.""" +def _get_int_param(request, param): + """Extract integer param from request or fail.""" try: - limit = int(request.GET['limit']) + int_param = int(request.GET[param]) except ValueError: - msg = _('limit param must be an integer') + msg = _('%s param must be an integer') % param raise webob.exc.HTTPBadRequest(explanation=msg) - if limit < 0: - msg = _('limit param must be positive') + if int_param < 0: + msg = _('%s param must be positive') % param raise webob.exc.HTTPBadRequest(explanation=msg) - return limit + return int_param def _get_marker_param(request): diff --git a/nova/image/glance.py b/nova/image/glance.py index 59e9e87dd4aa..e1df52c1239b 100644 --- a/nova/image/glance.py +++ b/nova/image/glance.py @@ -270,7 +270,7 @@ class GlanceImageService(object): def _extract_query_params(self, params): _params = {} accepted_params = ('filters', 'marker', 'limit', - 'sort_key', 'sort_dir') + 'page_size', 'sort_key', 'sort_dir') for param in accepted_params: if params.get(param): _params[param] = params.get(param) diff --git a/nova/tests/api/openstack/compute/test_images.py b/nova/tests/api/openstack/compute/test_images.py index 4e3c1f0d7da4..da3f52e86c97 100644 --- a/nova/tests/api/openstack/compute/test_images.py +++ b/nova/tests/api/openstack/compute/test_images.py @@ -275,6 +275,25 @@ class ImagesControllerTest(test.TestCase): self.assertThat({'limit': ['2'], 'marker': ['124']}, matchers.DictMatches(params)) + def test_get_image_details_with_limit_and_page_size(self): + request = fakes.HTTPRequest.blank( + '/v2/fake/images/detail?limit=2&page_size=1') + response = self.controller.detail(request) + response_list = response["images"] + response_links = response["images_links"] + + expected = [self.expected_image_123["image"], + self.expected_image_124["image"]] + + self.assertThat(expected, matchers.DictListMatches(response_list)) + + href_parts = urlparse.urlparse(response_links[0]['href']) + self.assertEqual('/v2/fake/images', href_parts.path) + params = urlparse.parse_qs(href_parts.query) + + self.assertThat({'limit': ['2'], 'page_size': ['1'], + 'marker': ['124']}, matchers.DictMatches(params)) + def _detail_request(self, filters, request): context = request.environ['nova.context'] self.image_service.detail(context, filters=filters).AndReturn([]) diff --git a/nova/tests/api/openstack/test_common.py b/nova/tests/api/openstack/test_common.py index 4e080fe81dbd..4d9ae03afe7b 100644 --- a/nova/tests/api/openstack/test_common.py +++ b/nova/tests/api/openstack/test_common.py @@ -196,6 +196,24 @@ class PaginationParamsTest(test.TestCase): self.assertEqual(common.get_pagination_params(req), {'marker': marker, 'limit': 20}) + def test_valid_page_size(self): + # Test valid page_size param. + req = webob.Request.blank('/?page_size=10') + self.assertEqual(common.get_pagination_params(req), + {'page_size': 10}) + + def test_invalid_page_size(self): + # Test invalid page_size param. + req = webob.Request.blank('/?page_size=-2') + self.assertRaises( + webob.exc.HTTPBadRequest, common.get_pagination_params, req) + + def test_valid_limit_and_page_size(self): + # Test valid limit and page_size parameters. + req = webob.Request.blank('/?limit=20&page_size=5') + self.assertEqual(common.get_pagination_params(req), + {'page_size': 5, 'limit': 20}) + class MiscFunctionsTest(test.TestCase): diff --git a/nova/tests/glance/stubs.py b/nova/tests/glance/stubs.py index 189d626089ee..d8a288d04d33 100644 --- a/nova/tests/glance/stubs.py +++ b/nova/tests/glance/stubs.py @@ -40,7 +40,7 @@ class StubGlanceClient(object): setattr(self.images, fn, getattr(self, fn)) #TODO(bcwaldon): implement filters - def list(self, filters=None, marker=None, limit=30): + def list(self, filters=None, marker=None, limit=30, page_size=20): if marker is None: index = 0 else: @@ -50,7 +50,6 @@ class StubGlanceClient(object): break else: raise glanceclient.exc.BadRequest('Marker not found') - return self._images[index:index + limit] def get(self, image_id): diff --git a/nova/tests/image/test_glance.py b/nova/tests/image/test_glance.py index 94d9751ce57b..c3916b590e5d 100644 --- a/nova/tests/image/test_glance.py +++ b/nova/tests/image/test_glance.py @@ -26,6 +26,7 @@ import time import sys import testtools +from mock import patch import mox import glanceclient.exc @@ -34,6 +35,7 @@ from oslo.config import cfg from nova import context from nova import exception from nova.image import glance +from nova.image.glance import GlanceClientWrapper from nova import test from nova.tests.api.openstack import fakes from nova.tests.glance import stubs as glance_stubs @@ -115,8 +117,8 @@ class TestGlanceImageService(test.TestCase): super(TestGlanceImageService, self).setUp() fakes.stub_out_compute_api_snapshot(self.stubs) - client = glance_stubs.StubGlanceClient() - self.service = self._create_image_service(client) + self.client = glance_stubs.StubGlanceClient() + self.service = self._create_image_service(self.client) self.context = context.RequestContext('fake', 'fake', auth_token=True) self.mox = mox.Mox() self.files_to_clean = [] @@ -304,6 +306,14 @@ class TestGlanceImageService(test.TestCase): image_metas = self.service.detail(self.context, limit=5) self.assertEquals(len(image_metas), 5) + def test_page_size(self): + with patch.object(GlanceClientWrapper, 'call') as a_mock: + self.service.detail(self.context, page_size=5) + self.assertEquals(a_mock.called, True) + a_mock.assert_called_with(self.context, 1, 'list', + filters={'is_public': 'none'}, + page_size=5) + def test_detail_default_limit(self): fixtures = [] ids = []