
Code was stripping last char from path when expiring the cookie that controls profiling. For example, cookie created at /admin/info/ was being expired as /admin/info without final "/". This can fail to find the cookie (browser dependent?) and leave profiling active for all subsequent refreshes of the view. Change-Id: I71d36ab3ff121cc43b8d0b43a056ba26aa030058
146 lines
5.0 KiB
Python
146 lines
5.0 KiB
Python
# Copyright 2016 Mirantis Inc.
|
|
# 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.
|
|
|
|
from django.conf import settings
|
|
from django.core import exceptions
|
|
from django.urls import reverse
|
|
from django.utils import safestring
|
|
from django.utils.translation import ugettext_lazy as _
|
|
from osprofiler import _utils as profiler_utils
|
|
from osprofiler import profiler
|
|
from osprofiler import web
|
|
import six
|
|
|
|
from horizon import messages
|
|
from openstack_dashboard.contrib.developer.profiler import api
|
|
|
|
_REQUIRED_KEYS = ("base_id", "hmac_key")
|
|
_OPTIONAL_KEYS = ("parent_id",)
|
|
|
|
PROFILER_CONF = getattr(settings, 'OPENSTACK_PROFILER', {})
|
|
PROFILER_ENABLED = PROFILER_CONF.get('enabled', False)
|
|
|
|
|
|
class ProfilerClientMiddleware(object):
|
|
profiler_headers = [
|
|
('HTTP_X_TRACE_INFO', 'X-Trace-Info'),
|
|
('HTTP_X_TRACE_HMAC', 'X-Trace-HMAC')
|
|
]
|
|
|
|
def __init__(self, get_response):
|
|
if not PROFILER_ENABLED:
|
|
raise exceptions.MiddlewareNotUsed()
|
|
super(ProfilerClientMiddleware, self).__init__()
|
|
self.get_response = get_response
|
|
|
|
def __call__(self, request):
|
|
self.process_request(request)
|
|
response = self.get_response(request)
|
|
return response
|
|
|
|
def is_async_profiling(self, request):
|
|
return self.profiler_headers[0][0] in request.META
|
|
|
|
def process_request(self, request):
|
|
if self.is_async_profiling(request):
|
|
for src_header, dst_header in self.profiler_headers:
|
|
request.META[dst_header] = request.META.get(src_header)
|
|
return None
|
|
|
|
if 'profile_page' in request.COOKIES:
|
|
hmac_key = PROFILER_CONF.get('keys')[0]
|
|
profiler.init(hmac_key)
|
|
for hdr_key, hdr_value in web.get_trace_id_headers().items():
|
|
request.META[hdr_key] = hdr_value
|
|
return None
|
|
|
|
|
|
class ProfilerMiddleware(object):
|
|
def __init__(self, get_response):
|
|
self.name = PROFILER_CONF.get('facility_name', 'horizon')
|
|
self.hmac_keys = PROFILER_CONF.get('keys', [])
|
|
self.get_response = get_response
|
|
if PROFILER_ENABLED:
|
|
api.init_notifier(PROFILER_CONF.get('notifier_connection_string'))
|
|
else:
|
|
raise exceptions.MiddlewareNotUsed()
|
|
|
|
def __call__(self, request):
|
|
response = self.get_response(request)
|
|
response = self.process_response(request, response)
|
|
return response
|
|
|
|
@staticmethod
|
|
def is_authenticated(request):
|
|
return hasattr(request, "user") and request.user.is_authenticated
|
|
|
|
def is_enabled(self, request):
|
|
return self.is_authenticated(request) and settings.DEBUG
|
|
|
|
@staticmethod
|
|
def _trace_is_valid(trace_info):
|
|
if not isinstance(trace_info, dict):
|
|
return False
|
|
trace_keys = set(six.iterkeys(trace_info))
|
|
if not all(k in trace_keys for k in _REQUIRED_KEYS):
|
|
return False
|
|
if trace_keys.difference(_REQUIRED_KEYS + _OPTIONAL_KEYS):
|
|
return False
|
|
return True
|
|
|
|
def process_view(self, request, view_func, view_args, view_kwargs):
|
|
|
|
if not self.is_enabled(request):
|
|
return None
|
|
|
|
trace_info = profiler_utils.signed_unpack(
|
|
request.META.get('X-Trace-Info'),
|
|
request.META.get('X-Trace-HMAC'),
|
|
self.hmac_keys)
|
|
|
|
if not self._trace_is_valid(trace_info):
|
|
return None
|
|
|
|
profiler.init(**trace_info)
|
|
info = {
|
|
'request': {
|
|
'path': request.path,
|
|
'query': request.GET.urlencode(),
|
|
'method': request.method,
|
|
'scheme': request.scheme
|
|
}
|
|
}
|
|
with api.traced(request, view_func.__name__, info) as trace_id:
|
|
request.META[api.ROOT_HEADER] = profiler.get().get_id()
|
|
|
|
response = view_func(request, *view_args, **view_kwargs)
|
|
url = reverse('horizon:developer:profiler:index')
|
|
message = safestring.mark_safe(
|
|
_('Traced with id %(id)s. Go to <a href="%(url)s">page</a>') %
|
|
{'id': trace_id, 'url': url})
|
|
messages.info(request, message)
|
|
return response
|
|
|
|
@staticmethod
|
|
def clear_profiling_cookies(request, response):
|
|
"""Expire any cookie that initiated profiling request."""
|
|
if 'profile_page' in request.COOKIES:
|
|
path = request.path
|
|
response.set_cookie('profile_page', max_age=0, path=path)
|
|
|
|
def process_response(self, request, response):
|
|
self.clear_profiling_cookies(request, response)
|
|
return response
|