Add datastore filter to backup-list
This fix enhances the backup-list command to optionally receive a datastore name or ID to filter the backup list by. The filter is sent as a query string. To attach the query string to the URL and have it still work with the URL for pagination, i have made some changes in the way url with query strings are constructed. This includes the pagination URL. partially implements: blueprint backup-metadata Change-Id: I0b9ef3ec7f51ed76517a22f9c0edfdce3694a36f
This commit is contained in:
@@ -62,8 +62,12 @@ class Manager(utils.HookableMixin):
|
|||||||
def __init__(self, api):
|
def __init__(self, api):
|
||||||
self.api = api
|
self.api = api
|
||||||
|
|
||||||
def _paginated(self, url, response_key, limit=None, marker=None):
|
def _paginated(self, url, response_key, limit=None, marker=None,
|
||||||
resp, body = self.api.client.get(common.limit_url(url, limit, marker))
|
query_strings=None):
|
||||||
|
query_strings = query_strings or {}
|
||||||
|
url = common.append_query_strings(url, limit=limit, marker=marker,
|
||||||
|
**query_strings)
|
||||||
|
resp, body = self.api.client.get(url)
|
||||||
if not body:
|
if not body:
|
||||||
raise Exception("Call to " + url + " did not return a body.")
|
raise Exception("Call to " + url + " did not return a body.")
|
||||||
links = body.get('links', [])
|
links = body.get('links', [])
|
||||||
|
@@ -23,16 +23,12 @@ def check_for_exceptions(resp, body, url):
|
|||||||
raise exceptions.from_response(resp, body, url)
|
raise exceptions.from_response(resp, body, url)
|
||||||
|
|
||||||
|
|
||||||
def limit_url(url, limit=None, marker=None):
|
def append_query_strings(url, **query_strings):
|
||||||
if not limit and not marker:
|
if not query_strings:
|
||||||
return url
|
return url
|
||||||
query = []
|
query = '&'.join('{0}={1}'.format(key, val)
|
||||||
if marker:
|
for key, val in query_strings.items() if val)
|
||||||
query.append("marker=%s" % marker)
|
return url + ('?' + query if query else "")
|
||||||
if limit:
|
|
||||||
query.append("limit=%s" % limit)
|
|
||||||
query = '?' + '&'.join(query)
|
|
||||||
return url + query
|
|
||||||
|
|
||||||
|
|
||||||
def quote_user_host(user, host):
|
def quote_user_host(user, host):
|
||||||
|
@@ -80,7 +80,17 @@ class BackupManagerTest(testtools.TestCase):
|
|||||||
limit = "test-limit"
|
limit = "test-limit"
|
||||||
marker = "test-marker"
|
marker = "test-marker"
|
||||||
self.backups.list(limit, marker)
|
self.backups.list(limit, marker)
|
||||||
page_mock.assert_called_with("/backups", "backups", limit, marker)
|
page_mock.assert_called_with("/backups", "backups", limit, marker, {})
|
||||||
|
|
||||||
|
def test_list_by_datastore(self):
|
||||||
|
page_mock = mock.Mock()
|
||||||
|
self.backups._paginated = page_mock
|
||||||
|
limit = "test-limit"
|
||||||
|
marker = "test-marker"
|
||||||
|
datastore = "test-mysql"
|
||||||
|
self.backups.list(limit, marker, datastore)
|
||||||
|
page_mock.assert_called_with("/backups", "backups", limit, marker,
|
||||||
|
{'datastore': datastore})
|
||||||
|
|
||||||
def test_get(self):
|
def test_get(self):
|
||||||
get_mock = mock.Mock()
|
get_mock = mock.Mock()
|
||||||
|
@@ -279,9 +279,14 @@ class MangerPaginationTests(ManagerTest):
|
|||||||
|
|
||||||
def side_effect(url):
|
def side_effect(url):
|
||||||
if url == self.url:
|
if url == self.url:
|
||||||
return (None, self.body)
|
return None, self.body
|
||||||
if url == self.next_url:
|
# In python 3 the order in the dictionary is not constant
|
||||||
return (None, self.next_body)
|
# between runs. So we cant rely on the URL params to be
|
||||||
|
# in the same order
|
||||||
|
if ('marker=%s' % self.marker in url and
|
||||||
|
'limit=%s' % self.limit in url):
|
||||||
|
self.next_url = url
|
||||||
|
return None, self.next_body
|
||||||
|
|
||||||
self.manager.api.client.get = mock.Mock(side_effect=side_effect)
|
self.manager.api.client.get = mock.Mock(side_effect=side_effect)
|
||||||
|
|
||||||
|
@@ -31,15 +31,34 @@ class CommonTest(testtools.TestCase):
|
|||||||
self.assertRaises(Exception,
|
self.assertRaises(Exception,
|
||||||
common.check_for_exceptions, resp, "body")
|
common.check_for_exceptions, resp, "body")
|
||||||
|
|
||||||
def test_limit_url(self):
|
def test_append_query_strings(self):
|
||||||
url = "test-url"
|
url = "test-url"
|
||||||
self.assertEqual(url, common.limit_url(url, limit=None, marker=None))
|
self.assertEqual(url, common.append_query_strings(url))
|
||||||
|
|
||||||
|
self.assertEqual(url, common.append_query_strings(
|
||||||
|
url, limit=None, marker=None))
|
||||||
|
|
||||||
limit = "test-limit"
|
limit = "test-limit"
|
||||||
marker = "test-marker"
|
marker = "test-marker"
|
||||||
expected = "test-url?marker=test-marker&limit=test-limit"
|
result = common.append_query_strings(url, limit=limit, marker=marker)
|
||||||
self.assertEqual(expected,
|
self.assertTrue(result.startswith(url + '?'))
|
||||||
common.limit_url(url, limit=limit, marker=marker))
|
self.assertIn("limit=%s" % limit, result)
|
||||||
|
self.assertIn("marker=%s" % marker, result)
|
||||||
|
self.assertEqual(result.count('&'), 1)
|
||||||
|
|
||||||
|
opts = {}
|
||||||
|
self.assertEqual(url, common.append_query_strings(
|
||||||
|
url, limit=None, marker=None, **opts))
|
||||||
|
|
||||||
|
opts = {'key1': 'value1', 'key2': None}
|
||||||
|
result = common.append_query_strings(url, limit=None, marker=marker,
|
||||||
|
**opts)
|
||||||
|
self.assertTrue(result.startswith(url + '?'))
|
||||||
|
self.assertEqual(result.count('&'), 1)
|
||||||
|
self.assertNotIn("limit=%s" % limit, result)
|
||||||
|
self.assertIn("marker=%s" % marker, result)
|
||||||
|
self.assertIn("key1=%s" % opts['key1'], result)
|
||||||
|
self.assertNotIn("key2=%s" % opts['key2'], result)
|
||||||
|
|
||||||
|
|
||||||
class PaginatedTest(testtools.TestCase):
|
class PaginatedTest(testtools.TestCase):
|
||||||
|
@@ -38,12 +38,17 @@ class Backups(base.ManagerWithFind):
|
|||||||
return self._get("/backups/%s" % base.getid(backup),
|
return self._get("/backups/%s" % base.getid(backup),
|
||||||
"backup")
|
"backup")
|
||||||
|
|
||||||
def list(self, limit=None, marker=None):
|
def list(self, limit=None, marker=None, datastore=None):
|
||||||
"""Get a list of all backups.
|
"""Get a list of all backups.
|
||||||
|
|
||||||
:rtype: list of :class:`Backups`.
|
:rtype: list of :class:`Backups`.
|
||||||
"""
|
"""
|
||||||
return self._paginated("/backups", "backups", limit, marker)
|
query_strings = {}
|
||||||
|
if datastore:
|
||||||
|
query_strings = {'datastore': datastore}
|
||||||
|
|
||||||
|
return self._paginated("/backups", "backups", limit, marker,
|
||||||
|
query_strings)
|
||||||
|
|
||||||
def create(self, name, instance, description=None, parent_id=None):
|
def create(self, name, instance, description=None, parent_id=None):
|
||||||
"""Create a new backup from the given instance."""
|
"""Create a new backup from the given instance."""
|
||||||
|
@@ -332,10 +332,13 @@ def do_backup_list_instance(cs, args):
|
|||||||
@utils.arg('--limit', metavar='<limit>',
|
@utils.arg('--limit', metavar='<limit>',
|
||||||
default=None,
|
default=None,
|
||||||
help='Return up to N number of the most recent backups.')
|
help='Return up to N number of the most recent backups.')
|
||||||
|
@utils.arg('--datastore', metavar='<datastore>',
|
||||||
|
default=None,
|
||||||
|
help='Name or ID of the datastore to list backups for.')
|
||||||
@utils.service_type('database')
|
@utils.service_type('database')
|
||||||
def do_backup_list(cs, args):
|
def do_backup_list(cs, args):
|
||||||
"""Lists available backups."""
|
"""Lists available backups."""
|
||||||
wrapper = cs.backups.list(limit=args.limit)
|
wrapper = cs.backups.list(limit=args.limit, datastore=args.datastore)
|
||||||
backups = wrapper.items
|
backups = wrapper.items
|
||||||
while wrapper.next and not args.limit:
|
while wrapper.next and not args.limit:
|
||||||
wrapper = cs.backups.list(marker=wrapper.next)
|
wrapper = cs.backups.list(marker=wrapper.next)
|
||||||
|
Reference in New Issue
Block a user