From: Chris Buccella Date: Sat, 15 Feb 2014 02:29:43 +0000 (+0000) Subject: Sync request_id, request_utils for cinder X-Git-Url: https://review.fuel-infra.org/gitweb?a=commitdiff_plain;h=7e3698417ad8ca0112a05969bcfd7aa60f5bcdd3;p=openstack-build%2Fcinder-build.git Sync request_id, request_utils for cinder This is in preparation for work being done on request ID tracing across OpenStack services. Per discussion in blueprint cross-service-request-id , the standard header name for the request ID across OpenStack projects should be x-openstack-request-id. The request_id middleware was created for just this purpose, as oslo change Ic7967cd62e7b743343d70f751b9238339171e013 . Using the same middleware allows for consistency across projects. There is an additional oslo module, request_utils, introduced as change Id085c4444fee2bb68b80738bfe77ccb0ba1908ec to allow uniform logging of request IDs. Bring that in as well so we have it when necessary. Implements: blueprint add-standard-req-id-header Change-Id: If080df2a323347924c93a912538dab5ffeffe982 --- diff --git a/cinder/openstack/common/middleware/__init__.py b/cinder/openstack/common/middleware/__init__.py new file mode 100644 index 000000000..e69de29bb diff --git a/cinder/openstack/common/middleware/base.py b/cinder/openstack/common/middleware/base.py new file mode 100644 index 000000000..464a1ccd7 --- /dev/null +++ b/cinder/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/cinder/openstack/common/middleware/request_id.py b/cinder/openstack/common/middleware/request_id.py new file mode 100644 index 000000000..53d9813b0 --- /dev/null +++ b/cinder/openstack/common/middleware/request_id.py @@ -0,0 +1,38 @@ +# 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. +""" + +from cinder.openstack.common import context +from cinder.openstack.common.middleware import base + + +ENV_REQUEST_ID = 'openstack.request_id' +HTTP_RESP_HEADER_REQUEST_ID = 'x-openstack-request-id' + + +class RequestIdMiddleware(base.Middleware): + + def process_request(self, req): + self.req_id = context.generate_request_id() + req.environ[ENV_REQUEST_ID] = self.req_id + + def process_response(self, response): + response.headers.add(HTTP_RESP_HEADER_REQUEST_ID, self.req_id) + return response diff --git a/cinder/openstack/common/request_utils.py b/cinder/openstack/common/request_utils.py new file mode 100644 index 000000000..105a628ff --- /dev/null +++ b/cinder/openstack/common/request_utils.py @@ -0,0 +1,88 @@ +# Copyright 2014 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. + +""" +Utilities for linking request ID's across service calls. +""" + +import logging + +from openstack.common.gettextutils import _ # noqa + + +LOG = logging.getLogger(__name__) + + +def link_request_ids(context, source_id, target_id=None, stage=None, + target_name=None, notifier=None): + """Links the Request ID from the Source service to + the Request ID returned from the Target service. + + Linkages are logged and emitted as INFO notifications. + + :params context: context object + :params source_id: the Request ID of the source + :params target_id: the Request ID of the target + :params stage: optional event name extension to + indicate which part of the linkage + this is. + :params target_name: human readable name of the + target system you are talking to. + :params notifier: notifier object + + A typical use case is: System A asking System B + to perform some action. The linkages might look + like this: + + link_request_ids(sys_A.request_ID, stage="start") + # send request to System B and get request ID + link_request_ids(sys_A.request_ID, target_id=sys_B.request.ID) + # optionally wait for System B to complete + link_request_ids(sys_A.request_ID, target_id=sys_B.request.ID, + stage="end") + + But, it could be as simple as: + link_request_ids(sys_A.request_ID, target_id=sys_B.request.ID) + """ + + event_name = "request.link" + if stage: + event_name += ".%s" % stage + + rtarget_id = "" + if target_id: + rtarget_id = _("TargetId=%(id)s ") % {'id': target_id} + + rtarget_name = "" + if target_name: + rtarget_name = _("Target='%(name)s' ") % {'name': target_name} + + arrow = "" + if target_name or target_id: + arrow = " -> " + + LOG.info(_("Request ID Link: %(event_name)s '%(source_id)s'%(arrow)s" + "%(target_name)s%(target_id)s") % {"event_name": event_name, + "source_id": source_id, + "target_name": rtarget_name, + "arrow": arrow, + "target_id": rtarget_id}) + + if notifier: + payload = {"source_request_id": source_id, + "target_request_id": target_id, + "target_name": target_name, + "stage": stage} + notifier.info(context, event_name, payload) diff --git a/openstack-common.conf b/openstack-common.conf index d5ed6e43c..44e5a96e2 100644 --- a/openstack-common.conf +++ b/openstack-common.conf @@ -17,11 +17,13 @@ module=local module=lockutils module=log module=log_handler +module=middleware module=network_utils module=notifier module=periodic_task module=policy module=processutils +module=request_utils module=rootwrap module=rpc module=scheduler