From 7bc24fc2a00940dfd5a963fb0dba2657fd78aad8 Mon Sep 17 00:00:00 2001 From: Alexander Zobnin Date: Sat, 23 May 2015 17:08:12 +0300 Subject: [PATCH 01/49] Add templated items and apps search. --- zabbix/datasource.js | 165 ++++++++++++++++++++++++++++++++++++------- zabbix/queryCtrl.js | 10 ++- 2 files changed, 150 insertions(+), 25 deletions(-) diff --git a/zabbix/datasource.js b/zabbix/datasource.js index d1e4b04..298500f 100644 --- a/zabbix/datasource.js +++ b/zabbix/datasource.js @@ -183,7 +183,7 @@ function (angular, _, kbn) { // Get the list of hosts ZabbixAPIDatasource.prototype.performHostSuggestQuery = function(groupid) { var params = { - output: ['name'], + output: ['name', 'host'], sortfield: 'name' }; // Return only hosts in given group @@ -229,22 +229,65 @@ function (angular, _, kbn) { }; + ZabbixAPIDatasource.prototype.findZabbixGroup = function (group) { + var params = { + output: ['name'], + search: { + name: group + }, + searchWildcardsEnabled: true + } + return this.performZabbixAPIRequest('hostgroup.get', params); + }; + + + ZabbixAPIDatasource.prototype.findZabbixHost = function (hostname) { + var params = { + output: ['host', 'name'], + search: { + host: hostname, + name: hostname + }, + searchWildcardsEnabled: true, + searchByAny: true + } + return this.performZabbixAPIRequest('host.get', params); + }; + + + ZabbixAPIDatasource.prototype.findZabbixApp = function (application) { + var params = { + output: ['name'], + search: { + name: application + }, + searchWildcardsEnabled: true + } + return this.performZabbixAPIRequest('application.get', params); + }; + // For templated query ZabbixAPIDatasource.prototype.metricFindQuery = function (query) { var interpolated; try { - interpolated = templateSrv.replace(query); + interpolated = encodeURIComponent(templateSrv.replace(query)); } catch (err) { return $q.reject(err); } - var parts = interpolated.split('.'); - var template = { - 'group': parts[0], - 'host': parts[1], - 'item': parts[2] - }; + // Split query. Query structure: + // group.host.app.key + var parts = []; + _.each(query.split('.'), function (part) { + part = templateSrv.replace(part); + if (part[0] === '{') { + parts.push(part.slice(1, -1).split(',')); + } else { + parts.push(part); + } + }); + var template = _.object(['group', 'host', 'app', 'key'], parts) var params = { output: ['name'], @@ -255,23 +298,97 @@ function (angular, _, kbn) { } }; - var self = this; - return this.performZabbixAPIRequest('hostgroup.get', params) - .then(function (result) { - var groupid = null; - if (result.length && template.group) { - groupid = result[0].groupid; - } - return self.performHostSuggestQuery(groupid) - .then(function (result) { - return _.map(result, function (host) { - return { - text: host.name, - expandable: false - }; - }); + // Get items + if (parts.length === 4) { + var params = { + output: ['name', 'key_'], + sortfield: 'name', + }; + if (template.group != '*' && template.group) { + params.group = template.group; + } + if (template.host != '*' && template.host) { + params.host = template.host; + } + if (template.application != '*' && template.application) { + params.application = template.app; + } + return this.performZabbixAPIRequest('item.get', params) + .then(function (result) { + return _.map(result, function (item) { + return { + text: item.key_, + expandable: false + }; }); - }); + }); + } + // Get applications + else if (parts.length === 3) { + return this.appFindQuery(template); + } + // Return empty object + else { + var d = $q.defer(); + d.resolve({ data: [] }); + return d.promise; + } + }; + + ZabbixAPIDatasource.prototype.appFindQuery = function(template) { + var params = { + output: ['name'], + sortfield: 'name' + }; + var promises = []; + if (template.group != '*' && template.group) { + if (_.isArray(template.group)) { + _.each(template.group, function (group) { + promises.push(this.findZabbixGroup(group)); + }, this); + } else { + promises.push(this.findZabbixGroup(template.group)); + } + } + if (template.host != '*' && template.host) { + if (_.isArray(template.host)) { + _.each(template.host, function (host) { + promises.push(this.findZabbixHost(host)); + }, this); + } else { + promises.push(this.findZabbixHost(template.host)); + } + } + + var self = this; + return $q.all(promises).then(function (results) { + results = _.flatten(results); + var groupids = _.map(_.filter(results, function (object) { + return object.groupid; + }), 'groupid'); + var hostids = _.map(_.filter(results, function (object) { + return object.hostid; + }), 'hostid'); + + var params = { + output: ['name'] + } + if (hostids) { + params.hostids = hostids; + } else if (groupids) { + params.groupids = groupids; + } + + return self.performZabbixAPIRequest('application.get', params) + .then(function (result) { + return _.map(result, function (app) { + return { + text: app.name, + expandable: false + }; + }); + }); + }); }; diff --git a/zabbix/queryCtrl.js b/zabbix/queryCtrl.js index 2c511dc..9ea45ea 100644 --- a/zabbix/queryCtrl.js +++ b/zabbix/queryCtrl.js @@ -8,7 +8,7 @@ function (angular, _) { var module = angular.module('grafana.controllers'); var targetLetters = 'ABCDEFGHIJKLMNOPQRSTUVWXYZ'; - module.controller('ZabbixAPIQueryCtrl', function($scope) { + module.controller('ZabbixAPIQueryCtrl', function($scope, $sce, templateSrv) { $scope.init = function() { $scope.targetLetters = targetLetters; @@ -162,6 +162,14 @@ function (angular, _) { $scope.datasource.performHostSuggestQuery(groupid).then(function (series) { $scope.metric.hostList = series; + // Add templated variables + _.each(templateSrv.variables, function(variable) { + $scope.metric.hostList.push({ + 'name': '$' + variable.name, + 'hostid': 0 + }) + }); + if ($scope.target.host) { $scope.target.host = $scope.metric.hostList.filter(function (item, index, array) { // Find selected host in metric.hostList From 0e0f5907c4337bb3c36716d8ba2f7ff89f26684c Mon Sep 17 00:00:00 2001 From: Alexander Zobnin Date: Sat, 23 May 2015 17:45:07 +0300 Subject: [PATCH 02/49] Add itemFindQuery for search templated items. --- zabbix/datasource.js | 111 ++++++++++++++++++++++++++++++++----------- 1 file changed, 82 insertions(+), 29 deletions(-) diff --git a/zabbix/datasource.js b/zabbix/datasource.js index 298500f..6d7569c 100644 --- a/zabbix/datasource.js +++ b/zabbix/datasource.js @@ -282,6 +282,8 @@ function (angular, _, kbn) { _.each(query.split('.'), function (part) { part = templateSrv.replace(part); if (part[0] === '{') { + // Convert multiple mettrics to array + // "{metric1,metcic2,...,metricN}" --> [metric1, metcic2,..., metricN] parts.push(part.slice(1, -1).split(',')); } else { parts.push(part); @@ -300,28 +302,7 @@ function (angular, _, kbn) { // Get items if (parts.length === 4) { - var params = { - output: ['name', 'key_'], - sortfield: 'name', - }; - if (template.group != '*' && template.group) { - params.group = template.group; - } - if (template.host != '*' && template.host) { - params.host = template.host; - } - if (template.application != '*' && template.application) { - params.application = template.app; - } - return this.performZabbixAPIRequest('item.get', params) - .then(function (result) { - return _.map(result, function (item) { - return { - text: item.key_, - expandable: false - }; - }); - }); + return this.itemFindQuery(template); } // Get applications else if (parts.length === 3) { @@ -335,12 +316,11 @@ function (angular, _, kbn) { } }; - ZabbixAPIDatasource.prototype.appFindQuery = function(template) { - var params = { - output: ['name'], - sortfield: 'name' - }; + + ZabbixAPIDatasource.prototype.itemFindQuery = function(template) { var promises = []; + + // Get groupids from names if (template.group != '*' && template.group) { if (_.isArray(template.group)) { _.each(template.group, function (group) { @@ -350,6 +330,79 @@ function (angular, _, kbn) { promises.push(this.findZabbixGroup(template.group)); } } + // Get hostids from names + if (template.host != '*' && template.host) { + if (_.isArray(template.host)) { + _.each(template.host, function (host) { + promises.push(this.findZabbixHost(host)); + }, this); + } else { + promises.push(this.findZabbixHost(template.host)); + } + } + // Get applicationids from names + if (template.app != '*' && template.app) { + if (_.isArray(template.app)) { + _.each(template.app, function (app) { + promises.push(this.findZabbixHost(app)); + }, this); + } else { + promises.push(this.findZabbixHost(template.app)); + } + } + + var self = this; + return $q.all(promises).then(function (results) { + results = _.flatten(results); + var groupids = _.map(_.filter(results, function (object) { + return object.groupid; + }), 'groupid'); + var hostids = _.map(_.filter(results, function (object) { + return object.hostid; + }), 'hostid'); + var applicationids = _.map(_.filter(results, function (object) { + return object.applicationid; + }), 'applicationid'); + + var params = { + output: ['name', 'key_'], + sortfield: 'name', + }; + if (applicationids.length) { + params.applicationids = applicationids; + } + if (hostids.length) { + params.hostids = hostids; + } else if (groupids.length) { + params.groupids = groupids; + } + return self.performZabbixAPIRequest('item.get', params) + .then(function (result) { + return _.map(result, function (item) { + return { + text: item.key_, + expandable: false + }; + }); + }); + }); + }; + + + ZabbixAPIDatasource.prototype.appFindQuery = function(template) { + var promises = []; + + // Get groupids from names + if (template.group != '*' && template.group) { + if (_.isArray(template.group)) { + _.each(template.group, function (group) { + promises.push(this.findZabbixGroup(group)); + }, this); + } else { + promises.push(this.findZabbixGroup(template.group)); + } + } + // Get hostids from names if (template.host != '*' && template.host) { if (_.isArray(template.host)) { _.each(template.host, function (host) { @@ -373,9 +426,9 @@ function (angular, _, kbn) { var params = { output: ['name'] } - if (hostids) { + if (hostids.length) { params.hostids = hostids; - } else if (groupids) { + } else if (groupids.length) { params.groupids = groupids; } From 6c20946e7a41fb6fde19cb23001862ba0918f10c Mon Sep 17 00:00:00 2001 From: Alexander Zobnin Date: Sat, 23 May 2015 23:06:19 +0300 Subject: [PATCH 03/49] Add host and group templated variables query. --- zabbix/datasource.js | 110 +++++++++++++++++++++++++++++++++++++++---- 1 file changed, 102 insertions(+), 8 deletions(-) diff --git a/zabbix/datasource.js b/zabbix/datasource.js index 6d7569c..351eb4a 100644 --- a/zabbix/datasource.js +++ b/zabbix/datasource.js @@ -266,16 +266,22 @@ function (angular, _, kbn) { return this.performZabbixAPIRequest('application.get', params); }; + + ZabbixAPIDatasource.prototype.findZabbixItem = function (host, key) { + var params = { + output: ['name', 'key_', 'value_type'], + host: host, + search: { + key_: key + }, + searchWildcardsEnabled: true + } + return this.performZabbixAPIRequest('application.get', params); + }; + + // For templated query ZabbixAPIDatasource.prototype.metricFindQuery = function (query) { - var interpolated; - try { - interpolated = encodeURIComponent(templateSrv.replace(query)); - } - catch (err) { - return $q.reject(err); - } - // Split query. Query structure: // group.host.app.key var parts = []; @@ -308,6 +314,14 @@ function (angular, _, kbn) { else if (parts.length === 3) { return this.appFindQuery(template); } + // Get hosts + else if (parts.length === 2) { + return this.hostFindQuery(template); + } + // Get groups + else if (parts.length === 1) { + return this.groupFindQuery(template); + } // Return empty object else { var d = $q.defer(); @@ -445,6 +459,86 @@ function (angular, _, kbn) { }; + ZabbixAPIDatasource.prototype.hostFindQuery = function(template) { + var promises = []; + + // Get groupids from names + if (template.group != '*' && template.group) { + if (_.isArray(template.group)) { + _.each(template.group, function (group) { + promises.push(this.findZabbixGroup(group)); + }, this); + } else { + promises.push(this.findZabbixGroup(template.group)); + } + } + + var self = this; + return $q.all(promises).then(function (results) { + results = _.flatten(results); + var groupids = _.map(_.filter(results, function (object) { + return object.groupid; + }), 'groupid'); + + var params = { + output: ['name', 'host'] + } + if (groupids.length) { + params.groupids = groupids; + } + + return self.performZabbixAPIRequest('host.get', params) + .then(function (result) { + return _.map(result, function (host) { + return { + text: host.name, + expandable: false + }; + }); + }); + }); + }; + + + ZabbixAPIDatasource.prototype.groupFindQuery = function(template) { + var promises = []; + + // Get groupids from names + if (_.isArray(template.group)) { + _.each(template.group, function (group) { + promises.push(this.findZabbixGroup(group)); + }, this); + } else { + promises.push(this.findZabbixGroup(template.group)); + } + + var self = this; + return $q.all(promises).then(function (results) { + results = _.flatten(results); + var groupids = _.map(_.filter(results, function (object) { + return object.groupid; + }), 'groupid'); + + var params = { + output: ['name', 'host'] + } + if (groupids.length) { + params.groupids = groupids; + } + + return self.performZabbixAPIRequest('hostgroup.get', params) + .then(function (result) { + return _.map(result, function (hostgroup) { + return { + text: hostgroup.name, + expandable: false + }; + }); + }); + }); + }; + + 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); From 69d889b5c294597887296cbd2f7b84d40380292e Mon Sep 17 00:00:00 2001 From: Alexander Zobnin Date: Sun, 24 May 2015 23:53:13 +0300 Subject: [PATCH 04/49] Add templated variables to selection list in query editor. --- zabbix/partials/query.editor.html | 4 +- zabbix/queryCtrl.js | 69 ++++++++++++++++++++----------- 2 files changed, 47 insertions(+), 26 deletions(-) diff --git a/zabbix/partials/query.editor.html b/zabbix/partials/query.editor.html index a480c0a..ba6b5fb 100644 --- a/zabbix/partials/query.editor.html +++ b/zabbix/partials/query.editor.html @@ -106,8 +106,8 @@ class="tight-form-input input-medium" ng-change="selectItem()" ng-model="target.item" - bs-tooltip="target.expandedName.length > 30 ? target.expandedName : ''" - ng-options="item.expandedName for item in metric.itemList" > + bs-tooltip="target.name.length > 30 ? target.name : ''" + ng-options="item.name for item in metric.itemList" > Date: Mon, 25 May 2015 00:03:26 +0300 Subject: [PATCH 05/49] Fix item selection. --- zabbix/queryCtrl.js | 21 +++++++-------------- 1 file changed, 7 insertions(+), 14 deletions(-) diff --git a/zabbix/queryCtrl.js b/zabbix/queryCtrl.js index b6c99dc..2493624 100644 --- a/zabbix/queryCtrl.js +++ b/zabbix/queryCtrl.js @@ -189,13 +189,6 @@ function (angular, _) { $scope.datasource.performAppSuggestQuery(hostid).then(function (series) { $scope.metric.applicationList = $scope.metric.applicationList.concat(series); - // Add templated variables - _.each(templateSrv.variables, function(variable) { - $scope.metric.applicationList.push({ - name: '$' + variable.name - }) - }); - if ($scope.target.application) { $scope.target.application = $scope.metric.applicationList.filter(function (item, index, array) { // Find selected application in metric.hostList @@ -225,13 +218,13 @@ function (angular, _) { } }); }); - } - - if ($scope.target.item) { - $scope.target.item = $scope.metric.itemList.filter(function (item, index, array) { - // Find selected item in metric.hostList - return (item.name == $scope.target.item.name); - }).pop(); + } else { + if ($scope.target.item) { + $scope.target.item = $scope.metric.itemList.filter(function (item, index, array) { + // Find selected item in metric.hostList + return (item.name == $scope.target.item.name); + }).pop(); + } } }; From 0213512e3bcec375772a9e6bbd3b7e7bfcb5452a Mon Sep 17 00:00:00 2001 From: Alexander Zobnin Date: Mon, 25 May 2015 01:26:09 +0300 Subject: [PATCH 06/49] Add templated graphs. --- zabbix/datasource.js | 53 +++++++++++++++++++++++++++++++++++--------- zabbix/queryCtrl.js | 6 +++++ 2 files changed, 49 insertions(+), 10 deletions(-) diff --git a/zabbix/datasource.js b/zabbix/datasource.js index 351eb4a..3060362 100644 --- a/zabbix/datasource.js +++ b/zabbix/datasource.js @@ -31,25 +31,57 @@ function (angular, _, kbn) { // Create request for each target var promises = _.map(options.targets, function(target) { - // Remove undefined and hidden targets if (target.hide || !target.item) { return []; } - // Perform request and then handle result - return this.performTimeSeriesQuery(target.item, from, to).then(_.partial( - this.handleZabbixAPIResponse, target)); + if (!target.item.templated) { + + // Perform request and then handle result + return this.performTimeSeriesQuery(target.item, from, to).then(_.partial( + this.handleZabbixAPIResponse, target.alias)); + } else { + // Handle templated target + + var item_key = templateSrv.replace(target.item.name); + var hostname = templateSrv.replace(target.host.name); + + var keys = []; + if (item_key[0] === '{') { + // Convert multiple mettrics to array + // "{metric1,metcic2,...,metricN}" --> [metric1, metcic2,..., metricN] + keys= item_key.slice(1, -1).split(','); + } else { + keys.push(item_key); + } + + var self = this; + + var results = []; + _.each(keys, function (key) { + results.push(this.findZabbixItem(hostname, key).then(function (items) { + if (items.length) { + var item = items[0]; + return self.performTimeSeriesQuery(item, from, to).then(_.partial( + self.handleZabbixAPIResponse, item.name)); + } else { + return []; + } + })); + }, this); + return results; + } }, this); - return $q.all(promises).then(function(results) { + return $q.all(_.flatten(promises)).then(function(results) { return { data: _.flatten(results) }; }); }; // Request data from Zabbix API - ZabbixAPIDatasource.prototype.handleZabbixAPIResponse = function(target, response) { + ZabbixAPIDatasource.prototype.handleZabbixAPIResponse = function(alias, response) { /** * Response should be in the format: * data: [ @@ -65,7 +97,7 @@ function (angular, _, kbn) { */ var series = { - target: target.alias, + target: alias, datapoints: _.map(response, function (p) { // Value must be a number for properly work var value = Number(p.value); @@ -274,9 +306,10 @@ function (angular, _, kbn) { search: { key_: key }, - searchWildcardsEnabled: true + searchWildcardsEnabled: true, + searchByAny: true } - return this.performZabbixAPIRequest('application.get', params); + return this.performZabbixAPIRequest('item.get', params); }; @@ -361,7 +394,7 @@ function (angular, _, kbn) { promises.push(this.findZabbixHost(app)); }, this); } else { - promises.push(this.findZabbixHost(template.app)); + promises.push(this.findZabbixApp(template.app)); } } diff --git a/zabbix/queryCtrl.js b/zabbix/queryCtrl.js index 2493624..a3622a9 100644 --- a/zabbix/queryCtrl.js +++ b/zabbix/queryCtrl.js @@ -217,6 +217,12 @@ function (angular, _) { item.name = expandItemName(item); } }); + if ($scope.target.item) { + $scope.target.item = $scope.metric.itemList.filter(function (item, index, array) { + // Find selected item in metric.hostList + return (item.name == $scope.target.item.name); + }).pop(); + } }); } else { if ($scope.target.item) { From 4d89b82de189b85931566ddab581de1646cee7f2 Mon Sep 17 00:00:00 2001 From: Alexander Zobnin Date: Mon, 25 May 2015 01:49:33 +0300 Subject: [PATCH 07/49] Fix templated items search. --- zabbix/datasource.js | 24 ++++++++++++------------ 1 file changed, 12 insertions(+), 12 deletions(-) diff --git a/zabbix/datasource.js b/zabbix/datasource.js index 3060362..442bac5 100644 --- a/zabbix/datasource.js +++ b/zabbix/datasource.js @@ -367,16 +367,6 @@ function (angular, _, kbn) { ZabbixAPIDatasource.prototype.itemFindQuery = function(template) { var promises = []; - // Get groupids from names - if (template.group != '*' && template.group) { - if (_.isArray(template.group)) { - _.each(template.group, function (group) { - promises.push(this.findZabbixGroup(group)); - }, this); - } else { - promises.push(this.findZabbixGroup(template.group)); - } - } // Get hostids from names if (template.host != '*' && template.host) { if (_.isArray(template.host)) { @@ -387,11 +377,21 @@ function (angular, _, kbn) { promises.push(this.findZabbixHost(template.host)); } } + // Get groupids from names + else if (template.group != '*' && template.group) { + if (_.isArray(template.group)) { + _.each(template.group, function (group) { + promises.push(this.findZabbixGroup(group)); + }, this); + } else { + promises.push(this.findZabbixGroup(template.group)); + } + } // Get applicationids from names - if (template.app != '*' && template.app) { + if (template.app != '*' && template.app && !_.isArray(template.app)) { if (_.isArray(template.app)) { _.each(template.app, function (app) { - promises.push(this.findZabbixHost(app)); + promises.push(this.findZabbixApp(app)); }, this); } else { promises.push(this.findZabbixApp(template.app)); From 974d49b0b9f38712db8dd157180e914d75299410 Mon Sep 17 00:00:00 2001 From: Alexander Zobnin Date: Tue, 26 May 2015 22:48:02 +0300 Subject: [PATCH 08/49] Handle templated queries. --- zabbix/datasource.js | 47 ++++++++++++++++++++++++++++++-------------- 1 file changed, 32 insertions(+), 15 deletions(-) diff --git a/zabbix/datasource.js b/zabbix/datasource.js index 442bac5..69fdf3c 100644 --- a/zabbix/datasource.js +++ b/zabbix/datasource.js @@ -47,30 +47,24 @@ function (angular, _, kbn) { var item_key = templateSrv.replace(target.item.name); var hostname = templateSrv.replace(target.host.name); - var keys = []; - if (item_key[0] === '{') { - // Convert multiple mettrics to array - // "{metric1,metcic2,...,metricN}" --> [metric1, metcic2,..., metricN] - keys= item_key.slice(1, -1).split(','); - } else { - keys.push(item_key); - } + // extract all keys + var pattern = /([\w.]+(?:\[[^\[]*\])|[\w.]+)/g; + var keys = item_key.match(pattern); var self = this; - var results = []; - _.each(keys, function (key) { - results.push(this.findZabbixItem(hostname, key).then(function (items) { + return _.map(keys, function (key) { + return this.findZabbixItem(hostname, key).then(function (items) { if (items.length) { var item = items[0]; + var itemname = expandItemName(item); return self.performTimeSeriesQuery(item, from, to).then(_.partial( - self.handleZabbixAPIResponse, item.name)); + self.handleZabbixAPIResponse, itemname)); } else { return []; } - })); + }); }, this); - return results; } }, this); @@ -358,7 +352,7 @@ function (angular, _, kbn) { // Return empty object else { var d = $q.defer(); - d.resolve({ data: [] }); + d.resolve([]); return d.promise; } }; @@ -618,3 +612,26 @@ function (angular, _, kbn) { return ZabbixAPIDatasource; }); }); + + +/** + * Expand item parameters, for example: + * CPU $2 time ($3) --> CPU system time (avg1) + * + * @param item: zabbix api item object + * @return: expanded item name (string) + */ +function expandItemName(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; +}; \ No newline at end of file From dace394060d42a5103cfe8370a1ce57597503469 Mon Sep 17 00:00:00 2001 From: Alexander Zobnin Date: Wed, 27 May 2015 17:00:25 +0300 Subject: [PATCH 09/49] Refactoring of query() method. --- zabbix/datasource.js | 13 ++++--------- 1 file changed, 4 insertions(+), 9 deletions(-) diff --git a/zabbix/datasource.js b/zabbix/datasource.js index 69fdf3c..bc541c5 100644 --- a/zabbix/datasource.js +++ b/zabbix/datasource.js @@ -51,18 +51,13 @@ function (angular, _, kbn) { var pattern = /([\w.]+(?:\[[^\[]*\])|[\w.]+)/g; var keys = item_key.match(pattern); - var self = this; - return _.map(keys, function (key) { + var self = this; return this.findZabbixItem(hostname, key).then(function (items) { - if (items.length) { - var item = items[0]; - var itemname = expandItemName(item); + return $q.all(_.map(items, function (item) { return self.performTimeSeriesQuery(item, from, to).then(_.partial( - self.handleZabbixAPIResponse, itemname)); - } else { - return []; - } + self.handleZabbixAPIResponse, expandItemName(item))); + }, this)); }); }, this); } From 2c58bc4ba9e5f8ae0851051873625a247eda7ce8 Mon Sep 17 00:00:00 2001 From: Alexander Zobnin Date: Thu, 28 May 2015 21:04:33 +0300 Subject: [PATCH 10/49] One request for each item's value type. Reduce api requests. --- zabbix/datasource.js | 112 +++++++++++++++++++++++++------------------ 1 file changed, 66 insertions(+), 46 deletions(-) diff --git a/zabbix/datasource.js b/zabbix/datasource.js index bc541c5..602de26 100644 --- a/zabbix/datasource.js +++ b/zabbix/datasource.js @@ -47,30 +47,69 @@ function (angular, _, kbn) { var item_key = templateSrv.replace(target.item.name); var hostname = templateSrv.replace(target.host.name); - // extract all keys - var pattern = /([\w.]+(?:\[[^\[]*\])|[\w.]+)/g; - var keys = item_key.match(pattern); + // Extract zabbix hosts + var host_pattern = /([\w\.\s]+)/g; + var hosts = hostname.match(host_pattern); - return _.map(keys, function (key) { - var self = this; - return this.findZabbixItem(hostname, key).then(function (items) { - return $q.all(_.map(items, function (item) { - return self.performTimeSeriesQuery(item, from, to).then(_.partial( - self.handleZabbixAPIResponse, expandItemName(item))); - }, this)); + // Extract zabbix keys + var key_pattern = /([\w\.]+(?:\[[^\[]*\])|[\w\.]+)/g; + var keys = item_key.match(key_pattern); + + // Find items by keys and perform queries + var self = this; + return $q.all(_.map(hosts, function (hostname) { + return $q.all(_.map(keys, function (key) { + return this.findZabbixItem(hostname, key); + }, this)); + }, this)).then(function (items) { + items = _.flatten(items); + return self.performTimeSeriesQuery(items, from, to) + .then(_.partial(self.handleZabbixAPIResponse, items)); }); - }, this); } }, this); - return $q.all(_.flatten(promises)).then(function(results) { + return $q.all(_.flatten(promises)).then(function (results) { return { data: _.flatten(results) }; }); }; + /** + * Perform time series query to Zabbix API + * + * @param items: array of zabbix api item objects + */ + ZabbixAPIDatasource.prototype.performTimeSeriesQuery = function(items, start, end) { + // Group items by value type + var grouped_items = _.groupBy(items, 'value_type'); + + // Perform request for each value type + return $q.all(_.map(grouped_items, function (items, value_type) { + var itemids = _.map(items, 'itemid'); + var params = { + output: 'extend', + history: value_type, + itemids: itemids, + sortfield: 'clock', + sortorder: 'ASC', + time_from: start + }; + + // Relative queries (e.g. last hour) don't include an end time + if (end) { + params.time_till = end; + } + + return this.performZabbixAPIRequest('history.get', params); + }, this)).then(function (results) { + return _.flatten(results); + }); + }; + + // Request data from Zabbix API - ZabbixAPIDatasource.prototype.handleZabbixAPIResponse = function(alias, response) { + ZabbixAPIDatasource.prototype.handleZabbixAPIResponse = function(items, response) { /** * Response should be in the format: * data: [ @@ -85,16 +124,21 @@ function (angular, _, kbn) { * ] */ - var series = { - target: alias, - datapoints: _.map(response, function (p) { - // Value must be a number for properly work - var value = Number(p.value); - return [value, p.clock * 1000]; - }) - }; + // Group items and history by itemid + var indexed_items = _.indexBy(items, 'itemid'); + var grouped_history = _.groupBy(response, 'itemid'); - return $q.when(series); + return $q.when(_.map(grouped_history, function (history, itemid) { + var series = { + target: expandItemName(indexed_items[itemid]), + datapoints: _.map(history, function (p) { + // Value must be a number for properly work + var value = Number(p.value); + return [value, p.clock * 1000]; + }) + }; + return series; + })); }; @@ -139,30 +183,6 @@ function (angular, _, kbn) { }; - /** - * Perform time series query to Zabbix API - * - * @param items: array of zabbix api item objects - */ - ZabbixAPIDatasource.prototype.performTimeSeriesQuery = function(items, start, end) { - var params = { - output: 'extend', - history: items.value_type, - itemids: items.itemid, - sortfield: 'clock', - sortorder: 'ASC', - time_from: start - }; - - // Relative queries (e.g. last hour) don't include an end time - if (end) { - params.time_till = end; - } - - return this.performZabbixAPIRequest('history.get', params); - }; - - // Get authentication token ZabbixAPIDatasource.prototype.performZabbixAPILogin = function() { var options = { From feb729de525fcd21270c160590bd47e20535cf7f Mon Sep 17 00:00:00 2001 From: Alexander Zobnin Date: Thu, 28 May 2015 21:12:40 +0300 Subject: [PATCH 11/49] Add metrics limit per panel. --- zabbix/datasource.js | 31 +++++++++++++++++++------------ 1 file changed, 19 insertions(+), 12 deletions(-) diff --git a/zabbix/datasource.js b/zabbix/datasource.js index 602de26..c9de3e7 100644 --- a/zabbix/datasource.js +++ b/zabbix/datasource.js @@ -19,6 +19,9 @@ function (angular, _, kbn) { this.username = datasource.meta.username; this.password = datasource.meta.password; + // Limit metrics per panel + this.limitmetrics = datasource.meta.limitmetrics || 20; + // For testing this.ds = datasource; } @@ -55,17 +58,21 @@ function (angular, _, kbn) { var key_pattern = /([\w\.]+(?:\[[^\[]*\])|[\w\.]+)/g; var keys = item_key.match(key_pattern); - // Find items by keys and perform queries - var self = this; - return $q.all(_.map(hosts, function (hostname) { - return $q.all(_.map(keys, function (key) { - return this.findZabbixItem(hostname, key); - }, this)); - }, this)).then(function (items) { - items = _.flatten(items); - return self.performTimeSeriesQuery(items, from, to) - .then(_.partial(self.handleZabbixAPIResponse, items)); - }); + if (keys.length < this.limitmetrics) { + // Find items by keys and perform queries + var self = this; + return $q.all(_.map(hosts, function (hostname) { + return $q.all(_.map(keys, function (key) { + return this.findZabbixItem(hostname, key); + }, this)); + }, this)).then(function (items) { + items = _.flatten(items); + return self.performTimeSeriesQuery(items, from, to) + .then(_.partial(self.handleZabbixAPIResponse, items)); + }); + } else { + return []; + } } }, this); @@ -108,7 +115,7 @@ function (angular, _, kbn) { }; - // Request data from Zabbix API + // Convert Zabbix API data to Grafana format ZabbixAPIDatasource.prototype.handleZabbixAPIResponse = function(items, response) { /** * Response should be in the format: From 149a2bd85123a74e855944fa88f9d01b76dbffe1 Mon Sep 17 00:00:00 2001 From: Alexander Zobnin Date: Thu, 28 May 2015 21:46:20 +0300 Subject: [PATCH 12/49] Templated items selection by item name. --- zabbix/datasource.js | 34 +++++++++++++++++----------------- 1 file changed, 17 insertions(+), 17 deletions(-) diff --git a/zabbix/datasource.js b/zabbix/datasource.js index c9de3e7..9585a8b 100644 --- a/zabbix/datasource.js +++ b/zabbix/datasource.js @@ -47,23 +47,23 @@ function (angular, _, kbn) { } else { // Handle templated target - var item_key = templateSrv.replace(target.item.name); + var itemname = templateSrv.replace(target.item.name); var hostname = templateSrv.replace(target.host.name); // Extract zabbix hosts var host_pattern = /([\w\.\s]+)/g; var hosts = hostname.match(host_pattern); - // Extract zabbix keys - var key_pattern = /([\w\.]+(?:\[[^\[]*\])|[\w\.]+)/g; - var keys = item_key.match(key_pattern); + // Extract item names + var itemname_pattern = /([^{},]+)/g; + var itemnames = itemname.match(itemname_pattern); - if (keys.length < this.limitmetrics) { - // Find items by keys and perform queries + if (itemnames.length < this.limitmetrics) { + // Find items by item names and perform queries var self = this; return $q.all(_.map(hosts, function (hostname) { - return $q.all(_.map(keys, function (key) { - return this.findZabbixItem(hostname, key); + return $q.all(_.map(itemnames, function (name) { + return this.findZabbixItem(hostname, name); }, this)); }, this)).then(function (items) { items = _.flatten(items); @@ -315,17 +315,16 @@ function (angular, _, kbn) { }; - ZabbixAPIDatasource.prototype.findZabbixItem = function (host, key) { + ZabbixAPIDatasource.prototype.findZabbixItem = function (host, itemname) { var params = { output: ['name', 'key_', 'value_type'], - host: host, - search: { - key_: key - }, - searchWildcardsEnabled: true, - searchByAny: true + host: host } - return this.performZabbixAPIRequest('item.get', params); + return this.performZabbixAPIRequest('item.get', params).then(function (items) { + return _.filter(items, function (item) { + return expandItemName(item) == itemname; + }); + }); }; @@ -443,7 +442,8 @@ function (angular, _, kbn) { .then(function (result) { return _.map(result, function (item) { return { - text: item.key_, + //text: item.key_, + text: expandItemName(item), expandable: false }; }); From a1fb1f75934f0498accd4913240b994a0a2787f9 Mon Sep 17 00:00:00 2001 From: Alexander Zobnin Date: Thu, 28 May 2015 23:05:02 +0300 Subject: [PATCH 13/49] Reduce number of api requests generated by query() method. --- zabbix/datasource.js | 25 +++++++++++++------------ 1 file changed, 13 insertions(+), 12 deletions(-) diff --git a/zabbix/datasource.js b/zabbix/datasource.js index 9585a8b..0fac6cf 100644 --- a/zabbix/datasource.js +++ b/zabbix/datasource.js @@ -62,9 +62,7 @@ function (angular, _, kbn) { // Find items by item names and perform queries var self = this; return $q.all(_.map(hosts, function (hostname) { - return $q.all(_.map(itemnames, function (name) { - return this.findZabbixItem(hostname, name); - }, this)); + return this.findZabbixItem(hostname, itemnames); }, this)).then(function (items) { items = _.flatten(items); return self.performTimeSeriesQuery(items, from, to) @@ -309,20 +307,20 @@ function (angular, _, kbn) { search: { name: application }, - searchWildcardsEnabled: true + searchWildcardsEnabled: true, } return this.performZabbixAPIRequest('application.get', params); }; - ZabbixAPIDatasource.prototype.findZabbixItem = function (host, itemname) { + ZabbixAPIDatasource.prototype.findZabbixItem = function (host, itemnames) { var params = { output: ['name', 'key_', 'value_type'], host: host } return this.performZabbixAPIRequest('item.get', params).then(function (items) { return _.filter(items, function (item) { - return expandItemName(item) == itemname; + return _.contains(itemnames, expandItemName(item)); }); }); }; @@ -383,7 +381,7 @@ function (angular, _, kbn) { var promises = []; // Get hostids from names - if (template.host != '*' && template.host) { + if (template.host && template.host != '*') { if (_.isArray(template.host)) { _.each(template.host, function (host) { promises.push(this.findZabbixHost(host)); @@ -393,7 +391,7 @@ function (angular, _, kbn) { } } // Get groupids from names - else if (template.group != '*' && template.group) { + else if (template.group && template.group != '*') { if (_.isArray(template.group)) { _.each(template.group, function (group) { promises.push(this.findZabbixGroup(group)); @@ -403,7 +401,7 @@ function (angular, _, kbn) { } } // Get applicationids from names - if (template.app != '*' && template.app && !_.isArray(template.app)) { + if (template.app && template.app != '*') { if (_.isArray(template.app)) { _.each(template.app, function (app) { promises.push(this.findZabbixApp(app)); @@ -487,7 +485,8 @@ function (angular, _, kbn) { }), 'hostid'); var params = { - output: ['name'] + output: ['name'], + sortfield: 'name' } if (hostids.length) { params.hostids = hostids; @@ -530,7 +529,8 @@ function (angular, _, kbn) { }), 'groupid'); var params = { - output: ['name', 'host'] + output: ['name', 'host'], + sortfield: 'name' } if (groupids.length) { params.groupids = groupids; @@ -569,7 +569,8 @@ function (angular, _, kbn) { }), 'groupid'); var params = { - output: ['name', 'host'] + output: ['name', 'host'], + sortfield: 'name' } if (groupids.length) { params.groupids = groupids; From 8c59c87b22b0a875e7fd873104343aa1191dc68e Mon Sep 17 00:00:00 2001 From: Alexander Zobnin Date: Thu, 28 May 2015 23:27:07 +0300 Subject: [PATCH 14/49] Fix not-templated metric request. --- zabbix/datasource.js | 10 ++++++++-- 1 file changed, 8 insertions(+), 2 deletions(-) diff --git a/zabbix/datasource.js b/zabbix/datasource.js index 0fac6cf..7c184ec 100644 --- a/zabbix/datasource.js +++ b/zabbix/datasource.js @@ -42,8 +42,14 @@ function (angular, _, kbn) { if (!target.item.templated) { // Perform request and then handle result - return this.performTimeSeriesQuery(target.item, from, to).then(_.partial( - this.handleZabbixAPIResponse, target.alias)); + var item = [target.item]; + var alias = [{ + itemid: target.item.itemid, + key_: '', + name: target.alias + }]; + return this.performTimeSeriesQuery(item, from, to).then(_.partial( + this.handleZabbixAPIResponse, alias)); } else { // Handle templated target From d7ff952761512ed05fdc010247ea59d5b304a500 Mon Sep 17 00:00:00 2001 From: Alexander Zobnin Date: Thu, 28 May 2015 23:28:28 +0300 Subject: [PATCH 15/49] Add limitmetrics configuration parameter to plugin.json --- zabbix/plugin.json | 2 ++ 1 file changed, 2 insertions(+) diff --git a/zabbix/plugin.json b/zabbix/plugin.json index 8fd4ad4..460ffda 100644 --- a/zabbix/plugin.json +++ b/zabbix/plugin.json @@ -16,6 +16,8 @@ "username": "guest", "password": "", + "limitmetrics": 20, + "metrics": true, "annotations": true } From ac848710859e5f47048a6cdaece0b27f86f92570 Mon Sep 17 00:00:00 2001 From: Alexander Zobnin Date: Fri, 29 May 2015 00:08:07 +0300 Subject: [PATCH 16/49] Fix panel error when no items returned by templated variable query. --- zabbix/datasource.js | 71 ++++++++------------------------------------ 1 file changed, 12 insertions(+), 59 deletions(-) diff --git a/zabbix/datasource.js b/zabbix/datasource.js index 7c184ec..3faf918 100644 --- a/zabbix/datasource.js +++ b/zabbix/datasource.js @@ -64,7 +64,7 @@ function (angular, _, kbn) { var itemname_pattern = /([^{},]+)/g; var itemnames = itemname.match(itemname_pattern); - if (itemnames.length < this.limitmetrics) { + if (itemnames && (itemnames.length < this.limitmetrics)) { // Find items by item names and perform queries var self = this; return $q.all(_.map(hosts, function (hostname) { @@ -388,33 +388,15 @@ function (angular, _, kbn) { // Get hostids from names if (template.host && template.host != '*') { - if (_.isArray(template.host)) { - _.each(template.host, function (host) { - promises.push(this.findZabbixHost(host)); - }, this); - } else { - promises.push(this.findZabbixHost(template.host)); - } + promises.push(this.findZabbixHost(template.host)); } // Get groupids from names else if (template.group && template.group != '*') { - if (_.isArray(template.group)) { - _.each(template.group, function (group) { - promises.push(this.findZabbixGroup(group)); - }, this); - } else { - promises.push(this.findZabbixGroup(template.group)); - } + promises.push(this.findZabbixGroup(template.group)); } // Get applicationids from names if (template.app && template.app != '*') { - if (_.isArray(template.app)) { - _.each(template.app, function (app) { - promises.push(this.findZabbixApp(app)); - }, this); - } else { - promises.push(this.findZabbixApp(template.app)); - } + promises.push(this.findZabbixApp(template.app)); } var self = this; @@ -460,24 +442,12 @@ function (angular, _, kbn) { var promises = []; // Get groupids from names - if (template.group != '*' && template.group) { - if (_.isArray(template.group)) { - _.each(template.group, function (group) { - promises.push(this.findZabbixGroup(group)); - }, this); - } else { - promises.push(this.findZabbixGroup(template.group)); - } + if (template.group && template.group != '*') { + promises.push(this.findZabbixGroup(template.group)); } // Get hostids from names - if (template.host != '*' && template.host) { - if (_.isArray(template.host)) { - _.each(template.host, function (host) { - promises.push(this.findZabbixHost(host)); - }, this); - } else { - promises.push(this.findZabbixHost(template.host)); - } + if (template.host && template.host != '*') { + promises.push(this.findZabbixHost(template.host)); } var self = this; @@ -517,18 +487,12 @@ function (angular, _, kbn) { var promises = []; // Get groupids from names - if (template.group != '*' && template.group) { - if (_.isArray(template.group)) { - _.each(template.group, function (group) { - promises.push(this.findZabbixGroup(group)); - }, this); - } else { - promises.push(this.findZabbixGroup(template.group)); - } + if (template.group && template.group != '*') { + promises.push(this.findZabbixGroup(template.group)); } var self = this; - return $q.all(promises).then(function (results) { + return this.findZabbixGroup(template.group).then(function (results) { results = _.flatten(results); var groupids = _.map(_.filter(results, function (object) { return object.groupid; @@ -556,19 +520,8 @@ function (angular, _, kbn) { ZabbixAPIDatasource.prototype.groupFindQuery = function(template) { - var promises = []; - - // Get groupids from names - if (_.isArray(template.group)) { - _.each(template.group, function (group) { - promises.push(this.findZabbixGroup(group)); - }, this); - } else { - promises.push(this.findZabbixGroup(template.group)); - } - var self = this; - return $q.all(promises).then(function (results) { + return this.findZabbixGroup(template.group).then(function (results) { results = _.flatten(results); var groupids = _.map(_.filter(results, function (object) { return object.groupid; From 936c19310e9b03748c6900684fb3daebed9bf01e Mon Sep 17 00:00:00 2001 From: Alexander Zobnin Date: Fri, 29 May 2015 19:33:47 +0300 Subject: [PATCH 17/49] Fix getting item alias from name for non-templated items. --- zabbix/queryCtrl.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/zabbix/queryCtrl.js b/zabbix/queryCtrl.js index a3622a9..f1c37fd 100644 --- a/zabbix/queryCtrl.js +++ b/zabbix/queryCtrl.js @@ -43,7 +43,7 @@ function (angular, _) { // Take alias from item name by default function setItemAlias() { if (!$scope.target.alias && $scope.target.item) { - $scope.target.alias = $scope.target.item.expandedName; + $scope.target.alias = expandItemName($scope.target.item); } }; From bf6c573883707dd3049fee6d2eb3e2e3be89ed09 Mon Sep 17 00:00:00 2001 From: Alexander Zobnin Date: Fri, 29 May 2015 20:12:50 +0300 Subject: [PATCH 18/49] Fix multiple groups hosts search. --- zabbix/datasource.js | 37 +++++++++++-------------------------- zabbix/queryCtrl.js | 8 ++++---- 2 files changed, 15 insertions(+), 30 deletions(-) diff --git a/zabbix/datasource.js b/zabbix/datasource.js index 3faf918..8dbd269 100644 --- a/zabbix/datasource.js +++ b/zabbix/datasource.js @@ -287,6 +287,7 @@ function (angular, _, kbn) { search: { name: group }, + searchByAny: true, searchWildcardsEnabled: true } return this.performZabbixAPIRequest('hostgroup.get', params); @@ -300,8 +301,8 @@ function (angular, _, kbn) { host: hostname, name: hostname }, - searchWildcardsEnabled: true, - searchByAny: true + searchByAny: true, + searchWildcardsEnabled: true } return this.performZabbixAPIRequest('host.get', params); }; @@ -313,6 +314,7 @@ function (angular, _, kbn) { search: { name: application }, + searchByAny: true, searchWildcardsEnabled: true, } return this.performZabbixAPIRequest('application.get', params); @@ -520,30 +522,13 @@ function (angular, _, kbn) { ZabbixAPIDatasource.prototype.groupFindQuery = function(template) { - var self = this; - return this.findZabbixGroup(template.group).then(function (results) { - results = _.flatten(results); - var groupids = _.map(_.filter(results, function (object) { - return object.groupid; - }), 'groupid'); - - var params = { - output: ['name', 'host'], - sortfield: 'name' - } - if (groupids.length) { - params.groupids = groupids; - } - - return self.performZabbixAPIRequest('hostgroup.get', params) - .then(function (result) { - return _.map(result, function (hostgroup) { - return { - text: hostgroup.name, - expandable: false - }; - }); - }); + return this.performHostGroupSuggestQuery().then(function (result) { + return _.map(result, function (hostgroup) { + return { + text: hostgroup.name, + expandable: false + }; + }); }); }; diff --git a/zabbix/queryCtrl.js b/zabbix/queryCtrl.js index f1c37fd..ab81600 100644 --- a/zabbix/queryCtrl.js +++ b/zabbix/queryCtrl.js @@ -152,7 +152,7 @@ function (angular, _) { if ($scope.target.hostGroup) { $scope.target.hostGroup = $scope.metric.hostGroupList.filter(function (item, index, array) { // Find selected host in metric.hostList - return (item.name == $scope.target.hostGroup.name); + return item.name == $scope.target.hostGroup.name; }).pop(); } }); @@ -172,7 +172,7 @@ function (angular, _) { if ($scope.target.host) { $scope.target.host = $scope.metric.hostList.filter(function (item, index, array) { // Find selected host in metric.hostList - return (item.name == $scope.target.host.name); + return item.name == $scope.target.host.name; }).pop(); } }); @@ -192,7 +192,7 @@ function (angular, _) { if ($scope.target.application) { $scope.target.application = $scope.metric.applicationList.filter(function (item, index, array) { // Find selected application in metric.hostList - return (item.name == $scope.target.application.name); + return item.name == $scope.target.application.name; }).pop(); } }); @@ -220,7 +220,7 @@ function (angular, _) { if ($scope.target.item) { $scope.target.item = $scope.metric.itemList.filter(function (item, index, array) { // Find selected item in metric.hostList - return (item.name == $scope.target.item.name); + return item.name == $scope.target.item.name; }).pop(); } }); From fe710e7d1ad52c268558ff4c39c435eb6ee2b489 Mon Sep 17 00:00:00 2001 From: Alexander Zobnin Date: Fri, 29 May 2015 20:48:10 +0300 Subject: [PATCH 19/49] Improve templated variables search, reduced the number of requests to api. --- zabbix/datasource.js | 102 ++++++++++++++++--------------------------- 1 file changed, 38 insertions(+), 64 deletions(-) diff --git a/zabbix/datasource.js b/zabbix/datasource.js index 8dbd269..ba4f11e 100644 --- a/zabbix/datasource.js +++ b/zabbix/datasource.js @@ -233,48 +233,60 @@ function (angular, _, kbn) { // Get the list of hosts - ZabbixAPIDatasource.prototype.performHostSuggestQuery = function(groupid) { + ZabbixAPIDatasource.prototype.performHostSuggestQuery = function(groupids) { var params = { output: ['name', 'host'], - sortfield: 'name' + sortfield: 'name', + // Return only hosts that have items with numeric type of information. + with_simple_graph_items: true }; // Return only hosts in given group - if (groupid) { - params.groupids = groupid; + if (groupids) { + params.groupids = groupids; } return this.performZabbixAPIRequest('host.get', params); }; // Get the list of applications - ZabbixAPIDatasource.prototype.performAppSuggestQuery = function(hostid) { + ZabbixAPIDatasource.prototype.performAppSuggestQuery = function(hostids, /* optional */ groupids) { var params = { output: ['name'], - sortfield: 'name', - hostids: hostid + sortfield: 'name' }; + if (hostids) { + params.hostids = hostids; + } + else if (groupids) { + params.groupids = groupids; + } return this.performZabbixAPIRequest('application.get', params); }; // Get the list of host items - ZabbixAPIDatasource.prototype.performItemSuggestQuery = function(hostid, applicationid) { + ZabbixAPIDatasource.prototype.performItemSuggestQuery = function(hostids, applicationids, /* optional */ groupids) { var params = { output: ['name', 'key_', 'value_type', 'delay'], sortfield: 'name', - hostids: hostid, - //Include web items in the result webitems: true, // Return only numeric items filter: { value_type: [0,3] - } + }, + searchByAny: true }; + if (hostids) { + params.hostids = hostids; + } + else if (groupids) { + params.groupids = groupids; + } // If application selected return only relative items - if (applicationid) { - params.applicationids = applicationid; + if (applicationids) { + params.applicationids = applicationids; } return this.performZabbixAPIRequest('item.get', params); @@ -414,19 +426,7 @@ function (angular, _, kbn) { return object.applicationid; }), 'applicationid'); - var params = { - output: ['name', 'key_'], - sortfield: 'name', - }; - if (applicationids.length) { - params.applicationids = applicationids; - } - if (hostids.length) { - params.hostids = hostids; - } else if (groupids.length) { - params.groupids = groupids; - } - return self.performZabbixAPIRequest('item.get', params) + return self.performItemSuggestQuery(hostids, applicationids, groupids) .then(function (result) { return _.map(result, function (item) { return { @@ -443,14 +443,14 @@ function (angular, _, kbn) { ZabbixAPIDatasource.prototype.appFindQuery = function(template) { var promises = []; - // Get groupids from names - if (template.group && template.group != '*') { - promises.push(this.findZabbixGroup(template.group)); - } // Get hostids from names if (template.host && template.host != '*') { promises.push(this.findZabbixHost(template.host)); } + // Get groupids from names + else if (template.group && template.group != '*') { + promises.push(this.findZabbixGroup(template.group)); + } var self = this; return $q.all(promises).then(function (results) { @@ -462,17 +462,7 @@ function (angular, _, kbn) { return object.hostid; }), 'hostid'); - var params = { - output: ['name'], - sortfield: 'name' - } - if (hostids.length) { - params.hostids = hostids; - } else if (groupids.length) { - params.groupids = groupids; - } - - return self.performZabbixAPIRequest('application.get', params) + return self.performAppSuggestQuery(hostids, groupids) .then(function (result) { return _.map(result, function (app) { return { @@ -486,13 +476,6 @@ function (angular, _, kbn) { ZabbixAPIDatasource.prototype.hostFindQuery = function(template) { - var promises = []; - - // Get groupids from names - if (template.group && template.group != '*') { - promises.push(this.findZabbixGroup(template.group)); - } - var self = this; return this.findZabbixGroup(template.group).then(function (results) { results = _.flatten(results); @@ -500,23 +483,14 @@ function (angular, _, kbn) { return object.groupid; }), 'groupid'); - var params = { - output: ['name', 'host'], - sortfield: 'name' - } - if (groupids.length) { - params.groupids = groupids; - } - - return self.performZabbixAPIRequest('host.get', params) - .then(function (result) { - return _.map(result, function (host) { - return { - text: host.name, - expandable: false - }; - }); + return self.performHostSuggestQuery(groupids).then(function (result) { + return _.map(result, function (host) { + return { + text: host.name, + expandable: false + }; }); + }); }); }; From ff3fe7f2194c015be79acc68232e30c942b4fb57 Mon Sep 17 00:00:00 2001 From: Alexander Zobnin Date: Fri, 29 May 2015 21:51:23 +0300 Subject: [PATCH 20/49] Metrics search for multiple hosts. --- zabbix/datasource.js | 8 ++++++-- 1 file changed, 6 insertions(+), 2 deletions(-) diff --git a/zabbix/datasource.js b/zabbix/datasource.js index ba4f11e..9dc4370 100644 --- a/zabbix/datasource.js +++ b/zabbix/datasource.js @@ -288,6 +288,10 @@ function (angular, _, kbn) { if (applicationids) { params.applicationids = applicationids; } + // Return host property for multiple hosts + if (!hostids || (_.isArray(hostids) && hostids.length > 1)) { + params.selectHosts = ['name']; + } return this.performZabbixAPIRequest('item.get', params); }; @@ -429,9 +433,9 @@ function (angular, _, kbn) { return self.performItemSuggestQuery(hostids, applicationids, groupids) .then(function (result) { return _.map(result, function (item) { + var itemname = expandItemName(item) return { - //text: item.key_, - text: expandItemName(item), + text: (item.hosts ? item.hosts[0].name + ': ' : '') + itemname, expandable: false }; }); From 81f10ea6a9b5432365e942a1dfbb801160cc9d86 Mon Sep 17 00:00:00 2001 From: Alexander Zobnin Date: Fri, 29 May 2015 23:35:14 +0300 Subject: [PATCH 21/49] [hostname]: itemname alias for multiple hosts items. --- zabbix/datasource.js | 21 ++++++++++++++++----- 1 file changed, 16 insertions(+), 5 deletions(-) diff --git a/zabbix/datasource.js b/zabbix/datasource.js index 9dc4370..33a8f4b 100644 --- a/zabbix/datasource.js +++ b/zabbix/datasource.js @@ -61,14 +61,21 @@ function (angular, _, kbn) { var hosts = hostname.match(host_pattern); // Extract item names + var delete_hostname_pattern = /(?:\[[\w\.]+\]\:\s)/g; var itemname_pattern = /([^{},]+)/g; - var itemnames = itemname.match(itemname_pattern); + // Remove hostnames from item name + // [hostname]: itemname --> itemname + var itemnames = itemname.replace(delete_hostname_pattern, '').match(itemname_pattern); + //var aliases = itemname.match(itemname_pattern); if (itemnames && (itemnames.length < this.limitmetrics)) { // Find items by item names and perform queries var self = this; return $q.all(_.map(hosts, function (hostname) { - return this.findZabbixItem(hostname, itemnames); + if (hosts.length > 1) { + var selectHosts = true; + } + return this.findZabbixItem(hostname, itemnames, selectHosts); }, this)).then(function (items) { items = _.flatten(items); return self.performTimeSeriesQuery(items, from, to) @@ -140,8 +147,9 @@ function (angular, _, kbn) { var grouped_history = _.groupBy(response, 'itemid'); return $q.when(_.map(grouped_history, function (history, itemid) { + var item = indexed_items[itemid]; var series = { - target: expandItemName(indexed_items[itemid]), + target: (item.hosts ? '['+item.hosts[0].name+']: ' : '') + expandItemName(item), datapoints: _.map(history, function (p) { // Value must be a number for properly work var value = Number(p.value); @@ -337,10 +345,13 @@ function (angular, _, kbn) { }; - ZabbixAPIDatasource.prototype.findZabbixItem = function (host, itemnames) { + ZabbixAPIDatasource.prototype.findZabbixItem = function (host, itemnames, /* optional */ selectHosts) { var params = { output: ['name', 'key_', 'value_type'], host: host + }; + if (selectHosts) { + params.selectHosts = ['name']; } return this.performZabbixAPIRequest('item.get', params).then(function (items) { return _.filter(items, function (item) { @@ -435,7 +446,7 @@ function (angular, _, kbn) { return _.map(result, function (item) { var itemname = expandItemName(item) return { - text: (item.hosts ? item.hosts[0].name + ': ' : '') + itemname, + text: (item.hosts ? '['+item.hosts[0].name+']: ' : '') + itemname, expandable: false }; }); From 46c9fa90be212a74419a3762cf54ab57193f2939 Mon Sep 17 00:00:00 2001 From: Alexander Zobnin Date: Sat, 30 May 2015 20:54:03 +0300 Subject: [PATCH 22/49] Fix query editor for work with templated variables. --- zabbix/datasource.js | 7 +- zabbix/partials/query.editor.html | 2 +- zabbix/queryCtrl.js | 106 ++++++++++-------------------- 3 files changed, 41 insertions(+), 74 deletions(-) diff --git a/zabbix/datasource.js b/zabbix/datasource.js index 33a8f4b..af924a2 100644 --- a/zabbix/datasource.js +++ b/zabbix/datasource.js @@ -49,7 +49,7 @@ function (angular, _, kbn) { name: target.alias }]; return this.performTimeSeriesQuery(item, from, to).then(_.partial( - this.handleZabbixAPIResponse, alias)); + this.handleHistoryResponse, alias)); } else { // Handle templated target @@ -73,13 +73,14 @@ function (angular, _, kbn) { var self = this; return $q.all(_.map(hosts, function (hostname) { if (hosts.length > 1) { + // Select the host that the item belongs for multiple hosts request var selectHosts = true; } return this.findZabbixItem(hostname, itemnames, selectHosts); }, this)).then(function (items) { items = _.flatten(items); return self.performTimeSeriesQuery(items, from, to) - .then(_.partial(self.handleZabbixAPIResponse, items)); + .then(_.partial(self.handleHistoryResponse, items)); }); } else { return []; @@ -127,7 +128,7 @@ function (angular, _, kbn) { // Convert Zabbix API data to Grafana format - ZabbixAPIDatasource.prototype.handleZabbixAPIResponse = function(items, response) { + ZabbixAPIDatasource.prototype.handleHistoryResponse = function(items, response) { /** * Response should be in the format: * data: [ diff --git a/zabbix/partials/query.editor.html b/zabbix/partials/query.editor.html index ba6b5fb..05d969d 100644 --- a/zabbix/partials/query.editor.html +++ b/zabbix/partials/query.editor.html @@ -59,7 +59,7 @@ ng-change="selectHostGroup()" ng-model="target.hostGroup" bs-tooltip="target.hostGroup.name.length > 25 ? target.hostGroup.name : ''" - ng-options="hostgroup.name for hostgroup in metric.hostGroupList" > + ng-options="hostgroup.name for hostgroup in metric.groupList" > Date: Sat, 30 May 2015 21:12:33 +0300 Subject: [PATCH 23/49] Fixed expandItemName() in query editor (no need to expand templated item name in editor). Improve hosts and items search - return only monitored hosts and enabled items. --- zabbix/datasource.js | 6 +++++- zabbix/queryCtrl.js | 16 +++++++++------- 2 files changed, 14 insertions(+), 8 deletions(-) diff --git a/zabbix/datasource.js b/zabbix/datasource.js index af924a2..c97e901 100644 --- a/zabbix/datasource.js +++ b/zabbix/datasource.js @@ -247,7 +247,9 @@ function (angular, _, kbn) { output: ['name', 'host'], sortfield: 'name', // Return only hosts that have items with numeric type of information. - with_simple_graph_items: true + with_simple_graph_items: true, + // Return only monitored hosts. + monitored_hosts: true }; // Return only hosts in given group if (groupids) { @@ -285,6 +287,8 @@ function (angular, _, kbn) { filter: { value_type: [0,3] }, + // Return only enabled items + monitored: true, searchByAny: true }; if (hostids) { diff --git a/zabbix/queryCtrl.js b/zabbix/queryCtrl.js index 5c43b3b..3c3401b 100644 --- a/zabbix/queryCtrl.js +++ b/zabbix/queryCtrl.js @@ -222,14 +222,16 @@ function (angular, _) { 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(','); + if (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]); - }; + // replace item parameters + for (var i = key_params.length; i >= 1; i--) { + name = name.replace('$' + i, key_params[i - 1]); + }; + } return name; }; From c71ad03cf5e2d3699f10732e010bab46cb3e7c06 Mon Sep 17 00:00:00 2001 From: Alexander Zobnin Date: Sun, 31 May 2015 09:25:07 +0300 Subject: [PATCH 24/49] Improve app search in query editor - return only unique app names and search by app name in multiple hosts. --- zabbix/queryCtrl.js | 39 +++++++++++++++++++++++---------------- 1 file changed, 23 insertions(+), 16 deletions(-) diff --git a/zabbix/queryCtrl.js b/zabbix/queryCtrl.js index 3c3401b..f8056c1 100644 --- a/zabbix/queryCtrl.js +++ b/zabbix/queryCtrl.js @@ -159,12 +159,15 @@ function (angular, _) { var hostid = $scope.target.host ? $scope.target.host.hostid : null; var groupid = $scope.target.hostGroup ? $scope.target.hostGroup.groupid: null; $scope.datasource.performAppSuggestQuery(hostid, groupid).then(function (series) { - $scope.metric.applicationList = $scope.metric.applicationList.concat(series); + var apps = _.map(_.uniq(_.map(series, 'name')), function (appname) { + return {name: appname}; + }); + $scope.metric.applicationList = $scope.metric.applicationList.concat(apps); if ($scope.target.application) { - $scope.target.application = $scope.metric.applicationList.filter(function (item, index, array) { + $scope.target.application = $scope.metric.applicationList.filter(function (app) { // Find selected application in metric.hostList - return item.name == $scope.target.application.name; + return app.name == $scope.target.application.name; }).pop(); } }); @@ -180,23 +183,27 @@ function (angular, _) { var groupids = $scope.target.hostGroup ? $scope.target.hostGroup.groupid: null; var hostids = $scope.target.host ? $scope.target.host.hostid : null; - var applicationids = $scope.target.application ? $scope.target.application.applicationid : null; + var application = $scope.target.application || null; - $scope.datasource.performItemSuggestQuery(hostids, applicationids, groupids).then(function (series) { - $scope.metric.itemList = $scope.metric.itemList.concat(series); + // Get application ids from name + $scope.datasource.findZabbixApp(application).then(function (result) { + var applicationids = _.map(result, 'applicationid'); + $scope.datasource.performItemSuggestQuery(hostids, applicationids, groupids).then(function (series) { + $scope.metric.itemList = $scope.metric.itemList.concat(series); - // Expand item parameters - $scope.metric.itemList.forEach(function (item, index, array) { - if (item && item.key_ && item.name) { - item.name = expandItemName(item); + // Expand item parameters + $scope.metric.itemList.forEach(function (item, index, array) { + if (item && item.key_ && item.name) { + item.name = expandItemName(item); + } + }); + if ($scope.target.item) { + $scope.target.item = $scope.metric.itemList.filter(function (item, index, array) { + // Find selected item in metric.hostList + return item.name == $scope.target.item.name; + }).pop(); } }); - if ($scope.target.item) { - $scope.target.item = $scope.metric.itemList.filter(function (item, index, array) { - // Find selected item in metric.hostList - return item.name == $scope.target.item.name; - }).pop(); - } }); }; From ed0138560ce5f04cf4a9f941be3de767009fde2d Mon Sep 17 00:00:00 2001 From: Alexander Zobnin Date: Sun, 31 May 2015 14:29:48 +0300 Subject: [PATCH 25/49] Reduce number of api requests in query() method. --- zabbix/datasource.js | 47 ++++++++++++++++++++++++-------------------- zabbix/queryCtrl.js | 3 ++- 2 files changed, 28 insertions(+), 22 deletions(-) diff --git a/zabbix/datasource.js b/zabbix/datasource.js index c97e901..bf3f4c3 100644 --- a/zabbix/datasource.js +++ b/zabbix/datasource.js @@ -71,13 +71,12 @@ function (angular, _, kbn) { if (itemnames && (itemnames.length < this.limitmetrics)) { // Find items by item names and perform queries var self = this; - return $q.all(_.map(hosts, function (hostname) { - if (hosts.length > 1) { - // Select the host that the item belongs for multiple hosts request - var selectHosts = true; - } - return this.findZabbixItem(hostname, itemnames, selectHosts); - }, this)).then(function (items) { + // TODO: do one query. + if (hosts.length > 1) { + // Select the host that the item belongs for multiple hosts request + var selectHosts = true; + } + return this.findZabbixItem(hosts, itemnames, selectHosts).then(function (items) { items = _.flatten(items); return self.performTimeSeriesQuery(items, from, to) .then(_.partial(self.handleHistoryResponse, items)); @@ -150,6 +149,7 @@ function (angular, _, kbn) { return $q.when(_.map(grouped_history, function (history, itemid) { var item = indexed_items[itemid]; var series = { + // TODO: remove [] target: (item.hosts ? '['+item.hosts[0].name+']: ' : '') + expandItemName(item), datapoints: _.map(history, function (p) { // Value must be a number for properly work @@ -323,12 +323,12 @@ function (angular, _, kbn) { }; - ZabbixAPIDatasource.prototype.findZabbixHost = function (hostname) { + ZabbixAPIDatasource.prototype.findZabbixHost = function (hostnames) { var params = { output: ['host', 'name'], search: { - host: hostname, - name: hostname + host: hostnames, + name: hostnames }, searchByAny: true, searchWildcardsEnabled: true @@ -350,17 +350,21 @@ function (angular, _, kbn) { }; - ZabbixAPIDatasource.prototype.findZabbixItem = function (host, itemnames, /* optional */ selectHosts) { - var params = { - output: ['name', 'key_', 'value_type'], - host: host - }; - if (selectHosts) { - params.selectHosts = ['name']; - } - return this.performZabbixAPIRequest('item.get', params).then(function (items) { - return _.filter(items, function (item) { - return _.contains(itemnames, expandItemName(item)); + ZabbixAPIDatasource.prototype.findZabbixItem = function (hosts, itemnames, /* optional */ selectHosts) { + var self = this; + return this.findZabbixHost(hosts).then(function (hosts) { + var hostids = _.map(hosts, 'hostid'); + var params = { + output: ['name', 'key_', 'value_type'], + hostids: hostids + }; + if (selectHosts) { + params.selectHosts = ['name']; + } + return self.performZabbixAPIRequest('item.get', params).then(function (items) { + return _.filter(items, function (item) { + return _.contains(itemnames, expandItemName(item)); + }); }); }); }; @@ -451,6 +455,7 @@ function (angular, _, kbn) { return _.map(result, function (item) { var itemname = expandItemName(item) return { + // TODO: select only unique names text: (item.hosts ? '['+item.hosts[0].name+']: ' : '') + itemname, expandable: false }; diff --git a/zabbix/queryCtrl.js b/zabbix/queryCtrl.js index f8056c1..dba67b8 100644 --- a/zabbix/queryCtrl.js +++ b/zabbix/queryCtrl.js @@ -21,7 +21,8 @@ function (angular, _) { // Update host group, host, application and item lists $scope.updateGroupList(); - $scope.updateHostList() + $scope.updateHostList(); + $scope.updateAppList(); $scope.updateItemList(); setItemAlias(); From 1eefaa78d8d7407eda29155541f95100bd4689af Mon Sep 17 00:00:00 2001 From: Alexander Zobnin Date: Sun, 31 May 2015 14:32:31 +0300 Subject: [PATCH 26/49] Removed [] from aliases for templated graphs. --- zabbix/datasource.js | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/zabbix/datasource.js b/zabbix/datasource.js index bf3f4c3..e951462 100644 --- a/zabbix/datasource.js +++ b/zabbix/datasource.js @@ -71,7 +71,6 @@ function (angular, _, kbn) { if (itemnames && (itemnames.length < this.limitmetrics)) { // Find items by item names and perform queries var self = this; - // TODO: do one query. if (hosts.length > 1) { // Select the host that the item belongs for multiple hosts request var selectHosts = true; @@ -149,8 +148,7 @@ function (angular, _, kbn) { return $q.when(_.map(grouped_history, function (history, itemid) { var item = indexed_items[itemid]; var series = { - // TODO: remove [] - target: (item.hosts ? '['+item.hosts[0].name+']: ' : '') + expandItemName(item), + target: (item.hosts ? item.hosts[0].name+': ' : '') + expandItemName(item), datapoints: _.map(history, function (p) { // Value must be a number for properly work var value = Number(p.value); From 4313ecb800b14cfd17b87df355c92b0cd00ba100 Mon Sep 17 00:00:00 2001 From: Alexander Zobnin Date: Sun, 31 May 2015 15:12:25 +0300 Subject: [PATCH 27/49] Templated items search: select only unique names. --- zabbix/datasource.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/zabbix/datasource.js b/zabbix/datasource.js index e951462..b5ef788 100644 --- a/zabbix/datasource.js +++ b/zabbix/datasource.js @@ -454,7 +454,7 @@ function (angular, _, kbn) { var itemname = expandItemName(item) return { // TODO: select only unique names - text: (item.hosts ? '['+item.hosts[0].name+']: ' : '') + itemname, + text: itemname, expandable: false }; }); From 2ab15e6c1867f536182d105a833f650eae75ed01 Mon Sep 17 00:00:00 2001 From: Alexander Zobnin Date: Sun, 31 May 2015 23:37:24 +0300 Subject: [PATCH 28/49] Refactoring: code formatting and add functions descriptions and some comments. --- zabbix/datasource.js | 129 +++++++++++++++++++++++++++---------------- 1 file changed, 80 insertions(+), 49 deletions(-) diff --git a/zabbix/datasource.js b/zabbix/datasource.js index b5ef788..0d86d09 100644 --- a/zabbix/datasource.js +++ b/zabbix/datasource.js @@ -27,13 +27,22 @@ function (angular, _, kbn) { } + /** + * Calls for each panel in dashboard. + * @param {Object} options Query options. Contains time range, targets + * and other info. + * @return {Object} Grafana metrics object with timeseries data + * for each target. + */ ZabbixAPIDatasource.prototype.query = function(options) { + // get from & to in seconds var from = Math.ceil(kbn.parseDate(options.range.from).getTime() / 1000); var to = Math.ceil(kbn.parseDate(options.range.to).getTime() / 1000); // Create request for each target var promises = _.map(options.targets, function(target) { + // Remove undefined and hidden targets if (target.hide || !target.item) { return []; @@ -51,7 +60,9 @@ function (angular, _, kbn) { return this.performTimeSeriesQuery(item, from, to).then(_.partial( this.handleHistoryResponse, alias)); } else { - // Handle templated target + /** + * Handle templated target + */ var itemname = templateSrv.replace(target.item.name); var hostname = templateSrv.replace(target.host.name); @@ -60,25 +71,31 @@ function (angular, _, kbn) { var host_pattern = /([\w\.\s]+)/g; var hosts = hostname.match(host_pattern); + // Remove hostnames from item names and then // Extract item names - var delete_hostname_pattern = /(?:\[[\w\.]+\]\:\s)/g; - var itemname_pattern = /([^{},]+)/g; - // Remove hostnames from item name // [hostname]: itemname --> itemname - var itemnames = itemname.replace(delete_hostname_pattern, '').match(itemname_pattern); + var delete_hostname_pattern = /(?:\[[\w\.]+\]\:\s)/g; + var itemname_pattern = /([^{},]+)/g; + var itemnames = itemname.replace(delete_hostname_pattern, '') + .match(itemname_pattern); //var aliases = itemname.match(itemname_pattern); - if (itemnames && (itemnames.length < this.limitmetrics)) { + // Don't perform query for high number of items + // to prevent Grafana slowdown + if (itemnames && (itemnames.length < this.limitmetrics)) { + + // Select the host that the item belongs for multiple hosts request + if (hosts.length > 1) { + var selectHosts = true; + } + // Find items by item names and perform queries var self = this; - if (hosts.length > 1) { - // Select the host that the item belongs for multiple hosts request - var selectHosts = true; - } - return this.findZabbixItem(hosts, itemnames, selectHosts).then(function (items) { - items = _.flatten(items); - return self.performTimeSeriesQuery(items, from, to) - .then(_.partial(self.handleHistoryResponse, items)); + return this.findZabbixItem(hosts, itemnames, selectHosts) + .then(function (items) { + items = _.flatten(items); + return self.performTimeSeriesQuery(items, from, to) + .then(_.partial(self.handleHistoryResponse, items)); }); } else { return []; @@ -93,9 +110,11 @@ function (angular, _, kbn) { /** - * Perform time series query to Zabbix API - * - * @param items: array of zabbix api item objects + * Perform time series query from Zabbix API + * @param {Array} items Array of Zabbix item objects + * @param {Number} start Time in seconds + * @param {Number} end Time in seconds + * @return {Array} Array of Zabbix history objects */ ZabbixAPIDatasource.prototype.performTimeSeriesQuery = function(items, start, end) { // Group items by value type @@ -125,8 +144,17 @@ function (angular, _, kbn) { }; - // Convert Zabbix API data to Grafana format - ZabbixAPIDatasource.prototype.handleHistoryResponse = function(items, response) { + /** + * 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, history) { /** * Response should be in the format: * data: [ @@ -143,7 +171,7 @@ function (angular, _, kbn) { // Group items and history by itemid var indexed_items = _.indexBy(items, 'itemid'); - var grouped_history = _.groupBy(response, 'itemid'); + var grouped_history = _.groupBy(history, 'itemid'); return $q.when(_.map(grouped_history, function (history, itemid) { var item = indexed_items[itemid]; @@ -359,11 +387,12 @@ function (angular, _, kbn) { if (selectHosts) { params.selectHosts = ['name']; } - return self.performZabbixAPIRequest('item.get', params).then(function (items) { - return _.filter(items, function (item) { - return _.contains(itemnames, expandItemName(item)); + return self.performZabbixAPIRequest('item.get', params) + .then(function (items) { + return _.filter(items, function (item) { + return _.contains(itemnames, expandItemName(item)); + }); }); - }); }); }; @@ -542,34 +571,36 @@ function (angular, _, kbn) { }, }; - return this.performZabbixAPIRequest('trigger.get', params).then(function (result) { - if(result) { - var obs = {}; - obs = _.indexBy(result, 'triggerid'); + return this.performZabbixAPIRequest('trigger.get', params) + .then(function (result) { + if(result) { + var obs = {}; + obs = _.indexBy(result, 'triggerid'); - var params = { - output: 'extend', - sortorder: 'DESC', - time_from: from, - time_till: to, - objectids: _.keys(obs) - }; + var params = { + output: 'extend', + sortorder: 'DESC', + time_from: from, + time_till: to, + objectids: _.keys(obs) + }; - return self.performZabbixAPIRequest('event.get', params).then(function (result) { - var events = []; - _.each(result, function(e) { - events.push({ - annotation: annotation, - time: e.clock * 1000, - title: obs[e.objectid].description, - text: e.eventid, - }); + return self.performZabbixAPIRequest('event.get', params) + .then(function (result) { + var events = []; + _.each(result, function(e) { + events.push({ + annotation: annotation, + time: e.clock * 1000, + title: obs[e.objectid].description, + text: e.eventid, + }); + }); + return events; }); - return events; - }); - } else { - return []; - } + } else { + return []; + } }); }; From ae9d4b2258c0485c42d2c4099d4b640e7820debd Mon Sep 17 00:00:00 2001 From: Alexander Zobnin Date: Wed, 3 Jun 2015 21:35:43 +0300 Subject: [PATCH 29/49] Fixed templated hostnames extracting (fixed regex). --- zabbix/datasource.js | 25 ++++++++++++++++++------- 1 file changed, 18 insertions(+), 7 deletions(-) diff --git a/zabbix/datasource.js b/zabbix/datasource.js index 0d86d09..6340cbb 100644 --- a/zabbix/datasource.js +++ b/zabbix/datasource.js @@ -11,6 +11,12 @@ function (angular, _, kbn) { module.factory('ZabbixAPIDatasource', function($q, backendSrv, templateSrv) { + /** + * Datasource initialization. Calls when you refresh page, add + * or modify datasource. + * + * @param {Object} datasource Grafana datasource object. + */ function ZabbixAPIDatasource(datasource) { this.name = datasource.name; this.url = datasource.url; @@ -29,6 +35,7 @@ function (angular, _, kbn) { /** * Calls for each panel in dashboard. + * * @param {Object} options Query options. Contains time range, targets * and other info. * @return {Object} Grafana metrics object with timeseries data @@ -67,27 +74,28 @@ function (angular, _, kbn) { var itemname = templateSrv.replace(target.item.name); var hostname = templateSrv.replace(target.host.name); - // Extract zabbix hosts - var host_pattern = /([\w\.\s]+)/g; + // Extract zabbix hosts from hosts string: + // "{host1,host2,...,hostN}" --> [host1, host2, ..., hostN] + var host_pattern = /([^{},]+)/g; var hosts = hostname.match(host_pattern); // Remove hostnames from item names and then // Extract item names - // [hostname]: itemname --> itemname + // "hostname: itemname" --> "itemname" var delete_hostname_pattern = /(?:\[[\w\.]+\]\:\s)/g; - var itemname_pattern = /([^{},]+)/g; + var itemname_pattern = /([^{},]+)/g; var itemnames = itemname.replace(delete_hostname_pattern, '') .match(itemname_pattern); //var aliases = itemname.match(itemname_pattern); // Don't perform query for high number of items // to prevent Grafana slowdown - if (itemnames && (itemnames.length < this.limitmetrics)) { + if (itemnames && (itemnames.length < this.limitmetrics)) { // Select the host that the item belongs for multiple hosts request - if (hosts.length > 1) { + if (hosts.length > 1) { var selectHosts = true; - } + } // Find items by item names and perform queries var self = this; @@ -111,6 +119,7 @@ function (angular, _, kbn) { /** * Perform time series query from Zabbix API + * * @param {Array} items Array of Zabbix item objects * @param {Number} start Time in seconds * @param {Number} end Time in seconds @@ -146,6 +155,7 @@ function (angular, _, kbn) { /** * 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 @@ -222,6 +232,7 @@ function (angular, _, kbn) { // Handle response return performedQuery.then(function (response) { if (!response.data) { + // TODO: handle "auth token expired" error return []; } return response.data.result; From 83c82a3481e92d4a1283db8f8ee7ad4b28f41774 Mon Sep 17 00:00:00 2001 From: Alexander Zobnin Date: Wed, 3 Jun 2015 21:57:05 +0300 Subject: [PATCH 30/49] Fixed "Session terminated, re-login, please." error when auth token expired. --- zabbix/datasource.js | 25 +++++++++++++++++++++---- 1 file changed, 21 insertions(+), 4 deletions(-) diff --git a/zabbix/datasource.js b/zabbix/datasource.js index 6340cbb..8dbe4cd 100644 --- a/zabbix/datasource.js +++ b/zabbix/datasource.js @@ -30,6 +30,7 @@ function (angular, _, kbn) { // For testing this.ds = datasource; + this.auth = 'testauth' } @@ -216,10 +217,10 @@ function (angular, _, kbn) { }; var performedQuery; + var self = this; // Check authorization first if (!this.auth) { - var self = this; performedQuery = this.performZabbixAPILogin().then(function (response) { self.auth = response; options.data.auth = response; @@ -232,9 +233,22 @@ function (angular, _, kbn) { // Handle response return performedQuery.then(function (response) { if (!response.data) { - // TODO: handle "auth token expired" error return []; } + else if (response.data.error) { + // Handle Zabbix API errors + + if (response.data.error.data == "Session terminated, re-login, please.") { + // Handle "Session terminated, re-login, please." error + return self.performZabbixAPILogin().then(function (response) { + self.auth = response; + options.data.auth = response; + return backendSrv.datasourceRequest(options); + }).then(function (response) { + return response.data.result; + }); + } + } return response.data.result; }); }; @@ -270,8 +284,11 @@ function (angular, _, kbn) { ZabbixAPIDatasource.prototype.performHostGroupSuggestQuery = function() { var params = { output: ['name'], - real_hosts: true, //Return only host groups that contain hosts - sortfield: 'name' + sortfield: 'name', + // Return only host groups that contain hosts + real_hosts: true, + // Return only host groups that contain monitored hosts. + monitored_hosts: true }; return this.performZabbixAPIRequest('hostgroup.get', params); From 3a31b8b27cc3e29f39fc8496268458f902c47242 Mon Sep 17 00:00:00 2001 From: Alexander Zobnin Date: Wed, 3 Jun 2015 23:21:20 +0300 Subject: [PATCH 31/49] Fixed templated items parsing (fixed regexps) --- zabbix/datasource.js | 16 +++++++--------- zabbix/queryCtrl.js | 33 ++++++++++++++++++++++++++------- 2 files changed, 33 insertions(+), 16 deletions(-) diff --git a/zabbix/datasource.js b/zabbix/datasource.js index 8dbe4cd..51d7211 100644 --- a/zabbix/datasource.js +++ b/zabbix/datasource.js @@ -25,12 +25,8 @@ function (angular, _, kbn) { this.username = datasource.meta.username; this.password = datasource.meta.password; - // Limit metrics per panel - this.limitmetrics = datasource.meta.limitmetrics || 20; - - // For testing - this.ds = datasource; - this.auth = 'testauth' + // Limit metrics per panel for templated request + this.limitmetrics = datasource.meta.limitmetrics || 50; } @@ -81,12 +77,14 @@ function (angular, _, kbn) { var hosts = hostname.match(host_pattern); // Remove hostnames from item names and then - // Extract item names + // extract item names // "hostname: itemname" --> "itemname" var delete_hostname_pattern = /(?:\[[\w\.]+\]\:\s)/g; - var itemname_pattern = /([^{},]+)/g; + var remove_brackets_pattern = /^{|}$/g; + var itemname_split_pattern = /(,(?!\s))/g; var itemnames = itemname.replace(delete_hostname_pattern, '') - .match(itemname_pattern); + .replace(remove_brackets_pattern, '') + .split(itemname_split_pattern); //var aliases = itemname.match(itemname_pattern); // Don't perform query for high number of items diff --git a/zabbix/queryCtrl.js b/zabbix/queryCtrl.js index dba67b8..116f22e 100644 --- a/zabbix/queryCtrl.js +++ b/zabbix/queryCtrl.js @@ -30,13 +30,17 @@ function (angular, _) { $scope.target.errors = validateTarget($scope.target); }; - // Take alias from item name by default + + /** + * Take alias from item name by default + */ function setItemAlias() { if (!$scope.target.alias && $scope.target.item) { $scope.target.alias = expandItemName($scope.target.item); } }; + $scope.targetBlur = function() { setItemAlias(); $scope.target.errors = validateTarget($scope.target); @@ -46,7 +50,10 @@ function (angular, _) { } }; - // Call when host group selected + + /** + * Call when host group selected + */ $scope.selectHostGroup = function() { $scope.updateHostList() $scope.updateAppList(); @@ -58,7 +65,10 @@ function (angular, _) { } }; - // Call when host selected + + /** + * Call when host selected + */ $scope.selectHost = function() { $scope.updateItemList(); $scope.updateAppList(); @@ -71,7 +81,9 @@ function (angular, _) { }; - // Call when application selected + /** + * Call when application selected + */ $scope.selectApplication = function() { $scope.updateItemList(); @@ -83,7 +95,9 @@ function (angular, _) { }; - // Call when item selected + /** + * Call when item selected + */ $scope.selectItem = function() { setItemAlias(); $scope.target.errors = validateTarget($scope.target); @@ -209,6 +223,11 @@ function (angular, _) { }; + /** + * Add templated variables to list of available metrics + * + * @param {Array} metricList List of metrics which variables add to + */ function addTemplatedVariables(metricList) { _.each(templateSrv.variables, function(variable) { metricList.push({ @@ -223,8 +242,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) + * @param {Object} item Zabbix item object + * @return {string} expanded item name */ function expandItemName(item) { var name = item.name; From f84443692b3eafc33f917d1300d3493c9a44d511 Mon Sep 17 00:00:00 2001 From: Alexander Zobnin Date: Wed, 3 Jun 2015 23:50:10 +0300 Subject: [PATCH 32/49] Add splitMetrics() function for extracting metrics from "{metric1,metcic2,...,metricN}" string. --- zabbix/datasource.js | 34 +++++++++++++++++----------------- 1 file changed, 17 insertions(+), 17 deletions(-) diff --git a/zabbix/datasource.js b/zabbix/datasource.js index 51d7211..286c1c0 100644 --- a/zabbix/datasource.js +++ b/zabbix/datasource.js @@ -73,18 +73,13 @@ function (angular, _, kbn) { // Extract zabbix hosts from hosts string: // "{host1,host2,...,hostN}" --> [host1, host2, ..., hostN] - var host_pattern = /([^{},]+)/g; - var hosts = hostname.match(host_pattern); + var hosts = splitMetrics(hostname); // Remove hostnames from item names and then // extract item names // "hostname: itemname" --> "itemname" var delete_hostname_pattern = /(?:\[[\w\.]+\]\:\s)/g; - var remove_brackets_pattern = /^{|}$/g; - var itemname_split_pattern = /(,(?!\s))/g; - var itemnames = itemname.replace(delete_hostname_pattern, '') - .replace(remove_brackets_pattern, '') - .split(itemname_split_pattern); + var itemnames = splitMetrics(itemname.replace(delete_hostname_pattern, '')); //var aliases = itemname.match(itemname_pattern); // Don't perform query for high number of items @@ -433,22 +428,13 @@ function (angular, _, kbn) { if (part[0] === '{') { // Convert multiple mettrics to array // "{metric1,metcic2,...,metricN}" --> [metric1, metcic2,..., metricN] - parts.push(part.slice(1, -1).split(',')); + parts.push(splitMetrics(part)); } else { parts.push(part); } }); var template = _.object(['group', 'host', 'app', 'key'], parts) - var params = { - output: ['name'], - sortfield: 'name', - // Case insensitive search - search: { - name : template.group - } - }; - // Get items if (parts.length === 4) { return this.itemFindQuery(template); @@ -635,6 +621,20 @@ function (angular, _, kbn) { }); +/** + * 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) +} + + /** * Expand item parameters, for example: * CPU $2 time ($3) --> CPU system time (avg1) From 04ccba8501e4dd31cff2b06b681bfd17c75eaeef Mon Sep 17 00:00:00 2001 From: Alexander Zobnin Date: Thu, 4 Jun 2015 00:09:29 +0300 Subject: [PATCH 33/49] Fixed splitMetrics() --- zabbix/datasource.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/zabbix/datasource.js b/zabbix/datasource.js index 286c1c0..61d1427 100644 --- a/zabbix/datasource.js +++ b/zabbix/datasource.js @@ -630,7 +630,7 @@ function (angular, _, kbn) { */ function splitMetrics(metrics) { var remove_brackets_pattern = /^{|}$/g; - var metric_split_pattern = /(,(?!\s))/g; + var metric_split_pattern = /,(?!\s)/g; return metrics.replace(remove_brackets_pattern, '').split(metric_split_pattern) } From 4dbbb496a8b8bbcafec6d79fc3cdb942f70db0c2 Mon Sep 17 00:00:00 2001 From: Alexander Zobnin Date: Thu, 4 Jun 2015 21:11:22 +0300 Subject: [PATCH 34/49] Improve auth error handle - more clear. --- zabbix/datasource.js | 36 +++++++++++++----------------------- 1 file changed, 13 insertions(+), 23 deletions(-) diff --git a/zabbix/datasource.js b/zabbix/datasource.js index 61d1427..2eed9d7 100644 --- a/zabbix/datasource.js +++ b/zabbix/datasource.js @@ -112,7 +112,7 @@ function (angular, _, kbn) { /** - * Perform time series query from Zabbix API + * Perform history query from Zabbix API * * @param {Array} items Array of Zabbix item objects * @param {Number} start Time in seconds @@ -192,7 +192,13 @@ function (angular, _, kbn) { }; - // Request data from Zabbix API + /** + * Request data from Zabbix API + * + * @param {string} method Zabbix API method name + * @param {object} params method params + * @return {object} result + */ ZabbixAPIDatasource.prototype.performZabbixAPIRequest = function(method, params) { var options = { method: 'POST', @@ -209,36 +215,20 @@ function (angular, _, kbn) { } }; - var performedQuery; var self = this; - - // Check authorization first - if (!this.auth) { - performedQuery = this.performZabbixAPILogin().then(function (response) { - self.auth = response; - options.data.auth = response; - return backendSrv.datasourceRequest(options); - }); - } else { - performedQuery = backendSrv.datasourceRequest(options); - } - - // Handle response - return performedQuery.then(function (response) { + return backendSrv.datasourceRequest(options).then(function (response) { if (!response.data) { return []; } else if (response.data.error) { // Handle Zabbix API errors - if (response.data.error.data == "Session terminated, re-login, please.") { - // Handle "Session terminated, re-login, please." error + if (response.data.error.data == "Session terminated, re-login, please." || + response.data.error.data == 'Not authorised.') { + // Handle auth errors return self.performZabbixAPILogin().then(function (response) { self.auth = response; - options.data.auth = response; - return backendSrv.datasourceRequest(options); - }).then(function (response) { - return response.data.result; + return self.performZabbixAPIRequest(method, params); }); } } From 9ab20414945bc97aaad67ed8f1521eaa84c05cfc Mon Sep 17 00:00:00 2001 From: Alexander Zobnin Date: Thu, 4 Jun 2015 21:35:52 +0300 Subject: [PATCH 35/49] Refoctoring of metricFindQuery(). --- zabbix/datasource.js | 83 ++++++++++++++++++++------------------------ 1 file changed, 38 insertions(+), 45 deletions(-) diff --git a/zabbix/datasource.js b/zabbix/datasource.js index 2eed9d7..d572ef9 100644 --- a/zabbix/datasource.js +++ b/zabbix/datasource.js @@ -223,9 +223,9 @@ function (angular, _, kbn) { else if (response.data.error) { // Handle Zabbix API errors + // Handle auth errors if (response.data.error.data == "Session terminated, re-login, please." || response.data.error.data == 'Not authorised.') { - // Handle auth errors return self.performZabbixAPILogin().then(function (response) { self.auth = response; return self.performZabbixAPIRequest(method, params); @@ -427,19 +427,49 @@ function (angular, _, kbn) { // Get items if (parts.length === 4) { - return this.itemFindQuery(template); + return this.itemFindQuery(template).then(function (result) { + return _.map(result, function (item) { + var itemname = expandItemName(item) + return { + // TODO: select only unique names + text: itemname, + expandable: false + }; + }); + }); } // Get applications else if (parts.length === 3) { - return this.appFindQuery(template); + return this.appFindQuery(template).then(function (result) { + return _.map(result, function (app) { + return { + text: app.name, + expandable: false + }; + }); + }); } // Get hosts else if (parts.length === 2) { - return this.hostFindQuery(template); + return this.hostFindQuery(template).then(function (result) { + return _.map(result, function (host) { + return { + text: host.name, + expandable: false + }; + }); + }); } // Get groups else if (parts.length === 1) { - return this.groupFindQuery(template); + return this.performHostGroupSuggestQuery().then(function (result) { + return _.map(result, function (hostgroup) { + return { + text: hostgroup.name, + expandable: false + }; + }); + }); } // Return empty object else { @@ -479,17 +509,7 @@ function (angular, _, kbn) { return object.applicationid; }), 'applicationid'); - return self.performItemSuggestQuery(hostids, applicationids, groupids) - .then(function (result) { - return _.map(result, function (item) { - var itemname = expandItemName(item) - return { - // TODO: select only unique names - text: itemname, - expandable: false - }; - }); - }); + return self.performItemSuggestQuery(hostids, applicationids, groupids); }); }; @@ -516,15 +536,7 @@ function (angular, _, kbn) { return object.hostid; }), 'hostid'); - return self.performAppSuggestQuery(hostids, groupids) - .then(function (result) { - return _.map(result, function (app) { - return { - text: app.name, - expandable: false - }; - }); - }); + return self.performAppSuggestQuery(hostids, groupids); }); }; @@ -537,26 +549,7 @@ function (angular, _, kbn) { return object.groupid; }), 'groupid'); - return self.performHostSuggestQuery(groupids).then(function (result) { - return _.map(result, function (host) { - return { - text: host.name, - expandable: false - }; - }); - }); - }); - }; - - - ZabbixAPIDatasource.prototype.groupFindQuery = function(template) { - return this.performHostGroupSuggestQuery().then(function (result) { - return _.map(result, function (hostgroup) { - return { - text: hostgroup.name, - expandable: false - }; - }); + return self.performHostSuggestQuery(groupids); }); }; From 53ad2a07f5e5a3204ad46bc6137b54f1640dc190 Mon Sep 17 00:00:00 2001 From: Alexander Zobnin Date: Thu, 4 Jun 2015 21:51:18 +0300 Subject: [PATCH 36/49] Some comments add for templated queries. --- zabbix/datasource.js | 25 ++++++++++++++++++++++--- 1 file changed, 22 insertions(+), 3 deletions(-) diff --git a/zabbix/datasource.js b/zabbix/datasource.js index d572ef9..4885f91 100644 --- a/zabbix/datasource.js +++ b/zabbix/datasource.js @@ -408,10 +408,16 @@ function (angular, _, kbn) { }; - // For templated query + /** + * For templated query + * + * @param {string} query Query from Templating + * @return {string} Metric name - group, host, app or item or list + * of metrics in "{metric1,metcic2,...,metricN}" format. + */ ZabbixAPIDatasource.prototype.metricFindQuery = function (query) { // Split query. Query structure: - // group.host.app.key + // group.host.app.item var parts = []; _.each(query.split('.'), function (part) { part = templateSrv.replace(part); @@ -423,7 +429,7 @@ function (angular, _, kbn) { parts.push(part); } }); - var template = _.object(['group', 'host', 'app', 'key'], parts) + var template = _.object(['group', 'host', 'app', 'item'], parts) // Get items if (parts.length === 4) { @@ -480,6 +486,19 @@ function (angular, _, kbn) { }; + /** + * Find items from templated request + * + * @param {object} template Template object in format: + * { + * group: [groupnames], + * host: [hostnames], + * app: [appnames], + * item: [itemnames] + * } + * + * @return {Array} Array of Zabbix API item objects + */ ZabbixAPIDatasource.prototype.itemFindQuery = function(template) { var promises = []; From a75af217fda0691107c275ad1435071b7bc4104b Mon Sep 17 00:00:00 2001 From: Alexander Zobnin Date: Thu, 4 Jun 2015 22:38:02 +0300 Subject: [PATCH 37/49] Query editor: templated variables support. --- zabbix/datasource.js | 39 +++++++++++++++------------ zabbix/queryCtrl.js | 63 +++++++++++++++++++++++++------------------- 2 files changed, 58 insertions(+), 44 deletions(-) diff --git a/zabbix/datasource.js b/zabbix/datasource.js index 4885f91..20330f6 100644 --- a/zabbix/datasource.js +++ b/zabbix/datasource.js @@ -433,7 +433,7 @@ function (angular, _, kbn) { // Get items if (parts.length === 4) { - return this.itemFindQuery(template).then(function (result) { + return this.itemFindQuery(template.group, template.host, template.app).then(function (result) { return _.map(result, function (item) { var itemname = expandItemName(item) return { @@ -446,7 +446,7 @@ function (angular, _, kbn) { } // Get applications else if (parts.length === 3) { - return this.appFindQuery(template).then(function (result) { + return this.appFindQuery(template.host, template.group).then(function (result) { return _.map(result, function (app) { return { text: app.name, @@ -457,7 +457,7 @@ function (angular, _, kbn) { } // Get hosts else if (parts.length === 2) { - return this.hostFindQuery(template).then(function (result) { + return this.hostFindQuery(template.group).then(function (result) { return _.map(result, function (host) { return { text: host.name, @@ -499,20 +499,20 @@ function (angular, _, kbn) { * * @return {Array} Array of Zabbix API item objects */ - ZabbixAPIDatasource.prototype.itemFindQuery = function(template) { + ZabbixAPIDatasource.prototype.itemFindQuery = function(groups, hosts, apps) { var promises = []; // Get hostids from names - if (template.host && template.host != '*') { - promises.push(this.findZabbixHost(template.host)); + if (hosts && hosts != '*') { + promises.push(this.findZabbixHost(hosts)); } // Get groupids from names - else if (template.group && template.group != '*') { - promises.push(this.findZabbixGroup(template.group)); + else if (groups && groups != '*') { + promises.push(this.findZabbixGroup(groups)); } // Get applicationids from names - if (template.app && template.app != '*') { - promises.push(this.findZabbixApp(template.app)); + if (apps && apps != '*') { + promises.push(this.findZabbixApp(apps)); } var self = this; @@ -533,16 +533,16 @@ function (angular, _, kbn) { }; - ZabbixAPIDatasource.prototype.appFindQuery = function(template) { + ZabbixAPIDatasource.prototype.appFindQuery = function(hosts, groups) { var promises = []; // Get hostids from names - if (template.host && template.host != '*') { - promises.push(this.findZabbixHost(template.host)); + if (hosts && hosts != '*') { + promises.push(this.findZabbixHost(hosts)); } // Get groupids from names - else if (template.group && template.group != '*') { - promises.push(this.findZabbixGroup(template.group)); + else if (groups && groups != '*') { + promises.push(this.findZabbixGroup(groups)); } var self = this; @@ -560,9 +560,9 @@ function (angular, _, kbn) { }; - ZabbixAPIDatasource.prototype.hostFindQuery = function(template) { + ZabbixAPIDatasource.prototype.hostFindQuery = function(groups) { var self = this; - return this.findZabbixGroup(template.group).then(function (results) { + return this.findZabbixGroup(groups).then(function (results) { results = _.flatten(results); var groupids = _.map(_.filter(results, function (object) { return object.groupid; @@ -573,6 +573,11 @@ 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); diff --git a/zabbix/queryCtrl.js b/zabbix/queryCtrl.js index 116f22e..f4ec779 100644 --- a/zabbix/queryCtrl.js +++ b/zabbix/queryCtrl.js @@ -150,9 +150,9 @@ function (angular, _) { $scope.metric.hostList = []; addTemplatedVariables($scope.metric.hostList); - var groupid = $scope.target.hostGroup ? $scope.target.hostGroup.groupid: null; - $scope.datasource.performHostSuggestQuery(groupid).then(function (series) { - $scope.metric.hostList = $scope.metric.hostList.concat(series); + var groups = $scope.target.hostGroup ? splitMetrics(templateSrv.replace($scope.target.hostGroup.name)) : []; + $scope.datasource.hostFindQuery(groups).then(function (hosts) { + $scope.metric.hostList = $scope.metric.hostList.concat(hosts); if ($scope.target.host) { $scope.target.host = $scope.metric.hostList.filter(function (item, index, array) { @@ -171,10 +171,10 @@ function (angular, _) { $scope.metric.applicationList = []; addTemplatedVariables($scope.metric.applicationList); - var hostid = $scope.target.host ? $scope.target.host.hostid : null; - var groupid = $scope.target.hostGroup ? $scope.target.hostGroup.groupid: null; - $scope.datasource.performAppSuggestQuery(hostid, groupid).then(function (series) { - var apps = _.map(_.uniq(_.map(series, 'name')), function (appname) { + var groups = $scope.target.hostGroup ? splitMetrics(templateSrv.replace($scope.target.hostGroup.name)) : []; + var hosts = $scope.target.host ? splitMetrics(templateSrv.replace($scope.target.host.name)) : []; + $scope.datasource.appFindQuery(hosts, groups).then(function (apps) { + var apps = _.map(_.uniq(_.map(apps, 'name')), function (appname) { return {name: appname}; }); $scope.metric.applicationList = $scope.metric.applicationList.concat(apps); @@ -196,29 +196,24 @@ function (angular, _) { $scope.metric.itemList = []; addTemplatedVariables($scope.metric.itemList); - var groupids = $scope.target.hostGroup ? $scope.target.hostGroup.groupid: null; - var hostids = $scope.target.host ? $scope.target.host.hostid : null; - var application = $scope.target.application || null; + var groups = $scope.target.hostGroup ? splitMetrics(templateSrv.replace($scope.target.hostGroup.name)) : []; + var hosts = $scope.target.host ? splitMetrics(templateSrv.replace($scope.target.host.name)) : []; + var apps = $scope.target.application ? splitMetrics(templateSrv.replace($scope.target.application.name)) : []; + $scope.datasource.itemFindQuery(groups, hosts, apps).then(function (items) { + $scope.metric.itemList = $scope.metric.itemList.concat(items); - // Get application ids from name - $scope.datasource.findZabbixApp(application).then(function (result) { - var applicationids = _.map(result, 'applicationid'); - $scope.datasource.performItemSuggestQuery(hostids, applicationids, groupids).then(function (series) { - $scope.metric.itemList = $scope.metric.itemList.concat(series); - - // Expand item parameters - $scope.metric.itemList.forEach(function (item, index, array) { - if (item && item.key_ && item.name) { - item.name = expandItemName(item); - } - }); - if ($scope.target.item) { - $scope.target.item = $scope.metric.itemList.filter(function (item, index, array) { - // Find selected item in metric.hostList - return item.name == $scope.target.item.name; - }).pop(); + // Expand item parameters + $scope.metric.itemList.forEach(function (item, index, array) { + if (item && item.key_ && item.name) { + item.name = expandItemName(item); } }); + if ($scope.target.item) { + $scope.target.item = $scope.metric.itemList.filter(function (item, index, array) { + // Find selected item in metric.hostList + return item.name == $scope.target.item.name; + }).pop(); + } }); }; @@ -276,3 +271,17 @@ function (angular, _) { }); }); + + +/** + * 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) +} \ No newline at end of file From 485cfd6687402e92074955c533da7e59e78f3df3 Mon Sep 17 00:00:00 2001 From: Alexander Zobnin Date: Fri, 5 Jun 2015 14:50:13 +0300 Subject: [PATCH 38/49] Fixed query editor: show only unique item names. --- zabbix/queryCtrl.js | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/zabbix/queryCtrl.js b/zabbix/queryCtrl.js index f4ec779..af7dd10 100644 --- a/zabbix/queryCtrl.js +++ b/zabbix/queryCtrl.js @@ -174,6 +174,7 @@ function (angular, _) { var groups = $scope.target.hostGroup ? splitMetrics(templateSrv.replace($scope.target.hostGroup.name)) : []; var hosts = $scope.target.host ? splitMetrics(templateSrv.replace($scope.target.host.name)) : []; $scope.datasource.appFindQuery(hosts, groups).then(function (apps) { + // TODO: work with app names, not objects var apps = _.map(_.uniq(_.map(apps, 'name')), function (appname) { return {name: appname}; }); @@ -200,7 +201,11 @@ function (angular, _) { var hosts = $scope.target.host ? splitMetrics(templateSrv.replace($scope.target.host.name)) : []; var apps = $scope.target.application ? splitMetrics(templateSrv.replace($scope.target.application.name)) : []; $scope.datasource.itemFindQuery(groups, hosts, apps).then(function (items) { - $scope.metric.itemList = $scope.metric.itemList.concat(items); + // Show only unique item names + var uniq_items = _.uniq(items, function (item) { + return expandItemName(item); + }); + $scope.metric.itemList = $scope.metric.itemList.concat(uniq_items); // Expand item parameters $scope.metric.itemList.forEach(function (item, index, array) { From 1e3af4c7427c5897332ec533600481d9df5ddc4a Mon Sep 17 00:00:00 2001 From: Alexander Zobnin Date: Fri, 5 Jun 2015 15:04:06 +0300 Subject: [PATCH 39/49] Testing unified style for templated and regular queries. --- zabbix/datasource.js | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/zabbix/datasource.js b/zabbix/datasource.js index 20330f6..64bc7df 100644 --- a/zabbix/datasource.js +++ b/zabbix/datasource.js @@ -52,7 +52,8 @@ function (angular, _, kbn) { return []; } - if (!target.item.templated) { + // From !target.item.templated for testing + if (false) { // Perform request and then handle result var item = [target.item]; From beee9973d7452e76277e7db0cbff43125110984e Mon Sep 17 00:00:00 2001 From: Alexander Zobnin Date: Mon, 8 Jun 2015 16:25:20 +0300 Subject: [PATCH 40/49] Improved query editor - more flexible metric selection. --- .gitignore | 2 + zabbix/datasource.js | 125 ++++++++++++++++++++++--------------------- zabbix/queryCtrl.js | 15 +++--- 3 files changed, 73 insertions(+), 69 deletions(-) diff --git a/.gitignore b/.gitignore index b543f8f..20d58b0 100644 --- a/.gitignore +++ b/.gitignore @@ -1,2 +1,4 @@ *.sublime-workspace *.sublime-project + +.idea/ \ No newline at end of file diff --git a/zabbix/datasource.js b/zabbix/datasource.js index 64bc7df..4cc8ec6 100644 --- a/zabbix/datasource.js +++ b/zabbix/datasource.js @@ -48,61 +48,51 @@ function (angular, _, kbn) { var promises = _.map(options.targets, function(target) { // Remove undefined and hidden targets - if (target.hide || !target.item) { + if (target.hide) { return []; } - // From !target.item.templated for testing - if (false) { + var groupname = target.hostGroup ? templateSrv.replace(target.hostGroup.name) : undefined; + var hostname = target.host ? templateSrv.replace(target.host.name) : undefined; + var appname = target.application ? templateSrv.replace(target.application.name) : undefined; + var itemname = target.item ? templateSrv.replace(target.item.name) : undefined; - // Perform request and then handle result - var item = [target.item]; - var alias = [{ - itemid: target.item.itemid, - key_: '', - name: target.alias - }]; - return this.performTimeSeriesQuery(item, from, to).then(_.partial( - this.handleHistoryResponse, alias)); - } else { - /** - * Handle templated target - */ + // Extract zabbix hosts from hosts string: + // "{host1,host2,...,hostN}" --> [host1, host2, ..., hostN] + var hosts = hostname ? splitMetrics(hostname) : undefined; - var itemname = templateSrv.replace(target.item.name); - var hostname = templateSrv.replace(target.host.name); + var groups = groupname ? splitMetrics(groupname) : undefined; + var apps = appname ? splitMetrics(appname) : undefined; - // Extract zabbix hosts from hosts string: - // "{host1,host2,...,hostN}" --> [host1, host2, ..., hostN] - var hosts = splitMetrics(hostname); + // Remove hostnames from item names and then + // extract item names + // "hostname: itemname" --> "itemname" + var delete_hostname_pattern = /(?:\[[\w\.]+\]\:\s)/g; + var itemnames = itemname ? splitMetrics(itemname.replace(delete_hostname_pattern, '')) : []; + //var aliases = itemname.match(itemname_pattern); - // 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 aliases = itemname.match(itemname_pattern); + // Don't perform query for high number of items + // to prevent Grafana slowdown + if (itemnames.length < this.limitmetrics) { - // Don't perform query for high number of items - // to prevent Grafana slowdown - if (itemnames && (itemnames.length < this.limitmetrics)) { - - // Select the host that the item belongs for multiple hosts request - if (hosts.length > 1) { - var selectHosts = true; - } - - // Find items by item names and perform queries - var self = this; - return this.findZabbixItem(hosts, itemnames, selectHosts) - .then(function (items) { - items = _.flatten(items); - return self.performTimeSeriesQuery(items, from, to) + // Find items by item names and perform queries + var self = this; + return this.itemFindQuery(groups, hosts, apps) + .then(function (items) { + if (itemnames.length) { + return _.filter(items, function (item) { + return _.contains(itemnames, expandItemName(item)); + }); + } else { + return items; + } + }).then(function (items) { + items = _.flatten(items); + return self.performTimeSeriesQuery(items, from, to) .then(_.partial(self.handleHistoryResponse, items)); - }); - } else { - return []; - } + }); + } else { + return []; } }, this); @@ -394,7 +384,8 @@ function (angular, _, kbn) { var hostids = _.map(hosts, 'hostid'); var params = { output: ['name', 'key_', 'value_type'], - hostids: hostids + hostids: hostids, + searchWildcardsEnabled: true }; if (selectHosts) { params.selectHosts = ['name']; @@ -519,15 +510,21 @@ function (angular, _, kbn) { var self = this; return $q.all(promises).then(function (results) { results = _.flatten(results); - var groupids = _.map(_.filter(results, function (object) { - return object.groupid; - }), 'groupid'); - var hostids = _.map(_.filter(results, function (object) { - return object.hostid; - }), 'hostid'); - var applicationids = _.map(_.filter(results, function (object) { - return object.applicationid; - }), 'applicationid'); + if (groups) { + var groupids = _.map(_.filter(results, function (object) { + return object.groupid; + }), 'groupid'); + } + if (hosts) { + var hostids = _.map(_.filter(results, function (object) { + return object.hostid; + }), 'hostid'); + } + if (apps) { + var applicationids = _.map(_.filter(results, function (object) { + return object.applicationid; + }), 'applicationid'); + } return self.performItemSuggestQuery(hostids, applicationids, groupids); }); @@ -549,12 +546,16 @@ function (angular, _, kbn) { var self = this; return $q.all(promises).then(function (results) { results = _.flatten(results); - var groupids = _.map(_.filter(results, function (object) { - return object.groupid; - }), 'groupid'); - var hostids = _.map(_.filter(results, function (object) { - return object.hostid; - }), 'hostid'); + if (groups) { + var groupids = _.map(_.filter(results, function (object) { + return object.groupid; + }), 'groupid'); + } + if (hosts) { + var hostids = _.map(_.filter(results, function (object) { + return object.hostid; + }), 'hostid'); + } return self.performAppSuggestQuery(hostids, groupids); }); diff --git a/zabbix/queryCtrl.js b/zabbix/queryCtrl.js index af7dd10..e44f379 100644 --- a/zabbix/queryCtrl.js +++ b/zabbix/queryCtrl.js @@ -57,6 +57,7 @@ function (angular, _) { $scope.selectHostGroup = function() { $scope.updateHostList() $scope.updateAppList(); + $scope.updateItemList(); $scope.target.errors = validateTarget($scope.target); if (!_.isEqual($scope.oldTarget, $scope.target) && _.isEmpty($scope.target.errors)) { @@ -70,8 +71,8 @@ function (angular, _) { * Call when host selected */ $scope.selectHost = function() { - $scope.updateItemList(); $scope.updateAppList(); + $scope.updateItemList(); $scope.target.errors = validateTarget($scope.target); if (!_.isEqual($scope.oldTarget, $scope.target) && _.isEmpty($scope.target.errors)) { @@ -150,7 +151,7 @@ function (angular, _) { $scope.metric.hostList = []; addTemplatedVariables($scope.metric.hostList); - var groups = $scope.target.hostGroup ? splitMetrics(templateSrv.replace($scope.target.hostGroup.name)) : []; + var groups = $scope.target.hostGroup ? splitMetrics(templateSrv.replace($scope.target.hostGroup.name)) : undefined; $scope.datasource.hostFindQuery(groups).then(function (hosts) { $scope.metric.hostList = $scope.metric.hostList.concat(hosts); @@ -171,8 +172,8 @@ function (angular, _) { $scope.metric.applicationList = []; addTemplatedVariables($scope.metric.applicationList); - var groups = $scope.target.hostGroup ? splitMetrics(templateSrv.replace($scope.target.hostGroup.name)) : []; - var hosts = $scope.target.host ? splitMetrics(templateSrv.replace($scope.target.host.name)) : []; + var groups = $scope.target.hostGroup ? splitMetrics(templateSrv.replace($scope.target.hostGroup.name)) : undefined; + var hosts = $scope.target.host ? splitMetrics(templateSrv.replace($scope.target.host.name)) : undefined; $scope.datasource.appFindQuery(hosts, groups).then(function (apps) { // TODO: work with app names, not objects var apps = _.map(_.uniq(_.map(apps, 'name')), function (appname) { @@ -197,9 +198,9 @@ function (angular, _) { $scope.metric.itemList = []; addTemplatedVariables($scope.metric.itemList); - var groups = $scope.target.hostGroup ? splitMetrics(templateSrv.replace($scope.target.hostGroup.name)) : []; - var hosts = $scope.target.host ? splitMetrics(templateSrv.replace($scope.target.host.name)) : []; - var apps = $scope.target.application ? splitMetrics(templateSrv.replace($scope.target.application.name)) : []; + var groups = $scope.target.hostGroup ? splitMetrics(templateSrv.replace($scope.target.hostGroup.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; $scope.datasource.itemFindQuery(groups, hosts, apps).then(function (items) { // Show only unique item names var uniq_items = _.uniq(items, function (item) { From ffd4573efa15fe33edda90b15680f16212bc42c2 Mon Sep 17 00:00:00 2001 From: Alexander Zobnin Date: Mon, 8 Jun 2015 20:16:13 +0300 Subject: [PATCH 41/49] Increase limitmetrics default to 50. --- zabbix/plugin.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/zabbix/plugin.json b/zabbix/plugin.json index 460ffda..75f81cf 100644 --- a/zabbix/plugin.json +++ b/zabbix/plugin.json @@ -16,7 +16,7 @@ "username": "guest", "password": "", - "limitmetrics": 20, + "limitmetrics": 50, "metrics": true, "annotations": true From d9c08ca43932b0716732f98258c6c2d2e8345047 Mon Sep 17 00:00:00 2001 From: Alexander Zobnin Date: Tue, 9 Jun 2015 11:58:55 +0300 Subject: [PATCH 42/49] Improve metrics filtering in query editor. --- zabbix/datasource.js | 16 ++++++++-------- zabbix/partials/query.editor.html | 18 +++++++++--------- zabbix/queryCtrl.js | 20 ++++++++++---------- 3 files changed, 27 insertions(+), 27 deletions(-) diff --git a/zabbix/datasource.js b/zabbix/datasource.js index 4cc8ec6..cc8d95c 100644 --- a/zabbix/datasource.js +++ b/zabbix/datasource.js @@ -48,11 +48,11 @@ function (angular, _, kbn) { var promises = _.map(options.targets, function(target) { // Remove undefined and hidden targets - if (target.hide) { + if (target.hide || !target.group || !target.host || !target.application || !target.item) { return []; } - var groupname = target.hostGroup ? templateSrv.replace(target.hostGroup.name) : undefined; + var groupname = target.group ? templateSrv.replace(target.group.name) : undefined; var hostname = target.host ? templateSrv.replace(target.host.name) : undefined; var appname = target.application ? templateSrv.replace(target.application.name) : undefined; var itemname = target.item ? templateSrv.replace(target.item.name) : undefined; @@ -79,7 +79,7 @@ function (angular, _, kbn) { var self = this; return this.itemFindQuery(groups, hosts, apps) .then(function (items) { - if (itemnames.length) { + if (itemnames != 'All') { return _.filter(items, function (item) { return _.contains(itemnames, expandItemName(item)); }); @@ -499,11 +499,11 @@ function (angular, _, kbn) { promises.push(this.findZabbixHost(hosts)); } // Get groupids from names - else if (groups && groups != '*') { + else if (groups) { promises.push(this.findZabbixGroup(groups)); } // Get applicationids from names - if (apps && apps != '*') { + if (apps) { promises.push(this.findZabbixApp(apps)); } @@ -515,7 +515,7 @@ function (angular, _, kbn) { return object.groupid; }), 'groupid'); } - if (hosts) { + if (hosts && hosts != '*') { var hostids = _.map(_.filter(results, function (object) { return object.hostid; }), 'hostid'); @@ -539,7 +539,7 @@ function (angular, _, kbn) { promises.push(this.findZabbixHost(hosts)); } // Get groupids from names - else if (groups && groups != '*') { + else if (groups) { promises.push(this.findZabbixGroup(groups)); } @@ -551,7 +551,7 @@ function (angular, _, kbn) { return object.groupid; }), 'groupid'); } - if (hosts) { + if (hosts && hosts != '*') { var hostids = _.map(_.filter(results, function (object) { return object.hostid; }), 'hostid'); diff --git a/zabbix/partials/query.editor.html b/zabbix/partials/query.editor.html index 05d969d..f1985fd 100644 --- a/zabbix/partials/query.editor.html +++ b/zabbix/partials/query.editor.html @@ -57,10 +57,10 @@ - + ng-options="host.visible_name ? host.visible_name : host.name for host in metric.hostList" > + - + ng-options="app.visible_name ? app.visible_name : app.name for app in metric.applicationList" > + - + Date: Tue, 9 Jun 2015 12:24:58 +0300 Subject: [PATCH 43/49] Add "track by item.name" for metrics selection. --- zabbix/partials/query.editor.html | 8 ++++---- zabbix/queryCtrl.js | 31 ++----------------------------- 2 files changed, 6 insertions(+), 33 deletions(-) diff --git a/zabbix/partials/query.editor.html b/zabbix/partials/query.editor.html index f1985fd..57709fb 100644 --- a/zabbix/partials/query.editor.html +++ b/zabbix/partials/query.editor.html @@ -59,7 +59,7 @@ ng-change="selectHostGroup()" ng-model="target.group" bs-tooltip="target.group.name.length > 25 ? target.group.name : ''" - ng-options="group.visible_name ? group.visible_name : group.name for group in metric.groupList" > + ng-options="group.visible_name ? group.visible_name : group.name for group in metric.groupList track by group.name" > + ng-options="host.visible_name ? host.visible_name : host.name for host in metric.hostList track by host.name" > + ng-options="app.visible_name ? app.visible_name : app.name for app in metric.applicationList track by app.name" > + ng-options="item.name for item in metric.itemList track by item.name" > Date: Tue, 9 Jun 2015 12:39:12 +0300 Subject: [PATCH 44/49] Compact updateItemList() function. --- zabbix/datasource.js | 2 +- zabbix/queryCtrl.js | 11 +++-------- 2 files changed, 4 insertions(+), 9 deletions(-) diff --git a/zabbix/datasource.js b/zabbix/datasource.js index cc8d95c..2be6941 100644 --- a/zabbix/datasource.js +++ b/zabbix/datasource.js @@ -2,7 +2,7 @@ define([ 'angular', 'lodash', 'kbn', - './queryCtrl', + './queryCtrl' ], function (angular, _, kbn) { 'use strict'; diff --git a/zabbix/queryCtrl.js b/zabbix/queryCtrl.js index e166d96..ce9f52d 100644 --- a/zabbix/queryCtrl.js +++ b/zabbix/queryCtrl.js @@ -182,17 +182,12 @@ function (angular, _) { var apps = $scope.target.application ? splitMetrics(templateSrv.replace($scope.target.application.name)) : undefined; $scope.datasource.itemFindQuery(groups, hosts, apps).then(function (items) { // Show only unique item names - var uniq_items = _.uniq(items, function (item) { + var uniq_items = _.map(_.uniq(items, function (item) { return expandItemName(item); + }), function (item) { + return {name: expandItemName(item)} }); $scope.metric.itemList = $scope.metric.itemList.concat(uniq_items); - - // Expand item parameters - $scope.metric.itemList.forEach(function (item, index, array) { - if (item && item.key_ && item.name) { - item.name = expandItemName(item); - } - }); }); }; From 3a4dd9616f01972b4567a69482a590d3d345f62b Mon Sep 17 00:00:00 2001 From: Alexander Zobnin Date: Tue, 9 Jun 2015 13:03:55 +0300 Subject: [PATCH 45/49] Fixed item tooltip in query editor. --- zabbix/partials/query.editor.html | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/zabbix/partials/query.editor.html b/zabbix/partials/query.editor.html index 57709fb..ce67aaf 100644 --- a/zabbix/partials/query.editor.html +++ b/zabbix/partials/query.editor.html @@ -46,7 +46,7 @@
  • - From 29cbe303aeb206c471ef626355f5ce781c89b9ca Mon Sep 17 00:00:00 2001 From: Alexander Zobnin Date: Tue, 9 Jun 2015 16:41:31 +0300 Subject: [PATCH 46/49] query() method refactoring. --- zabbix/datasource.js | 68 +++++++++++++++++++++++--------------------- 1 file changed, 35 insertions(+), 33 deletions(-) diff --git a/zabbix/datasource.js b/zabbix/datasource.js index 2be6941..96b0ccb 100644 --- a/zabbix/datasource.js +++ b/zabbix/datasource.js @@ -47,53 +47,55 @@ function (angular, _, kbn) { // Create request for each target var promises = _.map(options.targets, function(target) { - // Remove undefined and hidden targets - if (target.hide || !target.group || !target.host || !target.application || !target.item) { + // Don't show undefined and hidden targets + if (target.hide || !target.group || !target.host + || !target.application || !target.item) { return []; } - var groupname = target.group ? templateSrv.replace(target.group.name) : undefined; - var hostname = target.host ? templateSrv.replace(target.host.name) : undefined; - var appname = target.application ? templateSrv.replace(target.application.name) : undefined; - var itemname = target.item ? templateSrv.replace(target.item.name) : undefined; + // Replace templated variables + var groupname = templateSrv.replace(target.group.name); + var hostname = templateSrv.replace(target.host.name); + var appname = templateSrv.replace(target.application.name); + var itemname = templateSrv.replace(target.item.name); - // Extract zabbix hosts from hosts string: + // Extract zabbix groups, hosts and apps from string: // "{host1,host2,...,hostN}" --> [host1, host2, ..., hostN] - var hosts = hostname ? splitMetrics(hostname) : undefined; - - var groups = groupname ? splitMetrics(groupname) : undefined; - var apps = appname ? splitMetrics(appname) : undefined; + var groups = splitMetrics(groupname); + var hosts = splitMetrics(hostname); + var apps = splitMetrics(appname); // Remove hostnames from item names and then // extract item names // "hostname: itemname" --> "itemname" var delete_hostname_pattern = /(?:\[[\w\.]+\]\:\s)/g; - var itemnames = itemname ? splitMetrics(itemname.replace(delete_hostname_pattern, '')) : []; - //var aliases = itemname.match(itemname_pattern); + var itemnames = splitMetrics(itemname.replace(delete_hostname_pattern, '')); - // Don't perform query for high number of items - // to prevent Grafana slowdown - if (itemnames.length < this.limitmetrics) { + // Find items by item names and perform queries + var self = this; + return this.itemFindQuery(groups, hosts, apps) + .then(function (items) { + if (itemnames == 'All') { + return items; + } else { - // Find items by item names and perform queries - var self = this; - return this.itemFindQuery(groups, hosts, apps) - .then(function (items) { - if (itemnames != 'All') { - return _.filter(items, function (item) { - return _.contains(itemnames, expandItemName(item)); - }); - } else { - return items; - } - }).then(function (items) { + // Filtering items + return _.filter(items, function (item) { + return _.contains(itemnames, expandItemName(item)); + }); + } + }).then(function (items) { + + // Don't perform query for high number of items + // to prevent Grafana slowdown + if (items.length > self.limitmetrics) { + return []; + } else { items = _.flatten(items); return self.performTimeSeriesQuery(items, from, to) - .then(_.partial(self.handleHistoryResponse, items)); - }); - } else { - return []; - } + .then(_.partial(self.handleHistoryResponse, items)); + } + }); }, this); return $q.all(_.flatten(promises)).then(function (results) { From e2c94965ba73474dddf761f73b8fcaaa62e846aa Mon Sep 17 00:00:00 2001 From: Alexander Zobnin Date: Tue, 9 Jun 2015 17:09:25 +0300 Subject: [PATCH 47/49] datasource.js refactoring. --- zabbix/datasource.js | 44 +++++++++++++++++++++++++++++++++++++++----- 1 file changed, 39 insertions(+), 5 deletions(-) diff --git a/zabbix/datasource.js b/zabbix/datasource.js index 96b0ccb..78b01bf 100644 --- a/zabbix/datasource.js +++ b/zabbix/datasource.js @@ -35,6 +35,7 @@ function (angular, _, kbn) { * * @param {Object} options Query options. Contains time range, targets * and other info. + * * @return {Object} Grafana metrics object with timeseries data * for each target. */ @@ -110,6 +111,7 @@ function (angular, _, kbn) { * @param {Array} items Array of Zabbix item objects * @param {Number} start Time in seconds * @param {Number} end Time in seconds + * * @return {Array} Array of Zabbix history objects */ ZabbixAPIDatasource.prototype.performTimeSeriesQuery = function(items, start, end) { @@ -145,6 +147,7 @@ function (angular, _, kbn) { * * @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", @@ -175,6 +178,7 @@ function (angular, _, kbn) { var series = { target: (item.hosts ? item.hosts[0].name+': ' : '') + expandItemName(item), datapoints: _.map(history, function (p) { + // Value must be a number for properly work var value = Number(p.value); return [value, p.clock * 1000]; @@ -190,7 +194,8 @@ function (angular, _, kbn) { * * @param {string} method Zabbix API method name * @param {object} params method params - * @return {object} result + * + * @return {object} data.result field or [] */ ZabbixAPIDatasource.prototype.performZabbixAPIRequest = function(method, params) { var options = { @@ -213,8 +218,8 @@ function (angular, _, kbn) { if (!response.data) { return []; } + // Handle Zabbix API errors else if (response.data.error) { - // Handle Zabbix API errors // Handle auth errors if (response.data.error.data == "Session terminated, re-login, please." || @@ -306,7 +311,15 @@ function (angular, _, kbn) { }; - // Get the list of host items + /** + * Items request + * + * @param {string or Array} hostids /////////////////////////// + * @param {string or Array} applicationids // Zabbix API parameters // + * @param {string or Array} groupids /////////////////////////// + * + * @return {string or Array} Array of Zabbix API item objects + */ ZabbixAPIDatasource.prototype.performItemSuggestQuery = function(hostids, applicationids, /* optional */ groupids) { var params = { output: ['name', 'key_', 'value_type', 'delay'], @@ -321,16 +334,19 @@ function (angular, _, kbn) { monitored: true, searchByAny: true }; + + // Filter by hosts or by groups if (hostids) { params.hostids = hostids; - } - else if (groupids) { + } else if (groupids) { params.groupids = groupids; } + // If application selected return only relative items if (applicationids) { params.applicationids = applicationids; } + // Return host property for multiple hosts if (!hostids || (_.isArray(hostids) && hostids.length > 1)) { params.selectHosts = ['name']; @@ -340,6 +356,12 @@ function (angular, _, kbn) { }; + /** + * Find groups by names + * + * @param {string or array} group group names + * @return {array} array of Zabbix API hostgroup objects + */ ZabbixAPIDatasource.prototype.findZabbixGroup = function (group) { var params = { output: ['name'], @@ -353,6 +375,12 @@ function (angular, _, kbn) { }; + /** + * Find hosts by names + * + * @param {string or array} hostnames hosts names + * @return {array} array of Zabbix API host objects + */ ZabbixAPIDatasource.prototype.findZabbixHost = function (hostnames) { var params = { output: ['host', 'name'], @@ -367,6 +395,12 @@ function (angular, _, kbn) { }; + /** + * Find applications by names + * + * @param {string or array} application applications names + * @return {array} array of Zabbix API application objects + */ ZabbixAPIDatasource.prototype.findZabbixApp = function (application) { var params = { output: ['name'], From 2390172c7ad0e6ff26114cd848d4737943128c05 Mon Sep 17 00:00:00 2001 From: Alexander Zobnin Date: Tue, 9 Jun 2015 17:10:36 +0300 Subject: [PATCH 48/49] Remove unused findZabbixItem() method. --- zabbix/datasource.js | 22 ---------------------- 1 file changed, 22 deletions(-) diff --git a/zabbix/datasource.js b/zabbix/datasource.js index 78b01bf..b8055bc 100644 --- a/zabbix/datasource.js +++ b/zabbix/datasource.js @@ -414,28 +414,6 @@ function (angular, _, kbn) { }; - ZabbixAPIDatasource.prototype.findZabbixItem = function (hosts, itemnames, /* optional */ selectHosts) { - var self = this; - return this.findZabbixHost(hosts).then(function (hosts) { - var hostids = _.map(hosts, 'hostid'); - var params = { - output: ['name', 'key_', 'value_type'], - hostids: hostids, - searchWildcardsEnabled: true - }; - if (selectHosts) { - params.selectHosts = ['name']; - } - return self.performZabbixAPIRequest('item.get', params) - .then(function (items) { - return _.filter(items, function (item) { - return _.contains(itemnames, expandItemName(item)); - }); - }); - }); - }; - - /** * For templated query * From fba21c45c9320468d0c66c5e02c27ea9765479c1 Mon Sep 17 00:00:00 2001 From: Alexander Zobnin Date: Tue, 9 Jun 2015 17:25:34 +0300 Subject: [PATCH 49/49] datasource.js refactoring. --- zabbix/datasource.js | 35 +++++++++++++++++++++++------------ 1 file changed, 23 insertions(+), 12 deletions(-) diff --git a/zabbix/datasource.js b/zabbix/datasource.js index b8055bc..7e487a5 100644 --- a/zabbix/datasource.js +++ b/zabbix/datasource.js @@ -415,7 +415,8 @@ function (angular, _, kbn) { /** - * For templated query + * For templated query. + * Find metrics from templated request. * * @param {string} query Query from Templating * @return {string} Metric name - group, host, app or item or list @@ -443,7 +444,6 @@ function (angular, _, kbn) { return _.map(result, function (item) { var itemname = expandItemName(item) return { - // TODO: select only unique names text: itemname, expandable: false }; @@ -483,7 +483,7 @@ function (angular, _, kbn) { }); }); } - // Return empty object + // Return empty object for invalid request else { var d = $q.defer(); d.resolve([]); @@ -493,17 +493,14 @@ function (angular, _, kbn) { /** - * Find items from templated request + * Find items belongs to passed groups, hosts and + * applications * - * @param {object} template Template object in format: - * { - * group: [groupnames], - * host: [hostnames], - * app: [appnames], - * item: [itemnames] - * } + * @param {string or array} groups + * @param {string or array} hosts + * @param {string or array} apps * - * @return {Array} Array of Zabbix API item objects + * @return {array} array of Zabbix API item objects */ ZabbixAPIDatasource.prototype.itemFindQuery = function(groups, hosts, apps) { var promises = []; @@ -545,6 +542,14 @@ function (angular, _, kbn) { }; + /** + * Find applications belongs to passed groups and hosts + * + * @param {string or array} hosts + * @param {string or array} groups + * + * @return {array} array of Zabbix API application objects + */ ZabbixAPIDatasource.prototype.appFindQuery = function(hosts, groups) { var promises = []; @@ -576,6 +581,12 @@ function (angular, _, kbn) { }; + /** + * Find hosts belongs to passed groups + * + * @param {string or array} groups + * @return {array} array of Zabbix API host objects + */ ZabbixAPIDatasource.prototype.hostFindQuery = function(groups) { var self = this; return this.findZabbixGroup(groups).then(function (results) {