Browse Source

Merge "Remove experimental flag from share groups feature"

tags/10.0.0.0rc1
Zuul 3 months ago
committed by Gerrit Code Review
parent
commit
ea90fd17b8
11 changed files with 615 additions and 177 deletions
  1. +2
    -1
      manila/api/openstack/api_version_request.py
  2. +4
    -0
      manila/api/openstack/rest_api_version_history.rst
  3. +79
    -20
      manila/api/v2/share_group_snapshots.py
  4. +54
    -15
      manila/api/v2/share_group_type_specs.py
  5. +86
    -19
      manila/api/v2/share_group_types.py
  6. +69
    -21
      manila/api/v2/share_groups.py
  7. +88
    -23
      manila/tests/api/v2/test_share_group_snapshots.py
  8. +55
    -11
      manila/tests/api/v2/test_share_group_type_specs.py
  9. +81
    -40
      manila/tests/api/v2/test_share_group_types.py
  10. +87
    -27
      manila/tests/api/v2/test_share_groups.py
  11. +10
    -0
      releasenotes/notes/graduate-share-groups-feature-5f751b49ccc62969.yaml

+ 2
- 1
manila/api/openstack/api_version_request.py View File

@@ -147,13 +147,14 @@ REST_API_VERSION_HISTORY = """
* 2.54 - Share and share instance objects include a new field called
"progress" which indicates the completion of a share creation
operation as a percentage.
* 2.55 - Share groups feature is no longer considered experimental.
"""

# 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.54"
_MAX_API_VERSION = "2.55"
DEFAULT_API_VERSION = _MIN_API_VERSION




+ 4
- 0
manila/api/openstack/rest_api_version_history.rst View File

@@ -301,3 +301,7 @@ user documentation.
----
Share and share instance objects include a new field called "progress" which
indicates the completion of a share creation operation as a percentage.

2.55
----
Share groups feature is no longer considered experimental.

+ 79
- 20
manila/api/v2/share_group_snapshots.py View File

@@ -29,6 +29,7 @@ from manila.i18n import _
import manila.share_group.api as share_group_api

LOG = log.getLogger(__name__)
SG_GRADUATION_VERSION = '2.55'


class ShareGroupSnapshotController(wsgi.Controller, wsgi.AdminActionsMixin):
@@ -50,17 +51,23 @@ class ShareGroupSnapshotController(wsgi.Controller, wsgi.AdminActionsMixin):
msg = _("Share group snapshot %s not found.") % sg_snapshot_id
raise exc.HTTPNotFound(explanation=msg)

@wsgi.Controller.api_version('2.31', experimental=True)
@wsgi.Controller.authorize('get')
def show(self, req, id):
def _show(self, req, id):
"""Return data about the given share group snapshot."""
context = req.environ['manila.context']
sg_snapshot = self._get_share_group_snapshot(context, id)
return self._view_builder.detail(req, sg_snapshot)

@wsgi.Controller.api_version('2.31', experimental=True)
@wsgi.Controller.authorize
def delete(self, req, id):
@wsgi.Controller.api_version('2.31', '2.54', experimental=True)
def show(self, req, id):
return self._show(req, id)

@wsgi.Controller.api_version(SG_GRADUATION_VERSION) # noqa
def show(self, req, id): # pylint: disable=function-redefined
return self._show(req, id)

@wsgi.Controller.authorize('delete')
def _delete_group_snapshot(self, req, id):
"""Delete a share group snapshot."""
context = req.environ['manila.context']
LOG.info("Delete share group snapshot with id: %s",
@@ -73,18 +80,35 @@ class ShareGroupSnapshotController(wsgi.Controller, wsgi.AdminActionsMixin):
raise exc.HTTPConflict(explanation=six.text_type(e))
return webob.Response(status_int=http_client.ACCEPTED)

@wsgi.Controller.api_version('2.31', experimental=True)
@wsgi.Controller.authorize('get_all')
@wsgi.Controller.api_version('2.31', '2.54', experimental=True)
def delete(self, req, id):
return self._delete_group_snapshot(req, id)

@wsgi.Controller.api_version(SG_GRADUATION_VERSION) # noqa
def delete(self, req, id): # pylint: disable=function-redefined
return self._delete_group_snapshot(req, id)

@wsgi.Controller.api_version('2.31', '2.54', experimental=True)
def index(self, req):
"""Returns a summary list of share group snapshots."""
return self._get_share_group_snaps(req, is_detail=False)

@wsgi.Controller.api_version('2.31', experimental=True)
@wsgi.Controller.authorize('get_all')
@wsgi.Controller.api_version(SG_GRADUATION_VERSION) # noqa
def index(self, req): # pylint: disable=function-redefined
"""Returns a summary list of share group snapshots."""
return self._get_share_group_snaps(req, is_detail=False)

@wsgi.Controller.api_version('2.31', '2.54', experimental=True)
def detail(self, req):
"""Returns a detailed list of share group snapshots."""
return self._get_share_group_snaps(req, is_detail=True)

@wsgi.Controller.api_version(SG_GRADUATION_VERSION) # noqa
def detail(self, req): # pylint: disable=function-redefined
"""Returns a detailed list of share group snapshots."""
return self._get_share_group_snaps(req, is_detail=True)

@wsgi.Controller.authorize('get_all')
def _get_share_group_snaps(self, req, is_detail):
"""Returns a list of share group snapshots."""
context = req.environ['manila.context']
@@ -110,9 +134,8 @@ class ShareGroupSnapshotController(wsgi.Controller, wsgi.AdminActionsMixin):
snaps = self._view_builder.summary_list(req, limited_list)
return snaps

@wsgi.Controller.api_version('2.31', experimental=True)
@wsgi.Controller.authorize
def update(self, req, id, body):
@wsgi.Controller.authorize('update')
def _update_group_snapshot(self, req, id, body):
"""Update a share group snapshot."""
context = req.environ['manila.context']
key = 'share_group_snapshot'
@@ -135,10 +158,16 @@ class ShareGroupSnapshotController(wsgi.Controller, wsgi.AdminActionsMixin):
context, sg_snapshot, sg_snapshot_data)
return self._view_builder.detail(req, sg_snapshot)

@wsgi.Controller.api_version('2.31', experimental=True)
@wsgi.response(202)
@wsgi.Controller.authorize
def create(self, req, body):
@wsgi.Controller.api_version('2.31', '2.54', experimental=True)
def update(self, req, id, body):
return self._update_group_snapshot(req, id, body)

@wsgi.Controller.api_version(SG_GRADUATION_VERSION) # noqa
def update(self, req, id, body): # pylint: disable=function-redefined
return self._update_group_snapshot(req, id, body)

@wsgi.Controller.authorize('create')
def _create(self, req, body):
"""Creates a new share group snapshot."""
context = req.environ['manila.context']

@@ -172,9 +201,18 @@ class ShareGroupSnapshotController(wsgi.Controller, wsgi.AdminActionsMixin):

return self._view_builder.detail(req, dict(new_snapshot.items()))

@wsgi.Controller.api_version('2.31', experimental=True)
@wsgi.Controller.api_version('2.31', '2.54', experimental=True)
@wsgi.response(202)
def create(self, req, body):
return self._create(req, body)

@wsgi.Controller.api_version(SG_GRADUATION_VERSION) # noqa
@wsgi.response(202)
def create(self, req, body): # pylint: disable=function-redefined
return self._create(req, body)

@wsgi.Controller.authorize('get')
def members(self, req, id):
def _members(self, req, id):
"""Returns a list of share group snapshot members."""
context = req.environ['manila.context']

@@ -186,6 +224,14 @@ class ShareGroupSnapshotController(wsgi.Controller, wsgi.AdminActionsMixin):
snaps = self._view_builder.member_list(req, limited_list)
return snaps

@wsgi.Controller.api_version('2.31', '2.54', experimental=True)
def members(self, req, id):
return self._members(req, id)

@wsgi.Controller.api_version(SG_GRADUATION_VERSION) # noqa
def members(self, req, id): # pylint: disable=function-redefined
return self._members(req, id)

def _update(self, *args, **kwargs):
db.share_group_snapshot_update(*args, **kwargs)

@@ -195,12 +241,25 @@ class ShareGroupSnapshotController(wsgi.Controller, wsgi.AdminActionsMixin):
def _delete(self, context, resource, force=True):
db.share_group_snapshot_destroy(context.elevated(), resource['id'])

@wsgi.Controller.api_version('2.31', experimental=True)
@wsgi.Controller.api_version('2.31', '2.54', experimental=True)
@wsgi.action('reset_status')
def share_group_snapshot_reset_status(self, req, id, body):
return self._reset_status(req, id, body)

# pylint: disable=function-redefined
@wsgi.Controller.api_version(SG_GRADUATION_VERSION) # noqa
@wsgi.action('reset_status')
def share_group_snapshot_reset_status(self, req, id, body):
return self._reset_status(req, id, body)

@wsgi.Controller.api_version('2.31', experimental=True)
# pylint: enable=function-redefined
@wsgi.Controller.api_version('2.31', '2.54', experimental=True)
@wsgi.action('force_delete')
def share_group_snapshot_force_delete(self, req, id, body):
return self._force_delete(req, id, body)

# pylint: disable=function-redefined
@wsgi.Controller.api_version(SG_GRADUATION_VERSION) # noqa
@wsgi.action('force_delete')
def share_group_snapshot_force_delete(self, req, id, body):
return self._force_delete(req, id, body)


+ 54
- 15
manila/api/v2/share_group_type_specs.py View File

@@ -22,6 +22,8 @@ from manila import exception
from manila.i18n import _
from manila.share_group import share_group_types

SG_GRADUATION_VERSION = '2.55'


class ShareGroupTypeSpecsController(wsgi.Controller):
"""The share group type specs API controller for the OpenStack API."""
@@ -57,18 +59,24 @@ class ShareGroupTypeSpecsController(wsgi.Controller):
}
raise webob.exc.HTTPBadRequest(explanation=expl)

@wsgi.Controller.api_version('2.31', experimental=True)
@wsgi.Controller.authorize
def index(self, req, id):
@wsgi.Controller.authorize('index')
def _index(self, req, id):
"""Returns the list of group specs for a given share group type."""

context = req.environ['manila.context']
self._assert_share_group_type_exists(context, id)
return self._get_group_specs(context, id)

@wsgi.Controller.api_version('2.31', experimental=True)
@wsgi.Controller.authorize
def create(self, req, id, body=None):
@wsgi.Controller.api_version('2.31', '2.54', experimental=True)
def index(self, req, id):
return self._index(req, id)

@wsgi.Controller.api_version(SG_GRADUATION_VERSION) # noqa
def index(self, req, id): # pylint: disable=function-redefined
return self._index(req, id)

@wsgi.Controller.authorize('create')
def _create(self, req, id, body=None):
context = req.environ['manila.context']
if not self.is_valid_body(body, 'group_specs'):
raise webob.exc.HTTPBadRequest()
@@ -80,9 +88,16 @@ class ShareGroupTypeSpecsController(wsgi.Controller):
db.share_group_type_specs_update_or_create(context, id, specs)
return body

@wsgi.Controller.api_version('2.31', experimental=True)
@wsgi.Controller.authorize
def update(self, req, id, key, body=None):
@wsgi.Controller.api_version('2.31', '2.54', experimental=True)
def create(self, req, id, body=None):
return self._create(req, id, body)

@wsgi.Controller.api_version(SG_GRADUATION_VERSION) # noqa
def create(self, req, id, body=None): # pylint: disable=function-redefined
return self._create(req, id, body)

@wsgi.Controller.authorize('update')
def _update(self, req, id, key, body=None):
context = req.environ['manila.context']
if not body:
expl = _('Request body empty.')
@@ -98,9 +113,17 @@ class ShareGroupTypeSpecsController(wsgi.Controller):
db.share_group_type_specs_update_or_create(context, id, body)
return body

@wsgi.Controller.api_version('2.31', experimental=True)
@wsgi.Controller.authorize
def show(self, req, id, key):
@wsgi.Controller.api_version('2.31', '2.54', experimental=True)
def update(self, req, id, key, body=None):
return self._update(req, id, key, body)

# pylint: disable=function-redefined
@wsgi.Controller.api_version(SG_GRADUATION_VERSION) # noqa
def update(self, req, id, key, body=None):
return self._update(req, id, key, body)

@wsgi.Controller.authorize('show')
def _show(self, req, id, key):
"""Return a single group spec item."""
context = req.environ['manila.context']
self._assert_share_group_type_exists(context, id)
@@ -110,9 +133,17 @@ class ShareGroupTypeSpecsController(wsgi.Controller):
else:
raise webob.exc.HTTPNotFound()

@wsgi.Controller.api_version('2.31', experimental=True)
@wsgi.Controller.authorize
def delete(self, req, id, key):
# pylint: enable=function-redefined
@wsgi.Controller.api_version('2.31', '2.54', experimental=True)
def show(self, req, id, key):
return self._show(req, id, key)

@wsgi.Controller.api_version(SG_GRADUATION_VERSION) # noqa
def show(self, req, id, key): # pylint: disable=function-redefined
return self._show(req, id, key)

@wsgi.Controller.authorize('delete')
def _delete(self, req, id, key):
"""Deletes an existing group spec."""
context = req.environ['manila.context']
self._assert_share_group_type_exists(context, id)
@@ -122,6 +153,14 @@ class ShareGroupTypeSpecsController(wsgi.Controller):
raise webob.exc.HTTPNotFound(explanation=error.msg)
return webob.Response(status_int=http_client.NO_CONTENT)

@wsgi.Controller.api_version('2.31', '2.54', experimental=True)
def delete(self, req, id, key):
return self._delete(req, id, key)

@wsgi.Controller.api_version(SG_GRADUATION_VERSION) # noqa
def delete(self, req, id, key): # pylint: disable=function-redefined
return self._delete(req, id, key)

def _check_key_names(self, keys):
if not common.validate_key_names(keys):
expl = _('Key names can only contain alphanumeric characters, '


+ 86
- 19
manila/api/v2/share_group_types.py View File

@@ -25,6 +25,8 @@ from manila import exception
from manila.i18n import _
from manila.share_group import share_group_types

SG_GRADUATION_VERSION = '2.55'


class ShareGroupTypesController(wsgi.Controller):
"""The share group types API controller for the OpenStack API."""
@@ -41,16 +43,22 @@ class ShareGroupTypesController(wsgi.Controller):
msg = _("Project value (%s) must be in uuid format.") % project
raise webob.exc.HTTPBadRequest(explanation=msg)

@wsgi.Controller.api_version('2.31', experimental=True)
@wsgi.Controller.authorize
def index(self, req):
@wsgi.Controller.authorize('index')
def _index(self, req):
"""Returns the list of share group types."""
limited_types = self._get_share_group_types(req)
return self._view_builder.index(req, limited_types)

@wsgi.Controller.api_version('2.31', experimental=True)
@wsgi.Controller.authorize
def show(self, req, id):
@wsgi.Controller.api_version('2.31', '2.54', experimental=True)
def index(self, req):
return self._index(req)

@wsgi.Controller.api_version(SG_GRADUATION_VERSION) # noqa
def index(self, req): # pylint: disable=function-redefined
return self._index(req)

@wsgi.Controller.authorize('show')
def _show(self, req, id):
"""Return a single share group type item."""
context = req.environ['manila.context']
try:
@@ -62,9 +70,16 @@ class ShareGroupTypesController(wsgi.Controller):
share_group_type['id'] = six.text_type(share_group_type['id'])
return self._view_builder.show(req, share_group_type)

@wsgi.Controller.api_version('2.31', experimental=True)
@wsgi.Controller.authorize
def default(self, req):
@wsgi.Controller.api_version('2.31', '2.54', experimental=True)
def show(self, req, id):
return self._show(req, id)

@wsgi.Controller.api_version(SG_GRADUATION_VERSION) # noqa
def show(self, req, id): # pylint: disable=function-redefined
return self._show(req, id)

@wsgi.Controller.authorize('default')
def _default(self, req):
"""Return default share group type."""
context = req.environ['manila.context']
share_group_type = share_group_types.get_default(context)
@@ -75,6 +90,14 @@ class ShareGroupTypesController(wsgi.Controller):
share_group_type['id'] = six.text_type(share_group_type['id'])
return self._view_builder.show(req, share_group_type)

@wsgi.Controller.api_version('2.31', '2.54', experimental=True)
def default(self, req):
return self._default(req)

@wsgi.Controller.api_version(SG_GRADUATION_VERSION) # noqa
def default(self, req): # pylint: disable=function-redefined
return self._default(req)

def _get_share_group_types(self, req):
"""Helper function that returns a list of share group type dicts."""
filters = {}
@@ -110,8 +133,6 @@ class ShareGroupTypesController(wsgi.Controller):
msg = _('Invalid is_public filter [%s]') % is_public
raise exc.HTTPBadRequest(explanation=msg)

@wsgi.Controller.api_version('2.31', experimental=True)
@wsgi.action("create")
@wsgi.Controller.authorize('create')
def _create(self, req, body):
"""Creates a new share group type."""
@@ -153,8 +174,16 @@ class ShareGroupTypesController(wsgi.Controller):
raise webob.exc.HTTPNotFound()
return self._view_builder.show(req, share_group_type)

@wsgi.Controller.api_version('2.31', experimental=True)
@wsgi.action("delete")
@wsgi.Controller.api_version('2.31', '2.54', experimental=True)
@wsgi.action("create")
def create(self, req, body):
return self._create(req, body)

@wsgi.Controller.api_version(SG_GRADUATION_VERSION) # noqa
@wsgi.action("create")
def create(self, req, body): # pylint: disable=function-redefined
return self._create(req, body)

@wsgi.Controller.authorize('delete')
def _delete(self, req, id):
"""Deletes an existing group type."""
@@ -169,9 +198,18 @@ class ShareGroupTypesController(wsgi.Controller):
raise webob.exc.HTTPNotFound()
return webob.Response(status_int=http_client.NO_CONTENT)

@wsgi.Controller.api_version('2.31', experimental=True)
@wsgi.Controller.api_version('2.31', '2.54', experimental=True)
@wsgi.action("delete")
def delete(self, req, id):
return self._delete(req, id)

@wsgi.Controller.api_version(SG_GRADUATION_VERSION) # noqa
@wsgi.action("delete")
def delete(self, req, id): # pylint: disable=function-redefined
return self._delete(req, id)

@wsgi.Controller.authorize('list_project_access')
def share_group_type_access(self, req, id):
def _share_group_type_access(self, req, id):
context = req.environ['manila.context']
try:
share_group_type = share_group_types.get(
@@ -192,8 +230,15 @@ class ShareGroupTypesController(wsgi.Controller):
)
return {'share_group_type_access': projects}

@wsgi.Controller.api_version('2.31', experimental=True)
@wsgi.action('addProjectAccess')
@wsgi.Controller.api_version('2.31', '2.54', experimental=True)
def share_group_type_access(self, req, id):
return self._share_group_type_access(req, id)

# pylint: disable=function-redefined
@wsgi.Controller.api_version(SG_GRADUATION_VERSION) # noqa
def share_group_type_access(self, req, id):
return self._share_group_type_access(req, id)

@wsgi.Controller.authorize('add_project_access')
def _add_project_access(self, req, id, body):
context = req.environ['manila.context']
@@ -207,8 +252,18 @@ class ShareGroupTypesController(wsgi.Controller):
raise webob.exc.HTTPConflict(explanation=six.text_type(err))
return webob.Response(status_int=http_client.ACCEPTED)

@wsgi.Controller.api_version('2.31', experimental=True)
@wsgi.action('removeProjectAccess')
# pylint: enable=function-redefined
@wsgi.Controller.api_version('2.31', '2.54', experimental=True)
@wsgi.action('addProjectAccess')
def add_project_access(self, req, id, body):
return self._add_project_access(req, id, body)

# pylint: disable=function-redefined
@wsgi.Controller.api_version(SG_GRADUATION_VERSION) # noqa
@wsgi.action('addProjectAccess')
def add_project_access(self, req, id, body):
return self._add_project_access(req, id, body)

@wsgi.Controller.authorize('remove_project_access')
def _remove_project_access(self, req, id, body):
context = req.environ['manila.context']
@@ -222,6 +277,18 @@ class ShareGroupTypesController(wsgi.Controller):
raise webob.exc.HTTPNotFound(explanation=six.text_type(err))
return webob.Response(status_int=http_client.ACCEPTED)

# pylint: enable=function-redefined
@wsgi.Controller.api_version('2.31', '2.54', experimental=True)
@wsgi.action('removeProjectAccess')
def remove_project_access(self, req, id, body):
return self._remove_project_access(req, id, body)

# pylint: disable=function-redefined
@wsgi.Controller.api_version(SG_GRADUATION_VERSION) # noqa
@wsgi.action('removeProjectAccess')
def remove_project_access(self, req, id, body):
return self._remove_project_access(req, id, body)

def _assert_non_public_share_group_type(self, context, type_id):
try:
share_group_type = share_group_types.get(


+ 69
- 21
manila/api/v2/share_groups.py View File

@@ -33,6 +33,7 @@ from manila.share_group import share_group_types


LOG = log.getLogger(__name__)
SG_GRADUATION_VERSION = '2.55'


class ShareGroupController(wsgi.Controller, wsgi.AdminActionsMixin):
@@ -52,17 +53,23 @@ class ShareGroupController(wsgi.Controller, wsgi.AdminActionsMixin):
msg = _("Share group %s not found.") % share_group_id
raise exc.HTTPNotFound(explanation=msg)

@wsgi.Controller.api_version('2.31', experimental=True)
@wsgi.Controller.authorize('get')
def show(self, req, id):
def _show(self, req, id):
"""Return data about the given share group."""
context = req.environ['manila.context']
share_group = self._get_share_group(context, id)
return self._view_builder.detail(req, share_group)

@wsgi.Controller.api_version('2.31', experimental=True)
@wsgi.Controller.authorize
def delete(self, req, id):
@wsgi.Controller.api_version('2.31', '2.54', experimental=True)
def show(self, req, id):
return self._show(req, id)

@wsgi.Controller.api_version(SG_GRADUATION_VERSION) # noqa
def show(self, req, id): # pylint: disable=function-redefined
return self._show(req, id)

@wsgi.Controller.authorize('delete')
def _delete_share_group(self, req, id):
"""Delete a share group."""
context = req.environ['manila.context']

@@ -74,20 +81,33 @@ class ShareGroupController(wsgi.Controller, wsgi.AdminActionsMixin):
raise exc.HTTPConflict(explanation=six.text_type(e))
return webob.Response(status_int=http_client.ACCEPTED)

@wsgi.Controller.api_version('2.31', experimental=True)
@wsgi.Controller.authorize('get_all')
@wsgi.Controller.api_version('2.31', '2.54', experimental=True)
def delete(self, req, id):
return self._delete_share_group(req, id)

@wsgi.Controller.api_version(SG_GRADUATION_VERSION) # noqa
def delete(self, req, id): # pylint: disable=function-redefined
return self._delete_share_group(req, id)

@wsgi.Controller.api_version('2.31', '2.54', experimental=True)
def index(self, req):
"""Returns a summary list of share groups."""
return self._get_share_groups(req, is_detail=False)

@wsgi.Controller.api_version('2.31', experimental=True)
@wsgi.Controller.authorize('get_all')
@wsgi.Controller.api_version(SG_GRADUATION_VERSION) # noqa
def index(self, req): # pylint: disable=function-redefined
return self._get_share_groups(req, is_detail=False)

@wsgi.Controller.api_version('2.31', '2.54', experimental=True)
def detail(self, req):
"""Returns a detailed list of share groups."""
return self._get_share_groups(req, is_detail=True)

@wsgi.Controller.api_version(SG_GRADUATION_VERSION) # noqa
def detail(self, req): # pylint: disable=function-redefined
return self._get_share_groups(req, is_detail=True)

@wsgi.Controller.authorize('get_all')
def _get_share_groups(self, req, is_detail):
"""Returns a list of share groups, transformed through view builder."""
"""Returns a summary or detail list of share groups."""
context = req.environ['manila.context']

search_opts = {}
@@ -118,9 +138,8 @@ class ShareGroupController(wsgi.Controller, wsgi.AdminActionsMixin):
share_groups = self._view_builder.summary_list(req, limited_list)
return share_groups

@wsgi.Controller.api_version('2.31', experimental=True)
@wsgi.Controller.authorize
def update(self, req, id, body):
@wsgi.Controller.authorize('update')
def _update_share_group(self, req, id, body):
"""Update a share group."""
context = req.environ['manila.context']

@@ -140,10 +159,16 @@ class ShareGroupController(wsgi.Controller, wsgi.AdminActionsMixin):
context, share_group, share_group_data)
return self._view_builder.detail(req, share_group)

@wsgi.Controller.api_version('2.31', experimental=True)
@wsgi.response(202)
@wsgi.Controller.authorize
def create(self, req, body):
@wsgi.Controller.api_version('2.31', '2.54', experimental=True)
def update(self, req, id, body):
return self._update_share_group(req, id, body)

@wsgi.Controller.api_version(SG_GRADUATION_VERSION) # noqa
def update(self, req, id, body): # pylint: disable=function-redefined
return self._update_share_group(req, id, body)

@wsgi.Controller.authorize('create')
def _create(self, req, body):
"""Creates a new share group."""
context = req.environ['manila.context']

@@ -260,6 +285,16 @@ class ShareGroupController(wsgi.Controller, wsgi.AdminActionsMixin):
return self._view_builder.detail(
req, {k: v for k, v in new_share_group.items()})

@wsgi.Controller.api_version('2.31', '2.54', experimental=True)
@wsgi.response(202)
def create(self, req, body):
return self._create(req, body)

@wsgi.Controller.api_version(SG_GRADUATION_VERSION) # noqa
@wsgi.response(202)
def create(self, req, body): # pylint: disable=function-redefined
return self._create(req, body)

def _update(self, *args, **kwargs):
db.share_group_update(*args, **kwargs)

@@ -277,12 +312,25 @@ class ShareGroupController(wsgi.Controller, wsgi.AdminActionsMixin):

db.share_group_destroy(context.elevated(), resource['id'])

@wsgi.Controller.api_version('2.31', experimental=True)
@wsgi.Controller.api_version('2.31', '2.54', experimental=True)
@wsgi.action('reset_status')
def share_group_reset_status(self, req, id, body):
return self._reset_status(req, id, body)

@wsgi.Controller.api_version('2.31', experimental=True)
# pylint: disable=function-redefined
@wsgi.Controller.api_version(SG_GRADUATION_VERSION) # noqa
@wsgi.action('reset_status')
def share_group_reset_status(self, req, id, body):
return self._reset_status(req, id, body)

# pylint: enable=function-redefined
@wsgi.Controller.api_version('2.31', '2.54', experimental=True)
@wsgi.action('force_delete')
def share_group_force_delete(self, req, id, body):
return self._force_delete(req, id, body)

# pylint: disable=function-redefined
@wsgi.Controller.api_version(SG_GRADUATION_VERSION) # noqa
@wsgi.action('force_delete')
def share_group_force_delete(self, req, id, body):
return self._force_delete(req, id, body)


+ 88
- 23
manila/tests/api/v2/test_share_group_snapshots.py View File

@@ -36,6 +36,7 @@ from manila.tests.api import fakes
from manila.tests import db_utils

CONF = cfg.CONF
SG_GRADUATION_VERSION = '2.55'


@ddt.ddt
@@ -106,6 +107,13 @@ class ShareGroupSnapshotAPITest(test.TestCase):
del expected_member['share_proto']
return member, expected_member

def _get_fake_custom_request_and_context(self, microversion, experimental):
req = fakes.HTTPRequest.blank(
'/share-group-snapshots', version=microversion,
experimental=experimental)
req_context = req.environ['manila.context']
return req, req_context

def test_create_invalid_body(self):
body = {"not_group_snapshot": {}}

@@ -126,20 +134,25 @@ class ShareGroupSnapshotAPITest(test.TestCase):
self.mock_policy_check.assert_called_once_with(
self.context, self.resource_name, 'create')

def test_create(self):
@ddt.data({'microversion': '2.31', 'experimental': True},
{'microversion': SG_GRADUATION_VERSION, 'experimental': False})
@ddt.unpack
def test_create(self, microversion, experimental):
fake_snap, expected_snap = self._get_fake_share_group_snapshot()
fake_id = six.text_type(uuidutils.generate_uuid())
body = {"share_group_snapshot": {"share_group_id": fake_id}}
mock_create = self.mock_object(
self.controller.share_group_api, 'create_share_group_snapshot',
mock.Mock(return_value=fake_snap))
req, req_context = self._get_fake_custom_request_and_context(
microversion, experimental)

res_dict = self.controller.create(self.request, body)
res_dict = self.controller.create(req, body)

self.mock_policy_check.assert_called_once_with(
self.context, self.resource_name, 'create')
req_context, self.resource_name, 'create')
mock_create.assert_called_once_with(
self.context, share_group_id=fake_id)
req_context, share_group_id=fake_id)
res_dict['share_group_snapshot'].pop('links')

self.assertEqual(expected_snap, res_dict['share_group_snapshot'])
@@ -265,7 +278,11 @@ class ShareGroupSnapshotAPITest(test.TestCase):
self.mock_policy_check.assert_called_once_with(
self.context, self.resource_name, 'create')

def test_update_with_name_and_description(self):
@ddt.data({'microversion': '2.31', 'experimental': True},
{'microversion': SG_GRADUATION_VERSION, 'experimental': False})
@ddt.unpack
def test_update_with_name_and_description(self, microversion,
experimental):
fake_name = 'fake_name'
fake_description = 'fake_description'
fake_id = six.text_type(uuidutils.generate_uuid())
@@ -277,6 +294,8 @@ class ShareGroupSnapshotAPITest(test.TestCase):
mock_update = self.mock_object(
self.controller.share_group_api, 'update_share_group_snapshot',
mock.Mock(return_value=fake_snap))
req, req_context = self._get_fake_custom_request_and_context(
microversion, experimental)

body = {
"share_group_snapshot": {
@@ -284,16 +303,16 @@ class ShareGroupSnapshotAPITest(test.TestCase):
"name": fake_name,
}
}
res_dict = self.controller.update(self.request, fake_id, body)
res_dict = self.controller.update(req, fake_id, body)

res_dict['share_group_snapshot'].pop('links')

mock_update.assert_called_once_with(
self.context, fake_snap,
req_context, fake_snap,
{"name": fake_name, "description": fake_description})
self.assertEqual(expected_snap, res_dict['share_group_snapshot'])
self.mock_policy_check.assert_called_once_with(
self.context, self.resource_name, 'update')
req_context, self.resource_name, 'update')

def test_update_snapshot_not_found(self):
body = {"share_group_snapshot": {}}
@@ -334,19 +353,24 @@ class ShareGroupSnapshotAPITest(test.TestCase):
self.mock_policy_check.assert_called_once_with(
self.context, self.resource_name, 'update')

def test_list_index(self):
@ddt.data({'microversion': '2.31', 'experimental': True},
{'microversion': SG_GRADUATION_VERSION, 'experimental': False})
@ddt.unpack
def test_list_index(self, microversion, experimental):
fake_snap, expected_snap = self._get_fake_simple_share_group_snapshot()
self.mock_object(
self.controller.share_group_api, 'get_all_share_group_snapshots',
mock.Mock(return_value=[fake_snap]))
req, req_context = self._get_fake_custom_request_and_context(
microversion, experimental)

res_dict = self.controller.index(self.request)
res_dict = self.controller.index(req)

res_dict['share_group_snapshots'][0].pop('links')

self.assertEqual([expected_snap], res_dict['share_group_snapshots'])
self.mock_policy_check.assert_called_once_with(
self.context, self.resource_name, 'get_all')
req_context, self.resource_name, 'get_all')

def test_list_index_no_share_groups(self):
self.mock_object(
@@ -402,20 +426,25 @@ class ShareGroupSnapshotAPITest(test.TestCase):
self.mock_policy_check.assert_called_once_with(
req_context, self.resource_name, 'get_all')

def test_list_detail(self):
@ddt.data({'microversion': '2.31', 'experimental': True},
{'microversion': SG_GRADUATION_VERSION, 'experimental': False})
@ddt.unpack
def test_list_detail(self, microversion, experimental):
fake_snap, expected_snap = self._get_fake_share_group_snapshot()
self.mock_object(
self.controller.share_group_api, 'get_all_share_group_snapshots',
mock.Mock(return_value=[fake_snap]))
req, context = self._get_fake_custom_request_and_context(
microversion, experimental)

res_dict = self.controller.detail(self.request)
res_dict = self.controller.detail(req)

res_dict['share_group_snapshots'][0].pop('links')

self.assertEqual(1, len(res_dict['share_group_snapshots']))
self.assertEqual(expected_snap, res_dict['share_group_snapshots'][0])
self.mock_policy_check.assert_called_once_with(
self.context, self.resource_name, 'get_all')
context, self.resource_name, 'get_all')

def test_list_detail_no_share_groups(self):
self.mock_object(
@@ -456,8 +485,7 @@ class ShareGroupSnapshotAPITest(test.TestCase):
mock.Mock(return_value=[fake_snap, fake_snap2]))
req = fakes.HTTPRequest.blank(
'/share-group-snapshots?limit=1&offset=1',
version=self.api_version,
experimental=True)
version=self.api_version, experimental=True)
req_context = req.environ['manila.context']

res_dict = self.controller.detail(req)
@@ -469,19 +497,24 @@ class ShareGroupSnapshotAPITest(test.TestCase):
self.mock_policy_check.assert_called_once_with(
req_context, self.resource_name, 'get_all')

def test_delete(self):
@ddt.data({'microversion': '2.31', 'experimental': True},
{'microversion': SG_GRADUATION_VERSION, 'experimental': False})
@ddt.unpack
def test_delete(self, microversion, experimental):
fake_snap, expected_snap = self._get_fake_share_group_snapshot()
self.mock_object(
self.controller.share_group_api, 'get_share_group_snapshot',
mock.Mock(return_value=fake_snap))
self.mock_object(
self.controller.share_group_api, 'delete_share_group_snapshot')
req, req_context = self._get_fake_custom_request_and_context(
microversion, experimental)

res = self.controller.delete(self.request, fake_snap['id'])
res = self.controller.delete(req, fake_snap['id'])

self.assertEqual(202, res.status_code)
self.mock_policy_check.assert_called_once_with(
self.context, self.resource_name, 'delete')
req_context, self.resource_name, 'delete')

def test_delete_not_found(self):
fake_snap, expected_snap = self._get_fake_share_group_snapshot()
@@ -513,19 +546,24 @@ class ShareGroupSnapshotAPITest(test.TestCase):
self.mock_policy_check.assert_called_once_with(
self.context, self.resource_name, 'delete')

def test_show(self):
@ddt.data({'microversion': '2.31', 'experimental': True},
{'microversion': SG_GRADUATION_VERSION, 'experimental': False})
@ddt.unpack
def test_show(self, microversion, experimental):
fake_snap, expected_snap = self._get_fake_share_group_snapshot()
self.mock_object(
self.controller.share_group_api, 'get_share_group_snapshot',
mock.Mock(return_value=fake_snap))
req, req_context = self._get_fake_custom_request_and_context(
microversion, experimental)

res_dict = self.controller.show(self.request, fake_snap['id'])
res_dict = self.controller.show(req, fake_snap['id'])

res_dict['share_group_snapshot'].pop('links')

self.assertEqual(expected_snap, res_dict['share_group_snapshot'])
self.mock_policy_check.assert_called_once_with(
self.context, self.resource_name, 'get')
req_context, self.resource_name, 'get')

def test_show_share_group_not_found(self):
fake_snap, expected_snap = self._get_fake_share_group_snapshot()
@@ -553,7 +591,6 @@ class ShareGroupSnapshotAPITest(test.TestCase):
share_group_snapshot['id'])
req = fakes.HTTPRequest.blank(path, script_name=path, version=version)
req.headers[wsgi.API_VERSION_REQUEST_HEADER] = version
req.headers[wsgi.EXPERIMENTAL_API_REQUEST_HEADER] = 'True'
return share_group_snapshot, req

@ddt.data(*fakes.fixture_force_delete_with_different_roles)
@@ -568,6 +605,7 @@ class ShareGroupSnapshotAPITest(test.TestCase):
body = {action_name: {'status': constants.STATUS_ERROR}}
req.body = six.b(jsonutils.dumps(body))
req.headers['X-Openstack-Manila-Api-Version'] = self.api_version
req.headers['X-Openstack-Manila-Api-Experimental'] = True
req.environ['manila.context'] = ctxt

with mock.patch.object(
@@ -577,6 +615,19 @@ class ShareGroupSnapshotAPITest(test.TestCase):
# Validate response
self.assertEqual(resp_code, resp.status_int)

@ddt.data({'microversion': '2.31', 'experimental': True},
{'microversion': SG_GRADUATION_VERSION, 'experimental': False})
@ddt.unpack
def test__force_delete_call(self, microversion, experimental):
self.mock_object(self.controller, '_force_delete')
req, _junk = self._get_fake_custom_request_and_context(
microversion, experimental)
sg_id = 'fake'
body = {'force_delete': {}}

self.controller.share_group_snapshot_force_delete(req, sg_id, body)
self.controller._force_delete.assert_called_once_with(req, sg_id, body)

@ddt.data(*fakes.fixture_reset_status_with_different_roles)
@ddt.unpack
def test_share_group_snapshot_reset_status_with_different_roles(
@@ -589,6 +640,7 @@ class ShareGroupSnapshotAPITest(test.TestCase):
req.headers['content-type'] = 'application/json'
req.body = six.b(jsonutils.dumps(body))
req.headers['X-Openstack-Manila-Api-Version'] = self.api_version
req.headers['X-Openstack-Manila-Api-Experimental'] = True
req.environ['manila.context'] = ctxt

with mock.patch.object(
@@ -600,3 +652,16 @@ class ShareGroupSnapshotAPITest(test.TestCase):

actual_model = db.share_group_snapshot_get(ctxt, group_snap['id'])
self.assertEqual(valid_status, actual_model['status'])

@ddt.data({'microversion': '2.31', 'experimental': True},
{'microversion': SG_GRADUATION_VERSION, 'experimental': False})
@ddt.unpack
def test__reset_status_call(self, microversion, experimental):
self.mock_object(self.controller, '_reset_status')
req, _junk = self._get_fake_custom_request_and_context(
microversion, experimental)
sg_id = 'fake'
body = {'reset_status': {'status': constants.STATUS_ERROR}}

self.controller.share_group_snapshot_reset_status(req, sg_id, body)
self.controller._reset_status.assert_called_once_with(req, sg_id, body)

+ 55
- 11
manila/tests/api/v2/test_share_group_type_specs.py View File

@@ -62,11 +62,14 @@ def get_group_specs_dict(group_specs, include_required=True):
return {'group_specs': group_specs}


def fake_request(url, admin=False, experimental=True, version='2.31',
def fake_request(url, admin=False, version='2.31', experimental=True,
**kwargs):
return fakes.HTTPRequest.blank(
url, use_admin_context=admin, experimental=experimental,
version=version, **kwargs)
url, use_admin_context=admin, version=version,
experimental=experimental, **kwargs)


SG_GRADUATION_VERSION = '2.55'


@ddt.ddt
@@ -82,11 +85,15 @@ class ShareGroupTypesSpecsTest(test.TestCase):
self.resource_name = self.controller.resource_name
self.mock_policy_check = self.mock_object(policy, 'check_policy')

def test_index(self):
@ddt.data({'microversion': '2.31', 'experimental': True},
{'microversion': SG_GRADUATION_VERSION, 'experimental': False})
@ddt.unpack
def test_index(self, microversion, experimental):
self.mock_object(
manila.db, 'share_group_type_specs_get',
return_share_group_type_specs)
req = fake_request(self.api_path)
req = fake_request(self.api_path, version=microversion,
experimental=experimental)
req_context = req.environ['manila.context']

res_dict = self.controller.index(req, 1)
@@ -107,10 +114,14 @@ class ShareGroupTypesSpecsTest(test.TestCase):
self.mock_policy_check.assert_called_once_with(
req_context, self.resource_name, 'index')

def test_show(self):
@ddt.data({'microversion': '2.31', 'experimental': True},
{'microversion': SG_GRADUATION_VERSION, 'experimental': False})
@ddt.unpack
def test_show(self, microversion, experimental):
self.mock_object(manila.db, 'share_group_type_specs_get',
return_share_group_type_specs)
req = fake_request(self.api_path + '/key5')
req = fake_request(self.api_path + '/key5', version=microversion,
experimental=experimental)
req_context = req.environ['manila.context']

res_dict = self.controller.show(req, 1, 'key5')
@@ -131,10 +142,14 @@ class ShareGroupTypesSpecsTest(test.TestCase):
self.mock_policy_check.assert_called_once_with(
req_context, self.resource_name, 'show')

def test_delete(self):
@ddt.data({'microversion': '2.31', 'experimental': True},
{'microversion': SG_GRADUATION_VERSION, 'experimental': False})
@ddt.unpack
def test_delete(self, microversion, experimental):
self.mock_object(manila.db, 'share_group_type_specs_delete',
delete_share_group_type_specs)
req = fake_request(self.api_path + '/key5')
req = fake_request(self.api_path + '/key5', version=microversion,
experimental=experimental)
req_context = req.environ['manila.context']

self.controller.delete(req, 1, 'key5')
@@ -162,12 +177,26 @@ class ShareGroupTypesSpecsTest(test.TestCase):
*[{CONSISTENT_SNAPSHOTS: v}
for v in strutils.TRUE_STRINGS + strutils.FALSE_STRINGS]
)
def test_create(self, data):
def test_create_experimental(self, data):
self._validate_create(data)

@ddt.data(
get_group_specs_dict({}),
{'foo': 'bar'},
{CONSISTENT_SNAPSHOTS + 'foo': True},
{'foo' + CONSISTENT_SNAPSHOTS: False}
)
def test_create_non_experimental(self, data):
self._validate_create(data, microversion=SG_GRADUATION_VERSION,
experimental=False)

def _validate_create(self, data, microversion='2.31', experimental=True):
body = {'group_specs': data}
mock_spec_update_or_create = self.mock_object(
manila.db, 'share_group_type_specs_update_or_create',
mock.Mock(return_value=return_create_share_group_type_specs))
req = fake_request(self.api_path)
req = fake_request(self.api_path, version=microversion,
experimental=experimental)
req_context = req.environ['manila.context']

res_dict = self.controller.create(req, 1, body)
@@ -292,6 +321,21 @@ class ShareGroupTypesSpecsTest(test.TestCase):
self.mock_policy_check.assert_called_once_with(
req_context, self.resource_name, 'create')

@ddt.data({'microversion': '2.31', 'experimental': True},
{'microversion': SG_GRADUATION_VERSION, 'experimental': False})
@ddt.unpack
def test__update_call(self, microversion, experimental):
req = fake_request(self.api_path + '/key1', version=microversion,
experimental=experimental)
sg_id = 'fake_id'
key = 'fake_key'
body = {"group_specs": {"key1": "fake_value"}}
self.mock_object(self.controller, '_update')

self.controller.update(req, sg_id, key, body)

self.controller._update.assert_called_once_with(req, sg_id, key, body)

def test_update_item_too_many_keys(self):
self.mock_object(manila.db, 'share_group_type_specs_update_or_create')
body = {"key1": "value1", "key2": "value2"}


+ 81
- 40
manila/tests/api/v2/test_share_group_types.py View File

@@ -68,8 +68,10 @@ GROUP_TYPE_3 = {
'share_types': [],
}

SG_GRADUATION_VERSION = '2.55'

def fake_request(url, admin=False, experimental=True, version='2.31',

def fake_request(url, admin=False, version='2.31', experimental=True,
**kwargs):

return fakes.HTTPRequest.blank(
@@ -91,11 +93,15 @@ class ShareGroupTypesAPITest(test.TestCase):
self.resource_name = self.controller.resource_name
self.mock_object(policy, 'check_policy', mock.Mock(return_value=True))

def test_share_group_types_index(self):
@ddt.data({'microversion': '2.31', 'experimental': True},
{'microversion': SG_GRADUATION_VERSION, 'experimental': False})
@ddt.unpack
def test_share_group_types_index(self, microversion, experimental):
fake_types = {GROUP_TYPE_1['name']: GROUP_TYPE_1}
mock_get_all = self.mock_object(
share_group_types, 'get_all', mock.Mock(return_value=fake_types))
req = fake_request('/v2/fake/share-group-types', admin=False)
req = fake_request('/v2/fake/share-group-types', admin=False,
version=microversion, experimental=experimental)
expected_list = [{
'id': GROUP_TYPE_1['id'],
'name': GROUP_TYPE_1['name'],
@@ -103,6 +109,8 @@ class ShareGroupTypesAPITest(test.TestCase):
'group_specs': {},
'share_types': [],
}]
if self.is_microversion_ge(microversion, '2.46'):
expected_list[0]['is_default'] = False

res_dict = self.controller.index(req)

@@ -159,7 +167,8 @@ class ShareGroupTypesAPITest(test.TestCase):
def test_share_group_types_index_not_experimental(self):
self.mock_object(
share_group_types, 'get_all', mock.Mock(return_value={}))
req = fake_request('/v2/fake/share-group-types', experimental=False)
req = fake_request('/v2/fake/share-group-types', experimental=False,
version='2.54')

self.assertRaises(
exception.VersionNotFoundForAPIMethod, self.controller.index, req)
@@ -183,12 +192,16 @@ class ShareGroupTypesAPITest(test.TestCase):

self.assertEqual(0, len(res_dict['share_group_types']))

def test_share_group_types_show(self):
@ddt.data({'microversion': '2.31', 'experimental': True},
{'microversion': SG_GRADUATION_VERSION, 'experimental': False})
@ddt.unpack
def test_share_group_types_show(self, microversion, experimental):
mock_get = self.mock_object(
share_group_types, 'get',
mock.Mock(return_value=GROUP_TYPE_1))
req = fake_request(
'/v2/fake/share-group-types/%s' % GROUP_TYPE_1['id'])
'/v2/fake/share-group-types/%s' % GROUP_TYPE_1['id'],
version=microversion, experimental=experimental)
expected_type = {
'id': GROUP_TYPE_1['id'],
'name': GROUP_TYPE_1['name'],
@@ -196,6 +209,8 @@ class ShareGroupTypesAPITest(test.TestCase):
'group_specs': {},
'share_types': [],
}
if self.is_microversion_ge(microversion, '2.46'):
expected_type['is_default'] = False

res_dict = self.controller.show(req, GROUP_TYPE_1['id'])

@@ -233,11 +248,15 @@ class ShareGroupTypesAPITest(test.TestCase):

mock_get.assert_called_once_with(mock.ANY, GROUP_TYPE_2['id'])

def test_share_group_types_default(self):
@ddt.data({'microversion': '2.31', 'experimental': True},
{'microversion': SG_GRADUATION_VERSION, 'experimental': False})
@ddt.unpack
def test_share_group_types_default(self, microversion, experimental):
mock_get = self.mock_object(
share_group_types, 'get_default',
mock.Mock(return_value=GROUP_TYPE_2))
req = fake_request('/v2/fake/share-group-types/default')
req = fake_request('/v2/fake/share-group-types/default',
version=microversion, experimental=experimental)
expected_type = {
'id': GROUP_TYPE_2['id'],
'name': GROUP_TYPE_2['name'],
@@ -245,6 +264,8 @@ class ShareGroupTypesAPITest(test.TestCase):
'group_specs': {'consistent_snapshots': 'true'},
'share_types': [SHARE_TYPE_ID],
}
if self.is_microversion_ge(microversion, '2.46'):
expected_type['is_default'] = False

res_dict = self.controller.default(req)

@@ -260,14 +281,18 @@ class ShareGroupTypesAPITest(test.TestCase):

mock_get.assert_called_once_with(mock.ANY)

def test_share_group_types_delete(self):
@ddt.data({'microversion': '2.31', 'experimental': True},
{'microversion': SG_GRADUATION_VERSION, 'experimental': False})
@ddt.unpack
def test_share_group_types_delete(self, microversion, experimental):
mock_get = self.mock_object(
share_group_types, 'get', mock.Mock(return_value=GROUP_TYPE_1))
mock_destroy = self.mock_object(share_group_types, 'destroy')
req = fake_request(
'/v2/fake/share-group-types/%s' % GROUP_TYPE_1['id'])
'/v2/fake/share-group-types/%s' % GROUP_TYPE_1['id'],
version=microversion, experimental=experimental)

self.controller._delete(req, GROUP_TYPE_1['id'])
self.controller.delete(req, GROUP_TYPE_1['id'])

mock_get.assert_called_once_with(mock.ANY, GROUP_TYPE_1['id'])
mock_destroy.assert_called_once_with(mock.ANY, GROUP_TYPE_1['id'])
@@ -280,19 +305,23 @@ class ShareGroupTypesAPITest(test.TestCase):
req = fake_request(
'/v2/fake/share-group-types/%s' % GROUP_TYPE_2['id'])

self.assertRaises(webob.exc.HTTPNotFound, self.controller._delete,
self.assertRaises(webob.exc.HTTPNotFound, self.controller.delete,
req, GROUP_TYPE_2['id'])

mock_get.assert_called_once_with(mock.ANY, GROUP_TYPE_2['id'])

def test_create_minimal(self):
@ddt.data({'microversion': '2.31', 'experimental': True},
{'microversion': SG_GRADUATION_VERSION, 'experimental': False})
@ddt.unpack
def test_create_minimal(self, microversion, experimental):
fake_type = copy.deepcopy(GROUP_TYPE_1)
fake_type['share_types'] = [{'share_type_id': SHARE_TYPE_ID}]
mock_create = self.mock_object(share_group_types, 'create')
mock_get = self.mock_object(
share_group_types, 'get_by_name',
mock.Mock(return_value=fake_type))
req = fake_request('/v2/fake/share-group-types')
req = fake_request('/v2/fake/share-group-types', version=microversion,
experimental=experimental)
fake_body = {'share_group_type': {
'name': GROUP_TYPE_1['name'],
'share_types': [SHARE_TYPE_ID],
@@ -304,8 +333,10 @@ class ShareGroupTypesAPITest(test.TestCase):
'group_specs': {},
'share_types': [SHARE_TYPE_ID],
}
if self.is_microversion_ge(microversion, '2.46'):
expected_type['is_default'] = False

res_dict = self.controller._create(req, fake_body)
res_dict = self.controller.create(req, fake_body)

mock_create.assert_called_once_with(
mock.ANY, GROUP_TYPE_1['name'],
@@ -338,7 +369,7 @@ class ShareGroupTypesAPITest(test.TestCase):
'share_types': [SHARE_TYPE_ID],
}

res_dict = self.controller._create(req, fake_body)
res_dict = self.controller.create(req, fake_body)

mock_create.assert_called_once_with(
mock.ANY, GROUP_TYPE_1['name'], [SHARE_TYPE_ID], specs,
@@ -366,7 +397,7 @@ class ShareGroupTypesAPITest(test.TestCase):
}}

self.assertRaises(
webob.exc.HTTPBadRequest, self.controller._create, req, fake_body)
webob.exc.HTTPBadRequest, self.controller.create, req, fake_body)

self.assertEqual(0, mock_create.call_count)
self.assertEqual(0, mock_get.call_count)
@@ -393,7 +424,7 @@ class ShareGroupTypesAPITest(test.TestCase):
'share_types': [SHARE_TYPE_ID],
}

res_dict = self.controller._create(req, fake_body)
res_dict = self.controller.create(req, fake_body)

mock_create.assert_called_once_with(
mock.ANY, GROUP_TYPE_1['name'], [SHARE_TYPE_ID], {}, False)
@@ -412,7 +443,7 @@ class ShareGroupTypesAPITest(test.TestCase):
}}

self.assertRaises(
webob.exc.HTTPConflict, self.controller._create, req, fake_body)
webob.exc.HTTPConflict, self.controller.create, req, fake_body)

mock_create.assert_called_once_with(
mock.ANY, GROUP_TYPE_1['name'], [SHARE_TYPE_ID], {}, True)
@@ -422,7 +453,7 @@ class ShareGroupTypesAPITest(test.TestCase):
fake_body = {'share_group_type': {'share_types': [SHARE_TYPE_ID]}}

self.assertRaises(
webob.exc.HTTPBadRequest, self.controller._create, req, fake_body)
webob.exc.HTTPBadRequest, self.controller.create, req, fake_body)

def test_create_invalid_request_missing_share_types(self):
req = fake_request('/v2/fake/share-group-types')
@@ -430,7 +461,7 @@ class ShareGroupTypesAPITest(test.TestCase):

self.assertRaises(
webob.exc.HTTPBadRequest,
self.controller._create, req, fake_body)
self.controller.create, req, fake_body)

def test_create_provided_share_type_does_not_exist(self):
req = fake_request('/v2/fake/share-group-types', admin=True)
@@ -443,7 +474,7 @@ class ShareGroupTypesAPITest(test.TestCase):

self.assertRaises(
webob.exc.HTTPNotFound,
self.controller._create, req, fake_body)
self.controller.create, req, fake_body)

@ddt.data(('2.45', True), ('2.45', False),
('2.46', True), ('2.46', False))
@@ -465,7 +496,7 @@ class ShareGroupTypesAPITest(test.TestCase):
'name': GROUP_TYPE_1['name'],
'share_types': [SHARE_TYPE_ID],
}}
res_dict = self.controller._create(req, fake_body)
res_dict = self.controller.create(req, fake_body)
if self.is_microversion_ge(version, '2.46'):
self.assertIn('is_default', res_dict['share_group_type'])
self.assertIs(False, res_dict['share_group_type']['is_default'])
@@ -489,7 +520,7 @@ class ShareGroupTypesAPITest(test.TestCase):
'name': GROUP_TYPE_3['name'],
'share_types': [SHARE_TYPE_ID],
}}
res_dict = self.controller._create(req, fake_body)
res_dict = self.controller.create(req, fake_body)
if self.is_microversion_ge(version, '2.46'):
self.assertIn('is_default', res_dict['share_group_type'])
self.assertIs(True, res_dict['share_group_type']['is_default'])
@@ -538,6 +569,7 @@ class ShareGroupTypesAPITest(test.TestCase):
self.assertNotIn('is_default', res_dict['share_group_type'])


@ddt.ddt
class ShareGroupTypeAccessTest(test.TestCase):

def setUp(self):
@@ -584,16 +616,21 @@ class ShareGroupTypeAccessTest(test.TestCase):
webob.exc.HTTPNotFound,
self.controller.share_group_type_access, req, GROUP_TYPE_2['id'])

def test_add_project_access(self):
@ddt.data({'microversion': '2.31', 'experimental': True},
{'microversion': SG_GRADUATION_VERSION, 'experimental': False})
@ddt.unpack
def test_add_project_access(self, microversion, experimental):
self.mock_object(share_group_types, 'get',
mock.Mock(return_value=GROUP_TYPE_2))
mock_add_access = self.mock_object(
share_group_types, 'add_share_group_type_access')
body = {'addProjectAccess': {'project': PROJ1_UUID}}
req = fake_request(
'/v2/fake/share-group-types/%s' % GROUP_TYPE_2['id'], admin=True)
'/v2/fake/share-group-types/%s' % GROUP_TYPE_2['id'], admin=True,
experimental=experimental, version=microversion
)

response = self.controller._add_project_access(
response = self.controller.add_project_access(
req, GROUP_TYPE_2['id'], body)

mock_add_access.assert_called_once_with(
@@ -611,7 +648,7 @@ class ShareGroupTypeAccessTest(test.TestCase):

self.assertRaises(
webob.exc.HTTPNotFound,
self.controller._add_project_access, req, GROUP_TYPE_2['id'], body)
self.controller.add_project_access, req, GROUP_TYPE_2['id'], body)

def test_add_project_access_missing_project_in_body(self):
body = {'addProjectAccess': {}}
@@ -620,7 +657,7 @@ class ShareGroupTypeAccessTest(test.TestCase):

self.assertRaises(
webob.exc.HTTPBadRequest,
self.controller._add_project_access, req, GROUP_TYPE_2['id'], body)
self.controller.add_project_access, req, GROUP_TYPE_2['id'], body)

def test_add_project_access_missing_add_project_access_in_body(self):
body = {}
@@ -629,7 +666,7 @@ class ShareGroupTypeAccessTest(test.TestCase):

self.assertRaises(
webob.exc.HTTPBadRequest,
self.controller._add_project_access, req, GROUP_TYPE_2['id'], body)
self.controller.add_project_access, req, GROUP_TYPE_2['id'], body)

def test_add_project_access_with_already_added_access(self):
self.mock_object(
@@ -645,7 +682,7 @@ class ShareGroupTypeAccessTest(test.TestCase):

self.assertRaises(
webob.exc.HTTPConflict,
self.controller._add_project_access, req, GROUP_TYPE_2['id'], body)
self.controller.add_project_access, req, GROUP_TYPE_2['id'], body)

mock_add_access.assert_called_once_with(
mock.ANY, GROUP_TYPE_2['id'], PROJ1_UUID)
@@ -659,18 +696,22 @@ class ShareGroupTypeAccessTest(test.TestCase):

self.assertRaises(
webob.exc.HTTPConflict,
self.controller._add_project_access, req, GROUP_TYPE_1['id'], body)
self.controller.add_project_access, req, GROUP_TYPE_1['id'], body)

def test_remove_project_access(self):
@ddt.data({'microversion': '2.31', 'experimental': True},
{'microversion': SG_GRADUATION_VERSION, 'experimental': False})
@ddt.unpack
def test_remove_project_access(self, microversion, experimental):
self.mock_object(
share_group_types, 'get', mock.Mock(return_value=GROUP_TYPE_2))
mock_remove_access = self.mock_object(
share_group_types, 'remove_share_group_type_access')
body = {'removeProjectAccess': {'project': PROJ1_UUID}}
req = fake_request(
'/v2/fake/share-group-types/%s' % GROUP_TYPE_2['id'], admin=True)
'/v2/fake/share-group-types/%s' % GROUP_TYPE_2['id'], admin=True,
version=microversion, experimental=experimental)

response = self.controller._remove_project_access(
response = self.controller.remove_project_access(
req, GROUP_TYPE_2['id'], body)

mock_remove_access.assert_called_once_with(
@@ -690,7 +731,7 @@ class ShareGroupTypeAccessTest(test.TestCase):

self.assertRaises(
webob.exc.HTTPNotFound,
self.controller._remove_project_access,
self.controller.remove_project_access,
req, GROUP_TYPE_2['id'], body)

mock_remove_access.assert_called_once_with(
@@ -704,7 +745,7 @@ class ShareGroupTypeAccessTest(test.TestCase):
'/v2/fake/share-group-types/%s' % GROUP_TYPE_1['id'], admin=True)

self.assertRaises(webob.exc.HTTPConflict,
self.controller._remove_project_access, req,
self.controller.remove_project_access, req,
GROUP_TYPE_1['id'], body)

def test_remove_project_access_non_existent_type(self):
@@ -717,7 +758,7 @@ class ShareGroupTypeAccessTest(test.TestCase):
'/v2/fake/share-group-types/%s' % GROUP_TYPE_2['id'], admin=True)

self.assertRaises(webob.exc.HTTPNotFound,
self.controller._remove_project_access, req,
self.controller.remove_project_access, req,
GROUP_TYPE_2['id'], body)

def test_remove_project_access_missing_project_in_body(self):
@@ -726,7 +767,7 @@ class ShareGroupTypeAccessTest(test.TestCase):
'/v2/fake/share-group-types/%s' % GROUP_TYPE_2['id'], admin=True)

self.assertRaises(webob.exc.HTTPBadRequest,
self.controller._remove_project_access, req,
self.controller.remove_project_access, req,
GROUP_TYPE_2['id'], body)

def test_remove_project_access_missing_remove_project_access_in_body(self):
@@ -735,5 +776,5 @@ class ShareGroupTypeAccessTest(test.TestCase):
'/v2/fake/share-group-types/%s' % GROUP_TYPE_2['id'], admin=True)

self.assertRaises(webob.exc.HTTPBadRequest,
self.controller._remove_project_access, req,
self.controller.remove_project_access, req,
GROUP_TYPE_2['id'], body)

+ 87
- 27
manila/tests/api/v2/test_share_groups.py View File

@@ -40,6 +40,7 @@ from manila.tests import db_utils


CONF = cfg.CONF
SG_GRADUATION_VERSION = '2.55'


@ddt.ddt
@@ -137,22 +138,33 @@ class ShareGroupAPITest(test.TestCase):
expected_share_group['links'] = mock.ANY
return share_group, expected_share_group

def test_share_group_create(self):
def _get_fake_custom_request_and_context(self, microversion, experimental):
req = fakes.HTTPRequest.blank(
'/share-groups', version=microversion, experimental=experimental)
req_context = req.environ['manila.context']
return req, req_context

@ddt.data({'microversion': '2.34', 'experimental': True},
{'microversion': SG_GRADUATION_VERSION, 'experimental': False})
@ddt.unpack
def test_share_group_create(self, microversion, experimental):
fake, expected = self._get_fake_share_group()
self.mock_object(share_types, 'get_default_share_type',
mock.Mock(return_value=self.fake_share_type))
self.mock_object(self.controller.share_group_api, 'create',
mock.Mock(return_value=fake))
req, req_context = self._get_fake_custom_request_and_context(
microversion, experimental)
body = {"share_group": {}}

res_dict = self.controller.create(self.request, body)
res_dict = self.controller.create(req, body)

self.controller.share_group_api.create.assert_called_once_with(
self.context, share_group_type_id=self.fake_share_group_type['id'],
req_context, share_group_type_id=self.fake_share_group_type['id'],
share_type_ids=[self.fake_share_type['id']])
self.assertEqual(expected, res_dict['share_group'])
self.mock_policy_check.assert_called_once_with(
self.context, self.resource_name, 'create')
req_context, self.resource_name, 'create')

def test_group_create_invalid_group_snapshot_state(self):
fake_snap_id = six.text_type(uuidutils.generate_uuid())
@@ -622,7 +634,11 @@ class ShareGroupAPITest(test.TestCase):
self.mock_policy_check.assert_called_once_with(
self.context, self.resource_name, 'create')

def test_share_group_update_with_name_and_description(self):
@ddt.data({'microversion': '2.34', 'experimental': True},
{'microversion': SG_GRADUATION_VERSION, 'experimental': False})
@ddt.unpack
def test_share_group_update_with_name_and_description(
self, microversion, experimental):
fake_name = 'fake_name'
fake_description = 'fake_description'
fake_group, expected_group = self._get_fake_share_group(
@@ -631,22 +647,23 @@ class ShareGroupAPITest(test.TestCase):
mock.Mock(return_value=fake_group))
self.mock_object(self.controller.share_group_api, 'update',
mock.Mock(return_value=fake_group))
req, req_context = self._get_fake_custom_request_and_context(
microversion, experimental)
body = {
"share_group": {
"name": fake_name,
"description": fake_description,
}
}
context = self.request.environ['manila.context']

res_dict = self.controller.update(self.request, fake_group['id'], body)
res_dict = self.controller.update(req, fake_group['id'], body)

self.controller.share_group_api.update.assert_called_once_with(
context, fake_group,
req_context, fake_group,
{"name": fake_name, "description": fake_description})
self.assertEqual(expected_group, res_dict['share_group'])
self.mock_policy_check.assert_called_once_with(
self.context, self.resource_name, 'update')
req_context, self.resource_name, 'update')

def test_share_group_update_group_not_found(self):
body = {"share_group": {}}
@@ -692,16 +709,21 @@ class ShareGroupAPITest(test.TestCase):
self.mock_policy_check.assert_called_once_with(
self.context, self.resource_name, 'update')

def test_share_group_list_index(self):
@ddt.data({'microversion': '2.31', 'experimental': True},
{'microversion': SG_GRADUATION_VERSION, 'experimental': False})
@ddt.unpack
def test_share_group_list_index(self, microversion, experimental):
fake, expected = self._get_fake_simple_share_group()
self.mock_object(
share_group_api.API, 'get_all', mock.Mock(return_value=[fake]))
req, req_context = self._get_fake_custom_request_and_context(
microversion, experimental)

res_dict = self.controller.index(self.request)
res_dict = self.controller.index(req)

self.assertEqual([expected], res_dict['share_groups'])
self.mock_policy_check.assert_called_once_with(
self.context, self.resource_name, 'get_all')
req_context, self.resource_name, 'get_all')

def test_share_group_list_index_no_groups(self):
self.mock_object(
@@ -758,8 +780,7 @@ class ShareGroupAPITest(test.TestCase):
mock.Mock(return_value=[fake, fake2]))
req = fakes.HTTPRequest.blank(
'/share-groups?name~=fake&description~=fake',
version='2.36',
experimental=True)
version='2.36', experimental=True)
req_context = req.environ['manila.context']

res_dict = self.controller.index(req)
@@ -771,16 +792,21 @@ class ShareGroupAPITest(test.TestCase):
self.mock_policy_check.assert_called_once_with(
req_context, self.resource_name, 'get_all')

def test_share_group_list_detail(self):
@ddt.data({'microversion': '2.34', 'experimental': True},
{'microversion': SG_GRADUATION_VERSION, 'experimental': False})
@ddt.unpack
def test_share_group_list_detail(self, microversion, experimental):
fake, expected = self._get_fake_share_group()
self.mock_object(
share_group_api.API, 'get_all', mock.Mock(return_value=[fake]))
req, req_context = self._get_fake_custom_request_and_context(
microversion, experimental)

res_dict = self.controller.detail(self.request)
res_dict = self.controller.detail(req)

self.assertEqual([expected], res_dict['share_groups'])
self.mock_policy_check.assert_called_once_with(
self.context, self.resource_name, 'get_all')
req_context, self.resource_name, 'get_all')

def test_share_group_list_detail_no_groups(self):
self.mock_object(
@@ -830,17 +856,22 @@ class ShareGroupAPITest(test.TestCase):
self.mock_policy_check.assert_called_once_with(
req_context, self.resource_name, 'get_all')

def test_share_group_delete(self):
@ddt.data({'microversion': '2.31', 'experimental': True},
{'microversion': SG_GRADUATION_VERSION, 'experimental': False})
@ddt.unpack
def test_share_group_delete(self, microversion, experimental):
fake_group, expected_group = self._get_fake_share_group()
self.mock_object(share_group_api.API, 'get',
mock.Mock(return_value=fake_group))
self.mock_object(share_group_api.API, 'delete')
req, req_context = self._get_fake_custom_request_and_context(
microversion, experimental)

res = self.controller.delete(self.request, fake_group['id'])
res = self.controller.delete(req, fake_group['id'])

self.assertEqual(202, res.status_code)
self.mock_policy_check.assert_called_once_with(
self.context, self.resource_name, 'delete')
req_context, self.resource_name, 'delete')

def test_share_group_delete_group_not_found(self):
fake_group, expected_group = self._get_fake_share_group()
@@ -866,13 +897,16 @@ class ShareGroupAPITest(test.TestCase):
self.mock_policy_check.assert_called_once_with(
self.context, self.resource_name, 'delete')

def test_share_group_show(self):
@ddt.data({'microversion': '2.34', 'experimental': True},
{'microversion': SG_GRADUATION_VERSION, 'experimental': False})
@ddt.unpack
def test_share_group_show(self, microversion, experimental):
fake, expected = self._get_fake_share_group()
self.mock_object(
share_group_api.API, 'get', mock.Mock(return_value=fake))
req = fakes.HTTPRequest.blank(
'/share-groupss/%s' % fake['id'], version=self.api_version,
experimental=True)
'/share-groupss/%s' % fake['id'], version=microversion,
experimental=experimental)
req_context = req.environ['manila.context']

res_dict = self.controller.show(req, fake['id'])
@@ -883,8 +917,8 @@ class ShareGroupAPITest(test.TestCase):

def test_share_group_show_as_admin(self):
req = fakes.HTTPRequest.blank(
'/share-groupss/my_group_id',
version=self.api_version, experimental=True)
'/share-groupss/my_group_id', version=self.api_version,
experimental=True)
admin_context = req.environ['manila.context'].elevated()
req.environ['manila.context'] = admin_context
fake_group, expected_group = self._get_fake_share_group(
@@ -901,8 +935,8 @@ class ShareGroupAPITest(test.TestCase):

def test_share_group_show_group_not_found(self):
req = fakes.HTTPRequest.blank(
'/share-groupss/myfakegroup',
version=self.api_version, experimental=True)
'/share-groupss/myfakegroup', version=self.api_version,
experimental=True)
req_context = req.environ['manila.context']
fake, expected = self._get_fake_share_group(
ctxt=req_context, id='myfakegroup')
@@ -915,6 +949,19 @@ class ShareGroupAPITest(test.TestCase):
self.mock_policy_check.assert_called_once_with(
req_context, self.resource_name, 'get')

@ddt.data({'microversion': '2.31', 'experimental': True},
{'microversion': SG_GRADUATION_VERSION, 'experimental': False})
@ddt.unpack
def test__reset_status_call(self, microversion, experimental):
self.mock_object(self.controller, '_reset_status')
req, _junk = self._get_fake_custom_request_and_context(
microversion, experimental)
sg_id = 'fake'
body = {'reset_status': {'status': constants.STATUS_ERROR}}

self.controller.share_group_reset_status(req, sg_id, body)
self.controller._reset_status.assert_called_once_with(req, sg_id, body)

@ddt.data(*fakes.fixture_reset_status_with_different_roles)
@ddt.unpack
def test_share_groups_reset_status_with_different_roles(
@@ -960,3 +1007,16 @@ class ShareGroupAPITest(test.TestCase):

# validate response
self.assertEqual(resp_code, resp.status_int)

@ddt.data({'microversion': '2.31', 'experimental': True},
{'microversion': SG_GRADUATION_VERSION, 'experimental': False})
@ddt.unpack
def test__force_delete_call(self, microversion, experimental):
self.mock_object(self.controller, '_force_delete')
req, _junk = self._get_fake_custom_request_and_context(
microversion, experimental)
sg_id = 'fake'
body = {'force_delete': {}}

self.controller.share_group_force_delete(req, sg_id, body)
self.controller._force_delete.assert_called_once_with(req, sg_id, body)

+ 10
- 0
releasenotes/notes/graduate-share-groups-feature-5f751b49ccc62969.yaml View File

@@ -0,0 +1,10 @@
---
prelude: >
- |
Share group APIs have graduated from their `experimental feature state
<https://docs.openstack.org/manila/latest/contributor/experimental_apis.html>`_
from API version ``2.55``. Share group types can be created to encompass
one or more share types, share groups can be created, updated, snapshotted
and deleted, and shares can be created within share groups. These actions
no longer require the inclusion of ``X-OpenStack-Manila-API-Experimental``
header in the API requests.

Loading…
Cancel
Save