diff --git a/cinderclient/base.py b/cinderclient/base.py index e1504ee2f..88ab9511c 100644 --- a/cinderclient/base.py +++ b/cinderclient/base.py @@ -403,9 +403,14 @@ class ManagerWithFind(six.with_metaclass(abc.ABCMeta, Manager)): search_opts['display_name'] = kwargs['display_name'] found = common_base.ListWithMeta([], None) + # list_volume is used for group query, it's not resource's property. + list_volume = kwargs.pop('list_volume', False) searches = kwargs.items() - - listing = self.list(search_opts=search_opts) + if list_volume: + listing = self.list(search_opts=search_opts, + list_volume=list_volume) + else: + listing = self.list(search_opts=search_opts) found.append_request_ids(listing.request_ids) # Not all resources attributes support filters on server side # (e.g. 'human_id' doesn't), so when doing findall some client diff --git a/cinderclient/shell_utils.py b/cinderclient/shell_utils.py index e38655881..7bd96e524 100644 --- a/cinderclient/shell_utils.py +++ b/cinderclient/shell_utils.py @@ -85,9 +85,10 @@ def find_consistencygroup(cs, consistencygroup): return utils.find_resource(cs.consistencygroups, consistencygroup) -def find_group(cs, group): +def find_group(cs, group, **kwargs): """Gets a group by name or ID.""" - return utils.find_resource(cs.groups, group) + kwargs['is_group'] = True + return utils.find_resource(cs.groups, group, **kwargs) def find_cgsnapshot(cs, cgsnapshot): diff --git a/cinderclient/tests/unit/test_utils.py b/cinderclient/tests/unit/test_utils.py index a62425e4a..791b5862c 100644 --- a/cinderclient/tests/unit/test_utils.py +++ b/cinderclient/tests/unit/test_utils.py @@ -53,13 +53,13 @@ class FakeManager(base.ManagerWithFind): FakeResource('5678', {'name': '9876'}) ] - def get(self, resource_id): + def get(self, resource_id, **kwargs): for resource in self.resources: if resource.id == str(resource_id): return resource raise exceptions.NotFound(resource_id) - def list(self, search_opts): + def list(self, search_opts, **kwargs): return common_base.ListWithMeta(self.resources, fakes.REQUEST_ID) @@ -132,6 +132,18 @@ class FindResourceTestCase(test_utils.TestCase): output = utils.find_resource(display_manager, 'entity_three') self.assertEqual(display_manager.get('4242'), output) + def test_find_by_group_id(self): + output = utils.find_resource(self.manager, 1234, is_group=True, + list_volume=True) + self.assertEqual(self.manager.get('1234', list_volume=True), output) + + def test_find_by_group_name(self): + display_manager = FakeDisplayManager(None) + output = utils.find_resource(display_manager, 'entity_three', + is_group=True, list_volume=True) + self.assertEqual(display_manager.get('4242', list_volume=True), + output) + class CaptureStdout(object): """Context manager for capturing stdout from statements in its block.""" diff --git a/cinderclient/tests/unit/v3/test_groups.py b/cinderclient/tests/unit/v3/test_groups.py index 1946b55e9..72e72a006 100644 --- a/cinderclient/tests/unit/v3/test_groups.py +++ b/cinderclient/tests/unit/v3/test_groups.py @@ -101,6 +101,11 @@ class GroupsTest(utils.TestCase): cs.assert_called('GET', '/groups/detail?foo=bar') self._assert_request_id(lst) + def test_list_group_with_volume(self): + lst = cs.groups.list(list_volume=True) + cs.assert_called('GET', '/groups/detail?list_volume=True') + self._assert_request_id(lst) + def test_list_group_with_empty_search_opt(self): lst = cs.groups.list( search_opts={'foo': 'bar', 'abc': None} @@ -114,6 +119,12 @@ class GroupsTest(utils.TestCase): cs.assert_called('GET', '/groups/%s' % group_id) self._assert_request_id(grp) + def test_get_group_with_list_volume(self): + group_id = '1234' + grp = cs.groups.get(group_id, list_volume=True) + cs.assert_called('GET', '/groups/%s?list_volume=True' % group_id) + self._assert_request_id(grp) + def test_create_group_from_src_snap(self): grp = cs.groups.create_from_src('5678', None, name='group') expected = { diff --git a/cinderclient/tests/unit/v3/test_shell.py b/cinderclient/tests/unit/v3/test_shell.py index 08bd2c25f..a459c55aa 100644 --- a/cinderclient/tests/unit/v3/test_shell.py +++ b/cinderclient/tests/unit/v3/test_shell.py @@ -329,6 +329,11 @@ class ShellTest(utils.TestCase): 'group-show 1234') self.assert_called('GET', '/groups/1234') + def test_group_show_with_list_volume(self): + self.run_command('--os-volume-api-version 3.25 ' + 'group-show 1234 --list-volume') + self.assert_called('GET', '/groups/1234?list_volume=True') + @ddt.data(True, False) def test_group_delete(self, delete_vol): cmd = '--os-volume-api-version 3.13 group-delete 1234' diff --git a/cinderclient/utils.py b/cinderclient/utils.py index 2ee36958e..e1198a205 100644 --- a/cinderclient/utils.py +++ b/cinderclient/utils.py @@ -220,11 +220,14 @@ def print_dict(d, property="Property", formatters=None): _print(pt, property) -def find_resource(manager, name_or_id): +def find_resource(manager, name_or_id, **kwargs): """Helper for the _find_* methods.""" + is_group = kwargs.pop('is_group', False) # first try to get entity as integer id try: if isinstance(name_or_id, int) or name_or_id.isdigit(): + if is_group: + return manager.get(int(name_or_id), **kwargs) return manager.get(int(name_or_id)) except exceptions.NotFound: pass @@ -232,6 +235,8 @@ def find_resource(manager, name_or_id): # now try to get entity as uuid try: uuid.UUID(name_or_id) + if is_group: + return manager.get(name_or_id, **kwargs) return manager.get(name_or_id) except (ValueError, exceptions.NotFound): pass @@ -243,12 +248,18 @@ def find_resource(manager, name_or_id): try: resource = getattr(manager, 'resource_class', None) name_attr = resource.NAME_ATTR if resource else 'name' + if is_group: + kwargs[name_attr] = name_or_id + return manager.find(**kwargs) return manager.find(**{name_attr: name_or_id}) except exceptions.NotFound: pass # finally try to find entity by human_id try: + if is_group: + kwargs['human_id'] = name_or_id + return manager.find(**kwargs) return manager.find(human_id=name_or_id) except exceptions.NotFound: msg = "No %s with a name or ID of '%s' exists." % \ diff --git a/cinderclient/v3/groups.py b/cinderclient/v3/groups.py index 4bd5643b8..386a6a307 100644 --- a/cinderclient/v3/groups.py +++ b/cinderclient/v3/groups.py @@ -14,6 +14,7 @@ # under the License. """Group interface (v3 extension).""" +from six.moves.urllib import parse from cinderclient import api_versions from cinderclient import base @@ -106,20 +107,31 @@ class GroupManager(base.ManagerWithFind): "/groups/action", body=body) return common_base.DictWithMeta(body['group'], resp) - def get(self, group_id): + def get(self, group_id, **kwargs): """Get a group. :param group_id: The ID of the group to get. :rtype: :class:`Group` """ - return self._get("/groups/%s" % group_id, + query_params = utils.unicode_key_value_to_string(kwargs) + + query_string = "" + if query_params: + params = sorted(query_params.items(), key=lambda x: x[0]) + query_string = "?%s" % parse.urlencode(params) + + return self._get("/groups/%s" % group_id + query_string, "group") - def list(self, detailed=True, search_opts=None): + def list(self, detailed=True, search_opts=None, list_volume=False): """Lists all groups. :rtype: list of :class:`Group` """ + if list_volume: + if not search_opts: + search_opts = {} + search_opts['list_volume'] = True query_string = utils.build_query_param(search_opts) detail = "" diff --git a/cinderclient/v3/shell.py b/cinderclient/v3/shell.py index 4f85097f0..57586e447 100644 --- a/cinderclient/v3/shell.py +++ b/cinderclient/v3/shell.py @@ -831,13 +831,26 @@ def do_group_list(cs, args): @api_versions.wraps('3.13') +@utils.arg('--list-volume', + dest='list_volume', + metavar='', + nargs='?', + type=bool, + const=True, + default=False, + help='Shows volumes included in the group.', + start_version='3.25') @utils.arg('group', metavar='', help='Name or ID of a group.') def do_group_show(cs, args): """Shows details of a group.""" info = dict() - group = shell_utils.find_group(cs, args.group) + if getattr(args, 'list_volume', None): + group = shell_utils.find_group(cs, args.group, + list_volume=args.list_volume) + else: + group = shell_utils.find_group(cs, args.group) info.update(group._info) info.pop('links', None) diff --git a/releasenotes/notes/support-show-group-with-volume-ad820b8442e8a9e8.yaml b/releasenotes/notes/support-show-group-with-volume-ad820b8442e8a9e8.yaml new file mode 100644 index 000000000..9bed0f8dd --- /dev/null +++ b/releasenotes/notes/support-show-group-with-volume-ad820b8442e8a9e8.yaml @@ -0,0 +1,5 @@ +--- +features: + - | + Support show group with ``list-volume`` argument. + The command is : cinder group-show {group_id} --list-volume