From 4194a55a096dbc02275b9a52f60ec96ae8d64473 Mon Sep 17 00:00:00 2001 From: Louis Taylor Date: Mon, 20 Oct 2014 14:26:14 +0000 Subject: [PATCH] Add --property-filter option to v2 image-list The option is present in the v1 shell, but missing from the v2 shell. This allows the user to filter based on any image property. Example: $ glance --os-image-api-version 2 image-list --property-filter os_distro=NixOS DocImpact Closes-Bug: #1383326 Change-Id: Ia65a08520e3eaf3ecd9b9018be9f6a8e2d3d4f0b --- glanceclient/v2/shell.py | 9 +++++++++ tests/v2/test_images.py | 30 ++++++++++++++++++++++++++++++ tests/v2/test_shell_v2.py | 33 ++++++++++++++++++++++++++++++++- 3 files changed, 71 insertions(+), 1 deletion(-) diff --git a/glanceclient/v2/shell.py b/glanceclient/v2/shell.py index 7f5660949..a77efd487 100644 --- a/glanceclient/v2/shell.py +++ b/glanceclient/v2/shell.py @@ -93,6 +93,9 @@ def do_image_update(gc, args): help='The status of images to display.') @utils.arg('--owner', metavar='', help='Display images owned by .') +@utils.arg('--property-filter', metavar='', + help="Filter images by a user-defined image property.", + action='append', dest='properties', default=[]) @utils.arg('--checksum', metavar='', help='Displays images that match the checksum.') @utils.arg('--tag', metavar='', action='append', @@ -101,6 +104,12 @@ def do_image_list(gc, args): """List images you can access.""" filter_keys = ['visibility', 'member_status', 'owner', 'checksum', 'tag'] filter_items = [(key, getattr(args, key)) for key in filter_keys] + if args.properties: + filter_properties = [prop.split('=', 1) for prop in args.properties] + if False in (len(pair) == 2 for pair in filter_properties): + utils.exit('Argument --property-filter expected properties in the' + ' format KEY=VALUE') + filter_items += filter_properties filters = dict([item for item in filter_items if item[1] is not None]) kwargs = {'filters': filters} diff --git a/tests/v2/test_images.py b/tests/v2/test_images.py index a314a24fa..b717eba9f 100644 --- a/tests/v2/test_images.py +++ b/tests/v2/test_images.py @@ -346,6 +346,25 @@ '', ) }, + '/v2/images?limit=%d&os_distro=NixOS' % images.DEFAULT_PAGE_SIZE: { + 'GET': ( + {}, + {'images': [ + { + 'id': '8b052954-c76c-4e02-8e90-be89a70183a8', + 'name': 'image-5', + 'os_distro': 'NixOS', + }, + ]}, + ), + }, + '/v2/images?limit=%d&my_little_property=cant_be_this_cute' % + images.DEFAULT_PAGE_SIZE: { + 'GET': ( + {}, + {'images': []}, + ), + }, } @@ -503,6 +522,17 @@ def test_list_images_for_non_existent_tag(self): images = list(self.controller.list(**filters)) self.assertEqual(0, len(images)) + def test_list_images_for_property(self): + filters = {'filters': dict([('os_distro', 'NixOS')])} + images = list(self.controller.list(**filters)) + self.assertEqual(1, len(images)) + + def test_list_images_for_non_existent_property(self): + filters = {'filters': dict([('my_little_property', + 'cant_be_this_cute')])} + images = list(self.controller.list(**filters)) + self.assertEqual(0, len(images)) + def test_get_image(self): image = self.controller.get('3a4560a1-e585-443e-9b39-553b46ec92d1') self.assertEqual('3a4560a1-e585-443e-9b39-553b46ec92d1', image.id) diff --git a/tests/v2/test_shell_v2.py b/tests/v2/test_shell_v2.py index 6d1e32730..0dd4fc33d 100644 --- a/tests/v2/test_shell_v2.py +++ b/tests/v2/test_shell_v2.py @@ -64,7 +64,8 @@ def test_do_image_list(self): 'member_status': 'Fake', 'owner': 'test', 'checksum': 'fake_checksum', - 'tag': 'fake tag' + 'tag': 'fake tag', + 'properties': [] } args = self._make_args(input) with mock.patch.object(self.gc.images, 'list') as mocked_list: @@ -83,6 +84,36 @@ def test_do_image_list(self): filters=exp_img_filters) utils.print_list.assert_called_once_with({}, ['ID', 'Name']) + def test_do_image_list_with_property_filter(self): + input = { + 'page_size': 1, + 'visibility': True, + 'member_status': 'Fake', + 'owner': 'test', + 'checksum': 'fake_checksum', + 'tag': 'fake tag', + 'properties': ['os_distro=NixOS', 'architecture=x86_64'] + } + 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', + 'os_distro': 'NixOS', + 'architecture': 'x86_64' + } + + mocked_list.assert_called_once_with(page_size=1, + filters=exp_img_filters) + utils.print_list.assert_called_once_with({}, ['ID', 'Name']) + def test_do_image_show(self): args = self._make_args({'id': 'pass', 'page_size': 18, 'max_column_width': 120})