diff --git a/novaclient/base.py b/novaclient/base.py index 6bcb527c4..328b25126 100644 --- a/novaclient/base.py +++ b/novaclient/base.py @@ -247,7 +247,10 @@ class Manager(HookableMixin): def api_version(self): return self.api.api_version - def _list(self, url, response_key, obj_class=None, body=None): + def _list(self, url, response_key, obj_class=None, body=None, + filters=None): + if filters: + url = utils.get_url_with_filter(url, filters) if body: resp, body = self.api.client.post(url, body=body) else: @@ -347,7 +350,9 @@ class Manager(HookableMixin): if cache: cache.write("%s\n" % val) - def _get(self, url, response_key): + def _get(self, url, response_key, filters=None): + if filters: + url = utils.get_url_with_filter(url, filters) resp, body = self.api.client.get(url) if response_key is not None: content = body[response_key] diff --git a/novaclient/tests/unit/test_utils.py b/novaclient/tests/unit/test_utils.py index 919d4cb2b..372e6f007 100644 --- a/novaclient/tests/unit/test_utils.py +++ b/novaclient/tests/unit/test_utils.py @@ -443,19 +443,31 @@ class RecordTimeTestCase(test_utils.TestCase): class PrepareQueryStringTestCase(test_utils.TestCase): - def test_convert_dict_to_string(self): - ustr = b'?\xd0\xbf=1&\xd1\x80=2' + + def setUp(self): + super(PrepareQueryStringTestCase, self).setUp() + self.ustr = b'?\xd0\xbf=1&\xd1\x80=2' if six.PY3: # in py3 real unicode symbols will be urlencoded - ustr = ustr.decode('utf8') - cases = ( + self.ustr = self.ustr.decode('utf8') + self.cases = ( ({}, ''), + (None, ''), ({'2': 2, '10': 1}, '?10=1&2=2'), ({'abc': 1, 'abc1': 2}, '?abc=1&abc1=2'), - ({b'\xd0\xbf': 1, b'\xd1\x80': 2}, ustr), + ({b'\xd0\xbf': 1, b'\xd1\x80': 2}, self.ustr), ({(1, 2): '1', (3, 4): '2'}, '?(1, 2)=1&(3, 4)=2') ) - for case in cases: + + def test_convert_dict_to_string(self): + for case in self.cases: self.assertEqual( case[1], parse.unquote_plus(utils.prepare_query_string(case[0]))) + + def test_get_url_with_filter(self): + url = '/fake' + for case in self.cases: + self.assertEqual( + '%s%s' % (url, case[1]), + parse.unquote_plus(utils.get_url_with_filter(url, case[0]))) diff --git a/novaclient/utils.py b/novaclient/utils.py index 451b2372d..df4464e6c 100644 --- a/novaclient/utils.py +++ b/novaclient/utils.py @@ -454,5 +454,15 @@ def record_time(times, enabled, *args): def prepare_query_string(params): """Convert dict params to query string""" + # Transform the dict to a sequence of two-element tuples in fixed + # order, then the encoded string will be consistent in Python 2&3. + if not params: + return '' params = sorted(params.items(), key=lambda x: x[0]) return '?%s' % parse.urlencode(params) if params else '' + + +def get_url_with_filter(url, filters): + query_string = prepare_query_string(filters) + url = "%s%s" % (url, query_string) + return url diff --git a/novaclient/v2/flavors.py b/novaclient/v2/flavors.py index 30fa2a8e6..c4b6e40ce 100644 --- a/novaclient/v2/flavors.py +++ b/novaclient/v2/flavors.py @@ -17,7 +17,6 @@ Flavor interface. """ from oslo_utils import strutils -from six.moves.urllib import parse from novaclient import api_versions from novaclient import base @@ -139,14 +138,11 @@ class FlavorManager(base.ManagerWithFind): qparams['sort_dir'] = str(sort_dir) if not is_public: qparams['is_public'] = is_public - qparams = sorted(qparams.items(), key=lambda x: x[0]) - query_string = "?%s" % parse.urlencode(qparams) if qparams else "" - detail = "" if detailed: detail = "/detail" - return self._list("/flavors%s%s" % (detail, query_string), "flavors") + return self._list("/flavors%s" % detail, "flavors", filters=qparams) def get(self, flavor): """Get a specific flavor. diff --git a/novaclient/v2/keypairs.py b/novaclient/v2/keypairs.py index 021cc6273..08c5ea39e 100644 --- a/novaclient/v2/keypairs.py +++ b/novaclient/v2/keypairs.py @@ -19,7 +19,6 @@ Keypair interface from novaclient import api_versions from novaclient import base -from novaclient import utils class Keypair(base.Resource): @@ -170,9 +169,11 @@ class KeypairManager(base.ManagerWithFind): :param user_id: Id of key-pairs owner (Admin only). """ - query_string = "?user_id=%s" % user_id if user_id else "" - url = '/%s%s' % (self.keypair_prefix, query_string) - return self._list(url, 'keypairs') + params = {} + if user_id: + params['user_id'] = user_id + return self._list('/%s' % self.keypair_prefix, 'keypairs', + filters=params) @api_versions.wraps("2.35") def list(self, user_id=None, marker=None, limit=None): @@ -192,6 +193,5 @@ class KeypairManager(base.ManagerWithFind): params['limit'] = int(limit) if marker: params['marker'] = str(marker) - query_string = utils.prepare_query_string(params) - url = '/%s%s' % (self.keypair_prefix, query_string) - return self._list(url, 'keypairs') + return self._list('/%s' % self.keypair_prefix, 'keypairs', + filters=params) diff --git a/novaclient/v2/limits.py b/novaclient/v2/limits.py index 46d77def8..d90d3e9a4 100644 --- a/novaclient/v2/limits.py +++ b/novaclient/v2/limits.py @@ -12,8 +12,6 @@ # License for the specific language governing permissions and limitations # under the License. -from six.moves.urllib import parse - from novaclient import base @@ -95,6 +93,4 @@ class LimitsManager(base.Manager): opts['reserved'] = 1 if tenant_id: opts['tenant_id'] = tenant_id - query_string = "?%s" % parse.urlencode(opts) if opts else "" - - return self._get("/limits%s" % query_string, "limits") + return self._get("/limits", "limits", filters=opts) diff --git a/novaclient/v2/migrations.py b/novaclient/v2/migrations.py index 72e0deda9..d1562b711 100644 --- a/novaclient/v2/migrations.py +++ b/novaclient/v2/migrations.py @@ -14,8 +14,6 @@ migration interface """ -from six.moves.urllib import parse - from novaclient import base from novaclient.i18n import _ @@ -50,10 +48,4 @@ class MigrationManager(base.ManagerWithFind): if instance_uuid: opts['instance_uuid'] = instance_uuid - # Transform the dict to a sequence of two-element tuples in fixed - # order, then the encoded string will be consistent in Python 2&3. - new_opts = sorted(opts.items(), key=lambda x: x[0]) - - query_string = "?%s" % parse.urlencode(new_opts) if new_opts else "" - - return self._list("/os-migrations%s" % query_string, "migrations") + return self._list("/os-migrations", "migrations", filters=opts) diff --git a/novaclient/v2/server_groups.py b/novaclient/v2/server_groups.py index 7a01ba0db..62a5c9a09 100644 --- a/novaclient/v2/server_groups.py +++ b/novaclient/v2/server_groups.py @@ -17,8 +17,6 @@ Server group interface. """ -from six.moves.urllib import parse - from novaclient import base @@ -62,10 +60,8 @@ class ServerGroupsManager(base.ManagerWithFind): qparams['limit'] = int(limit) if offset: qparams['offset'] = int(offset) - qparams = sorted(qparams.items(), key=lambda x: x[0]) - query_string = "?%s" % parse.urlencode(qparams) if qparams else "" - return self._list('/os-server-groups%s' % query_string, - 'server_groups') + return self._list('/os-server-groups', 'server_groups', + filters=qparams) def get(self, id): """Get a specific server group.