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:
Valeriy Ponomaryov 2015-10-27 17:23:50 +02:00
parent 9912d296ec
commit 0cb695fd54
12 changed files with 657 additions and 836 deletions

View File

@ -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",

View File

@ -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]

View File

@ -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]

View File

@ -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',

View File

@ -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())

View File

@ -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())

View File

@ -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:

View File

@ -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)

View File

@ -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))

View File

@ -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)

View File

@ -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)

View File

@ -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": "",