Hello community, here is the log from the commit of package python-osprofiler for openSUSE:Factory checked in at 2018-01-17 21:56:39 ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ Comparing /work/SRC/openSUSE:Factory/python-osprofiler (Old) and /work/SRC/openSUSE:Factory/.python-osprofiler.new (New) ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Package is "python-osprofiler" Wed Jan 17 21:56:39 2018 rev:5 rq:565775 version:1.14.0 Changes: -------- --- /work/SRC/openSUSE:Factory/python-osprofiler/python-osprofiler.changes 2017-08-28 15:30:00.400435309 +0200 +++ /work/SRC/openSUSE:Factory/.python-osprofiler.new/python-osprofiler.changes 2018-01-17 21:56:41.491767995 +0100 @@ -1,0 +2,16 @@ +Mon Jan 15 12:04:06 UTC 2018 - cloud-de...@suse.de + +- update to version 1.14.0 + - Remove unused parameters from Profiler class + - Make dependency on oslo.messaging runtime only + - Update reno for stable/pike + - Add loading local static files option of template.html + - Do not require OpenStack authentication to run osprofiler CLI + - Improve unit test coverage + - Make test_notifier independent of test case execution order + - Remove dependency on oslo.log library + - Extend messaging driver to support reporting + - Handle and report SQLAlchemy errors + - Add function/sql results to trace info + +------------------------------------------------------------------- Old: ---- osprofiler-1.11.0.tar.gz New: ---- osprofiler-1.14.0.tar.gz ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ Other differences: ------------------ ++++++ python-osprofiler.spec ++++++ --- /var/tmp/diff_new_pack.8PDYwi/_old 2018-01-17 21:56:42.163736562 +0100 +++ /var/tmp/diff_new_pack.8PDYwi/_new 2018-01-17 21:56:42.171736188 +0100 @@ -1,7 +1,7 @@ # # spec file for package python-osprofiler # -# Copyright (c) 2017 SUSE LINUX GmbH, Nuernberg, Germany. +# Copyright (c) 2018 SUSE LINUX GmbH, Nuernberg, Germany. # # All modifications and additions to the file contributed by third parties # remain the property of their copyright owners, unless otherwise agreed @@ -16,39 +16,63 @@ # -%global sname osprofiler Name: python-osprofiler -Version: 1.11.0 +Version: 1.14.0 Release: 0 Summary: OpenStack Profiler Library License: Apache-2.0 Group: Development/Languages/Python Url: https://launchpad.net/osprofiler -Source0: https://pypi.io/packages/source/o/%{sname}/%{sname}-%{version}.tar.gz +Source0: https://files.pythonhosted.org/packages/source/o/osprofiler/osprofiler-1.14.0.tar.gz BuildRequires: openstack-macros -BuildRequires: python-WebOb >= 1.7.1 -BuildRequires: python-ceilometerclient >= 2.5.0 -BuildRequires: python-ddt >= 1.0.1 BuildRequires: python-devel -BuildRequires: python-elasticsearch >= 2.0.0 -BuildRequires: python-mock >= 2.0 -BuildRequires: python-oslo.concurrency >= 3.8.0 -BuildRequires: python-oslo.config >= 4.0.0 -BuildRequires: python-oslo.log >= 3.22.0 -BuildRequires: python-oslo.utils >= 3.20.0 -BuildRequires: python-pymongo >= 3.0.2 -BuildRequires: python-python-subunit >= 0.0.18 -BuildRequires: python-redis >= 2.10.0 -BuildRequires: python-six >= 1.9.0 -BuildRequires: python-testrepository >= 0.0.18 -BuildRequires: python-testtools >= 1.4.0 +BuildRequires: python2-WebOb >= 1.7.1 +BuildRequires: python2-ceilometerclient >= 2.5.0 +BuildRequires: python2-ddt >= 1.0.1 +BuildRequires: python2-elasticsearch >= 2.0.0 +BuildRequires: python2-mock >= 2.0.0 +BuildRequires: python2-oslo.concurrency >= 3.20.0 +BuildRequires: python2-oslo.config >= 4.6.0 +BuildRequires: python2-oslo.log >= 3.30.0 +BuildRequires: python2-oslo.utils >= 3.31.0 +BuildRequires: python2-pymongo >= 3.0.2 +BuildRequires: python2-python-subunit >= 1.0.0 +BuildRequires: python2-redis >= 2.10.0 +BuildRequires: python2-six >= 1.10.0 +BuildRequires: python2-testrepository >= 0.0.18 +BuildRequires: python2-testtools >= 2.2.0 +BuildRequires: python3-WebOb >= 1.7.1 +BuildRequires: python3-ceilometerclient >= 2.5.0 +BuildRequires: python3-ddt >= 1.0.1 +BuildRequires: python3-devel +BuildRequires: python3-elasticsearch >= 2.0.0 +BuildRequires: python3-mock >= 2.0.0 +BuildRequires: python3-oslo.concurrency >= 3.20.0 +BuildRequires: python3-oslo.config >= 4.6.0 +BuildRequires: python3-oslo.log >= 3.30.0 +BuildRequires: python3-oslo.utils >= 3.31.0 +BuildRequires: python3-pymongo >= 3.0.2 +BuildRequires: python3-python-subunit >= 1.0.0 +BuildRequires: python3-redis >= 2.10.0 +BuildRequires: python3-six >= 1.10.0 +BuildRequires: python3-testrepository >= 0.0.18 +BuildRequires: python3-testtools >= 2.2.0 Requires: python-WebOb >= 1.7.1 -Requires: python-oslo.concurrency >= 3.8.0 -Requires: python-oslo.config >= 4.0.0 -Requires: python-oslo.log >= 3.22.0 -Requires: python-oslo.utils >= 3.20.0 -Requires: python-six >= 1.9.0 +Requires: python-oslo.concurrency >= 3.20.0 +Requires: python-oslo.config >= 4.6.0 +Requires: python-oslo.log >= 3.30.0 +Requires: python-oslo.utils >= 3.31.0 +Requires: python-six >= 1.10.0 BuildArch: noarch +%if 0%{?suse_version} +Requires(post): update-alternatives +Requires(postun): update-alternatives +%else +# on RDO, update-alternatives is in chkconfig +Requires(post): chkconfig +Requires(postun): chkconfig +%endif +%python_subpackages %description OSProfiler provides a tiny but powerful library that is used by @@ -58,42 +82,51 @@ to build a tree of calls which can be quite handy for a variety of reasons (for example in isolating cross-project performance issues). -%package doc +%package -n python-osprofiler-doc Summary: Documentation for OSProfiler Group: Development/Languages/Python BuildRequires: python-Sphinx -BuildRequires: python-openstackdocstheme >= 1.11.0 +BuildRequires: python-openstackdocstheme >= 1.17.0 -%description doc +%description -n python-osprofiler-doc Documentation for OSProfiler. %prep -%autosetup -n %{sname}-%{version} +%autosetup -p1 -n osprofiler-1.14.0 %py_req_cleanup sed -i 's/^warning-is-error.*/warning-is-error = 0/g' setup.cfg %build -%{py2_build} +%{python_build} %install -%{py2_install} +%{python_install} +%python_clone -a %{buildroot}%{_bindir}/osprofiler # generate html docs %{__python2} setup.py build_sphinx # remove the sphinx-build leftovers rm -rf doc/build/html/.{doctrees,buildinfo} +%post +%python_install_alternative osprofiler + +%postun +%python_uninstall_alternative osprofiler + %check -%{__python2} setup.py testr +%{python_expand rm -rf .testrepository +$python setup.py testr +} -%files +%files %{python_files} %license LICENSE %doc README.rst ChangeLog -%{python2_sitelib}/osprofiler -%{python2_sitelib}/*.egg-info -%{_bindir}/osprofiler +%{python_sitelib}/osprofiler +%{python_sitelib}/*.egg-info +%python_alternative %{_bindir}/osprofiler -%files doc +%files -n python-osprofiler-doc %license LICENSE %doc doc/build/html ++++++ _service ++++++ --- /var/tmp/diff_new_pack.8PDYwi/_old 2018-01-17 21:56:42.211734317 +0100 +++ /var/tmp/diff_new_pack.8PDYwi/_new 2018-01-17 21:56:42.211734317 +0100 @@ -1,8 +1,8 @@ <services> <service mode="disabled" name="renderspec"> - <param name="input-template">https://raw.githubusercontent.com/openstack/rpm-packaging/stable/pike/openstack/osprofiler/osprofiler.spec.j2</param> + <param name="input-template">https://raw.githubusercontent.com/openstack/rpm-packaging/master/openstack/osprofiler/osprofiler.spec.j2</param> <param name="output-name">python-osprofiler.spec</param> - <param name="requirements">https://raw.githubusercontent.com/openstack/rpm-packaging/stable/pike/global-requirements.txt</param> + <param name="requirements">https://raw.githubusercontent.com/openstack/rpm-packaging/master/requirements.txt</param> <param name="changelog-email">cloud-de...@suse.de</param> <param name="changelog-provider">gh,openstack,osprofiler</param> </service> ++++++ osprofiler-1.11.0.tar.gz -> osprofiler-1.14.0.tar.gz ++++++ diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/osprofiler-1.11.0/AUTHORS new/osprofiler-1.14.0/AUTHORS --- old/osprofiler-1.11.0/AUTHORS 2017-07-18 15:11:04.000000000 +0200 +++ new/osprofiler-1.14.0/AUTHORS 2017-11-13 16:32:10.000000000 +0100 @@ -38,6 +38,8 @@ Vipin Balachandran <vb...@vmware.com> Vu Cong Tuan <tua...@vn.fujitsu.com> Zhi Yan Liu <zhiy...@cn.ibm.com> +Zuul <z...@review.openstack.org> +chenxu <424024...@qq.com> gecong1973 <ge.c...@zte.com.cn> howardlee <lihongwe...@inspur.com> kavithahr <kavith...@nectechnologies.in> diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/osprofiler-1.11.0/ChangeLog new/osprofiler-1.14.0/ChangeLog --- old/osprofiler-1.11.0/ChangeLog 2017-07-18 15:11:03.000000000 +0200 +++ new/osprofiler-1.14.0/ChangeLog 2017-11-13 16:32:10.000000000 +0100 @@ -1,6 +1,29 @@ CHANGES ======= +1.14.0 +------ + +* Extend messaging driver to support reporting +* Handle and report SQLAlchemy errors + +1.13.0 +------ + +* Remove dependency on oslo.log library + +1.12.0 +------ + +* Do not require OpenStack authentication to run osprofiler CLI +* Make dependency on oslo.messaging runtime only +* Make test\_notifier independent of test case execution order +* Add function/sql results to trace info +* Improve unit test coverage +* Remove unused parameters from Profiler class +* Add loading local static files option of template.html +* Update reno for stable/pike + 1.11.0 ------ diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/osprofiler-1.11.0/PKG-INFO new/osprofiler-1.14.0/PKG-INFO --- old/osprofiler-1.11.0/PKG-INFO 2017-07-18 15:11:04.000000000 +0200 +++ new/osprofiler-1.14.0/PKG-INFO 2017-11-13 16:32:11.000000000 +0100 @@ -1,11 +1,12 @@ Metadata-Version: 1.1 Name: osprofiler -Version: 1.11.0 +Version: 1.14.0 Summary: OpenStack Profiler Library Home-page: https://docs.openstack.org/osprofiler/latest/ Author: OpenStack Author-email: openstack-...@lists.openstack.org License: UNKNOWN +Description-Content-Type: UNKNOWN Description: ======================== Team and repository tags ======================== diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/osprofiler-1.11.0/osprofiler/cmd/commands.py new/osprofiler-1.14.0/osprofiler/cmd/commands.py --- old/osprofiler-1.11.0/osprofiler/cmd/commands.py 2017-07-18 15:08:28.000000000 +0200 +++ new/osprofiler-1.14.0/osprofiler/cmd/commands.py 2017-11-13 16:29:18.000000000 +0100 @@ -37,10 +37,18 @@ help="Storage driver's connection string. Defaults to " "env[OSPROFILER_CONNECTION_STRING] if set, else " "ceilometer://") + @cliutils.arg("--transport-url", dest="transport_url", + help="Oslo.messaging transport URL (for messaging:// driver " + "only), e.g. rabbit://user:password@host:5672/") + @cliutils.arg("--idle-timeout", dest="idle_timeout", type=int, default=1, + help="How long to wait for the trace to finish, in seconds " + "(for messaging:// driver only)") @cliutils.arg("--json", dest="use_json", action="store_true", help="show trace in JSON") @cliutils.arg("--html", dest="use_html", action="store_true", help="show trace in HTML") + @cliutils.arg("--local-libs", dest="local_libs", action="store_true", + help="use local static files of html in /libs/") @cliutils.arg("--dot", dest="use_dot", action="store_true", help="show trace in DOT language") @cliutils.arg("--render-dot", dest="render_dot_filename", @@ -87,6 +95,10 @@ "$DATA", json.dumps(trace, indent=4, separators=(",", ": "), default=datetime_json_serialize)) + if args.local_libs: + output = output.replace("$LOCAL", "true") + else: + output = output.replace("$LOCAL", "false") elif args.use_dot: dot_graph = self._create_dot_graph(trace) output = dot_graph.source diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/osprofiler-1.11.0/osprofiler/cmd/shell.py new/osprofiler-1.14.0/osprofiler/cmd/shell.py --- old/osprofiler-1.11.0/osprofiler/cmd/shell.py 2017-07-18 15:08:28.000000000 +0200 +++ new/osprofiler-1.14.0/osprofiler/cmd/shell.py 2017-11-13 16:29:18.000000000 +0100 @@ -37,31 +37,6 @@ args = self._get_base_parser().parse_args(argv) opts.set_defaults(cfg.CONF) - if not (args.os_auth_token and args.ceilometer_url): - if not args.os_username: - raise exc.CommandError( - "You must provide a username via either --os-username or " - "via env[OS_USERNAME]") - - if not args.os_password: - raise exc.CommandError( - "You must provide a password via either --os-password or " - "via env[OS_PASSWORD]") - - if self._no_project_and_domain_set(args): - # steer users towards Keystone V3 API - raise exc.CommandError( - "You must provide a project_id via either --os-project-id " - "or via env[OS_PROJECT_ID] and a domain_name via either " - "--os-user-domain-name or via env[OS_USER_DOMAIN_NAME] or " - "a domain_id via either --os-user-domain-id or via " - "env[OS_USER_DOMAIN_ID]") - - if not args.os_auth_url: - raise exc.CommandError( - "You must provide an auth url via either --os-auth-url or " - "via env[OS_AUTH_URL]") - args.func(args) def _get_base_parser(self): diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/osprofiler-1.11.0/osprofiler/cmd/template.html new/osprofiler-1.14.0/osprofiler/cmd/template.html --- old/osprofiler-1.11.0/osprofiler/cmd/template.html 2017-07-18 15:08:28.000000000 +0200 +++ new/osprofiler-1.14.0/osprofiler/cmd/template.html 2017-11-13 16:29:18.000000000 +0100 @@ -2,12 +2,6 @@ <html ng-app="app"> <head> - <link rel="stylesheet" href="https://maxcdn.bootstrapcdn.com/bootstrap/3.3.7/css/bootstrap.min.css"> - <link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/highlight.js/9.8.0/styles/github.min.css"> - <script src="https://ajax.googleapis.com/ajax/libs/angularjs/1.6.0/angular.min.js"></script> - <script src="https://angular-ui.github.io/bootstrap/ui-bootstrap-tpls-2.3.1.min.js"></script> - <script src="https://cdnjs.cloudflare.com/ajax/libs/highlight.js/9.8.0/highlight.min.js"></script> - <script src="https://pc035860.github.io/angular-highlightjs/angular-highlightjs.min.js"></script> <style> .trace { min-width: 900px; @@ -77,6 +71,28 @@ </style> <script> + var static_files = $LOCAL; + if (static_files){ + document.write('<link rel="stylesheet" href="/libs/bootstrap.min.css">'); + document.write('<link rel="stylesheet" href="/libs/github.min.css">'); + document.write('<script type="text/javascript" src="/libs/angular.min.js"><\/script>'); + document.write('<script type="text/javascript" src="/libs/ui-bootstrap-tpls-2.3.1.min.js"><\/script>'); + document.write('<script type="text/javascript" src="/libs/highlight.min.js"><\/script>'); + document.write('<script type="text/javascript" src="/libs/angular-highlightjs.min.js"><\/script>'); + } + else{ + document.write('<link rel="stylesheet" href="https://maxcdn.bootstrapcdn.com/bootstrap/3.3.7/css/bootstrap.min.css">'); + document.write('<link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/highlight.js/9.8.0/styles/github.min.css">'); + document.write('<script type="text/javascript" src="https://ajax.googleapis.com/ajax/libs/angularjs/1.6.0/angular.min.js"><\/script>'); + document.write('<script type="text/javascript" src="https://angular-ui.github.io/bootstrap/ui-bootstrap-tpls-2.3.1.min.js"><\/script>'); + document.write('<script type="text/javascript" src="https://cdnjs.cloudflare.com/ajax/libs/highlight.js/9.8.0/highlight.min.js"><\/script>'); + document.write('<script type="text/javascript" src="https://pc035860.github.io/angular-highlightjs/angular-highlightjs.min.js"><\/script>'); + } + </script> +</head> + +<body> + <script> (function(angular) { 'use strict'; @@ -208,9 +224,6 @@ } })(window.angular); </script> -</head> - -<body> <!--Tree item template--> <script type="text/ng-template" id="tree_item_renderer.html"> <div ng-init="hide_children=false"> diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/osprofiler-1.11.0/osprofiler/drivers/base.py new/osprofiler-1.14.0/osprofiler/drivers/base.py --- old/osprofiler-1.11.0/osprofiler/drivers/base.py 2017-07-18 15:08:28.000000000 +0200 +++ new/osprofiler-1.14.0/osprofiler/drivers/base.py 2017-11-13 16:29:18.000000000 +0100 @@ -14,13 +14,13 @@ # under the License. import datetime +import logging -from oslo_log import log import six.moves.urllib.parse as urlparse from osprofiler import _utils -LOG = log.getLogger(__name__) +LOG = logging.getLogger(__name__) def get_driver(connection_string, *args, **kwargs): diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/osprofiler-1.11.0/osprofiler/drivers/messaging.py new/osprofiler-1.14.0/osprofiler/drivers/messaging.py --- old/osprofiler-1.11.0/osprofiler/drivers/messaging.py 2017-07-18 15:08:28.000000000 +0200 +++ new/osprofiler-1.14.0/osprofiler/drivers/messaging.py 2017-11-13 16:29:18.000000000 +0100 @@ -13,25 +13,66 @@ # License for the specific language governing permissions and limitations # under the License. +import functools +import signal +import time + +from oslo_utils import importutils + from osprofiler.drivers import base class Messaging(base.Driver): - def __init__(self, connection_str, messaging=None, context=None, - transport=None, project=None, service=None, - host=None, **kwargs): - """Driver sending notifications via message queues.""" + def __init__(self, connection_str, project=None, service=None, host=None, + context=None, conf=None, transport_url=None, + idle_timeout=1, **kwargs): + """Driver that uses messaging as transport for notifications + + :param connection_str: OSProfiler driver connection string, + equals to messaging:// + :param project: project name that will be included into notification + :param service: service name that will be included into notification + :param host: host name that will be included into notification + :param context: oslo.messaging context + :param conf: oslo.config CONF object + :param transport_url: oslo.messaging transport, e.g. + rabbit://rabbit:password@devstack:5672/ + :param idle_timeout: how long to wait for new notifications after + the last one seen in the trace; this parameter is useful to + collect full trace of asynchronous commands, e.g. when user + runs `osprofiler` right after `openstack server create` + :param kwargs: black hole for any other parameters + """ + + self.oslo_messaging = importutils.try_import("oslo_messaging") + if not self.oslo_messaging: + raise ValueError("Oslo.messaging library is required for " + "messaging driver") super(Messaging, self).__init__(connection_str, project=project, service=service, host=host) - self.messaging = messaging self.context = context - self.client = messaging.Notifier( - transport, publisher_id=self.host, driver="messaging", + if not conf: + oslo_config = importutils.try_import("oslo_config") + if not oslo_config: + raise ValueError("Oslo.config library is required for " + "messaging driver") + conf = oslo_config.cfg.CONF + + transport_kwargs = {} + if transport_url: + transport_kwargs["url"] = transport_url + + self.transport = self.oslo_messaging.get_notification_transport( + conf, **transport_kwargs) + self.client = self.oslo_messaging.Notifier( + self.transport, publisher_id=self.host, driver="messaging", topics=["profiler"], retry=0) + self.idle_timeout = idle_timeout + @classmethod def get_name(cls): return "messaging" @@ -60,3 +101,98 @@ self.client.info(context or self.context, "profiler.%s" % info["service"], info) + + def get_report(self, base_id): + notification_endpoint = NotifyEndpoint(self.oslo_messaging, base_id) + endpoints = [notification_endpoint] + targets = [self.oslo_messaging.Target(topic="profiler")] + server = self.oslo_messaging.notify.get_notification_listener( + self.transport, targets, endpoints, executor="threading") + + state = dict(running=False) + sfn = functools.partial(signal_handler, state=state) + + # modify signal handlers to handle interruption gracefully + old_sigterm_handler = signal.signal(signal.SIGTERM, sfn) + old_sigint_handler = signal.signal(signal.SIGINT, sfn) + + try: + server.start() + except self.oslo_messaging.server.ServerListenError: + # failed to start the server + raise + except SignalExit: + print("Execution interrupted while trying to connect to " + "messaging server. No data was collected.") + return {} + + # connected to server, now read the data + try: + # run until the trace is complete + state["running"] = True + + while state["running"]: + last_read_time = notification_endpoint.get_last_read_time() + wait = self.idle_timeout - (time.time() - last_read_time) + if wait < 0: + state["running"] = False + else: + time.sleep(wait) + except SignalExit: + print("Execution interrupted. Terminating") + finally: + server.stop() + server.wait() + + # restore original signal handlers + signal.signal(signal.SIGTERM, old_sigterm_handler) + signal.signal(signal.SIGINT, old_sigint_handler) + + events = notification_endpoint.get_messages() + + if not events: + print("No events are collected for Trace UUID %s. Please note " + "that osprofiler has read ALL events from profiler topic, " + "but has not found any for specified Trace UUID." % base_id) + + for n in events: + trace_id = n["trace_id"] + parent_id = n["parent_id"] + name = n["name"] + project = n["project"] + service = n["service"] + host = n["info"]["host"] + timestamp = n["timestamp"] + + self._append_results(trace_id, parent_id, name, project, service, + host, timestamp, n) + + return self._parse_results() + + +class NotifyEndpoint(object): + + def __init__(self, oslo_messaging, base_id): + self.received_messages = [] + self.last_read_time = time.time() + self.filter_rule = oslo_messaging.NotificationFilter( + payload={"base_id": base_id}) + + def info(self, ctxt, publisher_id, event_type, payload, metadata): + self.received_messages.append(payload) + self.last_read_time = time.time() + + def get_messages(self): + return self.received_messages + + def get_last_read_time(self): + return self.last_read_time # time when the latest event was received + + +class SignalExit(BaseException): + pass + + +def signal_handler(signum, frame, state): + state["running"] = False + raise SignalExit() diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/osprofiler-1.11.0/osprofiler/initializer.py new/osprofiler-1.14.0/osprofiler/initializer.py --- old/osprofiler-1.11.0/osprofiler/initializer.py 2017-07-18 15:08:28.000000000 +0200 +++ new/osprofiler-1.14.0/osprofiler/initializer.py 2017-11-13 16:29:18.000000000 +0100 @@ -13,8 +13,6 @@ # License for the specific language governing permissions and limitations # under the License. -import oslo_messaging - from osprofiler import notifier from osprofiler import web @@ -30,17 +28,12 @@ running on. """ connection_str = conf.profiler.connection_string - kwargs = {} - if connection_str.startswith("messaging"): - kwargs = {"messaging": oslo_messaging, - "transport": oslo_messaging.get_notification_transport(conf)} _notifier = notifier.create( connection_str, context=context, project=project, service=service, host=host, - conf=conf, - **kwargs) + conf=conf) notifier.set(_notifier) web.enable(conf.profiler.hmac_keys) diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/osprofiler-1.11.0/osprofiler/profiler.py new/osprofiler-1.14.0/osprofiler/profiler.py --- old/osprofiler-1.11.0/osprofiler/profiler.py 2017-07-18 15:08:28.000000000 +0200 +++ new/osprofiler-1.14.0/osprofiler/profiler.py 2017-11-13 16:29:18.000000000 +0100 @@ -44,8 +44,7 @@ % (attr_name, traced_times)) -def init(hmac_key, base_id=None, parent_id=None, connection_str=None, - project=None, service=None): +def init(hmac_key, base_id=None, parent_id=None): """Init profiler instance for current thread. You should call profiler.init() before using osprofiler. @@ -54,16 +53,10 @@ :param hmac_key: secret key to sign trace information. :param base_id: Used to bind all related traces. :param parent_id: Used to build tree of traces. - :param connection_str: Connection string to the backend to use for - notifications. - :param project: Project name that is under profiling - :param service: Service name that is under profiling :returns: Profiler instance """ __local_ctx.profiler = _Profiler(hmac_key, base_id=base_id, - parent_id=parent_id, - connection_str=connection_str, - project=project, service=service) + parent_id=parent_id) return __local_ctx.profiler @@ -94,7 +87,8 @@ profiler.stop(info=info) -def trace(name, info=None, hide_args=False, allow_multiple_trace=True): +def trace(name, info=None, hide_args=False, hide_result=True, + allow_multiple_trace=True): """Trace decorator for functions. Very useful if you would like to add trace point on existing function: @@ -109,6 +103,9 @@ :param hide_args: Don't push to trace info args and kwargs. Quite useful if you have some info in args that you wont to share, e.g. passwords. + :param hide_result: Boolean value to hide/show function result in trace. + True - hide function result (default). + False - show function result in trace. :param allow_multiple_trace: If the wrapped function has already been traced either allow the new trace to occur or raise a value error denoting that multiple @@ -140,24 +137,43 @@ @functools.wraps(f) def wrapper(*args, **kwargs): - if "name" not in info["function"]: + # NOTE(tovin07): Workaround for this issue + # F823 local variable 'info' + # (defined in enclosing scope on line xxx) + # referenced before assignment + info_ = info + if "name" not in info_["function"]: # Get this once (as it should **not** be changing in # subsequent calls). - info["function"]["name"] = reflection.get_callable_name(f) + info_["function"]["name"] = reflection.get_callable_name(f) if not hide_args: - info["function"]["args"] = str(args) - info["function"]["kwargs"] = str(kwargs) + info_["function"]["args"] = str(args) + info_["function"]["kwargs"] = str(kwargs) - with Trace(name, info=info): - return f(*args, **kwargs) + stop_info = None + try: + start(name, info=info_) + result = f(*args, **kwargs) + except Exception as ex: + stop_info = {"etype": reflection.get_class_name(ex)} + raise + else: + if not hide_result: + stop_info = {"function": {"result": repr(result)}} + return result + finally: + if stop_info: + stop(info=stop_info) + else: + stop() return wrapper return decorator -def trace_cls(name, info=None, hide_args=False, +def trace_cls(name, info=None, hide_args=False, hide_result=True, trace_private=False, allow_multiple_trace=True, trace_class_methods=False, trace_static_methods=False): """Trace decorator for instances of class . @@ -180,6 +196,9 @@ :param hide_args: Don't push to trace info args and kwargs. Quite useful if you have some info in args that you wont to share, e.g. passwords. + :param hide_result: Boolean value to hide/show function result in trace. + True - hide function result (default). + False - show function result in trace. :param trace_private: Trace methods that starts with "_". It wont trace methods that starts "__" even if it is turned on. :param trace_static_methods: Trace staticmethods. This may be prone to @@ -233,7 +252,8 @@ # halfway trace this class). _ensure_no_multiple_traced(traceable_attrs) for i, (attr_name, attr) in enumerate(traceable_attrs): - wrapped_method = trace(name, info=info, hide_args=hide_args)(attr) + wrapped_method = trace(name, info=info, hide_args=hide_args, + hide_result=hide_result)(attr) wrapper = traceable_wrappers[i] if wrapper is not None: wrapped_method = wrapper(wrapped_method) @@ -253,6 +273,7 @@ >>> __trace_args__ = {'name': 'rpc', >>> 'info': None, >>> 'hide_args': False, + >>> 'hide_result': True, >>> 'trace_private': False} >>> >>> def my_method(self, some_args): @@ -329,17 +350,13 @@ class _Profiler(object): - def __init__(self, hmac_key, base_id=None, parent_id=None, - connection_str=None, project=None, service=None): + def __init__(self, hmac_key, base_id=None, parent_id=None): self.hmac_key = hmac_key if not base_id: base_id = str(uuidutils.generate_uuid()) self._trace_stack = collections.deque([base_id, parent_id or base_id]) self._name = collections.deque() self._host = socket.gethostname() - self._connection_str = connection_str - self._project = project - self._service = service def get_base_id(self): """Return base id of a trace. @@ -373,8 +390,6 @@ info = info or {} info["host"] = self._host - info["project"] = self._project - info["service"] = self._service self._name.append(name) self._trace_stack.append(str(uuidutils.generate_uuid())) self._notify("%s-start" % name, info) @@ -388,8 +403,6 @@ """ info = info or {} info["host"] = self._host - info["project"] = self._project - info["service"] = self._service self._notify("%s-stop" % self._name.pop(), info) self._trace_stack.pop() diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/osprofiler-1.11.0/osprofiler/sqlalchemy.py new/osprofiler-1.14.0/osprofiler/sqlalchemy.py --- old/osprofiler-1.11.0/osprofiler/sqlalchemy.py 2017-07-18 15:08:28.000000000 +0200 +++ new/osprofiler-1.14.0/osprofiler/sqlalchemy.py 2017-11-13 16:29:18.000000000 +0100 @@ -14,9 +14,13 @@ # under the License. import contextlib +import logging as log + +from oslo_utils import reflection from osprofiler import profiler +LOG = log.getLogger(__name__) _DISABLED = False @@ -34,14 +38,17 @@ _DISABLED = False -def add_tracing(sqlalchemy, engine, name): +def add_tracing(sqlalchemy, engine, name, hide_result=True): """Add tracing to all sqlalchemy calls.""" if not _DISABLED: sqlalchemy.event.listen(engine, "before_cursor_execute", _before_cursor_execute(name)) - sqlalchemy.event.listen(engine, "after_cursor_execute", - _after_cursor_execute()) + sqlalchemy.event.listen( + engine, "after_cursor_execute", + _after_cursor_execute(hide_result=hide_result) + ) + sqlalchemy.event.listen(engine, "handle_error", handle_error) @contextlib.contextmanager @@ -66,10 +73,43 @@ return handler -def _after_cursor_execute(): - """Add listener that will send trace info after query is executed.""" +def _after_cursor_execute(hide_result=True): + """Add listener that will send trace info after query is executed. + + :param hide_result: Boolean value to hide or show SQL result in trace. + True - hide SQL result (default). + False - show SQL result in trace. + """ def handler(conn, cursor, statement, params, context, executemany): - profiler.stop() + if not hide_result: + # Add SQL result to trace info in *-stop phase + info = { + "db": { + "result": str(cursor._rows) + } + } + profiler.stop(info=info) + else: + profiler.stop() return handler + + +def handle_error(exception_context): + """Handle SQLAlchemy errors""" + exception_class_name = reflection.get_class_name( + exception_context.original_exception) + original_exception = str(exception_context.original_exception) + chained_exception = str(exception_context.chained_exception) + + info = { + "etype": exception_class_name, + "db": { + "original_exception": original_exception, + "chained_exception": chained_exception + } + } + profiler.stop(info=info) + LOG.debug("OSProfiler has handled SQLAlchemy error: %s", + original_exception) diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/osprofiler-1.11.0/osprofiler/tests/functional/test_driver.py new/osprofiler-1.14.0/osprofiler/tests/functional/test_driver.py --- old/osprofiler-1.11.0/osprofiler/tests/functional/test_driver.py 2017-07-18 15:08:28.000000000 +0200 +++ new/osprofiler-1.14.0/osprofiler/tests/functional/test_driver.py 2017-11-13 16:29:18.000000000 +0100 @@ -61,10 +61,7 @@ "project": self.PROJECT} self._assert_dict(child["info"], **exp_info) - exp_raw_info = {"project": self.PROJECT, - "service": self.SERVICE} raw_start = child["info"]["meta.raw_payload.%s-start" % name] - self._assert_dict(raw_start["info"], **exp_raw_info) self.assertEqual(fn_name, raw_start["info"]["function"]["name"]) exp_raw = {"name": "%s-start" % name, "service": self.SERVICE, @@ -74,14 +71,13 @@ self._assert_dict(raw_start, **exp_raw) raw_stop = child["info"]["meta.raw_payload.%s-stop" % name] - self._assert_dict(raw_stop["info"], **exp_raw_info) exp_raw["name"] = "%s-stop" % name self._assert_dict(raw_stop, **exp_raw) def test_get_report(self): initializer.init_from_conf( CONF, None, self.PROJECT, self.SERVICE, "host") - profiler.init("SECRET_KEY", project=self.PROJECT, service=self.SERVICE) + profiler.init("SECRET_KEY") foo = DriverTestCase.Foo() foo.bar(1) diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/osprofiler-1.11.0/osprofiler/tests/unit/cmd/test_shell.py new/osprofiler-1.14.0/osprofiler/tests/unit/cmd/test_shell.py --- old/osprofiler-1.11.0/osprofiler/tests/unit/cmd/test_shell.py 2017-07-18 15:08:28.000000000 +0200 +++ new/osprofiler-1.14.0/osprofiler/tests/unit/cmd/test_shell.py 2017-11-13 16:29:18.000000000 +0100 @@ -92,39 +92,6 @@ "Expected: `osprofiler.exc.CommandError` is raised with " "message: '%s'." % expected_message) - def test_username_is_not_presented(self): - os.environ.pop("OS_USERNAME") - msg = ("You must provide a username via either --os-username or " - "via env[OS_USERNAME]") - self._test_with_command_error(self._trace_show_cmd(), msg) - - def test_password_is_not_presented(self): - os.environ.pop("OS_PASSWORD") - msg = ("You must provide a password via either --os-password or " - "via env[OS_PASSWORD]") - self._test_with_command_error(self._trace_show_cmd(), msg) - - def test_auth_url(self): - os.environ.pop("OS_AUTH_URL") - msg = ("You must provide an auth url via either --os-auth-url or " - "via env[OS_AUTH_URL]") - self._test_with_command_error(self._trace_show_cmd(), msg) - - def test_no_project_and_domain_set(self): - os.environ.pop("OS_PROJECT_ID") - os.environ.pop("OS_PROJECT_NAME") - os.environ.pop("OS_TENANT_ID") - os.environ.pop("OS_TENANT_NAME") - os.environ.pop("OS_USER_DOMAIN_ID") - os.environ.pop("OS_USER_DOMAIN_NAME") - - msg = ("You must provide a project_id via either --os-project-id or " - "via env[OS_PROJECT_ID] and a domain_name via either " - "--os-user-domain-name or via env[OS_USER_DOMAIN_NAME] or a " - "domain_id via either --os-user-domain-id or via " - "env[OS_USER_DOMAIN_ID]") - self._test_with_command_error(self._trace_show_cmd(), msg) - def test_trace_show_ceilometerclient_is_missed(self): sys.modules["ceilometerclient"] = None sys.modules["ceilometerclient.client"] = None diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/osprofiler-1.11.0/osprofiler/tests/unit/drivers/test_base.py new/osprofiler-1.14.0/osprofiler/tests/unit/drivers/test_base.py --- old/osprofiler-1.11.0/osprofiler/tests/unit/drivers/test_base.py 2017-07-18 15:08:28.000000000 +0200 +++ new/osprofiler-1.14.0/osprofiler/tests/unit/drivers/test_base.py 2017-11-13 16:29:18.000000000 +0100 @@ -13,8 +13,6 @@ # License for the specific language governing permissions and limitations # under the License. -import mock - from osprofiler.drivers import base from osprofiler.tests import test @@ -55,10 +53,6 @@ "Driver not found for connection string: " "nonexisting://") - def test_plugins_are_imported(self): - base.get_driver("messaging://", mock.MagicMock(), "context", - "transport", "host") - def test_build_empty_tree(self): class C(base.Driver): @classmethod diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/osprofiler-1.11.0/osprofiler/tests/unit/drivers/test_messaging.py new/osprofiler-1.14.0/osprofiler/tests/unit/drivers/test_messaging.py --- old/osprofiler-1.11.0/osprofiler/tests/unit/drivers/test_messaging.py 2017-07-18 15:08:28.000000000 +0200 +++ new/osprofiler-1.14.0/osprofiler/tests/unit/drivers/test_messaging.py 2017-11-13 16:29:18.000000000 +0100 @@ -21,20 +21,37 @@ class MessagingTestCase(test.TestCase): - def test_init_and_notify(self): + @mock.patch("oslo_utils.importutils.try_import") + def test_init_no_oslo_messaging(self, try_import_mock): + try_import_mock.return_value = None + + self.assertRaises( + ValueError, base.get_driver, + "messaging://", project="project", service="service", + host="host", context={}) - messaging = mock.MagicMock() + @mock.patch("oslo_utils.importutils.try_import") + def test_init_and_notify(self, try_import_mock): context = "context" transport = "transport" project = "project" service = "service" host = "host" + # emulate dynamic load of oslo.messaging library + oslo_messaging_mock = mock.Mock() + try_import_mock.return_value = oslo_messaging_mock + + # mock oslo.messaging APIs + notifier_mock = mock.Mock() + oslo_messaging_mock.Notifier.return_value = notifier_mock + oslo_messaging_mock.get_notification_transport.return_value = transport + notify_func = base.get_driver( - "messaging://", messaging, context, transport, - project, service, host).notify + "messaging://", project=project, service=service, + context=context, host=host).notify - messaging.Notifier.assert_called_once_with( + oslo_messaging_mock.Notifier.assert_called_once_with( transport, publisher_id=host, driver="messaging", topics=["profiler"], retry=0) @@ -46,10 +63,10 @@ } notify_func(info) - messaging.Notifier().info.assert_called_once_with( + notifier_mock.info.assert_called_once_with( context, "profiler.service", info) - messaging.reset_mock() + notifier_mock.reset_mock() notify_func(info, context="my_context") - messaging.Notifier().info.assert_called_once_with( + notifier_mock.info.assert_called_once_with( "my_context", "profiler.service", info) diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/osprofiler-1.11.0/osprofiler/tests/unit/test_initializer.py new/osprofiler-1.14.0/osprofiler/tests/unit/test_initializer.py --- old/osprofiler-1.11.0/osprofiler/tests/unit/test_initializer.py 1970-01-01 01:00:00.000000000 +0100 +++ new/osprofiler-1.14.0/osprofiler/tests/unit/test_initializer.py 2017-11-13 16:29:18.000000000 +0100 @@ -0,0 +1,43 @@ +# 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 testtools + +from osprofiler import initializer + + +class InitializerTestCase(testtools.TestCase): + + @mock.patch("osprofiler.notifier.set") + @mock.patch("osprofiler.notifier.create") + @mock.patch("osprofiler.web.enable") + def test_initializer(self, web_enable_mock, notifier_create_mock, + notifier_set_mock): + conf = mock.Mock() + conf.profiler.connection_string = "driver://" + conf.profiler.hmac_keys = "hmac_keys" + context = {} + project = "my-project" + service = "my-service" + host = "my-host" + + notifier_mock = mock.Mock() + notifier_create_mock.return_value = notifier_mock + + initializer.init_from_conf(conf, context, project, service, host) + + notifier_create_mock.assert_called_once_with( + "driver://", context=context, project=project, service=service, + host=host, conf=conf) + notifier_set_mock.assert_called_once_with(notifier_mock) + web_enable_mock.assert_called_once_with("hmac_keys") diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/osprofiler-1.11.0/osprofiler/tests/unit/test_notifier.py new/osprofiler-1.14.0/osprofiler/tests/unit/test_notifier.py --- old/osprofiler-1.11.0/osprofiler/tests/unit/test_notifier.py 2017-07-18 15:08:28.000000000 +0200 +++ new/osprofiler-1.14.0/osprofiler/tests/unit/test_notifier.py 2017-11-13 16:29:18.000000000 +0100 @@ -22,7 +22,7 @@ class NotifierTestCase(test.TestCase): def tearDown(self): - notifier.__notifier = notifier._noop_notifier + notifier.set(notifier._noop_notifier) # restore defaults super(NotifierTestCase, self).tearDown() def test_set(self): diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/osprofiler-1.11.0/osprofiler/tests/unit/test_profiler.py new/osprofiler-1.14.0/osprofiler/tests/unit/test_profiler.py --- old/osprofiler-1.11.0/osprofiler/tests/unit/test_profiler.py 2017-07-18 15:08:28.000000000 +0200 +++ new/osprofiler-1.14.0/osprofiler/tests/unit/test_profiler.py 2017-11-13 16:29:18.000000000 +0100 @@ -171,7 +171,7 @@ @profiler.trace("function", info={"info": "some_info"}) -def tracede_func(i): +def traced_func(i): return i @@ -180,6 +180,16 @@ return (a, i) +@profiler.trace("foo", hide_args=True) +def test_fn_exc(): + raise ValueError() + + +@profiler.trace("hide_result", hide_result=False) +def trace_with_result_func(a, i=10): + return (a, i) + + class TraceDecoratorTestCase(test.TestCase): @mock.patch("osprofiler.profiler.stop") @@ -198,11 +208,11 @@ @mock.patch("osprofiler.profiler.stop") @mock.patch("osprofiler.profiler.start") def test_with_args(self, mock_start, mock_stop): - self.assertEqual(1, tracede_func(1)) + self.assertEqual(1, traced_func(1)) expected_info = { "info": "some_info", "function": { - "name": "osprofiler.tests.unit.test_profiler.tracede_func", + "name": "osprofiler.tests.unit.test_profiler.traced_func", "args": str((1,)), "kwargs": str({}) } @@ -223,6 +233,41 @@ mock_start.assert_called_once_with("hide_args", info=expected_info) mock_stop.assert_called_once_with() + @mock.patch("osprofiler.profiler.stop") + @mock.patch("osprofiler.profiler.start") + def test_with_exception(self, mock_start, mock_stop): + + self.assertRaises(ValueError, test_fn_exc) + expected_info = { + "function": { + "name": "osprofiler.tests.unit.test_profiler.test_fn_exc" + } + } + expected_stop_info = {"etype": "ValueError"} + mock_start.assert_called_once_with("foo", info=expected_info) + mock_stop.assert_called_once_with(info=expected_stop_info) + + @mock.patch("osprofiler.profiler.stop") + @mock.patch("osprofiler.profiler.start") + def test_with_result(self, mock_start, mock_stop): + self.assertEqual((1, 2), trace_with_result_func(1, i=2)) + start_info = { + "function": { + "name": "osprofiler.tests.unit.test_profiler" + ".trace_with_result_func", + "args": str((1,)), + "kwargs": str({"i": 2}) + } + } + + stop_info = { + "function": { + "result": str((1, 2)) + } + } + mock_start.assert_called_once_with("hide_result", info=start_info) + mock_stop.assert_called_once_with(info=stop_info) + class FakeTracedCls(object): diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/osprofiler-1.11.0/osprofiler/tests/unit/test_sqlalchemy.py new/osprofiler-1.14.0/osprofiler/tests/unit/test_sqlalchemy.py --- old/osprofiler-1.11.0/osprofiler/tests/unit/test_sqlalchemy.py 2017-07-18 15:08:28.000000000 +0200 +++ new/osprofiler-1.14.0/osprofiler/tests/unit/test_sqlalchemy.py 2017-11-13 16:29:18.000000000 +0100 @@ -37,9 +37,43 @@ handler(mock.MagicMock(), 1, 2, 3, 4, 5) mock_profiler.stop.assert_called_once_with() + @mock.patch("osprofiler.sqlalchemy.profiler") + def test_after_execute_with_sql_result(self, mock_profiler): + handler = sqlalchemy._after_cursor_execute(hide_result=False) + cursor = mock.MagicMock() + cursor._rows = (1,) + handler(1, cursor, 2, 3, 4, 5) + info = { + "db": { + "result": str(cursor._rows) + } + } + mock_profiler.stop.assert_called_once_with(info=info) + + @mock.patch("osprofiler.sqlalchemy.profiler") + def test_handle_error(self, mock_profiler): + original_exception = Exception("error") + chained_exception = Exception("error and the reason") + + sqlalchemy_exception_ctx = mock.MagicMock() + sqlalchemy_exception_ctx.original_exception = original_exception + sqlalchemy_exception_ctx.chained_exception = chained_exception + + sqlalchemy.handle_error(sqlalchemy_exception_ctx) + expected_info = { + "etype": "Exception", + "db": { + "original_exception": str(original_exception), + "chained_exception": str(chained_exception), + } + } + mock_profiler.stop.assert_called_once_with(info=expected_info) + + @mock.patch("osprofiler.sqlalchemy.handle_error") @mock.patch("osprofiler.sqlalchemy._before_cursor_execute") @mock.patch("osprofiler.sqlalchemy._after_cursor_execute") - def test_add_tracing(self, mock_after_exc, mock_before_exc): + def test_add_tracing(self, mock_after_exc, mock_before_exc, + mock_handle_error): sa = mock.MagicMock() engine = mock.MagicMock() @@ -49,16 +83,20 @@ sqlalchemy.add_tracing(sa, engine, "sql") mock_before_exc.assert_called_once_with("sql") - mock_after_exc.assert_called_once_with() + # Default set hide_result=True + mock_after_exc.assert_called_once_with(hide_result=True) expected_calls = [ mock.call(engine, "before_cursor_execute", "before"), - mock.call(engine, "after_cursor_execute", "after") + mock.call(engine, "after_cursor_execute", "after"), + mock.call(engine, "handle_error", mock_handle_error), ] self.assertEqual(sa.event.listen.call_args_list, expected_calls) + @mock.patch("osprofiler.sqlalchemy.handle_error") @mock.patch("osprofiler.sqlalchemy._before_cursor_execute") @mock.patch("osprofiler.sqlalchemy._after_cursor_execute") - def test_wrap_session(self, mock_after_exc, mock_before_exc): + def test_wrap_session(self, mock_after_exc, mock_before_exc, + mock_handle_error): sa = mock.MagicMock() @contextlib.contextmanager @@ -78,14 +116,40 @@ pass mock_before_exc.assert_called_once_with("db") - mock_after_exc.assert_called_once_with() + # Default set hide_result=True + mock_after_exc.assert_called_once_with(hide_result=True) expected_calls = [ mock.call(sess.bind, "before_cursor_execute", "before"), - mock.call(sess.bind, "after_cursor_execute", "after") + mock.call(sess.bind, "after_cursor_execute", "after"), + mock.call(sess.bind, "handle_error", mock_handle_error), ] self.assertEqual(sa.event.listen.call_args_list, expected_calls) + @mock.patch("osprofiler.sqlalchemy.handle_error") + @mock.patch("osprofiler.sqlalchemy._before_cursor_execute") + @mock.patch("osprofiler.sqlalchemy._after_cursor_execute") + @mock.patch("osprofiler.profiler") + def test_with_sql_result(self, mock_profiler, mock_after_exc, + mock_before_exc, mock_handle_error): + sa = mock.MagicMock() + engine = mock.MagicMock() + + mock_before_exc.return_value = "before" + mock_after_exc.return_value = "after" + + sqlalchemy.add_tracing(sa, engine, "sql", hide_result=False) + + mock_before_exc.assert_called_once_with("sql") + # Default set hide_result=True + mock_after_exc.assert_called_once_with(hide_result=False) + expected_calls = [ + mock.call(engine, "before_cursor_execute", "before"), + mock.call(engine, "after_cursor_execute", "after"), + mock.call(engine, "handle_error", mock_handle_error), + ] + self.assertEqual(sa.event.listen.call_args_list, expected_calls) + @mock.patch("osprofiler.sqlalchemy._before_cursor_execute") @mock.patch("osprofiler.sqlalchemy._after_cursor_execute") def test_disable_and_enable(self, mock_after_exc, mock_before_exc): diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/osprofiler-1.11.0/osprofiler.egg-info/PKG-INFO new/osprofiler-1.14.0/osprofiler.egg-info/PKG-INFO --- old/osprofiler-1.11.0/osprofiler.egg-info/PKG-INFO 2017-07-18 15:11:04.000000000 +0200 +++ new/osprofiler-1.14.0/osprofiler.egg-info/PKG-INFO 2017-11-13 16:32:10.000000000 +0100 @@ -1,11 +1,12 @@ Metadata-Version: 1.1 Name: osprofiler -Version: 1.11.0 +Version: 1.14.0 Summary: OpenStack Profiler Library Home-page: https://docs.openstack.org/osprofiler/latest/ Author: OpenStack Author-email: openstack-...@lists.openstack.org License: UNKNOWN +Description-Content-Type: UNKNOWN Description: ======================== Team and repository tags ======================== diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/osprofiler-1.11.0/osprofiler.egg-info/SOURCES.txt new/osprofiler-1.14.0/osprofiler.egg-info/SOURCES.txt --- old/osprofiler-1.11.0/osprofiler.egg-info/SOURCES.txt 2017-07-18 15:11:04.000000000 +0200 +++ new/osprofiler-1.14.0/osprofiler.egg-info/SOURCES.txt 2017-11-13 16:32:11.000000000 +0100 @@ -69,6 +69,7 @@ osprofiler/tests/functional/config.cfg osprofiler/tests/functional/test_driver.py osprofiler/tests/unit/__init__.py +osprofiler/tests/unit/test_initializer.py osprofiler/tests/unit/test_notifier.py osprofiler/tests/unit/test_opts.py osprofiler/tests/unit/test_profiler.py @@ -91,6 +92,7 @@ releasenotes/source/conf.py releasenotes/source/index.rst releasenotes/source/ocata.rst +releasenotes/source/pike.rst releasenotes/source/unreleased.rst releasenotes/source/_static/.placeholder releasenotes/source/_templates/.placeholder diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/osprofiler-1.11.0/osprofiler.egg-info/pbr.json new/osprofiler-1.14.0/osprofiler.egg-info/pbr.json --- old/osprofiler-1.11.0/osprofiler.egg-info/pbr.json 2017-07-18 15:11:04.000000000 +0200 +++ new/osprofiler-1.14.0/osprofiler.egg-info/pbr.json 2017-11-13 16:32:10.000000000 +0100 @@ -1 +1 @@ -{"git_version": "ba4732d", "is_release": true} \ No newline at end of file +{"git_version": "ffd8d7d", "is_release": true} \ No newline at end of file diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/osprofiler-1.11.0/osprofiler.egg-info/requires.txt new/osprofiler-1.14.0/osprofiler.egg-info/requires.txt --- old/osprofiler-1.11.0/osprofiler.egg-info/requires.txt 2017-07-18 15:11:04.000000000 +0200 +++ new/osprofiler-1.14.0/osprofiler.egg-info/requires.txt 2017-11-13 16:32:10.000000000 +0100 @@ -1,6 +1,4 @@ six>=1.9.0 -oslo.messaging>=5.2.0 -oslo.log>=3.11.0 oslo.utils>=3.16.0 WebOb>=1.6.0 requests>=2.10.0 diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/osprofiler-1.11.0/releasenotes/source/index.rst new/osprofiler-1.14.0/releasenotes/source/index.rst --- old/osprofiler-1.11.0/releasenotes/source/index.rst 2017-07-18 15:08:28.000000000 +0200 +++ new/osprofiler-1.14.0/releasenotes/source/index.rst 2017-11-13 16:29:18.000000000 +0100 @@ -6,4 +6,5 @@ :maxdepth: 1 unreleased + pike ocata diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/osprofiler-1.11.0/releasenotes/source/pike.rst new/osprofiler-1.14.0/releasenotes/source/pike.rst --- old/osprofiler-1.11.0/releasenotes/source/pike.rst 1970-01-01 01:00:00.000000000 +0100 +++ new/osprofiler-1.14.0/releasenotes/source/pike.rst 2017-11-13 16:29:18.000000000 +0100 @@ -0,0 +1,6 @@ +=================================== + Pike Series Release Notes +=================================== + +.. release-notes:: + :branch: stable/pike diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/osprofiler-1.11.0/requirements.txt new/osprofiler-1.14.0/requirements.txt --- old/osprofiler-1.11.0/requirements.txt 2017-07-18 15:08:28.000000000 +0200 +++ new/osprofiler-1.14.0/requirements.txt 2017-11-13 16:29:18.000000000 +0100 @@ -1,6 +1,4 @@ six>=1.9.0 # MIT -oslo.messaging>=5.2.0 # Apache-2.0 -oslo.log>=3.11.0 # Apache-2.0 oslo.utils>=3.16.0 # Apache-2.0 WebOb>=1.6.0 # MIT requests>=2.10.0 # Apache-2.0