novaclient sort parameters support

Adds sorting support to the 'nova list' command.

--sort <key>[:<direction>]

The --sort parameter is comma-separated and is used to specify
one or more sort keys and directions. The direction defaults to
'desc' for each sort key and the user can supply 'asc' to
override.

Partially implements: blueprint nova-pagination

Change-Id: I635e017c7f9ab61812333983bfecccd6fce8d394
This commit is contained in:
Steven Kaufer 2014-08-28 02:58:58 +00:00
parent 5f2ad683ce
commit 4b530bf9d6
4 changed files with 114 additions and 5 deletions

View File

@ -49,6 +49,25 @@ class ServersTest(utils.FixturedTestCase):
for s in sl:
self.assertIsInstance(s, servers.Server)
def test_list_servers_sort_single(self):
sl = self.cs.servers.list(sort_keys=['display_name'],
sort_dirs=['asc'])
self.assert_called(
'GET',
'/servers/detail?sort_dir=asc&sort_key=display_name')
for s in sl:
self.assertIsInstance(s, servers.Server)
def test_list_servers_sort_multiple(self):
sl = self.cs.servers.list(sort_keys=['display_name', 'id'],
sort_dirs=['asc', 'desc'])
self.assert_called(
'GET',
('/servers/detail?sort_dir=asc&sort_dir=desc&'
'sort_key=display_name&sort_key=id'))
for s in sl:
self.assertIsInstance(s, servers.Server)
def test_get_server_details(self):
s = self.cs.servers.get(1234)
self.assert_called('GET', '/servers/1234')

View File

@ -851,6 +851,60 @@ class ShellTest(utils.TestCase):
'GET',
'/servers/detail?all_tenants=1&user_id=fake_user')
def test_list_with_single_sort_key_no_dir(self):
self.run_command('list --sort 1')
self.assert_called(
'GET', ('/servers/detail?sort_dir=desc&sort_key=1'))
def test_list_with_single_sort_key_and_dir(self):
self.run_command('list --sort 1:asc')
self.assert_called(
'GET', ('/servers/detail?sort_dir=asc&sort_key=1'))
def test_list_with_sort_keys_no_dir(self):
self.run_command('list --sort 1,2')
self.assert_called(
'GET', ('/servers/detail?sort_dir=desc&sort_dir=desc&'
'sort_key=1&sort_key=2'))
def test_list_with_sort_keys_and_dirs(self):
self.run_command('list --sort 1:asc,2:desc')
self.assert_called(
'GET', ('/servers/detail?sort_dir=asc&sort_dir=desc&'
'sort_key=1&sort_key=2'))
def test_list_with_sort_keys_and_some_dirs(self):
self.run_command('list --sort 1,2:asc')
self.assert_called(
'GET', ('/servers/detail?sort_dir=desc&sort_dir=asc&'
'sort_key=1&sort_key=2'))
def test_list_with_invalid_sort_dir_one(self):
cmd = 'list --sort 1:foo'
self.assertRaises(exceptions.CommandError, self.run_command, cmd)
def test_list_with_invalid_sort_dir_two(self):
cmd = 'list --sort 1:asc,2:foo'
self.assertRaises(exceptions.CommandError, self.run_command, cmd)
def test_list_sortby_index_with_sort(self):
# sortby_index is None if there is sort information
for cmd in ['list --sort key',
'list --sort key:desc',
'list --sort key1,key2:asc']:
with mock.patch('novaclient.utils.print_list') as mock_print_list:
self.run_command(cmd)
mock_print_list.assert_called_once_with(
mock.ANY, mock.ANY, mock.ANY, sortby_index=None)
def test_list_sortby_index_without_sort(self):
# sortby_index is 1 without sort information
for cmd in ['list', 'list --minimal', 'list --deleted']:
with mock.patch('novaclient.utils.print_list') as mock_print_list:
self.run_command(cmd)
mock_print_list.assert_called_once_with(
mock.ANY, mock.ANY, mock.ANY, sortby_index=1)
def test_list_fields(self):
output = self.run_command(
'list --fields '

View File

@ -566,7 +566,8 @@ class ServerManager(base.BootingManagerWithFind):
"""
return self._get("/servers/%s" % base.getid(server), "server")
def list(self, detailed=True, search_opts=None, marker=None, limit=None):
def list(self, detailed=True, search_opts=None, marker=None, limit=None,
sort_keys=None, sort_dirs=None):
"""
Get a list of servers.
@ -575,6 +576,8 @@ class ServerManager(base.BootingManagerWithFind):
:param marker: Begin returning servers that appear later in the server
list than that represented by this server id (optional).
:param limit: Maximum number of servers to return (optional).
:param sort_keys: List of sort keys
:param sort_dirs: List of sort directions
:rtype: list of :class:`Server`
"""
@ -595,8 +598,16 @@ class ServerManager(base.BootingManagerWithFind):
# 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 qparams:
new_qparams = sorted(qparams.items(), key=lambda x: x[0])
if qparams or sort_keys or sort_dirs:
# sort keys and directions are unique since the same parameter
# key is repeated for each associated value
# (ie, &sort_key=key1&sort_key=key2&sort_key=key3)
items = list(qparams.items())
if sort_keys:
items.extend(('sort_key', sort_key) for sort_key in sort_keys)
if sort_dirs:
items.extend(('sort_dir', sort_dir) for sort_dir in sort_dirs)
new_qparams = sorted(items, key=lambda x: x[0])
query_string = "?%s" % parse.urlencode(new_qparams)
else:
query_string = ""

View File

@ -1308,6 +1308,13 @@ def do_image_delete(cs, args):
action="store_true",
default=False,
help=_('Get only uuid and name.'))
@cliutils.arg(
'--sort',
dest='sort',
metavar='<key>[:<direction>]',
help=('Comma-separated list of sort keys and directions in the form'
' of <key>[:<asc|desc>]. The direction defaults to descending if'
' not specified.'))
def do_list(cs, args):
"""List active servers."""
imageid = None
@ -1350,8 +1357,23 @@ def do_list(cs, args):
detailed = not args.minimal
sort_keys = []
sort_dirs = []
if args.sort:
for sort in args.sort.split(','):
sort_key, _sep, sort_dir = sort.partition(':')
if not sort_dir:
sort_dir = 'desc'
elif sort_dir not in ('asc', 'desc'):
raise exceptions.CommandError(_(
'Unknown sort direction: %s') % sort_dir)
sort_keys.append(sort_key)
sort_dirs.append(sort_dir)
servers = cs.servers.list(detailed=detailed,
search_opts=search_opts)
search_opts=search_opts,
sort_keys=sort_keys,
sort_dirs=sort_dirs)
convert = [('OS-EXT-SRV-ATTR:host', 'host'),
('OS-EXT-STS:task_state', 'task_state'),
('OS-EXT-SRV-ATTR:instance_name', 'instance_name'),
@ -1375,8 +1397,11 @@ def do_list(cs, args):
'Networks'
]
formatters['Networks'] = utils._format_servers_list_networks
sortby_index = 1
if args.sort:
sortby_index = None
utils.print_list(servers, columns,
formatters, sortby_index=1)
formatters, sortby_index=sortby_index)
@cliutils.arg(