diff --git a/.gitignore b/.gitignore index 1028d1268..93706d652 100644 --- a/.gitignore +++ b/.gitignore @@ -4,3 +4,8 @@ .idea/ *.bat + +# Grafana linter config +.jshintrc +.jscs.json +.jsfmtrc \ No newline at end of file diff --git a/zabbix/datasource.js b/zabbix/datasource.js index d62b75b93..f05186632 100644 --- a/zabbix/datasource.js +++ b/zabbix/datasource.js @@ -1,16 +1,18 @@ +'use strict'; define([ 'angular', 'lodash', 'kbn', './zabbixAPIWrapper', + './helperFunctions', './queryCtrl' ], function (angular, _, kbn) { - 'use strict'; + //'use strict'; var module = angular.module('grafana.services'); - module.factory('ZabbixAPIDatasource', function($q, backendSrv, templateSrv, zabbix) { + module.factory('ZabbixAPIDatasource', function($q, backendSrv, templateSrv, alertSrv, ZabbixAPI, zabbixHelperSrv) { /** * Datasource initialization. Calls when you refresh page, add @@ -21,6 +23,8 @@ function (angular, _, kbn) { function ZabbixAPIDatasource(datasource) { this.name = datasource.name; this.url = datasource.url; + this.basicAuth = datasource.basicAuth; + this.withCredentials = datasource.withCredentials; // TODO: fix passing username and password from config.html this.username = datasource.meta.username; @@ -31,13 +35,12 @@ function (angular, _, kbn) { this.trendsFrom = datasource.meta.trendsFrom || '7d'; // Limit metrics per panel for templated request - this.limitmetrics = datasource.meta.limitmetrics || 50; + this.limitmetrics = datasource.meta.limitmetrics || 100; // Initialize Zabbix API - zabbix.init(this.url, this.username, this.password); + this.zabbixAPI = new ZabbixAPI(this.url, this.username, this.password, this.basicAuth, this.withCredentials); } - /** * Calls for each panel in dashboard. * @@ -71,25 +74,27 @@ function (angular, _, kbn) { // Extract zabbix groups, hosts and apps from string: // "{host1,host2,...,hostN}" --> [host1, host2, ..., hostN] - var groups = splitMetrics(groupname); - var hosts = splitMetrics(hostname); - var apps = splitMetrics(appname); + var groups = zabbixHelperSrv.splitMetrics(groupname); + var hosts = zabbixHelperSrv.splitMetrics(hostname); + var apps = zabbixHelperSrv.splitMetrics(appname); // Remove hostnames from item names and then // extract item names // "hostname: itemname" --> "itemname" var delete_hostname_pattern = /(?:\[[\w\.]+\]\:\s)/g; - var itemnames = splitMetrics(itemname.replace(delete_hostname_pattern, '')); + var itemnames = zabbixHelperSrv.splitMetrics(itemname.replace(delete_hostname_pattern, '')); // Find items by item names and perform queries var self = this; - return zabbix.itemFindQuery(groups, hosts, apps) + return this.zabbixAPI.itemFindQuery(groups, hosts, apps) .then(function (items) { // Filter hosts by regex - if (target.host.visible_name == 'All') { + if (target.host.visible_name === 'All') { if (target.hostFilter && _.every(items, _.identity.hosts)) { - var host_pattern = new RegExp(target.hostFilter); + + // Use templated variables in filter + var host_pattern = new RegExp(templateSrv.replace(target.hostFilter)); items = _.filter(items, function (item) { return _.some(item.hosts, function (host) { return host_pattern.test(host.name); @@ -98,13 +103,15 @@ function (angular, _, kbn) { } } - if (itemnames == 'All') { + if (itemnames[0] === 'All') { // Filter items by regex if (target.itemFilter) { - var item_pattern = new RegExp(target.itemFilter); + + // Use templated variables in filter + var item_pattern = new RegExp(templateSrv.replace(target.itemFilter)); return _.filter(items, function (item) { - return item_pattern.test(zabbix.expandItemName(item)); + return item_pattern.test(zabbixHelperSrv.expandItemName(item)); }); } else { return items; @@ -113,7 +120,7 @@ function (angular, _, kbn) { // Filtering items return _.filter(items, function (item) { - return _.contains(itemnames, zabbix.expandItemName(item)); + return _.contains(itemnames, zabbixHelperSrv.expandItemName(item)); }); } }).then(function (items) { @@ -121,113 +128,47 @@ function (angular, _, kbn) { // Don't perform query for high number of items // to prevent Grafana slowdown if (items.length > self.limitmetrics) { + var message = "Try to increase limitmetrics parameter in datasource config.
" + + "Current limitmetrics value is " + self.limitmetrics; + alertSrv.set("Metrics limit exceeded", message, "warning", 10000); return []; } else { items = _.flatten(items); - var alias = target.item.name === 'All' ? undefined : templateSrv.replace(target.alias); + + // Use alias only for single metric, otherwise use item names + var alias = target.item.name === 'All' || itemnames.length > 1 ? undefined : templateSrv.replace(target.alias); if ((from < useTrendsFrom) && self.trends) { - return zabbix.getTrends(items, from, to) - .then(_.partial(self.handleTrendResponse, items, alias, target.scale)); + return self.zabbixAPI.getTrends(items, from, to) + .then(_.bind(zabbixHelperSrv.handleTrendResponse, zabbixHelperSrv, items, alias, target.scale)); } else { - return zabbix.getHistory(items, from, to) - .then(_.partial(self.handleHistoryResponse, items, alias, target.scale)); + return self.zabbixAPI.getHistory(items, from, to) + .then(_.bind(zabbixHelperSrv.handleHistoryResponse, zabbixHelperSrv, items, alias, target.scale)); } } }); }, this); return $q.all(_.flatten(promises)).then(function (results) { - return { data: _.flatten(results) }; - }); - }; - - - ZabbixAPIDatasource.prototype.handleTrendResponse = function(items, alias, scale, trends) { - - // Group items and trends by itemid - var indexed_items = _.indexBy(items, 'itemid'); - var grouped_history = _.groupBy(trends, 'itemid'); + var timeseries_data = _.flatten(results); + var data = _.map(timeseries_data, function (timeseries) { - return $q.when(_.map(grouped_history, function (trends, itemid) { - var item = indexed_items[itemid]; - var series = { - target: (item.hosts ? item.hosts[0].name+': ' : '') + (alias ? alias : zabbix.expandItemName(item)), - datapoints: _.map(trends, function (p) { - - // Value must be a number for properly work - var value = Number(p.value_avg); - - // Apply scale - if (scale) { - value *= scale; - } - return [value, p.clock * 1000]; - }) - }; - return series; - })).then(function (result) { - return _.sortBy(result, 'target'); - }); - }; - - - /** - * Convert Zabbix API data to Grafana format - * - * @param {Array} items Array of Zabbix Items - * @param {Array} history Array of Zabbix History - * - * @return {Array} Array of timeseries in Grafana format - * { - * target: "Metric name", - * datapoints: [[, ], ...] - * } - */ - ZabbixAPIDatasource.prototype.handleHistoryResponse = function(items, alias, scale, history) { - /** - * Response should be in the format: - * data: [ - * { - * target: "Metric name", - * datapoints: [[, ], ...] - * }, - * { - * target: "Metric name", - * datapoints: [[, ], ...] - * }, - * ] - */ - - // Group items and history by itemid - var indexed_items = _.indexBy(items, 'itemid'); - var grouped_history = _.groupBy(history, 'itemid'); - - return $q.when(_.map(grouped_history, function (history, itemid) { - var item = indexed_items[itemid]; - var series = { - target: (item.hosts ? item.hosts[0].name+': ' : '') + (alias ? alias : zabbix.expandItemName(item)), - datapoints: _.map(history, function (p) { - - // Value must be a number for properly work - var value = Number(p.value); - - // Apply scale - if (scale) { - value *= scale; - } - return [value, p.clock * 1000]; - }) - }; - return series; - })).then(function (result) { - return _.sortBy(result, 'target'); + // Series downsampling + if (timeseries.datapoints.length > options.maxDataPoints) { + var ms_interval = Math.floor((to - from) / options.maxDataPoints) * 1000; + timeseries.datapoints = zabbixHelperSrv.downsampleSeries(timeseries.datapoints, to, ms_interval); + } + return timeseries; + }); + return { data: data }; }); }; + //////////////// + // Templating // + //////////////// /** - * For templated query. * Find metrics from templated request. * * @param {string} query Query from Templating @@ -243,28 +184,29 @@ function (angular, _, kbn) { if (part[0] === '{') { // Convert multiple mettrics to array // "{metric1,metcic2,...,metricN}" --> [metric1, metcic2,..., metricN] - parts.push(splitMetrics(part)); + parts.push(zabbixHelperSrv.splitMetrics(part)); } else { parts.push(part); } }); - var template = _.object(['group', 'host', 'app', 'item'], parts) + var template = _.object(['group', 'host', 'app', 'item'], parts); // Get items if (parts.length === 4) { - return zabbix.itemFindQuery(template.group, template.host, template.app).then(function (result) { - return _.map(result, function (item) { - var itemname = zabbix.expandItemName(item) - return { - text: itemname, - expandable: false - }; + return this.zabbixAPI.itemFindQuery(template.group, template.host, template.app) + .then(function (result) { + return _.map(result, function (item) { + var itemname = zabbixHelperSrv.expandItemName(item); + return { + text: itemname, + expandable: false + }; + }); }); - }); } // Get applications else if (parts.length === 3) { - return zabbix.appFindQuery(template.host, template.group).then(function (result) { + return this.zabbixAPI.appFindQuery(template.host, template.group).then(function (result) { return _.map(result, function (app) { return { text: app.name, @@ -275,7 +217,7 @@ function (angular, _, kbn) { } // Get hosts else if (parts.length === 2) { - return zabbix.hostFindQuery(template.group).then(function (result) { + return this.zabbixAPI.hostFindQuery(template.group).then(function (result) { return _.map(result, function (host) { return { text: host.name, @@ -286,7 +228,7 @@ function (angular, _, kbn) { } // Get groups else if (parts.length === 1) { - return zabbix.getGroupByName(template.group).then(function (result) { + return this.zabbixAPI.getGroupByName(template.group).then(function (result) { return _.map(result, function (hostgroup) { return { text: hostgroup.name, @@ -303,12 +245,10 @@ function (angular, _, kbn) { } }; - ///////////////// // Annotations // ///////////////// - ZabbixAPIDatasource.prototype.annotationQuery = function(annotation, rangeUnparsed) { var from = Math.ceil(kbn.parseDate(rangeUnparsed.from).getTime() / 1000); var to = Math.ceil(kbn.parseDate(rangeUnparsed.to).getTime() / 1000); @@ -323,7 +263,7 @@ function (angular, _, kbn) { expandDescription: true }; - return this.performZabbixAPIRequest('trigger.get', params) + return this.zabbixAPI.performZabbixAPIRequest('trigger.get', params) .then(function (result) { if(result) { var objects = _.indexBy(result, 'triggerid'); @@ -340,11 +280,11 @@ function (angular, _, kbn) { params.value = 1; } - return self.performZabbixAPIRequest('event.get', params) + return self.zabbixAPI.performZabbixAPIRequest('event.get', params) .then(function (result) { var events = []; _.each(result, function(e) { - var formatted_acknowledges = formatAcknowledges(e.acknowledges);; + var formatted_acknowledges = zabbixHelperSrv.formatAcknowledges(e.acknowledges); events.push({ annotation: annotation, time: e.clock * 1000, @@ -353,67 +293,13 @@ function (angular, _, kbn) { }); }); return events; - }); + }); } else { return []; } - }); + }); }; return ZabbixAPIDatasource; }); }); - - -/** - * Convert multiple mettrics to array - * "{metric1,metcic2,...,metricN}" --> [metric1, metcic2,..., metricN] - * - * @param {string} metrics "{metric1,metcic2,...,metricN}" - * @return {Array} [metric1, metcic2,..., metricN] - */ -function splitMetrics(metrics) { - var remove_brackets_pattern = /^{|}$/g; - var metric_split_pattern = /,(?!\s)/g; - return metrics.replace(remove_brackets_pattern, '').split(metric_split_pattern) -} - - -/** - * Convert Date object to local time in format - * YYYY-MM-DD HH:mm:ss - * - * @param {Date} date Date object - * @return {string} formatted local time YYYY-MM-DD HH:mm:ss - */ -function getShortTime(date) { - var MM = date.getMonth() < 10 ? '0' + date.getMonth() : date.getMonth(); - var DD = date.getDate() < 10 ? '0' + date.getDate() : date.getDate(); - var HH = date.getHours() < 10 ? '0' + date.getHours() : date.getHours(); - var mm = date.getMinutes() < 10 ? '0' + date.getMinutes() : date.getMinutes(); - var ss = date.getSeconds() < 10 ? '0' + date.getSeconds() : date.getSeconds(); - return date.getFullYear() + '-' + MM + '-' + DD + ' ' + HH + ':' + mm + ':' + ss; -} - - -/** - * Format acknowledges. - * - * @param {array} acknowledges array of Zabbix acknowledge objects - * @return {string} HTML-formatted table - */ -function formatAcknowledges(acknowledges) { - if (acknowledges.length) { - var formatted_acknowledges = '

Acknowledges:
'; - _.each(_.map(acknowledges, function (ack) { - var time = new Date(ack.clock * 1000); - return ''; - }), function (ack) { - formatted_acknowledges = formatted_acknowledges.concat(ack) - }); - formatted_acknowledges = formatted_acknowledges.concat('
TimeUserComments
' + getShortTime(time) + '' + ack.alias + ' (' + ack.name+ ' ' + ack.surname + ')' + '' + ack.message + '
') - return formatted_acknowledges; - } else { - return ''; - } -} diff --git a/zabbix/helperFunctions.js b/zabbix/helperFunctions.js new file mode 100644 index 000000000..34bb84e25 --- /dev/null +++ b/zabbix/helperFunctions.js @@ -0,0 +1,226 @@ +define([ + 'angular', + 'lodash' +], +function (angular, _) { + 'use strict'; + + var module = angular.module('grafana.services'); + + module.service('zabbixHelperSrv', function($q) { + var self = this; + + /** + * Convert Zabbix API history.get response to Grafana format + * + * @param {Array} items Array of Zabbix Items + * @param {Array} history Array of Zabbix History + * + * @return {Array} Array of timeseries in Grafana format + * { + * target: "Metric name", + * datapoints: [[, ], ...] + * } + */ + this.handleHistoryResponse = function(items, alias, scale, history) { + /** + * Response should be in the format: + * data: [ + * { + * target: "Metric name", + * datapoints: [[, ], ...] + * }, + * { + * target: "Metric name", + * datapoints: [[, ], ...] + * }, + * ] + */ + + // Group items and history by itemid + var indexed_items = _.indexBy(items, 'itemid'); + var grouped_history = _.groupBy(history, 'itemid'); + + var self = this; + return $q.when(_.map(grouped_history, function (history, itemid) { + var item = indexed_items[itemid]; + var series = { + target: (item.hosts ? item.hosts[0].name+': ' : '') + + (alias ? alias : self.expandItemName(item)), + datapoints: _.map(history, function (p) { + + // Value must be a number for properly work + var value = Number(p.value); + + // Apply scale + if (scale) { + value *= scale; + } + return [value, p.clock * 1000]; + }) + }; + return series; + })).then(function (result) { + return _.sortBy(result, 'target'); + }); + }; + + /** + * Convert Zabbix API trends.get response to Grafana format + * + * @param {Array} items Array of Zabbix Items + * @param {Array} trends Array of Zabbix Trends + * + * @return {Array} Array of timeseries in Grafana format + * { + * target: "Metric name", + * datapoints: [[, ], ...] + * } + */ + this.handleTrendResponse = function (items, alias, scale, trends) { + + // Group items and trends by itemid + var indexed_items = _.indexBy(items, 'itemid'); + var grouped_trends = _.groupBy(trends, 'itemid'); + + var self = this; + return $q.when(_.map(grouped_trends, function (trends, itemid) { + var item = indexed_items[itemid]; + var series = { + target: (item.hosts ? item.hosts[0].name+': ' : '') + + (alias ? alias : self.expandItemName(item)), + datapoints: _.map(trends, function (p) { + + // Value must be a number for properly work + var value = Number(p.value_avg); + + // Apply scale + if (scale) { + value *= scale; + } + return [value, p.clock * 1000]; + }) + }; + return series; + })).then(function (result) { + return _.sortBy(result, 'target'); + }); + }; + + /** + * Expand item parameters, for example: + * CPU $2 time ($3) --> CPU system time (avg1) + * + * @param item: zabbix api item object + * @return: expanded item name (string) + */ + this.expandItemName = function(item) { + var name = item.name; + var key = item.key_; + + // extract params from key: + // "system.cpu.util[,system,avg1]" --> ["", "system", "avg1"] + var key_params = key.substring(key.indexOf('[') + 1, key.lastIndexOf(']')).split(','); + + // replace item parameters + for (var i = key_params.length; i >= 1; i--) { + name = name.replace('$' + i, key_params[i - 1]); + } + return name; + }; + + /** + * Convert multiple mettrics to array + * "{metric1,metcic2,...,metricN}" --> [metric1, metcic2,..., metricN] + * + * @param {string} metrics "{metric1,metcic2,...,metricN}" + * @return {Array} [metric1, metcic2,..., metricN] + */ + this.splitMetrics = function(metrics) { + var remove_brackets_pattern = /^{|}$/g; + var metric_split_pattern = /,(?!\s)/g; + return metrics.replace(remove_brackets_pattern, '').split(metric_split_pattern); + }; + + /** + * Convert Date object to local time in format + * YYYY-MM-DD HH:mm:ss + * + * @param {Date} date Date object + * @return {string} formatted local time YYYY-MM-DD HH:mm:ss + */ + this.getShortTime = function(date) { + var MM = date.getMonth() < 10 ? '0' + date.getMonth() : date.getMonth(); + var DD = date.getDate() < 10 ? '0' + date.getDate() : date.getDate(); + var HH = date.getHours() < 10 ? '0' + date.getHours() : date.getHours(); + var mm = date.getMinutes() < 10 ? '0' + date.getMinutes() : date.getMinutes(); + var ss = date.getSeconds() < 10 ? '0' + date.getSeconds() : date.getSeconds(); + return date.getFullYear() + '-' + MM + '-' + DD + ' ' + HH + ':' + mm + ':' + ss; + }; + + /** + * Format acknowledges. + * + * @param {array} acknowledges array of Zabbix acknowledge objects + * @return {string} HTML-formatted table + */ + this.formatAcknowledges = function(acknowledges) { + if (acknowledges.length) { + var formatted_acknowledges = '

Acknowledges:
' + + ''; + _.each(_.map(acknowledges, function (ack) { + var time = new Date(ack.clock * 1000); + return ''; + }), function (ack) { + formatted_acknowledges = formatted_acknowledges.concat(ack); + }); + formatted_acknowledges = formatted_acknowledges.concat('
TimeUserComments
' + self.getShortTime(time) + '' + ack.alias + + ' (' + ack.name + ' ' + ack.surname + ')' + '' + ack.message + '
'); + return formatted_acknowledges; + } else { + return ''; + } + }; + + /** + * Downsample datapoints series + * + * @param {array} datapoints [[, ], ...] + * @param {integer} time_to Panel time to + * @param {integer} ms_interval Interval in milliseconds for grouping datapoints + * @return {array} [[, ], ...] + */ + this.downsampleSeries = function(datapoints, time_to, ms_interval) { + var downsampledSeries = []; + var timeWindow = { + from: time_to * 1000 - ms_interval, + to: time_to * 1000 + }; + + var points_sum = 0; + var points_num = 0; + var value_avg = 0; + for (var i = datapoints.length - 1; i >= 0; i -= 1) { + if (timeWindow.from < datapoints[i][1] && datapoints[i][1] <= timeWindow.to) { + points_sum += datapoints[i][0]; + points_num++; + } + else { + value_avg = points_num ? points_sum / points_num : 0; + downsampledSeries.push([value_avg, timeWindow.to]); + + // Shift time window + timeWindow.to = timeWindow.from; + timeWindow.from -= ms_interval; + + points_sum = 0; + points_num = 0; + + // Process point again + i++; + } + } + return downsampledSeries.reverse(); + }; + }); +}); \ No newline at end of file diff --git a/zabbix/partials/query.editor.html b/zabbix/partials/query.editor.html index 44dcf6638..6fc6627e6 100644 --- a/zabbix/partials/query.editor.html +++ b/zabbix/partials/query.editor.html @@ -174,3 +174,57 @@ + +
+
+
    +
  • + +
  • +
  • + Max data points +
  • +
  • + +
  • +
+
+
+
+ +
+
+
+ +
+
+ +
+
Max data points
+
    +
  • Grafana-Zabbix plugin uses maxDataPoints parameter to consolidate the real number of values down to this number
  • +
  • If there are more real values, then by default they will be consolidated using averages
  • +
  • This could hide real peaks and max values in your series
  • +
  • Point consolidation will effect series legend values (min,max,total,current)
  • +
  • If you override maxDataPoint and set a high value performance can be severely effected
  • +
+
+ +
+
\ No newline at end of file diff --git a/zabbix/plugin.json b/zabbix/plugin.json index 5d10ceda6..c95ec0127 100644 --- a/zabbix/plugin.json +++ b/zabbix/plugin.json @@ -16,10 +16,10 @@ "username": "guest", "password": "", - "trends": true, + "trends": false, "trendsFrom": "7d", - "limitmetrics": 50, + "limitmetrics": 100, "metrics": true, "annotations": true diff --git a/zabbix/queryCtrl.js b/zabbix/queryCtrl.js index 4f847c97c..053d4f488 100644 --- a/zabbix/queryCtrl.js +++ b/zabbix/queryCtrl.js @@ -1,7 +1,7 @@ define([ 'angular', 'lodash', - './zabbixAPIWrapper' + './helperFunctions' ], function (angular, _) { 'use strict'; @@ -9,7 +9,7 @@ function (angular, _) { var module = angular.module('grafana.controllers'); var targetLetters = 'ABCDEFGHIJKLMNOPQRSTUVWXYZ'; - module.controller('ZabbixAPIQueryCtrl', function($scope, $sce, templateSrv, zabbix) { + module.controller('ZabbixAPIQueryCtrl', function($scope, $sce, templateSrv, zabbixHelperSrv) { $scope.init = function() { $scope.targetLetters = targetLetters; @@ -31,7 +31,6 @@ function (angular, _) { $scope.target.errors = validateTarget($scope.target); }; - /** * Take alias from item name by default */ @@ -39,8 +38,7 @@ function (angular, _) { if (!$scope.target.alias && $scope.target.item) { $scope.target.alias = $scope.target.item.name; } - }; - + } $scope.targetBlur = function() { setItemAlias(); @@ -51,12 +49,11 @@ function (angular, _) { } }; - /** * Call when host group selected */ $scope.selectHostGroup = function() { - $scope.updateHostList() + $scope.updateHostList(); $scope.updateAppList(); $scope.updateItemList(); @@ -67,7 +64,6 @@ function (angular, _) { } }; - /** * Call when host selected */ @@ -82,7 +78,6 @@ function (angular, _) { } }; - /** * Call when application selected */ @@ -96,7 +91,6 @@ function (angular, _) { } }; - /** * Call when item selected */ @@ -109,13 +103,11 @@ function (angular, _) { } }; - $scope.duplicate = function() { var clone = angular.copy($scope.target); $scope.panel.targets.push(clone); }; - $scope.moveMetricQuery = function(fromIndex, toIndex) { _.move($scope.panel.targets, fromIndex, toIndex); }; @@ -124,7 +116,6 @@ function (angular, _) { // SUGGESTION QUERIES ////////////////////////////// - /** * Update list of host groups */ @@ -132,12 +123,11 @@ function (angular, _) { $scope.metric.groupList = [{name: '*', visible_name: 'All'}]; addTemplatedVariables($scope.metric.groupList); - zabbix.performHostGroupSuggestQuery().then(function (groups) { + $scope.datasource.zabbixAPI.performHostGroupSuggestQuery().then(function (groups) { $scope.metric.groupList = $scope.metric.groupList.concat(groups); }); }; - /** * Update list of hosts */ @@ -145,13 +135,12 @@ function (angular, _) { $scope.metric.hostList = [{name: '*', visible_name: 'All'}]; addTemplatedVariables($scope.metric.hostList); - var groups = $scope.target.group ? splitMetrics(templateSrv.replace($scope.target.group.name)) : undefined; - zabbix.hostFindQuery(groups).then(function (hosts) { + var groups = $scope.target.group ? zabbixHelperSrv.splitMetrics(templateSrv.replace($scope.target.group.name)) : undefined; + $scope.datasource.zabbixAPI.hostFindQuery(groups).then(function (hosts) { $scope.metric.hostList = $scope.metric.hostList.concat(hosts); }); }; - /** * Update list of host applications */ @@ -159,39 +148,37 @@ function (angular, _) { $scope.metric.applicationList = [{name: '*', visible_name: 'All'}]; addTemplatedVariables($scope.metric.applicationList); - var groups = $scope.target.group ? splitMetrics(templateSrv.replace($scope.target.group.name)) : undefined; - var hosts = $scope.target.host ? splitMetrics(templateSrv.replace($scope.target.host.name)) : undefined; - zabbix.appFindQuery(hosts, groups).then(function (apps) { - var apps = _.map(_.uniq(_.map(apps, 'name')), function (appname) { + var groups = $scope.target.group ? zabbixHelperSrv.splitMetrics(templateSrv.replace($scope.target.group.name)) : undefined; + var hosts = $scope.target.host ? zabbixHelperSrv.splitMetrics(templateSrv.replace($scope.target.host.name)) : undefined; + $scope.datasource.zabbixAPI.appFindQuery(hosts, groups).then(function (apps) { + apps = _.map(_.uniq(_.map(apps, 'name')), function (appname) { return {name: appname}; }); $scope.metric.applicationList = $scope.metric.applicationList.concat(apps); }); }; - /** * Update list of items */ $scope.updateItemList = function() { - $scope.metric.itemList = [{name: 'All'}];; + $scope.metric.itemList = [{name: 'All'}]; addTemplatedVariables($scope.metric.itemList); - var groups = $scope.target.group ? splitMetrics(templateSrv.replace($scope.target.group.name)) : undefined; - var hosts = $scope.target.host ? splitMetrics(templateSrv.replace($scope.target.host.name)) : undefined; - var apps = $scope.target.application ? splitMetrics(templateSrv.replace($scope.target.application.name)) : undefined; - zabbix.itemFindQuery(groups, hosts, apps).then(function (items) { + var groups = $scope.target.group ? zabbixHelperSrv.splitMetrics(templateSrv.replace($scope.target.group.name)) : undefined; + var hosts = $scope.target.host ? zabbixHelperSrv.splitMetrics(templateSrv.replace($scope.target.host.name)) : undefined; + var apps = $scope.target.application ? zabbixHelperSrv.splitMetrics(templateSrv.replace($scope.target.application.name)) : undefined; + $scope.datasource.zabbixAPI.itemFindQuery(groups, hosts, apps).then(function (items) { // Show only unique item names var uniq_items = _.map(_.uniq(items, function (item) { - return zabbix.expandItemName(item); + return zabbixHelperSrv.expandItemName(item); }), function (item) { - return {name: zabbix.expandItemName(item)} + return {name: zabbixHelperSrv.expandItemName(item)}; }); $scope.metric.itemList = $scope.metric.itemList.concat(uniq_items); }); }; - /** * Add templated variables to list of available metrics * @@ -202,10 +189,9 @@ function (angular, _) { metricList.push({ name: '$' + variable.name, templated: true - }) + }); }); - }; - + } ////////////////////////////// // VALIDATION @@ -213,24 +199,12 @@ function (angular, _) { function validateTarget(target) { var errs = {}; - + if (!target) { + errs = 'Not defined'; + } return errs; } }); }); - - -/** - * Convert multiple mettrics to array - * "{metric1,metcic2,...,metricN}" --> [metric1, metcic2,..., metricN] - * - * @param {string} metrics "{metric1,metcic2,...,metricN}" - * @return {Array} [metric1, metcic2,..., metricN] - */ -function splitMetrics(metrics) { - var remove_brackets_pattern = /^{|}$/g; - var metric_split_pattern = /,(?!\s)/g; - return metrics.replace(remove_brackets_pattern, '').split(metric_split_pattern) -} diff --git a/zabbix/zabbixAPIWrapper.js b/zabbix/zabbixAPIWrapper.js index eae7ea5c3..9e9fb15b6 100644 --- a/zabbix/zabbixAPIWrapper.js +++ b/zabbix/zabbixAPIWrapper.js @@ -7,23 +7,23 @@ function (angular, _) { var module = angular.module('grafana.services'); - module.service('zabbix', function($q, backendSrv) { - - /** - * Initialize API parameters. - */ - this.init = function(api_url, username, password) { - this.url = api_url; - this.username = username; - this.password = password; + module.factory('ZabbixAPI', function($q, backendSrv) { + + function ZabbixAPI(api_url, username, password, basicAuth, withCredentials) { + // Initialize API parameters. + this.url = api_url; + this.username = username; + this.password = password; + this.basicAuth = basicAuth; + this.withCredentials = withCredentials; } + var p = ZabbixAPI.prototype; ////////////////// // Core methods // ////////////////// - /** * Request data from Zabbix API * @@ -31,7 +31,7 @@ function (angular, _) { * @param {object} params method params * @return {object} data.result field or [] */ - this.performZabbixAPIRequest = function(method, params) { + p.performZabbixAPIRequest = function(method, params) { var options = { method: 'POST', headers: { @@ -47,6 +47,13 @@ function (angular, _) { } }; + if (this.basicAuth || this.withCredentials) { + options.withCredentials = true; + } + if (this.basicAuth) { + options.headers.Authorization = this.basicAuth; + } + var self = this; return backendSrv.datasourceRequest(options).then(function (response) { if (!response.data) { @@ -56,9 +63,9 @@ function (angular, _) { else if (response.data.error) { // Handle auth errors - if (response.data.error.data == "Session terminated, re-login, please." || - response.data.error.data == "Not authorised." || - response.data.error.data == "Not authorized") { + if (response.data.error.data === "Session terminated, re-login, please." || + response.data.error.data === "Not authorised." || + response.data.error.data === "Not authorized") { return self.performZabbixAPILogin().then(function (response) { self.auth = response; return self.performZabbixAPIRequest(method, params); @@ -69,13 +76,12 @@ function (angular, _) { }); }; - /** * Get authentication token. * * @return {string} auth token */ - this.performZabbixAPILogin = function() { + p.performZabbixAPILogin = function() { var options = { url : this.url, method : 'POST', @@ -91,6 +97,14 @@ function (angular, _) { }, }; + if (this.basicAuth || this.withCredentials) { + options.withCredentials = true; + } + if (this.basicAuth) { + options.headers = options.headers || {}; + options.headers.Authorization = this.basicAuth; + } + return backendSrv.datasourceRequest(options).then(function (result) { if (!result.data) { return null; @@ -99,13 +113,10 @@ function (angular, _) { }); }; - - ///////////////////////// // API method wrappers // ///////////////////////// - /** * Perform history query from Zabbix API * @@ -114,7 +125,7 @@ function (angular, _) { * @param {Number} end Time in seconds * @return {Array} Array of Zabbix history objects */ - this.getHistory = function(items, start, end) { + p.getHistory = function(items, start, end) { // Group items by value type var grouped_items = _.groupBy(items, 'value_type'); @@ -141,7 +152,6 @@ function (angular, _) { }); }; - /** * Perform trends query from Zabbix API * Use trends api extension from ZBXNEXT-1193 patch. @@ -151,7 +161,7 @@ function (angular, _) { * @param {Number} end Time in seconds * @return {Array} Array of Zabbix trend objects */ - this.getTrends = function(items, start, end) { + p.getTrends = function(items, start, end) { // Group items by value type var grouped_items = _.groupBy(items, 'value_type'); @@ -178,13 +188,12 @@ function (angular, _) { }); }; - /** * Get the list of host groups * * @return {array} array of Zabbix hostgroup objects */ - this.performHostGroupSuggestQuery = function() { + p.performHostGroupSuggestQuery = function() { var params = { output: ['name'], sortfield: 'name', @@ -197,14 +206,13 @@ function (angular, _) { return this.performZabbixAPIRequest('hostgroup.get', params); }; - /** * Get the list of hosts * * @param {array} groupids * @return {array} array of Zabbix host objects */ - this.performHostSuggestQuery = function(groupids) { + p.performHostSuggestQuery = function(groupids) { var params = { output: ['name', 'host'], sortfield: 'name', @@ -220,7 +228,6 @@ function (angular, _) { return this.performZabbixAPIRequest('host.get', params); }; - /** * Get the list of applications * @@ -228,7 +235,7 @@ function (angular, _) { * @param {array} groupids * @return {array} array of Zabbix application objects */ - this.performAppSuggestQuery = function(hostids, /* optional */ groupids) { + p.performAppSuggestQuery = function(hostids, /* optional */ groupids) { var params = { output: ['name'], sortfield: 'name' @@ -243,7 +250,6 @@ function (angular, _) { return this.performZabbixAPIRequest('application.get', params); }; - /** * Items request * @@ -252,7 +258,7 @@ function (angular, _) { * @param {string or Array} groupids /////////////////////////// * @return {string or Array} Array of Zabbix API item objects */ - this.performItemSuggestQuery = function(hostids, applicationids, /* optional */ groupids) { + p.performItemSuggestQuery = function(hostids, applicationids, /* optional */ groupids) { var params = { output: ['name', 'key_', 'value_type', 'delay'], sortfield: 'name', @@ -287,18 +293,17 @@ function (angular, _) { return this.performZabbixAPIRequest('item.get', params); }; - /** * Get groups by names * * @param {string or array} group group names * @return {array} array of Zabbix API hostgroup objects */ - this.getGroupByName = function (group) { + p.getGroupByName = function (group) { var params = { output: ['name'] }; - if (group != '*') { + if (group[0] !== '*') { params.filter = { name: group }; @@ -306,14 +311,13 @@ function (angular, _) { return this.performZabbixAPIRequest('hostgroup.get', params); }; - /** * Search group by name. * * @param {string} group group name * @return {array} groups */ - this.searchGroup = function (group) { + p.searchGroup = function (group) { var params = { output: ['name'], search: { @@ -324,18 +328,17 @@ function (angular, _) { return this.performZabbixAPIRequest('hostgroup.get', params); }; - /** * Get hosts by names * * @param {string or array} hostnames hosts names * @return {array} array of Zabbix API host objects */ - this.getHostByName = function (hostnames) { + p.getHostByName = function (hostnames) { var params = { output: ['host', 'name'] }; - if (hostnames != '*') { + if (hostnames[0] !== '*') { params.filter = { name: hostnames }; @@ -343,26 +346,24 @@ function (angular, _) { return this.performZabbixAPIRequest('host.get', params); }; - /** * Get applications by names * * @param {string or array} application applications names * @return {array} array of Zabbix API application objects */ - this.getAppByName = function (application) { + p.getAppByName = function (application) { var params = { output: ['name'] - } - if (application != '*') { + }; + if (application[0] !== '*') { params.filter = { name: application }; - }; + } return this.performZabbixAPIRequest('application.get', params); }; - /** * Get items belongs to passed groups, hosts and * applications @@ -372,11 +373,11 @@ function (angular, _) { * @param {string or array} apps * @return {array} array of Zabbix API item objects */ - this.itemFindQuery = function(groups, hosts, apps) { + p.itemFindQuery = function(groups, hosts, apps) { var promises = []; // Get hostids from names - if (hosts && hosts != '*') { + if (hosts && hosts[0] !== '*') { promises.push(this.getHostByName(hosts)); } // Get groupids from names @@ -391,18 +392,21 @@ function (angular, _) { var self = this; return $q.all(promises).then(function (results) { results = _.flatten(results); + var groupids; + var hostids; + var applicationids; if (groups) { - var groupids = _.map(_.filter(results, function (object) { + groupids = _.map(_.filter(results, function (object) { return object.groupid; }), 'groupid'); } - if (hosts && hosts != '*') { - var hostids = _.map(_.filter(results, function (object) { + if (hosts && hosts[0] !== '*') { + hostids = _.map(_.filter(results, function (object) { return object.hostid; }), 'hostid'); } if (apps) { - var applicationids = _.map(_.filter(results, function (object) { + applicationids = _.map(_.filter(results, function (object) { return object.applicationid; }), 'applicationid'); } @@ -411,7 +415,6 @@ function (angular, _) { }); }; - /** * Find applications belongs to passed groups and hosts * @@ -419,11 +422,11 @@ function (angular, _) { * @param {string or array} groups * @return {array} array of Zabbix API application objects */ - this.appFindQuery = function(hosts, groups) { + p.appFindQuery = function(hosts, groups) { var promises = []; // Get hostids from names - if (hosts && hosts != '*') { + if (hosts && hosts[0] !== '*') { promises.push(this.getHostByName(hosts)); } // Get groupids from names @@ -434,13 +437,15 @@ function (angular, _) { var self = this; return $q.all(promises).then(function (results) { results = _.flatten(results); + var groupids; + var hostids; if (groups) { - var groupids = _.map(_.filter(results, function (object) { + groupids = _.map(_.filter(results, function (object) { return object.groupid; }), 'groupid'); } - if (hosts && hosts != '*') { - var hostids = _.map(_.filter(results, function (object) { + if (hosts && hosts[0] !== '*') { + hostids = _.map(_.filter(results, function (object) { return object.hostid; }), 'hostid'); } @@ -449,14 +454,13 @@ function (angular, _) { }); }; - /** * Find hosts belongs to passed groups * * @param {string or array} groups * @return {array} array of Zabbix API host objects */ - this.hostFindQuery = function(groups) { + p.hostFindQuery = function(groups) { var self = this; return this.getGroupByName(groups).then(function (results) { results = _.flatten(results); @@ -468,28 +472,8 @@ function (angular, _) { }); }; - - /** - * Expand item parameters, for example: - * CPU $2 time ($3) --> CPU system time (avg1) - * - * @param item: zabbix api item object - * @return: expanded item name (string) - */ - this.expandItemName = function(item) { - var name = item.name; - var key = item.key_; - - // extract params from key: - // "system.cpu.util[,system,avg1]" --> ["", "system", "avg1"] - var key_params = key.substring(key.indexOf('[') + 1, key.lastIndexOf(']')).split(','); - - // replace item parameters - for (var i = key_params.length; i >= 1; i--) { - name = name.replace('$' + i, key_params[i - 1]); - }; - return name; - } + return ZabbixAPI; }); + });