api: Remove v1 API
Remove the api-paste definitions, the router, and the remaining controllers. Change-Id: I27cc12db7c3824ef3abc007ce97154508891d74b Signed-off-by: Stephen Finucane <stephenfin@redhat.com> Depends-on: https://review.opendev.org/c/openstack/manila-tempest-plugin/+/976938
This commit is contained in:
@@ -496,10 +496,6 @@ function create_manila_accounts {
|
||||
|
||||
create_service_user "manila"
|
||||
|
||||
get_or_create_service "manila" "share" "Manila Shared Filesystem Service"
|
||||
get_or_create_endpoint "share" "$REGION_NAME" \
|
||||
"$MANILA_ENDPOINT_BASE/v1/\$(project_id)s"
|
||||
|
||||
# Set up Manila v2 service and endpoint - as of microversion 2.60,
|
||||
# project_id is no longer necessary in the v2 endpoint
|
||||
get_or_create_service "manilav2" "sharev2" "Manila Shared Filesystem Service V2"
|
||||
|
||||
@@ -56,12 +56,16 @@ set -o xtrace
|
||||
# Install the target manila
|
||||
install_manila
|
||||
|
||||
# calls upgrade-manila for specific release
|
||||
# Calls upgrade-manila for specific release
|
||||
upgrade_project manila $RUN_DIR $BASE_DEVSTACK_BRANCH $TARGET_DEVSTACK_BRANCH
|
||||
|
||||
# Migrate the database
|
||||
$MANILA_BIN_DIR/manila-manage db sync || die $LINENO "DB migration error"
|
||||
|
||||
# Overwrite the api-paste.ini
|
||||
# TODO(stephenfin): We can remove this in 2026.2 (Hibiscus)
|
||||
cp $MANILA_DIR/etc/manila/api-paste.ini $MANILA_API_PASTE_INI
|
||||
|
||||
start_manila
|
||||
|
||||
# Don't succeed unless the services come up
|
||||
|
||||
@@ -6,15 +6,8 @@
|
||||
use = call:manila.api:root_app_factory
|
||||
/: apiversions
|
||||
/healthcheck: healthcheck
|
||||
/v1: openstack_share_api
|
||||
/v2: openstack_share_api_v2
|
||||
|
||||
[composite:openstack_share_api]
|
||||
use = call:manila.api.middleware.auth:pipeline_factory
|
||||
noauth = cors request_id faultwrap http_proxy_to_wsgi sizelimit osprofiler noauth api
|
||||
keystone = cors request_id faultwrap http_proxy_to_wsgi sizelimit osprofiler authtoken keystonecontext api
|
||||
keystone_nolimit = cors request_id faultwrap http_proxy_to_wsgi sizelimit osprofiler authtoken keystonecontext api
|
||||
|
||||
[composite:openstack_share_api_v2]
|
||||
use = call:manila.api.middleware.auth:pipeline_factory
|
||||
noauth = cors request_id faultwrap http_proxy_to_wsgi sizelimit osprofiler noauth apiv2
|
||||
@@ -40,9 +33,6 @@ paste.filter_factory = osprofiler.web:WsgiMiddleware.factory
|
||||
[filter:http_proxy_to_wsgi]
|
||||
paste.filter_factory = oslo_middleware.http_proxy_to_wsgi:HTTPProxyToWSGI.factory
|
||||
|
||||
[app:api]
|
||||
paste.app_factory = manila.api.v1.router:APIRouter.factory
|
||||
|
||||
[app:apiv2]
|
||||
paste.app_factory = manila.api.v2.router:APIRouter.factory
|
||||
|
||||
|
||||
@@ -44,8 +44,8 @@ REST_API_VERSION_HISTORY = """
|
||||
|
||||
REST API Version History:
|
||||
|
||||
* 1.0 - Initial version. Includes all V1 APIs and extensions in Kilo.
|
||||
* 2.0 - Versions API updated to reflect beginning of microversions epoch.
|
||||
* 2.0 - Initial version. Includes all V1 APIs and extensions in Kilo and
|
||||
the addition of microversions.
|
||||
* 2.1 - Share create() doesn't ignore availability_zone field of share.
|
||||
* 2.2 - Snapshots become optional feature.
|
||||
* 2.3 - Share instances admin API
|
||||
|
||||
@@ -217,15 +217,12 @@ class Request(webob.Request):
|
||||
def set_api_version_request(self):
|
||||
"""Set API version request based on the request header information.
|
||||
|
||||
Microversions starts with /v2, so if a client sends a /v1 URL, then
|
||||
ignore the headers and request 1.0 APIs.
|
||||
Microversions starts with /v2, so if a client sends a different URL
|
||||
then ignore the headers and request unversioned APIs.
|
||||
"""
|
||||
if not self.script_name or not (V1_SCRIPT_NAME in self.script_name or
|
||||
V2_SCRIPT_NAME in self.script_name):
|
||||
if not self.script_name or V2_SCRIPT_NAME not in self.script_name:
|
||||
# The request is on the base URL without a major version specified
|
||||
self.api_version_request = api_version.APIVersionRequest()
|
||||
elif V1_SCRIPT_NAME in self.script_name:
|
||||
self.api_version_request = api_version.APIVersionRequest('1.0')
|
||||
else:
|
||||
if API_VERSION_REQUEST_HEADER in self.headers:
|
||||
hdr_string = self.headers[API_VERSION_REQUEST_HEADER]
|
||||
|
||||
@@ -1,168 +0,0 @@
|
||||
# Copyright 2011 OpenStack LLC.
|
||||
# Copyright 2011 United States Government as represented by the
|
||||
# Administrator of the National Aeronautics and Space Administration.
|
||||
# All Rights Reserved.
|
||||
#
|
||||
# Licensed under the Apache License, Version 2.0 (the "License"); you may
|
||||
# not use this file except in compliance with the License. You may obtain
|
||||
# a copy of the License at
|
||||
#
|
||||
# http://www.apache.org/licenses/LICENSE-2.0
|
||||
#
|
||||
# Unless required by applicable law or agreed to in writing, software
|
||||
# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
|
||||
# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
|
||||
# License for the specific language governing permissions and limitations
|
||||
# under the License.
|
||||
|
||||
"""
|
||||
WSGI middleware for OpenStack Share API v1.
|
||||
"""
|
||||
|
||||
from manila.api import extensions
|
||||
import manila.api.openstack
|
||||
from manila.api.v1 import share_metadata
|
||||
from manila.api.v1 import share_servers
|
||||
from manila.api.v1 import share_snapshots
|
||||
from manila.api.v1 import shares
|
||||
from manila.api.v2 import availability_zones
|
||||
from manila.api.v2 import limits
|
||||
from manila.api.v2 import quota_class_sets
|
||||
from manila.api.v2 import quota_sets
|
||||
from manila.api.v2 import scheduler_stats
|
||||
from manila.api.v2 import security_service
|
||||
from manila.api.v2 import services
|
||||
from manila.api.v2 import share_manage
|
||||
from manila.api.v2 import share_networks
|
||||
from manila.api.v2 import share_types
|
||||
from manila.api.v2 import share_types_extra_specs
|
||||
from manila.api.v2 import share_unmanage
|
||||
from manila.api import versions
|
||||
|
||||
|
||||
class APIRouter(manila.api.openstack.APIRouter):
|
||||
"""Route API requests.
|
||||
|
||||
Routes requests on the OpenStack API to the appropriate controller
|
||||
and method.
|
||||
"""
|
||||
ExtensionManager = extensions.ExtensionManager
|
||||
|
||||
def _setup_routes(self, mapper):
|
||||
self.resources['versions'] = versions.create_resource()
|
||||
mapper.connect("versions", "/",
|
||||
controller=self.resources['versions'],
|
||||
action='index')
|
||||
|
||||
mapper.redirect("", "/")
|
||||
|
||||
self.resources["availability_zones"] = (
|
||||
availability_zones.create_resource_legacy())
|
||||
mapper.resource("availability-zone",
|
||||
"os-availability-zone",
|
||||
controller=self.resources["availability_zones"])
|
||||
|
||||
self.resources["services"] = services.create_resource_legacy()
|
||||
mapper.resource("service",
|
||||
"os-services",
|
||||
controller=self.resources["services"])
|
||||
|
||||
self.resources["quota_sets"] = quota_sets.create_resource_legacy()
|
||||
mapper.resource("quota-set",
|
||||
"os-quota-sets",
|
||||
controller=self.resources["quota_sets"],
|
||||
member={'defaults': 'GET'})
|
||||
|
||||
self.resources["quota_class_sets"] = (
|
||||
quota_class_sets.create_resource_legacy())
|
||||
mapper.resource("quota-class-set",
|
||||
"os-quota-class-sets",
|
||||
controller=self.resources["quota_class_sets"])
|
||||
|
||||
self.resources["share_manage"] = share_manage.create_resource()
|
||||
mapper.resource("share_manage",
|
||||
"os-share-manage",
|
||||
controller=self.resources["share_manage"])
|
||||
|
||||
self.resources["share_unmanage"] = share_unmanage.create_resource()
|
||||
mapper.resource("share_unmanage",
|
||||
"os-share-unmanage",
|
||||
controller=self.resources["share_unmanage"],
|
||||
member={'unmanage': 'POST'})
|
||||
|
||||
self.resources['shares'] = shares.create_resource()
|
||||
mapper.resource("share", "shares",
|
||||
controller=self.resources['shares'],
|
||||
collection={'detail': 'GET'},
|
||||
member={'action': 'POST'})
|
||||
|
||||
self.resources['snapshots'] = share_snapshots.create_resource()
|
||||
mapper.resource("snapshot", "snapshots",
|
||||
controller=self.resources['snapshots'],
|
||||
collection={'detail': 'GET'},
|
||||
member={'action': 'POST'})
|
||||
|
||||
self.resources['share_metadata'] = share_metadata.create_resource()
|
||||
share_metadata_controller = self.resources['share_metadata']
|
||||
|
||||
mapper.resource("share_metadata", "metadata",
|
||||
controller=share_metadata_controller,
|
||||
parent_resource=dict(member_name='share',
|
||||
collection_name='shares'))
|
||||
|
||||
mapper.connect("metadata",
|
||||
"/{project_id}/shares/{share_id}/metadata",
|
||||
controller=share_metadata_controller,
|
||||
action='update_all',
|
||||
conditions={"method": ['PUT']})
|
||||
|
||||
self.resources['limits'] = limits.create_resource()
|
||||
mapper.resource("limit", "limits",
|
||||
controller=self.resources['limits'])
|
||||
|
||||
self.resources["security_services"] = (
|
||||
security_service.create_resource())
|
||||
mapper.resource("security-service", "security-services",
|
||||
controller=self.resources['security_services'],
|
||||
collection={'detail': 'GET'})
|
||||
|
||||
self.resources['share_networks'] = share_networks.create_resource()
|
||||
mapper.resource(share_networks.RESOURCE_NAME,
|
||||
'share-networks',
|
||||
controller=self.resources['share_networks'],
|
||||
collection={'detail': 'GET'},
|
||||
member={'action': 'POST'})
|
||||
|
||||
self.resources['share_servers'] = share_servers.create_resource()
|
||||
mapper.resource('share_server',
|
||||
'share-servers',
|
||||
controller=self.resources['share_servers'])
|
||||
mapper.connect('details',
|
||||
'/{project_id}/share-servers/{id}/details',
|
||||
controller=self.resources['share_servers'],
|
||||
action='details',
|
||||
conditions={"method": ['GET']})
|
||||
|
||||
self.resources['types'] = share_types.create_resource()
|
||||
mapper.resource("type", "types",
|
||||
controller=self.resources['types'],
|
||||
collection={'detail': 'GET', 'default': 'GET'},
|
||||
member={'action': 'POST',
|
||||
'os-share-type-access': 'GET'})
|
||||
|
||||
self.resources['extra_specs'] = (
|
||||
share_types_extra_specs.create_resource())
|
||||
mapper.resource('extra_spec', 'extra_specs',
|
||||
controller=self.resources['extra_specs'],
|
||||
parent_resource=dict(member_name='type',
|
||||
collection_name='types'))
|
||||
|
||||
self.resources['scheduler_stats'] = scheduler_stats.create_resource()
|
||||
mapper.connect('pools', '/{project_id}/scheduler-stats/pools',
|
||||
controller=self.resources['scheduler_stats'],
|
||||
action='pools_index',
|
||||
conditions={'method': ['GET']})
|
||||
mapper.connect('pools', '/{project_id}/scheduler-stats/pools/detail',
|
||||
controller=self.resources['scheduler_stats'],
|
||||
action='pools_detail',
|
||||
conditions={'method': ['GET']})
|
||||
@@ -1,199 +0,0 @@
|
||||
# Copyright 2011 OpenStack Foundation
|
||||
# All Rights Reserved.
|
||||
#
|
||||
# Licensed under the Apache License, Version 2.0 (the "License"); you may
|
||||
# not use this file except in compliance with the License. You may obtain
|
||||
# a copy of the License at
|
||||
#
|
||||
# http://www.apache.org/licenses/LICENSE-2.0
|
||||
#
|
||||
# Unless required by applicable law or agreed to in writing, software
|
||||
# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
|
||||
# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
|
||||
# License for the specific language governing permissions and limitations
|
||||
# under the License.
|
||||
|
||||
from http import client as http_client
|
||||
from oslo_log import log
|
||||
import webob
|
||||
from webob import exc
|
||||
|
||||
from oslo_config import cfg
|
||||
|
||||
from manila.api import common as api_common
|
||||
from manila.api.openstack import wsgi
|
||||
from manila import db
|
||||
from manila import exception
|
||||
from manila.i18n import _
|
||||
from manila import policy
|
||||
from manila import share
|
||||
|
||||
|
||||
LOG = log.getLogger(__name__)
|
||||
CONF = cfg.CONF
|
||||
|
||||
|
||||
class ShareMetadataController(object):
|
||||
"""The share metadata API controller for the OpenStack API."""
|
||||
|
||||
def __init__(self):
|
||||
self.share_api = share.API()
|
||||
super(ShareMetadataController, self).__init__()
|
||||
|
||||
def _get_metadata(self, context, share_id):
|
||||
try:
|
||||
share = self.share_api.get(context, share_id)
|
||||
rv = db.share_metadata_get(context, share['id'])
|
||||
meta = dict(rv.items())
|
||||
except exception.NotFound:
|
||||
msg = _('share does not exist')
|
||||
raise exc.HTTPNotFound(explanation=msg)
|
||||
return meta
|
||||
|
||||
def index(self, req, share_id):
|
||||
"""Returns the list of metadata for a given share."""
|
||||
context = req.environ['manila.context']
|
||||
return {'metadata': self._get_metadata(context, share_id)}
|
||||
|
||||
def create(self, req, share_id, body):
|
||||
try:
|
||||
metadata = body['metadata']
|
||||
except (KeyError, TypeError):
|
||||
msg = _("Malformed request body")
|
||||
raise exc.HTTPBadRequest(explanation=msg)
|
||||
|
||||
context = req.environ['manila.context']
|
||||
|
||||
new_metadata = self._update_share_metadata(context,
|
||||
share_id,
|
||||
metadata,
|
||||
delete=False)
|
||||
return {'metadata': new_metadata}
|
||||
|
||||
def update(self, req, share_id, id, body):
|
||||
try:
|
||||
meta_item = body['meta']
|
||||
except (TypeError, KeyError):
|
||||
expl = _('Malformed request body')
|
||||
raise exc.HTTPBadRequest(explanation=expl)
|
||||
|
||||
if id not in meta_item:
|
||||
expl = _('Request body and URI mismatch')
|
||||
raise exc.HTTPBadRequest(explanation=expl)
|
||||
|
||||
if len(meta_item) > 1:
|
||||
expl = _('Request body contains too many items')
|
||||
raise exc.HTTPBadRequest(explanation=expl)
|
||||
|
||||
context = req.environ['manila.context']
|
||||
self._update_share_metadata(context,
|
||||
share_id,
|
||||
meta_item,
|
||||
delete=False)
|
||||
|
||||
return {'meta': meta_item}
|
||||
|
||||
def update_all(self, req, share_id, body):
|
||||
try:
|
||||
metadata = body['metadata']
|
||||
except (TypeError, KeyError):
|
||||
expl = _('Malformed request body')
|
||||
raise exc.HTTPBadRequest(explanation=expl)
|
||||
|
||||
context = req.environ['manila.context']
|
||||
|
||||
new_metadata = self._update_share_metadata(
|
||||
context, share_id, metadata, delete=True)
|
||||
return {'metadata': new_metadata}
|
||||
|
||||
def _update_share_metadata(self, context,
|
||||
share_id, metadata,
|
||||
delete=False):
|
||||
ignore_keys = getattr(CONF, 'admin_only_metadata', [])
|
||||
try:
|
||||
share = self.share_api.get(context, share_id)
|
||||
if set(metadata).intersection(set(ignore_keys)):
|
||||
try:
|
||||
policy.check_policy(
|
||||
context, 'share', 'update_admin_only_metadata')
|
||||
except exception.PolicyNotAuthorized:
|
||||
msg = _("Cannot set or update admin only metadata.")
|
||||
LOG.exception(msg)
|
||||
raise exc.HTTPForbidden(explanation=msg)
|
||||
ignore_keys = []
|
||||
|
||||
rv = db.share_metadata_get(context, share['id'])
|
||||
orig_meta = dict(rv.items())
|
||||
if delete:
|
||||
_metadata = metadata
|
||||
for key in ignore_keys:
|
||||
if key in orig_meta:
|
||||
_metadata[key] = orig_meta[key]
|
||||
else:
|
||||
metadata_copy = metadata.copy()
|
||||
for key in ignore_keys:
|
||||
metadata_copy.pop(key, None)
|
||||
_metadata = orig_meta.copy()
|
||||
_metadata.update(metadata_copy)
|
||||
|
||||
api_common.check_metadata_properties(_metadata)
|
||||
db.share_metadata_update(context, share['id'],
|
||||
_metadata, delete)
|
||||
|
||||
return _metadata
|
||||
except exception.NotFound:
|
||||
msg = _('share does not exist')
|
||||
raise exc.HTTPNotFound(explanation=msg)
|
||||
|
||||
except (ValueError, AttributeError):
|
||||
msg = _("Malformed request body")
|
||||
raise exc.HTTPBadRequest(explanation=msg)
|
||||
|
||||
except exception.InvalidMetadata as error:
|
||||
raise exc.HTTPBadRequest(explanation=error.msg)
|
||||
|
||||
except exception.InvalidMetadataSize as error:
|
||||
raise exc.HTTPBadRequest(explanation=error.msg)
|
||||
|
||||
def show(self, req, share_id, id):
|
||||
"""Return a single metadata item."""
|
||||
context = req.environ['manila.context']
|
||||
data = self._get_metadata(context, share_id)
|
||||
|
||||
try:
|
||||
return {'meta': {id: data[id]}}
|
||||
except KeyError:
|
||||
msg = _("Metadata item was not found")
|
||||
raise exc.HTTPNotFound(explanation=msg)
|
||||
|
||||
def delete(self, req, share_id, id):
|
||||
"""Deletes an existing metadata."""
|
||||
context = req.environ['manila.context']
|
||||
|
||||
metadata = self._get_metadata(context, share_id)
|
||||
|
||||
if id not in metadata:
|
||||
msg = _("Metadata item was not found")
|
||||
raise exc.HTTPNotFound(explanation=msg)
|
||||
|
||||
try:
|
||||
share = self.share_api.get(context, share_id)
|
||||
admin_only_metadata_keys = (
|
||||
getattr(CONF, 'admin_only_metadata', set())
|
||||
)
|
||||
if id in admin_only_metadata_keys:
|
||||
policy.check_policy(context, 'share',
|
||||
'update_admin_only_metadata')
|
||||
db.share_metadata_delete(context, share['id'], id)
|
||||
except exception.NotFound:
|
||||
msg = _('share does not exist')
|
||||
raise exc.HTTPNotFound(explanation=msg)
|
||||
except exception.PolicyNotAuthorized:
|
||||
msg = _("Cannot delete admin only metadata.")
|
||||
LOG.exception(msg)
|
||||
raise exc.HTTPForbidden(explanation=msg)
|
||||
return webob.Response(status_int=http_client.OK)
|
||||
|
||||
|
||||
def create_resource():
|
||||
return wsgi.Resource(ShareMetadataController())
|
||||
@@ -1,154 +0,0 @@
|
||||
# Copyright 2014 OpenStack Foundation
|
||||
# All Rights Reserved.
|
||||
#
|
||||
# Licensed under the Apache License, Version 2.0 (the "License"); you may
|
||||
# not use this file except in compliance with the License. You may obtain
|
||||
# a copy of the License at
|
||||
#
|
||||
# http://www.apache.org/licenses/LICENSE-2.0
|
||||
#
|
||||
# Unless required by applicable law or agreed to in writing, software
|
||||
# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
|
||||
# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
|
||||
# License for the specific language governing permissions and limitations
|
||||
# under the License.
|
||||
|
||||
from http import client as http_client
|
||||
|
||||
from oslo_log import log
|
||||
import webob
|
||||
from webob import exc
|
||||
|
||||
from manila.api.openstack import wsgi
|
||||
from manila.api.views import share_servers as share_servers_views
|
||||
from manila.common import constants
|
||||
from manila.db import api as db_api
|
||||
from manila import exception
|
||||
from manila.i18n import _
|
||||
from manila import share
|
||||
|
||||
LOG = log.getLogger(__name__)
|
||||
|
||||
|
||||
class ShareServerController(wsgi.Controller):
|
||||
"""The Share Server API controller for the OpenStack API."""
|
||||
|
||||
_view_builder_class = share_servers_views.ViewBuilder
|
||||
resource_name = 'share_server'
|
||||
|
||||
def __init__(self):
|
||||
self.share_api = share.API()
|
||||
super(ShareServerController, self).__init__()
|
||||
|
||||
@wsgi.Controller.authorize
|
||||
def index(self, req):
|
||||
"""Returns a list of share servers."""
|
||||
|
||||
context = req.environ['manila.context']
|
||||
|
||||
search_opts = {}
|
||||
search_opts.update(req.GET)
|
||||
|
||||
ss_filters = {}
|
||||
if 'host' in search_opts:
|
||||
ss_filters['host'] = search_opts.pop('host')
|
||||
if 'status' in search_opts:
|
||||
ss_filters['status'] = search_opts.pop('status')
|
||||
if 'source_share_server_id' in search_opts:
|
||||
ss_filters['source_share_server_id'] = search_opts.pop(
|
||||
'source_share_server_id')
|
||||
if 'identifier' in search_opts:
|
||||
ss_filters['identifier'] = search_opts.pop('identifier')
|
||||
share_servers = db_api.share_server_get_all_with_filters(
|
||||
context, ss_filters)
|
||||
|
||||
for s in share_servers:
|
||||
try:
|
||||
share_network = db_api.share_network_get(
|
||||
context, s.share_network_id)
|
||||
s.project_id = share_network['project_id']
|
||||
if share_network['name']:
|
||||
s.share_network_name = share_network['name']
|
||||
else:
|
||||
s.share_network_name = share_network['id']
|
||||
except exception.ShareNetworkNotFound:
|
||||
# NOTE(dviroel): The share-network may already be deleted while
|
||||
# the share-server is in 'deleting' state. In this scenario,
|
||||
# we will return some empty values.
|
||||
LOG.debug("Unable to retrieve share network details for share "
|
||||
"server %(server)s, the network %(network)s was "
|
||||
"not found.",
|
||||
{'server': s.id, 'network': s.share_network_id})
|
||||
s.project_id = ''
|
||||
s.share_network_name = ''
|
||||
if search_opts:
|
||||
for k, v in search_opts.items():
|
||||
share_servers = [s for s in share_servers if
|
||||
(hasattr(s, k) and
|
||||
s[k] == v or k == 'share_network' and
|
||||
v in [s.share_network_name,
|
||||
s.share_network_id] or
|
||||
k == 'share_network_subnet_id' and
|
||||
v in s.share_network_subnet_ids)]
|
||||
return self._view_builder.build_share_servers(req, share_servers)
|
||||
|
||||
@wsgi.Controller.authorize
|
||||
def show(self, req, id):
|
||||
"""Return data about the requested share server."""
|
||||
context = req.environ['manila.context']
|
||||
try:
|
||||
server = db_api.share_server_get(context, id)
|
||||
share_network = db_api.share_network_get(
|
||||
context, server['share_network_id'])
|
||||
server.project_id = share_network['project_id']
|
||||
if share_network['name']:
|
||||
server.share_network_name = share_network['name']
|
||||
else:
|
||||
server.share_network_name = share_network['id']
|
||||
except exception.ShareServerNotFound as e:
|
||||
raise exc.HTTPNotFound(explanation=e.msg)
|
||||
except exception.ShareNetworkNotFound:
|
||||
msg = _("Share server could not be found. Its associated share "
|
||||
"network %s does not exist.") % server['share_network_id']
|
||||
raise exc.HTTPNotFound(explanation=msg)
|
||||
return self._view_builder.build_share_server(req, server)
|
||||
|
||||
@wsgi.Controller.authorize
|
||||
def details(self, req, id):
|
||||
"""Return details for requested share server."""
|
||||
context = req.environ['manila.context']
|
||||
try:
|
||||
share_server = db_api.share_server_get(context, id)
|
||||
except exception.ShareServerNotFound as e:
|
||||
raise exc.HTTPNotFound(explanation=e.msg)
|
||||
|
||||
return self._view_builder.build_share_server_details(
|
||||
share_server['backend_details'])
|
||||
|
||||
@wsgi.Controller.authorize
|
||||
def delete(self, req, id):
|
||||
"""Delete specified share server."""
|
||||
context = req.environ['manila.context']
|
||||
try:
|
||||
share_server = db_api.share_server_get(context, id)
|
||||
except exception.ShareServerNotFound as e:
|
||||
raise exc.HTTPNotFound(explanation=e.msg)
|
||||
allowed_statuses = [constants.STATUS_ERROR, constants.STATUS_ACTIVE]
|
||||
if share_server['status'] not in allowed_statuses:
|
||||
data = {
|
||||
'status': share_server['status'],
|
||||
'allowed_statuses': allowed_statuses,
|
||||
}
|
||||
msg = _("Share server's actual status is %(status)s, allowed "
|
||||
"statuses for deletion are %(allowed_statuses)s.") % (data)
|
||||
raise exc.HTTPForbidden(explanation=msg)
|
||||
LOG.debug("Deleting share server with id: %s.", id)
|
||||
try:
|
||||
self.share_api.delete_share_server(context, share_server)
|
||||
except exception.ShareServerInUse as e:
|
||||
raise exc.HTTPConflict(explanation=e.msg)
|
||||
return webob.Response(status_int=http_client.ACCEPTED)
|
||||
|
||||
|
||||
def create_resource():
|
||||
return wsgi.Resource(ShareServerController())
|
||||
|
||||
@@ -1,51 +0,0 @@
|
||||
# Copyright 2013 NetApp
|
||||
# All Rights Reserved.
|
||||
#
|
||||
# Licensed under the Apache License, Version 2.0 (the "License"); you may
|
||||
# not use this file except in compliance with the License. You may obtain
|
||||
# a copy of the License at
|
||||
#
|
||||
# http://www.apache.org/licenses/LICENSE-2.0
|
||||
#
|
||||
# Unless required by applicable law or agreed to in writing, software
|
||||
# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
|
||||
# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
|
||||
# License for the specific language governing permissions and limitations
|
||||
# under the License.
|
||||
|
||||
"""The share snapshots api."""
|
||||
|
||||
from oslo_log import log
|
||||
|
||||
from manila.api.openstack import wsgi
|
||||
from manila.api.v2 import share_snapshots
|
||||
from manila.api.views import share_snapshots as snapshot_views
|
||||
from manila import share
|
||||
|
||||
LOG = log.getLogger(__name__)
|
||||
|
||||
|
||||
class ShareSnapshotsController(
|
||||
share_snapshots.ShareSnapshotMixin, wsgi.Controller,
|
||||
wsgi.AdminActionsMixin
|
||||
):
|
||||
"""The Share Snapshots API controller for the OpenStack API."""
|
||||
|
||||
resource_name = 'share_snapshot'
|
||||
_view_builder_class = snapshot_views.ViewBuilder
|
||||
|
||||
def __init__(self):
|
||||
super(ShareSnapshotsController, self).__init__()
|
||||
self.share_api = share.API()
|
||||
|
||||
@wsgi.action('os-reset_status')
|
||||
def snapshot_reset_status_legacy(self, req, id, body):
|
||||
return self._reset_status(req, id, body)
|
||||
|
||||
@wsgi.action('os-force_delete')
|
||||
def snapshot_force_delete_legacy(self, req, id, body):
|
||||
return self._force_delete(req, id, body)
|
||||
|
||||
|
||||
def create_resource():
|
||||
return wsgi.Resource(ShareSnapshotsController())
|
||||
@@ -1,80 +0,0 @@
|
||||
# Copyright 2013 NetApp
|
||||
# All Rights Reserved.
|
||||
#
|
||||
# Licensed under the Apache License, Version 2.0 (the "License"); you may
|
||||
# not use this file except in compliance with the License. You may obtain
|
||||
# a copy of the License at
|
||||
#
|
||||
# http://www.apache.org/licenses/LICENSE-2.0
|
||||
#
|
||||
# Unless required by applicable law or agreed to in writing, software
|
||||
# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
|
||||
# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
|
||||
# License for the specific language governing permissions and limitations
|
||||
# under the License.
|
||||
|
||||
"""The shares api."""
|
||||
|
||||
from oslo_log import log
|
||||
|
||||
from manila.api.openstack import wsgi
|
||||
from manila.api.v2 import shares
|
||||
from manila.api.views import share_accesses as share_access_views
|
||||
from manila.api.views import shares as share_views
|
||||
from manila.lock import api as resource_locks
|
||||
from manila import share
|
||||
|
||||
LOG = log.getLogger(__name__)
|
||||
|
||||
|
||||
class ShareController(
|
||||
wsgi.Controller, shares.ShareMixin, wsgi.AdminActionsMixin
|
||||
):
|
||||
"""The Shares API v1 controller for the OpenStack API."""
|
||||
resource_name = 'share'
|
||||
_view_builder_class = share_views.ViewBuilder
|
||||
|
||||
def __init__(self):
|
||||
super(ShareController, self).__init__()
|
||||
self.share_api = share.API()
|
||||
self.resource_locks_api = resource_locks.API()
|
||||
self._access_view_builder = share_access_views.ViewBuilder()
|
||||
|
||||
@wsgi.action('os-reset_status')
|
||||
def share_reset_status(self, req, id, body):
|
||||
"""Reset status of a share."""
|
||||
return self._reset_status(req, id, body)
|
||||
|
||||
@wsgi.action('os-force_delete')
|
||||
def share_force_delete(self, req, id, body):
|
||||
"""Delete a share, bypassing the check for status."""
|
||||
return self._force_delete(req, id, body)
|
||||
|
||||
@wsgi.action('os-allow_access')
|
||||
def allow_access(self, req, id, body):
|
||||
"""Add share access rule."""
|
||||
return self._allow_access(req, id, body)
|
||||
|
||||
@wsgi.action('os-deny_access')
|
||||
def deny_access(self, req, id, body):
|
||||
"""Remove share access rule."""
|
||||
return self._deny_access(req, id, body)
|
||||
|
||||
@wsgi.action('os-access_list')
|
||||
def access_list(self, req, id, body):
|
||||
"""List share access rules."""
|
||||
return self._access_list(req, id, body)
|
||||
|
||||
@wsgi.action('os-extend')
|
||||
def extend(self, req, id, body):
|
||||
"""Extend size of a share."""
|
||||
return self._extend(req, id, body)
|
||||
|
||||
@wsgi.action('os-shrink')
|
||||
def shrink(self, req, id, body):
|
||||
"""Shrink size of a share."""
|
||||
return self._shrink(req, id, body)
|
||||
|
||||
|
||||
def create_resource():
|
||||
return wsgi.Resource(ShareController())
|
||||
@@ -45,7 +45,7 @@ class AvailabilityZoneControllerLegacy(AvailabilityZoneMixin, wsgi.Controller):
|
||||
'os-availability-zone'.
|
||||
"""
|
||||
|
||||
@wsgi.Controller.api_version('1.0', '2.6')
|
||||
@wsgi.Controller.api_version('2.0', '2.6')
|
||||
@validation.request_query_schema(schema.index_request_query)
|
||||
@validation.response_body_schema(schema.index_response_body)
|
||||
def index(self, req):
|
||||
|
||||
@@ -75,13 +75,13 @@ class QuotaClassSetsControllerLegacy(QuotaClassSetsMixin, wsgi.Controller):
|
||||
'os-quota-class-sets'.
|
||||
"""
|
||||
|
||||
@wsgi.Controller.api_version('1.0', '2.6')
|
||||
@wsgi.Controller.api_version('2.0', '2.6')
|
||||
@validation.request_query_schema(schema.show_request_query)
|
||||
@validation.response_body_schema(schema.show_response_body)
|
||||
def show(self, req, id):
|
||||
return self._show(req, id)
|
||||
|
||||
@wsgi.Controller.api_version('1.0', '2.6')
|
||||
@wsgi.Controller.api_version('2.0', '2.6')
|
||||
@validation.request_body_schema(schema.update_request_body)
|
||||
@validation.response_body_schema(schema.update_response_body)
|
||||
def update(self, req, id, body):
|
||||
|
||||
@@ -289,16 +289,16 @@ class QuotaSetsControllerLegacy(QuotaSetsMixin, wsgi.Controller):
|
||||
'os-quota-sets'.
|
||||
"""
|
||||
|
||||
@wsgi.Controller.api_version('1.0', '2.6')
|
||||
@wsgi.Controller.api_version('2.0', '2.6')
|
||||
def show(self, req, id):
|
||||
self._ensure_share_type_arg_is_absent(req)
|
||||
return self._show(req, id)
|
||||
|
||||
@wsgi.Controller.api_version('1.0', '2.6')
|
||||
@wsgi.Controller.api_version('2.0', '2.6')
|
||||
def defaults(self, req, id):
|
||||
return self._defaults(req, id)
|
||||
|
||||
@wsgi.Controller.api_version('1.0', '2.6')
|
||||
@wsgi.Controller.api_version('2.0', '2.6')
|
||||
def update(self, req, id, body):
|
||||
self._ensure_share_type_arg_is_absent(req)
|
||||
self._ensure_specific_microversion_args_are_absent(
|
||||
@@ -307,7 +307,7 @@ class QuotaSetsControllerLegacy(QuotaSetsMixin, wsgi.Controller):
|
||||
body, ['share_replicas', 'replica_gigabytes'], "2.53")
|
||||
return self._update(req, id, body)
|
||||
|
||||
@wsgi.Controller.api_version('1.0', '2.6')
|
||||
@wsgi.Controller.api_version('2.0', '2.6')
|
||||
def delete(self, req, id):
|
||||
self._ensure_share_type_arg_is_absent(req)
|
||||
return self._delete(req, id)
|
||||
|
||||
@@ -31,7 +31,7 @@ class SchedulerStatsController(wsgi.Controller):
|
||||
self._view_builder_class = scheduler_stats_views.ViewBuilder
|
||||
super(SchedulerStatsController, self).__init__()
|
||||
|
||||
@wsgi.Controller.api_version('1.0', '2.22')
|
||||
@wsgi.Controller.api_version('2.0', '2.22')
|
||||
@wsgi.Controller.authorize('index')
|
||||
def pools_index(self, req):
|
||||
"""Returns a list of storage pools known to the scheduler."""
|
||||
@@ -42,7 +42,7 @@ class SchedulerStatsController(wsgi.Controller):
|
||||
def pools_index(self, req): # pylint: disable=function-redefined # noqa F811
|
||||
return self._pools(req, action='index', enable_share_type=True)
|
||||
|
||||
@wsgi.Controller.api_version('1.0', '2.22')
|
||||
@wsgi.Controller.api_version('2.0', '2.22')
|
||||
@wsgi.Controller.authorize('detail')
|
||||
def pools_detail(self, req):
|
||||
"""Returns a detailed list of storage pools known to the scheduler."""
|
||||
|
||||
@@ -130,11 +130,11 @@ class ServiceControllerLegacy(ServiceMixin, wsgi.Controller):
|
||||
'os-services'.
|
||||
"""
|
||||
|
||||
@wsgi.Controller.api_version('1.0', '2.6')
|
||||
@wsgi.Controller.api_version('2.0', '2.6')
|
||||
def index(self, req):
|
||||
return self._index(req)
|
||||
|
||||
@wsgi.Controller.api_version('1.0', '2.6')
|
||||
@wsgi.Controller.api_version('2.0', '2.6')
|
||||
def update(self, req, id, body):
|
||||
return self._update(req, id, body, support_disabled_reason=False)
|
||||
|
||||
|
||||
@@ -136,7 +136,7 @@ class ShareManageController(ShareManageMixin, wsgi.Controller):
|
||||
super(ShareManageController, self).__init__(*args, **kwargs)
|
||||
self.share_api = share.API()
|
||||
|
||||
@wsgi.Controller.api_version('1.0', '2.6')
|
||||
@wsgi.Controller.api_version('2.0', '2.6')
|
||||
def create(self, req, body):
|
||||
"""Legacy method for 'manage share' operation.
|
||||
|
||||
|
||||
@@ -146,7 +146,7 @@ class ShareTypesController(wsgi.Controller):
|
||||
context, search_opts=filters).values()
|
||||
return list(limited_types)
|
||||
|
||||
@wsgi.Controller.api_version("1.0", "2.23")
|
||||
@wsgi.Controller.api_version("2.0", "2.23")
|
||||
@wsgi.action("create")
|
||||
def create(self, req, body):
|
||||
return self._create(req, body, set_defaults=True)
|
||||
|
||||
@@ -138,7 +138,7 @@ class ShareTypeExtraSpecsController(wsgi.Controller):
|
||||
else:
|
||||
raise webob.exc.HTTPNotFound()
|
||||
|
||||
@wsgi.Controller.api_version('1.0', '2.23')
|
||||
@wsgi.Controller.api_version('2.0', '2.23')
|
||||
@wsgi.Controller.authorize
|
||||
def delete(self, req, type_id, id):
|
||||
"""Deletes an existing extra spec."""
|
||||
|
||||
@@ -89,7 +89,7 @@ class ShareUnmanageController(ShareUnmanageMixin, wsgi.Controller):
|
||||
super(ShareUnmanageController, self).__init__(*args, **kwargs)
|
||||
self.share_api = share.API()
|
||||
|
||||
@wsgi.Controller.api_version('1.0', '2.6')
|
||||
@wsgi.Controller.api_version('2.0', '2.6')
|
||||
def unmanage(self, req, id):
|
||||
return self._unmanage(req, id)
|
||||
|
||||
|
||||
+3
-22
@@ -38,15 +38,6 @@ _MEDIA_TYPES = [{
|
||||
}]
|
||||
|
||||
_KNOWN_VERSIONS = {
|
||||
'v1.0': {
|
||||
'id': 'v1.0',
|
||||
'status': 'DEPRECATED',
|
||||
'version': '',
|
||||
'min_version': '',
|
||||
'updated': '2015-08-27T11:33:21Z',
|
||||
'links': _LINKS,
|
||||
'media-types': _MEDIA_TYPES,
|
||||
},
|
||||
'v2.0': {
|
||||
'id': 'v2.0',
|
||||
'status': 'CURRENT',
|
||||
@@ -75,27 +66,17 @@ class VersionsRouter(openstack.APIRouter):
|
||||
class VersionsController(wsgi.Controller):
|
||||
|
||||
def __init__(self):
|
||||
super(VersionsController, self).__init__(None)
|
||||
|
||||
@wsgi.Controller.api_version('1.0', '1.0')
|
||||
def index(self, req):
|
||||
"""Return versions supported prior to the microversions epoch."""
|
||||
builder = views_versions.get_view_builder(req)
|
||||
known_versions = copy.deepcopy(_KNOWN_VERSIONS)
|
||||
known_versions.pop('v2.0')
|
||||
return builder.build_versions(known_versions)
|
||||
super().__init__(None)
|
||||
|
||||
@wsgi.Controller.api_version('2.0') # noqa
|
||||
def index(self, req): # pylint: disable=function-redefined # noqa F811
|
||||
"""Return versions supported after the start of microversions."""
|
||||
builder = views_versions.get_view_builder(req)
|
||||
known_versions = copy.deepcopy(_KNOWN_VERSIONS)
|
||||
known_versions.pop('v1.0')
|
||||
return builder.build_versions(known_versions)
|
||||
|
||||
# NOTE (cknight): Calling the versions API without
|
||||
# /v1 or /v2 in the URL will lead to this unversioned
|
||||
# method, which should always return info about all
|
||||
# NOTE (cknight): Calling the versions API without /v2 in the URL will lead
|
||||
# to this unversioned method, which should always return info about all
|
||||
# available versions.
|
||||
@wsgi.response(300)
|
||||
def all(self, req):
|
||||
|
||||
@@ -89,7 +89,7 @@ class ViewBuilder(common.ViewBuilder):
|
||||
metadata = {item['key']: item['value'] for item in metadata}
|
||||
access_dict['metadata'] = metadata
|
||||
|
||||
@common.ViewBuilder.versioned_method("1.0", "2.27")
|
||||
@common.ViewBuilder.versioned_method("2.0", "2.27")
|
||||
def translate_transitional_statuses(self, context, access_dict, access):
|
||||
"""In 2.28, the per access rule status was (re)introduced."""
|
||||
api = share_api.API()
|
||||
|
||||
@@ -97,7 +97,7 @@ class ViewBuilder(common.ViewBuilder):
|
||||
instance_dict['cast_rules_to_readonly'] = share_instance.get(
|
||||
'cast_rules_to_readonly', False)
|
||||
|
||||
@common.ViewBuilder.versioned_method("1.0", "2.53")
|
||||
@common.ViewBuilder.versioned_method("2.0", "2.53")
|
||||
def translate_creating_from_snapshot_status(self, context, instance_dict,
|
||||
share_instance):
|
||||
if (share_instance.get('status') ==
|
||||
|
||||
@@ -137,7 +137,7 @@ class ViewBuilder(common.ViewBuilder):
|
||||
def add_mtu(self, context, network_dict, network):
|
||||
network_dict['mtu'] = network.get('mtu')
|
||||
|
||||
@common.ViewBuilder.versioned_method("1.0", "2.25")
|
||||
@common.ViewBuilder.versioned_method("2.0", "2.25")
|
||||
def add_nova_net_id(self, context, network_dict, network):
|
||||
network_dict['nova_net_id'] = None
|
||||
|
||||
|
||||
@@ -192,7 +192,7 @@ class ViewBuilder(common.ViewBuilder):
|
||||
|
||||
return shares_dict
|
||||
|
||||
@common.ViewBuilder.versioned_method("1.0", "2.53")
|
||||
@common.ViewBuilder.versioned_method("2.0", "2.53")
|
||||
def translate_creating_from_snapshot_status(self, context, share_dict,
|
||||
share):
|
||||
if share.get('status') == constants.STATUS_CREATING_FROM_SNAPSHOT:
|
||||
|
||||
@@ -66,7 +66,7 @@ class ViewBuilder(common.ViewBuilder):
|
||||
share_type_dict['share_type_access:is_public'] = share_type.get(
|
||||
'is_public', True)
|
||||
|
||||
@common.ViewBuilder.versioned_method("1.0", "2.6")
|
||||
@common.ViewBuilder.versioned_method("2.0", "2.6")
|
||||
def add_is_public_attr_extension_like(self, context, share_type_dict,
|
||||
share_type):
|
||||
share_type_dict['os-share-type-access:is_public'] = share_type.get(
|
||||
|
||||
@@ -23,9 +23,6 @@ def get_view_builder(req):
|
||||
return ViewBuilder(req.application_url)
|
||||
|
||||
|
||||
_URL_SUFFIX = {'v1.0': 'v1', 'v2.0': 'v2'}
|
||||
|
||||
|
||||
class ViewBuilder(object):
|
||||
def __init__(self, base_url):
|
||||
"""Initialize ViewBuilder.
|
||||
@@ -47,9 +44,8 @@ class ViewBuilder(object):
|
||||
def _build_links(self, version_data):
|
||||
"""Generate a container of links that refer to the provided version."""
|
||||
links = copy.deepcopy(version_data.get('links', {}))
|
||||
version = _URL_SUFFIX.get(version_data['id'])
|
||||
links.append({'rel': 'self',
|
||||
'href': self._generate_href(version=version)})
|
||||
'href': self._generate_href(version='v2')})
|
||||
return links
|
||||
|
||||
def _generate_href(self, version='v1', path=None):
|
||||
|
||||
@@ -25,8 +25,7 @@ from manila.api import common as api_common
|
||||
from manila.api.openstack import api_version_request as api_version
|
||||
from manila.api.openstack import wsgi as os_wsgi
|
||||
from manila.api import urlmap
|
||||
from manila.api.v1 import router as router_v1
|
||||
from manila.api.v2 import router as router_v2
|
||||
from manila.api.v2 import router as router
|
||||
from manila.common import constants
|
||||
from manila import context
|
||||
from manila import exception
|
||||
@@ -155,8 +154,7 @@ def app():
|
||||
No auth, just let environ['manila.context'] pass through.
|
||||
"""
|
||||
mapper = urlmap.URLMap()
|
||||
mapper['/v1'] = router_v1.APIRouter()
|
||||
mapper['/v2'] = router_v2.APIRouter()
|
||||
mapper['/v2'] = router.APIRouter()
|
||||
return mapper
|
||||
|
||||
|
||||
|
||||
@@ -176,12 +176,9 @@ class RequestTest(test.TestCase):
|
||||
|
||||
self.assertIsNone(request.set_api_version_request())
|
||||
|
||||
if not resource or not ('/v1' in resource or '/v2' in resource):
|
||||
if not resource or '/v2' not in resource:
|
||||
self.assertEqual(api_version.APIVersionRequest(),
|
||||
request.api_version_request)
|
||||
elif 'v1' in resource:
|
||||
self.assertEqual(api_version.APIVersionRequest('1.0'),
|
||||
request.api_version_request)
|
||||
else:
|
||||
self.assertEqual(api_version.APIVersionRequest('2.117'),
|
||||
request.api_version_request)
|
||||
|
||||
@@ -23,7 +23,7 @@ from oslo_utils import timeutils
|
||||
import webob
|
||||
|
||||
from manila.api import extensions
|
||||
from manila.api.v1 import router
|
||||
from manila.api.v2 import router
|
||||
from manila import policy
|
||||
from manila import test
|
||||
|
||||
|
||||
@@ -21,7 +21,7 @@ from oslo_utils import encodeutils
|
||||
|
||||
from manila.api.openstack import api_version_request
|
||||
from manila.api.openstack import wsgi
|
||||
from manila.api.v1 import router
|
||||
from manila.api.v2 import router
|
||||
from manila.api import versions
|
||||
from manila import test
|
||||
from manila.tests.api import fakes
|
||||
@@ -51,15 +51,10 @@ class VersionsControllerTestCase(test.TestCase):
|
||||
version_list = body['versions']
|
||||
|
||||
ids = [v['id'] for v in version_list]
|
||||
self.assertEqual({'v1.0', 'v2.0'}, set(ids))
|
||||
self.assertEqual({'v2.0'}, set(ids))
|
||||
self.assertNotIn(version_header_name, response.headers)
|
||||
self.assertNotIn('Vary', response.headers)
|
||||
|
||||
v1 = [v for v in version_list if v['id'] == 'v1.0'][0]
|
||||
self.assertEqual('', v1.get('min_version'))
|
||||
self.assertEqual('', v1.get('version'))
|
||||
self.assertEqual('DEPRECATED', v1.get('status'))
|
||||
|
||||
v2 = [v for v in version_list if v['id'] == 'v2.0'][0]
|
||||
self.assertEqual(api_version_request._MIN_API_VERSION,
|
||||
v2.get('min_version'))
|
||||
@@ -67,29 +62,6 @@ class VersionsControllerTestCase(test.TestCase):
|
||||
v2.get('version'))
|
||||
self.assertEqual('CURRENT', v2.get('status'))
|
||||
|
||||
@ddt.data('1.0',
|
||||
'1.1',
|
||||
api_version_request._MIN_API_VERSION,
|
||||
api_version_request._MAX_API_VERSION)
|
||||
def test_versions_v1(self, version):
|
||||
req = fakes.HTTPRequest.blank('/', base_url='http://localhost/v1')
|
||||
req.method = 'GET'
|
||||
req.content_type = 'application/json'
|
||||
req.headers = {version_header_name: version}
|
||||
|
||||
response = req.get_response(router.APIRouter())
|
||||
self.assertEqual(200, response.status_int)
|
||||
body = jsonutils.loads(response.body)
|
||||
version_list = body['versions']
|
||||
|
||||
ids = [v['id'] for v in version_list]
|
||||
self.assertEqual({'v1.0'}, set(ids))
|
||||
self.assertEqual('1.0', response.headers[version_header_name])
|
||||
self.assertEqual(version_header_name, response.headers['Vary'])
|
||||
self.assertEqual('', version_list[0].get('min_version'))
|
||||
self.assertEqual('', version_list[0].get('version'))
|
||||
self.assertEqual('DEPRECATED', version_list[0].get('status'))
|
||||
|
||||
@ddt.data(api_version_request._MIN_API_VERSION,
|
||||
api_version_request._MAX_API_VERSION)
|
||||
def test_versions_v2(self, version):
|
||||
|
||||
@@ -0,0 +1,6 @@
|
||||
---
|
||||
upgrade:
|
||||
- |
|
||||
The Manila v1 API, first deprecated in the Mitaka (v2.0.0) release, has now
|
||||
been removed. The v2 API with the ``2.0`` microversion is a drop-in
|
||||
replacement.
|
||||
Reference in New Issue
Block a user