api: Stop using wsgi.Controller.api_version to switch between API versions

This obscures the purpose of the APIs and makes the code harder to read.
It also does not play nice with our schema checks by creating multiple
potential functions to check. We're already being pretty inconsistent in
how we do versioning like this so simply standardise on another
approach, namely checking in the function.

Note that there are docs that need updating here, but we're leaving that
to a separate PR that will also remove this method in favour of a new,
much simpler method.

Change-Id: Ia5e4c6cadb6c88ccdf7e89566573f1f89087fbe5
Signed-off-by: Stephen Finucane <stephenfin@redhat.com>
This commit is contained in:
Stephen Finucane
2025-03-27 12:12:09 +00:00
parent 89977c3661
commit d73a0861f8
12 changed files with 330 additions and 453 deletions

View File

@@ -15,6 +15,7 @@
import webob
from nova.api.openstack import api_version_request
from nova.api.openstack.compute.schemas import console_auth_tokens as schema
from nova.api.openstack import wsgi
from nova.api import validation
@@ -29,6 +30,34 @@ CONF = nova.conf.CONF
class ConsoleAuthTokensController(wsgi.Controller):
@wsgi.expected_errors((400, 401, 404), '2.1', '2.30')
@wsgi.expected_errors((400, 404), '2.31', '2.98')
@wsgi.expected_errors((400, 404), '2.99')
@validation.query_schema(schema.show_query, '2.1', '2.98')
@validation.query_schema(schema.show_query_v299, '2.99')
# NOTE(stephenfin): Technically this will never return a response now for
# microversion <= 2.30, (as an exception will be raised instead) but we use
# the same schema for documentation purposes
@validation.response_body_schema(schema.show_response, '2.1', '2.98')
@validation.response_body_schema(schema.show_response_v299, '2.99')
def show(self, req, id):
"""Show console auth token.
Until microversion 2.30, this API was available only for the rdp-html5
console type which has been removed along with the HyperV driver in the
Nova 29.0.0 (Caracal) release. As a result, we now return a HTTP 400
error for microversion <= 2.30. Starting from 2.31 microversion, this
API works for all the other supported console types.
"""
if not api_version_request.is_supported(req, '2.31'):
raise webob.exc.HTTPBadRequest()
include_tls_port = False
if api_version_request.is_supported(req, '2.99'):
include_tls_port = True
return self._show(req, id, include_tls_port=include_tls_port)
def _show(self, req, id, include_tls_port=False):
"""Checks a console auth token and returns the related connect info."""
context = req.environ['nova.context']
@@ -70,36 +99,3 @@ class ConsoleAuthTokensController(wsgi.Controller):
retval['console']['tls_port'] = connect_info.tls_port
return retval
@wsgi.Controller.api_version("2.1", "2.30")
@wsgi.expected_errors((400, 401, 404))
@validation.query_schema(schema.show_query)
# NOTE(stephenfin): Technically this will never return a response now (as
# an exception will be raised instead) but we use the same schema for
# documentation purposes
@validation.response_body_schema(schema.show_response)
def show(self, req, id):
"""Until microversion 2.30, this API was available only for the
rdp-html5 console type which has been removed along with the HyperV
driver in the Nova 29.0.0 (Caracal) release. As this method is for
microversion <= 2.30, it will return an http 400 error. Starting
from 2.31 microversion, this API works for all the supported
console types that are handled by the separate show method
defined below.
"""
raise webob.exc.HTTPBadRequest()
@wsgi.Controller.api_version("2.31") # noqa
@wsgi.Controller.api_version("2.31", "2.98") # noqa
@wsgi.expected_errors((400, 404))
@validation.query_schema(schema.show_query)
@validation.response_body_schema(schema.show_response)
def show(self, req, id): # noqa
return self._show(req, id, include_tls_port=False)
@wsgi.Controller.api_version("2.99") # noqa
@wsgi.expected_errors((400, 404))
@validation.query_schema(schema.show_query_v299)
@validation.response_body_schema(schema.show_response_v299)
def show(self, req, id): # noqa
return self._show(req, id, include_tls_port=True)

View File

@@ -23,7 +23,7 @@ import webob.exc
from nova.api.openstack import api_version_request
from nova.api.openstack import common
from nova.api.openstack.compute.schemas import hypervisors as hyper_schema
from nova.api.openstack.compute.schemas import hypervisors as schema
from nova.api.openstack.compute.views import hypervisors as hyper_view
from nova.api.openstack import wsgi
from nova.api import validation
@@ -223,31 +223,29 @@ class HypervisorsController(wsgi.Controller):
hypervisors_dict['hypervisors_links'] = hypervisors_links
return hypervisors_dict
@wsgi.Controller.api_version("2.53")
@wsgi.expected_errors((400, 404))
@validation.query_schema(hyper_schema.index_query_v253, "2.53")
@wsgi.expected_errors((), '2.1', '2.32')
@wsgi.expected_errors(400, '2.33', '2.52')
@wsgi.expected_errors((400, 404), '2.53')
@validation.query_schema(schema.index_query, '2.1', '2.32')
@validation.query_schema(schema.index_query_v233, '2.33', '2.52')
@validation.query_schema(schema.index_query_v253, '2.53')
def index(self, req):
"""Starting with the 2.53 microversion, the id field in the response
"""List hypervisors.
Starting with the 2.53 microversion, the id field in the response
is the compute_nodes.uuid value. Also, the search and servers routes
are superseded and replaced with query parameters for listing
hypervisors by a hostname pattern and whether or not to include
hosted servers in the response.
"""
limit = None
marker = None
links = False
if api_version_request.is_supported(req, '2.33'):
limit, marker = common.get_limit_and_marker(req)
return self._index(req, limit=limit, marker=marker, links=True)
links = True
@wsgi.Controller.api_version("2.33", "2.52") # noqa
@wsgi.expected_errors(400)
@validation.query_schema(hyper_schema.index_query_v233)
def index(self, req): # noqa
limit, marker = common.get_limit_and_marker(req)
return self._index(req, limit=limit, marker=marker, links=True)
@wsgi.Controller.api_version("2.1", "2.32") # noqa
@wsgi.expected_errors(())
@validation.query_schema(hyper_schema.index_query)
def index(self, req): # noqa
return self._index(req)
return self._index(req, limit=limit, marker=marker, links=links)
def _index(self, req, limit=None, marker=None, links=False):
context = req.environ['nova.context']
@@ -255,31 +253,29 @@ class HypervisorsController(wsgi.Controller):
return self._get_hypervisors(req, detail=False, limit=limit,
marker=marker, links=links)
@wsgi.Controller.api_version("2.53")
@wsgi.expected_errors((400, 404))
@validation.query_schema(hyper_schema.index_query_v253, "2.53")
@wsgi.expected_errors((), '2.1', '2.32')
@wsgi.expected_errors((400), '2.33', '2.52')
@wsgi.expected_errors((400, 404), '2.53')
@validation.query_schema(schema.index_query, '2.1', '2.32')
@validation.query_schema(schema.index_query_v233, '2.33', '2.52')
@validation.query_schema(schema.index_query_v253, '2.53')
def detail(self, req):
"""Starting with the 2.53 microversion, the id field in the response
"""List hypervisors with extra details.
Starting with the 2.53 microversion, the id field in the response
is the compute_nodes.uuid value. Also, the search and servers routes
are superseded and replaced with query parameters for listing
hypervisors by a hostname pattern and whether or not to include
hosted servers in the response.
"""
limit = None
marker = None
links = False
if api_version_request.is_supported(req, '2.33'):
limit, marker = common.get_limit_and_marker(req)
return self._detail(req, limit=limit, marker=marker, links=True)
links = True
@wsgi.Controller.api_version("2.33", "2.52") # noqa
@wsgi.expected_errors((400))
@validation.query_schema(hyper_schema.index_query_v233)
def detail(self, req): # noqa
limit, marker = common.get_limit_and_marker(req)
return self._detail(req, limit=limit, marker=marker, links=True)
@wsgi.Controller.api_version("2.1", "2.32") # noqa
@wsgi.expected_errors(())
@validation.query_schema(hyper_schema.index_query)
def detail(self, req): # noqa
return self._detail(req)
return self._detail(req, limit=limit, marker=marker, links=links)
def _detail(self, req, limit=None, marker=None, links=False):
context = req.environ['nova.context']
@@ -311,25 +307,25 @@ class HypervisorsController(wsgi.Controller):
hypervisor_id)
raise webob.exc.HTTPNotFound(explanation=msg)
@wsgi.Controller.api_version("2.53")
@wsgi.expected_errors((400, 404))
@validation.query_schema(hyper_schema.show_query_v253, "2.53")
@wsgi.expected_errors(404, '2.1', '2.52')
@wsgi.expected_errors((400, 404), '2.53')
@validation.query_schema(schema.show_query, '2.1', '2.52')
@validation.query_schema(schema.show_query_v253, '2.53')
def show(self, req, id):
"""The 2.53 microversion requires that the id is a uuid and as a result
"""Show a hypervisor.
The 2.53 microversion requires that the id is a uuid and as a result
it can also return a 400 response if an invalid uuid is passed.
The 2.53 microversion also supports the with_servers query parameter
to include a list of servers on the given hypervisor if requested.
"""
with_servers = False
if api_version_request.is_supported(req, '2.53'):
with_servers = strutils.bool_from_string(
req.GET.get('with_servers', False), strict=True)
return self._show(req, id, with_servers)
@wsgi.Controller.api_version("2.1", "2.52") # noqa F811
@wsgi.expected_errors(404)
@validation.query_schema(hyper_schema.show_query)
def show(self, req, id): # noqa
return self._show(req, id)
return self._show(req, id, with_servers=with_servers)
def _show(self, req, id, with_servers=False):
context = req.environ['nova.context']
@@ -372,7 +368,7 @@ class HypervisorsController(wsgi.Controller):
@wsgi.Controller.api_version('2.1', '2.87')
@wsgi.expected_errors((400, 404, 501))
@validation.query_schema(hyper_schema.uptime_query)
@validation.query_schema(schema.uptime_query)
def uptime(self, req, id):
"""Prior to microversion 2.88, you could retrieve a special version of
the hypervisor detail view that included uptime. Starting in 2.88, this
@@ -425,7 +421,7 @@ class HypervisorsController(wsgi.Controller):
@wsgi.Controller.api_version('2.1', '2.52')
@wsgi.expected_errors(404)
@validation.query_schema(hyper_schema.search_query)
@validation.query_schema(schema.search_query)
def search(self, req, id):
"""Prior to microversion 2.53 you could search for hypervisors by a
hostname pattern on a dedicated route. Starting with 2.53, searching
@@ -464,7 +460,7 @@ class HypervisorsController(wsgi.Controller):
@wsgi.Controller.api_version('2.1', '2.52')
@wsgi.expected_errors(404)
@validation.query_schema(hyper_schema.servers_query)
@validation.query_schema(schema.servers_query)
def servers(self, req, id):
"""Prior to microversion 2.53 you could search for hypervisors by a
hostname pattern and include servers on those hosts in the response on
@@ -510,7 +506,7 @@ class HypervisorsController(wsgi.Controller):
@wsgi.Controller.api_version('2.1', '2.87')
@wsgi.expected_errors(())
@validation.query_schema(hyper_schema.statistics_query)
@validation.query_schema(schema.statistics_query)
def statistics(self, req):
"""Prior to microversion 2.88, you could get statistics for the
hypervisor. Most of these are now accessible from placement and the few

View File

@@ -19,8 +19,7 @@ from oslo_utils import timeutils
from nova.api.openstack import api_version_request
from nova.api.openstack import common
from nova.api.openstack.compute.schemas \
import instance_actions as schema_instance_actions
from nova.api.openstack.compute.schemas import instance_actions as schema
from nova.api.openstack.compute.views \
import instance_actions as instance_actions_view
from nova.api.openstack import wsgi
@@ -72,41 +71,26 @@ class InstanceActionsController(wsgi.Controller):
event['details'] = event_raw['details']
return event
@wsgi.Controller.api_version("2.1", "2.20")
def _get_instance(self, req, context, server_id):
if not api_version_request.is_supported(req, min_version="2.21"):
return common.get_instance(self.compute_api, context, server_id)
@wsgi.Controller.api_version("2.21") # noqa
def _get_instance(self, req, context, server_id): # noqa
with utils.temporary_mutation(context, read_deleted='yes'):
return common.get_instance(self.compute_api, context, server_id)
@wsgi.Controller.api_version("2.1", "2.57")
@wsgi.expected_errors(404)
@validation.query_schema(schema_instance_actions.list_query)
@wsgi.expected_errors(404, "2.1", "2.57")
@wsgi.expected_errors((400, 404), "2.58")
@validation.query_schema(schema.list_query, "2.1", "2.57")
@validation.query_schema(schema.list_query_v258, "2.58", "2.65")
@validation.query_schema(schema.list_query_v266, "2.66")
def index(self, req, server_id):
"""Returns the list of actions recorded for a given instance."""
context = req.environ["nova.context"]
instance = self._get_instance(req, context, server_id)
context.can(ia_policies.BASE_POLICY_NAME % 'list',
target={'project_id': instance.project_id})
actions_raw = self.action_api.actions_get(context, instance)
actions = [self._format_action(action, ACTION_KEYS)
for action in actions_raw]
return {'instanceActions': actions}
@wsgi.Controller.api_version("2.58") # noqa
@wsgi.expected_errors((400, 404))
@validation.query_schema(schema_instance_actions.list_query_v266,
"2.66")
@validation.query_schema(schema_instance_actions.list_query_v258,
"2.58", "2.65")
def index(self, req, server_id): # noqa
"""Returns the list of actions recorded for a given instance."""
context = req.environ["nova.context"]
instance = self._get_instance(req, context, server_id)
context.can(ia_policies.BASE_POLICY_NAME % 'list',
target={'project_id': instance.project_id})
if api_version_request.is_supported(req, '2.58'):
search_opts = {}
search_opts.update(req.GET)
if 'changes-since' in search_opts:
@@ -124,6 +108,9 @@ class InstanceActionsController(wsgi.Controller):
raise exc.HTTPBadRequest(explanation=msg)
limit, marker = common.get_limit_and_marker(req)
else:
limit, marker, search_opts = None, None, None
try:
actions_raw = self.action_api.actions_get(context, instance,
limit=limit,
@@ -131,16 +118,25 @@ class InstanceActionsController(wsgi.Controller):
filters=search_opts)
except exception.MarkerNotFound as e:
raise exc.HTTPBadRequest(explanation=e.format_message())
if api_version_request.is_supported(req, min_version="2.58"):
actions = [self._format_action(action, ACTION_KEYS_V258)
for action in actions_raw]
else:
actions = [self._format_action(action, ACTION_KEYS)
for action in actions_raw]
actions_dict = {'instanceActions': actions}
actions_links = self._view_builder.get_links(req, server_id, actions)
if actions_links:
if api_version_request.is_supported(req, '2.58'):
if actions_links := self._view_builder.get_links(
req, server_id, actions
):
actions_dict['links'] = actions_links
return actions_dict
@wsgi.expected_errors(404)
@validation.query_schema(schema_instance_actions.show_query)
@validation.query_schema(schema.show_query)
def show(self, req, server_id, id):
"""Return data about the given instance action."""
context = req.environ['nova.context']
@@ -186,8 +182,7 @@ class InstanceActionsController(wsgi.Controller):
# NOTE(brinzhang): Event details are shown since microversion
# 2.84.
show_details = False
support_v284 = api_version_request.is_supported(req, '2.84')
if support_v284:
if api_version_request.is_supported(req, '2.84'):
show_details = context.can(
ia_policies.BASE_POLICY_NAME % 'events:details',
target={'project_id': instance.project_id}, fatal=False)

View File

@@ -40,67 +40,39 @@ class KeypairController(wsgi.Controller):
super(KeypairController, self).__init__()
self.api = compute_api.KeypairAPI()
@wsgi.Controller.api_version("2.10")
@wsgi.response(201)
@wsgi.response(200, "2.0", "2.1")
@wsgi.response(201, "2.2")
@wsgi.expected_errors((400, 403, 409))
@validation.schema(keypairs.create_v20, "2.0", "2.0")
@validation.schema(keypairs.create, "2.1", "2.1")
@validation.schema(keypairs.create_v22, "2.2", "2.9")
@validation.schema(keypairs.create_v210, "2.10", "2.91")
@validation.schema(keypairs.create_v292, "2.92")
def create(self, req, body):
"""Create or import keypair.
Keypair generations are allowed until version 2.91.
Afterwards, only imports are allowed.
Sending name will generate a key and return private_key and
fingerprint. You can send a public_key to add an existing ssh key.
A policy check restricts users from creating keys for other users
Starting in API microversion 2.2, keypairs will have the type ssh or
x509, specified by type.
params: keypair object with:
name (required) - string
public_key (optional or required if >=2.92) - string
type (optional) - string
user_id (optional) - string
Starting in API microversion 2.10, you can request a user if you are an
admin.
Starting in API microversion 2.91, keypair generation is no longer
permitted.
"""
key_type = False
if api_version_request.is_supported(req, '2.2'):
key_type = True
user_id = None
if api_version_request.is_supported(req, '2.10'):
# handle optional user-id for admin only
user_id = body['keypair'].get('user_id')
return self._create(req, body, key_type=True, user_id=user_id)
@wsgi.Controller.api_version("2.2", "2.9") # noqa
@wsgi.response(201)
@wsgi.expected_errors((400, 403, 409))
@validation.schema(keypairs.create_v22)
def create(self, req, body): # noqa
"""Create or import keypair.
Sending name will generate a key and return private_key
and fingerprint.
Keypair will have the type ssh or x509, specified by type.
You can send a public_key to add an existing ssh/x509 key.
params: keypair object with:
name (required) - string
public_key (optional) - string
type (optional) - string
"""
return self._create(req, body, key_type=True)
@wsgi.Controller.api_version("2.1", "2.1") # noqa
@wsgi.expected_errors((400, 403, 409))
@validation.schema(keypairs.create_v20, "2.0", "2.0")
@validation.schema(keypairs.create, "2.1", "2.1")
def create(self, req, body): # noqa
"""Create or import keypair.
Sending name will generate a key and return private_key
and fingerprint.
You can send a public_key to add an existing ssh key.
params: keypair object with:
name (required) - string
public_key (optional) - string
"""
return self._create(req, body)
return self._create(req, body, key_type=key_type, user_id=user_id)
def _create(self, req, body, user_id=None, key_type=False):
context = req.environ['nova.context']
@@ -135,28 +107,23 @@ class KeypairController(wsgi.Controller):
private_key=return_priv_key,
key_type=key_type)
@wsgi.Controller.api_version("2.1", "2.1")
@validation.query_schema(keypairs.delete_query_schema_v20)
@wsgi.response(202)
def _get_user_id(self, req):
if 'user_id' in req.GET.keys():
user_id = req.GET.getall('user_id')[0]
return user_id
@wsgi.response(202, '2.0', '2.1')
@wsgi.response(204, '2.2')
@validation.query_schema(keypairs.delete_query_schema_v20, '2.1', '2.9')
@validation.query_schema(keypairs.delete_query_schema_v210, '2.10', '2.74')
@validation.query_schema(keypairs.delete_query_schema_v275, '2.75')
@wsgi.expected_errors(404)
def delete(self, req, id):
self._delete(req, id)
@wsgi.Controller.api_version("2.2", "2.9") # noqa
@validation.query_schema(keypairs.delete_query_schema_v20)
@wsgi.response(204)
@wsgi.expected_errors(404)
def delete(self, req, id): # noqa
self._delete(req, id)
@wsgi.Controller.api_version("2.10") # noqa
@validation.query_schema(keypairs.delete_query_schema_v275, '2.75')
@validation.query_schema(keypairs.delete_query_schema_v210, '2.10', '2.74')
@wsgi.response(204)
@wsgi.expected_errors(404)
def delete(self, req, id): # noqa
user_id = None
if api_version_request.is_supported(req, '2.10'):
# handle optional user-id for admin only
user_id = self._get_user_id(req)
self._delete(req, id, user_id=user_id)
def _delete(self, req, id, user_id=None):
@@ -171,31 +138,21 @@ class KeypairController(wsgi.Controller):
except exception.KeypairNotFound as exc:
raise webob.exc.HTTPNotFound(explanation=exc.format_message())
def _get_user_id(self, req):
if 'user_id' in req.GET.keys():
user_id = req.GET.getall('user_id')[0]
return user_id
@wsgi.Controller.api_version("2.10")
@validation.query_schema(keypairs.show_query_schema_v20, '2.0', '2.9')
@validation.query_schema(keypairs.show_query_schema_v210, '2.10', '2.74')
@validation.query_schema(keypairs.show_query_schema_v275, '2.75')
@wsgi.expected_errors(404)
def show(self, req, id):
key_type = False
if api_version_request.is_supported(req, '2.2'):
key_type = True
user_id = None
if api_version_request.is_supported(req, '2.10'):
# handle optional user-id for admin only
user_id = self._get_user_id(req)
return self._show(req, id, key_type=True, user_id=user_id)
@wsgi.Controller.api_version("2.2", "2.9") # noqa
@validation.query_schema(keypairs.show_query_schema_v20)
@wsgi.expected_errors(404)
def show(self, req, id): # noqa
return self._show(req, id, key_type=True)
@wsgi.Controller.api_version("2.1", "2.1") # noqa
@validation.query_schema(keypairs.show_query_schema_v20)
@wsgi.expected_errors(404)
def show(self, req, id): # noqa
return self._show(req, id)
return self._show(req, id, key_type=key_type, user_id=user_id)
def _show(self, req, id, key_type=False, user_id=None):
"""Return data for the given key name."""
@@ -210,33 +167,29 @@ class KeypairController(wsgi.Controller):
raise webob.exc.HTTPNotFound(explanation=exc.format_message())
return self._view_builder.show(keypair, key_type=key_type)
@wsgi.Controller.api_version("2.35")
@validation.query_schema(keypairs.index_query_schema_v275, '2.75')
@validation.query_schema(keypairs.index_query_schema_v20, '2.0', '2.9')
@validation.query_schema(keypairs.index_query_schema_v210, '2.10', '2.34')
@validation.query_schema(keypairs.index_query_schema_v235, '2.35', '2.74')
@wsgi.expected_errors(400)
@validation.query_schema(keypairs.index_query_schema_v275, '2.75')
@wsgi.expected_errors((), '2.0', '2.9')
@wsgi.expected_errors(400, '2.10')
def index(self, req):
user_id = self._get_user_id(req)
return self._index(req, key_type=True, user_id=user_id, links=True)
key_type = False
if api_version_request.is_supported(req, '2.2'):
key_type = True
@wsgi.Controller.api_version("2.10", "2.34") # noqa
@validation.query_schema(keypairs.index_query_schema_v210)
@wsgi.expected_errors(())
def index(self, req): # noqa
user_id = None
if api_version_request.is_supported(req, '2.10'):
# handle optional user-id for admin only
user_id = self._get_user_id(req)
return self._index(req, key_type=True, user_id=user_id)
@wsgi.Controller.api_version("2.2", "2.9") # noqa
@validation.query_schema(keypairs.index_query_schema_v20)
@wsgi.expected_errors(())
def index(self, req): # noqa
return self._index(req, key_type=True)
links = False
if api_version_request.is_supported(req, '2.35'):
links = True
@wsgi.Controller.api_version("2.1", "2.1") # noqa
@validation.query_schema(keypairs.index_query_schema_v20)
@wsgi.expected_errors(())
def index(self, req): # noqa
return self._index(req)
return self._index(
req, key_type=key_type, user_id=user_id, links=links
)
def _index(self, req, key_type=False, user_id=None, links=False):
"""List of keypairs for a user."""
@@ -245,7 +198,7 @@ class KeypairController(wsgi.Controller):
context.can(kp_policies.POLICY_ROOT % 'index',
target={'user_id': user_id})
if api_version_request.is_supported(req, min_version='2.35'):
if api_version_request.is_supported(req, '2.35'):
limit, marker = common.get_limit_and_marker(req)
else:
limit = marker = None

View File

@@ -13,14 +13,7 @@
# License for the specific language governing permissions and limitations
# under the License.
from nova.api.openstack.api_version_request \
import MAX_IMAGE_META_PROXY_API_VERSION
from nova.api.openstack.api_version_request \
import MAX_PROXY_API_SUPPORT_VERSION
from nova.api.openstack.api_version_request \
import MIN_WITHOUT_IMAGE_META_PROXY_API_VERSION
from nova.api.openstack.api_version_request \
import MIN_WITHOUT_PROXY_API_SUPPORT_VERSION
from nova.api.openstack import api_version_request
from nova.api.openstack.compute.schemas import limits
from nova.api.openstack.compute.views import limits as limits_views
from nova.api.openstack import wsgi
@@ -44,32 +37,23 @@ FILTERED_LIMITS_2_57.extend(['injected_files', 'injected_file_content_bytes'])
class LimitsController(wsgi.Controller):
"""Controller for accessing limits in the OpenStack API."""
@wsgi.Controller.api_version("2.1", MAX_PROXY_API_SUPPORT_VERSION)
@wsgi.expected_errors(())
@validation.query_schema(limits.limits_query_schema)
def index(self, req):
return self._index(req)
@wsgi.Controller.api_version(MIN_WITHOUT_PROXY_API_SUPPORT_VERSION, # noqa
MAX_IMAGE_META_PROXY_API_VERSION)
@wsgi.expected_errors(())
@validation.query_schema(limits.limits_query_schema)
def index(self, req): # noqa
return self._index(req, FILTERED_LIMITS_2_36)
@wsgi.Controller.api_version( # noqa
MIN_WITHOUT_IMAGE_META_PROXY_API_VERSION, '2.56')
@wsgi.expected_errors(())
@validation.query_schema(limits.limits_query_schema)
def index(self, req): # noqa
return self._index(req, FILTERED_LIMITS_2_36, max_image_meta=False)
@wsgi.Controller.api_version('2.57') # noqa
@wsgi.expected_errors(())
@validation.query_schema(limits.limits_query_schema_275, '2.75')
@validation.query_schema(limits.limits_query_schema, '2.1', '2.56')
@validation.query_schema(limits.limits_query_schema, '2.57', '2.74')
def index(self, req): # noqa
return self._index(req, FILTERED_LIMITS_2_57, max_image_meta=False)
@validation.query_schema(limits.limits_query_schema_275, '2.75')
def index(self, req):
filtered_limits = []
if api_version_request.is_supported(req, '2.57'):
filtered_limits = FILTERED_LIMITS_2_57
elif api_version_request.is_supported(req, '2.36'):
filtered_limits = FILTERED_LIMITS_2_36
max_image_meta = True
if api_version_request.is_supported(req, '2.39'):
max_image_meta = False
return self._index(req, filtered_limits=filtered_limits,
max_image_meta=max_image_meta)
def _index(self, req, filtered_limits=None, max_image_meta=True):
"""Return all global limit information."""
@@ -80,8 +64,7 @@ class LimitsController(wsgi.Controller):
project_id = req.GET.get('tenant_id')
context.can(limits_policies.OTHER_PROJECT_LIMIT_POLICY_NAME)
quotas = QUOTAS.get_project_quotas(context, project_id,
usages=True)
quotas = QUOTAS.get_project_quotas(context, project_id, usages=True)
builder = limits_views.ViewBuilder()
return builder.build(req, quotas, filtered_limits=filtered_limits,
max_image_meta=max_image_meta)

View File

@@ -15,7 +15,7 @@ from webob import exc
from nova.api.openstack import api_version_request
from nova.api.openstack import common
from nova.api.openstack.compute.schemas import migrations as schema_migrations
from nova.api.openstack.compute.schemas import migrations as schema
from nova.api.openstack.compute.views import migrations as migrations_view
from nova.api.openstack import wsgi
from nova.api import validation
@@ -145,47 +145,41 @@ class MigrationsController(wsgi.Controller):
migrations_dict['migrations_links'] = migrations_links
return migrations_dict
@wsgi.Controller.api_version("2.1", "2.22") # noqa
@wsgi.expected_errors(())
@validation.query_schema(schema_migrations.list_query_schema_v20,
"2.0", "2.22")
@wsgi.expected_errors((), "2.1", "2.58")
@wsgi.expected_errors(400, "2.59")
@validation.query_schema(schema.list_query_schema_v20, "2.0", "2.22")
@validation.query_schema(schema.list_query_schema_v20, "2.23", "2.58")
@validation.query_schema(schema.list_query_params_v259, "2.59", "2.65")
@validation.query_schema(schema.list_query_params_v266, "2.66", "2.79")
@validation.query_schema(schema.list_query_params_v280, "2.80")
def index(self, req):
"""Return all migrations using the query parameters as filters."""
return self._index(req)
add_link = False
if api_version_request.is_supported(req, '2.23'):
add_link = True
@wsgi.Controller.api_version("2.23", "2.58") # noqa
@wsgi.expected_errors(())
@validation.query_schema(schema_migrations.list_query_schema_v20,
"2.23", "2.58")
def index(self, req): # noqa
"""Return all migrations using the query parameters as filters."""
return self._index(req, add_link=True)
@wsgi.Controller.api_version("2.59", "2.65") # noqa
@wsgi.expected_errors(400)
@validation.query_schema(schema_migrations.list_query_params_v259,
"2.59", "2.65")
def index(self, req): # noqa
"""Return all migrations using the query parameters as filters."""
next_link = False
add_uuid = False
sort_keys = None
sort_dirs = None
limit = None
marker = None
allow_changes_since = False
if api_version_request.is_supported(req, '2.59'):
next_link = True
add_uuid = True
sort_keys = ['created_at', 'id']
# FIXME(stephenfin): This looks like a typo?
sort_dirs = ['desc', 'desc']
limit, marker = common.get_limit_and_marker(req)
return self._index(req, add_link=True, next_link=True, add_uuid=True,
sort_keys=['created_at', 'id'],
sort_dirs=['desc', 'desc'],
limit=limit, marker=marker,
allow_changes_since=True)
allow_changes_since = True
@wsgi.Controller.api_version("2.66") # noqa
@wsgi.expected_errors(400)
@validation.query_schema(schema_migrations.list_query_params_v266,
"2.66", "2.79")
@validation.query_schema(schema_migrations.list_query_params_v280,
"2.80")
def index(self, req): # noqa
"""Return all migrations using the query parameters as filters."""
limit, marker = common.get_limit_and_marker(req)
return self._index(req, add_link=True, next_link=True, add_uuid=True,
sort_keys=['created_at', 'id'],
sort_dirs=['desc', 'desc'],
limit=limit, marker=marker,
allow_changes_since=True,
allow_changes_before=True)
allow_changes_before = False
if api_version_request.is_supported(req, '2.66'):
allow_changes_before = True
return self._index(
req, add_link=add_link, next_link=next_link, add_uuid=add_uuid,
sort_keys=sort_keys, sort_dirs=sort_dirs, limit=limit,
marker=marker, allow_changes_since=allow_changes_since,
allow_changes_before=allow_changes_before)

View File

@@ -16,6 +16,7 @@
import copy
import webob
from nova.api.openstack import api_version_request
from nova.api.openstack.compute.schemas import quota_classes
from nova.api.openstack import wsgi
from nova.api import validation
@@ -76,23 +77,27 @@ class QuotaClassSetsController(wsgi.Controller):
return dict(quota_class_set=result)
@wsgi.Controller.api_version('2.1', '2.49')
def _get_filtered_quotas(self, req):
if api_version_request.is_supported(req, '2.57'):
return FILTERED_QUOTAS_2_57
elif api_version_request.is_supported(req, '2.50'):
return FILTERED_QUOTAS_2_50
else:
return []
@wsgi.expected_errors(())
@validation.query_schema(quota_classes.show_query)
def show(self, req, id):
return self._show(req, id, exclude_server_groups=True)
filtered_quotas = self._get_filtered_quotas(req)
@wsgi.Controller.api_version('2.50', '2.56') # noqa
@wsgi.expected_errors(())
@validation.query_schema(quota_classes.show_query)
def show(self, req, id): # noqa
return self._show(req, id, FILTERED_QUOTAS_2_50)
exclude_server_groups = True
if api_version_request.is_supported(req, '2.50'):
exclude_server_groups = False
@wsgi.Controller.api_version('2.57') # noqa
@wsgi.expected_errors(())
@validation.query_schema(quota_classes.show_query)
def show(self, req, id): # noqa
return self._show(req, id, FILTERED_QUOTAS_2_57)
return self._show(
req, id, filtered_quotas=filtered_quotas,
exclude_server_groups=exclude_server_groups,
)
def _show(self, req, id, filtered_quotas=None,
exclude_server_groups=False):
@@ -102,23 +107,21 @@ class QuotaClassSetsController(wsgi.Controller):
return self._format_quota_set(id, values, filtered_quotas,
exclude_server_groups)
@wsgi.Controller.api_version("2.1", "2.49") # noqa
@wsgi.expected_errors(400)
@validation.schema(quota_classes.update)
@validation.schema(quota_classes.update, '2.1', '2.49')
@validation.schema(quota_classes.update_v250, '2.50', '2.56')
@validation.schema(quota_classes.update_v257, '2.57')
def update(self, req, id, body):
return self._update(req, id, body, exclude_server_groups=True)
filtered_quotas = self._get_filtered_quotas(req)
@wsgi.Controller.api_version("2.50", "2.56") # noqa
@wsgi.expected_errors(400)
@validation.schema(quota_classes.update_v250)
def update(self, req, id, body): # noqa
return self._update(req, id, body, FILTERED_QUOTAS_2_50)
exclude_server_groups = True
if api_version_request.is_supported(req, '2.50'):
exclude_server_groups = False
@wsgi.Controller.api_version("2.57") # noqa
@wsgi.expected_errors(400)
@validation.schema(quota_classes.update_v257)
def update(self, req, id, body): # noqa
return self._update(req, id, body, FILTERED_QUOTAS_2_57)
return self._update(
req, id, body, filtered_quotas=filtered_quotas,
exclude_server_groups=exclude_server_groups,
)
def _update(self, req, id, body, filtered_quotas=None,
exclude_server_groups=False):

View File

@@ -18,10 +18,7 @@ from urllib import parse as urlparse
from oslo_utils import strutils
import webob
from nova.api.openstack.api_version_request \
import MAX_PROXY_API_SUPPORT_VERSION
from nova.api.openstack.api_version_request \
import MIN_WITHOUT_PROXY_API_SUPPORT_VERSION
from nova.api.openstack import api_version_request
from nova.api.openstack.compute.schemas import quota_sets
from nova.api.openstack import identity
from nova.api.openstack import wsgi
@@ -105,24 +102,21 @@ class QuotaSetsController(wsgi.Controller):
else:
return {k: v['limit'] for k, v in values.items()}
@wsgi.Controller.api_version("2.1", MAX_PROXY_API_SUPPORT_VERSION)
@wsgi.expected_errors(400)
@validation.query_schema(quota_sets.show_query, '2.0', '2.74')
def show(self, req, id):
return self._show(req, id, [])
def _get_filtered_quotas(self, req):
if api_version_request.is_supported(req, '2.57'):
return FILTERED_QUOTAS_2_57
elif api_version_request.is_supported(req, '2.36'):
return FILTERED_QUOTAS_2_36
else:
return []
@wsgi.Controller.api_version(MIN_WITHOUT_PROXY_API_SUPPORT_VERSION, '2.56')
@wsgi.expected_errors(400)
@validation.query_schema(quota_sets.show_query, '2.0', '2.74')
def show(self, req, id): # noqa
return self._show(req, id, FILTERED_QUOTAS_2_36)
@wsgi.Controller.api_version('2.57') # noqa
@wsgi.Controller.api_version('2.1')
@wsgi.expected_errors(400)
@validation.query_schema(quota_sets.show_query, '2.0', '2.74')
@validation.query_schema(quota_sets.show_query_v275, '2.75')
def show(self, req, id): # noqa
return self._show(req, id, FILTERED_QUOTAS_2_57)
def show(self, req, id):
filtered_quotas = self._get_filtered_quotas(req)
return self._show(req, id, filtered_quotas)
def _show(self, req, id, filtered_quotas):
context = req.environ['nova.context']
@@ -131,29 +125,17 @@ class QuotaSetsController(wsgi.Controller):
params = urlparse.parse_qs(req.environ.get('QUERY_STRING', ''))
user_id = params.get('user_id', [None])[0]
return self._format_quota_set(id,
return self._format_quota_set(
id,
self._get_quotas(context, id, user_id=user_id),
filtered_quotas=filtered_quotas)
@wsgi.Controller.api_version("2.1", MAX_PROXY_API_SUPPORT_VERSION)
@wsgi.expected_errors(400)
@validation.query_schema(quota_sets.show_query, '2.0', '2.74')
@validation.query_schema(quota_sets.show_query_v275, '2.75')
def detail(self, req, id):
return self._detail(req, id, [])
@wsgi.Controller.api_version(MIN_WITHOUT_PROXY_API_SUPPORT_VERSION, '2.56')
@wsgi.expected_errors(400)
@validation.query_schema(quota_sets.show_query, '2.0', '2.74')
@validation.query_schema(quota_sets.show_query_v275, '2.75')
def detail(self, req, id): # noqa
return self._detail(req, id, FILTERED_QUOTAS_2_36)
@wsgi.Controller.api_version('2.57') # noqa
@wsgi.expected_errors(400)
@validation.query_schema(quota_sets.show_query, '2.0', '2.74')
@validation.query_schema(quota_sets.show_query_v275, '2.75')
def detail(self, req, id): # noqa
return self._detail(req, id, FILTERED_QUOTAS_2_57)
filtered_quotas = self._get_filtered_quotas(req)
return self._detail(req, id, filtered_quotas)
def _detail(self, req, id, filtered_quotas):
context = req.environ['nova.context']
@@ -166,26 +148,16 @@ class QuotaSetsController(wsgi.Controller):
self._get_quotas(context, id, user_id=user_id, usages=True),
filtered_quotas=filtered_quotas)
@wsgi.Controller.api_version("2.1", MAX_PROXY_API_SUPPORT_VERSION)
@wsgi.Controller.api_version('2.1')
@wsgi.expected_errors(400)
@validation.schema(quota_sets.update)
def update(self, req, id, body):
return self._update(req, id, body, [])
@wsgi.Controller.api_version(MIN_WITHOUT_PROXY_API_SUPPORT_VERSION, '2.56')
@wsgi.expected_errors(400)
@validation.schema(quota_sets.update_v236)
@validation.query_schema(quota_sets.show_query, '2.0', '2.74')
def update(self, req, id, body): # noqa
return self._update(req, id, body, FILTERED_QUOTAS_2_36)
@wsgi.Controller.api_version('2.57') # noqa
@wsgi.expected_errors(400)
@validation.schema(quota_sets.update_v257)
@validation.schema(quota_sets.update, '2.0', '2.35')
@validation.schema(quota_sets.update_v236, '2.36', '2.56')
@validation.schema(quota_sets.update_v257, '2.57')
@validation.query_schema(quota_sets.show_query, '2.0', '2.74')
@validation.query_schema(quota_sets.show_query_v275, '2.75')
def update(self, req, id, body): # noqa
return self._update(req, id, body, FILTERED_QUOTAS_2_57)
def update(self, req, id, body):
filtered_quotas = self._get_filtered_quotas(req)
return self._update(req, id, body, filtered_quotas)
def _update(self, req, id, body, filtered_quotas):
context = req.environ['nova.context']
@@ -249,23 +221,12 @@ class QuotaSetsController(wsgi.Controller):
self._get_quotas(context, id, user_id=user_id),
filtered_quotas=filtered_quotas)
@wsgi.Controller.api_version("2.0", MAX_PROXY_API_SUPPORT_VERSION)
@wsgi.Controller.api_version('2.0')
@wsgi.expected_errors(400)
@validation.query_schema(quota_sets.defaults_query)
def defaults(self, req, id):
return self._defaults(req, id, [])
@wsgi.Controller.api_version(MIN_WITHOUT_PROXY_API_SUPPORT_VERSION, '2.56')
@wsgi.expected_errors(400)
@validation.query_schema(quota_sets.defaults_query)
def defaults(self, req, id): # noqa
return self._defaults(req, id, FILTERED_QUOTAS_2_36)
@wsgi.Controller.api_version('2.57') # noqa
@wsgi.expected_errors(400)
@validation.query_schema(quota_sets.defaults_query)
def defaults(self, req, id): # noqa
return self._defaults(req, id, FILTERED_QUOTAS_2_57)
filtered_quotas = self._get_filtered_quotas(req)
return self._defaults(req, id, filtered_quotas)
def _defaults(self, req, id, filtered_quotas):
context = req.environ['nova.context']

View File

@@ -53,7 +53,7 @@ service_update_v211 = {
# be specified in the body. If status=='disabled', then 'disabled_reason' is
# also checked in the body but is not required. Requesting status='enabled' and
# including a 'disabled_reason' results in a 400, but this is checked in code.
service_update_v2_53 = {
service_update_v253 = {
'type': 'object',
'properties': {
'status': {

View File

@@ -51,8 +51,8 @@ show_query_v240 = copy.deepcopy(show_query)
show_query_v240['properties'].update(
parameter_types.pagination_parameters)
index_query_275 = copy.deepcopy(index_query_v240)
index_query_275['additionalProperties'] = False
index_query_v275 = copy.deepcopy(index_query_v240)
index_query_v275['additionalProperties'] = False
show_query_275 = copy.deepcopy(show_query_v240)
show_query_275['additionalProperties'] = False
show_query_v275 = copy.deepcopy(show_query_v240)
show_query_v275['additionalProperties'] = False

View File

@@ -369,9 +369,9 @@ class ServiceController(wsgi.Controller):
'in-progress migrations. Complete the '
'migrations or delete the instances first.'))
@validation.query_schema(services.index_query_schema_275, '2.75')
@validation.query_schema(services.index_query_schema, '2.0', '2.74')
@wsgi.expected_errors(())
@validation.query_schema(services.index_query_schema, '2.0', '2.74')
@validation.query_schema(services.index_query_schema_275, '2.75')
def index(self, req):
"""Return a list of all running services. Filter by host & service
name
@@ -385,10 +385,10 @@ class ServiceController(wsgi.Controller):
return {'services': _services}
@wsgi.Controller.api_version('2.1', '2.52')
@wsgi.expected_errors((400, 404))
@validation.schema(services.service_update, '2.0', '2.10')
@validation.schema(services.service_update_v211, '2.11', '2.52')
@validation.schema(services.service_update_v253, '2.53')
def update(self, req, id, body):
"""Perform service update
@@ -396,7 +396,17 @@ class ServiceController(wsgi.Controller):
to identify the service on which to perform the action. There is no
service ID passed on the path, just the action, for example
PUT /os-services/disable.
Starting with microversion 2.53, the service uuid is passed in on the
path of the request to uniquely identify the service record on which to
perform a given update, which is defined in the body of the request.
"""
if api_version_request.is_supported(req, min_version='2.53'):
return self._update_v253(req, id, body)
else:
return self._update_v21(req, id, body)
def _update_v21(self, req, id, body):
context = req.environ['nova.context']
context.can(services_policies.BASE_POLICY_NAME % 'update', target={})
if api_version_request.is_supported(req, min_version='2.11'):
@@ -407,16 +417,7 @@ class ServiceController(wsgi.Controller):
return self._perform_action(req, id, body, actions)
@wsgi.Controller.api_version('2.53') # noqa F811
@wsgi.expected_errors((400, 404))
@validation.schema(services.service_update_v2_53, '2.53')
def update(self, req, id, body): # noqa
"""Perform service update
Starting with microversion 2.53, the service uuid is passed in on the
path of the request to uniquely identify the service record on which to
perform a given update, which is defined in the body of the request.
"""
def _update_v253(self, req, id, body):
service_id = id
# Validate that the service ID is a UUID.
if not uuidutils.is_uuid_like(service_id):

View File

@@ -21,6 +21,7 @@ import iso8601
from oslo_utils import timeutils
from webob import exc
from nova.api.openstack import api_version_request
from nova.api.openstack import common
from nova.api.openstack.compute.schemas import simple_tenant_usage as schema
from nova.api.openstack.compute.views import usages as usages_view
@@ -261,35 +262,17 @@ class SimpleTenantUsageController(wsgi.Controller):
detailed = env.get('detailed', ['0'])[0] == '1'
return (period_start, period_stop, detailed)
@wsgi.Controller.api_version("2.40")
@validation.query_schema(schema.index_query_275, '2.75')
@validation.query_schema(schema.index_query, '2.1', '2.39')
@validation.query_schema(schema.index_query_v240, '2.40', '2.74')
@validation.query_schema(schema.index_query_v275, '2.75')
@wsgi.expected_errors(400)
def index(self, req):
"""Retrieve tenant_usage for all tenants."""
return self._index(req, links=True)
links = False
if api_version_request.is_supported(req, '2.40'):
links = True
@wsgi.Controller.api_version("2.1", "2.39") # noqa
@validation.query_schema(schema.index_query)
@wsgi.expected_errors(400)
def index(self, req): # noqa
"""Retrieve tenant_usage for all tenants."""
return self._index(req)
@wsgi.Controller.api_version("2.40")
@validation.query_schema(schema.show_query_275, '2.75')
@validation.query_schema(schema.show_query_v240, '2.40', '2.74')
@wsgi.expected_errors(400)
def show(self, req, id):
"""Retrieve tenant_usage for a specified tenant."""
return self._show(req, id, links=True)
@wsgi.Controller.api_version("2.1", "2.39") # noqa
@validation.query_schema(schema.show_query)
@wsgi.expected_errors(400)
def show(self, req, id): # noqa
"""Retrieve tenant_usage for a specified tenant."""
return self._show(req, id)
return self._index(req, links=links)
def _index(self, req, links=False):
context = req.environ['nova.context']
@@ -327,6 +310,18 @@ class SimpleTenantUsageController(wsgi.Controller):
return tenant_usages
@validation.query_schema(schema.show_query, '2.1', '2.39')
@validation.query_schema(schema.show_query_v240, '2.40', '2.74')
@validation.query_schema(schema.show_query_v275, '2.75')
@wsgi.expected_errors(400)
def show(self, req, id):
"""Retrieve tenant_usage for a specified tenant."""
links = False
if api_version_request.is_supported(req, '2.40'):
links = True
return self._show(req, id, links=links)
def _show(self, req, id, links=False):
tenant_id = id
context = req.environ['nova.context']