]> review.fuel-infra Code Review - openstack-build/neutron-build.git/commitdiff
Migrate to oslo.middleware
authorgordon chung <gord@live.ca>
Wed, 8 Oct 2014 21:51:47 +0000 (17:51 -0400)
committerIhar Hrachyshka <ihrachys@redhat.com>
Wed, 26 Nov 2014 21:12:21 +0000 (22:12 +0100)
Synced middleware module from incubator instead of removing it
completely. This is needed for grenade and to keep backwards
compatibility with existing installations with old api-paste.ini.

'log' module is updated as a dependency for middleware module.

'versionutils' are added as a new dependency for middleware module.

Closes-Bug: #1371701
Change-Id: Ib1c3161ccc98642091134f2285fed7c90244e600
Co-Authored-By: Ihar Hrachyshka <ihrachys@redhat.com>
13 files changed:
etc/api-paste.ini
neutron/auth.py
neutron/openstack/common/log.py
neutron/openstack/common/middleware/base.py [deleted file]
neutron/openstack/common/middleware/catch_errors.py
neutron/openstack/common/middleware/correlation_id.py [deleted file]
neutron/openstack/common/middleware/debug.py [deleted file]
neutron/openstack/common/middleware/request_id.py
neutron/openstack/common/middleware/sizelimit.py [deleted file]
neutron/openstack/common/versionutils.py [new file with mode: 0644]
neutron/tests/unit/test_auth.py
openstack-common.conf
requirements.txt

index bbcd41527df396749f7ce906b0de64c8d91fc9da..29f01e20b24b826af2597e450db0a1d07cf4d878 100644 (file)
@@ -9,10 +9,10 @@ noauth = request_id catch_errors extensions neutronapiapp_v2_0
 keystone = request_id catch_errors authtoken keystonecontext extensions neutronapiapp_v2_0
 
 [filter:request_id]
-paste.filter_factory = neutron.openstack.common.middleware.request_id:RequestIdMiddleware.factory
+paste.filter_factory = oslo.middleware:RequestId.factory
 
 [filter:catch_errors]
-paste.filter_factory = neutron.openstack.common.middleware.catch_errors:CatchErrorsMiddleware.factory
+paste.filter_factory = oslo.middleware:CatchErrors.factory
 
 [filter:keystonecontext]
 paste.filter_factory = neutron.auth:NeutronKeystoneContext.factory
index a91ab6d31ae343caf137daf02b27fbd6f8c88723..c0852f72a455d45cb6511fcdd2f4435776bc78fe 100644 (file)
 #    under the License.
 
 from oslo.config import cfg
+from oslo.middleware import request_id
 import webob.dec
 import webob.exc
 
 from neutron import context
 from neutron.openstack.common import log as logging
-from neutron.openstack.common.middleware import request_id
 from neutron import wsgi
 
 LOG = logging.getLogger(__name__)
index a6f00b0c20e8c6641edce53e58665be0a95a09fc..ddb1891ad91193b3ae7f4fcf4435186658e3fe6b 100644 (file)
@@ -509,14 +509,9 @@ def _setup_logging_from_conf(project, version):
         log_root.addHandler(streamlog)
 
     if CONF.publish_errors:
-        try:
-            handler = importutils.import_object(
-                "neutron.openstack.common.log_handler.PublishErrorsHandler",
-                logging.ERROR)
-        except ImportError:
-            handler = importutils.import_object(
-                "oslo.messaging.notify.log_handler.PublishErrorsHandler",
-                logging.ERROR)
+        handler = importutils.import_object(
+            "oslo.messaging.notify.log_handler.PublishErrorsHandler",
+            logging.ERROR)
         log_root.addHandler(handler)
 
     datefmt = CONF.log_date_format
diff --git a/neutron/openstack/common/middleware/base.py b/neutron/openstack/common/middleware/base.py
deleted file mode 100644 (file)
index 464a1cc..0000000
+++ /dev/null
@@ -1,56 +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.
-
-"""Base class(es) for WSGI Middleware."""
-
-import webob.dec
-
-
-class Middleware(object):
-    """Base WSGI middleware wrapper.
-
-    These classes require an application to be initialized that will be called
-    next.  By default the middleware will simply call its wrapped app, or you
-    can override __call__ to customize its behavior.
-    """
-
-    @classmethod
-    def factory(cls, global_conf, **local_conf):
-        """Factory method for paste.deploy."""
-        return cls
-
-    def __init__(self, application):
-        self.application = application
-
-    def process_request(self, req):
-        """Called on each request.
-
-        If this returns None, the next application down the stack will be
-        executed. If it returns a response then that response will be returned
-        and execution will stop here.
-        """
-        return None
-
-    def process_response(self, response):
-        """Do whatever you'd like to the response."""
-        return response
-
-    @webob.dec.wsgify
-    def __call__(self, req):
-        response = self.process_request(req)
-        if response:
-            return response
-        response = req.get_response(self.application)
-        return self.process_response(response)
index b692aeec8dc0a48c8a030dcf621ae7c717cd1ec2..d5dca1513a68ba8c42dc74377f8e69109e09868e 100644 (file)
@@ -1,6 +1,3 @@
-# Copyright (c) 2013 NEC Corporation
-# 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
 #    License for the specific language governing permissions and limitations
 #    under the License.
 
-"""Middleware that provides high-level error handling.
-
-It catches all exceptions from subsequent applications in WSGI pipeline
-to hide internal errors from API response.
-"""
-
-import webob.dec
-import webob.exc
-
-from neutron.openstack.common.gettextutils import _  # noqa
-from neutron.openstack.common import log as logging
-from neutron.openstack.common.middleware import base
-
+"""Compatibility shim for Kilo, while operators migrate to oslo.middleware."""
 
-LOG = logging.getLogger(__name__)
+from oslo.middleware import catch_errors
 
+from neutron.openstack.common import versionutils
 
-class CatchErrorsMiddleware(base.Middleware):
 
-    @webob.dec.wsgify
-    def __call__(self, req):
-        try:
-            response = req.get_response(self.application)
-        except Exception:
-            LOG.exception(_('An error occurred during '
-                            'processing the request: %s'))
-            response = webob.exc.HTTPInternalServerError()
-        return response
+@versionutils.deprecated(as_of=versionutils.deprecated.KILO,
+                         in_favor_of='oslo.middleware.CatchErrors')
+class CatchErrorsMiddleware(catch_errors.CatchErrors):
+    pass
diff --git a/neutron/openstack/common/middleware/correlation_id.py b/neutron/openstack/common/middleware/correlation_id.py
deleted file mode 100644 (file)
index 80ee63f..0000000
+++ /dev/null
@@ -1,28 +0,0 @@
-# Copyright (c) 2013 Rackspace Hosting
-# 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.
-
-"""Middleware that attaches a correlation id to WSGI request"""
-
-import uuid
-
-from neutron.openstack.common.middleware import base
-
-
-class CorrelationIdMiddleware(base.Middleware):
-
-    def process_request(self, req):
-        correlation_id = (req.headers.get("X_CORRELATION_ID") or
-                          str(uuid.uuid4()))
-        req.headers['X_CORRELATION_ID'] = correlation_id
diff --git a/neutron/openstack/common/middleware/debug.py b/neutron/openstack/common/middleware/debug.py
deleted file mode 100644 (file)
index 5ab9605..0000000
+++ /dev/null
@@ -1,60 +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.
-
-"""Debug middleware"""
-
-from __future__ import print_function
-
-import sys
-
-import six
-import webob.dec
-
-from neutron.openstack.common.middleware import base
-
-
-class Debug(base.Middleware):
-    """Helper class that returns debug information.
-
-    Can be inserted into any WSGI application chain to get information about
-    the request and response.
-    """
-
-    @webob.dec.wsgify
-    def __call__(self, req):
-        print(("*" * 40) + " REQUEST ENVIRON")
-        for key, value in req.environ.items():
-            print(key, "=", value)
-        print()
-        resp = req.get_response(self.application)
-
-        print(("*" * 40) + " RESPONSE HEADERS")
-        for (key, value) in six.iteritems(resp.headers):
-            print(key, "=", value)
-        print()
-
-        resp.app_iter = self.print_generator(resp.app_iter)
-
-        return resp
-
-    @staticmethod
-    def print_generator(app_iter):
-        """Prints the contents of a wrapper string iterator when iterated."""
-        print(("*" * 40) + " BODY")
-        for part in app_iter:
-            sys.stdout.write(part)
-            sys.stdout.flush()
-            yield part
-        print()
index 5c2620cce42f54f2073fa11bf27667723803de0b..7fea9c3e0598afe590ef38c1842c69e368d06f9b 100644 (file)
@@ -1,6 +1,3 @@
-# Copyright (c) 2013 NEC Corporation
-# 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
 #    License for the specific language governing permissions and limitations
 #    under the License.
 
-"""Middleware that ensures request ID.
-
-It ensures to assign request ID for each API request and set it to
-request environment. The request ID is also added to API response.
-"""
+"""Compatibility shim for Kilo, while operators migrate to oslo.middleware."""
 
-import webob.dec
+from oslo.middleware import request_id
 
-from neutron.openstack.common import context
-from neutron.openstack.common.middleware import base
+from neutron.openstack.common import versionutils
 
 
 ENV_REQUEST_ID = 'openstack.request_id'
 HTTP_RESP_HEADER_REQUEST_ID = 'x-openstack-request-id'
 
 
-class RequestIdMiddleware(base.Middleware):
-
-    @webob.dec.wsgify
-    def __call__(self, req):
-        req_id = context.generate_request_id()
-        req.environ[ENV_REQUEST_ID] = req_id
-        response = req.get_response(self.application)
-        if HTTP_RESP_HEADER_REQUEST_ID not in response.headers:
-            response.headers.add(HTTP_RESP_HEADER_REQUEST_ID, req_id)
-        return response
+@versionutils.deprecated(as_of=versionutils.deprecated.KILO,
+                         in_favor_of='oslo.middleware.RequestId')
+class RequestIdMiddleware(request_id.RequestId):
+    pass
diff --git a/neutron/openstack/common/middleware/sizelimit.py b/neutron/openstack/common/middleware/sizelimit.py
deleted file mode 100644 (file)
index 56b3200..0000000
+++ /dev/null
@@ -1,81 +0,0 @@
-# Copyright (c) 2012 Red Hat, Inc.
-#
-#    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.
-
-"""
-Request Body limiting middleware.
-
-"""
-
-from oslo.config import cfg
-import webob.dec
-import webob.exc
-
-from neutron.openstack.common.gettextutils import _
-from neutron.openstack.common.middleware import base
-
-
-#default request size is 112k
-max_req_body_size = cfg.IntOpt('max_request_body_size',
-                               deprecated_name='osapi_max_request_body_size',
-                               default=114688,
-                               help='the maximum body size '
-                                    'per each request(bytes)')
-
-CONF = cfg.CONF
-CONF.register_opt(max_req_body_size)
-
-
-class LimitingReader(object):
-    """Reader to limit the size of an incoming request."""
-    def __init__(self, data, limit):
-        """Initiates LimitingReader object.
-
-        :param data: Underlying data object
-        :param limit: maximum number of bytes the reader should allow
-        """
-        self.data = data
-        self.limit = limit
-        self.bytes_read = 0
-
-    def __iter__(self):
-        for chunk in self.data:
-            self.bytes_read += len(chunk)
-            if self.bytes_read > self.limit:
-                msg = _("Request is too large.")
-                raise webob.exc.HTTPRequestEntityTooLarge(explanation=msg)
-            else:
-                yield chunk
-
-    def read(self, i=None):
-        result = self.data.read(i)
-        self.bytes_read += len(result)
-        if self.bytes_read > self.limit:
-            msg = _("Request is too large.")
-            raise webob.exc.HTTPRequestEntityTooLarge(explanation=msg)
-        return result
-
-
-class RequestBodySizeLimiter(base.Middleware):
-    """Limit the size of incoming requests."""
-
-    @webob.dec.wsgify
-    def __call__(self, req):
-        if req.content_length > CONF.max_request_body_size:
-            msg = _("Request is too large.")
-            raise webob.exc.HTTPRequestEntityTooLarge(explanation=msg)
-        if req.content_length is None and req.is_body_readable:
-            limiter = LimitingReader(req.body_file,
-                                     CONF.max_request_body_size)
-            req.body_file = limiter
-        return self.application
diff --git a/neutron/openstack/common/versionutils.py b/neutron/openstack/common/versionutils.py
new file mode 100644 (file)
index 0000000..7f1e023
--- /dev/null
@@ -0,0 +1,203 @@
+# Copyright (c) 2013 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.
+
+"""
+Helpers for comparing version strings.
+"""
+
+import functools
+import inspect
+
+import pkg_resources
+import six
+
+from neutron.openstack.common._i18n import _
+from neutron.openstack.common import log as logging
+
+
+LOG = logging.getLogger(__name__)
+
+
+class deprecated(object):
+    """A decorator to mark callables as deprecated.
+
+    This decorator logs a deprecation message when the callable it decorates is
+    used. The message will include the release where the callable was
+    deprecated, the release where it may be removed and possibly an optional
+    replacement.
+
+    Examples:
+
+    1. Specifying the required deprecated release
+
+    >>> @deprecated(as_of=deprecated.ICEHOUSE)
+    ... def a(): pass
+
+    2. Specifying a replacement:
+
+    >>> @deprecated(as_of=deprecated.ICEHOUSE, in_favor_of='f()')
+    ... def b(): pass
+
+    3. Specifying the release where the functionality may be removed:
+
+    >>> @deprecated(as_of=deprecated.ICEHOUSE, remove_in=+1)
+    ... def c(): pass
+
+    4. Specifying the deprecated functionality will not be removed:
+    >>> @deprecated(as_of=deprecated.ICEHOUSE, remove_in=0)
+    ... def d(): pass
+
+    5. Specifying a replacement, deprecated functionality will not be removed:
+    >>> @deprecated(as_of=deprecated.ICEHOUSE, in_favor_of='f()', remove_in=0)
+    ... def e(): pass
+
+    """
+
+    # NOTE(morganfainberg): Bexar is used for unit test purposes, it is
+    # expected we maintain a gap between Bexar and Folsom in this list.
+    BEXAR = 'B'
+    FOLSOM = 'F'
+    GRIZZLY = 'G'
+    HAVANA = 'H'
+    ICEHOUSE = 'I'
+    JUNO = 'J'
+    KILO = 'K'
+
+    _RELEASES = {
+        # NOTE(morganfainberg): Bexar is used for unit test purposes, it is
+        # expected we maintain a gap between Bexar and Folsom in this list.
+        'B': 'Bexar',
+        'F': 'Folsom',
+        'G': 'Grizzly',
+        'H': 'Havana',
+        'I': 'Icehouse',
+        'J': 'Juno',
+        'K': 'Kilo',
+    }
+
+    _deprecated_msg_with_alternative = _(
+        '%(what)s is deprecated as of %(as_of)s in favor of '
+        '%(in_favor_of)s and may be removed in %(remove_in)s.')
+
+    _deprecated_msg_no_alternative = _(
+        '%(what)s is deprecated as of %(as_of)s and may be '
+        'removed in %(remove_in)s. It will not be superseded.')
+
+    _deprecated_msg_with_alternative_no_removal = _(
+        '%(what)s is deprecated as of %(as_of)s in favor of %(in_favor_of)s.')
+
+    _deprecated_msg_with_no_alternative_no_removal = _(
+        '%(what)s is deprecated as of %(as_of)s. It will not be superseded.')
+
+    def __init__(self, as_of, in_favor_of=None, remove_in=2, what=None):
+        """Initialize decorator
+
+        :param as_of: the release deprecating the callable. Constants
+            are define in this class for convenience.
+        :param in_favor_of: the replacement for the callable (optional)
+        :param remove_in: an integer specifying how many releases to wait
+            before removing (default: 2)
+        :param what: name of the thing being deprecated (default: the
+            callable's name)
+
+        """
+        self.as_of = as_of
+        self.in_favor_of = in_favor_of
+        self.remove_in = remove_in
+        self.what = what
+
+    def __call__(self, func_or_cls):
+        if not self.what:
+            self.what = func_or_cls.__name__ + '()'
+        msg, details = self._build_message()
+
+        if inspect.isfunction(func_or_cls):
+
+            @six.wraps(func_or_cls)
+            def wrapped(*args, **kwargs):
+                LOG.deprecated(msg, details)
+                return func_or_cls(*args, **kwargs)
+            return wrapped
+        elif inspect.isclass(func_or_cls):
+            orig_init = func_or_cls.__init__
+
+            # TODO(tsufiev): change `functools` module to `six` as
+            # soon as six 1.7.4 (with fix for passing `assigned`
+            # argument to underlying `functools.wraps`) is released
+            # and added to the neutron-incubator requrements
+            @functools.wraps(orig_init, assigned=('__name__', '__doc__'))
+            def new_init(self, *args, **kwargs):
+                LOG.deprecated(msg, details)
+                orig_init(self, *args, **kwargs)
+            func_or_cls.__init__ = new_init
+            return func_or_cls
+        else:
+            raise TypeError('deprecated can be used only with functions or '
+                            'classes')
+
+    def _get_safe_to_remove_release(self, release):
+        # TODO(dstanek): this method will have to be reimplemented once
+        #    when we get to the X release because once we get to the Y
+        #    release, what is Y+2?
+        new_release = chr(ord(release) + self.remove_in)
+        if new_release in self._RELEASES:
+            return self._RELEASES[new_release]
+        else:
+            return new_release
+
+    def _build_message(self):
+        details = dict(what=self.what,
+                       as_of=self._RELEASES[self.as_of],
+                       remove_in=self._get_safe_to_remove_release(self.as_of))
+
+        if self.in_favor_of:
+            details['in_favor_of'] = self.in_favor_of
+            if self.remove_in > 0:
+                msg = self._deprecated_msg_with_alternative
+            else:
+                # There are no plans to remove this function, but it is
+                # now deprecated.
+                msg = self._deprecated_msg_with_alternative_no_removal
+        else:
+            if self.remove_in > 0:
+                msg = self._deprecated_msg_no_alternative
+            else:
+                # There are no plans to remove this function, but it is
+                # now deprecated.
+                msg = self._deprecated_msg_with_no_alternative_no_removal
+        return msg, details
+
+
+def is_compatible(requested_version, current_version, same_major=True):
+    """Determine whether `requested_version` is satisfied by
+    `current_version`; in other words, `current_version` is >=
+    `requested_version`.
+
+    :param requested_version: version to check for compatibility
+    :param current_version: version to check against
+    :param same_major: if True, the major version must be identical between
+        `requested_version` and `current_version`. This is used when a
+        major-version difference indicates incompatibility between the two
+        versions. Since this is the common-case in practice, the default is
+        True.
+    :returns: True if compatible, False if not
+    """
+    requested_parts = pkg_resources.parse_version(requested_version)
+    current_parts = pkg_resources.parse_version(current_version)
+
+    if same_major and (requested_parts[0] != current_parts[0]):
+        return False
+
+    return current_parts >= requested_parts
index 77f9d580ccddcb2aaa687af7e5999854e3ebe751..8375c22a3525d5766a1fa00aea3b37d5be7212b1 100644 (file)
 #    License for the specific language governing permissions and limitations
 #    under the License.
 
+from oslo.middleware import request_id
 import webob
 
 from neutron import auth
-from neutron.openstack.common.middleware import request_id
 from neutron.tests import base
 
 
index 250aac48ad2be0a34244c7f471dd4623840365c3..56f22ee7d059004773cf7cba32431fda6240dc1a 100644 (file)
@@ -11,12 +11,7 @@ module=local
 module=lockutils
 module=log
 module=loopingcall
-module=middleware.base
-module=middleware.catch_errors
-module=middleware.correlation_id
-module=middleware.debug
-module=middleware.request_id
-module=middleware.sizelimit
+module=middleware
 module=periodic_task
 module=policy
 module=processutils
index c070f65cd1e41b09784939031c1ff2b15f706843..241a73c275437200b8ed43320c30c08e4288c14e 100644 (file)
@@ -29,6 +29,7 @@ oslo.config>=1.4.0  # Apache-2.0
 oslo.db>=1.1.0  # Apache-2.0
 oslo.i18n>=1.0.0  # Apache-2.0
 oslo.messaging>=1.4.0
+oslo.middleware>=0.1.0                  # Apache-2.0
 oslo.rootwrap>=1.3.0
 oslo.serialization>=1.0.0               # Apache-2.0
 oslo.utils>=1.0.0                       # Apache-2.0