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
required: false
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:
description: |
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
- project_id: project_id_path
- is_public: is_public_query
- group_specs: group_specs_query
Response parameters
-------------------

View File

@ -266,6 +266,27 @@ def check_share_network_is_active(share_network):
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):
"""Model API responses as dictionaries."""

View File

@ -172,13 +172,14 @@ REST_API_VERSION_HISTORY = """
'update_security_service', 'update_security_service_check' and
'add_security_service_check'.
* 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 default api version request is defined to be the
# minimum version of the API supported.
_MIN_API_VERSION = "2.0"
_MAX_API_VERSION = "2.65"
_MAX_API_VERSION = "2.66"
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
/shares request. These hints will invoke Affinity/Anti-Affinity scheduler
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.
"""The group type API controller module."""
import ast
from oslo_utils import strutils
from oslo_utils import uuidutils
import six
from six.moves import http_client
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_group_types as views
from manila import exception
@ -104,35 +106,27 @@ class ShareGroupTypesController(wsgi.Controller):
context = req.environ['manila.context']
if context.is_admin:
# 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'))
else:
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(
context, search_opts=filters).values()
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')
def _create(self, req, body):
"""Creates a new share group type."""

View File

@ -25,6 +25,7 @@ from six.moves import http_client
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 types as views_types
@ -125,7 +126,7 @@ class ShareTypesController(wsgi.Controller):
context = req.environ['manila.context']
if context.is_admin:
# 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'))
else:
filters['is_public'] = True
@ -148,26 +149,6 @@ class ShareTypesController(wsgi.Controller):
context, search_opts=filters).values()
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.action("create")
def create(self, req, body):

View File

@ -344,6 +344,16 @@ class MiscFunctionsTest(test.TestCase):
result = common.check_net_id_and_subnet_id(body)
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
class ViewBuilderTest(test.TestCase):

View File

@ -430,16 +430,6 @@ class ShareTypesAPITest(test.TestCase):
self.assertDictEqual(expected_share_type,
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(
("new_name", "new_description", "wrong_bool"),
(" ", "new_description", "true"),

View File

@ -102,7 +102,7 @@ class ShareGroupTypesTestCase(test.TestCase):
def test_get_all_types_search(self):
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(
db, 'share_group_type_get_all',
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(
mock.ANY, 0, filters={'is_public': True})
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(
self.context, search_opts=search_filter)
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):
project_id = '456'
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.