*) Add osprofiler wsgi middleware This middleware is used for 2 things: 1) It checks that the person who wants to trace is trusted and knows the secret HMAC key. 2) It start tracing in case of proper trace headers and adds the first wsgi trace point, with info about the HTTP request. *) Add initialization of osprofiler at start of service Initialize osprofiler with oslo.messaging notifier which is used to send notifications to Ceilometer. *) Use profile enabled context in services rpc interaction Change context serializer to pass profile context to service; rpc server will use trace info to initialize their profile context to continue the profiling for the request as a transaction. *) Add tracing on service manager and sqlalchemy db engine object NOTE to test this: You should put to localrc: CEILOMETER_NOTIFICATION_TOPICS=notifications,profiler ENABLED_SERVICES+=,ceilometer-acompute,ceilometer-acentral ENABLED_SERVICES+=,ceilometer-anotification,ceilometer-collector ENABLED_SERVICES+=,ceilometer-alarm-evaluator,ceilometer-alarm-notifier ENABLED_SERVICES+=,ceilometer-api Run any command with --profile <SECRET_KEY> $ trove --profile <SECRET_KEY> list # it will print <Trace ID> Get pretty HTML with traces: $ osprofiler trace show --html <Trace ID> Note: Trace showing can be executed with the admin account only. The change to enable Trove exchange in ceilometer has been merged: Idce1c327c6d21a767c612c13c1ad52a794017d71 . The change to enable profile in python-troveclient has been merged: I5a76e11d428c63d33f6d2c2021426090ebf8340c We prepared a common BP in oslo-spec as the integration change is similar in all projects: I95dccdc9f274661767d2659c18b96da169891f30 Currently there are 2 other projects are using osprofiler: Glance & Cinder, and some others are a work in progress. Change-Id: I580cce8d2b3c4ec9ce625ac09de6f14e1249f6f5 Signed-off-by: Zhi Yan Liu <zhiyanl@cn.ibm.com>
177 lines
5.0 KiB
Python
177 lines
5.0 KiB
Python
# Copyright 2014 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.
|
|
|
|
|
|
# NOTE(esp): This code was taken from nova
|
|
|
|
__all__ = [
|
|
'init',
|
|
'cleanup',
|
|
'set_defaults',
|
|
'add_extra_exmods',
|
|
'clear_extra_exmods',
|
|
'get_allowed_exmods',
|
|
'RequestContextSerializer',
|
|
'get_client',
|
|
'get_server',
|
|
'get_notifier',
|
|
'TRANSPORT_ALIASES',
|
|
]
|
|
|
|
|
|
from oslo.config import cfg
|
|
from oslo import messaging
|
|
from osprofiler import profiler
|
|
|
|
from trove.common.context import TroveContext
|
|
import trove.common.exception
|
|
from trove.openstack.common import jsonutils
|
|
|
|
|
|
CONF = cfg.CONF
|
|
TRANSPORT = None
|
|
NOTIFIER = None
|
|
|
|
ALLOWED_EXMODS = [
|
|
trove.common.exception.__name__,
|
|
]
|
|
|
|
EXTRA_EXMODS = []
|
|
|
|
# TODO(esp): Remove or update these paths
|
|
TRANSPORT_ALIASES = {
|
|
'trove.openstack.common.rpc.impl_kombu': 'rabbit',
|
|
'trove.openstack.common.rpc.impl_qpid': 'qpid',
|
|
'trove.openstack.common.rpc.impl_zmq': 'zmq',
|
|
'trove.rpc.impl_kombu': 'rabbit',
|
|
'trove.rpc.impl_qpid': 'qpid',
|
|
'trove.rpc.impl_zmq': 'zmq',
|
|
}
|
|
|
|
|
|
def init(conf):
|
|
global TRANSPORT, NOTIFIER
|
|
exmods = get_allowed_exmods()
|
|
TRANSPORT = messaging.get_transport(conf,
|
|
allowed_remote_exmods=exmods,
|
|
aliases=TRANSPORT_ALIASES)
|
|
|
|
#serializer = RequestContextSerializer(JsonPayloadSerializer())
|
|
# https://review.openstack.org/#/c/71532/1/nova/rpc.py
|
|
NOTIFIER = messaging.Notifier(TRANSPORT, serializer=None)
|
|
|
|
|
|
def cleanup():
|
|
global TRANSPORT, NOTIFIER
|
|
assert TRANSPORT is not None
|
|
assert NOTIFIER is not None
|
|
TRANSPORT.cleanup()
|
|
TRANSPORT = NOTIFIER = None
|
|
|
|
|
|
def set_defaults(control_exchange):
|
|
messaging.set_transport_defaults(control_exchange)
|
|
|
|
|
|
def add_extra_exmods(*args):
|
|
EXTRA_EXMODS.extend(args)
|
|
|
|
|
|
def clear_extra_exmods():
|
|
del EXTRA_EXMODS[:]
|
|
|
|
|
|
def get_allowed_exmods():
|
|
return ALLOWED_EXMODS + EXTRA_EXMODS
|
|
|
|
|
|
class JsonPayloadSerializer(messaging.NoOpSerializer):
|
|
@staticmethod
|
|
def serialize_entity(context, entity):
|
|
return jsonutils.to_primitive(entity, convert_instances=True)
|
|
|
|
|
|
class RequestContextSerializer(messaging.Serializer):
|
|
|
|
def __init__(self, base):
|
|
self._base = base
|
|
|
|
def serialize_entity(self, context, entity):
|
|
if not self._base:
|
|
return entity
|
|
return self._base.serialize_entity(context, entity)
|
|
|
|
def deserialize_entity(self, context, entity):
|
|
if not self._base:
|
|
return entity
|
|
return self._base.deserialize_entity(context, entity)
|
|
|
|
def serialize_context(self, context):
|
|
_context = context.to_dict()
|
|
prof = profiler.get()
|
|
if prof:
|
|
trace_info = {
|
|
"hmac_key": prof.hmac_key,
|
|
"base_id": prof.get_base_id(),
|
|
"parent_id": prof.get_id()
|
|
}
|
|
_context.update({"trace_info": trace_info})
|
|
return _context
|
|
|
|
def deserialize_context(self, context):
|
|
trace_info = context.pop("trace_info", None)
|
|
if trace_info:
|
|
profiler.init(**trace_info)
|
|
return TroveContext.from_dict(context)
|
|
|
|
|
|
def get_transport_url(url_str=None):
|
|
return messaging.TransportURL.parse(CONF, url_str, TRANSPORT_ALIASES)
|
|
|
|
|
|
def get_client(target, version_cap=None, serializer=None):
|
|
assert TRANSPORT is not None
|
|
serializer = RequestContextSerializer(serializer)
|
|
return messaging.RPCClient(TRANSPORT,
|
|
target,
|
|
version_cap=version_cap,
|
|
serializer=serializer)
|
|
|
|
|
|
def get_server(target, endpoints, serializer=None):
|
|
assert TRANSPORT is not None
|
|
|
|
# Thread module is not monkeypatched if remote debugging is enabled.
|
|
# Using eventlet executor without monkepatching thread module will
|
|
# lead to unpredictable results.
|
|
from trove.common import debug_utils
|
|
debug_utils.setup()
|
|
|
|
executor = "blocking" if debug_utils.enabled() else "eventlet"
|
|
|
|
serializer = RequestContextSerializer(serializer)
|
|
return messaging.get_rpc_server(TRANSPORT,
|
|
target,
|
|
endpoints,
|
|
executor=executor,
|
|
serializer=serializer)
|
|
|
|
|
|
def get_notifier(service=None, host=None, publisher_id=None):
|
|
assert NOTIFIER is not None
|
|
if not publisher_id:
|
|
publisher_id = "%s.%s" % (service, host or CONF.host)
|
|
return NOTIFIER.prepare(publisher_id=publisher_id)
|