diff --git a/cinder/api/common.py b/cinder/api/common.py index 4bf09eb8705..5cbab7530f2 100644 --- a/cinder/api/common.py +++ b/cinder/api/common.py @@ -24,6 +24,7 @@ from oslo_log import log as logging from six.moves import urllib import webob +from cinder.api import microversions as mv from cinder.common import constants from cinder import exception from cinder.i18n import _ @@ -66,8 +67,6 @@ CONF.register_opts(api_common_opts) LOG = logging.getLogger(__name__) _FILTERS_COLLECTION = None -FILTERING_VERSION = '3.31' -LIKE_FILTER_VERSION = '3.34' ATTRIBUTE_CONVERTERS = {'name~': 'display_name~', 'description~': 'display_description~'} @@ -492,9 +491,9 @@ def process_general_filtering(resource): req_version = kwargs.get('req_version') filters = kwargs.get('filters') context = kwargs.get('context') - if req_version.matches(FILTERING_VERSION): + if req_version.matches(mv.RESOURCE_FILTER): support_like = False - if req_version.matches(LIKE_FILTER_VERSION): + if req_version.matches(mv.LIKE_FILTER): support_like = True reject_invalid_filters(context, filters, resource, support_like) diff --git a/cinder/api/contrib/admin_actions.py b/cinder/api/contrib/admin_actions.py index 4d9dfea0a68..8056a243928 100644 --- a/cinder/api/contrib/admin_actions.py +++ b/cinder/api/contrib/admin_actions.py @@ -20,6 +20,7 @@ from webob import exc from cinder.api import common from cinder.api import extensions +from cinder.api import microversions as mv from cinder.api.openstack import wsgi from cinder import backup from cinder import db @@ -245,7 +246,8 @@ class VolumeAdminController(AdminController): volume = self._get(context, id) 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) lock_volume = utils.get_bool_param('lock_volume', params) self.volume_api.migrate_volume(context, volume, host, cluster_name, diff --git a/cinder/api/contrib/backups.py b/cinder/api/contrib/backups.py index ca69b657320..80c03bc635b 100644 --- a/cinder/api/contrib/backups.py +++ b/cinder/api/contrib/backups.py @@ -23,6 +23,7 @@ from webob import exc from cinder.api import common from cinder.api import extensions +from cinder.api import microversions as mv from cinder.api.openstack import wsgi from cinder.api.views import backups as backup_views from cinder import backup as backupAPI @@ -151,8 +152,8 @@ class BackupsController(wsgi.Controller): incremental = backup.get('incremental', False) force = backup.get('force', False) snapshot_id = backup.get('snapshot_id', None) - metadata = backup.get( - 'metadata', None) if req_version.matches("3.43") else None + metadata = backup.get('metadata', None) if req_version.matches( + mv.BACKUP_METADATA) else None LOG.info("Creating backup of volume %(volume_id)s in container" " %(container)s", {'volume_id': volume_id, 'container': container}, diff --git a/cinder/api/contrib/resource_common_manage.py b/cinder/api/contrib/resource_common_manage.py index 01396516814..5967d367267 100644 --- a/cinder/api/contrib/resource_common_manage.py +++ b/cinder/api/contrib/resource_common_manage.py @@ -15,6 +15,7 @@ import oslo_messaging as messaging from cinder.api import common +from cinder.api import microversions as mv from cinder import exception from cinder.i18n import _ @@ -23,7 +24,8 @@ def get_manageable_resources(req, is_detail, function_get_manageable, view_builder): context = req.environ['cinder.context'] 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) sort_keys, sort_dirs = common.get_sort_params(params, default_key='reference') diff --git a/cinder/api/contrib/scheduler_stats.py b/cinder/api/contrib/scheduler_stats.py index 875d42ca54e..347f469eef6 100644 --- a/cinder/api/contrib/scheduler_stats.py +++ b/cinder/api/contrib/scheduler_stats.py @@ -16,14 +16,12 @@ from cinder.api import common from cinder.api import extensions +from cinder.api import microversions as mv from cinder.api.openstack import wsgi from cinder.api.views import scheduler_stats as scheduler_stats_view from cinder.scheduler import rpcapi 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): action = 'scheduler_stats:%s' % action_name @@ -42,7 +40,7 @@ class SchedulerStatsController(wsgi.Controller): @common.process_general_filtering('pool') def _process_pool_filtering(self, context=None, filters=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() def get_pools(self, req): @@ -60,7 +58,7 @@ class SchedulerStatsController(wsgi.Controller): filters=filters, 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) pools = self.scheduler_api.get_pools(context, filters=filters) diff --git a/cinder/api/contrib/services.py b/cinder/api/contrib/services.py index 4a78665377c..676a4a8f2a4 100644 --- a/cinder/api/contrib/services.py +++ b/cinder/api/contrib/services.py @@ -22,6 +22,7 @@ import webob.exc from cinder.api import common from cinder.api import extensions +from cinder.api import microversions as mv from cinder.api.openstack import wsgi from cinder.backup import rpcapi as backup_rpcapi from cinder.common import constants @@ -94,8 +95,8 @@ class ServiceController(wsgi.Controller): 'status': active, 'state': art, 'updated_at': updated_at} - # On V3.7 we added cluster support - if req.api_version_request.matches('3.7'): + # On CLUSTER_SUPPORT we added cluster support + if req.api_version_request.matches(mv.CLUSTER_SUPPORT): ret_fields['cluster'] = svc.cluster_name if detailed: @@ -125,20 +126,23 @@ class ServiceController(wsgi.Controller): raise exception.InvalidInput(ex.msg) 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, host, cluster_name) 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, host, cluster_name) def _failover(self, context, req, body, clustered): # 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 - # it if the requested version is 3.26 or higher. - version = '3.26' if clustered else False + # to False when we don't want to get it, and REPLICATION_CLUSTER when + # we only want it if the requested version is REPLICATION_CLUSTER or + # higher. + version = mv.REPLICATION_CLUSTER if clustered else False cluster_name, host = common.get_cluster_host(req, body, version) self._volume_api_proxy(self.volume_api.failover, context, host, cluster_name, body.get('backend_id')) @@ -221,7 +225,7 @@ class ServiceController(wsgi.Controller): context = req.environ['cinder.context'] 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') ret_val = {} @@ -240,7 +244,8 @@ class ServiceController(wsgi.Controller): return self._thaw(context, req, body) elif id == "failover_host": 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) elif support_dynamic_log and id == 'set-log': return self._set_log(context, body) diff --git a/cinder/api/contrib/used_limits.py b/cinder/api/contrib/used_limits.py index 1c638a2826b..445f8082f8b 100644 --- a/cinder/api/contrib/used_limits.py +++ b/cinder/api/contrib/used_limits.py @@ -13,6 +13,7 @@ # under the License. from cinder.api import extensions +from cinder.api import microversions as mv from cinder.api.openstack import wsgi from cinder import quota @@ -32,7 +33,8 @@ class UsedLimitsController(wsgi.Controller): # TODO(wangxiyuan): Support "tenant_id" here to keep the backwards # 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('tenant_id', None) project_id = params.get( diff --git a/cinder/api/contrib/volume_actions.py b/cinder/api/contrib/volume_actions.py index 6b1d0f57c48..c438a5deaed 100644 --- a/cinder/api/contrib/volume_actions.py +++ b/cinder/api/contrib/volume_actions.py @@ -22,7 +22,7 @@ from six.moves import http_client import webob 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 import exception from cinder.i18n import _ @@ -271,7 +271,8 @@ class VolumeActionsController(wsgi.Controller): 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['protected'] = params.get('protected', 'False') @@ -321,7 +322,8 @@ class VolumeActionsController(wsgi.Controller): raise webob.exc.HTTPBadRequest(explanation=msg) 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) else: self.volume_api.extend(context, volume, size) diff --git a/cinder/api/contrib/volume_manage.py b/cinder/api/contrib/volume_manage.py index 99ff0377832..f1089a1c2bb 100644 --- a/cinder/api/contrib/volume_manage.py +++ b/cinder/api/contrib/volume_manage.py @@ -18,6 +18,7 @@ from six.moves import http_client from cinder.api import common from cinder.api.contrib import resource_common_manage from cinder.api import extensions +from cinder.api import microversions as mv from cinder.api.openstack import wsgi from cinder.api.v2.views import volumes as volume_views from cinder.api.views import manageable_volumes as list_manageable_view @@ -110,7 +111,8 @@ class VolumeManageController(wsgi.Controller): if 'ref' not in volume: 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) diff --git a/cinder/api/microversions.py b/cinder/api/microversions.py new file mode 100644 index 00000000000..2f691bb0b48 --- /dev/null +++ b/cinder/api/microversions.py @@ -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) diff --git a/cinder/api/openstack/api_version_request.py b/cinder/api/openstack/api_version_request.py index bcdde392a93..ec0d4967f1e 100644 --- a/cinder/api/openstack/api_version_request.py +++ b/cinder/api/openstack/api_version_request.py @@ -116,6 +116,7 @@ REST_API_VERSION_HISTORY = """ _MIN_API_VERSION = "3.0" _MAX_API_VERSION = "3.44" _LEGACY_API_VERSION2 = "2.0" +UPDATED = "2017-09-19T20:18:14Z" # NOTE(cyeoh): min and max versions declared as functions so we can diff --git a/cinder/api/v2/volumes.py b/cinder/api/v2/volumes.py index 47fc2bfd876..f84a363866e 100644 --- a/cinder/api/v2/volumes.py +++ b/cinder/api/v2/volumes.py @@ -95,7 +95,7 @@ class VolumeController(wsgi.Controller): filters = params # 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) utils.remove_invalid_filter_options(context, filters, diff --git a/cinder/api/v3/attachments.py b/cinder/api/v3/attachments.py index 36d6678898d..c0aacb7b99f 100644 --- a/cinder/api/v3/attachments.py +++ b/cinder/api/v3/attachments.py @@ -16,6 +16,7 @@ from oslo_log import log as logging import webob from cinder.api import common +from cinder.api import microversions as mv from cinder.api.openstack import wsgi from cinder.api.v3.views import attachments as attachment_views from cinder import exception @@ -26,8 +27,6 @@ from cinder.volume import api as volume_api LOG = logging.getLogger(__name__) -API_VERSION = '3.27' -ATTACHMENT_COMPLETION_VERSION = '3.44' class AttachmentsController(wsgi.Controller): @@ -43,20 +42,20 @@ class AttachmentsController(wsgi.Controller): self.ext_mgr = ext_mgr super(AttachmentsController, self).__init__() - @wsgi.Controller.api_version(API_VERSION) + @wsgi.Controller.api_version(mv.NEW_ATTACH) def show(self, req, id): """Return data about the given attachment.""" context = req.environ['cinder.context'] attachment = objects.VolumeAttachment.get_by_id(context, id) return attachment_views.ViewBuilder.detail(attachment) - @wsgi.Controller.api_version(API_VERSION) + @wsgi.Controller.api_version(mv.NEW_ATTACH) def index(self, req): """Return a summary list of attachments.""" attachments = self._items(req) return attachment_views.ViewBuilder.list(attachments) - @wsgi.Controller.api_version(API_VERSION) + @wsgi.Controller.api_version(mv.NEW_ATTACH) def detail(self, req): """Return a detailed list of attachments.""" attachments = self._items(req) @@ -94,7 +93,7 @@ class AttachmentsController(wsgi.Controller): marker=marker, limit=limit, offset=offset, sort_keys=sort_keys, sort_direction=sort_dirs) - @wsgi.Controller.api_version(API_VERSION) + @wsgi.Controller.api_version(mv.NEW_ATTACH) @wsgi.response(202) def create(self, req, body): """Create an attachment. @@ -192,7 +191,7 @@ class AttachmentsController(wsgi.Controller): raise webob.exc.HTTPInternalServerError(explanation=err_msg) 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): """Update an attachment record. @@ -252,7 +251,7 @@ class AttachmentsController(wsgi.Controller): # or a dict? 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): """Delete an attachment. @@ -268,7 +267,7 @@ class AttachmentsController(wsgi.Controller): return attachment_views.ViewBuilder.list(attachments) @wsgi.response(202) - @wsgi.Controller.api_version(ATTACHMENT_COMPLETION_VERSION) + @wsgi.Controller.api_version(mv.NEW_ATTACH_COMPLETION) @wsgi.action('os-complete') def complete(self, req, id, body): """Mark a volume attachment process as completed (in-use).""" diff --git a/cinder/api/v3/backups.py b/cinder/api/v3/backups.py index d08e265eabf..a98a8a2e945 100644 --- a/cinder/api/v3/backups.py +++ b/cinder/api/v3/backups.py @@ -19,22 +19,20 @@ from oslo_log import log as logging from webob import exc from cinder.api.contrib import backups as backups_v2 +from cinder.api import microversions as mv from cinder.api.openstack import wsgi from cinder.backup import api as backup_api from cinder import exception from cinder.i18n import _ -BACKUP_UPDATE_MICRO_VERSION = '3.9' -BACKUP_TENANT_MICRO_VERSION = '3.18' - LOG = logging.getLogger(__name__) class BackupsController(backups_v2.BackupsController): """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): """Update a backup.""" context = req.environ['cinder.context'] @@ -50,7 +48,8 @@ class BackupsController(backups_v2.BackupsController): if 'description' in backup_update: update_dict['display_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') # Check no unsupported fields. if backup_update: @@ -77,7 +76,7 @@ class BackupsController(backups_v2.BackupsController): req.cache_db_backup(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: backup_api.check_policy(context, 'backup_project_attribute') self._add_backup_project_attribute(req, resp_backup['backup']) @@ -90,7 +89,7 @@ class BackupsController(backups_v2.BackupsController): context = req.environ['cinder.context'] req_version = req.api_version_request - if req_version.matches(BACKUP_TENANT_MICRO_VERSION): + if req_version.matches(mv.BACKUP_PROJECT): try: backup_api.check_policy(context, 'backup_project_attribute') for bak in resp_backup['backups']: @@ -100,7 +99,7 @@ class BackupsController(backups_v2.BackupsController): return resp_backup 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' diff --git a/cinder/api/v3/clusters.py b/cinder/api/v3/clusters.py index 120f1e6a106..53e43ac8104 100644 --- a/cinder/api/v3/clusters.py +++ b/cinder/api/v3/clusters.py @@ -13,6 +13,7 @@ # License for the specific language governing permissions and limitations # under the License. +from cinder.api import microversions as mv from cinder.api.openstack import wsgi from cinder.api.v3.views import clusters as clusters_view from cinder import exception @@ -21,10 +22,6 @@ from cinder import objects from cinder import utils -CLUSTER_MICRO_VERSION = '3.7' -REPLICATION_DATA_MICRO_VERSION = '3.26' - - class ClusterController(wsgi.Controller): allowed_list_keys = {'name', 'binary', 'is_up', 'disabled', 'num_hosts', 'num_down_hosts', 'binary', 'replication_status', @@ -33,7 +30,7 @@ class ClusterController(wsgi.Controller): 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'): """Return data for a given cluster name with optional binary.""" # 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, name=id, services_summary=True) replication_data = req.api_version_request.matches( - REPLICATION_DATA_MICRO_VERSION) + mv.REPLICATION_CLUSTER) 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): """Return a non detailed list of all existing clusters. @@ -53,7 +50,7 @@ class ClusterController(wsgi.Controller): """ 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): """Return a detailed list of all existing clusters. @@ -65,7 +62,7 @@ class ClusterController(wsgi.Controller): # Let the wsgi middleware convert NotAuthorized exceptions context = self.policy_checker(req, 'get_all') replication_data = req.api_version_request.matches( - REPLICATION_DATA_MICRO_VERSION) + mv.REPLICATION_CLUSTER) filters = dict(req.GET) allowed = self.allowed_list_keys if not replication_data: @@ -89,7 +86,7 @@ class ClusterController(wsgi.Controller): return clusters_view.ViewBuilder.list(clusters, detail, replication_data) - @wsgi.Controller.api_version(CLUSTER_MICRO_VERSION) + @wsgi.Controller.api_version(mv.CLUSTER_SUPPORT) def update(self, req, id, body): """Enable/Disable scheduling for a cluster.""" # 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 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['cluster']['disabled_reason'] = disabled_reason diff --git a/cinder/api/v3/consistencygroups.py b/cinder/api/v3/consistencygroups.py index a7e542293db..6ff3efba88c 100644 --- a/cinder/api/v3/consistencygroups.py +++ b/cinder/api/v3/consistencygroups.py @@ -19,6 +19,7 @@ import webob from webob import exc from cinder.api.contrib import consistencygroups as cg_v2 +from cinder.api import microversions as mv from cinder.api.openstack import wsgi from cinder.i18n import _ @@ -30,7 +31,8 @@ class ConsistencyGroupsController(cg_v2.ConsistencyGroupsController): def _check_update_parameters_v3(self, req, name, description, add_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 (name is None and description is None and not add_volumes and not remove_volumes): diff --git a/cinder/api/v3/group_snapshots.py b/cinder/api/v3/group_snapshots.py index f1b18763a06..300ea36e609 100644 --- a/cinder/api/v3/group_snapshots.py +++ b/cinder/api/v3/group_snapshots.py @@ -22,6 +22,7 @@ import webob from webob import exc from cinder.api import common +from cinder.api import microversions as mv from cinder.api.openstack import wsgi from cinder.api.v3.views import group_snapshots as group_snapshot_views from cinder import exception @@ -32,8 +33,6 @@ from cinder.volume import group_types LOG = logging.getLogger(__name__) -GROUP_SNAPSHOT_API_VERSION = '3.14' - class GroupSnapshotsController(wsgi.Controller): """The group_snapshots API controller for the OpenStack API.""" @@ -52,7 +51,7 @@ class GroupSnapshotsController(wsgi.Controller): % {'group_type': group_type_id}) 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): """Return data about the given group_snapshot.""" LOG.debug('show called for member %s', id) @@ -66,7 +65,7 @@ class GroupSnapshotsController(wsgi.Controller): 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): """Delete a group_snapshot.""" LOG.debug('delete called for member %s', id) @@ -93,12 +92,12 @@ class GroupSnapshotsController(wsgi.Controller): 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): """Returns a summary list of group_snapshots.""" 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): """Returns a detailed list of group_snapshots.""" return self._get_group_snapshots(req, is_detail=True) @@ -109,14 +108,14 @@ class GroupSnapshotsController(wsgi.Controller): context = req.environ['cinder.context'] req_version = req.api_version_request 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() marker, limit, offset = common.get_pagination_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( - common.LIKE_FILTER_VERSION) else False) + mv.LIKE_FILTER) else False) common.reject_invalid_filters(context, filters, 'group_snapshot', support_like) @@ -145,7 +144,7 @@ class GroupSnapshotsController(wsgi.Controller): group_snapshots['group_snapshots'] = new_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) def create(self, req, body): """Create a new group_snapshot.""" @@ -183,7 +182,7 @@ class GroupSnapshotsController(wsgi.Controller): return retval - @wsgi.Controller.api_version('3.19') + @wsgi.Controller.api_version(mv.GROUP_SNAPSHOT_RESET_STATUS) @wsgi.action("reset_status") def reset_status(self, req, id, body): return self._reset_status(req, id, body) diff --git a/cinder/api/v3/group_specs.py b/cinder/api/v3/group_specs.py index ee89cfe0815..e851839048b 100644 --- a/cinder/api/v3/group_specs.py +++ b/cinder/api/v3/group_specs.py @@ -18,6 +18,7 @@ from six.moves import http_client import webob from cinder.api import common +from cinder.api import microversions as mv from cinder.api.openstack import wsgi from cinder import db from cinder import exception @@ -51,7 +52,7 @@ class GroupTypeSpecsController(wsgi.Controller): except exception.GroupTypeNotFound as ex: 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): """Returns the list of group specs for a given group type.""" context = req.environ['cinder.context'] @@ -59,7 +60,7 @@ class GroupTypeSpecsController(wsgi.Controller): self._check_type(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) def create(self, req, group_type_id, body=None): context = req.environ['cinder.context'] @@ -80,7 +81,7 @@ class GroupTypeSpecsController(wsgi.Controller): notifier_info) 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): context = req.environ['cinder.context'] self._check_policy(context) @@ -108,7 +109,7 @@ class GroupTypeSpecsController(wsgi.Controller): notifier_info) return body - @wsgi.Controller.api_version('3.11') + @wsgi.Controller.api_version(mv.GROUP_TYPE) def show(self, req, group_type_id, id): """Return a single extra spec item.""" context = req.environ['cinder.context'] @@ -123,7 +124,7 @@ class GroupTypeSpecsController(wsgi.Controller): "%(id)s.") % ({'type_id': group_type_id, 'id': id}) 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): """Deletes an existing group spec.""" context = req.environ['cinder.context'] diff --git a/cinder/api/v3/group_types.py b/cinder/api/v3/group_types.py index 1163f5197f9..c7fbad10216 100644 --- a/cinder/api/v3/group_types.py +++ b/cinder/api/v3/group_types.py @@ -21,6 +21,7 @@ import webob from webob import exc from cinder.api import common +from cinder.api import microversions as mv from cinder.api.openstack import wsgi from cinder.api.v3.views import group_types as views_types from cinder import exception @@ -55,7 +56,7 @@ class GroupTypesController(wsgi.Controller): payload = dict(group_types=group_type) 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) def create(self, req, body): """Creates a new group type.""" @@ -103,7 +104,7 @@ class GroupTypesController(wsgi.Controller): 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): # Update description for a given group type. context = req.environ['cinder.context'] @@ -163,7 +164,7 @@ class GroupTypesController(wsgi.Controller): 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): """Deletes an existing group type.""" context = req.environ['cinder.context'] @@ -186,14 +187,14 @@ class GroupTypesController(wsgi.Controller): 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): """Returns the list of group types.""" limited_types = self._get_group_types(req) req.cache_resource(limited_types, name='group_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): """Return a single group type item.""" context = req.environ['cinder.context'] diff --git a/cinder/api/v3/groups.py b/cinder/api/v3/groups.py index b0e029b0f51..90c9524f263 100644 --- a/cinder/api/v3/groups.py +++ b/cinder/api/v3/groups.py @@ -22,6 +22,7 @@ import webob from webob import exc from cinder.api import common +from cinder.api import microversions as mv from cinder.api.openstack import wsgi from cinder.api.v3.views import groups as views_groups from cinder import exception @@ -32,10 +33,6 @@ from cinder.volume import group_types 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): """The groups API controller for the OpenStack API.""" @@ -53,7 +50,7 @@ class GroupsController(wsgi.Controller): "CG APIs.") % {'group_type': group_type_id} raise exc.HTTPBadRequest(explanation=msg) - @wsgi.Controller.api_version(GROUP_API_VERSION) + @wsgi.Controller.api_version(mv.GROUP_VOLUME) def show(self, req, id): """Return data about the given group.""" LOG.debug('show called for member %s', id) @@ -68,7 +65,7 @@ class GroupsController(wsgi.Controller): 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") def reset_status(self, req, id, body): return self._reset_status(req, id, body) @@ -109,7 +106,7 @@ class GroupsController(wsgi.Controller): raise exc.HTTPBadRequest(explanation=error.msg) 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") def delete_group(self, 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) - @wsgi.Controller.api_version(GROUP_API_VERSION) + @wsgi.Controller.api_version(mv.GROUP_VOLUME) def index(self, req): """Returns a summary list of groups.""" 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): """Returns a detailed list of groups.""" 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) 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( - common.LIKE_FILTER_VERSION) else False) + mv.LIKE_FILTER) else False) common.reject_invalid_filters(context, filters, 'group', support_like) @@ -197,7 +194,7 @@ class GroupsController(wsgi.Controller): req, new_groups) return groups - @wsgi.Controller.api_version(GROUP_API_VERSION) + @wsgi.Controller.api_version(mv.GROUP_VOLUME) @wsgi.response(http_client.ACCEPTED) def create(self, req, body): """Create a new group.""" @@ -243,7 +240,7 @@ class GroupsController(wsgi.Controller): retval = self._view_builder.summary(req, new_group) 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.response(http_client.ACCEPTED) def create_from_src(self, req, body): @@ -308,7 +305,7 @@ class GroupsController(wsgi.Controller): retval = self._view_builder.summary(req, new_group) return retval - @wsgi.Controller.api_version(GROUP_API_VERSION) + @wsgi.Controller.api_version(mv.GROUP_VOLUME) def update(self, req, id, body): """Update the group. @@ -373,7 +370,7 @@ class GroupsController(wsgi.Controller): 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") def enable_replication(self, req, id, body): """Enables replications for a group.""" @@ -397,7 +394,7 @@ class GroupsController(wsgi.Controller): 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") def disable_replication(self, req, id, body): """Disables replications for a group.""" @@ -421,7 +418,7 @@ class GroupsController(wsgi.Controller): 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") def failover_replication(self, req, id, body): """Fails over replications for a group.""" @@ -457,7 +454,7 @@ class GroupsController(wsgi.Controller): 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") def list_replication_targets(self, req, id, body): """List replication targets for a group.""" diff --git a/cinder/api/v3/limits.py b/cinder/api/v3/limits.py index cc739a3df32..2e7b6988bd9 100644 --- a/cinder/api/v3/limits.py +++ b/cinder/api/v3/limits.py @@ -13,6 +13,7 @@ """The limits V3 api.""" +from cinder.api import microversions as mv from cinder.api.openstack import wsgi from cinder.api.v2 import limits as limits_v2 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 # 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('tenant_id', None) project_id = params.get( diff --git a/cinder/api/v3/messages.py b/cinder/api/v3/messages.py index ee1a789bbb4..c03e398b7f8 100644 --- a/cinder/api/v3/messages.py +++ b/cinder/api/v3/messages.py @@ -17,6 +17,7 @@ from six.moves import http_client import webob from cinder.api import common +from cinder.api import microversions as mv from cinder.api.openstack import wsgi from cinder.api.v3.views import messages as messages_view from cinder.message import api as message_api @@ -25,9 +26,6 @@ from cinder.message import message_field import cinder.policy -MESSAGES_BASE_MICRO_VERSION = '3.3' - - def check_policy(context, action, target_obj=None): target = { 'project_id': context.project_id, @@ -62,7 +60,7 @@ class MessagesController(wsgi.Controller): message_field.translate_action(message['action_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): """Return the given message.""" context = req.environ['cinder.context'] @@ -75,7 +73,7 @@ class MessagesController(wsgi.Controller): self._build_user_message(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): """Delete a message.""" context = req.environ['cinder.context'] @@ -87,7 +85,7 @@ class MessagesController(wsgi.Controller): 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): """Returns a list of messages, transformed through view builder.""" context = req.environ['cinder.context'] @@ -100,14 +98,14 @@ class MessagesController(wsgi.Controller): sort_keys = None sort_dirs = None - if api_version.matches("3.5"): + if api_version.matches(mv.MESSAGES_PAGINATION): filters = req.params.copy() marker, limit, offset = common.get_pagination_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( - common.LIKE_FILTER_VERSION) else False) + mv.LIKE_FILTER) else False) common.reject_invalid_filters(context, filters, 'message', support_like) diff --git a/cinder/api/v3/resource_common_manage.py b/cinder/api/v3/resource_common_manage.py index 4265fca3df7..2d57b87a389 100644 --- a/cinder/api/v3/resource_common_manage.py +++ b/cinder/api/v3/resource_common_manage.py @@ -15,6 +15,7 @@ from cinder.api import common from cinder.api import extensions +from cinder.api import microversions as mv from cinder.api.openstack import wsgi from cinder import exception from cinder.i18n import _ @@ -41,13 +42,14 @@ class ManageResource(object): raise exception.VersionNotFoundForAPIMethod(version=version) 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'] self._authorizer(context) 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) sort_keys, sort_dirs = common.get_sort_params(params, default_key='reference') diff --git a/cinder/api/v3/resource_filters.py b/cinder/api/v3/resource_filters.py index 955ae208980..e4cb32db672 100644 --- a/cinder/api/v3/resource_filters.py +++ b/cinder/api/v3/resource_filters.py @@ -13,13 +13,11 @@ """The resource filters api.""" from cinder.api import common +from cinder.api import microversions as mv from cinder.api.openstack import wsgi from cinder.api.v3.views import resource_filters as filter_views -FILTER_API_VERSION = '3.33' - - class ResourceFiltersController(wsgi.Controller): """The resource filter API controller for the OpenStack API.""" @@ -30,7 +28,7 @@ class ResourceFiltersController(wsgi.Controller): self.ext_mgr = ext_mgr super(ResourceFiltersController, self).__init__() - @wsgi.Controller.api_version(FILTER_API_VERSION) + @wsgi.Controller.api_version(mv.RESOURCE_FILTER_CONFIG) def index(self, req): """Return a list of resource filters.""" resource = req.params.get('resource', None) diff --git a/cinder/api/v3/snapshot_manage.py b/cinder/api/v3/snapshot_manage.py index ae9a54d60bf..baeef54bd2a 100644 --- a/cinder/api/v3/snapshot_manage.py +++ b/cinder/api/v3/snapshot_manage.py @@ -15,6 +15,7 @@ from six.moves import http_client 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.v3 import resource_common_manage as common @@ -27,7 +28,7 @@ class SnapshotManageController(common.ManageResource, @wsgi.response(http_client.ACCEPTED) 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) diff --git a/cinder/api/v3/snapshots.py b/cinder/api/v3/snapshots.py index 025e74fe09c..00c55a958df 100644 --- a/cinder/api/v3/snapshots.py +++ b/cinder/api/v3/snapshots.py @@ -20,6 +20,7 @@ import ast from oslo_log import log as logging from cinder.api import common +from cinder.api import microversions as mv from cinder.api.openstack import wsgi from cinder.api.v2 import snapshots as snapshots_v2 from cinder.api.v3.views import snapshots as snapshot_views @@ -56,9 +57,10 @@ class SnapshotsController(snapshots_v2.SnapshotsController): req_version=None): """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 - 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) # Filter out invalid options @@ -84,7 +86,7 @@ class SnapshotsController(snapshots_v2.SnapshotsController): self._format_snapshot_filter_options(search_opts) 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' # NOTE(thingee): v3 API allows name instead of display_name diff --git a/cinder/api/v3/views/groups.py b/cinder/api/v3/views/groups.py index 5b20c2928ed..90461f3d507 100644 --- a/cinder/api/v3/views/groups.py +++ b/cinder/api/v3/views/groups.py @@ -14,6 +14,7 @@ # under the License. from cinder.api import common +from cinder.api import microversions as mv from cinder import utils @@ -60,20 +61,21 @@ class ViewBuilder(common.ViewBuilder): req_version = request.api_version_request # Add group_snapshot_id and source_group_id if min version is greater - # than or equal to 3.14. - if req_version.matches("3.14", None): + # than or equal to GROUP_SNAPSHOTS. + if req_version.matches(mv.GROUP_SNAPSHOTS, None): group_ref['group']['group_snapshot_id'] = group.group_snapshot_id group_ref['group']['source_group_id'] = group.source_group_id - # Add volumes if min version is greater than or equal to 3.25. - if req_version.matches("3.25", None): + # Add volumes if min version is greater than or equal to + # GROUP_VOLUME_LIST. + if req_version.matches(mv.GROUP_VOLUME_LIST, None): if utils.get_bool_param('list_volume', request.params): group_ref['group']['volumes'] = [volume.id for volume in group.volumes] # Add replication_status if min version is greater than or equal - # to 3.38. - if req_version.matches("3.38", None): + # to GROUP_REPLICATION. + if req_version.matches(mv.GROUP_REPLICATION, None): group_ref['group']['replication_status'] = group.replication_status return group_ref diff --git a/cinder/api/v3/views/snapshots.py b/cinder/api/v3/views/snapshots.py index c5bd87005a5..857df059ca9 100644 --- a/cinder/api/v3/views/snapshots.py +++ b/cinder/api/v3/views/snapshots.py @@ -13,6 +13,7 @@ # License for the specific language governing permissions and limitations # under the License. +from cinder.api import microversions as mv from cinder.api.views import snapshots as views_v2 @@ -25,10 +26,10 @@ class ViewBuilder(views_v2.ViewBuilder): req_version = request.api_version_request # Add group_snapshot_id if min version is greater than or equal - # to 3.14. - if req_version.matches("3.14", None): + # to GROUP_SNAPSHOTS. + if req_version.matches(mv.GROUP_SNAPSHOTS, None): snapshot_ref['snapshot']['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') return snapshot_ref diff --git a/cinder/api/v3/views/volumes.py b/cinder/api/v3/views/volumes.py index eb9b4058702..038d4e0f750 100644 --- a/cinder/api/v3/views/volumes.py +++ b/cinder/api/v3/views/volumes.py @@ -13,6 +13,7 @@ # License for the specific language governing permissions and limitations # under the License. +from cinder.api import microversions as mv 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) req_version = request.api_version_request - # Add group_id if min version is greater than or equal to 3.13. - if req_version.matches("3.13", None): + # Add group_id if min version is greater than or equal to GROUP_VOLUME. + if req_version.matches(mv.GROUP_VOLUME, None): volume_ref['volume']['group_id'] = volume.get('group_id') - # Add provider_id if min version is greater than or equal to 3.21 - # for admin. + # Add provider_id if min version is greater than or equal to + # VOLUME_DETAIL_PROVIDER_ID for admin. 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') return volume_ref diff --git a/cinder/api/v3/volume_manage.py b/cinder/api/v3/volume_manage.py index 4b2c5677e91..a09ddd5dd34 100644 --- a/cinder/api/v3/volume_manage.py +++ b/cinder/api/v3/volume_manage.py @@ -15,6 +15,7 @@ from six.moves import http_client 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.v3 import resource_common_manage as common @@ -27,7 +28,7 @@ class VolumeManageController(common.ManageResource, @wsgi.response(http_client.ACCEPTED) 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) diff --git a/cinder/api/v3/volume_metadata.py b/cinder/api/v3/volume_metadata.py index 91f8d7d6f7a..27c2443d428 100644 --- a/cinder/api/v3/volume_metadata.py +++ b/cinder/api/v3/volume_metadata.py @@ -22,13 +22,11 @@ import six from six.moves import http_client import webob +from cinder.api import microversions as mv from cinder.api.openstack import wsgi from cinder.api.v2 import volume_metadata as volume_meta_v2 -METADATA_MICRO_VERSION = '3.15' - - class Controller(volume_meta_v2.Controller): """The volume metadata API controller for the OpenStack API.""" def _validate_etag(self, req, volume_id): @@ -46,7 +44,7 @@ class Controller(volume_meta_v2.Controller): def index(self, req, volume_id): req_version = req.api_version_request 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) if six.PY3: data = data.encode('utf-8') @@ -59,7 +57,7 @@ class Controller(volume_meta_v2.Controller): @wsgi.extends def update(self, req, volume_id, id, body): 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): return webob.Response( status_int=http_client.PRECONDITION_FAILED) @@ -69,7 +67,7 @@ class Controller(volume_meta_v2.Controller): @wsgi.extends def update_all(self, req, volume_id, body): 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): return webob.Response( status_int=http_client.PRECONDITION_FAILED) diff --git a/cinder/api/v3/volumes.py b/cinder/api/v3/volumes.py index 2674cf9f813..8f41694a5da 100644 --- a/cinder/api/v3/volumes.py +++ b/cinder/api/v3/volumes.py @@ -21,6 +21,7 @@ import webob from webob import exc from cinder.api import common +from cinder.api import microversions as mv from cinder.api.openstack import wsgi from cinder.api.v2 import volumes as volumes_v2 from cinder.api.v3.views import volumes as volume_views_v3 @@ -33,8 +34,6 @@ from cinder import utils LOG = logging.getLogger(__name__) -SUMMARY_BASE_MICRO_VERSION = '3.12' - def check_policy(context, action, target_obj=None): target = { @@ -65,7 +64,7 @@ class VolumeController(volumes_v2.VolumeController): force = False params = "" - if req_version.matches('3.23'): + if req_version.matches(mv.VOLUME_LIST_BOOTABLE): force = utils.get_bool_param('force', req.params) if cascade or force: params = "(cascade: %(c)s, force: %(f)s)" % {'c': cascade, @@ -88,10 +87,10 @@ class VolumeController(volumes_v2.VolumeController): @common.process_general_filtering('volume') def _process_volume_filtering(self, context=None, filters=None, req_version=None): - if req_version.matches(None, "3.3"): + if req_version.matches(None, mv.MESSAGES): 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) utils.remove_invalid_filter_options( @@ -119,7 +118,8 @@ class VolumeController(volumes_v2.VolumeController): if 'name' in filters: 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) 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) return volumes - @wsgi.Controller.api_version(SUMMARY_BASE_MICRO_VERSION) + @wsgi.Controller.api_version(mv.VOLUME_SUMMARY) def summary(self, req): """Return summary of volumes.""" view_builder_v3 = volume_views_v3.ViewBuilder() @@ -154,7 +154,7 @@ class VolumeController(volumes_v2.VolumeController): context, filters=filters) req_version = req.api_version_request - if req_version.matches("3.36"): + if req_version.matches(mv.VOLUME_SUMMARY_METADATA): all_distinct_metadata = metadata else: all_distinct_metadata = None @@ -163,7 +163,7 @@ class VolumeController(volumes_v2.VolumeController): all_distinct_metadata) @wsgi.response(http_client.ACCEPTED) - @wsgi.Controller.api_version('3.40') + @wsgi.Controller.api_version(mv.VOLUME_REVERT) @wsgi.action('revert') def revert(self, req, id, body): """revert a volume to a snapshot""" @@ -208,8 +208,8 @@ class VolumeController(volumes_v2.VolumeController): context = req.environ['cinder.context'] req_version = req.api_version_request - # Remove group_id from body if max version is less than 3.13. - if req_version.matches(None, "3.12"): + # Remove group_id from body if max version is less than GROUP_VOLUME. + if req_version.matches(None, mv.get_prior_version(mv.GROUP_VOLUME)): # 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 # for this volume. Also if group_id is passed in, that means diff --git a/cinder/api/v3/workers.py b/cinder/api/v3/workers.py index f38a5baa9cc..fa0a8540d46 100644 --- a/cinder/api/v3/workers.py +++ b/cinder/api/v3/workers.py @@ -16,6 +16,7 @@ from oslo_utils import timeutils from oslo_utils import uuidutils +from cinder.api import microversions as mv from cinder.api.openstack import wsgi from cinder.api.v3.views import workers as workers_view from cinder import db @@ -98,7 +99,7 @@ class WorkerController(wsgi.Controller): return params - @wsgi.Controller.api_version('3.24') + @wsgi.Controller.api_version(mv.WORKERS_CLEANUP) @wsgi.response(202) def cleanup(self, req, body=None): """Do the cleanup on resources from a specific service/host/node.""" diff --git a/cinder/api/versions.py b/cinder/api/versions.py index edaa5954883..4d51eccb8ea 100644 --- a/cinder/api/versions.py +++ b/cinder/api/versions.py @@ -51,7 +51,7 @@ _KNOWN_VERSIONS = { "status": "CURRENT", "version": api_version_request._MAX_API_VERSION, "min_version": api_version_request._MIN_API_VERSION, - "updated": "2016-02-08T12:20:21Z", + "updated": api_version_request.UPDATED, "links": _LINKS, "media-types": [{ "base": "application/json", diff --git a/cinder/api/views/backups.py b/cinder/api/views/backups.py index fee52bf225f..a4fec2abf55 100644 --- a/cinder/api/views/backups.py +++ b/cinder/api/views/backups.py @@ -14,6 +14,7 @@ # under the License. from cinder.api import common +from cinder.api import microversions as mv class ViewBuilder(common.ViewBuilder): @@ -78,7 +79,7 @@ class ViewBuilder(common.ViewBuilder): } } 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 return backup_dict diff --git a/cinder/tests/tempest/api/volume/test_volume_revert.py b/cinder/tests/tempest/api/volume/test_volume_revert.py index b5151c59da8..bb7cde61644 100644 --- a/cinder/tests/tempest/api/volume/test_volume_revert.py +++ b/cinder/tests/tempest/api/volume/test_volume_revert.py @@ -24,7 +24,6 @@ CONF = config.CONF class VolumeRevertTests(volume_base.BaseVolumeTest): - min_microversion = '3.40' @classmethod def setup_clients(cls): diff --git a/cinder/tests/unit/api/contrib/test_admin_actions.py b/cinder/tests/unit/api/contrib/test_admin_actions.py index 4af9c794af8..9363a5cd420 100644 --- a/cinder/tests/unit/api/contrib/test_admin_actions.py +++ b/cinder/tests/unit/api/contrib/test_admin_actions.py @@ -23,7 +23,7 @@ import webob from webob import exc 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 rpcapi as backup_rpcapi from cinder.common import constants @@ -525,18 +525,16 @@ class AdminActionsTest(BaseAdminTest): force_host_copy=False, version=None, cluster=None): # 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' % ( fake.PROJECT_ID, volume['id'])) req.method = 'POST' req.headers['content-type'] = 'application/json' body = {'os-migrate_volume': {'host': host, 'force_host_copy': force_host_copy}} - version = version or '3.0' - req.headers = {'OpenStack-API-Version': 'volume %s' % version} - req.api_version_request = api_version.APIVersionRequest(version) - if version == '3.16': + version = version or mv.BASE_VERSION + req.headers = mv.get_mv_header(version) + req.api_version_request = mv.get_api_version(version) + if version == mv.VOLUME_MIGRATE_CLUSTER: body['os-migrate_volume']['cluster'] = cluster req.body = jsonutils.dump_as_bytes(body) req.environ['cinder.context'] = ctx @@ -547,7 +545,9 @@ class AdminActionsTest(BaseAdminTest): volume = db.volume_get(self.ctx, volume['id']) 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): expected_status = http_client.ACCEPTED host = 'test2' @@ -563,7 +563,8 @@ class AdminActionsTest(BaseAdminTest): cluster = 'cluster' volume = self._migrate_volume_prep() volume = self._migrate_volume_3_exec(self.ctx, volume, host, - expected_status, version='3.16', + expected_status, + version=mv.VOLUME_MIGRATE_CLUSTER, cluster=cluster) self.assertEqual('starting', volume['migration_status']) @@ -574,7 +575,8 @@ class AdminActionsTest(BaseAdminTest): volume = self._migrate_volume_prep() self.assertRaises(exception.InvalidInput, 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, force_host_copy=False): diff --git a/cinder/tests/unit/api/contrib/test_backup_project_attribute.py b/cinder/tests/unit/api/contrib/test_backup_project_attribute.py index d467ca0cbab..7e4c46830f6 100644 --- a/cinder/tests/unit/api/contrib/test_backup_project_attribute.py +++ b/cinder/tests/unit/api/contrib/test_backup_project_attribute.py @@ -17,7 +17,7 @@ import ddt from oslo_serialization import jsonutils 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.backup import api as backup_api 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_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 if detail: req = webob.Request.blank(('/v3/%s/backups/detail' @@ -67,8 +68,8 @@ class BackupProjectAttributeTest(test.TestCase): fake.BACKUP_ID)) req.method = 'GET' req.environ['cinder.context'] = ctx - req.headers['OpenStack-API-Version'] = 'volume ' + version - req.api_version_request = api_version.APIVersionRequest(version) + req.headers = mv.get_mv_header(version) + req.api_version_request = mv.get_api_version(version) res = req.get_response(app()) if detail: @@ -97,5 +98,6 @@ class BackupProjectAttributeTest(test.TestCase): def test_get_backup_under_allowed_api_version(self): 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) diff --git a/cinder/tests/unit/api/contrib/test_backups.py b/cinder/tests/unit/api/contrib/test_backups.py index 847c78df0fc..69bd7d3f638 100644 --- a/cinder/tests/unit/api/contrib/test_backups.py +++ b/cinder/tests/unit/api/contrib/test_backups.py @@ -25,6 +25,7 @@ from six.moves import http_client import webob from cinder.api.contrib import backups +from cinder.api import microversions as mv from cinder.api.openstack import api_version_request as api_version # needed for stubs to work import cinder.backup @@ -117,8 +118,8 @@ class BackupsAPITestCase(test.TestCase): req = webob.Request.blank('/v3/%s/backups/%s' % ( fake.PROJECT_ID, backup.id)) req.method = 'GET' + req.headers = mv.get_mv_header(mv.BACKUP_METADATA) req.headers['Content-Type'] = 'application/json' - req.headers['OpenStack-API-Version'] = 'volume 3.43' res = req.get_response(fakes.wsgi_app( fake_auth_context=self.user_context)) res_dict = jsonutils.loads(res.body) @@ -131,6 +132,7 @@ class BackupsAPITestCase(test.TestCase): req = webob.Request.blank('/v2/%s/backups/%s' % ( fake.PROJECT_ID, fake.WILL_NOT_BE_FOUND_ID)) req.method = 'GET' + req.headers = mv.get_mv_header(mv.BACKUP_METADATA) req.headers['Content-Type'] = 'application/json' res = req.get_response(fakes.wsgi_app( 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.method = 'GET' + req.headers = mv.get_mv_header(mv.BACKUP_METADATA) req.headers['Content-Type'] = 'application/json' req.headers['Accept'] = 'application/json' - req.headers['OpenStack-API-Version'] = 'volume 3.43' res = req.get_response(fakes.wsgi_app( fake_auth_context=self.user_context)) 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.method = 'POST' + req.headers = mv.get_mv_header(mv.BACKUP_METADATA) req.headers['Content-Type'] = 'application/json' - req.headers['OpenStack-API-Version'] = 'volume 3.43' req.body = jsonutils.dump_as_bytes(body) res = req.get_response(fakes.wsgi_app( fake_auth_context=self.user_context)) @@ -546,8 +548,8 @@ class BackupsAPITestCase(test.TestCase): req = webob.Request.blank('/v3/%s/backups/%s' % ( fake.PROJECT_ID, res_dict['backup']['id'])) req.method = 'GET' + req.headers = mv.get_mv_header(mv.BACKUP_METADATA) req.headers['Content-Type'] = 'application/json' - req.headers['OpenStack-API-Version'] = 'volume 3.43' res = req.get_response(fakes.wsgi_app( fake_auth_context=self.user_context)) res_dict = jsonutils.loads(res.body) diff --git a/cinder/tests/unit/api/contrib/test_scheduler_stats.py b/cinder/tests/unit/api/contrib/test_scheduler_stats.py index 63ac4a673c3..d0262eb2668 100644 --- a/cinder/tests/unit/api/contrib/test_scheduler_stats.py +++ b/cinder/tests/unit/api/contrib/test_scheduler_stats.py @@ -19,6 +19,7 @@ import mock import webob 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 import context from cinder import exception @@ -84,7 +85,7 @@ class SchedulerStatsAPITest(test.TestCase): fake.PROJECT_ID) mock_rpcapi.return_value = [dict(name='pool1', 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 res = self.controller.get_pools(req) @@ -106,7 +107,7 @@ class SchedulerStatsAPITest(test.TestCase): '&foo=bar' % fake.PROJECT_ID) mock_rpcapi.return_value = [dict(name='pool1', 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 res = self.controller.get_pools(req) @@ -175,8 +176,8 @@ class SchedulerStatsAPITest(test.TestCase): self.controller.get_pools, req) - @ddt.data(('3.34', False), - ('3.35', True)) + @ddt.data((mv.get_prior_version(mv.POOL_TYPE_FILTER), False), + (mv.POOL_TYPE_FILTER, True)) @ddt.unpack @mock.patch('cinder.scheduler.rpcapi.SchedulerAPI.get_pools') @mock.patch('cinder.api.common.reject_invalid_filters') diff --git a/cinder/tests/unit/api/contrib/test_services.py b/cinder/tests/unit/api/contrib/test_services.py index cf2fe948450..5bde460cd56 100644 --- a/cinder/tests/unit/api/contrib/test_services.py +++ b/cinder/tests/unit/api/contrib/test_services.py @@ -25,7 +25,7 @@ import webob.exc from cinder.api.contrib import services 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 exception from cinder import objects @@ -114,10 +114,10 @@ fake_services_list = [ class FakeRequest(object): 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.headers = {'OpenStack-API-Version': 'volume ' + version} - self.api_version_request = api_version.APIVersionRequest(version) + self.headers = mv.get_mv_header(version) + self.api_version_request = mv.get_api_version(version) class FakeRequestWithBinary(FakeRequest): @@ -246,19 +246,19 @@ class ServicesTest(test.TestCase): self.assertEqual(response, res_dict) 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, 'failover', {'cluster': 'cluster1'}) 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, 'failover', {'backend_id': 'replica1'}) @ddt.data({'host': 'hostname'}, {'cluster': 'mycluster'}) @mock.patch('cinder.volume.api.API.failover') def test_failover(self, body, failover_mock): - req = FakeRequest(version='3.26') + req = FakeRequest(version=mv.REPLICATION_CLUSTER) body['backend_id'] = 'replica1' res = self.controller.update(req, 'failover', body) self.assertEqual(202, res.status_code) @@ -269,14 +269,14 @@ class ServicesTest(test.TestCase): @ddt.data({}, {'host': 'hostname', 'cluster': 'mycluster'}) @mock.patch('cinder.volume.api.API.failover') def test_failover_invalid_input(self, body, failover_mock): - req = FakeRequest(version='3.26') + req = FakeRequest(version=mv.REPLICATION_CLUSTER) body['backend_id'] = 'replica1' self.assertRaises(exception.InvalidInput, self.controller.update, req, 'failover', body) failover_mock.assert_not_called() 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) response = {'services': [{'binary': 'cinder-scheduler', @@ -689,7 +689,7 @@ class ServicesTest(test.TestCase): def test_services_action_cluster_not_found(self, method, body, mock_get_all_services): 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 = [] msg = 'No service found with cluster=%s' % mock.sentinel.cluster result = self.assertRaises(exception.InvalidInput, @@ -729,7 +729,7 @@ class ServicesTest(test.TestCase): @mock.patch('cinder.api.contrib.services.ServiceController._set_log') def test_set_log(self, set_log_mock): set_log_mock.return_value = None - req = FakeRequest(version='3.32') + req = FakeRequest(version=mv.LOG_LEVEL) body = mock.sentinel.body res = self.controller.update(req, 'set-log', body) 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') def test_get_log(self, get_log_mock): get_log_mock.return_value = None - req = FakeRequest(version='3.32') + req = FakeRequest(version=mv.LOG_LEVEL) body = mock.sentinel.body res = self.controller.update(req, 'get-log', body) self.assertEqual(get_log_mock.return_value, res) diff --git a/cinder/tests/unit/api/contrib/test_used_limits.py b/cinder/tests/unit/api/contrib/test_used_limits.py index cca43233d5a..7492e0ee5a0 100644 --- a/cinder/tests/unit/api/contrib/test_used_limits.py +++ b/cinder/tests/unit/api/contrib/test_used_limits.py @@ -17,6 +17,7 @@ import ddt import mock 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 wsgi from cinder import exception @@ -40,8 +41,11 @@ class UsedLimitsTestCase(test.TestCase): super(UsedLimitsTestCase, self).setUp() self.controller = used_limits.UsedLimitsController() - @ddt.data(('2.0', False), ('3.38', True), ('3.38', False), ('3.39', True), - ('3.39', False)) + @ddt.data(('2.0', 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.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) abs_limits = res.obj['limits']['absolute'] - # if admin, only 3.39 and req contains project_id filter, cinder - # returns the specified project's quota. - if version == '3.39' and has_project: + # if admin, only LIMITS_ADMIN_FILTER and req contains project_id + # filter, cinder returns the specified project's quota. + if version == mv.LIMITS_ADMIN_FILTER and has_project: self.assertEqual(1, abs_limits['totalGigabytesUsed']) else: self.assertEqual(2, abs_limits['totalGigabytesUsed']) diff --git a/cinder/tests/unit/api/contrib/test_volume_actions.py b/cinder/tests/unit/api/contrib/test_volume_actions.py index 9b282106f0e..0ceedf6bcbd 100644 --- a/cinder/tests/unit/api/contrib/test_volume_actions.py +++ b/cinder/tests/unit/api/contrib/test_volume_actions.py @@ -24,7 +24,7 @@ from six.moves import http_client import webob 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 db from cinder import exception @@ -818,7 +818,7 @@ class VolumeImageActionsTest(test.TestCase): 'size': 0} return ret - def fake_image_service_create_3_1(self, *args): + def fake_image_service_create_with_params(self, *args): ret = { 'status': u'queued', 'name': u'image_name', @@ -1017,13 +1017,13 @@ class VolumeImageActionsTest(test.TestCase): id, body) - @ddt.data({'version': '3.41', + @ddt.data({'version': mv.get_prior_version(mv.VOLUME_EXTEND_INUSE), 'status': 'available'}, - {'version': '3.41', + {'version': mv.get_prior_version(mv.VOLUME_EXTEND_INUSE), 'status': 'in-use'}, - {'version': '3.42', + {'version': mv.VOLUME_EXTEND_INUSE, 'status': 'available'}, - {'version': '3.42', + {'version': mv.VOLUME_EXTEND_INUSE, 'status': 'in-use'}) @ddt.unpack def test_extend_attached_volume(self, version, status): @@ -1035,9 +1035,9 @@ class VolumeImageActionsTest(test.TestCase): body = {"os-extend": {"new_size": 2}} req = fakes.HTTPRequest.blank('/v3/%s/volumes/%s/action' % (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) - 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'], vol, 2, attached=True) else: @@ -1117,8 +1117,8 @@ class VolumeImageActionsTest(test.TestCase): id = 'aaaaaaaa-bbbb-cccc-dddd-eeeeeeeeeeee' req = fakes.HTTPRequest.blank('/v3/tenant1/volumes/%s/action' % id) req.environ['cinder.context'].is_admin = False - req.headers = {'OpenStack-API-Version': 'volume 3.1'} - req.api_version_request = api_version.APIVersionRequest('3.1') + req.headers = mv.get_mv_header(mv.UPLOAD_IMAGE_PARAMS) + req.api_version_request = mv.get_api_version(mv.UPLOAD_IMAGE_PARAMS) body = self._get_os_volume_upload_image() body['os-volume_upload_image']['visibility'] = 'public' 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(glance.GlanceImageService, "create") @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, mock_copy_volume_to_image, mock_create, @@ -1323,7 +1323,7 @@ class VolumeImageActionsTest(test.TestCase): "volume_id": volume.id, "key": "x_billing_code_license", "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 = \ 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), use_admin_context=self.context.is_admin) req.environ['cinder.context'].is_admin = True - req.headers = {'OpenStack-API-Version': 'volume 3.1'} - req.api_version_request = api_version.APIVersionRequest('3.1') + req.headers = mv.get_mv_header(mv.UPLOAD_IMAGE_PARAMS) + req.api_version_request = mv.get_api_version(mv.UPLOAD_IMAGE_PARAMS) body = self._get_os_volume_upload_image() body['os-volume_upload_image']['visibility'] = 'public' body['os-volume_upload_image']['protected'] = True diff --git a/cinder/tests/unit/api/contrib/test_volume_manage.py b/cinder/tests/unit/api/contrib/test_volume_manage.py index 5f6287f9497..34d07f591de 100644 --- a/cinder/tests/unit/api/contrib/test_volume_manage.py +++ b/cinder/tests/unit/api/contrib/test_volume_manage.py @@ -23,6 +23,7 @@ from six.moves.urllib.parse import urlencode import webob 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 import context from cinder import exception @@ -226,15 +227,15 @@ class VolumeManageTest(test.TestCase): self.assertEqual(body['volume']['ref'], args[3]) 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 req = webob.Request.blank(url, base_url='http://localhost.com' + url) req.method = 'POST' + req.headers = mv.get_mv_header(version) req.headers['Content-Type'] = 'application/json' req.environ['cinder.context'] = self._admin_ctxt req.body = jsonutils.dump_as_bytes(body) - req.headers = {'OpenStack-API-Version': 'volume %s' % version} - req.api_version_request = api_version.APIVersionRequest(version) + req.api_version_request = mv.get_api_version(version) res = self.controller.create(req, body) return res @@ -244,7 +245,7 @@ class VolumeManageTest(test.TestCase): def test_manage_volume_ok_cluster(self, mock_validate, mock_api_manage): body = {'volume': {'cluster': 'cluster', '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())) # Check that the manage API was called with the correct arguments. @@ -262,7 +263,8 @@ class VolumeManageTest(test.TestCase): 'cluster': 'cluster', 'ref': 'fake_ref'}} 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): """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.""" body = {'volume': {'host': 'host_ok', '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(1, mock_api_manage.call_count) self.assertEqual('creating', diff --git a/cinder/tests/unit/api/v3/test_attachments.py b/cinder/tests/unit/api/v3/test_attachments.py index 950106672cd..f780f9d5b8f 100644 --- a/cinder/tests/unit/api/v3/test_attachments.py +++ b/cinder/tests/unit/api/v3/test_attachments.py @@ -21,6 +21,7 @@ import ddt import mock import webob +from cinder.api import microversions as mv from cinder.api.v3 import attachments as v3_attachments from cinder import context 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 rpcapi as volume_rpcapi -ATTACHMENTS_MICRO_VERSION = '3.27' - @ddt.ddt class AttachmentsAPITestCase(test.TestCase): @@ -81,7 +80,7 @@ class AttachmentsAPITestCase(test.TestCase): def test_create_attachment(self): req = fakes.HTTPRequest.blank('/v3/%s/attachments' % fake.PROJECT_ID, - version=ATTACHMENTS_MICRO_VERSION) + version=mv.NEW_ATTACH) body = { "attachment": { @@ -104,7 +103,7 @@ class AttachmentsAPITestCase(test.TestCase): mock_update.return_value = fake_connector req = fakes.HTTPRequest.blank('/v3/%s/attachments/%s' % (fake.PROJECT_ID, self.attachment1.id), - version=ATTACHMENTS_MICRO_VERSION, + version=mv.NEW_ATTACH, use_admin_context=True) body = { "attachment": @@ -124,7 +123,7 @@ class AttachmentsAPITestCase(test.TestCase): mock_get.return_value = {'project_id': fake.PROJECT2_ID} req = fakes.HTTPRequest.blank('/v3/%s/attachments/%s' % (fake.PROJECT_ID, self.attachment1.id), - version=ATTACHMENTS_MICRO_VERSION, + version=mv.NEW_ATTACH, use_admin_context=False) body = { "attachment": @@ -139,7 +138,8 @@ class AttachmentsAPITestCase(test.TestCase): self.controller.delete, req, 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') def test_attachment_list_with_general_filter(self, version, mock_update): url = '/v3/%s/attachments' % fake.PROJECT_ID @@ -148,8 +148,8 @@ class AttachmentsAPITestCase(test.TestCase): use_admin_context=False) self.controller.index(req) - if version != '3.30': - support_like = True if version == '3.34' else False + if version != mv.get_prior_version(mv.RESOURCE_FILTER): + support_like = True if version == mv.LIKE_FILTER else False mock_update.assert_called_once_with(req.environ['cinder.context'], mock.ANY, 'attachment', support_like) @@ -164,7 +164,7 @@ class AttachmentsAPITestCase(test.TestCase): attach_status=status) req = fakes.HTTPRequest.blank('/v3/%s/attachments/%s' % (fake.PROJECT_ID, attachment.id), - version=ATTACHMENTS_MICRO_VERSION, + version=mv.NEW_ATTACH, use_admin_context=True) self.controller.delete(req, attachment.id) @@ -201,7 +201,7 @@ class AttachmentsAPITestCase(test.TestCase): def test_create_attachment_without_resource_uuid(self, resource_uuid): req = fakes.HTTPRequest.blank('/v3/%s/attachments' % fake.PROJECT_ID, - version=ATTACHMENTS_MICRO_VERSION) + version=mv.NEW_ATTACH) body = { "attachment": { @@ -220,7 +220,7 @@ class AttachmentsAPITestCase(test.TestCase): if is_detail: url = '/v3/%s/groups/detail' % fake.PROJECT_ID 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) res_dict = list_func(req) @@ -231,7 +231,7 @@ class AttachmentsAPITestCase(test.TestCase): def test_list_attachments_with_limit(self): 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) res_dict = self.controller.index(req) @@ -241,7 +241,7 @@ class AttachmentsAPITestCase(test.TestCase): def test_list_attachments_with_marker(self): url = '/v3/%s/attachments?marker=%s' % (fake.PROJECT_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) res_dict = self.controller.index(req) @@ -254,7 +254,7 @@ class AttachmentsAPITestCase(test.TestCase): def test_list_attachments_with_sort(self, sort_dir): url = '/v3/%s/attachments?sort_key=id&sort_dir=%s' % (fake.PROJECT_ID, sort_dir) - req = fakes.HTTPRequest.blank(url, version=ATTACHMENTS_MICRO_VERSION, + req = fakes.HTTPRequest.blank(url, version=mv.NEW_ATTACH, use_admin_context=True) res_dict = self.controller.index(req) @@ -280,7 +280,7 @@ class AttachmentsAPITestCase(test.TestCase): @ddt.unpack def test_list_attachment_with_tenants(self, admin, request_url, count): 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) res_dict = self.controller.index(req) diff --git a/cinder/tests/unit/api/v3/test_backups.py b/cinder/tests/unit/api/v3/test_backups.py index 33fdabae805..d055c6513d6 100644 --- a/cinder/tests/unit/api/v3/test_backups.py +++ b/cinder/tests/unit/api/v3/test_backups.py @@ -19,6 +19,7 @@ import ddt import mock import webob +from cinder.api import microversions as mv from cinder.api.openstack import api_version_request as api_version from cinder.api.v3 import backups from cinder.api.views import backups as backup_view @@ -44,7 +45,7 @@ class BackupsControllerAPITestCase(test.TestCase): is_admin=True) 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' % (fake.PROJECT_ID, backup_id)) req.environ['cinder.context'].is_admin = True @@ -54,7 +55,8 @@ class BackupsControllerAPITestCase(test.TestCase): return req 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", }} self.assertRaises(exception.VersionNotFoundForAPIMethod, self.controller.update, req, fake.BACKUP_ID, @@ -86,7 +88,9 @@ class BackupsControllerAPITestCase(test.TestCase): self.controller.update, 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') def test_backup_list_with_general_filter(self, version, mock_update): url = '/v3/%s/backups' % fake.PROJECT_ID @@ -95,13 +99,14 @@ class BackupsControllerAPITestCase(test.TestCase): use_admin_context=False) self.controller.index(req) - if version != '3.30': - support_like = True if version == '3.34' else False + if version != mv.get_prior_version(mv.RESOURCE_FILTER): + support_like = True if version == mv.LIKE_FILTER else False mock_update.assert_called_once_with(req.environ['cinder.context'], mock.ANY, 'backup', 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): backup1 = test_utils.create_backup( self.ctxt, display_name='b_test_name', @@ -111,7 +116,7 @@ class BackupsControllerAPITestCase(test.TestCase): status=fields.BackupStatus.AVAILABLE) url = '/v3/%s/backups?sort_key=name' % fake.PROJECT_ID 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.controller.index, req) diff --git a/cinder/tests/unit/api/v3/test_cluster.py b/cinder/tests/unit/api/v3/test_cluster.py index a08fbede992..9e82af9578d 100644 --- a/cinder/tests/unit/api/v3/test_cluster.py +++ b/cinder/tests/unit/api/v3/test_cluster.py @@ -21,6 +21,7 @@ import mock from oslo_utils import versionutils 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.v3 import clusters from cinder import context @@ -78,7 +79,7 @@ EXPECTED = [{'created_at': datetime.datetime(2016, 6, 1, 2, 46, 28), 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.headers = {'OpenStack-API-Version': 'volume ' + version} self.api_version_request = api_version.APIVersionRequest(version) @@ -108,8 +109,10 @@ class ClustersTestCase(test.TestCase): REPLICATION_FILTERS = ({'replication_status': 'error'}, {'frozen': True}, {'active_backend_id': 'replication'}) - def _get_expected(self, version='3.8'): - if versionutils.convert_version_to_tuple(version) >= (3, 19): + def _get_expected(self, + 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 expect = [] @@ -130,7 +133,7 @@ class ClustersTestCase(test.TestCase): @mock.patch('cinder.db.cluster_get_all', return_value=CLUSTERS_ORM) 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 {} req = FakeRequest(version=version, **filters) 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.""" self.assertRaises(exception.VersionNotFoundForAPIMethod, self._test_list, detailed=detailed, - version='3.6') + version=mv.get_prior_version(mv.CLUSTER_SUPPORT)) @ddt.data(*REPLICATION_FILTERS) def test_index_detail_replication_new_fields(self, filters): - version = '3.26' - expected = {'clusters': self._get_expected(version)} + expected = {'clusters': self._get_expected(mv.REPLICATION_CLUSTER)} self._test_list(detailed=True, filters=filters, expected=expected, - version=version) + version=mv.REPLICATION_CLUSTER) @ddt.data(*REPLICATION_FILTERS) def test_index_summary_replication_new_fields(self, filters): @@ -209,7 +211,7 @@ class ClustersTestCase(test.TestCase): 'replication_status': 'error', 'status': 'disabled'}]} self._test_list(detailed=False, filters=filters, expected=expected, - version='3.26') + version=mv.REPLICATION_CLUSTER) @mock.patch('cinder.db.sqlalchemy.api.cluster_get', return_value=CLUSTERS_ORM[0]) @@ -232,7 +234,7 @@ class ClustersTestCase(test.TestCase): self.controller.show, req, 'name') 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.controller.show, req, 'name') @@ -307,6 +309,6 @@ class ClustersTestCase(test.TestCase): @ddt.data('enable', 'disable') 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.controller.update, req, action, {}) diff --git a/cinder/tests/unit/api/v3/test_consistencygroups.py b/cinder/tests/unit/api/v3/test_consistencygroups.py index ece7524ab04..b6e7aefdcc3 100644 --- a/cinder/tests/unit/api/v3/test_consistencygroups.py +++ b/cinder/tests/unit/api/v3/test_consistencygroups.py @@ -15,6 +15,7 @@ import ddt from six.moves import http_client import webob +from cinder.api import microversions as mv from cinder.api.openstack import api_version_request as api_version from cinder.api.v3 import consistencygroups from cinder import context @@ -72,9 +73,10 @@ class ConsistencyGroupsAPITestCase(test.TestCase): req = fakes.HTTPRequest.blank('/v3/%s/consistencygroups/%s/update' % (fake.PROJECT_ID, consistencygroup.id)) 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['OpenStack-API-Version'] = 'volume 3.6' - req.api_version_request = api_version.APIVersionRequest('3.6') + req.api_version_request = mv.get_api_version( + mv.CG_UPDATE_BLANK_PROPERTIES) body = {"consistencygroup": {"name": "", "description": "", "add_volumes": None, @@ -118,9 +120,10 @@ class ConsistencyGroupsAPITestCase(test.TestCase): req = fakes.HTTPRequest.blank('/v3/%s/consistencygroups/%s/update' % (fake.PROJECT_ID, consistencygroup.id)) 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['OpenStack-API-Version'] = 'volume 3.6' - req.api_version_request = api_version.APIVersionRequest('3.6') + req.api_version_request = mv.get_api_version( + mv.CG_UPDATE_BLANK_PROPERTIES) body = {"consistencygroup": {"name": None, "description": None, "add_volumes": None, @@ -132,16 +135,20 @@ class ConsistencyGroupsAPITestCase(test.TestCase): req, consistencygroup.id, body) 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( ctxt=self.ctxt, status=fields.ConsistencyGroupStatus.AVAILABLE) req = fakes.HTTPRequest.blank('/v3/%s/consistencygroups/%s/update' % (fake.PROJECT_ID, consistencygroup.id)) 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['OpenStack-API-Version'] = 'volume 3.5' - req.api_version_request = api_version.APIVersionRequest('3.5') + body = {"consistencygroup": {"name": None, "description": None, "add_volumes": None, @@ -160,9 +167,13 @@ class ConsistencyGroupsAPITestCase(test.TestCase): req = fakes.HTTPRequest.blank('/v3/%s/consistencygroups/%s/update' % (fake.PROJECT_ID, consistencygroup.id)) 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['OpenStack-API-Version'] = 'volume 3.5' - req.api_version_request = api_version.APIVersionRequest('3.5') + body = None self.assertRaisesRegexp(webob.exc.HTTPBadRequest, "Missing request body", @@ -177,9 +188,13 @@ class ConsistencyGroupsAPITestCase(test.TestCase): req = fakes.HTTPRequest.blank('/v3/%s/consistencygroups/%s/update' % (fake.PROJECT_ID, consistencygroup.id)) 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['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", "description": "fake consistency group", "add_volumes": "volume-uuid-1", diff --git a/cinder/tests/unit/api/v3/test_group_snapshots.py b/cinder/tests/unit/api/v3/test_group_snapshots.py index d464c4e5f90..3d230ecc0af 100644 --- a/cinder/tests/unit/api/v3/test_group_snapshots.py +++ b/cinder/tests/unit/api/v3/test_group_snapshots.py @@ -22,6 +22,7 @@ import mock from six.moves import http_client import webob +from cinder.api import microversions as mv from cinder.api.v3 import group_snapshots as v3_group_snapshots from cinder import context from cinder import db @@ -35,9 +36,6 @@ from cinder.tests.unit import fake_constants as fake from cinder.tests.unit import utils import cinder.volume -GROUP_MICRO_VERSION = '3.14' -SUPPORT_FILTER_VERSION = '3.29' - @ddt.ddt class GroupSnapshotsAPITestCase(test.TestCase): @@ -76,7 +74,7 @@ class GroupSnapshotsAPITestCase(test.TestCase): self.context, group_id=self.group.id) req = fakes.HTTPRequest.blank('/v3/%s/group_snapshots/%s' % (fake.PROJECT_ID, group_snapshot.id), - version=GROUP_MICRO_VERSION) + version=mv.GROUP_SNAPSHOTS) res_dict = self.controller.show(req, group_snapshot.id) self.assertEqual(1, len(res_dict)) @@ -95,7 +93,8 @@ class GroupSnapshotsAPITestCase(test.TestCase): url = '/v3/%s/group_snapshots?limit=1' % fake.PROJECT_ID if is_detail: 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: res_dict = self.controller.detail(req) else: @@ -122,7 +121,8 @@ class GroupSnapshotsAPITestCase(test.TestCase): url = '/v3/%s/group_snapshots?offset=1' % fake.PROJECT_ID if is_detail: 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: res_dict = self.controller.detail(req) else: @@ -146,7 +146,8 @@ class GroupSnapshotsAPITestCase(test.TestCase): if is_detail: url = ('/v3/%s/group_snapshots/detail?offset=234523423455454' % 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: self.assertRaises(webob.exc.HTTPBadRequest, self.controller.detail, req) @@ -164,7 +165,8 @@ class GroupSnapshotsAPITestCase(test.TestCase): if is_detail: url = ('/v3/%s/group_snapshots/detail?limit=2&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: res_dict = self.controller.detail(req) else: @@ -184,7 +186,9 @@ class GroupSnapshotsAPITestCase(test.TestCase): res_dict['group_snapshots'][0].keys()) 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') def test_group_snapshot_list_with_general_filter(self, version, mock_update): @@ -194,8 +198,8 @@ class GroupSnapshotsAPITestCase(test.TestCase): use_admin_context=False) self.controller.index(req) - if version != '3.30': - support_like = True if version == '3.34' else False + if version != mv.get_prior_version(mv.RESOURCE_FILTER): + support_like = True if version == mv.LIKE_FILTER else False mock_update.assert_called_once_with(req.environ['cinder.context'], mock.ANY, 'group_snapshot', support_like) @@ -209,7 +213,8 @@ class GroupSnapshotsAPITestCase(test.TestCase): url = ('/v3/%s/group_snapshots/detail?' 'all_tenants=True&id=%s') % (fake.PROJECT_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) if is_detail: res_dict = self.controller.detail(req) @@ -226,10 +231,10 @@ class GroupSnapshotsAPITestCase(test.TestCase): self.assertNotIn('description', res_dict['group_snapshots'][0].keys()) - @ddt.data({'is_detail': True, 'version': GROUP_MICRO_VERSION}, - {'is_detail': False, 'version': GROUP_MICRO_VERSION}, - {'is_detail': True, 'version': '3.28'}, - {'is_detail': False, 'version': '3.28'},) + @ddt.data({'is_detail': True, 'version': mv.GROUP_SNAPSHOTS}, + {'is_detail': False, 'version': mv.GROUP_SNAPSHOTS}, + {'is_detail': True, 'version': mv.POOL_FILTER}, + {'is_detail': False, 'version': mv.POOL_FILTER},) @ddt.unpack def test_list_group_snapshot_with_filter_previous_version(self, is_detail, version): @@ -257,7 +262,8 @@ class GroupSnapshotsAPITestCase(test.TestCase): if is_detail: url = ('/v3/%s/group_snapshots/detail?sort=id:asc' % 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.sort() if is_detail: @@ -282,7 +288,7 @@ class GroupSnapshotsAPITestCase(test.TestCase): req = fakes.HTTPRequest.blank('/v3/%s/group_snapshots/%s' % (fake.PROJECT_ID, fake.WILL_NOT_BE_FOUND_ID), - version=GROUP_MICRO_VERSION) + version=mv.GROUP_SNAPSHOTS) self.assertRaises(exception.GroupSnapshotNotFound, self.controller.show, req, fake.WILL_NOT_BE_FOUND_ID) @@ -294,7 +300,7 @@ class GroupSnapshotsAPITestCase(test.TestCase): else: request_url = '/v3/%s/group_snapshots' req = fakes.HTTPRequest.blank(request_url % fake.PROJECT_ID, - version=GROUP_MICRO_VERSION) + version=mv.GROUP_SNAPSHOTS) if is_detail: res_dict = self.controller.detail(req) else: @@ -326,7 +332,7 @@ class GroupSnapshotsAPITestCase(test.TestCase): "group_id": self.group.id}} req = fakes.HTTPRequest.blank('/v3/%s/group_snapshots' % fake.PROJECT_ID, - version=GROUP_MICRO_VERSION) + version=mv.GROUP_SNAPSHOTS) res_dict = self.controller.create(req, body) self.assertEqual(1, len(res_dict)) @@ -356,7 +362,7 @@ class GroupSnapshotsAPITestCase(test.TestCase): "group_id": group.id}} req = fakes.HTTPRequest.blank('/v3/%s/group_snapshots' % fake.PROJECT_ID, - version=GROUP_MICRO_VERSION) + version=mv.GROUP_SNAPSHOTS) self.assertRaises(webob.exc.HTTPBadRequest, self.controller.create, req, body) self.assertTrue(mock_validate.called) @@ -369,7 +375,7 @@ class GroupSnapshotsAPITestCase(test.TestCase): # omit body from the request req = fakes.HTTPRequest.blank('/v3/%s/group_snapshots' % fake.PROJECT_ID, - version=GROUP_MICRO_VERSION) + version=mv.GROUP_SNAPSHOTS) self.assertRaises(webob.exc.HTTPBadRequest, self.controller.create, req, None) @@ -383,7 +389,7 @@ class GroupSnapshotsAPITestCase(test.TestCase): "group_id": self.group.id}} req = fakes.HTTPRequest.blank('/v3/%s/group_snapshots' % fake.PROJECT_ID, - version=GROUP_MICRO_VERSION) + version=mv.GROUP_SNAPSHOTS) self.assertRaises(webob.exc.HTTPBadRequest, self.controller.create, req, body) @@ -397,7 +403,7 @@ class GroupSnapshotsAPITestCase(test.TestCase): "group_id": self.group.id}} req = fakes.HTTPRequest.blank('/v3/%s/group_snapshots' % fake.PROJECT_ID, - version=GROUP_MICRO_VERSION) + version=mv.GROUP_SNAPSHOTS) self.assertRaises(exception.GroupSnapshotNotFound, self.controller.create, req, body) @@ -413,7 +419,7 @@ class GroupSnapshotsAPITestCase(test.TestCase): "group_id": empty_group.id}} req = fakes.HTTPRequest.blank('/v3/%s/group_snapshots' % fake.PROJECT_ID, - version=GROUP_MICRO_VERSION) + version=mv.GROUP_SNAPSHOTS) self.assertRaises(webob.exc.HTTPBadRequest, self.controller.create, req, body) @@ -426,7 +432,7 @@ class GroupSnapshotsAPITestCase(test.TestCase): status=fields.GroupSnapshotStatus.AVAILABLE) req = fakes.HTTPRequest.blank('/v3/%s/group_snapshots/%s' % (fake.PROJECT_ID, group_snapshot.id), - version=GROUP_MICRO_VERSION) + version=mv.GROUP_SNAPSHOTS) res_dict = self.controller.delete(req, group_snapshot.id) group_snapshot = objects.GroupSnapshot.get_by_id(self.context, @@ -450,7 +456,7 @@ class GroupSnapshotsAPITestCase(test.TestCase): volume_type_ids=[fake.VOLUME_TYPE_ID],) req = fakes.HTTPRequest.blank('/v3/%s/group_snapshots/%s' % (fake.PROJECT_ID, group_snapshot.id), - version=GROUP_MICRO_VERSION) + version=mv.GROUP_SNAPSHOTS) self.assertRaises(webob.exc.HTTPBadRequest, self.controller.delete, req, group_snapshot.id) @@ -461,7 +467,7 @@ class GroupSnapshotsAPITestCase(test.TestCase): req = fakes.HTTPRequest.blank('/v3/%s/group_snapshots/%s' % (fake.PROJECT_ID, fake.WILL_NOT_BE_FOUND_ID), - version=GROUP_MICRO_VERSION) + version=mv.GROUP_SNAPSHOTS) self.assertRaises(exception.GroupSnapshotNotFound, self.controller.delete, req, fake.WILL_NOT_BE_FOUND_ID) @@ -473,19 +479,20 @@ class GroupSnapshotsAPITestCase(test.TestCase): status='invalid') req = fakes.HTTPRequest.blank('/v3/%s/group_snapshots/%s' % (fake.PROJECT_ID, group_snapshot.id), - version=GROUP_MICRO_VERSION) + version=mv.GROUP_SNAPSHOTS) self.assertRaises(webob.exc.HTTPBadRequest, self.controller.delete, req, group_snapshot.id) group_snapshot.destroy() - @ddt.data(('3.11', 'fake_snapshot_001', + @ddt.data((mv.GROUP_TYPE, 'fake_snapshot_001', fields.GroupSnapshotStatus.AVAILABLE, exception.VersionNotFoundForAPIMethod), - ('3.18', 'fake_snapshot_001', + (mv.get_prior_version(mv.GROUP_SNAPSHOT_RESET_STATUS), + 'fake_snapshot_001', fields.GroupSnapshotStatus.AVAILABLE, exception.VersionNotFoundForAPIMethod), - ('3.19', 'fake_snapshot_001', + (mv.GROUP_SNAPSHOT_RESET_STATUS, 'fake_snapshot_001', fields.GroupSnapshotStatus.AVAILABLE, exception.GroupSnapshotNotFound)) @ddt.unpack @@ -509,7 +516,7 @@ class GroupSnapshotsAPITestCase(test.TestCase): status=fields.GroupSnapshotStatus.CREATING) req = fakes.HTTPRequest.blank('/v3/%s/group_snapshots/%s/action' % (fake.PROJECT_ID, group_snapshot.id), - version='3.19') + version=mv.GROUP_SNAPSHOT_RESET_STATUS) body = {"reset_status": { "status": "invalid_test_status" }} @@ -525,7 +532,7 @@ class GroupSnapshotsAPITestCase(test.TestCase): status=fields.GroupSnapshotStatus.CREATING) req = fakes.HTTPRequest.blank('/v3/%s/group_snapshots/%s/action' % (fake.PROJECT_ID, group_snapshot.id), - version='3.19') + version=mv.GROUP_SNAPSHOT_RESET_STATUS) body = {"reset_status": { "status": fields.GroupSnapshotStatus.AVAILABLE }} diff --git a/cinder/tests/unit/api/v3/test_group_specs.py b/cinder/tests/unit/api/v3/test_group_specs.py index 4c15d75b612..6ab66a09a59 100644 --- a/cinder/tests/unit/api/v3/test_group_specs.py +++ b/cinder/tests/unit/api/v3/test_group_specs.py @@ -21,12 +21,11 @@ from cinder import db from cinder import rpc from cinder import test +from cinder.api import microversions as mv from cinder.api.v3 import group_specs as v3_group_specs from cinder.tests.unit.api import fakes from cinder.tests.unit import fake_constants as fake -GROUP_TYPE_MICRO_VERSION = '3.11' - fake_group_specs = { 'key1': 'value1', 'key2': 'value2' @@ -71,7 +70,7 @@ class GroupSpecsTestCase(test.TestCase): req = fakes.HTTPRequest.blank('v3/%s/group_specs' % fake.PROJECT_ID, use_admin_context=True, - version=GROUP_TYPE_MICRO_VERSION) + version=mv.GROUP_TYPE) req.environ['cinder.context'] = self.ctxt res_dict = self.controller.index(req, fake.GROUP_ID) group_specs_dict = res_dict['group_specs'] @@ -90,7 +89,7 @@ class GroupSpecsTestCase(test.TestCase): req = fakes.HTTPRequest.blank('v3/%s/group_specs' % fake.PROJECT_ID, 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.assertTrue(mock_rpc_notifier.called) @@ -108,7 +107,7 @@ class GroupSpecsTestCase(test.TestCase): req = fakes.HTTPRequest.blank('v3/%s/group_specs' % fake.PROJECT_ID, use_admin_context=True, - version=GROUP_TYPE_MICRO_VERSION) + version=mv.GROUP_TYPE) self.controller.update(req, fake.GROUP_TYPE_ID, 'id', @@ -124,7 +123,7 @@ class GroupSpecsTestCase(test.TestCase): req = fakes.HTTPRequest.blank('v3/%s/group_specs' % fake.PROJECT_ID, use_admin_context=True, - version=GROUP_TYPE_MICRO_VERSION) + version=mv.GROUP_TYPE) res_dict = self.controller.show(req, fake.GROUP_TYPE_ID, 'key1') self.assertEqual('value1', res_dict['key1']) @@ -138,7 +137,7 @@ class GroupSpecsTestCase(test.TestCase): req = fakes.HTTPRequest.blank('v3/%s/group_specs' % fake.PROJECT_ID, use_admin_context=True, - version=GROUP_TYPE_MICRO_VERSION) + version=mv.GROUP_TYPE) self.controller.delete(req, fake.GROUP_TYPE_ID, 'key1') self.assertTrue(rpc_notifier_mock.called) @@ -151,7 +150,7 @@ class GroupSpecsTestCase(test.TestCase): req = fakes.HTTPRequest.blank('v3/%s/group_specs' % fake.PROJECT_ID, use_admin_context=True, - version=GROUP_TYPE_MICRO_VERSION) + version=mv.GROUP_TYPE) self.assertRaises(webob.exc.HTTPNotFound, self.controller.create, req, @@ -166,7 +165,7 @@ class GroupSpecsTestCase(test.TestCase): req = fakes.HTTPRequest.blank('v3/%s/group_specs' % fake.PROJECT_ID, use_admin_context=True, - version=GROUP_TYPE_MICRO_VERSION) + version=mv.GROUP_TYPE) self.assertRaises(webob.exc.HTTPNotFound, self.controller.delete, req, @@ -178,7 +177,7 @@ class GroupSpecsTestCase(test.TestCase): req = fakes.HTTPRequest.blank('v3/%s/group_specs' % fake.PROJECT_ID, use_admin_context=True, - version=GROUP_TYPE_MICRO_VERSION) + version=mv.GROUP_TYPE) self.assertRaises(webob.exc.HTTPBadRequest, self.controller.update, req, @@ -198,7 +197,7 @@ class GroupSpecsTestCase(test.TestCase): req = fakes.HTTPRequest.blank('v3/%s/group_specs' % fake.PROJECT_ID, use_admin_context=True, - version=GROUP_TYPE_MICRO_VERSION) + version=mv.GROUP_TYPE) self.assertRaises(webob.exc.HTTPNotFound, self.controller.show, req, @@ -216,6 +215,6 @@ class GroupSpecsTestCase(test.TestCase): req = fakes.HTTPRequest.blank('v3/%s/group_specs' % fake.PROJECT_ID, use_admin_context=True, - version=GROUP_TYPE_MICRO_VERSION) + version=mv.GROUP_TYPE) self.assertRaises(webob.exc.HTTPBadRequest, self.controller.create, req, fake.GROUP_ID, incorrect_fake_group_specs) diff --git a/cinder/tests/unit/api/v3/test_group_types.py b/cinder/tests/unit/api/v3/test_group_types.py index 48ea6a6f43e..b979e108ee1 100644 --- a/cinder/tests/unit/api/v3/test_group_types.py +++ b/cinder/tests/unit/api/v3/test_group_types.py @@ -23,6 +23,7 @@ import six import webob 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_types as v3_group_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.volume import group_types -GROUP_TYPE_MICRO_VERSION = '3.11' IN_USE_GROUP_TYPE = fake.GROUP_TYPE3_ID @@ -132,7 +132,7 @@ class GroupTypesApiTest(test.TestCase): mock_create, mock_get): boolean_is_public = strutils.bool_from_string(is_public) 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 body = {"group_type": {"is_public": is_public, "name": "group_type1", @@ -157,7 +157,7 @@ class GroupTypesApiTest(test.TestCase): '_notify_group_type_error') req = fakes.HTTPRequest.blank('/v3/%s/group_types/%s' % ( fake.PROJECT_ID, grp_type_id), - version=GROUP_TYPE_MICRO_VERSION) + version=mv.GROUP_TYPE) req.environ['cinder.context'] = self.ctxt if grp_type_id == IN_USE_GROUP_TYPE: self.assertRaises(webob.exc.HTTPBadRequest, @@ -177,7 +177,7 @@ class GroupTypesApiTest(test.TestCase): req = fakes.HTTPRequest.blank('/v3/%s/group_types' % fake.PROJECT_ID, use_admin_context=True, - version=GROUP_TYPE_MICRO_VERSION) + version=mv.GROUP_TYPE) res_dict = self.controller.index(req) self.assertEqual(3, len(res_dict['group_types'])) @@ -193,7 +193,7 @@ class GroupTypesApiTest(test.TestCase): return_empty_group_types_get_all_types) 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) self.assertEqual(0, len(res_dict['group_types'])) @@ -201,7 +201,7 @@ class GroupTypesApiTest(test.TestCase): def test_group_types_index_with_limit(self): req = fakes.HTTPRequest.blank('/v3/%s/group_types?limit=1' % fake.PROJECT_ID, - version=GROUP_TYPE_MICRO_VERSION) + version=mv.GROUP_TYPE) req.environ['cinder.context'] = self.ctxt res = self.controller.index(req) @@ -216,7 +216,7 @@ class GroupTypesApiTest(test.TestCase): def test_group_types_index_with_offset(self): req = fakes.HTTPRequest.blank( '/v3/%s/group_types?offset=1' % fake.PROJECT_ID, - version=GROUP_TYPE_MICRO_VERSION) + version=mv.GROUP_TYPE) req.environ['cinder.context'] = self.ctxt res = self.controller.index(req) @@ -224,14 +224,14 @@ class GroupTypesApiTest(test.TestCase): def test_group_types_index_with_offset_out_of_range(self): 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.controller.index, req) def test_group_types_index_with_limit_and_offset(self): req = fakes.HTTPRequest.blank( '/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 res = self.controller.index(req) @@ -244,7 +244,7 @@ class GroupTypesApiTest(test.TestCase): '&marker=%s' % (fake.PROJECT_ID, self.type_id2), - version=GROUP_TYPE_MICRO_VERSION) + version=mv.GROUP_TYPE) req.environ['cinder.context'] = self.ctxt res = self.controller.index(req) @@ -254,7 +254,7 @@ class GroupTypesApiTest(test.TestCase): def test_group_types_index_with_valid_filter(self): req = fakes.HTTPRequest.blank( '/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 res = self.controller.index(req) @@ -267,7 +267,7 @@ class GroupTypesApiTest(test.TestCase): def test_group_types_index_with_invalid_filter(self): req = fakes.HTTPRequest.blank( '/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 res = self.controller.index(req) @@ -276,7 +276,7 @@ class GroupTypesApiTest(test.TestCase): def test_group_types_index_with_sort_keys(self): req = fakes.HTTPRequest.blank('/v3/%s/group_types?sort=id' % fake.PROJECT_ID, - version=GROUP_TYPE_MICRO_VERSION) + version=mv.GROUP_TYPE) req.environ['cinder.context'] = self.ctxt res = self.controller.index(req) 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): req = fakes.HTTPRequest.blank( '/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 res = self.controller.index(req) 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): req = fakes.HTTPRequest.blank( '/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 res = self.controller.index(req) 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()) req = fakes.HTTPRequest.blank( '/v3/%s/types/%s' % (fake.PROJECT_ID, type_id), - version=GROUP_TYPE_MICRO_VERSION) + version=mv.GROUP_TYPE) req.environ['cinder.context'] = self.ctxt body = {"group_type": {"is_public": is_public, "name": "group_type1"}} self.controller.update(req, type_id, body) @@ -347,7 +347,7 @@ class GroupTypesApiTest(test.TestCase): type_id = six.text_type(uuid.uuid4()) req = fakes.HTTPRequest.blank('/v3/%s/group_types/' % fake.PROJECT_ID + type_id, - version=GROUP_TYPE_MICRO_VERSION) + version=mv.GROUP_TYPE) res_dict = self.controller.show(req, type_id) self.assertEqual(1, len(res_dict)) @@ -359,10 +359,10 @@ class GroupTypesApiTest(test.TestCase): self.mock_object(group_types, 'get_group_type', return_group_types_get_group_type) - type_id = six.text_type(uuid.uuid4()) - req = fakes.HTTPRequest.blank('/v3/%s/group_types/' % fake.PROJECT_ID - + type_id, - version='3.5') + type_id = uuid.uuid4() + req = fakes.HTTPRequest.blank( + '/v3/%s/group_types/%s' % (fake.PROJECT_ID, type_id), + version=mv.get_prior_version(mv.GROUP_TYPE)) self.assertRaises(exception.VersionNotFoundForAPIMethod, self.controller.show, req, type_id) @@ -374,7 +374,7 @@ class GroupTypesApiTest(test.TestCase): req = fakes.HTTPRequest.blank('/v3/%s/group_types/%s' % (fake.PROJECT_ID, fake.WILL_NOT_BE_FOUND_ID), - version=GROUP_TYPE_MICRO_VERSION) + version=mv.GROUP_TYPE) self.assertRaises(webob.exc.HTTPNotFound, self.controller.show, req, fake.WILL_NOT_BE_FOUND_ID) @@ -383,7 +383,7 @@ class GroupTypesApiTest(test.TestCase): return_group_types_get_default) req = fakes.HTTPRequest.blank('/v3/%s/group_types/default' % fake.PROJECT_ID, - version=GROUP_TYPE_MICRO_VERSION) + version=mv.GROUP_TYPE) req.method = 'GET' res_dict = self.controller.show(req, 'default') self.assertEqual(1, len(res_dict)) @@ -396,7 +396,7 @@ class GroupTypesApiTest(test.TestCase): return_group_types_get_default_not_found) req = fakes.HTTPRequest.blank('/v3/%s/group_types/default' % fake.PROJECT_ID, - version=GROUP_TYPE_MICRO_VERSION) + version=mv.GROUP_TYPE) req.method = 'GET' self.assertRaises(webob.exc.HTTPNotFound, @@ -419,7 +419,7 @@ class GroupTypesApiTest(test.TestCase): ) request = fakes.HTTPRequest.blank("/v3", - version=GROUP_TYPE_MICRO_VERSION) + version=mv.GROUP_TYPE) output = view_builder.show(request, raw_group_type) self.assertIn('group_type', output) @@ -448,7 +448,7 @@ class GroupTypesApiTest(test.TestCase): ) 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) self.assertIn('group_type', output) @@ -479,7 +479,7 @@ class GroupTypesApiTest(test.TestCase): ) request = fakes.HTTPRequest.blank("/v3", - version=GROUP_TYPE_MICRO_VERSION) + version=mv.GROUP_TYPE) output = view_builder.show(request, raw_group_type) self.assertIn('group_type', output) @@ -510,7 +510,7 @@ class GroupTypesApiTest(test.TestCase): ) request = fakes.HTTPRequest.blank("/v3", - version=GROUP_TYPE_MICRO_VERSION) + version=mv.GROUP_TYPE) output = view_builder.show(request, raw_group_type) self.assertIn('group_type', output) @@ -542,7 +542,7 @@ class GroupTypesApiTest(test.TestCase): ) request = fakes.HTTPRequest.blank("/v3", - version=GROUP_TYPE_MICRO_VERSION) + version=mv.GROUP_TYPE) output = view_builder.show(request, raw_group_type) self.assertIn('group_type', output) @@ -576,7 +576,7 @@ class GroupTypesApiTest(test.TestCase): ) request = fakes.HTTPRequest.blank("/v3", - version=GROUP_TYPE_MICRO_VERSION) + version=mv.GROUP_TYPE) output = view_builder.index(request, raw_group_types) self.assertIn('group_types', output) @@ -611,7 +611,7 @@ class GroupTypesApiTest(test.TestCase): ) 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) self.assertIn('group_types', output) diff --git a/cinder/tests/unit/api/v3/test_groups.py b/cinder/tests/unit/api/v3/test_groups.py index ad5c57c6403..45318128448 100644 --- a/cinder/tests/unit/api/v3/test_groups.py +++ b/cinder/tests/unit/api/v3/test_groups.py @@ -22,6 +22,7 @@ import mock from six.moves import http_client import webob +from cinder.api import microversions as mv from cinder.api.v3 import groups as v3_groups from cinder import context 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.volume import api as volume_api -GROUP_MICRO_VERSION = '3.13' -GROUP_FROM_SRC_MICRO_VERSION = '3.14' -GROUP_REPLICATION_MICRO_VERSION = '3.38' -INVALID_GROUP_REPLICATION_MICRO_VERSION = '3.37' +INVALID_GROUP_REPLICATION = mv.get_prior_version(mv.GROUP_REPLICATION) @ddt.ddt @@ -134,7 +132,7 @@ class GroupsAPITestCase(test.TestCase): req = fakes.HTTPRequest.blank('/v3/%s/groups/%s' % (fake.PROJECT_ID, self.group1.id), - version=GROUP_MICRO_VERSION) + version=mv.GROUP_VOLUME) res_dict = self.controller.show(req, self.group1.id) self.assertEqual(1, len(res_dict)) @@ -149,7 +147,10 @@ class GroupsAPITestCase(test.TestCase): self.assertEqual([fake.VOLUME_TYPE_ID], 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 @mock.patch('cinder.objects.volume_type.VolumeTypeList.get_all_by_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) # 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. 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], res_dict['groups'][0]['volumes']) else: @@ -211,7 +212,7 @@ class GroupsAPITestCase(test.TestCase): # be contained in the response body. req = fakes.HTTPRequest.blank('/v3/%s/groups/%s?list_volume=True' % (fake.PROJECT_ID, self.group1.id), - version='3.25') + version=mv.GROUP_VOLUME_LIST) res_dict = self.controller.show(req, self.group1.id) self.assertEqual(1, len(res_dict)) self.assertEqual([fake.VOLUME_ID], @@ -221,16 +222,17 @@ class GroupsAPITestCase(test.TestCase): # should not be contained in the response body. req = fakes.HTTPRequest.blank('/v3/%s/groups/%s' % (fake.PROJECT_ID, self.group1.id), - version='3.25') + version=mv.GROUP_VOLUME_LIST) res_dict = self.controller.show(req, self.group1.id) self.assertEqual(1, len(res_dict)) self.assertIsNone(res_dict['group'].get('volumes', None)) # If the microversion < 3.25, "volumes" should not be contained in the # response body. - req = fakes.HTTPRequest.blank('/v3/%s/groups/%s?list_volume=True' % - (fake.PROJECT_ID, self.group1.id), - version='3.24') + req = fakes.HTTPRequest.blank( + '/v3/%s/groups/%s?list_volume=True' % + (fake.PROJECT_ID, self.group1.id), + version=mv.get_prior_version(mv.GROUP_VOLUME_LIST)) res_dict = self.controller.show(req, self.group1.id) self.assertEqual(1, len(res_dict)) self.assertIsNone(res_dict['group'].get('volumes', None)) @@ -239,11 +241,12 @@ class GroupsAPITestCase(test.TestCase): req = fakes.HTTPRequest.blank('/v3/%s/groups/%s' % (fake.PROJECT_ID, fake.WILL_NOT_BE_FOUND_ID), - version=GROUP_MICRO_VERSION) + version=mv.GROUP_VOLUME) self.assertRaises(exception.GroupNotFound, self.controller.show, 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') def test_group_list_with_general_filter(self, version, mock_update): url = '/v3/%s/groups' % fake.PROJECT_ID @@ -252,8 +255,8 @@ class GroupsAPITestCase(test.TestCase): use_admin_context=False) self.controller.index(req) - if version != '3.30': - support_like = True if version == '3.34' else False + if version != mv.get_prior_version(mv.RESOURCE_FILTER): + support_like = True if version == mv.LIKE_FILTER else False mock_update.assert_called_once_with(req.environ['cinder.context'], mock.ANY, 'group', support_like) @@ -273,7 +276,7 @@ class GroupsAPITestCase(test.TestCase): self.group3.save() req = fakes.HTTPRequest.blank('/v3/%s/groups' % fake.PROJECT_ID, - version=GROUP_MICRO_VERSION) + version=mv.GROUP_VOLUME) res_dict = self.controller.index(req) self.assertEqual(1, len(res_dict)) @@ -295,7 +298,7 @@ class GroupsAPITestCase(test.TestCase): url = '/v3/%s/groups?limit=1' % fake.PROJECT_ID if is_detail: 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: res_dict = self.controller.detail(req) @@ -320,7 +323,7 @@ class GroupsAPITestCase(test.TestCase): url = '/v3/%s/groups?offset=1' % fake.PROJECT_ID if is_detail: 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) self.assertEqual(1, len(res_dict)) @@ -337,7 +340,7 @@ class GroupsAPITestCase(test.TestCase): if is_detail: url = ('/v3/%s/groups/detail?offset=234523423455454' % fake.PROJECT_ID) - req = fakes.HTTPRequest.blank(url, version=GROUP_MICRO_VERSION) + req = fakes.HTTPRequest.blank(url, version=mv.GROUP_VOLUME) if is_detail: self.assertRaises(webob.exc.HTTPBadRequest, self.controller.detail, req) @@ -351,7 +354,7 @@ class GroupsAPITestCase(test.TestCase): if is_detail: url = ('/v3/%s/groups/detail?limit=2&offset=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: res_dict = self.controller.detail(req) @@ -377,7 +380,7 @@ class GroupsAPITestCase(test.TestCase): url = ('/v3/%s/groups/detail?' 'all_tenants=True&id=%s') % (fake.PROJECT_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) if is_detail: @@ -398,7 +401,7 @@ class GroupsAPITestCase(test.TestCase): if is_detail: url = ('/v3/%s/groups/detail?sort=id:asc' % 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, self.group3.id] expect_result.sort() @@ -438,7 +441,7 @@ class GroupsAPITestCase(test.TestCase): # self.group3.save() req = fakes.HTTPRequest.blank('/v3/%s/groups/detail' % fake.PROJECT_ID, - version=GROUP_MICRO_VERSION) + version=mv.GROUP_VOLUME) res_dict = self.controller.detail(req) self.assertEqual(1, len(res_dict)) @@ -476,7 +479,7 @@ class GroupsAPITestCase(test.TestCase): "description": "Group 1", }} 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) self.assertEqual(1, len(res_dict)) @@ -489,7 +492,7 @@ class GroupsAPITestCase(test.TestCase): def test_create_group_with_no_body(self): # omit body from the request 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, req, None) @@ -498,7 +501,7 @@ class GroupsAPITestCase(test.TestCase): self.group1.save() req = fakes.HTTPRequest.blank('/v3/%s/groups/%s/action' % (fake.PROJECT_ID, self.group1.id), - version=GROUP_MICRO_VERSION) + version=mv.GROUP_VOLUME) body = {"delete": {"delete-volumes": False}} res_dict = self.controller.delete_group( req, self.group1.id, body) @@ -513,7 +516,7 @@ class GroupsAPITestCase(test.TestCase): self.group1.save() req = fakes.HTTPRequest.blank('/v3/%s/groups/%s/action' % (fake.PROJECT_ID, self.group1.id), - version=GROUP_MICRO_VERSION) + version=mv.GROUP_VOLUME) body = {"delete": {"delete-volumes": False}} res_dict = self.controller.delete_group( req, self.group1.id, body) @@ -528,7 +531,7 @@ class GroupsAPITestCase(test.TestCase): req = fakes.HTTPRequest.blank('/v3/%s/groups/%s/action' % (fake.PROJECT_ID, fake.WILL_NOT_BE_FOUND_ID), - version=GROUP_MICRO_VERSION) + version=mv.GROUP_VOLUME) body = {"delete": {"delete-volumes": False}} self.assertRaises(exception.GroupNotFound, self.controller.delete_group, @@ -538,7 +541,7 @@ class GroupsAPITestCase(test.TestCase): req = fakes.HTTPRequest.blank('/v3/%s/groups/%s/action' % (fake.PROJECT_ID, self.group1.id), - version=GROUP_MICRO_VERSION) + version=mv.GROUP_VOLUME) body = {"delete": {"delete-volumes": False}} self.assertRaises(webob.exc.HTTPBadRequest, self.controller.delete_group, @@ -548,7 +551,7 @@ class GroupsAPITestCase(test.TestCase): req = fakes.HTTPRequest.blank('/v3/%s/groups/%s/action' % (fake.PROJECT_ID, self.group1.id), - version=GROUP_MICRO_VERSION) + version=mv.GROUP_VOLUME) body = {"delete": {"delete-volumes": True}} res_dict = self.controller.delete_group( req, self.group1.id, body) @@ -565,7 +568,7 @@ class GroupsAPITestCase(test.TestCase): req = fakes.HTTPRequest.blank('/v3/%s/groups/%s/action' % (fake.PROJECT_ID, self.group1.id), - version=GROUP_MICRO_VERSION) + version=mv.GROUP_VOLUME) body = {"delete": {"delete-volumes": True}} res_dict = self.controller.delete_group( req, self.group1.id, body) @@ -620,7 +623,7 @@ class GroupsAPITestCase(test.TestCase): "description": "Group 1", }} req = fakes.HTTPRequest.blank('/v3/%s/groups' % fake.PROJECT_ID, - version=GROUP_MICRO_VERSION) + version=mv.GROUP_VOLUME) ex = self.assertRaises(exception.GroupLimitExceeded, self.controller.create, req, body) @@ -631,7 +634,7 @@ class GroupsAPITestCase(test.TestCase): self.group1.save() req = fakes.HTTPRequest.blank('/v3/%s/groups/%s/action' % (fake.PROJECT_ID, self.group1.id), - version=GROUP_MICRO_VERSION) + version=mv.GROUP_VOLUME) body = {"invalid_request_element": {"delete-volumes": False}} self.assertRaises(webob.exc.HTTPBadRequest, self.controller.delete_group, @@ -642,7 +645,7 @@ class GroupsAPITestCase(test.TestCase): self.group1.save() req = fakes.HTTPRequest.blank('/v3/%s/groups/%s/action' % (fake.PROJECT_ID, self.group1.id), - version=GROUP_MICRO_VERSION) + version=mv.GROUP_VOLUME) body = {"delete": {"delete-volumes": "abcd"}} self.assertRaises(webob.exc.HTTPBadRequest, self.controller.delete_group, @@ -653,7 +656,7 @@ class GroupsAPITestCase(test.TestCase): self.group1.save() req = fakes.HTTPRequest.blank('/v3/%s/groups/%s/action' % (fake.PROJECT_ID, self.group1.id), - version=GROUP_MICRO_VERSION) + version=mv.GROUP_VOLUME) body = {"delete": {"delete-volumes": ""}} self.assertRaises(webob.exc.HTTPBadRequest, self.controller.delete_group, @@ -666,7 +669,7 @@ class GroupsAPITestCase(test.TestCase): req = fakes.HTTPRequest.blank('/v3/%s/groups/%s/action' % (fake.PROJECT_ID, self.group1.id), - version=GROUP_MICRO_VERSION) + version=mv.GROUP_VOLUME) body = {"delete": {"delete-volumes": True}} self.assertRaises(webob.exc.HTTPBadRequest, self.controller.delete_group, @@ -688,7 +691,7 @@ class GroupsAPITestCase(test.TestCase): vol = utils.create_volume(self.ctxt, group_id=self.group1.id) req = fakes.HTTPRequest.blank('/v3/%s/groups/%s/action' % (fake.PROJECT_ID, self.group1.id), - version=GROUP_MICRO_VERSION) + version=mv.GROUP_VOLUME) body = {"delete": {"delete-volumes": True}} res_dict = self.controller.delete_group( req, self.group1.id, body) @@ -707,7 +710,7 @@ class GroupsAPITestCase(test.TestCase): attach_status='attached') req = fakes.HTTPRequest.blank('/v3/%s/groups/%s/action' % (fake.PROJECT_ID, self.group1.id), - version=GROUP_MICRO_VERSION) + version=mv.GROUP_VOLUME) body = {"delete": {"delete-volumes": True}} self.assertRaises(webob.exc.HTTPBadRequest, self.controller.delete_group, @@ -722,7 +725,7 @@ class GroupsAPITestCase(test.TestCase): utils.create_snapshot(self.ctxt, vol.id) req = fakes.HTTPRequest.blank('/v3/%s/groups/%s/action' % (fake.PROJECT_ID, self.group1.id), - version=GROUP_MICRO_VERSION) + version=mv.GROUP_VOLUME) body = {"delete": {"delete-volumes": True}} self.assertRaises(webob.exc.HTTPBadRequest, self.controller.delete_group, @@ -739,7 +742,7 @@ class GroupsAPITestCase(test.TestCase): deleted=True) req = fakes.HTTPRequest.blank('/v3/%s/groups/%s/action' % (fake.PROJECT_ID, self.group1.id), - version=GROUP_MICRO_VERSION) + version=mv.GROUP_VOLUME) body = {"delete": {"delete-volumes": True}} res_dict = self.controller.delete_group( req, self.group1.id, body) @@ -758,7 +761,7 @@ class GroupsAPITestCase(test.TestCase): "description": "Group 1", }} 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, req, body) @@ -770,7 +773,7 @@ class GroupsAPITestCase(test.TestCase): "description": "Group 1", }} 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, req, body) @@ -820,7 +823,7 @@ class GroupsAPITestCase(test.TestCase): volume_type_id=volume_type_id) req = fakes.HTTPRequest.blank('/v3/%s/groups/%s/update' % (fake.PROJECT_ID, self.group1.id), - version=GROUP_MICRO_VERSION) + version=mv.GROUP_VOLUME) name = 'newgroup' description = 'New Group Description' add_volumes = add_volume.id + "," + add_volume2.id @@ -851,7 +854,7 @@ class GroupsAPITestCase(test.TestCase): self.group1.status = status req = fakes.HTTPRequest.blank('/v3/%s/groups/%s/update' % (fake.PROJECT_ID, self.group1.id), - version=GROUP_MICRO_VERSION) + version=mv.GROUP_VOLUME) body = {"group": {"name": "new name", "description": "new description", "add_volumes": None, @@ -870,7 +873,7 @@ class GroupsAPITestCase(test.TestCase): self.group1.save() req = fakes.HTTPRequest.blank('/v3/%s/groups/%s/update' % (fake.PROJECT_ID, self.group1.id), - version=GROUP_MICRO_VERSION) + version=mv.GROUP_VOLUME) body = {"group": {"name": None, "description": None, "add_volumes": "fake-volume-uuid", @@ -885,7 +888,7 @@ class GroupsAPITestCase(test.TestCase): self.group1.save() req = fakes.HTTPRequest.blank('/v3/%s/groups/%s/update' % (fake.PROJECT_ID, self.group1.id), - version=GROUP_MICRO_VERSION) + version=mv.GROUP_VOLUME) body = {"group": {"name": None, "description": "new description", "add_volumes": None, @@ -900,7 +903,7 @@ class GroupsAPITestCase(test.TestCase): self.group1.save() req = fakes.HTTPRequest.blank('/v3/%s/groups/%s/update' % (fake.PROJECT_ID, self.group1.id), - version=GROUP_MICRO_VERSION) + version=mv.GROUP_VOLUME) body = {"group": {"name": None, "description": None, "add_volumes": None, @@ -919,7 +922,7 @@ class GroupsAPITestCase(test.TestCase): status='wrong_status') req = fakes.HTTPRequest.blank('/v3/%s/groups/%s/update' % (fake.PROJECT_ID, self.group1.id), - version=GROUP_MICRO_VERSION) + version=mv.GROUP_VOLUME) add_volumes = add_volume.id body = {"group": {"name": "group1", "description": "", @@ -941,7 +944,7 @@ class GroupsAPITestCase(test.TestCase): volume_type_id=wrong_type) req = fakes.HTTPRequest.blank('/v3/%s/groups/%s/update' % (fake.PROJECT_ID, self.group1.id), - version=GROUP_MICRO_VERSION) + version=mv.GROUP_VOLUME) add_volumes = add_volume.id body = {"group": {"name": "group1", "description": "", @@ -962,7 +965,7 @@ class GroupsAPITestCase(test.TestCase): group_id=fake.GROUP2_ID) req = fakes.HTTPRequest.blank('/v3/%s/groups/%s/update' % (fake.PROJECT_ID, self.group1.id), - version=GROUP_MICRO_VERSION) + version=mv.GROUP_VOLUME) add_volumes = add_volume.id body = {"group": {"name": "group1", "description": "", @@ -984,7 +987,7 @@ class GroupsAPITestCase(test.TestCase): host=self.group1.host) req = fakes.HTTPRequest.blank('/v3/%s/groups/%s/update' % (fake.PROJECT_ID, self.group1.id), - version=GROUP_MICRO_VERSION) + version=mv.GROUP_VOLUME) body = {"group": {"name": "new name", "description": None, @@ -999,16 +1002,16 @@ class GroupsAPITestCase(test.TestCase): self.assertEqual(add_volume.status, vol.status) add_volume.destroy() - @ddt.data(('3.11', 'fake_group_001', + @ddt.data((mv.GROUP_TYPE, 'fake_group_001', fields.GroupStatus.AVAILABLE, exception.VersionNotFoundForAPIMethod), - ('3.19', 'fake_group_001', + (mv.GROUP_SNAPSHOT_RESET_STATUS, 'fake_group_001', fields.GroupStatus.AVAILABLE, exception.VersionNotFoundForAPIMethod), - ('3.20', 'fake_group_001', + (mv.GROUP_VOLUME_RESET_STATUS, 'fake_group_001', fields.GroupStatus.AVAILABLE, exception.GroupNotFound), - ('3.20', None, + (mv.GROUP_VOLUME_RESET_STATUS, None, 'invalid_test_status', webob.exc.HTTPBadRequest), ) @@ -1029,7 +1032,7 @@ class GroupsAPITestCase(test.TestCase): def test_reset_group_status(self): req = fakes.HTTPRequest.blank('/v3/%s/groups/%s/action' % (fake.PROJECT_ID, self.group2.id), - version='3.20') + version=mv.GROUP_VOLUME_RESET_STATUS) body = {"reset_status": { "status": fields.GroupStatus.AVAILABLE }} @@ -1068,7 +1071,7 @@ class GroupsAPITestCase(test.TestCase): "group_snapshot_id": group_snapshot.id}} req = fakes.HTTPRequest.blank('/v3/%s/groups/action' % fake.PROJECT_ID, - version=GROUP_FROM_SRC_MICRO_VERSION) + version=mv.GROUP_SNAPSHOTS) res_dict = self.controller.create_from_src(req, body) self.assertIn('id', res_dict['group']) @@ -1101,7 +1104,7 @@ class GroupsAPITestCase(test.TestCase): "source_group_id": source_grp.id}} req = fakes.HTTPRequest.blank('/v3/%s/groups/action' % fake.PROJECT_ID, - version=GROUP_FROM_SRC_MICRO_VERSION) + version=mv.GROUP_SNAPSHOTS) res_dict = self.controller.create_from_src(req, body) 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): req = fakes.HTTPRequest.blank('/v3/%s/groups/%s/action' % (fake.PROJECT_ID, self.group3.id), - version=GROUP_REPLICATION_MICRO_VERSION) + version=mv.GROUP_REPLICATION) self.group3.status = fields.GroupStatus.AVAILABLE self.group3.save() body = {"enable_replication": {}} @@ -1145,7 +1148,7 @@ class GroupsAPITestCase(test.TestCase): mock_rep_vol_type.return_value = is_vol_rep_type req = fakes.HTTPRequest.blank('/v3/%s/groups/%s/action' % (fake.PROJECT_ID, self.group3.id), - version=GROUP_REPLICATION_MICRO_VERSION) + version=mv.GROUP_REPLICATION) self.group3.status = fields.GroupStatus.AVAILABLE self.group3.save() body = {"enable_replication": {}} @@ -1161,7 +1164,7 @@ class GroupsAPITestCase(test.TestCase): mock_rep_vol_type): req = fakes.HTTPRequest.blank('/v3/%s/groups/%s/action' % (fake.PROJECT_ID, self.group3.id), - version=GROUP_REPLICATION_MICRO_VERSION) + version=mv.GROUP_REPLICATION) self.group3.status = fields.GroupStatus.AVAILABLE self.group3.save() body = {"enable_replication": {}} @@ -1173,13 +1176,13 @@ class GroupsAPITestCase(test.TestCase): return_value=True) @mock.patch('cinder.volume.utils.is_group_a_type', return_value=True) - @ddt.data((GROUP_REPLICATION_MICRO_VERSION, True, + @ddt.data((mv.GROUP_REPLICATION, True, fields.GroupStatus.CREATING, webob.exc.HTTPBadRequest), - (GROUP_REPLICATION_MICRO_VERSION, False, + (mv.GROUP_REPLICATION, False, fields.GroupStatus.AVAILABLE, exception.GroupNotFound), - (INVALID_GROUP_REPLICATION_MICRO_VERSION, True, + (INVALID_GROUP_REPLICATION, True, fields.GroupStatus.AVAILABLE, exception.VersionNotFoundForAPIMethod), ) @@ -1209,7 +1212,7 @@ class GroupsAPITestCase(test.TestCase): def test_disable_replication(self, mock_rep_grp_type, mock_rep_vol_type): req = fakes.HTTPRequest.blank('/v3/%s/groups/%s/action' % (fake.PROJECT_ID, self.group3.id), - version=GROUP_REPLICATION_MICRO_VERSION) + version=mv.GROUP_REPLICATION) self.group3.status = fields.GroupStatus.AVAILABLE self.group3.replication_status = fields.ReplicationStatus.ENABLED self.group3.save() @@ -1227,19 +1230,19 @@ class GroupsAPITestCase(test.TestCase): return_value=True) @mock.patch('cinder.volume.utils.is_group_a_type', return_value=True) - @ddt.data((GROUP_REPLICATION_MICRO_VERSION, True, + @ddt.data((mv.GROUP_REPLICATION, True, fields.GroupStatus.CREATING, fields.ReplicationStatus.ENABLED, webob.exc.HTTPBadRequest), - (GROUP_REPLICATION_MICRO_VERSION, True, + (mv.GROUP_REPLICATION, True, fields.GroupStatus.AVAILABLE, fields.ReplicationStatus.DISABLED, webob.exc.HTTPBadRequest), - (GROUP_REPLICATION_MICRO_VERSION, False, + (mv.GROUP_REPLICATION, False, fields.GroupStatus.AVAILABLE, fields.ReplicationStatus.DISABLED, exception.GroupNotFound), - (INVALID_GROUP_REPLICATION_MICRO_VERSION, True, + (INVALID_GROUP_REPLICATION, True, fields.GroupStatus.AVAILABLE, fields.ReplicationStatus.ENABLED, exception.VersionNotFoundForAPIMethod), @@ -1272,7 +1275,7 @@ class GroupsAPITestCase(test.TestCase): def test_failover_replication(self, mock_rep_grp_type, mock_rep_vol_type): req = fakes.HTTPRequest.blank('/v3/%s/groups/%s/action' % (fake.PROJECT_ID, self.group3.id), - version=GROUP_REPLICATION_MICRO_VERSION) + version=mv.GROUP_REPLICATION) self.group3.status = fields.GroupStatus.AVAILABLE self.group3.replication_status = fields.ReplicationStatus.ENABLED self.group3.save() @@ -1290,19 +1293,19 @@ class GroupsAPITestCase(test.TestCase): return_value=True) @mock.patch('cinder.volume.utils.is_group_a_type', return_value=True) - @ddt.data((GROUP_REPLICATION_MICRO_VERSION, True, + @ddt.data((mv.GROUP_REPLICATION, True, fields.GroupStatus.CREATING, fields.ReplicationStatus.ENABLED, webob.exc.HTTPBadRequest), - (GROUP_REPLICATION_MICRO_VERSION, True, + (mv.GROUP_REPLICATION, True, fields.GroupStatus.AVAILABLE, fields.ReplicationStatus.DISABLED, webob.exc.HTTPBadRequest), - (GROUP_REPLICATION_MICRO_VERSION, False, + (mv.GROUP_REPLICATION, False, fields.GroupStatus.AVAILABLE, fields.ReplicationStatus.DISABLED, exception.GroupNotFound), - (INVALID_GROUP_REPLICATION_MICRO_VERSION, True, + (INVALID_GROUP_REPLICATION, True, fields.GroupStatus.AVAILABLE, fields.ReplicationStatus.ENABLED, exception.VersionNotFoundForAPIMethod), @@ -1337,7 +1340,7 @@ class GroupsAPITestCase(test.TestCase): mock_rep_grp_type, mock_rep_vol_type): req = fakes.HTTPRequest.blank('/v3/%s/groups/%s/action' % (fake.PROJECT_ID, self.group3.id), - version=GROUP_REPLICATION_MICRO_VERSION) + version=mv.GROUP_REPLICATION) targets = { 'replication_targets': [ {'backend_id': 'lvm_backend_1'} diff --git a/cinder/tests/unit/api/v3/test_limits.py b/cinder/tests/unit/api/v3/test_limits.py index 14aa726b57c..7e9694fe7f0 100644 --- a/cinder/tests/unit/api/v3/test_limits.py +++ b/cinder/tests/unit/api/v3/test_limits.py @@ -16,12 +16,16 @@ import ddt import mock +from cinder.api import microversions as mv from cinder.api.openstack import api_version_request as api_version from cinder.api.v3 import limits from cinder import test from cinder.tests.unit.api import fakes 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 class LimitsControllerTest(test.TestCase): @@ -29,7 +33,8 @@ class LimitsControllerTest(test.TestCase): super(LimitsControllerTest, self).setUp() 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') def test_get_limit_with_project_id(self, ver_project, mock_get_quotas): max_ver, has_project = ver_project @@ -48,9 +53,9 @@ class LimitsControllerTest(test.TestCase): mock_get_quotas.side_effect = get_project_quotas resp_dict = self.controller.index(req) - # if admin, only 3.39 and req contains project_id filter, cinder - # returns the specified project's quota. - if max_ver == '3.39' and has_project: + # if admin, only LIMITS_FILTER and req contains project_id filter, + # cinder returns the specified project's quota. + if max_ver == LIMITS_FILTER and has_project: self.assertEqual( 5, resp_dict['limits']['absolute']['maxTotalVolumeGigabytes']) else: diff --git a/cinder/tests/unit/api/v3/test_messages.py b/cinder/tests/unit/api/v3/test_messages.py index 06ad44b9b66..bb6bce435c0 100644 --- a/cinder/tests/unit/api/v3/test_messages.py +++ b/cinder/tests/unit/api/v3/test_messages.py @@ -15,6 +15,7 @@ import mock from six.moves import http_client from cinder.api import extensions +from cinder.api import microversions as mv from cinder.api.v3 import messages from cinder import context from cinder import exception @@ -66,8 +67,7 @@ class MessageApiTest(test.TestCase): self.mock_object(message_api.API, 'get', v3_fakes.fake_message_get) req = fakes.HTTPRequest.blank( - '/v3/messages/%s' % fakes.FAKE_UUID, - version=messages.MESSAGES_BASE_MICRO_VERSION) + '/v3/messages/%s' % fakes.FAKE_UUID, version=mv.MESSAGES) req.environ['cinder.context'] = self.ctxt res_dict = self.controller.show(req, fakes.FAKE_UUID) @@ -81,8 +81,7 @@ class MessageApiTest(test.TestCase): message_id=fakes.FAKE_UUID)) req = fakes.HTTPRequest.blank( - '/v3/messages/%s' % fakes.FAKE_UUID, - version=messages.MESSAGES_BASE_MICRO_VERSION) + '/v3/messages/%s' % fakes.FAKE_UUID, version=mv.MESSAGES) req.environ['cinder.context'] = self.ctxt 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) req = fakes.HTTPRequest.blank('/v3/messages/%s' % fakes.FAKE_UUID, - version='3.0') + version=mv.BASE_VERSION) req.environ['cinder.context'] = self.ctxt self.assertRaises(exception.VersionNotFoundForAPIMethod, @@ -103,8 +102,7 @@ class MessageApiTest(test.TestCase): self.mock_object(message_api.API, 'delete') req = fakes.HTTPRequest.blank( - '/v3/messages/%s' % fakes.FAKE_UUID, - version=messages.MESSAGES_BASE_MICRO_VERSION) + '/v3/messages/%s' % fakes.FAKE_UUID, version=mv.MESSAGES) req.environ['cinder.context'] = self.ctxt resp = self.controller.delete(req, fakes.FAKE_UUID) @@ -118,13 +116,13 @@ class MessageApiTest(test.TestCase): message_id=fakes.FAKE_UUID)) req = fakes.HTTPRequest.blank( - '/v3/messages/%s' % fakes.FAKE_UUID, - version=messages.MESSAGES_BASE_MICRO_VERSION) + '/v3/messages/%s' % fakes.FAKE_UUID, version=mv.MESSAGES) self.assertRaises(exception.MessageNotFound, self.controller.delete, 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') def test_message_list_with_general_filter(self, version, mock_update): url = '/v3/%s/messages' % fakes.FAKE_UUID @@ -133,8 +131,8 @@ class MessageApiTest(test.TestCase): use_admin_context=False) self.controller.index(req) - if version != '3.30': - support_like = True if version == '3.34' else False + if version != mv.get_prior_version(mv.RESOURCE_FILTER): + support_like = True if version == mv.LIKE_FILTER else False mock_update.assert_called_once_with(req.environ['cinder.context'], mock.ANY, 'message', support_like) @@ -143,8 +141,7 @@ class MessageApiTest(test.TestCase): self.mock_object(message_api.API, 'get_all', return_value=[v3_fakes.fake_message(fakes.FAKE_UUID)]) req = fakes.HTTPRequest.blank( - '/v3/messages/%s' % fakes.FAKE_UUID, - version=messages.MESSAGES_BASE_MICRO_VERSION) + '/v3/messages/%s' % fakes.FAKE_UUID, version=mv.MESSAGES) req.environ['cinder.context'] = self.ctxt res_dict = self.controller.index(req) diff --git a/cinder/tests/unit/api/v3/test_resource_filters.py b/cinder/tests/unit/api/v3/test_resource_filters.py index 917f1f996c5..f348cbbcbc4 100644 --- a/cinder/tests/unit/api/v3/test_resource_filters.py +++ b/cinder/tests/unit/api/v3/test_resource_filters.py @@ -18,13 +18,12 @@ import ddt import mock import six +from cinder.api import microversions as mv from cinder.api.v3 import resource_filters as v3_filters from cinder import test from cinder.tests.unit.api import fakes from cinder.tests.unit import fake_constants as fake -FILTERS_MICRO_VERSION = '3.33' - @ddt.ddt class ResourceFiltersAPITestCase(test.TestCase): @@ -53,7 +52,7 @@ class ResourceFiltersAPITestCase(test.TestCase): if resource is not None: request_url += '?resource=%s' % resource req = fakes.HTTPRequest.blank(request_url, - version=FILTERS_MICRO_VERSION) + version=mv.RESOURCE_FILTER_CONFIG) with mock.patch('cinder.api.common._FILTERS_COLLECTION', filters): result = self.controller.index(req) diff --git a/cinder/tests/unit/api/v3/test_snapshot_manage.py b/cinder/tests/unit/api/v3/test_snapshot_manage.py index 21f3fc15622..b333cd54052 100644 --- a/cinder/tests/unit/api/v3/test_snapshot_manage.py +++ b/cinder/tests/unit/api/v3/test_snapshot_manage.py @@ -20,6 +20,7 @@ from six.moves import http_client from six.moves.urllib.parse import urlencode import webob +from cinder.api import microversions as mv from cinder.api.v3 import router as router_v3 from cinder import context from cinder import objects @@ -51,13 +52,13 @@ class SnapshotManageTest(test.TestCase): fake.PROJECT_ID, 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.""" req = webob.Request.blank('/v3/%s/manageable_snapshots' % fake.PROJECT_ID) req.method = 'POST' + req.headers = mv.get_mv_header(version) req.headers['Content-Type'] = 'application/json' - req.headers['OpenStack-API-Version'] = 'volume ' + version req.environ['cinder.context'] = self._admin_ctxt req.body = jsonutils.dump_as_bytes(body) res = req.get_response(app()) @@ -84,10 +85,12 @@ class SnapshotManageTest(test.TestCase): def test_manage_snapshot_previous_version(self): 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) - 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.""" params = {'host': host} if host else {} params.update(kwargs) @@ -101,8 +104,8 @@ class SnapshotManageTest(test.TestCase): req = webob.Request.blank('/v3/%s/manageable_snapshots%s%s' % (fake.PROJECT_ID, detail, query_string)) req.method = 'GET' + req.headers = mv.get_mv_header(version) req.headers['Content-Type'] = 'application/json' - req.headers['OpenStack-API-Version'] = 'volume ' + version req.environ['cinder.context'] = self._admin_ctxt res = req.get_response(app()) return res @@ -120,7 +123,9 @@ class SnapshotManageTest(test.TestCase): self.assertEqual(http_client.OK, res.status_int) 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) @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) 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) @ddt.data((True, True, 'detail_list'), (True, False, 'summary_list'), @@ -150,12 +157,12 @@ class SnapshotManageTest(test.TestCase): if clustered: host = None cluster_name = 'mycluster' - version = '3.17' + version = mv.MANAGE_EXISTING_CLUSTER kwargs = {'cluster': cluster_name} else: host = 'fakehost' cluster_name = None - version = '3.8' + version = mv.MANAGE_EXISTING_LIST kwargs = {} service = objects.Service(disabled=False, host='fakehost', cluster_name=cluster_name) @@ -183,12 +190,13 @@ class SnapshotManageTest(test.TestCase): mock.ANY, None, host=host, binary='cinder-volume', 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): res = self._get_resp_get(None, True, False, version=version) self.assertEqual(http_client.BAD_REQUEST, res.status_int) 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') self.assertEqual(http_client.BAD_REQUEST, res.status_int) diff --git a/cinder/tests/unit/api/v3/test_snapshots.py b/cinder/tests/unit/api/v3/test_snapshots.py index b15bc70c1b0..3a831897858 100644 --- a/cinder/tests/unit/api/v3/test_snapshots.py +++ b/cinder/tests/unit/api/v3/test_snapshots.py @@ -17,7 +17,7 @@ import ddt 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 import context from cinder import exception @@ -54,9 +54,8 @@ def create_snapshot_query_with_metadata(metadata_query_string, """Helper to create metadata querystring with microversion""" req = fakes.HTTPRequest.blank('/v3/snapshots?metadata=' + metadata_query_string) - req.headers["OpenStack-API-Version"] = "volume " + api_microversion - req.api_version_request = api_version.APIVersionRequest( - api_microversion) + req.headers = mv.get_mv_header(api_microversion) + req.api_version_request = mv.get_api_version(api_microversion) return req @@ -69,7 +68,9 @@ class SnapshotApiTest(test.TestCase): self.controller = snapshots.SnapshotsController() 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.objects.Volume.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 volume_get_by_id.return_value = fake_volume_obj 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) self.assertIn('snapshot', resp_dict) self.assertEqual(UUID, resp_dict['snapshot']['id']) 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.assertNotIn('user_id', resp_dict['snapshot']) - elif max_ver == '3.13': + else: self.assertNotIn('group_snapshot_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): snapshot_id = INVALID_UUID @@ -144,7 +145,7 @@ class SnapshotApiTest(test.TestCase): # Generic filtering is introduced since '3,31' and we add # 'availability_zone' support by using generic filtering. req = fakes.HTTPRequest.blank(url, use_admin_context=is_admin_user, - version='3.31') + version=mv.RESOURCE_FILTER) res_dict = self.controller.detail(req) self.assertEqual(1, len(res_dict['snapshots'])) @@ -154,12 +155,13 @@ class SnapshotApiTest(test.TestCase): self._create_snapshot(name='test1') self._create_snapshot(name='test2') - req = fakes.HTTPRequest.blank('/v3/snapshots?sort_key=name', - version='3.29') + req = fakes.HTTPRequest.blank( + '/v3/snapshots?sort_key=name', + version=mv.get_prior_version(mv.SNAPSHOT_SORT)) self.assertRaises(exception.InvalidInput, self.controller.detail, req) req = fakes.HTTPRequest.blank('/v3/snapshots?sort_key=name', - version='3.30') + version=mv.SNAPSHOT_SORT) res_dict = self.controller.detail(req) self.assertEqual(2, len(res_dict['snapshots'])) self.assertEqual('test2', res_dict['snapshots'][0]['name']) @@ -171,7 +173,8 @@ class SnapshotApiTest(test.TestCase): self._create_snapshot(metadata=metadata) # 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 res_dict = self.controller.detail(req) @@ -184,7 +187,8 @@ class SnapshotApiTest(test.TestCase): 'metadata']) # 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 res_dict = self.controller.detail(req) @@ -199,7 +203,8 @@ class SnapshotApiTest(test.TestCase): # Create request with metadata filter key1: value1, key11: value11 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 res_dict = self.controller.detail(req) @@ -212,7 +217,8 @@ class SnapshotApiTest(test.TestCase): 'snapshots'][0]['metadata']) # 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 res_dict = self.controller.detail(req) @@ -224,7 +230,19 @@ class SnapshotApiTest(test.TestCase): self.assertDictEqual({"key1": "val1", "key11": "val11"}, res_dict[ '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') def test_snapshot_list_with_general_filter(self, version, mock_update): url = '/v3/%s/snapshots' % fake.PROJECT_ID @@ -233,8 +251,8 @@ class SnapshotApiTest(test.TestCase): use_admin_context=False) self.controller.index(req) - if version != '3.30': - support_like = True if version == '3.34' else False + if version != mv.get_prior_version(mv.RESOURCE_FILTER): + support_like = True if version == mv.LIKE_FILTER else False mock_update.assert_called_once_with(req.environ['cinder.context'], mock.ANY, 'snapshot', support_like) @@ -245,7 +263,9 @@ class SnapshotApiTest(test.TestCase): self._create_snapshot(metadata=metadata) # 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 res_dict = self.controller.detail(req) diff --git a/cinder/tests/unit/api/v3/test_volume_manage.py b/cinder/tests/unit/api/v3/test_volume_manage.py index 22c82342bd9..96406183767 100644 --- a/cinder/tests/unit/api/v3/test_volume_manage.py +++ b/cinder/tests/unit/api/v3/test_volume_manage.py @@ -20,6 +20,7 @@ from six.moves import http_client from six.moves.urllib.parse import urlencode import webob +from cinder.api import microversions as mv from cinder.api.v3 import router as router_v3 from cinder import context from cinder import objects @@ -56,13 +57,13 @@ class VolumeManageTest(test.TestCase): fake.PROJECT_ID, 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.""" req = webob.Request.blank('/v3/%s/manageable_volumes' % fake.PROJECT_ID) req.method = 'POST' + req.headers = mv.get_mv_header(version) req.headers['Content-Type'] = 'application/json' - req.headers['OpenStack-API-Version'] = 'volume ' + version req.environ['cinder.context'] = self._admin_ctxt req.body = jsonutils.dump_as_bytes(body) res = req.get_response(app()) @@ -88,7 +89,8 @@ class VolumeManageTest(test.TestCase): res = self._get_resp_post(body) 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.""" params = {'host': host} if host else {} params.update(kwargs) @@ -103,8 +105,8 @@ class VolumeManageTest(test.TestCase): req = webob.Request.blank('/v3/%s/manageable_volumes%s%s' % (fake.PROJECT_ID, detail, query_string)) req.method = 'GET' + req.headers = mv.get_mv_header(version) req.headers['Content-Type'] = 'application/json' - req.headers['OpenStack-API-Version'] = 'volume ' + version req.environ['cinder.context'] = self._admin_ctxt res = req.get_response(app()) return res @@ -122,7 +124,9 @@ class VolumeManageTest(test.TestCase): self.assertEqual(http_client.OK, res.status_int) 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) @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) 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) @ddt.data((True, True, 'detail_list'), (True, False, 'summary_list'), @@ -152,12 +158,12 @@ class VolumeManageTest(test.TestCase): if clustered: host = None cluster_name = 'mycluster' - version = '3.17' + version = mv.MANAGE_EXISTING_CLUSTER kwargs = {'cluster': cluster_name} else: host = 'fakehost' cluster_name = None - version = '3.8' + version = mv.MANAGE_EXISTING_LIST kwargs = {} service = objects.Service(disabled=False, host='fakehost', cluster_name=cluster_name) @@ -185,12 +191,13 @@ class VolumeManageTest(test.TestCase): mock.ANY, None, host=host, binary='cinder-volume', 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): res = self._get_resp_get(None, True, False, version=version) self.assertEqual(http_client.BAD_REQUEST, res.status_int) 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') self.assertEqual(http_client.BAD_REQUEST, res.status_int) diff --git a/cinder/tests/unit/api/v3/test_volume_metadata.py b/cinder/tests/unit/api/v3/test_volume_metadata.py index a1ac313f962..1ba61557f3b 100644 --- a/cinder/tests/unit/api/v3/test_volume_metadata.py +++ b/cinder/tests/unit/api/v3/test_volume_metadata.py @@ -18,6 +18,7 @@ from oslo_serialization import jsonutils import webob from cinder.api import extensions +from cinder.api import microversions as mv from cinder.api.v3 import volume_metadata from cinder.api.v3 import volumes from cinder import db @@ -150,7 +151,7 @@ class VolumeMetaDataTest(test.TestCase): self.volume_controller.create(req, body) 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) expected = { @@ -166,14 +167,14 @@ class VolumeMetaDataTest(test.TestCase): def test_index_nonexistent_volume(self): self.mock_object(db, 'volume_metadata_get', 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.controller.index, req, self.url) def test_index_no_data(self): self.mock_object(db, 'volume_metadata_get', 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) expected = {'metadata': {}} result = jsonutils.loads(data.body) @@ -182,7 +183,7 @@ class VolumeMetaDataTest(test.TestCase): def test_validate_etag_true(self): self.mock_object(db, 'volume_metadata_get', 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.if_match.etags = ['d5103bf7b26ff0310200d110da3ed186'] 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_context = mock.Mock() 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.content_type = "application/json" expected = { @@ -217,7 +218,7 @@ class VolumeMetaDataTest(test.TestCase): fake_volume = {'id': self.req_id, 'status': 'available'} fake_context = mock.Mock() 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' body = {"meta": {"key1": "value1"}} req.body = jsonutils.dump_as_bytes(body) @@ -235,7 +236,7 @@ class VolumeMetaDataTest(test.TestCase): def test_create_metadata_keys_value_none(self): self.mock_object(db, 'volume_metadata_update', 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.headers["content-type"] = "application/json" body = {"meta": {"key": None}} @@ -245,7 +246,7 @@ class VolumeMetaDataTest(test.TestCase): def test_update_items_value_none(self): self.mock_object(db, 'volume_metadata_update', 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' body = {"metadata": {"key": None}} req.body = jsonutils.dump_as_bytes(body) diff --git a/cinder/tests/unit/api/v3/test_volumes.py b/cinder/tests/unit/api/v3/test_volumes.py index 7eff927b021..d644c6ce9db 100644 --- a/cinder/tests/unit/api/v3/test_volumes.py +++ b/cinder/tests/unit/api/v3/test_volumes.py @@ -19,7 +19,7 @@ import mock import webob 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.v3 import volumes 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 vol_get -version_header_name = 'OpenStack-API-Version' - DEFAULT_AZ = "zone1:host1" -REVERT_TO_SNAPSHOT_VERSION = '3.40' @ddt.ddt @@ -61,7 +58,7 @@ class VolumeApiTest(test.TestCase): req = fakes.HTTPRequest.blank('/v3/volumes?bootable=True') req.method = 'GET' 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 self.override_config('query_volume_filters', 'bootable') @@ -77,9 +74,10 @@ class VolumeApiTest(test.TestCase): req = fakes.HTTPRequest.blank('/v3/volumes?bootable=True') req.method = 'GET' 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.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.controller.index(req) @@ -119,8 +117,9 @@ class VolumeApiTest(test.TestCase): vols = self._create_volume_with_glance_metadata() req = fakes.HTTPRequest.blank("/v3/volumes?glance_metadata=" "{'image_name': 'imageTestOne'}") - req.headers["OpenStack-API-Version"] = "volume 3.4" - req.api_version_request = api_version.APIVersionRequest('3.4') + req.headers = mv.get_mv_header(mv.VOLUME_LIST_GLANCE_METADATA) + req.api_version_request = mv.get_api_version( + mv.VOLUME_LIST_GLANCE_METADATA) req.environ['cinder.context'] = self.ctxt res_dict = self.controller.index(req) volumes = res_dict['volumes'] @@ -131,8 +130,8 @@ class VolumeApiTest(test.TestCase): self._create_volume_with_glance_metadata() req = fakes.HTTPRequest.blank("/v3/volumes?glance_metadata=" "{'image_name': 'imageTestOne'}") - req.headers["OpenStack-API-Version"] = "volume 3.0" - req.api_version_request = api_version.APIVersionRequest('3.0') + req.headers = mv.get_mv_header(mv.BASE_VERSION) + req.api_version_request = mv.get_api_version(mv.BASE_VERSION) req.environ['cinder.context'] = self.ctxt res_dict = self.controller.index(req) volumes = res_dict['volumes'] @@ -142,8 +141,8 @@ class VolumeApiTest(test.TestCase): vols = self._create_volume_with_group() req = fakes.HTTPRequest.blank(("/v3/volumes?group_id=%s") % fake.GROUP_ID) - req.headers["OpenStack-API-Version"] = "volume 3.10" - req.api_version_request = api_version.APIVersionRequest('3.10') + req.headers = mv.get_mv_header(mv.VOLUME_LIST_GROUP) + req.api_version_request = mv.get_api_version(mv.VOLUME_LIST_GROUP) req.environ['cinder.context'] = self.ctxt res_dict = self.controller.index(req) volumes = res_dict['volumes'] @@ -154,26 +153,29 @@ class VolumeApiTest(test.TestCase): self._create_volume_with_group() req = fakes.HTTPRequest.blank(("/v3/volumes?group_id=%s") % fake.GROUP_ID) - req.headers["OpenStack-API-Version"] = "volume 3.9" - req.api_version_request = api_version.APIVersionRequest('3.9') + req.headers = mv.get_mv_header(mv.BACKUP_UPDATE) + req.api_version_request = mv.get_api_version(mv.BACKUP_UPDATE) req.environ['cinder.context'] = self.ctxt res_dict = self.controller.index(req) volumes = res_dict['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): req_url = '/v3/volumes/summary' if all_tenant: req_url += '?all_tenants=True' req = fakes.HTTPRequest.blank(req_url, use_admin_context=is_admin) - req.headers = {'OpenStack-API-Version': 'volume ' + version} - req.api_version_request = api_version.APIVersionRequest(version) + req.headers = mv.get_mv_header(version) + req.api_version_request = mv.get_api_version(version) return req def test_volumes_summary_in_unsupport_version(self): """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.controller.summary, req) @@ -196,11 +198,12 @@ class VolumeApiTest(test.TestCase): self.assertEqual(expected, res_dict) @ddt.data( - ('3.35', {'volume-summary': {'total_size': 0.0, - 'total_count': 0}}), - ('3.36', {'volume-summary': {'total_size': 0.0, - 'total_count': 0, - 'metadata': {}}})) + (mv.get_prior_version(mv.VOLUME_SUMMARY_METADATA), + {'volume-summary': {'total_size': 0.0, + 'total_count': 0}}), + (mv.VOLUME_SUMMARY_METADATA, {'volume-summary': {'total_size': 0.0, + 'total_count': 0, + 'metadata': {}}})) @ddt.unpack def test_volume_summary_empty(self, summary_api_version, expect_result): req = self._fake_volumes_summary_request(version=summary_api_version) @@ -208,13 +211,15 @@ class VolumeApiTest(test.TestCase): self.assertEqual(expect_result, res_dict) @ddt.data( - ('3.35', {'volume-summary': {'total_size': 2, - 'total_count': 2}}), - ('3.36', {'volume-summary': {'total_size': 2, - 'total_count': 2, - 'metadata': { - 'name': ['test_name1', 'test_name2'], - 'age': ['test_age']}}})) + (mv.get_prior_version(mv.VOLUME_SUMMARY_METADATA), + {'volume-summary': {'total_size': 2, + 'total_count': 2}}), + (mv.VOLUME_SUMMARY_METADATA, + {'volume-summary': {'total_size': 2, + 'total_count': 2, + 'metadata': { + 'name': ['test_name1', 'test_name2'], + 'age': ['test_age']}}})) @ddt.unpack def test_volume_summary_return_metadata(self, summary_api_version, expect_result): @@ -230,13 +235,15 @@ class VolumeApiTest(test.TestCase): self.assertEqual(expect_result, res_dict) @ddt.data( - ('3.35', {'volume-summary': {'total_size': 2, - 'total_count': 2}}), - ('3.36', {'volume-summary': {'total_size': 2, - 'total_count': 2, - 'metadata': { - 'name': ['test_name1', 'test_name2'], - 'age': ['test_age']}}})) + (mv.get_prior_version(mv.VOLUME_SUMMARY_METADATA), + {'volume-summary': {'total_size': 2, + 'total_count': 2}}), + (mv.VOLUME_SUMMARY_METADATA, + {'volume-summary': {'total_size': 2, + 'total_count': 2, + 'metadata': { + 'name': ['test_name1', 'test_name2'], + 'age': ['test_age']}}})) @ddt.unpack def test_volume_summary_return_metadata_all_tenant( self, summary_api_version, expect_result): @@ -334,8 +341,9 @@ class VolumeApiTest(test.TestCase): if with_migration_status: volume['volume']['migration_status'] = None - # Remove group_id if max version is less than 3.13. - if req_version and req_version.matches(None, "3.12"): + # Remove group_id if max version is less than GROUP_VOLUME. + if req_version and req_version.matches( + None, mv.get_prior_version(mv.GROUP_VOLUME)): volume['volume'].pop('group_id') return volume @@ -356,13 +364,14 @@ class VolumeApiTest(test.TestCase): 'group': test_group, } - # Remove group_id if max version is less than 3.13. - if req_version and req_version.matches(None, "3.12"): + # Remove group_id if max version is less than GROUP_VOLUME. + if req_version and req_version.matches( + None, mv.get_prior_version(mv.GROUP_VOLUME)): volume.pop('group') return volume - @ddt.data('3.13', '3.12') + @ddt.data(mv.GROUP_VOLUME, mv.get_prior_version(mv.GROUP_VOLUME)) @mock.patch( 'cinder.api.openstack.wsgi.Controller.validate_name_and_description') def test_volume_create(self, max_ver, mock_validate): @@ -375,14 +384,14 @@ class VolumeApiTest(test.TestCase): vol = self._vol_in_request_body() body = {"volume": vol} 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) ex = self._expected_vol_from_controller( req_version=req.api_version_request) self.assertEqual(ex, res_dict) 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(db.sqlalchemy.api, '_volume_type_get_full', autospec=True) @@ -405,7 +414,7 @@ class VolumeApiTest(test.TestCase): group_id=fake.GROUP_ID) body = {"volume": vol} 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) ex = self._expected_vol_from_controller( snapshot_id=snapshot_id, @@ -450,23 +459,26 @@ class VolumeApiTest(test.TestCase): self.assertRaises(webob.exc.HTTPBadRequest, self.controller.create, 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(utils, 'add_visible_admin_metadata', mock.Mock()) @mock.patch('cinder.api.common.reject_invalid_filters') def test_list_volume_with_general_filter(self, version, mock_update): req = fakes.HTTPRequest.blank('/v3/volumes', version=version) self.controller.index(req) - if version != '3.30': - support_like = True if version == '3.34' else False + if version >= mv.RESOURCE_FILTER: + support_like = True if version == mv.LIKE_FILTER else False mock_update.assert_called_once_with(req.environ['cinder.context'], mock.ANY, 'volume', support_like) - @ddt.data({'admin': True, 'version': '3.21'}, - {'admin': False, 'version': '3.21'}, - {'admin': True, 'version': '3.20'}, - {'admin': False, 'version': '3.20'}) + @ddt.data({'admin': True, 'version': mv.VOLUME_DETAIL_PROVIDER_ID}, + {'admin': False, 'version': mv.VOLUME_DETAIL_PROVIDER_ID}, + {'admin': True, + 'version': mv.get_prior_version(mv.VOLUME_DETAIL_PROVIDER_ID)}, + {'admin': False, + 'version': mv.get_prior_version(mv.VOLUME_DETAIL_PROVIDER_ID)}) @ddt.unpack def test_volume_show_provider_id(self, admin, version): 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) req_version = req.api_version_request # provider_id is in view if min version is greater than or equal to - # 3.21 for admin. - if req_version.matches("3.21", None) and admin: + # VOLUME_DETAIL_PROVIDER_ID for admin. + if req_version.matches(mv.VOLUME_DETAIL_PROVIDER_ID, None) and admin: self.assertIn('provider_id', res_dict['volume']) else: self.assertNotIn('provider_id', res_dict['volume']) @@ -516,10 +528,9 @@ class VolumeApiTest(test.TestCase): mock_latest.side_effect = exception.VolumeSnapshotNotFound(volume_id= 'fake_id') req = fakes.HTTPRequest.blank('/v3/volumes/fake_id/revert') - req.headers = {'OpenStack-API-Version': - 'volume %s' % REVERT_TO_SNAPSHOT_VERSION} - req.api_version_request = api_version.APIVersionRequest( - REVERT_TO_SNAPSHOT_VERSION) + req.headers = mv.get_mv_header(mv.VOLUME_REVERT) + req.api_version_request = mv.get_api_version( + mv.VOLUME_REVERT) self.assertRaises(webob.exc.HTTPBadRequest, self.controller.revert, req, 'fake_id', {'revert': {'snapshot_id': @@ -534,10 +545,9 @@ class VolumeApiTest(test.TestCase): fake_snapshot = self._fake_create_snapshot(fake.UUID1) mock_latest.return_value = fake_snapshot req = fakes.HTTPRequest.blank('/v3/volumes/fake_id/revert') - req.headers = {'OpenStack-API-Version': - 'volume %s' % REVERT_TO_SNAPSHOT_VERSION} - req.api_version_request = api_version.APIVersionRequest( - REVERT_TO_SNAPSHOT_VERSION) + req.headers = mv.get_mv_header(mv.VOLUME_REVERT) + req.api_version_request = mv.get_api_version( + mv.VOLUME_REVERT) self.assertRaises(webob.exc.HTTPBadRequest, self.controller.revert, req, 'fake_id', {'revert': {'snapshot_id': @@ -557,10 +567,9 @@ class VolumeApiTest(test.TestCase): mock_latest.return_value = fake_snapshot req = fakes.HTTPRequest.blank('/v3/volumes/%s/revert' % fake_volume['id']) - req.headers = {'OpenStack-API-Version': - 'volume %s' % REVERT_TO_SNAPSHOT_VERSION} - req.api_version_request = api_version.APIVersionRequest( - REVERT_TO_SNAPSHOT_VERSION) + req.headers = mv.get_mv_header(mv.VOLUME_REVERT) + req.api_version_request = mv.get_api_version( + mv.VOLUME_REVERT) # update volume's status failed mock_update.side_effect = [False, True] diff --git a/cinder/tests/unit/api/v3/test_workers.py b/cinder/tests/unit/api/v3/test_workers.py index 9fe9e34aa9c..ea154f24bd6 100644 --- a/cinder/tests/unit/api/v3/test_workers.py +++ b/cinder/tests/unit/api/v3/test_workers.py @@ -19,6 +19,7 @@ from oslo_serialization import jsonutils from six.moves import http_client import webob +from cinder.api import microversions as mv from cinder.api.v3 import router as router_v3 from cinder.api.v3 import workers from cinder import context @@ -61,7 +62,7 @@ class WorkersTestCase(test.TestCase): overwrite=False) 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.""" req = webob.Request.blank('/v3/%s/workers/cleanup' % fake.PROJECT_ID) req.method = 'POST' @@ -74,7 +75,7 @@ class WorkersTestCase(test.TestCase): @mock.patch('cinder.scheduler.rpcapi.SchedulerAPI.work_cleanup') 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) rpc_mock.assert_not_called() diff --git a/cinder/tests/unit/message/test_api.py b/cinder/tests/unit/message/test_api.py index 1233a3328c7..e9cee6533fd 100644 --- a/cinder/tests/unit/message/test_api.py +++ b/cinder/tests/unit/message/test_api.py @@ -16,6 +16,7 @@ from oslo_config import cfg from oslo_utils import timeutils 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.v3 import messages from cinder import context @@ -128,8 +129,8 @@ class MessageApiTest(test.TestCase): req = fakes.HTTPRequest.blank(url) req.method = 'GET' req.content_type = 'application/json' - req.headers = {version_header_name: 'volume 3.5'} - req.api_version_request = api_version.APIVersionRequest('3.30') + req.headers = mv.get_mv_header(mv.MESSAGES_PAGINATION) + req.api_version_request = mv.get_api_version(mv.RESOURCE_FILTER) req.environ['cinder.context'].is_admin = True res = self.controller.index(req) @@ -139,8 +140,8 @@ class MessageApiTest(test.TestCase): req = fakes.HTTPRequest.blank(url) req.method = 'GET' req.content_type = 'application/json' - req.headers = {version_header_name: 'volume 3.5'} - req.api_version_request = api_version.APIVersionRequest('3.30') + req.headers = mv.get_mv_header(mv.MESSAGES_PAGINATION) + req.api_version_request = mv.get_api_version(mv.RESOURCE_FILTER) req.environ['cinder.context'].is_admin = True res = self.controller.index(req) @@ -149,12 +150,14 @@ class MessageApiTest(test.TestCase): def test_get_all_messages_with_limit_wrong_version(self): self.create_message_for_tests() + PRE_MESSAGES_PAGINATION = mv.get_prior_version(mv.MESSAGES_PAGINATION) + url = '/v3/messages?limit=1' req = fakes.HTTPRequest.blank(url) req.method = 'GET' req.content_type = 'application/json' - req.headers["OpenStack-API-Version"] = "volume 3.3" - req.api_version_request = api_version.APIVersionRequest('3.3') + req.headers = mv.get_mv_header(PRE_MESSAGES_PAGINATION) + req.api_version_request = mv.get_api_version(PRE_MESSAGES_PAGINATION) req.environ['cinder.context'].is_admin = True res = self.controller.index(req) @@ -167,8 +170,8 @@ class MessageApiTest(test.TestCase): req = fakes.HTTPRequest.blank(url) req.method = 'GET' req.content_type = 'application/json' - req.headers["OpenStack-API-Version"] = "volume 3.5" - req.api_version_request = api_version.APIVersionRequest('3.5') + req.headers = mv.get_mv_header(mv.MESSAGES_PAGINATION) + req.api_version_request = mv.get_api_version(mv.MESSAGES_PAGINATION) req.environ['cinder.context'].is_admin = True res = self.controller.index(req) @@ -181,8 +184,8 @@ class MessageApiTest(test.TestCase): req = fakes.HTTPRequest.blank(url) req.method = 'GET' req.content_type = 'application/json' - req.headers["OpenStack-API-Version"] = "volume 3.5" - req.api_version_request = api_version.APIVersionRequest('3.5') + req.headers = mv.get_mv_header(mv.MESSAGES_PAGINATION) + req.api_version_request = mv.get_api_version(mv.MESSAGES_PAGINATION) req.environ['cinder.context'].is_admin = True res = self.controller.index(req) @@ -196,8 +199,8 @@ class MessageApiTest(test.TestCase): req = fakes.HTTPRequest.blank(url) req.method = 'GET' req.content_type = 'application/json' - req.headers["OpenStack-API-Version"] = "volume 3.5" - req.api_version_request = api_version.APIVersionRequest('3.5') + req.headers = mv.get_mv_header(mv.MESSAGES_PAGINATION) + req.api_version_request = mv.get_api_version(mv.MESSAGES_PAGINATION) req.environ['cinder.context'].is_admin = True res = self.controller.index(req) @@ -210,8 +213,8 @@ class MessageApiTest(test.TestCase): req = fakes.HTTPRequest.blank(url) req.method = 'GET' req.content_type = 'application/json' - req.headers["OpenStack-API-Version"] = "volume 3.5" - req.api_version_request = api_version.APIVersionRequest('3.5') + req.headers = mv.get_mv_header(mv.MESSAGES_PAGINATION) + req.api_version_request = mv.get_api_version(mv.MESSAGES_PAGINATION) req.environ['cinder.context'].is_admin = True res = self.controller.index(req) @@ -242,8 +245,8 @@ class MessageApiTest(test.TestCase): req = fakes.HTTPRequest.blank(url) req.method = 'GET' req.content_type = 'application/json' - req.headers = {version_header_name: 'volume 3.5'} - req.api_version_request = api_version.APIVersionRequest('3.30') + req.headers = mv.get_mv_header(mv.MESSAGES_PAGINATION) + req.api_version_request = mv.get_api_version(mv.RESOURCE_FILTER) req.environ['cinder.context'].is_admin = True res = self.controller.index(req) @@ -263,7 +266,7 @@ class MessageApiTest(test.TestCase): req = fakes.HTTPRequest.blank(url) req.method = 'GET' 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.environ['cinder.context'].is_admin = True diff --git a/cinder/volume/api.py b/cinder/volume/api.py index 257c4f1c60f..3f04ba50af3 100644 --- a/cinder/volume/api.py +++ b/cinder/volume/api.py @@ -1909,20 +1909,20 @@ class API(base.Base): def _check_boolean_filter_value(self, key, val, strict=False): """Boolean filter values in Volume GET. - Before V3.2, all values other than 'False', 'false', 'FALSE' were - trated as True for specific boolean filter parameters in Volume - GET request. + Before VOLUME_LIST_BOOTABLE, all values other than 'False', 'false', + 'FALSE' were trated as True for specific boolean filter parameters in + Volume GET request. - But V3.2 onwards, only true/True/0/1/False/false parameters are - supported. + But VOLUME_LIST_BOOTABLE onwards, only true/True/0/1/False/false + parameters are supported. All other input values to specific boolean filter parameter will lead to raising exception. - This changes API behavior. So, micro version introduced for V3.2 - onwards. + This changes API behavior. So, micro version introduced for + VOLUME_LIST_BOOTABLE onwards. """ 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 # which is only acceptable format in database queries. try: @@ -1932,7 +1932,7 @@ class API(base.Base): 'value': val} raise exception.InvalidInput(reason=msg) else: - # For existing behavior(before version 3.2) + # For existing behavior(before version VOLUME_LIST_BOOTABLE) accepted_true = ['True', 'true', 'TRUE'] accepted_false = ['False', 'false', 'FALSE'] diff --git a/doc/source/contributor/api_microversion_dev.rst b/doc/source/contributor/api_microversion_dev.rst index e8970559c2c..c5315a320a1 100644 --- a/doc/source/contributor/api_microversion_dev.rst +++ b/doc/source/contributor/api_microversion_dev.rst @@ -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 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. +* 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 -------------------------