Add new URLs for APIs ported from extensions

After port of extensions to core API we need to rename their URLs.

So, rename URLs and bump microversion for it.
Make new URLs work only with new microversion(s) 2.7+
and old with old microversions 1.0-2.6.
Add separate API router for v2 API as now we should split v1 and v2
logic.
Also, move updated APIs under v2 directory that will be used by both
API routers - v1 and v2.

List of updated collections is following:
 - os-availability-zone -> availability-zones
 - os-services -> services
 - os-quota-sets -> quota-sets
 - os-quota-class-sets -> quota-class-sets
 - os-share-manage -> shares/manage
 - os-share-unmanage -> shares/%s/action

List of updated member actions is following:
 - os-share-unmanage/%(share_id)s/unmanage -> shares/%(share_id)s/action
 - types/%(id)s/os-share-type-access -> types/%(id)s/share_type_access

List of updated action names is following:
 - os-access_allow -> access_allow
 - os-access_deny -> access_deny
 - os-access_list -> access_list
 - os-reset_status -> reset_status
 - os-force_delete -> force_delete
 - os-migrate_share -> migrate_share
 - os-extend -> extend
 - os-shrink -> shrink

List of updated attribute names is following:
 - os-share-type-access -> share-type-access

Partially implements bp ext-to-core

Change-Id: I82f00114db985b4b3bf4db0a64191559508ac600
This commit is contained in:
Valeriy Ponomaryov 2015-10-29 13:34:32 +02:00 committed by vponomaryov
parent 56ccb015c8
commit 2467ccf223
46 changed files with 3299 additions and 585 deletions

View File

@ -6,7 +6,7 @@
use = call:manila.api:root_app_factory
/: apiversions
/v1: openstack_share_api
/v2: openstack_share_api
/v2: openstack_share_api_v2
[composite:openstack_share_api]
use = call:manila.api.middleware.auth:pipeline_factory
@ -14,6 +14,12 @@ noauth = cors faultwrap ssl sizelimit noauth api
keystone = cors faultwrap ssl sizelimit authtoken keystonecontext api
keystone_nolimit = cors faultwrap ssl sizelimit authtoken keystonecontext api
[composite:openstack_share_api_v2]
use = call:manila.api.middleware.auth:pipeline_factory
noauth = cors faultwrap ssl sizelimit noauth apiv2
keystone = cors faultwrap ssl sizelimit authtoken keystonecontext apiv2
keystone_nolimit = cors faultwrap ssl sizelimit authtoken keystonecontext apiv2
[filter:faultwrap]
paste.filter_factory = manila.api.middleware.fault:FaultWrapper.factory
@ -29,6 +35,9 @@ paste.filter_factory = oslo_middleware.ssl:SSLMiddleware.factory
[app:api]
paste.app_factory = manila.api.v1.router:APIRouter.factory
[app:apiv2]
paste.app_factory = manila.api.v2.router:APIRouter.factory
[pipeline:apiversions]
pipeline = cors faultwrap osshareversionapp

View File

@ -52,6 +52,7 @@ REST_API_VERSION_HISTORY = """
* 2.4 - Consistency Group support
* 2.5 - Share Migration admin API
* 2.6 - Return share_type UUID instead of name in Share API
* 2.7 - Rename old extension-like API URLs to core-API-like
"""
@ -59,7 +60,7 @@ REST_API_VERSION_HISTORY = """
# The default api version request is defined to be the
# the minimum version of the API supported.
_MIN_API_VERSION = "2.0"
_MAX_API_VERSION = "2.6"
_MAX_API_VERSION = "2.7"
DEFAULT_API_VERSION = _MIN_API_VERSION

View File

@ -61,3 +61,7 @@ user documentation.
---
Return share_type UUID instead of name in Share API and add share_type_name
field.
2.7
---
Rename old extension-like API URLs to core-API-like.

View File

@ -1199,12 +1199,12 @@ class AdminActionsMixin(object):
raise webob.exc.HTTPBadRequest(explanation=expl)
return update
@action('os-reset_status')
@Controller.authorize('reset_status')
def _reset_status(self, req, id, body):
"""Reset status on the resource."""
context = req.environ['manila.context']
update = self.validate_update(body['os-reset_status'])
update = self.validate_update(
body.get('reset_status', body.get('os-reset_status')))
msg = "Updating %(resource)s '%(id)s' with '%(update)r'"
LOG.debug(msg, {'resource': self.resource_name, 'id': id,
'update': update})
@ -1214,7 +1214,6 @@ class AdminActionsMixin(object):
raise webob.exc.HTTPNotFound(six.text_type(e))
return webob.Response(status_int=202)
@action('os-force_delete')
@Controller.authorize('force_delete')
def _force_delete(self, req, id, body):
"""Delete a resource, bypassing the check for status."""

View File

@ -16,32 +16,30 @@
# under the License.
"""
WSGI middleware for OpenStack Share API.
WSGI middleware for OpenStack Share API v1.
"""
from oslo_log import log
from manila.api import extensions
import manila.api.openstack
from manila.api.v1 import availability_zones
from manila.api.v1 import cgsnapshots
from manila.api.v1 import consistency_groups
from manila.api.v1 import limits
from manila.api.v1 import quota_class_sets
from manila.api.v1 import quota_sets
from manila.api.v1 import scheduler_stats
from manila.api.v1 import security_service
from manila.api.v1 import services
from manila.api.v1 import share_instances
from manila.api.v1 import share_manage
from manila.api.v1 import share_metadata
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.v2 import availability_zones
from manila.api.v2 import quota_class_sets
from manila.api.v2 import quota_sets
from manila.api.v2 import services
from manila.api.v2 import share_types
from manila.api import versions
LOG = log.getLogger(__name__)
@ -64,46 +62,35 @@ class APIRouter(manila.api.openstack.APIRouter):
mapper.redirect("", "/")
self.resources["availability_zones"] = (
availability_zones.create_resource())
availability_zones.create_resource_legacy())
mapper.resource("availability-zone",
# TODO(vponomaryov): rename 'os-availability-zone' to
# 'availability-zones' when API urls rename happens.
"os-availability-zone",
controller=self.resources["availability_zones"])
self.resources["services"] = services.create_resource()
self.resources["services"] = services.create_resource_legacy()
mapper.resource("service",
# TODO(vponomaryov): rename 'os-services' to
# 'services' when API urls rename happens.
"os-services",
controller=self.resources["services"])
self.resources["quota_sets"] = quota_sets.create_resource()
self.resources["quota_sets"] = quota_sets.create_resource_legacy()
mapper.resource("quota-set",
# TODO(vponomaryov): rename 'os-quota-sets' to
# 'quota-sets' when API urls rename happens.
"os-quota-sets",
controller=self.resources["quota_sets"],
member={'defaults': 'GET'})
self.resources["quota_class_sets"] = quota_class_sets.create_resource()
self.resources["quota_class_sets"] = (
quota_class_sets.create_resource_legacy())
mapper.resource("quota-class-set",
# TODO(vponomaryov): rename 'os-quota-class-sets' to
# 'quota-class-sets' when API urls rename happens.
"os-quota-class-sets",
controller=self.resources["quota_class_sets"])
self.resources["share_manage"] = share_manage.create_resource()
mapper.resource("share_manage",
# TODO(vponomaryov): remove it when it is ported
# to shares controller.
"os-share-manage",
controller=self.resources["share_manage"])
self.resources["share_unmanage"] = share_unmanage.create_resource()
mapper.resource("share_unmanage",
# TODO(vponomaryov): remove it when it is ported
# to shares controller.
"os-share-unmanage",
controller=self.resources["share_unmanage"],
member={'unmanage': 'POST'})
@ -196,20 +183,3 @@ class APIRouter(manila.api.openstack.APIRouter):
controller=self.resources['scheduler_stats'],
action='pools_detail',
conditions={'method': ['GET']})
self.resources['consistency-groups'] = (
consistency_groups.create_resource())
mapper.resource('consistency-group', 'consistency-groups',
controller=self.resources['consistency-groups'],
collection={'detail': 'GET'})
mapper.connect('consistency-groups',
'/{project_id}/consistency-groups/{id}/action',
controller=self.resources['consistency-groups'],
action='action',
conditions={"action": ['POST']})
self.resources['cgsnapshots'] = cgsnapshots.create_resource()
mapper.resource('cgsnapshot', 'cgsnapshots',
controller=self.resources['cgsnapshots'],
collection={'detail': 'GET'},
member={'members': 'GET', 'action': 'POST'})

View File

@ -41,6 +41,26 @@ class ShareInstancesController(wsgi.Controller, wsgi.AdminActionsMixin):
def _delete(self, *args, **kwargs):
return self.share_api.delete_instance(*args, **kwargs)
@wsgi.Controller.api_version('2.3', '2.6')
@wsgi.action('os-reset_status')
def instance_reset_status_legacy(self, req, id, body):
return self._reset_status(req, id, body)
@wsgi.Controller.api_version('2.7')
@wsgi.action('reset_status')
def instance_reset_status(self, req, id, body):
return self._reset_status(req, id, body)
@wsgi.Controller.api_version('2.3', '2.6')
@wsgi.action('os-force_delete')
def instance_force_delete_legacy(self, req, id, body):
return self._force_delete(req, id, body)
@wsgi.Controller.api_version('2.7')
@wsgi.action('force_delete')
def instance_force_delete(self, req, id, body):
return self._force_delete(req, id, body)
@wsgi.Controller.api_version("2.3")
@wsgi.Controller.authorize
def index(self, req):
@ -74,18 +94,6 @@ class ShareInstancesController(wsgi.Controller, wsgi.AdminActionsMixin):
view = instance_view.ViewBuilder()
return view.detail_list(req, share.instances)
@wsgi.Controller.api_version('2.3')
@wsgi.action('os-reset_status')
@wsgi.response(202)
def share_instance_reset_status(self, req, id, body):
super(self.__class__, self)._reset_status(req, id, body)
@wsgi.Controller.api_version('2.3')
@wsgi.action('os-force_delete')
@wsgi.response(202)
def share_instance_force_delete(self, req, id, body):
super(self.__class__, self)._force_delete(req, id, body)
def create_resource():
return wsgi.Resource(ShareInstancesController())

View File

@ -25,20 +25,10 @@ from manila.share import utils as share_utils
from manila import utils
class ShareManageController(wsgi.Controller):
"""Allows existing share to be 'managed' by Manila."""
resource_name = "share"
_view_builder_class = share_views.ViewBuilder
def __init__(self, *args, **kwargs):
super(self.__class__, self).__init__(*args, **kwargs)
self.share_api = share.API()
class ShareManageMixin(object):
@wsgi.Controller.authorize('manage')
def create(self, req, body):
# TODO(vponomaryov): move it to shares controller.
def _manage(self, req, body):
context = req.environ['manila.context']
share_data = self._validate_manage_parameters(context, body)
@ -118,5 +108,25 @@ class ShareManageController(wsgi.Controller):
raise exc.HTTPNotFound(explanation=six.text_type(e))
class ShareManageController(ShareManageMixin, wsgi.Controller):
"""Allows existing share to be 'managed' by Manila."""
resource_name = "share"
_view_builder_class = share_views.ViewBuilder
def __init__(self, *args, **kwargs):
super(self.__class__, self).__init__(*args, **kwargs)
self.share_api = share.API()
@wsgi.Controller.api_version('1.0', '2.6')
def create(self, req, body):
"""Legacy method for 'manage share' operation.
Should be removed when minimum API version becomes equal to or
greater than v2.7
"""
return self._manage(req, body)
def create_resource():
return wsgi.Resource(ShareManageController())

View File

@ -50,15 +50,25 @@ class ShareSnapshotsController(wsgi.Controller, wsgi.AdminActionsMixin):
def _delete(self, *args, **kwargs):
return self.share_api.delete_snapshot(*args, **kwargs)
@wsgi.Controller.api_version('1.0', '2.6')
@wsgi.action('os-reset_status')
@wsgi.response(202)
def share_snapshot_reset_status(self, req, id, body):
super(self.__class__, self)._reset_status(req, id, body)
def snapshot_reset_status_legacy(self, req, id, body):
return self._reset_status(req, id, body)
@wsgi.Controller.api_version('2.7')
@wsgi.action('reset_status')
def snapshot_reset_status(self, req, id, body):
return self._reset_status(req, id, body)
@wsgi.Controller.api_version('1.0', '2.6')
@wsgi.action('os-force_delete')
@wsgi.response(202)
def share_snapshot_force_delete(self, req, id, body):
super(self.__class__, self)._force_delete(req, id, body)
def snapshot_force_delete_legacy(self, req, id, body):
return self._force_delete(req, id, body)
@wsgi.Controller.api_version('2.7')
@wsgi.action('force_delete')
def snapshot_force_delete(self, req, id, body):
return self._force_delete(req, id, body)
def show(self, req, id):
"""Return data about the given snapshot."""

View File

@ -26,20 +26,11 @@ from manila import share
LOG = log.getLogger(__name__)
class ShareUnmanageController(wsgi.Controller):
"""The Unmanage API controller for the OpenStack API."""
class ShareUnmanageMixin(object):
resource_name = "share"
def __init__(self, *args, **kwargs):
super(self.__class__, self).__init__(*args, **kwargs)
self.share_api = share.API()
@wsgi.action("unmanage")
@wsgi.Controller.authorize
def unmanage(self, req, id):
@wsgi.Controller.authorize("unmanage")
def _unmanage(self, req, id, body=None):
"""Unmanage a share."""
# TODO(vponomaryov): move it to shares controller as 'unmanage' action.
context = req.environ['manila.context']
LOG.info(_LI("Unmanage share with id: %s"), id, context=context)
@ -72,5 +63,19 @@ class ShareUnmanageController(wsgi.Controller):
return webob.Response(status_int=202)
class ShareUnmanageController(ShareUnmanageMixin, wsgi.Controller):
"""The Unmanage API controller for the OpenStack API."""
resource_name = "share"
def __init__(self, *args, **kwargs):
super(self.__class__, self).__init__(*args, **kwargs)
self.share_api = share.API()
@wsgi.Controller.api_version('1.0', '2.6')
def unmanage(self, req, id):
return self._unmanage(req, id)
def create_resource():
return wsgi.Resource(ShareUnmanageController())

View File

@ -38,15 +38,8 @@ from manila.share import share_types
LOG = log.getLogger(__name__)
class ShareController(wsgi.Controller, wsgi.AdminActionsMixin):
"""The Shares API controller for the OpenStack API."""
resource_name = 'share'
_view_builder_class = share_views.ViewBuilder
def __init__(self):
super(ShareController, self).__init__()
self.share_api = share.API()
class ShareMixin(object):
"""Mixin class for Share API Controllers."""
def _update(self, *args, **kwargs):
db.share_update(*args, **kwargs)
@ -60,16 +53,6 @@ class ShareController(wsgi.Controller, wsgi.AdminActionsMixin):
def _migrate(self, *args, **kwargs):
return self.share_api.migrate_share(*args, **kwargs)
@wsgi.action('os-reset_status')
@wsgi.response(202)
def share_reset_status(self, req, id, body):
super(self.__class__, self)._reset_status(req, id, body)
@wsgi.action('os-force_delete')
@wsgi.response(202)
def share_force_delete(self, req, id, body):
super(self.__class__, self)._force_delete(req, id, body)
def show(self, req, id):
"""Return data about the given share."""
context = req.environ['manila.context']
@ -113,9 +96,7 @@ class ShareController(wsgi.Controller, wsgi.AdminActionsMixin):
return webob.Response(status_int=202)
@wsgi.Controller.api_version("2.5", None, True)
@wsgi.action("os-migrate_share")
def migrate_share(self, req, id, body):
def _migrate_share(self, req, id, body):
"""Migrate a share to the specified host."""
context = req.environ['manila.context']
try:
@ -123,7 +104,7 @@ class ShareController(wsgi.Controller, wsgi.AdminActionsMixin):
except exception.NotFound:
msg = _("Share %s not found.") % id
raise exc.HTTPNotFound(explanation=msg)
params = body['os-migrate_share']
params = body.get('migrate_share', body.get('os-migrate_share'))
try:
host = params['host']
except KeyError:
@ -229,12 +210,7 @@ class ShareController(wsgi.Controller, wsgi.AdminActionsMixin):
share.update(update_dict)
return self._view_builder.detail(req, share)
@wsgi.Controller.api_version("2.4")
def create(self, req, body):
return self._create(req, body)
@wsgi.Controller.api_version("1.0", "2.3") # noqa
def create(self, req, body): # pylint: disable=E0102
# Remove consistency group attributes
body.get('share', {}).pop('consistency_group_id', None)
share = self._create(req, body)
@ -400,11 +376,10 @@ class ShareController(wsgi.Controller, wsgi.AdminActionsMixin):
except ValueError:
raise webob.exc.HTTPBadRequest(explanation=exc_str)
@wsgi.action('os-allow_access')
def _allow_access(self, req, id, body):
"""Add share access rule."""
context = req.environ['manila.context']
access_data = body['os-allow_access']
access_data = body.get('allow_access', body.get('os-allow_access'))
share = self.share_api.get(context, id)
access_type = access_data['access_type']
@ -427,12 +402,12 @@ class ShareController(wsgi.Controller, wsgi.AdminActionsMixin):
raise webob.exc.HTTPBadRequest(explanation=e.msg)
return {'access': access}
@wsgi.action('os-deny_access')
def _deny_access(self, req, id, body):
"""Remove share access rule."""
context = req.environ['manila.context']
access_id = body['os-deny_access']['access_id']
access_id = body.get(
'deny_access', body.get('os-deny_access'))['access_id']
try:
access = self.share_api.access_get(context, access_id)
@ -444,7 +419,6 @@ class ShareController(wsgi.Controller, wsgi.AdminActionsMixin):
self.share_api.deny_access(context, share, access)
return webob.Response(status_int=202)
@wsgi.action('os-access_list')
def _access_list(self, req, id, body):
"""list share access rules."""
context = req.environ['manila.context']
@ -453,7 +427,6 @@ class ShareController(wsgi.Controller, wsgi.AdminActionsMixin):
access_list = self.share_api.access_get_all(context, share)
return {'access_list': access_list}
@wsgi.action('os-extend')
def _extend(self, req, id, body):
"""Extend size of a share."""
context = req.environ['manila.context']
@ -469,7 +442,6 @@ class ShareController(wsgi.Controller, wsgi.AdminActionsMixin):
return webob.Response(status_int=202)
@wsgi.action('os-shrink')
def _shrink(self, req, id, body):
"""Shrink size of a share."""
context = req.environ['manila.context']
@ -490,7 +462,7 @@ class ShareController(wsgi.Controller, wsgi.AdminActionsMixin):
raise webob.exc.HTTPNotFound(explanation=six.text_type(e))
try:
size = int(body[action]['new_size'])
size = int(body.get(action, action.split('os-')[-1])['new_size'])
except (KeyError, ValueError, TypeError):
msg = _("New share size must be specified as an integer.")
raise webob.exc.HTTPBadRequest(explanation=msg)
@ -498,5 +470,50 @@ class ShareController(wsgi.Controller, wsgi.AdminActionsMixin):
return share, size
class ShareController(wsgi.Controller, ShareMixin, wsgi.AdminActionsMixin):
"""The Shares API v1 controller for the OpenStack API."""
resource_name = 'share'
_view_builder_class = share_views.ViewBuilder
def __init__(self):
super(self.__class__, self).__init__()
self.share_api = share.API()
@wsgi.action('os-reset_status')
def share_reset_status(self, req, id, body):
"""Reset status of a share."""
return self._reset_status(req, id, body)
@wsgi.action('os-force_delete')
def share_force_delete(self, req, id, body):
"""Delete a share, bypassing the check for status."""
return self._force_delete(req, id, body)
@wsgi.action('os-allow_access')
def allow_access(self, req, id, body):
"""Add share access rule."""
return self._allow_access(req, id, body)
@wsgi.action('os-deny_access')
def deny_access(self, req, id, body):
"""Remove share access rule."""
return self._deny_access(req, id, body)
@wsgi.action('os-access_list')
def access_list(self, req, id, body):
"""List share access rules."""
return self._access_list(req, id, body)
@wsgi.action('os-extend')
def extend(self, req, id, body):
"""Extend size of a share."""
return self._extend(req, id, body)
@wsgi.action('os-shrink')
def shrink(self, req, id, body):
"""Shrink size of a share."""
return self._shrink(req, id, body)
def create_resource():
return wsgi.Resource(ShareController())

View File

View File

@ -18,21 +18,50 @@ from manila.api.views import availability_zones as availability_zones_views
from manila import db
class AvailabilityZoneController(wsgi.Controller):
"""The Availability Zone API controller for the OpenStack API."""
class AvailabilityZoneMixin(object):
"""The Availability Zone API controller common logic.
Mixin class that should be inherited by Availability Zone API controllers,
which are used for different API URLs and microversions.
"""
resource_name = "availability_zone"
_view_builder_class = availability_zones_views.ViewBuilder
@wsgi.Controller.authorize
def index(self, req):
return self._index(req)
@wsgi.Controller.authorize("index")
def _index(self, req):
"""Describe all known availability zones."""
views = db.availability_zone_get_all(req.environ['manila.context'])
return self._view_builder.detail_list(views)
class AvailabilityZoneControllerLegacy(AvailabilityZoneMixin, wsgi.Controller):
"""Deprecated Availability Zone API controller.
Used by legacy API v1 and v2 microversions from 2.0 to 2.6.
Registered under deprecated API URL 'os-availability-zone'.
"""
@wsgi.Controller.api_version('1.0', '2.6')
def index(self, req):
return self._index(req)
class AvailabilityZoneController(AvailabilityZoneMixin, wsgi.Controller):
"""Availability Zone API controller.
Used only by API v2 starting from microversion 2.7.
Registered under API URL 'availability-zones'.
"""
@wsgi.Controller.api_version('2.7')
def index(self, req):
return self._index(req)
def create_resource_legacy():
return wsgi.Resource(AvailabilityZoneControllerLegacy())
def create_resource():
return wsgi.Resource(AvailabilityZoneController())

View File

@ -211,17 +211,25 @@ class CGSnapshotController(wsgi.Controller, wsgi.AdminActionsMixin):
def _delete(self, context, resource, force=True):
db.cgsnapshot_destroy(context.elevated(), resource['id'])
@wsgi.Controller.api_version('2.4', experimental=True)
@wsgi.Controller.api_version('2.4', '2.6', experimental=True)
@wsgi.action('os-reset_status')
@wsgi.response(202)
def cgsnapshot_reset_status(self, req, id, body):
super(self.__class__, self)._reset_status(req, id, body)
def cgsnapshot_reset_status_legacy(self, req, id, body):
return self._reset_status(req, id, body)
@wsgi.Controller.api_version('2.4', experimental=True)
@wsgi.Controller.api_version('2.7', experimental=True)
@wsgi.action('reset_status')
def cgsnapshot_reset_status(self, req, id, body):
return self._reset_status(req, id, body)
@wsgi.Controller.api_version('2.4', '2.6', experimental=True)
@wsgi.action('os-force_delete')
@wsgi.response(202)
def cgsnapshot_force_delete_legacy(self, req, id, body):
return self._force_delete(req, id, body)
@wsgi.Controller.api_version('2.7', experimental=True)
@wsgi.action('force_delete')
def cgsnapshot_force_delete(self, req, id, body):
super(self.__class__, self)._force_delete(req, id, body)
return self._force_delete(req, id, body)
def create_resource():

View File

@ -224,17 +224,25 @@ class CGController(wsgi.Controller, wsgi.AdminActionsMixin):
def _delete(self, context, resource, force=True):
db.consistency_group_destroy(context.elevated(), resource['id'])
@wsgi.Controller.api_version('2.4', experimental=True)
@wsgi.Controller.api_version('2.4', '2.6', experimental=True)
@wsgi.action('os-reset_status')
@wsgi.response(202)
def cg_reset_status(self, req, id, body):
super(self.__class__, self)._reset_status(req, id, body)
def cg_reset_status_legacy(self, req, id, body):
return self._reset_status(req, id, body)
@wsgi.Controller.api_version('2.4', experimental=True)
@wsgi.Controller.api_version('2.7', experimental=True)
@wsgi.action('reset_status')
def cg_reset_status(self, req, id, body):
return self._reset_status(req, id, body)
@wsgi.Controller.api_version('2.4', '2.6', experimental=True)
@wsgi.action('os-force_delete')
@wsgi.response(202)
def cg_force_delete_legacy(self, req, id, body):
return self._force_delete(req, id, body)
@wsgi.Controller.api_version('2.7', experimental=True)
@wsgi.action('force_delete')
def cg_force_delete(self, req, id, body):
super(self.__class__, self)._force_delete(req, id, body)
return self._force_delete(req, id, body)
def create_resource():

View File

@ -25,13 +25,18 @@ from manila import quota
QUOTAS = quota.QUOTAS
class QuotaClassSetsController(wsgi.Controller):
class QuotaClassSetsMixin(object):
"""The Quota Class Sets API controller common logic.
Mixin class that should be inherited by Quota Class Sets API controllers,
which are used for different API URLs and microversions.
"""
resource_name = "quota_class_set"
_view_builder_class = quota_class_sets_views.ViewBuilder
@wsgi.Controller.authorize
def show(self, req, id):
@wsgi.Controller.authorize("show")
def _show(self, req, id):
context = req.environ['manila.context']
try:
db.authorize_quota_class_context(context, id)
@ -41,8 +46,8 @@ class QuotaClassSetsController(wsgi.Controller):
return self._view_builder.detail_list(
QUOTAS.get_class_quotas(context, id), id)
@wsgi.Controller.authorize
def update(self, req, id, body):
@wsgi.Controller.authorize("update")
def _update(self, req, id, body):
context = req.environ['manila.context']
quota_class = id
for key in body.get(self.resource_name, {}).keys():
@ -58,5 +63,41 @@ class QuotaClassSetsController(wsgi.Controller):
QUOTAS.get_class_quotas(context, quota_class))
class QuotaClassSetsControllerLegacy(QuotaClassSetsMixin, wsgi.Controller):
"""Deprecated Quota Class Sets API controller.
Used by legacy API v1 and v2 microversions from 2.0 to 2.6.
Registered under deprecated API URL 'os-quota-class-sets'.
"""
@wsgi.Controller.api_version('1.0', '2.6')
def show(self, req, id):
return self._show(req, id)
@wsgi.Controller.api_version('1.0', '2.6')
def update(self, req, id, body):
return self._update(req, id, body)
class QuotaClassSetsController(QuotaClassSetsMixin, wsgi.Controller):
"""Quota Class Sets API controller.
Used only by API v2 starting from microversion 2.7.
Registered under API URL 'quota-class-sets'.
"""
@wsgi.Controller.api_version('2.7')
def show(self, req, id):
return self._show(req, id)
@wsgi.Controller.api_version('2.7')
def update(self, req, id, body):
return self._update(req, id, body)
def create_resource_legacy():
return wsgi.Resource(QuotaClassSetsControllerLegacy())
def create_resource():
return wsgi.Resource(QuotaClassSetsController())

View File

@ -31,8 +31,12 @@ LOG = log.getLogger(__name__)
NON_QUOTA_KEYS = ('tenant_id', 'id', 'force')
class QuotaSetsController(wsgi.Controller):
"""The Quota Sets API controller for the OpenStack API."""
class QuotaSetsMixin(object):
"""The Quota Sets API controller common logic.
Mixin class that should be inherited by Quota Sets API controllers,
which are used for different API URLs and microversions.
"""
resource_name = "quota_set"
_view_builder_class = quota_sets_views.ViewBuilder
@ -61,8 +65,8 @@ class QuotaSetsController(wsgi.Controller):
return values
return dict((k, v['limit']) for k, v in values.items())
@wsgi.Controller.authorize
def show(self, req, id):
@wsgi.Controller.authorize("show")
def _show(self, req, id):
context = req.environ['manila.context']
params = parse.parse_qs(req.environ.get('QUERY_STRING', ''))
user_id = params.get('user_id', [None])[0]
@ -74,12 +78,12 @@ class QuotaSetsController(wsgi.Controller):
raise webob.exc.HTTPForbidden()
@wsgi.Controller.authorize('show')
def defaults(self, req, id):
def _defaults(self, req, id):
context = req.environ['manila.context']
return self._view_builder.detail_list(QUOTAS.get_defaults(context), id)
@wsgi.Controller.authorize
def update(self, req, id, body):
@wsgi.Controller.authorize("update")
def _update(self, req, id, body):
context = req.environ['manila.context']
project_id = id
bad_keys = []
@ -166,8 +170,8 @@ class QuotaSetsController(wsgi.Controller):
return self._view_builder.detail_list(
self._get_quotas(context, id, user_id=user_id))
@wsgi.Controller.authorize
def delete(self, req, id):
@wsgi.Controller.authorize("delete")
def _delete(self, req, id):
context = req.environ['manila.context']
params = parse.parse_qs(req.environ.get('QUERY_STRING', ''))
user_id = params.get('user_id', [None])[0]
@ -182,5 +186,57 @@ class QuotaSetsController(wsgi.Controller):
raise webob.exc.HTTPForbidden()
class QuotaSetsControllerLegacy(QuotaSetsMixin, wsgi.Controller):
"""Deprecated Quota Sets API controller.
Used by legacy API v1 and v2 microversions from 2.0 to 2.6.
Registered under deprecated API URL 'os-quota-sets'.
"""
@wsgi.Controller.api_version('1.0', '2.6')
def show(self, req, id):
return self._show(req, id)
@wsgi.Controller.api_version('1.0', '2.6')
def defaults(self, req, id):
return self._defaults(req, id)
@wsgi.Controller.api_version('1.0', '2.6')
def update(self, req, id, body):
return self._update(req, id, body)
@wsgi.Controller.api_version('1.0', '2.6')
def delete(self, req, id):
return self._delete(req, id)
class QuotaSetsController(QuotaSetsMixin, wsgi.Controller):
"""Quota Sets API controller.
Used only by API v2 starting from microversion 2.7.
Registered under API URL 'quota-sets'.
"""
@wsgi.Controller.api_version('2.7')
def show(self, req, id):
return self._show(req, id)
@wsgi.Controller.api_version('2.7')
def defaults(self, req, id):
return self._defaults(req, id)
@wsgi.Controller.api_version('2.7')
def update(self, req, id, body):
return self._update(req, id, body)
@wsgi.Controller.api_version('2.7')
def delete(self, req, id):
return self._delete(req, id)
def create_resource_legacy():
return wsgi.Resource(QuotaSetsControllerLegacy())
def create_resource():
return wsgi.Resource(QuotaSetsController())

253
manila/api/v2/router.py Normal file
View File

@ -0,0 +1,253 @@
# Copyright 2011 OpenStack LLC.
# Copyright 2011 United States Government as represented by the
# Administrator of the National Aeronautics and Space Administration.
# Copyright (c) 2015 Mirantis inc.
# All Rights Reserved.
#
# Licensed under the Apache License, Version 2.0 (the "License"); you may
# not use this file except in compliance with the License. You may obtain
# a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
# License for the specific language governing permissions and limitations
# under the License.
"""
WSGI middleware for OpenStack Share API v2.
"""
from oslo_log import log
from manila.api import extensions
import manila.api.openstack
from manila.api.v1 import limits
from manila.api.v1 import scheduler_stats
from manila.api.v1 import security_service
from manila.api.v1 import share_instances
from manila.api.v1 import share_manage
from manila.api.v1 import share_metadata
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_extra_specs
from manila.api.v1 import share_unmanage
from manila.api.v2 import availability_zones
from manila.api.v2 import cgsnapshots
from manila.api.v2 import consistency_groups
from manila.api.v2 import quota_class_sets
from manila.api.v2 import quota_sets
from manila.api.v2 import services
from manila.api.v2 import share_types
from manila.api.v2 import shares
from manila.api import versions
LOG = log.getLogger(__name__)
class APIRouter(manila.api.openstack.APIRouter):
"""Route API requests.
Routes requests on the OpenStack API to the appropriate controller
and method.
"""
ExtensionManager = extensions.ExtensionManager
def _setup_routes(self, mapper, ext_mgr):
self.resources["versions"] = versions.create_resource()
mapper.connect("versions", "/",
controller=self.resources["versions"],
action="index")
mapper.redirect("", "/")
self.resources["availability_zones_legacy"] = (
availability_zones.create_resource_legacy())
# TODO(vponomaryov): "os-availability-zone" is deprecated
# since v2.7. Remove it when minimum API version becomes equal to
# or greater than v2.7.
mapper.resource("availability-zone",
"os-availability-zone",
controller=self.resources["availability_zones_legacy"])
self.resources["availability_zones"] = (
availability_zones.create_resource())
mapper.resource("availability-zone",
"availability-zones",
controller=self.resources["availability_zones"])
self.resources["services_legacy"] = services.create_resource_legacy()
# TODO(vponomaryov): "os-services" is deprecated
# since v2.7. Remove it when minimum API version becomes equal to
# or greater than v2.7.
mapper.resource("service",
"os-services",
controller=self.resources["services_legacy"])
self.resources["services"] = services.create_resource()
mapper.resource("service",
"services",
controller=self.resources["services"])
self.resources["quota_sets_legacy"] = (
quota_sets.create_resource_legacy())
# TODO(vponomaryov): "os-quota-sets" is deprecated
# since v2.7. Remove it when minimum API version becomes equal to
# or greater than v2.7.
mapper.resource("quota-set",
"os-quota-sets",
controller=self.resources["quota_sets_legacy"],
member={"defaults": "GET"})
self.resources["quota_sets"] = quota_sets.create_resource()
mapper.resource("quota-set",
"quota-sets",
controller=self.resources["quota_sets"],
member={"defaults": "GET"})
self.resources["quota_class_sets_legacy"] = (
quota_class_sets.create_resource_legacy())
# TODO(vponomaryov): "os-quota-class-sets" is deprecated
# since v2.7. Remove it when minimum API version becomes equal to
# or greater than v2.7.
mapper.resource("quota-class-set",
"os-quota-class-sets",
controller=self.resources["quota_class_sets_legacy"])
self.resources["quota_class_sets"] = quota_class_sets.create_resource()
mapper.resource("quota-class-set",
"quota-class-sets",
controller=self.resources["quota_class_sets"])
self.resources["share_manage"] = share_manage.create_resource()
# TODO(vponomaryov): "os-share-manage" is deprecated
# since v2.7. Remove it when minimum API version becomes equal to
# or greater than v2.7.
mapper.resource("share_manage",
"os-share-manage",
controller=self.resources["share_manage"])
self.resources["share_unmanage"] = share_unmanage.create_resource()
# TODO(vponomaryov): "os-share-unmanage" is deprecated
# since v2.7. Remove it when minimum API version becomes equal to
# or greater than v2.7.
mapper.resource("share_unmanage",
"os-share-unmanage",
controller=self.resources["share_unmanage"],
member={"unmanage": "POST"})
self.resources["shares"] = shares.create_resource()
mapper.resource("share", "shares",
controller=self.resources["shares"],
collection={"detail": "GET"},
member={"action": "POST"})
mapper.connect("shares",
"/{project_id}/shares/manage",
controller=self.resources["shares"],
action="manage",
conditions={"method": ["POST"]})
self.resources["share_instances"] = share_instances.create_resource()
mapper.resource("share_instance", "share_instances",
controller=self.resources["share_instances"],
collection={"detail": "GET"},
member={"action": "POST"})
mapper.connect("share_instance",
"/{project_id}/shares/{share_id}/instances",
controller=self.resources["share_instances"],
action="get_share_instances",
conditions={"method": ["GET"]})
self.resources["snapshots"] = share_snapshots.create_resource()
mapper.resource("snapshot", "snapshots",
controller=self.resources["snapshots"],
collection={"detail": "GET"},
member={"action": "POST"})
self.resources["share_metadata"] = share_metadata.create_resource()
share_metadata_controller = self.resources["share_metadata"]
mapper.resource("share_metadata", "metadata",
controller=share_metadata_controller,
parent_resource=dict(member_name="share",
collection_name="shares"))
mapper.connect("metadata",
"/{project_id}/shares/{share_id}/metadata",
controller=share_metadata_controller,
action="update_all",
conditions={"method": ["PUT"]})
self.resources["limits"] = limits.create_resource()
mapper.resource("limit", "limits",
controller=self.resources["limits"])
self.resources["security_services"] = (
security_service.create_resource())
mapper.resource("security-service", "security-services",
controller=self.resources["security_services"],
collection={"detail": "GET"})
self.resources["share_networks"] = share_networks.create_resource()
mapper.resource(share_networks.RESOURCE_NAME,
"share-networks",
controller=self.resources["share_networks"],
collection={"detail": "GET"},
member={"action": "POST"})
self.resources["share_servers"] = share_servers.create_resource()
mapper.resource("share_server",
"share-servers",
controller=self.resources["share_servers"])
mapper.connect("details",
"/{project_id}/share-servers/{id}/details",
controller=self.resources["share_servers"],
action="details",
conditions={"method": ["GET"]})
self.resources["types"] = share_types.create_resource()
mapper.resource("type", "types",
controller=self.resources["types"],
collection={"detail": "GET", "default": "GET"},
member={"action": "POST",
"os-share-type-access": "GET",
"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",
controller=self.resources["scheduler_stats"],
action="pools_index",
conditions={"method": ["GET"]})
mapper.connect("pools", "/{project_id}/scheduler-stats/pools/detail",
controller=self.resources["scheduler_stats"],
action="pools_detail",
conditions={"method": ["GET"]})
self.resources["consistency-groups"] = (
consistency_groups.create_resource())
mapper.resource("consistency-group", "consistency-groups",
controller=self.resources["consistency-groups"],
collection={"detail": "GET"})
mapper.connect("consistency-groups",
"/{project_id}/consistency-groups/{id}/action",
controller=self.resources["consistency-groups"],
action="action",
conditions={"action": ["POST"]})
self.resources["cgsnapshots"] = cgsnapshots.create_resource()
mapper.resource("cgsnapshot", "cgsnapshots",
controller=self.resources["cgsnapshots"],
collection={"detail": "GET"},
member={"members": "GET", "action": "POST"})

View File

@ -25,14 +25,18 @@ from manila import utils
LOG = log.getLogger(__name__)
class ServiceController(wsgi.Controller):
"""The Services API controller for the OpenStack API."""
class ServiceMixin(object):
"""The Services API controller common logic.
Mixin class that should be inherited by Services API controllers,
which are used for different API URLs and microversions.
"""
resource_name = "service"
_view_builder_class = services_views.ViewBuilder
@wsgi.Controller.authorize
def index(self, req):
@wsgi.Controller.authorize("index")
def _index(self, req):
"""Return a list of all running services."""
context = req.environ['manila.context']
@ -68,8 +72,8 @@ class ServiceController(wsgi.Controller):
return self._view_builder.detail_list(services)
@wsgi.Controller.authorize
def update(self, req, id, body):
@wsgi.Controller.authorize("update")
def _update(self, req, id, body):
"""Enable/Disable scheduling for a service."""
context = req.environ['manila.context']
@ -93,5 +97,41 @@ class ServiceController(wsgi.Controller):
return self._view_builder.summary(data)
class ServiceControllerLegacy(ServiceMixin, wsgi.Controller):
"""Deprecated Services API controller.
Used by legacy API v1 and v2 microversions from 2.0 to 2.6.
Registered under deprecated API URL 'os-services'.
"""
@wsgi.Controller.api_version('1.0', '2.6')
def index(self, req):
return self._index(req)
@wsgi.Controller.api_version('1.0', '2.6')
def update(self, req, id, body):
return self._update(req, id, body)
class ServiceController(ServiceMixin, wsgi.Controller):
"""Services API controller.
Used only by API v2 starting from microversion 2.7.
Registered under API URL 'services'.
"""
@wsgi.Controller.api_version('2.7')
def index(self, req):
return self._index(req)
@wsgi.Controller.api_version('2.7')
def update(self, req, id, body):
return self._update(req, id, body)
def create_resource_legacy():
return wsgi.Resource(ServiceControllerLegacy())
def create_resource():
return wsgi.Resource(ServiceController())

View File

@ -37,7 +37,7 @@ class ShareTypesController(wsgi.Controller):
def __getattr__(self, key):
if key == 'os-share-type-access':
return self._list_project_access
return self.share_type_access
return super(self.__class__, self).__getattr__(key)
def _notify_share_type_error(self, context, method, payload):
@ -143,7 +143,10 @@ class ShareTypesController(wsgi.Controller):
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)
is_public = share_type.get(
'os-share-type-access:is_public',
share_type.get('share_type_access:is_public', True),
)
if name is None or name == "" or len(name) > 255:
msg = _("Type name is not valid.")
@ -209,7 +212,7 @@ class ShareTypesController(wsgi.Controller):
return webob.Response(status_int=202)
@wsgi.Controller.authorize('list_project_access')
def _list_project_access(self, req, id):
def share_type_access(self, req, id):
context = req.environ['manila.context']
try:

150
manila/api/v2/shares.py Normal file
View File

@ -0,0 +1,150 @@
# Copyright (c) 2015 Mirantis inc.
# All Rights Reserved.
#
# Licensed under the Apache License, Version 2.0 (the "License"); you may
# not use this file except in compliance with the License. You may obtain
# a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
# License for the specific language governing permissions and limitations
# under the License.
from manila.api.openstack import wsgi
from manila.api.v1 import share_manage
from manila.api.v1 import share_unmanage
from manila.api.v1 import shares
from manila.api.views import shares as share_views
from manila import share
class ShareController(shares.ShareMixin,
share_manage.ShareManageMixin,
share_unmanage.ShareUnmanageMixin,
wsgi.Controller,
wsgi.AdminActionsMixin):
"""The Shares API v2 controller for the OpenStack API."""
resource_name = 'share'
_view_builder_class = share_views.ViewBuilder
def __init__(self):
super(self.__class__, self).__init__()
self.share_api = share.API()
@wsgi.Controller.api_version("2.4")
def create(self, req, body):
return self._create(req, body)
@wsgi.Controller.api_version("2.0", "2.3") # noqa
def create(self, req, body): # pylint: disable=E0102
# Remove consistency group attributes
body.get('share', {}).pop('consistency_group_id', None)
share = self._create(req, body)
return share
@wsgi.Controller.api_version('2.0', '2.6')
@wsgi.action('os-reset_status')
def share_reset_status_legacy(self, req, id, body):
return self._reset_status(req, id, body)
@wsgi.Controller.api_version('2.7')
@wsgi.action('reset_status')
def share_reset_status(self, req, id, body):
return self._reset_status(req, id, body)
@wsgi.Controller.api_version('2.0', '2.6')
@wsgi.action('os-force_delete')
def share_force_delete_legacy(self, req, id, body):
return self._force_delete(req, id, body)
@wsgi.Controller.api_version('2.7')
@wsgi.action('force_delete')
def share_force_delete(self, req, id, body):
return self._force_delete(req, id, body)
@wsgi.Controller.api_version('2.5', '2.6', experimental=True)
@wsgi.action("os-migrate_share")
def migrate_share_legacy(self, req, id, body):
return self._migrate_share(req, id, body)
@wsgi.Controller.api_version('2.7', experimental=True)
@wsgi.action("migrate_share")
def migrate_share(self, req, id, body):
return self._migrate_share(req, id, body)
@wsgi.Controller.api_version('2.0', '2.6')
@wsgi.action('os-allow_access')
def allow_access_legacy(self, req, id, body):
"""Add share access rule."""
return self._allow_access(req, id, body)
@wsgi.Controller.api_version('2.7')
@wsgi.action('allow_access')
def allow_access(self, req, id, body):
"""Add share access rule."""
return self._allow_access(req, id, body)
@wsgi.Controller.api_version('2.0', '2.6')
@wsgi.action('os-deny_access')
def deny_access_legacy(self, req, id, body):
"""Remove share access rule."""
return self._deny_access(req, id, body)
@wsgi.Controller.api_version('2.7')
@wsgi.action('deny_access')
def deny_access(self, req, id, body):
"""Remove share access rule."""
return self._deny_access(req, id, body)
@wsgi.Controller.api_version('2.0', '2.6')
@wsgi.action('os-access_list')
def access_list_legacy(self, req, id, body):
"""List share access rules."""
return self._access_list(req, id, body)
@wsgi.Controller.api_version('2.7')
@wsgi.action('access_list')
def access_list(self, req, id, body):
"""List share access rules."""
return self._access_list(req, id, body)
@wsgi.Controller.api_version('2.0', '2.6')
@wsgi.action('os-extend')
def extend_legacy(self, req, id, body):
"""Extend size of a share."""
return self._extend(req, id, body)
@wsgi.Controller.api_version('2.7')
@wsgi.action('extend')
def extend(self, req, id, body):
"""Extend size of a share."""
return self._extend(req, id, body)
@wsgi.Controller.api_version('2.0', '2.6')
@wsgi.action('os-shrink')
def shrink_legacy(self, req, id, body):
"""Shrink size of a share."""
return self._shrink(req, id, body)
@wsgi.Controller.api_version('2.7')
@wsgi.action('shrink')
def shrink(self, req, id, body):
"""Shrink size of a share."""
return self._shrink(req, id, body)
@wsgi.Controller.api_version('2.7')
def manage(self, req, body):
return self._manage(req, body)
@wsgi.Controller.api_version('2.7')
@wsgi.action('unmanage')
def unmanage(self, req, id, body=None):
return self._unmanage(req, id, body)
def create_resource():
return wsgi.Resource(ShareController())

View File

@ -21,6 +21,11 @@ class ViewBuilder(common.ViewBuilder):
_collection_name = 'types'
_detail_version_modifiers = [
"add_is_public_attr_core_api_like",
"add_is_public_attr_extension_like",
]
def show(self, request, share_type, brief=False):
"""Trim away extraneous share type attributes."""
@ -38,16 +43,25 @@ class ViewBuilder(common.ViewBuilder):
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,
}
self.update_versioned_resource_dict(request, trimmed, share_type)
if brief:
return trimmed
else:
return dict(volume_type=trimmed, share_type=trimmed)
@common.ViewBuilder.versioned_method("2.7")
def add_is_public_attr_core_api_like(self, share_type_dict, share_type):
share_type_dict['share_type_access:is_public'] = share_type.get(
'is_public', True)
@common.ViewBuilder.versioned_method("1.0", "2.6")
def add_is_public_attr_extension_like(self, share_type_dict, share_type):
share_type_dict['os-share-type-access:is_public'] = share_type.get(
'is_public', True)
def index(self, request, share_types):
"""Index over trimmed share types."""
share_types_list = [self.show(request, share_type, True)

View File

@ -28,7 +28,8 @@ from manila.api.openstack import api_version_request as api_version
from manila.api.openstack import wsgi as os_wsgi
from manila.api import urlmap
from manila.api.v1 import limits
from manila.api.v1 import router
from manila.api.v1 import router as router_v1
from manila.api.v2 import router as router_v2
from manila.api import versions
from manila.common import constants
from manila import context
@ -64,7 +65,7 @@ def fake_wsgi(self, req):
def wsgi_app(inner_app_v2=None, fake_auth=True, fake_auth_context=None,
use_no_auth=False, ext_mgr=None):
if not inner_app_v2:
inner_app_v2 = router.APIRouter(ext_mgr)
inner_app_v2 = router_v2.APIRouter(ext_mgr)
if fake_auth:
if fake_auth_context is not None:
@ -174,28 +175,45 @@ def app():
No auth, just let environ['manila.context'] pass through.
"""
api = router.APIRouter()
mapper = urlmap.URLMap()
mapper['/v2'] = api
mapper['/v1'] = api
mapper['/v1'] = router_v1.APIRouter()
mapper['/v2'] = router_v2.APIRouter()
return mapper
fixture_reset_status_with_different_roles = (
{
'role': 'admin', 'valid_code': 202,
'role': 'admin',
'valid_code': 202,
'valid_status': constants.STATUS_ERROR,
'version': '2.6',
},
{
'role': 'member', 'valid_code': 403,
'role': 'admin',
'valid_code': 202,
'valid_status': constants.STATUS_ERROR,
'version': '2.7',
},
{
'role': 'member',
'valid_code': 403,
'valid_status': constants.STATUS_AVAILABLE,
'version': '2.6',
},
{
'role': 'member',
'valid_code': 403,
'valid_status': constants.STATUS_AVAILABLE,
'version': '2.7',
},
)
fixture_force_delete_with_different_roles = (
{'role': 'admin', 'resp_code': 202},
{'role': 'member', 'resp_code': 403},
{'role': 'admin', 'resp_code': 202, 'version': '2.6'},
{'role': 'admin', 'resp_code': 202, 'version': '2.7'},
{'role': 'member', 'resp_code': 403, 'version': '2.6'},
{'role': 'member', 'resp_code': 403, 'version': '2.7'},
)

View File

@ -15,7 +15,6 @@ import mock
from oslo_config import cfg
from oslo_serialization import jsonutils
import six
import webob
from webob import exc as webob_exc
from manila.api.v1 import share_instances
@ -48,12 +47,13 @@ class ShareInstancesAPITest(test.TestCase):
def _get_context(self, role):
return getattr(self, '%s_context' % role)
def _setup_share_instance_data(self, instance=None):
def _setup_share_instance_data(self, instance=None, version='2.7'):
if instance is None:
instance = db_utils.create_share(status=constants.STATUS_AVAILABLE,
size='1').instance
req = webob.Request.blank(
'/v2/fake/share_instances/%s/action' % instance['id'])
req = fakes.HTTPRequest.blank(
'/v2/fake/share_instances/%s/action' % instance['id'],
version=version)
return instance, req
def _get_request(self, uri, context=None):
@ -139,11 +139,16 @@ class ShareInstancesAPITest(test.TestCase):
webob_exc.HTTPForbidden, target_method, req, *args)
def _reset_status(self, ctxt, model, req, db_access_method,
valid_code, valid_status=None, body=None):
valid_code, valid_status=None, body=None, version='2.7'):
if float(version) > 2.6:
action_name = 'reset_status'
else:
action_name = 'os-reset_status'
if body is None:
body = {'os-reset_status': {'status': constants.STATUS_ERROR}}
body = {action_name: {'status': constants.STATUS_ERROR}}
req.method = 'POST'
req.headers['content-type'] = 'application/json'
req.headers['X-Openstack-Manila-Api-Version'] = version
req.body = six.b(jsonutils.dumps(body))
req.environ['manila.context'] = ctxt
@ -167,29 +172,40 @@ class ShareInstancesAPITest(test.TestCase):
@ddt.unpack
def test_share_instances_reset_status_with_different_roles(self, role,
valid_code,
valid_status):
valid_status,
version):
ctxt = self._get_context(role)
instance, req = self._setup_share_instance_data()
req.headers['X-Openstack-Manila-Api-Version'] = '2.3'
instance, req = self._setup_share_instance_data(version=version)
self._reset_status(ctxt, instance, req, db.share_instance_get,
valid_code, valid_status)
valid_code, valid_status, version=version)
@ddt.data(*fakes.fixture_invalid_reset_status_body)
def test_share_instance_invalid_reset_status_body(self, body):
@ddt.data(
({'os-reset_status': {'x-status': 'bad'}}, '2.6'),
({'os-reset_status': {'status': 'invalid'}}, '2.6'),
({'reset_status': {'x-status': 'bad'}}, '2.7'),
({'reset_status': {'status': 'invalid'}}, '2.7'),
)
@ddt.unpack
def test_share_instance_invalid_reset_status_body(self, body, version):
instance, req = self._setup_share_instance_data()
req.headers['X-Openstack-Manila-Api-Version'] = '2.3'
req.headers['X-Openstack-Manila-Api-Version'] = version
self._reset_status(self.admin_context, instance, req,
db.share_instance_get, 400,
constants.STATUS_AVAILABLE, body)
constants.STATUS_AVAILABLE, body, version=version)
def _force_delete(self, ctxt, model, req, db_access_method, valid_code,
check_model_in_db=False):
check_model_in_db=False, version='2.7'):
if float(version) > 2.6:
action_name = 'force_delete'
else:
action_name = 'os-force_delete'
body = {action_name: {'status': constants.STATUS_ERROR}}
req.method = 'POST'
req.headers['content-type'] = 'application/json'
req.headers['X-Openstack-Manila-Api-Version'] = '2.3'
req.body = six.b(jsonutils.dumps({'os-force_delete': {}}))
req.headers['X-Openstack-Manila-Api-Version'] = version
req.body = six.b(jsonutils.dumps(body))
req.environ['manila.context'] = ctxt
with mock.patch.object(
@ -207,12 +223,13 @@ class ShareInstancesAPITest(test.TestCase):
@ddt.data(*fakes.fixture_force_delete_with_different_roles)
@ddt.unpack
def test_instance_force_delete_with_different_roles(self, role, resp_code):
instance, req = self._setup_share_instance_data()
def test_instance_force_delete_with_different_roles(self, role, resp_code,
version):
instance, req = self._setup_share_instance_data(version=version)
ctxt = self._get_context(role)
self._force_delete(ctxt, instance, req, db.share_instance_get,
resp_code)
resp_code, version=version)
def test_instance_force_delete_missing(self):
instance, req = self._setup_share_instance_data(

View File

@ -390,21 +390,26 @@ class ShareSnapshotAdminActionsAPITest(test.TestCase):
def _get_context(self, role):
return getattr(self, '%s_context' % role)
def _setup_snapshot_data(self, snapshot=None):
def _setup_snapshot_data(self, snapshot=None, version='2.7'):
if snapshot is None:
share = db_utils.create_share()
snapshot = db_utils.create_snapshot(
status=constants.STATUS_AVAILABLE, share_id=share['id'])
req = webob.Request.blank('/v2/fake/snapshots/%s/action' %
snapshot['id'])
req = fakes.HTTPRequest.blank('/v2/fake/snapshots/%s/action' %
snapshot['id'], version=version)
return snapshot, req
def _reset_status(self, ctxt, model, req, db_access_method,
valid_code, valid_status=None, body=None):
valid_code, valid_status=None, body=None, version='2.7'):
if float(version) > 2.6:
action_name = 'reset_status'
else:
action_name = 'os-reset_status'
if body is None:
body = {'os-reset_status': {'status': constants.STATUS_ERROR}}
body = {action_name: {'status': constants.STATUS_ERROR}}
req.method = 'POST'
req.headers['content-type'] = 'application/json'
req.headers['X-Openstack-Manila-Api-Version'] = version
req.body = six.b(jsonutils.dumps(body))
req.environ['manila.context'] = ctxt
@ -425,25 +430,37 @@ class ShareSnapshotAdminActionsAPITest(test.TestCase):
@ddt.data(*fakes.fixture_reset_status_with_different_roles)
@ddt.unpack
def test_snapshot_reset_status_with_different_roles(self, role, valid_code,
valid_status):
valid_status, version):
ctxt = self._get_context(role)
snapshot, req = self._setup_snapshot_data()
snapshot, req = self._setup_snapshot_data(version=version)
self._reset_status(ctxt, snapshot, req, db.share_snapshot_get,
valid_code, valid_status)
valid_code, valid_status, version=version)
@ddt.data(*fakes.fixture_invalid_reset_status_body)
def test_snapshot_invalid_reset_status_body(self, body):
snapshot, req = self._setup_snapshot_data()
@ddt.data(
({'os-reset_status': {'x-status': 'bad'}}, '2.6'),
({'reset_status': {'x-status': 'bad'}}, '2.7'),
({'os-reset_status': {'status': 'invalid'}}, '2.6'),
({'reset_status': {'status': 'invalid'}}, '2.7'),
)
@ddt.unpack
def test_snapshot_invalid_reset_status_body(self, body, version):
snapshot, req = self._setup_snapshot_data(version=version)
self._reset_status(self.admin_context, snapshot, req,
db.share_snapshot_get, 400,
constants.STATUS_AVAILABLE, body)
constants.STATUS_AVAILABLE, body, version=version)
def _force_delete(self, ctxt, model, req, db_access_method, valid_code):
def _force_delete(self, ctxt, model, req, db_access_method, valid_code,
version='2.7'):
if float(version) > 2.6:
action_name = 'force_delete'
else:
action_name = 'os-force_delete'
req.method = 'POST'
req.headers['content-type'] = 'application/json'
req.body = six.b(jsonutils.dumps({'os-force_delete': {}}))
req.headers['X-Openstack-Manila-Api-Version'] = version
req.body = six.b(jsonutils.dumps({action_name: {}}))
req.environ['manila.context'] = ctxt
resp = req.get_response(fakes.app())
@ -453,12 +470,13 @@ class ShareSnapshotAdminActionsAPITest(test.TestCase):
@ddt.data(*fakes.fixture_force_delete_with_different_roles)
@ddt.unpack
def test_snapshot_force_delete_with_different_roles(self, role, resp_code):
def test_snapshot_force_delete_with_different_roles(self, role, resp_code,
version):
ctxt = self._get_context(role)
snapshot, req = self._setup_snapshot_data()
snapshot, req = self._setup_snapshot_data(version=version)
self._force_delete(ctxt, snapshot, req, db.share_snapshot_get,
resp_code)
resp_code, version=version)
def test_snapshot_force_delete_missing(self):
ctxt = self._get_context('admin')

View File

@ -41,14 +41,6 @@ from manila import utils
CONF = cfg.CONF
def app():
# no auth, just let environ['manila.context'] pass through
api = fakes.router.APIRouter()
mapper = fakes.urlmap.URLMap()
mapper['/v1'] = api
return mapper
@ddt.ddt
class ShareAPITest(test.TestCase):
"""Share API Test."""
@ -230,62 +222,6 @@ class ShareAPITest(test.TestCase):
self.assertEqual("fakenetid",
create_mock.call_args[1]['share_network_id'])
def test_migrate_share(self):
share = db_utils.create_share()
req = fakes.HTTPRequest.blank('/shares/%s/action' % share['id'],
use_admin_context=True)
req.method = 'POST'
req.headers['content-type'] = 'application/json'
req.api_version_request = api_version.APIVersionRequest('2.5')
req.api_version_request.experimental = True
body = {'os-migrate_share': {'host': 'fake_host'}}
self.mock_object(share_api.API, 'migrate_share')
self.controller.migrate_share(req, share['id'], body)
def test_migrate_share_no_share_id(self):
req = fakes.HTTPRequest.blank('/shares/%s/action' % 'fake_id',
use_admin_context=True)
req.method = 'POST'
req.headers['content-type'] = 'application/json'
req.api_version_request = api_version.APIVersionRequest('2.5')
req.api_version_request.experimental = True
body = {'os-migrate_share': {'host': 'fake_host'}}
self.mock_object(share_api.API, 'migrate_share')
self.mock_object(share_api.API, 'get',
mock.Mock(side_effect=[exception.NotFound]))
self.assertRaises(webob.exc.HTTPNotFound,
self.controller.migrate_share,
req, 'fake_id', body)
def test_migrate_share_no_host(self):
share = db_utils.create_share()
req = fakes.HTTPRequest.blank('/shares/%s/action' % share['id'],
use_admin_context=True)
req.method = 'POST'
req.headers['content-type'] = 'application/json'
req.api_version_request = api_version.APIVersionRequest('2.5')
req.api_version_request.experimental = True
body = {'os-migrate_share': {}}
self.mock_object(share_api.API, 'migrate_share')
self.assertRaises(webob.exc.HTTPBadRequest,
self.controller.migrate_share,
req, share['id'], body)
def test_migrate_share_no_host_invalid_force_host_copy(self):
share = db_utils.create_share()
req = fakes.HTTPRequest.blank('/shares/%s/action' % share['id'],
use_admin_context=True)
req.method = 'POST'
req.headers['content-type'] = 'application/json'
req.api_version_request = api_version.APIVersionRequest('2.5')
req.api_version_request.experimental = True
body = {'os-migrate_share': {'host': 'fake_host',
'force_host_copy': 'fake'}}
self.mock_object(share_api.API, 'migrate_share')
self.assertRaises(webob.exc.HTTPBadRequest,
self.controller.migrate_share,
req, share['id'], body)
def test_share_create_from_snapshot_without_share_net_no_parent(self):
shr = {
"size": 100,
@ -1059,6 +995,27 @@ class ShareAdminActionsAPITest(test.TestCase):
actual_model = db_access_method(ctxt, model['id'])
self.assertEqual(valid_status, actual_model['status'])
@ddt.data(
{
'role': 'admin',
'valid_code': 202,
'valid_status': constants.STATUS_ERROR,
},
{
'role': 'member',
'valid_code': 403,
'valid_status': constants.STATUS_AVAILABLE,
},
)
@ddt.unpack
def test_share_reset_status_with_different_roles(self, role, valid_code,
valid_status):
share, req = self._setup_share_data()
ctxt = self._get_context(role)
self._reset_status(ctxt, share, req, db.share_get, valid_code,
valid_status)
@ddt.data(*fakes.fixture_invalid_reset_status_body)
def test_share_invalid_reset_status_body(self, body):
share, req = self._setup_share_data()
@ -1093,17 +1050,10 @@ class ShareAdminActionsAPITest(test.TestCase):
ctxt,
model['id'])
@ddt.data(*fakes.fixture_reset_status_with_different_roles)
@ddt.unpack
def test_share_reset_status_with_different_roles(self, role, valid_code,
valid_status):
share, req = self._setup_share_data()
ctxt = self._get_context(role)
self._reset_status(ctxt, share, req, db.share_get, valid_code,
valid_status)
@ddt.data(*fakes.fixture_force_delete_with_different_roles)
@ddt.data(
{'role': 'admin', 'resp_code': 202},
{'role': 'member', 'resp_code': 403},
)
@ddt.unpack
def test_share_force_delete_with_different_roles(self, role, resp_code):
share, req = self._setup_share_data()

View File

View File

@ -15,8 +15,9 @@
import ddt
import mock
from manila.api.v1 import availability_zones
from manila.api.v2 import availability_zones
from manila import context
from manila import exception
from manila import policy
from manila import test
from manila.tests.api import fakes
@ -25,16 +26,26 @@ from manila.tests.api import fakes
@ddt.ddt
class AvailabilityZonesAPITest(test.TestCase):
def test_instantiate_controller(self):
controller = availability_zones.AvailabilityZoneController()
@ddt.data(
availability_zones.AvailabilityZoneControllerLegacy,
availability_zones.AvailabilityZoneController,
)
def test_instantiate_controller(self, controller):
az_controller = controller()
self.assertTrue(hasattr(controller, "resource_name"))
self.assertEqual("availability_zone", controller.resource_name)
self.assertTrue(hasattr(controller, "_view_builder"))
self.assertTrue(hasattr(controller._view_builder, "detail_list"))
self.assertTrue(hasattr(az_controller, "resource_name"))
self.assertEqual("availability_zone", az_controller.resource_name)
self.assertTrue(hasattr(az_controller, "_view_builder"))
self.assertTrue(hasattr(az_controller._view_builder, "detail_list"))
@ddt.data('1.0', '2.0', '2.6')
def test_index(self, version):
@ddt.data(
('1.0', availability_zones.AvailabilityZoneControllerLegacy),
('2.0', availability_zones.AvailabilityZoneControllerLegacy),
('2.6', availability_zones.AvailabilityZoneControllerLegacy),
('2.7', availability_zones.AvailabilityZoneController),
)
@ddt.unpack
def test_index(self, version, controller):
azs = [
{
"id": "fake_id1",
@ -54,12 +65,12 @@ class AvailabilityZonesAPITest(test.TestCase):
mock_policy_check = self.mock_object(policy, 'check_policy')
self.mock_object(availability_zones.db, 'availability_zone_get_all',
mock.Mock(return_value=azs))
controller = availability_zones.AvailabilityZoneController()
az_controller = controller()
ctxt = context.RequestContext("admin", "fake", True)
req = fakes.HTTPRequest.blank('/shares', version=version)
req.environ['manila.context'] = ctxt
result = controller.index(req)
result = az_controller.index(req)
availability_zones.db.availability_zone_get_all.\
assert_called_once_with(ctxt)
@ -73,3 +84,19 @@ class AvailabilityZonesAPITest(test.TestCase):
azs[1].pop("deleted")
azs[1].pop("redundant_key")
self.assertTrue(azs[1] in result["availability_zones"])
@ddt.data(
('1.0', availability_zones.AvailabilityZoneController),
('2.0', availability_zones.AvailabilityZoneController),
('2.6', availability_zones.AvailabilityZoneController),
('2.7', availability_zones.AvailabilityZoneControllerLegacy),
)
@ddt.unpack
def test_index_with_unsupported_versions(self, version, controller):
ctxt = context.RequestContext("admin", "fake", True)
req = fakes.HTTPRequest.blank('/shares', version=version)
req.environ['manila.context'] = ctxt
az_controller = controller()
self.assertRaises(
exception.VersionNotFoundForAPIMethod, az_controller.index, req)

View File

@ -25,7 +25,7 @@ import six
import webob
from manila.api.openstack import wsgi
import manila.api.v1.cgsnapshots as cgs
import manila.api.v2.cgsnapshots as cgs
from manila.common import constants
from manila import context
from manila import db
@ -518,25 +518,31 @@ class CGSnapshotApiTest(test.TestCase):
def _get_context(self, role):
return getattr(self, '%s_context' % role)
def _setup_cgsnapshot_data(self, cgsnapshot=None):
def _setup_cgsnapshot_data(self, cgsnapshot=None, version='2.7'):
if cgsnapshot is None:
cgsnapshot = db_utils.create_cgsnapshot(
'fake_id', status=constants.STATUS_AVAILABLE)
req = webob.Request.blank('/v2/fake/cgsnapshots/%s/action' %
cgsnapshot['id'])
req.headers[wsgi.API_VERSION_REQUEST_HEADER] = '2.4'
req = fakes.HTTPRequest.blank('/v2/fake/cgsnapshots/%s/action' %
cgsnapshot['id'], version=version)
req.headers[wsgi.API_VERSION_REQUEST_HEADER] = version
req.headers[wsgi.EXPERIMENTAL_API_REQUEST_HEADER] = 'True'
return cgsnapshot, req
@ddt.data(*fakes.fixture_force_delete_with_different_roles)
@ddt.unpack
def test_cgsnapshot_force_delete_with_different_roles(self, role,
resp_code):
resp_code, version):
cgsnap, req = self._setup_cgsnapshot_data()
ctxt = self._get_context(role)
req.method = 'POST'
req.headers['content-type'] = 'application/json'
req.body = six.b(jsonutils.dumps({'os-force_delete': {}}))
if float(version) > 2.6:
action_name = 'force_delete'
else:
action_name = 'os-force_delete'
body = {action_name: {'status': constants.STATUS_ERROR}}
req.body = six.b(jsonutils.dumps(body))
req.headers['X-Openstack-Manila-Api-Version'] = version
req.environ['manila.context'] = ctxt
with mock.patch.object(
@ -549,13 +555,18 @@ class CGSnapshotApiTest(test.TestCase):
@ddt.data(*fakes.fixture_reset_status_with_different_roles)
@ddt.unpack
def test_cgsnapshot_reset_status_with_different_roles(
self, role, valid_code, valid_status):
self, role, valid_code, valid_status, version):
ctxt = self._get_context(role)
cgsnap, req = self._setup_cgsnapshot_data()
body = {'os-reset_status': {'status': constants.STATUS_ERROR}}
cgsnap, req = self._setup_cgsnapshot_data(version=version)
if float(version) > 2.6:
action_name = 'reset_status'
else:
action_name = 'os-reset_status'
body = {action_name: {'status': constants.STATUS_ERROR}}
req.method = 'POST'
req.headers['content-type'] = 'application/json'
req.body = six.b(jsonutils.dumps(body))
req.headers['X-Openstack-Manila-Api-Version'] = version
req.environ['manila.context'] = ctxt
with mock.patch.object(

View File

@ -25,7 +25,7 @@ import six
import webob
from manila.api.openstack import wsgi
import manila.api.v1.consistency_groups as cgs
import manila.api.v2.consistency_groups as cgs
from manila.common import constants
import manila.consistency_group.api as cg_api
from manila import context
@ -64,13 +64,13 @@ class CGApiTest(test.TestCase):
def _get_context(self, role):
return getattr(self, '%s_context' % role)
def _setup_cg_data(self, cg=None):
def _setup_cg_data(self, cg=None, version='2.7'):
if cg is None:
cg = db_utils.create_consistency_group(
status=constants.STATUS_AVAILABLE)
req = webob.Request.blank('/v2/fake/consistency-groups/%s/action' %
cg['id'])
req.headers[wsgi.API_VERSION_REQUEST_HEADER] = '2.4'
req = fakes.HTTPRequest.blank('/v2/fake/consistency-groups/%s/action' %
cg['id'], version=version)
req.headers[wsgi.API_VERSION_REQUEST_HEADER] = version
req.headers[wsgi.EXPERIMENTAL_API_REQUEST_HEADER] = 'True'
return cg, req
@ -629,14 +629,19 @@ class CGApiTest(test.TestCase):
@ddt.data(*fakes.fixture_reset_status_with_different_roles)
@ddt.unpack
def test_consistency_groups_reset_status_with_different_roles(
self, role, valid_code, valid_status):
self, role, valid_code, valid_status, version):
ctxt = self._get_context(role)
cg, req = self._setup_cg_data()
cg, req = self._setup_cg_data(version=version)
body = {'os-reset_status': {'status': constants.STATUS_ERROR}}
if float(version) > 2.6:
action_name = 'reset_status'
else:
action_name = 'os-reset_status'
body = {action_name: {'status': constants.STATUS_ERROR}}
req.method = 'POST'
req.headers['content-type'] = 'application/json'
req.body = six.b(jsonutils.dumps(body))
req.headers['X-Openstack-Manila-Api-Version'] = version
req.environ['manila.context'] = ctxt
with mock.patch.object(
@ -658,12 +663,18 @@ class CGApiTest(test.TestCase):
@ddt.data(*fakes.fixture_force_delete_with_different_roles)
@ddt.unpack
def test_consistency_group_force_delete_with_different_roles(self, role,
resp_code):
resp_code,
version):
ctxt = self._get_context(role)
cg, req = self._setup_cg_data()
cg, req = self._setup_cg_data(version=version)
req.method = 'POST'
req.headers['content-type'] = 'application/json'
req.body = six.b(jsonutils.dumps({'os-force_delete': {}}))
if float(version) > 2.6:
action_name = 'force_delete'
else:
action_name = 'os-force_delete'
body = {action_name: {}}
req.body = six.b(jsonutils.dumps(body))
req.environ['manila.context'] = ctxt
with mock.patch.object(

View File

@ -26,11 +26,12 @@ from oslo_config import cfg
import webob.exc
import webob.response
from manila.api.v1 import quota_class_sets
from manila.api.v2 import quota_class_sets
from manila import context
from manila import exception
from manila import policy
from manila import test
from manila.tests.api import fakes
CONF = cfg.CONF
@ -55,7 +56,16 @@ class QuotaSetsControllerTest(test.TestCase):
self.mock_policy_check = self.mock_object(
policy, 'check_policy', mock.Mock(return_value=True))
def test_show_quota(self):
@ddt.data(
('os-', '1.0', quota_class_sets.QuotaClassSetsControllerLegacy),
('os-', '2.6', quota_class_sets.QuotaClassSetsControllerLegacy),
('', '2.7', quota_class_sets.QuotaClassSetsController),
)
@ddt.unpack
def test_show_quota(self, url, version, controller):
req = fakes.HTTPRequest.blank(
'/fooproject/%squota-class-sets' % url,
version=version, use_admin_context=True)
quotas = {
"shares": 23,
"snapshots": 34,
@ -76,11 +86,11 @@ class QuotaSetsControllerTest(test.TestCase):
for k, v in quotas.items():
CONF.set_default('quota_' + k, v)
result = self.controller.show(REQ, self.class_name)
result = controller().show(req, self.class_name)
self.assertEqual(expected, result)
self.mock_policy_check.assert_called_once_with(
REQ.environ['manila.context'], self.resource_name, 'show')
req.environ['manila.context'], self.resource_name, 'show')
def test_show_quota_not_authorized(self):
self.mock_object(
@ -95,7 +105,16 @@ class QuotaSetsControllerTest(test.TestCase):
self.mock_policy_check.assert_called_once_with(
REQ.environ['manila.context'], self.resource_name, 'show')
def test_update_quota(self):
@ddt.data(
('os-', '1.0', quota_class_sets.QuotaClassSetsControllerLegacy),
('os-', '2.6', quota_class_sets.QuotaClassSetsControllerLegacy),
('', '2.7', quota_class_sets.QuotaClassSetsController),
)
@ddt.unpack
def test_update_quota(self, url, version, controller):
req = fakes.HTTPRequest.blank(
'/fooproject/%squota-class-sets' % url,
version=version, use_admin_context=True)
CONF.set_default('quota_shares', 789)
body = {
'quota_class_set': {
@ -113,17 +132,17 @@ class QuotaSetsControllerTest(test.TestCase):
}
}
update_result = self.controller.update(
REQ, self.class_name, body=body)
update_result = controller().update(
req, self.class_name, body=body)
self.assertEqual(expected, update_result)
show_result = self.controller.show(REQ, self.class_name)
show_result = controller().show(req, self.class_name)
expected['quota_class_set']['id'] = self.class_name
self.assertEqual(expected, show_result)
self.mock_policy_check.assert_has_calls([mock.call(
REQ.environ['manila.context'], self.resource_name, action_name)
req.environ['manila.context'], self.resource_name, action_name)
for action_name in ('update', 'show')])
def test_update_quota_not_authorized(self):
@ -140,3 +159,32 @@ class QuotaSetsControllerTest(test.TestCase):
REQ_MEMBER, self.class_name, body=body)
self.mock_policy_check.assert_called_once_with(
REQ_MEMBER.environ['manila.context'], self.resource_name, 'update')
@ddt.data(
('os-', '2.7', quota_class_sets.QuotaClassSetsControllerLegacy),
('', '2.6', quota_class_sets.QuotaClassSetsController),
('', '2.0', quota_class_sets.QuotaClassSetsController),
)
@ddt.unpack
def test_api_not_found(self, url, version, controller):
req = fakes.HTTPRequest.blank(
'/fooproject/%squota-class-sets' % url, version=version)
for method_name in ('show', 'update'):
self.assertRaises(
exception.VersionNotFoundForAPIMethod,
getattr(controller(), method_name),
req, self.class_name)
@ddt.data(
('os-', '2.7', quota_class_sets.QuotaClassSetsControllerLegacy),
('', '2.6', quota_class_sets.QuotaClassSetsController),
('', '2.0', quota_class_sets.QuotaClassSetsController),
)
@ddt.unpack
def test_update_api_not_found(self, url, version, controller):
req = fakes.HTTPRequest.blank(
'/fooproject/%squota-class-sets' % url, version=version)
self.assertRaises(
exception.VersionNotFoundForAPIMethod,
controller().update,
req, self.class_name)

View File

@ -26,11 +26,12 @@ from oslo_config import cfg
import webob.exc
import webob.response
from manila.api.v1 import quota_sets
from manila.api.v2 import quota_sets
from manila import context
from manila import exception
from manila import policy
from manila import test
from manila.tests.api import fakes
from manila import utils
CONF = cfg.CONF
@ -91,6 +92,35 @@ class QuotaSetsControllerTest(test.TestCase):
self.mock_policy_check.assert_called_once_with(
REQ.environ['manila.context'], self.resource_name, 'show')
@ddt.data(
('os-', '1.0', quota_sets.QuotaSetsControllerLegacy, 'defaults'),
('os-', '2.6', quota_sets.QuotaSetsControllerLegacy, 'defaults'),
('', '2.7', quota_sets.QuotaSetsController, 'defaults'),
('os-', '1.0', quota_sets.QuotaSetsControllerLegacy, 'show'),
('os-', '2.6', quota_sets.QuotaSetsControllerLegacy, 'show'),
('', '2.7', quota_sets.QuotaSetsController, 'show'),
)
@ddt.unpack
def test_get_quotas_with_different_api_versions(self, url, version,
controller, method_name):
expected = {
'quota_set': {
'id': self.project_id,
'shares': 50,
'gigabytes': 1000,
'snapshots': 50,
'snapshot_gigabytes': 1000,
'share_networks': 10,
}
}
req = fakes.HTTPRequest.blank(
'/fooproject/%squota-sets' % url,
version=version, use_admin_context=True)
result = getattr(controller(), method_name)(req, self.project_id)
self.assertEqual(expected, result)
@ddt.data(REQ, REQ_WITH_USER)
def test_show_quota(self, request):
quotas = {
@ -216,7 +246,15 @@ class QuotaSetsControllerTest(test.TestCase):
self.mock_policy_check.assert_called_once_with(
REQ_MEMBER.environ['manila.context'], self.resource_name, 'update')
def test_update_all_quotas_with_force(self):
@ddt.data(
('os-quota-sets', '1.0', quota_sets.QuotaSetsControllerLegacy),
('os-quota-sets', '2.6', quota_sets.QuotaSetsControllerLegacy),
('quota-sets', '2.7', quota_sets.QuotaSetsController),
)
@ddt.unpack
def test_update_all_quotas_with_force(self, url, version, controller):
req = fakes.HTTPRequest.blank(
'/fooproject/%s' % url, version=version, use_admin_context=True)
quotas = (
('quota_shares', 13),
('quota_gigabytes', 14),
@ -237,30 +275,37 @@ class QuotaSetsControllerTest(test.TestCase):
'force': True,
}
}
mock_policy_update_check_call = mock.call(
REQ.environ['manila.context'], self.resource_name, 'update')
mock_policy_show_check_call = mock.call(
REQ.environ['manila.context'], self.resource_name, 'show')
update_result = self.controller.update(
REQ, self.project_id, body=expected)
update_result = controller().update(
req, self.project_id, body=expected)
expected['quota_set'].pop('force')
expected['quota_set'].pop('tenant_id')
self.assertEqual(expected, update_result)
show_result = self.controller.show(REQ, self.project_id)
show_result = controller().show(req, self.project_id)
expected['quota_set']['id'] = self.project_id
self.assertEqual(expected, show_result)
self.mock_policy_check.assert_has_calls([
mock_policy_update_check_call, mock_policy_show_check_call])
mock.call(req.environ['manila.context'],
self.resource_name, action)
for action in ('update', 'show')
])
def test_delete_tenant_quota(self):
@ddt.data(
('os-quota-sets', '1.0', quota_sets.QuotaSetsControllerLegacy),
('os-quota-sets', '2.6', quota_sets.QuotaSetsControllerLegacy),
('quota-sets', '2.7', quota_sets.QuotaSetsController),
)
@ddt.unpack
def test_delete_tenant_quota(self, url, version, controller):
self.mock_object(quota_sets.QUOTAS, 'destroy_all_by_project_and_user')
self.mock_object(quota_sets.QUOTAS, 'destroy_all_by_project')
req = fakes.HTTPRequest.blank(
'/fooproject/%s' % url, version=version, use_admin_context=True)
result = self.controller.delete(REQ, self.project_id)
result = controller().delete(req, self.project_id)
self.assertTrue(
utils.IsAMatcher(webob.response.Response) == result
@ -270,9 +315,9 @@ class QuotaSetsControllerTest(test.TestCase):
self.assertFalse(
quota_sets.QUOTAS.destroy_all_by_project_and_user.called)
quota_sets.QUOTAS.destroy_all_by_project.assert_called_once_with(
REQ.environ['manila.context'], self.project_id)
req.environ['manila.context'], self.project_id)
self.mock_policy_check.assert_called_once_with(
REQ.environ['manila.context'], self.resource_name, 'delete')
req.environ['manila.context'], self.resource_name, 'delete')
def test_delete_user_quota(self):
project_id = 'foo_project_id'
@ -303,3 +348,30 @@ class QuotaSetsControllerTest(test.TestCase):
REQ_MEMBER, self.project_id)
self.mock_policy_check.assert_called_once_with(
REQ_MEMBER.environ['manila.context'], self.resource_name, 'delete')
@ddt.data(
('os-quota-sets', '2.7', quota_sets.QuotaSetsControllerLegacy),
('quota-sets', '2.6', quota_sets.QuotaSetsController),
('quota-sets', '2.0', quota_sets.QuotaSetsController),
)
@ddt.unpack
def test_api_not_found(self, url, version, controller):
req = fakes.HTTPRequest.blank('/fooproject/%s' % url, version=version)
for method_name in ('show', 'defaults', 'delete'):
self.assertRaises(
exception.VersionNotFoundForAPIMethod,
getattr(controller(), method_name),
req, self.project_id)
@ddt.data(
('os-quota-sets', '2.7', quota_sets.QuotaSetsControllerLegacy),
('quota-sets', '2.6', quota_sets.QuotaSetsController),
('quota-sets', '2.0', quota_sets.QuotaSetsController),
)
@ddt.unpack
def test_update_api_not_found(self, url, version, controller):
req = fakes.HTTPRequest.blank('/fooproject/%s' % url, version=version)
self.assertRaises(
exception.VersionNotFoundForAPIMethod,
controller().update,
req, self.project_id)

View File

@ -17,10 +17,11 @@
import datetime
import ddt
import mock
from oslo_utils import timeutils
from manila.api.v1 import services
from manila.api.v2 import services
from manila import context
from manila import db
from manila import exception
@ -106,41 +107,6 @@ fake_response_service_list = {'services': [
]}
class FakeRequest(object):
environ = {"manila.context": context.get_admin_context()}
GET = {}
class FakeRequestWithBinary(object):
environ = {"manila.context": context.get_admin_context()}
GET = {"binary": "manila-share"}
class FakeRequestWithHost(object):
environ = {"manila.context": context.get_admin_context()}
GET = {"host": "host1"}
class FakeRequestWithZone(object):
environ = {"manila.context": context.get_admin_context()}
GET = {"zone": "manila1"}
class FakeRequestWithStatus(object):
environ = {"manila.context": context.get_admin_context()}
GET = {"status": "enabled"}
class FakeRequestWithState(object):
environ = {"manila.context": context.get_admin_context()}
GET = {"state": "up"}
class FakeRequestWithHostBinary(object):
environ = {"manila.context": context.get_admin_context()}
GET = {"host": "host1", "binary": "manila-share"}
def fake_service_get_all(context):
return fake_services_list
@ -172,10 +138,11 @@ def fake_utcnow():
return datetime.datetime(2012, 10, 29, 13, 42, 11)
@ddt.ddt
class ServicesTest(test.TestCase):
def setUp(self):
super(ServicesTest, self).setUp()
super(self.__class__, self).setUp()
self.mock_object(db, "service_get_all", fake_service_get_all)
self.mock_object(timeutils, "utcnow", fake_utcnow)
@ -184,22 +151,31 @@ class ServicesTest(test.TestCase):
self.mock_object(db, "service_update", fake_service_update)
self.context = context.get_admin_context()
self.controller = services.ServiceController()
self.controller_legacy = services.ServiceControllerLegacy()
self.resource_name = self.controller.resource_name
self.mock_policy_check = self.mock_object(
policy, 'check_policy', mock.Mock(return_value=True))
def tearDown(self):
super(ServicesTest, self).tearDown()
@ddt.data(
('os-services', '1.0', services.ServiceControllerLegacy),
('os-services', '2.6', services.ServiceControllerLegacy),
('services', '2.7', services.ServiceController),
)
@ddt.unpack
def test_services_list(self, url, version, controller):
req = fakes.HTTPRequest.blank('/%s' % url, version=version)
req.environ['manila.context'] = self.context
res_dict = controller().index(req)
def test_services_list(self):
req = FakeRequest()
res_dict = self.controller.index(req)
self.assertEqual(fake_response_service_list, res_dict)
self.mock_policy_check.assert_called_once_with(
req.environ['manila.context'], self.resource_name, 'index')
def test_services_list_with_host(self):
req = FakeRequestWithHost()
req = fakes.HTTPRequest.blank('/services?host=host1', version='2.7')
req.environ['manila.context'] = self.context
res_dict = self.controller.index(req)
response = {'services': [
@ -211,8 +187,12 @@ class ServicesTest(test.TestCase):
req.environ['manila.context'], self.resource_name, 'index')
def test_services_list_with_binary(self):
req = FakeRequestWithBinary()
req = fakes.HTTPRequest.blank(
'/services?binary=manila-share', version='2.7')
req.environ['manila.context'] = self.context
res_dict = self.controller.index(req)
response = {'services': [
fake_response_service_list['services'][1],
fake_response_service_list['services'][3],
@ -223,8 +203,11 @@ class ServicesTest(test.TestCase):
req.environ['manila.context'], self.resource_name, 'index')
def test_services_list_with_zone(self):
req = FakeRequestWithZone()
req = fakes.HTTPRequest.blank('/services?zone=manila1', version='2.7')
req.environ['manila.context'] = self.context
res_dict = self.controller.index(req)
response = {'services': [
fake_response_service_list['services'][0],
fake_response_service_list['services'][1],
@ -234,8 +217,12 @@ class ServicesTest(test.TestCase):
req.environ['manila.context'], self.resource_name, 'index')
def test_services_list_with_status(self):
req = FakeRequestWithStatus()
req = fakes.HTTPRequest.blank(
'/services?status=enabled', version='2.7')
req.environ['manila.context'] = self.context
res_dict = self.controller.index(req)
response = {'services': [
fake_response_service_list['services'][2],
]}
@ -244,8 +231,11 @@ class ServicesTest(test.TestCase):
req.environ['manila.context'], self.resource_name, 'index')
def test_services_list_with_state(self):
req = FakeRequestWithState()
req = fakes.HTTPRequest.blank('/services?state=up', version='2.7')
req.environ['manila.context'] = self.context
res_dict = self.controller.index(req)
response = {'services': [
fake_response_service_list['services'][0],
fake_response_service_list['services'][1],
@ -255,25 +245,77 @@ class ServicesTest(test.TestCase):
req.environ['manila.context'], self.resource_name, 'index')
def test_services_list_with_host_binary(self):
req = FakeRequestWithHostBinary()
req = fakes.HTTPRequest.blank(
"/services?binary=manila-share&state=up", version='2.7')
req.environ['manila.context'] = self.context
res_dict = self.controller.index(req)
response = {'services': [fake_response_service_list['services'][1], ]}
self.assertEqual(response, res_dict)
self.mock_policy_check.assert_called_once_with(
req.environ['manila.context'], self.resource_name, 'index')
def test_services_enable(self):
@ddt.data(
('os-services', '1.0', services.ServiceControllerLegacy),
('os-services', '2.6', services.ServiceControllerLegacy),
('services', '2.7', services.ServiceController),
)
@ddt.unpack
def test_services_enable(self, url, version, controller):
body = {'host': 'host1', 'binary': 'manila-share'}
req = fakes.HTTPRequest.blank('/v1/fake/os-services/enable')
res_dict = self.controller.update(req, "enable", body)
req = fakes.HTTPRequest.blank('/fooproject/%s' % url, version=version)
res_dict = controller().update(req, "enable", body)
self.assertFalse(res_dict['disabled'])
self.mock_policy_check.assert_called_once_with(
req.environ['manila.context'], self.resource_name, 'update')
def test_services_disable(self):
req = fakes.HTTPRequest.blank('/v1/fake/os-services/disable')
@ddt.data(
('os-services', '1.0', services.ServiceControllerLegacy),
('os-services', '2.6', services.ServiceControllerLegacy),
('services', '2.7', services.ServiceController),
)
@ddt.unpack
def test_services_disable(self, url, version, controller):
req = fakes.HTTPRequest.blank(
'/fooproject/%s/disable' % url, version=version)
body = {'host': 'host1', 'binary': 'manila-share'}
res_dict = self.controller.update(req, "disable", body)
res_dict = controller().update(req, "disable", body)
self.assertTrue(res_dict['disabled'])
self.mock_policy_check.assert_called_once_with(
req.environ['manila.context'], self.resource_name, 'update')
@ddt.data(
('os-services', '2.7', services.ServiceControllerLegacy),
('services', '2.6', services.ServiceController),
('services', '1.0', services.ServiceController),
)
@ddt.unpack
def test_services_update_legacy_url_2_dot_7_api_not_found(self, url,
version,
controller):
req = fakes.HTTPRequest.blank(
'/fooproject/%s/fake' % url, version=version)
body = {'host': 'host1', 'binary': 'manila-share'}
self.assertRaises(
exception.VersionNotFoundForAPIMethod,
controller().update,
req, "disable", body,
)
@ddt.data(
('os-services', '2.7', services.ServiceControllerLegacy),
('services', '2.6', services.ServiceController),
('services', '1.0', services.ServiceController),
)
@ddt.unpack
def test_services_list_api_not_found(self, url, version, controller):
req = fakes.HTTPRequest.blank('/fooproject/%s' % url, version=version)
self.assertRaises(
exception.VersionNotFoundForAPIMethod, controller().index, req)

View File

@ -21,7 +21,7 @@ from oslo_utils import timeutils
import six
import webob
from manila.api.v1 import share_types as types
from manila.api.v2 import share_types as types
from manila.api.views import types as views_types
from manila.common import constants
from manila import context
@ -217,7 +217,14 @@ class ShareTypesAPITest(test.TestCase):
policy.check_policy.assert_called_once_with(
req.environ['manila.context'], self.resource_name, 'default')
def test_view_builder_show(self):
@ddt.data(
('1.0', 'os-share-type-access'),
('2.0', 'os-share-type-access'),
('2.6', 'os-share-type-access'),
('2.7', 'share_type_access'),
)
@ddt.unpack
def test_view_builder_show(self, version, prefix):
view_builder = views_types.ViewBuilder()
now = timeutils.isotime()
@ -232,14 +239,16 @@ class ShareTypesAPITest(test.TestCase):
id=42,
)
request = fakes.HTTPRequest.blank("/v2")
request = fakes.HTTPRequest.blank("/v%s" % version[0], version=version)
request.headers['X-Openstack-Manila-Api-Version'] = version
output = view_builder.show(request, raw_share_type)
self.assertIn('share_type', output)
expected_share_type = {
'name': 'new_type',
'extra_specs': {},
'os-share-type-access:is_public': True,
'%s:is_public' % prefix: True,
'required_extra_specs': {},
'id': 42,
}
@ -444,7 +453,7 @@ class ShareTypeAccessTest(test.TestCase):
use_admin_context=True)
self.assertRaises(webob.exc.HTTPNotFound,
self.controller._list_project_access,
self.controller.share_type_access,
req, '1')
def test_list_type_access_private(self):
@ -453,7 +462,7 @@ class ShareTypeAccessTest(test.TestCase):
{'share_type_id': '2', 'project_id': PROJ3_UUID},
]}
result = self.controller._list_project_access(self.req, '2')
result = self.controller.share_type_access(self.req, '2')
self.assertEqual(expected, result)
@ -464,7 +473,7 @@ class ShareTypeAccessTest(test.TestCase):
raise exception.PolicyNotAuthorized(action='index')
self.assertRaises(webob.exc.HTTPForbidden,
self.controller._list_project_access,
self.controller.share_type_access,
req, 'fake')
def test_list_type_with_admin_default_proj1(self):

File diff suppressed because it is too large Load Diff

View File

@ -36,7 +36,7 @@ ShareGroup = [
help="The minimum api microversion is configured to be the "
"value of the minimum microversion supported by Manila."),
cfg.StrOpt("max_api_microversion",
default="2.6",
default="2.7",
help="The maximum api microversion is configured to be the "
"value of the latest microversion supported by Manila."),
cfg.StrOpt("region",

View File

@ -96,14 +96,20 @@ class SharesV2Client(shares_client.SharesClient):
return super(SharesV2Client, self).copy(url, headers=headers)
def reset_state(self, s_id, status="error", s_type="shares",
headers=None, version=LATEST_MICROVERSION):
headers=None, version=LATEST_MICROVERSION,
action_name=None):
"""Resets the state of a share, snapshot, cg, or a cgsnapshot.
status: available, error, creating, deleting, error_deleting
s_type: shares, share_instances, snapshots, consistency-groups,
cgsnapshots.
"""
body = {"os-reset_status": {"status": status}}
if action_name is None:
if float(version) > 2.6:
action_name = 'reset_status'
else:
action_name = 'os-reset_status'
body = {action_name: {"status": status}}
body = json.dumps(body)
resp, body = self.post("%s/%s/action" % (s_type, s_id), body,
headers=headers, extra_headers=True,
@ -112,12 +118,17 @@ class SharesV2Client(shares_client.SharesClient):
return body
def force_delete(self, s_id, s_type="shares", headers=None,
version=LATEST_MICROVERSION):
version=LATEST_MICROVERSION, action_name=None):
"""Force delete share or snapshot.
s_type: shares, snapshots
"""
body = {"os-force_delete": None}
if action_name is None:
if float(version) > 2.6:
action_name = 'force_delete'
else:
action_name = 'os-force_delete'
body = {action_name: None}
body = json.dumps(body)
resp, body = self.post("%s/%s/action" % (s_type, s_id), body,
headers=headers, extra_headers=True,
@ -276,6 +287,267 @@ class SharesV2Client(shares_client.SharesClient):
(instance_id, status, self.build_timeout))
raise exceptions.TimeoutException(message)
###############
def extend_share(self, share_id, new_size, version=LATEST_MICROVERSION,
action_name=None):
if action_name is None:
action_name = 'extend' if float(version) > 2.6 else 'os-extend'
post_body = {
action_name: {
"new_size": new_size,
}
}
body = json.dumps(post_body)
resp, body = self.post(
"shares/%s/action" % share_id, body, version=version)
self.expected_success(202, resp.status)
return body
def shrink_share(self, share_id, new_size, version=LATEST_MICROVERSION,
action_name=None):
if action_name is None:
action_name = 'shrink' if float(version) > 2.6 else 'os-shrnk'
post_body = {
action_name: {
"new_size": new_size,
}
}
body = json.dumps(post_body)
resp, body = self.post(
"shares/%s/action" % share_id, body, version=version)
self.expected_success(202, resp.status)
return body
###############
def manage_share(self, service_host, protocol, export_path,
share_type_id, name=None, description=None,
version=LATEST_MICROVERSION, url=None):
post_body = {
"share": {
"export_path": export_path,
"service_host": service_host,
"protocol": protocol,
"share_type": share_type_id,
"name": name,
"description": description,
}
}
if url is None:
if float(version) > 2.6:
url = 'shares/manage'
else:
url = 'os-share-manage'
body = json.dumps(post_body)
resp, body = self.post(url, body, version=version)
self.expected_success(200, resp.status)
return self._parse_resp(body)
def unmanage_share(self, share_id, version=LATEST_MICROVERSION, url=None,
action_name=None, body=None):
if url is None:
url = 'shares' if float(version) > 2.6 else 'os-share-unmanage'
if action_name is None:
action_name = 'action' if float(version) > 2.6 else 'unmanage'
if body is None and float(version) > 2.6:
body = json.dumps({'unmanage': {}})
resp, body = self.post(
"%(url)s/%(share_id)s/%(action_name)s" % {
'url': url, 'share_id': share_id, 'action_name': action_name},
body,
version=version)
self.expected_success(202, resp.status)
return body
###############
def _get_access_action_name(self, version):
if float(version) > 2.6:
return 'allow_access'
return 'os-allow_access'
def create_access_rule(self, share_id, access_type="ip",
access_to="0.0.0.0", access_level=None,
version=LATEST_MICROVERSION, action_name=None):
post_body = {
self._get_access_action_name(version): {
"access_type": access_type,
"access_to": access_to,
"access_level": access_level,
}
}
body = json.dumps(post_body)
resp, body = self.post(
"shares/%s/action" % share_id, body, version=version)
self.expected_success(200, resp.status)
return self._parse_resp(body)
def list_access_rules(self, share_id, version=LATEST_MICROVERSION,
action_name=None):
body = {self._get_access_action_name(version): None}
resp, body = self.post(
"shares/%s/action" % share_id, json.dumps(body), version=version)
self.expected_success(200, resp.status)
return self._parse_resp(body)
def delete_access_rule(self, share_id, rule_id,
version=LATEST_MICROVERSION, action_name=None):
post_body = {
self._get_access_action_name(version): {
"access_id": rule_id,
}
}
body = json.dumps(post_body)
resp, body = self.post(
"shares/%s/action" % share_id, body, version=version)
self.expected_success(202, resp.status)
return body
###############
def list_availability_zones(self, url='availability-zones',
version=LATEST_MICROVERSION):
"""Get list of availability zones."""
if url is None:
if float(version) > 2.6:
url = 'availability-zones'
else:
url = 'os-availability-zone'
resp, body = self.get(url, version=version)
self.expected_success(200, resp.status)
return self._parse_resp(body)
###############
def list_services(self, params=None, url=None,
version=LATEST_MICROVERSION):
"""List services."""
if url is None:
url = 'services' if float(version) > 2.6 else 'os-services'
if params:
url += '?%s' % urllib.urlencode(params)
resp, body = self.get(url, version=version)
self.expected_success(200, resp.status)
return self._parse_resp(body)
###############
def list_share_types(self, params=None, version=LATEST_MICROVERSION):
uri = 'types'
if params is not None:
uri += '?%s' % urllib.urlencode(params)
resp, body = self.get(uri, version=version)
self.expected_success(200, resp.status)
return self._parse_resp(body)
def create_share_type(self, name, is_public=True,
version=LATEST_MICROVERSION, **kwargs):
if float(version) > 2.6:
is_public_keyname = 'share_type_access:is_public'
else:
is_public_keyname = 'os-share-type-access:is_public'
post_body = {
'name': name,
'extra_specs': kwargs.get('extra_specs'),
is_public_keyname: is_public,
}
post_body = json.dumps({'share_type': post_body})
resp, body = self.post('types', post_body, version=version)
self.expected_success(200, resp.status)
return self._parse_resp(body)
def delete_share_type(self, share_type_id, version=LATEST_MICROVERSION):
resp, body = self.delete("types/%s" % share_type_id, version=version)
self.expected_success(202, resp.status)
return body
def get_share_type(self, share_type_id, version=LATEST_MICROVERSION):
resp, body = self.get("types/%s" % share_type_id, version=version)
self.expected_success(200, resp.status)
return self._parse_resp(body)
def list_access_to_share_type(self, share_type_id,
version=LATEST_MICROVERSION,
action_name=None):
if action_name is None:
if float(version) > 2.6:
action_name = 'share_type_access'
else:
action_name = 'os-share-type-access'
url = 'types/%(st_id)s/%(action_name)s' % {
'st_id': share_type_id, 'action_name': action_name}
resp, body = self.get(url, version=version)
# [{"share_type_id": "%st_id%", "project_id": "%project_id%"}, ]
self.expected_success(200, resp.status)
return self._parse_resp(body)
###############
def _get_quotas_url(self, version):
if float(version) > 2.6:
return 'quota-sets'
return 'os-quota-sets'
def default_quotas(self, tenant_id, url=None, version=LATEST_MICROVERSION):
if url is None:
url = self._get_quotas_url(version)
url += '/%s' % tenant_id
resp, body = self.get("%s/defaults" % url, version=version)
self.expected_success(200, resp.status)
return self._parse_resp(body)
def show_quotas(self, tenant_id, user_id=None, url=None,
version=LATEST_MICROVERSION):
if url is None:
url = self._get_quotas_url(version)
url += '/%s' % tenant_id
if user_id is not None:
url += "?user_id=%s" % user_id
resp, body = self.get(url, version=version)
self.expected_success(200, resp.status)
return self._parse_resp(body)
def reset_quotas(self, tenant_id, user_id=None, url=None,
version=LATEST_MICROVERSION):
if url is None:
url = self._get_quotas_url(version)
url += '/%s' % tenant_id
if user_id is not None:
url += "?user_id=%s" % user_id
resp, body = self.delete(url, version=version)
self.expected_success(202, resp.status)
return body
def update_quotas(self, tenant_id, user_id=None, shares=None,
snapshots=None, gigabytes=None, snapshot_gigabytes=None,
share_networks=None, force=True, url=None,
version=LATEST_MICROVERSION):
if url is None:
url = self._get_quotas_url(version)
url += '/%s' % tenant_id
if user_id is not None:
url += "?user_id=%s" % user_id
put_body = {"tenant_id": tenant_id}
if force:
put_body["force"] = "true"
if shares is not None:
put_body["shares"] = shares
if snapshots is not None:
put_body["snapshots"] = snapshots
if gigabytes is not None:
put_body["gigabytes"] = gigabytes
if snapshot_gigabytes is not None:
put_body["snapshot_gigabytes"] = snapshot_gigabytes
if share_networks is not None:
put_body["share_networks"] = share_networks
put_body = json.dumps({"quota_set": put_body})
resp, body = self.put(url, put_body, version=version)
self.expected_success(200, resp.status)
return self._parse_resp(body)
###############
def create_consistency_group(self, name=None, description=None,
@ -481,9 +753,15 @@ class SharesV2Client(shares_client.SharesClient):
###############
def migrate_share(self, share_id, host, version=LATEST_MICROVERSION):
def migrate_share(self, share_id, host, version=LATEST_MICROVERSION,
action_name=None):
if action_name is None:
if float(version) > 2.6:
action_name = 'migrate_share'
else:
action_name = 'os-migrate_share'
post_body = {
'os-migrate_share': {
action_name: {
'host': host,
}
}

View File

@ -28,12 +28,12 @@ class SharesAdminQuotasTest(base.BaseSharesAdminTest):
def resource_setup(cls):
cls.os = clients.AdminManager()
super(SharesAdminQuotasTest, cls).resource_setup()
cls.user_id = cls.shares_client.user_id
cls.tenant_id = cls.shares_client.tenant_id
cls.user_id = cls.shares_v2_client.user_id
cls.tenant_id = cls.shares_v2_client.tenant_id
@test.attr(type=["gate", "smoke", ])
def test_default_quotas(self):
quotas = self.shares_client.default_quotas(self.tenant_id)
quotas = self.shares_v2_client.default_quotas(self.tenant_id)
self.assertGreater(int(quotas["gigabytes"]), -2)
self.assertGreater(int(quotas["snapshot_gigabytes"]), -2)
self.assertGreater(int(quotas["shares"]), -2)
@ -42,7 +42,7 @@ class SharesAdminQuotasTest(base.BaseSharesAdminTest):
@test.attr(type=["gate", "smoke", ])
def test_show_quotas(self):
quotas = self.shares_client.show_quotas(self.tenant_id)
quotas = self.shares_v2_client.show_quotas(self.tenant_id)
self.assertGreater(int(quotas["gigabytes"]), -2)
self.assertGreater(int(quotas["snapshot_gigabytes"]), -2)
self.assertGreater(int(quotas["shares"]), -2)
@ -51,7 +51,8 @@ class SharesAdminQuotasTest(base.BaseSharesAdminTest):
@test.attr(type=["gate", "smoke", ])
def test_show_quotas_for_user(self):
quotas = self.shares_client.show_quotas(self.tenant_id, self.user_id)
quotas = self.shares_v2_client.show_quotas(
self.tenant_id, self.user_id)
self.assertGreater(int(quotas["gigabytes"]), -2)
self.assertGreater(int(quotas["snapshot_gigabytes"]), -2)
self.assertGreater(int(quotas["shares"]), -2)
@ -62,159 +63,145 @@ class SharesAdminQuotasTest(base.BaseSharesAdminTest):
class SharesAdminQuotasUpdateTest(base.BaseSharesAdminTest):
force_tenant_isolation = True
client_version = '2'
def setUp(self):
super(self.__class__, self).setUp()
self.client = self.get_client_with_isolated_creds(
client_version=self.client_version)
self.tenant_id = self.client.tenant_id
self.user_id = self.client.user_id
@test.attr(type=["gate", "smoke", ])
def test_update_tenant_quota_shares(self):
client = self.get_client_with_isolated_creds()
# get current quotas
quotas = client.show_quotas(client.tenant_id)
quotas = self.client.show_quotas(self.tenant_id)
new_quota = int(quotas["shares"]) + 2
# set new quota for shares
updated = client.update_quotas(client.tenant_id, shares=new_quota)
updated = self.client.update_quotas(self.tenant_id, shares=new_quota)
self.assertEqual(int(updated["shares"]), new_quota)
@test.attr(type=["gate", "smoke", ])
def test_update_user_quota_shares(self):
client = self.get_client_with_isolated_creds()
# get current quotas
quotas = client.show_quotas(client.tenant_id, client.user_id)
quotas = self.client.show_quotas(self.tenant_id, self.user_id)
new_quota = int(quotas["shares"]) - 1
# set new quota for shares
updated = client.update_quotas(
client.tenant_id, client.user_id, shares=new_quota)
updated = self.client.update_quotas(
self.tenant_id, self.user_id, shares=new_quota)
self.assertEqual(int(updated["shares"]), new_quota)
@test.attr(type=["gate", "smoke", ])
def test_update_tenant_quota_snapshots(self):
client = self.get_client_with_isolated_creds()
# get current quotas
quotas = client.show_quotas(client.tenant_id)
quotas = self.client.show_quotas(self.tenant_id)
new_quota = int(quotas["snapshots"]) + 2
# set new quota for snapshots
updated = client.update_quotas(client.tenant_id, snapshots=new_quota)
updated = self.client.update_quotas(
self.tenant_id, snapshots=new_quota)
self.assertEqual(int(updated["snapshots"]), new_quota)
@test.attr(type=["gate", "smoke", ])
def test_update_user_quota_snapshots(self):
client = self.get_client_with_isolated_creds()
# get current quotas
quotas = client.show_quotas(client.tenant_id, client.user_id)
quotas = self.client.show_quotas(self.tenant_id, self.user_id)
new_quota = int(quotas["snapshots"]) - 1
# set new quota for snapshots
updated = client.update_quotas(
client.tenant_id, client.user_id, snapshots=new_quota)
updated = self.client.update_quotas(
self.tenant_id, self.user_id, snapshots=new_quota)
self.assertEqual(int(updated["snapshots"]), new_quota)
@test.attr(type=["gate", "smoke", ])
def test_update_tenant_quota_gigabytes(self):
client = self.get_client_with_isolated_creds()
# get current quotas
custom = client.show_quotas(client.tenant_id)
custom = self.client.show_quotas(self.tenant_id)
# make quotas for update
gigabytes = int(custom["gigabytes"]) + 2
# set new quota for shares
updated = client.update_quotas(
client.tenant_id, gigabytes=gigabytes)
updated = self.client.update_quotas(
self.tenant_id, gigabytes=gigabytes)
self.assertEqual(int(updated["gigabytes"]), gigabytes)
@test.attr(type=["gate", "smoke", ])
def test_update_tenant_quota_snapshot_gigabytes(self):
client = self.get_client_with_isolated_creds()
# get current quotas
custom = client.show_quotas(client.tenant_id)
custom = self.client.show_quotas(self.tenant_id)
# make quotas for update
snapshot_gigabytes = int(custom["snapshot_gigabytes"]) + 2
# set new quota for shares
updated = client.update_quotas(
client.tenant_id,
updated = self.client.update_quotas(
self.tenant_id,
snapshot_gigabytes=snapshot_gigabytes)
self.assertEqual(
int(updated["snapshot_gigabytes"]), snapshot_gigabytes)
@test.attr(type=["gate", "smoke", ])
def test_update_user_quota_gigabytes(self):
client = self.get_client_with_isolated_creds()
# get current quotas
custom = client.show_quotas(client.tenant_id, client.user_id)
custom = self.client.show_quotas(self.tenant_id, self.user_id)
# make quotas for update
gigabytes = int(custom["gigabytes"]) - 1
# set new quota for shares
updated = client.update_quotas(
client.tenant_id, client.user_id,
gigabytes=gigabytes)
updated = self.client.update_quotas(
self.tenant_id, self.user_id, gigabytes=gigabytes)
self.assertEqual(int(updated["gigabytes"]), gigabytes)
@test.attr(type=["gate", "smoke", ])
def test_update_user_quota_snapshot_gigabytes(self):
client = self.get_client_with_isolated_creds()
# get current quotas
custom = client.show_quotas(client.tenant_id, client.user_id)
custom = self.client.show_quotas(self.tenant_id, self.user_id)
# make quotas for update
snapshot_gigabytes = int(custom["snapshot_gigabytes"]) - 1
# set new quota for shares
updated = client.update_quotas(
client.tenant_id, client.user_id,
updated = self.client.update_quotas(
self.tenant_id, self.user_id,
snapshot_gigabytes=snapshot_gigabytes)
self.assertEqual(
int(updated["snapshot_gigabytes"]), snapshot_gigabytes)
@test.attr(type=["gate", "smoke", ])
def test_update_tenant_quota_share_networks(self):
client = self.get_client_with_isolated_creds()
# get current quotas
quotas = client.show_quotas(client.tenant_id)
quotas = self.client.show_quotas(self.tenant_id)
new_quota = int(quotas["share_networks"]) + 2
# set new quota for share-networks
updated = client.update_quotas(
client.tenant_id, share_networks=new_quota)
updated = self.client.update_quotas(
self.tenant_id, share_networks=new_quota)
self.assertEqual(int(updated["share_networks"]), new_quota)
@test.attr(type=["gate", "smoke", ])
def test_update_user_quota_share_networks(self):
client = self.get_client_with_isolated_creds()
# get current quotas
quotas = client.show_quotas(
client.tenant_id, client.user_id)
quotas = self.client.show_quotas(
self.tenant_id, self.user_id)
new_quota = int(quotas["share_networks"]) - 1
# set new quota for share-networks
updated = client.update_quotas(
client.tenant_id, client.user_id,
updated = self.client.update_quotas(
self.tenant_id, self.user_id,
share_networks=new_quota)
self.assertEqual(int(updated["share_networks"]), new_quota)
@test.attr(type=["gate", "smoke", ])
def test_reset_tenant_quotas(self):
client = self.get_client_with_isolated_creds()
# get default_quotas
default = client.default_quotas(client.tenant_id)
default = self.client.default_quotas(self.tenant_id)
# get current quotas
custom = client.show_quotas(client.tenant_id)
custom = self.client.show_quotas(self.tenant_id)
# make quotas for update
shares = int(custom["shares"]) + 2
@ -224,8 +211,8 @@ class SharesAdminQuotasUpdateTest(base.BaseSharesAdminTest):
share_networks = int(custom["share_networks"]) + 2
# set new quota
updated = client.update_quotas(
client.tenant_id,
updated = self.client.update_quotas(
self.tenant_id,
shares=shares,
snapshots=snapshots,
gigabytes=gigabytes,
@ -239,10 +226,10 @@ class SharesAdminQuotasUpdateTest(base.BaseSharesAdminTest):
self.assertEqual(int(updated["share_networks"]), share_networks)
# reset customized quotas
client.reset_quotas(client.tenant_id)
self.client.reset_quotas(self.tenant_id)
# verify quotas
reseted = client.show_quotas(client.tenant_id)
reseted = self.client.show_quotas(self.tenant_id)
self.assertEqual(int(reseted["shares"]), int(default["shares"]))
self.assertEqual(int(reseted["snapshots"]), int(default["snapshots"]))
self.assertEqual(int(reseted["gigabytes"]), int(default["gigabytes"]))
@ -251,101 +238,86 @@ class SharesAdminQuotasUpdateTest(base.BaseSharesAdminTest):
@test.attr(type=["gate", "smoke", ])
def test_unlimited_quota_for_shares(self):
client = self.get_client_with_isolated_creds()
client.update_quotas(client.tenant_id, shares=-1)
self.client.update_quotas(self.tenant_id, shares=-1)
quotas = client.show_quotas(client.tenant_id)
quotas = self.client.show_quotas(self.tenant_id)
self.assertEqual(-1, quotas.get('shares'))
@test.attr(type=["gate", "smoke", ])
def test_unlimited_user_quota_for_shares(self):
client = self.get_client_with_isolated_creds()
client.update_quotas(
client.tenant_id, client.user_id,
shares=-1)
self.client.update_quotas(
self.tenant_id, self.user_id, shares=-1)
quotas = client.show_quotas(client.tenant_id, client.user_id)
quotas = self.client.show_quotas(self.tenant_id, self.user_id)
self.assertEqual(-1, quotas.get('shares'))
@test.attr(type=["gate", "smoke", ])
def test_unlimited_quota_for_snapshots(self):
client = self.get_client_with_isolated_creds()
client.update_quotas(client.tenant_id, snapshots=-1)
self.client.update_quotas(self.tenant_id, snapshots=-1)
quotas = client.show_quotas(client.tenant_id)
quotas = self.client.show_quotas(self.tenant_id)
self.assertEqual(-1, quotas.get('snapshots'))
@test.attr(type=["gate", "smoke", ])
def test_unlimited_user_quota_for_snapshots(self):
client = self.get_client_with_isolated_creds()
client.update_quotas(
client.tenant_id, client.user_id,
snapshots=-1)
self.client.update_quotas(
self.tenant_id, self.user_id, snapshots=-1)
quotas = client.show_quotas(client.tenant_id, client.user_id)
quotas = self.client.show_quotas(self.tenant_id, self.user_id)
self.assertEqual(-1, quotas.get('snapshots'))
@test.attr(type=["gate", "smoke", ])
def test_unlimited_quota_for_gigabytes(self):
client = self.get_client_with_isolated_creds()
client.update_quotas(client.tenant_id, gigabytes=-1)
self.client.update_quotas(self.tenant_id, gigabytes=-1)
quotas = client.show_quotas(client.tenant_id)
quotas = self.client.show_quotas(self.tenant_id)
self.assertEqual(-1, quotas.get('gigabytes'))
@test.attr(type=["gate", "smoke", ])
def test_unlimited_quota_for_snapshot_gigabytes(self):
client = self.get_client_with_isolated_creds()
client.update_quotas(
client.tenant_id, snapshot_gigabytes=-1)
self.client.update_quotas(
self.tenant_id, snapshot_gigabytes=-1)
quotas = client.show_quotas(client.tenant_id)
quotas = self.client.show_quotas(self.tenant_id)
self.assertEqual(-1, quotas.get('snapshot_gigabytes'))
@test.attr(type=["gate", "smoke", ])
def test_unlimited_user_quota_for_gigabytes(self):
client = self.get_client_with_isolated_creds()
client.update_quotas(
client.tenant_id, client.user_id,
gigabytes=-1)
self.client.update_quotas(
self.tenant_id, self.user_id, gigabytes=-1)
quotas = client.show_quotas(client.tenant_id, client.user_id)
quotas = self.client.show_quotas(self.tenant_id, self.user_id)
self.assertEqual(-1, quotas.get('gigabytes'))
@test.attr(type=["gate", "smoke", ])
def test_unlimited_user_quota_for_snapshot_gigabytes(self):
client = self.get_client_with_isolated_creds()
client.update_quotas(
client.tenant_id, client.user_id,
snapshot_gigabytes=-1)
self.client.update_quotas(
self.tenant_id, self.user_id, snapshot_gigabytes=-1)
quotas = client.show_quotas(client.tenant_id, client.user_id)
quotas = self.client.show_quotas(self.tenant_id, self.user_id)
self.assertEqual(-1, quotas.get('snapshot_gigabytes'))
@test.attr(type=["gate", "smoke", ])
def test_unlimited_quota_for_share_networks(self):
client = self.get_client_with_isolated_creds()
client.update_quotas(client.tenant_id, share_networks=-1)
self.client.update_quotas(self.tenant_id, share_networks=-1)
quotas = client.show_quotas(client.tenant_id)
quotas = self.client.show_quotas(self.tenant_id)
self.assertEqual(-1, quotas.get('share_networks'))
@test.attr(type=["gate", "smoke", ])
def test_unlimited_user_quota_for_share_networks(self):
client = self.get_client_with_isolated_creds()
client.update_quotas(
client.tenant_id, client.user_id,
share_networks=-1)
self.client.update_quotas(
self.tenant_id, self.user_id, share_networks=-1)
quotas = client.show_quotas(client.tenant_id, client.user_id)
quotas = self.client.show_quotas(self.tenant_id, self.user_id)
self.assertEqual(-1, quotas.get('share_networks'))

View File

@ -13,11 +13,13 @@
# License for the specific language governing permissions and limitations
# under the License.
from tempest import test # noqa
import ddt
from tempest import test
from manila_tempest_tests.tests.api import base
@ddt.ddt
class ServicesAdminTest(base.BaseSharesAdminTest):
def setUp(self):
@ -25,60 +27,67 @@ class ServicesAdminTest(base.BaseSharesAdminTest):
self.services = self.shares_client.list_services()
@test.attr(type=["gate", "smoke", ])
def test_list_services(self):
services = self.shares_client.list_services()
@ddt.data('shares_client', 'shares_v2_client')
def test_list_services(self, client_name):
services = getattr(self, client_name).list_services()
self.assertNotEqual(0, len(services))
for service in services:
self.assertIsNotNone(service['id'])
@test.attr(type=["gate", "smoke", ])
def test_get_services_by_host_name(self):
@ddt.data('shares_client', 'shares_v2_client')
def test_get_services_by_host_name(self, client_name):
host = self.services[0]["host"]
params = {"host": host}
services = self.shares_client.list_services(params)
services = getattr(self, client_name).list_services(params)
self.assertNotEqual(0, len(services))
for service in services:
self.assertEqual(host, service["host"])
@test.attr(type=["gate", "smoke", ])
def test_get_services_by_binary_name(self):
@ddt.data('shares_client', 'shares_v2_client')
def test_get_services_by_binary_name(self, client_name):
binary = self.services[0]["binary"]
params = {"binary": binary, }
services = self.shares_client.list_services(params)
services = getattr(self, client_name).list_services(params)
self.assertNotEqual(0, len(services))
for service in services:
self.assertEqual(binary, service["binary"])
@test.attr(type=["gate", "smoke", ])
def test_get_services_by_availability_zone(self):
@ddt.data('shares_client', 'shares_v2_client')
def test_get_services_by_availability_zone(self, client_name):
zone = self.services[0]["zone"]
params = {"zone": zone, }
services = self.shares_client.list_services(params)
services = getattr(self, client_name).list_services(params)
self.assertNotEqual(0, len(services))
for service in services:
self.assertEqual(zone, service["zone"])
@test.attr(type=["gate", "smoke", ])
def test_get_services_by_status(self):
@ddt.data('shares_client', 'shares_v2_client')
def test_get_services_by_status(self, client_name):
status = self.services[0]["status"]
params = {"status": status, }
services = self.shares_client.list_services(params)
services = getattr(self, client_name).list_services(params)
self.assertNotEqual(0, len(services))
for service in services:
self.assertEqual(status, service["status"])
@test.attr(type=["gate", "smoke", ])
def test_get_services_by_state(self):
@ddt.data('shares_client', 'shares_v2_client')
def test_get_services_by_state(self, client_name):
state = self.services[0]["state"]
params = {"state": state, }
services = self.shares_client.list_services(params)
services = getattr(self, client_name).list_services(params)
self.assertNotEqual(0, len(services))
for service in services:
self.assertEqual(state, service["state"])
@test.attr(type=["gate", "smoke", ])
def test_get_services_by_all_filters(self):
@ddt.data('shares_client', 'shares_v2_client')
def test_get_services_by_all_filters(self, client_name):
params = {
"host": self.services[0]["host"],
"binary": self.services[0]["binary"],
@ -86,7 +95,7 @@ class ServicesAdminTest(base.BaseSharesAdminTest):
"status": self.services[0]["status"],
"state": self.services[0]["state"],
}
services = self.shares_client.list_services(params)
services = getattr(self, client_name).list_services(params)
self.assertNotEqual(0, len(services))
for service in services:
self.assertEqual(params["host"], service["host"])

View File

@ -13,13 +13,15 @@
# License for the specific language governing permissions and limitations
# under the License.
from tempest import test # noqa
from tempest_lib import exceptions as lib_exc # noqa
import ddt
from tempest import test
from tempest_lib import exceptions as lib_exc
from manila_tempest_tests import clients_share as clients
from manila_tempest_tests.tests.api import base
@ddt.ddt
class ServicesAdminNegativeTest(base.BaseSharesAdminTest):
@classmethod
@ -76,3 +78,18 @@ class ServicesAdminNegativeTest(base.BaseSharesAdminTest):
params = {'state': 'fake_state'}
services_fake = self.shares_client.list_services(params)
self.assertEqual(0, len(services_fake))
@test.attr(type=["gate", "smoke", "negative", ])
@ddt.data(
('os-services', '2.7'),
('services', '2.6'),
('services', '2.0'),
)
@ddt.unpack
@base.skip_if_microversion_not_supported("2.7")
def test_list_services_with_wrong_versions(self, url, version):
self.assertRaises(
lib_exc.NotFound,
self.shares_v2_client.list_services,
version=version, url=url,
)

View File

@ -79,14 +79,13 @@ class ManageNFSShareTest(base.BaseSharesAdminTest):
cls.shares = cls.create_shares([creation_data, creation_data])
# Load all share data (host, etc.)
cls.share1 = cls.shares_client.get_share(cls.shares[0]['id'])
cls.share2 = cls.shares_client.get_share(cls.shares[1]['id'])
cls.share1 = cls.shares_v2_client.get_share(cls.shares[0]['id'])
cls.share2 = cls.shares_v2_client.get_share(cls.shares[1]['id'])
# Unmanage shares from manila
cls.shares_client.unmanage_share(cls.share1['id'])
cls.shares_client.wait_for_resource_deletion(share_id=cls.share1['id'])
cls.shares_client.unmanage_share(cls.share2['id'])
cls.shares_client.wait_for_resource_deletion(share_id=cls.share2['id'])
for share_id in (cls.share1['id'], cls.share2['id']):
cls.shares_v2_client.unmanage_share(share_id)
cls.shares_v2_client.wait_for_resource_deletion(share_id=share_id)
@test.attr(type=["gate", "smoke"])
def test_manage(self):
@ -94,7 +93,7 @@ class ManageNFSShareTest(base.BaseSharesAdminTest):
description = "Description for 'managed' share"
# Manage share
share = self.shares_client.manage_share(
share = self.shares_v2_client.manage_share(
service_host=self.share1['host'],
export_path=self.share1['export_locations'][0],
protocol=self.share1['share_proto'],
@ -109,7 +108,7 @@ class ManageNFSShareTest(base.BaseSharesAdminTest):
'client': self.shares_client})
# Wait for success
self.shares_client.wait_for_share_status(share['id'], 'available')
self.shares_v2_client.wait_for_share_status(share['id'], 'available')
# Verify data of managed share
get = self.shares_v2_client.get_share(share['id'], version="2.5")
@ -123,10 +122,10 @@ class ManageNFSShareTest(base.BaseSharesAdminTest):
self.assertEqual(self.st['share_type']['id'], get['share_type'])
# Delete share
self.shares_client.delete_share(share['id'])
self.shares_client.wait_for_resource_deletion(share_id=share['id'])
self.shares_v2_client.delete_share(share['id'])
self.shares_v2_client.wait_for_resource_deletion(share_id=share['id'])
self.assertRaises(lib_exc.NotFound,
self.shares_client.get_share,
self.shares_v2_client.get_share,
share['id'])
@test.attr(type=["gate", "smoke"])
@ -137,7 +136,7 @@ class ManageNFSShareTest(base.BaseSharesAdminTest):
(self.st['share_type']['id'], 'available')]
for share_type_id, status in parameters:
share = self.shares_client.manage_share(
share = self.shares_v2_client.manage_share(
service_host=self.share2['host'],
export_path=self.share2['export_locations'][0],
protocol=self.share2['share_proto'],
@ -146,16 +145,16 @@ class ManageNFSShareTest(base.BaseSharesAdminTest):
# Add managed share to cleanup queue
self.method_resources.insert(
0, {'type': 'share', 'id': share['id'],
'client': self.shares_client})
'client': self.shares_v2_client})
# Wait for success
self.shares_client.wait_for_share_status(share['id'], status)
self.shares_v2_client.wait_for_share_status(share['id'], status)
# Delete share
self.shares_client.delete_share(share['id'])
self.shares_client.wait_for_resource_deletion(share_id=share['id'])
self.shares_v2_client.delete_share(share['id'])
self.shares_v2_client.wait_for_resource_deletion(share_id=share['id'])
self.assertRaises(lib_exc.NotFound,
self.shares_client.get_share,
self.shares_v2_client.get_share,
share['id'])

View File

@ -13,6 +13,7 @@
# License for the specific language governing permissions and limitations
# under the License.
import ddt
from tempest import config # noqa
from tempest import test # noqa
from tempest_lib.common.utils import data_utils # noqa
@ -24,6 +25,7 @@ from manila_tempest_tests.tests.api import base
CONF = config.CONF
@ddt.ddt
class ShareTypesAdminTest(base.BaseSharesAdminTest):
@test.attr(type=["gate", "smoke", ])
@ -32,53 +34,76 @@ class ShareTypesAdminTest(base.BaseSharesAdminTest):
extra_specs = self.add_required_extra_specs_to_dict()
# Create share type
st_create = self.shares_client.create_share_type(
st_create = self.shares_v2_client.create_share_type(
name, extra_specs=extra_specs)
self.assertEqual(name, st_create['share_type']['name'])
st_id = st_create['share_type']['id']
# Delete share type
self.shares_client.delete_share_type(st_id)
self.shares_v2_client.delete_share_type(st_id)
# Verify deletion of share type
self.shares_client.wait_for_resource_deletion(st_id=st_id)
self.shares_v2_client.wait_for_resource_deletion(st_id=st_id)
self.assertRaises(lib_exc.NotFound,
self.shares_client.get_share_type,
self.shares_v2_client.get_share_type,
st_id)
def _verify_is_public_key_name(self, share_type, version):
old_key_name = 'os-share-type-access:is_public'
new_key_name = 'share_type_access:is_public'
if float(version) > 2.6:
self.assertIn(new_key_name, share_type)
self.assertNotIn(old_key_name, share_type)
else:
self.assertIn(old_key_name, share_type)
self.assertNotIn(new_key_name, share_type)
@test.attr(type=["gate", "smoke", ])
def test_share_type_create_get(self):
@ddt.data('2.0', '2.6', '2.7')
def test_share_type_create_get(self, version):
self.skip_if_microversion_not_supported(version)
name = data_utils.rand_name("tempest-manila")
extra_specs = self.add_required_extra_specs_to_dict({"key": "value", })
# Create share type
st_create = self.create_share_type(name, extra_specs=extra_specs)
st_create = self.create_share_type(
name, extra_specs=extra_specs, version=version)
self.assertEqual(name, st_create['share_type']['name'])
self._verify_is_public_key_name(st_create['share_type'], version)
st_id = st_create["share_type"]["id"]
# Get share type
get = self.shares_client.get_share_type(st_id)
get = self.shares_v2_client.get_share_type(st_id, version=version)
self.assertEqual(name, get["share_type"]["name"])
self.assertEqual(st_id, get["share_type"]["id"])
self.assertEqual(extra_specs, get["share_type"]["extra_specs"])
self._verify_is_public_key_name(get['share_type'], version)
# Check that backwards compatibility didn't break
self.assertDictMatch(get["volume_type"], get["share_type"])
@test.attr(type=["gate", "smoke", ])
def test_share_type_create_list(self):
@ddt.data('2.0', '2.6', '2.7')
def test_share_type_create_list(self, version):
self.skip_if_microversion_not_supported(version)
name = data_utils.rand_name("tempest-manila")
extra_specs = self.add_required_extra_specs_to_dict()
# Create share type
st_create = self.create_share_type(name, extra_specs=extra_specs)
st_create = self.create_share_type(
name, extra_specs=extra_specs, version=version)
self._verify_is_public_key_name(st_create['share_type'], version)
st_id = st_create["share_type"]["id"]
# list share types
st_list = self.shares_client.list_share_types()
st_list = self.shares_v2_client.list_share_types(version=version)
sts = st_list["share_types"]
self.assertTrue(len(sts) >= 1)
self.assertTrue(any(st_id in st["id"] for st in sts))
for st in sts:
self._verify_is_public_key_name(st, version)
# Check that backwards compatibility didn't break
vts = st_list["volume_types"]
@ -128,16 +153,16 @@ class ShareTypesAdminTest(base.BaseSharesAdminTest):
st_id = st_create["share_type"]["id"]
# It should not be listed without access
st_list = self.shares_client.list_share_types()
st_list = self.shares_v2_client.list_share_types()
sts = st_list["share_types"]
self.assertFalse(any(st_id in st["id"] for st in sts))
# List projects that have access for share type - none expected
access = self.shares_client.list_access_to_share_type(st_id)
access = self.shares_v2_client.list_access_to_share_type(st_id)
self.assertEqual([], access)
# Add project access to share type
access = self.shares_client.add_access_to_share_type(
access = self.shares_v2_client.add_access_to_share_type(
st_id, project_id)
# Now it should be listed
@ -146,12 +171,12 @@ class ShareTypesAdminTest(base.BaseSharesAdminTest):
self.assertTrue(any(st_id in st["id"] for st in sts))
# List projects that have access for share type - one expected
access = self.shares_client.list_access_to_share_type(st_id)
access = self.shares_v2_client.list_access_to_share_type(st_id)
expected = [{'share_type_id': st_id, 'project_id': project_id}, ]
self.assertEqual(expected, access)
# Remove project access from share type
access = self.shares_client.remove_access_from_share_type(
access = self.shares_v2_client.remove_access_from_share_type(
st_id, project_id)
# It should not be listed without access
@ -160,5 +185,5 @@ class ShareTypesAdminTest(base.BaseSharesAdminTest):
self.assertFalse(any(st_id in st["id"] for st in sts))
# List projects that have access for share type - none expected
access = self.shares_client.list_access_to_share_type(st_id)
access = self.shares_v2_client.list_access_to_share_type(st_id)
self.assertEqual([], access)

View File

@ -26,6 +26,7 @@ from tempest import config
from tempest import test
from tempest_lib.common.utils import data_utils
from tempest_lib import exceptions
import testtools
from manila_tempest_tests import clients_share as clients
from manila_tempest_tests import share_exceptions
@ -89,6 +90,21 @@ def network_synchronized(f):
return wrapped_func
def is_microversion_supported(microversion):
if (float(microversion) > float(CONF.share.max_api_microversion) or
float(microversion) < float(CONF.share.min_api_microversion)):
return False
return True
def skip_if_microversion_not_supported(microversion):
"""Decorator for tests that are microversion-specific."""
if not is_microversion_supported(microversion):
reason = ("Skipped. Test requires microversion '%s'." % microversion)
return testtools.skip(reason)
return lambda f: f
class BaseSharesTest(test.BaseTestCase):
"""Base test case class for all Manila API tests."""
@ -107,6 +123,11 @@ class BaseSharesTest(test.BaseTestCase):
# Will be cleaned up in tearDown method
method_isolated_creds = []
def skip_if_microversion_not_supported(self, microversion):
if not is_microversion_supported(microversion):
raise self.skipException(
"Microversion '%s' is not supported." % microversion)
@classmethod
def get_client_with_isolated_creds(cls,
name=None,
@ -525,7 +546,7 @@ class BaseSharesTest(test.BaseTestCase):
def create_share_type(cls, name, is_public=True, client=None,
cleanup_in_class=True, **kwargs):
if client is None:
client = cls.shares_client
client = cls.shares_v2_client
share_type = client.create_share_type(name, is_public, **kwargs)
resource = {
"type": "share_type",

View File

@ -29,8 +29,23 @@ class AvailabilityZonesTest(base.BaseSharesTest):
self.assertIn(key, az)
@test.attr(type=["smoke", "gate"])
def test_list_availability_zones_extension_url(self):
def test_list_availability_zones_legacy_url_api_v1(self):
# NOTE(vponomaryov): remove this test with removal of availability zone
# extension url support.
azs = self.shares_client.list_availability_zones()
self._list_availability_zones_assertions(azs)
@test.attr(type=["smoke", "gate"])
@base.skip_if_microversion_not_supported("2.6")
def test_list_availability_zones_legacy_url_api_v2(self):
# NOTE(vponomaryov): remove this test with removal of availability zone
# extension url support.
azs = self.shares_v2_client.list_availability_zones(
url='os-availability-zone', version='2.6')
self._list_availability_zones_assertions(azs)
@test.attr(type=["smoke", "gate"])
@base.skip_if_microversion_not_supported("2.7")
def test_list_availability_zones(self):
azs = self.shares_v2_client.list_availability_zones(version='2.7')
self._list_availability_zones_assertions(azs)

View File

@ -0,0 +1,43 @@
# Copyright 2015 Mirantis Inc.
# All Rights Reserved.
#
# Licensed under the Apache License, Version 2.0 (the "License"); you may
# not use this file except in compliance with the License. You may obtain
# a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
# License for the specific language governing permissions and limitations
# under the License.
from tempest import test
from tempest_lib import exceptions as lib_exc
from manila_tempest_tests.tests.api import base
@base.skip_if_microversion_not_supported("2.7")
class AvailabilityZonesNegativeTest(base.BaseSharesTest):
@test.attr(type=["smoke", "gate"])
def test_list_availability_zones_api_not_found_with_legacy_url(self):
# NOTE(vponomaryov): remove this test with removal of availability zone
# extension url support.
self.assertRaises(
lib_exc.NotFound,
self.shares_v2_client.list_availability_zones,
url='os-availability-zone',
version='2.7',
)
@test.attr(type=["smoke", "gate"])
def test_list_availability_zones_api_not_found(self):
self.assertRaises(
lib_exc.NotFound,
self.shares_v2_client.list_availability_zones,
url='availability-zones',
version='2.6',
)

View File

@ -13,22 +13,25 @@
# License for the specific language governing permissions and limitations
# under the License.
import ddt
from tempest import test # noqa
from manila_tempest_tests.tests.api import base
@ddt.data
class SharesQuotasTest(base.BaseSharesTest):
@classmethod
def resource_setup(cls):
super(SharesQuotasTest, cls).resource_setup()
cls.user_id = cls.shares_client.user_id
cls.tenant_id = cls.shares_client.tenant_id
cls.user_id = cls.shares_v2_client.user_id
cls.tenant_id = cls.shares_v2_client.tenant_id
@test.attr(type=["gate", "smoke", ])
def test_default_quotas(self):
quotas = self.shares_client.default_quotas(self.tenant_id)
@ddt.data('shares_client', 'shares_v2_client')
def test_default_quotas(self, client_name):
quotas = getattr(self, client_name).default_quotas(self.tenant_id)
self.assertGreater(int(quotas["gigabytes"]), -2)
self.assertGreater(int(quotas["snapshot_gigabytes"]), -2)
self.assertGreater(int(quotas["shares"]), -2)
@ -36,8 +39,9 @@ class SharesQuotasTest(base.BaseSharesTest):
self.assertGreater(int(quotas["share_networks"]), -2)
@test.attr(type=["gate", "smoke", ])
def test_show_quotas(self):
quotas = self.shares_client.show_quotas(self.tenant_id)
@ddt.data('shares_client', 'shares_v2_client')
def test_show_quotas(self, client_name):
quotas = getattr(self, client_name).show_quotas(self.tenant_id)
self.assertGreater(int(quotas["gigabytes"]), -2)
self.assertGreater(int(quotas["snapshot_gigabytes"]), -2)
self.assertGreater(int(quotas["shares"]), -2)
@ -45,8 +49,9 @@ class SharesQuotasTest(base.BaseSharesTest):
self.assertGreater(int(quotas["share_networks"]), -2)
@test.attr(type=["gate", "smoke", ])
def test_show_quotas_for_user(self):
quotas = self.shares_client.show_quotas(
@ddt.data('shares_client', 'shares_v2_client')
def test_show_quotas_for_user(self, client_name):
quotas = getattr(self, client_name).show_quotas(
self.tenant_id, self.user_id)
self.assertGreater(int(quotas["gigabytes"]), -2)
self.assertGreater(int(quotas["snapshot_gigabytes"]), -2)

View File

@ -13,29 +13,55 @@
# License for the specific language governing permissions and limitations
# under the License.
from tempest import test # noqa
from tempest_lib import exceptions as lib_exc # noqa
import testtools # noqa
import ddt
from tempest import test
from tempest_lib import exceptions as lib_exc
from manila_tempest_tests.tests.api import base
@ddt.ddt
class SharesQuotasNegativeTest(base.BaseSharesTest):
@test.attr(type=["gate", "smoke", "negative"])
def test_get_quotas_with_empty_tenant_id(self):
self.assertRaises(lib_exc.NotFound,
self.shares_client.show_quotas, "")
self.shares_v2_client.show_quotas, "")
@test.attr(type=["gate", "smoke", "negative", ])
def test_try_reset_quotas_with_user(self):
self.assertRaises(lib_exc.Forbidden,
self.shares_client.reset_quotas,
self.shares_client.tenant_id)
self.shares_v2_client.reset_quotas,
self.shares_v2_client.tenant_id)
@test.attr(type=["gate", "smoke", "negative", ])
def test_try_update_quotas_with_user(self):
self.assertRaises(lib_exc.Forbidden,
self.shares_client.update_quotas,
self.shares_client.tenant_id,
self.shares_v2_client.update_quotas,
self.shares_v2_client.tenant_id,
shares=9)
@ddt.data(
('services', '2.0', 'show_quotas'),
('services', '2.0', 'default_quotas'),
('services', '2.0', 'reset_quotas'),
('services', '2.0', 'update_quotas'),
('services', '2.6', 'show_quotas'),
('services', '2.6', 'default_quotas'),
('services', '2.6', 'reset_quotas'),
('services', '2.6', 'update_quotas'),
('os-services', '2.7', 'show_quotas'),
('os-services', '2.7', 'default_quotas'),
('os-services', '2.7', 'reset_quotas'),
('os-services', '2.7', 'update_quotas'),
)
@ddt.unpack
@test.attr(type=["gate", "smoke", "negative", ])
@base.skip_if_microversion_not_supported("2.7")
def test_show_quotas_with_wrong_versions(self, url, version, method_name):
self.assertRaises(
lib_exc.NotFound,
getattr(self.shares_v2_client, method_name),
self.shares_v2_client.tenant_id,
version=version, url=url,
)