Integrate OSProfiler in Zun

* Add osprofiler wsgi middleware. This middleware is used for 2 things:

* Add initialization of osprofiler at start of service
  Currently sending Ceilometer backend notifications will be implemented
  after ceilometer integrated.

* Traces HTTP/RPC/DB API calls

Demo:http://kevinzhao.org/container-create.html
How to use it:http://kevinzhao.org/2017/03/14/zun-integrate-with-osprofile.html

Change-Id: I28b3e7256235b3b8ad66ce60fd2e6bb51943a7f5
Implements: blueprint integrate-with-osprofiler
Signed-off-by: Kevin Zhao <kevin.zhao@linaro.org>
This commit is contained in:
Kevin Zhao 2017-03-13 16:52:33 +08:00
parent 92387ce610
commit 70c8790f34
16 changed files with 578 additions and 16 deletions

View File

@ -1,5 +1,5 @@
[pipeline:main]
pipeline = cors request_id authtoken api_v1
pipeline = cors request_id osprofiler authtoken api_v1
[app:api_v1]
paste.app_factory = zun.api.app:app_factory
@ -8,6 +8,9 @@ paste.app_factory = zun.api.app:app_factory
acl_public_routes = /, /v1
paste.filter_factory = zun.api.middleware.auth_token:AuthTokenMiddleware.factory
[filter:osprofiler]
paste.filter_factory = zun.common.profiler:WsgiMiddleware.factory
[filter:request_id]
paste.filter_factory = oslo_middleware:RequestId.factory

View File

@ -70,3 +70,7 @@ zun.image.driver =
tempest.test_plugins =
zun_tests = zun.tests.tempest.plugin:ZunTempestPlugin
[extras]
osprofiler =
osprofiler>=1.4.0 # Apache-2.0

View File

@ -13,6 +13,7 @@ python-subunit>=0.0.18 # Apache-2.0/BSD
sphinx>=1.5.1 # BSD
oslosphinx>=4.7.0 # Apache-2.0
oslotest>=1.10.0 # Apache-2.0
osprofiler>=1.4.0 # Apache-2.0
os-testr>=0.8.0 # Apache-2.0
PyMySQL>=0.7.6 # MIT License
tempest>=14.0.0 # Apache-2.0

View File

@ -0,0 +1,20 @@
# Copyright 2017 Fujitsu Ltd.
# 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(hieulq): we monkey patch all eventlet services for easier tracking/debug
import eventlet
eventlet.monkey_patch()

View File

@ -19,6 +19,7 @@
import sys
from zun.common import profiler
from zun.common import service as zun_service
import zun.conf
@ -33,6 +34,9 @@ def main():
# TODO(yuanying): Uncomment after rpc services are implemented
# base.zunObject.indirection_api = base.zunObjectIndirectionAPI()
# Setup OSprofiler for WSGI service
profiler.setup('zun-api', CONF.host)
# Build and start the WSGI app
launcher = zun_service.process_launcher()
server = zun_service.WSGIService(

105
zun/common/profiler.py Normal file
View File

@ -0,0 +1,105 @@
# Copyright 2017 Fujitsu Ltd.
# 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.
###
# This code is taken from nova. Goal is minimal modification.
###
from oslo_log import log as logging
from oslo_utils import importutils
import webob.dec
from zun.common import context
from zun.common.i18n import _
import zun.conf
profiler = importutils.try_import("osprofiler.profiler")
profiler_initializer = importutils.try_import("osprofiler.initializer")
profiler_web = importutils.try_import("osprofiler.web")
CONF = zun.conf.CONF
LOG = logging.getLogger(__name__)
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(binary, host):
if CONF.profiler.enabled:
profiler_initializer.init_from_conf(
conf=CONF,
context=context.get_admin_context().to_dict(),
project="zun",
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 'profiler' in CONF:
trace_decorator = profiler.trace_cls(name, kwargs)
return trace_decorator(cls)
return cls
return decorator
def trace(name, **kwargs):
"""Wrap the OSprofiler trace.
Wrap the OSprofiler trace decorator so that it will not try to
patch the functions 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
"""
def decorator(f):
if profiler and 'profiler' in CONF:
trace_decorator = profiler.trace(name, kwargs)
return trace_decorator(f)
return f
return decorator

View File

@ -28,11 +28,14 @@ __all__ = [
import oslo_messaging as messaging
from oslo_serialization import jsonutils
from oslo_utils import importutils
from zun.common import context as zun_context
from zun.common import exception
import zun.conf
profiler = importutils.try_import("osprofiler.profiler")
CONF = zun.conf.CONF
TRANSPORT = None
NOTIFIER = None
@ -104,22 +107,56 @@ class RequestContextSerializer(messaging.Serializer):
return zun_context.RequestContext.from_dict(context)
class ProfilerRequestContextSerializer(RequestContextSerializer):
def serialize_context(self, context):
_context = super(ProfilerRequestContextSerializer,
self).serialize_context(context)
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 super(ProfilerRequestContextSerializer,
self).deserialize_context(context)
def get_transport_url(url_str=None):
return messaging.TransportURL.parse(CONF, url_str)
def get_client(target, version_cap=None, serializer=None):
def get_client(target, version_cap=None, serializer=None, timeout=None):
assert TRANSPORT is not None
serializer = RequestContextSerializer(serializer)
if profiler:
serializer = ProfilerRequestContextSerializer(serializer)
else:
serializer = RequestContextSerializer(serializer)
return messaging.RPCClient(TRANSPORT,
target,
version_cap=version_cap,
serializer=serializer)
serializer=serializer,
timeout=timeout)
def get_server(target, endpoints, serializer=None):
assert TRANSPORT is not None
serializer = RequestContextSerializer(serializer)
if profiler:
serializer = ProfilerRequestContextSerializer(serializer)
else:
serializer = RequestContextSerializer(serializer)
return messaging.get_rpc_server(TRANSPORT,
target,
endpoints,

View File

@ -14,37 +14,43 @@
"""Common RPC service and API tools for Zun."""
import eventlet
import oslo_messaging as messaging
from oslo_service import service
from oslo_utils import importutils
from zun.common import profiler
from zun.common import rpc
import zun.conf
from zun.objects import base as objects_base
from zun.servicegroup import zun_service_periodic as servicegroup
# NOTE(paulczar):
# Ubuntu 14.04 forces librabbitmq when kombu is used
# Unfortunately it forces a version that has a crash
# bug. Calling eventlet.monkey_patch() tells kombu
# to use libamqp instead.
eventlet.monkey_patch()
osprofiler = importutils.try_import("osprofiler.profiler")
CONF = zun.conf.CONF
def _init_serializer():
serializer = rpc.RequestContextSerializer(
objects_base.ZunObjectSerializer())
if osprofiler:
serializer = rpc.ProfilerRequestContextSerializer(serializer)
else:
serializer = rpc.RequestContextSerializer(serializer)
return serializer
class Service(service.Service):
def __init__(self, topic, server, handlers, binary):
super(Service, self).__init__()
serializer = rpc.RequestContextSerializer(
objects_base.ZunObjectSerializer())
serializer = _init_serializer()
transport = messaging.get_transport(CONF)
# TODO(asalkeld) add support for version='x.y'
target = messaging.Target(topic=topic, server=server)
self._server = messaging.get_rpc_server(transport, target, handlers,
serializer=serializer)
self.binary = binary
profiler.setup(binary, CONF.host)
def start(self):
servicegroup.setup(CONF, self.binary, self.tg)
@ -65,8 +71,7 @@ class Service(service.Service):
class API(object):
def __init__(self, transport=None, context=None, topic=None, server=None,
timeout=None):
serializer = rpc.RequestContextSerializer(
objects_base.ZunObjectSerializer())
serializer = _init_serializer()
if transport is None:
exmods = rpc.get_allowed_exmods()
transport = messaging.get_transport(CONF,

View File

@ -13,11 +13,13 @@
"""Handles all requests relating to compute resources (e.g. containers,
networking and storage of containers, and compute hosts on which they run)."""
from zun.common import profiler
from zun.compute import rpcapi
from zun.objects import fields
from zun.scheduler import client as scheduler_client
@profiler.trace_cls("rpc")
class API(object):
"""API for interacting with the compute manager."""

View File

@ -13,10 +13,12 @@
# under the License.
from zun.common import profiler
from zun.common import rpc_service
import zun.conf
@profiler.trace_cls("rpc")
class API(rpc_service.API):
'''Client side of the container compute rpc API.

View File

@ -23,6 +23,7 @@ from zun.conf import glance_client
from zun.conf import image_driver
from zun.conf import nova_client
from zun.conf import path
from zun.conf import profiler
from zun.conf import scheduler
from zun.conf import services
from zun.conf import ssl
@ -43,3 +44,4 @@ scheduler.register_opts(CONF)
services.register_opts(CONF)
zun_client.register_opts(CONF)
ssl.register_opts(CONF)
profiler.register_opts(CONF)

27
zun/conf/profiler.py Normal file
View File

@ -0,0 +1,27 @@
# 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_utils import importutils
profiler_opts = importutils.try_import('osprofiler.opts')
def register_opts(conf):
if profiler_opts:
profiler_opts.set_defaults(conf)
def list_opts():
return {
profiler_opts._profiler_opt_group: profiler_opts._PROFILER_OPTS
}

View File

@ -19,6 +19,7 @@ from oslo_db import api as db_api
from zun.common import exception
from zun.common.i18n import _
from zun.common import profiler
import zun.conf
"""Add the database backend mapping here"""
@ -30,6 +31,7 @@ IMPL = db_api.DBAPI.from_config(CONF,
lazy=True)
@profiler.trace("db")
def _get_dbdriver_instance():
"""Return a DB API instance."""
if CONF.db_type == 'sql':
@ -43,6 +45,7 @@ def _get_dbdriver_instance():
"must be sql or etcd") % CONF.db_type)
@profiler.trace("db")
def list_containers(context, filters=None, limit=None, marker=None,
sort_key=None, sort_dir=None):
"""List matching containers.
@ -63,6 +66,7 @@ def list_containers(context, filters=None, limit=None, marker=None,
context, filters, limit, marker, sort_key, sort_dir)
@profiler.trace("db")
def create_container(context, values):
"""Create a new container.
@ -83,6 +87,7 @@ def create_container(context, values):
return _get_dbdriver_instance().create_container(context, values)
@profiler.trace("db")
def get_container_by_uuid(context, container_uuid):
"""Return a container.
@ -94,6 +99,7 @@ def get_container_by_uuid(context, container_uuid):
context, container_uuid)
@profiler.trace("db")
def get_container_by_name(context, container_name):
"""Return a container.
@ -105,6 +111,7 @@ def get_container_by_name(context, container_name):
context, container_name)
@profiler.trace("db")
def destroy_container(context, container_id):
"""Destroy a container and all associated interfaces.
@ -114,6 +121,7 @@ def destroy_container(context, container_id):
return _get_dbdriver_instance().destroy_container(context, container_id)
@profiler.trace("db")
def update_container(context, container_id, values):
"""Update properties of a container.
@ -127,6 +135,7 @@ def update_container(context, container_id, values):
context, container_id, values)
@profiler.trace("db")
def destroy_zun_service(host, binary):
"""Destroys a zun_service record.
@ -137,6 +146,7 @@ def destroy_zun_service(host, binary):
return _get_dbdriver_instance().destroy_zun_service(host, binary)
@profiler.trace("db")
def update_zun_service(host, binary, values):
"""Update properties of a zun_service.
@ -148,6 +158,7 @@ def update_zun_service(host, binary, values):
return _get_dbdriver_instance().update_zun_service(host, binary, values)
@profiler.trace("db")
def get_zun_service(context, host, binary):
"""Return a zun_service record.
@ -159,6 +170,7 @@ def get_zun_service(context, host, binary):
return _get_dbdriver_instance().get_zun_service(host, binary)
@profiler.trace("db")
def create_zun_service(values):
"""Create a new zun_service record.
@ -169,6 +181,7 @@ def create_zun_service(values):
return _get_dbdriver_instance().create_zun_service(values)
@profiler.trace("db")
def list_zun_services(context, filters=None, limit=None,
marker=None, sort_key=None, sort_dir=None):
"""Get matching zun_service records.
@ -190,6 +203,7 @@ def list_zun_services(context, filters=None, limit=None,
filters, limit, marker, sort_key, sort_dir)
@profiler.trace("db")
def list_zun_services_by_binary(context, binary):
"""List matching zun services.
@ -201,6 +215,7 @@ def list_zun_services_by_binary(context, binary):
return _get_dbdriver_instance().list_zun_services_by_binary(binary)
@profiler.trace("db")
def pull_image(context, values):
"""Create a new image.
@ -221,6 +236,7 @@ def pull_image(context, values):
return _get_dbdriver_instance().pull_image(context, values)
@profiler.trace("db")
def update_image(image_id, values):
"""Update properties of an image.
@ -231,6 +247,7 @@ def update_image(image_id, values):
return _get_dbdriver_instance().update_image(image_id, values)
@profiler.trace("db")
def list_images(context, filters=None,
limit=None, marker=None,
sort_key=None, sort_dir=None):
@ -252,6 +269,7 @@ def list_images(context, filters=None,
context, filters, limit, marker, sort_key, sort_dir)
@profiler.trace("db")
def get_image_by_id(context, image_id):
"""Return an image.
@ -262,6 +280,7 @@ def get_image_by_id(context, image_id):
return _get_dbdriver_instance().get_image_by_id(context, image_id)
@profiler.trace("db")
def get_image_by_uuid(context, image_uuid):
"""Return an image.
@ -272,6 +291,7 @@ def get_image_by_uuid(context, image_uuid):
return _get_dbdriver_instance().get_image_by_uuid(context, image_uuid)
@profiler.trace("db")
def list_resource_providers(context, filters=None, limit=None, marker=None,
sort_key=None, sort_dir=None):
"""Get matching resource providers.
@ -291,6 +311,7 @@ def list_resource_providers(context, filters=None, limit=None, marker=None,
context, filters, limit, marker, sort_key, sort_dir)
@profiler.trace("db")
def create_resource_provider(context, values):
"""Create a new resource provider.
@ -304,6 +325,7 @@ def create_resource_provider(context, values):
return _get_dbdriver_instance().create_resource_provider(context, values)
@profiler.trace("db")
def get_resource_provider(context, provider_ident):
"""Return a resource provider.
@ -315,6 +337,7 @@ def get_resource_provider(context, provider_ident):
context, provider_ident)
@profiler.trace("db")
def destroy_resource_provider(context, provider_id):
"""Destroy a resource provider and all associated interfaces.
@ -325,6 +348,7 @@ def destroy_resource_provider(context, provider_id):
context, provider_id)
@profiler.trace("db")
def update_resource_provider(context, provider_id, values):
"""Update properties of a resource provider.
@ -338,6 +362,7 @@ def update_resource_provider(context, provider_id, values):
context, provider_id, values)
@profiler.trace("db")
def list_resource_classes(context, limit=None, marker=None, sort_key=None,
sort_dir=None):
"""Get matching resource classes.
@ -356,6 +381,7 @@ def list_resource_classes(context, limit=None, marker=None, sort_key=None,
context, limit, marker, sort_key, sort_dir)
@profiler.trace("db")
def create_resource_class(context, values):
"""Create a new resource class.
@ -368,6 +394,7 @@ def create_resource_class(context, values):
return _get_dbdriver_instance().create_resource_class(context, values)
@profiler.trace("db")
def get_resource_class(context, resource_ident):
"""Return a resource class.
@ -379,6 +406,7 @@ def get_resource_class(context, resource_ident):
context, resource_ident)
@profiler.trace("db")
def destroy_resource_class(context, resource_uuid):
"""Destroy a resource class and all associated interfaces.
@ -389,6 +417,7 @@ def destroy_resource_class(context, resource_uuid):
context, resource_uuid)
@profiler.trace("db")
def update_resource_class(context, resource_uuid, values):
"""Update properties of a resource class.
@ -402,6 +431,7 @@ def update_resource_class(context, resource_uuid, values):
context, resource_uuid, values)
@profiler.trace("db")
def list_inventories(context, filters=None, limit=None, marker=None,
sort_key=None, sort_dir=None):
"""List matching inventories.
@ -422,6 +452,7 @@ def list_inventories(context, filters=None, limit=None, marker=None,
context, filters, limit, marker, sort_key, sort_dir)
@profiler.trace("db")
def create_inventory(context, provider_id, values):
"""Create a new inventory.
@ -436,6 +467,7 @@ def create_inventory(context, provider_id, values):
context, provider_id, values)
@profiler.trace("db")
def get_inventory(context, inventory_ident):
"""Return a inventory.
@ -447,6 +479,7 @@ def get_inventory(context, inventory_ident):
context, inventory_ident)
@profiler.trace("db")
def destroy_inventory(context, inventory_id):
"""Destroy an inventory and all associated interfaces.
@ -456,6 +489,7 @@ def destroy_inventory(context, inventory_id):
return _get_dbdriver_instance().destroy_inventory(context, inventory_id)
@profiler.trace("db")
def update_inventory(context, inventory_id, values):
"""Update properties of an inventory.
@ -469,6 +503,7 @@ def update_inventory(context, inventory_id, values):
context, inventory_id, values)
@profiler.trace("db")
def list_allocations(context, filters=None, limit=None, marker=None,
sort_key=None, sort_dir=None):
"""List matching allocations.
@ -489,6 +524,7 @@ def list_allocations(context, filters=None, limit=None, marker=None,
context, filters, limit, marker, sort_key, sort_dir)
@profiler.trace("db")
def create_allocation(context, values):
"""Create a new allocation.
@ -501,6 +537,7 @@ def create_allocation(context, values):
return _get_dbdriver_instance().create_allocation(context, values)
@profiler.trace("db")
def get_allocation(context, allocation_id):
"""Return an allocation.
@ -511,6 +548,7 @@ def get_allocation(context, allocation_id):
return _get_dbdriver_instance().get_allocation(context, allocation_id)
@profiler.trace("db")
def destroy_allocation(context, allocation_id):
"""Destroy an allocation and all associated interfaces.
@ -520,6 +558,7 @@ def destroy_allocation(context, allocation_id):
return _get_dbdriver_instance().destroy_allocation(context, allocation_id)
@profiler.trace("db")
def update_allocation(context, allocation_id, values):
"""Update properties of an allocation.
@ -533,6 +572,7 @@ def update_allocation(context, allocation_id, values):
context, allocation_id, values)
@profiler.trace("db")
def list_compute_nodes(context, filters=None, limit=None, marker=None,
sort_key=None, sort_dir=None):
"""List matching compute nodes.
@ -553,6 +593,7 @@ def list_compute_nodes(context, filters=None, limit=None, marker=None,
context, filters, limit, marker, sort_key, sort_dir)
@profiler.trace("db")
def create_compute_node(context, values):
"""Create a new compute node.
@ -565,6 +606,7 @@ def create_compute_node(context, values):
return _get_dbdriver_instance().create_compute_node(context, values)
@profiler.trace("db")
def get_compute_node(context, node_uuid):
"""Return a compute node.
@ -575,6 +617,7 @@ def get_compute_node(context, node_uuid):
return _get_dbdriver_instance().get_compute_node(context, node_uuid)
@profiler.trace("db")
def get_compute_node_by_hostname(context, hostname):
"""Return a compute node.
@ -586,6 +629,7 @@ def get_compute_node_by_hostname(context, hostname):
context, hostname)
@profiler.trace("db")
def destroy_compute_node(context, node_uuid):
"""Destroy a compute node and all associated interfaces.
@ -595,6 +639,7 @@ def destroy_compute_node(context, node_uuid):
return _get_dbdriver_instance().destroy_compute_node(context, node_uuid)
@profiler.trace("db")
def update_compute_node(context, node_uuid, values):
"""Update properties of a compute node.

View File

@ -17,9 +17,11 @@
from oslo_db import exception as db_exc
from oslo_db.sqlalchemy import session as db_session
from oslo_db.sqlalchemy import utils as db_utils
from oslo_utils import importutils
from oslo_utils import strutils
from oslo_utils import timeutils
from oslo_utils import uuidutils
import sqlalchemy as sa
from sqlalchemy.orm import contains_eager
from sqlalchemy.orm.exc import MultipleResultsFound
from sqlalchemy.orm.exc import NoResultFound
@ -30,6 +32,8 @@ from zun.common.i18n import _
import zun.conf
from zun.db.sqlalchemy import models
profiler_sqlalchemy = importutils.try_import('osprofiler.sqlalchemy')
CONF = zun.conf.CONF
_FACADE = None
@ -39,6 +43,9 @@ def _create_facade_lazily():
global _FACADE
if _FACADE is None:
_FACADE = db_session.enginefacade.get_legacy_facade()
if profiler_sqlalchemy:
if CONF.profiler.enabled and CONF.profiler.trace_sqlalchemy:
profiler_sqlalchemy.add_tracing(sa, _FACADE.get_engine(), "db")
return _FACADE

View File

@ -0,0 +1,70 @@
# Copyright 2017 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.
import inspect
import mock
from oslo_utils import importutils
from osprofiler import initializer as profiler_init
from osprofiler import opts as profiler_opts
import six.moves as six
from zun.common import profiler
from zun import conf
from zun.tests import base
class TestProfiler(base.TestCase):
def test_all_public_methods_are_traced(self):
profiler_opts.set_defaults(conf.CONF)
self.config(enabled=True,
group='profiler')
classes = [
'zun.compute.api.API',
'zun.compute.rpcapi.API',
]
for clsname in classes:
# give the metaclass and trace_cls() decorator a chance to patch
# methods of the classes above
six.reload_module(
importutils.import_module(clsname.rsplit('.', 1)[0]))
cls = importutils.import_class(clsname)
for attr, obj in cls.__dict__.items():
# only public methods are traced
if attr.startswith('_'):
continue
# only checks callables
if not (inspect.ismethod(obj) or inspect.isfunction(obj)):
continue
# osprofiler skips static methods
if isinstance(obj, staticmethod):
continue
self.assertTrue(getattr(obj, '__traced__', False), obj)
@mock.patch.object(profiler_init, 'init_from_conf')
def test_setup_profiler(self, mock_init):
self.config(enabled=True,
group='profiler')
profiler.setup('foo', 'localhost')
mock_init.assert_called_once_with(conf=conf.CONF,
context=mock.ANY,
project="zun",
service='foo',
host='localhost')

View File

@ -0,0 +1,228 @@
# Copyright 2017 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.
import mock
import oslo_messaging as messaging
from oslo_serialization import jsonutils
from zun.common import context
from zun.common import rpc
from zun.tests import base
class TestRpc(base.TestCase):
@mock.patch.object(rpc, 'profiler', None)
@mock.patch.object(rpc, 'RequestContextSerializer')
@mock.patch.object(messaging, 'RPCClient')
def test_get_client(self, mock_client, mock_ser):
rpc.TRANSPORT = mock.Mock()
tgt = mock.Mock()
ser = mock.Mock()
mock_client.return_value = 'client'
mock_ser.return_value = ser
client = rpc.get_client(tgt, version_cap='1.0', serializer='foo',
timeout=7139)
mock_ser.assert_called_once_with('foo')
mock_client.assert_called_once_with(rpc.TRANSPORT,
tgt, version_cap='1.0',
serializer=ser, timeout=7139)
self.assertEqual('client', client)
@mock.patch.object(rpc, 'profiler', mock.Mock())
@mock.patch.object(rpc, 'ProfilerRequestContextSerializer')
@mock.patch.object(messaging, 'RPCClient')
def test_get_client_profiler_enabled(self, mock_client, mock_ser):
rpc.TRANSPORT = mock.Mock()
tgt = mock.Mock()
ser = mock.Mock()
mock_client.return_value = 'client'
mock_ser.return_value = ser
client = rpc.get_client(tgt, version_cap='1.0', serializer='foo',
timeout=6969)
mock_ser.assert_called_once_with('foo')
mock_client.assert_called_once_with(rpc.TRANSPORT,
tgt, version_cap='1.0',
serializer=ser, timeout=6969)
self.assertEqual('client', client)
@mock.patch.object(rpc, 'profiler', None)
@mock.patch.object(rpc, 'RequestContextSerializer')
@mock.patch.object(messaging, 'get_rpc_server')
def test_get_server(self, mock_get, mock_ser):
rpc.TRANSPORT = mock.Mock()
ser = mock.Mock()
tgt = mock.Mock()
ends = mock.Mock()
mock_ser.return_value = ser
mock_get.return_value = 'server'
server = rpc.get_server(tgt, ends, serializer='foo')
mock_ser.assert_called_once_with('foo')
mock_get.assert_called_once_with(rpc.TRANSPORT, tgt, ends,
executor='eventlet', serializer=ser)
self.assertEqual('server', server)
@mock.patch.object(rpc, 'profiler', mock.Mock())
@mock.patch.object(rpc, 'ProfilerRequestContextSerializer')
@mock.patch.object(messaging, 'get_rpc_server')
def test_get_server_profiler_enabled(self, mock_get, mock_ser):
rpc.TRANSPORT = mock.Mock()
ser = mock.Mock()
tgt = mock.Mock()
ends = mock.Mock()
mock_ser.return_value = ser
mock_get.return_value = 'server'
server = rpc.get_server(tgt, ends, serializer='foo')
mock_ser.assert_called_once_with('foo')
mock_get.assert_called_once_with(rpc.TRANSPORT, tgt, ends,
executor='eventlet', serializer=ser)
self.assertEqual('server', server)
def test_cleanup_transport_null(self):
rpc.TRANSPORT = None
rpc.NOTIFIER = mock.Mock()
self.assertRaises(AssertionError, rpc.cleanup)
def test_cleanup_notifier_null(self):
rpc.TRANSPORT = mock.Mock()
rpc.NOTIFIER = None
self.assertRaises(AssertionError, rpc.cleanup)
def test_cleanup(self):
rpc.NOTIFIER = mock.Mock()
rpc.TRANSPORT = mock.Mock()
trans_cleanup = mock.Mock()
rpc.TRANSPORT.cleanup = trans_cleanup
rpc.cleanup()
trans_cleanup.assert_called_once_with()
self.assertIsNone(rpc.TRANSPORT)
self.assertIsNone(rpc.NOTIFIER)
def test_add_extra_exmods(self):
rpc.EXTRA_EXMODS = []
rpc.add_extra_exmods('foo', 'bar')
self.assertEqual(['foo', 'bar'], rpc.EXTRA_EXMODS)
def test_clear_extra_exmods(self):
rpc.EXTRA_EXMODS = ['foo', 'bar']
rpc.clear_extra_exmods()
self.assertEqual(0, len(rpc.EXTRA_EXMODS))
def test_serialize_entity(self):
with mock.patch.object(jsonutils, 'to_primitive') as mock_prim:
rpc.JsonPayloadSerializer.serialize_entity('context', 'entity')
mock_prim.assert_called_once_with('entity', convert_instances=True)
class TestRequestContextSerializer(base.TestCase):
def setUp(self):
super(TestRequestContextSerializer, self).setUp()
self.mock_base = mock.Mock()
self.ser = rpc.RequestContextSerializer(self.mock_base)
self.ser_null = rpc.RequestContextSerializer(None)
def test_serialize_entity(self):
self.mock_base.serialize_entity.return_value = 'foo'
ser_ent = self.ser.serialize_entity('context', 'entity')
self.mock_base.serialize_entity.assert_called_once_with('context',
'entity')
self.assertEqual('foo', ser_ent)
def test_serialize_entity_null_base(self):
ser_ent = self.ser_null.serialize_entity('context', 'entity')
self.assertEqual('entity', ser_ent)
def test_deserialize_entity(self):
self.mock_base.deserialize_entity.return_value = 'foo'
deser_ent = self.ser.deserialize_entity('context', 'entity')
self.mock_base.deserialize_entity.assert_called_once_with('context',
'entity')
self.assertEqual('foo', deser_ent)
def test_deserialize_entity_null_base(self):
deser_ent = self.ser_null.deserialize_entity('context', 'entity')
self.assertEqual('entity', deser_ent)
def test_serialize_context(self):
context = mock.Mock()
self.ser.serialize_context(context)
context.to_dict.assert_called_once_with()
@mock.patch.object(context, 'RequestContext')
def test_deserialize_context(self, mock_req):
self.ser.deserialize_context('context')
mock_req.from_dict.assert_called_once_with('context')
class TestProfilerRequestContextSerializer(base.TestCase):
def setUp(self):
super(TestProfilerRequestContextSerializer, self).setUp()
self.ser = rpc.ProfilerRequestContextSerializer(mock.Mock())
@mock.patch('zun.common.rpc.profiler')
def test_serialize_context(self, mock_profiler):
prof = mock_profiler.get.return_value
prof.hmac_key = 'swordfish'
prof.get_base_id.return_value = 'baseid'
prof.get_id.return_value = 'parentid'
context = mock.Mock()
context.to_dict.return_value = {'project_id': 'test'}
self.assertEqual({
'project_id': 'test',
'trace_info': {
'hmac_key': 'swordfish',
'base_id': 'baseid',
'parent_id': 'parentid'
}
}, self.ser.serialize_context(context))
@mock.patch('zun.common.rpc.profiler')
def test_deserialize_context(self, mock_profiler):
serialized = {'project_id': 'test',
'trace_info': {
'hmac_key': 'swordfish',
'base_id': 'baseid',
'parent_id': 'parentid'}}
context = self.ser.deserialize_context(serialized)
self.assertEqual('test', context.project_id)
mock_profiler.init.assert_called_once_with(
hmac_key='swordfish', base_id='baseid', parent_id='parentid')