Add like filter

Add like filter support in ``shares``, ``snapshots``,
``share-networks``, ``share-groups`` list APIs.

APIImpact

Implements BP like-filter
Change-Id: I5fdf6d89d0b6c7fa182ddfaac60979bc6c0fc2a5
This commit is contained in:
zhongjun 2017-05-04 17:27:14 +08:00
parent ebea34399f
commit c96df66823
24 changed files with 311 additions and 49 deletions

View File

@ -104,6 +104,8 @@ REST_API_VERSION_HISTORY = """
fields to 'share_group' object.
* 2.35 - Added support to retrieve shares filtered by export_location_id
and export_location_path.
* 2.36 - Added like filter support in ``shares``, ``snapshots``,
``share-networks``, ``share-groups`` list APIs.
"""
@ -111,7 +113,7 @@ REST_API_VERSION_HISTORY = """
# The default api version request is defined to be the
# minimum version of the API supported.
_MIN_API_VERSION = "2.0"
_MAX_API_VERSION = "2.35"
_MAX_API_VERSION = "2.36"
DEFAULT_API_VERSION = _MIN_API_VERSION

View File

@ -205,3 +205,8 @@ user documentation.
----
Added support to retrieve shares filtered by export_location_id and
export_location_path.
2.36
----
Added like filter support in ``shares``, ``snapshots``, ``share-networks``,
``share-groups`` list APIs.

View File

@ -72,10 +72,14 @@ class ShareSnapshotMixin(object):
def index(self, req):
"""Returns a summary list of snapshots."""
req.GET.pop('name~', None)
req.GET.pop('description~', None)
return self._get_snapshots(req, is_detail=False)
def detail(self, req):
"""Returns a detailed list of snapshots."""
req.GET.pop('name~', None)
req.GET.pop('description~', None)
return self._get_snapshots(req, is_detail=True)
def _get_snapshots(self, req, is_detail):
@ -97,6 +101,12 @@ class ShareSnapshotMixin(object):
if 'name' in search_opts:
search_opts['display_name'] = search_opts.pop('name')
# like filter
for key, db_key in (('name~', 'display_name~'),
('description~', 'display_description~')):
if key in search_opts:
search_opts[db_key] = search_opts.pop(key)
common.remove_invalid_options(context, search_opts,
self._get_snapshots_search_options())
@ -120,7 +130,8 @@ class ShareSnapshotMixin(object):
def _get_snapshots_search_options(self):
"""Return share snapshot search options allowed by non-admin."""
return ('display_name', 'name', 'status', 'share_id', 'size')
return ('display_name', 'name', 'status', 'share_id', 'size',
'display_name~', 'display_description~')
def update(self, req, id, body):
"""Update a snapshot."""

View File

@ -102,12 +102,16 @@ class ShareMixin(object):
"""Returns a summary list of shares."""
req.GET.pop('export_location_id', None)
req.GET.pop('export_location_path', None)
req.GET.pop('name~', None)
req.GET.pop('description~', None)
return self._get_shares(req, is_detail=False)
def detail(self, req):
"""Returns a detailed list of shares."""
req.GET.pop('export_location_id', None)
req.GET.pop('export_location_path', None)
req.GET.pop('name~', None)
req.GET.pop('description~', None)
return self._get_shares(req, is_detail=True)
def _get_shares(self, req, is_detail):
@ -135,6 +139,13 @@ class ShareMixin(object):
# from Cinder v1 and v2 APIs.
if 'name' in search_opts:
search_opts['display_name'] = search_opts.pop('name')
# like filter
for key, db_key in (('name~', 'display_name~'),
('description~', 'display_description~')):
if key in search_opts:
search_opts[db_key] = search_opts.pop(key)
if sort_key == 'name':
sort_key = 'display_name'
@ -164,7 +175,8 @@ class ShareMixin(object):
'share_type_id', 'snapshot_id', 'host', 'share_network_id',
'is_public', 'metadata', 'extra_specs', 'sort_key', 'sort_dir',
'share_group_id', 'share_group_snapshot_id',
'export_location_id', 'export_location_path'
'export_location_id', 'export_location_path', 'display_name~',
'display_description~'
)
def update(self, req, id, body):

View File

@ -20,6 +20,7 @@ import webob
from webob import exc
from manila.api import common
from manila.api.openstack import api_version_request as api_version
from manila.api.openstack import wsgi
from manila.api.views import share_groups as share_group_views
from manila import db
@ -96,6 +97,9 @@ class ShareGroupController(wsgi.Controller, wsgi.AdminActionsMixin):
search_opts.pop('offset', None)
sort_key = search_opts.pop('sort_key', 'created_at')
sort_dir = search_opts.pop('sort_dir', 'desc')
if req.api_version_request < api_version.APIVersionRequest("2.36"):
search_opts.pop('name~', None)
search_opts.pop('description~', None)
if 'group_type_id' in search_opts:
search_opts['share_group_type_id'] = search_opts.pop(
'group_type_id')

View File

@ -23,6 +23,7 @@ import webob
from webob import exc
from manila.api import common
from manila.api.openstack import api_version_request as api_version
from manila.api.openstack import wsgi
from manila.api.views import share_networks as share_networks_views
from manila.db import api as db_api
@ -166,8 +167,15 @@ class ShareNetworkController(wsgi.Controller):
for key, value in search_opts.items():
if key in ['ip_version', 'segmentation_id']:
value = int(value)
networks = [network for network in networks
if network[key] == value]
if (req.api_version_request >=
api_version.APIVersionRequest("2.36")):
networks = [network for network in networks
if network.get(key) == value or
(value in network.get(key.rstrip('~'))
if network.get(key.rstrip('~')) else ())]
else:
networks = [network for network in networks
if network.get(key) == value]
limited_list = common.limited(networks, req)
return self._view_builder.build_share_networks(

View File

@ -22,6 +22,7 @@ import webob
from webob import exc
from manila.api import common
from manila.api.openstack import api_version_request as api_version
from manila.api.openstack import wsgi
from manila.api.v1 import share_snapshots
from manila.api.views import share_snapshots as snapshot_views
@ -282,6 +283,22 @@ class ShareSnapshotsController(share_snapshots.ShareSnapshotMixin,
def access_list(self, req, snapshot_id):
return self._access_list(req, snapshot_id)
@wsgi.Controller.api_version("2.0")
def index(self, req):
"""Returns a summary list of shares."""
if req.api_version_request < api_version.APIVersionRequest("2.36"):
req.GET.pop('name~', None)
req.GET.pop('description~', None)
return self._get_snapshots(req, is_detail=False)
@wsgi.Controller.api_version("2.0")
def detail(self, req):
"""Returns a detailed list of shares."""
if req.api_version_request < api_version.APIVersionRequest("2.36"):
req.GET.pop('name~', None)
req.GET.pop('description~', None)
return self._get_snapshots(req, is_detail=True)
def create_resource():
return wsgi.Resource(ShareSnapshotsController())

View File

@ -415,28 +415,30 @@ class ShareController(shares.ShareMixin,
def revert(self, req, id, body=None):
return self._revert(req, id, body)
@wsgi.Controller.api_version("2.0", "2.34") # noqa
def index(self, req): # pylint: disable=E0102
@wsgi.Controller.api_version("2.0")
def index(self, req):
"""Returns a summary list of shares."""
req.GET.pop('export_location_id', None)
req.GET.pop('export_location_path', None)
if req.api_version_request < api_version.APIVersionRequest("2.35"):
req.GET.pop('export_location_id', None)
req.GET.pop('export_location_path', None)
if req.api_version_request < api_version.APIVersionRequest("2.36"):
req.GET.pop('name~', None)
req.GET.pop('description~', None)
return self._get_shares(req, is_detail=False)
@wsgi.Controller.api_version("2.35") # noqa
def index(self, req): # pylint: disable=E0102
"""Returns a summary list of shares."""
return self._get_shares(req, is_detail=False)
@wsgi.Controller.api_version("2.0", "2.34") # noqa
def detail(self, req): # pylint: disable=E0102
@wsgi.Controller.api_version("2.0")
def detail(self, req):
"""Returns a detailed list of shares."""
req.GET.pop('export_location_id', None)
req.GET.pop('export_location_path', None)
return self._get_shares(req, is_detail=True)
if req.api_version_request < api_version.APIVersionRequest("2.35"):
req.GET.pop('export_location_id', None)
req.GET.pop('export_location_path', None)
if req.api_version_request < api_version.APIVersionRequest("2.36"):
req.GET.pop('name~', None)
req.GET.pop('description~', None)
@wsgi.Controller.api_version("2.35") # noqa
def detail(self, req): # pylint: disable=E0102
"""Returns a detailed list of shares."""
return self._get_shares(req, is_detail=True)

View File

@ -120,6 +120,8 @@ SUPPORTED_SHARE_PROTOCOLS = (
SECURITY_SERVICES_ALLOWED_TYPES = ['active_directory', 'ldap', 'kerberos']
LIKE_FILTER = ['name~', 'description~']
NFS_EXPORTS_FILE = '/etc/exports'
NFS_EXPORTS_FILE_TEMP = '/var/lib/nfs/etab'

View File

@ -3903,11 +3903,17 @@ def _share_group_get_all(context, project_id=None, share_server_id=None,
filters = {}
no_key = 'key_is_absent'
for k, v in filters.items():
filter_attr = getattr(models.ShareGroup, k, no_key)
temp_k = k.rstrip('~') if k in constants.LIKE_FILTER else k
filter_attr = getattr(models.ShareGroup, temp_k, no_key)
if filter_attr == no_key:
msg = _("Share groups cannot be filtered using '%s' key.")
raise exception.InvalidInput(reason=msg % k)
query = query.filter(filter_attr == v)
if k in constants.LIKE_FILTER:
query = query.filter(filter_attr.op('LIKE')(u'%' + v + u'%'))
else:
query = query.filter(filter_attr == v)
if project_id:
query = query.filter(

View File

@ -1547,7 +1547,9 @@ class API(base.Base):
results = []
for s in shares:
# values in search_opts can be only strings
if all(s.get(k, None) == v for k, v in search_opts.items()):
if (all(s.get(k, None) == v or (v in (s.get(k.rstrip('~'))
if s.get(k.rstrip('~')) else ()))
for k, v in search_opts.items())):
results.append(s)
shares = results
return shares
@ -1590,10 +1592,10 @@ class API(base.Base):
results = []
not_found = object()
for snapshot in snapshots:
for opt, value in search_opts.items():
if snapshot.get(opt, not_found) != value:
break
else:
if (all(snapshot.get(k, not_found) == v or (v in
snapshot.get(k.rstrip('~')) if
snapshot.get(k.rstrip('~')) else ())
for k, v in search_opts.items())):
results.append(snapshot)
snapshots = results
return snapshots

View File

@ -91,8 +91,8 @@ class ShareGroupAPITest(test.TestCase):
'user_id': 'fakeuser',
'project_id': 'fakeproject',
'status': constants.STATUS_CREATING,
'name': None,
'description': None,
'name': 'fake name',
'description': 'fake description',
'host': None,
'availability_zone': None,
'consistent_snapshot_support': None,
@ -749,6 +749,28 @@ class ShareGroupAPITest(test.TestCase):
self.mock_policy_check.assert_called_once_with(
req_context, self.resource_name, 'get_all')
def test_share_group_list_index_with_like_filter(self):
fake, expected = self._get_fake_simple_share_group(
name='fake_1', description='fake_ds_1')
fake2, expected2 = self._get_fake_simple_share_group(
name='fake_2', description='fake_ds_2')
self.mock_object(share_group_api.API, 'get_all',
mock.Mock(return_value=[fake, fake2]))
req = fakes.HTTPRequest.blank(
'/share-groups?name~=fake&description~=fake',
version='2.36',
experimental=True)
req_context = req.environ['manila.context']
res_dict = self.controller.index(req)
expected.pop('description')
expected2.pop('description')
self.assertEqual(2, len(res_dict['share_groups']))
self.assertEqual([expected, expected2], res_dict['share_groups'])
self.mock_policy_check.assert_called_once_with(
req_context, self.resource_name, 'get_all')
def test_share_group_list_detail(self):
fake, expected = self._get_fake_share_group()
self.mock_object(

View File

@ -450,6 +450,23 @@ class ShareNetworkAPITest(test.TestCase):
result[share_networks.RESOURCES_NAME][0],
fake_sn_with_ss_shortened)
@mock.patch.object(db_api, 'share_network_get_all_by_project',
mock.Mock())
def test_index_filter_by_like_filter(self):
db_api.share_network_get_all_by_project.return_value = [
fake_share_network,
]
req = fakes.HTTPRequest.blank(
'/share_networks?name~=fake&description~=fake',
use_admin_context=True, version='2.36')
result = self.controller.index(req)
db_api.share_network_get_all_by_project.assert_called_once_with(
req.environ['manila.context'], self.context.project_id)
self.assertEqual(1, len(result[share_networks.RESOURCES_NAME]))
self._check_share_network_view_shortened(
result[share_networks.RESOURCES_NAME][0],
fake_share_network_shortened)
@mock.patch.object(db_api, 'share_network_get_all_by_project',
mock.Mock())
def test_index_all_filter_opts(self):

View File

@ -184,13 +184,19 @@ class ShareSnapshotAPITest(test.TestCase):
}
self.assertEqual(expected, res_dict)
def _snapshot_list_summary_with_search_opts(self, use_admin_context):
def _snapshot_list_summary_with_search_opts(self, version,
use_admin_context):
search_opts = fake_share.search_opts()
if (api_version.APIVersionRequest(version) >=
api_version.APIVersionRequest('2.36')):
search_opts.pop('name')
search_opts['display_name~'] = 'fake_name'
# fake_key should be filtered for non-admin
url = '/snapshots?fake_key=fake_value'
for k, v in search_opts.items():
url = url + '&' + k + '=' + v
req = fakes.HTTPRequest.blank(url, use_admin_context=use_admin_context)
req = fakes.HTTPRequest.blank(
url, use_admin_context=use_admin_context, version=version)
snapshots = [
{'id': 'id1', 'display_name': 'n1', 'status': 'fake_status', },
@ -203,10 +209,14 @@ class ShareSnapshotAPITest(test.TestCase):
result = self.controller.index(req)
search_opts_expected = {
'display_name': search_opts['name'],
'status': search_opts['status'],
'share_id': search_opts['share_id'],
}
if (api_version.APIVersionRequest(version) >=
api_version.APIVersionRequest('2.36')):
search_opts_expected['display_name~'] = 'fake_name'
else:
search_opts_expected['display_name'] = search_opts['name']
if use_admin_context:
search_opts_expected.update({'fake_key': 'fake_value'})
share_api.API.get_all_snapshots.assert_called_once_with(
@ -220,11 +230,15 @@ class ShareSnapshotAPITest(test.TestCase):
self.assertEqual(
snapshots[1]['display_name'], result['snapshots'][0]['name'])
def test_snapshot_list_summary_with_search_opts_by_non_admin(self):
self._snapshot_list_summary_with_search_opts(use_admin_context=False)
def test_snapshot_list_summary_with_search_opts_by_admin(self):
self._snapshot_list_summary_with_search_opts(use_admin_context=True)
@ddt.data({'version': '2.35', 'use_admin_context': True},
{'version': '2.36', 'use_admin_context': True},
{'version': '2.35', 'use_admin_context': False},
{'version': '2.36', 'use_admin_context': False})
@ddt.unpack
def test_snapshot_list_summary_with_search_opts(self, version,
use_admin_context):
self._snapshot_list_summary_with_search_opts(
version=version, use_admin_context=use_admin_context)
def _snapshot_list_detail_with_search_opts(self, use_admin_context):
search_opts = fake_share.search_opts()

View File

@ -1500,8 +1500,12 @@ class ShareAPITest(test.TestCase):
@ddt.data({'use_admin_context': False, 'version': '2.4'},
{'use_admin_context': True, 'version': '2.4'},
{'use_admin_context': True, 'version': '2.35'},
{'use_admin_context': False, 'version': '2.35'})
def share_list_summary_with_search_opts(self, use_admin_context, version):
{'use_admin_context': False, 'version': '2.35'},
{'use_admin_context': True, 'version': '2.36'},
{'use_admin_context': False, 'version': '2.36'})
@ddt.unpack
def test_share_list_summary_with_search_opts(self, use_admin_context,
version):
search_opts = {
'name': 'fake_name',
'status': constants.STATUS_AVAILABLE,
@ -1519,6 +1523,11 @@ class ShareAPITest(test.TestCase):
'export_location_id': 'fake_export_location_id',
'export_location_path': 'fake_export_location_path',
}
if (api_version.APIVersionRequest(version) >=
api_version.APIVersionRequest('2.36')):
search_opts.update(
{'display_name~': 'fake',
'display_description~': 'fake'})
if use_admin_context:
search_opts['host'] = 'fake_host'
# fake_key should be filtered for non-admin
@ -1555,6 +1564,11 @@ class ShareAPITest(test.TestCase):
search_opts['export_location_id'])
search_opts_expected['export_location_path'] = (
search_opts['export_location_path'])
if (api_version.APIVersionRequest(version) >=
api_version.APIVersionRequest('2.36')):
search_opts_expected.update(
{'display_name~': search_opts['display_name~'],
'display_description~': search_opts['display_description~']})
if use_admin_context:
search_opts_expected.update({'fake_key': 'fake_value'})

View File

@ -813,6 +813,19 @@ class ShareGroupDatabaseAPITestCase(test.TestCase):
self.assertDictMatch(dict(expected_group), dict(group))
self.assertEqual(fake_project, group['project_id'])
def test_share_group_get_all_by_like_filter(self):
expected_group = db_utils.create_share_group(
name='test1', description='test1')
db_utils.create_share_group(name='fake', description='fake')
groups = db_api.share_group_get_all(
self.ctxt, detailed=True,
filters={'name~': 'test', 'description~': 'test'})
self.assertEqual(1, len(groups))
group = groups[0]
self.assertDictMatch(dict(expected_group), dict(group))
def test_share_group_update(self):
fake_name = "my_fake_name"
expected_group = db_utils.create_share_group()

View File

@ -46,6 +46,7 @@ CONF = cfg.CONF
_FAKE_LIST_OF_ALL_SHARES = [
{
'name': 'foo',
'description': 'ds',
'status': constants.STATUS_AVAILABLE,
'project_id': 'fake_pid_1',
'share_server_id': 'fake_server_1',
@ -57,7 +58,8 @@ _FAKE_LIST_OF_ALL_SHARES = [
'share_server_id': 'fake_server_2',
},
{
'name': 'foo',
'name': 'foo1',
'description': 'ds1',
'status': constants.STATUS_AVAILABLE,
'project_id': 'fake_pid_2',
'share_server_id': 'fake_server_3',
@ -310,6 +312,21 @@ class ShareAPITestCase(test.TestCase):
)
self.assertEqual(_FAKE_LIST_OF_ALL_SHARES[1::2], shares)
def test_get_all_admin_filter_by_inexact_filter(self):
ctx = context.RequestContext('fake_uid', 'fake_pid_2', is_admin=True)
self.mock_object(db_api, 'share_get_all_by_project',
mock.Mock(return_value=_FAKE_LIST_OF_ALL_SHARES))
shares = self.api.get_all(ctx, {'name~': 'foo', 'description~': 'ds'})
share_api.policy.check_policy.assert_has_calls([
mock.call(ctx, 'share', 'get_all'),
])
db_api.share_get_all_by_project.assert_called_once_with(
ctx, sort_dir='desc', sort_key='created_at',
project_id='fake_pid_2',
filters={}, is_public=False
)
self.assertEqual(_FAKE_LIST_OF_ALL_SHARES[0::2], shares)
@ddt.data('id', 'path')
def test_get_all_admin_filter_by_export_location(self, type):
ctx = context.RequestContext('fake_uid', 'fake_pid_2', is_admin=True)
@ -1894,6 +1911,24 @@ class ShareAPITestCase(test.TestCase):
ctx, 'fakepid', sort_dir='desc', sort_key='share_id',
filters=search_opts)
def test_get_all_snapshots_not_admin_inexact_search_opts(self):
search_opts = {'name~': 'foo', 'description~': 'ds'}
fake_objs = [{'name': 'fo', 'description': 'd'},
{'name': 'foo', 'description': 'ds'},
{'name': 'foo1', 'description': 'ds1'}]
ctx = context.RequestContext('fakeuid', 'fakepid', is_admin=False)
self.mock_object(db_api, 'share_snapshot_get_all_by_project',
mock.Mock(return_value=fake_objs))
result = self.api.get_all_snapshots(ctx, search_opts)
self.assertEqual(fake_objs[1:], result)
share_api.policy.check_policy.assert_called_once_with(
ctx, 'share_snapshot', 'get_all_snapshots')
db_api.share_snapshot_get_all_by_project.assert_called_once_with(
ctx, 'fakepid', sort_dir='desc', sort_key='share_id',
filters=search_opts)
def test_get_all_snapshots_with_sorting_valid(self):
self.mock_object(
db_api, 'share_snapshot_get_all_by_project',

View File

@ -30,7 +30,7 @@ ShareGroup = [
help="The minimum api microversion is configured to be the "
"value of the minimum microversion supported by Manila."),
cfg.StrOpt("max_api_microversion",
default="2.35",
default="2.36",
help="The maximum api microversion is configured to be the "
"value of the latest microversion supported by Manila."),
cfg.StrOpt("region",

View File

@ -14,6 +14,7 @@
# License for the specific language governing permissions and limitations
# under the License.
import ddt
from tempest import config
from tempest.lib.common.utils import data_utils
import testtools
@ -21,6 +22,7 @@ from testtools import testcase as tc
from manila_tempest_tests.common import constants
from manila_tempest_tests.tests.api import base
from manila_tempest_tests import utils
CONF = config.CONF
@ -28,6 +30,7 @@ CONF = config.CONF
@testtools.skipUnless(
CONF.share.run_share_group_tests, 'Share Group tests disabled.')
@base.skip_if_microversion_lt(constants.MIN_SHARE_GROUP_MICROVERSION)
@ddt.ddt
class ShareGroupActionsTest(base.BaseSharesTest):
"""Covers share group functionality."""
@ -157,11 +160,14 @@ class ShareGroupActionsTest(base.BaseSharesTest):
self.assertEqual(1, len(gen), msg)
@tc.attr(base.TAG_POSITIVE, base.TAG_API_WITH_BACKEND)
def test_list_share_groups_with_detail_min(self):
@ddt.data(constants.MIN_SHARE_GROUP_MICROVERSION, '2.36')
def test_list_share_groups_with_detail_min(self, version):
params = None
if utils.is_microversion_ge(version, '2.36'):
params = {'name~': 'tempest', 'description~': 'tempest'}
# List share groups
share_groups = self.shares_v2_client.list_share_groups(
detailed=True, version=constants.MIN_SHARE_GROUP_MICROVERSION)
detailed=True, params=params, version=version)
# Verify keys
for sg in share_groups:

View File

@ -70,6 +70,22 @@ class ShareNetworkListMixin(object):
self.assertTrue(any(ss['id'] == self.ss_ldap['id']
for ss in ss_list))
@tc.attr(base.TAG_POSITIVE, base.TAG_API)
@base.skip_if_microversion_lt("2.36")
def test_list_share_networks_like_filter(self):
valid_filter_opts = {
'name': 'sn_with_ldap_ss',
'description': 'fake',
}
listed = self.shares_v2_client.list_share_networks_with_detail(
{'name~': 'ldap_ss', 'description~': 'fa'})
self.assertTrue(any(self.sn_with_ldap_ss['id'] == sn['id']
for sn in listed))
for sn in listed:
self.assertTrue(all(value in sn[key] for key, value in
valid_filter_opts.items()))
@tc.attr(base.TAG_POSITIVE, base.TAG_API)
def test_list_share_networks_all_filter_opts(self):
valid_filter_opts = {

View File

@ -128,3 +128,16 @@ class ShareNetworksNegativeTest(base.BaseSharesTest):
self.assertRaises(
lib_exc.Conflict,
self.shares_client.delete_share_network, new_sn['id'])
@tc.attr(base.TAG_NEGATIVE, base.TAG_API_WITH_BACKEND)
@base.skip_if_microversion_not_supported("2.35")
def test_list_shares_with_like_filter_not_exist(self):
filters = {
'name~': 'fake_not_exist',
'description~': 'fake_not_exist',
}
share_networks = (
self.shares_v2_client.list_share_networks_with_detail(
params=filters))
self.assertEqual(0, len(share_networks))

View File

@ -336,6 +336,15 @@ class SharesActionsTest(base.BaseSharesTest):
shares = self.shares_client.list_shares_with_detail(params)
self.assertEqual(self.share_name, shares[0]["name"])
@tc.attr(base.TAG_POSITIVE, base.TAG_API_WITH_BACKEND)
@base.skip_if_microversion_lt("2.36")
def test_list_shares_with_detail_filter_by_nonexistent_name(self):
# list shares by name, at least one share is expected
params = {"name~": 'tempest-share'}
shares = self.shares_v2_client.list_shares_with_detail(params)
for share in shares:
self.assertIn('tempest-share', share["name"])
@tc.attr(base.TAG_POSITIVE, base.TAG_API_WITH_BACKEND)
def test_list_shares_with_detail_filter_by_fake_name(self):
# list shares by fake name, no shares are expected
@ -481,16 +490,18 @@ class SharesActionsTest(base.BaseSharesTest):
@tc.attr(base.TAG_POSITIVE, base.TAG_API_WITH_BACKEND)
@testtools.skipUnless(CONF.share.run_snapshot_tests,
"Snapshot tests are disabled.")
@ddt.data(None, '2.16', LATEST_MICROVERSION)
@ddt.data(None, '2.16', '2.36', LATEST_MICROVERSION)
def test_list_snapshots_with_detail(self, version):
params = None
if version and utils.is_microversion_ge(version, '2.36'):
params = {'name~': 'tempest', 'description~': 'tempest'}
# list share snapshots
if version is None:
snaps = self.shares_client.list_snapshots_with_detail()
else:
utils.skip_if_microversion_not_supported(version)
snaps = self.shares_v2_client.list_snapshots_with_detail(
version=version)
version=version, params=params)
# verify keys
expected_keys = ["status", "links", "share_id", "name",

View File

@ -165,3 +165,29 @@ class SharesActionsNegativeTest(base.BaseSharesMixedTest):
params=filters)
self.assertEqual(0, len(shares))
@tc.attr(base.TAG_NEGATIVE, base.TAG_API_WITH_BACKEND)
@base.skip_if_microversion_not_supported("2.35")
def test_list_shares_with_like_filter_and_invalid_version(self):
# In API versions < v2.36, querying the share API by inexact
# filter (name or description) should have no effect. Those
# filters were supported from v2.36
filters = {
'name~': 'fake',
'description~': 'fake',
}
shares = self.shares_v2_client.list_shares(
params=filters, version="2.35")
self.assertGreater(len(shares), 0)
@tc.attr(base.TAG_NEGATIVE, base.TAG_API_WITH_BACKEND)
@base.skip_if_microversion_not_supported("2.35")
def test_list_shares_with_like_filter_not_exist(self):
filters = {
'name~': 'fake_not_exist',
'description~': 'fake_not_exist',
}
shares = self.shares_v2_client.list_shares(params=filters)
self.assertEqual(0, len(shares))

View File

@ -0,0 +1,4 @@
---
features:
- Added like filter support in ``shares``, ``snapshots``, ``share-networks``,
``share-groups`` list APIs.