From fba4941fd29337f806e2e8c70fe36619f629d149 Mon Sep 17 00:00:00 2001 From: Robert Myers Date: Thu, 5 Dec 2013 17:06:03 -0600 Subject: [PATCH] Adding pagination support for backups * add a _pagination method to base Manager * switched existing paginated list to use the new method * removed description from backup list and added updated Implements: blueprint paginate-backup-list Change-Id: If33c55a35bae8ebd6ed654af5ce6dfd7f9e40096 --- troveclient/base.py | 20 +++++- troveclient/tests/test_backups.py | 101 +++++++++++++++++++++++++++ troveclient/tests/test_base.py | 61 ++++++++++++++++ troveclient/tests/test_common.py | 4 +- troveclient/tests/test_datastores.py | 26 ++++--- troveclient/tests/test_instances.py | 25 ++----- troveclient/tests/test_management.py | 34 +++------ troveclient/tests/test_users.py | 36 +++------- troveclient/v1/backups.py | 2 +- troveclient/v1/databases.py | 25 +------ troveclient/v1/datastores.py | 6 +- troveclient/v1/instances.py | 35 ++-------- troveclient/v1/management.py | 21 +----- troveclient/v1/security_groups.py | 27 +------ troveclient/v1/shell.py | 27 +++++-- troveclient/v1/users.py | 24 +------ 16 files changed, 258 insertions(+), 216 deletions(-) create mode 100644 troveclient/tests/test_backups.py diff --git a/troveclient/base.py b/troveclient/base.py index 7f4adef7..4f38c313 100644 --- a/troveclient/base.py +++ b/troveclient/base.py @@ -27,9 +27,10 @@ import os import six -from troveclient.openstack.common.apiclient import exceptions from troveclient import utils - +from troveclient import common +from troveclient.openstack.common.apiclient import exceptions +from troveclient.openstack.common.py3kcompat import urlutils # Python 2.4 compat try: @@ -60,6 +61,21 @@ class Manager(utils.HookableMixin): def __init__(self, api): self.api = api + def _paginated(self, url, response_key, limit=None, marker=None): + resp, body = self.api.client.get(common.limit_url(url, limit, marker)) + if not body: + raise Exception("Call to " + url + " did not return a body.") + 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 = urlutils.urlparse(link) + query_dict = dict(urlutils.parse_qsl(parsed_url.query)) + next_marker = query_dict.get('marker') + data = [self.resource_class(self, res) for res in body[response_key]] + return common.Paginated(data, next_marker=next_marker, links=links) + def _list(self, url, response_key, obj_class=None, body=None): resp = None if body: diff --git a/troveclient/tests/test_backups.py b/troveclient/tests/test_backups.py new file mode 100644 index 00000000..d72ba894 --- /dev/null +++ b/troveclient/tests/test_backups.py @@ -0,0 +1,101 @@ +# Copyright 2013 OpenStack Foundation +# Copyright 2013 Rackspace Hosting +# +# Licensed under the Apache License, Version 2.0 (the "License"); you may +# not use this file except in compliance with the License. You may obtain +# a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT +# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the +# License for the specific language governing permissions and limitations +# under the License. + +import testtools +import mock +import uuid + +from troveclient.v1 import backups + +""" +Unit tests for backups.py +""" + + +class BackupTest(testtools.TestCase): + + def setUp(self): + super(BackupTest, self).setUp() + self.backup_id = str(uuid.uuid4()) + self.info = {'name': 'my backup', 'id': self.backup_id} + self.api = mock.Mock() + self.manager = backups.Backups(self.api) + self.backup = backups.Backup(self.manager, self.info) + + def tearDown(self): + super(BackupTest, self).tearDown() + + def test___repr__(self): + self.assertEqual(repr(self.backup), '') + + +class BackupManagerTest(testtools.TestCase): + + def setUp(self): + super(BackupManagerTest, self).setUp() + self.backups = backups.Backups(mock.Mock()) + + def tearDown(self): + super(BackupManagerTest, self).tearDown() + + def test_create(self): + create_mock = mock.Mock() + self.backups._create = create_mock + args = {'name': 'test_backup', 'instance': '1'} + body = {'backup': args} + self.backups.create(**args) + create_mock.assert_called_with('/backups', body, 'backup') + + def test_create_description(self): + create_mock = mock.Mock() + self.backups._create = create_mock + args = {'name': 'test_backup', 'instance': '1', 'description': 'foo'} + body = {'backup': args} + self.backups.create(**args) + create_mock.assert_called_with('/backups', body, 'backup') + + def test_list(self): + page_mock = mock.Mock() + self.backups._paginated = page_mock + limit = "test-limit" + marker = "test-marker" + self.backups.list(limit, marker) + page_mock.assert_called_with("/backups", "backups", limit, marker) + + def test_get(self): + get_mock = mock.Mock() + self.backups._get = get_mock + self.backups.get(1) + get_mock.assert_called_with('/backups/1', 'backup') + + def test_delete(self): + resp = mock.Mock() + resp.status_code = 200 + delete_mock = mock.Mock(return_value=(resp, None)) + self.backups.api.client.delete = delete_mock + self.backups.delete('backup1') + delete_mock.assert_called_with('/backups/backup1') + + def test_delete_500(self): + resp = mock.Mock() + resp.status_code = 500 + self.backups.api.client.delete = mock.Mock(return_value=(resp, None)) + self.assertRaises(Exception, self.backups.delete, 'backup1') + + def test_delete_422(self): + resp = mock.Mock() + resp.status_code = 422 + self.backups.api.client.delete = mock.Mock(return_value=(resp, None)) + self.assertRaises(Exception, self.backups.delete, 'backup1') diff --git a/troveclient/tests/test_base.py b/troveclient/tests/test_base.py index 898ce7f3..55efe95c 100644 --- a/troveclient/tests/test_base.py +++ b/troveclient/tests/test_base.py @@ -25,6 +25,7 @@ import mock from troveclient import base from troveclient.openstack.common.apiclient import exceptions +from troveclient import common from troveclient import utils """ @@ -254,6 +255,66 @@ class ManagerListTest(ManagerTest): self.assertEqual(len(data_), len(l)) +class MangerPaginationTests(ManagerTest): + + def setUp(self): + super(MangerPaginationTests, self).setUp() + self.manager = base.Manager() + self.manager.api = mock.Mock() + self.manager.api.client = mock.Mock() + self.manager.resource_class = base.Resource + + self.response_key = "response_key" + self.data = [{"foo": "p1"}, {"foo": "p2"}] + self.next_data = [{"foo": "p3"}, {"foo": "p4"}] + self.marker = 'test-marker' + self.limit = '20' + self.url = "http://test_url" + self.next_url = '%s?marker=%s&limit=%s' % (self.url, self.marker, + self.limit) + self.links = [{'href': self.next_url, 'rel': 'next'}] + self.body = { + self.response_key: self.data, + 'links': self.links + } + self.next_body = {self.response_key: self.next_data} + + def side_effect(url): + if url == self.url: + return (None, self.body) + if url == self.next_url: + return (None, self.next_body) + + self.manager.api.client.get = mock.Mock(side_effect=side_effect) + + def tearDown(self): + super(MangerPaginationTests, self).tearDown() + + def test_pagination(self): + resp = self.manager._paginated(self.url, self.response_key) + self.manager.api.client.get.assert_called_with(self.url) + self.assertEqual(resp.items[0].foo, 'p1') + self.assertEqual(resp.items[1].foo, 'p2') + self.assertEqual(resp.next, self.marker) + self.assertEqual(resp.links, self.links) + self.assertTrue(isinstance(resp, common.Paginated)) + + def test_pagination_next(self): + resp = self.manager._paginated(self.url, self.response_key, + limit=self.limit, marker=self.marker) + self.manager.api.client.get.assert_called_with(self.next_url) + self.assertEqual(resp.items[0].foo, 'p3') + self.assertEqual(resp.items[1].foo, 'p4') + self.assertEqual(resp.next, None) + self.assertEqual(resp.links, []) + self.assertTrue(isinstance(resp, common.Paginated)) + + def test_pagination_error(self): + self.manager.api.client.get = mock.Mock(return_value=(None, None)) + self.assertRaises(Exception, self.manager._paginated, + self.url, self.response_key) + + class FakeResource(object): def __init__(self, _id, properties): self.id = _id diff --git a/troveclient/tests/test_common.py b/troveclient/tests/test_common.py index 888f0d4c..13089235 100644 --- a/troveclient/tests/test_common.py +++ b/troveclient/tests/test_common.py @@ -35,9 +35,7 @@ class CommonTest(testtools.TestCase): def test_limit_url(self): url = "test-url" - limit = None - marker = None - self.assertEqual(url, common.limit_url(url)) + self.assertEqual(url, common.limit_url(url, limit=None, marker=None)) limit = "test-limit" marker = "test-marker" diff --git a/troveclient/tests/test_datastores.py b/troveclient/tests/test_datastores.py index df6d4541..f001aaa9 100644 --- a/troveclient/tests/test_datastores.py +++ b/troveclient/tests/test_datastores.py @@ -68,14 +68,15 @@ class DatastoresTest(testtools.TestCase): base.getid = self.orig_base_getid def test_list(self): - def side_effect_func(path, inst, limit, marker): - return path, inst, limit, marker - - self.datastores._list = mock.Mock(side_effect=side_effect_func) + page_mock = mock.Mock() + self.datastores._paginated = page_mock limit = "test-limit" marker = "test-marker" - expected = ("/datastores", "datastores", limit, marker) - self.assertEqual(expected, self.datastores.list(limit, marker)) + self.datastores.list(limit, marker) + page_mock.assert_called_with("/datastores", "datastores", + limit, marker) + self.datastores.list() + page_mock.assert_called_with("/datastores", "datastores", None, None) def test_get(self): def side_effect_func(path, inst): @@ -108,16 +109,13 @@ class DatastoreVersionsTest(testtools.TestCase): base.getid = self.orig_base_getid def test_list(self): - def side_effect_func(path, inst, limit, marker): - return path, inst, limit, marker - - self.datastore_versions._list = mock.Mock(side_effect=side_effect_func) + page_mock = mock.Mock() + self.datastore_versions._paginated = page_mock limit = "test-limit" marker = "test-marker" - expected = ("/datastores/datastore1/versions", - "versions", limit, marker) - self.assertEqual(expected, self.datastore_versions.list( - "datastore1", limit, marker)) + self.datastore_versions.list("datastore1", limit, marker) + page_mock.assert_called_with("/datastores/datastore1/versions", + "versions", limit, marker) def test_get(self): def side_effect_func(path, inst): diff --git a/troveclient/tests/test_instances.py b/troveclient/tests/test_instances.py index b0cdb0a6..36ecedac 100644 --- a/troveclient/tests/test_instances.py +++ b/troveclient/tests/test_instances.py @@ -104,30 +104,13 @@ class InstancesTest(testtools.TestCase): b["instance"]["datastore"]["version"]) self.assertEqual(103, b["instance"]["flavorRef"]) - def test__list(self): - self.instances.api.client.get = mock.Mock(return_value=('resp', None)) - self.assertRaises(Exception, self.instances._list, "url", None) - - body = mock.Mock() - body.get = mock.Mock( - return_value=[{'href': 'http://test.net/test_file', - 'rel': 'next'}] - ) - body.__getitem__ = mock.Mock(return_value='instance1') - #self.instances.resource_class = mock.Mock(return_value="instance-1") - self.instances.api.client.get = mock.Mock(return_value=('resp', body)) - _expected = [{'href': 'http://test.net/test_file', 'rel': 'next'}] - self.assertEqual(_expected, self.instances._list("url", None).links) - def test_list(self): - def side_effect_func(path, inst, limit, marker): - return path, inst, limit, marker - - self.instances._list = mock.Mock(side_effect=side_effect_func) + page_mock = mock.Mock() + self.instances._paginated = page_mock limit = "test-limit" marker = "test-marker" - expected = ("/instances", "instances", limit, marker) - self.assertEqual(expected, self.instances.list(limit, marker)) + self.instances.list(limit, marker) + page_mock.assert_called_with("/instances", "instances", limit, marker) def test_get(self): def side_effect_func(path, inst): diff --git a/troveclient/tests/test_management.py b/troveclient/tests/test_management.py index 7576c1a8..81e2431d 100644 --- a/troveclient/tests/test_management.py +++ b/troveclient/tests/test_management.py @@ -68,21 +68,6 @@ class ManagementTest(testtools.TestCase): management.RootHistory.__init__ = self.orig_hist__init base.getid = self.orig_base_getid - def test__list(self): - self.management.api.client.get = mock.Mock(return_value=('resp', None)) - self.assertRaises(Exception, self.management._list, "url", None) - - body = mock.Mock() - body.get = mock.Mock( - return_value=[{'href': 'http://test.net/test_file', - 'rel': 'next'}] - ) - body.__getitem__ = mock.Mock(return_value='instance1') - self.management.resource_class = mock.Mock(return_value="instance-1") - self.management.api.client.get = mock.Mock(return_value=('resp', body)) - _expected = [{'href': 'http://test.net/test_file', 'rel': 'next'}] - self.assertEqual(_expected, self.management._list("url", None).links) - def test_show(self): def side_effect_func(path, instance): return path, instance @@ -91,14 +76,17 @@ class ManagementTest(testtools.TestCase): self.assertEqual(('/mgmt/instances/instance1', 'instance'), (p, i)) def test_index(self): - def side_effect_func(url, name, limit, marker): - return url - - self.management._list = mock.Mock(side_effect=side_effect_func) - self.assertEqual('/mgmt/instances?deleted=true', - self.management.index(deleted=True)) - self.assertEqual('/mgmt/instances?deleted=false', - self.management.index(deleted=False)) + page_mock = mock.Mock() + self.management._paginated = page_mock + self.management.index(deleted=True) + page_mock.assert_called_with('/mgmt/instances?deleted=true', + 'instances', None, None) + self.management.index(deleted=False) + page_mock.assert_called_with('/mgmt/instances?deleted=false', + 'instances', None, None) + self.management.index(deleted=True, limit=10, marker="foo") + page_mock.assert_called_with('/mgmt/instances?deleted=true', + 'instances', 10, "foo") def test_root_enabled_history(self): self.management.api.client.get = mock.Mock(return_value=('resp', None)) diff --git a/troveclient/tests/test_users.py b/troveclient/tests/test_users.py index d8846c04..4fda0b39 100644 --- a/troveclient/tests/test_users.py +++ b/troveclient/tests/test_users.py @@ -117,30 +117,14 @@ class UsersTest(testtools.TestCase): self._resp.status_code = 400 self.assertRaises(Exception, self.users.delete, 34, 'user1') - def test__list(self): - def side_effect_func(self, val): - return val - - key = 'key' - body = mock.Mock() - body.get = mock.Mock( - return_value=[{'href': 'http://test.net/test_file', - 'rel': 'next'}] - ) - body.__getitem__ = mock.Mock(return_value=["test-value"]) - - resp = mock.Mock() - resp.status_code = 200 - self.users.resource_class = mock.Mock(side_effect=side_effect_func) - self.users.api.client.get = mock.Mock(return_value=(resp, body)) - self.assertEqual(["test-value"], self.users._list('url', key).items) - - self.users.api.client.get = mock.Mock(return_value=(resp, None)) - self.assertRaises(Exception, self.users._list, 'url', None) - def test_list(self): - def side_effect_func(path, user, limit, marker): - return path - - self.users._list = mock.Mock(side_effect=side_effect_func) - self.assertEqual('/instances/instance1/users', self.users.list(1)) + page_mock = mock.Mock() + self.users._paginated = page_mock + self.users.list(1) + page_mock.assert_called_with('/instances/instance1/users', + 'users', None, None) + limit = 'test-limit' + marker = 'test-marker' + self.users.list(1, limit, marker) + page_mock.assert_called_with('/instances/instance1/users', + 'users', limit, marker) diff --git a/troveclient/v1/backups.py b/troveclient/v1/backups.py index ed7aabfc..04f13c34 100644 --- a/troveclient/v1/backups.py +++ b/troveclient/v1/backups.py @@ -51,7 +51,7 @@ class Backups(base.ManagerWithFind): :rtype: list of :class:`Backups`. """ - return self._list("/backups", "backups", limit, marker) + return self._paginated("/backups", "backups", limit, marker) def create(self, name, instance, description=None): """ diff --git a/troveclient/v1/databases.py b/troveclient/v1/databases.py index af1688ca..8089230d 100644 --- a/troveclient/v1/databases.py +++ b/troveclient/v1/databases.py @@ -18,7 +18,6 @@ from troveclient import base from troveclient import common -from troveclient.openstack.common.py3kcompat import urlutils class Database(base.Resource): @@ -52,34 +51,14 @@ class Databases(base.ManagerWithFind): resp, body = self.api.client.delete(url) common.check_for_exceptions(resp, body) - def _list(self, url, response_key, limit=None, marker=None): - resp, body = self.api.client.get(common.limit_url(url, limit, marker)) - common.check_for_exceptions(resp, body) - if not body: - raise Exception("Call to " + url + - " did not return a body.") - 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 = urlutils.urlparse(link) - query_dict = dict(urlutils.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 common.Paginated( - databases, next_marker=next_marker, links=links - ) - 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", limit, marker) + url = "/instances/%s/databases" % base.getid(instance) + return self._paginated(url, "databases", limit, marker) # def get(self, instance, database): # """ diff --git a/troveclient/v1/datastores.py b/troveclient/v1/datastores.py index fdc6b354..9a2aba81 100644 --- a/troveclient/v1/datastores.py +++ b/troveclient/v1/datastores.py @@ -46,7 +46,7 @@ class Datastores(base.ManagerWithFind): :rtype: list of :class:`Datastore`. """ - return self._list("/datastores", "datastores", limit, marker) + return self._paginated("/datastores", "datastores", limit, marker) def get(self, datastore): """ @@ -73,8 +73,8 @@ class DatastoreVersions(base.ManagerWithFind): :rtype: list of :class:`DatastoreVersion`. """ - return self._list("/datastores/%s/versions" % datastore, - "versions", limit, marker) + return self._paginated("/datastores/%s/versions" % datastore, + "versions", limit, marker) def get(self, datastore, datastore_version): """ diff --git a/troveclient/v1/instances.py b/troveclient/v1/instances.py index 033f03b8..fad73314 100644 --- a/troveclient/v1/instances.py +++ b/troveclient/v1/instances.py @@ -19,11 +19,9 @@ from troveclient import base from troveclient import common -from troveclient.openstack.common.apiclient import exceptions -from troveclient.openstack.common.py3kcompat import urlutils - -REBOOT_SOFT, REBOOT_HARD = 'SOFT', 'HARD' +REBOOT_SOFT = 'SOFT' +REBOOT_HARD = 'HARD' class Instance(base.Resource): @@ -85,31 +83,13 @@ class Instances(base.ManagerWithFind): return self._create("/instances", body, "instance") - def _list(self, url, response_key, limit=None, marker=None): - resp, body = self.api.client.get(common.limit_url(url, limit, marker)) - if not body: - raise Exception("Call to " + url + " did not return a body.") - 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 = urlutils.urlparse(link) - query_dict = dict(urlutils.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 common.Paginated( - instances, next_marker=next_marker, links=links - ) - def list(self, limit=None, marker=None): """ Get a list of all instances. :rtype: list of :class:`Instance`. """ - return self._list("/instances", "instances", limit, marker) + return self._paginated("/instances", "instances", limit, marker) def get(self, instance): """ @@ -120,14 +100,14 @@ class Instances(base.ManagerWithFind): return self._get("/instances/%s" % base.getid(instance), "instance") - def backups(self, instance): + def backups(self, instance, limit=None, marker=None): """ Get the list of backups for a specific instance. :rtype: list of :class:`Backups`. """ - return self._list("/instances/%s/backups" % base.getid(instance), - "backups") + url = "/instances/%s/backups" % base.getid(instance) + return self._paginated(url, "backups", limit, marker) def delete(self, instance): """ @@ -137,8 +117,7 @@ class Instances(base.ManagerWithFind): """ resp, body = self.api.client.delete("/instances/%s" % base.getid(instance)) - if resp.status_code in (422, 500): - raise exceptions.from_response(resp, body) + common.check_for_exceptions(resp, body) def _action(self, instance_id, body): """ diff --git a/troveclient/v1/management.py b/troveclient/v1/management.py index 2831abb8..38c7c964 100644 --- a/troveclient/v1/management.py +++ b/troveclient/v1/management.py @@ -18,7 +18,6 @@ from troveclient import base from troveclient import common -from troveclient.openstack.common.py3kcompat import urlutils from troveclient.v1 import instances from troveclient.v1 import flavors @@ -39,24 +38,6 @@ class Management(base.ManagerWithFind): def list(self): pass - def _list(self, url, response_key, limit=None, marker=None): - resp, body = self.api.client.get(common.limit_url(url, limit, marker)) - if not body: - raise Exception("Call to " + url + " did not return a body.") - 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 = urlutils.urlparse(link) - query_dict = dict(urlutils.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 common.Paginated( - instances, next_marker=next_marker, links=links - ) - def show(self, instance): """ Get details of one instance. @@ -82,7 +63,7 @@ class Management(base.ManagerWithFind): form = "?deleted=false" url = "/mgmt/instances%s" % form - return self._list(url, "instances", limit, marker) + return self._paginated(url, "instances", limit, marker) def root_enabled_history(self, instance): """ diff --git a/troveclient/v1/security_groups.py b/troveclient/v1/security_groups.py index 6a9e1c63..0caf2771 100644 --- a/troveclient/v1/security_groups.py +++ b/troveclient/v1/security_groups.py @@ -19,8 +19,6 @@ from troveclient import base from troveclient import common -from troveclient.openstack.common.apiclient import exceptions -from troveclient.openstack.common.py3kcompat import urlutils class SecurityGroup(base.Resource): @@ -37,32 +35,14 @@ class SecurityGroups(base.ManagerWithFind): """ resource_class = SecurityGroup - def _list(self, url, response_key, limit=None, marker=None): - resp, body = self.api.client.get(common.limit_url(url, limit, marker)) - if not body: - raise Exception("Call to " + url + " did not return a body.") - 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 = urlutils.urlparse(link) - query_dict = dict(urlutils.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 common.Paginated( - instances, next_marker=next_marker, links=links - ) - def list(self, limit=None, marker=None): """ Get a list of all security groups. :rtype: list of :class:`SecurityGroup`. """ - return self._list("/security-groups", "security_groups", limit, - marker) + return self._paginated("/security-groups", "security_groups", + limit, marker) def get(self, security_group): """ @@ -118,8 +98,7 @@ class SecurityGroupRules(base.ManagerWithFind): """ resp, body = self.api.client.delete("/security-group-rules/%s" % base.getid(security_group_rule)) - if resp.status_code in (422, 500): - raise exceptions.from_response(resp, body) + common.check_for_exceptions(resp, body) # Appease the abc gods def list(self): diff --git a/troveclient/v1/shell.py b/troveclient/v1/shell.py index dd259b3e..c71f35cc 100644 --- a/troveclient/v1/shell.py +++ b/troveclient/v1/shell.py @@ -240,21 +240,36 @@ def do_backup_show(cs, args): _print_instance(backup) +@utils.arg('--limit', metavar='', + default=None, + help='Return up to N number of the most recent backups.') @utils.arg('instance', metavar='', help='ID of the instance.') @utils.service_type('database') def do_backup_list_instance(cs, args): """List available backups for an instance.""" - backups = cs.instances.backups(args.instance) - utils.print_list(backups, ['id', 'instance_id', - 'name', 'description', 'status']) + wrapper = cs.instances.backups(args.instance, limit=args.limit) + backups = wrapper.items + while wrapper.next and not args.limit: + wrapper = cs.instances.backups(args.instance, marker=wrapper.next) + backups += wrapper.items + utils.print_list(backups, ['id', 'name', 'status', 'updated'], + order_by='updated') +@utils.arg('--limit', metavar='', + default=None, + help='Return up to N number of the most recent backups.') @utils.service_type('database') def do_backup_list(cs, args): """List available backups.""" - backups = cs.backups.list() - utils.print_list(backups, ['id', 'instance_id', - 'name', 'description', 'status']) + wrapper = cs.backups.list(limit=args.limit) + backups = wrapper.items + while wrapper.next and not args.limit: + wrapper = cs.backups.list(marker=wrapper.next) + backups += wrapper.items + utils.print_list(backups, ['id', 'instance_id', 'name', + 'status', 'updated'], + order_by='updated') @utils.arg('backup', metavar='', help='ID of the backup.') diff --git a/troveclient/v1/users.py b/troveclient/v1/users.py index c7ed5b53..2da58d04 100644 --- a/troveclient/v1/users.py +++ b/troveclient/v1/users.py @@ -19,7 +19,6 @@ from troveclient import base from troveclient.v1 import databases from troveclient import common -from troveclient.openstack.common.py3kcompat import urlutils class User(base.Resource): @@ -52,33 +51,14 @@ class Users(base.ManagerWithFind): resp, body = self.api.client.delete(url) common.check_for_exceptions(resp, body) - def _list(self, url, response_key, limit=None, marker=None): - resp, body = self.api.client.get(common.limit_url(url, limit, marker)) - common.check_for_exceptions(resp, body) - if not body: - raise Exception("Call to " + url + - " did not return a body.") - 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 = urlutils.urlparse(link) - query_dict = dict(urlutils.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 common.Paginated( - users, next_marker=next_marker, links=links - ) - 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", limit, marker) + url = "/instances/%s/users" % base.getid(instance) + return self._paginated(url, "users", limit, marker) def get(self, instance_id, username, hostname=None): """