From 389bdf81aa6d18c015de5495640b33404d8e8a47 Mon Sep 17 00:00:00 2001 From: Ed Cranford Date: Tue, 22 May 2012 16:35:47 -0500 Subject: [PATCH] Adds pagination limit and marker to instances, databases, and users indices. --- reddwarfclient/cli.py | 26 ++++++++++++++++------ reddwarfclient/client.py | 2 +- reddwarfclient/common.py | 43 +++++++++++++++++++++++++++++++++++++ reddwarfclient/databases.py | 23 +++++++++++++++----- reddwarfclient/instances.py | 23 +++++++++++++++----- reddwarfclient/users.py | 22 ++++++++++++++----- 6 files changed, 117 insertions(+), 22 deletions(-) diff --git a/reddwarfclient/cli.py b/reddwarfclient/cli.py index c908cb85..03fa845f 100644 --- a/reddwarfclient/cli.py +++ b/reddwarfclient/cli.py @@ -81,12 +81,18 @@ class InstanceCommands(object): except: print sys.exc_info()[1] - def list(self): + def list(self, limit=None, marker=None): """List all instances for account""" dbaas = common.get_client() + if limit: + limit = int(limit, 10) try: - for instance in dbaas.instances.list(): + instances = dbaas.instances.list(limit, marker) + for instance in instances: _pretty_print(instance._info) + if instances.links: + for link in instances.links: + _pretty_print(link) except: print sys.exc_info()[1] @@ -160,12 +166,16 @@ class DatabaseCommands(object): except: print sys.exc_info()[1] - def list(self, id): + def list(self, id, limit=None, marker=None): """List the databases""" dbaas = common.get_client() try: - for database in dbaas.databases.list(id): + databases = dbaas.databases.list(id, limit, marker) + for database in databases: _pretty_print(database._info) + if databases.links: + for link in databases.links: + _pretty_print(link) except: print sys.exc_info()[1] @@ -196,12 +206,16 @@ class UserCommands(object): except: print sys.exc_info()[1] - def list(self, id): + def list(self, id, limit=None, marker=None): """List all the users for an instance""" dbaas = common.get_client() try: - for user in dbaas.users.list(id): + users = dbaas.users.list(id, limit, marker) + for user in users: _pretty_print(user._info) + if users.next: + for link in users.next: + _pretty_print(link) except: print sys.exc_info()[1] diff --git a/reddwarfclient/client.py b/reddwarfclient/client.py index 0d3a5100..252be0d4 100644 --- a/reddwarfclient/client.py +++ b/reddwarfclient/client.py @@ -37,7 +37,7 @@ class ReddwarfHTTPClient(HTTPClient): def __init__(self, user, apikey, tenant, auth_url, service_name, service_url=None, - auth_strategy=None, **kwargs): + auth_strategy=None, **kwargs): super(ReddwarfHTTPClient, self).__init__(user, apikey, tenant, auth_url, **kwargs) diff --git a/reddwarfclient/common.py b/reddwarfclient/common.py index 48bfdb74..2d8a7fb3 100644 --- a/reddwarfclient/common.py +++ b/reddwarfclient/common.py @@ -74,6 +74,18 @@ def print_commands(commands): sys.exit(2) +def limit_url(url, limit=None, marker=None): + if not limit and not marker: + return url + query = [] + if marker: + query.append("marker=%s" % marker) + if limit: + query.append("limit=%s" % limit) + query = '?' + '&'.join(query) + return url + query + + class APIToken(object): """A token object containing the user, apikey and token which is pickleable.""" @@ -111,3 +123,34 @@ class Auth(object): print apitoken._token except: print sys.exc_info()[1] + + +class Paginated(object): + """ Pretends to be a list if you iterate over it, but also keeps a + next property you can use to get the next page of data. """ + + def __init__(self, items=[], next_marker=None, links=[]): + self.items = items + self.next = next_marker + self.links = links + + def __len__(self): + return len(self.items) + + def __iter__(self): + return self.items.__iter__() + + def __getitem__(self, key): + return self.items[key] + + def __setitem__(self, key, value): + self.items[key] = value + + def __delitem(self, key): + del self.items[key] + + def __reversed__(self): + return reversed(self.items) + + def __contains__(self, needle): + return needle in self.items diff --git a/reddwarfclient/databases.py b/reddwarfclient/databases.py index 2c721f35..48934e12 100644 --- a/reddwarfclient/databases.py +++ b/reddwarfclient/databases.py @@ -1,6 +1,9 @@ from novaclient import base from reddwarfclient.common import check_for_exceptions +from reddwarfclient.common import limit_url +from reddwarfclient.common import Paginated import exceptions +import urlparse class Database(base.Resource): @@ -34,22 +37,32 @@ class Databases(base.ManagerWithFind): resp, body = self.api.client.delete(url) check_for_exceptions(resp, body) - def _list(self, url, response_key): - resp, body = self.api.client.get(url) + def _list(self, url, response_key, limit=None, marker=None): + resp, body = self.api.client.get(limit_url(url, limit, marker)) check_for_exceptions(resp, body) if not body: raise Exception("Call to " + url + " did not return a body.") - return [self.resource_class(self, res) for res in body[response_key]] + links = body.get('links', []) + next_links = [link['href'] for link in links if link['rel'] == 'next'] + next_marker = None + for link in next_links: + # Extract the marker from the url. + parsed_url = urlparse.urlparse(link) + query_dict = dict(urlparse.parse_qsl(parsed_url.query)) + next_marker = query_dict.get('marker', None) + databases = body[response_key] + databases = [self.resource_class(self, res) for res in databases] + return Paginated(databases, next_marker=next_marker, links=links) - def list(self, instance): + def list(self, instance, limit=None, marker=None): """ Get a list of all Databases from the instance. :rtype: list of :class:`Database`. """ return self._list("/instances/%s/databases" % base.getid(instance), - "databases") + "databases", limit, marker) # def get(self, instance, database): # """ diff --git a/reddwarfclient/instances.py b/reddwarfclient/instances.py index e918d48c..866e072e 100644 --- a/reddwarfclient/instances.py +++ b/reddwarfclient/instances.py @@ -16,8 +16,11 @@ from novaclient import base import exceptions +import urlparse from reddwarfclient.common import check_for_exceptions +from reddwarfclient.common import limit_url +from reddwarfclient.common import Paginated REBOOT_SOFT, REBOOT_HARD = 'SOFT', 'HARD' @@ -68,19 +71,29 @@ class Instances(base.ManagerWithFind): return self._create("/instances", body, "instance") - def _list(self, url, response_key): - resp, body = self.api.client.get(url) + def _list(self, url, response_key, limit=None, marker=None): + resp, body = self.api.client.get(limit_url(url, limit, marker)) if not body: raise Exception("Call to " + url + " did not return a body.") - return [self.resource_class(self, res) for res in body[response_key]] + links = body.get('links', []) + next_links = [link['href'] for link in links if link['rel'] == 'next'] + next_marker = None + for link in next_links: + # Extract the marker from the url. + parsed_url = urlparse.urlparse(link) + query_dict = dict(urlparse.parse_qsl(parsed_url.query)) + next_marker = query_dict.get('marker', None) + instances = body[response_key] + instances = [self.resource_class(self, res) for res in instances] + return Paginated(instances, next_marker=next_marker, links=links) - def list(self): + def list(self, limit=None, marker=None): """ Get a list of all instances. :rtype: list of :class:`Instance`. """ - return self._list("/instances/detail", "instances") + return self._list("/instances/detail", "instances", limit, marker) def index(self): """ diff --git a/reddwarfclient/users.py b/reddwarfclient/users.py index f3a49a67..5f21ada2 100644 --- a/reddwarfclient/users.py +++ b/reddwarfclient/users.py @@ -15,7 +15,10 @@ from novaclient import base from reddwarfclient.common import check_for_exceptions +from reddwarfclient.common import limit_url +from reddwarfclient.common import Paginated import exceptions +import urlparse class User(base.Resource): @@ -47,19 +50,28 @@ class Users(base.ManagerWithFind): resp, body = self.api.client.delete(url) check_for_exceptions(resp, body) - def _list(self, url, response_key): - resp, body = self.api.client.get(url) + def _list(self, url, response_key, limit=None, marker=None): + resp, body = self.api.client.get(limit_url(url, limit, marker)) check_for_exceptions(resp, body) if not body: raise Exception("Call to " + url + " did not return a body.") - return [self.resource_class(self, res) for res in body[response_key]] + links = body.get('links', []) + next_links = [link['href'] for link in links if link['rel'] == 'next'] + next_marker = None + for link in next_links: + # Extract the marker from the url. + parsed_url = urlparse.urlparse(link) + query_dict = dict(urlparse.parse_qsl(parsed_url.query)) + next_marker = query_dict.get('marker', None) + users = [self.resource_class(self, res) for res in body[response_key]] + return Paginated(users, next_marker=next_marker, links=links) - def list(self, instance): + def list(self, instance, limit=None, marker=None): """ Get a list of all Users from the instance's Database. :rtype: list of :class:`User`. """ return self._list("/instances/%s/users" % base.getid(instance), - "users") + "users", limit, marker)