Port admin actions extension to core API

Changes:
- Register admin actions API as core API.
- Remove extension code for admin actions.
- Leave rename of admin actions API 'post' data for future update which
  will be done with bump of microversion
  after port of all extensions to core API.

Partially implements bp ext-to-core
Closes-Bug: #1510620

Change-Id: Ic8804cff5fa46003d2991372c6eb2a854b886dec
This commit is contained in:
Valeriy Ponomaryov 2015-10-27 17:03:27 +02:00
parent 61bab6cc29
commit 9912d296ec
21 changed files with 733 additions and 626 deletions

View File

@ -23,11 +23,6 @@
"share:get_all": "rule:default",
"share:list_by_share_server_id": "rule:admin_api",
"share:update": "rule:default",
"share:snapshot_update": "rule:default",
"share:create_snapshot": "rule:default",
"share:delete_snapshot": "rule:default",
"share:get_snapshot": "rule:default",
"share:get_all_snapshots": "rule:default",
"share:access_get": "rule:default",
"share:access_get_all": "rule:default",
"share:allow_access": "rule:default",
@ -40,30 +35,28 @@
"share:migrate": "rule:admin_api",
"share:manage": "rule:admin_api",
"share:unmanage": "rule:admin_api",
"share:force_delete": "rule:admin_api",
"share:reset_status": "rule:admin_api",
"share_instance:index": "rule:admin_api",
"share_instance:show": "rule:admin_api",
"share_instance:force_delete": "rule:admin_api",
"share_instance:reset_status": "rule:admin_api",
"share_snapshot:create_snapshot": "rule:default",
"share_snapshot:delete_snapshot": "rule:default",
"share_snapshot:get_snapshot": "rule:default",
"share_snapshot:get_all_snapshots": "rule:default",
"share_snapshot:snapshot_update": "rule:default",
"share_snapshot:force_delete": "rule:admin_api",
"share_snapshot:reset_status": "rule:admin_api",
"share_type:index": "rule:default",
"share_type:show": "rule:default",
"share_type:default": "rule:default",
"share_instance:index": "rule:admin_api",
"share_instance:show": "rule:admin_api",
"share_extension:share_admin_actions:force_delete": "rule:admin_api",
"share_extension:share_admin_actions:reset_status": "rule:admin_api",
"share_extension:snapshot_admin_actions:force_delete": "rule:admin_api",
"share_extension:snapshot_admin_actions:reset_status": "rule:admin_api",
"share_extension:share_instance_admin_actions:force_delete": "rule:admin_api",
"share_extension:share_instance_admin_actions:reset_status": "rule:admin_api",
"share_extension:consistency_group_admin_actions:force_delete": "rule:admin_api",
"share_extension:consistency_group_admin_actions:reset_status": "rule:admin_api",
"share_extension:cgsnapshot_admin_actions:force_delete": "rule:admin_api",
"share_extension:cgsnapshot_admin_actions:reset_status": "rule:admin_api",
"share_extension:availability_zones": "",
"share_extension:types_manage": "rule:admin_api",
"share_extension:types_extra_specs": "rule:admin_api",
"share_extension:share_type_access": "",
"share_extension:share_type_access:addProjectAccess": "rule:admin_api",
"share_extension:share_type_access:removeProjectAccess": "rule:admin_api",
@ -99,9 +92,13 @@
"consistency_group:update": "rule:default",
"consistency_group:get": "rule:default",
"consistency_group:get_all": "rule:default",
"consistency_group:create_cgsnapshot" : "rule:default",
"consistency_group:delete_cgsnapshot": "rule:default",
"consistency_group:force_delete": "rule:admin_api",
"consistency_group:reset_status": "rule:admin_api",
"consistency_group:get_cgsnapshot": "rule:default",
"consistency_group:get_all_cgsnapshots": "rule:default"
"consistency_group:get_all_cgsnapshots": "rule:default",
"cgsnapshot:force_delete": "rule:admin_api",
"cgsnapshot:reset_status": "rule:admin_api"
}

View File

@ -1,230 +0,0 @@
# Copyright 2012 OpenStack Foundation
#
# Licensed under the Apache License, Version 2.0 (the "License"); you may
# not use this file except in compliance with the License. You may obtain
# a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
# License for the specific language governing permissions and limitations
# under the License.
from oslo_log import log
import six
import webob
from webob import exc
from manila.api import extensions
from manila.api.openstack import wsgi
from manila.common import constants
import manila.consistency_group.api as cg_api
from manila import db
from manila import exception
from manila import share
LOG = log.getLogger(__name__)
class AdminController(wsgi.Controller):
"""Abstract base class for AdminControllers."""
collection = None
valid_status = set([
constants.STATUS_CREATING,
constants.STATUS_AVAILABLE,
constants.STATUS_DELETING,
constants.STATUS_ERROR,
constants.STATUS_ERROR_DELETING,
])
def __init__(self, *args, **kwargs):
super(AdminController, self).__init__(*args, **kwargs)
self.resource_name = self.collection.rstrip('s').replace('-', '_')
self.share_api = share.API()
self.cg_api = cg_api.API()
def _update(self, *args, **kwargs):
raise NotImplementedError()
def _get(self, *args, **kwargs):
raise NotImplementedError()
def _delete(self, *args, **kwargs):
raise NotImplementedError()
def validate_update(self, body):
update = {}
try:
update['status'] = body['status']
except (TypeError, KeyError):
raise exc.HTTPBadRequest(explanation="Must specify 'status'")
if update['status'] not in self.valid_status:
expl = "Invalid state. Valid states: " +\
', '.join(self.valid_status) + '.'
raise exc.HTTPBadRequest(explanation=expl)
return update
def authorize(self, context, action_name):
action = '%s_admin_actions:%s' % (self.resource_name, action_name)
extensions.extension_authorizer('share', action)(context)
@wsgi.action('os-reset_status')
def _reset_status(self, req, id, body):
"""Reset status on the resource."""
context = req.environ['manila.context']
self.authorize(context, 'reset_status')
update = self.validate_update(body['os-reset_status'])
msg = "Updating %(resource)s '%(id)s' with '%(update)r'"
LOG.debug(msg, {'resource': self.resource_name, 'id': id,
'update': update})
try:
self._update(context, id, update)
except exception.NotFound as e:
raise exc.HTTPNotFound(six.text_type(e))
return webob.Response(status_int=202)
@wsgi.action('os-force_delete')
def _force_delete(self, req, id, body):
"""Delete a resource, bypassing the check for status."""
context = req.environ['manila.context']
self.authorize(context, 'force_delete')
try:
resource = self._get(context, id)
except exception.NotFound as e:
raise exc.HTTPNotFound(six.text_type(e))
self._delete(context, resource, force=True)
return webob.Response(status_int=202)
class ShareAdminController(AdminController):
"""AdminController for Shares."""
collection = 'shares'
def _update(self, *args, **kwargs):
db.share_update(*args, **kwargs)
def _get(self, *args, **kwargs):
return self.share_api.get(*args, **kwargs)
def _delete(self, *args, **kwargs):
return self.share_api.delete(*args, **kwargs)
def _migrate(self, *args, **kwargs):
return self.share_api.migrate_share(*args, **kwargs)
class ShareInstancesAdminController(AdminController):
"""AdminController for Share instances."""
collection = 'share_instances'
def _get(self, *args, **kwargs):
return db.share_instance_get(*args, **kwargs)
def _update(self, *args, **kwargs):
db.share_instance_update(*args, **kwargs)
def _delete(self, *args, **kwargs):
return self.share_api.delete_instance(*args, **kwargs)
class SnapshotAdminController(AdminController):
"""AdminController for Snapshots."""
collection = 'snapshots'
def _update(self, *args, **kwargs):
db.share_snapshot_update(*args, **kwargs)
def _get(self, *args, **kwargs):
return self.share_api.get_snapshot(*args, **kwargs)
def _delete(self, *args, **kwargs):
return self.share_api.delete_snapshot(*args, **kwargs)
class CGAdminController(AdminController):
"""AdminController for Consistency Groups."""
collection = 'consistency-groups'
def __init__(self, *args, **kwargs):
super(CGAdminController, self).__init__(*args, **kwargs)
self.cg_api = cg_api.API()
def _update(self, *args, **kwargs):
db.consistency_group_update(*args, **kwargs)
def _get(self, *args, **kwargs):
return self.cg_api.get(*args, **kwargs)
def _delete(self, context, resource, force=True):
db.consistency_group_destroy(context.elevated(), resource['id'])
@wsgi.Controller.api_version('2.4', experimental=True)
@wsgi.action('os-reset_status')
@wsgi.response(202)
def cg_reset_status(self, req, id, body):
super(CGAdminController, self)._reset_status(req, id, body)
@wsgi.Controller.api_version('2.4', experimental=True)
@wsgi.action('os-force_delete')
@wsgi.response(202)
def cg_force_delete(self, req, id, body):
super(CGAdminController, self)._force_delete(req, id, body)
class CGSnapshotAdminController(AdminController):
"""AdminController for CGSnapshots."""
collection = 'cgsnapshots'
def __init__(self, *args, **kwargs):
super(CGSnapshotAdminController, self).__init__(*args, **kwargs)
self.cg_api = cg_api.API()
def _update(self, *args, **kwargs):
db.cgsnapshot_update(*args, **kwargs)
def _get(self, *args, **kwargs):
return self.cg_api.get_cgsnapshot(*args, **kwargs)
def _delete(self, context, resource, force=True):
db.cgsnapshot_destroy(context.elevated(), resource['id'])
@wsgi.Controller.api_version('2.4', experimental=True)
@wsgi.action('os-reset_status')
@wsgi.response(202)
def cgsnapshot_reset_status(self, req, id, body):
super(CGSnapshotAdminController, self)._reset_status(req, id, body)
@wsgi.Controller.api_version('2.4', experimental=True)
@wsgi.action('os-force_delete')
@wsgi.response(202)
def cgsnapshot_force_delete(self, req, id, body):
super(CGSnapshotAdminController, self)._force_delete(req, id, body)
class Admin_actions(extensions.ExtensionDescriptor):
"""Enable admin actions."""
name = "AdminActions"
alias = "os-admin-actions"
updated = "2015-09-01T00:00:00+00:00"
def get_controller_extensions(self):
exts = []
for class_ in (ShareAdminController, SnapshotAdminController,
ShareInstancesAdminController,
CGAdminController, CGSnapshotAdminController):
controller = class_()
extension = extensions.ControllerExtension(
self, class_.collection, controller)
exts.append(extension)
return exts

View File

@ -27,6 +27,7 @@ import webob.exc
from manila.api.openstack import api_version_request as api_version
from manila.api.openstack import versioned_method
from manila.common import constants
from manila import exception
from manila.i18n import _
from manila.i18n import _LE
@ -1135,6 +1136,66 @@ class Controller(object):
return True
class AdminActionsMixin(object):
"""Mixin class for API controllers with admin actions."""
valid_statuses = set([
constants.STATUS_CREATING,
constants.STATUS_AVAILABLE,
constants.STATUS_DELETING,
constants.STATUS_ERROR,
constants.STATUS_ERROR_DELETING,
])
def _update(self, *args, **kwargs):
raise NotImplementedError()
def _get(self, *args, **kwargs):
raise NotImplementedError()
def _delete(self, *args, **kwargs):
raise NotImplementedError()
def validate_update(self, body):
update = {}
try:
update['status'] = body['status']
except (TypeError, KeyError):
raise webob.exc.HTTPBadRequest(explanation="Must specify 'status'")
if update['status'] not in self.valid_statuses:
expl = _("Invalid state. Valid states: " +
", ".join(self.valid_statuses) + ".")
raise webob.exc.HTTPBadRequest(explanation=expl)
return update
@action('os-reset_status')
def _reset_status(self, req, id, body):
"""Reset status on the resource."""
context = req.environ['manila.context']
self.authorize(context, 'reset_status')
update = self.validate_update(body['os-reset_status'])
msg = "Updating %(resource)s '%(id)s' with '%(update)r'"
LOG.debug(msg, {'resource': self.resource_name, 'id': id,
'update': update})
try:
self._update(context, id, update)
except exception.NotFound as e:
raise webob.exc.HTTPNotFound(six.text_type(e))
return webob.Response(status_int=202)
@action('os-force_delete')
def _force_delete(self, req, id, body):
"""Delete a resource, bypassing the check for status."""
context = req.environ['manila.context']
self.authorize(context, 'force_delete')
try:
resource = self._get(context, id)
except exception.NotFound as e:
raise webob.exc.HTTPNotFound(six.text_type(e))
self._delete(context, resource, force=True)
return webob.Response(status_int=202)
class Fault(webob.exc.HTTPException):
"""Wrap webob.exc.HTTPException to provide API friendly response."""

View File

@ -25,6 +25,7 @@ from manila.api import common
from manila.api.openstack import wsgi
import manila.api.views.cgsnapshots as cg_views
import manila.consistency_group.api as cg_api
from manila import db
from manila import exception
from manila.i18n import _
from manila.i18n import _LI
@ -32,9 +33,10 @@ from manila.i18n import _LI
LOG = log.getLogger(__name__)
class CGSnapshotController(wsgi.Controller):
class CGSnapshotController(wsgi.Controller, wsgi.AdminActionsMixin):
"""The Consistency Group Snapshots API controller for the OpenStack API."""
resource_name = 'cgsnapshot'
_view_builder_class = cg_views.CGSnapshotViewBuilder
def __init__(self):
@ -193,6 +195,27 @@ class CGSnapshotController(wsgi.Controller):
snaps = self._view_builder.member_list(req, limited_list)
return snaps
def _update(self, *args, **kwargs):
db.cgsnapshot_update(*args, **kwargs)
def _get(self, *args, **kwargs):
return self.cg_api.get_cgsnapshot(*args, **kwargs)
def _delete(self, context, resource, force=True):
db.cgsnapshot_destroy(context.elevated(), resource['id'])
@wsgi.Controller.api_version('2.4', experimental=True)
@wsgi.action('os-reset_status')
@wsgi.response(202)
def cgsnapshot_reset_status(self, req, id, body):
super(self.__class__, self)._reset_status(req, id, body)
@wsgi.Controller.api_version('2.4', experimental=True)
@wsgi.action('os-force_delete')
@wsgi.response(202)
def cgsnapshot_force_delete(self, req, id, body):
super(self.__class__, self)._force_delete(req, id, body)
def create_resource():
return wsgi.Resource(CGSnapshotController())

View File

@ -25,6 +25,7 @@ from manila.api import common
from manila.api.openstack import wsgi
import manila.api.views.consistency_groups as cg_views
import manila.consistency_group.api as cg_api
from manila import db
from manila import exception
from manila.i18n import _
from manila.i18n import _LI
@ -33,9 +34,10 @@ from manila.share import share_types
LOG = log.getLogger(__name__)
class CGController(wsgi.Controller):
class CGController(wsgi.Controller, wsgi.AdminActionsMixin):
"""The Consistency Groups API controller for the OpenStack API."""
resource_name = 'consistency_group'
_view_builder_class = cg_views.CGViewBuilder
def __init__(self):
@ -206,6 +208,27 @@ class CGController(wsgi.Controller):
return self._view_builder.detail(req, dict(six.iteritems(new_cg)))
def _update(self, *args, **kwargs):
db.consistency_group_update(*args, **kwargs)
def _get(self, *args, **kwargs):
return self.cg_api.get(*args, **kwargs)
def _delete(self, context, resource, force=True):
db.consistency_group_destroy(context.elevated(), resource['id'])
@wsgi.Controller.api_version('2.4', experimental=True)
@wsgi.action('os-reset_status')
@wsgi.response(202)
def cg_reset_status(self, req, id, body):
super(self.__class__, self)._reset_status(req, id, body)
@wsgi.Controller.api_version('2.4', experimental=True)
@wsgi.action('os-force_delete')
@wsgi.response(202)
def cg_force_delete(self, req, id, body):
super(self.__class__, self)._force_delete(req, id, body)
def create_resource():
return wsgi.Resource(CGController())

View File

@ -22,7 +22,7 @@ from manila import exception
from manila import share
class ShareInstancesController(wsgi.Controller):
class ShareInstancesController(wsgi.Controller, wsgi.AdminActionsMixin):
"""The share instances API controller for the OpenStack API."""
resource_name = 'share_instance'
@ -30,7 +30,16 @@ class ShareInstancesController(wsgi.Controller):
def __init__(self):
self.share_api = share.API()
super(ShareInstancesController, self).__init__()
super(self.__class__, self).__init__()
def _get(self, *args, **kwargs):
return db.share_instance_get(*args, **kwargs)
def _update(self, *args, **kwargs):
db.share_instance_update(*args, **kwargs)
def _delete(self, *args, **kwargs):
return self.share_api.delete_instance(*args, **kwargs)
@wsgi.Controller.api_version("2.3")
def index(self, req):
@ -65,6 +74,18 @@ class ShareInstancesController(wsgi.Controller):
view = instance_view.ViewBuilder()
return view.detail_list(req, share.instances)
@wsgi.Controller.api_version('2.3')
@wsgi.action('os-reset_status')
@wsgi.response(202)
def share_instance_reset_status(self, req, id, body):
super(self.__class__, self)._reset_status(req, id, body)
@wsgi.Controller.api_version('2.3')
@wsgi.action('os-force_delete')
@wsgi.response(202)
def share_instance_force_delete(self, req, id, body):
super(self.__class__, self)._force_delete(req, id, body)
def create_resource():
return wsgi.Resource(ShareInstancesController())

View File

@ -23,6 +23,7 @@ from webob import exc
from manila.api import common
from manila.api.openstack import wsgi
from manila.api.views import share_snapshots as snapshot_views
from manila import db
from manila import exception
from manila.i18n import _, _LI
from manila import share
@ -30,15 +31,35 @@ from manila import share
LOG = log.getLogger(__name__)
class ShareSnapshotsController(wsgi.Controller):
class ShareSnapshotsController(wsgi.Controller, wsgi.AdminActionsMixin):
"""The Share Snapshots API controller for the OpenStack API."""
resource_name = 'share_snapshot'
_view_builder_class = snapshot_views.ViewBuilder
def __init__(self):
super(ShareSnapshotsController, self).__init__()
self.share_api = share.API()
def _update(self, *args, **kwargs):
db.share_snapshot_update(*args, **kwargs)
def _get(self, *args, **kwargs):
return self.share_api.get_snapshot(*args, **kwargs)
def _delete(self, *args, **kwargs):
return self.share_api.delete_snapshot(*args, **kwargs)
@wsgi.action('os-reset_status')
@wsgi.response(202)
def share_snapshot_reset_status(self, req, id, body):
super(self.__class__, self)._reset_status(req, id, body)
@wsgi.action('os-force_delete')
@wsgi.response(202)
def share_snapshot_force_delete(self, req, id, body):
super(self.__class__, self)._force_delete(req, id, body)
def show(self, req, id):
"""Return data about the given snapshot."""
context = req.environ['manila.context']

View File

@ -38,15 +38,38 @@ from manila.share import share_types
LOG = log.getLogger(__name__)
class ShareController(wsgi.Controller):
class ShareController(wsgi.Controller, wsgi.AdminActionsMixin):
"""The Shares API controller for the OpenStack API."""
resource_name = 'share'
_view_builder_class = share_views.ViewBuilder
def __init__(self):
super(ShareController, self).__init__()
self.share_api = share.API()
def _update(self, *args, **kwargs):
db.share_update(*args, **kwargs)
def _get(self, *args, **kwargs):
return self.share_api.get(*args, **kwargs)
def _delete(self, *args, **kwargs):
return self.share_api.delete(*args, **kwargs)
def _migrate(self, *args, **kwargs):
return self.share_api.migrate_share(*args, **kwargs)
@wsgi.action('os-reset_status')
@wsgi.response(202)
def share_reset_status(self, req, id, body):
super(self.__class__, self)._reset_status(req, id, body)
@wsgi.action('os-force_delete')
@wsgi.response(202)
def share_force_delete(self, req, id, body):
super(self.__class__, self)._force_delete(req, id, body)
def show(self, req, id):
"""Return data about the given share."""
context = req.environ['manila.context']

View File

@ -720,13 +720,13 @@ class API(base.Base):
return shares
def get_snapshot(self, context, snapshot_id):
policy.check_policy(context, 'share', 'get_snapshot')
policy.check_policy(context, 'share_snapshot', 'get_snapshot')
rv = self.db.share_snapshot_get(context, snapshot_id)
return dict(six.iteritems(rv))
def get_all_snapshots(self, context, search_opts=None,
sort_key='share_id', sort_dir='desc'):
policy.check_policy(context, 'share', 'get_all_snapshots')
policy.check_policy(context, 'share_snapshot', 'get_all_snapshots')
search_opts = search_opts or {}
LOG.debug("Searching for snapshots by: %s", six.text_type(search_opts))

View File

@ -1,308 +0,0 @@
# Copyright 2013 Mirantis Inc.
# All Rights Reserved.
#
# Licensed under the Apache License, Version 2.0 (the "License"); you may
# not use this file except in compliance with the License. You may obtain
# a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
# License for the specific language governing permissions and limitations
# under the License.
import ddt
from oslo_config import cfg
from oslo_serialization import jsonutils
import six
import webob
from manila.api.openstack import wsgi
from manila.common import constants
from manila import context
from manila import db
from manila import exception
from manila.share import api as share_api
from manila import test
from manila.tests.api import fakes
from manila.tests import db_utils
CONF = cfg.CONF
def app():
# no auth, just let environ['manila.context'] pass through
api = fakes.router.APIRouter()
mapper = fakes.urlmap.URLMap()
mapper['/v2'] = api
return mapper
fixture_reset_status_with_different_roles = (
{'role': 'admin', 'valid_code': 202,
'valid_status': constants.STATUS_ERROR},
{'role': 'member', 'valid_code': 403,
'valid_status': constants.STATUS_AVAILABLE}
)
fixture_force_delete_with_different_roles = (
{'role': 'admin', 'resp_code': 202},
{'role': 'member', 'resp_code': 403},
)
fixture_invalid_reset_status_body = (
{'os-reset_status': {'x-status': 'bad'}},
{'os-reset_status': {'status': 'invalid'}}
)
@ddt.ddt
class AdminActionsTest(test.TestCase):
def setUp(self):
super(AdminActionsTest, self).setUp()
self.flags(rpc_backend='manila.openstack.common.rpc.impl_fake')
self.share_api = share_api.API()
self.admin_context = context.RequestContext('admin', 'fake', True)
self.member_context = context.RequestContext('fake', 'fake')
def _get_context(self, role):
return getattr(self, '%s_context' % role)
def _setup_share_data(self, share=None):
if share is None:
share = db_utils.create_share(status=constants.STATUS_AVAILABLE,
size='1',
override_defaults=True)
req = webob.Request.blank('/v2/fake/shares/%s/action' % share['id'])
return share, req
def _setup_snapshot_data(self, snapshot=None):
if snapshot is None:
share = db_utils.create_share()
snapshot = db_utils.create_snapshot(
status=constants.STATUS_AVAILABLE, share_id=share['id'])
req = webob.Request.blank('/v2/fake/snapshots/%s/action' %
snapshot['id'])
return snapshot, req
def _setup_share_instance_data(self, instance=None):
if instance is None:
instance = db_utils.create_share(status=constants.STATUS_AVAILABLE,
size='1').instance
req = webob.Request.blank(
'/v2/fake/share_instances/%s/action' % instance['id'])
return instance, req
def _setup_cg_data(self, cg=None):
if cg is None:
cg = db_utils.create_consistency_group(
status=constants.STATUS_AVAILABLE)
req = webob.Request.blank('/v2/fake/consistency-groups/%s/action' %
cg['id'])
req.headers[wsgi.API_VERSION_REQUEST_HEADER] = '2.4'
req.headers[wsgi.EXPERIMENTAL_API_REQUEST_HEADER] = 'True'
return cg, req
def _setup_cgsnapshot_data(self, cgsnapshot=None):
if cgsnapshot is None:
cgsnapshot = db_utils.create_cgsnapshot(
'fake_id', status=constants.STATUS_AVAILABLE)
req = webob.Request.blank('/v2/fake/cgsnapshots/%s/action' %
cgsnapshot['id'])
req.headers[wsgi.API_VERSION_REQUEST_HEADER] = '2.4'
req.headers[wsgi.EXPERIMENTAL_API_REQUEST_HEADER] = 'True'
return cgsnapshot, req
def _reset_status(self, ctxt, model, req, db_access_method,
valid_code, valid_status=None, body=None):
if body is None:
body = {'os-reset_status': {'status': constants.STATUS_ERROR}}
req.method = 'POST'
req.headers['content-type'] = 'application/json'
req.body = six.b(jsonutils.dumps(body))
req.environ['manila.context'] = ctxt
resp = req.get_response(app())
# validate response code and model status
self.assertEqual(valid_code, resp.status_int)
if valid_code == 404:
self.assertRaises(exception.NotFound,
db_access_method,
ctxt,
model['id'])
else:
actual_model = db_access_method(ctxt, model['id'])
self.assertEqual(valid_status, actual_model['status'])
@ddt.data(*fixture_reset_status_with_different_roles)
@ddt.unpack
def test_share_reset_status_with_different_roles(self, role, valid_code,
valid_status):
share, req = self._setup_share_data()
ctxt = self._get_context(role)
self._reset_status(ctxt, share, req, db.share_get, valid_code,
valid_status)
@ddt.data(*fixture_reset_status_with_different_roles)
@ddt.unpack
def test_snapshot_reset_status_with_different_roles(self, role, valid_code,
valid_status):
ctxt = self._get_context(role)
snapshot, req = self._setup_snapshot_data()
self._reset_status(ctxt, snapshot, req, db.share_snapshot_get,
valid_code, valid_status)
@ddt.data(*fixture_reset_status_with_different_roles)
@ddt.unpack
def test_share_instances_reset_status_with_different_roles(self, role,
valid_code,
valid_status):
ctxt = self._get_context(role)
instance, req = self._setup_share_instance_data()
self._reset_status(ctxt, instance, req, db.share_instance_get,
valid_code, valid_status)
@ddt.data(*fixture_reset_status_with_different_roles)
@ddt.unpack
def test_consistency_groups_reset_status_with_different_roles(
self, role, valid_code, valid_status):
ctxt = self._get_context(role)
cg, req = self._setup_cg_data()
self._reset_status(ctxt, cg, req, db.consistency_group_get,
valid_code, valid_status)
@ddt.data(*fixture_reset_status_with_different_roles)
@ddt.unpack
def test_cgsnapshot_reset_status_with_different_roles(
self, role, valid_code, valid_status):
ctxt = self._get_context(role)
cgsnap, req = self._setup_cgsnapshot_data()
self._reset_status(ctxt, cgsnap, req, db.cgsnapshot_get,
valid_code, valid_status)
@ddt.data(*fixture_invalid_reset_status_body)
def test_share_invalid_reset_status_body(self, body):
share, req = self._setup_share_data()
ctxt = self.admin_context
self._reset_status(ctxt, share, req, db.share_get, 400,
constants.STATUS_AVAILABLE, body)
@ddt.data(*fixture_invalid_reset_status_body)
def test_snapshot_invalid_reset_status_body(self, body):
snapshot, req = self._setup_snapshot_data()
self._reset_status(self.admin_context, snapshot, req,
db.share_snapshot_get, 400,
constants.STATUS_AVAILABLE, body)
@ddt.data(*fixture_invalid_reset_status_body)
def test_share_instance_invalid_reset_status_body(self, body):
instance, req = self._setup_share_instance_data()
self._reset_status(self.admin_context, instance, req,
db.share_instance_get, 400,
constants.STATUS_AVAILABLE, body)
def test_share_reset_status_for_missing(self):
fake_share = {'id': 'missing-share-id'}
req = webob.Request.blank('/v1/fake/shares/%s/action' %
fake_share['id'])
self._reset_status(self.admin_context, fake_share, req,
db.share_snapshot_get, 404)
def _force_delete(self, ctxt, model, req, db_access_method, valid_code,
check_model_in_db=False):
req.method = 'POST'
req.headers['content-type'] = 'application/json'
req.body = six.b(jsonutils.dumps({'os-force_delete': {}}))
req.environ['manila.context'] = ctxt
resp = req.get_response(app())
# validate response
self.assertEqual(valid_code, resp.status_int)
if valid_code == 202 and check_model_in_db:
self.assertRaises(exception.NotFound,
db_access_method,
ctxt,
model['id'])
@ddt.data(*fixture_force_delete_with_different_roles)
@ddt.unpack
def test_share_force_delete_with_different_roles(self, role, resp_code):
share, req = self._setup_share_data()
ctxt = self._get_context(role)
self._force_delete(ctxt, share, req, db.share_get, resp_code,
check_model_in_db=True)
def test_share_force_delete_missing(self):
share, req = self._setup_share_data(share={'id': 'fake'})
ctxt = self._get_context('admin')
self._force_delete(ctxt, share, req, db.share_get, 404)
@ddt.data(*fixture_force_delete_with_different_roles)
@ddt.unpack
def test_snapshot_force_delete_with_different_roles(self, role, resp_code):
snapshot, req = self._setup_snapshot_data()
ctxt = self._get_context(role)
self._force_delete(ctxt, snapshot, req, db.share_snapshot_get,
resp_code)
def test_snapshot_force_delete_missing(self):
snapshot, req = self._setup_snapshot_data(snapshot={'id': 'fake'})
ctxt = self._get_context('admin')
self._force_delete(ctxt, snapshot, req, db.share_snapshot_get, 404)
@ddt.data(*fixture_force_delete_with_different_roles)
@ddt.unpack
def test_instance_force_delete_with_different_roles(self, role, resp_code):
instance, req = self._setup_share_instance_data()
ctxt = self._get_context(role)
self._force_delete(ctxt, instance, req, db.share_instance_get,
resp_code)
def test_instance_force_delete_missing(self):
instance, req = self._setup_share_instance_data(
instance={'id': 'fake'})
ctxt = self._get_context('admin')
self._force_delete(ctxt, instance, req, db.share_instance_get, 404)
@ddt.data(*fixture_force_delete_with_different_roles)
@ddt.unpack
def test_consistency_group_force_delete_with_different_roles(self, role,
resp_code):
cg, req = self._setup_cg_data()
ctxt = self._get_context(role)
self._force_delete(ctxt, cg, req, db.consistency_group_get,
resp_code)
@ddt.data(*fixture_force_delete_with_different_roles)
@ddt.unpack
def test_cgsnapshot_force_delete_with_different_roles(self, role,
resp_code):
cgsnap, req = self._setup_cgsnapshot_data()
ctxt = self._get_context(role)
self._force_delete(ctxt, cgsnap, req, db.cgsnapshot_get,
resp_code)

View File

@ -30,6 +30,7 @@ from manila.api import urlmap
from manila.api.v1 import limits
from manila.api.v1 import router
from manila.api import versions
from manila.common import constants
from manila import context
from manila import wsgi
@ -165,3 +166,39 @@ def get_fake_uuid(token=0):
if token not in FAKE_UUIDS:
FAKE_UUIDS[token] = str(uuid.uuid4())
return FAKE_UUIDS[token]
def app():
"""API application.
No auth, just let environ['manila.context'] pass through.
"""
api = router.APIRouter()
mapper = urlmap.URLMap()
mapper['/v2'] = api
mapper['/v1'] = api
return mapper
fixture_reset_status_with_different_roles = (
{
'role': 'admin', 'valid_code': 202,
'valid_status': constants.STATUS_ERROR,
},
{
'role': 'member', 'valid_code': 403,
'valid_status': constants.STATUS_AVAILABLE,
},
)
fixture_force_delete_with_different_roles = (
{'role': 'admin', 'resp_code': 202},
{'role': 'member', 'resp_code': 403},
)
fixture_invalid_reset_status_body = (
{'os-reset_status': {'x-status': 'bad'}},
{'os-reset_status': {'status': 'invalid'}}
)

View File

@ -17,29 +17,39 @@ import copy
import datetime
import uuid
import ddt
import mock
from oslo_config import cfg
from oslo_serialization import jsonutils
import six
import webob
from manila.api.openstack import wsgi
import manila.api.v1.cgsnapshots as cgs
from manila.common import constants
from manila import context
from manila import db
from manila import exception
from manila import test
from manila.tests.api import fakes
from manila.tests import db_utils
CONF = cfg.CONF
@ddt.ddt
class CGSnapshotApiTest(test.TestCase):
def setUp(self):
super(CGSnapshotApiTest, self).setUp()
super(self.__class__, self).setUp()
self.controller = cgs.CGSnapshotController()
self.api_version = '2.4'
self.request = fakes.HTTPRequest.blank('/consistency-groups',
version=self.api_version,
experimental=True)
self.admin_context = context.RequestContext('admin', 'fake', True)
self.member_context = context.RequestContext('fake', 'fake')
self.flags(rpc_backend='manila.openstack.common.rpc.impl_fake')
def _get_fake_cgsnapshot(self, **values):
snap = {
@ -425,3 +435,58 @@ class CGSnapshotApiTest(test.TestCase):
self.assertEqual(1, len(res_dict['cgsnapshot_members']))
self.assertEqual([expected_member2], res_dict['cgsnapshot_members'])
def _get_context(self, role):
return getattr(self, '%s_context' % role)
def _setup_cgsnapshot_data(self, cgsnapshot=None):
if cgsnapshot is None:
cgsnapshot = db_utils.create_cgsnapshot(
'fake_id', status=constants.STATUS_AVAILABLE)
req = webob.Request.blank('/v2/fake/cgsnapshots/%s/action' %
cgsnapshot['id'])
req.headers[wsgi.API_VERSION_REQUEST_HEADER] = '2.4'
req.headers[wsgi.EXPERIMENTAL_API_REQUEST_HEADER] = 'True'
return cgsnapshot, req
@ddt.data(*fakes.fixture_force_delete_with_different_roles)
@ddt.unpack
def test_cgsnapshot_force_delete_with_different_roles(self, role,
resp_code):
cgsnap, req = self._setup_cgsnapshot_data()
ctxt = self._get_context(role)
req.method = 'POST'
req.headers['content-type'] = 'application/json'
req.body = six.b(jsonutils.dumps({'os-force_delete': {}}))
req.environ['manila.context'] = ctxt
resp = req.get_response(fakes.app())
# Validate response
self.assertEqual(resp_code, resp.status_int)
@ddt.data(*fakes.fixture_reset_status_with_different_roles)
@ddt.unpack
def test_cgsnapshot_reset_status_with_different_roles(
self, role, valid_code, valid_status):
ctxt = self._get_context(role)
cgsnap, req = self._setup_cgsnapshot_data()
body = {'os-reset_status': {'status': constants.STATUS_ERROR}}
req.method = 'POST'
req.headers['content-type'] = 'application/json'
req.body = six.b(jsonutils.dumps(body))
req.environ['manila.context'] = ctxt
resp = req.get_response(fakes.app())
# Validate response code and model status
self.assertEqual(valid_code, resp.status_int)
if valid_code == 404:
self.assertRaises(exception.NotFound,
db.cgsnapshot_get,
ctxt,
cgsnap['id'])
else:
actual_model = db.cgsnapshot_get(ctxt, cgsnap['id'])
self.assertEqual(valid_status, actual_model['status'])

View File

@ -17,33 +17,58 @@ import copy
import datetime
import uuid
import ddt
import mock
from oslo_config import cfg
from oslo_serialization import jsonutils
import six
import webob
from manila.api.openstack import wsgi
import manila.api.v1.consistency_groups as cgs
from manila.common import constants
import manila.consistency_group.api as cg_api
from manila import context
from manila import db
from manila import exception
from manila.share import share_types
from manila import test
from manila.tests.api import fakes
from manila.tests import db_utils
CONF = cfg.CONF
@ddt.ddt
class CGApiTest(test.TestCase):
"""Share Api Test."""
"""Consistency Groups API Test suite."""
def setUp(self):
super(CGApiTest, self).setUp()
super(self.__class__, self).setUp()
self.controller = cgs.CGController()
self.fake_share_type = {'id': six.text_type(uuid.uuid4())}
self.api_version = '2.4'
self.request = fakes.HTTPRequest.blank('/consistency-groups',
version=self.api_version,
experimental=True)
self.flags(rpc_backend='manila.openstack.common.rpc.impl_fake')
self.admin_context = context.RequestContext('admin', 'fake', True)
self.member_context = context.RequestContext('fake', 'fake')
def _get_context(self, role):
return getattr(self, '%s_context' % role)
def _setup_cg_data(self, cg=None):
if cg is None:
cg = db_utils.create_consistency_group(
status=constants.STATUS_AVAILABLE)
req = webob.Request.blank('/v2/fake/consistency-groups/%s/action' %
cg['id'])
req.headers[wsgi.API_VERSION_REQUEST_HEADER] = '2.4'
req.headers[wsgi.EXPERIMENTAL_API_REQUEST_HEADER] = 'True'
return cg, req
def _get_fake_cg(self, **values):
cg = {
@ -503,3 +528,46 @@ class CGApiTest(test.TestCase):
self.assertRaises(webob.exc.HTTPNotFound, self.controller.show,
req, fake_cg['id'])
@ddt.data(*fakes.fixture_reset_status_with_different_roles)
@ddt.unpack
def test_consistency_groups_reset_status_with_different_roles(
self, role, valid_code, valid_status):
ctxt = self._get_context(role)
cg, req = self._setup_cg_data()
body = {'os-reset_status': {'status': constants.STATUS_ERROR}}
req.method = 'POST'
req.headers['content-type'] = 'application/json'
req.body = six.b(jsonutils.dumps(body))
req.environ['manila.context'] = ctxt
resp = req.get_response(fakes.app())
# validate response code and model status
self.assertEqual(valid_code, resp.status_int)
if valid_code == 404:
self.assertRaises(exception.NotFound,
db.consistency_group_get,
ctxt,
cg['id'])
else:
actual_model = db.consistency_group_get(ctxt, cg['id'])
self.assertEqual(valid_status, actual_model['status'])
@ddt.data(*fakes.fixture_force_delete_with_different_roles)
@ddt.unpack
def test_consistency_group_force_delete_with_different_roles(self, role,
resp_code):
ctxt = self._get_context(role)
cg, req = self._setup_cg_data()
req.method = 'POST'
req.headers['content-type'] = 'application/json'
req.body = six.b(jsonutils.dumps({'os-force_delete': {}}))
req.environ['manila.context'] = ctxt
resp = req.get_response(fakes.app())
# validate response
self.assertEqual(resp_code, resp.status_int)

View File

@ -12,10 +12,16 @@
import ddt
from oslo_config import cfg
from oslo_serialization import jsonutils
import six
import webob
from webob import exc as webob_exc
from manila.api.v1 import share_instances
from manila.common import constants
from manila import context
from manila import db
from manila import exception
from manila import test
from manila.tests.api import fakes
from manila.tests import db_utils
@ -24,16 +30,29 @@ CONF = cfg.CONF
@ddt.ddt
class ShareInstancesApiTest(test.TestCase):
"""Share Api Test."""
class ShareInstancesAPITest(test.TestCase):
"""Share instances API Test."""
def setUp(self):
super(ShareInstancesApiTest, self).setUp()
super(self.__class__, self).setUp()
self.controller = share_instances.ShareInstancesController()
self.context = context.RequestContext('admin', 'fake', True)
self.admin_context = context.RequestContext('admin', 'fake', True)
self.member_context = context.RequestContext('fake', 'fake')
def _get_context(self, role):
return getattr(self, '%s_context' % role)
def _setup_share_instance_data(self, instance=None):
if instance is None:
instance = db_utils.create_share(status=constants.STATUS_AVAILABLE,
size='1').instance
req = webob.Request.blank(
'/v2/fake/share_instances/%s/action' % instance['id'])
return instance, req
def _get_request(self, uri, context=None):
if context is None:
context = self.context
context = self.admin_context
req = fakes.HTTPRequest.blank('/shares', version="2.3")
req.environ['manila.context'] = context
return req
@ -91,3 +110,82 @@ class ShareInstancesApiTest(test.TestCase):
args = [i for i in range(1, args_count)]
self.assertRaises(webob_exc.HTTPForbidden, target_method, req, *args)
def _reset_status(self, ctxt, model, req, db_access_method,
valid_code, valid_status=None, body=None):
if body is None:
body = {'os-reset_status': {'status': constants.STATUS_ERROR}}
req.method = 'POST'
req.headers['content-type'] = 'application/json'
req.body = six.b(jsonutils.dumps(body))
req.environ['manila.context'] = ctxt
resp = req.get_response(fakes.app())
# validate response code and model status
self.assertEqual(valid_code, resp.status_int)
if valid_code == 404:
self.assertRaises(exception.NotFound,
db_access_method,
ctxt,
model['id'])
else:
actual_model = db_access_method(ctxt, model['id'])
self.assertEqual(valid_status, actual_model['status'])
@ddt.data(*fakes.fixture_reset_status_with_different_roles)
@ddt.unpack
def test_share_instances_reset_status_with_different_roles(self, role,
valid_code,
valid_status):
ctxt = self._get_context(role)
instance, req = self._setup_share_instance_data()
req.headers['X-Openstack-Manila-Api-Version'] = '2.3'
self._reset_status(ctxt, instance, req, db.share_instance_get,
valid_code, valid_status)
@ddt.data(*fakes.fixture_invalid_reset_status_body)
def test_share_instance_invalid_reset_status_body(self, body):
instance, req = self._setup_share_instance_data()
req.headers['X-Openstack-Manila-Api-Version'] = '2.3'
self._reset_status(self.admin_context, instance, req,
db.share_instance_get, 400,
constants.STATUS_AVAILABLE, body)
def _force_delete(self, ctxt, model, req, db_access_method, valid_code,
check_model_in_db=False):
req.method = 'POST'
req.headers['content-type'] = 'application/json'
req.headers['X-Openstack-Manila-Api-Version'] = '2.3'
req.body = six.b(jsonutils.dumps({'os-force_delete': {}}))
req.environ['manila.context'] = ctxt
resp = req.get_response(fakes.app())
# validate response
self.assertEqual(valid_code, resp.status_int)
if valid_code == 202 and check_model_in_db:
self.assertRaises(exception.NotFound,
db_access_method,
ctxt,
model['id'])
@ddt.data(*fakes.fixture_force_delete_with_different_roles)
@ddt.unpack
def test_instance_force_delete_with_different_roles(self, role, resp_code):
instance, req = self._setup_share_instance_data()
ctxt = self._get_context(role)
self._force_delete(ctxt, instance, req, db.share_instance_get,
resp_code)
def test_instance_force_delete_missing(self):
instance, req = self._setup_share_instance_data(
instance={'id': 'fake'})
ctxt = self._get_context('admin')
self._force_delete(ctxt, instance, req, db.share_instance_get, 404)

View File

@ -17,21 +17,28 @@ import datetime
import ddt
import mock
from oslo_serialization import jsonutils
import six
import webob
from manila.api.v1 import share_snapshots
from manila.common import constants
from manila import context
from manila import db
from manila import exception
from manila.share import api as share_api
from manila import test
from manila.tests.api.contrib import stubs
from manila.tests.api import fakes
from manila.tests import db_utils
@ddt.ddt
class ShareSnapshotApiTest(test.TestCase):
"""Share Snapshot Api Test."""
class ShareSnapshotAPITest(test.TestCase):
"""Share Snapshot API Test."""
def setUp(self):
super(ShareSnapshotApiTest, self).setUp()
super(self.__class__, self).setUp()
self.controller = share_snapshots.ShareSnapshotsController()
self.mock_object(share_api.API, 'get', stubs.stub_share_get)
@ -368,3 +375,93 @@ class ShareSnapshotApiTest(test.TestCase):
res_dict = self.controller.update(req, 1, body)
self.assertNotEqual(snp["size"], res_dict['snapshot']["size"])
@ddt.ddt
class ShareSnapshotAdminActionsAPITest(test.TestCase):
def setUp(self):
super(self.__class__, self).setUp()
self.controller = share_snapshots.ShareSnapshotsController()
self.flags(rpc_backend='manila.openstack.common.rpc.impl_fake')
self.admin_context = context.RequestContext('admin', 'fake', True)
self.member_context = context.RequestContext('fake', 'fake')
def _get_context(self, role):
return getattr(self, '%s_context' % role)
def _setup_snapshot_data(self, snapshot=None):
if snapshot is None:
share = db_utils.create_share()
snapshot = db_utils.create_snapshot(
status=constants.STATUS_AVAILABLE, share_id=share['id'])
req = webob.Request.blank('/v2/fake/snapshots/%s/action' %
snapshot['id'])
return snapshot, req
def _reset_status(self, ctxt, model, req, db_access_method,
valid_code, valid_status=None, body=None):
if body is None:
body = {'os-reset_status': {'status': constants.STATUS_ERROR}}
req.method = 'POST'
req.headers['content-type'] = 'application/json'
req.body = six.b(jsonutils.dumps(body))
req.environ['manila.context'] = ctxt
resp = req.get_response(fakes.app())
# validate response code and model status
self.assertEqual(valid_code, resp.status_int)
if valid_code == 404:
self.assertRaises(exception.NotFound,
db_access_method,
ctxt,
model['id'])
else:
actual_model = db_access_method(ctxt, model['id'])
self.assertEqual(valid_status, actual_model['status'])
@ddt.data(*fakes.fixture_reset_status_with_different_roles)
@ddt.unpack
def test_snapshot_reset_status_with_different_roles(self, role, valid_code,
valid_status):
ctxt = self._get_context(role)
snapshot, req = self._setup_snapshot_data()
self._reset_status(ctxt, snapshot, req, db.share_snapshot_get,
valid_code, valid_status)
@ddt.data(*fakes.fixture_invalid_reset_status_body)
def test_snapshot_invalid_reset_status_body(self, body):
snapshot, req = self._setup_snapshot_data()
self._reset_status(self.admin_context, snapshot, req,
db.share_snapshot_get, 400,
constants.STATUS_AVAILABLE, body)
def _force_delete(self, ctxt, model, req, db_access_method, valid_code):
req.method = 'POST'
req.headers['content-type'] = 'application/json'
req.body = six.b(jsonutils.dumps({'os-force_delete': {}}))
req.environ['manila.context'] = ctxt
resp = req.get_response(fakes.app())
# Validate response
self.assertEqual(valid_code, resp.status_int)
@ddt.data(*fakes.fixture_force_delete_with_different_roles)
@ddt.unpack
def test_snapshot_force_delete_with_different_roles(self, role, resp_code):
ctxt = self._get_context(role)
snapshot, req = self._setup_snapshot_data()
self._force_delete(ctxt, snapshot, req, db.share_snapshot_get,
resp_code)
def test_snapshot_force_delete_missing(self):
ctxt = self._get_context('admin')
snapshot, req = self._setup_snapshot_data(snapshot={'id': 'fake'})
self._force_delete(ctxt, snapshot, req, db.share_snapshot_get, 404)

View File

@ -19,6 +19,8 @@ import datetime
import ddt
import mock
from oslo_config import cfg
from oslo_serialization import jsonutils
import six
import webob
from manila.api import common
@ -48,10 +50,11 @@ def app():
@ddt.ddt
class ShareApiTest(test.TestCase):
"""Share Api Test."""
class ShareAPITest(test.TestCase):
"""Share API Test."""
def setUp(self):
super(ShareApiTest, self).setUp()
super(self.__class__, self).setUp()
self.controller = shares.ShareController()
self.mock_object(db, 'availability_zone_get')
self.mock_object(share_api.API, 'get_all',
@ -1009,3 +1012,108 @@ class ShareActionsTest(test.TestCase):
mock.Mock(side_effect=source('fake')))
self.assertRaises(target, self.controller._shrink, req, id, body)
@ddt.ddt
class ShareAdminActionsAPITest(test.TestCase):
def setUp(self):
super(self.__class__, self).setUp()
CONF.set_default("default_share_type", None)
self.flags(rpc_backend='manila.openstack.common.rpc.impl_fake')
self.share_api = share_api.API()
self.admin_context = context.RequestContext('admin', 'fake', True)
self.member_context = context.RequestContext('fake', 'fake')
def _get_context(self, role):
return getattr(self, '%s_context' % role)
def _setup_share_data(self, share=None):
if share is None:
share = db_utils.create_share(status=constants.STATUS_AVAILABLE,
size='1',
override_defaults=True)
req = webob.Request.blank('/v2/fake/shares/%s/action' % share['id'])
return share, req
def _reset_status(self, ctxt, model, req, db_access_method,
valid_code, valid_status=None, body=None):
if body is None:
body = {'os-reset_status': {'status': constants.STATUS_ERROR}}
req.method = 'POST'
req.headers['content-type'] = 'application/json'
req.body = six.b(jsonutils.dumps(body))
req.environ['manila.context'] = ctxt
resp = req.get_response(fakes.app())
# validate response code and model status
self.assertEqual(valid_code, resp.status_int)
if valid_code == 404:
self.assertRaises(exception.NotFound,
db_access_method,
ctxt,
model['id'])
else:
actual_model = db_access_method(ctxt, model['id'])
self.assertEqual(valid_status, actual_model['status'])
@ddt.data(*fakes.fixture_invalid_reset_status_body)
def test_share_invalid_reset_status_body(self, body):
share, req = self._setup_share_data()
ctxt = self.admin_context
self._reset_status(ctxt, share, req, db.share_get, 400,
constants.STATUS_AVAILABLE, body)
def test_share_reset_status_for_missing(self):
fake_share = {'id': 'missing-share-id'}
req = webob.Request.blank('/v1/fake/shares/%s/action' %
fake_share['id'])
self._reset_status(self.admin_context, fake_share, req,
db.share_snapshot_get, 404)
def _force_delete(self, ctxt, model, req, db_access_method, valid_code,
check_model_in_db=False):
req.method = 'POST'
req.headers['content-type'] = 'application/json'
req.body = six.b(jsonutils.dumps({'os-force_delete': {}}))
req.environ['manila.context'] = ctxt
resp = req.get_response(fakes.app())
# validate response
self.assertEqual(valid_code, resp.status_int)
if valid_code == 202 and check_model_in_db:
self.assertRaises(exception.NotFound,
db_access_method,
ctxt,
model['id'])
@ddt.data(*fakes.fixture_reset_status_with_different_roles)
@ddt.unpack
def test_share_reset_status_with_different_roles(self, role, valid_code,
valid_status):
share, req = self._setup_share_data()
ctxt = self._get_context(role)
self._reset_status(ctxt, share, req, db.share_get, valid_code,
valid_status)
@ddt.data(*fakes.fixture_force_delete_with_different_roles)
@ddt.unpack
def test_share_force_delete_with_different_roles(self, role, resp_code):
share, req = self._setup_share_data()
ctxt = self._get_context(role)
self._force_delete(ctxt, share, req, db.share_get, resp_code,
check_model_in_db=True)
def test_share_force_delete_missing(self):
share, req = self._setup_share_data(share={'id': 'fake'})
ctxt = self._get_context('admin')
self._force_delete(ctxt, share, req, db.share_get, 404)

View File

@ -31,6 +31,8 @@
"share:shrink": "",
"share:manage": "rule:admin_api",
"share:unmanage": "rule:admin_api",
"share:force_delete": "rule:admin_api",
"share:reset_status": "rule:admin_api",
"share_type:index": "rule:default",
"share_type:show": "rule:default",
@ -38,6 +40,11 @@
"share_instance:index": "rule:admin_api",
"share_instance:show": "rule:admin_api",
"share_instance:force_delete": "rule:admin_api",
"share_instance:reset_status": "rule:admin_api",
"share_snapshot:force_delete": "rule:admin_api",
"share_snapshot:reset_status": "rule:admin_api",
"share_network:create": "",
"share_network:index": "",
@ -55,16 +62,6 @@
"share:get_share_metadata": "",
"share:delete_share_metadata": "",
"share:update_share_metadata": "",
"share_extension:share_admin_actions:force_delete": "rule:admin_api",
"share_extension:share_admin_actions:reset_status": "rule:admin_api",
"share_extension:snapshot_admin_actions:force_delete": "rule:admin_api",
"share_extension:snapshot_admin_actions:reset_status": "rule:admin_api",
"share_extension:share_instance_admin_actions:force_delete": "rule:admin_api",
"share_extension:share_instance_admin_actions:reset_status": "rule:admin_api",
"share_extension:consistency_group_admin_actions:force_delete": "rule:admin_api",
"share_extension:consistency_group_admin_actions:reset_status": "rule:admin_api",
"share_extension:cgsnapshot_admin_actions:force_delete": "rule:admin_api",
"share_extension:cgsnapshot_admin_actions:reset_status": "rule:admin_api",
"share_extension:types_manage": "",
"share_extension:types_extra_specs": "",
"share_extension:share_type_access": "",
@ -83,9 +80,13 @@
"consistency_group:update": "rule:default",
"consistency_group:get": "rule:default",
"consistency_group:get_all": "rule:default",
"consistency_group:create_cgsnapshot" : "rule:default",
"consistency_group:delete_cgsnapshot": "rule:default",
"consistency_group:force_delete": "rule:admin_api",
"consistency_group:reset_status": "rule:admin_api",
"consistency_group:get_cgsnapshot": "rule:default",
"consistency_group:get_all_cgsnapshots": "rule:default"
"consistency_group:get_all_cgsnapshots": "rule:default",
"cgsnapshot:force_delete": "rule:admin_api",
"cgsnapshot:reset_status": "rule:admin_api"
}

View File

@ -998,7 +998,7 @@ class ShareAPITestCase(test.TestCase):
rule = self.api.get_snapshot(self.context, 'fakeid')
self.assertEqual(fake_get_snap, rule)
share_api.policy.check_policy.assert_called_once_with(
self.context, 'share', 'get_snapshot')
self.context, 'share_snapshot', 'get_snapshot')
db_api.share_snapshot_get.assert_called_once_with(
self.context, 'fakeid')
@ -1157,7 +1157,7 @@ class ShareAPITestCase(test.TestCase):
ctx = context.RequestContext('fakeuid', 'fakepid', is_admin=True)
self.api.get_all_snapshots(ctx)
share_api.policy.check_policy.assert_called_once_with(
ctx, 'share', 'get_all_snapshots')
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={})
@ -1166,7 +1166,7 @@ class ShareAPITestCase(test.TestCase):
self.api.get_all_snapshots(self.context,
search_opts={'all_tenants': 1})
share_api.policy.check_policy.assert_called_once_with(
self.context, 'share', 'get_all_snapshots')
self.context, 'share_snapshot', 'get_all_snapshots')
db_api.share_snapshot_get_all.assert_called_once_with(
self.context, sort_dir='desc', sort_key='share_id', filters={})
@ -1176,7 +1176,7 @@ class ShareAPITestCase(test.TestCase):
ctx = context.RequestContext('fakeuid', 'fakepid', is_admin=False)
self.api.get_all_snapshots(ctx)
share_api.policy.check_policy.assert_called_once_with(
ctx, 'share', 'get_all_snapshots')
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={})
@ -1191,7 +1191,7 @@ class ShareAPITestCase(test.TestCase):
self.assertEqual([search_opts], result)
share_api.policy.check_policy.assert_called_once_with(
ctx, 'share', 'get_all_snapshots')
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)
@ -1204,7 +1204,7 @@ class ShareAPITestCase(test.TestCase):
snapshots = self.api.get_all_snapshots(
ctx, sort_key='status', sort_dir='asc')
share_api.policy.check_policy.assert_called_once_with(
ctx, 'share', 'get_all_snapshots')
ctx, 'share_snapshot', 'get_all_snapshots')
db_api.share_snapshot_get_all_by_project.assert_called_once_with(
ctx, 'fake_pid_1', sort_dir='asc', sort_key='status', filters={})
self.assertEqual(_FAKE_LIST_OF_ALL_SNAPSHOTS[0], snapshots)
@ -1221,7 +1221,7 @@ class ShareAPITestCase(test.TestCase):
sort_key=1,
)
share_api.policy.check_policy.assert_called_once_with(
ctx, 'share', 'get_all_snapshots')
ctx, 'share_snapshot', 'get_all_snapshots')
def test_get_all_snapshots_sort_dir_invalid(self):
self.mock_object(
@ -1235,7 +1235,7 @@ class ShareAPITestCase(test.TestCase):
sort_dir=1,
)
share_api.policy.check_policy.assert_called_once_with(
ctx, 'share', 'get_all_snapshots')
ctx, 'share_snapshot', 'get_all_snapshots')
@ddt.data(None, 'rw', 'ro')
def test_allow_access(self, level):

View File

@ -100,7 +100,8 @@ class SharesV2Client(shares_client.SharesClient):
"""Resets the state of a share, snapshot, cg, or a cgsnapshot.
status: available, error, creating, deleting, error_deleting
s_type: shares, snapshots, consistency-groups, cgsnapshots
s_type: shares, share_instances, snapshots, consistency-groups,
cgsnapshots.
"""
body = {"os-reset_status": {"status": status}}
body = json.dumps(body)

View File

@ -40,14 +40,14 @@ class AdminActionsTest(base.BaseSharesAdminTest):
@test.attr(type=["gate", ])
def test_reset_share_state(self):
for status in self.states:
self.shares_client.reset_state(self.sh["id"], status=status)
self.shares_client.wait_for_share_status(self.sh["id"], status)
self.shares_v2_client.reset_state(self.sh["id"], status=status)
self.shares_v2_client.wait_for_share_status(self.sh["id"], status)
@test.attr(type=["gate", ])
def test_reset_share_instance_state(self):
id = self.sh_instance["id"]
for status in self.states:
self.shares_client.reset_state(
self.shares_v2_client.reset_state(
id, s_type="share_instances", status=status)
self.shares_v2_client.wait_for_share_instance_status(id, status)
@ -56,24 +56,25 @@ class AdminActionsTest(base.BaseSharesAdminTest):
"Snapshot tests are disabled.")
def test_reset_snapshot_state_to_error(self):
for status in self.states:
self.shares_client.reset_state(
self.shares_v2_client.reset_state(
self.sn["id"], s_type="snapshots", status=status)
self.shares_client.wait_for_snapshot_status(self.sn["id"], status)
self.shares_v2_client.wait_for_snapshot_status(
self.sn["id"], status)
@test.attr(type=["gate", ])
def test_force_delete_share(self):
share = self.create_share()
# Change status from 'available' to 'error_deleting'
self.shares_client.reset_state(share["id"], status=self.bad_status)
self.shares_v2_client.reset_state(share["id"], status=self.bad_status)
# Check that status was changed
check_status = self.shares_client.get_share(share["id"])
check_status = self.shares_v2_client.get_share(share["id"])
self.assertEqual(check_status["status"], self.bad_status)
# Share with status 'error_deleting' should be deleted
self.shares_client.force_delete(share["id"])
self.shares_client.wait_for_resource_deletion(share_id=share["id"])
self.shares_v2_client.force_delete(share["id"])
self.shares_v2_client.wait_for_resource_deletion(share_id=share["id"])
@test.attr(type=["gate", ])
def test_force_delete_share_instance(self):
@ -85,7 +86,7 @@ class AdminActionsTest(base.BaseSharesAdminTest):
instance = instances[0]
# Change status from 'available' to 'error_deleting'
self.shares_client.reset_state(
self.shares_v2_client.reset_state(
instance["id"], s_type="share_instances", status=self.bad_status)
# Check that status was changed
@ -93,7 +94,7 @@ class AdminActionsTest(base.BaseSharesAdminTest):
self.assertEqual(self.bad_status, check_status["status"])
# Share with status 'error_deleting' should be deleted
self.shares_client.force_delete(
self.shares_v2_client.force_delete(
instance["id"], s_type="share_instances")
self.shares_v2_client.wait_for_resource_deletion(
share_instance_id=instance["id"])
@ -105,13 +106,13 @@ class AdminActionsTest(base.BaseSharesAdminTest):
sn = self.create_snapshot_wait_for_active(self.sh["id"])
# Change status from 'available' to 'error_deleting'
self.shares_client.reset_state(
self.shares_v2_client.reset_state(
sn["id"], s_type="snapshots", status=self.bad_status)
# Check that status was changed
check_status = self.shares_client.get_snapshot(sn["id"])
check_status = self.shares_v2_client.get_snapshot(sn["id"])
self.assertEqual(check_status["status"], self.bad_status)
# Snapshot with status 'error_deleting' should be deleted
self.shares_client.force_delete(sn["id"], s_type="snapshots")
self.shares_client.wait_for_resource_deletion(snapshot_id=sn["id"])
self.shares_v2_client.force_delete(sn["id"], s_type="snapshots")
self.shares_v2_client.wait_for_resource_deletion(snapshot_id=sn["id"])

View File

@ -45,7 +45,7 @@ class AdminActionsNegativeTest(base.BaseSharesAdminTest):
@test.attr(type=["gate", "negative", ])
def test_reset_nonexistent_share_instance_state(self):
self.assertRaises(lib_exc.NotFound, self.shares_client.reset_state,
self.assertRaises(lib_exc.NotFound, self.shares_v2_client.reset_state,
"fake", s_type="share_instances")
@test.attr(type=["gate", "negative", ])
@ -65,7 +65,7 @@ class AdminActionsNegativeTest(base.BaseSharesAdminTest):
def test_reset_share_instance_state_to_unacceptable_state(self):
self.assertRaises(
lib_exc.BadRequest,
self.shares_client.reset_state,
self.shares_v2_client.reset_state,
self.sh_instance["id"],
s_type="share_instances",
status="fake"
@ -90,7 +90,7 @@ class AdminActionsNegativeTest(base.BaseSharesAdminTest):
def test_try_reset_share_instance_state_with_member(self):
# Even if member from another tenant, it should be unauthorized
self.assertRaises(lib_exc.Forbidden,
self.member_shares_client.reset_state,
self.member_shares_v2_client.reset_state,
self.sh_instance["id"], s_type="share_instances")
@test.attr(type=["gate", "negative", ])
@ -110,7 +110,7 @@ class AdminActionsNegativeTest(base.BaseSharesAdminTest):
@test.attr(type=["gate", "negative", ])
def test_force_delete_nonexistent_share_instance(self):
self.assertRaises(lib_exc.NotFound,
self.shares_client.force_delete,
self.shares_v2_client.force_delete,
"fake",
s_type="share_instances")
@ -134,7 +134,7 @@ class AdminActionsNegativeTest(base.BaseSharesAdminTest):
def test_try_force_delete_share_instance_with_member(self):
# If a non-admin tries to do force_delete, it should be unauthorized
self.assertRaises(lib_exc.Forbidden,
self.member_shares_client.force_delete,
self.member_shares_v2_client.force_delete,
self.sh_instance["id"], s_type="share_instances")
@test.attr(type=["gate", "negative", ])