diff --git a/designate/api/middleware.py b/designate/api/middleware.py index 053fa7d42..c774525f4 100644 --- a/designate/api/middleware.py +++ b/designate/api/middleware.py @@ -22,11 +22,12 @@ from oslo import messaging from designate import exceptions from designate import notifications from designate import wsgi -from designate.context import DesignateContext +from designate import context from designate.openstack.common import jsonutils as json from designate.openstack.common import local from designate.openstack.common import log as logging from designate.openstack.common import strutils +from designate.openstack.common.middleware import request_id from designate.openstack.common.gettextutils import _LI from designate.openstack.common.gettextutils import _LE from designate.openstack.common.gettextutils import _LC @@ -84,16 +85,16 @@ def auth_pipeline_factory(loader, global_conf, **local_conf): class ContextMiddleware(wsgi.Middleware): - def process_response(self, response): - try: - context = local.store.context - except Exception: - pass - else: - # Add the Request ID as a response header - response.headers['X-DNS-Request-ID'] = context.request_id + def make_context(self, request, *args, **kwargs): + req_id = request.environ.get(request_id.ENV_REQUEST_ID) + kwargs.setdefault('request_id', req_id) - return response + ctxt = context.DesignateContext(*args, **kwargs) + + local.store.context = ctxt + request.environ['context'] = ctxt + + return ctxt class KeystoneContextMiddleware(ContextMiddleware): @@ -120,17 +121,13 @@ class KeystoneContextMiddleware(ContextMiddleware): roles = headers.get('X-Roles').split(',') - context = DesignateContext(auth_token=headers.get('X-Auth-Token'), - user=headers.get('X-User-ID'), - tenant=headers.get('X-Tenant-ID'), - roles=roles, - service_catalog=catalog) - - # Store the context where oslo-log exepcts to find it. - local.store.context = context - - # Attach the context to the request environment - request.environ['context'] = context + self.make_context( + request, + auth_token=headers.get('X-Auth-Token'), + user=headers.get('X-User-ID'), + tenant=headers.get('X-Tenant-ID'), + roles=roles, + service_catalog=catalog) class NoAuthContextMiddleware(ContextMiddleware): @@ -142,19 +139,14 @@ class NoAuthContextMiddleware(ContextMiddleware): def process_request(self, request): headers = request.headers - context = DesignateContext( + self.make_context( + request, auth_token=headers.get('X-Auth-Token', None), user=headers.get('X-Auth-User-ID', 'noauth-user'), tenant=headers.get('X-Auth-Project-ID', 'noauth-project'), roles=headers.get('X-Roles', 'admin').split(',') ) - # Store the context where oslo-log exepcts to find it. - local.store.context = context - - # Attach the context to the request environment - request.environ['context'] = context - class TestContextMiddleware(ContextMiddleware): def __init__(self, application, tenant_id=None, user_id=None): @@ -172,17 +164,12 @@ class TestContextMiddleware(ContextMiddleware): all_tenants = strutils.bool_from_string( headers.get('X-Test-All-Tenants', 'False')) - context = DesignateContext( + self.make_context( + request, user=headers.get('X-Test-User-ID', self.default_user_id), tenant=headers.get('X-Test-Tenant-ID', self.default_tenant_id), all_tenants=all_tenants) - # Store the context where oslo-log exepcts to find it. - local.store.context = context - - # Attach the context to the request environment - request.environ['context'] = context - class FaultWrapperMiddleware(wsgi.Middleware): def __init__(self, application): diff --git a/designate/openstack/common/middleware/__init__.py b/designate/openstack/common/middleware/__init__.py new file mode 100644 index 000000000..e69de29bb diff --git a/designate/openstack/common/middleware/base.py b/designate/openstack/common/middleware/base.py new file mode 100644 index 000000000..464a1ccd7 --- /dev/null +++ b/designate/openstack/common/middleware/base.py @@ -0,0 +1,56 @@ +# 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) diff --git a/designate/openstack/common/middleware/request_id.py b/designate/openstack/common/middleware/request_id.py new file mode 100644 index 000000000..87c1f09fd --- /dev/null +++ b/designate/openstack/common/middleware/request_id.py @@ -0,0 +1,41 @@ +# 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 +# +# 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 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. +""" + +import webob.dec + +from designate.openstack.common import context +from designate.openstack.common.middleware import base + + +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 diff --git a/etc/designate/api-paste.ini b/etc/designate/api-paste.ini index 24a4305a4..5551eab64 100644 --- a/etc/designate/api-paste.ini +++ b/etc/designate/api-paste.ini @@ -9,20 +9,23 @@ paste.app_factory = designate.api.versions:factory [composite:osapi_dns_v1] use = call:designate.api.middleware:auth_pipeline_factory -noauth = noauthcontext maintenance faultwrapper osapi_dns_app_v1 -keystone = authtoken keystonecontext maintenance faultwrapper osapi_dns_app_v1 +noauth = request_id noauthcontext maintenance faultwrapper osapi_dns_app_v1 +keystone = request_id authtoken keystonecontext maintenance faultwrapper osapi_dns_app_v1 [app:osapi_dns_app_v1] paste.app_factory = designate.api.v1:factory [composite:osapi_dns_v2] use = call:designate.api.middleware:auth_pipeline_factory -noauth = noauthcontext maintenance faultwrapper osapi_dns_app_v2 -keystone = authtoken keystonecontext maintenance faultwrapper osapi_dns_app_v2 +noauth = request_id noauthcontext maintenance faultwrapper osapi_dns_app_v2 +keystone = request_id authtoken keystonecontext maintenance faultwrapper osapi_dns_app_v2 [app:osapi_dns_app_v2] paste.app_factory = designate.api.v2:factory +[filter:request_id] +paste.filter_factory = designate.openstack.common.middleware.request_id:RequestIdMiddleware.factory + [filter:maintenance] paste.filter_factory = designate.api.middleware:MaintenanceMiddleware.factory diff --git a/openstack-common.conf b/openstack-common.conf index 0905e99ed..7df3f1c7b 100644 --- a/openstack-common.conf +++ b/openstack-common.conf @@ -12,6 +12,7 @@ module=install_venv_common module=jsonutils module=local module=log +module=middleware module=policy module=processutils module=service