Use constants for microversion values

We very often end up with merge conflicts for any patches that increment
microversions due to conflicting numbers. We can't really solve that,
but we can avoid the need to update version numbers throughout the code
by defining a constant value in one place and using that variable instead.

Change-Id: Ib3a80fee6caaabb49af097aa197f550c65d94985
This commit is contained in:
Sean McGinnis 2017-09-15 11:14:59 -06:00
parent b3833594da
commit e918482674
64 changed files with 868 additions and 577 deletions

View File

@ -24,6 +24,7 @@ from oslo_log import log as logging
from six.moves import urllib from six.moves import urllib
import webob import webob
from cinder.api import microversions as mv
from cinder.common import constants from cinder.common import constants
from cinder import exception from cinder import exception
from cinder.i18n import _ from cinder.i18n import _
@ -66,8 +67,6 @@ CONF.register_opts(api_common_opts)
LOG = logging.getLogger(__name__) LOG = logging.getLogger(__name__)
_FILTERS_COLLECTION = None _FILTERS_COLLECTION = None
FILTERING_VERSION = '3.31'
LIKE_FILTER_VERSION = '3.34'
ATTRIBUTE_CONVERTERS = {'name~': 'display_name~', ATTRIBUTE_CONVERTERS = {'name~': 'display_name~',
'description~': 'display_description~'} 'description~': 'display_description~'}
@ -492,9 +491,9 @@ def process_general_filtering(resource):
req_version = kwargs.get('req_version') req_version = kwargs.get('req_version')
filters = kwargs.get('filters') filters = kwargs.get('filters')
context = kwargs.get('context') context = kwargs.get('context')
if req_version.matches(FILTERING_VERSION): if req_version.matches(mv.RESOURCE_FILTER):
support_like = False support_like = False
if req_version.matches(LIKE_FILTER_VERSION): if req_version.matches(mv.LIKE_FILTER):
support_like = True support_like = True
reject_invalid_filters(context, filters, reject_invalid_filters(context, filters,
resource, support_like) resource, support_like)

View File

@ -20,6 +20,7 @@ from webob import exc
from cinder.api import common from cinder.api import common
from cinder.api import extensions from cinder.api import extensions
from cinder.api import microversions as mv
from cinder.api.openstack import wsgi from cinder.api.openstack import wsgi
from cinder import backup from cinder import backup
from cinder import db from cinder import db
@ -245,7 +246,8 @@ class VolumeAdminController(AdminController):
volume = self._get(context, id) volume = self._get(context, id)
params = body['os-migrate_volume'] params = body['os-migrate_volume']
cluster_name, host = common.get_cluster_host(req, params, '3.16') cluster_name, host = common.get_cluster_host(req, params,
mv.VOLUME_MIGRATE_CLUSTER)
force_host_copy = utils.get_bool_param('force_host_copy', params) force_host_copy = utils.get_bool_param('force_host_copy', params)
lock_volume = utils.get_bool_param('lock_volume', params) lock_volume = utils.get_bool_param('lock_volume', params)
self.volume_api.migrate_volume(context, volume, host, cluster_name, self.volume_api.migrate_volume(context, volume, host, cluster_name,

View File

@ -23,6 +23,7 @@ from webob import exc
from cinder.api import common from cinder.api import common
from cinder.api import extensions from cinder.api import extensions
from cinder.api import microversions as mv
from cinder.api.openstack import wsgi from cinder.api.openstack import wsgi
from cinder.api.views import backups as backup_views from cinder.api.views import backups as backup_views
from cinder import backup as backupAPI from cinder import backup as backupAPI
@ -151,8 +152,8 @@ class BackupsController(wsgi.Controller):
incremental = backup.get('incremental', False) incremental = backup.get('incremental', False)
force = backup.get('force', False) force = backup.get('force', False)
snapshot_id = backup.get('snapshot_id', None) snapshot_id = backup.get('snapshot_id', None)
metadata = backup.get( metadata = backup.get('metadata', None) if req_version.matches(
'metadata', None) if req_version.matches("3.43") else None mv.BACKUP_METADATA) else None
LOG.info("Creating backup of volume %(volume_id)s in container" LOG.info("Creating backup of volume %(volume_id)s in container"
" %(container)s", " %(container)s",
{'volume_id': volume_id, 'container': container}, {'volume_id': volume_id, 'container': container},

View File

@ -15,6 +15,7 @@
import oslo_messaging as messaging import oslo_messaging as messaging
from cinder.api import common from cinder.api import common
from cinder.api import microversions as mv
from cinder import exception from cinder import exception
from cinder.i18n import _ from cinder.i18n import _
@ -23,7 +24,8 @@ def get_manageable_resources(req, is_detail, function_get_manageable,
view_builder): view_builder):
context = req.environ['cinder.context'] context = req.environ['cinder.context']
params = req.params.copy() params = req.params.copy()
cluster_name, host = common.get_cluster_host(req, params, '3.17') cluster_name, host = common.get_cluster_host(
req, params, mv.MANAGE_EXISTING_CLUSTER)
marker, limit, offset = common.get_pagination_params(params) marker, limit, offset = common.get_pagination_params(params)
sort_keys, sort_dirs = common.get_sort_params(params, sort_keys, sort_dirs = common.get_sort_params(params,
default_key='reference') default_key='reference')

View File

@ -16,14 +16,12 @@
from cinder.api import common from cinder.api import common
from cinder.api import extensions from cinder.api import extensions
from cinder.api import microversions as mv
from cinder.api.openstack import wsgi from cinder.api.openstack import wsgi
from cinder.api.views import scheduler_stats as scheduler_stats_view from cinder.api.views import scheduler_stats as scheduler_stats_view
from cinder.scheduler import rpcapi from cinder.scheduler import rpcapi
from cinder import utils from cinder import utils
GET_POOL_NAME_FILTER_MICRO_VERSION = '3.28'
GET_POOL_VOLUME_TYPE_FILTER_MICRO_VERSION = '3.35'
def authorize(context, action_name): def authorize(context, action_name):
action = 'scheduler_stats:%s' % action_name action = 'scheduler_stats:%s' % action_name
@ -42,7 +40,7 @@ class SchedulerStatsController(wsgi.Controller):
@common.process_general_filtering('pool') @common.process_general_filtering('pool')
def _process_pool_filtering(self, context=None, filters=None, def _process_pool_filtering(self, context=None, filters=None,
req_version=None): req_version=None):
if not req_version.matches(GET_POOL_NAME_FILTER_MICRO_VERSION): if not req_version.matches(mv.POOL_FILTER):
filters.clear() filters.clear()
def get_pools(self, req): def get_pools(self, req):
@ -60,7 +58,7 @@ class SchedulerStatsController(wsgi.Controller):
filters=filters, filters=filters,
req_version=req_version) req_version=req_version)
if not req_version.matches(GET_POOL_VOLUME_TYPE_FILTER_MICRO_VERSION): if not req_version.matches(mv.POOL_TYPE_FILTER):
filters.pop('volume_type', None) filters.pop('volume_type', None)
pools = self.scheduler_api.get_pools(context, filters=filters) pools = self.scheduler_api.get_pools(context, filters=filters)

View File

@ -22,6 +22,7 @@ import webob.exc
from cinder.api import common from cinder.api import common
from cinder.api import extensions from cinder.api import extensions
from cinder.api import microversions as mv
from cinder.api.openstack import wsgi from cinder.api.openstack import wsgi
from cinder.backup import rpcapi as backup_rpcapi from cinder.backup import rpcapi as backup_rpcapi
from cinder.common import constants from cinder.common import constants
@ -94,8 +95,8 @@ class ServiceController(wsgi.Controller):
'status': active, 'state': art, 'status': active, 'state': art,
'updated_at': updated_at} 'updated_at': updated_at}
# On V3.7 we added cluster support # On CLUSTER_SUPPORT we added cluster support
if req.api_version_request.matches('3.7'): if req.api_version_request.matches(mv.CLUSTER_SUPPORT):
ret_fields['cluster'] = svc.cluster_name ret_fields['cluster'] = svc.cluster_name
if detailed: if detailed:
@ -125,20 +126,23 @@ class ServiceController(wsgi.Controller):
raise exception.InvalidInput(ex.msg) raise exception.InvalidInput(ex.msg)
def _freeze(self, context, req, body): def _freeze(self, context, req, body):
cluster_name, host = common.get_cluster_host(req, body, '3.26') cluster_name, host = common.get_cluster_host(
req, body, mv.REPLICATION_CLUSTER)
return self._volume_api_proxy(self.volume_api.freeze_host, context, return self._volume_api_proxy(self.volume_api.freeze_host, context,
host, cluster_name) host, cluster_name)
def _thaw(self, context, req, body): def _thaw(self, context, req, body):
cluster_name, host = common.get_cluster_host(req, body, '3.26') cluster_name, host = common.get_cluster_host(
req, body, mv.REPLICATION_CLUSTER)
return self._volume_api_proxy(self.volume_api.thaw_host, context, return self._volume_api_proxy(self.volume_api.thaw_host, context,
host, cluster_name) host, cluster_name)
def _failover(self, context, req, body, clustered): def _failover(self, context, req, body, clustered):
# We set version to None to always get the cluster name from the body, # We set version to None to always get the cluster name from the body,
# to False when we don't want to get it, and '3.26' when we only want # to False when we don't want to get it, and REPLICATION_CLUSTER when
# it if the requested version is 3.26 or higher. # we only want it if the requested version is REPLICATION_CLUSTER or
version = '3.26' if clustered else False # higher.
version = mv.REPLICATION_CLUSTER if clustered else False
cluster_name, host = common.get_cluster_host(req, body, version) cluster_name, host = common.get_cluster_host(req, body, version)
self._volume_api_proxy(self.volume_api.failover, context, host, self._volume_api_proxy(self.volume_api.failover, context, host,
cluster_name, body.get('backend_id')) cluster_name, body.get('backend_id'))
@ -221,7 +225,7 @@ class ServiceController(wsgi.Controller):
context = req.environ['cinder.context'] context = req.environ['cinder.context']
authorize(context, action='update') authorize(context, action='update')
support_dynamic_log = req.api_version_request.matches('3.32') support_dynamic_log = req.api_version_request.matches(mv.LOG_LEVEL)
ext_loaded = self.ext_mgr.is_loaded('os-extended-services') ext_loaded = self.ext_mgr.is_loaded('os-extended-services')
ret_val = {} ret_val = {}
@ -240,7 +244,8 @@ class ServiceController(wsgi.Controller):
return self._thaw(context, req, body) return self._thaw(context, req, body)
elif id == "failover_host": elif id == "failover_host":
return self._failover(context, req, body, False) return self._failover(context, req, body, False)
elif req.api_version_request.matches('3.26') and id == 'failover': elif (req.api_version_request.matches(mv.REPLICATION_CLUSTER) and
id == 'failover'):
return self._failover(context, req, body, True) return self._failover(context, req, body, True)
elif support_dynamic_log and id == 'set-log': elif support_dynamic_log and id == 'set-log':
return self._set_log(context, body) return self._set_log(context, body)

View File

@ -13,6 +13,7 @@
# under the License. # under the License.
from cinder.api import extensions from cinder.api import extensions
from cinder.api import microversions as mv
from cinder.api.openstack import wsgi from cinder.api.openstack import wsgi
from cinder import quota from cinder import quota
@ -32,7 +33,8 @@ class UsedLimitsController(wsgi.Controller):
# TODO(wangxiyuan): Support "tenant_id" here to keep the backwards # TODO(wangxiyuan): Support "tenant_id" here to keep the backwards
# compatibility. Remove it once we drop all support for "tenant". # compatibility. Remove it once we drop all support for "tenant".
if req_version.matches(None, "3.38") or not context.is_admin: if (req_version.matches(None, mv.GROUP_REPLICATION) or
not context.is_admin):
params.pop('project_id', None) params.pop('project_id', None)
params.pop('tenant_id', None) params.pop('tenant_id', None)
project_id = params.get( project_id = params.get(

View File

@ -22,7 +22,7 @@ from six.moves import http_client
import webob import webob
from cinder.api import extensions from cinder.api import extensions
from cinder.api.openstack import api_version_request from cinder.api import microversions as mv
from cinder.api.openstack import wsgi from cinder.api.openstack import wsgi
from cinder import exception from cinder import exception
from cinder.i18n import _ from cinder.i18n import _
@ -271,7 +271,8 @@ class VolumeActionsController(wsgi.Controller):
image_metadata['cinder_encryption_key_id'] = encryption_key_id image_metadata['cinder_encryption_key_id'] = encryption_key_id
if req_version >= api_version_request.APIVersionRequest('3.1'): if req_version >= mv.get_api_version(
mv.UPLOAD_IMAGE_PARAMS):
image_metadata['visibility'] = params.get('visibility', 'private') image_metadata['visibility'] = params.get('visibility', 'private')
image_metadata['protected'] = params.get('protected', 'False') image_metadata['protected'] = params.get('protected', 'False')
@ -321,7 +322,8 @@ class VolumeActionsController(wsgi.Controller):
raise webob.exc.HTTPBadRequest(explanation=msg) raise webob.exc.HTTPBadRequest(explanation=msg)
try: try:
if req_version.matches("3.42") and volume.status in ['in-use']: if (req_version.matches(mv.VOLUME_EXTEND_INUSE) and
volume.status in ['in-use']):
self.volume_api.extend_attached_volume(context, volume, size) self.volume_api.extend_attached_volume(context, volume, size)
else: else:
self.volume_api.extend(context, volume, size) self.volume_api.extend(context, volume, size)

View File

@ -18,6 +18,7 @@ from six.moves import http_client
from cinder.api import common from cinder.api import common
from cinder.api.contrib import resource_common_manage from cinder.api.contrib import resource_common_manage
from cinder.api import extensions from cinder.api import extensions
from cinder.api import microversions as mv
from cinder.api.openstack import wsgi from cinder.api.openstack import wsgi
from cinder.api.v2.views import volumes as volume_views from cinder.api.v2.views import volumes as volume_views
from cinder.api.views import manageable_volumes as list_manageable_view from cinder.api.views import manageable_volumes as list_manageable_view
@ -110,7 +111,8 @@ class VolumeManageController(wsgi.Controller):
if 'ref' not in volume: if 'ref' not in volume:
raise exception.MissingRequired(element='ref') raise exception.MissingRequired(element='ref')
cluster_name, host = common.get_cluster_host(req, volume, '3.16') cluster_name, host = common.get_cluster_host(
req, volume, mv.VOLUME_MIGRATE_CLUSTER)
LOG.debug('Manage volume request body: %s', body) LOG.debug('Manage volume request body: %s', body)

172
cinder/api/microversions.py Normal file
View File

@ -0,0 +1,172 @@
# 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.
"""API Microversion definitions.
All new microversions should have a constant added here to be used throughout
the code instead of the specific version number. Until patches land, it's
common to end up with merge conflicts with other microversion changes. Merge
conflicts will be easier to handle via the microversion constants defined here
as the version number will only need to be changed in a single location.
Actual version numbers should be used:
* In this file
* In cinder/api/openstack/rest_api_version_history.rst
* In cinder/api/openstack/api_version_request.py
* In release notes describing the new functionality
* In updates to api-ref
Nearly all microversion changes should include changes to all of those
locations. Make sure to add relevant documentation, and make sure that
documentation includes the final version number used.
"""
from cinder.api.openstack import api_version_request as api_version
from cinder import exception
# Add new constants here for each new microversion.
BASE_VERSION = '3.0'
UPLOAD_IMAGE_PARAMS = '3.1'
VOLUME_LIST_BOOTABLE = '3.2'
MESSAGES = '3.3'
VOLUME_LIST_GLANCE_METADATA = '3.4'
MESSAGES_PAGINATION = '3.5'
CG_UPDATE_BLANK_PROPERTIES = '3.6'
CLUSTER_SUPPORT = '3.7'
MANAGE_EXISTING_LIST = '3.8'
BACKUP_UPDATE = '3.9'
VOLUME_LIST_GROUP = '3.10'
GROUP_TYPE = '3.11'
VOLUME_SUMMARY = '3.12'
GROUP_VOLUME = '3.13'
GROUP_SNAPSHOTS = '3.14'
ETAGS = '3.15'
VOLUME_MIGRATE_CLUSTER = '3.16'
MANAGE_EXISTING_CLUSTER = '3.17'
BACKUP_PROJECT = '3.18'
GROUP_SNAPSHOT_RESET_STATUS = '3.19'
GROUP_VOLUME_RESET_STATUS = '3.20'
VOLUME_DETAIL_PROVIDER_ID = '3.21'
SNAPSHOT_LIST_METADATA_FILTER = '3.22'
VOLUME_DELETE_FORCE = '3.23'
WORKERS_CLEANUP = '3.24'
GROUP_VOLUME_LIST = '3.25'
REPLICATION_CLUSTER = '3.26'
NEW_ATTACH = '3.27'
POOL_FILTER = '3.28'
GROUP_SNAPSHOT_PAGINATION = '3.29'
SNAPSHOT_SORT = '3.30'
RESOURCE_FILTER = '3.31'
LOG_LEVEL = '3.32'
RESOURCE_FILTER_CONFIG = '3.33'
LIKE_FILTER = '3.34'
POOL_TYPE_FILTER = '3.35'
VOLUME_SUMMARY_METADATA = '3.36'
BACKUP_SORT_NAME = '3.37'
GROUP_REPLICATION = '3.38'
LIMITS_ADMIN_FILTER = '3.39'
VOLUME_REVERT = '3.40'
SNAPSHOT_LIST_USER_ID = '3.41'
VOLUME_EXTEND_INUSE = '3.42'
BACKUP_METADATA = '3.43'
NEW_ATTACH_COMPLETION = '3.44'
def get_mv_header(version):
"""Gets a formatted HTTP microversion header.
:param version: The microversion needed.
:return: A tuple containing the microversion header with the
requested version value.
"""
return {'OpenStack-API-Version':
'volume %s' % version}
def get_api_version(version):
"""Gets a ``APIVersionRequest`` instance.
:param version: The microversion needed.
:return: The ``APIVersionRequest`` instance.
"""
return api_version.APIVersionRequest(version)
def get_prior_version(version):
"""Gets the microversion before the given version.
Mostly useful for testing boundaries. This gets the microversion defined
just prior to the given version.
:param version: The version of interest.
:return: The version just prior to the given version.
"""
parts = version.split('.')
if len(parts) != 2 or parts[0] != '3':
raise exception.InvalidInput(reason='Version %s is not a valid '
'microversion format.' % version)
minor = int(parts[1]) - 1
if minor < 0:
# What's your problem? Are you trying to be difficult?
minor = 0
return '%s.%s' % (parts[0], minor)

View File

@ -116,6 +116,7 @@ REST_API_VERSION_HISTORY = """
_MIN_API_VERSION = "3.0" _MIN_API_VERSION = "3.0"
_MAX_API_VERSION = "3.44" _MAX_API_VERSION = "3.44"
_LEGACY_API_VERSION2 = "2.0" _LEGACY_API_VERSION2 = "2.0"
UPDATED = "2017-09-19T20:18:14Z"
# NOTE(cyeoh): min and max versions declared as functions so we can # NOTE(cyeoh): min and max versions declared as functions so we can

View File

@ -95,7 +95,7 @@ class VolumeController(wsgi.Controller):
filters = params filters = params
# NOTE(wanghao): Always removing glance_metadata since we support it # NOTE(wanghao): Always removing glance_metadata since we support it
# only in API version >= 3.4. # only in API version >= VOLUME_LIST_GLANCE_METADATA.
filters.pop('glance_metadata', None) filters.pop('glance_metadata', None)
utils.remove_invalid_filter_options(context, utils.remove_invalid_filter_options(context,
filters, filters,

View File

@ -16,6 +16,7 @@ from oslo_log import log as logging
import webob import webob
from cinder.api import common from cinder.api import common
from cinder.api import microversions as mv
from cinder.api.openstack import wsgi from cinder.api.openstack import wsgi
from cinder.api.v3.views import attachments as attachment_views from cinder.api.v3.views import attachments as attachment_views
from cinder import exception from cinder import exception
@ -26,8 +27,6 @@ from cinder.volume import api as volume_api
LOG = logging.getLogger(__name__) LOG = logging.getLogger(__name__)
API_VERSION = '3.27'
ATTACHMENT_COMPLETION_VERSION = '3.44'
class AttachmentsController(wsgi.Controller): class AttachmentsController(wsgi.Controller):
@ -43,20 +42,20 @@ class AttachmentsController(wsgi.Controller):
self.ext_mgr = ext_mgr self.ext_mgr = ext_mgr
super(AttachmentsController, self).__init__() super(AttachmentsController, self).__init__()
@wsgi.Controller.api_version(API_VERSION) @wsgi.Controller.api_version(mv.NEW_ATTACH)
def show(self, req, id): def show(self, req, id):
"""Return data about the given attachment.""" """Return data about the given attachment."""
context = req.environ['cinder.context'] context = req.environ['cinder.context']
attachment = objects.VolumeAttachment.get_by_id(context, id) attachment = objects.VolumeAttachment.get_by_id(context, id)
return attachment_views.ViewBuilder.detail(attachment) return attachment_views.ViewBuilder.detail(attachment)
@wsgi.Controller.api_version(API_VERSION) @wsgi.Controller.api_version(mv.NEW_ATTACH)
def index(self, req): def index(self, req):
"""Return a summary list of attachments.""" """Return a summary list of attachments."""
attachments = self._items(req) attachments = self._items(req)
return attachment_views.ViewBuilder.list(attachments) return attachment_views.ViewBuilder.list(attachments)
@wsgi.Controller.api_version(API_VERSION) @wsgi.Controller.api_version(mv.NEW_ATTACH)
def detail(self, req): def detail(self, req):
"""Return a detailed list of attachments.""" """Return a detailed list of attachments."""
attachments = self._items(req) attachments = self._items(req)
@ -94,7 +93,7 @@ class AttachmentsController(wsgi.Controller):
marker=marker, limit=limit, offset=offset, sort_keys=sort_keys, marker=marker, limit=limit, offset=offset, sort_keys=sort_keys,
sort_direction=sort_dirs) sort_direction=sort_dirs)
@wsgi.Controller.api_version(API_VERSION) @wsgi.Controller.api_version(mv.NEW_ATTACH)
@wsgi.response(202) @wsgi.response(202)
def create(self, req, body): def create(self, req, body):
"""Create an attachment. """Create an attachment.
@ -192,7 +191,7 @@ class AttachmentsController(wsgi.Controller):
raise webob.exc.HTTPInternalServerError(explanation=err_msg) raise webob.exc.HTTPInternalServerError(explanation=err_msg)
return attachment_views.ViewBuilder.detail(attachment_ref) return attachment_views.ViewBuilder.detail(attachment_ref)
@wsgi.Controller.api_version(API_VERSION) @wsgi.Controller.api_version(mv.NEW_ATTACH)
def update(self, req, id, body): def update(self, req, id, body):
"""Update an attachment record. """Update an attachment record.
@ -252,7 +251,7 @@ class AttachmentsController(wsgi.Controller):
# or a dict? # or a dict?
return attachment_views.ViewBuilder.detail(attachment_ref) return attachment_views.ViewBuilder.detail(attachment_ref)
@wsgi.Controller.api_version(API_VERSION) @wsgi.Controller.api_version(mv.NEW_ATTACH)
def delete(self, req, id): def delete(self, req, id):
"""Delete an attachment. """Delete an attachment.
@ -268,7 +267,7 @@ class AttachmentsController(wsgi.Controller):
return attachment_views.ViewBuilder.list(attachments) return attachment_views.ViewBuilder.list(attachments)
@wsgi.response(202) @wsgi.response(202)
@wsgi.Controller.api_version(ATTACHMENT_COMPLETION_VERSION) @wsgi.Controller.api_version(mv.NEW_ATTACH_COMPLETION)
@wsgi.action('os-complete') @wsgi.action('os-complete')
def complete(self, req, id, body): def complete(self, req, id, body):
"""Mark a volume attachment process as completed (in-use).""" """Mark a volume attachment process as completed (in-use)."""

View File

@ -19,22 +19,20 @@ from oslo_log import log as logging
from webob import exc from webob import exc
from cinder.api.contrib import backups as backups_v2 from cinder.api.contrib import backups as backups_v2
from cinder.api import microversions as mv
from cinder.api.openstack import wsgi from cinder.api.openstack import wsgi
from cinder.backup import api as backup_api from cinder.backup import api as backup_api
from cinder import exception from cinder import exception
from cinder.i18n import _ from cinder.i18n import _
BACKUP_UPDATE_MICRO_VERSION = '3.9'
BACKUP_TENANT_MICRO_VERSION = '3.18'
LOG = logging.getLogger(__name__) LOG = logging.getLogger(__name__)
class BackupsController(backups_v2.BackupsController): class BackupsController(backups_v2.BackupsController):
"""The backups API controller for the OpenStack API V3.""" """The backups API controller for the OpenStack API V3."""
@wsgi.Controller.api_version(BACKUP_UPDATE_MICRO_VERSION) @wsgi.Controller.api_version(mv.BACKUP_UPDATE)
def update(self, req, id, body): def update(self, req, id, body):
"""Update a backup.""" """Update a backup."""
context = req.environ['cinder.context'] context = req.environ['cinder.context']
@ -50,7 +48,8 @@ class BackupsController(backups_v2.BackupsController):
if 'description' in backup_update: if 'description' in backup_update:
update_dict['display_description'] = ( update_dict['display_description'] = (
backup_update.pop('description')) backup_update.pop('description'))
if req_version.matches('3.43') and 'metadata' in backup_update: if (req_version.matches(
mv.BACKUP_METADATA) and 'metadata' in backup_update):
update_dict['metadata'] = backup_update.pop('metadata') update_dict['metadata'] = backup_update.pop('metadata')
# Check no unsupported fields. # Check no unsupported fields.
if backup_update: if backup_update:
@ -77,7 +76,7 @@ class BackupsController(backups_v2.BackupsController):
req.cache_db_backup(backup) req.cache_db_backup(backup)
resp_backup = self._view_builder.detail(req, backup) resp_backup = self._view_builder.detail(req, backup)
if req_version.matches(BACKUP_TENANT_MICRO_VERSION): if req_version.matches(mv.BACKUP_PROJECT):
try: try:
backup_api.check_policy(context, 'backup_project_attribute') backup_api.check_policy(context, 'backup_project_attribute')
self._add_backup_project_attribute(req, resp_backup['backup']) self._add_backup_project_attribute(req, resp_backup['backup'])
@ -90,7 +89,7 @@ class BackupsController(backups_v2.BackupsController):
context = req.environ['cinder.context'] context = req.environ['cinder.context']
req_version = req.api_version_request req_version = req.api_version_request
if req_version.matches(BACKUP_TENANT_MICRO_VERSION): if req_version.matches(mv.BACKUP_PROJECT):
try: try:
backup_api.check_policy(context, 'backup_project_attribute') backup_api.check_policy(context, 'backup_project_attribute')
for bak in resp_backup['backups']: for bak in resp_backup['backups']:
@ -100,7 +99,7 @@ class BackupsController(backups_v2.BackupsController):
return resp_backup return resp_backup
def _convert_sort_name(self, req_version, sort_keys): def _convert_sort_name(self, req_version, sort_keys):
if req_version.matches("3.37") and 'name' in sort_keys: if req_version.matches(mv.BACKUP_SORT_NAME) and 'name' in sort_keys:
sort_keys[sort_keys.index('name')] = 'display_name' sort_keys[sort_keys.index('name')] = 'display_name'

View File

@ -13,6 +13,7 @@
# License for the specific language governing permissions and limitations # License for the specific language governing permissions and limitations
# under the License. # under the License.
from cinder.api import microversions as mv
from cinder.api.openstack import wsgi from cinder.api.openstack import wsgi
from cinder.api.v3.views import clusters as clusters_view from cinder.api.v3.views import clusters as clusters_view
from cinder import exception from cinder import exception
@ -21,10 +22,6 @@ from cinder import objects
from cinder import utils from cinder import utils
CLUSTER_MICRO_VERSION = '3.7'
REPLICATION_DATA_MICRO_VERSION = '3.26'
class ClusterController(wsgi.Controller): class ClusterController(wsgi.Controller):
allowed_list_keys = {'name', 'binary', 'is_up', 'disabled', 'num_hosts', allowed_list_keys = {'name', 'binary', 'is_up', 'disabled', 'num_hosts',
'num_down_hosts', 'binary', 'replication_status', 'num_down_hosts', 'binary', 'replication_status',
@ -33,7 +30,7 @@ class ClusterController(wsgi.Controller):
policy_checker = wsgi.Controller.get_policy_checker('clusters') policy_checker = wsgi.Controller.get_policy_checker('clusters')
@wsgi.Controller.api_version(CLUSTER_MICRO_VERSION) @wsgi.Controller.api_version(mv.CLUSTER_SUPPORT)
def show(self, req, id, binary='cinder-volume'): def show(self, req, id, binary='cinder-volume'):
"""Return data for a given cluster name with optional binary.""" """Return data for a given cluster name with optional binary."""
# Let the wsgi middleware convert NotAuthorized exceptions # Let the wsgi middleware convert NotAuthorized exceptions
@ -42,10 +39,10 @@ class ClusterController(wsgi.Controller):
cluster = objects.Cluster.get_by_id(context, None, binary=binary, cluster = objects.Cluster.get_by_id(context, None, binary=binary,
name=id, services_summary=True) name=id, services_summary=True)
replication_data = req.api_version_request.matches( replication_data = req.api_version_request.matches(
REPLICATION_DATA_MICRO_VERSION) mv.REPLICATION_CLUSTER)
return clusters_view.ViewBuilder.detail(cluster, replication_data) return clusters_view.ViewBuilder.detail(cluster, replication_data)
@wsgi.Controller.api_version(CLUSTER_MICRO_VERSION) @wsgi.Controller.api_version(mv.CLUSTER_SUPPORT)
def index(self, req): def index(self, req):
"""Return a non detailed list of all existing clusters. """Return a non detailed list of all existing clusters.
@ -53,7 +50,7 @@ class ClusterController(wsgi.Controller):
""" """
return self._get_clusters(req, detail=False) return self._get_clusters(req, detail=False)
@wsgi.Controller.api_version(CLUSTER_MICRO_VERSION) @wsgi.Controller.api_version(mv.CLUSTER_SUPPORT)
def detail(self, req): def detail(self, req):
"""Return a detailed list of all existing clusters. """Return a detailed list of all existing clusters.
@ -65,7 +62,7 @@ class ClusterController(wsgi.Controller):
# Let the wsgi middleware convert NotAuthorized exceptions # Let the wsgi middleware convert NotAuthorized exceptions
context = self.policy_checker(req, 'get_all') context = self.policy_checker(req, 'get_all')
replication_data = req.api_version_request.matches( replication_data = req.api_version_request.matches(
REPLICATION_DATA_MICRO_VERSION) mv.REPLICATION_CLUSTER)
filters = dict(req.GET) filters = dict(req.GET)
allowed = self.allowed_list_keys allowed = self.allowed_list_keys
if not replication_data: if not replication_data:
@ -89,7 +86,7 @@ class ClusterController(wsgi.Controller):
return clusters_view.ViewBuilder.list(clusters, detail, return clusters_view.ViewBuilder.list(clusters, detail,
replication_data) replication_data)
@wsgi.Controller.api_version(CLUSTER_MICRO_VERSION) @wsgi.Controller.api_version(mv.CLUSTER_SUPPORT)
def update(self, req, id, body): def update(self, req, id, body):
"""Enable/Disable scheduling for a cluster.""" """Enable/Disable scheduling for a cluster."""
# NOTE(geguileo): This method tries to be consistent with services # NOTE(geguileo): This method tries to be consistent with services
@ -123,7 +120,7 @@ class ClusterController(wsgi.Controller):
# We return summary data plus the disabled reason # We return summary data plus the disabled reason
replication_data = req.api_version_request.matches( replication_data = req.api_version_request.matches(
REPLICATION_DATA_MICRO_VERSION) mv.REPLICATION_CLUSTER)
ret_val = clusters_view.ViewBuilder.summary(cluster, replication_data) ret_val = clusters_view.ViewBuilder.summary(cluster, replication_data)
ret_val['cluster']['disabled_reason'] = disabled_reason ret_val['cluster']['disabled_reason'] = disabled_reason

View File

@ -19,6 +19,7 @@ import webob
from webob import exc from webob import exc
from cinder.api.contrib import consistencygroups as cg_v2 from cinder.api.contrib import consistencygroups as cg_v2
from cinder.api import microversions as mv
from cinder.api.openstack import wsgi from cinder.api.openstack import wsgi
from cinder.i18n import _ from cinder.i18n import _
@ -30,7 +31,8 @@ class ConsistencyGroupsController(cg_v2.ConsistencyGroupsController):
def _check_update_parameters_v3(self, req, name, description, add_volumes, def _check_update_parameters_v3(self, req, name, description, add_volumes,
remove_volumes): remove_volumes):
allow_empty = req.api_version_request.matches('3.6', None) allow_empty = req.api_version_request.matches(
mv.CG_UPDATE_BLANK_PROPERTIES, None)
if allow_empty: if allow_empty:
if (name is None and description is None if (name is None and description is None
and not add_volumes and not remove_volumes): and not add_volumes and not remove_volumes):

View File

@ -22,6 +22,7 @@ import webob
from webob import exc from webob import exc
from cinder.api import common from cinder.api import common
from cinder.api import microversions as mv
from cinder.api.openstack import wsgi from cinder.api.openstack import wsgi
from cinder.api.v3.views import group_snapshots as group_snapshot_views from cinder.api.v3.views import group_snapshots as group_snapshot_views
from cinder import exception from cinder import exception
@ -32,8 +33,6 @@ from cinder.volume import group_types
LOG = logging.getLogger(__name__) LOG = logging.getLogger(__name__)
GROUP_SNAPSHOT_API_VERSION = '3.14'
class GroupSnapshotsController(wsgi.Controller): class GroupSnapshotsController(wsgi.Controller):
"""The group_snapshots API controller for the OpenStack API.""" """The group_snapshots API controller for the OpenStack API."""
@ -52,7 +51,7 @@ class GroupSnapshotsController(wsgi.Controller):
% {'group_type': group_type_id}) % {'group_type': group_type_id})
raise exc.HTTPBadRequest(explanation=msg) raise exc.HTTPBadRequest(explanation=msg)
@wsgi.Controller.api_version(GROUP_SNAPSHOT_API_VERSION) @wsgi.Controller.api_version(mv.GROUP_SNAPSHOTS)
def show(self, req, id): def show(self, req, id):
"""Return data about the given group_snapshot.""" """Return data about the given group_snapshot."""
LOG.debug('show called for member %s', id) LOG.debug('show called for member %s', id)
@ -66,7 +65,7 @@ class GroupSnapshotsController(wsgi.Controller):
return self._view_builder.detail(req, group_snapshot) return self._view_builder.detail(req, group_snapshot)
@wsgi.Controller.api_version(GROUP_SNAPSHOT_API_VERSION) @wsgi.Controller.api_version(mv.GROUP_SNAPSHOTS)
def delete(self, req, id): def delete(self, req, id):
"""Delete a group_snapshot.""" """Delete a group_snapshot."""
LOG.debug('delete called for member %s', id) LOG.debug('delete called for member %s', id)
@ -93,12 +92,12 @@ class GroupSnapshotsController(wsgi.Controller):
return webob.Response(status_int=http_client.ACCEPTED) return webob.Response(status_int=http_client.ACCEPTED)
@wsgi.Controller.api_version(GROUP_SNAPSHOT_API_VERSION) @wsgi.Controller.api_version(mv.GROUP_SNAPSHOTS)
def index(self, req): def index(self, req):
"""Returns a summary list of group_snapshots.""" """Returns a summary list of group_snapshots."""
return self._get_group_snapshots(req, is_detail=False) return self._get_group_snapshots(req, is_detail=False)
@wsgi.Controller.api_version(GROUP_SNAPSHOT_API_VERSION) @wsgi.Controller.api_version(mv.GROUP_SNAPSHOTS)
def detail(self, req): def detail(self, req):
"""Returns a detailed list of group_snapshots.""" """Returns a detailed list of group_snapshots."""
return self._get_group_snapshots(req, is_detail=True) return self._get_group_snapshots(req, is_detail=True)
@ -109,14 +108,14 @@ class GroupSnapshotsController(wsgi.Controller):
context = req.environ['cinder.context'] context = req.environ['cinder.context']
req_version = req.api_version_request req_version = req.api_version_request
filters = marker = limit = offset = sort_keys = sort_dirs = None filters = marker = limit = offset = sort_keys = sort_dirs = None
if req_version.matches("3.29"): if req_version.matches(mv.GROUP_SNAPSHOT_PAGINATION):
filters = req.params.copy() filters = req.params.copy()
marker, limit, offset = common.get_pagination_params(filters) marker, limit, offset = common.get_pagination_params(filters)
sort_keys, sort_dirs = common.get_sort_params(filters) sort_keys, sort_dirs = common.get_sort_params(filters)
if req_version.matches(common.FILTERING_VERSION): if req_version.matches(mv.RESOURCE_FILTER):
support_like = (True if req_version.matches( support_like = (True if req_version.matches(
common.LIKE_FILTER_VERSION) else False) mv.LIKE_FILTER) else False)
common.reject_invalid_filters(context, filters, 'group_snapshot', common.reject_invalid_filters(context, filters, 'group_snapshot',
support_like) support_like)
@ -145,7 +144,7 @@ class GroupSnapshotsController(wsgi.Controller):
group_snapshots['group_snapshots'] = new_group_snapshots group_snapshots['group_snapshots'] = new_group_snapshots
return group_snapshots return group_snapshots
@wsgi.Controller.api_version(GROUP_SNAPSHOT_API_VERSION) @wsgi.Controller.api_version(mv.GROUP_SNAPSHOTS)
@wsgi.response(http_client.ACCEPTED) @wsgi.response(http_client.ACCEPTED)
def create(self, req, body): def create(self, req, body):
"""Create a new group_snapshot.""" """Create a new group_snapshot."""
@ -183,7 +182,7 @@ class GroupSnapshotsController(wsgi.Controller):
return retval return retval
@wsgi.Controller.api_version('3.19') @wsgi.Controller.api_version(mv.GROUP_SNAPSHOT_RESET_STATUS)
@wsgi.action("reset_status") @wsgi.action("reset_status")
def reset_status(self, req, id, body): def reset_status(self, req, id, body):
return self._reset_status(req, id, body) return self._reset_status(req, id, body)

View File

@ -18,6 +18,7 @@ from six.moves import http_client
import webob import webob
from cinder.api import common from cinder.api import common
from cinder.api import microversions as mv
from cinder.api.openstack import wsgi from cinder.api.openstack import wsgi
from cinder import db from cinder import db
from cinder import exception from cinder import exception
@ -51,7 +52,7 @@ class GroupTypeSpecsController(wsgi.Controller):
except exception.GroupTypeNotFound as ex: except exception.GroupTypeNotFound as ex:
raise webob.exc.HTTPNotFound(explanation=ex.msg) raise webob.exc.HTTPNotFound(explanation=ex.msg)
@wsgi.Controller.api_version('3.11') @wsgi.Controller.api_version(mv.GROUP_TYPE)
def index(self, req, group_type_id): def index(self, req, group_type_id):
"""Returns the list of group specs for a given group type.""" """Returns the list of group specs for a given group type."""
context = req.environ['cinder.context'] context = req.environ['cinder.context']
@ -59,7 +60,7 @@ class GroupTypeSpecsController(wsgi.Controller):
self._check_type(context, group_type_id) self._check_type(context, group_type_id)
return self._get_group_specs(context, group_type_id) return self._get_group_specs(context, group_type_id)
@wsgi.Controller.api_version('3.11') @wsgi.Controller.api_version(mv.GROUP_TYPE)
@wsgi.response(http_client.ACCEPTED) @wsgi.response(http_client.ACCEPTED)
def create(self, req, group_type_id, body=None): def create(self, req, group_type_id, body=None):
context = req.environ['cinder.context'] context = req.environ['cinder.context']
@ -80,7 +81,7 @@ class GroupTypeSpecsController(wsgi.Controller):
notifier_info) notifier_info)
return body return body
@wsgi.Controller.api_version('3.11') @wsgi.Controller.api_version(mv.GROUP_TYPE)
def update(self, req, group_type_id, id, body=None): def update(self, req, group_type_id, id, body=None):
context = req.environ['cinder.context'] context = req.environ['cinder.context']
self._check_policy(context) self._check_policy(context)
@ -108,7 +109,7 @@ class GroupTypeSpecsController(wsgi.Controller):
notifier_info) notifier_info)
return body return body
@wsgi.Controller.api_version('3.11') @wsgi.Controller.api_version(mv.GROUP_TYPE)
def show(self, req, group_type_id, id): def show(self, req, group_type_id, id):
"""Return a single extra spec item.""" """Return a single extra spec item."""
context = req.environ['cinder.context'] context = req.environ['cinder.context']
@ -123,7 +124,7 @@ class GroupTypeSpecsController(wsgi.Controller):
"%(id)s.") % ({'type_id': group_type_id, 'id': id}) "%(id)s.") % ({'type_id': group_type_id, 'id': id})
raise webob.exc.HTTPNotFound(explanation=msg) raise webob.exc.HTTPNotFound(explanation=msg)
@wsgi.Controller.api_version('3.11') @wsgi.Controller.api_version(mv.GROUP_TYPE)
def delete(self, req, group_type_id, id): def delete(self, req, group_type_id, id):
"""Deletes an existing group spec.""" """Deletes an existing group spec."""
context = req.environ['cinder.context'] context = req.environ['cinder.context']

View File

@ -21,6 +21,7 @@ import webob
from webob import exc from webob import exc
from cinder.api import common from cinder.api import common
from cinder.api import microversions as mv
from cinder.api.openstack import wsgi from cinder.api.openstack import wsgi
from cinder.api.v3.views import group_types as views_types from cinder.api.v3.views import group_types as views_types
from cinder import exception from cinder import exception
@ -55,7 +56,7 @@ class GroupTypesController(wsgi.Controller):
payload = dict(group_types=group_type) payload = dict(group_types=group_type)
rpc.get_notifier('groupType').info(context, method, payload) rpc.get_notifier('groupType').info(context, method, payload)
@wsgi.Controller.api_version('3.11') @wsgi.Controller.api_version(mv.GROUP_TYPE)
@wsgi.response(http_client.ACCEPTED) @wsgi.response(http_client.ACCEPTED)
def create(self, req, body): def create(self, req, body):
"""Creates a new group type.""" """Creates a new group type."""
@ -103,7 +104,7 @@ class GroupTypesController(wsgi.Controller):
return self._view_builder.show(req, grp_type) return self._view_builder.show(req, grp_type)
@wsgi.Controller.api_version('3.11') @wsgi.Controller.api_version(mv.GROUP_TYPE)
def update(self, req, id, body): def update(self, req, id, body):
# Update description for a given group type. # Update description for a given group type.
context = req.environ['cinder.context'] context = req.environ['cinder.context']
@ -163,7 +164,7 @@ class GroupTypesController(wsgi.Controller):
return self._view_builder.show(req, grp_type) return self._view_builder.show(req, grp_type)
@wsgi.Controller.api_version('3.11') @wsgi.Controller.api_version(mv.GROUP_TYPE)
def delete(self, req, id): def delete(self, req, id):
"""Deletes an existing group type.""" """Deletes an existing group type."""
context = req.environ['cinder.context'] context = req.environ['cinder.context']
@ -186,14 +187,14 @@ class GroupTypesController(wsgi.Controller):
return webob.Response(status_int=http_client.ACCEPTED) return webob.Response(status_int=http_client.ACCEPTED)
@wsgi.Controller.api_version('3.11') @wsgi.Controller.api_version(mv.GROUP_TYPE)
def index(self, req): def index(self, req):
"""Returns the list of group types.""" """Returns the list of group types."""
limited_types = self._get_group_types(req) limited_types = self._get_group_types(req)
req.cache_resource(limited_types, name='group_types') req.cache_resource(limited_types, name='group_types')
return self._view_builder.index(req, limited_types) return self._view_builder.index(req, limited_types)
@wsgi.Controller.api_version('3.11') @wsgi.Controller.api_version(mv.GROUP_TYPE)
def show(self, req, id): def show(self, req, id):
"""Return a single group type item.""" """Return a single group type item."""
context = req.environ['cinder.context'] context = req.environ['cinder.context']

View File

@ -22,6 +22,7 @@ import webob
from webob import exc from webob import exc
from cinder.api import common from cinder.api import common
from cinder.api import microversions as mv
from cinder.api.openstack import wsgi from cinder.api.openstack import wsgi
from cinder.api.v3.views import groups as views_groups from cinder.api.v3.views import groups as views_groups
from cinder import exception from cinder import exception
@ -32,10 +33,6 @@ from cinder.volume import group_types
LOG = logging.getLogger(__name__) LOG = logging.getLogger(__name__)
GROUP_API_VERSION = '3.13'
GROUP_CREATE_FROM_SRC_API_VERSION = '3.14'
GROUP_REPLICATION_API_VERSION = '3.38'
class GroupsController(wsgi.Controller): class GroupsController(wsgi.Controller):
"""The groups API controller for the OpenStack API.""" """The groups API controller for the OpenStack API."""
@ -53,7 +50,7 @@ class GroupsController(wsgi.Controller):
"CG APIs.") % {'group_type': group_type_id} "CG APIs.") % {'group_type': group_type_id}
raise exc.HTTPBadRequest(explanation=msg) raise exc.HTTPBadRequest(explanation=msg)
@wsgi.Controller.api_version(GROUP_API_VERSION) @wsgi.Controller.api_version(mv.GROUP_VOLUME)
def show(self, req, id): def show(self, req, id):
"""Return data about the given group.""" """Return data about the given group."""
LOG.debug('show called for member %s', id) LOG.debug('show called for member %s', id)
@ -68,7 +65,7 @@ class GroupsController(wsgi.Controller):
return self._view_builder.detail(req, group) return self._view_builder.detail(req, group)
@wsgi.Controller.api_version('3.20') @wsgi.Controller.api_version(mv.GROUP_VOLUME_RESET_STATUS)
@wsgi.action("reset_status") @wsgi.action("reset_status")
def reset_status(self, req, id, body): def reset_status(self, req, id, body):
return self._reset_status(req, id, body) return self._reset_status(req, id, body)
@ -109,7 +106,7 @@ class GroupsController(wsgi.Controller):
raise exc.HTTPBadRequest(explanation=error.msg) raise exc.HTTPBadRequest(explanation=error.msg)
return webob.Response(status_int=http_client.ACCEPTED) return webob.Response(status_int=http_client.ACCEPTED)
@wsgi.Controller.api_version(GROUP_API_VERSION) @wsgi.Controller.api_version(mv.GROUP_VOLUME)
@wsgi.action("delete") @wsgi.action("delete")
def delete_group(self, req, id, body): def delete_group(self, req, id, body):
return self._delete(req, id, body) return self._delete(req, id, body)
@ -150,12 +147,12 @@ class GroupsController(wsgi.Controller):
return webob.Response(status_int=http_client.ACCEPTED) return webob.Response(status_int=http_client.ACCEPTED)
@wsgi.Controller.api_version(GROUP_API_VERSION) @wsgi.Controller.api_version(mv.GROUP_VOLUME)
def index(self, req): def index(self, req):
"""Returns a summary list of groups.""" """Returns a summary list of groups."""
return self._get_groups(req, is_detail=False) return self._get_groups(req, is_detail=False)
@wsgi.Controller.api_version(GROUP_API_VERSION) @wsgi.Controller.api_version(mv.GROUP_VOLUME)
def detail(self, req): def detail(self, req):
"""Returns a detailed list of groups.""" """Returns a detailed list of groups."""
return self._get_groups(req, is_detail=True) return self._get_groups(req, is_detail=True)
@ -169,9 +166,9 @@ class GroupsController(wsgi.Controller):
sort_keys, sort_dirs = common.get_sort_params(filters) sort_keys, sort_dirs = common.get_sort_params(filters)
filters.pop('list_volume', None) filters.pop('list_volume', None)
if api_version.matches(common.FILTERING_VERSION): if api_version.matches(mv.RESOURCE_FILTER):
support_like = (True if api_version.matches( support_like = (True if api_version.matches(
common.LIKE_FILTER_VERSION) else False) mv.LIKE_FILTER) else False)
common.reject_invalid_filters(context, filters, 'group', common.reject_invalid_filters(context, filters, 'group',
support_like) support_like)
@ -197,7 +194,7 @@ class GroupsController(wsgi.Controller):
req, new_groups) req, new_groups)
return groups return groups
@wsgi.Controller.api_version(GROUP_API_VERSION) @wsgi.Controller.api_version(mv.GROUP_VOLUME)
@wsgi.response(http_client.ACCEPTED) @wsgi.response(http_client.ACCEPTED)
def create(self, req, body): def create(self, req, body):
"""Create a new group.""" """Create a new group."""
@ -243,7 +240,7 @@ class GroupsController(wsgi.Controller):
retval = self._view_builder.summary(req, new_group) retval = self._view_builder.summary(req, new_group)
return retval return retval
@wsgi.Controller.api_version(GROUP_CREATE_FROM_SRC_API_VERSION) @wsgi.Controller.api_version(mv.GROUP_SNAPSHOTS)
@wsgi.action("create-from-src") @wsgi.action("create-from-src")
@wsgi.response(http_client.ACCEPTED) @wsgi.response(http_client.ACCEPTED)
def create_from_src(self, req, body): def create_from_src(self, req, body):
@ -308,7 +305,7 @@ class GroupsController(wsgi.Controller):
retval = self._view_builder.summary(req, new_group) retval = self._view_builder.summary(req, new_group)
return retval return retval
@wsgi.Controller.api_version(GROUP_API_VERSION) @wsgi.Controller.api_version(mv.GROUP_VOLUME)
def update(self, req, id, body): def update(self, req, id, body):
"""Update the group. """Update the group.
@ -373,7 +370,7 @@ class GroupsController(wsgi.Controller):
return webob.Response(status_int=http_client.ACCEPTED) return webob.Response(status_int=http_client.ACCEPTED)
@wsgi.Controller.api_version(GROUP_REPLICATION_API_VERSION) @wsgi.Controller.api_version(mv.GROUP_REPLICATION)
@wsgi.action("enable_replication") @wsgi.action("enable_replication")
def enable_replication(self, req, id, body): def enable_replication(self, req, id, body):
"""Enables replications for a group.""" """Enables replications for a group."""
@ -397,7 +394,7 @@ class GroupsController(wsgi.Controller):
return webob.Response(status_int=202) return webob.Response(status_int=202)
@wsgi.Controller.api_version(GROUP_REPLICATION_API_VERSION) @wsgi.Controller.api_version(mv.GROUP_REPLICATION)
@wsgi.action("disable_replication") @wsgi.action("disable_replication")
def disable_replication(self, req, id, body): def disable_replication(self, req, id, body):
"""Disables replications for a group.""" """Disables replications for a group."""
@ -421,7 +418,7 @@ class GroupsController(wsgi.Controller):
return webob.Response(status_int=202) return webob.Response(status_int=202)
@wsgi.Controller.api_version(GROUP_REPLICATION_API_VERSION) @wsgi.Controller.api_version(mv.GROUP_REPLICATION)
@wsgi.action("failover_replication") @wsgi.action("failover_replication")
def failover_replication(self, req, id, body): def failover_replication(self, req, id, body):
"""Fails over replications for a group.""" """Fails over replications for a group."""
@ -457,7 +454,7 @@ class GroupsController(wsgi.Controller):
return webob.Response(status_int=202) return webob.Response(status_int=202)
@wsgi.Controller.api_version(GROUP_REPLICATION_API_VERSION) @wsgi.Controller.api_version(mv.GROUP_REPLICATION)
@wsgi.action("list_replication_targets") @wsgi.action("list_replication_targets")
def list_replication_targets(self, req, id, body): def list_replication_targets(self, req, id, body):
"""List replication targets for a group.""" """List replication targets for a group."""

View File

@ -13,6 +13,7 @@
"""The limits V3 api.""" """The limits V3 api."""
from cinder.api import microversions as mv
from cinder.api.openstack import wsgi from cinder.api.openstack import wsgi
from cinder.api.v2 import limits as limits_v2 from cinder.api.v2 import limits as limits_v2
from cinder.api.views import limits as limits_views from cinder.api.views import limits as limits_views
@ -32,7 +33,8 @@ class LimitsController(limits_v2.LimitsController):
# TODO(wangxiyuan): Support "tenant_id" here to keep the backwards # TODO(wangxiyuan): Support "tenant_id" here to keep the backwards
# compatibility. Remove it once we drop all support for "tenant". # compatibility. Remove it once we drop all support for "tenant".
if req_version.matches(None, "3.38") or not context.is_admin: if req_version.matches(None,
mv.GROUP_REPLICATION) or not context.is_admin:
params.pop('project_id', None) params.pop('project_id', None)
params.pop('tenant_id', None) params.pop('tenant_id', None)
project_id = params.get( project_id = params.get(

View File

@ -17,6 +17,7 @@ from six.moves import http_client
import webob import webob
from cinder.api import common from cinder.api import common
from cinder.api import microversions as mv
from cinder.api.openstack import wsgi from cinder.api.openstack import wsgi
from cinder.api.v3.views import messages as messages_view from cinder.api.v3.views import messages as messages_view
from cinder.message import api as message_api from cinder.message import api as message_api
@ -25,9 +26,6 @@ from cinder.message import message_field
import cinder.policy import cinder.policy
MESSAGES_BASE_MICRO_VERSION = '3.3'
def check_policy(context, action, target_obj=None): def check_policy(context, action, target_obj=None):
target = { target = {
'project_id': context.project_id, 'project_id': context.project_id,
@ -62,7 +60,7 @@ class MessagesController(wsgi.Controller):
message_field.translate_action(message['action_id']), message_field.translate_action(message['action_id']),
message_field.translate_detail(message['detail_id'])) message_field.translate_detail(message['detail_id']))
@wsgi.Controller.api_version(MESSAGES_BASE_MICRO_VERSION) @wsgi.Controller.api_version(mv.MESSAGES)
def show(self, req, id): def show(self, req, id):
"""Return the given message.""" """Return the given message."""
context = req.environ['cinder.context'] context = req.environ['cinder.context']
@ -75,7 +73,7 @@ class MessagesController(wsgi.Controller):
self._build_user_message(message) self._build_user_message(message)
return self._view_builder.detail(req, message) return self._view_builder.detail(req, message)
@wsgi.Controller.api_version(MESSAGES_BASE_MICRO_VERSION) @wsgi.Controller.api_version(mv.MESSAGES)
def delete(self, req, id): def delete(self, req, id):
"""Delete a message.""" """Delete a message."""
context = req.environ['cinder.context'] context = req.environ['cinder.context']
@ -87,7 +85,7 @@ class MessagesController(wsgi.Controller):
return webob.Response(status_int=http_client.NO_CONTENT) return webob.Response(status_int=http_client.NO_CONTENT)
@wsgi.Controller.api_version(MESSAGES_BASE_MICRO_VERSION) @wsgi.Controller.api_version(mv.MESSAGES)
def index(self, req): def index(self, req):
"""Returns a list of messages, transformed through view builder.""" """Returns a list of messages, transformed through view builder."""
context = req.environ['cinder.context'] context = req.environ['cinder.context']
@ -100,14 +98,14 @@ class MessagesController(wsgi.Controller):
sort_keys = None sort_keys = None
sort_dirs = None sort_dirs = None
if api_version.matches("3.5"): if api_version.matches(mv.MESSAGES_PAGINATION):
filters = req.params.copy() filters = req.params.copy()
marker, limit, offset = common.get_pagination_params(filters) marker, limit, offset = common.get_pagination_params(filters)
sort_keys, sort_dirs = common.get_sort_params(filters) sort_keys, sort_dirs = common.get_sort_params(filters)
if api_version.matches(common.FILTERING_VERSION): if api_version.matches(mv.RESOURCE_FILTER):
support_like = (True if api_version.matches( support_like = (True if api_version.matches(
common.LIKE_FILTER_VERSION) else False) mv.LIKE_FILTER) else False)
common.reject_invalid_filters(context, filters, 'message', common.reject_invalid_filters(context, filters, 'message',
support_like) support_like)

View File

@ -15,6 +15,7 @@
from cinder.api import common from cinder.api import common
from cinder.api import extensions from cinder.api import extensions
from cinder.api import microversions as mv
from cinder.api.openstack import wsgi from cinder.api.openstack import wsgi
from cinder import exception from cinder import exception
from cinder.i18n import _ from cinder.i18n import _
@ -41,13 +42,14 @@ class ManageResource(object):
raise exception.VersionNotFoundForAPIMethod(version=version) raise exception.VersionNotFoundForAPIMethod(version=version)
def _get_resources(self, req, is_detail): def _get_resources(self, req, is_detail):
self._ensure_min_version(req, '3.8') self._ensure_min_version(req, mv.MANAGE_EXISTING_LIST)
context = req.environ['cinder.context'] context = req.environ['cinder.context']
self._authorizer(context) self._authorizer(context)
params = req.params.copy() params = req.params.copy()
cluster_name, host = common.get_cluster_host(req, params, '3.17') cluster_name, host = common.get_cluster_host(
req, params, mv.MANAGE_EXISTING_CLUSTER)
marker, limit, offset = common.get_pagination_params(params) marker, limit, offset = common.get_pagination_params(params)
sort_keys, sort_dirs = common.get_sort_params(params, sort_keys, sort_dirs = common.get_sort_params(params,
default_key='reference') default_key='reference')

View File

@ -13,13 +13,11 @@
"""The resource filters api.""" """The resource filters api."""
from cinder.api import common from cinder.api import common
from cinder.api import microversions as mv
from cinder.api.openstack import wsgi from cinder.api.openstack import wsgi
from cinder.api.v3.views import resource_filters as filter_views from cinder.api.v3.views import resource_filters as filter_views
FILTER_API_VERSION = '3.33'
class ResourceFiltersController(wsgi.Controller): class ResourceFiltersController(wsgi.Controller):
"""The resource filter API controller for the OpenStack API.""" """The resource filter API controller for the OpenStack API."""
@ -30,7 +28,7 @@ class ResourceFiltersController(wsgi.Controller):
self.ext_mgr = ext_mgr self.ext_mgr = ext_mgr
super(ResourceFiltersController, self).__init__() super(ResourceFiltersController, self).__init__()
@wsgi.Controller.api_version(FILTER_API_VERSION) @wsgi.Controller.api_version(mv.RESOURCE_FILTER_CONFIG)
def index(self, req): def index(self, req):
"""Return a list of resource filters.""" """Return a list of resource filters."""
resource = req.params.get('resource', None) resource = req.params.get('resource', None)

View File

@ -15,6 +15,7 @@
from six.moves import http_client from six.moves import http_client
from cinder.api.contrib import snapshot_manage as snapshot_manage_v2 from cinder.api.contrib import snapshot_manage as snapshot_manage_v2
from cinder.api import microversions as mv
from cinder.api.openstack import wsgi from cinder.api.openstack import wsgi
from cinder.api.v3 import resource_common_manage as common from cinder.api.v3 import resource_common_manage as common
@ -27,7 +28,7 @@ class SnapshotManageController(common.ManageResource,
@wsgi.response(http_client.ACCEPTED) @wsgi.response(http_client.ACCEPTED)
def create(self, req, body): def create(self, req, body):
self._ensure_min_version(req, "3.8") self._ensure_min_version(req, mv.MANAGE_EXISTING_LIST)
return super(SnapshotManageController, self).create(req, body) return super(SnapshotManageController, self).create(req, body)

View File

@ -20,6 +20,7 @@ import ast
from oslo_log import log as logging from oslo_log import log as logging
from cinder.api import common from cinder.api import common
from cinder.api import microversions as mv
from cinder.api.openstack import wsgi from cinder.api.openstack import wsgi
from cinder.api.v2 import snapshots as snapshots_v2 from cinder.api.v2 import snapshots as snapshots_v2
from cinder.api.v3.views import snapshots as snapshot_views from cinder.api.v3.views import snapshots as snapshot_views
@ -56,9 +57,10 @@ class SnapshotsController(snapshots_v2.SnapshotsController):
req_version=None): req_version=None):
"""Formats allowed filters""" """Formats allowed filters"""
# if the max version is less than or same as 3.21 # if the max version is less than SNAPSHOT_LIST_METADATA_FILTER
# metadata based filtering is not supported # metadata based filtering is not supported
if req_version.matches(None, "3.21"): if req_version.matches(
None, mv.get_prior_version(mv.SNAPSHOT_LIST_METADATA_FILTER)):
filters.pop('metadata', None) filters.pop('metadata', None)
# Filter out invalid options # Filter out invalid options
@ -84,7 +86,7 @@ class SnapshotsController(snapshots_v2.SnapshotsController):
self._format_snapshot_filter_options(search_opts) self._format_snapshot_filter_options(search_opts)
req_version = req.api_version_request req_version = req.api_version_request
if req_version.matches("3.30", None) and 'name' in sort_keys: if req_version.matches(mv.SNAPSHOT_SORT, None) and 'name' in sort_keys:
sort_keys[sort_keys.index('name')] = 'display_name' sort_keys[sort_keys.index('name')] = 'display_name'
# NOTE(thingee): v3 API allows name instead of display_name # NOTE(thingee): v3 API allows name instead of display_name

View File

@ -14,6 +14,7 @@
# under the License. # under the License.
from cinder.api import common from cinder.api import common
from cinder.api import microversions as mv
from cinder import utils from cinder import utils
@ -60,20 +61,21 @@ class ViewBuilder(common.ViewBuilder):
req_version = request.api_version_request req_version = request.api_version_request
# Add group_snapshot_id and source_group_id if min version is greater # Add group_snapshot_id and source_group_id if min version is greater
# than or equal to 3.14. # than or equal to GROUP_SNAPSHOTS.
if req_version.matches("3.14", None): if req_version.matches(mv.GROUP_SNAPSHOTS, None):
group_ref['group']['group_snapshot_id'] = group.group_snapshot_id group_ref['group']['group_snapshot_id'] = group.group_snapshot_id
group_ref['group']['source_group_id'] = group.source_group_id group_ref['group']['source_group_id'] = group.source_group_id
# Add volumes if min version is greater than or equal to 3.25. # Add volumes if min version is greater than or equal to
if req_version.matches("3.25", None): # GROUP_VOLUME_LIST.
if req_version.matches(mv.GROUP_VOLUME_LIST, None):
if utils.get_bool_param('list_volume', request.params): if utils.get_bool_param('list_volume', request.params):
group_ref['group']['volumes'] = [volume.id group_ref['group']['volumes'] = [volume.id
for volume in group.volumes] for volume in group.volumes]
# Add replication_status if min version is greater than or equal # Add replication_status if min version is greater than or equal
# to 3.38. # to GROUP_REPLICATION.
if req_version.matches("3.38", None): if req_version.matches(mv.GROUP_REPLICATION, None):
group_ref['group']['replication_status'] = group.replication_status group_ref['group']['replication_status'] = group.replication_status
return group_ref return group_ref

View File

@ -13,6 +13,7 @@
# License for the specific language governing permissions and limitations # License for the specific language governing permissions and limitations
# under the License. # under the License.
from cinder.api import microversions as mv
from cinder.api.views import snapshots as views_v2 from cinder.api.views import snapshots as views_v2
@ -25,10 +26,10 @@ class ViewBuilder(views_v2.ViewBuilder):
req_version = request.api_version_request req_version = request.api_version_request
# Add group_snapshot_id if min version is greater than or equal # Add group_snapshot_id if min version is greater than or equal
# to 3.14. # to GROUP_SNAPSHOTS.
if req_version.matches("3.14", None): if req_version.matches(mv.GROUP_SNAPSHOTS, None):
snapshot_ref['snapshot']['group_snapshot_id'] = ( snapshot_ref['snapshot']['group_snapshot_id'] = (
snapshot.get('group_snapshot_id')) snapshot.get('group_snapshot_id'))
if req_version.matches("3.41", None): if req_version.matches(mv.SNAPSHOT_LIST_USER_ID, None):
snapshot_ref['snapshot']['user_id'] = snapshot.get('user_id') snapshot_ref['snapshot']['user_id'] = snapshot.get('user_id')
return snapshot_ref return snapshot_ref

View File

@ -13,6 +13,7 @@
# License for the specific language governing permissions and limitations # License for the specific language governing permissions and limitations
# under the License. # under the License.
from cinder.api import microversions as mv
from cinder.api.v2.views import volumes as views_v2 from cinder.api.v2.views import volumes as views_v2
@ -41,14 +42,14 @@ class ViewBuilder(views_v2.ViewBuilder):
volume_ref = super(ViewBuilder, self).detail(request, volume) volume_ref = super(ViewBuilder, self).detail(request, volume)
req_version = request.api_version_request req_version = request.api_version_request
# Add group_id if min version is greater than or equal to 3.13. # Add group_id if min version is greater than or equal to GROUP_VOLUME.
if req_version.matches("3.13", None): if req_version.matches(mv.GROUP_VOLUME, None):
volume_ref['volume']['group_id'] = volume.get('group_id') volume_ref['volume']['group_id'] = volume.get('group_id')
# Add provider_id if min version is greater than or equal to 3.21 # Add provider_id if min version is greater than or equal to
# for admin. # VOLUME_DETAIL_PROVIDER_ID for admin.
if (request.environ['cinder.context'].is_admin and if (request.environ['cinder.context'].is_admin and
req_version.matches("3.21", None)): req_version.matches(mv.VOLUME_DETAIL_PROVIDER_ID, None)):
volume_ref['volume']['provider_id'] = volume.get('provider_id') volume_ref['volume']['provider_id'] = volume.get('provider_id')
return volume_ref return volume_ref

View File

@ -15,6 +15,7 @@
from six.moves import http_client from six.moves import http_client
from cinder.api.contrib import volume_manage as volume_manage_v2 from cinder.api.contrib import volume_manage as volume_manage_v2
from cinder.api import microversions as mv
from cinder.api.openstack import wsgi from cinder.api.openstack import wsgi
from cinder.api.v3 import resource_common_manage as common from cinder.api.v3 import resource_common_manage as common
@ -27,7 +28,7 @@ class VolumeManageController(common.ManageResource,
@wsgi.response(http_client.ACCEPTED) @wsgi.response(http_client.ACCEPTED)
def create(self, req, body): def create(self, req, body):
self._ensure_min_version(req, "3.8") self._ensure_min_version(req, mv.MANAGE_EXISTING_LIST)
return super(VolumeManageController, self).create(req, body) return super(VolumeManageController, self).create(req, body)

View File

@ -22,13 +22,11 @@ import six
from six.moves import http_client from six.moves import http_client
import webob import webob
from cinder.api import microversions as mv
from cinder.api.openstack import wsgi from cinder.api.openstack import wsgi
from cinder.api.v2 import volume_metadata as volume_meta_v2 from cinder.api.v2 import volume_metadata as volume_meta_v2
METADATA_MICRO_VERSION = '3.15'
class Controller(volume_meta_v2.Controller): class Controller(volume_meta_v2.Controller):
"""The volume metadata API controller for the OpenStack API.""" """The volume metadata API controller for the OpenStack API."""
def _validate_etag(self, req, volume_id): def _validate_etag(self, req, volume_id):
@ -46,7 +44,7 @@ class Controller(volume_meta_v2.Controller):
def index(self, req, volume_id): def index(self, req, volume_id):
req_version = req.api_version_request req_version = req.api_version_request
metadata = super(Controller, self).index(req, volume_id) metadata = super(Controller, self).index(req, volume_id)
if req_version.matches(METADATA_MICRO_VERSION): if req_version.matches(mv.ETAGS):
data = jsonutils.dumps(metadata) data = jsonutils.dumps(metadata)
if six.PY3: if six.PY3:
data = data.encode('utf-8') data = data.encode('utf-8')
@ -59,7 +57,7 @@ class Controller(volume_meta_v2.Controller):
@wsgi.extends @wsgi.extends
def update(self, req, volume_id, id, body): def update(self, req, volume_id, id, body):
req_version = req.api_version_request req_version = req.api_version_request
if req_version.matches(METADATA_MICRO_VERSION): if req_version.matches(mv.ETAGS):
if not self._validate_etag(req, volume_id): if not self._validate_etag(req, volume_id):
return webob.Response( return webob.Response(
status_int=http_client.PRECONDITION_FAILED) status_int=http_client.PRECONDITION_FAILED)
@ -69,7 +67,7 @@ class Controller(volume_meta_v2.Controller):
@wsgi.extends @wsgi.extends
def update_all(self, req, volume_id, body): def update_all(self, req, volume_id, body):
req_version = req.api_version_request req_version = req.api_version_request
if req_version.matches(METADATA_MICRO_VERSION): if req_version.matches(mv.ETAGS):
if not self._validate_etag(req, volume_id): if not self._validate_etag(req, volume_id):
return webob.Response( return webob.Response(
status_int=http_client.PRECONDITION_FAILED) status_int=http_client.PRECONDITION_FAILED)

View File

@ -21,6 +21,7 @@ import webob
from webob import exc from webob import exc
from cinder.api import common from cinder.api import common
from cinder.api import microversions as mv
from cinder.api.openstack import wsgi from cinder.api.openstack import wsgi
from cinder.api.v2 import volumes as volumes_v2 from cinder.api.v2 import volumes as volumes_v2
from cinder.api.v3.views import volumes as volume_views_v3 from cinder.api.v3.views import volumes as volume_views_v3
@ -33,8 +34,6 @@ from cinder import utils
LOG = logging.getLogger(__name__) LOG = logging.getLogger(__name__)
SUMMARY_BASE_MICRO_VERSION = '3.12'
def check_policy(context, action, target_obj=None): def check_policy(context, action, target_obj=None):
target = { target = {
@ -65,7 +64,7 @@ class VolumeController(volumes_v2.VolumeController):
force = False force = False
params = "" params = ""
if req_version.matches('3.23'): if req_version.matches(mv.VOLUME_LIST_BOOTABLE):
force = utils.get_bool_param('force', req.params) force = utils.get_bool_param('force', req.params)
if cascade or force: if cascade or force:
params = "(cascade: %(c)s, force: %(f)s)" % {'c': cascade, params = "(cascade: %(c)s, force: %(f)s)" % {'c': cascade,
@ -88,10 +87,10 @@ class VolumeController(volumes_v2.VolumeController):
@common.process_general_filtering('volume') @common.process_general_filtering('volume')
def _process_volume_filtering(self, context=None, filters=None, def _process_volume_filtering(self, context=None, filters=None,
req_version=None): req_version=None):
if req_version.matches(None, "3.3"): if req_version.matches(None, mv.MESSAGES):
filters.pop('glance_metadata', None) filters.pop('glance_metadata', None)
if req_version.matches(None, "3.9"): if req_version.matches(None, mv.BACKUP_UPDATE):
filters.pop('group_id', None) filters.pop('group_id', None)
utils.remove_invalid_filter_options( utils.remove_invalid_filter_options(
@ -119,7 +118,8 @@ class VolumeController(volumes_v2.VolumeController):
if 'name' in filters: if 'name' in filters:
filters['display_name'] = filters.pop('name') filters['display_name'] = filters.pop('name')
strict = req.api_version_request.matches("3.2", None) strict = req.api_version_request.matches(
mv.VOLUME_LIST_BOOTABLE, None)
self.volume_api.check_volume_filters(filters, strict) self.volume_api.check_volume_filters(filters, strict)
volumes = self.volume_api.get_all(context, marker, limit, volumes = self.volume_api.get_all(context, marker, limit,
@ -140,7 +140,7 @@ class VolumeController(volumes_v2.VolumeController):
volumes = self._view_builder.summary_list(req, volumes) volumes = self._view_builder.summary_list(req, volumes)
return volumes return volumes
@wsgi.Controller.api_version(SUMMARY_BASE_MICRO_VERSION) @wsgi.Controller.api_version(mv.VOLUME_SUMMARY)
def summary(self, req): def summary(self, req):
"""Return summary of volumes.""" """Return summary of volumes."""
view_builder_v3 = volume_views_v3.ViewBuilder() view_builder_v3 = volume_views_v3.ViewBuilder()
@ -154,7 +154,7 @@ class VolumeController(volumes_v2.VolumeController):
context, filters=filters) context, filters=filters)
req_version = req.api_version_request req_version = req.api_version_request
if req_version.matches("3.36"): if req_version.matches(mv.VOLUME_SUMMARY_METADATA):
all_distinct_metadata = metadata all_distinct_metadata = metadata
else: else:
all_distinct_metadata = None all_distinct_metadata = None
@ -163,7 +163,7 @@ class VolumeController(volumes_v2.VolumeController):
all_distinct_metadata) all_distinct_metadata)
@wsgi.response(http_client.ACCEPTED) @wsgi.response(http_client.ACCEPTED)
@wsgi.Controller.api_version('3.40') @wsgi.Controller.api_version(mv.VOLUME_REVERT)
@wsgi.action('revert') @wsgi.action('revert')
def revert(self, req, id, body): def revert(self, req, id, body):
"""revert a volume to a snapshot""" """revert a volume to a snapshot"""
@ -208,8 +208,8 @@ class VolumeController(volumes_v2.VolumeController):
context = req.environ['cinder.context'] context = req.environ['cinder.context']
req_version = req.api_version_request req_version = req.api_version_request
# Remove group_id from body if max version is less than 3.13. # Remove group_id from body if max version is less than GROUP_VOLUME.
if req_version.matches(None, "3.12"): if req_version.matches(None, mv.get_prior_version(mv.GROUP_VOLUME)):
# NOTE(xyang): The group_id is from a group created with a # NOTE(xyang): The group_id is from a group created with a
# group_type. So with this group_id, we've got a group_type # group_type. So with this group_id, we've got a group_type
# for this volume. Also if group_id is passed in, that means # for this volume. Also if group_id is passed in, that means

View File

@ -16,6 +16,7 @@
from oslo_utils import timeutils from oslo_utils import timeutils
from oslo_utils import uuidutils from oslo_utils import uuidutils
from cinder.api import microversions as mv
from cinder.api.openstack import wsgi from cinder.api.openstack import wsgi
from cinder.api.v3.views import workers as workers_view from cinder.api.v3.views import workers as workers_view
from cinder import db from cinder import db
@ -98,7 +99,7 @@ class WorkerController(wsgi.Controller):
return params return params
@wsgi.Controller.api_version('3.24') @wsgi.Controller.api_version(mv.WORKERS_CLEANUP)
@wsgi.response(202) @wsgi.response(202)
def cleanup(self, req, body=None): def cleanup(self, req, body=None):
"""Do the cleanup on resources from a specific service/host/node.""" """Do the cleanup on resources from a specific service/host/node."""

View File

@ -51,7 +51,7 @@ _KNOWN_VERSIONS = {
"status": "CURRENT", "status": "CURRENT",
"version": api_version_request._MAX_API_VERSION, "version": api_version_request._MAX_API_VERSION,
"min_version": api_version_request._MIN_API_VERSION, "min_version": api_version_request._MIN_API_VERSION,
"updated": "2016-02-08T12:20:21Z", "updated": api_version_request.UPDATED,
"links": _LINKS, "links": _LINKS,
"media-types": [{ "media-types": [{
"base": "application/json", "base": "application/json",

View File

@ -14,6 +14,7 @@
# under the License. # under the License.
from cinder.api import common from cinder.api import common
from cinder.api import microversions as mv
class ViewBuilder(common.ViewBuilder): class ViewBuilder(common.ViewBuilder):
@ -78,7 +79,7 @@ class ViewBuilder(common.ViewBuilder):
} }
} }
req_version = request.api_version_request req_version = request.api_version_request
if req_version.matches("3.43"): if req_version.matches(mv.BACKUP_METADATA):
backup_dict['backup']['metadata'] = backup.metadata backup_dict['backup']['metadata'] = backup.metadata
return backup_dict return backup_dict

View File

@ -24,7 +24,6 @@ CONF = config.CONF
class VolumeRevertTests(volume_base.BaseVolumeTest): class VolumeRevertTests(volume_base.BaseVolumeTest):
min_microversion = '3.40'
@classmethod @classmethod
def setup_clients(cls): def setup_clients(cls):

View File

@ -23,7 +23,7 @@ import webob
from webob import exc from webob import exc
from cinder.api.contrib import admin_actions from cinder.api.contrib import admin_actions
from cinder.api.openstack import api_version_request as api_version from cinder.api import microversions as mv
from cinder.backup import api as backup_api from cinder.backup import api as backup_api
from cinder.backup import rpcapi as backup_rpcapi from cinder.backup import rpcapi as backup_rpcapi
from cinder.common import constants from cinder.common import constants
@ -525,18 +525,16 @@ class AdminActionsTest(BaseAdminTest):
force_host_copy=False, version=None, force_host_copy=False, version=None,
cluster=None): cluster=None):
# build request to migrate to host # build request to migrate to host
# req = fakes.HTTPRequest.blank('/v3/%s/volumes/%s/action' % (
# fake.PROJECT_ID, volume['id']))
req = webob.Request.blank('/v3/%s/volumes/%s/action' % ( req = webob.Request.blank('/v3/%s/volumes/%s/action' % (
fake.PROJECT_ID, volume['id'])) fake.PROJECT_ID, volume['id']))
req.method = 'POST' req.method = 'POST'
req.headers['content-type'] = 'application/json' req.headers['content-type'] = 'application/json'
body = {'os-migrate_volume': {'host': host, body = {'os-migrate_volume': {'host': host,
'force_host_copy': force_host_copy}} 'force_host_copy': force_host_copy}}
version = version or '3.0' version = version or mv.BASE_VERSION
req.headers = {'OpenStack-API-Version': 'volume %s' % version} req.headers = mv.get_mv_header(version)
req.api_version_request = api_version.APIVersionRequest(version) req.api_version_request = mv.get_api_version(version)
if version == '3.16': if version == mv.VOLUME_MIGRATE_CLUSTER:
body['os-migrate_volume']['cluster'] = cluster body['os-migrate_volume']['cluster'] = cluster
req.body = jsonutils.dump_as_bytes(body) req.body = jsonutils.dump_as_bytes(body)
req.environ['cinder.context'] = ctx req.environ['cinder.context'] = ctx
@ -547,7 +545,9 @@ class AdminActionsTest(BaseAdminTest):
volume = db.volume_get(self.ctx, volume['id']) volume = db.volume_get(self.ctx, volume['id'])
return volume return volume
@ddt.data('3.0', '3.15', '3.16') @ddt.data(mv.BASE_VERSION,
mv.get_prior_version(mv.VOLUME_MIGRATE_CLUSTER),
mv.VOLUME_MIGRATE_CLUSTER)
def test_migrate_volume_success_3(self, version): def test_migrate_volume_success_3(self, version):
expected_status = http_client.ACCEPTED expected_status = http_client.ACCEPTED
host = 'test2' host = 'test2'
@ -563,7 +563,8 @@ class AdminActionsTest(BaseAdminTest):
cluster = 'cluster' cluster = 'cluster'
volume = self._migrate_volume_prep() volume = self._migrate_volume_prep()
volume = self._migrate_volume_3_exec(self.ctx, volume, host, volume = self._migrate_volume_3_exec(self.ctx, volume, host,
expected_status, version='3.16', expected_status,
version=mv.VOLUME_MIGRATE_CLUSTER,
cluster=cluster) cluster=cluster)
self.assertEqual('starting', volume['migration_status']) self.assertEqual('starting', volume['migration_status'])
@ -574,7 +575,8 @@ class AdminActionsTest(BaseAdminTest):
volume = self._migrate_volume_prep() volume = self._migrate_volume_prep()
self.assertRaises(exception.InvalidInput, self.assertRaises(exception.InvalidInput,
self._migrate_volume_3_exec, self.ctx, volume, host, self._migrate_volume_3_exec, self.ctx, volume, host,
None, version='3.16', cluster=cluster) None, version=mv.VOLUME_MIGRATE_CLUSTER,
cluster=cluster)
def _migrate_volume_exec(self, ctx, volume, host, expected_status, def _migrate_volume_exec(self, ctx, volume, host, expected_status,
force_host_copy=False): force_host_copy=False):

View File

@ -17,7 +17,7 @@ import ddt
from oslo_serialization import jsonutils from oslo_serialization import jsonutils
import webob import webob
from cinder.api.openstack import api_version_request as api_version from cinder.api import microversions as mv
from cinder.api.v3 import router as router_v3 from cinder.api.v3 import router as router_v3
from cinder.backup import api as backup_api from cinder.backup import api as backup_api
from cinder import context from cinder import context
@ -57,7 +57,8 @@ class BackupProjectAttributeTest(test.TestCase):
self.stubs.Set(backup_api.API, 'get', fake_backup_get) self.stubs.Set(backup_api.API, 'get', fake_backup_get)
self.stubs.Set(backup_api.API, 'get_all', fake_backup_get_all) self.stubs.Set(backup_api.API, 'get_all', fake_backup_get_all)
def _send_backup_request(self, ctx, detail=False, version='3.18'): def _send_backup_request(self, ctx, detail=False,
version=mv.BACKUP_PROJECT):
req = None req = None
if detail: if detail:
req = webob.Request.blank(('/v3/%s/backups/detail' req = webob.Request.blank(('/v3/%s/backups/detail'
@ -67,8 +68,8 @@ class BackupProjectAttributeTest(test.TestCase):
fake.BACKUP_ID)) fake.BACKUP_ID))
req.method = 'GET' req.method = 'GET'
req.environ['cinder.context'] = ctx req.environ['cinder.context'] = ctx
req.headers['OpenStack-API-Version'] = 'volume ' + version req.headers = mv.get_mv_header(version)
req.api_version_request = api_version.APIVersionRequest(version) req.api_version_request = mv.get_api_version(version)
res = req.get_response(app()) res = req.get_response(app())
if detail: if detail:
@ -97,5 +98,6 @@ class BackupProjectAttributeTest(test.TestCase):
def test_get_backup_under_allowed_api_version(self): def test_get_backup_under_allowed_api_version(self):
ctx = context.RequestContext(fake.USER2_ID, fake.PROJECT_ID, True) ctx = context.RequestContext(fake.USER2_ID, fake.PROJECT_ID, True)
bak = self._send_backup_request(ctx, version='3.17') bak = self._send_backup_request(
ctx, version=mv.get_prior_version(mv.BACKUP_PROJECT))
self.assertNotIn('os-backup-project-attr:project_id', bak) self.assertNotIn('os-backup-project-attr:project_id', bak)

View File

@ -25,6 +25,7 @@ from six.moves import http_client
import webob import webob
from cinder.api.contrib import backups from cinder.api.contrib import backups
from cinder.api import microversions as mv
from cinder.api.openstack import api_version_request as api_version from cinder.api.openstack import api_version_request as api_version
# needed for stubs to work # needed for stubs to work
import cinder.backup import cinder.backup
@ -117,8 +118,8 @@ class BackupsAPITestCase(test.TestCase):
req = webob.Request.blank('/v3/%s/backups/%s' % ( req = webob.Request.blank('/v3/%s/backups/%s' % (
fake.PROJECT_ID, backup.id)) fake.PROJECT_ID, backup.id))
req.method = 'GET' req.method = 'GET'
req.headers = mv.get_mv_header(mv.BACKUP_METADATA)
req.headers['Content-Type'] = 'application/json' req.headers['Content-Type'] = 'application/json'
req.headers['OpenStack-API-Version'] = 'volume 3.43'
res = req.get_response(fakes.wsgi_app( res = req.get_response(fakes.wsgi_app(
fake_auth_context=self.user_context)) fake_auth_context=self.user_context))
res_dict = jsonutils.loads(res.body) res_dict = jsonutils.loads(res.body)
@ -131,6 +132,7 @@ class BackupsAPITestCase(test.TestCase):
req = webob.Request.blank('/v2/%s/backups/%s' % ( req = webob.Request.blank('/v2/%s/backups/%s' % (
fake.PROJECT_ID, fake.WILL_NOT_BE_FOUND_ID)) fake.PROJECT_ID, fake.WILL_NOT_BE_FOUND_ID))
req.method = 'GET' req.method = 'GET'
req.headers = mv.get_mv_header(mv.BACKUP_METADATA)
req.headers['Content-Type'] = 'application/json' req.headers['Content-Type'] = 'application/json'
res = req.get_response(fakes.wsgi_app( res = req.get_response(fakes.wsgi_app(
fake_auth_context=self.user_context)) fake_auth_context=self.user_context))
@ -330,9 +332,9 @@ class BackupsAPITestCase(test.TestCase):
req = webob.Request.blank('/v3/%s/backups/detail' % fake.PROJECT_ID) req = webob.Request.blank('/v3/%s/backups/detail' % fake.PROJECT_ID)
req.method = 'GET' req.method = 'GET'
req.headers = mv.get_mv_header(mv.BACKUP_METADATA)
req.headers['Content-Type'] = 'application/json' req.headers['Content-Type'] = 'application/json'
req.headers['Accept'] = 'application/json' req.headers['Accept'] = 'application/json'
req.headers['OpenStack-API-Version'] = 'volume 3.43'
res = req.get_response(fakes.wsgi_app( res = req.get_response(fakes.wsgi_app(
fake_auth_context=self.user_context)) fake_auth_context=self.user_context))
res_dict = jsonutils.loads(res.body) res_dict = jsonutils.loads(res.body)
@ -536,8 +538,8 @@ class BackupsAPITestCase(test.TestCase):
} }
req = webob.Request.blank('/v3/%s/backups' % fake.PROJECT_ID) req = webob.Request.blank('/v3/%s/backups' % fake.PROJECT_ID)
req.method = 'POST' req.method = 'POST'
req.headers = mv.get_mv_header(mv.BACKUP_METADATA)
req.headers['Content-Type'] = 'application/json' req.headers['Content-Type'] = 'application/json'
req.headers['OpenStack-API-Version'] = 'volume 3.43'
req.body = jsonutils.dump_as_bytes(body) req.body = jsonutils.dump_as_bytes(body)
res = req.get_response(fakes.wsgi_app( res = req.get_response(fakes.wsgi_app(
fake_auth_context=self.user_context)) fake_auth_context=self.user_context))
@ -546,8 +548,8 @@ class BackupsAPITestCase(test.TestCase):
req = webob.Request.blank('/v3/%s/backups/%s' % ( req = webob.Request.blank('/v3/%s/backups/%s' % (
fake.PROJECT_ID, res_dict['backup']['id'])) fake.PROJECT_ID, res_dict['backup']['id']))
req.method = 'GET' req.method = 'GET'
req.headers = mv.get_mv_header(mv.BACKUP_METADATA)
req.headers['Content-Type'] = 'application/json' req.headers['Content-Type'] = 'application/json'
req.headers['OpenStack-API-Version'] = 'volume 3.43'
res = req.get_response(fakes.wsgi_app( res = req.get_response(fakes.wsgi_app(
fake_auth_context=self.user_context)) fake_auth_context=self.user_context))
res_dict = jsonutils.loads(res.body) res_dict = jsonutils.loads(res.body)

View File

@ -19,6 +19,7 @@ import mock
import webob import webob
from cinder.api.contrib import scheduler_stats from cinder.api.contrib import scheduler_stats
from cinder.api import microversions as mv
from cinder.api.openstack import api_version_request as api_version from cinder.api.openstack import api_version_request as api_version
from cinder import context from cinder import context
from cinder import exception from cinder import exception
@ -84,7 +85,7 @@ class SchedulerStatsAPITest(test.TestCase):
fake.PROJECT_ID) fake.PROJECT_ID)
mock_rpcapi.return_value = [dict(name='pool1', mock_rpcapi.return_value = [dict(name='pool1',
capabilities=dict(foo='bar'))] capabilities=dict(foo='bar'))]
req.api_version_request = api_version.APIVersionRequest('3.28') req.api_version_request = mv.get_api_version(mv.POOL_FILTER)
req.environ['cinder.context'] = self.ctxt req.environ['cinder.context'] = self.ctxt
res = self.controller.get_pools(req) res = self.controller.get_pools(req)
@ -106,7 +107,7 @@ class SchedulerStatsAPITest(test.TestCase):
'&foo=bar' % fake.PROJECT_ID) '&foo=bar' % fake.PROJECT_ID)
mock_rpcapi.return_value = [dict(name='pool1', mock_rpcapi.return_value = [dict(name='pool1',
capabilities=dict(foo='bar'))] capabilities=dict(foo='bar'))]
req.api_version_request = api_version.APIVersionRequest('3.28') req.api_version_request = mv.get_api_version(mv.POOL_FILTER)
req.environ['cinder.context'] = self.ctxt req.environ['cinder.context'] = self.ctxt
res = self.controller.get_pools(req) res = self.controller.get_pools(req)
@ -175,8 +176,8 @@ class SchedulerStatsAPITest(test.TestCase):
self.controller.get_pools, self.controller.get_pools,
req) req)
@ddt.data(('3.34', False), @ddt.data((mv.get_prior_version(mv.POOL_TYPE_FILTER), False),
('3.35', True)) (mv.POOL_TYPE_FILTER, True))
@ddt.unpack @ddt.unpack
@mock.patch('cinder.scheduler.rpcapi.SchedulerAPI.get_pools') @mock.patch('cinder.scheduler.rpcapi.SchedulerAPI.get_pools')
@mock.patch('cinder.api.common.reject_invalid_filters') @mock.patch('cinder.api.common.reject_invalid_filters')

View File

@ -25,7 +25,7 @@ import webob.exc
from cinder.api.contrib import services from cinder.api.contrib import services
from cinder.api import extensions from cinder.api import extensions
from cinder.api.openstack import api_version_request as api_version from cinder.api import microversions as mv
from cinder import context from cinder import context
from cinder import exception from cinder import exception
from cinder import objects from cinder import objects
@ -114,10 +114,10 @@ fake_services_list = [
class FakeRequest(object): class FakeRequest(object):
environ = {"cinder.context": context.get_admin_context()} environ = {"cinder.context": context.get_admin_context()}
def __init__(self, version='3.0', **kwargs): def __init__(self, version=mv.BASE_VERSION, **kwargs):
self.GET = kwargs self.GET = kwargs
self.headers = {'OpenStack-API-Version': 'volume ' + version} self.headers = mv.get_mv_header(version)
self.api_version_request = api_version.APIVersionRequest(version) self.api_version_request = mv.get_api_version(version)
class FakeRequestWithBinary(FakeRequest): class FakeRequestWithBinary(FakeRequest):
@ -246,19 +246,19 @@ class ServicesTest(test.TestCase):
self.assertEqual(response, res_dict) self.assertEqual(response, res_dict)
def test_failover_old_version(self): def test_failover_old_version(self):
req = FakeRequest(version='3.18') req = FakeRequest(version=mv.BACKUP_PROJECT)
self.assertRaises(exception.InvalidInput, self.controller.update, req, self.assertRaises(exception.InvalidInput, self.controller.update, req,
'failover', {'cluster': 'cluster1'}) 'failover', {'cluster': 'cluster1'})
def test_failover_no_values(self): def test_failover_no_values(self):
req = FakeRequest(version='3.26') req = FakeRequest(version=mv.REPLICATION_CLUSTER)
self.assertRaises(exception.InvalidInput, self.controller.update, req, self.assertRaises(exception.InvalidInput, self.controller.update, req,
'failover', {'backend_id': 'replica1'}) 'failover', {'backend_id': 'replica1'})
@ddt.data({'host': 'hostname'}, {'cluster': 'mycluster'}) @ddt.data({'host': 'hostname'}, {'cluster': 'mycluster'})
@mock.patch('cinder.volume.api.API.failover') @mock.patch('cinder.volume.api.API.failover')
def test_failover(self, body, failover_mock): def test_failover(self, body, failover_mock):
req = FakeRequest(version='3.26') req = FakeRequest(version=mv.REPLICATION_CLUSTER)
body['backend_id'] = 'replica1' body['backend_id'] = 'replica1'
res = self.controller.update(req, 'failover', body) res = self.controller.update(req, 'failover', body)
self.assertEqual(202, res.status_code) self.assertEqual(202, res.status_code)
@ -269,14 +269,14 @@ class ServicesTest(test.TestCase):
@ddt.data({}, {'host': 'hostname', 'cluster': 'mycluster'}) @ddt.data({}, {'host': 'hostname', 'cluster': 'mycluster'})
@mock.patch('cinder.volume.api.API.failover') @mock.patch('cinder.volume.api.API.failover')
def test_failover_invalid_input(self, body, failover_mock): def test_failover_invalid_input(self, body, failover_mock):
req = FakeRequest(version='3.26') req = FakeRequest(version=mv.REPLICATION_CLUSTER)
body['backend_id'] = 'replica1' body['backend_id'] = 'replica1'
self.assertRaises(exception.InvalidInput, self.assertRaises(exception.InvalidInput,
self.controller.update, req, 'failover', body) self.controller.update, req, 'failover', body)
failover_mock.assert_not_called() failover_mock.assert_not_called()
def test_services_list_with_cluster_name(self): def test_services_list_with_cluster_name(self):
req = FakeRequest(version='3.7') req = FakeRequest(version=mv.CLUSTER_SUPPORT)
res_dict = self.controller.index(req) res_dict = self.controller.index(req)
response = {'services': [{'binary': 'cinder-scheduler', response = {'services': [{'binary': 'cinder-scheduler',
@ -689,7 +689,7 @@ class ServicesTest(test.TestCase):
def test_services_action_cluster_not_found(self, method, body, def test_services_action_cluster_not_found(self, method, body,
mock_get_all_services): mock_get_all_services):
url = '/v3/%s/os-services/%s' % (fake.PROJECT_ID, method) url = '/v3/%s/os-services/%s' % (fake.PROJECT_ID, method)
req = fakes.HTTPRequest.blank(url, version='3.26') req = fakes.HTTPRequest.blank(url, version=mv.REPLICATION_CLUSTER)
mock_get_all_services.return_value = [] mock_get_all_services.return_value = []
msg = 'No service found with cluster=%s' % mock.sentinel.cluster msg = 'No service found with cluster=%s' % mock.sentinel.cluster
result = self.assertRaises(exception.InvalidInput, result = self.assertRaises(exception.InvalidInput,
@ -729,7 +729,7 @@ class ServicesTest(test.TestCase):
@mock.patch('cinder.api.contrib.services.ServiceController._set_log') @mock.patch('cinder.api.contrib.services.ServiceController._set_log')
def test_set_log(self, set_log_mock): def test_set_log(self, set_log_mock):
set_log_mock.return_value = None set_log_mock.return_value = None
req = FakeRequest(version='3.32') req = FakeRequest(version=mv.LOG_LEVEL)
body = mock.sentinel.body body = mock.sentinel.body
res = self.controller.update(req, 'set-log', body) res = self.controller.update(req, 'set-log', body)
self.assertEqual(set_log_mock.return_value, res) self.assertEqual(set_log_mock.return_value, res)
@ -738,7 +738,7 @@ class ServicesTest(test.TestCase):
@mock.patch('cinder.api.contrib.services.ServiceController._get_log') @mock.patch('cinder.api.contrib.services.ServiceController._get_log')
def test_get_log(self, get_log_mock): def test_get_log(self, get_log_mock):
get_log_mock.return_value = None get_log_mock.return_value = None
req = FakeRequest(version='3.32') req = FakeRequest(version=mv.LOG_LEVEL)
body = mock.sentinel.body body = mock.sentinel.body
res = self.controller.update(req, 'get-log', body) res = self.controller.update(req, 'get-log', body)
self.assertEqual(get_log_mock.return_value, res) self.assertEqual(get_log_mock.return_value, res)

View File

@ -17,6 +17,7 @@ import ddt
import mock import mock
from cinder.api.contrib import used_limits from cinder.api.contrib import used_limits
from cinder.api import microversions as mv
from cinder.api.openstack import api_version_request from cinder.api.openstack import api_version_request
from cinder.api.openstack import wsgi from cinder.api.openstack import wsgi
from cinder import exception from cinder import exception
@ -40,8 +41,11 @@ class UsedLimitsTestCase(test.TestCase):
super(UsedLimitsTestCase, self).setUp() super(UsedLimitsTestCase, self).setUp()
self.controller = used_limits.UsedLimitsController() self.controller = used_limits.UsedLimitsController()
@ddt.data(('2.0', False), ('3.38', True), ('3.38', False), ('3.39', True), @ddt.data(('2.0', False),
('3.39', False)) (mv.get_prior_version(mv.LIMITS_ADMIN_FILTER), True),
(mv.get_prior_version(mv.LIMITS_ADMIN_FILTER), False),
(mv.LIMITS_ADMIN_FILTER, True),
(mv.LIMITS_ADMIN_FILTER, False))
@mock.patch('cinder.quota.QUOTAS.get_project_quotas') @mock.patch('cinder.quota.QUOTAS.get_project_quotas')
@mock.patch('cinder.policy.enforce') @mock.patch('cinder.policy.enforce')
def test_used_limits(self, ver_project, _mock_policy_enforce, def test_used_limits(self, ver_project, _mock_policy_enforce,
@ -78,9 +82,9 @@ class UsedLimitsTestCase(test.TestCase):
self.controller.index(fake_req, res) self.controller.index(fake_req, res)
abs_limits = res.obj['limits']['absolute'] abs_limits = res.obj['limits']['absolute']
# if admin, only 3.39 and req contains project_id filter, cinder # if admin, only LIMITS_ADMIN_FILTER and req contains project_id
# returns the specified project's quota. # filter, cinder returns the specified project's quota.
if version == '3.39' and has_project: if version == mv.LIMITS_ADMIN_FILTER and has_project:
self.assertEqual(1, abs_limits['totalGigabytesUsed']) self.assertEqual(1, abs_limits['totalGigabytesUsed'])
else: else:
self.assertEqual(2, abs_limits['totalGigabytesUsed']) self.assertEqual(2, abs_limits['totalGigabytesUsed'])

View File

@ -24,7 +24,7 @@ from six.moves import http_client
import webob import webob
from cinder.api.contrib import volume_actions from cinder.api.contrib import volume_actions
from cinder.api.openstack import api_version_request as api_version from cinder.api import microversions as mv
from cinder import context from cinder import context
from cinder import db from cinder import db
from cinder import exception from cinder import exception
@ -818,7 +818,7 @@ class VolumeImageActionsTest(test.TestCase):
'size': 0} 'size': 0}
return ret return ret
def fake_image_service_create_3_1(self, *args): def fake_image_service_create_with_params(self, *args):
ret = { ret = {
'status': u'queued', 'status': u'queued',
'name': u'image_name', 'name': u'image_name',
@ -1017,13 +1017,13 @@ class VolumeImageActionsTest(test.TestCase):
id, id,
body) body)
@ddt.data({'version': '3.41', @ddt.data({'version': mv.get_prior_version(mv.VOLUME_EXTEND_INUSE),
'status': 'available'}, 'status': 'available'},
{'version': '3.41', {'version': mv.get_prior_version(mv.VOLUME_EXTEND_INUSE),
'status': 'in-use'}, 'status': 'in-use'},
{'version': '3.42', {'version': mv.VOLUME_EXTEND_INUSE,
'status': 'available'}, 'status': 'available'},
{'version': '3.42', {'version': mv.VOLUME_EXTEND_INUSE,
'status': 'in-use'}) 'status': 'in-use'})
@ddt.unpack @ddt.unpack
def test_extend_attached_volume(self, version, status): def test_extend_attached_volume(self, version, status):
@ -1035,9 +1035,9 @@ class VolumeImageActionsTest(test.TestCase):
body = {"os-extend": {"new_size": 2}} body = {"os-extend": {"new_size": 2}}
req = fakes.HTTPRequest.blank('/v3/%s/volumes/%s/action' % req = fakes.HTTPRequest.blank('/v3/%s/volumes/%s/action' %
(fake.PROJECT_ID, vol['id'])) (fake.PROJECT_ID, vol['id']))
req.api_version_request = api_version.APIVersionRequest(version) req.api_version_request = mv.get_api_version(version)
self.controller._extend(req, vol['id'], body) self.controller._extend(req, vol['id'], body)
if version == '3.42' and status == 'in-use': if version == mv.VOLUME_EXTEND_INUSE and status == 'in-use':
mock_extend.assert_called_with(req.environ['cinder.context'], mock_extend.assert_called_with(req.environ['cinder.context'],
vol, 2, attached=True) vol, 2, attached=True)
else: else:
@ -1117,8 +1117,8 @@ class VolumeImageActionsTest(test.TestCase):
id = 'aaaaaaaa-bbbb-cccc-dddd-eeeeeeeeeeee' id = 'aaaaaaaa-bbbb-cccc-dddd-eeeeeeeeeeee'
req = fakes.HTTPRequest.blank('/v3/tenant1/volumes/%s/action' % id) req = fakes.HTTPRequest.blank('/v3/tenant1/volumes/%s/action' % id)
req.environ['cinder.context'].is_admin = False req.environ['cinder.context'].is_admin = False
req.headers = {'OpenStack-API-Version': 'volume 3.1'} req.headers = mv.get_mv_header(mv.UPLOAD_IMAGE_PARAMS)
req.api_version_request = api_version.APIVersionRequest('3.1') req.api_version_request = mv.get_api_version(mv.UPLOAD_IMAGE_PARAMS)
body = self._get_os_volume_upload_image() body = self._get_os_volume_upload_image()
body['os-volume_upload_image']['visibility'] = 'public' body['os-volume_upload_image']['visibility'] = 'public'
self.assertRaises(exception.PolicyNotAuthorized, self.assertRaises(exception.PolicyNotAuthorized,
@ -1311,7 +1311,7 @@ class VolumeImageActionsTest(test.TestCase):
@mock.patch.object(volume_api.API, "get_volume_image_metadata") @mock.patch.object(volume_api.API, "get_volume_image_metadata")
@mock.patch.object(glance.GlanceImageService, "create") @mock.patch.object(glance.GlanceImageService, "create")
@mock.patch.object(volume_rpcapi.VolumeAPI, "copy_volume_to_image") @mock.patch.object(volume_rpcapi.VolumeAPI, "copy_volume_to_image")
def test_copy_volume_to_image_version_3_1( def test_copy_volume_to_image_version_with_params(
self, self,
mock_copy_volume_to_image, mock_copy_volume_to_image,
mock_create, mock_create,
@ -1323,7 +1323,7 @@ class VolumeImageActionsTest(test.TestCase):
"volume_id": volume.id, "volume_id": volume.id,
"key": "x_billing_code_license", "key": "x_billing_code_license",
"value": "246254365"} "value": "246254365"}
mock_create.side_effect = self.fake_image_service_create_3_1 mock_create.side_effect = self.fake_image_service_create_with_params
mock_copy_volume_to_image.side_effect = \ mock_copy_volume_to_image.side_effect = \
self.fake_rpc_copy_volume_to_image self.fake_rpc_copy_volume_to_image
@ -1333,8 +1333,8 @@ class VolumeImageActionsTest(test.TestCase):
'/v3/%s/volumes/%s/action' % (fake.PROJECT_ID, volume.id), '/v3/%s/volumes/%s/action' % (fake.PROJECT_ID, volume.id),
use_admin_context=self.context.is_admin) use_admin_context=self.context.is_admin)
req.environ['cinder.context'].is_admin = True req.environ['cinder.context'].is_admin = True
req.headers = {'OpenStack-API-Version': 'volume 3.1'} req.headers = mv.get_mv_header(mv.UPLOAD_IMAGE_PARAMS)
req.api_version_request = api_version.APIVersionRequest('3.1') req.api_version_request = mv.get_api_version(mv.UPLOAD_IMAGE_PARAMS)
body = self._get_os_volume_upload_image() body = self._get_os_volume_upload_image()
body['os-volume_upload_image']['visibility'] = 'public' body['os-volume_upload_image']['visibility'] = 'public'
body['os-volume_upload_image']['protected'] = True body['os-volume_upload_image']['protected'] = True

View File

@ -23,6 +23,7 @@ from six.moves.urllib.parse import urlencode
import webob import webob
from cinder.api.contrib import volume_manage from cinder.api.contrib import volume_manage
from cinder.api import microversions as mv
from cinder.api.openstack import api_version_request as api_version from cinder.api.openstack import api_version_request as api_version
from cinder import context from cinder import context
from cinder import exception from cinder import exception
@ -226,15 +227,15 @@ class VolumeManageTest(test.TestCase):
self.assertEqual(body['volume']['ref'], args[3]) self.assertEqual(body['volume']['ref'], args[3])
self.assertTrue(mock_validate.called) self.assertTrue(mock_validate.called)
def _get_resp_create(self, body, version='3.0'): def _get_resp_create(self, body, version=mv.BASE_VERSION):
url = '/v3/%s/os-volume-manage' % fake.PROJECT_ID url = '/v3/%s/os-volume-manage' % fake.PROJECT_ID
req = webob.Request.blank(url, base_url='http://localhost.com' + url) req = webob.Request.blank(url, base_url='http://localhost.com' + url)
req.method = 'POST' req.method = 'POST'
req.headers = mv.get_mv_header(version)
req.headers['Content-Type'] = 'application/json' req.headers['Content-Type'] = 'application/json'
req.environ['cinder.context'] = self._admin_ctxt req.environ['cinder.context'] = self._admin_ctxt
req.body = jsonutils.dump_as_bytes(body) req.body = jsonutils.dump_as_bytes(body)
req.headers = {'OpenStack-API-Version': 'volume %s' % version} req.api_version_request = mv.get_api_version(version)
req.api_version_request = api_version.APIVersionRequest(version)
res = self.controller.create(req, body) res = self.controller.create(req, body)
return res return res
@ -244,7 +245,7 @@ class VolumeManageTest(test.TestCase):
def test_manage_volume_ok_cluster(self, mock_validate, mock_api_manage): def test_manage_volume_ok_cluster(self, mock_validate, mock_api_manage):
body = {'volume': {'cluster': 'cluster', body = {'volume': {'cluster': 'cluster',
'ref': 'fake_ref'}} 'ref': 'fake_ref'}}
res = self._get_resp_create(body, '3.16') res = self._get_resp_create(body, mv.VOLUME_MIGRATE_CLUSTER)
self.assertEqual(['volume'], list(res.keys())) self.assertEqual(['volume'], list(res.keys()))
# Check that the manage API was called with the correct arguments. # Check that the manage API was called with the correct arguments.
@ -262,7 +263,8 @@ class VolumeManageTest(test.TestCase):
'cluster': 'cluster', 'cluster': 'cluster',
'ref': 'fake_ref'}} 'ref': 'fake_ref'}}
self.assertRaises(exception.InvalidInput, self.assertRaises(exception.InvalidInput,
self._get_resp_create, body, '3.16') self._get_resp_create, body,
mv.VOLUME_MIGRATE_CLUSTER)
def test_manage_volume_missing_host(self): def test_manage_volume_missing_host(self):
"""Test correct failure when host is not specified.""" """Test correct failure when host is not specified."""
@ -485,7 +487,7 @@ class VolumeManageTest(test.TestCase):
"""Test managing volume to return 'creating' status in V3 API.""" """Test managing volume to return 'creating' status in V3 API."""
body = {'volume': {'host': 'host_ok', body = {'volume': {'host': 'host_ok',
'ref': 'fake_ref'}} 'ref': 'fake_ref'}}
res = self._get_resp_post_v3(body, '3.15') res = self._get_resp_post_v3(body, mv.ETAGS)
self.assertEqual(http_client.ACCEPTED, res.status_int) self.assertEqual(http_client.ACCEPTED, res.status_int)
self.assertEqual(1, mock_api_manage.call_count) self.assertEqual(1, mock_api_manage.call_count)
self.assertEqual('creating', self.assertEqual('creating',

View File

@ -21,6 +21,7 @@ import ddt
import mock import mock
import webob import webob
from cinder.api import microversions as mv
from cinder.api.v3 import attachments as v3_attachments from cinder.api.v3 import attachments as v3_attachments
from cinder import context from cinder import context
from cinder import exception from cinder import exception
@ -31,8 +32,6 @@ from cinder.tests.unit import fake_constants as fake
from cinder.volume import api as volume_api from cinder.volume import api as volume_api
from cinder.volume import rpcapi as volume_rpcapi from cinder.volume import rpcapi as volume_rpcapi
ATTACHMENTS_MICRO_VERSION = '3.27'
@ddt.ddt @ddt.ddt
class AttachmentsAPITestCase(test.TestCase): class AttachmentsAPITestCase(test.TestCase):
@ -81,7 +80,7 @@ class AttachmentsAPITestCase(test.TestCase):
def test_create_attachment(self): def test_create_attachment(self):
req = fakes.HTTPRequest.blank('/v3/%s/attachments' % req = fakes.HTTPRequest.blank('/v3/%s/attachments' %
fake.PROJECT_ID, fake.PROJECT_ID,
version=ATTACHMENTS_MICRO_VERSION) version=mv.NEW_ATTACH)
body = { body = {
"attachment": "attachment":
{ {
@ -104,7 +103,7 @@ class AttachmentsAPITestCase(test.TestCase):
mock_update.return_value = fake_connector mock_update.return_value = fake_connector
req = fakes.HTTPRequest.blank('/v3/%s/attachments/%s' % req = fakes.HTTPRequest.blank('/v3/%s/attachments/%s' %
(fake.PROJECT_ID, self.attachment1.id), (fake.PROJECT_ID, self.attachment1.id),
version=ATTACHMENTS_MICRO_VERSION, version=mv.NEW_ATTACH,
use_admin_context=True) use_admin_context=True)
body = { body = {
"attachment": "attachment":
@ -124,7 +123,7 @@ class AttachmentsAPITestCase(test.TestCase):
mock_get.return_value = {'project_id': fake.PROJECT2_ID} mock_get.return_value = {'project_id': fake.PROJECT2_ID}
req = fakes.HTTPRequest.blank('/v3/%s/attachments/%s' % req = fakes.HTTPRequest.blank('/v3/%s/attachments/%s' %
(fake.PROJECT_ID, self.attachment1.id), (fake.PROJECT_ID, self.attachment1.id),
version=ATTACHMENTS_MICRO_VERSION, version=mv.NEW_ATTACH,
use_admin_context=False) use_admin_context=False)
body = { body = {
"attachment": "attachment":
@ -139,7 +138,8 @@ class AttachmentsAPITestCase(test.TestCase):
self.controller.delete, req, self.controller.delete, req,
self.attachment1.id) self.attachment1.id)
@ddt.data('3.30', '3.31', '3.34') @ddt.data(mv.get_prior_version(mv.RESOURCE_FILTER),
mv.RESOURCE_FILTER, mv.LIKE_FILTER)
@mock.patch('cinder.api.common.reject_invalid_filters') @mock.patch('cinder.api.common.reject_invalid_filters')
def test_attachment_list_with_general_filter(self, version, mock_update): def test_attachment_list_with_general_filter(self, version, mock_update):
url = '/v3/%s/attachments' % fake.PROJECT_ID url = '/v3/%s/attachments' % fake.PROJECT_ID
@ -148,8 +148,8 @@ class AttachmentsAPITestCase(test.TestCase):
use_admin_context=False) use_admin_context=False)
self.controller.index(req) self.controller.index(req)
if version != '3.30': if version != mv.get_prior_version(mv.RESOURCE_FILTER):
support_like = True if version == '3.34' else False support_like = True if version == mv.LIKE_FILTER else False
mock_update.assert_called_once_with(req.environ['cinder.context'], mock_update.assert_called_once_with(req.environ['cinder.context'],
mock.ANY, 'attachment', mock.ANY, 'attachment',
support_like) support_like)
@ -164,7 +164,7 @@ class AttachmentsAPITestCase(test.TestCase):
attach_status=status) attach_status=status)
req = fakes.HTTPRequest.blank('/v3/%s/attachments/%s' % req = fakes.HTTPRequest.blank('/v3/%s/attachments/%s' %
(fake.PROJECT_ID, attachment.id), (fake.PROJECT_ID, attachment.id),
version=ATTACHMENTS_MICRO_VERSION, version=mv.NEW_ATTACH,
use_admin_context=True) use_admin_context=True)
self.controller.delete(req, attachment.id) self.controller.delete(req, attachment.id)
@ -201,7 +201,7 @@ class AttachmentsAPITestCase(test.TestCase):
def test_create_attachment_without_resource_uuid(self, resource_uuid): def test_create_attachment_without_resource_uuid(self, resource_uuid):
req = fakes.HTTPRequest.blank('/v3/%s/attachments' % req = fakes.HTTPRequest.blank('/v3/%s/attachments' %
fake.PROJECT_ID, fake.PROJECT_ID,
version=ATTACHMENTS_MICRO_VERSION) version=mv.NEW_ATTACH)
body = { body = {
"attachment": "attachment":
{ {
@ -220,7 +220,7 @@ class AttachmentsAPITestCase(test.TestCase):
if is_detail: if is_detail:
url = '/v3/%s/groups/detail' % fake.PROJECT_ID url = '/v3/%s/groups/detail' % fake.PROJECT_ID
list_func = self.controller.detail list_func = self.controller.detail
req = fakes.HTTPRequest.blank(url, version=ATTACHMENTS_MICRO_VERSION, req = fakes.HTTPRequest.blank(url, version=mv.NEW_ATTACH,
use_admin_context=True) use_admin_context=True)
res_dict = list_func(req) res_dict = list_func(req)
@ -231,7 +231,7 @@ class AttachmentsAPITestCase(test.TestCase):
def test_list_attachments_with_limit(self): def test_list_attachments_with_limit(self):
url = '/v3/%s/attachments?limit=1' % fake.PROJECT_ID url = '/v3/%s/attachments?limit=1' % fake.PROJECT_ID
req = fakes.HTTPRequest.blank(url, version=ATTACHMENTS_MICRO_VERSION, req = fakes.HTTPRequest.blank(url, version=mv.NEW_ATTACH,
use_admin_context=True) use_admin_context=True)
res_dict = self.controller.index(req) res_dict = self.controller.index(req)
@ -241,7 +241,7 @@ class AttachmentsAPITestCase(test.TestCase):
def test_list_attachments_with_marker(self): def test_list_attachments_with_marker(self):
url = '/v3/%s/attachments?marker=%s' % (fake.PROJECT_ID, url = '/v3/%s/attachments?marker=%s' % (fake.PROJECT_ID,
self.attachment3.id) self.attachment3.id)
req = fakes.HTTPRequest.blank(url, version=ATTACHMENTS_MICRO_VERSION, req = fakes.HTTPRequest.blank(url, version=mv.NEW_ATTACH,
use_admin_context=True) use_admin_context=True)
res_dict = self.controller.index(req) res_dict = self.controller.index(req)
@ -254,7 +254,7 @@ class AttachmentsAPITestCase(test.TestCase):
def test_list_attachments_with_sort(self, sort_dir): def test_list_attachments_with_sort(self, sort_dir):
url = '/v3/%s/attachments?sort_key=id&sort_dir=%s' % (fake.PROJECT_ID, url = '/v3/%s/attachments?sort_key=id&sort_dir=%s' % (fake.PROJECT_ID,
sort_dir) sort_dir)
req = fakes.HTTPRequest.blank(url, version=ATTACHMENTS_MICRO_VERSION, req = fakes.HTTPRequest.blank(url, version=mv.NEW_ATTACH,
use_admin_context=True) use_admin_context=True)
res_dict = self.controller.index(req) res_dict = self.controller.index(req)
@ -280,7 +280,7 @@ class AttachmentsAPITestCase(test.TestCase):
@ddt.unpack @ddt.unpack
def test_list_attachment_with_tenants(self, admin, request_url, count): def test_list_attachment_with_tenants(self, admin, request_url, count):
url = '/v3/%s/attachments%s' % (fake.PROJECT_ID, request_url) url = '/v3/%s/attachments%s' % (fake.PROJECT_ID, request_url)
req = fakes.HTTPRequest.blank(url, version=ATTACHMENTS_MICRO_VERSION, req = fakes.HTTPRequest.blank(url, version=mv.NEW_ATTACH,
use_admin_context=admin) use_admin_context=admin)
res_dict = self.controller.index(req) res_dict = self.controller.index(req)

View File

@ -19,6 +19,7 @@ import ddt
import mock import mock
import webob import webob
from cinder.api import microversions as mv
from cinder.api.openstack import api_version_request as api_version from cinder.api.openstack import api_version_request as api_version
from cinder.api.v3 import backups from cinder.api.v3 import backups
from cinder.api.views import backups as backup_view from cinder.api.views import backups as backup_view
@ -44,7 +45,7 @@ class BackupsControllerAPITestCase(test.TestCase):
is_admin=True) is_admin=True)
self.controller = backups.BackupsController() self.controller = backups.BackupsController()
def _fake_update_request(self, backup_id, version='3.9'): def _fake_update_request(self, backup_id, version=mv.BACKUP_UPDATE):
req = fakes.HTTPRequest.blank('/v3/%s/backups/%s/update' % req = fakes.HTTPRequest.blank('/v3/%s/backups/%s/update' %
(fake.PROJECT_ID, backup_id)) (fake.PROJECT_ID, backup_id))
req.environ['cinder.context'].is_admin = True req.environ['cinder.context'].is_admin = True
@ -54,7 +55,8 @@ class BackupsControllerAPITestCase(test.TestCase):
return req return req
def test_update_wrong_version(self): def test_update_wrong_version(self):
req = self._fake_update_request(fake.BACKUP_ID, version='3.6') req = self._fake_update_request(
fake.BACKUP_ID, version=mv.get_prior_version(mv.BACKUP_UPDATE))
body = {"backup": {"name": "Updated Test Name", }} body = {"backup": {"name": "Updated Test Name", }}
self.assertRaises(exception.VersionNotFoundForAPIMethod, self.assertRaises(exception.VersionNotFoundForAPIMethod,
self.controller.update, req, fake.BACKUP_ID, self.controller.update, req, fake.BACKUP_ID,
@ -86,7 +88,9 @@ class BackupsControllerAPITestCase(test.TestCase):
self.controller.update, self.controller.update,
req, fake.BACKUP_ID, body) req, fake.BACKUP_ID, body)
@ddt.data('3.30', '3.31', '3.34') @ddt.data(mv.get_prior_version(mv.RESOURCE_FILTER),
mv.RESOURCE_FILTER,
mv.LIKE_FILTER)
@mock.patch('cinder.api.common.reject_invalid_filters') @mock.patch('cinder.api.common.reject_invalid_filters')
def test_backup_list_with_general_filter(self, version, mock_update): def test_backup_list_with_general_filter(self, version, mock_update):
url = '/v3/%s/backups' % fake.PROJECT_ID url = '/v3/%s/backups' % fake.PROJECT_ID
@ -95,13 +99,14 @@ class BackupsControllerAPITestCase(test.TestCase):
use_admin_context=False) use_admin_context=False)
self.controller.index(req) self.controller.index(req)
if version != '3.30': if version != mv.get_prior_version(mv.RESOURCE_FILTER):
support_like = True if version == '3.34' else False support_like = True if version == mv.LIKE_FILTER else False
mock_update.assert_called_once_with(req.environ['cinder.context'], mock_update.assert_called_once_with(req.environ['cinder.context'],
mock.ANY, 'backup', mock.ANY, 'backup',
support_like) support_like)
@ddt.data('3.36', '3.37') @ddt.data(mv.get_prior_version(mv.BACKUP_SORT_NAME),
mv.BACKUP_SORT_NAME)
def test_backup_list_with_name(self, version): def test_backup_list_with_name(self, version):
backup1 = test_utils.create_backup( backup1 = test_utils.create_backup(
self.ctxt, display_name='b_test_name', self.ctxt, display_name='b_test_name',
@ -111,7 +116,7 @@ class BackupsControllerAPITestCase(test.TestCase):
status=fields.BackupStatus.AVAILABLE) status=fields.BackupStatus.AVAILABLE)
url = '/v3/%s/backups?sort_key=name' % fake.PROJECT_ID url = '/v3/%s/backups?sort_key=name' % fake.PROJECT_ID
req = fakes.HTTPRequest.blank(url, version=version) req = fakes.HTTPRequest.blank(url, version=version)
if version == '3.36': if version == mv.get_prior_version(mv.BACKUP_SORT_NAME):
self.assertRaises(exception.InvalidInput, self.assertRaises(exception.InvalidInput,
self.controller.index, self.controller.index,
req) req)

View File

@ -21,6 +21,7 @@ import mock
from oslo_utils import versionutils from oslo_utils import versionutils
from cinder.api import extensions from cinder.api import extensions
from cinder.api import microversions as mv
from cinder.api.openstack import api_version_request as api_version from cinder.api.openstack import api_version_request as api_version
from cinder.api.v3 import clusters from cinder.api.v3 import clusters
from cinder import context from cinder import context
@ -78,7 +79,7 @@ EXPECTED = [{'created_at': datetime.datetime(2016, 6, 1, 2, 46, 28),
class FakeRequest(object): class FakeRequest(object):
def __init__(self, is_admin=True, version='3.7', **kwargs): def __init__(self, is_admin=True, version=mv.CLUSTER_SUPPORT, **kwargs):
self.GET = kwargs self.GET = kwargs
self.headers = {'OpenStack-API-Version': 'volume ' + version} self.headers = {'OpenStack-API-Version': 'volume ' + version}
self.api_version_request = api_version.APIVersionRequest(version) self.api_version_request = api_version.APIVersionRequest(version)
@ -108,8 +109,10 @@ class ClustersTestCase(test.TestCase):
REPLICATION_FILTERS = ({'replication_status': 'error'}, {'frozen': True}, REPLICATION_FILTERS = ({'replication_status': 'error'}, {'frozen': True},
{'active_backend_id': 'replication'}) {'active_backend_id': 'replication'})
def _get_expected(self, version='3.8'): def _get_expected(self,
if versionutils.convert_version_to_tuple(version) >= (3, 19): version=mv.get_prior_version(mv.REPLICATION_CLUSTER)):
if (versionutils.convert_version_to_tuple(version) >=
versionutils.convert_version_to_tuple(mv.REPLICATION_CLUSTER)):
return EXPECTED return EXPECTED
expect = [] expect = []
@ -130,7 +133,7 @@ class ClustersTestCase(test.TestCase):
@mock.patch('cinder.db.cluster_get_all', return_value=CLUSTERS_ORM) @mock.patch('cinder.db.cluster_get_all', return_value=CLUSTERS_ORM)
def _test_list(self, get_all_mock, detailed, filters=None, expected=None, def _test_list(self, get_all_mock, detailed, filters=None, expected=None,
version='3.8'): version=mv.get_prior_version(mv.REPLICATION_CLUSTER)):
filters = filters or {} filters = filters or {}
req = FakeRequest(version=version, **filters) req = FakeRequest(version=version, **filters)
method = getattr(self.controller, 'detail' if detailed else 'index') method = getattr(self.controller, 'detail' if detailed else 'index')
@ -187,14 +190,13 @@ class ClustersTestCase(test.TestCase):
"""Verify the wrong version so that user can't list clusters.""" """Verify the wrong version so that user can't list clusters."""
self.assertRaises(exception.VersionNotFoundForAPIMethod, self.assertRaises(exception.VersionNotFoundForAPIMethod,
self._test_list, detailed=detailed, self._test_list, detailed=detailed,
version='3.6') version=mv.get_prior_version(mv.CLUSTER_SUPPORT))
@ddt.data(*REPLICATION_FILTERS) @ddt.data(*REPLICATION_FILTERS)
def test_index_detail_replication_new_fields(self, filters): def test_index_detail_replication_new_fields(self, filters):
version = '3.26' expected = {'clusters': self._get_expected(mv.REPLICATION_CLUSTER)}
expected = {'clusters': self._get_expected(version)}
self._test_list(detailed=True, filters=filters, expected=expected, self._test_list(detailed=True, filters=filters, expected=expected,
version=version) version=mv.REPLICATION_CLUSTER)
@ddt.data(*REPLICATION_FILTERS) @ddt.data(*REPLICATION_FILTERS)
def test_index_summary_replication_new_fields(self, filters): def test_index_summary_replication_new_fields(self, filters):
@ -209,7 +211,7 @@ class ClustersTestCase(test.TestCase):
'replication_status': 'error', 'replication_status': 'error',
'status': 'disabled'}]} 'status': 'disabled'}]}
self._test_list(detailed=False, filters=filters, expected=expected, self._test_list(detailed=False, filters=filters, expected=expected,
version='3.26') version=mv.REPLICATION_CLUSTER)
@mock.patch('cinder.db.sqlalchemy.api.cluster_get', @mock.patch('cinder.db.sqlalchemy.api.cluster_get',
return_value=CLUSTERS_ORM[0]) return_value=CLUSTERS_ORM[0])
@ -232,7 +234,7 @@ class ClustersTestCase(test.TestCase):
self.controller.show, req, 'name') self.controller.show, req, 'name')
def test_show_wrong_version(self): def test_show_wrong_version(self):
req = FakeRequest(version='3.5') req = FakeRequest(version=mv.get_prior_version(mv.CLUSTER_SUPPORT))
self.assertRaises(exception.VersionNotFoundForAPIMethod, self.assertRaises(exception.VersionNotFoundForAPIMethod,
self.controller.show, req, 'name') self.controller.show, req, 'name')
@ -307,6 +309,6 @@ class ClustersTestCase(test.TestCase):
@ddt.data('enable', 'disable') @ddt.data('enable', 'disable')
def test_update_wrong_version(self, action): def test_update_wrong_version(self, action):
req = FakeRequest(version='3.5') req = FakeRequest(version=mv.get_prior_version(mv.CLUSTER_SUPPORT))
self.assertRaises(exception.VersionNotFoundForAPIMethod, self.assertRaises(exception.VersionNotFoundForAPIMethod,
self.controller.update, req, action, {}) self.controller.update, req, action, {})

View File

@ -15,6 +15,7 @@ import ddt
from six.moves import http_client from six.moves import http_client
import webob import webob
from cinder.api import microversions as mv
from cinder.api.openstack import api_version_request as api_version from cinder.api.openstack import api_version_request as api_version
from cinder.api.v3 import consistencygroups from cinder.api.v3 import consistencygroups
from cinder import context from cinder import context
@ -72,9 +73,10 @@ class ConsistencyGroupsAPITestCase(test.TestCase):
req = fakes.HTTPRequest.blank('/v3/%s/consistencygroups/%s/update' % req = fakes.HTTPRequest.blank('/v3/%s/consistencygroups/%s/update' %
(fake.PROJECT_ID, consistencygroup.id)) (fake.PROJECT_ID, consistencygroup.id))
req.environ['cinder.context'].is_admin = True req.environ['cinder.context'].is_admin = True
req.headers = mv.get_mv_header(mv.CG_UPDATE_BLANK_PROPERTIES)
req.headers['Content-Type'] = 'application/json' req.headers['Content-Type'] = 'application/json'
req.headers['OpenStack-API-Version'] = 'volume 3.6' req.api_version_request = mv.get_api_version(
req.api_version_request = api_version.APIVersionRequest('3.6') mv.CG_UPDATE_BLANK_PROPERTIES)
body = {"consistencygroup": {"name": "", body = {"consistencygroup": {"name": "",
"description": "", "description": "",
"add_volumes": None, "add_volumes": None,
@ -118,9 +120,10 @@ class ConsistencyGroupsAPITestCase(test.TestCase):
req = fakes.HTTPRequest.blank('/v3/%s/consistencygroups/%s/update' % req = fakes.HTTPRequest.blank('/v3/%s/consistencygroups/%s/update' %
(fake.PROJECT_ID, consistencygroup.id)) (fake.PROJECT_ID, consistencygroup.id))
req.environ['cinder.context'].is_admin = True req.environ['cinder.context'].is_admin = True
req.headers = mv.get_mv_header(mv.CG_UPDATE_BLANK_PROPERTIES)
req.headers['Content-Type'] = 'application/json' req.headers['Content-Type'] = 'application/json'
req.headers['OpenStack-API-Version'] = 'volume 3.6' req.api_version_request = mv.get_api_version(
req.api_version_request = api_version.APIVersionRequest('3.6') mv.CG_UPDATE_BLANK_PROPERTIES)
body = {"consistencygroup": {"name": None, body = {"consistencygroup": {"name": None,
"description": None, "description": None,
"add_volumes": None, "add_volumes": None,
@ -132,16 +135,20 @@ class ConsistencyGroupsAPITestCase(test.TestCase):
req, consistencygroup.id, body) req, consistencygroup.id, body)
consistencygroup.destroy() consistencygroup.destroy()
def test_update_consistencygroup_all_empty_parameters_not_version_36(self): def test_update_consistencygroup_all_empty_parameters_not_version_ok(self):
consistencygroup = self._create_consistencygroup( consistencygroup = self._create_consistencygroup(
ctxt=self.ctxt, ctxt=self.ctxt,
status=fields.ConsistencyGroupStatus.AVAILABLE) status=fields.ConsistencyGroupStatus.AVAILABLE)
req = fakes.HTTPRequest.blank('/v3/%s/consistencygroups/%s/update' % req = fakes.HTTPRequest.blank('/v3/%s/consistencygroups/%s/update' %
(fake.PROJECT_ID, consistencygroup.id)) (fake.PROJECT_ID, consistencygroup.id))
req.environ['cinder.context'].is_admin = True req.environ['cinder.context'].is_admin = True
non_supported_version = mv.get_prior_version(
mv.CG_UPDATE_BLANK_PROPERTIES)
req.headers = mv.get_mv_header(non_supported_version)
req.api_version_request = mv.get_api_version(non_supported_version)
req.headers['Content-Type'] = 'application/json' req.headers['Content-Type'] = 'application/json'
req.headers['OpenStack-API-Version'] = 'volume 3.5'
req.api_version_request = api_version.APIVersionRequest('3.5')
body = {"consistencygroup": {"name": None, body = {"consistencygroup": {"name": None,
"description": None, "description": None,
"add_volumes": None, "add_volumes": None,
@ -160,9 +167,13 @@ class ConsistencyGroupsAPITestCase(test.TestCase):
req = fakes.HTTPRequest.blank('/v3/%s/consistencygroups/%s/update' % req = fakes.HTTPRequest.blank('/v3/%s/consistencygroups/%s/update' %
(fake.PROJECT_ID, consistencygroup.id)) (fake.PROJECT_ID, consistencygroup.id))
req.environ['cinder.context'].is_admin = True req.environ['cinder.context'].is_admin = True
non_supported_version = mv.get_prior_version(
mv.CG_UPDATE_BLANK_PROPERTIES)
req.headers = mv.get_mv_header(non_supported_version)
req.api_version_request = mv.get_api_version(non_supported_version)
req.headers['Content-Type'] = 'application/json' req.headers['Content-Type'] = 'application/json'
req.headers['OpenStack-API-Version'] = 'volume 3.5'
req.api_version_request = api_version.APIVersionRequest('3.5')
body = None body = None
self.assertRaisesRegexp(webob.exc.HTTPBadRequest, self.assertRaisesRegexp(webob.exc.HTTPBadRequest,
"Missing request body", "Missing request body",
@ -177,9 +188,13 @@ class ConsistencyGroupsAPITestCase(test.TestCase):
req = fakes.HTTPRequest.blank('/v3/%s/consistencygroups/%s/update' % req = fakes.HTTPRequest.blank('/v3/%s/consistencygroups/%s/update' %
(fake.PROJECT_ID, consistencygroup.id)) (fake.PROJECT_ID, consistencygroup.id))
req.environ['cinder.context'].is_admin = True req.environ['cinder.context'].is_admin = True
non_supported_version = mv.get_prior_version(
mv.CG_UPDATE_BLANK_PROPERTIES)
req.headers = mv.get_mv_header(non_supported_version)
req.headers['Content-Type'] = 'application/json' req.headers['Content-Type'] = 'application/json'
req.headers['OpenStack-API-Version'] = 'volume 3.5'
req.api_version_request = api_version.APIVersionRequest('3.5') req.api_version_request = mv.get_api_version(non_supported_version)
body = {"consistencygroup": {"name": "my_fake_cg", body = {"consistencygroup": {"name": "my_fake_cg",
"description": "fake consistency group", "description": "fake consistency group",
"add_volumes": "volume-uuid-1", "add_volumes": "volume-uuid-1",

View File

@ -22,6 +22,7 @@ import mock
from six.moves import http_client from six.moves import http_client
import webob import webob
from cinder.api import microversions as mv
from cinder.api.v3 import group_snapshots as v3_group_snapshots from cinder.api.v3 import group_snapshots as v3_group_snapshots
from cinder import context from cinder import context
from cinder import db from cinder import db
@ -35,9 +36,6 @@ from cinder.tests.unit import fake_constants as fake
from cinder.tests.unit import utils from cinder.tests.unit import utils
import cinder.volume import cinder.volume
GROUP_MICRO_VERSION = '3.14'
SUPPORT_FILTER_VERSION = '3.29'
@ddt.ddt @ddt.ddt
class GroupSnapshotsAPITestCase(test.TestCase): class GroupSnapshotsAPITestCase(test.TestCase):
@ -76,7 +74,7 @@ class GroupSnapshotsAPITestCase(test.TestCase):
self.context, group_id=self.group.id) self.context, group_id=self.group.id)
req = fakes.HTTPRequest.blank('/v3/%s/group_snapshots/%s' % req = fakes.HTTPRequest.blank('/v3/%s/group_snapshots/%s' %
(fake.PROJECT_ID, group_snapshot.id), (fake.PROJECT_ID, group_snapshot.id),
version=GROUP_MICRO_VERSION) version=mv.GROUP_SNAPSHOTS)
res_dict = self.controller.show(req, group_snapshot.id) res_dict = self.controller.show(req, group_snapshot.id)
self.assertEqual(1, len(res_dict)) self.assertEqual(1, len(res_dict))
@ -95,7 +93,8 @@ class GroupSnapshotsAPITestCase(test.TestCase):
url = '/v3/%s/group_snapshots?limit=1' % fake.PROJECT_ID url = '/v3/%s/group_snapshots?limit=1' % fake.PROJECT_ID
if is_detail: if is_detail:
url = '/v3/%s/group_snapshots/detail?limit=1' % fake.PROJECT_ID url = '/v3/%s/group_snapshots/detail?limit=1' % fake.PROJECT_ID
req = fakes.HTTPRequest.blank(url, version=SUPPORT_FILTER_VERSION) req = fakes.HTTPRequest.blank(url,
version=mv.GROUP_SNAPSHOT_PAGINATION)
if is_detail: if is_detail:
res_dict = self.controller.detail(req) res_dict = self.controller.detail(req)
else: else:
@ -122,7 +121,8 @@ class GroupSnapshotsAPITestCase(test.TestCase):
url = '/v3/%s/group_snapshots?offset=1' % fake.PROJECT_ID url = '/v3/%s/group_snapshots?offset=1' % fake.PROJECT_ID
if is_detail: if is_detail:
url = '/v3/%s/group_snapshots/detail?offset=1' % fake.PROJECT_ID url = '/v3/%s/group_snapshots/detail?offset=1' % fake.PROJECT_ID
req = fakes.HTTPRequest.blank(url, version=SUPPORT_FILTER_VERSION) req = fakes.HTTPRequest.blank(url,
version=mv.GROUP_SNAPSHOT_PAGINATION)
if is_detail: if is_detail:
res_dict = self.controller.detail(req) res_dict = self.controller.detail(req)
else: else:
@ -146,7 +146,8 @@ class GroupSnapshotsAPITestCase(test.TestCase):
if is_detail: if is_detail:
url = ('/v3/%s/group_snapshots/detail?offset=234523423455454' % url = ('/v3/%s/group_snapshots/detail?offset=234523423455454' %
fake.PROJECT_ID) fake.PROJECT_ID)
req = fakes.HTTPRequest.blank(url, version=SUPPORT_FILTER_VERSION) req = fakes.HTTPRequest.blank(url,
version=mv.GROUP_SNAPSHOT_PAGINATION)
if is_detail: if is_detail:
self.assertRaises(webob.exc.HTTPBadRequest, self.controller.detail, self.assertRaises(webob.exc.HTTPBadRequest, self.controller.detail,
req) req)
@ -164,7 +165,8 @@ class GroupSnapshotsAPITestCase(test.TestCase):
if is_detail: if is_detail:
url = ('/v3/%s/group_snapshots/detail?limit=2&offset=1' % url = ('/v3/%s/group_snapshots/detail?limit=2&offset=1' %
fake.PROJECT_ID) fake.PROJECT_ID)
req = fakes.HTTPRequest.blank(url, version=SUPPORT_FILTER_VERSION) req = fakes.HTTPRequest.blank(url,
version=mv.GROUP_SNAPSHOT_PAGINATION)
if is_detail: if is_detail:
res_dict = self.controller.detail(req) res_dict = self.controller.detail(req)
else: else:
@ -184,7 +186,9 @@ class GroupSnapshotsAPITestCase(test.TestCase):
res_dict['group_snapshots'][0].keys()) res_dict['group_snapshots'][0].keys())
group_snapshot.destroy() group_snapshot.destroy()
@ddt.data('3.30', '3.31', '3.34') @ddt.data(mv.get_prior_version(mv.RESOURCE_FILTER),
mv.RESOURCE_FILTER,
mv.LIKE_FILTER)
@mock.patch('cinder.api.common.reject_invalid_filters') @mock.patch('cinder.api.common.reject_invalid_filters')
def test_group_snapshot_list_with_general_filter(self, def test_group_snapshot_list_with_general_filter(self,
version, mock_update): version, mock_update):
@ -194,8 +198,8 @@ class GroupSnapshotsAPITestCase(test.TestCase):
use_admin_context=False) use_admin_context=False)
self.controller.index(req) self.controller.index(req)
if version != '3.30': if version != mv.get_prior_version(mv.RESOURCE_FILTER):
support_like = True if version == '3.34' else False support_like = True if version == mv.LIKE_FILTER else False
mock_update.assert_called_once_with(req.environ['cinder.context'], mock_update.assert_called_once_with(req.environ['cinder.context'],
mock.ANY, 'group_snapshot', mock.ANY, 'group_snapshot',
support_like) support_like)
@ -209,7 +213,8 @@ class GroupSnapshotsAPITestCase(test.TestCase):
url = ('/v3/%s/group_snapshots/detail?' url = ('/v3/%s/group_snapshots/detail?'
'all_tenants=True&id=%s') % (fake.PROJECT_ID, 'all_tenants=True&id=%s') % (fake.PROJECT_ID,
self.g_snapshots_array[0].id) self.g_snapshots_array[0].id)
req = fakes.HTTPRequest.blank(url, version=SUPPORT_FILTER_VERSION, req = fakes.HTTPRequest.blank(url,
version=mv.GROUP_SNAPSHOT_PAGINATION,
use_admin_context=True) use_admin_context=True)
if is_detail: if is_detail:
res_dict = self.controller.detail(req) res_dict = self.controller.detail(req)
@ -226,10 +231,10 @@ class GroupSnapshotsAPITestCase(test.TestCase):
self.assertNotIn('description', self.assertNotIn('description',
res_dict['group_snapshots'][0].keys()) res_dict['group_snapshots'][0].keys())
@ddt.data({'is_detail': True, 'version': GROUP_MICRO_VERSION}, @ddt.data({'is_detail': True, 'version': mv.GROUP_SNAPSHOTS},
{'is_detail': False, 'version': GROUP_MICRO_VERSION}, {'is_detail': False, 'version': mv.GROUP_SNAPSHOTS},
{'is_detail': True, 'version': '3.28'}, {'is_detail': True, 'version': mv.POOL_FILTER},
{'is_detail': False, 'version': '3.28'},) {'is_detail': False, 'version': mv.POOL_FILTER},)
@ddt.unpack @ddt.unpack
def test_list_group_snapshot_with_filter_previous_version(self, is_detail, def test_list_group_snapshot_with_filter_previous_version(self, is_detail,
version): version):
@ -257,7 +262,8 @@ class GroupSnapshotsAPITestCase(test.TestCase):
if is_detail: if is_detail:
url = ('/v3/%s/group_snapshots/detail?sort=id:asc' % url = ('/v3/%s/group_snapshots/detail?sort=id:asc' %
fake.PROJECT_ID) fake.PROJECT_ID)
req = fakes.HTTPRequest.blank(url, version=SUPPORT_FILTER_VERSION) req = fakes.HTTPRequest.blank(url,
version=mv.GROUP_SNAPSHOT_PAGINATION)
expect_result = [snapshot.id for snapshot in self.g_snapshots_array] expect_result = [snapshot.id for snapshot in self.g_snapshots_array]
expect_result.sort() expect_result.sort()
if is_detail: if is_detail:
@ -282,7 +288,7 @@ class GroupSnapshotsAPITestCase(test.TestCase):
req = fakes.HTTPRequest.blank('/v3/%s/group_snapshots/%s' % req = fakes.HTTPRequest.blank('/v3/%s/group_snapshots/%s' %
(fake.PROJECT_ID, (fake.PROJECT_ID,
fake.WILL_NOT_BE_FOUND_ID), fake.WILL_NOT_BE_FOUND_ID),
version=GROUP_MICRO_VERSION) version=mv.GROUP_SNAPSHOTS)
self.assertRaises(exception.GroupSnapshotNotFound, self.assertRaises(exception.GroupSnapshotNotFound,
self.controller.show, self.controller.show,
req, fake.WILL_NOT_BE_FOUND_ID) req, fake.WILL_NOT_BE_FOUND_ID)
@ -294,7 +300,7 @@ class GroupSnapshotsAPITestCase(test.TestCase):
else: else:
request_url = '/v3/%s/group_snapshots' request_url = '/v3/%s/group_snapshots'
req = fakes.HTTPRequest.blank(request_url % fake.PROJECT_ID, req = fakes.HTTPRequest.blank(request_url % fake.PROJECT_ID,
version=GROUP_MICRO_VERSION) version=mv.GROUP_SNAPSHOTS)
if is_detail: if is_detail:
res_dict = self.controller.detail(req) res_dict = self.controller.detail(req)
else: else:
@ -326,7 +332,7 @@ class GroupSnapshotsAPITestCase(test.TestCase):
"group_id": self.group.id}} "group_id": self.group.id}}
req = fakes.HTTPRequest.blank('/v3/%s/group_snapshots' % req = fakes.HTTPRequest.blank('/v3/%s/group_snapshots' %
fake.PROJECT_ID, fake.PROJECT_ID,
version=GROUP_MICRO_VERSION) version=mv.GROUP_SNAPSHOTS)
res_dict = self.controller.create(req, body) res_dict = self.controller.create(req, body)
self.assertEqual(1, len(res_dict)) self.assertEqual(1, len(res_dict))
@ -356,7 +362,7 @@ class GroupSnapshotsAPITestCase(test.TestCase):
"group_id": group.id}} "group_id": group.id}}
req = fakes.HTTPRequest.blank('/v3/%s/group_snapshots' % req = fakes.HTTPRequest.blank('/v3/%s/group_snapshots' %
fake.PROJECT_ID, fake.PROJECT_ID,
version=GROUP_MICRO_VERSION) version=mv.GROUP_SNAPSHOTS)
self.assertRaises(webob.exc.HTTPBadRequest, self.controller.create, self.assertRaises(webob.exc.HTTPBadRequest, self.controller.create,
req, body) req, body)
self.assertTrue(mock_validate.called) self.assertTrue(mock_validate.called)
@ -369,7 +375,7 @@ class GroupSnapshotsAPITestCase(test.TestCase):
# omit body from the request # omit body from the request
req = fakes.HTTPRequest.blank('/v3/%s/group_snapshots' % req = fakes.HTTPRequest.blank('/v3/%s/group_snapshots' %
fake.PROJECT_ID, fake.PROJECT_ID,
version=GROUP_MICRO_VERSION) version=mv.GROUP_SNAPSHOTS)
self.assertRaises(webob.exc.HTTPBadRequest, self.controller.create, self.assertRaises(webob.exc.HTTPBadRequest, self.controller.create,
req, None) req, None)
@ -383,7 +389,7 @@ class GroupSnapshotsAPITestCase(test.TestCase):
"group_id": self.group.id}} "group_id": self.group.id}}
req = fakes.HTTPRequest.blank('/v3/%s/group_snapshots' % req = fakes.HTTPRequest.blank('/v3/%s/group_snapshots' %
fake.PROJECT_ID, fake.PROJECT_ID,
version=GROUP_MICRO_VERSION) version=mv.GROUP_SNAPSHOTS)
self.assertRaises(webob.exc.HTTPBadRequest, self.controller.create, self.assertRaises(webob.exc.HTTPBadRequest, self.controller.create,
req, body) req, body)
@ -397,7 +403,7 @@ class GroupSnapshotsAPITestCase(test.TestCase):
"group_id": self.group.id}} "group_id": self.group.id}}
req = fakes.HTTPRequest.blank('/v3/%s/group_snapshots' % req = fakes.HTTPRequest.blank('/v3/%s/group_snapshots' %
fake.PROJECT_ID, fake.PROJECT_ID,
version=GROUP_MICRO_VERSION) version=mv.GROUP_SNAPSHOTS)
self.assertRaises(exception.GroupSnapshotNotFound, self.assertRaises(exception.GroupSnapshotNotFound,
self.controller.create, self.controller.create,
req, body) req, body)
@ -413,7 +419,7 @@ class GroupSnapshotsAPITestCase(test.TestCase):
"group_id": empty_group.id}} "group_id": empty_group.id}}
req = fakes.HTTPRequest.blank('/v3/%s/group_snapshots' % req = fakes.HTTPRequest.blank('/v3/%s/group_snapshots' %
fake.PROJECT_ID, fake.PROJECT_ID,
version=GROUP_MICRO_VERSION) version=mv.GROUP_SNAPSHOTS)
self.assertRaises(webob.exc.HTTPBadRequest, self.controller.create, self.assertRaises(webob.exc.HTTPBadRequest, self.controller.create,
req, body) req, body)
@ -426,7 +432,7 @@ class GroupSnapshotsAPITestCase(test.TestCase):
status=fields.GroupSnapshotStatus.AVAILABLE) status=fields.GroupSnapshotStatus.AVAILABLE)
req = fakes.HTTPRequest.blank('/v3/%s/group_snapshots/%s' % req = fakes.HTTPRequest.blank('/v3/%s/group_snapshots/%s' %
(fake.PROJECT_ID, group_snapshot.id), (fake.PROJECT_ID, group_snapshot.id),
version=GROUP_MICRO_VERSION) version=mv.GROUP_SNAPSHOTS)
res_dict = self.controller.delete(req, group_snapshot.id) res_dict = self.controller.delete(req, group_snapshot.id)
group_snapshot = objects.GroupSnapshot.get_by_id(self.context, group_snapshot = objects.GroupSnapshot.get_by_id(self.context,
@ -450,7 +456,7 @@ class GroupSnapshotsAPITestCase(test.TestCase):
volume_type_ids=[fake.VOLUME_TYPE_ID],) volume_type_ids=[fake.VOLUME_TYPE_ID],)
req = fakes.HTTPRequest.blank('/v3/%s/group_snapshots/%s' % req = fakes.HTTPRequest.blank('/v3/%s/group_snapshots/%s' %
(fake.PROJECT_ID, group_snapshot.id), (fake.PROJECT_ID, group_snapshot.id),
version=GROUP_MICRO_VERSION) version=mv.GROUP_SNAPSHOTS)
self.assertRaises(webob.exc.HTTPBadRequest, self.controller.delete, self.assertRaises(webob.exc.HTTPBadRequest, self.controller.delete,
req, group_snapshot.id) req, group_snapshot.id)
@ -461,7 +467,7 @@ class GroupSnapshotsAPITestCase(test.TestCase):
req = fakes.HTTPRequest.blank('/v3/%s/group_snapshots/%s' % req = fakes.HTTPRequest.blank('/v3/%s/group_snapshots/%s' %
(fake.PROJECT_ID, (fake.PROJECT_ID,
fake.WILL_NOT_BE_FOUND_ID), fake.WILL_NOT_BE_FOUND_ID),
version=GROUP_MICRO_VERSION) version=mv.GROUP_SNAPSHOTS)
self.assertRaises(exception.GroupSnapshotNotFound, self.assertRaises(exception.GroupSnapshotNotFound,
self.controller.delete, self.controller.delete,
req, fake.WILL_NOT_BE_FOUND_ID) req, fake.WILL_NOT_BE_FOUND_ID)
@ -473,19 +479,20 @@ class GroupSnapshotsAPITestCase(test.TestCase):
status='invalid') status='invalid')
req = fakes.HTTPRequest.blank('/v3/%s/group_snapshots/%s' % req = fakes.HTTPRequest.blank('/v3/%s/group_snapshots/%s' %
(fake.PROJECT_ID, group_snapshot.id), (fake.PROJECT_ID, group_snapshot.id),
version=GROUP_MICRO_VERSION) version=mv.GROUP_SNAPSHOTS)
self.assertRaises(webob.exc.HTTPBadRequest, self.controller.delete, self.assertRaises(webob.exc.HTTPBadRequest, self.controller.delete,
req, group_snapshot.id) req, group_snapshot.id)
group_snapshot.destroy() group_snapshot.destroy()
@ddt.data(('3.11', 'fake_snapshot_001', @ddt.data((mv.GROUP_TYPE, 'fake_snapshot_001',
fields.GroupSnapshotStatus.AVAILABLE, fields.GroupSnapshotStatus.AVAILABLE,
exception.VersionNotFoundForAPIMethod), exception.VersionNotFoundForAPIMethod),
('3.18', 'fake_snapshot_001', (mv.get_prior_version(mv.GROUP_SNAPSHOT_RESET_STATUS),
'fake_snapshot_001',
fields.GroupSnapshotStatus.AVAILABLE, fields.GroupSnapshotStatus.AVAILABLE,
exception.VersionNotFoundForAPIMethod), exception.VersionNotFoundForAPIMethod),
('3.19', 'fake_snapshot_001', (mv.GROUP_SNAPSHOT_RESET_STATUS, 'fake_snapshot_001',
fields.GroupSnapshotStatus.AVAILABLE, fields.GroupSnapshotStatus.AVAILABLE,
exception.GroupSnapshotNotFound)) exception.GroupSnapshotNotFound))
@ddt.unpack @ddt.unpack
@ -509,7 +516,7 @@ class GroupSnapshotsAPITestCase(test.TestCase):
status=fields.GroupSnapshotStatus.CREATING) status=fields.GroupSnapshotStatus.CREATING)
req = fakes.HTTPRequest.blank('/v3/%s/group_snapshots/%s/action' % req = fakes.HTTPRequest.blank('/v3/%s/group_snapshots/%s/action' %
(fake.PROJECT_ID, group_snapshot.id), (fake.PROJECT_ID, group_snapshot.id),
version='3.19') version=mv.GROUP_SNAPSHOT_RESET_STATUS)
body = {"reset_status": { body = {"reset_status": {
"status": "invalid_test_status" "status": "invalid_test_status"
}} }}
@ -525,7 +532,7 @@ class GroupSnapshotsAPITestCase(test.TestCase):
status=fields.GroupSnapshotStatus.CREATING) status=fields.GroupSnapshotStatus.CREATING)
req = fakes.HTTPRequest.blank('/v3/%s/group_snapshots/%s/action' % req = fakes.HTTPRequest.blank('/v3/%s/group_snapshots/%s/action' %
(fake.PROJECT_ID, group_snapshot.id), (fake.PROJECT_ID, group_snapshot.id),
version='3.19') version=mv.GROUP_SNAPSHOT_RESET_STATUS)
body = {"reset_status": { body = {"reset_status": {
"status": fields.GroupSnapshotStatus.AVAILABLE "status": fields.GroupSnapshotStatus.AVAILABLE
}} }}

View File

@ -21,12 +21,11 @@ from cinder import db
from cinder import rpc from cinder import rpc
from cinder import test from cinder import test
from cinder.api import microversions as mv
from cinder.api.v3 import group_specs as v3_group_specs from cinder.api.v3 import group_specs as v3_group_specs
from cinder.tests.unit.api import fakes from cinder.tests.unit.api import fakes
from cinder.tests.unit import fake_constants as fake from cinder.tests.unit import fake_constants as fake
GROUP_TYPE_MICRO_VERSION = '3.11'
fake_group_specs = { fake_group_specs = {
'key1': 'value1', 'key1': 'value1',
'key2': 'value2' 'key2': 'value2'
@ -71,7 +70,7 @@ class GroupSpecsTestCase(test.TestCase):
req = fakes.HTTPRequest.blank('v3/%s/group_specs' % req = fakes.HTTPRequest.blank('v3/%s/group_specs' %
fake.PROJECT_ID, fake.PROJECT_ID,
use_admin_context=True, use_admin_context=True,
version=GROUP_TYPE_MICRO_VERSION) version=mv.GROUP_TYPE)
req.environ['cinder.context'] = self.ctxt req.environ['cinder.context'] = self.ctxt
res_dict = self.controller.index(req, fake.GROUP_ID) res_dict = self.controller.index(req, fake.GROUP_ID)
group_specs_dict = res_dict['group_specs'] group_specs_dict = res_dict['group_specs']
@ -90,7 +89,7 @@ class GroupSpecsTestCase(test.TestCase):
req = fakes.HTTPRequest.blank('v3/%s/group_specs' % req = fakes.HTTPRequest.blank('v3/%s/group_specs' %
fake.PROJECT_ID, fake.PROJECT_ID,
use_admin_context=True, use_admin_context=True,
version=GROUP_TYPE_MICRO_VERSION) version=mv.GROUP_TYPE)
self.controller.create(req, fake.GROUP_ID, create_fake_group_specs) self.controller.create(req, fake.GROUP_ID, create_fake_group_specs)
self.assertTrue(mock_rpc_notifier.called) self.assertTrue(mock_rpc_notifier.called)
@ -108,7 +107,7 @@ class GroupSpecsTestCase(test.TestCase):
req = fakes.HTTPRequest.blank('v3/%s/group_specs' % req = fakes.HTTPRequest.blank('v3/%s/group_specs' %
fake.PROJECT_ID, fake.PROJECT_ID,
use_admin_context=True, use_admin_context=True,
version=GROUP_TYPE_MICRO_VERSION) version=mv.GROUP_TYPE)
self.controller.update(req, self.controller.update(req,
fake.GROUP_TYPE_ID, fake.GROUP_TYPE_ID,
'id', 'id',
@ -124,7 +123,7 @@ class GroupSpecsTestCase(test.TestCase):
req = fakes.HTTPRequest.blank('v3/%s/group_specs' % req = fakes.HTTPRequest.blank('v3/%s/group_specs' %
fake.PROJECT_ID, fake.PROJECT_ID,
use_admin_context=True, use_admin_context=True,
version=GROUP_TYPE_MICRO_VERSION) version=mv.GROUP_TYPE)
res_dict = self.controller.show(req, fake.GROUP_TYPE_ID, 'key1') res_dict = self.controller.show(req, fake.GROUP_TYPE_ID, 'key1')
self.assertEqual('value1', res_dict['key1']) self.assertEqual('value1', res_dict['key1'])
@ -138,7 +137,7 @@ class GroupSpecsTestCase(test.TestCase):
req = fakes.HTTPRequest.blank('v3/%s/group_specs' % req = fakes.HTTPRequest.blank('v3/%s/group_specs' %
fake.PROJECT_ID, fake.PROJECT_ID,
use_admin_context=True, use_admin_context=True,
version=GROUP_TYPE_MICRO_VERSION) version=mv.GROUP_TYPE)
self.controller.delete(req, fake.GROUP_TYPE_ID, 'key1') self.controller.delete(req, fake.GROUP_TYPE_ID, 'key1')
self.assertTrue(rpc_notifier_mock.called) self.assertTrue(rpc_notifier_mock.called)
@ -151,7 +150,7 @@ class GroupSpecsTestCase(test.TestCase):
req = fakes.HTTPRequest.blank('v3/%s/group_specs' % req = fakes.HTTPRequest.blank('v3/%s/group_specs' %
fake.PROJECT_ID, fake.PROJECT_ID,
use_admin_context=True, use_admin_context=True,
version=GROUP_TYPE_MICRO_VERSION) version=mv.GROUP_TYPE)
self.assertRaises(webob.exc.HTTPNotFound, self.assertRaises(webob.exc.HTTPNotFound,
self.controller.create, self.controller.create,
req, req,
@ -166,7 +165,7 @@ class GroupSpecsTestCase(test.TestCase):
req = fakes.HTTPRequest.blank('v3/%s/group_specs' % req = fakes.HTTPRequest.blank('v3/%s/group_specs' %
fake.PROJECT_ID, fake.PROJECT_ID,
use_admin_context=True, use_admin_context=True,
version=GROUP_TYPE_MICRO_VERSION) version=mv.GROUP_TYPE)
self.assertRaises(webob.exc.HTTPNotFound, self.assertRaises(webob.exc.HTTPNotFound,
self.controller.delete, self.controller.delete,
req, req,
@ -178,7 +177,7 @@ class GroupSpecsTestCase(test.TestCase):
req = fakes.HTTPRequest.blank('v3/%s/group_specs' % req = fakes.HTTPRequest.blank('v3/%s/group_specs' %
fake.PROJECT_ID, fake.PROJECT_ID,
use_admin_context=True, use_admin_context=True,
version=GROUP_TYPE_MICRO_VERSION) version=mv.GROUP_TYPE)
self.assertRaises(webob.exc.HTTPBadRequest, self.assertRaises(webob.exc.HTTPBadRequest,
self.controller.update, self.controller.update,
req, req,
@ -198,7 +197,7 @@ class GroupSpecsTestCase(test.TestCase):
req = fakes.HTTPRequest.blank('v3/%s/group_specs' % req = fakes.HTTPRequest.blank('v3/%s/group_specs' %
fake.PROJECT_ID, fake.PROJECT_ID,
use_admin_context=True, use_admin_context=True,
version=GROUP_TYPE_MICRO_VERSION) version=mv.GROUP_TYPE)
self.assertRaises(webob.exc.HTTPNotFound, self.assertRaises(webob.exc.HTTPNotFound,
self.controller.show, self.controller.show,
req, req,
@ -216,6 +215,6 @@ class GroupSpecsTestCase(test.TestCase):
req = fakes.HTTPRequest.blank('v3/%s/group_specs' % req = fakes.HTTPRequest.blank('v3/%s/group_specs' %
fake.PROJECT_ID, fake.PROJECT_ID,
use_admin_context=True, use_admin_context=True,
version=GROUP_TYPE_MICRO_VERSION) version=mv.GROUP_TYPE)
self.assertRaises(webob.exc.HTTPBadRequest, self.controller.create, self.assertRaises(webob.exc.HTTPBadRequest, self.controller.create,
req, fake.GROUP_ID, incorrect_fake_group_specs) req, fake.GROUP_ID, incorrect_fake_group_specs)

View File

@ -23,6 +23,7 @@ import six
import webob import webob
import cinder.api.common as common import cinder.api.common as common
from cinder.api import microversions as mv
from cinder.api.v3 import group_specs as v3_group_specs from cinder.api.v3 import group_specs as v3_group_specs
from cinder.api.v3 import group_types as v3_group_types from cinder.api.v3 import group_types as v3_group_types
from cinder.api.v3.views import group_types as views_types from cinder.api.v3.views import group_types as views_types
@ -33,7 +34,6 @@ from cinder.tests.unit.api import fakes
from cinder.tests.unit import fake_constants as fake from cinder.tests.unit import fake_constants as fake
from cinder.volume import group_types from cinder.volume import group_types
GROUP_TYPE_MICRO_VERSION = '3.11'
IN_USE_GROUP_TYPE = fake.GROUP_TYPE3_ID IN_USE_GROUP_TYPE = fake.GROUP_TYPE3_ID
@ -132,7 +132,7 @@ class GroupTypesApiTest(test.TestCase):
mock_create, mock_get): mock_create, mock_get):
boolean_is_public = strutils.bool_from_string(is_public) boolean_is_public = strutils.bool_from_string(is_public)
req = fakes.HTTPRequest.blank('/v3/%s/types' % fake.PROJECT_ID, req = fakes.HTTPRequest.blank('/v3/%s/types' % fake.PROJECT_ID,
version=GROUP_TYPE_MICRO_VERSION) version=mv.GROUP_TYPE)
req.environ['cinder.context'] = self.ctxt req.environ['cinder.context'] = self.ctxt
body = {"group_type": {"is_public": is_public, "name": "group_type1", body = {"group_type": {"is_public": is_public, "name": "group_type1",
@ -157,7 +157,7 @@ class GroupTypesApiTest(test.TestCase):
'_notify_group_type_error') '_notify_group_type_error')
req = fakes.HTTPRequest.blank('/v3/%s/group_types/%s' % ( req = fakes.HTTPRequest.blank('/v3/%s/group_types/%s' % (
fake.PROJECT_ID, grp_type_id), fake.PROJECT_ID, grp_type_id),
version=GROUP_TYPE_MICRO_VERSION) version=mv.GROUP_TYPE)
req.environ['cinder.context'] = self.ctxt req.environ['cinder.context'] = self.ctxt
if grp_type_id == IN_USE_GROUP_TYPE: if grp_type_id == IN_USE_GROUP_TYPE:
self.assertRaises(webob.exc.HTTPBadRequest, self.assertRaises(webob.exc.HTTPBadRequest,
@ -177,7 +177,7 @@ class GroupTypesApiTest(test.TestCase):
req = fakes.HTTPRequest.blank('/v3/%s/group_types' % fake.PROJECT_ID, req = fakes.HTTPRequest.blank('/v3/%s/group_types' % fake.PROJECT_ID,
use_admin_context=True, use_admin_context=True,
version=GROUP_TYPE_MICRO_VERSION) version=mv.GROUP_TYPE)
res_dict = self.controller.index(req) res_dict = self.controller.index(req)
self.assertEqual(3, len(res_dict['group_types'])) self.assertEqual(3, len(res_dict['group_types']))
@ -193,7 +193,7 @@ class GroupTypesApiTest(test.TestCase):
return_empty_group_types_get_all_types) return_empty_group_types_get_all_types)
req = fakes.HTTPRequest.blank('/v3/%s/group_types' % fake.PROJECT_ID, req = fakes.HTTPRequest.blank('/v3/%s/group_types' % fake.PROJECT_ID,
version=GROUP_TYPE_MICRO_VERSION) version=mv.GROUP_TYPE)
res_dict = self.controller.index(req) res_dict = self.controller.index(req)
self.assertEqual(0, len(res_dict['group_types'])) self.assertEqual(0, len(res_dict['group_types']))
@ -201,7 +201,7 @@ class GroupTypesApiTest(test.TestCase):
def test_group_types_index_with_limit(self): def test_group_types_index_with_limit(self):
req = fakes.HTTPRequest.blank('/v3/%s/group_types?limit=1' % req = fakes.HTTPRequest.blank('/v3/%s/group_types?limit=1' %
fake.PROJECT_ID, fake.PROJECT_ID,
version=GROUP_TYPE_MICRO_VERSION) version=mv.GROUP_TYPE)
req.environ['cinder.context'] = self.ctxt req.environ['cinder.context'] = self.ctxt
res = self.controller.index(req) res = self.controller.index(req)
@ -216,7 +216,7 @@ class GroupTypesApiTest(test.TestCase):
def test_group_types_index_with_offset(self): def test_group_types_index_with_offset(self):
req = fakes.HTTPRequest.blank( req = fakes.HTTPRequest.blank(
'/v3/%s/group_types?offset=1' % fake.PROJECT_ID, '/v3/%s/group_types?offset=1' % fake.PROJECT_ID,
version=GROUP_TYPE_MICRO_VERSION) version=mv.GROUP_TYPE)
req.environ['cinder.context'] = self.ctxt req.environ['cinder.context'] = self.ctxt
res = self.controller.index(req) res = self.controller.index(req)
@ -224,14 +224,14 @@ class GroupTypesApiTest(test.TestCase):
def test_group_types_index_with_offset_out_of_range(self): def test_group_types_index_with_offset_out_of_range(self):
url = '/v3/%s/group_types?offset=424366766556787' % fake.PROJECT_ID url = '/v3/%s/group_types?offset=424366766556787' % fake.PROJECT_ID
req = fakes.HTTPRequest.blank(url, version=GROUP_TYPE_MICRO_VERSION) req = fakes.HTTPRequest.blank(url, version=mv.GROUP_TYPE)
self.assertRaises(webob.exc.HTTPBadRequest, self.assertRaises(webob.exc.HTTPBadRequest,
self.controller.index, req) self.controller.index, req)
def test_group_types_index_with_limit_and_offset(self): def test_group_types_index_with_limit_and_offset(self):
req = fakes.HTTPRequest.blank( req = fakes.HTTPRequest.blank(
'/v3/%s/group_types?limit=2&offset=1' % fake.PROJECT_ID, '/v3/%s/group_types?limit=2&offset=1' % fake.PROJECT_ID,
version=GROUP_TYPE_MICRO_VERSION) version=mv.GROUP_TYPE)
req.environ['cinder.context'] = self.ctxt req.environ['cinder.context'] = self.ctxt
res = self.controller.index(req) res = self.controller.index(req)
@ -244,7 +244,7 @@ class GroupTypesApiTest(test.TestCase):
'&marker=%s' % '&marker=%s' %
(fake.PROJECT_ID, (fake.PROJECT_ID,
self.type_id2), self.type_id2),
version=GROUP_TYPE_MICRO_VERSION) version=mv.GROUP_TYPE)
req.environ['cinder.context'] = self.ctxt req.environ['cinder.context'] = self.ctxt
res = self.controller.index(req) res = self.controller.index(req)
@ -254,7 +254,7 @@ class GroupTypesApiTest(test.TestCase):
def test_group_types_index_with_valid_filter(self): def test_group_types_index_with_valid_filter(self):
req = fakes.HTTPRequest.blank( req = fakes.HTTPRequest.blank(
'/v3/%s/group_types?is_public=True' % fake.PROJECT_ID, '/v3/%s/group_types?is_public=True' % fake.PROJECT_ID,
version=GROUP_TYPE_MICRO_VERSION) version=mv.GROUP_TYPE)
req.environ['cinder.context'] = self.ctxt req.environ['cinder.context'] = self.ctxt
res = self.controller.index(req) res = self.controller.index(req)
@ -267,7 +267,7 @@ class GroupTypesApiTest(test.TestCase):
def test_group_types_index_with_invalid_filter(self): def test_group_types_index_with_invalid_filter(self):
req = fakes.HTTPRequest.blank( req = fakes.HTTPRequest.blank(
'/v3/%s/group_types?id=%s' % (fake.PROJECT_ID, self.type_id1), '/v3/%s/group_types?id=%s' % (fake.PROJECT_ID, self.type_id1),
version=GROUP_TYPE_MICRO_VERSION) version=mv.GROUP_TYPE)
req.environ['cinder.context'] = self.ctxt req.environ['cinder.context'] = self.ctxt
res = self.controller.index(req) res = self.controller.index(req)
@ -276,7 +276,7 @@ class GroupTypesApiTest(test.TestCase):
def test_group_types_index_with_sort_keys(self): def test_group_types_index_with_sort_keys(self):
req = fakes.HTTPRequest.blank('/v3/%s/group_types?sort=id' % req = fakes.HTTPRequest.blank('/v3/%s/group_types?sort=id' %
fake.PROJECT_ID, fake.PROJECT_ID,
version=GROUP_TYPE_MICRO_VERSION) version=mv.GROUP_TYPE)
req.environ['cinder.context'] = self.ctxt req.environ['cinder.context'] = self.ctxt
res = self.controller.index(req) res = self.controller.index(req)
expect_result = [self.type_id0, self.type_id1, self.type_id2, expect_result = [self.type_id0, self.type_id1, self.type_id2,
@ -292,7 +292,7 @@ class GroupTypesApiTest(test.TestCase):
def test_group_types_index_with_sort_and_limit(self): def test_group_types_index_with_sort_and_limit(self):
req = fakes.HTTPRequest.blank( req = fakes.HTTPRequest.blank(
'/v3/%s/group_types?sort=id&limit=2' % fake.PROJECT_ID, '/v3/%s/group_types?sort=id&limit=2' % fake.PROJECT_ID,
version=GROUP_TYPE_MICRO_VERSION) version=mv.GROUP_TYPE)
req.environ['cinder.context'] = self.ctxt req.environ['cinder.context'] = self.ctxt
res = self.controller.index(req) res = self.controller.index(req)
expect_result = [self.type_id0, self.type_id1, self.type_id2, expect_result = [self.type_id0, self.type_id1, self.type_id2,
@ -306,7 +306,7 @@ class GroupTypesApiTest(test.TestCase):
def test_group_types_index_with_sort_keys_and_sort_dirs(self): def test_group_types_index_with_sort_keys_and_sort_dirs(self):
req = fakes.HTTPRequest.blank( req = fakes.HTTPRequest.blank(
'/v3/%s/group_types?sort=id:asc' % fake.PROJECT_ID, '/v3/%s/group_types?sort=id:asc' % fake.PROJECT_ID,
version=GROUP_TYPE_MICRO_VERSION) version=mv.GROUP_TYPE)
req.environ['cinder.context'] = self.ctxt req.environ['cinder.context'] = self.ctxt
res = self.controller.index(req) res = self.controller.index(req)
expect_result = [self.type_id0, self.type_id1, self.type_id2, expect_result = [self.type_id0, self.type_id1, self.type_id2,
@ -332,7 +332,7 @@ class GroupTypesApiTest(test.TestCase):
type_id = six.text_type(uuid.uuid4()) type_id = six.text_type(uuid.uuid4())
req = fakes.HTTPRequest.blank( req = fakes.HTTPRequest.blank(
'/v3/%s/types/%s' % (fake.PROJECT_ID, type_id), '/v3/%s/types/%s' % (fake.PROJECT_ID, type_id),
version=GROUP_TYPE_MICRO_VERSION) version=mv.GROUP_TYPE)
req.environ['cinder.context'] = self.ctxt req.environ['cinder.context'] = self.ctxt
body = {"group_type": {"is_public": is_public, "name": "group_type1"}} body = {"group_type": {"is_public": is_public, "name": "group_type1"}}
self.controller.update(req, type_id, body) self.controller.update(req, type_id, body)
@ -347,7 +347,7 @@ class GroupTypesApiTest(test.TestCase):
type_id = six.text_type(uuid.uuid4()) type_id = six.text_type(uuid.uuid4())
req = fakes.HTTPRequest.blank('/v3/%s/group_types/' % fake.PROJECT_ID req = fakes.HTTPRequest.blank('/v3/%s/group_types/' % fake.PROJECT_ID
+ type_id, + type_id,
version=GROUP_TYPE_MICRO_VERSION) version=mv.GROUP_TYPE)
res_dict = self.controller.show(req, type_id) res_dict = self.controller.show(req, type_id)
self.assertEqual(1, len(res_dict)) self.assertEqual(1, len(res_dict))
@ -359,10 +359,10 @@ class GroupTypesApiTest(test.TestCase):
self.mock_object(group_types, 'get_group_type', self.mock_object(group_types, 'get_group_type',
return_group_types_get_group_type) return_group_types_get_group_type)
type_id = six.text_type(uuid.uuid4()) type_id = uuid.uuid4()
req = fakes.HTTPRequest.blank('/v3/%s/group_types/' % fake.PROJECT_ID req = fakes.HTTPRequest.blank(
+ type_id, '/v3/%s/group_types/%s' % (fake.PROJECT_ID, type_id),
version='3.5') version=mv.get_prior_version(mv.GROUP_TYPE))
self.assertRaises(exception.VersionNotFoundForAPIMethod, self.assertRaises(exception.VersionNotFoundForAPIMethod,
self.controller.show, req, type_id) self.controller.show, req, type_id)
@ -374,7 +374,7 @@ class GroupTypesApiTest(test.TestCase):
req = fakes.HTTPRequest.blank('/v3/%s/group_types/%s' % req = fakes.HTTPRequest.blank('/v3/%s/group_types/%s' %
(fake.PROJECT_ID, (fake.PROJECT_ID,
fake.WILL_NOT_BE_FOUND_ID), fake.WILL_NOT_BE_FOUND_ID),
version=GROUP_TYPE_MICRO_VERSION) version=mv.GROUP_TYPE)
self.assertRaises(webob.exc.HTTPNotFound, self.controller.show, self.assertRaises(webob.exc.HTTPNotFound, self.controller.show,
req, fake.WILL_NOT_BE_FOUND_ID) req, fake.WILL_NOT_BE_FOUND_ID)
@ -383,7 +383,7 @@ class GroupTypesApiTest(test.TestCase):
return_group_types_get_default) return_group_types_get_default)
req = fakes.HTTPRequest.blank('/v3/%s/group_types/default' % req = fakes.HTTPRequest.blank('/v3/%s/group_types/default' %
fake.PROJECT_ID, fake.PROJECT_ID,
version=GROUP_TYPE_MICRO_VERSION) version=mv.GROUP_TYPE)
req.method = 'GET' req.method = 'GET'
res_dict = self.controller.show(req, 'default') res_dict = self.controller.show(req, 'default')
self.assertEqual(1, len(res_dict)) self.assertEqual(1, len(res_dict))
@ -396,7 +396,7 @@ class GroupTypesApiTest(test.TestCase):
return_group_types_get_default_not_found) return_group_types_get_default_not_found)
req = fakes.HTTPRequest.blank('/v3/%s/group_types/default' % req = fakes.HTTPRequest.blank('/v3/%s/group_types/default' %
fake.PROJECT_ID, fake.PROJECT_ID,
version=GROUP_TYPE_MICRO_VERSION) version=mv.GROUP_TYPE)
req.method = 'GET' req.method = 'GET'
self.assertRaises(webob.exc.HTTPNotFound, self.assertRaises(webob.exc.HTTPNotFound,
@ -419,7 +419,7 @@ class GroupTypesApiTest(test.TestCase):
) )
request = fakes.HTTPRequest.blank("/v3", request = fakes.HTTPRequest.blank("/v3",
version=GROUP_TYPE_MICRO_VERSION) version=mv.GROUP_TYPE)
output = view_builder.show(request, raw_group_type) output = view_builder.show(request, raw_group_type)
self.assertIn('group_type', output) self.assertIn('group_type', output)
@ -448,7 +448,7 @@ class GroupTypesApiTest(test.TestCase):
) )
request = fakes.HTTPRequest.blank("/v3", use_admin_context=True, request = fakes.HTTPRequest.blank("/v3", use_admin_context=True,
version=GROUP_TYPE_MICRO_VERSION) version=mv.GROUP_TYPE)
output = view_builder.show(request, raw_group_type) output = view_builder.show(request, raw_group_type)
self.assertIn('group_type', output) self.assertIn('group_type', output)
@ -479,7 +479,7 @@ class GroupTypesApiTest(test.TestCase):
) )
request = fakes.HTTPRequest.blank("/v3", request = fakes.HTTPRequest.blank("/v3",
version=GROUP_TYPE_MICRO_VERSION) version=mv.GROUP_TYPE)
output = view_builder.show(request, raw_group_type) output = view_builder.show(request, raw_group_type)
self.assertIn('group_type', output) self.assertIn('group_type', output)
@ -510,7 +510,7 @@ class GroupTypesApiTest(test.TestCase):
) )
request = fakes.HTTPRequest.blank("/v3", request = fakes.HTTPRequest.blank("/v3",
version=GROUP_TYPE_MICRO_VERSION) version=mv.GROUP_TYPE)
output = view_builder.show(request, raw_group_type) output = view_builder.show(request, raw_group_type)
self.assertIn('group_type', output) self.assertIn('group_type', output)
@ -542,7 +542,7 @@ class GroupTypesApiTest(test.TestCase):
) )
request = fakes.HTTPRequest.blank("/v3", request = fakes.HTTPRequest.blank("/v3",
version=GROUP_TYPE_MICRO_VERSION) version=mv.GROUP_TYPE)
output = view_builder.show(request, raw_group_type) output = view_builder.show(request, raw_group_type)
self.assertIn('group_type', output) self.assertIn('group_type', output)
@ -576,7 +576,7 @@ class GroupTypesApiTest(test.TestCase):
) )
request = fakes.HTTPRequest.blank("/v3", request = fakes.HTTPRequest.blank("/v3",
version=GROUP_TYPE_MICRO_VERSION) version=mv.GROUP_TYPE)
output = view_builder.index(request, raw_group_types) output = view_builder.index(request, raw_group_types)
self.assertIn('group_types', output) self.assertIn('group_types', output)
@ -611,7 +611,7 @@ class GroupTypesApiTest(test.TestCase):
) )
request = fakes.HTTPRequest.blank("/v3", use_admin_context=True, request = fakes.HTTPRequest.blank("/v3", use_admin_context=True,
version=GROUP_TYPE_MICRO_VERSION) version=mv.GROUP_TYPE)
output = view_builder.index(request, raw_group_types) output = view_builder.index(request, raw_group_types)
self.assertIn('group_types', output) self.assertIn('group_types', output)

View File

@ -22,6 +22,7 @@ import mock
from six.moves import http_client from six.moves import http_client
import webob import webob
from cinder.api import microversions as mv
from cinder.api.v3 import groups as v3_groups from cinder.api.v3 import groups as v3_groups
from cinder import context from cinder import context
from cinder import db from cinder import db
@ -36,10 +37,7 @@ from cinder.tests.unit import fake_constants as fake
from cinder.tests.unit import utils from cinder.tests.unit import utils
from cinder.volume import api as volume_api from cinder.volume import api as volume_api
GROUP_MICRO_VERSION = '3.13' INVALID_GROUP_REPLICATION = mv.get_prior_version(mv.GROUP_REPLICATION)
GROUP_FROM_SRC_MICRO_VERSION = '3.14'
GROUP_REPLICATION_MICRO_VERSION = '3.38'
INVALID_GROUP_REPLICATION_MICRO_VERSION = '3.37'
@ddt.ddt @ddt.ddt
@ -134,7 +132,7 @@ class GroupsAPITestCase(test.TestCase):
req = fakes.HTTPRequest.blank('/v3/%s/groups/%s' % req = fakes.HTTPRequest.blank('/v3/%s/groups/%s' %
(fake.PROJECT_ID, self.group1.id), (fake.PROJECT_ID, self.group1.id),
version=GROUP_MICRO_VERSION) version=mv.GROUP_VOLUME)
res_dict = self.controller.show(req, self.group1.id) res_dict = self.controller.show(req, self.group1.id)
self.assertEqual(1, len(res_dict)) self.assertEqual(1, len(res_dict))
@ -149,7 +147,10 @@ class GroupsAPITestCase(test.TestCase):
self.assertEqual([fake.VOLUME_TYPE_ID], self.assertEqual([fake.VOLUME_TYPE_ID],
res_dict['group']['volume_types']) res_dict['group']['volume_types'])
@ddt.data(('3.24', False), ('3.24', True), ('3.25', False), ('3.25', True)) @ddt.data((mv.get_prior_version(mv.GROUP_VOLUME_LIST), False),
(mv.get_prior_version(mv.GROUP_VOLUME_LIST), True),
(mv.GROUP_VOLUME_LIST, False),
(mv.GROUP_VOLUME_LIST, True))
@ddt.unpack @ddt.unpack
@mock.patch('cinder.objects.volume_type.VolumeTypeList.get_all_by_group') @mock.patch('cinder.objects.volume_type.VolumeTypeList.get_all_by_group')
@mock.patch('cinder.objects.volume.VolumeList.get_all_by_generic_group') @mock.patch('cinder.objects.volume.VolumeList.get_all_by_generic_group')
@ -178,10 +179,10 @@ class GroupsAPITestCase(test.TestCase):
res_dict = self.controller.detail(req) res_dict = self.controller.detail(req)
# If the microversion >= 3.25 and "list_volume=True", "volumes" should # If the microversion >= 3.25 and "list_volume=True", "volumes" should
# be contained in the response body. Else,"volumes" should not be # be contained in the response body. Else, "volumes" should not be
# contained in the response body. # contained in the response body.
self.assertEqual(3, len(res_dict['groups'])) self.assertEqual(3, len(res_dict['groups']))
if (version, has_list_volume) == ('3.25', True): if (version, has_list_volume) == (mv.GROUP_VOLUME_LIST, True):
self.assertEqual([fake.VOLUME_ID], self.assertEqual([fake.VOLUME_ID],
res_dict['groups'][0]['volumes']) res_dict['groups'][0]['volumes'])
else: else:
@ -211,7 +212,7 @@ class GroupsAPITestCase(test.TestCase):
# be contained in the response body. # be contained in the response body.
req = fakes.HTTPRequest.blank('/v3/%s/groups/%s?list_volume=True' % req = fakes.HTTPRequest.blank('/v3/%s/groups/%s?list_volume=True' %
(fake.PROJECT_ID, self.group1.id), (fake.PROJECT_ID, self.group1.id),
version='3.25') version=mv.GROUP_VOLUME_LIST)
res_dict = self.controller.show(req, self.group1.id) res_dict = self.controller.show(req, self.group1.id)
self.assertEqual(1, len(res_dict)) self.assertEqual(1, len(res_dict))
self.assertEqual([fake.VOLUME_ID], self.assertEqual([fake.VOLUME_ID],
@ -221,16 +222,17 @@ class GroupsAPITestCase(test.TestCase):
# should not be contained in the response body. # should not be contained in the response body.
req = fakes.HTTPRequest.blank('/v3/%s/groups/%s' % req = fakes.HTTPRequest.blank('/v3/%s/groups/%s' %
(fake.PROJECT_ID, self.group1.id), (fake.PROJECT_ID, self.group1.id),
version='3.25') version=mv.GROUP_VOLUME_LIST)
res_dict = self.controller.show(req, self.group1.id) res_dict = self.controller.show(req, self.group1.id)
self.assertEqual(1, len(res_dict)) self.assertEqual(1, len(res_dict))
self.assertIsNone(res_dict['group'].get('volumes', None)) self.assertIsNone(res_dict['group'].get('volumes', None))
# If the microversion < 3.25, "volumes" should not be contained in the # If the microversion < 3.25, "volumes" should not be contained in the
# response body. # response body.
req = fakes.HTTPRequest.blank('/v3/%s/groups/%s?list_volume=True' % req = fakes.HTTPRequest.blank(
(fake.PROJECT_ID, self.group1.id), '/v3/%s/groups/%s?list_volume=True' %
version='3.24') (fake.PROJECT_ID, self.group1.id),
version=mv.get_prior_version(mv.GROUP_VOLUME_LIST))
res_dict = self.controller.show(req, self.group1.id) res_dict = self.controller.show(req, self.group1.id)
self.assertEqual(1, len(res_dict)) self.assertEqual(1, len(res_dict))
self.assertIsNone(res_dict['group'].get('volumes', None)) self.assertIsNone(res_dict['group'].get('volumes', None))
@ -239,11 +241,12 @@ class GroupsAPITestCase(test.TestCase):
req = fakes.HTTPRequest.blank('/v3/%s/groups/%s' % req = fakes.HTTPRequest.blank('/v3/%s/groups/%s' %
(fake.PROJECT_ID, (fake.PROJECT_ID,
fake.WILL_NOT_BE_FOUND_ID), fake.WILL_NOT_BE_FOUND_ID),
version=GROUP_MICRO_VERSION) version=mv.GROUP_VOLUME)
self.assertRaises(exception.GroupNotFound, self.controller.show, self.assertRaises(exception.GroupNotFound, self.controller.show,
req, fake.WILL_NOT_BE_FOUND_ID) req, fake.WILL_NOT_BE_FOUND_ID)
@ddt.data('3.30', '3.31', '3.34') @ddt.data(mv.get_prior_version(mv.RESOURCE_FILTER),
mv.RESOURCE_FILTER, mv.LIKE_FILTER)
@mock.patch('cinder.api.common.reject_invalid_filters') @mock.patch('cinder.api.common.reject_invalid_filters')
def test_group_list_with_general_filter(self, version, mock_update): def test_group_list_with_general_filter(self, version, mock_update):
url = '/v3/%s/groups' % fake.PROJECT_ID url = '/v3/%s/groups' % fake.PROJECT_ID
@ -252,8 +255,8 @@ class GroupsAPITestCase(test.TestCase):
use_admin_context=False) use_admin_context=False)
self.controller.index(req) self.controller.index(req)
if version != '3.30': if version != mv.get_prior_version(mv.RESOURCE_FILTER):
support_like = True if version == '3.34' else False support_like = True if version == mv.LIKE_FILTER else False
mock_update.assert_called_once_with(req.environ['cinder.context'], mock_update.assert_called_once_with(req.environ['cinder.context'],
mock.ANY, 'group', mock.ANY, 'group',
support_like) support_like)
@ -273,7 +276,7 @@ class GroupsAPITestCase(test.TestCase):
self.group3.save() self.group3.save()
req = fakes.HTTPRequest.blank('/v3/%s/groups' % fake.PROJECT_ID, req = fakes.HTTPRequest.blank('/v3/%s/groups' % fake.PROJECT_ID,
version=GROUP_MICRO_VERSION) version=mv.GROUP_VOLUME)
res_dict = self.controller.index(req) res_dict = self.controller.index(req)
self.assertEqual(1, len(res_dict)) self.assertEqual(1, len(res_dict))
@ -295,7 +298,7 @@ class GroupsAPITestCase(test.TestCase):
url = '/v3/%s/groups?limit=1' % fake.PROJECT_ID url = '/v3/%s/groups?limit=1' % fake.PROJECT_ID
if is_detail: if is_detail:
url = '/v3/%s/groups/detail?limit=1' % fake.PROJECT_ID url = '/v3/%s/groups/detail?limit=1' % fake.PROJECT_ID
req = fakes.HTTPRequest.blank(url, version=GROUP_MICRO_VERSION) req = fakes.HTTPRequest.blank(url, version=mv.GROUP_VOLUME)
if is_detail: if is_detail:
res_dict = self.controller.detail(req) res_dict = self.controller.detail(req)
@ -320,7 +323,7 @@ class GroupsAPITestCase(test.TestCase):
url = '/v3/%s/groups?offset=1' % fake.PROJECT_ID url = '/v3/%s/groups?offset=1' % fake.PROJECT_ID
if is_detail: if is_detail:
url = '/v3/%s/groups/detail?offset=1' % fake.PROJECT_ID url = '/v3/%s/groups/detail?offset=1' % fake.PROJECT_ID
req = fakes.HTTPRequest.blank(url, version=GROUP_MICRO_VERSION) req = fakes.HTTPRequest.blank(url, version=mv.GROUP_VOLUME)
res_dict = self.controller.index(req) res_dict = self.controller.index(req)
self.assertEqual(1, len(res_dict)) self.assertEqual(1, len(res_dict))
@ -337,7 +340,7 @@ class GroupsAPITestCase(test.TestCase):
if is_detail: if is_detail:
url = ('/v3/%s/groups/detail?offset=234523423455454' % url = ('/v3/%s/groups/detail?offset=234523423455454' %
fake.PROJECT_ID) fake.PROJECT_ID)
req = fakes.HTTPRequest.blank(url, version=GROUP_MICRO_VERSION) req = fakes.HTTPRequest.blank(url, version=mv.GROUP_VOLUME)
if is_detail: if is_detail:
self.assertRaises(webob.exc.HTTPBadRequest, self.controller.detail, self.assertRaises(webob.exc.HTTPBadRequest, self.controller.detail,
req) req)
@ -351,7 +354,7 @@ class GroupsAPITestCase(test.TestCase):
if is_detail: if is_detail:
url = ('/v3/%s/groups/detail?limit=2&offset=1' % url = ('/v3/%s/groups/detail?limit=2&offset=1' %
fake.PROJECT_ID) fake.PROJECT_ID)
req = fakes.HTTPRequest.blank(url, version=GROUP_MICRO_VERSION) req = fakes.HTTPRequest.blank(url, version=mv.GROUP_VOLUME)
if is_detail: if is_detail:
res_dict = self.controller.detail(req) res_dict = self.controller.detail(req)
@ -377,7 +380,7 @@ class GroupsAPITestCase(test.TestCase):
url = ('/v3/%s/groups/detail?' url = ('/v3/%s/groups/detail?'
'all_tenants=True&id=%s') % (fake.PROJECT_ID, 'all_tenants=True&id=%s') % (fake.PROJECT_ID,
self.group3.id) self.group3.id)
req = fakes.HTTPRequest.blank(url, version=GROUP_MICRO_VERSION, req = fakes.HTTPRequest.blank(url, version=mv.GROUP_VOLUME,
use_admin_context=True) use_admin_context=True)
if is_detail: if is_detail:
@ -398,7 +401,7 @@ class GroupsAPITestCase(test.TestCase):
if is_detail: if is_detail:
url = ('/v3/%s/groups/detail?sort=id:asc' % url = ('/v3/%s/groups/detail?sort=id:asc' %
fake.PROJECT_ID) fake.PROJECT_ID)
req = fakes.HTTPRequest.blank(url, version=GROUP_MICRO_VERSION) req = fakes.HTTPRequest.blank(url, version=mv.GROUP_VOLUME)
expect_result = [self.group1.id, self.group2.id, expect_result = [self.group1.id, self.group2.id,
self.group3.id] self.group3.id]
expect_result.sort() expect_result.sort()
@ -438,7 +441,7 @@ class GroupsAPITestCase(test.TestCase):
# self.group3.save() # self.group3.save()
req = fakes.HTTPRequest.blank('/v3/%s/groups/detail' % req = fakes.HTTPRequest.blank('/v3/%s/groups/detail' %
fake.PROJECT_ID, fake.PROJECT_ID,
version=GROUP_MICRO_VERSION) version=mv.GROUP_VOLUME)
res_dict = self.controller.detail(req) res_dict = self.controller.detail(req)
self.assertEqual(1, len(res_dict)) self.assertEqual(1, len(res_dict))
@ -476,7 +479,7 @@ class GroupsAPITestCase(test.TestCase):
"description": "description":
"Group 1", }} "Group 1", }}
req = fakes.HTTPRequest.blank('/v3/%s/groups' % fake.PROJECT_ID, req = fakes.HTTPRequest.blank('/v3/%s/groups' % fake.PROJECT_ID,
version=GROUP_MICRO_VERSION) version=mv.GROUP_VOLUME)
res_dict = self.controller.create(req, body) res_dict = self.controller.create(req, body)
self.assertEqual(1, len(res_dict)) self.assertEqual(1, len(res_dict))
@ -489,7 +492,7 @@ class GroupsAPITestCase(test.TestCase):
def test_create_group_with_no_body(self): def test_create_group_with_no_body(self):
# omit body from the request # omit body from the request
req = fakes.HTTPRequest.blank('/v3/%s/groups' % fake.PROJECT_ID, req = fakes.HTTPRequest.blank('/v3/%s/groups' % fake.PROJECT_ID,
version=GROUP_MICRO_VERSION) version=mv.GROUP_VOLUME)
self.assertRaises(webob.exc.HTTPBadRequest, self.controller.create, self.assertRaises(webob.exc.HTTPBadRequest, self.controller.create,
req, None) req, None)
@ -498,7 +501,7 @@ class GroupsAPITestCase(test.TestCase):
self.group1.save() self.group1.save()
req = fakes.HTTPRequest.blank('/v3/%s/groups/%s/action' % req = fakes.HTTPRequest.blank('/v3/%s/groups/%s/action' %
(fake.PROJECT_ID, self.group1.id), (fake.PROJECT_ID, self.group1.id),
version=GROUP_MICRO_VERSION) version=mv.GROUP_VOLUME)
body = {"delete": {"delete-volumes": False}} body = {"delete": {"delete-volumes": False}}
res_dict = self.controller.delete_group( res_dict = self.controller.delete_group(
req, self.group1.id, body) req, self.group1.id, body)
@ -513,7 +516,7 @@ class GroupsAPITestCase(test.TestCase):
self.group1.save() self.group1.save()
req = fakes.HTTPRequest.blank('/v3/%s/groups/%s/action' % req = fakes.HTTPRequest.blank('/v3/%s/groups/%s/action' %
(fake.PROJECT_ID, self.group1.id), (fake.PROJECT_ID, self.group1.id),
version=GROUP_MICRO_VERSION) version=mv.GROUP_VOLUME)
body = {"delete": {"delete-volumes": False}} body = {"delete": {"delete-volumes": False}}
res_dict = self.controller.delete_group( res_dict = self.controller.delete_group(
req, self.group1.id, body) req, self.group1.id, body)
@ -528,7 +531,7 @@ class GroupsAPITestCase(test.TestCase):
req = fakes.HTTPRequest.blank('/v3/%s/groups/%s/action' % req = fakes.HTTPRequest.blank('/v3/%s/groups/%s/action' %
(fake.PROJECT_ID, (fake.PROJECT_ID,
fake.WILL_NOT_BE_FOUND_ID), fake.WILL_NOT_BE_FOUND_ID),
version=GROUP_MICRO_VERSION) version=mv.GROUP_VOLUME)
body = {"delete": {"delete-volumes": False}} body = {"delete": {"delete-volumes": False}}
self.assertRaises(exception.GroupNotFound, self.assertRaises(exception.GroupNotFound,
self.controller.delete_group, self.controller.delete_group,
@ -538,7 +541,7 @@ class GroupsAPITestCase(test.TestCase):
req = fakes.HTTPRequest.blank('/v3/%s/groups/%s/action' % req = fakes.HTTPRequest.blank('/v3/%s/groups/%s/action' %
(fake.PROJECT_ID, (fake.PROJECT_ID,
self.group1.id), self.group1.id),
version=GROUP_MICRO_VERSION) version=mv.GROUP_VOLUME)
body = {"delete": {"delete-volumes": False}} body = {"delete": {"delete-volumes": False}}
self.assertRaises(webob.exc.HTTPBadRequest, self.assertRaises(webob.exc.HTTPBadRequest,
self.controller.delete_group, self.controller.delete_group,
@ -548,7 +551,7 @@ class GroupsAPITestCase(test.TestCase):
req = fakes.HTTPRequest.blank('/v3/%s/groups/%s/action' % req = fakes.HTTPRequest.blank('/v3/%s/groups/%s/action' %
(fake.PROJECT_ID, (fake.PROJECT_ID,
self.group1.id), self.group1.id),
version=GROUP_MICRO_VERSION) version=mv.GROUP_VOLUME)
body = {"delete": {"delete-volumes": True}} body = {"delete": {"delete-volumes": True}}
res_dict = self.controller.delete_group( res_dict = self.controller.delete_group(
req, self.group1.id, body) req, self.group1.id, body)
@ -565,7 +568,7 @@ class GroupsAPITestCase(test.TestCase):
req = fakes.HTTPRequest.blank('/v3/%s/groups/%s/action' % req = fakes.HTTPRequest.blank('/v3/%s/groups/%s/action' %
(fake.PROJECT_ID, (fake.PROJECT_ID,
self.group1.id), self.group1.id),
version=GROUP_MICRO_VERSION) version=mv.GROUP_VOLUME)
body = {"delete": {"delete-volumes": True}} body = {"delete": {"delete-volumes": True}}
res_dict = self.controller.delete_group( res_dict = self.controller.delete_group(
req, self.group1.id, body) req, self.group1.id, body)
@ -620,7 +623,7 @@ class GroupsAPITestCase(test.TestCase):
"description": "description":
"Group 1", }} "Group 1", }}
req = fakes.HTTPRequest.blank('/v3/%s/groups' % fake.PROJECT_ID, req = fakes.HTTPRequest.blank('/v3/%s/groups' % fake.PROJECT_ID,
version=GROUP_MICRO_VERSION) version=mv.GROUP_VOLUME)
ex = self.assertRaises(exception.GroupLimitExceeded, ex = self.assertRaises(exception.GroupLimitExceeded,
self.controller.create, self.controller.create,
req, body) req, body)
@ -631,7 +634,7 @@ class GroupsAPITestCase(test.TestCase):
self.group1.save() self.group1.save()
req = fakes.HTTPRequest.blank('/v3/%s/groups/%s/action' % req = fakes.HTTPRequest.blank('/v3/%s/groups/%s/action' %
(fake.PROJECT_ID, self.group1.id), (fake.PROJECT_ID, self.group1.id),
version=GROUP_MICRO_VERSION) version=mv.GROUP_VOLUME)
body = {"invalid_request_element": {"delete-volumes": False}} body = {"invalid_request_element": {"delete-volumes": False}}
self.assertRaises(webob.exc.HTTPBadRequest, self.assertRaises(webob.exc.HTTPBadRequest,
self.controller.delete_group, self.controller.delete_group,
@ -642,7 +645,7 @@ class GroupsAPITestCase(test.TestCase):
self.group1.save() self.group1.save()
req = fakes.HTTPRequest.blank('/v3/%s/groups/%s/action' % req = fakes.HTTPRequest.blank('/v3/%s/groups/%s/action' %
(fake.PROJECT_ID, self.group1.id), (fake.PROJECT_ID, self.group1.id),
version=GROUP_MICRO_VERSION) version=mv.GROUP_VOLUME)
body = {"delete": {"delete-volumes": "abcd"}} body = {"delete": {"delete-volumes": "abcd"}}
self.assertRaises(webob.exc.HTTPBadRequest, self.assertRaises(webob.exc.HTTPBadRequest,
self.controller.delete_group, self.controller.delete_group,
@ -653,7 +656,7 @@ class GroupsAPITestCase(test.TestCase):
self.group1.save() self.group1.save()
req = fakes.HTTPRequest.blank('/v3/%s/groups/%s/action' % req = fakes.HTTPRequest.blank('/v3/%s/groups/%s/action' %
(fake.PROJECT_ID, self.group1.id), (fake.PROJECT_ID, self.group1.id),
version=GROUP_MICRO_VERSION) version=mv.GROUP_VOLUME)
body = {"delete": {"delete-volumes": ""}} body = {"delete": {"delete-volumes": ""}}
self.assertRaises(webob.exc.HTTPBadRequest, self.assertRaises(webob.exc.HTTPBadRequest,
self.controller.delete_group, self.controller.delete_group,
@ -666,7 +669,7 @@ class GroupsAPITestCase(test.TestCase):
req = fakes.HTTPRequest.blank('/v3/%s/groups/%s/action' % req = fakes.HTTPRequest.blank('/v3/%s/groups/%s/action' %
(fake.PROJECT_ID, self.group1.id), (fake.PROJECT_ID, self.group1.id),
version=GROUP_MICRO_VERSION) version=mv.GROUP_VOLUME)
body = {"delete": {"delete-volumes": True}} body = {"delete": {"delete-volumes": True}}
self.assertRaises(webob.exc.HTTPBadRequest, self.assertRaises(webob.exc.HTTPBadRequest,
self.controller.delete_group, self.controller.delete_group,
@ -688,7 +691,7 @@ class GroupsAPITestCase(test.TestCase):
vol = utils.create_volume(self.ctxt, group_id=self.group1.id) vol = utils.create_volume(self.ctxt, group_id=self.group1.id)
req = fakes.HTTPRequest.blank('/v3/%s/groups/%s/action' % req = fakes.HTTPRequest.blank('/v3/%s/groups/%s/action' %
(fake.PROJECT_ID, self.group1.id), (fake.PROJECT_ID, self.group1.id),
version=GROUP_MICRO_VERSION) version=mv.GROUP_VOLUME)
body = {"delete": {"delete-volumes": True}} body = {"delete": {"delete-volumes": True}}
res_dict = self.controller.delete_group( res_dict = self.controller.delete_group(
req, self.group1.id, body) req, self.group1.id, body)
@ -707,7 +710,7 @@ class GroupsAPITestCase(test.TestCase):
attach_status='attached') attach_status='attached')
req = fakes.HTTPRequest.blank('/v3/%s/groups/%s/action' % req = fakes.HTTPRequest.blank('/v3/%s/groups/%s/action' %
(fake.PROJECT_ID, self.group1.id), (fake.PROJECT_ID, self.group1.id),
version=GROUP_MICRO_VERSION) version=mv.GROUP_VOLUME)
body = {"delete": {"delete-volumes": True}} body = {"delete": {"delete-volumes": True}}
self.assertRaises(webob.exc.HTTPBadRequest, self.assertRaises(webob.exc.HTTPBadRequest,
self.controller.delete_group, self.controller.delete_group,
@ -722,7 +725,7 @@ class GroupsAPITestCase(test.TestCase):
utils.create_snapshot(self.ctxt, vol.id) utils.create_snapshot(self.ctxt, vol.id)
req = fakes.HTTPRequest.blank('/v3/%s/groups/%s/action' % req = fakes.HTTPRequest.blank('/v3/%s/groups/%s/action' %
(fake.PROJECT_ID, self.group1.id), (fake.PROJECT_ID, self.group1.id),
version=GROUP_MICRO_VERSION) version=mv.GROUP_VOLUME)
body = {"delete": {"delete-volumes": True}} body = {"delete": {"delete-volumes": True}}
self.assertRaises(webob.exc.HTTPBadRequest, self.assertRaises(webob.exc.HTTPBadRequest,
self.controller.delete_group, self.controller.delete_group,
@ -739,7 +742,7 @@ class GroupsAPITestCase(test.TestCase):
deleted=True) deleted=True)
req = fakes.HTTPRequest.blank('/v3/%s/groups/%s/action' % req = fakes.HTTPRequest.blank('/v3/%s/groups/%s/action' %
(fake.PROJECT_ID, self.group1.id), (fake.PROJECT_ID, self.group1.id),
version=GROUP_MICRO_VERSION) version=mv.GROUP_VOLUME)
body = {"delete": {"delete-volumes": True}} body = {"delete": {"delete-volumes": True}}
res_dict = self.controller.delete_group( res_dict = self.controller.delete_group(
req, self.group1.id, body) req, self.group1.id, body)
@ -758,7 +761,7 @@ class GroupsAPITestCase(test.TestCase):
"description": "description":
"Group 1", }} "Group 1", }}
req = fakes.HTTPRequest.blank('/v3/%s/groups' % fake.PROJECT_ID, req = fakes.HTTPRequest.blank('/v3/%s/groups' % fake.PROJECT_ID,
version=GROUP_MICRO_VERSION) version=mv.GROUP_VOLUME)
self.assertRaises(webob.exc.HTTPBadRequest, self.assertRaises(webob.exc.HTTPBadRequest,
self.controller.create, self.controller.create,
req, body) req, body)
@ -770,7 +773,7 @@ class GroupsAPITestCase(test.TestCase):
"description": "description":
"Group 1", }} "Group 1", }}
req = fakes.HTTPRequest.blank('/v3/%s/groups' % fake.PROJECT_ID, req = fakes.HTTPRequest.blank('/v3/%s/groups' % fake.PROJECT_ID,
version=GROUP_MICRO_VERSION) version=mv.GROUP_VOLUME)
self.assertRaises(webob.exc.HTTPBadRequest, self.assertRaises(webob.exc.HTTPBadRequest,
self.controller.create, self.controller.create,
req, body) req, body)
@ -820,7 +823,7 @@ class GroupsAPITestCase(test.TestCase):
volume_type_id=volume_type_id) volume_type_id=volume_type_id)
req = fakes.HTTPRequest.blank('/v3/%s/groups/%s/update' % req = fakes.HTTPRequest.blank('/v3/%s/groups/%s/update' %
(fake.PROJECT_ID, self.group1.id), (fake.PROJECT_ID, self.group1.id),
version=GROUP_MICRO_VERSION) version=mv.GROUP_VOLUME)
name = 'newgroup' name = 'newgroup'
description = 'New Group Description' description = 'New Group Description'
add_volumes = add_volume.id + "," + add_volume2.id add_volumes = add_volume.id + "," + add_volume2.id
@ -851,7 +854,7 @@ class GroupsAPITestCase(test.TestCase):
self.group1.status = status self.group1.status = status
req = fakes.HTTPRequest.blank('/v3/%s/groups/%s/update' % req = fakes.HTTPRequest.blank('/v3/%s/groups/%s/update' %
(fake.PROJECT_ID, self.group1.id), (fake.PROJECT_ID, self.group1.id),
version=GROUP_MICRO_VERSION) version=mv.GROUP_VOLUME)
body = {"group": {"name": "new name", body = {"group": {"name": "new name",
"description": "new description", "description": "new description",
"add_volumes": None, "add_volumes": None,
@ -870,7 +873,7 @@ class GroupsAPITestCase(test.TestCase):
self.group1.save() self.group1.save()
req = fakes.HTTPRequest.blank('/v3/%s/groups/%s/update' % req = fakes.HTTPRequest.blank('/v3/%s/groups/%s/update' %
(fake.PROJECT_ID, self.group1.id), (fake.PROJECT_ID, self.group1.id),
version=GROUP_MICRO_VERSION) version=mv.GROUP_VOLUME)
body = {"group": {"name": None, body = {"group": {"name": None,
"description": None, "description": None,
"add_volumes": "fake-volume-uuid", "add_volumes": "fake-volume-uuid",
@ -885,7 +888,7 @@ class GroupsAPITestCase(test.TestCase):
self.group1.save() self.group1.save()
req = fakes.HTTPRequest.blank('/v3/%s/groups/%s/update' % req = fakes.HTTPRequest.blank('/v3/%s/groups/%s/update' %
(fake.PROJECT_ID, self.group1.id), (fake.PROJECT_ID, self.group1.id),
version=GROUP_MICRO_VERSION) version=mv.GROUP_VOLUME)
body = {"group": {"name": None, body = {"group": {"name": None,
"description": "new description", "description": "new description",
"add_volumes": None, "add_volumes": None,
@ -900,7 +903,7 @@ class GroupsAPITestCase(test.TestCase):
self.group1.save() self.group1.save()
req = fakes.HTTPRequest.blank('/v3/%s/groups/%s/update' % req = fakes.HTTPRequest.blank('/v3/%s/groups/%s/update' %
(fake.PROJECT_ID, self.group1.id), (fake.PROJECT_ID, self.group1.id),
version=GROUP_MICRO_VERSION) version=mv.GROUP_VOLUME)
body = {"group": {"name": None, body = {"group": {"name": None,
"description": None, "description": None,
"add_volumes": None, "add_volumes": None,
@ -919,7 +922,7 @@ class GroupsAPITestCase(test.TestCase):
status='wrong_status') status='wrong_status')
req = fakes.HTTPRequest.blank('/v3/%s/groups/%s/update' % req = fakes.HTTPRequest.blank('/v3/%s/groups/%s/update' %
(fake.PROJECT_ID, self.group1.id), (fake.PROJECT_ID, self.group1.id),
version=GROUP_MICRO_VERSION) version=mv.GROUP_VOLUME)
add_volumes = add_volume.id add_volumes = add_volume.id
body = {"group": {"name": "group1", body = {"group": {"name": "group1",
"description": "", "description": "",
@ -941,7 +944,7 @@ class GroupsAPITestCase(test.TestCase):
volume_type_id=wrong_type) volume_type_id=wrong_type)
req = fakes.HTTPRequest.blank('/v3/%s/groups/%s/update' % req = fakes.HTTPRequest.blank('/v3/%s/groups/%s/update' %
(fake.PROJECT_ID, self.group1.id), (fake.PROJECT_ID, self.group1.id),
version=GROUP_MICRO_VERSION) version=mv.GROUP_VOLUME)
add_volumes = add_volume.id add_volumes = add_volume.id
body = {"group": {"name": "group1", body = {"group": {"name": "group1",
"description": "", "description": "",
@ -962,7 +965,7 @@ class GroupsAPITestCase(test.TestCase):
group_id=fake.GROUP2_ID) group_id=fake.GROUP2_ID)
req = fakes.HTTPRequest.blank('/v3/%s/groups/%s/update' % req = fakes.HTTPRequest.blank('/v3/%s/groups/%s/update' %
(fake.PROJECT_ID, self.group1.id), (fake.PROJECT_ID, self.group1.id),
version=GROUP_MICRO_VERSION) version=mv.GROUP_VOLUME)
add_volumes = add_volume.id add_volumes = add_volume.id
body = {"group": {"name": "group1", body = {"group": {"name": "group1",
"description": "", "description": "",
@ -984,7 +987,7 @@ class GroupsAPITestCase(test.TestCase):
host=self.group1.host) host=self.group1.host)
req = fakes.HTTPRequest.blank('/v3/%s/groups/%s/update' % req = fakes.HTTPRequest.blank('/v3/%s/groups/%s/update' %
(fake.PROJECT_ID, self.group1.id), (fake.PROJECT_ID, self.group1.id),
version=GROUP_MICRO_VERSION) version=mv.GROUP_VOLUME)
body = {"group": {"name": "new name", body = {"group": {"name": "new name",
"description": None, "description": None,
@ -999,16 +1002,16 @@ class GroupsAPITestCase(test.TestCase):
self.assertEqual(add_volume.status, vol.status) self.assertEqual(add_volume.status, vol.status)
add_volume.destroy() add_volume.destroy()
@ddt.data(('3.11', 'fake_group_001', @ddt.data((mv.GROUP_TYPE, 'fake_group_001',
fields.GroupStatus.AVAILABLE, fields.GroupStatus.AVAILABLE,
exception.VersionNotFoundForAPIMethod), exception.VersionNotFoundForAPIMethod),
('3.19', 'fake_group_001', (mv.GROUP_SNAPSHOT_RESET_STATUS, 'fake_group_001',
fields.GroupStatus.AVAILABLE, fields.GroupStatus.AVAILABLE,
exception.VersionNotFoundForAPIMethod), exception.VersionNotFoundForAPIMethod),
('3.20', 'fake_group_001', (mv.GROUP_VOLUME_RESET_STATUS, 'fake_group_001',
fields.GroupStatus.AVAILABLE, fields.GroupStatus.AVAILABLE,
exception.GroupNotFound), exception.GroupNotFound),
('3.20', None, (mv.GROUP_VOLUME_RESET_STATUS, None,
'invalid_test_status', 'invalid_test_status',
webob.exc.HTTPBadRequest), webob.exc.HTTPBadRequest),
) )
@ -1029,7 +1032,7 @@ class GroupsAPITestCase(test.TestCase):
def test_reset_group_status(self): def test_reset_group_status(self):
req = fakes.HTTPRequest.blank('/v3/%s/groups/%s/action' % req = fakes.HTTPRequest.blank('/v3/%s/groups/%s/action' %
(fake.PROJECT_ID, self.group2.id), (fake.PROJECT_ID, self.group2.id),
version='3.20') version=mv.GROUP_VOLUME_RESET_STATUS)
body = {"reset_status": { body = {"reset_status": {
"status": fields.GroupStatus.AVAILABLE "status": fields.GroupStatus.AVAILABLE
}} }}
@ -1068,7 +1071,7 @@ class GroupsAPITestCase(test.TestCase):
"group_snapshot_id": group_snapshot.id}} "group_snapshot_id": group_snapshot.id}}
req = fakes.HTTPRequest.blank('/v3/%s/groups/action' % req = fakes.HTTPRequest.blank('/v3/%s/groups/action' %
fake.PROJECT_ID, fake.PROJECT_ID,
version=GROUP_FROM_SRC_MICRO_VERSION) version=mv.GROUP_SNAPSHOTS)
res_dict = self.controller.create_from_src(req, body) res_dict = self.controller.create_from_src(req, body)
self.assertIn('id', res_dict['group']) self.assertIn('id', res_dict['group'])
@ -1101,7 +1104,7 @@ class GroupsAPITestCase(test.TestCase):
"source_group_id": source_grp.id}} "source_group_id": source_grp.id}}
req = fakes.HTTPRequest.blank('/v3/%s/groups/action' % req = fakes.HTTPRequest.blank('/v3/%s/groups/action' %
fake.PROJECT_ID, fake.PROJECT_ID,
version=GROUP_FROM_SRC_MICRO_VERSION) version=mv.GROUP_SNAPSHOTS)
res_dict = self.controller.create_from_src(req, body) res_dict = self.controller.create_from_src(req, body)
self.assertIn('id', res_dict['group']) self.assertIn('id', res_dict['group'])
@ -1120,7 +1123,7 @@ class GroupsAPITestCase(test.TestCase):
def test_enable_replication(self, mock_rep_grp_type, mock_rep_vol_type): def test_enable_replication(self, mock_rep_grp_type, mock_rep_vol_type):
req = fakes.HTTPRequest.blank('/v3/%s/groups/%s/action' % req = fakes.HTTPRequest.blank('/v3/%s/groups/%s/action' %
(fake.PROJECT_ID, self.group3.id), (fake.PROJECT_ID, self.group3.id),
version=GROUP_REPLICATION_MICRO_VERSION) version=mv.GROUP_REPLICATION)
self.group3.status = fields.GroupStatus.AVAILABLE self.group3.status = fields.GroupStatus.AVAILABLE
self.group3.save() self.group3.save()
body = {"enable_replication": {}} body = {"enable_replication": {}}
@ -1145,7 +1148,7 @@ class GroupsAPITestCase(test.TestCase):
mock_rep_vol_type.return_value = is_vol_rep_type mock_rep_vol_type.return_value = is_vol_rep_type
req = fakes.HTTPRequest.blank('/v3/%s/groups/%s/action' % req = fakes.HTTPRequest.blank('/v3/%s/groups/%s/action' %
(fake.PROJECT_ID, self.group3.id), (fake.PROJECT_ID, self.group3.id),
version=GROUP_REPLICATION_MICRO_VERSION) version=mv.GROUP_REPLICATION)
self.group3.status = fields.GroupStatus.AVAILABLE self.group3.status = fields.GroupStatus.AVAILABLE
self.group3.save() self.group3.save()
body = {"enable_replication": {}} body = {"enable_replication": {}}
@ -1161,7 +1164,7 @@ class GroupsAPITestCase(test.TestCase):
mock_rep_vol_type): mock_rep_vol_type):
req = fakes.HTTPRequest.blank('/v3/%s/groups/%s/action' % req = fakes.HTTPRequest.blank('/v3/%s/groups/%s/action' %
(fake.PROJECT_ID, self.group3.id), (fake.PROJECT_ID, self.group3.id),
version=GROUP_REPLICATION_MICRO_VERSION) version=mv.GROUP_REPLICATION)
self.group3.status = fields.GroupStatus.AVAILABLE self.group3.status = fields.GroupStatus.AVAILABLE
self.group3.save() self.group3.save()
body = {"enable_replication": {}} body = {"enable_replication": {}}
@ -1173,13 +1176,13 @@ class GroupsAPITestCase(test.TestCase):
return_value=True) return_value=True)
@mock.patch('cinder.volume.utils.is_group_a_type', @mock.patch('cinder.volume.utils.is_group_a_type',
return_value=True) return_value=True)
@ddt.data((GROUP_REPLICATION_MICRO_VERSION, True, @ddt.data((mv.GROUP_REPLICATION, True,
fields.GroupStatus.CREATING, fields.GroupStatus.CREATING,
webob.exc.HTTPBadRequest), webob.exc.HTTPBadRequest),
(GROUP_REPLICATION_MICRO_VERSION, False, (mv.GROUP_REPLICATION, False,
fields.GroupStatus.AVAILABLE, fields.GroupStatus.AVAILABLE,
exception.GroupNotFound), exception.GroupNotFound),
(INVALID_GROUP_REPLICATION_MICRO_VERSION, True, (INVALID_GROUP_REPLICATION, True,
fields.GroupStatus.AVAILABLE, fields.GroupStatus.AVAILABLE,
exception.VersionNotFoundForAPIMethod), exception.VersionNotFoundForAPIMethod),
) )
@ -1209,7 +1212,7 @@ class GroupsAPITestCase(test.TestCase):
def test_disable_replication(self, mock_rep_grp_type, mock_rep_vol_type): def test_disable_replication(self, mock_rep_grp_type, mock_rep_vol_type):
req = fakes.HTTPRequest.blank('/v3/%s/groups/%s/action' % req = fakes.HTTPRequest.blank('/v3/%s/groups/%s/action' %
(fake.PROJECT_ID, self.group3.id), (fake.PROJECT_ID, self.group3.id),
version=GROUP_REPLICATION_MICRO_VERSION) version=mv.GROUP_REPLICATION)
self.group3.status = fields.GroupStatus.AVAILABLE self.group3.status = fields.GroupStatus.AVAILABLE
self.group3.replication_status = fields.ReplicationStatus.ENABLED self.group3.replication_status = fields.ReplicationStatus.ENABLED
self.group3.save() self.group3.save()
@ -1227,19 +1230,19 @@ class GroupsAPITestCase(test.TestCase):
return_value=True) return_value=True)
@mock.patch('cinder.volume.utils.is_group_a_type', @mock.patch('cinder.volume.utils.is_group_a_type',
return_value=True) return_value=True)
@ddt.data((GROUP_REPLICATION_MICRO_VERSION, True, @ddt.data((mv.GROUP_REPLICATION, True,
fields.GroupStatus.CREATING, fields.GroupStatus.CREATING,
fields.ReplicationStatus.ENABLED, fields.ReplicationStatus.ENABLED,
webob.exc.HTTPBadRequest), webob.exc.HTTPBadRequest),
(GROUP_REPLICATION_MICRO_VERSION, True, (mv.GROUP_REPLICATION, True,
fields.GroupStatus.AVAILABLE, fields.GroupStatus.AVAILABLE,
fields.ReplicationStatus.DISABLED, fields.ReplicationStatus.DISABLED,
webob.exc.HTTPBadRequest), webob.exc.HTTPBadRequest),
(GROUP_REPLICATION_MICRO_VERSION, False, (mv.GROUP_REPLICATION, False,
fields.GroupStatus.AVAILABLE, fields.GroupStatus.AVAILABLE,
fields.ReplicationStatus.DISABLED, fields.ReplicationStatus.DISABLED,
exception.GroupNotFound), exception.GroupNotFound),
(INVALID_GROUP_REPLICATION_MICRO_VERSION, True, (INVALID_GROUP_REPLICATION, True,
fields.GroupStatus.AVAILABLE, fields.GroupStatus.AVAILABLE,
fields.ReplicationStatus.ENABLED, fields.ReplicationStatus.ENABLED,
exception.VersionNotFoundForAPIMethod), exception.VersionNotFoundForAPIMethod),
@ -1272,7 +1275,7 @@ class GroupsAPITestCase(test.TestCase):
def test_failover_replication(self, mock_rep_grp_type, mock_rep_vol_type): def test_failover_replication(self, mock_rep_grp_type, mock_rep_vol_type):
req = fakes.HTTPRequest.blank('/v3/%s/groups/%s/action' % req = fakes.HTTPRequest.blank('/v3/%s/groups/%s/action' %
(fake.PROJECT_ID, self.group3.id), (fake.PROJECT_ID, self.group3.id),
version=GROUP_REPLICATION_MICRO_VERSION) version=mv.GROUP_REPLICATION)
self.group3.status = fields.GroupStatus.AVAILABLE self.group3.status = fields.GroupStatus.AVAILABLE
self.group3.replication_status = fields.ReplicationStatus.ENABLED self.group3.replication_status = fields.ReplicationStatus.ENABLED
self.group3.save() self.group3.save()
@ -1290,19 +1293,19 @@ class GroupsAPITestCase(test.TestCase):
return_value=True) return_value=True)
@mock.patch('cinder.volume.utils.is_group_a_type', @mock.patch('cinder.volume.utils.is_group_a_type',
return_value=True) return_value=True)
@ddt.data((GROUP_REPLICATION_MICRO_VERSION, True, @ddt.data((mv.GROUP_REPLICATION, True,
fields.GroupStatus.CREATING, fields.GroupStatus.CREATING,
fields.ReplicationStatus.ENABLED, fields.ReplicationStatus.ENABLED,
webob.exc.HTTPBadRequest), webob.exc.HTTPBadRequest),
(GROUP_REPLICATION_MICRO_VERSION, True, (mv.GROUP_REPLICATION, True,
fields.GroupStatus.AVAILABLE, fields.GroupStatus.AVAILABLE,
fields.ReplicationStatus.DISABLED, fields.ReplicationStatus.DISABLED,
webob.exc.HTTPBadRequest), webob.exc.HTTPBadRequest),
(GROUP_REPLICATION_MICRO_VERSION, False, (mv.GROUP_REPLICATION, False,
fields.GroupStatus.AVAILABLE, fields.GroupStatus.AVAILABLE,
fields.ReplicationStatus.DISABLED, fields.ReplicationStatus.DISABLED,
exception.GroupNotFound), exception.GroupNotFound),
(INVALID_GROUP_REPLICATION_MICRO_VERSION, True, (INVALID_GROUP_REPLICATION, True,
fields.GroupStatus.AVAILABLE, fields.GroupStatus.AVAILABLE,
fields.ReplicationStatus.ENABLED, fields.ReplicationStatus.ENABLED,
exception.VersionNotFoundForAPIMethod), exception.VersionNotFoundForAPIMethod),
@ -1337,7 +1340,7 @@ class GroupsAPITestCase(test.TestCase):
mock_rep_grp_type, mock_rep_vol_type): mock_rep_grp_type, mock_rep_vol_type):
req = fakes.HTTPRequest.blank('/v3/%s/groups/%s/action' % req = fakes.HTTPRequest.blank('/v3/%s/groups/%s/action' %
(fake.PROJECT_ID, self.group3.id), (fake.PROJECT_ID, self.group3.id),
version=GROUP_REPLICATION_MICRO_VERSION) version=mv.GROUP_REPLICATION)
targets = { targets = {
'replication_targets': [ 'replication_targets': [
{'backend_id': 'lvm_backend_1'} {'backend_id': 'lvm_backend_1'}

View File

@ -16,12 +16,16 @@
import ddt import ddt
import mock import mock
from cinder.api import microversions as mv
from cinder.api.openstack import api_version_request as api_version from cinder.api.openstack import api_version_request as api_version
from cinder.api.v3 import limits from cinder.api.v3 import limits
from cinder import test from cinder import test
from cinder.tests.unit.api import fakes from cinder.tests.unit.api import fakes
from cinder.tests.unit import fake_constants as fake from cinder.tests.unit import fake_constants as fake
LIMITS_FILTER = mv.LIMITS_ADMIN_FILTER
PRE_LIMITS_FILTER = mv.get_prior_version(LIMITS_FILTER)
@ddt.ddt @ddt.ddt
class LimitsControllerTest(test.TestCase): class LimitsControllerTest(test.TestCase):
@ -29,7 +33,8 @@ class LimitsControllerTest(test.TestCase):
super(LimitsControllerTest, self).setUp() super(LimitsControllerTest, self).setUp()
self.controller = limits.LimitsController() self.controller = limits.LimitsController()
@ddt.data(('3.38', True), ('3.38', False), ('3.39', True), ('3.39', False)) @ddt.data((PRE_LIMITS_FILTER, True), (PRE_LIMITS_FILTER, False),
(LIMITS_FILTER, True), (LIMITS_FILTER, False))
@mock.patch('cinder.quota.VolumeTypeQuotaEngine.get_project_quotas') @mock.patch('cinder.quota.VolumeTypeQuotaEngine.get_project_quotas')
def test_get_limit_with_project_id(self, ver_project, mock_get_quotas): def test_get_limit_with_project_id(self, ver_project, mock_get_quotas):
max_ver, has_project = ver_project max_ver, has_project = ver_project
@ -48,9 +53,9 @@ class LimitsControllerTest(test.TestCase):
mock_get_quotas.side_effect = get_project_quotas mock_get_quotas.side_effect = get_project_quotas
resp_dict = self.controller.index(req) resp_dict = self.controller.index(req)
# if admin, only 3.39 and req contains project_id filter, cinder # if admin, only LIMITS_FILTER and req contains project_id filter,
# returns the specified project's quota. # cinder returns the specified project's quota.
if max_ver == '3.39' and has_project: if max_ver == LIMITS_FILTER and has_project:
self.assertEqual( self.assertEqual(
5, resp_dict['limits']['absolute']['maxTotalVolumeGigabytes']) 5, resp_dict['limits']['absolute']['maxTotalVolumeGigabytes'])
else: else:

View File

@ -15,6 +15,7 @@ import mock
from six.moves import http_client from six.moves import http_client
from cinder.api import extensions from cinder.api import extensions
from cinder.api import microversions as mv
from cinder.api.v3 import messages from cinder.api.v3 import messages
from cinder import context from cinder import context
from cinder import exception from cinder import exception
@ -66,8 +67,7 @@ class MessageApiTest(test.TestCase):
self.mock_object(message_api.API, 'get', v3_fakes.fake_message_get) self.mock_object(message_api.API, 'get', v3_fakes.fake_message_get)
req = fakes.HTTPRequest.blank( req = fakes.HTTPRequest.blank(
'/v3/messages/%s' % fakes.FAKE_UUID, '/v3/messages/%s' % fakes.FAKE_UUID, version=mv.MESSAGES)
version=messages.MESSAGES_BASE_MICRO_VERSION)
req.environ['cinder.context'] = self.ctxt req.environ['cinder.context'] = self.ctxt
res_dict = self.controller.show(req, fakes.FAKE_UUID) res_dict = self.controller.show(req, fakes.FAKE_UUID)
@ -81,8 +81,7 @@ class MessageApiTest(test.TestCase):
message_id=fakes.FAKE_UUID)) message_id=fakes.FAKE_UUID))
req = fakes.HTTPRequest.blank( req = fakes.HTTPRequest.blank(
'/v3/messages/%s' % fakes.FAKE_UUID, '/v3/messages/%s' % fakes.FAKE_UUID, version=mv.MESSAGES)
version=messages.MESSAGES_BASE_MICRO_VERSION)
req.environ['cinder.context'] = self.ctxt req.environ['cinder.context'] = self.ctxt
self.assertRaises(exception.MessageNotFound, self.controller.show, self.assertRaises(exception.MessageNotFound, self.controller.show,
@ -92,7 +91,7 @@ class MessageApiTest(test.TestCase):
self.mock_object(message_api.API, 'get', v3_fakes.fake_message_get) self.mock_object(message_api.API, 'get', v3_fakes.fake_message_get)
req = fakes.HTTPRequest.blank('/v3/messages/%s' % fakes.FAKE_UUID, req = fakes.HTTPRequest.blank('/v3/messages/%s' % fakes.FAKE_UUID,
version='3.0') version=mv.BASE_VERSION)
req.environ['cinder.context'] = self.ctxt req.environ['cinder.context'] = self.ctxt
self.assertRaises(exception.VersionNotFoundForAPIMethod, self.assertRaises(exception.VersionNotFoundForAPIMethod,
@ -103,8 +102,7 @@ class MessageApiTest(test.TestCase):
self.mock_object(message_api.API, 'delete') self.mock_object(message_api.API, 'delete')
req = fakes.HTTPRequest.blank( req = fakes.HTTPRequest.blank(
'/v3/messages/%s' % fakes.FAKE_UUID, '/v3/messages/%s' % fakes.FAKE_UUID, version=mv.MESSAGES)
version=messages.MESSAGES_BASE_MICRO_VERSION)
req.environ['cinder.context'] = self.ctxt req.environ['cinder.context'] = self.ctxt
resp = self.controller.delete(req, fakes.FAKE_UUID) resp = self.controller.delete(req, fakes.FAKE_UUID)
@ -118,13 +116,13 @@ class MessageApiTest(test.TestCase):
message_id=fakes.FAKE_UUID)) message_id=fakes.FAKE_UUID))
req = fakes.HTTPRequest.blank( req = fakes.HTTPRequest.blank(
'/v3/messages/%s' % fakes.FAKE_UUID, '/v3/messages/%s' % fakes.FAKE_UUID, version=mv.MESSAGES)
version=messages.MESSAGES_BASE_MICRO_VERSION)
self.assertRaises(exception.MessageNotFound, self.controller.delete, self.assertRaises(exception.MessageNotFound, self.controller.delete,
req, fakes.FAKE_UUID) req, fakes.FAKE_UUID)
@ddt.data('3.30', '3.31', '3.34') @ddt.data(mv.get_prior_version(mv.RESOURCE_FILTER),
mv.RESOURCE_FILTER, mv.LIKE_FILTER)
@mock.patch('cinder.api.common.reject_invalid_filters') @mock.patch('cinder.api.common.reject_invalid_filters')
def test_message_list_with_general_filter(self, version, mock_update): def test_message_list_with_general_filter(self, version, mock_update):
url = '/v3/%s/messages' % fakes.FAKE_UUID url = '/v3/%s/messages' % fakes.FAKE_UUID
@ -133,8 +131,8 @@ class MessageApiTest(test.TestCase):
use_admin_context=False) use_admin_context=False)
self.controller.index(req) self.controller.index(req)
if version != '3.30': if version != mv.get_prior_version(mv.RESOURCE_FILTER):
support_like = True if version == '3.34' else False support_like = True if version == mv.LIKE_FILTER else False
mock_update.assert_called_once_with(req.environ['cinder.context'], mock_update.assert_called_once_with(req.environ['cinder.context'],
mock.ANY, 'message', mock.ANY, 'message',
support_like) support_like)
@ -143,8 +141,7 @@ class MessageApiTest(test.TestCase):
self.mock_object(message_api.API, 'get_all', self.mock_object(message_api.API, 'get_all',
return_value=[v3_fakes.fake_message(fakes.FAKE_UUID)]) return_value=[v3_fakes.fake_message(fakes.FAKE_UUID)])
req = fakes.HTTPRequest.blank( req = fakes.HTTPRequest.blank(
'/v3/messages/%s' % fakes.FAKE_UUID, '/v3/messages/%s' % fakes.FAKE_UUID, version=mv.MESSAGES)
version=messages.MESSAGES_BASE_MICRO_VERSION)
req.environ['cinder.context'] = self.ctxt req.environ['cinder.context'] = self.ctxt
res_dict = self.controller.index(req) res_dict = self.controller.index(req)

View File

@ -18,13 +18,12 @@ import ddt
import mock import mock
import six import six
from cinder.api import microversions as mv
from cinder.api.v3 import resource_filters as v3_filters from cinder.api.v3 import resource_filters as v3_filters
from cinder import test from cinder import test
from cinder.tests.unit.api import fakes from cinder.tests.unit.api import fakes
from cinder.tests.unit import fake_constants as fake from cinder.tests.unit import fake_constants as fake
FILTERS_MICRO_VERSION = '3.33'
@ddt.ddt @ddt.ddt
class ResourceFiltersAPITestCase(test.TestCase): class ResourceFiltersAPITestCase(test.TestCase):
@ -53,7 +52,7 @@ class ResourceFiltersAPITestCase(test.TestCase):
if resource is not None: if resource is not None:
request_url += '?resource=%s' % resource request_url += '?resource=%s' % resource
req = fakes.HTTPRequest.blank(request_url, req = fakes.HTTPRequest.blank(request_url,
version=FILTERS_MICRO_VERSION) version=mv.RESOURCE_FILTER_CONFIG)
with mock.patch('cinder.api.common._FILTERS_COLLECTION', filters): with mock.patch('cinder.api.common._FILTERS_COLLECTION', filters):
result = self.controller.index(req) result = self.controller.index(req)

View File

@ -20,6 +20,7 @@ from six.moves import http_client
from six.moves.urllib.parse import urlencode from six.moves.urllib.parse import urlencode
import webob import webob
from cinder.api import microversions as mv
from cinder.api.v3 import router as router_v3 from cinder.api.v3 import router as router_v3
from cinder import context from cinder import context
from cinder import objects from cinder import objects
@ -51,13 +52,13 @@ class SnapshotManageTest(test.TestCase):
fake.PROJECT_ID, fake.PROJECT_ID,
True) True)
def _get_resp_post(self, body, version="3.8"): def _get_resp_post(self, body, version=mv.MANAGE_EXISTING_LIST):
"""Helper to execute a POST manageable_snapshots API call.""" """Helper to execute a POST manageable_snapshots API call."""
req = webob.Request.blank('/v3/%s/manageable_snapshots' % req = webob.Request.blank('/v3/%s/manageable_snapshots' %
fake.PROJECT_ID) fake.PROJECT_ID)
req.method = 'POST' req.method = 'POST'
req.headers = mv.get_mv_header(version)
req.headers['Content-Type'] = 'application/json' req.headers['Content-Type'] = 'application/json'
req.headers['OpenStack-API-Version'] = 'volume ' + version
req.environ['cinder.context'] = self._admin_ctxt req.environ['cinder.context'] = self._admin_ctxt
req.body = jsonutils.dump_as_bytes(body) req.body = jsonutils.dump_as_bytes(body)
res = req.get_response(app()) res = req.get_response(app())
@ -84,10 +85,12 @@ class SnapshotManageTest(test.TestCase):
def test_manage_snapshot_previous_version(self): def test_manage_snapshot_previous_version(self):
body = {'snapshot': {'volume_id': fake.VOLUME_ID, 'ref': 'fake_ref'}} body = {'snapshot': {'volume_id': fake.VOLUME_ID, 'ref': 'fake_ref'}}
res = self._get_resp_post(body, version="3.7") res = self._get_resp_post(
body, version=mv.get_prior_version(mv.MANAGE_EXISTING_LIST))
self.assertEqual(http_client.NOT_FOUND, res.status_int, res) self.assertEqual(http_client.NOT_FOUND, res.status_int, res)
def _get_resp_get(self, host, detailed, paging, version="3.8", **kwargs): def _get_resp_get(self, host, detailed, paging,
version=mv.MANAGE_EXISTING_LIST, **kwargs):
"""Helper to execute a GET os-snapshot-manage API call.""" """Helper to execute a GET os-snapshot-manage API call."""
params = {'host': host} if host else {} params = {'host': host} if host else {}
params.update(kwargs) params.update(kwargs)
@ -101,8 +104,8 @@ class SnapshotManageTest(test.TestCase):
req = webob.Request.blank('/v3/%s/manageable_snapshots%s%s' % req = webob.Request.blank('/v3/%s/manageable_snapshots%s%s' %
(fake.PROJECT_ID, detail, query_string)) (fake.PROJECT_ID, detail, query_string))
req.method = 'GET' req.method = 'GET'
req.headers = mv.get_mv_header(version)
req.headers['Content-Type'] = 'application/json' req.headers['Content-Type'] = 'application/json'
req.headers['OpenStack-API-Version'] = 'volume ' + version
req.environ['cinder.context'] = self._admin_ctxt req.environ['cinder.context'] = self._admin_ctxt
res = req.get_response(app()) res = req.get_response(app())
return res return res
@ -120,7 +123,9 @@ class SnapshotManageTest(test.TestCase):
self.assertEqual(http_client.OK, res.status_int) self.assertEqual(http_client.OK, res.status_int)
def test_get_manageable_snapshots_previous_version(self): def test_get_manageable_snapshots_previous_version(self):
res = self._get_resp_get('fakehost', False, False, version="3.7") res = self._get_resp_get(
'fakehost', False, False,
version=mv.get_prior_version(mv.MANAGE_EXISTING_LIST))
self.assertEqual(http_client.NOT_FOUND, res.status_int) self.assertEqual(http_client.NOT_FOUND, res.status_int)
@mock.patch('cinder.volume.api.API.get_manageable_snapshots', @mock.patch('cinder.volume.api.API.get_manageable_snapshots',
@ -136,7 +141,9 @@ class SnapshotManageTest(test.TestCase):
self.assertEqual(http_client.OK, res.status_int) self.assertEqual(http_client.OK, res.status_int)
def test_get_manageable_snapshots_detail_previous_version(self): def test_get_manageable_snapshots_detail_previous_version(self):
res = self._get_resp_get('fakehost', True, True, version="3.7") res = self._get_resp_get(
'fakehost', True, True,
version=mv.get_prior_version(mv.MANAGE_EXISTING_LIST))
self.assertEqual(http_client.NOT_FOUND, res.status_int) self.assertEqual(http_client.NOT_FOUND, res.status_int)
@ddt.data((True, True, 'detail_list'), (True, False, 'summary_list'), @ddt.data((True, True, 'detail_list'), (True, False, 'summary_list'),
@ -150,12 +157,12 @@ class SnapshotManageTest(test.TestCase):
if clustered: if clustered:
host = None host = None
cluster_name = 'mycluster' cluster_name = 'mycluster'
version = '3.17' version = mv.MANAGE_EXISTING_CLUSTER
kwargs = {'cluster': cluster_name} kwargs = {'cluster': cluster_name}
else: else:
host = 'fakehost' host = 'fakehost'
cluster_name = None cluster_name = None
version = '3.8' version = mv.MANAGE_EXISTING_LIST
kwargs = {} kwargs = {}
service = objects.Service(disabled=False, host='fakehost', service = objects.Service(disabled=False, host='fakehost',
cluster_name=cluster_name) cluster_name=cluster_name)
@ -183,12 +190,13 @@ class SnapshotManageTest(test.TestCase):
mock.ANY, None, host=host, binary='cinder-volume', mock.ANY, None, host=host, binary='cinder-volume',
cluster_name=cluster_name) cluster_name=cluster_name)
@ddt.data('3.8', '3.17') @ddt.data(mv.MANAGE_EXISTING_LIST, mv.MANAGE_EXISTING_CLUSTER)
def test_get_manageable_missing_host(self, version): def test_get_manageable_missing_host(self, version):
res = self._get_resp_get(None, True, False, version=version) res = self._get_resp_get(None, True, False, version=version)
self.assertEqual(http_client.BAD_REQUEST, res.status_int) self.assertEqual(http_client.BAD_REQUEST, res.status_int)
def test_get_manageable_both_host_cluster(self): def test_get_manageable_both_host_cluster(self):
res = self._get_resp_get('host', True, False, version='3.17', res = self._get_resp_get('host', True, False,
version=mv.MANAGE_EXISTING_CLUSTER,
cluster='cluster') cluster='cluster')
self.assertEqual(http_client.BAD_REQUEST, res.status_int) self.assertEqual(http_client.BAD_REQUEST, res.status_int)

View File

@ -17,7 +17,7 @@ import ddt
import mock import mock
from cinder.api.openstack import api_version_request as api_version from cinder.api import microversions as mv
from cinder.api.v3 import snapshots from cinder.api.v3 import snapshots
from cinder import context from cinder import context
from cinder import exception from cinder import exception
@ -54,9 +54,8 @@ def create_snapshot_query_with_metadata(metadata_query_string,
"""Helper to create metadata querystring with microversion""" """Helper to create metadata querystring with microversion"""
req = fakes.HTTPRequest.blank('/v3/snapshots?metadata=' + req = fakes.HTTPRequest.blank('/v3/snapshots?metadata=' +
metadata_query_string) metadata_query_string)
req.headers["OpenStack-API-Version"] = "volume " + api_microversion req.headers = mv.get_mv_header(api_microversion)
req.api_version_request = api_version.APIVersionRequest( req.api_version_request = mv.get_api_version(api_microversion)
api_microversion)
return req return req
@ -69,7 +68,9 @@ class SnapshotApiTest(test.TestCase):
self.controller = snapshots.SnapshotsController() self.controller = snapshots.SnapshotsController()
self.ctx = context.RequestContext(fake.USER_ID, fake.PROJECT_ID, True) self.ctx = context.RequestContext(fake.USER_ID, fake.PROJECT_ID, True)
@ddt.data('3.14', '3.13', '3.41') @ddt.data(mv.GROUP_SNAPSHOTS,
mv.get_prior_version(mv.GROUP_SNAPSHOTS),
mv.SNAPSHOT_LIST_USER_ID)
@mock.patch('cinder.db.snapshot_metadata_get', return_value=dict()) @mock.patch('cinder.db.snapshot_metadata_get', return_value=dict())
@mock.patch('cinder.objects.Volume.get_by_id') @mock.patch('cinder.objects.Volume.get_by_id')
@mock.patch('cinder.objects.Snapshot.get_by_id') @mock.patch('cinder.objects.Snapshot.get_by_id')
@ -91,20 +92,20 @@ class SnapshotApiTest(test.TestCase):
snapshot_get_by_id.return_value = snapshot_obj snapshot_get_by_id.return_value = snapshot_obj
volume_get_by_id.return_value = fake_volume_obj volume_get_by_id.return_value = fake_volume_obj
req = fakes.HTTPRequest.blank('/v3/snapshots/%s' % UUID) req = fakes.HTTPRequest.blank('/v3/snapshots/%s' % UUID)
req.api_version_request = api_version.APIVersionRequest(max_ver) req.api_version_request = mv.get_api_version(max_ver)
resp_dict = self.controller.show(req, UUID) resp_dict = self.controller.show(req, UUID)
self.assertIn('snapshot', resp_dict) self.assertIn('snapshot', resp_dict)
self.assertEqual(UUID, resp_dict['snapshot']['id']) self.assertEqual(UUID, resp_dict['snapshot']['id'])
self.assertIn('updated_at', resp_dict['snapshot']) self.assertIn('updated_at', resp_dict['snapshot'])
if max_ver == '3.14': if max_ver == mv.SNAPSHOT_LIST_USER_ID:
self.assertIn('user_id', resp_dict['snapshot'])
elif max_ver == mv.GROUP_SNAPSHOTS:
self.assertIn('group_snapshot_id', resp_dict['snapshot']) self.assertIn('group_snapshot_id', resp_dict['snapshot'])
self.assertNotIn('user_id', resp_dict['snapshot']) self.assertNotIn('user_id', resp_dict['snapshot'])
elif max_ver == '3.13': else:
self.assertNotIn('group_snapshot_id', resp_dict['snapshot']) self.assertNotIn('group_snapshot_id', resp_dict['snapshot'])
self.assertNotIn('user_id', resp_dict['snapshot']) self.assertNotIn('user_id', resp_dict['snapshot'])
elif max_ver == '3.41':
self.assertIn('user_id', resp_dict['snapshot'])
def test_snapshot_show_invalid_id(self): def test_snapshot_show_invalid_id(self):
snapshot_id = INVALID_UUID snapshot_id = INVALID_UUID
@ -144,7 +145,7 @@ class SnapshotApiTest(test.TestCase):
# Generic filtering is introduced since '3,31' and we add # Generic filtering is introduced since '3,31' and we add
# 'availability_zone' support by using generic filtering. # 'availability_zone' support by using generic filtering.
req = fakes.HTTPRequest.blank(url, use_admin_context=is_admin_user, req = fakes.HTTPRequest.blank(url, use_admin_context=is_admin_user,
version='3.31') version=mv.RESOURCE_FILTER)
res_dict = self.controller.detail(req) res_dict = self.controller.detail(req)
self.assertEqual(1, len(res_dict['snapshots'])) self.assertEqual(1, len(res_dict['snapshots']))
@ -154,12 +155,13 @@ class SnapshotApiTest(test.TestCase):
self._create_snapshot(name='test1') self._create_snapshot(name='test1')
self._create_snapshot(name='test2') self._create_snapshot(name='test2')
req = fakes.HTTPRequest.blank('/v3/snapshots?sort_key=name', req = fakes.HTTPRequest.blank(
version='3.29') '/v3/snapshots?sort_key=name',
version=mv.get_prior_version(mv.SNAPSHOT_SORT))
self.assertRaises(exception.InvalidInput, self.controller.detail, req) self.assertRaises(exception.InvalidInput, self.controller.detail, req)
req = fakes.HTTPRequest.blank('/v3/snapshots?sort_key=name', req = fakes.HTTPRequest.blank('/v3/snapshots?sort_key=name',
version='3.30') version=mv.SNAPSHOT_SORT)
res_dict = self.controller.detail(req) res_dict = self.controller.detail(req)
self.assertEqual(2, len(res_dict['snapshots'])) self.assertEqual(2, len(res_dict['snapshots']))
self.assertEqual('test2', res_dict['snapshots'][0]['name']) self.assertEqual('test2', res_dict['snapshots'][0]['name'])
@ -171,7 +173,8 @@ class SnapshotApiTest(test.TestCase):
self._create_snapshot(metadata=metadata) self._create_snapshot(metadata=metadata)
# Create request with metadata filter key1: value1 # Create request with metadata filter key1: value1
req = create_snapshot_query_with_metadata('{"key1":"val1"}', '3.22') req = create_snapshot_query_with_metadata(
'{"key1":"val1"}', mv.SNAPSHOT_LIST_METADATA_FILTER)
# query controller with above request # query controller with above request
res_dict = self.controller.detail(req) res_dict = self.controller.detail(req)
@ -184,7 +187,8 @@ class SnapshotApiTest(test.TestCase):
'metadata']) 'metadata'])
# Create request with metadata filter key2: value2 # Create request with metadata filter key2: value2
req = create_snapshot_query_with_metadata('{"key2":"val2"}', '3.22') req = create_snapshot_query_with_metadata(
'{"key2":"val2"}', mv.SNAPSHOT_LIST_METADATA_FILTER)
# query controller with above request # query controller with above request
res_dict = self.controller.detail(req) res_dict = self.controller.detail(req)
@ -199,7 +203,8 @@ class SnapshotApiTest(test.TestCase):
# Create request with metadata filter key1: value1, key11: value11 # Create request with metadata filter key1: value1, key11: value11
req = create_snapshot_query_with_metadata( req = create_snapshot_query_with_metadata(
'{"key1":"val1", "key11":"val11"}', '3.22') '{"key1":"val1", "key11":"val11"}',
mv.SNAPSHOT_LIST_METADATA_FILTER)
# query controller with above request # query controller with above request
res_dict = self.controller.detail(req) res_dict = self.controller.detail(req)
@ -212,7 +217,8 @@ class SnapshotApiTest(test.TestCase):
'snapshots'][0]['metadata']) 'snapshots'][0]['metadata'])
# Create request with metadata filter key1: value1 # Create request with metadata filter key1: value1
req = create_snapshot_query_with_metadata('{"key1":"val1"}', '3.22') req = create_snapshot_query_with_metadata(
'{"key1":"val1"}', mv.SNAPSHOT_LIST_METADATA_FILTER)
# query controller with above request # query controller with above request
res_dict = self.controller.detail(req) res_dict = self.controller.detail(req)
@ -224,7 +230,19 @@ class SnapshotApiTest(test.TestCase):
self.assertDictEqual({"key1": "val1", "key11": "val11"}, res_dict[ self.assertDictEqual({"key1": "val1", "key11": "val11"}, res_dict[
'snapshots'][0]['metadata']) 'snapshots'][0]['metadata'])
@ddt.data('3.30', '3.31', '3.34') # Create request with metadata filter key2: value2
req = create_snapshot_query_with_metadata(
'{"key2":"val2"}', mv.SNAPSHOT_LIST_METADATA_FILTER)
# query controller with above request
res_dict = self.controller.detail(req)
# verify no snapshot is returned
self.assertEqual(0, len(res_dict['snapshots']))
@ddt.data(mv.get_prior_version(mv.RESOURCE_FILTER),
mv.RESOURCE_FILTER,
mv.LIKE_FILTER)
@mock.patch('cinder.api.common.reject_invalid_filters') @mock.patch('cinder.api.common.reject_invalid_filters')
def test_snapshot_list_with_general_filter(self, version, mock_update): def test_snapshot_list_with_general_filter(self, version, mock_update):
url = '/v3/%s/snapshots' % fake.PROJECT_ID url = '/v3/%s/snapshots' % fake.PROJECT_ID
@ -233,8 +251,8 @@ class SnapshotApiTest(test.TestCase):
use_admin_context=False) use_admin_context=False)
self.controller.index(req) self.controller.index(req)
if version != '3.30': if version != mv.get_prior_version(mv.RESOURCE_FILTER):
support_like = True if version == '3.34' else False support_like = True if version == mv.LIKE_FILTER else False
mock_update.assert_called_once_with(req.environ['cinder.context'], mock_update.assert_called_once_with(req.environ['cinder.context'],
mock.ANY, 'snapshot', mock.ANY, 'snapshot',
support_like) support_like)
@ -245,7 +263,9 @@ class SnapshotApiTest(test.TestCase):
self._create_snapshot(metadata=metadata) self._create_snapshot(metadata=metadata)
# Create request with metadata filter key2: value2 # Create request with metadata filter key2: value2
req = create_snapshot_query_with_metadata('{"key2":"val2"}', '3.21') req = create_snapshot_query_with_metadata(
'{"key2":"val2"}',
mv.get_prior_version(mv.SNAPSHOT_LIST_METADATA_FILTER))
# query controller with above request # query controller with above request
res_dict = self.controller.detail(req) res_dict = self.controller.detail(req)

View File

@ -20,6 +20,7 @@ from six.moves import http_client
from six.moves.urllib.parse import urlencode from six.moves.urllib.parse import urlencode
import webob import webob
from cinder.api import microversions as mv
from cinder.api.v3 import router as router_v3 from cinder.api.v3 import router as router_v3
from cinder import context from cinder import context
from cinder import objects from cinder import objects
@ -56,13 +57,13 @@ class VolumeManageTest(test.TestCase):
fake.PROJECT_ID, fake.PROJECT_ID,
True) True)
def _get_resp_post(self, body, version="3.8"): def _get_resp_post(self, body, version=mv.MANAGE_EXISTING_LIST):
"""Helper to execute a POST manageable_volumes API call.""" """Helper to execute a POST manageable_volumes API call."""
req = webob.Request.blank('/v3/%s/manageable_volumes' % req = webob.Request.blank('/v3/%s/manageable_volumes' %
fake.PROJECT_ID) fake.PROJECT_ID)
req.method = 'POST' req.method = 'POST'
req.headers = mv.get_mv_header(version)
req.headers['Content-Type'] = 'application/json' req.headers['Content-Type'] = 'application/json'
req.headers['OpenStack-API-Version'] = 'volume ' + version
req.environ['cinder.context'] = self._admin_ctxt req.environ['cinder.context'] = self._admin_ctxt
req.body = jsonutils.dump_as_bytes(body) req.body = jsonutils.dump_as_bytes(body)
res = req.get_response(app()) res = req.get_response(app())
@ -88,7 +89,8 @@ class VolumeManageTest(test.TestCase):
res = self._get_resp_post(body) res = self._get_resp_post(body)
self.assertEqual(http_client.BAD_REQUEST, res.status_int, res) self.assertEqual(http_client.BAD_REQUEST, res.status_int, res)
def _get_resp_get(self, host, detailed, paging, version="3.8", **kwargs): def _get_resp_get(self, host, detailed, paging,
version=mv.MANAGE_EXISTING_LIST, **kwargs):
"""Helper to execute a GET os-volume-manage API call.""" """Helper to execute a GET os-volume-manage API call."""
params = {'host': host} if host else {} params = {'host': host} if host else {}
params.update(kwargs) params.update(kwargs)
@ -103,8 +105,8 @@ class VolumeManageTest(test.TestCase):
req = webob.Request.blank('/v3/%s/manageable_volumes%s%s' % req = webob.Request.blank('/v3/%s/manageable_volumes%s%s' %
(fake.PROJECT_ID, detail, query_string)) (fake.PROJECT_ID, detail, query_string))
req.method = 'GET' req.method = 'GET'
req.headers = mv.get_mv_header(version)
req.headers['Content-Type'] = 'application/json' req.headers['Content-Type'] = 'application/json'
req.headers['OpenStack-API-Version'] = 'volume ' + version
req.environ['cinder.context'] = self._admin_ctxt req.environ['cinder.context'] = self._admin_ctxt
res = req.get_response(app()) res = req.get_response(app())
return res return res
@ -122,7 +124,9 @@ class VolumeManageTest(test.TestCase):
self.assertEqual(http_client.OK, res.status_int) self.assertEqual(http_client.OK, res.status_int)
def test_get_manageable_volumes_previous_version(self): def test_get_manageable_volumes_previous_version(self):
res = self._get_resp_get('fakehost', False, True, version="3.7") res = self._get_resp_get(
'fakehost', False, True,
version=mv.get_prior_version(mv.MANAGE_EXISTING_LIST))
self.assertEqual(http_client.NOT_FOUND, res.status_int) self.assertEqual(http_client.NOT_FOUND, res.status_int)
@mock.patch('cinder.volume.api.API.get_manageable_volumes', @mock.patch('cinder.volume.api.API.get_manageable_volumes',
@ -138,7 +142,9 @@ class VolumeManageTest(test.TestCase):
self.assertEqual(http_client.OK, res.status_int) self.assertEqual(http_client.OK, res.status_int)
def test_get_manageable_volumes_detail_previous_version(self): def test_get_manageable_volumes_detail_previous_version(self):
res = self._get_resp_get('fakehost', True, False, version="3.7") res = self._get_resp_get(
'fakehost', True, False,
version=mv.get_prior_version(mv.MANAGE_EXISTING_LIST))
self.assertEqual(http_client.NOT_FOUND, res.status_int) self.assertEqual(http_client.NOT_FOUND, res.status_int)
@ddt.data((True, True, 'detail_list'), (True, False, 'summary_list'), @ddt.data((True, True, 'detail_list'), (True, False, 'summary_list'),
@ -152,12 +158,12 @@ class VolumeManageTest(test.TestCase):
if clustered: if clustered:
host = None host = None
cluster_name = 'mycluster' cluster_name = 'mycluster'
version = '3.17' version = mv.MANAGE_EXISTING_CLUSTER
kwargs = {'cluster': cluster_name} kwargs = {'cluster': cluster_name}
else: else:
host = 'fakehost' host = 'fakehost'
cluster_name = None cluster_name = None
version = '3.8' version = mv.MANAGE_EXISTING_LIST
kwargs = {} kwargs = {}
service = objects.Service(disabled=False, host='fakehost', service = objects.Service(disabled=False, host='fakehost',
cluster_name=cluster_name) cluster_name=cluster_name)
@ -185,12 +191,13 @@ class VolumeManageTest(test.TestCase):
mock.ANY, None, host=host, binary='cinder-volume', mock.ANY, None, host=host, binary='cinder-volume',
cluster_name=cluster_name) cluster_name=cluster_name)
@ddt.data('3.8', '3.17') @ddt.data(mv.MANAGE_EXISTING_LIST, mv.MANAGE_EXISTING_CLUSTER)
def test_get_manageable_missing_host(self, version): def test_get_manageable_missing_host(self, version):
res = self._get_resp_get(None, True, False, version=version) res = self._get_resp_get(None, True, False, version=version)
self.assertEqual(http_client.BAD_REQUEST, res.status_int) self.assertEqual(http_client.BAD_REQUEST, res.status_int)
def test_get_manageable_both_host_cluster(self): def test_get_manageable_both_host_cluster(self):
res = self._get_resp_get('host', True, False, version='3.17', res = self._get_resp_get('host', True, False,
version=mv.MANAGE_EXISTING_CLUSTER,
cluster='cluster') cluster='cluster')
self.assertEqual(http_client.BAD_REQUEST, res.status_int) self.assertEqual(http_client.BAD_REQUEST, res.status_int)

View File

@ -18,6 +18,7 @@ from oslo_serialization import jsonutils
import webob import webob
from cinder.api import extensions from cinder.api import extensions
from cinder.api import microversions as mv
from cinder.api.v3 import volume_metadata from cinder.api.v3 import volume_metadata
from cinder.api.v3 import volumes from cinder.api.v3 import volumes
from cinder import db from cinder import db
@ -150,7 +151,7 @@ class VolumeMetaDataTest(test.TestCase):
self.volume_controller.create(req, body) self.volume_controller.create(req, body)
def test_index(self): def test_index(self):
req = fakes.HTTPRequest.blank(self.url, version="3.15") req = fakes.HTTPRequest.blank(self.url, version=mv.ETAGS)
data = self.controller.index(req, self.req_id) data = self.controller.index(req, self.req_id)
expected = { expected = {
@ -166,14 +167,14 @@ class VolumeMetaDataTest(test.TestCase):
def test_index_nonexistent_volume(self): def test_index_nonexistent_volume(self):
self.mock_object(db, 'volume_metadata_get', self.mock_object(db, 'volume_metadata_get',
return_volume_nonexistent) return_volume_nonexistent)
req = fakes.HTTPRequest.blank(self.url, version="3.15") req = fakes.HTTPRequest.blank(self.url, version=mv.ETAGS)
self.assertRaises(exception.VolumeNotFound, self.assertRaises(exception.VolumeNotFound,
self.controller.index, req, self.url) self.controller.index, req, self.url)
def test_index_no_data(self): def test_index_no_data(self):
self.mock_object(db, 'volume_metadata_get', self.mock_object(db, 'volume_metadata_get',
return_empty_volume_metadata) return_empty_volume_metadata)
req = fakes.HTTPRequest.blank(self.url, version="3.15") req = fakes.HTTPRequest.blank(self.url, version=mv.ETAGS)
data = self.controller.index(req, self.req_id) data = self.controller.index(req, self.req_id)
expected = {'metadata': {}} expected = {'metadata': {}}
result = jsonutils.loads(data.body) result = jsonutils.loads(data.body)
@ -182,7 +183,7 @@ class VolumeMetaDataTest(test.TestCase):
def test_validate_etag_true(self): def test_validate_etag_true(self):
self.mock_object(db, 'volume_metadata_get', self.mock_object(db, 'volume_metadata_get',
return_value={'key1': 'vanue1', 'key2': 'value2'}) return_value={'key1': 'vanue1', 'key2': 'value2'})
req = fakes.HTTPRequest.blank(self.url, version="3.15") req = fakes.HTTPRequest.blank(self.url, version=mv.ETAGS)
req.environ['cinder.context'] = mock.Mock() req.environ['cinder.context'] = mock.Mock()
req.if_match.etags = ['d5103bf7b26ff0310200d110da3ed186'] req.if_match.etags = ['d5103bf7b26ff0310200d110da3ed186']
self.assertTrue(self.controller._validate_etag(req, self.req_id)) self.assertTrue(self.controller._validate_etag(req, self.req_id))
@ -192,7 +193,7 @@ class VolumeMetaDataTest(test.TestCase):
fake_volume = {'id': self.req_id, 'status': 'available'} fake_volume = {'id': self.req_id, 'status': 'available'}
fake_context = mock.Mock() fake_context = mock.Mock()
metadata_update.side_effect = return_new_volume_metadata metadata_update.side_effect = return_new_volume_metadata
req = fakes.HTTPRequest.blank(self.url, version="3.15") req = fakes.HTTPRequest.blank(self.url, version=mv.ETAGS)
req.method = 'PUT' req.method = 'PUT'
req.content_type = "application/json" req.content_type = "application/json"
expected = { expected = {
@ -217,7 +218,7 @@ class VolumeMetaDataTest(test.TestCase):
fake_volume = {'id': self.req_id, 'status': 'available'} fake_volume = {'id': self.req_id, 'status': 'available'}
fake_context = mock.Mock() fake_context = mock.Mock()
metadata_update.side_effect = return_create_volume_metadata metadata_update.side_effect = return_create_volume_metadata
req = fakes.HTTPRequest.blank(self.url + '/key1', version="3.15") req = fakes.HTTPRequest.blank(self.url + '/key1', version=mv.ETAGS)
req.method = 'PUT' req.method = 'PUT'
body = {"meta": {"key1": "value1"}} body = {"meta": {"key1": "value1"}}
req.body = jsonutils.dump_as_bytes(body) req.body = jsonutils.dump_as_bytes(body)
@ -235,7 +236,7 @@ class VolumeMetaDataTest(test.TestCase):
def test_create_metadata_keys_value_none(self): def test_create_metadata_keys_value_none(self):
self.mock_object(db, 'volume_metadata_update', self.mock_object(db, 'volume_metadata_update',
return_create_volume_metadata) return_create_volume_metadata)
req = fakes.HTTPRequest.blank(self.url, version="3.15") req = fakes.HTTPRequest.blank(self.url, version=mv.ETAGS)
req.method = 'POST' req.method = 'POST'
req.headers["content-type"] = "application/json" req.headers["content-type"] = "application/json"
body = {"meta": {"key": None}} body = {"meta": {"key": None}}
@ -245,7 +246,7 @@ class VolumeMetaDataTest(test.TestCase):
def test_update_items_value_none(self): def test_update_items_value_none(self):
self.mock_object(db, 'volume_metadata_update', self.mock_object(db, 'volume_metadata_update',
return_create_volume_metadata) return_create_volume_metadata)
req = fakes.HTTPRequest.blank(self.url + '/key1', version="3.15") req = fakes.HTTPRequest.blank(self.url + '/key1', version=mv.ETAGS)
req.method = 'PUT' req.method = 'PUT'
body = {"metadata": {"key": None}} body = {"metadata": {"key": None}}
req.body = jsonutils.dump_as_bytes(body) req.body = jsonutils.dump_as_bytes(body)

View File

@ -19,7 +19,7 @@ import mock
import webob import webob
from cinder.api import extensions from cinder.api import extensions
from cinder.api.openstack import api_version_request as api_version from cinder.api import microversions as mv
from cinder.api.v2.views.volumes import ViewBuilder from cinder.api.v2.views.volumes import ViewBuilder
from cinder.api.v3 import volumes from cinder.api.v3 import volumes
from cinder import context from cinder import context
@ -38,10 +38,7 @@ from cinder import utils
from cinder.volume import api as volume_api from cinder.volume import api as volume_api
from cinder.volume import api as vol_get from cinder.volume import api as vol_get
version_header_name = 'OpenStack-API-Version'
DEFAULT_AZ = "zone1:host1" DEFAULT_AZ = "zone1:host1"
REVERT_TO_SNAPSHOT_VERSION = '3.40'
@ddt.ddt @ddt.ddt
@ -61,7 +58,7 @@ class VolumeApiTest(test.TestCase):
req = fakes.HTTPRequest.blank('/v3/volumes?bootable=True') req = fakes.HTTPRequest.blank('/v3/volumes?bootable=True')
req.method = 'GET' req.method = 'GET'
req.content_type = 'application/json' req.content_type = 'application/json'
req.headers = {version_header_name: 'volume 3.0'} req.headers = mv.get_mv_header(mv.BASE_VERSION)
req.environ['cinder.context'].is_admin = True req.environ['cinder.context'].is_admin = True
self.override_config('query_volume_filters', 'bootable') self.override_config('query_volume_filters', 'bootable')
@ -77,9 +74,10 @@ class VolumeApiTest(test.TestCase):
req = fakes.HTTPRequest.blank('/v3/volumes?bootable=True') req = fakes.HTTPRequest.blank('/v3/volumes?bootable=True')
req.method = 'GET' req.method = 'GET'
req.content_type = 'application/json' req.content_type = 'application/json'
req.headers = {version_header_name: 'volume 3.2'} req.headers = mv.get_mv_header(mv.VOLUME_LIST_BOOTABLE)
req.environ['cinder.context'].is_admin = True req.environ['cinder.context'].is_admin = True
req.api_version_request = api_version.APIVersionRequest('3.29') req.api_version_request = mv.get_api_version(
mv.VOLUME_LIST_BOOTABLE)
self.override_config('query_volume_filters', 'bootable') self.override_config('query_volume_filters', 'bootable')
self.controller.index(req) self.controller.index(req)
@ -119,8 +117,9 @@ class VolumeApiTest(test.TestCase):
vols = self._create_volume_with_glance_metadata() vols = self._create_volume_with_glance_metadata()
req = fakes.HTTPRequest.blank("/v3/volumes?glance_metadata=" req = fakes.HTTPRequest.blank("/v3/volumes?glance_metadata="
"{'image_name': 'imageTestOne'}") "{'image_name': 'imageTestOne'}")
req.headers["OpenStack-API-Version"] = "volume 3.4" req.headers = mv.get_mv_header(mv.VOLUME_LIST_GLANCE_METADATA)
req.api_version_request = api_version.APIVersionRequest('3.4') req.api_version_request = mv.get_api_version(
mv.VOLUME_LIST_GLANCE_METADATA)
req.environ['cinder.context'] = self.ctxt req.environ['cinder.context'] = self.ctxt
res_dict = self.controller.index(req) res_dict = self.controller.index(req)
volumes = res_dict['volumes'] volumes = res_dict['volumes']
@ -131,8 +130,8 @@ class VolumeApiTest(test.TestCase):
self._create_volume_with_glance_metadata() self._create_volume_with_glance_metadata()
req = fakes.HTTPRequest.blank("/v3/volumes?glance_metadata=" req = fakes.HTTPRequest.blank("/v3/volumes?glance_metadata="
"{'image_name': 'imageTestOne'}") "{'image_name': 'imageTestOne'}")
req.headers["OpenStack-API-Version"] = "volume 3.0" req.headers = mv.get_mv_header(mv.BASE_VERSION)
req.api_version_request = api_version.APIVersionRequest('3.0') req.api_version_request = mv.get_api_version(mv.BASE_VERSION)
req.environ['cinder.context'] = self.ctxt req.environ['cinder.context'] = self.ctxt
res_dict = self.controller.index(req) res_dict = self.controller.index(req)
volumes = res_dict['volumes'] volumes = res_dict['volumes']
@ -142,8 +141,8 @@ class VolumeApiTest(test.TestCase):
vols = self._create_volume_with_group() vols = self._create_volume_with_group()
req = fakes.HTTPRequest.blank(("/v3/volumes?group_id=%s") % req = fakes.HTTPRequest.blank(("/v3/volumes?group_id=%s") %
fake.GROUP_ID) fake.GROUP_ID)
req.headers["OpenStack-API-Version"] = "volume 3.10" req.headers = mv.get_mv_header(mv.VOLUME_LIST_GROUP)
req.api_version_request = api_version.APIVersionRequest('3.10') req.api_version_request = mv.get_api_version(mv.VOLUME_LIST_GROUP)
req.environ['cinder.context'] = self.ctxt req.environ['cinder.context'] = self.ctxt
res_dict = self.controller.index(req) res_dict = self.controller.index(req)
volumes = res_dict['volumes'] volumes = res_dict['volumes']
@ -154,26 +153,29 @@ class VolumeApiTest(test.TestCase):
self._create_volume_with_group() self._create_volume_with_group()
req = fakes.HTTPRequest.blank(("/v3/volumes?group_id=%s") % req = fakes.HTTPRequest.blank(("/v3/volumes?group_id=%s") %
fake.GROUP_ID) fake.GROUP_ID)
req.headers["OpenStack-API-Version"] = "volume 3.9" req.headers = mv.get_mv_header(mv.BACKUP_UPDATE)
req.api_version_request = api_version.APIVersionRequest('3.9') req.api_version_request = mv.get_api_version(mv.BACKUP_UPDATE)
req.environ['cinder.context'] = self.ctxt req.environ['cinder.context'] = self.ctxt
res_dict = self.controller.index(req) res_dict = self.controller.index(req)
volumes = res_dict['volumes'] volumes = res_dict['volumes']
self.assertEqual(2, len(volumes)) self.assertEqual(2, len(volumes))
def _fake_volumes_summary_request(self, version='3.12', all_tenant=False, def _fake_volumes_summary_request(self,
version=mv.VOLUME_SUMMARY,
all_tenant=False,
is_admin=False): is_admin=False):
req_url = '/v3/volumes/summary' req_url = '/v3/volumes/summary'
if all_tenant: if all_tenant:
req_url += '?all_tenants=True' req_url += '?all_tenants=True'
req = fakes.HTTPRequest.blank(req_url, use_admin_context=is_admin) req = fakes.HTTPRequest.blank(req_url, use_admin_context=is_admin)
req.headers = {'OpenStack-API-Version': 'volume ' + version} req.headers = mv.get_mv_header(version)
req.api_version_request = api_version.APIVersionRequest(version) req.api_version_request = mv.get_api_version(version)
return req return req
def test_volumes_summary_in_unsupport_version(self): def test_volumes_summary_in_unsupport_version(self):
"""Function call to test summary volumes API in unsupported version""" """Function call to test summary volumes API in unsupported version"""
req = self._fake_volumes_summary_request(version='3.7') req = self._fake_volumes_summary_request(
version=mv.get_prior_version(mv.VOLUME_SUMMARY))
self.assertRaises(exception.VersionNotFoundForAPIMethod, self.assertRaises(exception.VersionNotFoundForAPIMethod,
self.controller.summary, req) self.controller.summary, req)
@ -196,11 +198,12 @@ class VolumeApiTest(test.TestCase):
self.assertEqual(expected, res_dict) self.assertEqual(expected, res_dict)
@ddt.data( @ddt.data(
('3.35', {'volume-summary': {'total_size': 0.0, (mv.get_prior_version(mv.VOLUME_SUMMARY_METADATA),
'total_count': 0}}), {'volume-summary': {'total_size': 0.0,
('3.36', {'volume-summary': {'total_size': 0.0, 'total_count': 0}}),
'total_count': 0, (mv.VOLUME_SUMMARY_METADATA, {'volume-summary': {'total_size': 0.0,
'metadata': {}}})) 'total_count': 0,
'metadata': {}}}))
@ddt.unpack @ddt.unpack
def test_volume_summary_empty(self, summary_api_version, expect_result): def test_volume_summary_empty(self, summary_api_version, expect_result):
req = self._fake_volumes_summary_request(version=summary_api_version) req = self._fake_volumes_summary_request(version=summary_api_version)
@ -208,13 +211,15 @@ class VolumeApiTest(test.TestCase):
self.assertEqual(expect_result, res_dict) self.assertEqual(expect_result, res_dict)
@ddt.data( @ddt.data(
('3.35', {'volume-summary': {'total_size': 2, (mv.get_prior_version(mv.VOLUME_SUMMARY_METADATA),
'total_count': 2}}), {'volume-summary': {'total_size': 2,
('3.36', {'volume-summary': {'total_size': 2, 'total_count': 2}}),
'total_count': 2, (mv.VOLUME_SUMMARY_METADATA,
'metadata': { {'volume-summary': {'total_size': 2,
'name': ['test_name1', 'test_name2'], 'total_count': 2,
'age': ['test_age']}}})) 'metadata': {
'name': ['test_name1', 'test_name2'],
'age': ['test_age']}}}))
@ddt.unpack @ddt.unpack
def test_volume_summary_return_metadata(self, summary_api_version, def test_volume_summary_return_metadata(self, summary_api_version,
expect_result): expect_result):
@ -230,13 +235,15 @@ class VolumeApiTest(test.TestCase):
self.assertEqual(expect_result, res_dict) self.assertEqual(expect_result, res_dict)
@ddt.data( @ddt.data(
('3.35', {'volume-summary': {'total_size': 2, (mv.get_prior_version(mv.VOLUME_SUMMARY_METADATA),
'total_count': 2}}), {'volume-summary': {'total_size': 2,
('3.36', {'volume-summary': {'total_size': 2, 'total_count': 2}}),
'total_count': 2, (mv.VOLUME_SUMMARY_METADATA,
'metadata': { {'volume-summary': {'total_size': 2,
'name': ['test_name1', 'test_name2'], 'total_count': 2,
'age': ['test_age']}}})) 'metadata': {
'name': ['test_name1', 'test_name2'],
'age': ['test_age']}}}))
@ddt.unpack @ddt.unpack
def test_volume_summary_return_metadata_all_tenant( def test_volume_summary_return_metadata_all_tenant(
self, summary_api_version, expect_result): self, summary_api_version, expect_result):
@ -334,8 +341,9 @@ class VolumeApiTest(test.TestCase):
if with_migration_status: if with_migration_status:
volume['volume']['migration_status'] = None volume['volume']['migration_status'] = None
# Remove group_id if max version is less than 3.13. # Remove group_id if max version is less than GROUP_VOLUME.
if req_version and req_version.matches(None, "3.12"): if req_version and req_version.matches(
None, mv.get_prior_version(mv.GROUP_VOLUME)):
volume['volume'].pop('group_id') volume['volume'].pop('group_id')
return volume return volume
@ -356,13 +364,14 @@ class VolumeApiTest(test.TestCase):
'group': test_group, 'group': test_group,
} }
# Remove group_id if max version is less than 3.13. # Remove group_id if max version is less than GROUP_VOLUME.
if req_version and req_version.matches(None, "3.12"): if req_version and req_version.matches(
None, mv.get_prior_version(mv.GROUP_VOLUME)):
volume.pop('group') volume.pop('group')
return volume return volume
@ddt.data('3.13', '3.12') @ddt.data(mv.GROUP_VOLUME, mv.get_prior_version(mv.GROUP_VOLUME))
@mock.patch( @mock.patch(
'cinder.api.openstack.wsgi.Controller.validate_name_and_description') 'cinder.api.openstack.wsgi.Controller.validate_name_and_description')
def test_volume_create(self, max_ver, mock_validate): def test_volume_create(self, max_ver, mock_validate):
@ -375,14 +384,14 @@ class VolumeApiTest(test.TestCase):
vol = self._vol_in_request_body() vol = self._vol_in_request_body()
body = {"volume": vol} body = {"volume": vol}
req = fakes.HTTPRequest.blank('/v3/volumes') req = fakes.HTTPRequest.blank('/v3/volumes')
req.api_version_request = api_version.APIVersionRequest(max_ver) req.api_version_request = mv.get_api_version(max_ver)
res_dict = self.controller.create(req, body) res_dict = self.controller.create(req, body)
ex = self._expected_vol_from_controller( ex = self._expected_vol_from_controller(
req_version=req.api_version_request) req_version=req.api_version_request)
self.assertEqual(ex, res_dict) self.assertEqual(ex, res_dict)
self.assertTrue(mock_validate.called) self.assertTrue(mock_validate.called)
@ddt.data('3.14', '3.13') @ddt.data(mv.GROUP_SNAPSHOTS, mv.get_prior_version(mv.GROUP_SNAPSHOTS))
@mock.patch.object(group_api.API, 'get') @mock.patch.object(group_api.API, 'get')
@mock.patch.object(db.sqlalchemy.api, '_volume_type_get_full', @mock.patch.object(db.sqlalchemy.api, '_volume_type_get_full',
autospec=True) autospec=True)
@ -405,7 +414,7 @@ class VolumeApiTest(test.TestCase):
group_id=fake.GROUP_ID) group_id=fake.GROUP_ID)
body = {"volume": vol} body = {"volume": vol}
req = fakes.HTTPRequest.blank('/v3/volumes') req = fakes.HTTPRequest.blank('/v3/volumes')
req.api_version_request = api_version.APIVersionRequest(max_ver) req.api_version_request = mv.get_api_version(max_ver)
res_dict = self.controller.create(req, body) res_dict = self.controller.create(req, body)
ex = self._expected_vol_from_controller( ex = self._expected_vol_from_controller(
snapshot_id=snapshot_id, snapshot_id=snapshot_id,
@ -450,23 +459,26 @@ class VolumeApiTest(test.TestCase):
self.assertRaises(webob.exc.HTTPBadRequest, self.controller.create, self.assertRaises(webob.exc.HTTPBadRequest, self.controller.create,
req, body) req, body)
@ddt.data('3.30', '3.31', '3.34') @ddt.data(mv.get_prior_version(mv.RESOURCE_FILTER), mv.RESOURCE_FILTER,
mv.LIKE_FILTER)
@mock.patch.object(volume_api.API, 'check_volume_filters', mock.Mock()) @mock.patch.object(volume_api.API, 'check_volume_filters', mock.Mock())
@mock.patch.object(utils, 'add_visible_admin_metadata', mock.Mock()) @mock.patch.object(utils, 'add_visible_admin_metadata', mock.Mock())
@mock.patch('cinder.api.common.reject_invalid_filters') @mock.patch('cinder.api.common.reject_invalid_filters')
def test_list_volume_with_general_filter(self, version, mock_update): def test_list_volume_with_general_filter(self, version, mock_update):
req = fakes.HTTPRequest.blank('/v3/volumes', version=version) req = fakes.HTTPRequest.blank('/v3/volumes', version=version)
self.controller.index(req) self.controller.index(req)
if version != '3.30': if version >= mv.RESOURCE_FILTER:
support_like = True if version == '3.34' else False support_like = True if version == mv.LIKE_FILTER else False
mock_update.assert_called_once_with(req.environ['cinder.context'], mock_update.assert_called_once_with(req.environ['cinder.context'],
mock.ANY, 'volume', mock.ANY, 'volume',
support_like) support_like)
@ddt.data({'admin': True, 'version': '3.21'}, @ddt.data({'admin': True, 'version': mv.VOLUME_DETAIL_PROVIDER_ID},
{'admin': False, 'version': '3.21'}, {'admin': False, 'version': mv.VOLUME_DETAIL_PROVIDER_ID},
{'admin': True, 'version': '3.20'}, {'admin': True,
{'admin': False, 'version': '3.20'}) 'version': mv.get_prior_version(mv.VOLUME_DETAIL_PROVIDER_ID)},
{'admin': False,
'version': mv.get_prior_version(mv.VOLUME_DETAIL_PROVIDER_ID)})
@ddt.unpack @ddt.unpack
def test_volume_show_provider_id(self, admin, version): def test_volume_show_provider_id(self, admin, version):
self.mock_object(volume_api.API, 'get', v2_fakes.fake_volume_api_get) self.mock_object(volume_api.API, 'get', v2_fakes.fake_volume_api_get)
@ -482,8 +494,8 @@ class VolumeApiTest(test.TestCase):
res_dict = self.controller.show(req, fake.VOLUME_ID) res_dict = self.controller.show(req, fake.VOLUME_ID)
req_version = req.api_version_request req_version = req.api_version_request
# provider_id is in view if min version is greater than or equal to # provider_id is in view if min version is greater than or equal to
# 3.21 for admin. # VOLUME_DETAIL_PROVIDER_ID for admin.
if req_version.matches("3.21", None) and admin: if req_version.matches(mv.VOLUME_DETAIL_PROVIDER_ID, None) and admin:
self.assertIn('provider_id', res_dict['volume']) self.assertIn('provider_id', res_dict['volume'])
else: else:
self.assertNotIn('provider_id', res_dict['volume']) self.assertNotIn('provider_id', res_dict['volume'])
@ -516,10 +528,9 @@ class VolumeApiTest(test.TestCase):
mock_latest.side_effect = exception.VolumeSnapshotNotFound(volume_id= mock_latest.side_effect = exception.VolumeSnapshotNotFound(volume_id=
'fake_id') 'fake_id')
req = fakes.HTTPRequest.blank('/v3/volumes/fake_id/revert') req = fakes.HTTPRequest.blank('/v3/volumes/fake_id/revert')
req.headers = {'OpenStack-API-Version': req.headers = mv.get_mv_header(mv.VOLUME_REVERT)
'volume %s' % REVERT_TO_SNAPSHOT_VERSION} req.api_version_request = mv.get_api_version(
req.api_version_request = api_version.APIVersionRequest( mv.VOLUME_REVERT)
REVERT_TO_SNAPSHOT_VERSION)
self.assertRaises(webob.exc.HTTPBadRequest, self.controller.revert, self.assertRaises(webob.exc.HTTPBadRequest, self.controller.revert,
req, 'fake_id', {'revert': {'snapshot_id': req, 'fake_id', {'revert': {'snapshot_id':
@ -534,10 +545,9 @@ class VolumeApiTest(test.TestCase):
fake_snapshot = self._fake_create_snapshot(fake.UUID1) fake_snapshot = self._fake_create_snapshot(fake.UUID1)
mock_latest.return_value = fake_snapshot mock_latest.return_value = fake_snapshot
req = fakes.HTTPRequest.blank('/v3/volumes/fake_id/revert') req = fakes.HTTPRequest.blank('/v3/volumes/fake_id/revert')
req.headers = {'OpenStack-API-Version': req.headers = mv.get_mv_header(mv.VOLUME_REVERT)
'volume %s' % REVERT_TO_SNAPSHOT_VERSION} req.api_version_request = mv.get_api_version(
req.api_version_request = api_version.APIVersionRequest( mv.VOLUME_REVERT)
REVERT_TO_SNAPSHOT_VERSION)
self.assertRaises(webob.exc.HTTPBadRequest, self.controller.revert, self.assertRaises(webob.exc.HTTPBadRequest, self.controller.revert,
req, 'fake_id', {'revert': {'snapshot_id': req, 'fake_id', {'revert': {'snapshot_id':
@ -557,10 +567,9 @@ class VolumeApiTest(test.TestCase):
mock_latest.return_value = fake_snapshot mock_latest.return_value = fake_snapshot
req = fakes.HTTPRequest.blank('/v3/volumes/%s/revert' req = fakes.HTTPRequest.blank('/v3/volumes/%s/revert'
% fake_volume['id']) % fake_volume['id'])
req.headers = {'OpenStack-API-Version': req.headers = mv.get_mv_header(mv.VOLUME_REVERT)
'volume %s' % REVERT_TO_SNAPSHOT_VERSION} req.api_version_request = mv.get_api_version(
req.api_version_request = api_version.APIVersionRequest( mv.VOLUME_REVERT)
REVERT_TO_SNAPSHOT_VERSION)
# update volume's status failed # update volume's status failed
mock_update.side_effect = [False, True] mock_update.side_effect = [False, True]

View File

@ -19,6 +19,7 @@ from oslo_serialization import jsonutils
from six.moves import http_client from six.moves import http_client
import webob import webob
from cinder.api import microversions as mv
from cinder.api.v3 import router as router_v3 from cinder.api.v3 import router as router_v3
from cinder.api.v3 import workers from cinder.api.v3 import workers
from cinder import context from cinder import context
@ -61,7 +62,7 @@ class WorkersTestCase(test.TestCase):
overwrite=False) overwrite=False)
self.controller = workers.create_resource() self.controller = workers.create_resource()
def _get_resp_post(self, body, version='3.24', ctxt=None): def _get_resp_post(self, body, version=mv.WORKERS_CLEANUP, ctxt=None):
"""Helper to execute a POST workers API call.""" """Helper to execute a POST workers API call."""
req = webob.Request.blank('/v3/%s/workers/cleanup' % fake.PROJECT_ID) req = webob.Request.blank('/v3/%s/workers/cleanup' % fake.PROJECT_ID)
req.method = 'POST' req.method = 'POST'
@ -74,7 +75,7 @@ class WorkersTestCase(test.TestCase):
@mock.patch('cinder.scheduler.rpcapi.SchedulerAPI.work_cleanup') @mock.patch('cinder.scheduler.rpcapi.SchedulerAPI.work_cleanup')
def test_cleanup_old_api_version(self, rpc_mock): def test_cleanup_old_api_version(self, rpc_mock):
res = self._get_resp_post({}, '3.19') res = self._get_resp_post({}, mv.get_prior_version(mv.WORKERS_CLEANUP))
self.assertEqual(http_client.NOT_FOUND, res.status_code) self.assertEqual(http_client.NOT_FOUND, res.status_code)
rpc_mock.assert_not_called() rpc_mock.assert_not_called()

View File

@ -16,6 +16,7 @@ from oslo_config import cfg
from oslo_utils import timeutils from oslo_utils import timeutils
from cinder.api import extensions from cinder.api import extensions
from cinder.api import microversions as mv
from cinder.api.openstack import api_version_request as api_version from cinder.api.openstack import api_version_request as api_version
from cinder.api.v3 import messages from cinder.api.v3 import messages
from cinder import context from cinder import context
@ -128,8 +129,8 @@ class MessageApiTest(test.TestCase):
req = fakes.HTTPRequest.blank(url) req = fakes.HTTPRequest.blank(url)
req.method = 'GET' req.method = 'GET'
req.content_type = 'application/json' req.content_type = 'application/json'
req.headers = {version_header_name: 'volume 3.5'} req.headers = mv.get_mv_header(mv.MESSAGES_PAGINATION)
req.api_version_request = api_version.APIVersionRequest('3.30') req.api_version_request = mv.get_api_version(mv.RESOURCE_FILTER)
req.environ['cinder.context'].is_admin = True req.environ['cinder.context'].is_admin = True
res = self.controller.index(req) res = self.controller.index(req)
@ -139,8 +140,8 @@ class MessageApiTest(test.TestCase):
req = fakes.HTTPRequest.blank(url) req = fakes.HTTPRequest.blank(url)
req.method = 'GET' req.method = 'GET'
req.content_type = 'application/json' req.content_type = 'application/json'
req.headers = {version_header_name: 'volume 3.5'} req.headers = mv.get_mv_header(mv.MESSAGES_PAGINATION)
req.api_version_request = api_version.APIVersionRequest('3.30') req.api_version_request = mv.get_api_version(mv.RESOURCE_FILTER)
req.environ['cinder.context'].is_admin = True req.environ['cinder.context'].is_admin = True
res = self.controller.index(req) res = self.controller.index(req)
@ -149,12 +150,14 @@ class MessageApiTest(test.TestCase):
def test_get_all_messages_with_limit_wrong_version(self): def test_get_all_messages_with_limit_wrong_version(self):
self.create_message_for_tests() self.create_message_for_tests()
PRE_MESSAGES_PAGINATION = mv.get_prior_version(mv.MESSAGES_PAGINATION)
url = '/v3/messages?limit=1' url = '/v3/messages?limit=1'
req = fakes.HTTPRequest.blank(url) req = fakes.HTTPRequest.blank(url)
req.method = 'GET' req.method = 'GET'
req.content_type = 'application/json' req.content_type = 'application/json'
req.headers["OpenStack-API-Version"] = "volume 3.3" req.headers = mv.get_mv_header(PRE_MESSAGES_PAGINATION)
req.api_version_request = api_version.APIVersionRequest('3.3') req.api_version_request = mv.get_api_version(PRE_MESSAGES_PAGINATION)
req.environ['cinder.context'].is_admin = True req.environ['cinder.context'].is_admin = True
res = self.controller.index(req) res = self.controller.index(req)
@ -167,8 +170,8 @@ class MessageApiTest(test.TestCase):
req = fakes.HTTPRequest.blank(url) req = fakes.HTTPRequest.blank(url)
req.method = 'GET' req.method = 'GET'
req.content_type = 'application/json' req.content_type = 'application/json'
req.headers["OpenStack-API-Version"] = "volume 3.5" req.headers = mv.get_mv_header(mv.MESSAGES_PAGINATION)
req.api_version_request = api_version.APIVersionRequest('3.5') req.api_version_request = mv.get_api_version(mv.MESSAGES_PAGINATION)
req.environ['cinder.context'].is_admin = True req.environ['cinder.context'].is_admin = True
res = self.controller.index(req) res = self.controller.index(req)
@ -181,8 +184,8 @@ class MessageApiTest(test.TestCase):
req = fakes.HTTPRequest.blank(url) req = fakes.HTTPRequest.blank(url)
req.method = 'GET' req.method = 'GET'
req.content_type = 'application/json' req.content_type = 'application/json'
req.headers["OpenStack-API-Version"] = "volume 3.5" req.headers = mv.get_mv_header(mv.MESSAGES_PAGINATION)
req.api_version_request = api_version.APIVersionRequest('3.5') req.api_version_request = mv.get_api_version(mv.MESSAGES_PAGINATION)
req.environ['cinder.context'].is_admin = True req.environ['cinder.context'].is_admin = True
res = self.controller.index(req) res = self.controller.index(req)
@ -196,8 +199,8 @@ class MessageApiTest(test.TestCase):
req = fakes.HTTPRequest.blank(url) req = fakes.HTTPRequest.blank(url)
req.method = 'GET' req.method = 'GET'
req.content_type = 'application/json' req.content_type = 'application/json'
req.headers["OpenStack-API-Version"] = "volume 3.5" req.headers = mv.get_mv_header(mv.MESSAGES_PAGINATION)
req.api_version_request = api_version.APIVersionRequest('3.5') req.api_version_request = mv.get_api_version(mv.MESSAGES_PAGINATION)
req.environ['cinder.context'].is_admin = True req.environ['cinder.context'].is_admin = True
res = self.controller.index(req) res = self.controller.index(req)
@ -210,8 +213,8 @@ class MessageApiTest(test.TestCase):
req = fakes.HTTPRequest.blank(url) req = fakes.HTTPRequest.blank(url)
req.method = 'GET' req.method = 'GET'
req.content_type = 'application/json' req.content_type = 'application/json'
req.headers["OpenStack-API-Version"] = "volume 3.5" req.headers = mv.get_mv_header(mv.MESSAGES_PAGINATION)
req.api_version_request = api_version.APIVersionRequest('3.5') req.api_version_request = mv.get_api_version(mv.MESSAGES_PAGINATION)
req.environ['cinder.context'].is_admin = True req.environ['cinder.context'].is_admin = True
res = self.controller.index(req) res = self.controller.index(req)
@ -242,8 +245,8 @@ class MessageApiTest(test.TestCase):
req = fakes.HTTPRequest.blank(url) req = fakes.HTTPRequest.blank(url)
req.method = 'GET' req.method = 'GET'
req.content_type = 'application/json' req.content_type = 'application/json'
req.headers = {version_header_name: 'volume 3.5'} req.headers = mv.get_mv_header(mv.MESSAGES_PAGINATION)
req.api_version_request = api_version.APIVersionRequest('3.30') req.api_version_request = mv.get_api_version(mv.RESOURCE_FILTER)
req.environ['cinder.context'].is_admin = True req.environ['cinder.context'].is_admin = True
res = self.controller.index(req) res = self.controller.index(req)
@ -263,7 +266,7 @@ class MessageApiTest(test.TestCase):
req = fakes.HTTPRequest.blank(url) req = fakes.HTTPRequest.blank(url)
req.method = 'GET' req.method = 'GET'
req.content_type = 'application/json' req.content_type = 'application/json'
req.headers = {version_header_name: 'volume 3.5'} req.headers = mv.get_mv_header(mv.MESSAGES_PAGINATION)
req.api_version_request = api_version.max_api_version() req.api_version_request = api_version.max_api_version()
req.environ['cinder.context'].is_admin = True req.environ['cinder.context'].is_admin = True

View File

@ -1909,20 +1909,20 @@ class API(base.Base):
def _check_boolean_filter_value(self, key, val, strict=False): def _check_boolean_filter_value(self, key, val, strict=False):
"""Boolean filter values in Volume GET. """Boolean filter values in Volume GET.
Before V3.2, all values other than 'False', 'false', 'FALSE' were Before VOLUME_LIST_BOOTABLE, all values other than 'False', 'false',
trated as True for specific boolean filter parameters in Volume 'FALSE' were trated as True for specific boolean filter parameters in
GET request. Volume GET request.
But V3.2 onwards, only true/True/0/1/False/false parameters are But VOLUME_LIST_BOOTABLE onwards, only true/True/0/1/False/false
supported. parameters are supported.
All other input values to specific boolean filter parameter will All other input values to specific boolean filter parameter will
lead to raising exception. lead to raising exception.
This changes API behavior. So, micro version introduced for V3.2 This changes API behavior. So, micro version introduced for
onwards. VOLUME_LIST_BOOTABLE onwards.
""" """
if strict: if strict:
# for updated behavior, from V3.2 onwards. # for updated behavior, from VOLUME_LIST_BOOTABLE onwards.
# To translate any true/false/t/f/0/1 to True/False # To translate any true/false/t/f/0/1 to True/False
# which is only acceptable format in database queries. # which is only acceptable format in database queries.
try: try:
@ -1932,7 +1932,7 @@ class API(base.Base):
'value': val} 'value': val}
raise exception.InvalidInput(reason=msg) raise exception.InvalidInput(reason=msg)
else: else:
# For existing behavior(before version 3.2) # For existing behavior(before version VOLUME_LIST_BOOTABLE)
accepted_true = ['True', 'true', 'TRUE'] accepted_true = ['True', 'true', 'TRUE']
accepted_false = ['False', 'false', 'FALSE'] accepted_false = ['False', 'false', 'FALSE']

View File

@ -278,8 +278,17 @@ necessary to add changes to other places which describe your change:
be enough information that it could be used by the docs team for be enough information that it could be used by the docs team for
release notes. release notes.
* Constants should be used in the code to minimize errors on microversion
merge conflicts. Define a constant for the new microversion in the
``cinder/api/microversions.py`` file and use that in the rest of the code.
* Update the expected versions in affected tests. * Update the expected versions in affected tests.
* API changes should almost always include a release note announcing the
availability of the new API functionality. The description of the API change
should indicate which microversion is required for the change, and it should
refer to the numerical value of the microversion and not its constant name.
Allocating a microversion Allocating a microversion
------------------------- -------------------------