Adds the ability to sort images with multiple keys

Adds client code to consume API modified in change
Ib7a6aeb2df3bc5d23fe8e070290b5bfcab00c0f5

Extends CLI for v2 with multiple sort keys
Example:
glance --os-image-api-version 2 image-list --sort-key name --sort-key size

Implements-blueprint: glance-sorting-enhancements
Change-Id: If79779a4c52c8dc5c4f39192d3d247335a76ba24
DocImpact
Closes-Bug: 1221274
This commit is contained in:
Mike Fedosin 2014-09-11 16:58:45 +04:00
parent cc4c402f09
commit fc79467ff6
4 changed files with 139 additions and 3 deletions

View File

@ -26,6 +26,10 @@ from glanceclient.v2 import schemas
DEFAULT_PAGE_SIZE = 20 DEFAULT_PAGE_SIZE = 20
SORT_DIR_VALUES = ('asc', 'desc')
SORT_KEY_VALUES = ('name', 'status', 'container_format', 'disk_format',
'size', 'id', 'created_at', 'updated_at')
class Controller(object): class Controller(object):
def __init__(self, http_client, schema_client): def __init__(self, http_client, schema_client):
@ -94,6 +98,10 @@ class Controller(object):
# the page_size as Glance's limit. # the page_size as Glance's limit.
filters['limit'] = page_size filters['limit'] = page_size
sort_dir = kwargs.get('sort_dir')
if sort_dir is not None:
filters['sort_dir'] = sort_dir
tags = filters.pop('tag', []) tags = filters.pop('tag', [])
tags_url_params = [] tags_url_params = []
@ -110,6 +118,13 @@ class Controller(object):
for param in tags_url_params: for param in tags_url_params:
url = '%s&%s' % (url, parse.urlencode(param)) url = '%s&%s' % (url, parse.urlencode(param))
sort_key = kwargs.get('sort_key')
if sort_key is not None:
if isinstance(sort_key, six.string_types):
sort_key = [sort_key]
for key in sort_key:
url = '%s&sort_key=%s' % (url, key)
for image in paginate(url, page_size, limit): for image in paginate(url, page_size, limit):
yield image yield image

View File

@ -16,6 +16,7 @@
from glanceclient.common import progressbar from glanceclient.common import progressbar
from glanceclient.common import utils from glanceclient.common import utils
from glanceclient import exc from glanceclient import exc
from glanceclient.v2 import images
from glanceclient.v2 import tasks from glanceclient.v2 import tasks
import json import json
import os import os
@ -124,6 +125,12 @@ def do_image_update(gc, args):
help='Displays images that match the checksum.') help='Displays images that match the checksum.')
@utils.arg('--tag', metavar='<TAG>', action='append', @utils.arg('--tag', metavar='<TAG>', action='append',
help="Filter images by a user-defined tag.") help="Filter images by a user-defined tag.")
@utils.arg('--sort-key', default=[], action='append',
choices=images.SORT_KEY_VALUES,
help='Sort image list by specified fields.')
@utils.arg('--sort-dir', default='asc',
choices=images.SORT_DIR_VALUES,
help='Sort image list in specified direction.')
def do_image_list(gc, args): def do_image_list(gc, args):
"""List images you can access.""" """List images you can access."""
filter_keys = ['visibility', 'member_status', 'owner', 'checksum', 'tag'] filter_keys = ['visibility', 'member_status', 'owner', 'checksum', 'tag']
@ -141,6 +148,11 @@ def do_image_list(gc, args):
kwargs['limit'] = args.limit kwargs['limit'] = args.limit
if args.page_size is not None: if args.page_size is not None:
kwargs['page_size'] = args.page_size kwargs['page_size'] = args.page_size
if args.sort_key:
kwargs['sort_key'] = args.sort_key
else:
kwargs['sort_key'] = ['name']
kwargs['sort_dir'] = args.sort_dir
images = gc.images.list(**kwargs) images = gc.images.list(**kwargs)
columns = ['ID', 'Name'] columns = ['ID', 'Name']

View File

@ -394,9 +394,55 @@ data_fixtures = {
{'images': []}, {'images': []},
), ),
}, },
'/v2/images?limit=%d&sort_key=name' % images.DEFAULT_PAGE_SIZE: {
'GET': (
{},
{'images': [
{
'id': '2a4560b2-e585-443e-9b39-553b46ec92d1',
'name': 'image-1',
},
{
'id': '6f99bf80-2ee6-47cf-acfe-1f1fabb7e810',
'name': 'image-2',
},
]},
),
},
'/v2/images?limit=%d&sort_key=name&sort_key=id'
% images.DEFAULT_PAGE_SIZE: {
'GET': (
{},
{'images': [
{
'id': '2a4560b2-e585-443e-9b39-553b46ec92d1',
'name': 'image',
},
{
'id': '6f99bf80-2ee6-47cf-acfe-1f1fabb7e810',
'name': 'image',
},
]},
),
},
'/v2/images?limit=%d&sort_dir=desc&sort_key=id'
% images.DEFAULT_PAGE_SIZE: {
'GET': (
{},
{'images': [
{
'id': '6f99bf80-2ee6-47cf-acfe-1f1fabb7e810',
'name': 'image-2',
},
{
'id': '2a4560b2-e585-443e-9b39-553b46ec92d1',
'name': 'image-1',
},
]},
),
}
} }
schema_fixtures = { schema_fixtures = {
'image': { 'image': {
'GET': ( 'GET': (
@ -559,6 +605,29 @@ class TestController(testtools.TestCase):
images = list(self.controller.list(**filters)) images = list(self.controller.list(**filters))
self.assertEqual(0, len(images)) self.assertEqual(0, len(images))
def test_list_images_with_single_sort_key(self):
img_id1 = '2a4560b2-e585-443e-9b39-553b46ec92d1'
sort_key = 'name'
images = list(self.controller.list(sort_key=sort_key))
self.assertEqual(2, len(images))
self.assertEqual('%s' % img_id1, images[0].id)
def test_list_with_multiple_sort_keys(self):
img_id1 = '2a4560b2-e585-443e-9b39-553b46ec92d1'
sort_key = ['name', 'id']
images = list(self.controller.list(sort_key=sort_key))
self.assertEqual(2, len(images))
self.assertEqual('%s' % img_id1, images[0].id)
def test_list_images_with_desc_sort_dir(self):
img_id1 = '2a4560b2-e585-443e-9b39-553b46ec92d1'
sort_key = 'id'
sort_dir = 'desc'
images = list(self.controller.list(sort_key=sort_key,
sort_dir=sort_dir))
self.assertEqual(2, len(images))
self.assertEqual('%s' % img_id1, images[1].id)
def test_list_images_for_property(self): def test_list_images_for_property(self):
filters = {'filters': dict([('os_distro', 'NixOS')])} filters = {'filters': dict([('os_distro', 'NixOS')])}
images = list(self.controller.list(**filters)) images = list(self.controller.list(**filters))

View File

@ -68,7 +68,9 @@ class ShellV2Test(testtools.TestCase):
'owner': 'test', 'owner': 'test',
'checksum': 'fake_checksum', 'checksum': 'fake_checksum',
'tag': 'fake tag', 'tag': 'fake tag',
'properties': [] 'properties': [],
'sort_key': ['name', 'id'],
'sort_dir': 'desc'
} }
args = self._make_args(input) args = self._make_args(input)
with mock.patch.object(self.gc.images, 'list') as mocked_list: with mock.patch.object(self.gc.images, 'list') as mocked_list:
@ -84,6 +86,40 @@ class ShellV2Test(testtools.TestCase):
'tag': 'fake tag' 'tag': 'fake tag'
} }
mocked_list.assert_called_once_with(page_size=18, mocked_list.assert_called_once_with(page_size=18,
sort_key=['name', 'id'],
sort_dir='desc',
filters=exp_img_filters)
utils.print_list.assert_called_once_with({}, ['ID', 'Name'])
def test_do_image_list_with_single_sort_key(self):
input = {
'limit': None,
'page_size': 18,
'visibility': True,
'member_status': 'Fake',
'owner': 'test',
'checksum': 'fake_checksum',
'tag': 'fake tag',
'properties': [],
'sort_key': ['name'],
'sort_dir': 'desc'
}
args = self._make_args(input)
with mock.patch.object(self.gc.images, 'list') as mocked_list:
mocked_list.return_value = {}
test_shell.do_image_list(self.gc, args)
exp_img_filters = {
'owner': 'test',
'member_status': 'Fake',
'visibility': True,
'checksum': 'fake_checksum',
'tag': 'fake tag'
}
mocked_list.assert_called_once_with(page_size=18,
sort_key=['name'],
sort_dir='desc',
filters=exp_img_filters) filters=exp_img_filters)
utils.print_list.assert_called_once_with({}, ['ID', 'Name']) utils.print_list.assert_called_once_with({}, ['ID', 'Name'])
@ -96,7 +132,9 @@ class ShellV2Test(testtools.TestCase):
'owner': 'test', 'owner': 'test',
'checksum': 'fake_checksum', 'checksum': 'fake_checksum',
'tag': 'fake tag', 'tag': 'fake tag',
'properties': ['os_distro=NixOS', 'architecture=x86_64'] 'properties': ['os_distro=NixOS', 'architecture=x86_64'],
'sort_key': ['name'],
'sort_dir': 'desc'
} }
args = self._make_args(input) args = self._make_args(input)
with mock.patch.object(self.gc.images, 'list') as mocked_list: with mock.patch.object(self.gc.images, 'list') as mocked_list:
@ -115,6 +153,8 @@ class ShellV2Test(testtools.TestCase):
} }
mocked_list.assert_called_once_with(page_size=1, mocked_list.assert_called_once_with(page_size=1,
sort_key=['name'],
sort_dir='desc',
filters=exp_img_filters) filters=exp_img_filters)
utils.print_list.assert_called_once_with({}, ['ID', 'Name']) utils.print_list.assert_called_once_with({}, ['ID', 'Name'])