# 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 from horizon import messages from horizon.utils import settings as horizon_settings from openstack_dashboard.contrib.developer.profiler import api _REQUIRED_KEYS = ("base_id", "hmac_key") _OPTIONAL_KEYS = ("parent_id",) PROFILER_ENABLED = horizon_settings.get_dict_config( 'OPENSTACK_PROFILER', 'enabled') 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().__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 = horizon_settings.get_dict_config( 'OPENSTACK_PROFILER', '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 = horizon_settings.get_dict_config( 'OPENSTACK_PROFILER', 'facility_name') self.hmac_keys = horizon_settings.get_dict_config( 'OPENSTACK_PROFILER', 'keys') self.get_response = get_response if PROFILER_ENABLED: api.init_notifier(horizon_settings.get_dict_config( 'OPENSTACK_PROFILER', '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 = trace_info.keys() 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 page') % {'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): if profiler.get() is not None: profiler.clean() self.clear_profiling_cookies(request, response) return response