Michal Arbet e531b7babe Fix osprofiler support in horizon
This patch is fixing usage of osprofiler in horizon.
It has been merged once, but after merge to master,
manage.py compress starts to fail with python2.7.

This fail was caused by "TypeError: 'encoding' is an
invalid keyword argument for this function" which is
now fixed by using six library.

Change-Id: I21aa8d49833bef9e7673958d15674faec82a0925
Closes-Bug: #1814028
Related-Bug: #1811261
2019-01-31 14:06:45 +01:00

136 lines
4.7 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.
import contextlib
import json
from django.conf import settings
from osprofiler import _utils as utils
from osprofiler.drivers.base import get_driver as profiler_get_driver
from osprofiler import notifier
from osprofiler import profiler
from osprofiler import web
import six
from six.moves.urllib.parse import urlparse
ROOT_HEADER = 'PARENT_VIEW_TRACE_ID'
PROFILER_SETTINGS = getattr(settings, 'OPENSTACK_PROFILER', {})
def init_notifier(connection_str, host="localhost"):
_notifier = notifier.create(
connection_str, project='horizon', service='horizon', host=host)
notifier.set(_notifier)
@contextlib.contextmanager
def traced(request, name, info=None):
if info is None:
info = {}
profiler_instance = profiler.get()
if profiler_instance is not None:
trace_id = profiler_instance.get_base_id()
info['user_id'] = request.user.id
with profiler.Trace(name, info=info):
yield trace_id
else:
yield
def _get_engine_kwargs(request, connection_str):
from openstack_dashboard.api import base
engines_kwargs = {
# NOTE(tsufiev): actually Horizon doesn't use ceilometer backend (too
# slow for UI), but since osprofiler still supports it (due to API
# deprecation cycle limitations), Horizon also should support this
# option
'ceilometer': lambda req: {
'endpoint': base.url_for(req, 'metering'),
'insecure': getattr(settings, 'OPENSTACK_SSL_NO_VERIFY', False),
'cacert': getattr(settings, 'OPENSTACK_SSL_CACERT', None),
'token': (lambda: req.user.token.id),
'ceilometer_api_version': '2'
}
}
engine = urlparse(connection_str).scheme
return engines_kwargs.get(engine, lambda req: {})(request)
def _get_engine(request):
connection_str = PROFILER_SETTINGS.get(
'receiver_connection_string', "mongodb://")
kwargs = _get_engine_kwargs(request, connection_str)
return profiler_get_driver(connection_str, **kwargs)
def list_traces(request):
engine = _get_engine(request)
fields = ['base_id', 'timestamp', 'info.request.path', 'info']
traces = engine.list_traces(fields)
return [{'id': trace['base_id'],
'timestamp': trace['timestamp'],
'origin': trace['info']['request']['path']} for trace in traces]
def get_trace(request, trace_id):
def rec(_data, level=0):
_data['level'] = level
_data['is_leaf'] = not _data['children']
_data['visible'] = True
_data['childrenVisible'] = True
finished = _data['info']['finished']
for child in _data['children']:
__, child_finished = rec(child, level + 1)
# NOTE(tsufiev): in case of async requests the root request usually
# finishes before the dependent requests do so, to we need to
# normalize the duration of all requests by the finishing time of
# the one which took longest
if child_finished > finished:
finished = child_finished
return _data, finished
engine = _get_engine(request)
trace = engine.get_report(trace_id)
data, max_finished = rec(trace)
data['info']['max_finished'] = max_finished
return data
def update_trace_headers(keys, **kwargs):
trace_headers = web.get_trace_id_headers()
trace_info = utils.signed_unpack(
trace_headers[web.X_TRACE_INFO], trace_headers[web.X_TRACE_HMAC],
keys)
trace_info.update(kwargs)
p = profiler.get()
trace_data = utils.signed_pack(trace_info, p.hmac_key)
if six.PY3:
trace_data = [key.decode() if isinstance(key, six.binary_type)
else key for key in trace_data]
return json.dumps({web.X_TRACE_INFO: trace_data[0],
web.X_TRACE_HMAC: trace_data[1]})
if not PROFILER_SETTINGS.get('enabled', False):
def trace(function):
return function
else:
def trace(function):
func_name = function.__module__ + '.' + function.__name__
decorator = profiler.trace(func_name)
return decorator(function)