heat-cfntools/heat/api/middleware/version_negotiation.py

124 lines
4.9 KiB
Python

# vim: tabstop=4 shiftwidth=4 softtabstop=4
# Copyright 2011 OpenStack LLC.
# 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.
"""
A filter middleware that inspects the requested URI for a version string
and/or Accept headers and attempts to negotiate an API controller to
return
"""
import logging
import re
import routes
from heat.api import v1
from heat.api import versions
from heat.common import wsgi
logger = logging.getLogger('heat.api.middleware.version_negotiation')
class VersionNegotiationFilter(wsgi.Middleware):
def __init__(self, app, conf, **local_conf):
self.versions_app = versions.Controller(conf)
self.version_uri_regex = re.compile(r"^v(\d+)\.?(\d+)?")
self.conf = conf
super(VersionNegotiationFilter, self).__init__(app)
def process_request(self, req):
"""
If there is a version identifier in the URI, simply
return the correct API controller, otherwise, if we
find an Accept: header, process it
"""
# See if a version identifier is in the URI passed to
# us already. If so, simply return the right version
# API controller
msg = _("Processing request: %(method)s %(path)s Accept: "
"%(accept)s") % ({'method': req.method,
'path': req.path, 'accept': req.accept})
logger.debug(msg)
# If the request is for /versions, just return the versions container
if req.path_info_peek() == "versions":
return self.versions_app
match = self._match_version_string(req.path_info_peek(), req)
if match:
if (req.environ['api.major_version'] == 1 and
req.environ['api.minor_version'] == 0):
logger.debug(_("Matched versioned URI. Version: %d.%d"),
req.environ['api.major_version'],
req.environ['api.minor_version'])
# Strip the version from the path
req.path_info_pop()
return None
else:
logger.debug(_("Unknown version in versioned URI: %d.%d. "
"Returning version choices."),
req.environ['api.major_version'],
req.environ['api.minor_version'])
return self.versions_app
accept = str(req.accept)
if accept.startswith('application/vnd.openstack.images-'):
token_loc = len('application/vnd.openstack.images-')
accept_version = accept[token_loc:]
match = self._match_version_string(accept_version, req)
if match:
if (req.environ['api.major_version'] == 1 and
req.environ['api.minor_version'] == 0):
logger.debug(_("Matched versioned media type. "
"Version: %d.%d"),
req.environ['api.major_version'],
req.environ['api.minor_version'])
return None
else:
logger.debug(_("Unknown version in accept header: %d.%d..."
"returning version choices."),
req.environ['api.major_version'],
req.environ['api.minor_version'])
return self.versions_app
else:
if req.accept not in ('*/*', ''):
logger.debug(_("Unknown accept header: %s..."
"returning version choices."), req.accept)
return self.versions_app
return None
def _match_version_string(self, subject, req):
"""
Given a subject string, tries to match a major and/or
minor version number. If found, sets the api.major_version
and api.minor_version environ variables.
Returns True if there was a match, false otherwise.
:param subject: The string to check
:param req: Webob.Request object
"""
match = self.version_uri_regex.match(subject)
if match:
major_version, minor_version = match.groups(0)
major_version = int(major_version)
minor_version = int(minor_version)
req.environ['api.major_version'] = major_version
req.environ['api.minor_version'] = minor_version
return match is not None