summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--cinderclient/api_versions.py2
-rw-r--r--cinderclient/shell_utils.py25
-rw-r--r--cinderclient/tests/unit/test_utils.py17
-rw-r--r--cinderclient/tests/unit/v3/fakes.py6
-rw-r--r--cinderclient/tests/unit/v3/test_resource_filters.py32
-rw-r--r--cinderclient/tests/unit/v3/test_shell.py98
-rw-r--r--cinderclient/tests/unit/v3/test_volumes.py13
-rw-r--r--cinderclient/v3/client.py2
-rw-r--r--cinderclient/v3/resource_filters.py37
-rw-r--r--cinderclient/v3/shell.py268
-rw-r--r--cinderclient/v3/volumes.py20
-rw-r--r--releasenotes/notes/support-generialized-resource-filter-8yf6w23f66bf5903.yaml14
-rwxr-xr-xtools/lintstack.py3
13 files changed, 512 insertions, 25 deletions
diff --git a/cinderclient/api_versions.py b/cinderclient/api_versions.py
index dee7c78..d1bb1e1 100644
--- a/cinderclient/api_versions.py
+++ b/cinderclient/api_versions.py
@@ -29,7 +29,7 @@ LOG = logging.getLogger(__name__)
# key is a deprecated version and value is an alternative version.
DEPRECATED_VERSIONS = {"1": "2"}
DEPRECATED_VERSION = "2.0"
-MAX_VERSION = "3.28"
+MAX_VERSION = "3.33"
MIN_VERSION = "3.0"
_SUBSTITUTIONS = {}
diff --git a/cinderclient/shell_utils.py b/cinderclient/shell_utils.py
index e386558..949f7f7 100644
--- a/cinderclient/shell_utils.py
+++ b/cinderclient/shell_utils.py
@@ -143,6 +143,26 @@ def translate_availability_zone_keys(collection):
translate_keys(collection, convert)
+def extract_filters(args):
+ filters = {}
+ for f in args:
+ if '=' in f:
+ (key, value) = f.split('=', 1)
+ if value.startswith('{') and value.endswith('}'):
+ value = _build_internal_dict(value[1:-1])
+ filters[key] = value
+
+ return filters
+
+
+def _build_internal_dict(content):
+ result = {}
+ for pair in content.split(','):
+ k, v = pair.split(':', 1)
+ result.update({k.strip(): v.strip()})
+ return result
+
+
def extract_metadata(args, type='user_metadata'):
metadata = {}
if type == 'image_metadata':
@@ -169,6 +189,11 @@ def print_group_type_list(gtypes):
utils.print_list(gtypes, ['ID', 'Name', 'Description'])
+def print_resource_filter_list(filters):
+ formatter = {'Filters': lambda resource: ', '.join(resource.filters)}
+ utils.print_list(filters, ['Resource', 'Filters'], formatters=formatter)
+
+
def quota_show(quotas):
quotas_info_dict = utils.unicode_key_value_to_string(quotas._info)
quota_dict = {}
diff --git a/cinderclient/tests/unit/test_utils.py b/cinderclient/tests/unit/test_utils.py
index a62425e..eeb4800 100644
--- a/cinderclient/tests/unit/test_utils.py
+++ b/cinderclient/tests/unit/test_utils.py
@@ -12,6 +12,7 @@
# limitations under the License.
import collections
+import ddt
import sys
import mock
@@ -21,6 +22,7 @@ import six
from cinderclient import api_versions
from cinderclient.apiclient import base as common_base
from cinderclient import exceptions
+from cinderclient import shell_utils
from cinderclient import utils
from cinderclient import base
from cinderclient.tests.unit import utils as test_utils
@@ -187,6 +189,21 @@ class BuildQueryParamTestCase(test_utils.TestCase):
self.assertFalse(result_2)
+@ddt.ddt
+class ExtractFilterTestCase(test_utils.TestCase):
+
+ @ddt.data({'content': ['key1=value1'],
+ 'expected': {'key1': 'value1'}},
+ {'content': ['key1={key2:value2}'],
+ 'expected': {'key1': {'key2': 'value2'}}},
+ {'content': ['key1=value1', 'key2={key22:value22}'],
+ 'expected': {'key1': 'value1', 'key2': {'key22': 'value22'}}})
+ @ddt.unpack
+ def test_extract_filters(self, content, expected):
+ result = shell_utils.extract_filters(content)
+ self.assertEqual(expected, result)
+
+
class PrintListTestCase(test_utils.TestCase):
def test_print_list_with_list(self):
diff --git a/cinderclient/tests/unit/v3/fakes.py b/cinderclient/tests/unit/v3/fakes.py
index 6a62477..25a8151 100644
--- a/cinderclient/tests/unit/v3/fakes.py
+++ b/cinderclient/tests/unit/v3/fakes.py
@@ -544,6 +544,12 @@ class FakeHTTPClient(fake_v2.FakeHTTPClient):
}
return 200, {}, {'message': message}
+ #
+ # resource filters
+ #
+ def get_resource_filters(self, **kw):
+ return 200, {}, {'resource_filters': []}
+
def fake_request_get():
versions = {'versions': [{'id': 'v1.0',
diff --git a/cinderclient/tests/unit/v3/test_resource_filters.py b/cinderclient/tests/unit/v3/test_resource_filters.py
new file mode 100644
index 0000000..3b14124
--- /dev/null
+++ b/cinderclient/tests/unit/v3/test_resource_filters.py
@@ -0,0 +1,32 @@
+# Licensed under the Apache License, Version 2.0 (the "License"); you may
+# not use this file except in compliance with the License. You may obtain
+# a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
+# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
+# License for the specific language governing permissions and limitations
+# under the License.
+
+import ddt
+
+from cinderclient.tests.unit import utils
+from cinderclient.tests.unit.v3 import fakes
+
+cs = fakes.FakeClient()
+
+
+@ddt.ddt
+class ResourceFilterTests(utils.TestCase):
+ @ddt.data({'resource': None, 'query_url': None},
+ {'resource': 'volume', 'query_url': '?resource=volume'},
+ {'resource': 'group', 'query_url': '?resource=group'})
+ @ddt.unpack
+ def test_list_messages(self, resource, query_url):
+ cs.resource_filters.list(resource)
+ url = '/resource_filters'
+ if resource is not None:
+ url += query_url
+ cs.assert_called('GET', url)
diff --git a/cinderclient/tests/unit/v3/test_shell.py b/cinderclient/tests/unit/v3/test_shell.py
index 08bd2c2..c13cdb2 100644
--- a/cinderclient/tests/unit/v3/test_shell.py
+++ b/cinderclient/tests/unit/v3/test_shell.py
@@ -66,6 +66,104 @@ class ShellTest(utils.TestCase):
return self.shell.cs.assert_called(method, url, body,
partial_body, **kwargs)
+ @ddt.data({'resource': None, 'query_url': None},
+ {'resource': 'volume', 'query_url': '?resource=volume'},
+ {'resource': 'group', 'query_url': '?resource=group'})
+ @ddt.unpack
+ def test_list_filters(self, resource, query_url):
+ url = '/resource_filters'
+ if resource is not None:
+ url += query_url
+ self.run_command('--os-volume-api-version 3.33 '
+ 'list-filters --resource=%s' % resource)
+ else:
+ self.run_command('--os-volume-api-version 3.33 list-filters')
+
+ self.assert_called('GET', url)
+
+ @ddt.data(
+ # testcases for list volume
+ {'command':
+ 'list --name=123 --filters name=456',
+ 'expected':
+ '/volumes/detail?name=456'},
+ {'command':
+ 'list --filters name=123',
+ 'expected':
+ '/volumes/detail?name=123'},
+ {'command':
+ 'list --filters metadata={key1:value1}',
+ 'expected':
+ '/volumes/detail?metadata=%7B%27key1%27%3A+%27value1%27%7D'},
+ # testcases for list group
+ {'command':
+ 'group-list --filters name=456',
+ 'expected':
+ '/groups/detail?name=456'},
+ {'command':
+ 'group-list --filters status=available',
+ 'expected':
+ '/groups/detail?status=available'},
+ # testcases for list group-snapshot
+ {'command':
+ 'group-snapshot-list --status=error --filters status=available',
+ 'expected':
+ '/group_snapshots/detail?status=available'},
+ {'command':
+ 'group-snapshot-list --filters availability_zone=123',
+ 'expected':
+ '/group_snapshots/detail?availability_zone=123'},
+ # testcases for list message
+ {'command':
+ 'message-list --event_id=123 --filters event_id=456',
+ 'expected':
+ '/messages?event_id=456'},
+ {'command':
+ 'message-list --filters request_id=123',
+ 'expected':
+ '/messages?request_id=123'},
+ # testcases for list attachment
+ {'command':
+ 'attachment-list --volume-id=123 --filters volume_id=456',
+ 'expected':
+ '/attachments?volume_id=456'},
+ {'command':
+ 'attachment-list --filters mountpoint=123',
+ 'expected':
+ '/attachments?mountpoint=123'},
+ # testcases for list backup
+ {'command':
+ 'backup-list --volume-id=123 --filters volume_id=456',
+ 'expected':
+ '/backups/detail?volume_id=456'},
+ {'command':
+ 'backup-list --filters name=123',
+ 'expected':
+ '/backups/detail?name=123'},
+ # testcases for list snapshot
+ {'command':
+ 'snapshot-list --volume-id=123 --filters volume_id=456',
+ 'expected':
+ '/snapshots/detail?volume_id=456'},
+ {'command':
+ 'snapshot-list --filters name=123',
+ 'expected':
+ '/snapshots/detail?name=123'},
+ # testcases for get pools
+ {'command':
+ 'get-pools --filters name=456 --detail',
+ 'expected':
+ '/scheduler-stats/get_pools?detail=True&name=456'},
+ {'command':
+ 'get-pools --filters name=456',
+ 'expected':
+ '/scheduler-stats/get_pools?name=456'}
+ )
+ @ddt.unpack
+ def test_list_with_filters_mixed(self, command, expected):
+ self.run_command('--os-volume-api-version 3.33 %s' % command)
+ self.assert_called('GET', expected)
+
def test_list(self):
self.run_command('list')
# NOTE(jdg): we default to detail currently
diff --git a/cinderclient/tests/unit/v3/test_volumes.py b/cinderclient/tests/unit/v3/test_volumes.py
index 4613f37..f6164a5 100644
--- a/cinderclient/tests/unit/v3/test_volumes.py
+++ b/cinderclient/tests/unit/v3/test_volumes.py
@@ -15,6 +15,8 @@
# License for the specific language governing permissions and limitations
# under the License.
+import ddt
+
from cinderclient import api_versions
from cinderclient.tests.unit import utils
from cinderclient.tests.unit.v3 import fakes
@@ -25,6 +27,7 @@ from six.moves.urllib import parse
cs = fakes.FakeClient()
+@ddt.data
class VolumesTest(utils.TestCase):
def test_volume_manager_upload_to_image(self):
@@ -100,3 +103,13 @@ class VolumesTest(utils.TestCase):
expected = ("/volumes/detail?glance_metadata=%s"
% parse.quote_plus("{'key1': 'val1'}"))
cs.assert_called('GET', expected)
+
+ @ddt.data(True, False)
+ def test_get_pools_filter_by_name(self, detail):
+ cs = fakes.FakeClient(api_version=api_versions.APIVersion('3.33'))
+ vol = cs.volumes.get_pools(detail, 'pool1')
+ request_url = '/scheduler-stats/get_pools?name=pool1'
+ if detail:
+ request_url = '/scheduler-stats/get_pools?detail=True&name=pool1'
+ cs.assert_called('GET', request_url)
+ self._assert_request_id(vol)
diff --git a/cinderclient/v3/client.py b/cinderclient/v3/client.py
index 0d4bb86..ef242f4 100644
--- a/cinderclient/v3/client.py
+++ b/cinderclient/v3/client.py
@@ -32,6 +32,7 @@ from cinderclient.v3 import pools
from cinderclient.v3 import qos_specs
from cinderclient.v3 import quota_classes
from cinderclient.v3 import quotas
+from cinderclient.v3 import resource_filters
from cinderclient.v3 import services
from cinderclient.v3 import volumes
from cinderclient.v3 import volume_snapshots
@@ -85,6 +86,7 @@ class Client(object):
self.quotas = quotas.QuotaSetManager(self)
self.backups = volume_backups.VolumeBackupManager(self)
self.messages = messages.MessageManager(self)
+ self.resource_filters = resource_filters.ResourceFilterManager(self)
self.restores = volume_backups_restore.VolumeBackupRestoreManager(self)
self.transfers = volume_transfers.VolumeTransferManager(self)
self.services = services.ServiceManager(self)
diff --git a/cinderclient/v3/resource_filters.py b/cinderclient/v3/resource_filters.py
new file mode 100644
index 0000000..c726f8c
--- /dev/null
+++ b/cinderclient/v3/resource_filters.py
@@ -0,0 +1,37 @@
+# Licensed under the Apache License, Version 2.0 (the "License"); you may
+# not use this file except in compliance with the License. You may obtain
+# a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
+# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
+# License for the specific language governing permissions and limitations
+# under the License.
+
+"""Resource filters interface."""
+
+from cinderclient import base
+from cinderclient import api_versions
+
+
+class ResourceFilter(base.Resource):
+ NAME_ATTR = 'resource'
+
+ def __repr__(self):
+ return "<ResourceFilter: %s>" % self.resource
+
+
+class ResourceFilterManager(base.ManagerWithFind):
+ """Manage :class:`ResourceFilter` resources."""
+
+ resource_class = ResourceFilter
+
+ @api_versions.wraps('3.33')
+ def list(self, resource=None):
+ """List all resource filters."""
+ url = '/resource_filters'
+ if resource is not None:
+ url += '?resource=%s' % resource
+ return self._list(url, "resource_filters")
diff --git a/cinderclient/v3/shell.py b/cinderclient/v3/shell.py
index 6348e33..7bc8c29 100644
--- a/cinderclient/v3/shell.py
+++ b/cinderclient/v3/shell.py
@@ -32,6 +32,136 @@ from cinderclient import utils
from cinderclient.v2.shell import * # flake8: noqa
+FILTER_DEPRECATED = ("This option is deprecated and will be removed in "
+ "newer release. Please use '--filters' option which "
+ "is introduced since 3.33 instead.")
+
+
+@api_versions.wraps('3.33')
+@utils.arg('--resource',
+ metavar='<resource>',
+ default=None,
+ help='Show enabled filters for specified resource. Default=None.')
+def do_list_filters(cs, args):
+ filters = cs.resource_filters.list(resource=args.resource)
+ shell_utils.print_resource_filter_list(filters)
+
+
+@utils.arg('--all-tenants',
+ metavar='<all_tenants>',
+ nargs='?',
+ type=int,
+ const=1,
+ default=0,
+ help='Shows details for all tenants. Admin only.')
+@utils.arg('--all_tenants',
+ nargs='?',
+ type=int,
+ const=1,
+ help=argparse.SUPPRESS)
+@utils.arg('--name',
+ metavar='<name>',
+ default=None,
+ help="Filters results by a name. Default=None. "
+ "%s" % FILTER_DEPRECATED)
+@utils.arg('--status',
+ metavar='<status>',
+ default=None,
+ help="Filters results by a status. Default=None. "
+ "%s" % FILTER_DEPRECATED)
+@utils.arg('--volume-id',
+ metavar='<volume-id>',
+ default=None,
+ help="Filters results by a volume ID. Default=None. "
+ "%s" % FILTER_DEPRECATED)
+@utils.arg('--volume_id',
+ help=argparse.SUPPRESS)
+@utils.arg('--marker',
+ metavar='<marker>',
+ default=None,
+ help='Begin returning backups that appear later in the backup '
+ 'list than that represented by this id. '
+ 'Default=None.')
+@utils.arg('--limit',
+ metavar='<limit>',
+ default=None,
+ help='Maximum number of backups to return. Default=None.')
+@utils.arg('--sort',
+ metavar='<key>[:<direction>]',
+ default=None,
+ help=(('Comma-separated list of sort keys and directions in the '
+ 'form of <key>[:<asc|desc>]. '
+ 'Valid keys: %s. '
+ 'Default=None.') % ', '.join(base.SORT_KEY_VALUES)))
+@utils.arg('--filters',
+ type=str,
+ nargs='*',
+ start_version='3.33',
+ metavar='<key=value>',
+ default=None,
+ help="Filter key and value pairs. Please use 'cinder list-filters' "
+ "to check enabled filters from server, Default=None.")
+def do_backup_list(cs, args):
+ """Lists all backups."""
+ # pylint: disable=function-redefined
+
+ search_opts = {
+ 'all_tenants': args.all_tenants,
+ 'name': args.name,
+ 'status': args.status,
+ 'volume_id': args.volume_id,
+ }
+
+ # Update search option with `filters`
+ if hasattr(args, 'filters') and args.filters is not None:
+ search_opts.update(shell_utils.extract_filters(args.filters))
+
+ backups = cs.backups.list(search_opts=search_opts,
+ marker=args.marker,
+ limit=args.limit,
+ sort=args.sort)
+ shell_utils.translate_volume_snapshot_keys(backups)
+ columns = ['ID', 'Volume ID', 'Status', 'Name', 'Size', 'Object Count',
+ 'Container']
+ if args.sort:
+ sortby_index = None
+ else:
+ sortby_index = 0
+ utils.print_list(backups, columns, sortby_index=sortby_index)
+
+
+@utils.arg('--detail',
+ action='store_true',
+ help='Show detailed information about pools.')
+@utils.arg('--filters',
+ type=str,
+ nargs='*',
+ start_version='3.33',
+ metavar='<key=value>',
+ default=None,
+ help="Filter key and value pairs. Please use 'cinder list-filters' "
+ "to check enabled filters from server, Default=None.")
+def do_get_pools(cs, args):
+ """Show pool information for backends. Admin only."""
+ # pylint: disable=function-redefined
+ search_opts = {}
+ # Update search option with `filters`
+ if hasattr(args, 'filters') and args.filters is not None:
+ search_opts.update(shell_utils.extract_filters(args.filters))
+ if cs.api_version >= api_versions.APIVersion("3.33"):
+ pools = cs.volumes.get_pools(args.detail, search_opts)
+ else:
+ pools = cs.volumes.get_pools(args.detail)
+ infos = dict()
+ infos.update(pools._info)
+
+ for info in infos['pools']:
+ backend = dict()
+ backend['name'] = info['name']
+ if args.detail:
+ backend.update(info['capabilities'])
+ utils.print_dict(backend)
+
RESET_STATE_RESOURCES = {'volume': utils.find_volume,
'backup': shell_utils.find_backup,
@@ -43,7 +173,8 @@ RESET_STATE_RESOURCES = {'volume': utils.find_volume,
@utils.arg('--group_id',
metavar='<group_id>',
default=None,
- help='Filters results by a group_id. Default=None.',
+ help="Filters results by a group_id. Default=None."
+ "%s" % FILTER_DEPRECATED,
start_version='3.10')
@utils.arg('--all-tenants',
dest='all_tenants',
@@ -61,37 +192,43 @@ RESET_STATE_RESOURCES = {'volume': utils.find_volume,
@utils.arg('--name',
metavar='<name>',
default=None,
- help='Filters results by a name. Default=None.')
+ help="Filters results by a name. Default=None. "
+ "%s" % FILTER_DEPRECATED)
@utils.arg('--display-name',
help=argparse.SUPPRESS)
@utils.arg('--status',
metavar='<status>',
default=None,
- help='Filters results by a status. Default=None.')
+ help="Filters results by a status. Default=None. "
+ "%s" % FILTER_DEPRECATED)
@utils.arg('--bootable',
metavar='<True|true|False|false>',
const=True,
nargs='?',
choices=['True', 'true', 'False', 'false'],
- help='Filters results by bootable status. Default=None.')
+ help="Filters results by bootable status. Default=None. "
+ "%s" % FILTER_DEPRECATED)
@utils.arg('--migration_status',
metavar='<migration_status>',
default=None,
- help='Filters results by a migration status. Default=None. '
- 'Admin only.')
+ help="Filters results by a migration status. Default=None. "
+ "Admin only. "
+ "%s" % FILTER_DEPRECATED)
@utils.arg('--metadata',
nargs='*',
metavar='<key=value>',
default=None,
- help='Filters results by a metadata key and value pair. '
- 'Default=None.')
+ help="Filters results by a metadata key and value pair. "
+ "Default=None. "
+ "%s" % FILTER_DEPRECATED)
@utils.arg('--image_metadata',
nargs='*',
metavar='<key=value>',
default=None,
start_version='3.4',
- help='Filters results by a image metadata key and value pair. '
- 'Require volume api version >=3.4. Default=None.')
+ help="Filters results by a image metadata key and value pair. "
+ "Require volume api version >=3.4. Default=None."
+ "%s" % FILTER_DEPRECATED)
@utils.arg('--marker',
metavar='<marker>',
default=None,
@@ -130,8 +267,17 @@ RESET_STATE_RESOURCES = {'volume': utils.find_volume,
nargs='?',
metavar='<tenant>',
help='Display information from single tenant (Admin only).')
+@utils.arg('--filters',
+ type=str,
+ nargs='*',
+ start_version='3.33',
+ metavar='<key=value>',
+ default=None,
+ help="Filter key and value pairs. Please use 'cinder list-filters' "
+ "to check enabled filters from server, Default=None.")
def do_list(cs, args):
"""Lists all volumes."""
+ # pylint: disable=function-redefined
# NOTE(thingee): Backwards-compatibility with v1 args
if args.display_name is not None:
args.name = args.display_name
@@ -152,6 +298,9 @@ def do_list(cs, args):
if hasattr(args, 'image_metadata') and args.image_metadata else None,
'group_id': getattr(args, 'group_id', None),
}
+ # Update search option with `filters`
+ if hasattr(args, 'filters') and args.filters is not None:
+ search_opts.update(shell_utils.extract_filters(args.filters))
# If unavailable/non-existent fields are specified, these fields will
# be removed from key_list at the print_list() during key validation.
@@ -820,10 +969,22 @@ def do_manageable_list(cs, args):
const=1,
default=utils.env('ALL_TENANTS', default=0),
help='Shows details for all tenants. Admin only.')
+@utils.arg('--filters',
+ type=str,
+ nargs='*',
+ start_version='3.33',
+ metavar='<key=value>',
+ default=None,
+ help="Filter key and value pairs. Please use 'cinder list-filters' "
+ "to check enabled filters from server, Default=None.")
def do_group_list(cs, args):
"""Lists all groups."""
search_opts = {'all_tenants': args.all_tenants}
+ # Update search option with `filters`
+ if hasattr(args, 'filters') and args.filters is not None:
+ search_opts.update(shell_utils.extract_filters(args.filters))
+
groups = cs.groups.list(search_opts=search_opts)
columns = ['ID', 'Status', 'Name']
@@ -1003,11 +1164,21 @@ def do_group_update(cs, args):
@utils.arg('--status',
metavar='<status>',
default=None,
- help='Filters results by a status. Default=None.')
+ help="Filters results by a status. Default=None. "
+ "%s" % FILTER_DEPRECATED)
@utils.arg('--group-id',
metavar='<group_id>',
default=None,
- help='Filters results by a group ID. Default=None.')
+ help="Filters results by a group ID. Default=None. "
+ "%s" % FILTER_DEPRECATED)
+@utils.arg('--filters',
+ type=str,
+ nargs='*',
+ start_version='3.33',
+ metavar='<key=value>',
+ default=None,
+ help="Filter key and value pairs. Please use 'cinder list-filters' "
+ "to check enabled filters from server, Default=None.")
def do_group_snapshot_list(cs, args):
"""Lists all group snapshots."""
@@ -1018,6 +1189,9 @@ def do_group_snapshot_list(cs, args):
'status': args.status,
'group_id': args.group_id,
}
+ # Update search option with `filters`
+ if hasattr(args, 'filters') and args.filters is not None:
+ search_opts.update(shell_utils.extract_filters(args.filters))
group_snapshots = cs.group_snapshots.list(search_opts=search_opts)
@@ -1200,23 +1374,36 @@ def do_api_version(cs, args):
@utils.arg('--resource_uuid',
metavar='<resource_uuid>',
default=None,
- help='Filters results by a resource uuid. Default=None.')
+ help="Filters results by a resource uuid. Default=None. "
+ "%s" % FILTER_DEPRECATED)
@utils.arg('--resource_type',
metavar='<type>',
default=None,
- help='Filters results by a resource type. Default=None.')
+ help="Filters results by a resource type. Default=None. "
+ "%s" % FILTER_DEPRECATED)
@utils.arg('--event_id',
metavar='<id>',
default=None,
- help='Filters results by event id. Default=None.')
+ help="Filters results by event id. Default=None. "
+ "%s" % FILTER_DEPRECATED)
@utils.arg('--request_id',
metavar='<request_id>',
default=None,
- help='Filters results by request id. Default=None.')
+ help="Filters results by request id. Default=None. "
+ "%s" % FILTER_DEPRECATED)
@utils.arg('--level',
metavar='<level>',
default=None,
- help='Filters results by the message level. Default=None.')
+ help="Filters results by the message level. Default=None. "
+ "%s" % FILTER_DEPRECATED)
+@utils.arg('--filters',
+ type=str,
+ nargs='*',
+ start_version='3.33',
+ metavar='<key=value>',
+ default=None,
+ help="Filter key and value pairs. Please use 'cinder list-filters' "
+ "to check enabled filters from server, Default=None.")
def do_message_list(cs, args):
"""Lists all messages."""
search_opts = {
@@ -1224,6 +1411,9 @@ def do_message_list(cs, args):
'event_id': args.event_id,
'request_id': args.request_id,
}
+ # Update search option with `filters`
+ if hasattr(args, 'filters') and args.filters is not None:
+ search_opts.update(shell_utils.extract_filters(args.filters))
if args.resource_type:
search_opts['resource_type'] = args.resource_type.upper()
if args.level:
@@ -1294,7 +1484,8 @@ def do_message_delete(cs, args):
@utils.arg('--name',
metavar='<name>',
default=None,
- help='Filters results by a name. Default=None.')
+ help="Filters results by a name. Default=None. "
+ "%s" % FILTER_DEPRECATED)
@utils.arg('--display-name',
help=argparse.SUPPRESS)
@utils.arg('--display_name',
@@ -1302,11 +1493,13 @@ def do_message_delete(cs, args):
@utils.arg('--status',
metavar='<status>',
default=None,
- help='Filters results by a status. Default=None.')
+ help="Filters results by a status. Default=None. "
+ "%s" % FILTER_DEPRECATED)
@utils.arg('--volume-id',
metavar='<volume-id>',
default=None,
- help='Filters results by a volume ID. Default=None.')
+ help="Filters results by a volume ID. Default=None. "
+ "%s" % FILTER_DEPRECATED)
@utils.arg('--volume_id',
help=argparse.SUPPRESS)
@utils.arg('--marker',
@@ -1337,10 +1530,20 @@ def do_message_delete(cs, args):
metavar='<key=value>',
default=None,
start_version='3.22',
- help='Filters results by a metadata key and value pair. Require '
- 'volume api version >=3.22. Default=None.')
+ help="Filters results by a metadata key and value pair. Require "
+ "volume api version >=3.22. Default=None. "
+ "%s" % FILTER_DEPRECATED)
+@utils.arg('--filters',
+ type=str,
+ nargs='*',
+ start_version='3.33',
+ metavar='<key=value>',
+ default=None,
+ help="Filter key and value pairs. Please use 'cinder list-filters' "
+ "to check enabled filters from server, Default=None.")
def do_snapshot_list(cs, args):
"""Lists all snapshots."""
+ # pylint: disable=function-redefined
all_tenants = (1 if args.tenant else
int(os.environ.get("ALL_TENANTS", args.all_tenants)))
@@ -1363,6 +1566,10 @@ def do_snapshot_list(cs, args):
'metadata': metadata
}
+ # Update search option with `filters`
+ if hasattr(args, 'filters') and args.filters is not None:
+ search_opts.update(shell_utils.extract_filters(args.filters))
+
snapshots = cs.volume_snapshots.list(search_opts=search_opts,
marker=args.marker,
limit=args.limit,
@@ -1386,11 +1593,13 @@ def do_snapshot_list(cs, args):
@utils.arg('--volume-id',
metavar='<volume-id>',
default=None,
- help='Filters results by a volume ID. Default=None.')
+ help="Filters results by a volume ID. Default=None. "
+ "%s" % FILTER_DEPRECATED)
@utils.arg('--status',
metavar='<status>',
default=None,
- help='Filters results by a status. Default=None.')
+ help="Filters results by a status. Default=None. "
+ "%s" % FILTER_DEPRECATED)
@utils.arg('--marker',
metavar='<marker>',
default=None,
@@ -1414,6 +1623,14 @@ def do_snapshot_list(cs, args):
nargs='?',
metavar='<tenant>',
help='Display information from single tenant (Admin only).')
+@utils.arg('--filters',
+ type=str,
+ nargs='*',
+ start_version='3.33',
+ metavar='<key=value>',
+ default=None,
+ help="Filter key and value pairs. Please use 'cinder list-filters' "
+ "to check enabled filters from server, Default=None.")
def do_attachment_list(cs, args):
"""Lists all attachments."""
search_opts = {
@@ -1422,6 +1639,9 @@ def do_attachment_list(cs, args):
'status': args.status,
'volume_id': args.volume_id,
}
+ # Update search option with `filters`
+ if hasattr(args, 'filters') and args.filters is not None:
+ search_opts.update(shell_utils.extract_filters(args.filters))
attachments = cs.attachments.list(search_opts=search_opts,
marker=args.marker,
diff --git a/cinderclient/v3/volumes.py b/cinderclient/v3/volumes.py
index c786adb..2224489 100644
--- a/cinderclient/v3/volumes.py
+++ b/cinderclient/v3/volumes.py
@@ -176,3 +176,23 @@ class VolumeManager(volumes.VolumeManager):
search_opts={'host': host}, marker=marker,
limit=limit, offset=offset, sort=sort)
return self._list(url, "manageable-volumes")
+
+ @api_versions.wraps("2.0", "3.32")
+ def get_pools(self, detail):
+ """Show pool information for backends."""
+ query_string = ""
+ if detail:
+ query_string = "?detail=True"
+
+ return self._get('/scheduler-stats/get_pools%s' % query_string, None)
+
+ @api_versions.wraps("3.33")
+ def get_pools(self, detail, search_opts):
+ """Show pool information for backends."""
+ # pylint: disable=function-redefined
+ options = {'detail': detail}
+ options.update(search_opts)
+ url = self._build_list_url('scheduler-stats/get_pools', detailed=False,
+ search_opts=options)
+
+ return self._get(url, None)
diff --git a/releasenotes/notes/support-generialized-resource-filter-8yf6w23f66bf5903.yaml b/releasenotes/notes/support-generialized-resource-filter-8yf6w23f66bf5903.yaml
new file mode 100644
index 0000000..fdfb4c0
--- /dev/null
+++ b/releasenotes/notes/support-generialized-resource-filter-8yf6w23f66bf5903.yaml
@@ -0,0 +1,14 @@
+---
+features:
+ - |
+ Added new command ``list-filters`` to retrieve enabled resource filters,
+ Added new option ``--filters`` to these list commands:
+
+ - list
+ - snapshot-list
+ - backup-list
+ - group-list
+ - group-snapshot-list
+ - attachment-list
+ - message-list
+ - get-pools
diff --git a/tools/lintstack.py b/tools/lintstack.py
index acde2b0..34ca056 100755
--- a/tools/lintstack.py
+++ b/tools/lintstack.py
@@ -43,6 +43,9 @@ ignore_messages = [
# six.moves
"Instance of '_MovedItems' has no 'builtins' member",
+
+ # This error message is for code [E1101]
+ "Instance of 'ResourceFilterManager' has no '_list' member",
]
ignore_modules = ["cinderclient/tests/"]