Port share type extensions to core API
Changes: - Register share type APIs as core API. - Remove extensions code for share types. - Leave rename of 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 Change-Id: I334cc0fa8e69c2f9b5226f247149a0b9b748dd60
This commit is contained in:
parent
9912d296ec
commit
0cb695fd54
@ -54,12 +54,17 @@
|
||||
"share_type:index": "rule:default",
|
||||
"share_type:show": "rule:default",
|
||||
"share_type:default": "rule:default",
|
||||
"share_type:create": "rule:admin_api",
|
||||
"share_type:delete": "rule:admin_api",
|
||||
"share_type:add_project_access": "rule:admin_api",
|
||||
"share_type:list_project_access": "rule:admin_api",
|
||||
"share_type:remove_project_access": "rule:admin_api",
|
||||
|
||||
"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",
|
||||
"share_types_extra_spec:create": "rule:admin_api",
|
||||
"share_types_extra_spec:update": "rule:admin_api",
|
||||
"share_types_extra_spec:show": "rule:admin_api",
|
||||
"share_types_extra_spec:index": "rule:admin_api",
|
||||
"share_types_extra_spec:delete": "rule:admin_api",
|
||||
|
||||
"security_service:create": "rule:default",
|
||||
"security_service:delete": "rule:default",
|
||||
|
@ -1,163 +0,0 @@
|
||||
#
|
||||
# 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.
|
||||
|
||||
"""The share type access extension."""
|
||||
|
||||
from oslo_utils import uuidutils
|
||||
import six
|
||||
import webob
|
||||
|
||||
|
||||
from manila.api import extensions
|
||||
from manila.api.openstack import wsgi
|
||||
from manila import exception
|
||||
from manila.i18n import _
|
||||
from manila.share import share_types
|
||||
|
||||
|
||||
soft_authorize = extensions.soft_extension_authorizer('share',
|
||||
'share_type_access')
|
||||
authorize = extensions.extension_authorizer('share', 'share_type_access')
|
||||
|
||||
|
||||
def _marshall_share_type_access(share_type):
|
||||
rval = []
|
||||
for project_id in share_type['projects']:
|
||||
rval.append({'share_type_id': share_type['id'],
|
||||
'project_id': project_id})
|
||||
|
||||
return {'share_type_access': rval}
|
||||
|
||||
|
||||
class ShareTypeAccessController(object):
|
||||
"""The share type access API controller for the OpenStack API."""
|
||||
|
||||
def index(self, req, type_id):
|
||||
context = req.environ['manila.context']
|
||||
authorize(context)
|
||||
|
||||
try:
|
||||
share_type = share_types.get_share_type(
|
||||
context, type_id, expected_fields=['projects'])
|
||||
except exception.ShareTypeNotFound:
|
||||
explanation = _("Share type %s not found.") % type_id
|
||||
raise webob.exc.HTTPNotFound(explanation=explanation)
|
||||
|
||||
if share_type['is_public']:
|
||||
expl = _("Access list not available for public share types.")
|
||||
raise webob.exc.HTTPNotFound(explanation=expl)
|
||||
|
||||
return _marshall_share_type_access(share_type)
|
||||
|
||||
|
||||
class ShareTypeActionController(wsgi.Controller):
|
||||
"""The share type access API controller for the OpenStack API."""
|
||||
|
||||
def _check_body(self, body, action_name):
|
||||
if not self.is_valid_body(body, action_name):
|
||||
raise webob.exc.HTTPBadRequest()
|
||||
access = body[action_name]
|
||||
project = access.get('project')
|
||||
if not uuidutils.is_uuid_like(project):
|
||||
msg = _("Bad project format: "
|
||||
"project is not in proper format (%s)") % project
|
||||
raise webob.exc.HTTPBadRequest(explanation=msg)
|
||||
|
||||
def _extend_share_type(self, share_type_rval, share_type_ref):
|
||||
if share_type_ref:
|
||||
key = "%s:is_public" % (Share_type_access.alias)
|
||||
share_type_rval[key] = share_type_ref.get('is_public', True)
|
||||
|
||||
@wsgi.extends
|
||||
def show(self, req, resp_obj, id):
|
||||
context = req.environ['manila.context']
|
||||
if soft_authorize(context):
|
||||
share_type = req.get_db_share_type(id)
|
||||
self._extend_share_type(resp_obj.obj['share_type'], share_type)
|
||||
|
||||
@wsgi.extends
|
||||
def index(self, req, resp_obj):
|
||||
context = req.environ['manila.context']
|
||||
if soft_authorize(context):
|
||||
for share_type_rval in list(resp_obj.obj['share_types']):
|
||||
type_id = share_type_rval['id']
|
||||
share_type = req.get_db_share_type(type_id)
|
||||
self._extend_share_type(share_type_rval, share_type)
|
||||
|
||||
@wsgi.extends(action='create')
|
||||
def create(self, req, body, resp_obj):
|
||||
context = req.environ['manila.context']
|
||||
if soft_authorize(context):
|
||||
type_id = resp_obj.obj['share_type']['id']
|
||||
share_type = req.get_db_share_type(type_id)
|
||||
self._extend_share_type(resp_obj.obj['share_type'], share_type)
|
||||
|
||||
@wsgi.action('addProjectAccess')
|
||||
def _addProjectAccess(self, req, id, body):
|
||||
context = req.environ['manila.context']
|
||||
authorize(context, action="addProjectAccess")
|
||||
self._check_body(body, 'addProjectAccess')
|
||||
project = body['addProjectAccess']['project']
|
||||
|
||||
try:
|
||||
share_type = share_types.get_share_type(context, id)
|
||||
|
||||
if share_type['is_public']:
|
||||
msg = _("You cannot add project to public share_type.")
|
||||
raise webob.exc.HTTPForbidden(explanation=msg)
|
||||
|
||||
except exception.ShareTypeNotFound as err:
|
||||
raise webob.exc.HTTPNotFound(explanation=six.text_type(err))
|
||||
|
||||
try:
|
||||
share_types.add_share_type_access(context, id, project)
|
||||
except exception.ShareTypeAccessExists as err:
|
||||
raise webob.exc.HTTPConflict(explanation=six.text_type(err))
|
||||
|
||||
return webob.Response(status_int=202)
|
||||
|
||||
@wsgi.action('removeProjectAccess')
|
||||
def _removeProjectAccess(self, req, id, body):
|
||||
context = req.environ['manila.context']
|
||||
authorize(context, action="removeProjectAccess")
|
||||
self._check_body(body, 'removeProjectAccess')
|
||||
project = body['removeProjectAccess']['project']
|
||||
|
||||
try:
|
||||
share_types.remove_share_type_access(context, id, project)
|
||||
except (exception.ShareTypeNotFound,
|
||||
exception.ShareTypeAccessNotFound) as err:
|
||||
raise webob.exc.HTTPNotFound(explanation=six.text_type(err))
|
||||
return webob.Response(status_int=202)
|
||||
|
||||
|
||||
class Share_type_access(extensions.ExtensionDescriptor):
|
||||
"""share type access support."""
|
||||
|
||||
name = "ShareTypeAccess"
|
||||
alias = "os-share-type-access"
|
||||
updated = "2015-03-02T00:00:00Z"
|
||||
|
||||
def get_resources(self):
|
||||
resources = []
|
||||
res = extensions.ResourceExtension(
|
||||
Share_type_access.alias,
|
||||
ShareTypeAccessController(),
|
||||
parent=dict(member_name='type', collection_name='types'))
|
||||
resources.append(res)
|
||||
return resources
|
||||
|
||||
def get_controller_extensions(self):
|
||||
controller = ShareTypeActionController()
|
||||
extension = extensions.ControllerExtension(self, 'types', controller)
|
||||
return [extension]
|
@ -1,132 +0,0 @@
|
||||
# Copyright (c) 2011 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.
|
||||
|
||||
"""The share types manage extension."""
|
||||
|
||||
import six
|
||||
import webob
|
||||
|
||||
from manila.api import extensions
|
||||
from manila.api.openstack import wsgi
|
||||
from manila.api.views import types as views_types
|
||||
from manila import exception
|
||||
from manila.i18n import _
|
||||
from manila import rpc
|
||||
from manila.share import share_types
|
||||
|
||||
|
||||
authorize = extensions.extension_authorizer('share', 'types_manage')
|
||||
|
||||
|
||||
class ShareTypesManageController(wsgi.Controller):
|
||||
"""The share types API controller for the OpenStack API."""
|
||||
|
||||
_view_builder_class = views_types.ViewBuilder
|
||||
|
||||
def _notify_share_type_error(self, context, method, payload):
|
||||
rpc.get_notifier('shareType').error(context, method, payload)
|
||||
|
||||
@wsgi.action("create")
|
||||
def _create(self, req, body):
|
||||
"""Creates a new share type."""
|
||||
context = req.environ['manila.context']
|
||||
authorize(context)
|
||||
|
||||
if not self.is_valid_body(body, 'share_type') and \
|
||||
not self.is_valid_body(body, 'volume_type'):
|
||||
raise webob.exc.HTTPBadRequest()
|
||||
|
||||
elif self.is_valid_body(body, 'share_type'):
|
||||
share_type = body['share_type']
|
||||
else:
|
||||
share_type = body['volume_type']
|
||||
name = share_type.get('name', None)
|
||||
specs = share_type.get('extra_specs', {})
|
||||
is_public = share_type.get('os-share-type-access:is_public', True)
|
||||
|
||||
if name is None or name == "" or len(name) > 255:
|
||||
msg = _("Type name is not valid.")
|
||||
raise webob.exc.HTTPBadRequest(explanation=msg)
|
||||
|
||||
try:
|
||||
required_extra_specs = (
|
||||
share_types.get_valid_required_extra_specs(specs)
|
||||
)
|
||||
except exception.InvalidExtraSpec as e:
|
||||
raise webob.exc.HTTPBadRequest(explanation=six.text_type(e))
|
||||
|
||||
try:
|
||||
share_types.create(context, name, specs, is_public)
|
||||
share_type = share_types.get_share_type_by_name(context, name)
|
||||
share_type['required_extra_specs'] = required_extra_specs
|
||||
req.cache_db_share_type(share_type)
|
||||
notifier_info = dict(share_types=share_type)
|
||||
rpc.get_notifier('shareType').info(
|
||||
context, 'share_type.create', notifier_info)
|
||||
|
||||
except exception.ShareTypeExists as err:
|
||||
notifier_err = dict(share_types=share_type,
|
||||
error_message=six.text_type(err))
|
||||
self._notify_share_type_error(context, 'share_type.create',
|
||||
notifier_err)
|
||||
|
||||
raise webob.exc.HTTPConflict(explanation=six.text_type(err))
|
||||
except exception.NotFound as err:
|
||||
notifier_err = dict(share_types=share_type,
|
||||
error_message=six.text_type(err))
|
||||
self._notify_share_type_error(context, 'share_type.create',
|
||||
notifier_err)
|
||||
raise webob.exc.HTTPNotFound()
|
||||
|
||||
return self._view_builder.show(req, share_type)
|
||||
|
||||
@wsgi.action("delete")
|
||||
def _delete(self, req, id):
|
||||
"""Deletes an existing share type."""
|
||||
context = req.environ['manila.context']
|
||||
authorize(context)
|
||||
|
||||
try:
|
||||
share_type = share_types.get_share_type(context, id)
|
||||
share_types.destroy(context, share_type['id'])
|
||||
notifier_info = dict(share_types=share_type)
|
||||
rpc.get_notifier('shareType').info(
|
||||
context, 'share_type.delete', notifier_info)
|
||||
except exception.ShareTypeInUse as err:
|
||||
notifier_err = dict(id=id, error_message=six.text_type(err))
|
||||
self._notify_share_type_error(context, 'share_type.delete',
|
||||
notifier_err)
|
||||
msg = 'Target share type is still in use.'
|
||||
raise webob.exc.HTTPBadRequest(explanation=msg)
|
||||
except exception.NotFound as err:
|
||||
notifier_err = dict(id=id, error_message=six.text_type(err))
|
||||
self._notify_share_type_error(context, 'share_type.delete',
|
||||
notifier_err)
|
||||
|
||||
raise webob.exc.HTTPNotFound()
|
||||
|
||||
return webob.Response(status_int=202)
|
||||
|
||||
|
||||
class Types_manage(extensions.ExtensionDescriptor):
|
||||
"""Types manage support."""
|
||||
|
||||
name = "TypesManage"
|
||||
alias = "os-types-manage"
|
||||
updated = "2011-08-24T00:00:00+00:00"
|
||||
|
||||
def get_controller_extensions(self):
|
||||
controller = ShareTypesManageController()
|
||||
extension = extensions.ControllerExtension(self, 'types', controller)
|
||||
return [extension]
|
@ -39,6 +39,7 @@ from manila.api.v1 import share_networks
|
||||
from manila.api.v1 import share_servers
|
||||
from manila.api.v1 import share_snapshots
|
||||
from manila.api.v1 import share_types
|
||||
from manila.api.v1 import share_types_extra_specs
|
||||
from manila.api.v1 import share_unmanage
|
||||
from manila.api.v1 import shares
|
||||
from manila.api import versions
|
||||
@ -176,7 +177,15 @@ class APIRouter(manila.api.openstack.APIRouter):
|
||||
mapper.resource("type", "types",
|
||||
controller=self.resources['types'],
|
||||
collection={'detail': 'GET', 'default': 'GET'},
|
||||
member={'action': 'POST'})
|
||||
member={'action': 'POST',
|
||||
'os-share-type-access': 'GET'})
|
||||
|
||||
self.resources['extra_specs'] = (
|
||||
share_types_extra_specs.create_resource())
|
||||
mapper.resource('extra_spec', 'extra_specs',
|
||||
controller=self.resources['extra_specs'],
|
||||
parent_resource=dict(member_name='type',
|
||||
collection_name='types'))
|
||||
|
||||
self.resources['scheduler_stats'] = scheduler_stats.create_resource()
|
||||
mapper.connect('pools', '/{project_id}/scheduler-stats/pools',
|
||||
|
@ -1,3 +1,4 @@
|
||||
# Copyright (c) 2011 OpenStack Foundation
|
||||
# Copyright (c) 2014 NetApp, Inc.
|
||||
#
|
||||
# Licensed under the Apache License, Version 2.0 (the "License"); you may
|
||||
@ -12,10 +13,12 @@
|
||||
# License for the specific language governing permissions and limitations
|
||||
# under the License.
|
||||
|
||||
"""The share type & share types extra specs extension."""
|
||||
"""The share type API controller module.."""
|
||||
|
||||
from oslo_utils import strutils
|
||||
from oslo_utils import uuidutils
|
||||
import six
|
||||
import webob
|
||||
from webob import exc
|
||||
|
||||
from manila.api.openstack import wsgi
|
||||
@ -23,20 +26,38 @@ from manila.api.views import types as views_types
|
||||
from manila import exception
|
||||
from manila.i18n import _
|
||||
from manila import policy
|
||||
from manila import rpc
|
||||
from manila.share import share_types
|
||||
|
||||
RESOURCE_NAME = 'share_type'
|
||||
|
||||
|
||||
class ShareTypesController(wsgi.Controller):
|
||||
"""The share types API controller for the OpenStack API."""
|
||||
|
||||
resource_name = 'share_type'
|
||||
_view_builder_class = views_types.ViewBuilder
|
||||
|
||||
def __getattr__(self, key):
|
||||
if key == 'os-share-type-access':
|
||||
return self._list_project_access
|
||||
return super(self.__class__, self).__getattr__(key)
|
||||
|
||||
def _notify_share_type_error(self, context, method, payload):
|
||||
rpc.get_notifier('shareType').error(context, method, payload)
|
||||
|
||||
def _check_body(self, body, action_name):
|
||||
if not self.is_valid_body(body, action_name):
|
||||
raise webob.exc.HTTPBadRequest()
|
||||
access = body[action_name]
|
||||
project = access.get('project')
|
||||
if not uuidutils.is_uuid_like(project):
|
||||
msg = _("Bad project format: "
|
||||
"project is not in proper format (%s)") % project
|
||||
raise webob.exc.HTTPBadRequest(explanation=msg)
|
||||
|
||||
def index(self, req):
|
||||
"""Returns the list of share types."""
|
||||
context = req.environ['manila.context']
|
||||
policy.check_policy(context, RESOURCE_NAME, 'index')
|
||||
policy.check_policy(context, self.resource_name, 'index')
|
||||
|
||||
limited_types = self._get_share_types(req)
|
||||
req.cache_db_share_types(limited_types)
|
||||
@ -45,7 +66,7 @@ class ShareTypesController(wsgi.Controller):
|
||||
def show(self, req, id):
|
||||
"""Return a single share type item."""
|
||||
context = req.environ['manila.context']
|
||||
policy.check_policy(context, RESOURCE_NAME, 'show')
|
||||
policy.check_policy(context, self.resource_name, 'show')
|
||||
|
||||
try:
|
||||
share_type = share_types.get_share_type(context, id)
|
||||
@ -60,7 +81,7 @@ class ShareTypesController(wsgi.Controller):
|
||||
def default(self, req):
|
||||
"""Return default volume type."""
|
||||
context = req.environ['manila.context']
|
||||
policy.check_policy(context, RESOURCE_NAME, 'default')
|
||||
policy.check_policy(context, self.resource_name, 'default')
|
||||
|
||||
try:
|
||||
share_type = share_types.get_default_share_type(context)
|
||||
@ -109,6 +130,148 @@ class ShareTypesController(wsgi.Controller):
|
||||
msg = _('Invalid is_public filter [%s]') % is_public
|
||||
raise exc.HTTPBadRequest(explanation=msg)
|
||||
|
||||
@wsgi.action("create")
|
||||
def _create(self, req, body):
|
||||
"""Creates a new share type."""
|
||||
context = req.environ['manila.context']
|
||||
self.authorize(context, 'create')
|
||||
|
||||
if not self.is_valid_body(body, 'share_type') and \
|
||||
not self.is_valid_body(body, 'volume_type'):
|
||||
raise webob.exc.HTTPBadRequest()
|
||||
|
||||
elif self.is_valid_body(body, 'share_type'):
|
||||
share_type = body['share_type']
|
||||
else:
|
||||
share_type = body['volume_type']
|
||||
name = share_type.get('name', None)
|
||||
specs = share_type.get('extra_specs', {})
|
||||
is_public = share_type.get('os-share-type-access:is_public', True)
|
||||
|
||||
if name is None or name == "" or len(name) > 255:
|
||||
msg = _("Type name is not valid.")
|
||||
raise webob.exc.HTTPBadRequest(explanation=msg)
|
||||
|
||||
try:
|
||||
required_extra_specs = (
|
||||
share_types.get_valid_required_extra_specs(specs)
|
||||
)
|
||||
except exception.InvalidExtraSpec as e:
|
||||
raise webob.exc.HTTPBadRequest(explanation=six.text_type(e))
|
||||
|
||||
try:
|
||||
share_types.create(context, name, specs, is_public)
|
||||
share_type = share_types.get_share_type_by_name(context, name)
|
||||
share_type['required_extra_specs'] = required_extra_specs
|
||||
req.cache_db_share_type(share_type)
|
||||
notifier_info = dict(share_types=share_type)
|
||||
rpc.get_notifier('shareType').info(
|
||||
context, 'share_type.create', notifier_info)
|
||||
|
||||
except exception.ShareTypeExists as err:
|
||||
notifier_err = dict(share_types=share_type,
|
||||
error_message=six.text_type(err))
|
||||
self._notify_share_type_error(context, 'share_type.create',
|
||||
notifier_err)
|
||||
|
||||
raise webob.exc.HTTPConflict(explanation=six.text_type(err))
|
||||
except exception.NotFound as err:
|
||||
notifier_err = dict(share_types=share_type,
|
||||
error_message=six.text_type(err))
|
||||
self._notify_share_type_error(context, 'share_type.create',
|
||||
notifier_err)
|
||||
raise webob.exc.HTTPNotFound()
|
||||
|
||||
return self._view_builder.show(req, share_type)
|
||||
|
||||
@wsgi.action("delete")
|
||||
def _delete(self, req, id):
|
||||
"""Deletes an existing share type."""
|
||||
context = req.environ['manila.context']
|
||||
self.authorize(context, 'delete')
|
||||
|
||||
try:
|
||||
share_type = share_types.get_share_type(context, id)
|
||||
share_types.destroy(context, share_type['id'])
|
||||
notifier_info = dict(share_types=share_type)
|
||||
rpc.get_notifier('shareType').info(
|
||||
context, 'share_type.delete', notifier_info)
|
||||
except exception.ShareTypeInUse as err:
|
||||
notifier_err = dict(id=id, error_message=six.text_type(err))
|
||||
self._notify_share_type_error(context, 'share_type.delete',
|
||||
notifier_err)
|
||||
msg = 'Target share type is still in use.'
|
||||
raise webob.exc.HTTPBadRequest(explanation=msg)
|
||||
except exception.NotFound as err:
|
||||
notifier_err = dict(id=id, error_message=six.text_type(err))
|
||||
self._notify_share_type_error(context, 'share_type.delete',
|
||||
notifier_err)
|
||||
|
||||
raise webob.exc.HTTPNotFound()
|
||||
|
||||
return webob.Response(status_int=202)
|
||||
|
||||
def _list_project_access(self, req, id):
|
||||
context = req.environ['manila.context']
|
||||
self.authorize(context, 'list_project_access')
|
||||
|
||||
try:
|
||||
share_type = share_types.get_share_type(
|
||||
context, id, expected_fields=['projects'])
|
||||
except exception.ShareTypeNotFound:
|
||||
explanation = _("Share type %s not found.") % id
|
||||
raise webob.exc.HTTPNotFound(explanation=explanation)
|
||||
|
||||
if share_type['is_public']:
|
||||
expl = _("Access list not available for public share types.")
|
||||
raise webob.exc.HTTPNotFound(explanation=expl)
|
||||
|
||||
# TODO(vponomaryov): move to views.
|
||||
rval = []
|
||||
for project_id in share_type['projects']:
|
||||
rval.append(
|
||||
{'share_type_id': share_type['id'], 'project_id': project_id}
|
||||
)
|
||||
return {'share_type_access': rval}
|
||||
|
||||
@wsgi.action('addProjectAccess')
|
||||
def _add_project_access(self, req, id, body):
|
||||
context = req.environ['manila.context']
|
||||
self.authorize(context, 'add_project_access')
|
||||
self._check_body(body, 'addProjectAccess')
|
||||
project = body['addProjectAccess']['project']
|
||||
|
||||
try:
|
||||
share_type = share_types.get_share_type(context, id)
|
||||
|
||||
if share_type['is_public']:
|
||||
msg = _("Project cannot be added to public share_type.")
|
||||
raise webob.exc.HTTPForbidden(explanation=msg)
|
||||
|
||||
except exception.ShareTypeNotFound as err:
|
||||
raise webob.exc.HTTPNotFound(explanation=six.text_type(err))
|
||||
|
||||
try:
|
||||
share_types.add_share_type_access(context, id, project)
|
||||
except exception.ShareTypeAccessExists as err:
|
||||
raise webob.exc.HTTPConflict(explanation=six.text_type(err))
|
||||
|
||||
return webob.Response(status_int=202)
|
||||
|
||||
@wsgi.action('removeProjectAccess')
|
||||
def _remove_project_access(self, req, id, body):
|
||||
context = req.environ['manila.context']
|
||||
self.authorize(context, 'remove_project_access')
|
||||
self._check_body(body, 'removeProjectAccess')
|
||||
project = body['removeProjectAccess']['project']
|
||||
|
||||
try:
|
||||
share_types.remove_share_type_access(context, id, project)
|
||||
except (exception.ShareTypeNotFound,
|
||||
exception.ShareTypeAccessNotFound) as err:
|
||||
raise webob.exc.HTTPNotFound(explanation=six.text_type(err))
|
||||
return webob.Response(status_int=202)
|
||||
|
||||
|
||||
def create_resource():
|
||||
return wsgi.Resource(ShareTypesController())
|
||||
|
@ -13,13 +13,10 @@
|
||||
# License for the specific language governing permissions and limitations
|
||||
# under the License.
|
||||
|
||||
"""The share types extra specs extension"""
|
||||
|
||||
import six
|
||||
import webob
|
||||
|
||||
from manila.api import common
|
||||
from manila.api import extensions
|
||||
from manila.api.openstack import wsgi
|
||||
from manila import db
|
||||
from manila import exception
|
||||
@ -27,12 +24,12 @@ from manila.i18n import _
|
||||
from manila import rpc
|
||||
from manila.share import share_types
|
||||
|
||||
authorize = extensions.extension_authorizer('share', 'types_extra_specs')
|
||||
|
||||
|
||||
class ShareTypeExtraSpecsController(wsgi.Controller):
|
||||
"""The share type extra specs API controller for the OpenStack API."""
|
||||
|
||||
resource_name = 'share_types_extra_spec'
|
||||
|
||||
def _get_extra_specs(self, context, type_id):
|
||||
extra_specs = db.share_type_extra_specs_get(context, type_id)
|
||||
specs_dict = {}
|
||||
@ -77,13 +74,13 @@ class ShareTypeExtraSpecsController(wsgi.Controller):
|
||||
def index(self, req, type_id):
|
||||
"""Returns the list of extra specs for a given share type."""
|
||||
context = req.environ['manila.context']
|
||||
authorize(context)
|
||||
self.authorize(context, 'index')
|
||||
self._check_type(context, type_id)
|
||||
return self._get_extra_specs(context, type_id)
|
||||
|
||||
def create(self, req, type_id, body=None):
|
||||
context = req.environ['manila.context']
|
||||
authorize(context)
|
||||
self.authorize(context, 'create')
|
||||
|
||||
if not self.is_valid_body(body, 'extra_specs'):
|
||||
raise webob.exc.HTTPBadRequest()
|
||||
@ -100,7 +97,7 @@ class ShareTypeExtraSpecsController(wsgi.Controller):
|
||||
|
||||
def update(self, req, type_id, id, body=None):
|
||||
context = req.environ['manila.context']
|
||||
authorize(context)
|
||||
self.authorize(context, 'update')
|
||||
if not body:
|
||||
expl = _('Request body empty')
|
||||
raise webob.exc.HTTPBadRequest(explanation=expl)
|
||||
@ -121,7 +118,7 @@ class ShareTypeExtraSpecsController(wsgi.Controller):
|
||||
def show(self, req, type_id, id):
|
||||
"""Return a single extra spec item."""
|
||||
context = req.environ['manila.context']
|
||||
authorize(context)
|
||||
self.authorize(context, 'show')
|
||||
self._check_type(context, type_id)
|
||||
specs = self._get_extra_specs(context, type_id)
|
||||
if id in specs['extra_specs']:
|
||||
@ -133,7 +130,7 @@ class ShareTypeExtraSpecsController(wsgi.Controller):
|
||||
"""Deletes an existing extra spec."""
|
||||
context = req.environ['manila.context']
|
||||
self._check_type(context, type_id)
|
||||
authorize(context)
|
||||
self.authorize(context, 'delete')
|
||||
|
||||
if id in share_types.get_undeletable_extra_specs():
|
||||
msg = _("Extra spec '%s' can't be deleted.") % id
|
||||
@ -157,21 +154,5 @@ class ShareTypeExtraSpecsController(wsgi.Controller):
|
||||
raise webob.exc.HTTPBadRequest(explanation=expl)
|
||||
|
||||
|
||||
class Types_extra_specs(extensions.ExtensionDescriptor):
|
||||
"""Type extra specs support."""
|
||||
|
||||
name = "TypesExtraSpecs"
|
||||
alias = "os-types-extra-specs"
|
||||
updated = "2011-08-24T00:00:00+00:00"
|
||||
|
||||
def get_resources(self):
|
||||
resources = []
|
||||
res = extensions.ResourceExtension(
|
||||
'extra_specs',
|
||||
ShareTypeExtraSpecsController(),
|
||||
parent=dict(member_name='type',
|
||||
collection_name='types')
|
||||
)
|
||||
resources.append(res)
|
||||
|
||||
return resources
|
||||
def create_resource():
|
||||
return wsgi.Resource(ShareTypeExtraSpecsController())
|
@ -35,10 +35,14 @@ class ViewBuilder(common.ViewBuilder):
|
||||
required_extra_specs = self._filter_extra_specs(
|
||||
required_extra_specs, extra_spec_names)
|
||||
|
||||
trimmed = dict(id=share_type.get('id'),
|
||||
name=share_type.get('name'),
|
||||
extra_specs=extra_specs,
|
||||
required_extra_specs=required_extra_specs)
|
||||
trimmed = {
|
||||
'id': share_type.get('id'),
|
||||
'name': share_type.get('name'),
|
||||
'os-share-type-access:is_public': share_type.get(
|
||||
'is_public', True),
|
||||
'extra_specs': extra_specs,
|
||||
'required_extra_specs': required_extra_specs,
|
||||
}
|
||||
if brief:
|
||||
return trimmed
|
||||
else:
|
||||
|
@ -1,317 +0,0 @@
|
||||
#
|
||||
# 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 datetime
|
||||
|
||||
import mock
|
||||
import six
|
||||
import webob
|
||||
|
||||
from manila.api.contrib import share_type_access as type_access
|
||||
from manila.api.v1 import share_types
|
||||
from manila import context
|
||||
from manila import db
|
||||
from manila import exception
|
||||
from manila.share import share_types as share_types_api
|
||||
from manila import test
|
||||
from manila.tests.api import fakes
|
||||
|
||||
|
||||
def generate_type(type_id, is_public):
|
||||
return {
|
||||
'id': type_id,
|
||||
'name': u'test',
|
||||
'deleted': False,
|
||||
'created_at': datetime.datetime(2012, 1, 1, 1, 1, 1, 1),
|
||||
'updated_at': None,
|
||||
'deleted_at': None,
|
||||
'is_public': bool(is_public),
|
||||
'extra_specs': {}
|
||||
}
|
||||
|
||||
|
||||
SHARE_TYPES = {
|
||||
'0': generate_type('0', True),
|
||||
'1': generate_type('1', True),
|
||||
'2': generate_type('2', False),
|
||||
'3': generate_type('3', False)}
|
||||
|
||||
PROJ1_UUID = '11111111-1111-1111-1111-111111111111'
|
||||
PROJ2_UUID = '22222222-2222-2222-2222-222222222222'
|
||||
PROJ3_UUID = '33333333-3333-3333-3333-333333333333'
|
||||
|
||||
ACCESS_LIST = [{'share_type_id': '2', 'project_id': PROJ2_UUID},
|
||||
{'share_type_id': '2', 'project_id': PROJ3_UUID},
|
||||
{'share_type_id': '3', 'project_id': PROJ3_UUID}]
|
||||
|
||||
|
||||
def fake_share_type_get(context, id, inactive=False, expected_fields=None):
|
||||
vol = SHARE_TYPES[id]
|
||||
if expected_fields and 'projects' in expected_fields:
|
||||
vol['projects'] = [a['project_id']
|
||||
for a in ACCESS_LIST if a['share_type_id'] == id]
|
||||
return vol
|
||||
|
||||
|
||||
def _has_type_access(type_id, project_id):
|
||||
for access in ACCESS_LIST:
|
||||
if (access['share_type_id'] == type_id
|
||||
and access['project_id'] == project_id):
|
||||
return True
|
||||
return False
|
||||
|
||||
|
||||
def fake_share_type_get_all(context, inactive=False, filters=None):
|
||||
if filters is None or filters.get('is_public', None) is None:
|
||||
return SHARE_TYPES
|
||||
res = {}
|
||||
for k, v in six.iteritems(SHARE_TYPES):
|
||||
if filters['is_public'] and _has_type_access(k, context.project_id):
|
||||
res.update({k: v})
|
||||
continue
|
||||
if v['is_public'] == filters['is_public']:
|
||||
res.update({k: v})
|
||||
return res
|
||||
|
||||
|
||||
class FakeResponse(object):
|
||||
obj = {'share_type': {'id': '0'},
|
||||
'share_types': [
|
||||
{'id': '0'},
|
||||
{'id': '2'}]}
|
||||
|
||||
def attach(self, **kwargs):
|
||||
pass
|
||||
|
||||
|
||||
class FakeRequest(object):
|
||||
environ = {"manila.context": context.get_admin_context()}
|
||||
|
||||
def get_db_share_type(self, resource_id):
|
||||
return SHARE_TYPES[resource_id]
|
||||
|
||||
|
||||
class ShareTypeAccessTest(test.TestCase):
|
||||
|
||||
def setUp(self):
|
||||
super(ShareTypeAccessTest, self).setUp()
|
||||
self.type_access_controller = type_access.ShareTypeAccessController()
|
||||
self.type_action_controller = type_access.ShareTypeActionController()
|
||||
self.type_controller = share_types.ShareTypesController()
|
||||
self.req = FakeRequest()
|
||||
self.context = self.req.environ['manila.context']
|
||||
self.mock_object(db, 'share_type_get',
|
||||
fake_share_type_get)
|
||||
self.mock_object(db, 'share_type_get_all',
|
||||
fake_share_type_get_all)
|
||||
|
||||
def assertShareTypeListEqual(self, expected, observed):
|
||||
self.assertEqual(len(expected), len(observed))
|
||||
expected = sorted(expected, key=lambda item: item['id'])
|
||||
observed = sorted(observed, key=lambda item: item['id'])
|
||||
for d1, d2 in zip(expected, observed):
|
||||
self.assertEqual(d1['id'], d2['id'])
|
||||
|
||||
def test_list_type_access_public(self):
|
||||
"""Querying os-share-type-access on public type should return 404."""
|
||||
req = fakes.HTTPRequest.blank('/v1/fake/types/os-share-type-access',
|
||||
use_admin_context=True)
|
||||
self.assertRaises(webob.exc.HTTPNotFound,
|
||||
self.type_access_controller.index,
|
||||
req, '1')
|
||||
|
||||
def test_list_type_access_private(self):
|
||||
expected = {'share_type_access': [
|
||||
{'share_type_id': '2', 'project_id': PROJ2_UUID},
|
||||
{'share_type_id': '2', 'project_id': PROJ3_UUID}]}
|
||||
result = self.type_access_controller.index(self.req, '2')
|
||||
self.assertEqual(expected, result)
|
||||
|
||||
def test_list_with_no_context(self):
|
||||
req = fakes.HTTPRequest.blank('/v1/types/fake/types')
|
||||
|
||||
def fake_authorize(context, target=None, action=None):
|
||||
raise exception.PolicyNotAuthorized(action='index')
|
||||
self.mock_object(type_access, 'authorize', fake_authorize)
|
||||
|
||||
self.assertRaises(exception.PolicyNotAuthorized,
|
||||
self.type_access_controller.index,
|
||||
req, 'fake')
|
||||
|
||||
def test_list_type_with_admin_default_proj1(self):
|
||||
expected = {'share_types': [{'id': '0'}, {'id': '1'}]}
|
||||
req = fakes.HTTPRequest.blank('/v1/fake/types',
|
||||
use_admin_context=True)
|
||||
req.environ['manila.context'].project_id = PROJ1_UUID
|
||||
result = self.type_controller.index(req)
|
||||
self.assertShareTypeListEqual(expected['share_types'],
|
||||
result['share_types'])
|
||||
|
||||
def test_list_type_with_admin_default_proj2(self):
|
||||
expected = {'share_types': [{'id': '0'}, {'id': '1'}, {'id': '2'}]}
|
||||
req = fakes.HTTPRequest.blank('/v2/fake/types',
|
||||
use_admin_context=True)
|
||||
req.environ['manila.context'].project_id = PROJ2_UUID
|
||||
result = self.type_controller.index(req)
|
||||
self.assertShareTypeListEqual(expected['share_types'],
|
||||
result['share_types'])
|
||||
|
||||
def test_list_type_with_admin_ispublic_true(self):
|
||||
expected = {'share_types': [{'id': '0'}, {'id': '1'}]}
|
||||
req = fakes.HTTPRequest.blank('/v2/fake/types?is_public=true',
|
||||
use_admin_context=True)
|
||||
result = self.type_controller.index(req)
|
||||
self.assertShareTypeListEqual(expected['share_types'],
|
||||
result['share_types'])
|
||||
|
||||
def test_list_type_with_admin_ispublic_false(self):
|
||||
expected = {'share_types': [{'id': '2'}, {'id': '3'}]}
|
||||
req = fakes.HTTPRequest.blank('/v2/fake/types?is_public=false',
|
||||
use_admin_context=True)
|
||||
result = self.type_controller.index(req)
|
||||
self.assertShareTypeListEqual(expected['share_types'],
|
||||
result['share_types'])
|
||||
|
||||
def test_list_type_with_admin_ispublic_false_proj2(self):
|
||||
expected = {'share_types': [{'id': '2'}, {'id': '3'}]}
|
||||
req = fakes.HTTPRequest.blank('/v2/fake/types?is_public=false',
|
||||
use_admin_context=True)
|
||||
req.environ['manila.context'].project_id = PROJ2_UUID
|
||||
result = self.type_controller.index(req)
|
||||
self.assertShareTypeListEqual(expected['share_types'],
|
||||
result['share_types'])
|
||||
|
||||
def test_list_type_with_admin_ispublic_none(self):
|
||||
expected = {'share_types': [{'id': '0'}, {'id': '1'}, {'id': '2'},
|
||||
{'id': '3'}]}
|
||||
req = fakes.HTTPRequest.blank('/v2/fake/types?is_public=all',
|
||||
use_admin_context=True)
|
||||
result = self.type_controller.index(req)
|
||||
self.assertShareTypeListEqual(expected['share_types'],
|
||||
result['share_types'])
|
||||
|
||||
def test_list_type_with_no_admin_default(self):
|
||||
expected = {'share_types': [{'id': '0'}, {'id': '1'}]}
|
||||
req = fakes.HTTPRequest.blank('/v2/fake/types',
|
||||
use_admin_context=False)
|
||||
result = self.type_controller.index(req)
|
||||
self.assertShareTypeListEqual(expected['share_types'],
|
||||
result['share_types'])
|
||||
|
||||
def test_list_type_with_no_admin_ispublic_true(self):
|
||||
expected = {'share_types': [{'id': '0'}, {'id': '1'}]}
|
||||
req = fakes.HTTPRequest.blank('/v2/fake/types?is_public=true',
|
||||
use_admin_context=False)
|
||||
result = self.type_controller.index(req)
|
||||
self.assertShareTypeListEqual(expected['share_types'],
|
||||
result['share_types'])
|
||||
|
||||
def test_list_type_with_no_admin_ispublic_false(self):
|
||||
expected = {'share_types': [{'id': '0'}, {'id': '1'}]}
|
||||
req = fakes.HTTPRequest.blank('/v2/fake/types?is_public=false',
|
||||
use_admin_context=False)
|
||||
result = self.type_controller.index(req)
|
||||
self.assertShareTypeListEqual(expected['share_types'],
|
||||
result['share_types'])
|
||||
|
||||
def test_list_type_with_no_admin_ispublic_none(self):
|
||||
expected = {'share_types': [{'id': '0'}, {'id': '1'}]}
|
||||
req = fakes.HTTPRequest.blank('/v2/fake/types?is_public=all',
|
||||
use_admin_context=False)
|
||||
result = self.type_controller.index(req)
|
||||
self.assertShareTypeListEqual(expected['share_types'],
|
||||
result['share_types'])
|
||||
|
||||
def test_show(self):
|
||||
resp = FakeResponse()
|
||||
self.type_action_controller.show(self.req, resp, '0')
|
||||
self.assertEqual({'id': '0', 'os-share-type-access:is_public': True},
|
||||
resp.obj['share_type'])
|
||||
self.type_action_controller.show(self.req, resp, '2')
|
||||
self.assertEqual({'id': '0', 'os-share-type-access:is_public': False},
|
||||
resp.obj['share_type'])
|
||||
|
||||
def test_create(self):
|
||||
resp = FakeResponse()
|
||||
self.type_action_controller.create(self.req, {}, resp)
|
||||
self.assertEqual({'id': '0', 'os-share-type-access:is_public': True},
|
||||
resp.obj['share_type'])
|
||||
|
||||
def test_add_project_access(self):
|
||||
def stub_add_share_type_access(context, type_id, project_id):
|
||||
self.assertEqual('3', type_id, "type_id")
|
||||
self.assertEqual(PROJ2_UUID, project_id, "project_id")
|
||||
self.mock_object(db, 'share_type_access_add',
|
||||
stub_add_share_type_access)
|
||||
body = {'addProjectAccess': {'project': PROJ2_UUID}}
|
||||
req = fakes.HTTPRequest.blank('/v2/fake/types/2/action',
|
||||
use_admin_context=True)
|
||||
result = self.type_action_controller._addProjectAccess(req, '3', body)
|
||||
self.assertEqual(202, result.status_code)
|
||||
|
||||
def test_add_project_access_with_no_admin_user(self):
|
||||
req = fakes.HTTPRequest.blank('/v2/fake/types/2/action',
|
||||
use_admin_context=False)
|
||||
body = {'addProjectAccess': {'project': PROJ2_UUID}}
|
||||
self.assertRaises(exception.PolicyNotAuthorized,
|
||||
self.type_action_controller._addProjectAccess,
|
||||
req, '2', body)
|
||||
|
||||
def test_add_project_access_with_already_added_access(self):
|
||||
def stub_add_share_type_access(context, type_id, project_id):
|
||||
raise exception.ShareTypeAccessExists(share_type_id=type_id,
|
||||
project_id=project_id)
|
||||
self.mock_object(db, 'share_type_access_add',
|
||||
stub_add_share_type_access)
|
||||
body = {'addProjectAccess': {'project': PROJ2_UUID}}
|
||||
req = fakes.HTTPRequest.blank('/v2/fake/types/2/action',
|
||||
use_admin_context=True)
|
||||
self.assertRaises(webob.exc.HTTPConflict,
|
||||
self.type_action_controller._addProjectAccess,
|
||||
req, '3', body)
|
||||
|
||||
def test_add_project_access_to_public_share_type(self):
|
||||
share_type_id = '3'
|
||||
body = {'addProjectAccess': {'project': PROJ2_UUID}}
|
||||
self.mock_object(share_types_api, 'get_share_type',
|
||||
mock.Mock(return_value={"is_public": True}))
|
||||
|
||||
req = fakes.HTTPRequest.blank('/v2/fake/types/2/action',
|
||||
use_admin_context=True)
|
||||
|
||||
self.assertRaises(webob.exc.HTTPForbidden,
|
||||
self.type_action_controller._addProjectAccess,
|
||||
req, share_type_id, body)
|
||||
share_types_api.get_share_type.assert_called_once_with(
|
||||
mock.ANY, share_type_id)
|
||||
|
||||
def test_remove_project_access_with_bad_access(self):
|
||||
def stub_remove_share_type_access(context, type_id, project_id):
|
||||
raise exception.ShareTypeAccessNotFound(share_type_id=type_id,
|
||||
project_id=project_id)
|
||||
self.mock_object(db, 'share_type_access_remove',
|
||||
stub_remove_share_type_access)
|
||||
body = {'removeProjectAccess': {'project': PROJ2_UUID}}
|
||||
req = fakes.HTTPRequest.blank('/v2/fake/types/2/action',
|
||||
use_admin_context=True)
|
||||
self.assertRaises(webob.exc.HTTPNotFound,
|
||||
self.type_action_controller._removeProjectAccess,
|
||||
req, '3', body)
|
||||
|
||||
def test_remove_project_access_with_no_admin_user(self):
|
||||
req = fakes.HTTPRequest.blank('/v2/fake/types/2/action',
|
||||
use_admin_context=False)
|
||||
body = {'removeProjectAccess': {'project': PROJ2_UUID}}
|
||||
self.assertRaises(exception.PolicyNotAuthorized,
|
||||
self.type_action_controller._removeProjectAccess,
|
||||
req, '2', body)
|
@ -1,153 +0,0 @@
|
||||
# Copyright 2011 OpenStack Foundation
|
||||
# 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
|
||||
import webob
|
||||
|
||||
from manila.api.contrib import types_manage
|
||||
from manila.common import constants
|
||||
from manila import exception
|
||||
from manila.share import share_types
|
||||
from manila import test
|
||||
from manila.tests.api import fakes
|
||||
from manila.tests import fake_notifier
|
||||
|
||||
|
||||
def stub_share_type(id):
|
||||
specs = {"key1": "value1",
|
||||
"key2": "value2",
|
||||
"key3": "value3",
|
||||
"key4": "value4",
|
||||
"key5": "value5"}
|
||||
return dict(id=id, name='share_type_%s' % str(id), extra_specs=specs)
|
||||
|
||||
|
||||
def return_share_types_get_share_type(context, id):
|
||||
if id == "777":
|
||||
raise exception.ShareTypeNotFound(share_type_id=id)
|
||||
return stub_share_type(int(id))
|
||||
|
||||
|
||||
def return_share_types_destroy(context, name):
|
||||
if name == "777":
|
||||
raise exception.ShareTypeNotFoundByName(share_type_name=name)
|
||||
pass
|
||||
|
||||
|
||||
def return_share_types_with_volumes_destroy(context, id):
|
||||
if id == "1":
|
||||
raise exception.ShareTypeInUse(share_type_id=id)
|
||||
pass
|
||||
|
||||
|
||||
def return_share_types_create(context, name, specs, is_public):
|
||||
pass
|
||||
|
||||
|
||||
def return_share_types_get_by_name(context, name):
|
||||
if name == "777":
|
||||
raise exception.ShareTypeNotFoundByName(share_type_name=name)
|
||||
return stub_share_type(int(name.split("_")[2]))
|
||||
|
||||
|
||||
def make_create_body(name="test_share_1", extra_specs=None,
|
||||
spec_driver_handles_share_servers=True):
|
||||
if not extra_specs:
|
||||
extra_specs = {}
|
||||
|
||||
if spec_driver_handles_share_servers is not None:
|
||||
extra_specs[constants.ExtraSpecs.DRIVER_HANDLES_SHARE_SERVERS] =\
|
||||
spec_driver_handles_share_servers
|
||||
|
||||
body = {
|
||||
"share_type": {
|
||||
"name": name,
|
||||
"extra_specs": extra_specs
|
||||
}
|
||||
}
|
||||
|
||||
return body
|
||||
|
||||
|
||||
@ddt.ddt
|
||||
class ShareTypesManageApiTest(test.TestCase):
|
||||
def setUp(self):
|
||||
super(ShareTypesManageApiTest, self).setUp()
|
||||
self.flags(host='fake')
|
||||
self.controller = types_manage.ShareTypesManageController()
|
||||
|
||||
"""to reset notifier drivers left over from other api/contrib tests"""
|
||||
fake_notifier.reset()
|
||||
self.addCleanup(fake_notifier.reset)
|
||||
self.mock_object(share_types, 'create',
|
||||
return_share_types_create)
|
||||
self.mock_object(share_types, 'get_share_type_by_name',
|
||||
return_share_types_get_by_name)
|
||||
self.mock_object(share_types, 'get_share_type',
|
||||
return_share_types_get_share_type)
|
||||
self.mock_object(share_types, 'destroy',
|
||||
return_share_types_destroy)
|
||||
|
||||
def test_share_types_delete(self):
|
||||
req = fakes.HTTPRequest.blank('/v2/fake/types/1')
|
||||
self.assertEqual(0, len(fake_notifier.NOTIFICATIONS))
|
||||
self.controller._delete(req, 1)
|
||||
self.assertEqual(1, len(fake_notifier.NOTIFICATIONS))
|
||||
|
||||
def test_share_types_delete_not_found(self):
|
||||
self.assertEqual(0, len(fake_notifier.NOTIFICATIONS))
|
||||
req = fakes.HTTPRequest.blank('/v2/fake/types/777')
|
||||
self.assertRaises(webob.exc.HTTPNotFound, self.controller._delete,
|
||||
req, '777')
|
||||
self.assertEqual(1, len(fake_notifier.NOTIFICATIONS))
|
||||
|
||||
def test_share_types_with_volumes_destroy(self):
|
||||
req = fakes.HTTPRequest.blank('/v2/fake/types/1')
|
||||
self.assertEqual(0, len(fake_notifier.NOTIFICATIONS))
|
||||
self.controller._delete(req, 1)
|
||||
self.assertEqual(1, len(fake_notifier.NOTIFICATIONS))
|
||||
|
||||
@ddt.data(make_create_body("share_type_1"),
|
||||
make_create_body(spec_driver_handles_share_servers="false"),
|
||||
make_create_body(spec_driver_handles_share_servers="true"),
|
||||
make_create_body(spec_driver_handles_share_servers="1"),
|
||||
make_create_body(spec_driver_handles_share_servers="0"),
|
||||
make_create_body(spec_driver_handles_share_servers="True"),
|
||||
make_create_body(spec_driver_handles_share_servers="False"),
|
||||
make_create_body(spec_driver_handles_share_servers="FalsE"))
|
||||
def test_create(self, body):
|
||||
req = fakes.HTTPRequest.blank('/v2/fake/types')
|
||||
self.assertEqual(0, len(fake_notifier.NOTIFICATIONS))
|
||||
res_dict = self.controller._create(req, body)
|
||||
self.assertEqual(1, len(fake_notifier.NOTIFICATIONS))
|
||||
self.assertEqual(2, len(res_dict))
|
||||
self.assertEqual('share_type_1', res_dict['share_type']['name'])
|
||||
self.assertEqual('share_type_1', res_dict['volume_type']['name'])
|
||||
|
||||
@ddt.data(None,
|
||||
make_create_body(""),
|
||||
make_create_body("n" * 256),
|
||||
{'foo': {'a': 'b'}},
|
||||
{'share_type': 'string'},
|
||||
make_create_body(spec_driver_handles_share_servers=None),
|
||||
make_create_body(spec_driver_handles_share_servers=""),
|
||||
make_create_body(spec_driver_handles_share_servers=[]),
|
||||
)
|
||||
def test_create_invalid_request(self, body):
|
||||
req = fakes.HTTPRequest.blank('/v2/fake/types')
|
||||
self.assertEqual(0, len(fake_notifier.NOTIFICATIONS))
|
||||
self.assertRaises(webob.exc.HTTPBadRequest,
|
||||
self.controller._create, req, body)
|
||||
self.assertEqual(0, len(fake_notifier.NOTIFICATIONS))
|
@ -13,19 +13,25 @@
|
||||
# License for the specific language governing permissions and limitations
|
||||
# under the License.
|
||||
|
||||
import datetime
|
||||
|
||||
import ddt
|
||||
import mock
|
||||
from oslo_utils import timeutils
|
||||
import six
|
||||
import webob
|
||||
|
||||
from manila.api.v1 import share_types as types
|
||||
from manila.api.views import types as views_types
|
||||
from manila.common import constants
|
||||
from manila import context
|
||||
from manila import db
|
||||
from manila import exception
|
||||
from manila import policy
|
||||
from manila.share import share_types
|
||||
from manila import test
|
||||
from manila.tests.api import fakes
|
||||
from manila.tests import fake_notifier
|
||||
|
||||
|
||||
def stub_share_type(id):
|
||||
@ -71,13 +77,60 @@ def return_share_types_get_by_name(context, name):
|
||||
return stub_share_type(int(name.split("_")[2]))
|
||||
|
||||
|
||||
def return_share_types_destroy(context, name):
|
||||
if name == "777":
|
||||
raise exception.ShareTypeNotFoundByName(share_type_name=name)
|
||||
pass
|
||||
|
||||
|
||||
def return_share_types_with_volumes_destroy(context, id):
|
||||
if id == "1":
|
||||
raise exception.ShareTypeInUse(share_type_id=id)
|
||||
pass
|
||||
|
||||
|
||||
def return_share_types_create(context, name, specs, is_public):
|
||||
pass
|
||||
|
||||
|
||||
def make_create_body(name="test_share_1", extra_specs=None,
|
||||
spec_driver_handles_share_servers=True):
|
||||
if not extra_specs:
|
||||
extra_specs = {}
|
||||
|
||||
if spec_driver_handles_share_servers is not None:
|
||||
extra_specs[constants.ExtraSpecs.DRIVER_HANDLES_SHARE_SERVERS] =\
|
||||
spec_driver_handles_share_servers
|
||||
|
||||
body = {
|
||||
"share_type": {
|
||||
"name": name,
|
||||
"extra_specs": extra_specs
|
||||
}
|
||||
}
|
||||
|
||||
return body
|
||||
|
||||
|
||||
@ddt.ddt
|
||||
class ShareTypesApiTest(test.TestCase):
|
||||
class ShareTypesAPITest(test.TestCase):
|
||||
|
||||
def setUp(self):
|
||||
super(ShareTypesApiTest, self).setUp()
|
||||
super(self.__class__, self).setUp()
|
||||
self.flags(host='fake')
|
||||
self.controller = types.ShareTypesController()
|
||||
self.mock_object(policy, 'check_policy',
|
||||
mock.Mock(return_value=True))
|
||||
fake_notifier.reset()
|
||||
self.addCleanup(fake_notifier.reset)
|
||||
self.mock_object(share_types, 'create',
|
||||
return_share_types_create)
|
||||
self.mock_object(share_types, 'get_share_type_by_name',
|
||||
return_share_types_get_by_name)
|
||||
self.mock_object(share_types, 'get_share_type',
|
||||
return_share_types_get_share_type)
|
||||
self.mock_object(share_types, 'destroy',
|
||||
return_share_types_destroy)
|
||||
|
||||
@ddt.data(True, False)
|
||||
def test_share_types_index(self, admin):
|
||||
@ -104,7 +157,8 @@ class ShareTypesApiTest(test.TestCase):
|
||||
constants.ExtraSpecs.DRIVER_HANDLES_SHARE_SERVERS, '')
|
||||
self.assertEqual('true', required_extra_spec)
|
||||
policy.check_policy.assert_called_once_with(
|
||||
req.environ['manila.context'], types.RESOURCE_NAME, 'index')
|
||||
req.environ['manila.context'], self.controller.resource_name,
|
||||
'index')
|
||||
|
||||
def test_share_types_index_no_data(self):
|
||||
self.mock_object(share_types, 'get_all_types',
|
||||
@ -115,7 +169,8 @@ class ShareTypesApiTest(test.TestCase):
|
||||
|
||||
self.assertEqual(0, len(res_dict['share_types']))
|
||||
policy.check_policy.assert_called_once_with(
|
||||
req.environ['manila.context'], types.RESOURCE_NAME, 'index')
|
||||
req.environ['manila.context'], self.controller.resource_name,
|
||||
'index')
|
||||
|
||||
def test_share_types_show(self):
|
||||
self.mock_object(share_types, 'get_share_type',
|
||||
@ -128,7 +183,8 @@ class ShareTypesApiTest(test.TestCase):
|
||||
self.assertEqual('1', res_dict['share_type']['id'])
|
||||
self.assertEqual('share_type_1', res_dict['share_type']['name'])
|
||||
policy.check_policy.assert_called_once_with(
|
||||
req.environ['manila.context'], types.RESOURCE_NAME, 'show')
|
||||
req.environ['manila.context'], self.controller.resource_name,
|
||||
'show')
|
||||
|
||||
def test_share_types_show_not_found(self):
|
||||
self.mock_object(share_types, 'get_share_type',
|
||||
@ -138,7 +194,8 @@ class ShareTypesApiTest(test.TestCase):
|
||||
self.assertRaises(webob.exc.HTTPNotFound, self.controller.show,
|
||||
req, '777')
|
||||
policy.check_policy.assert_called_once_with(
|
||||
req.environ['manila.context'], types.RESOURCE_NAME, 'show')
|
||||
req.environ['manila.context'], self.controller.resource_name,
|
||||
'show')
|
||||
|
||||
def test_share_types_default(self):
|
||||
self.mock_object(share_types, 'get_default_share_type',
|
||||
@ -151,7 +208,8 @@ class ShareTypesApiTest(test.TestCase):
|
||||
self.assertEqual('1', res_dict['share_type']['id'])
|
||||
self.assertEqual('share_type_1', res_dict['share_type']['name'])
|
||||
policy.check_policy.assert_called_once_with(
|
||||
req.environ['manila.context'], types.RESOURCE_NAME, 'default')
|
||||
req.environ['manila.context'], self.controller.resource_name,
|
||||
'default')
|
||||
|
||||
def test_share_types_default_not_found(self):
|
||||
self.mock_object(share_types, 'get_default_share_type',
|
||||
@ -161,7 +219,8 @@ class ShareTypesApiTest(test.TestCase):
|
||||
|
||||
self.assertRaises(webob.exc.HTTPNotFound, self.controller.default, req)
|
||||
policy.check_policy.assert_called_once_with(
|
||||
req.environ['manila.context'], types.RESOURCE_NAME, 'default')
|
||||
req.environ['manila.context'], self.controller.resource_name,
|
||||
'default')
|
||||
|
||||
def test_view_builder_show(self):
|
||||
view_builder = views_types.ViewBuilder()
|
||||
@ -182,12 +241,13 @@ class ShareTypesApiTest(test.TestCase):
|
||||
output = view_builder.show(request, raw_share_type)
|
||||
|
||||
self.assertIn('share_type', output)
|
||||
expected_share_type = dict(
|
||||
name='new_type',
|
||||
extra_specs={},
|
||||
required_extra_specs={},
|
||||
id=42,
|
||||
)
|
||||
expected_share_type = {
|
||||
'name': 'new_type',
|
||||
'extra_specs': {},
|
||||
'os-share-type-access:is_public': True,
|
||||
'required_extra_specs': {},
|
||||
'id': 42,
|
||||
}
|
||||
self.assertDictMatch(output['share_type'], expected_share_type)
|
||||
|
||||
def test_view_builder_list(self):
|
||||
@ -214,12 +274,13 @@ class ShareTypesApiTest(test.TestCase):
|
||||
|
||||
self.assertIn('share_types', output)
|
||||
for i in range(0, 10):
|
||||
expected_share_type = dict(
|
||||
name='new_type',
|
||||
extra_specs={},
|
||||
required_extra_specs={},
|
||||
id=42 + i
|
||||
)
|
||||
expected_share_type = {
|
||||
'name': 'new_type',
|
||||
'extra_specs': {},
|
||||
'os-share-type-access:is_public': True,
|
||||
'required_extra_specs': {},
|
||||
'id': 42 + i,
|
||||
}
|
||||
self.assertDictMatch(output['share_types'][i],
|
||||
expected_share_type)
|
||||
|
||||
@ -232,3 +293,359 @@ class ShareTypesApiTest(test.TestCase):
|
||||
self.assertRaises(webob.exc.HTTPBadRequest,
|
||||
self.controller._parse_is_public,
|
||||
'fakefakefake')
|
||||
|
||||
def test_share_types_delete(self):
|
||||
req = fakes.HTTPRequest.blank('/v2/fake/types/1')
|
||||
self.assertEqual(0, len(fake_notifier.NOTIFICATIONS))
|
||||
self.controller._delete(req, 1)
|
||||
self.assertEqual(1, len(fake_notifier.NOTIFICATIONS))
|
||||
|
||||
def test_share_types_delete_not_found(self):
|
||||
self.assertEqual(0, len(fake_notifier.NOTIFICATIONS))
|
||||
req = fakes.HTTPRequest.blank('/v2/fake/types/777')
|
||||
self.assertRaises(webob.exc.HTTPNotFound, self.controller._delete,
|
||||
req, '777')
|
||||
self.assertEqual(1, len(fake_notifier.NOTIFICATIONS))
|
||||
|
||||
def test_share_types_with_volumes_destroy(self):
|
||||
req = fakes.HTTPRequest.blank('/v2/fake/types/1')
|
||||
self.assertEqual(0, len(fake_notifier.NOTIFICATIONS))
|
||||
self.controller._delete(req, 1)
|
||||
self.assertEqual(1, len(fake_notifier.NOTIFICATIONS))
|
||||
|
||||
@ddt.data(make_create_body("share_type_1"),
|
||||
make_create_body(spec_driver_handles_share_servers="false"),
|
||||
make_create_body(spec_driver_handles_share_servers="true"),
|
||||
make_create_body(spec_driver_handles_share_servers="1"),
|
||||
make_create_body(spec_driver_handles_share_servers="0"),
|
||||
make_create_body(spec_driver_handles_share_servers="True"),
|
||||
make_create_body(spec_driver_handles_share_servers="False"),
|
||||
make_create_body(spec_driver_handles_share_servers="FalsE"))
|
||||
def test_create(self, body):
|
||||
req = fakes.HTTPRequest.blank('/v2/fake/types')
|
||||
self.assertEqual(0, len(fake_notifier.NOTIFICATIONS))
|
||||
res_dict = self.controller._create(req, body)
|
||||
self.assertEqual(1, len(fake_notifier.NOTIFICATIONS))
|
||||
self.assertEqual(2, len(res_dict))
|
||||
self.assertEqual('share_type_1', res_dict['share_type']['name'])
|
||||
self.assertEqual('share_type_1', res_dict['volume_type']['name'])
|
||||
|
||||
@ddt.data(None,
|
||||
make_create_body(""),
|
||||
make_create_body("n" * 256),
|
||||
{'foo': {'a': 'b'}},
|
||||
{'share_type': 'string'},
|
||||
make_create_body(spec_driver_handles_share_servers=None),
|
||||
make_create_body(spec_driver_handles_share_servers=""),
|
||||
make_create_body(spec_driver_handles_share_servers=[]),
|
||||
)
|
||||
def test_create_invalid_request(self, body):
|
||||
req = fakes.HTTPRequest.blank('/v2/fake/types')
|
||||
self.assertEqual(0, len(fake_notifier.NOTIFICATIONS))
|
||||
self.assertRaises(webob.exc.HTTPBadRequest,
|
||||
self.controller._create, req, body)
|
||||
self.assertEqual(0, len(fake_notifier.NOTIFICATIONS))
|
||||
|
||||
def assert_share_type_list_equal(self, expected, observed):
|
||||
self.assertEqual(len(expected), len(observed))
|
||||
expected = sorted(expected, key=lambda item: item['id'])
|
||||
observed = sorted(observed, key=lambda item: item['id'])
|
||||
for d1, d2 in zip(expected, observed):
|
||||
self.assertEqual(d1['id'], d2['id'])
|
||||
|
||||
|
||||
def generate_type(type_id, is_public):
|
||||
return {
|
||||
'id': type_id,
|
||||
'name': u'test',
|
||||
'deleted': False,
|
||||
'created_at': datetime.datetime(2012, 1, 1, 1, 1, 1, 1),
|
||||
'updated_at': None,
|
||||
'deleted_at': None,
|
||||
'is_public': bool(is_public),
|
||||
'extra_specs': {}
|
||||
}
|
||||
|
||||
|
||||
SHARE_TYPES = {
|
||||
'0': generate_type('0', True),
|
||||
'1': generate_type('1', True),
|
||||
'2': generate_type('2', False),
|
||||
'3': generate_type('3', False)}
|
||||
|
||||
PROJ1_UUID = '11111111-1111-1111-1111-111111111111'
|
||||
PROJ2_UUID = '22222222-2222-2222-2222-222222222222'
|
||||
PROJ3_UUID = '33333333-3333-3333-3333-333333333333'
|
||||
|
||||
ACCESS_LIST = [{'share_type_id': '2', 'project_id': PROJ2_UUID},
|
||||
{'share_type_id': '2', 'project_id': PROJ3_UUID},
|
||||
{'share_type_id': '3', 'project_id': PROJ3_UUID}]
|
||||
|
||||
|
||||
def fake_share_type_get(context, id, inactive=False, expected_fields=None):
|
||||
vol = SHARE_TYPES[id]
|
||||
if expected_fields and 'projects' in expected_fields:
|
||||
vol['projects'] = [a['project_id']
|
||||
for a in ACCESS_LIST if a['share_type_id'] == id]
|
||||
return vol
|
||||
|
||||
|
||||
def _has_type_access(type_id, project_id):
|
||||
for access in ACCESS_LIST:
|
||||
if (access['share_type_id'] == type_id
|
||||
and access['project_id'] == project_id):
|
||||
return True
|
||||
return False
|
||||
|
||||
|
||||
def fake_share_type_get_all(context, inactive=False, filters=None):
|
||||
if filters is None or filters.get('is_public', None) is None:
|
||||
return SHARE_TYPES
|
||||
res = {}
|
||||
for k, v in six.iteritems(SHARE_TYPES):
|
||||
if filters['is_public'] and _has_type_access(k, context.project_id):
|
||||
res.update({k: v})
|
||||
continue
|
||||
if v['is_public'] == filters['is_public']:
|
||||
res.update({k: v})
|
||||
return res
|
||||
|
||||
|
||||
class FakeResponse(object):
|
||||
obj = {'share_type': {'id': '0'},
|
||||
'share_types': [{'id': '0'}, {'id': '2'}]}
|
||||
|
||||
def attach(self, **kwargs):
|
||||
pass
|
||||
|
||||
|
||||
class FakeRequest(object):
|
||||
environ = {"manila.context": context.get_admin_context()}
|
||||
|
||||
def get_db_share_type(self, resource_id):
|
||||
return SHARE_TYPES[resource_id]
|
||||
|
||||
|
||||
class ShareTypeAccessTest(test.TestCase):
|
||||
|
||||
def setUp(self):
|
||||
super(self.__class__, self).setUp()
|
||||
self.controller = types.ShareTypesController()
|
||||
self.req = FakeRequest()
|
||||
self.context = self.req.environ['manila.context']
|
||||
self.mock_object(db, 'share_type_get', fake_share_type_get)
|
||||
self.mock_object(db, 'share_type_get_all', fake_share_type_get_all)
|
||||
|
||||
def assertShareTypeListEqual(self, expected, observed):
|
||||
self.assertEqual(len(expected), len(observed))
|
||||
expected = sorted(expected, key=lambda item: item['id'])
|
||||
observed = sorted(observed, key=lambda item: item['id'])
|
||||
for d1, d2 in zip(expected, observed):
|
||||
self.assertEqual(d1['id'], d2['id'])
|
||||
|
||||
def test_list_type_access_public(self):
|
||||
"""Querying os-share-type-access on public type should return 404."""
|
||||
req = fakes.HTTPRequest.blank('/v1/fake/types/os-share-type-access',
|
||||
use_admin_context=True)
|
||||
|
||||
self.assertRaises(webob.exc.HTTPNotFound,
|
||||
self.controller._list_project_access,
|
||||
req, '1')
|
||||
|
||||
def test_list_type_access_private(self):
|
||||
expected = {'share_type_access': [
|
||||
{'share_type_id': '2', 'project_id': PROJ2_UUID},
|
||||
{'share_type_id': '2', 'project_id': PROJ3_UUID},
|
||||
]}
|
||||
|
||||
result = self.controller._list_project_access(self.req, '2')
|
||||
|
||||
self.assertEqual(expected, result)
|
||||
|
||||
def test_list_with_no_context(self):
|
||||
req = fakes.HTTPRequest.blank('/v1/types/fake/types')
|
||||
|
||||
def fake_authorize(context, target=None, action=None):
|
||||
raise exception.PolicyNotAuthorized(action='index')
|
||||
|
||||
self.assertRaises(webob.exc.HTTPForbidden,
|
||||
self.controller._list_project_access,
|
||||
req, 'fake')
|
||||
|
||||
def test_list_type_with_admin_default_proj1(self):
|
||||
expected = {'share_types': [{'id': '0'}, {'id': '1'}]}
|
||||
req = fakes.HTTPRequest.blank('/v1/fake/types', use_admin_context=True)
|
||||
req.environ['manila.context'].project_id = PROJ1_UUID
|
||||
|
||||
result = self.controller.index(req)
|
||||
|
||||
self.assertShareTypeListEqual(expected['share_types'],
|
||||
result['share_types'])
|
||||
|
||||
def test_list_type_with_admin_default_proj2(self):
|
||||
expected = {'share_types': [{'id': '0'}, {'id': '1'}, {'id': '2'}]}
|
||||
req = fakes.HTTPRequest.blank('/v2/fake/types', use_admin_context=True)
|
||||
req.environ['manila.context'].project_id = PROJ2_UUID
|
||||
|
||||
result = self.controller.index(req)
|
||||
|
||||
self.assertShareTypeListEqual(expected['share_types'],
|
||||
result['share_types'])
|
||||
|
||||
def test_list_type_with_admin_ispublic_true(self):
|
||||
expected = {'share_types': [{'id': '0'}, {'id': '1'}]}
|
||||
req = fakes.HTTPRequest.blank('/v2/fake/types?is_public=true',
|
||||
use_admin_context=True)
|
||||
|
||||
result = self.controller.index(req)
|
||||
|
||||
self.assertShareTypeListEqual(expected['share_types'],
|
||||
result['share_types'])
|
||||
|
||||
def test_list_type_with_admin_ispublic_false(self):
|
||||
expected = {'share_types': [{'id': '2'}, {'id': '3'}]}
|
||||
req = fakes.HTTPRequest.blank('/v2/fake/types?is_public=false',
|
||||
use_admin_context=True)
|
||||
|
||||
result = self.controller.index(req)
|
||||
|
||||
self.assertShareTypeListEqual(expected['share_types'],
|
||||
result['share_types'])
|
||||
|
||||
def test_list_type_with_admin_ispublic_false_proj2(self):
|
||||
expected = {'share_types': [{'id': '2'}, {'id': '3'}]}
|
||||
req = fakes.HTTPRequest.blank('/v2/fake/types?is_public=false',
|
||||
use_admin_context=True)
|
||||
req.environ['manila.context'].project_id = PROJ2_UUID
|
||||
|
||||
result = self.controller.index(req)
|
||||
|
||||
self.assertShareTypeListEqual(expected['share_types'],
|
||||
result['share_types'])
|
||||
|
||||
def test_list_type_with_admin_ispublic_none(self):
|
||||
expected = {'share_types': [
|
||||
{'id': '0'}, {'id': '1'}, {'id': '2'}, {'id': '3'},
|
||||
]}
|
||||
req = fakes.HTTPRequest.blank('/v2/fake/types?is_public=all',
|
||||
use_admin_context=True)
|
||||
|
||||
result = self.controller.index(req)
|
||||
|
||||
self.assertShareTypeListEqual(expected['share_types'],
|
||||
result['share_types'])
|
||||
|
||||
def test_list_type_with_no_admin_default(self):
|
||||
expected = {'share_types': [{'id': '0'}, {'id': '1'}]}
|
||||
req = fakes.HTTPRequest.blank('/v2/fake/types',
|
||||
use_admin_context=False)
|
||||
|
||||
result = self.controller.index(req)
|
||||
|
||||
self.assertShareTypeListEqual(expected['share_types'],
|
||||
result['share_types'])
|
||||
|
||||
def test_list_type_with_no_admin_ispublic_true(self):
|
||||
expected = {'share_types': [{'id': '0'}, {'id': '1'}]}
|
||||
req = fakes.HTTPRequest.blank('/v2/fake/types?is_public=true',
|
||||
use_admin_context=False)
|
||||
|
||||
result = self.controller.index(req)
|
||||
|
||||
self.assertShareTypeListEqual(expected['share_types'],
|
||||
result['share_types'])
|
||||
|
||||
def test_list_type_with_no_admin_ispublic_false(self):
|
||||
expected = {'share_types': [{'id': '0'}, {'id': '1'}]}
|
||||
req = fakes.HTTPRequest.blank('/v2/fake/types?is_public=false',
|
||||
use_admin_context=False)
|
||||
|
||||
result = self.controller.index(req)
|
||||
|
||||
self.assertShareTypeListEqual(expected['share_types'],
|
||||
result['share_types'])
|
||||
|
||||
def test_list_type_with_no_admin_ispublic_none(self):
|
||||
expected = {'share_types': [{'id': '0'}, {'id': '1'}]}
|
||||
req = fakes.HTTPRequest.blank('/v2/fake/types?is_public=all',
|
||||
use_admin_context=False)
|
||||
|
||||
result = self.controller.index(req)
|
||||
|
||||
self.assertShareTypeListEqual(expected['share_types'],
|
||||
result['share_types'])
|
||||
|
||||
def test_add_project_access(self):
|
||||
def stub_add_share_type_access(context, type_id, project_id):
|
||||
self.assertEqual('3', type_id, "type_id")
|
||||
self.assertEqual(PROJ2_UUID, project_id, "project_id")
|
||||
self.mock_object(db, 'share_type_access_add',
|
||||
stub_add_share_type_access)
|
||||
body = {'addProjectAccess': {'project': PROJ2_UUID}}
|
||||
req = fakes.HTTPRequest.blank('/v2/fake/types/2/action',
|
||||
use_admin_context=True)
|
||||
|
||||
result = self.controller._add_project_access(req, '3', body)
|
||||
|
||||
self.assertEqual(202, result.status_code)
|
||||
|
||||
def test_add_project_access_with_no_admin_user(self):
|
||||
req = fakes.HTTPRequest.blank('/v2/fake/types/2/action',
|
||||
use_admin_context=False)
|
||||
body = {'addProjectAccess': {'project': PROJ2_UUID}}
|
||||
|
||||
self.assertRaises(webob.exc.HTTPForbidden,
|
||||
self.controller._add_project_access,
|
||||
req, '2', body)
|
||||
|
||||
def test_add_project_access_with_already_added_access(self):
|
||||
def stub_add_share_type_access(context, type_id, project_id):
|
||||
raise exception.ShareTypeAccessExists(share_type_id=type_id,
|
||||
project_id=project_id)
|
||||
self.mock_object(db, 'share_type_access_add',
|
||||
stub_add_share_type_access)
|
||||
body = {'addProjectAccess': {'project': PROJ2_UUID}}
|
||||
req = fakes.HTTPRequest.blank('/v2/fake/types/2/action',
|
||||
use_admin_context=True)
|
||||
|
||||
self.assertRaises(webob.exc.HTTPConflict,
|
||||
self.controller._add_project_access,
|
||||
req, '3', body)
|
||||
|
||||
def test_add_project_access_to_public_share_type(self):
|
||||
share_type_id = '3'
|
||||
body = {'addProjectAccess': {'project': PROJ2_UUID}}
|
||||
self.mock_object(share_types, 'get_share_type',
|
||||
mock.Mock(return_value={"is_public": True}))
|
||||
req = fakes.HTTPRequest.blank('/v2/fake/types/2/action',
|
||||
use_admin_context=True)
|
||||
|
||||
self.assertRaises(webob.exc.HTTPForbidden,
|
||||
self.controller._add_project_access,
|
||||
req, share_type_id, body)
|
||||
|
||||
share_types.get_share_type.assert_called_once_with(
|
||||
mock.ANY, share_type_id)
|
||||
|
||||
def test_remove_project_access_with_bad_access(self):
|
||||
def stub_remove_share_type_access(context, type_id, project_id):
|
||||
raise exception.ShareTypeAccessNotFound(share_type_id=type_id,
|
||||
project_id=project_id)
|
||||
self.mock_object(db, 'share_type_access_remove',
|
||||
stub_remove_share_type_access)
|
||||
body = {'removeProjectAccess': {'project': PROJ2_UUID}}
|
||||
req = fakes.HTTPRequest.blank('/v2/fake/types/2/action',
|
||||
use_admin_context=True)
|
||||
|
||||
self.assertRaises(webob.exc.HTTPNotFound,
|
||||
self.controller._remove_project_access,
|
||||
req, '3', body)
|
||||
|
||||
def test_remove_project_access_with_no_admin_user(self):
|
||||
req = fakes.HTTPRequest.blank('/v2/fake/types/2/action',
|
||||
use_admin_context=False)
|
||||
body = {'removeProjectAccess': {'project': PROJ2_UUID}}
|
||||
|
||||
self.assertRaises(webob.exc.HTTPForbidden,
|
||||
self.controller._remove_project_access,
|
||||
req, '2', body)
|
||||
|
@ -20,7 +20,7 @@ import mock
|
||||
from oslo_utils import strutils
|
||||
import webob
|
||||
|
||||
from manila.api.contrib import types_extra_specs
|
||||
from manila.api.v1 import share_types_extra_specs
|
||||
from manila.common import constants
|
||||
from manila import exception
|
||||
from manila import test
|
||||
@ -89,7 +89,8 @@ class ShareTypesExtraSpecsTest(test.TestCase):
|
||||
self.flags(host='fake')
|
||||
self.mock_object(manila.db, 'share_type_get', share_type_get)
|
||||
self.api_path = '/v2/fake/os-share-types/1/extra_specs'
|
||||
self.controller = types_extra_specs.ShareTypeExtraSpecsController()
|
||||
self.controller = (
|
||||
share_types_extra_specs.ShareTypeExtraSpecsController())
|
||||
|
||||
"""to reset notifier drivers left over from other api/contrib tests"""
|
||||
self.addCleanup(fake_notifier.reset)
|
@ -37,6 +37,17 @@
|
||||
"share_type:index": "rule:default",
|
||||
"share_type:show": "rule:default",
|
||||
"share_type:default": "rule:default",
|
||||
"share_type:create": "rule:default",
|
||||
"share_type:delete": "rule:default",
|
||||
"share_type:add_project_access": "rule:admin_api",
|
||||
"share_type:list_project_access": "rule:admin_api",
|
||||
"share_type:remove_project_access": "rule:admin_api",
|
||||
|
||||
"share_types_extra_spec:create": "rule:default",
|
||||
"share_types_extra_spec:update": "rule:default",
|
||||
"share_types_extra_spec:show": "rule:default",
|
||||
"share_types_extra_spec:index": "rule:default",
|
||||
"share_types_extra_spec:delete": "rule:default",
|
||||
|
||||
"share_instance:index": "rule:admin_api",
|
||||
"share_instance:show": "rule:admin_api",
|
||||
@ -62,11 +73,6 @@
|
||||
"share:get_share_metadata": "",
|
||||
"share:delete_share_metadata": "",
|
||||
"share:update_share_metadata": "",
|
||||
"share_extension:types_manage": "",
|
||||
"share_extension:types_extra_specs": "",
|
||||
"share_extension:share_type_access": "",
|
||||
"share_extension:share_type_access:addProjectAccess": "rule:admin_api",
|
||||
"share_extension:share_type_access:removeProjectAccess": "rule:admin_api",
|
||||
"share_extension:availability_zones": "",
|
||||
|
||||
"security_service:index": "",
|
||||
|
Loading…
Reference in New Issue
Block a user