diff --git a/app/assets/javascripts/controllers/live_metrics/ad_hoc_metrics_controller.js b/app/assets/javascripts/controllers/live_metrics/ad_hoc_metrics_controller.js index 595177a7cbc..b034c7940db 100644 --- a/app/assets/javascripts/controllers/live_metrics/ad_hoc_metrics_controller.js +++ b/app/assets/javascripts/controllers/live_metrics/ad_hoc_metrics_controller.js @@ -1,9 +1,9 @@ /* global miqHttpInject */ -ManageIQ.angular.app.controller('adHocMetricsController', ['$http', '$window', 'miqService', +ManageIQ.angular.app.controller('adHocMetricsController', ['$http', '$window', '$timeout', 'miqService', 'metricsUtilsFactory', 'metricsHttpFactory', 'metricsConfigFactory', 'metricsParseUrlFactory', - function($http, $window, miqService, metricsUtilsFactory, metricsHttpFactory, metricsConfigFactory, metricsParseUrlFactory) { + function($http, $window, $timeout, miqService, metricsUtilsFactory, metricsHttpFactory, metricsConfigFactory, metricsParseUrlFactory) { var dash = this; - var utils = metricsUtilsFactory(dash); + var utils = metricsUtilsFactory(dash, $timeout); var httpUtils = metricsHttpFactory(dash, $http, utils, miqService); dash.getTenants = httpUtils.getTenants; @@ -11,6 +11,7 @@ ManageIQ.angular.app.controller('adHocMetricsController', ['$http', '$window', ' dash.refreshGraph = httpUtils.refreshGraph; dash.setFilterOptions = utils.setFilterOptions; dash.metricPrefix = utils.metricPrefix; + dash.calcDataDifferentials = utils.calcDataDifferentials; dash.setPage = httpUtils.setPage; var pageSetup = function() { @@ -149,6 +150,9 @@ ManageIQ.angular.app.controller('adHocMetricsController', ['$http', '$window', ' dash.showGraph = true; dash.chartDataInit = false; httpUtils.refreshGraph(); + + // enable the bootstrapSwitch to run chart refresh + angular.element('[name=rate-switch]').bootstrapSwitch({ onSwitchChange: utils.redrawGraph }); }; dash.viewMetrics = function() { diff --git a/app/assets/javascripts/controllers/live_metrics/util/metrics-http-factory.js b/app/assets/javascripts/controllers/live_metrics/util/metrics-http-factory.js index 7730ffd885f..058b4044d19 100644 --- a/app/assets/javascripts/controllers/live_metrics/util/metrics-http-factory.js +++ b/app/assets/javascripts/controllers/live_metrics/util/metrics-http-factory.js @@ -3,6 +3,28 @@ angular.module('miq.util').factory('metricsHttpFactory', function() { var NUMBER_OF_MILLISEC_IN_HOUR = 60 * 60 * 1000; var NUMBER_OF_MILLISEC_IN_SEC = 1000; + function getMetricTagsData(response) { + 'use strict'; + dash.tagsLoaded = true; + + if (utils.checkResponse(response) === false) { + return; + } + + var data = response.data; + + dash.filterConfig.fields = []; + if (data && _.isArray(data.metric_tags)) { + data.metric_tags.sort(); + + // remember the metric tags + dash.metricTags = data.metric_tags; + + // apply dash.metricTags to the filter form + utils.setFilterOptions(); + } + } + function getMetricDefinitionsData(response) { 'use strict'; var data = response.data; @@ -26,7 +48,7 @@ angular.module('miq.util').factory('metricsHttpFactory', function() { dash.filterConfig.resultsCount = data.items; } - function refreshOneGraph(metricId, metricType, currentItem) { + function refreshOneGraph(currentItem) { var numberOfBucketsInChart = 300; var ends = dash.timeFilter.date.valueOf(); // javascript time is in milisec @@ -40,12 +62,12 @@ angular.module('miq.util').factory('metricsHttpFactory', function() { } // hawkular time is in milisec (hawkular bucket_duration is in seconds) - var params = '&query=get_data&type=' + metricType + '&metric_id=' + metricId + '&ends=' + ends + + var params = '&query=get_data&type=' + currentItem.type + '&metric_id=' + currentItem.id + '&ends=' + ends + '&starts=' + starts + '&bucket_duration=' + bucket_duration + 's'; $http.get(dash.url + params) .then(function(response) { - utils.getContainerParamsData(metricId, currentItem, response); }) + utils.getContainerParamsData(currentItem, response); }) .catch(function(error) { dash.loadCount++; if (dash.loadCount >= dash.selectedItems.length) { @@ -56,7 +78,7 @@ angular.module('miq.util').factory('metricsHttpFactory', function() { var getMetricTags = function() { $http.get(dash.url + '&query=metric_tags&limit=250') - .then(utils.getMetricTagsData) + .then(getMetricTagsData) .catch(function(error) { dash.tagsLoaded = true; dash.tenantChanged = false; @@ -102,11 +124,10 @@ angular.module('miq.util').factory('metricsHttpFactory', function() { dash.loadingData = true; dash.chartData = {}; - for (var i = 0; i < dash.selectedItems.length; i++) { - var metric_id = dash.selectedItems[i].id; - var metric_type = dash.selectedItems[i].type; - refreshOneGraph(metric_id, metric_type, i); - } + angular.forEach(dash.selectedItems, function(item, index) { + item.index = index; + refreshOneGraph(item); + }); }; var setPage = function(page) { diff --git a/app/assets/javascripts/controllers/live_metrics/util/metrics-utils-factory.js b/app/assets/javascripts/controllers/live_metrics/util/metrics-utils-factory.js index 3461dc6870d..98160eb986e 100644 --- a/app/assets/javascripts/controllers/live_metrics/util/metrics-utils-factory.js +++ b/app/assets/javascripts/controllers/live_metrics/util/metrics-utils-factory.js @@ -1,7 +1,21 @@ angular.module('miq.util').factory('metricsUtilsFactory', function() { - return function (dash) { + return function (dash, $timeout) { var UNKNOWN_ERROR_STR = __('Something is wrong, try reloading the page'); + function calcDataDifferentials(data) { + var outData = []; + + data.forEach(function(value, i) { + if ((value !== null) && (data[i + 1] !== null) && (typeof data[i + 1] !== 'undefined')) { + outData[i] = data[i + 1] - value; + } else { + outData[i] = null; + } + }); + + return outData; + } + var checkResponse = function(response) { if (response.error || response.data.error || typeof response.data === 'string') { add_flash(response.error || response.data.error || UNKNOWN_ERROR_STR, 'error'); @@ -50,48 +64,43 @@ angular.module('miq.util').factory('metricsUtilsFactory', function() { } } - var getMetricTagsData = function(response) { + var getContainerParamsData = function(currentItem, response) { 'use strict'; - dash.tagsLoaded = true; + dash.loadCount++; + if (dash.loadCount >= dash.selectedItems.length) { + dash.loadingData = false; + } if (checkResponse(response) === false) { return; } - var data = response.data; - - dash.filterConfig.fields = []; - if (data && _.isArray(data.metric_tags)) { - data.metric_tags.sort(); - - // remember the metric tags - dash.metricTags = data.metric_tags; + currentItem.responseData = response.data.data.slice(); + drawOneGraph(currentItem); + } - // apply dash.metricTags to the filter form - setFilterOptions(); - } + function redrawGraph() { + $timeout(function () { + angular.forEach(dash.selectedItems, drawOneGraph); + }, 10); } - var getContainerParamsData = function(metricId, currentItem, response) { - 'use strict'; - dash.loadCount++; - if (dash.loadCount >= dash.selectedItems.length) { - dash.loadingData = false; - } + function drawOneGraph(currentItem) { + var switchObj = angular.element('#rate-switch'); + var showRate = switchObj.bootstrapSwitch('state'); + var xData = currentItem.responseData.map(function(d) { return d.start; }); + var yData = currentItem.responseData.map(function(d) { return d.avg || null; }); - if (checkResponse(response) === false) { - return; + // if diff checkbox is on, do diff + if (showRate) { + yData = calcDataDifferentials(yData); } - var data = response.data.data; - var xData = data.map(function(d) { return d.start; }); - var yData = data.map(function(d) { return d.avg || null; }); - xData.unshift('time'); - yData.unshift(metricId); + yData.unshift(currentItem.id); dash.chartData.xData = xData; - dash.chartData['yData'+ currentItem] = yData; + dash.chartData['yData'+ currentItem.index] = yData; dash.chartDataInit = true; } @@ -178,11 +187,12 @@ angular.module('miq.util').factory('metricsUtilsFactory', function() { } return { - getMetricTagsData: getMetricTagsData, getContainerParamsData: getContainerParamsData, getContainerDashboardData: getContainerDashboardData, checkResponse: checkResponse, setFilterOptions: setFilterOptions, + calcDataDifferentials: calcDataDifferentials, + redrawGraph: redrawGraph, metricPrefix: metricPrefix } } diff --git a/app/assets/stylesheets/metrics.scss b/app/assets/stylesheets/metrics.scss index c0cd0d70a7d..2949db28171 100644 --- a/app/assets/stylesheets/metrics.scss +++ b/app/assets/stylesheets/metrics.scss @@ -26,6 +26,12 @@ .toolbar-pf { border: 0; } + .toolbar-pf-include-actions { + margin-left: 8px !important; + } + .form-group.toolbar-actions { + border: 0; + } .graph-view { .toolbar-pf { .toolbar-pf-actions { @@ -36,6 +42,9 @@ } overflow-x: hidden; overflow-y: hidden; + .rate-switch { + margin-left: 5px; + } .bootstrap-select.btn-group { .dropdown-menu { margin-left: 5px; diff --git a/app/views/ems_container/ad_hoc/_chart_view_form.html.haml b/app/views/ems_container/ad_hoc/_chart_view_form.html.haml index 48f579fe049..61fccb3e6a9 100644 --- a/app/views/ems_container/ad_hoc/_chart_view_form.html.haml +++ b/app/views/ems_container/ad_hoc/_chart_view_form.html.haml @@ -26,6 +26,13 @@ "options" => "dash.dateOptions", "date" => "dash.timeFilter.date"} + .pull-left.rate-switch + %label + = _("Rate") + %input.bootstrap-switch.ad-hoc-refresh{"type" => "checkbox", + "name" => "rate-switch", + "id" => "rate-switch"} + %button.btn.btn-primary.pull-left{"type" => "button", "ng-click" => "dash.refreshGraph()"} = _("Refresh") diff --git a/spec/javascripts/controllers/container_live_dashboard/container_live_dashboard_controller_spec.js b/spec/javascripts/controllers/container_live_dashboard/container_live_dashboard_controller_spec.js index 8413d2fdc7d..e64d728fe8d 100644 --- a/spec/javascripts/controllers/container_live_dashboard/container_live_dashboard_controller_spec.js +++ b/spec/javascripts/controllers/container_live_dashboard/container_live_dashboard_controller_spec.js @@ -24,8 +24,8 @@ describe('adHocMetricsController', function() { })); afterEach(function() { - $httpBackend.verifyNoOutstandingExpectation(); - $httpBackend.verifyNoOutstandingRequest(); + $httpBackend.verifyNoOutstandingExpectation(); + $httpBackend.verifyNoOutstandingRequest(); }); describe('loading page data', function() { @@ -59,15 +59,29 @@ describe('adHocMetricsController', function() { m = $controller.metricPrefix(10000, 'ms'); expect(m.multiplier).toBe(Math.pow(10, -3)); - expect(m.unitLable).toBe('s'); + expect(m.unitLabel).toBe('s'); m = $controller.metricPrefix(10000, 'ns'); expect(m.multiplier).toBe(Math.pow(10, -9)); - expect(m.unitLable).toBe('s'); + expect(m.unitLabel).toBe('s'); m = $controller.metricPrefix(10000, 's'); expect(m.multiplier).toBe(Math.pow(10, -3)); - expect(m.unitLable).toBe('Ks'); + expect(m.unitLabel).toBe('Ks'); + }); + + it('should calculate differentials currectly', function() { + var data = [1, 2, 3, 4, 5, 6]; + + data = $controller.calcDataDifferentials(data); + expect(data).toEqual([1, 1, 1, 1, 1, null]); + }); + + it('differentials should not fail on missing data', function() { + var data = [1, 2, null, 4, 5, 6]; + + data = $controller.calcDataDifferentials(data); + expect(data).toEqual([1, null, null, 1, 1, null]); }); }); });