From 4ceeef5376ac2572f706d950925711200bf35425 Mon Sep 17 00:00:00 2001 From: Timur Sufiev Date: Tue, 30 Aug 2016 21:30:57 +0300 Subject: [PATCH] Add the Profiler panel to the Developer dashboard Provide both pythonic Django part and the static assets (angular directives and styles) for the new panel. DEPLOY NOTES: To enable panel itself, copy openstack_dashboard/local/local_settings.d/_9030_profiler_settings.py.example file from the previous commit to openstack_dashboard/local/local_settings.d/_9030_profiler_settings.py and copy openstack_dashboard/contrib/developer/enabled/_9030_profiler.py to openstack_dashboard/local/enabled/_9030_profiler.py To support storing profiler data on server-side, MongoDB cluster needs to be installed on Devstack host (default configuration), see https://docs.mongodb.com/manual/tutorial/install-mongodb-on-ubuntu/#install-mongodb-community-edition for instructions. Then, change net:bindIp: key to 0.0.0.0 inside /etc/mongod.conf and invoke `sudo service mongod restart` for the changes to have an effect. Implements-blueprint: openstack-profiler-at-developer-dashboard Change-Id: Ice7b8b4b4decad2c45a9edef3f3c4cc2ff759de4 --- doc/source/contributing.rst | 29 ++++ openstack_dashboard/context_processors.py | 4 + .../developer/enabled/_9030_profiler.py | 21 +++ .../contrib/developer/profiler/middleware.py | 20 ++- .../contrib/developer/profiler/panel.py | 25 +++ .../templates/profiler/_mode_picker.html | 21 +++ .../profiler/templates/profiler/index.html | 49 ++++++ .../contrib/developer/profiler/urls.py | 24 +++ .../contrib/developer/profiler/views.py | 49 ++++++ .../dashboard/developer/developer.module.js | 3 +- .../static/dashboard/developer/developer.scss | 1 + .../developer/profiler/profiler.controller.js | 161 ++++++++++++++++++ .../developer/profiler/profiler.details.html | 17 ++ .../developer/profiler/profiler.directive.js | 87 ++++++++++ .../developer/profiler/profiler.module.js | 40 +++++ .../developer/profiler/profiler.scss | 5 + .../profiler/profiler.trace-table.html | 20 +++ .../profiler/profiler.tree-node.html | 28 +++ .../templates/header/_header.html | 3 + openstack_dashboard/test/settings.py | 2 + .../material/templates/header/_header.html | 3 + ...-developer-dashboard-da1b1556e30aa858.yaml | 22 +++ 22 files changed, 625 insertions(+), 9 deletions(-) create mode 100644 openstack_dashboard/contrib/developer/enabled/_9030_profiler.py create mode 100644 openstack_dashboard/contrib/developer/profiler/panel.py create mode 100644 openstack_dashboard/contrib/developer/profiler/templates/profiler/_mode_picker.html create mode 100644 openstack_dashboard/contrib/developer/profiler/templates/profiler/index.html create mode 100644 openstack_dashboard/contrib/developer/profiler/urls.py create mode 100644 openstack_dashboard/contrib/developer/profiler/views.py create mode 100644 openstack_dashboard/contrib/developer/static/dashboard/developer/profiler/profiler.controller.js create mode 100644 openstack_dashboard/contrib/developer/static/dashboard/developer/profiler/profiler.details.html create mode 100644 openstack_dashboard/contrib/developer/static/dashboard/developer/profiler/profiler.directive.js create mode 100644 openstack_dashboard/contrib/developer/static/dashboard/developer/profiler/profiler.module.js create mode 100644 openstack_dashboard/contrib/developer/static/dashboard/developer/profiler/profiler.scss create mode 100644 openstack_dashboard/contrib/developer/static/dashboard/developer/profiler/profiler.trace-table.html create mode 100644 openstack_dashboard/contrib/developer/static/dashboard/developer/profiler/profiler.tree-node.html create mode 100644 releasenotes/notes/openstack-profiler-at-developer-dashboard-da1b1556e30aa858.yaml diff --git a/doc/source/contributing.rst b/doc/source/contributing.rst index 05c6255053..60f30855dd 100644 --- a/doc/source/contributing.rst +++ b/doc/source/contributing.rst @@ -90,6 +90,7 @@ After You Write Your Patch Once you've made your changes, there are a few things to do: * Make sure the unit tests and linting tasks pass by running ``tox`` +* Take a look at your patch in API profiler, i.e. how it impacts the performance. See `Profiling Pages`_. * Make sure your code is ready for translation: See :ref:`pseudo_translation`. * Make sure your code is up-to-date with the latest master: ``git pull --rebase`` * Finally, run ``git review`` to upload your changes to Gerrit for review. @@ -100,6 +101,34 @@ If the review is approved, it is sent to Jenkins to verify the unit tests pass and it can be merged cleanly. Once Jenkins approves it, the change will be merged to the master repository and it's time to celebrate! +Profiling Pages +--------------- + +In Ocata release of Horizon a new "OpenStack Profiler" panel is introduced within +a Developer dashboard. Once it is enabled and all prerequisites are set up, you +can see what API calls Horizon actually makes when rendering a specific page. To +re-render the page while profiling it, you'll need to use "Profile" drop-down +menu located left to the User menu in top right corner of the screen. In order to +be able to use "Profile" menu the following steps need to be done: + +#. Ensure that the Developer dashboard is enabled (copy _9001_developer.py file +from the openstack_dashboard/contrib/developer/enabled folder into the +openstack_dashboard/local/enabled folder if it is not already there). +#. Copy openstack_dashboard/local/local_settings.d/_9030_profiler_settings.py.example +file to openstack_dashboard/local/local_settings.d/_9030_profiler_settings.py +#. Copy openstack_dashboard/contrib/developer/enabled/_9030_profiler.py to +openstack_dashboard/local/enabled/_9030_profiler.py . +#. To support storing profiler data on server-side, MongoDB cluster needs +to be installed on Devstack host (default configuration), see `Installing MongoDB`_. +Then, change net:bindIp: key to 0.0.0.0 inside /etc/mongod.conf and invoke +``sudo service mongod restart`` for the changes to have an effect. +#. Re-collect and re-compress static assets. +#. Re-start the production web-server in case you are serving Horizon from it. +#. The "Profile" drop-down menu should appear in the top-right corner, you are +ready to profile your pages! + +.. _installing MongoDB: https://docs.mongodb.com/manual/tutorial/install-mongodb-on-ubuntu/#install-mongodb-community-edition + Etiquette ========= diff --git a/openstack_dashboard/context_processors.py b/openstack_dashboard/context_processors.py index 804595c801..b5d4a7a766 100644 --- a/openstack_dashboard/context_processors.py +++ b/openstack_dashboard/context_processors.py @@ -60,6 +60,10 @@ def openstack(request): # Adding webroot access context['WEBROOT'] = getattr(settings, "WEBROOT", "/") + # Adding profiler support flag + enabled = getattr(settings, 'OPENSTACK_PROFILER', {}).get('enabled', False) + context['profiler_enabled'] = enabled + # Search for external plugins and append to javascript message catalog # internal plugins are under the openstack_dashboard domain # so we exclude them from the js_catalog diff --git a/openstack_dashboard/contrib/developer/enabled/_9030_profiler.py b/openstack_dashboard/contrib/developer/enabled/_9030_profiler.py new file mode 100644 index 0000000000..a8750cf0e1 --- /dev/null +++ b/openstack_dashboard/contrib/developer/enabled/_9030_profiler.py @@ -0,0 +1,21 @@ +# Copyright 2016 Mirantis Inc. +# All Rights Reserved. +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + + +PANEL = 'profiler' +PANEL_GROUP = 'default' +PANEL_DASHBOARD = 'developer' + +ADD_PANEL = 'openstack_dashboard.contrib.developer.profiler.panel.Profiler' diff --git a/openstack_dashboard/contrib/developer/profiler/middleware.py b/openstack_dashboard/contrib/developer/profiler/middleware.py index ba4dcf73e0..cb7c2e1f16 100644 --- a/openstack_dashboard/contrib/developer/profiler/middleware.py +++ b/openstack_dashboard/contrib/developer/profiler/middleware.py @@ -29,13 +29,19 @@ from openstack_dashboard.contrib.developer.profiler import api _REQUIRED_KEYS = ("base_id", "hmac_key") _OPTIONAL_KEYS = ("parent_id",) -PROFILER_SETTINGS = getattr(settings, 'OPENSTACK_PROFILER', {}) +PROFILER_CONF = getattr(settings, 'OPENSTACK_PROFILER', {}) +PROFILER_ENABLED = PROFILER_CONF.get('enabled', False) class ProfilerClientMiddleware(object): + def __init__(self): + if not PROFILER_ENABLED: + raise exceptions.MiddlewareNotUsed() + super(ProfilerClientMiddleware, self).__init__() + def process_request(self, request): if 'profile_page' in request.COOKIES: - hmac_key = PROFILER_SETTINGS.get('keys')[0] + hmac_key = PROFILER_CONF.get('keys')[0] profiler.init(hmac_key) for hdr_key, hdr_value in web.get_trace_id_headers().items(): request.META[hdr_key] = hdr_value @@ -44,12 +50,10 @@ class ProfilerClientMiddleware(object): class ProfilerMiddleware(object): def __init__(self): - self.name = PROFILER_SETTINGS.get('facility_name', 'horizon') - self.hmac_keys = PROFILER_SETTINGS.get('keys') - self._enabled = PROFILER_SETTINGS.get('enabled', False) - if self._enabled: - api.init_notifier(PROFILER_SETTINGS.get( - 'notifier_connection_string', 'mongodb://')) + self.name = PROFILER_CONF.get('facility_name', 'horizon') + self.hmac_keys = PROFILER_CONF.get('keys', []) + if PROFILER_ENABLED: + api.init_notifier(PROFILER_CONF.get('notifier_connection_string')) else: raise exceptions.MiddlewareNotUsed() diff --git a/openstack_dashboard/contrib/developer/profiler/panel.py b/openstack_dashboard/contrib/developer/profiler/panel.py new file mode 100644 index 0000000000..d09eb7b943 --- /dev/null +++ b/openstack_dashboard/contrib/developer/profiler/panel.py @@ -0,0 +1,25 @@ +# Copyright 2016 Mirantis Inc. +# All Rights Reserved. +# +# Licensed under the Apache License, Version 2.0 (the "License"); you may +# not use this file except in compliance with the License. You may obtain +# a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT +# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the +# License for the specific language governing permissions and limitations +# under the License. + +from django.conf import settings +from django.utils.translation import ugettext_lazy as _ + +import horizon + + +if getattr(settings, 'OPENSTACK_PROFILER', {}).get('enabled', False): + class Profiler(horizon.Panel): + name = _("OpenStack Profiler") + slug = 'profiler' diff --git a/openstack_dashboard/contrib/developer/profiler/templates/profiler/_mode_picker.html b/openstack_dashboard/contrib/developer/profiler/templates/profiler/_mode_picker.html new file mode 100644 index 0000000000..5daf5d35eb --- /dev/null +++ b/openstack_dashboard/contrib/developer/profiler/templates/profiler/_mode_picker.html @@ -0,0 +1,21 @@ +{% if not_list %} + +{% else %} + +{% endif %} \ No newline at end of file diff --git a/openstack_dashboard/contrib/developer/profiler/templates/profiler/index.html b/openstack_dashboard/contrib/developer/profiler/templates/profiler/index.html new file mode 100644 index 0000000000..dd87bd5714 --- /dev/null +++ b/openstack_dashboard/contrib/developer/profiler/templates/profiler/index.html @@ -0,0 +1,49 @@ +{% extends 'base.html' %} +{% load i18n %} + +{% block title %} + {% trans "OpenStack Profiler" %} +{% endblock %} + +{% block page_header %} +

OpenStack Profiler

+{% endblock %} + +{% block main %} +
+

Traces

+ + + + + + + + + + + + + + + + + + + + + + + +
Traced AtTrace IDOriginRequest time
+ + + {$ trace.timestamp $}{$ trace.id $}{$ trace.origin $}{$ trace.request_time $}
+
+
+
+{% endblock %} diff --git a/openstack_dashboard/contrib/developer/profiler/urls.py b/openstack_dashboard/contrib/developer/profiler/urls.py new file mode 100644 index 0000000000..78549a3554 --- /dev/null +++ b/openstack_dashboard/contrib/developer/profiler/urls.py @@ -0,0 +1,24 @@ +# Copyright 2016 Mirantis Inc. +# All Rights Reserved. +# +# Licensed under the Apache License, Version 2.0 (the "License"); you may +# not use this file except in compliance with the License. You may obtain +# a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT +# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the +# License for the specific language governing permissions and limitations +# under the License. + + +from django.conf.urls import url + +from openstack_dashboard.contrib.developer.profiler import views + + +urlpatterns = [ + url(r'^$', views.IndexView.as_view(), name='index'), +] diff --git a/openstack_dashboard/contrib/developer/profiler/views.py b/openstack_dashboard/contrib/developer/profiler/views.py new file mode 100644 index 0000000000..5c59074a6d --- /dev/null +++ b/openstack_dashboard/contrib/developer/profiler/views.py @@ -0,0 +1,49 @@ +# Copyright 2016 Mirantis Inc. +# All Rights Reserved. +# +# Licensed under the Apache License, Version 2.0 (the "License"); you may +# not use this file except in compliance with the License. You may obtain +# a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT +# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the +# License for the specific language governing permissions and limitations +# under the License. + +from django.utils.translation import ugettext_lazy as _ +from django.views import generic + +from horizon import views +from openstack_dashboard.api.rest import urls +from openstack_dashboard.api.rest import utils +from openstack_dashboard.contrib.developer.profiler import api + + +class IndexView(views.HorizonTemplateView): + template_name = 'developer/profiler/index.html' + page_title = _("OpenStack Profiler") + + def get_context_data(self, **kwargs): + context = super(IndexView, self).get_context_data(**kwargs) + return context + + +@urls.register +class Traces(generic.View): + url_regex = r'profiler/traces$' + + @utils.ajax() + def get(self, request): + return api.list_traces(request) + + +@urls.register +class Trace(generic.View): + url_regex = r'profiler/traces/(?P[^/]+)/$' + + @utils.ajax() + def get(self, request, trace_id): + return api.get_trace(request, trace_id) diff --git a/openstack_dashboard/contrib/developer/static/dashboard/developer/developer.module.js b/openstack_dashboard/contrib/developer/static/dashboard/developer/developer.module.js index f94e0c1cb2..c5798c895e 100644 --- a/openstack_dashboard/contrib/developer/static/dashboard/developer/developer.module.js +++ b/openstack_dashboard/contrib/developer/static/dashboard/developer/developer.module.js @@ -26,7 +26,8 @@ angular .module('horizon.dashboard.developer', [ 'horizon.dashboard.developer.theme-preview', - 'horizon.dashboard.developer.resource-browser' + 'horizon.dashboard.developer.resource-browser', + 'horizon.dashboard.developer.profiler' ]) .config(config); diff --git a/openstack_dashboard/contrib/developer/static/dashboard/developer/developer.scss b/openstack_dashboard/contrib/developer/static/dashboard/developer/developer.scss index 4ad9a92e7b..8cf2271445 100644 --- a/openstack_dashboard/contrib/developer/static/dashboard/developer/developer.scss +++ b/openstack_dashboard/contrib/developer/static/dashboard/developer/developer.scss @@ -1,2 +1,3 @@ // Top level file for Developer dashboard SCSS @import "theme-preview/theme-preview"; +@import "profiler/profiler"; diff --git a/openstack_dashboard/contrib/developer/static/dashboard/developer/profiler/profiler.controller.js b/openstack_dashboard/contrib/developer/static/dashboard/developer/profiler/profiler.controller.js new file mode 100644 index 0000000000..096f422bce --- /dev/null +++ b/openstack_dashboard/contrib/developer/static/dashboard/developer/profiler/profiler.controller.js @@ -0,0 +1,161 @@ +/* + * (c) Copyright 2015 Mirantis Inc. + * + * 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. + */ + +(function () { + 'use strict'; + + angular + .module('horizon.dashboard.developer.profiler') + .controller('topProfilerController', topProfilerController) + .controller('sharedProfilerController', sharedProfilerController) + .controller('profilerActionsController', actionsController); + + /** + * @ngdoc controller + * @name horizon.dashboard.developer.topProfilerController + * @description + * This is the top-level controller for the Profiler view. + * Its primary purpose is hand the list of traces over to profiler table widget. + */ + topProfilerController.$inject = ['horizon.framework.util.http.service']; + function topProfilerController($http) { + var ctrl = this; + + $http.get('/api/profiler/traces').then(function(response) { + ctrl.traces = response.data; + ctrl.tracesDisplayed = response.data; + }); + + } + + /** + * @ngdoc controller + * @name horizon.dashboard.developer.sharedProfilerController + * @description + * This is the controller being used inside directive, it is + * shared between all the trace node directives which recursively form the + * whole trace (hence the name). It contains various helper methods which + * are used while rendering trace node. + */ + sharedProfilerController.$inject = [ + '$modal', + '$rootScope', + '$templateCache', + 'horizon.dashboard.developer.profiler.basePath' + ]; + function sharedProfilerController($modal, $rootScope, $templateCache, basePath) { + var ctrl = this; + ctrl.getWidth = getWidth; + ctrl.getStarted = getStarted; + ctrl.isImportant = isImportant; + ctrl.display = display; + ctrl.toggleChildren = toggleChildren; + ctrl.getLeafCls = getLeafCls; + ctrl.getBranchCls = getBranchCls; + + function toggleChildren(data) { + function rec(data, value, nonRoot) { + if (nonRoot) { + data.visible = value; + } + // don't expand nodes collapsed explicitly when expanding one of their + // parents + if (!(value && !data.childrenVisible)) { + data.children.forEach(function(child) { + rec(child, value, true); + }); + } + } + data.childrenVisible = !data.childrenVisible; + rec(data, data.childrenVisible); + } + + function getLeafCls(data) { + return data.is_leaf ? 'fa-cloud' : ''; + } + + function getBranchCls(data) { + if (!data.children.length) { + return ''; + } + return data.children[0].visible ? 'fa-minus' : 'fa-plus'; + } + + function getWidth(data, rootData) { + var full_duration = rootData.info.finished; + var duration = (data.info.finished - data.info.started) * 100.0 / full_duration; + return (duration >= 0.5) ? duration : 0.5; + } + + function getStarted(data, rootData) { + var full_duration = rootData.info.finished; + return data.info.started * 100.0 / full_duration; + } + + function isImportant(data) { + return ["total", "wsgi", "rpc"].indexOf(data.info.name) != -1; + } + + function display(data){ + var scope = $rootScope.$new(); + var info = angular.copy(data.info); + var metadata = {}; + angular.forEach(info, function(value, key) { + var parts = key.split("."); + if (parts[0] == "meta") { + if (parts.length == 2){ + this[parts[1]] = value; + } + else{ + var group_name = parts[1]; + if (!(group_name in this)) + this[group_name] = {}; + this[group_name][parts[2]] = value; + } + } + }, metadata); + info["duration"] = info["finished"] - info["started"]; + info["metadata"] = JSON.stringify(metadata, "", 4); + scope.info = info; + scope.columns = ["name", "project", "service", "host", "started", + "finished", "duration", "metadata"]; + $modal.open({ + "size": "lg", + "template": $templateCache.get(basePath + 'profiler.details.html'), + "scope": scope + }); + } + } + + /** + * @ngdoc controller + * @name horizon.dashboard.developer.profilerActionsController + * @description + * This is the controller being used in header partial template for invoking + * various profiling actions through a drop-down control. + */ + actionsController.$inject = ['$cookies']; + function actionsController($cookies) { + var ctrl = this; + + ctrl.profilePage = profilePage; + + function profilePage() { + $cookies.put('profile_page', true); + window.location.reload(); + } + } +})(); \ No newline at end of file diff --git a/openstack_dashboard/contrib/developer/static/dashboard/developer/profiler/profiler.details.html b/openstack_dashboard/contrib/developer/static/dashboard/developer/profiler/profiler.details.html new file mode 100644 index 0000000000..bb85aa541b --- /dev/null +++ b/openstack_dashboard/contrib/developer/static/dashboard/developer/profiler/profiler.details.html @@ -0,0 +1,17 @@ + + + diff --git a/openstack_dashboard/contrib/developer/static/dashboard/developer/profiler/profiler.directive.js b/openstack_dashboard/contrib/developer/static/dashboard/developer/profiler/profiler.directive.js new file mode 100644 index 0000000000..a09e8bce48 --- /dev/null +++ b/openstack_dashboard/contrib/developer/static/dashboard/developer/profiler/profiler.directive.js @@ -0,0 +1,87 @@ +/* + * (c) Copyright 2015 Mirantis Inc. + * + * 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. + */ + +(function () { + 'use strict'; + + angular + .module('horizon.dashboard.developer.profiler') + .directive('traceTable', traceTable) + .directive('nodeData', nodeData); + + nodeData.$inject = [ + '$compile', + '$templateCache', + 'horizon.dashboard.developer.profiler.basePath']; + + function nodeData($compile, $templateCache, basePath) { + return { + restrict: 'A', + scope: { + hideChildren: '=', + rootData: '=', + data: '=nodeData', + visible: '=' + }, + require: '^traceTable', + link: function(scope, element, attrs, sharedCtrl) { + var destroyWatcher = scope.$watch('rootData', function(newValue) { + if (angular.isDefined(newValue)) { + var template = $templateCache.get(basePath + 'profiler.tree-node.html'); + scope.ctrl = sharedCtrl; + element.replaceWith($compile(template)(scope)); + } + }); + scope.$on('$destroy', function() { + destroyWatcher(); + }); + } + } + } + + traceTable.$inject = [ + 'horizon.framework.util.http.service', + 'horizon.dashboard.developer.basePath']; + + function traceTable($http, basePath) { + return { + restrict: 'A', + templateUrl: basePath + 'profiler/profiler.trace-table.html', + scope: { + trace: '=traceTable' + }, + replace: true, + controller: 'sharedProfilerController', + link: function(scope) { + var destroyWatcher = scope.$watch('trace', function(trace) { + if (trace) { + var traceId = trace.id; + scope.$on('hzTable:rowExpanded', function(e, traceItem) { + if (traceId === traceItem.id && !scope.data) { + $http.get('/api/profiler/traces/' + traceId).then(function(response) { + scope.data = response.data; + }); + } + }); + } + }); + scope.$on('$destroy', function() { + destroyWatcher(); + }); + } + } + } +})(); \ No newline at end of file diff --git a/openstack_dashboard/contrib/developer/static/dashboard/developer/profiler/profiler.module.js b/openstack_dashboard/contrib/developer/static/dashboard/developer/profiler/profiler.module.js new file mode 100644 index 0000000000..c3baad77ef --- /dev/null +++ b/openstack_dashboard/contrib/developer/static/dashboard/developer/profiler/profiler.module.js @@ -0,0 +1,40 @@ +/* + * (c) Copyright 2016 Mirantis Inc. + * + * 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. + */ + +(function () { + 'use strict'; + + /** + * @ngdoc module + * @ngname horizon.dashboard.developer.profiler + * @description + * Dashboard module for the profiler panel. + */ + angular + .module('horizon.dashboard.developer.profiler', ['ui.bootstrap']) + .config(config); + + config.$inject = [ + '$provide', + '$windowProvider' + ]; + + function config($provide, $windowProvider) { + var path = $windowProvider.$get().STATIC_URL + 'dashboard/developer/profiler/'; + $provide.constant('horizon.dashboard.developer.profiler.basePath', path); + } + +})(); diff --git a/openstack_dashboard/contrib/developer/static/dashboard/developer/profiler/profiler.scss b/openstack_dashboard/contrib/developer/static/dashboard/developer/profiler/profiler.scss new file mode 100644 index 0000000000..6e6cd8e615 --- /dev/null +++ b/openstack_dashboard/contrib/developer/static/dashboard/developer/profiler/profiler.scss @@ -0,0 +1,5 @@ +.profiler { + .progress-bar-transparent { + background-color: rgba(255, 255, 255, 0.0); + } +} diff --git a/openstack_dashboard/contrib/developer/static/dashboard/developer/profiler/profiler.trace-table.html b/openstack_dashboard/contrib/developer/static/dashboard/developer/profiler/profiler.trace-table.html new file mode 100644 index 0000000000..712a518063 --- /dev/null +++ b/openstack_dashboard/contrib/developer/static/dashboard/developer/profiler/profiler.trace-table.html @@ -0,0 +1,20 @@ + + + + + + + + + + + + + + + + + +
LevelsDurationTypeProjectServiceHostDetails
+ +
diff --git a/openstack_dashboard/contrib/developer/static/dashboard/developer/profiler/profiler.tree-node.html b/openstack_dashboard/contrib/developer/static/dashboard/developer/profiler/profiler.tree-node.html new file mode 100644 index 0000000000..a5edc20f6f --- /dev/null +++ b/openstack_dashboard/contrib/developer/static/dashboard/developer/profiler/profiler.tree-node.html @@ -0,0 +1,28 @@ + + + + + + +
+ + + + + + {$ data.info.finished - data.info.started $} ms + +
+ + {$ data.info.name $} + {$ data.info.project || "n/a"$} + {$ data.info.service || "n/a" $} + {$ data.info.host || "n/a"$} + Details + + diff --git a/openstack_dashboard/templates/header/_header.html b/openstack_dashboard/templates/header/_header.html index 178ff47d2e..88518eab77 100644 --- a/openstack_dashboard/templates/header/_header.html +++ b/openstack_dashboard/templates/header/_header.html @@ -29,6 +29,9 @@ diff --git a/openstack_dashboard/test/settings.py b/openstack_dashboard/test/settings.py index 5240711735..0e22750eb1 100644 --- a/openstack_dashboard/test/settings.py +++ b/openstack_dashboard/test/settings.py @@ -119,6 +119,8 @@ settings_utils.update_dashboards( INSTALLED_APPS, ) +OPENSTACK_PROFILER = {'enabled': False} + settings_utils.find_static_files(HORIZON_CONFIG, AVAILABLE_THEMES, THEME_COLLECTION_DIR, ROOT_PATH) diff --git a/openstack_dashboard/themes/material/templates/header/_header.html b/openstack_dashboard/themes/material/templates/header/_header.html index c88ced5326..0d97618bef 100644 --- a/openstack_dashboard/themes/material/templates/header/_header.html +++ b/openstack_dashboard/themes/material/templates/header/_header.html @@ -29,6 +29,9 @@ diff --git a/releasenotes/notes/openstack-profiler-at-developer-dashboard-da1b1556e30aa858.yaml b/releasenotes/notes/openstack-profiler-at-developer-dashboard-da1b1556e30aa858.yaml new file mode 100644 index 0000000000..3aa97d65d1 --- /dev/null +++ b/releasenotes/notes/openstack-profiler-at-developer-dashboard-da1b1556e30aa858.yaml @@ -0,0 +1,22 @@ +--- +features: + - A new Profiler panel in the Developer dashboard is + introduced. It integrates + `osprofiler library `_ + into horizon, thus implementing + `blueprint openstack-profiler-at-developer-dashboard `_. + Initially profiler is disabled. To enable it the value + ``OPENSTACK_PROFILER['enabled']`` has to be ``True``. + This in turn can be achieved by copying files + _9030_profiler_settings.py.example and _9030_profiler.py to + openstack_dashboard/local/local_settings.d/_9030_profiler_settings.py + and openstack_dashboard/local/enabled/_9030_profiler.py respectively. + Also, by default it expects MongoDB cluster + to be present on the same host where Keystone is located + (say, in a Devstack VM). But it also can be configured + with params with ``OPENSTACK_PROFILER['notifier_connection_string]'`` + and ``OPENSTACK_PROFILER['receiver_connection_string']`` values. + + MongoDB should be installed + `manually `_ + and allowed to receive requests on 0.0.0.0 interface.