diff --git a/glanceclient/v2/images.py b/glanceclient/v2/images.py index 0da8ceb7..764f9c6a 100644 --- a/glanceclient/v2/images.py +++ b/glanceclient/v2/images.py @@ -13,19 +13,35 @@ # License for the specific language governing permissions and limitations # under the License. +DEFAULT_PAGE_SIZE = 20 + class Controller(object): def __init__(self, http_client, model): self.http_client = http_client self.model = model - def list(self): + def list(self, page_size=DEFAULT_PAGE_SIZE): """Retrieve a listing of Image objects + :param page_size: Number of images to request in each paginated request :returns generator over list of Images """ - resp, body = self.http_client.json_request('GET', '/v2/images') - for image in body['images']: + def paginate(url): + resp, body = self.http_client.json_request('GET', url) + for image in body['images']: + yield image + try: + next_url = body['next'] + except KeyError: + return + else: + for image in paginate(next_url): + yield image + + url = '/v2/images?limit=%s' % page_size + + for image in paginate(url): #NOTE(bcwaldon): remove 'self' for now until we have an elegant # way to pass it into the model constructor without conflict image.pop('self', None) diff --git a/glanceclient/v2/shell.py b/glanceclient/v2/shell.py index abfa3bfa..8ad4d993 100644 --- a/glanceclient/v2/shell.py +++ b/glanceclient/v2/shell.py @@ -17,9 +17,14 @@ from glanceclient.common import utils from glanceclient import exc +@utils.arg('--page-size', metavar='', default=None, type=int, + help='Number of images to request in each paginated request.') def do_image_list(gc, args): """List images.""" - images = gc.images.list() + kwargs = {} + if args.page_size is not None: + kwargs['page_size'] = args.page_size + images = gc.images.list(**kwargs) columns = ['ID', 'Name'] utils.print_list(images, columns) diff --git a/tests/utils.py b/tests/utils.py index c3563918..81435bdd 100644 --- a/tests/utils.py +++ b/tests/utils.py @@ -15,15 +15,16 @@ class FakeAPI(object): - def __init__(self, fixtures): + def __init__(self, fixtures, strict_url_check=False): self.fixtures = fixtures self.calls = [] + self.strict_url_check = strict_url_check def _request(self, method, url, headers=None, body=None): call = (method, url, headers or {}, body) self.calls.append(call) - # drop any query params - url = url.split('?', 1)[0] + if not self.strict_url_check: + url = url.split('?', 1)[0] return self.fixtures[url][method] def raw_request(self, *args, **kwargs): diff --git a/tests/v2/test_images.py b/tests/v2/test_images.py index 298d5c10..d6d6d172 100644 --- a/tests/v2/test_images.py +++ b/tests/v2/test_images.py @@ -22,7 +22,7 @@ from tests import utils fixtures = { - '/v2/images': { + '/v2/images?limit=20': { 'GET': ( {}, {'images': [ @@ -37,6 +37,32 @@ fixtures = { ]}, ), }, + '/v2/images?limit=1': { + 'GET': ( + {}, + { + 'images': [ + { + 'id': '3a4560a1-e585-443e-9b39-553b46ec92d1', + 'name': 'image-1', + }, + ], + 'next': ('/v2/images?limit=1&' + 'marker=3a4560a1-e585-443e-9b39-553b46ec92d1'), + }, + ), + }, + ('/v2/images?limit=1&marker=3a4560a1-e585-443e-9b39-553b46ec92d1'): { + 'GET': ( + {}, + {'images': [ + { + 'id': '6f99bf80-2ee6-47cf-acfe-1f1fabb7e810', + 'name': 'image-2', + }, + ]}, + ), + }, '/v2/images/3a4560a1-e585-443e-9b39-553b46ec92d1': { 'GET': ( {}, @@ -58,7 +84,7 @@ FakeModel = warlock.model_factory(fake_schema) class TestController(unittest.TestCase): def setUp(self): super(TestController, self).setUp() - self.api = utils.FakeAPI(fixtures) + self.api = utils.FakeAPI(fixtures, strict_url_check=True) self.controller = images.Controller(self.api, FakeModel) def test_list_images(self): @@ -69,6 +95,14 @@ class TestController(unittest.TestCase): self.assertEqual(images[1].id, '6f99bf80-2ee6-47cf-acfe-1f1fabb7e810') self.assertEqual(images[1].name, 'image-2') + def test_list_images_paginated(self): + #NOTE(bcwaldon): cast to list since the controller returns a generator + images = list(self.controller.list(page_size=1)) + self.assertEqual(images[0].id, '3a4560a1-e585-443e-9b39-553b46ec92d1') + self.assertEqual(images[0].name, 'image-1') + self.assertEqual(images[1].id, '6f99bf80-2ee6-47cf-acfe-1f1fabb7e810') + self.assertEqual(images[1].name, 'image-2') + def test_get_image(self): image = self.controller.get('3a4560a1-e585-443e-9b39-553b46ec92d1') self.assertEqual(image.id, '3a4560a1-e585-443e-9b39-553b46ec92d1')