Merge "Integrate OSprofiler and Designate"

This commit is contained in:
Zuul 2022-02-14 20:28:53 +00:00 committed by Gerrit Code Review
commit 057329deb1
13 changed files with 189 additions and 7 deletions

View File

@ -18,6 +18,7 @@ from oslo_log import log as logging
from paste import deploy
from designate.common import config
from designate.common import profiler
from designate import conf
from designate import heartbeat_emitter
from designate import policy
@ -47,6 +48,7 @@ def init_application():
if not rpc.initialized():
rpc.init(CONF)
profiler.setup_profiler("designate-api", CONF.host)
heartbeat = heartbeat_emitter.get_heartbeat_emitter('api')
heartbeat.start()

View File

@ -18,6 +18,7 @@ from oslo_config import cfg
from oslo_log import log as logging
import oslo_messaging as messaging
from designate.common import profiler
from designate.loggingutils import rpc_logging
from designate import rpc
@ -31,6 +32,7 @@ def reset():
CENTRAL_API = None
@profiler.trace_cls("rpc")
@rpc_logging(LOG, 'central')
class CentralAPI(object):
"""

View File

@ -0,0 +1,88 @@
#
# 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 oslo_log import log as logging
from oslo_utils import importutils
import designate.conf
from designate.context import DesignateContext
import webob.dec
profiler = importutils.try_import("osprofiler.profiler")
profiler_initializer = importutils.try_import("osprofiler.initializer")
profiler_opts = importutils.try_import('osprofiler.opts')
profiler_web = importutils.try_import("osprofiler.web")
CONF = designate.conf.CONF
LOG = logging.getLogger(__name__)
if profiler_opts:
profiler_opts.set_defaults(CONF)
class WsgiMiddleware(object):
def __init__(self, application, **kwargs):
self.application = application
@classmethod
def factory(cls, global_conf, **local_conf):
if profiler_web:
return profiler_web.WsgiMiddleware.factory(global_conf,
**local_conf)
def filter_(app):
return cls(app, **local_conf)
return filter_
@webob.dec.wsgify
def __call__(self, request):
return request.get_response(self.application)
def setup_profiler(binary, host):
if hasattr(CONF, 'profiler') and not CONF.profiler.enabled:
return
if (profiler_initializer is None or profiler is None or
profiler_opts is None):
LOG.debug('osprofiler is not present')
return
profiler_initializer.init_from_conf(
conf=CONF,
context=DesignateContext.get_admin_context().to_dict(),
project="designate",
service=binary,
host=host)
LOG.info("osprofiler is enabled")
def trace_cls(name, **kwargs):
"""Wrap the OSprofiler trace_cls.
Wrap the OSprofiler trace_cls decorator so that it will not try to
patch the class unless OSprofiler is present.
:param name: The name of action. For example, wsgi, rpc, db, ...
:param kwargs: Any other keyword args used by profiler.trace_cls
"""
def decorator(cls):
if profiler and hasattr(CONF, 'profiler') and CONF.profiler.enabled:
trace_decorator = profiler.trace_cls(name, kwargs)
return trace_decorator(cls)
return cls
return decorator

View File

@ -16,6 +16,7 @@ from oslo_config import cfg
from oslo_log import log as logging
import oslo_messaging as messaging
from designate.common import profiler
from designate.loggingutils import rpc_logging
from designate import rpc
@ -30,6 +31,7 @@ def reset():
MDNS_API = None
@profiler.trace_cls("rpc")
@rpc_logging(LOG, 'mdns')
class MdnsAPI(object):

View File

@ -18,11 +18,14 @@ from oslo_config import cfg
import oslo_messaging as messaging
from oslo_messaging.rpc import dispatcher as rpc_dispatcher
from oslo_serialization import jsonutils
from oslo_utils import importutils
import designate.context
import designate.exceptions
from designate import objects
profiler = importutils.try_import('osprofiler.profiler')
__all__ = [
'init',
'cleanup',
@ -151,9 +154,23 @@ class RequestContextSerializer(messaging.Serializer):
return self._base.deserialize_entity(context, entity)
def serialize_context(self, context):
return context.to_dict()
_context = context.to_dict()
if profiler is not None:
prof = profiler.get()
if prof is not None:
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 is not None:
if profiler is not None:
profiler.init(**trace_info)
return designate.context.DesignateContext.from_dict(context)

View File

@ -30,6 +30,7 @@ from oslo_service import sslutils
from oslo_service import wsgi
from oslo_utils import netutils
from designate.common import profiler
import designate.conf
from designate.i18n import _
from designate.metrics import metrics
@ -54,6 +55,9 @@ class Service(service.Service):
if not rpc.initialized():
rpc.init(CONF)
profiler.setup_profiler((''.join(('designate-', self.name))),
self.host)
def start(self):
LOG.info('Starting %(name)s service (version: %(version)s)',
{

View File

@ -16,17 +16,44 @@
"""Session Handling for SQLAlchemy backend."""
import sqlalchemy
import threading
from oslo_config import cfg
from oslo_db.sqlalchemy import session
from oslo_log import log as logging
from oslo_utils import importutils
osprofiler_sqlalchemy = importutils.try_import('osprofiler.sqlalchemy')
LOG = logging.getLogger(__name__)
CONF = cfg.CONF
try:
CONF.import_group("profiler", "designate.service")
except cfg.NoSuchGroupError:
pass
_FACADES = {}
_LOCK = threading.Lock()
def add_db_tracing(cache_name):
global _LOCK
if not osprofiler_sqlalchemy:
return
if not hasattr(CONF, 'profiler'):
return
if not CONF.profiler.enabled or not CONF.profiler.trace_sqlalchemy:
return
with _LOCK:
osprofiler_sqlalchemy.add_tracing(
sqlalchemy,
_FACADES[cache_name].get_engine(),
"db"
)
def _create_facade_lazily(cfg_group, connection=None, discriminator=None):
@ -39,6 +66,7 @@ def _create_facade_lazily(cfg_group, connection=None, discriminator=None):
connection,
**conf
)
add_db_tracing(cache_name)
return _FACADES[cache_name]

View File

@ -19,6 +19,7 @@ import functools
import inspect
import os
import time
from unittest import mock
import eventlet
from oslo_config import cfg
@ -355,6 +356,8 @@ class TestCase(base.BaseTestCase):
group='service:api'
)
self._disable_osprofiler()
# The database fixture needs to be set up here (as opposed to isolated
# in a storage test case) because many tests end up using storage.
REPOSITORY = os.path.abspath(os.path.join(os.path.dirname(__file__),
@ -399,6 +402,22 @@ class TestCase(base.BaseTestCase):
# Setup the Default Pool with some useful settings
self._setup_default_pool()
def _disable_osprofiler(self):
"""Disable osprofiler.
osprofiler should not run for unit tests.
"""
def side_effect(value):
return value
mock_decorator = mock.MagicMock(side_effect=side_effect)
try:
p = mock.patch("osprofiler.profiler.trace_cls",
return_value=mock_decorator)
p.start()
except ModuleNotFoundError:
pass
def _setup_default_pool(self):
# Fetch the default pool
pool = self.storage.get_pool(self.admin_context, default_pool_id)

View File

@ -17,6 +17,7 @@ from oslo_config import cfg
from oslo_log import log as logging
import oslo_messaging as messaging
from designate.common import profiler
from designate.loggingutils import rpc_logging
from designate import rpc
@ -25,6 +26,7 @@ LOG = logging.getLogger(__name__)
WORKER_API = None
@profiler.trace_cls("rpc")
@rpc_logging(LOG, 'worker')
class WorkerAPI(object):
"""

View File

@ -7,8 +7,8 @@ use = egg:Paste#urlmap
[composite:osapi_dns_versions]
use = call:designate.api.middleware:auth_pipeline_factory
noauth = http_proxy_to_wsgi cors maintenance faultwrapper osapi_dns_app_versions
keystone = http_proxy_to_wsgi cors maintenance faultwrapper osapi_dns_app_versions
noauth = http_proxy_to_wsgi cors maintenance faultwrapper osprofiler osapi_dns_app_versions
keystone = http_proxy_to_wsgi cors maintenance faultwrapper osprofiler osapi_dns_app_versions
[app:osapi_dns_app_versions]
paste.app_factory = designate.api.versions:factory
@ -16,16 +16,16 @@ paste.app_factory = designate.api.versions:factory
[composite:osapi_dns_v2]
use = call:designate.api.middleware:auth_pipeline_factory
noauth = http_proxy_to_wsgi cors request_id faultwrapper validation_API_v2 noauthcontext maintenance normalizeuri osapi_dns_app_v2
keystone = http_proxy_to_wsgi cors request_id faultwrapper validation_API_v2 authtoken keystonecontext maintenance normalizeuri osapi_dns_app_v2
noauth = http_proxy_to_wsgi cors request_id faultwrapper validation_API_v2 osprofiler noauthcontext maintenance normalizeuri osapi_dns_app_v2
keystone = http_proxy_to_wsgi cors request_id faultwrapper validation_API_v2 osprofiler authtoken keystonecontext maintenance normalizeuri osapi_dns_app_v2
[app:osapi_dns_app_v2]
paste.app_factory = designate.api.v2:factory
[composite:osapi_dns_admin]
use = call:designate.api.middleware:auth_pipeline_factory
noauth = http_proxy_to_wsgi cors request_id faultwrapper noauthcontext maintenance normalizeuri osapi_dns_app_admin
keystone = http_proxy_to_wsgi cors request_id faultwrapper authtoken keystonecontext maintenance normalizeuri osapi_dns_app_admin
noauth = http_proxy_to_wsgi cors request_id faultwrapper osprofiler noauthcontext maintenance normalizeuri osapi_dns_app_admin
keystone = http_proxy_to_wsgi cors request_id faultwrapper osprofiler authtoken keystonecontext maintenance normalizeuri osapi_dns_app_admin
[app:osapi_dns_app_admin]
paste.app_factory = designate.api.admin:factory
@ -40,6 +40,9 @@ paste.filter_factory = oslo_middleware:RequestId.factory
[filter:http_proxy_to_wsgi]
paste.filter_factory = oslo_middleware:HTTPProxyToWSGI.factory
[filter:osprofiler]
paste.filter_factory = designate.common.profiler:WsgiMiddleware.factory
[filter:noauthcontext]
paste.filter_factory = designate.api.middleware:NoAuthContextMiddleware.factory

View File

@ -86,6 +86,7 @@ oslo.upgradecheck==1.3.0
oslo.utils==4.7.0
oslo.versionedobjects==1.31.2
oslotest==3.2.0
osprofiler==3.4.0
packaging==20.4
paramiko==2.7.1
Paste==2.0.2

View File

@ -0,0 +1,13 @@
---
features:
- |
OSprofiler support was introduced. To allow its usage, the api-paste.ini
file needs to be modified to contain osprofiler middleware. Also
`[profiler]` section needs to be added to the designate.conf file with
`enabled`, `hmac_keys` and `trace_sqlalchemy` flags defined.
security:
- OSprofiler support requires passing of trace information
between various OpenStack services. This information is
securely signed by one of HMAC keys, defined in designate.conf
configuration file. To allow cross-project tracing user should use the key,
that is common among all OpenStack services they want to trace.

View File

@ -22,6 +22,7 @@ oslo.service>=1.31.0 # Apache-2.0
oslo.upgradecheck>=1.3.0
oslo.utils>=4.7.0 # Apache-2.0
oslo.versionedobjects>=1.31.2 # Apache-2.0
osprofiler>=3.4.0 # Apache-2.0
Paste>=2.0.2 # MIT
PasteDeploy>=1.5.0 # MIT
pbr>=3.1.1 # Apache-2.0