Merge "Support group specs search for share group type API"

This commit is contained in:
Zuul 2021-12-21 23:26:46 +00:00 committed by Gerrit Code Review
commit 37c41e57cf
11 changed files with 82 additions and 57 deletions

View File

@ -298,6 +298,19 @@ group_snapshot_status_query:
in: query in: query
required: false required: false
type: string type: string
group_specs_query:
description: |
The group specifications as a set of one or more
key-value pairs. In each pair, the key is the name of the group
specification and the value is the share group type that was used to
filter search share group type list. The query must be a “percent-encoded” string,
for example, the following query parameters: {'group-specs':
{'consistent_snapshot_support': 'true'}} is encoded as
'group_specs=%7B%27consistent_snapshot_support%27%3A+%27True%27%7D'
in: query
required: false
type: string
min_version: 2.66
host_query: host_query:
description: | description: |
The host name of the resource to query with. Querying by hostname is a The host name of the resource to query with. Querying by hostname is a

View File

@ -58,6 +58,8 @@ Request
.. rest_parameters:: parameters.yaml .. rest_parameters:: parameters.yaml
- project_id: project_id_path - project_id: project_id_path
- is_public: is_public_query
- group_specs: group_specs_query
Response parameters Response parameters
------------------- -------------------

View File

@ -266,6 +266,27 @@ def check_share_network_is_active(share_network):
raise webob.exc.HTTPBadRequest(explanation=msg) raise webob.exc.HTTPBadRequest(explanation=msg)
def parse_is_public(is_public):
"""Parse is_public into something usable.
:returns:
- True: API should list public share group types only
- False: API should list private share group types only
- None: API should list both public and private share group types
"""
if is_public is None:
# preserve default value of showing only public types
return True
elif six.text_type(is_public).lower() == "all":
return None
else:
try:
return strutils.bool_from_string(is_public, strict=True)
except ValueError:
msg = _('Invalid is_public filter [%s]') % is_public
raise webob.exc.HTTPBadRequest(explanation=msg)
class ViewBuilder(object): class ViewBuilder(object):
"""Model API responses as dictionaries.""" """Model API responses as dictionaries."""

View File

@ -172,13 +172,14 @@ REST_API_VERSION_HISTORY = """
'update_security_service', 'update_security_service_check' and 'update_security_service', 'update_security_service_check' and
'add_security_service_check'. 'add_security_service_check'.
* 2.65 - Added ability to set scheduler hints via the share create API. * 2.65 - Added ability to set scheduler hints via the share create API.
* 2.66 - Added filter search by group spec for share group type list.
""" """
# The minimum and maximum versions of the API supported # The minimum and maximum versions of the API supported
# The default api version request is defined to be the # The default api version request is defined to be the
# minimum version of the API supported. # minimum version of the API supported.
_MIN_API_VERSION = "2.0" _MIN_API_VERSION = "2.0"
_MAX_API_VERSION = "2.65" _MAX_API_VERSION = "2.66"
DEFAULT_API_VERSION = _MIN_API_VERSION DEFAULT_API_VERSION = _MIN_API_VERSION

View File

@ -363,3 +363,7 @@ user documentation.
Added ability to specify "scheduler_hints" in the request body of the POST Added ability to specify "scheduler_hints" in the request body of the POST
/shares request. These hints will invoke Affinity/Anti-Affinity scheduler /shares request. These hints will invoke Affinity/Anti-Affinity scheduler
filters during share creation and share migration. filters during share creation and share migration.
2.66
----
Added filter search by group spec for share group type list.

View File

@ -11,14 +11,16 @@
# under the License. # under the License.
"""The group type API controller module.""" """The group type API controller module."""
import ast
from oslo_utils import strutils
from oslo_utils import uuidutils from oslo_utils import uuidutils
import six import six
from six.moves import http_client from six.moves import http_client
import webob import webob
from webob import exc 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.openstack import wsgi
from manila.api.views import share_group_types as views from manila.api.views import share_group_types as views
from manila import exception from manila import exception
@ -104,35 +106,27 @@ class ShareGroupTypesController(wsgi.Controller):
context = req.environ['manila.context'] context = req.environ['manila.context']
if context.is_admin: if context.is_admin:
# Only admin has query access to all group types # Only admin has query access to all group types
filters['is_public'] = self._parse_is_public( filters['is_public'] = common.parse_is_public(
req.params.get('is_public')) req.params.get('is_public'))
else: else:
filters['is_public'] = True filters['is_public'] = True
group_specs = req.params.get('group_specs', {})
group_specs_disallowed = (req.api_version_request <
api_version.APIVersionRequest("2.66"))
if group_specs and group_specs_disallowed:
msg = _("Filter by 'group_specs' is not supported by this "
"microversion. Use 2.66 or greater microversion to "
"be able to use filter search by 'group_specs.")
raise webob.exc.HTTPBadRequest(explanation=msg)
elif group_specs:
filters['group_specs'] = ast.literal_eval(group_specs)
limited_types = share_group_types.get_all( limited_types = share_group_types.get_all(
context, search_opts=filters).values() context, search_opts=filters).values()
return list(limited_types) return list(limited_types)
@staticmethod
def _parse_is_public(is_public):
"""Parse is_public into something usable.
:returns:
- True: API should list public share group types only
- False: API should list private share group types only
- None: API should list both public and private share group types
"""
if is_public is None:
# preserve default value of showing only public types
return True
elif six.text_type(is_public).lower() == "all":
return None
else:
try:
return strutils.bool_from_string(is_public, strict=True)
except ValueError:
msg = _('Invalid is_public filter [%s]') % is_public
raise exc.HTTPBadRequest(explanation=msg)
@wsgi.Controller.authorize('create') @wsgi.Controller.authorize('create')
def _create(self, req, body): def _create(self, req, body):
"""Creates a new share group type.""" """Creates a new share group type."""

View File

@ -25,6 +25,7 @@ from six.moves import http_client
import webob import webob
from webob import exc 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 api_version_request as api_version
from manila.api.openstack import wsgi from manila.api.openstack import wsgi
from manila.api.views import types as views_types from manila.api.views import types as views_types
@ -125,7 +126,7 @@ class ShareTypesController(wsgi.Controller):
context = req.environ['manila.context'] context = req.environ['manila.context']
if context.is_admin: if context.is_admin:
# Only admin has query access to all share types # Only admin has query access to all share types
filters['is_public'] = self._parse_is_public( filters['is_public'] = common.parse_is_public(
req.params.get('is_public')) req.params.get('is_public'))
else: else:
filters['is_public'] = True filters['is_public'] = True
@ -148,26 +149,6 @@ class ShareTypesController(wsgi.Controller):
context, search_opts=filters).values() context, search_opts=filters).values()
return list(limited_types) return list(limited_types)
@staticmethod
def _parse_is_public(is_public):
"""Parse is_public into something usable.
* True: API should list public share types only
* False: API should list private share types only
* None: API should list both public and private share types
"""
if is_public is None:
# preserve default value of showing only public types
return True
elif six.text_type(is_public).lower() == "all":
return None
else:
try:
return strutils.bool_from_string(is_public, strict=True)
except ValueError:
msg = _('Invalid is_public filter [%s]') % is_public
raise exc.HTTPBadRequest(explanation=msg)
@wsgi.Controller.api_version("1.0", "2.23") @wsgi.Controller.api_version("1.0", "2.23")
@wsgi.action("create") @wsgi.action("create")
def create(self, req, body): def create(self, req, body):

View File

@ -344,6 +344,16 @@ class MiscFunctionsTest(test.TestCase):
result = common.check_net_id_and_subnet_id(body) result = common.check_net_id_and_subnet_id(body)
self.assertIsNone(result) self.assertIsNone(result)
@ddt.data(None, True, 'true', 'false', 'all')
def test_parse_is_public_valid(self, value):
result = common.parse_is_public(value)
self.assertIn(result, (True, False, None))
def test_parse_is_public_invalid(self):
self.assertRaises(webob.exc.HTTPBadRequest,
common.parse_is_public,
'fakefakefake')
@ddt.ddt @ddt.ddt
class ViewBuilderTest(test.TestCase): class ViewBuilderTest(test.TestCase):

View File

@ -430,16 +430,6 @@ class ShareTypesAPITest(test.TestCase):
self.assertDictEqual(expected_share_type, self.assertDictEqual(expected_share_type,
output['share_types'][i]) output['share_types'][i])
@ddt.data(None, True, 'true', 'false', 'all')
def test_parse_is_public_valid(self, value):
result = self.controller._parse_is_public(value)
self.assertIn(result, (True, False, None))
def test_parse_is_public_invalid(self):
self.assertRaises(webob.exc.HTTPBadRequest,
self.controller._parse_is_public,
'fakefakefake')
@ddt.data( @ddt.data(
("new_name", "new_description", "wrong_bool"), ("new_name", "new_description", "wrong_bool"),
(" ", "new_description", "true"), (" ", "new_description", "true"),

View File

@ -102,7 +102,7 @@ class ShareGroupTypesTestCase(test.TestCase):
def test_get_all_types_search(self): def test_get_all_types_search(self):
share_group_type = self.fake_type_w_extra share_group_type = self.fake_type_w_extra
search_filter = {"group_specs": {"gold": "True"}, 'is_public': True} search_filter = {'group_specs': {'gold': 'True'}, 'is_public': True}
self.mock_object( self.mock_object(
db, 'share_group_type_get_all', db, 'share_group_type_get_all',
mock.Mock(return_value=share_group_type)) mock.Mock(return_value=share_group_type))
@ -113,11 +113,17 @@ class ShareGroupTypesTestCase(test.TestCase):
db.share_group_type_get_all.assert_called_once_with( db.share_group_type_get_all.assert_called_once_with(
mock.ANY, 0, filters={'is_public': True}) mock.ANY, 0, filters={'is_public': True})
self.assertEqual(sorted(share_group_type), sorted(returned_type)) self.assertEqual(sorted(share_group_type), sorted(returned_type))
search_filter = {"group_specs": {"gold": "False"}} search_filter = {'group_specs': {'gold': 'False'}}
returned_type = share_group_types.get_all( returned_type = share_group_types.get_all(
self.context, search_opts=search_filter) self.context, search_opts=search_filter)
self.assertEqual({}, returned_type) self.assertEqual({}, returned_type)
share_group_type = self.fake_type_w_extra
search_filter = {'group_specs': {'gold': 'True'}}
returned_type = share_group_types.get_all(
self.context, search_opts=search_filter)
self.assertItemsEqual(share_group_type, returned_type)
def test_add_access(self): def test_add_access(self):
project_id = '456' project_id = '456'
share_group_type = share_group_types.create(self.context, 'type2', []) share_group_type = share_group_types.create(self.context, 'type2', [])

View File

@ -0,0 +1,3 @@
---
features:
- Share group types can now be filtered with its group_specs.