From 1af4c1ee47ebcf84e63590c27a4d1f4c94c073cb Mon Sep 17 00:00:00 2001 From: William Wolf Date: Tue, 13 Sep 2011 18:16:28 -0400 Subject: Add next links to images requests This adds next links to list images responses in JSON and xml. It keeps all other filters and query parameters in tact to ensure pagination works correctly when using the next links. Change-Id: If61e34589a91f528093c0d05522600d3eee8d89e --- nova/api/openstack/images.py | 29 +++++++++++-------- nova/api/openstack/schemas/v1.1/images_index.rng | 3 ++ nova/api/openstack/views/images.py | 36 ++++++++++++++++++++++++ 3 files changed, 57 insertions(+), 11 deletions(-) (limited to 'nova/api') diff --git a/nova/api/openstack/images.py b/nova/api/openstack/images.py index 7795a3fc4..53150ad42 100644 --- a/nova/api/openstack/images.py +++ b/nova/api/openstack/images.py @@ -13,7 +13,6 @@ # License for the specific language governing permissions and limitations # under the License. -import urlparse import os.path from lxml import etree @@ -149,8 +148,7 @@ class ControllerV10(Controller): filters = self._get_filters(req) images = self._image_service.index(context, filters=filters) images = common.limited(images, req) - builder = self.get_builder(req).build - return dict(images=[builder(image, detail=False) for image in images]) + return self.get_builder(req).build_list(images) def detail(self, req): """Return a detailed index listing of images available to the request. @@ -183,11 +181,14 @@ class ControllerV11(Controller): """ context = req.environ['nova.context'] filters = self._get_filters(req) + params = req.GET.copy() page_params = common.get_pagination_params(req) + for key, val in page_params.iteritems(): + params[key] = val + images = self._image_service.index(context, filters=filters, **page_params) - builder = self.get_builder(req).build - return dict(images=[builder(image, detail=False) for image in images]) + return self.get_builder(req).build_list(images, **params) def detail(self, req): """Return a detailed index listing of images available to the request. @@ -197,11 +198,14 @@ class ControllerV11(Controller): """ context = req.environ['nova.context'] filters = self._get_filters(req) + params = req.GET.copy() page_params = common.get_pagination_params(req) + for key, val in page_params.iteritems(): + params[key] = val images = self._image_service.detail(context, filters=filters, **page_params) - builder = self.get_builder(req).build - return dict(images=[builder(image, detail=True) for image in images]) + + return self.get_builder(req).build_list(images, detail=True, **params) def create(self, *args, **kwargs): raise webob.exc.HTTPMethodNotAllowed() @@ -253,20 +257,23 @@ class ImageXMLSerializer(wsgi.XMLDictSerializer): image_dict.get('metadata', {})) image_elem.append(meta_elem) - for link in image_dict.get('links', []): - elem = etree.SubElement(image_elem, - '{%s}link' % xmlutil.XMLNS_ATOM) + self._populate_links(image_elem, image_dict.get('links', [])) + + def _populate_links(self, parent, links): + for link in links: + elem = etree.SubElement(parent, '{%s}link' % xmlutil.XMLNS_ATOM) elem.set('rel', link['rel']) if 'type' in link: elem.set('type', link['type']) elem.set('href', link['href']) - return image_elem def index(self, images_dict): images = etree.Element('images', nsmap=self.NSMAP) for image_dict in images_dict['images']: image = etree.SubElement(images, 'image') self._populate_image(image, image_dict, False) + + self._populate_links(images, images_dict.get('images_links', [])) return self._to_xml(images) def detail(self, images_dict): diff --git a/nova/api/openstack/schemas/v1.1/images_index.rng b/nova/api/openstack/schemas/v1.1/images_index.rng index 81af19cb5..3db0b2672 100644 --- a/nova/api/openstack/schemas/v1.1/images_index.rng +++ b/nova/api/openstack/schemas/v1.1/images_index.rng @@ -9,4 +9,7 @@ + + + diff --git a/nova/api/openstack/views/images.py b/nova/api/openstack/views/images.py index e366661c3..4e8584bad 100644 --- a/nova/api/openstack/views/images.py +++ b/nova/api/openstack/views/images.py @@ -59,6 +59,15 @@ class ViewBuilder(object): """Return an href string pointing to this object.""" return os.path.join(self.base_url, "images", str(image_id)) + def build_list(self, image_objs, detail=False, **kwargs): + """Return a standardized image list structure for display.""" + images = [] + for image_obj in image_objs: + image = self.build(image_obj, detail=detail) + images.append(image) + + return dict(images=images) + def build(self, image_obj, detail=False): """Return a standardized image structure for display by the API.""" self._format_dates(image_obj) @@ -135,6 +144,33 @@ class ViewBuilderV11(ViewBuilder): return os.path.join(self.base_url, self.project_id, "images", str(image_id)) + def generate_next_link(self, image_id, params): + """ Return an href string with proper limit and marker params""" + params['marker'] = image_id + return "%s?%s" % ( + os.path.join(self.base_url, self.project_id, "images"), + common.dict_to_query_str(params)) + + def build_list(self, image_objs, detail=False, **kwargs): + """Return a standardized image list structure for display.""" + limit = kwargs.get('limit', None) + images = [] + images_links = [] + + for image_obj in image_objs: + image = self.build(image_obj, detail=detail) + images.append(image) + + if (len(images) and limit) and (limit == len(images)): + next_link = self.generate_next_link(images[-1]["id"], kwargs) + images_links = [dict(rel="next", href=next_link)] + + reval = dict(images=images) + if len(images_links) > 0: + reval['images_links'] = images_links + + return reval + def build(self, image_obj, detail=False): """Return a standardized image structure for display by the API.""" image = ViewBuilder.build(self, image_obj, detail) -- cgit