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:
parent
ebea34399f
commit
c96df66823
|
@ -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
|
||||
|
||||
|
||||
|
|
|
@ -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.
|
||||
|
|
|
@ -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."""
|
||||
|
|
|
@ -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):
|
||||
|
|
|
@ -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')
|
||||
|
|
|
@ -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(
|
||||
|
|
|
@ -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())
|
||||
|
|
|
@ -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)
|
||||
|
||||
|
||||
|
|
|
@ -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'
|
||||
|
||||
|
|
|
@ -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(
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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(
|
||||
|
|
|
@ -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):
|
||||
|
|
|
@ -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()
|
||||
|
|
|
@ -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'})
|
||||
|
|
|
@ -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()
|
||||
|
|
|
@ -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',
|
||||
|
|
|
@ -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",
|
||||
|
|
|
@ -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:
|
||||
|
|
|
@ -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 = {
|
||||
|
|
|
@ -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))
|
||||
|
|
|
@ -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",
|
||||
|
|
|
@ -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))
|
||||
|
|
|
@ -0,0 +1,4 @@
|
|||
---
|
||||
features:
|
||||
- Added like filter support in ``shares``, ``snapshots``, ``share-networks``,
|
||||
``share-groups`` list APIs.
|
Loading…
Reference in New Issue