Merge "Support generalized resource filter in client"
This commit is contained in:
commit
b9a968f96d
|
@ -29,7 +29,7 @@ LOG = logging.getLogger(__name__)
|
||||||
# key is a deprecated version and value is an alternative version.
|
# key is a deprecated version and value is an alternative version.
|
||||||
DEPRECATED_VERSIONS = {"1": "2"}
|
DEPRECATED_VERSIONS = {"1": "2"}
|
||||||
DEPRECATED_VERSION = "2.0"
|
DEPRECATED_VERSION = "2.0"
|
||||||
MAX_VERSION = "3.28"
|
MAX_VERSION = "3.33"
|
||||||
MIN_VERSION = "3.0"
|
MIN_VERSION = "3.0"
|
||||||
|
|
||||||
_SUBSTITUTIONS = {}
|
_SUBSTITUTIONS = {}
|
||||||
|
|
|
@ -143,6 +143,26 @@ def translate_availability_zone_keys(collection):
|
||||||
translate_keys(collection, convert)
|
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'):
|
def extract_metadata(args, type='user_metadata'):
|
||||||
metadata = {}
|
metadata = {}
|
||||||
if type == 'image_metadata':
|
if type == 'image_metadata':
|
||||||
|
@ -169,6 +189,11 @@ def print_group_type_list(gtypes):
|
||||||
utils.print_list(gtypes, ['ID', 'Name', 'Description'])
|
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):
|
def quota_show(quotas):
|
||||||
quotas_info_dict = utils.unicode_key_value_to_string(quotas._info)
|
quotas_info_dict = utils.unicode_key_value_to_string(quotas._info)
|
||||||
quota_dict = {}
|
quota_dict = {}
|
||||||
|
|
|
@ -12,6 +12,7 @@
|
||||||
# limitations under the License.
|
# limitations under the License.
|
||||||
|
|
||||||
import collections
|
import collections
|
||||||
|
import ddt
|
||||||
import sys
|
import sys
|
||||||
|
|
||||||
import mock
|
import mock
|
||||||
|
@ -21,6 +22,7 @@ import six
|
||||||
from cinderclient import api_versions
|
from cinderclient import api_versions
|
||||||
from cinderclient.apiclient import base as common_base
|
from cinderclient.apiclient import base as common_base
|
||||||
from cinderclient import exceptions
|
from cinderclient import exceptions
|
||||||
|
from cinderclient import shell_utils
|
||||||
from cinderclient import utils
|
from cinderclient import utils
|
||||||
from cinderclient import base
|
from cinderclient import base
|
||||||
from cinderclient.tests.unit import utils as test_utils
|
from cinderclient.tests.unit import utils as test_utils
|
||||||
|
@ -187,6 +189,21 @@ class BuildQueryParamTestCase(test_utils.TestCase):
|
||||||
self.assertFalse(result_2)
|
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):
|
class PrintListTestCase(test_utils.TestCase):
|
||||||
|
|
||||||
def test_print_list_with_list(self):
|
def test_print_list_with_list(self):
|
||||||
|
|
|
@ -544,6 +544,12 @@ class FakeHTTPClient(fake_v2.FakeHTTPClient):
|
||||||
}
|
}
|
||||||
return 200, {}, {'message': message}
|
return 200, {}, {'message': message}
|
||||||
|
|
||||||
|
#
|
||||||
|
# resource filters
|
||||||
|
#
|
||||||
|
def get_resource_filters(self, **kw):
|
||||||
|
return 200, {}, {'resource_filters': []}
|
||||||
|
|
||||||
|
|
||||||
def fake_request_get():
|
def fake_request_get():
|
||||||
versions = {'versions': [{'id': 'v1.0',
|
versions = {'versions': [{'id': 'v1.0',
|
||||||
|
|
|
@ -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)
|
|
@ -66,6 +66,104 @@ class ShellTest(utils.TestCase):
|
||||||
return self.shell.cs.assert_called(method, url, body,
|
return self.shell.cs.assert_called(method, url, body,
|
||||||
partial_body, **kwargs)
|
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):
|
def test_list(self):
|
||||||
self.run_command('list')
|
self.run_command('list')
|
||||||
# NOTE(jdg): we default to detail currently
|
# NOTE(jdg): we default to detail currently
|
||||||
|
|
|
@ -15,6 +15,8 @@
|
||||||
# License for the specific language governing permissions and limitations
|
# License for the specific language governing permissions and limitations
|
||||||
# under the License.
|
# under the License.
|
||||||
|
|
||||||
|
import ddt
|
||||||
|
|
||||||
from cinderclient import api_versions
|
from cinderclient import api_versions
|
||||||
from cinderclient.tests.unit import utils
|
from cinderclient.tests.unit import utils
|
||||||
from cinderclient.tests.unit.v3 import fakes
|
from cinderclient.tests.unit.v3 import fakes
|
||||||
|
@ -25,6 +27,7 @@ from six.moves.urllib import parse
|
||||||
cs = fakes.FakeClient()
|
cs = fakes.FakeClient()
|
||||||
|
|
||||||
|
|
||||||
|
@ddt.data
|
||||||
class VolumesTest(utils.TestCase):
|
class VolumesTest(utils.TestCase):
|
||||||
|
|
||||||
def test_volume_manager_upload_to_image(self):
|
def test_volume_manager_upload_to_image(self):
|
||||||
|
@ -100,3 +103,13 @@ class VolumesTest(utils.TestCase):
|
||||||
expected = ("/volumes/detail?glance_metadata=%s"
|
expected = ("/volumes/detail?glance_metadata=%s"
|
||||||
% parse.quote_plus("{'key1': 'val1'}"))
|
% parse.quote_plus("{'key1': 'val1'}"))
|
||||||
cs.assert_called('GET', expected)
|
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)
|
||||||
|
|
|
@ -32,6 +32,7 @@ from cinderclient.v3 import pools
|
||||||
from cinderclient.v3 import qos_specs
|
from cinderclient.v3 import qos_specs
|
||||||
from cinderclient.v3 import quota_classes
|
from cinderclient.v3 import quota_classes
|
||||||
from cinderclient.v3 import quotas
|
from cinderclient.v3 import quotas
|
||||||
|
from cinderclient.v3 import resource_filters
|
||||||
from cinderclient.v3 import services
|
from cinderclient.v3 import services
|
||||||
from cinderclient.v3 import volumes
|
from cinderclient.v3 import volumes
|
||||||
from cinderclient.v3 import volume_snapshots
|
from cinderclient.v3 import volume_snapshots
|
||||||
|
@ -85,6 +86,7 @@ class Client(object):
|
||||||
self.quotas = quotas.QuotaSetManager(self)
|
self.quotas = quotas.QuotaSetManager(self)
|
||||||
self.backups = volume_backups.VolumeBackupManager(self)
|
self.backups = volume_backups.VolumeBackupManager(self)
|
||||||
self.messages = messages.MessageManager(self)
|
self.messages = messages.MessageManager(self)
|
||||||
|
self.resource_filters = resource_filters.ResourceFilterManager(self)
|
||||||
self.restores = volume_backups_restore.VolumeBackupRestoreManager(self)
|
self.restores = volume_backups_restore.VolumeBackupRestoreManager(self)
|
||||||
self.transfers = volume_transfers.VolumeTransferManager(self)
|
self.transfers = volume_transfers.VolumeTransferManager(self)
|
||||||
self.services = services.ServiceManager(self)
|
self.services = services.ServiceManager(self)
|
||||||
|
|
|
@ -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")
|
|
@ -32,6 +32,136 @@ from cinderclient import utils
|
||||||
|
|
||||||
from cinderclient.v2.shell import * # flake8: noqa
|
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,
|
RESET_STATE_RESOURCES = {'volume': utils.find_volume,
|
||||||
'backup': shell_utils.find_backup,
|
'backup': shell_utils.find_backup,
|
||||||
|
@ -43,7 +173,8 @@ RESET_STATE_RESOURCES = {'volume': utils.find_volume,
|
||||||
@utils.arg('--group_id',
|
@utils.arg('--group_id',
|
||||||
metavar='<group_id>',
|
metavar='<group_id>',
|
||||||
default=None,
|
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')
|
start_version='3.10')
|
||||||
@utils.arg('--all-tenants',
|
@utils.arg('--all-tenants',
|
||||||
dest='all_tenants',
|
dest='all_tenants',
|
||||||
|
@ -61,37 +192,43 @@ RESET_STATE_RESOURCES = {'volume': utils.find_volume,
|
||||||
@utils.arg('--name',
|
@utils.arg('--name',
|
||||||
metavar='<name>',
|
metavar='<name>',
|
||||||
default=None,
|
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',
|
@utils.arg('--display-name',
|
||||||
help=argparse.SUPPRESS)
|
help=argparse.SUPPRESS)
|
||||||
@utils.arg('--status',
|
@utils.arg('--status',
|
||||||
metavar='<status>',
|
metavar='<status>',
|
||||||
default=None,
|
default=None,
|
||||||
help='Filters results by a status. Default=None.')
|
help="Filters results by a status. Default=None. "
|
||||||
|
"%s" % FILTER_DEPRECATED)
|
||||||
@utils.arg('--bootable',
|
@utils.arg('--bootable',
|
||||||
metavar='<True|true|False|false>',
|
metavar='<True|true|False|false>',
|
||||||
const=True,
|
const=True,
|
||||||
nargs='?',
|
nargs='?',
|
||||||
choices=['True', 'true', 'False', 'false'],
|
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',
|
@utils.arg('--migration_status',
|
||||||
metavar='<migration_status>',
|
metavar='<migration_status>',
|
||||||
default=None,
|
default=None,
|
||||||
help='Filters results by a migration status. Default=None. '
|
help="Filters results by a migration status. Default=None. "
|
||||||
'Admin only.')
|
"Admin only. "
|
||||||
|
"%s" % FILTER_DEPRECATED)
|
||||||
@utils.arg('--metadata',
|
@utils.arg('--metadata',
|
||||||
nargs='*',
|
nargs='*',
|
||||||
metavar='<key=value>',
|
metavar='<key=value>',
|
||||||
default=None,
|
default=None,
|
||||||
help='Filters results by a metadata key and value pair. '
|
help="Filters results by a metadata key and value pair. "
|
||||||
'Default=None.')
|
"Default=None. "
|
||||||
|
"%s" % FILTER_DEPRECATED)
|
||||||
@utils.arg('--image_metadata',
|
@utils.arg('--image_metadata',
|
||||||
nargs='*',
|
nargs='*',
|
||||||
metavar='<key=value>',
|
metavar='<key=value>',
|
||||||
default=None,
|
default=None,
|
||||||
start_version='3.4',
|
start_version='3.4',
|
||||||
help='Filters results by a image metadata key and value pair. '
|
help="Filters results by a image metadata key and value pair. "
|
||||||
'Require volume api version >=3.4. Default=None.')
|
"Require volume api version >=3.4. Default=None."
|
||||||
|
"%s" % FILTER_DEPRECATED)
|
||||||
@utils.arg('--marker',
|
@utils.arg('--marker',
|
||||||
metavar='<marker>',
|
metavar='<marker>',
|
||||||
default=None,
|
default=None,
|
||||||
|
@ -130,8 +267,17 @@ RESET_STATE_RESOURCES = {'volume': utils.find_volume,
|
||||||
nargs='?',
|
nargs='?',
|
||||||
metavar='<tenant>',
|
metavar='<tenant>',
|
||||||
help='Display information from single tenant (Admin only).')
|
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):
|
def do_list(cs, args):
|
||||||
"""Lists all volumes."""
|
"""Lists all volumes."""
|
||||||
|
# pylint: disable=function-redefined
|
||||||
# NOTE(thingee): Backwards-compatibility with v1 args
|
# NOTE(thingee): Backwards-compatibility with v1 args
|
||||||
if args.display_name is not None:
|
if args.display_name is not None:
|
||||||
args.name = args.display_name
|
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,
|
if hasattr(args, 'image_metadata') and args.image_metadata else None,
|
||||||
'group_id': getattr(args, 'group_id', 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
|
# If unavailable/non-existent fields are specified, these fields will
|
||||||
# be removed from key_list at the print_list() during key validation.
|
# be removed from key_list at the print_list() during key validation.
|
||||||
|
@ -820,10 +969,22 @@ def do_manageable_list(cs, args):
|
||||||
const=1,
|
const=1,
|
||||||
default=utils.env('ALL_TENANTS', default=0),
|
default=utils.env('ALL_TENANTS', default=0),
|
||||||
help='Shows details for all tenants. Admin only.')
|
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):
|
def do_group_list(cs, args):
|
||||||
"""Lists all groups."""
|
"""Lists all groups."""
|
||||||
search_opts = {'all_tenants': args.all_tenants}
|
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)
|
groups = cs.groups.list(search_opts=search_opts)
|
||||||
|
|
||||||
columns = ['ID', 'Status', 'Name']
|
columns = ['ID', 'Status', 'Name']
|
||||||
|
@ -1003,11 +1164,21 @@ def do_group_update(cs, args):
|
||||||
@utils.arg('--status',
|
@utils.arg('--status',
|
||||||
metavar='<status>',
|
metavar='<status>',
|
||||||
default=None,
|
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',
|
@utils.arg('--group-id',
|
||||||
metavar='<group_id>',
|
metavar='<group_id>',
|
||||||
default=None,
|
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):
|
def do_group_snapshot_list(cs, args):
|
||||||
"""Lists all group snapshots."""
|
"""Lists all group snapshots."""
|
||||||
|
|
||||||
|
@ -1018,6 +1189,9 @@ def do_group_snapshot_list(cs, args):
|
||||||
'status': args.status,
|
'status': args.status,
|
||||||
'group_id': args.group_id,
|
'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)
|
group_snapshots = cs.group_snapshots.list(search_opts=search_opts)
|
||||||
|
|
||||||
|
@ -1200,23 +1374,36 @@ def do_api_version(cs, args):
|
||||||
@utils.arg('--resource_uuid',
|
@utils.arg('--resource_uuid',
|
||||||
metavar='<resource_uuid>',
|
metavar='<resource_uuid>',
|
||||||
default=None,
|
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',
|
@utils.arg('--resource_type',
|
||||||
metavar='<type>',
|
metavar='<type>',
|
||||||
default=None,
|
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',
|
@utils.arg('--event_id',
|
||||||
metavar='<id>',
|
metavar='<id>',
|
||||||
default=None,
|
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',
|
@utils.arg('--request_id',
|
||||||
metavar='<request_id>',
|
metavar='<request_id>',
|
||||||
default=None,
|
default=None,
|
||||||
help='Filters results by request id. Default=None.')
|
help="Filters results by request id. Default=None. "
|
||||||
|
"%s" % FILTER_DEPRECATED)
|
||||||
@utils.arg('--level',
|
@utils.arg('--level',
|
||||||
metavar='<level>',
|
metavar='<level>',
|
||||||
default=None,
|
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):
|
def do_message_list(cs, args):
|
||||||
"""Lists all messages."""
|
"""Lists all messages."""
|
||||||
search_opts = {
|
search_opts = {
|
||||||
|
@ -1224,6 +1411,9 @@ def do_message_list(cs, args):
|
||||||
'event_id': args.event_id,
|
'event_id': args.event_id,
|
||||||
'request_id': args.request_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:
|
if args.resource_type:
|
||||||
search_opts['resource_type'] = args.resource_type.upper()
|
search_opts['resource_type'] = args.resource_type.upper()
|
||||||
if args.level:
|
if args.level:
|
||||||
|
@ -1294,7 +1484,8 @@ def do_message_delete(cs, args):
|
||||||
@utils.arg('--name',
|
@utils.arg('--name',
|
||||||
metavar='<name>',
|
metavar='<name>',
|
||||||
default=None,
|
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',
|
@utils.arg('--display-name',
|
||||||
help=argparse.SUPPRESS)
|
help=argparse.SUPPRESS)
|
||||||
@utils.arg('--display_name',
|
@utils.arg('--display_name',
|
||||||
|
@ -1302,11 +1493,13 @@ def do_message_delete(cs, args):
|
||||||
@utils.arg('--status',
|
@utils.arg('--status',
|
||||||
metavar='<status>',
|
metavar='<status>',
|
||||||
default=None,
|
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',
|
@utils.arg('--volume-id',
|
||||||
metavar='<volume-id>',
|
metavar='<volume-id>',
|
||||||
default=None,
|
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',
|
@utils.arg('--volume_id',
|
||||||
help=argparse.SUPPRESS)
|
help=argparse.SUPPRESS)
|
||||||
@utils.arg('--marker',
|
@utils.arg('--marker',
|
||||||
|
@ -1337,10 +1530,20 @@ def do_message_delete(cs, args):
|
||||||
metavar='<key=value>',
|
metavar='<key=value>',
|
||||||
default=None,
|
default=None,
|
||||||
start_version='3.22',
|
start_version='3.22',
|
||||||
help='Filters results by a metadata key and value pair. Require '
|
help="Filters results by a metadata key and value pair. Require "
|
||||||
'volume api version >=3.22. Default=None.')
|
"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):
|
def do_snapshot_list(cs, args):
|
||||||
"""Lists all snapshots."""
|
"""Lists all snapshots."""
|
||||||
|
# pylint: disable=function-redefined
|
||||||
all_tenants = (1 if args.tenant else
|
all_tenants = (1 if args.tenant else
|
||||||
int(os.environ.get("ALL_TENANTS", args.all_tenants)))
|
int(os.environ.get("ALL_TENANTS", args.all_tenants)))
|
||||||
|
|
||||||
|
@ -1363,6 +1566,10 @@ def do_snapshot_list(cs, args):
|
||||||
'metadata': metadata
|
'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,
|
snapshots = cs.volume_snapshots.list(search_opts=search_opts,
|
||||||
marker=args.marker,
|
marker=args.marker,
|
||||||
limit=args.limit,
|
limit=args.limit,
|
||||||
|
@ -1386,11 +1593,13 @@ def do_snapshot_list(cs, args):
|
||||||
@utils.arg('--volume-id',
|
@utils.arg('--volume-id',
|
||||||
metavar='<volume-id>',
|
metavar='<volume-id>',
|
||||||
default=None,
|
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',
|
@utils.arg('--status',
|
||||||
metavar='<status>',
|
metavar='<status>',
|
||||||
default=None,
|
default=None,
|
||||||
help='Filters results by a status. Default=None.')
|
help="Filters results by a status. Default=None. "
|
||||||
|
"%s" % FILTER_DEPRECATED)
|
||||||
@utils.arg('--marker',
|
@utils.arg('--marker',
|
||||||
metavar='<marker>',
|
metavar='<marker>',
|
||||||
default=None,
|
default=None,
|
||||||
|
@ -1414,6 +1623,14 @@ def do_snapshot_list(cs, args):
|
||||||
nargs='?',
|
nargs='?',
|
||||||
metavar='<tenant>',
|
metavar='<tenant>',
|
||||||
help='Display information from single tenant (Admin only).')
|
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):
|
def do_attachment_list(cs, args):
|
||||||
"""Lists all attachments."""
|
"""Lists all attachments."""
|
||||||
search_opts = {
|
search_opts = {
|
||||||
|
@ -1422,6 +1639,9 @@ def do_attachment_list(cs, args):
|
||||||
'status': args.status,
|
'status': args.status,
|
||||||
'volume_id': args.volume_id,
|
'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,
|
attachments = cs.attachments.list(search_opts=search_opts,
|
||||||
marker=args.marker,
|
marker=args.marker,
|
||||||
|
|
|
@ -176,3 +176,23 @@ class VolumeManager(volumes.VolumeManager):
|
||||||
search_opts={'host': host}, marker=marker,
|
search_opts={'host': host}, marker=marker,
|
||||||
limit=limit, offset=offset, sort=sort)
|
limit=limit, offset=offset, sort=sort)
|
||||||
return self._list(url, "manageable-volumes")
|
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)
|
||||||
|
|
|
@ -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
|
|
@ -43,6 +43,9 @@ ignore_messages = [
|
||||||
|
|
||||||
# six.moves
|
# six.moves
|
||||||
"Instance of '_MovedItems' has no 'builtins' member",
|
"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/"]
|
ignore_modules = ["cinderclient/tests/"]
|
||||||
|
|
Loading…
Reference in New Issue