diff --git a/novaclient/__init__.py b/novaclient/__init__.py index e13263741..ad6d401ec 100644 --- a/novaclient/__init__.py +++ b/novaclient/__init__.py @@ -25,4 +25,4 @@ API_MIN_VERSION = api_versions.APIVersion("2.1") # when client supported the max version, and bumped sequentially, otherwise # the client may break due to server side new version may include some # backward incompatible change. -API_MAX_VERSION = api_versions.APIVersion("2.32") +API_MAX_VERSION = api_versions.APIVersion("2.33") diff --git a/novaclient/tests/unit/test_utils.py b/novaclient/tests/unit/test_utils.py index f3d88c21e..64a947ea5 100644 --- a/novaclient/tests/unit/test_utils.py +++ b/novaclient/tests/unit/test_utils.py @@ -16,6 +16,7 @@ import sys import mock from oslo_utils import encodeutils import six +from six.moves.urllib import parse from novaclient import base from novaclient import exceptions @@ -437,3 +438,22 @@ class RecordTimeTestCase(test_utils.TestCase): with utils.record_time(times, False, 'x'): pass self.assertEqual(0, len(times)) + + +class PrepareQueryStringTestCase(test_utils.TestCase): + def test_convert_dict_to_string(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 = ( + ({}, ''), + ({'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), + ({(1, 2): '1', (3, 4): '2'}, '?(1, 2)=1&(3, 4)=2') + ) + for case in cases: + self.assertEqual( + case[1], + parse.unquote_plus(utils.prepare_query_string(case[0]))) diff --git a/novaclient/tests/unit/v2/test_hypervisors.py b/novaclient/tests/unit/v2/test_hypervisors.py index a522b8537..d395eee85 100644 --- a/novaclient/tests/unit/v2/test_hypervisors.py +++ b/novaclient/tests/unit/v2/test_hypervisors.py @@ -13,6 +13,7 @@ # License for the specific language governing permissions and limitations # under the License. +from novaclient import api_versions from novaclient.tests.unit.fixture_data import client from novaclient.tests.unit.fixture_data import hypervisors as data from novaclient.tests.unit import utils @@ -185,3 +186,16 @@ class HypervisorsTest(utils.FixturedTestCase): # Test for Bug #1370415, the line below used to raise AttributeError self.assertEqual("", result.__repr__()) + + +class HypervisorsV233Test(HypervisorsTest): + def setUp(self): + super(HypervisorsV233Test, self).setUp() + self.cs.api_version = api_versions.APIVersion("2.33") + + def test_use_limit_marker_params(self): + params = {'limit': 10, 'marker': 'fake-marker'} + self.cs.hypervisors.list(**params) + for k, v in params.items(): + self.assertIn('%s=%s' % (k, v), + self.requests.last_request.path_url) diff --git a/novaclient/tests/unit/v2/test_shell.py b/novaclient/tests/unit/v2/test_shell.py index 587b6338f..eda8d101c 100644 --- a/novaclient/tests/unit/v2/test_shell.py +++ b/novaclient/tests/unit/v2/test_shell.py @@ -2154,6 +2154,11 @@ class ShellTest(utils.TestCase): self.run_command('hypervisor-list --matching hyper') self.assert_called('GET', '/os-hypervisors/hyper/search') + def test_hypervisor_list_limit_marker(self): + self.run_command('hypervisor-list --limit 10 --marker hyper1', + api_version='2.33') + self.assert_called('GET', '/os-hypervisors?limit=10&marker=hyper1') + def test_hypervisor_servers(self): self.run_command('hypervisor-servers hyper') self.assert_called('GET', '/os-hypervisors/hyper/servers') diff --git a/novaclient/utils.py b/novaclient/utils.py index 0170c9524..d7573758a 100644 --- a/novaclient/utils.py +++ b/novaclient/utils.py @@ -24,6 +24,7 @@ from oslo_utils import encodeutils import pkg_resources import prettytable import six +from six.moves.urllib import parse from novaclient import exceptions from novaclient.i18n import _ @@ -465,3 +466,9 @@ def record_time(times, enabled, *args): yield end = time.time() times.append((' '.join(args), start, end)) + + +def prepare_query_string(params): + """Convert dict params to query string""" + params = sorted(params.items(), key=lambda x: x[0]) + return '?%s' % parse.urlencode(params) if params else '' diff --git a/novaclient/v2/hypervisors.py b/novaclient/v2/hypervisors.py index 684a7fb34..8169b7613 100644 --- a/novaclient/v2/hypervisors.py +++ b/novaclient/v2/hypervisors.py @@ -19,7 +19,9 @@ Hypervisors interface (1.1 extension). from six.moves.urllib import parse +from novaclient import api_versions from novaclient import base +from novaclient import utils class Hypervisor(base.Resource): @@ -33,14 +35,35 @@ class HypervisorManager(base.ManagerWithFind): resource_class = Hypervisor is_alphanum_id_allowed = True + def _list_base(self, detailed=True, marker=None, limit=None): + path = '/os-hypervisors' + if detailed: + path += '/detail' + params = {} + if limit is not None: + params['limit'] = int(limit) + if marker is not None: + params['marker'] = str(marker) + path += utils.prepare_query_string(params) + return self._list(path, 'hypervisors') + + @api_versions.wraps("2.0", "2.32") def list(self, detailed=True): """ Get a list of hypervisors. """ - detail = "" - if detailed: - detail = "/detail" - return self._list('/os-hypervisors%s' % detail, 'hypervisors') + return self._list_base(detailed=detailed) + + @api_versions.wraps("2.33") + def list(self, detailed=True, marker=None, limit=None): + """ + Get a list of hypervisors. + :param marker: Begin returning hypervisor that appear later in the + keypair list than that represented by this keypair name + (optional). + :param limit: maximum number of keypairs to return (optional). + """ + return self._list_base(detailed=detailed, marker=marker, limit=limit) def search(self, hypervisor_match, servers=False): """ diff --git a/novaclient/v2/shell.py b/novaclient/v2/shell.py index d2aca4620..5d6a09023 100644 --- a/novaclient/v2/shell.py +++ b/novaclient/v2/shell.py @@ -3980,6 +3980,22 @@ def _find_hypervisor(cs, hypervisor): return utils.find_resource(cs.hypervisors, hypervisor) +def _do_hypervisor_list(cs, matching=None, limit=None, marker=None): + columns = ['ID', 'Hypervisor hostname', 'State', 'Status'] + if matching: + utils.print_list(cs.hypervisors.search(matching), columns) + else: + params = {} + if limit is not None: + params['limit'] = limit + if marker is not None: + params['marker'] = marker + # Since we're not outputting detail data, choose + # detailed=False for server-side efficiency + utils.print_list(cs.hypervisors.list(False, **params), columns) + + +@api_versions.wraps("2.0", "2.32") @utils.arg( '--matching', metavar='', @@ -3987,13 +4003,37 @@ def _find_hypervisor(cs, hypervisor): help=_('List hypervisors matching the given .')) def do_hypervisor_list(cs, args): """List hypervisors.""" - columns = ['ID', 'Hypervisor hostname', 'State', 'Status'] - if args.matching: - utils.print_list(cs.hypervisors.search(args.matching), columns) - else: - # Since we're not outputting detail data, choose - # detailed=False for server-side efficiency - utils.print_list(cs.hypervisors.list(False), columns) + _do_hypervisor_list(cs, matching=args.matching) + + +@api_versions.wraps("2.33") +@utils.arg( + '--matching', + metavar='', + default=None, + help=_('List hypervisors matching the given . ' + 'If matching is used limit and marker options will be ignored.')) +@utils.arg( + '--marker', + dest='marker', + metavar='', + default=None, + help=_('The last hypervisor of the previous page; displays list of ' + 'hypervisors after "marker".')) +@utils.arg( + '--limit', + dest='limit', + metavar='', + type=int, + default=None, + help=_("Maximum number of hypervisors to display. If limit == -1, all " + "hypervisors will be displayed. If limit is bigger than " + "'osapi_max_limit' option of Nova API, limit 'osapi_max_limit' " + "will be used instead.")) +def do_hypervisor_list(cs, args): + """List hypervisors.""" + _do_hypervisor_list( + cs, matching=args.matching, limit=args.limit, marker=args.marker) @utils.arg( diff --git a/releasenotes/notes/microversion-v2_33-10d12ea3b25839e8.yaml b/releasenotes/notes/microversion-v2_33-10d12ea3b25839e8.yaml new file mode 100644 index 000000000..0376b1455 --- /dev/null +++ b/releasenotes/notes/microversion-v2_33-10d12ea3b25839e8.yaml @@ -0,0 +1,5 @@ +--- +features: + - Added microversion v2.33 that adds pagination support for hypervisors with + the help of new optional parameters 'limit' and 'marker' which were added + to hypervisor-list command. \ No newline at end of file