From 530a833844234cbac95d6b899fb5cdcd8145878b Mon Sep 17 00:00:00 2001 From: Alexander Zobnin Date: Wed, 19 Aug 2015 20:25:59 +0300 Subject: [PATCH 001/205] iss #71 - Able to use regex for extracting a part of the text value. --- zabbix/datasource.js | 17 ++++++++++++++- zabbix/partials/query.editor.html | 36 +++++++++++++++++++++++++------ 2 files changed, 46 insertions(+), 7 deletions(-) diff --git a/zabbix/datasource.js b/zabbix/datasource.js index 5fa61b4cb..1cc62125d 100644 --- a/zabbix/datasource.js +++ b/zabbix/datasource.js @@ -218,7 +218,22 @@ function (angular, _, kbn) { return { target: target.item.name, datapoints: _.map(history, function (p) { - return [p.value, p.clock * 1000]; + var value = p.value; + if (target.textFilter) { + var text_extract_pattern = new RegExp(templateSrv.replace(target.textFilter, options.scopedVars)); + //var text_extract_pattern = new RegExp(target.textFilter); + var result = text_extract_pattern.exec(value); + if (result) { + if (target.useCaptureGroups) { + value = result[1]; + } else { + value = result[0]; + } + } else { + value = null; + } + } + return [value, p.clock * 1000]; }) }; }); diff --git a/zabbix/partials/query.editor.html b/zabbix/partials/query.editor.html index 26e2ea5f2..533d1f8ca 100644 --- a/zabbix/partials/query.editor.html +++ b/zabbix/partials/query.editor.html @@ -21,14 +21,14 @@ Numeric metrics -
  • - IT services -
  • Text metrics
  • +
  • + IT services +
  • Duplicate
  • Move up
  • @@ -81,7 +81,7 @@
    -
    +
    • Trends @@ -48,16 +48,3 @@
      Zabbix API details
    -
    -
      -
    • - Metrics limit -
    • -
    • - -
    • -
    -
    -
    From d53d095eb2f2d27fba29f6295e082a240dac8707 Mon Sep 17 00:00:00 2001 From: Alexander Zobnin Date: Sat, 16 Jan 2016 14:52:28 +0300 Subject: [PATCH 031/205] Some styles improvements on data source edit page. --- plugins/datasource-zabbix/partials/config.html | 9 ++++----- 1 file changed, 4 insertions(+), 5 deletions(-) diff --git a/plugins/datasource-zabbix/partials/config.html b/plugins/datasource-zabbix/partials/config.html index d129ddcc8..969d8a950 100644 --- a/plugins/datasource-zabbix/partials/config.html +++ b/plugins/datasource-zabbix/partials/config.html @@ -28,16 +28,15 @@
    Zabbix API details
    • - Trends -
    • -
    • - Enable  + Trends  
    • -
    • +
    • Use trends from
    • From 96f2c2b275d0a9e1fc19d7c6c57c2b43a682d036 Mon Sep 17 00:00:00 2001 From: Alexander Zobnin Date: Sat, 16 Jan 2016 14:56:03 +0300 Subject: [PATCH 032/205] Fixed HTTP settings view on data source edit page. --- plugins/datasource-zabbix/partials/config.html | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/plugins/datasource-zabbix/partials/config.html b/plugins/datasource-zabbix/partials/config.html index 969d8a950..cde0b2124 100644 --- a/plugins/datasource-zabbix/partials/config.html +++ b/plugins/datasource-zabbix/partials/config.html @@ -1,6 +1,4 @@ -
      - -
      +
      Zabbix API details
      From 07835dcc83493b3321de681752a758ef553fbf82 Mon Sep 17 00:00:00 2001 From: Alexander Zobnin Date: Sat, 16 Jan 2016 15:10:36 +0300 Subject: [PATCH 033/205] Fixed linter error. --- plugins/datasource-zabbix/datasource.js | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/plugins/datasource-zabbix/datasource.js b/plugins/datasource-zabbix/datasource.js index addbc0f0d..4ca75ebc3 100644 --- a/plugins/datasource-zabbix/datasource.js +++ b/plugins/datasource-zabbix/datasource.js @@ -83,9 +83,10 @@ function (angular, _, dateMath) { var promises = _.map(options.targets, function(target) { if (target.mode !== 1) { + // Don't show undefined and hidden targets - if (target.hide || !target.group || !target.host - || !target.application || !target.item) { + if (target.hide || !target.group || !target.host || + !target.application || !target.item) { return []; } From 195dfa2d008d3582beea14b9ccd3f34c604f87d4 Mon Sep 17 00:00:00 2001 From: Alexander Zobnin Date: Sat, 16 Jan 2016 15:17:05 +0300 Subject: [PATCH 034/205] Added linter config. --- .gitignore | 6 +++--- .jscs.json | 13 +++++++++++++ .jshintrc | 36 ++++++++++++++++++++++++++++++++++++ 3 files changed, 52 insertions(+), 3 deletions(-) create mode 100644 .jscs.json create mode 100644 .jshintrc diff --git a/.gitignore b/.gitignore index 55fcca717..e368599a3 100644 --- a/.gitignore +++ b/.gitignore @@ -6,9 +6,9 @@ *.bat # Grafana linter config -.jshintrc -.jscs.json -.jsfmtrc +# .jshintrc +# .jscs.json +# .jsfmtrc # Builded docs docs/site/ diff --git a/.jscs.json b/.jscs.json new file mode 100644 index 000000000..dcf694dcc --- /dev/null +++ b/.jscs.json @@ -0,0 +1,13 @@ +{ + "disallowImplicitTypeConversion": ["string"], + "disallowKeywords": ["with"], + "disallowMultipleLineBreaks": true, + "disallowMixedSpacesAndTabs": true, + "disallowTrailingWhitespace": true, + "requireSpacesInFunctionExpression": { + "beforeOpeningCurlyBrace": true + }, + "disallowSpacesInsideArrayBrackets": true, + "disallowSpacesInsideParentheses": true, + "validateIndentation": 2 +} \ No newline at end of file diff --git a/.jshintrc b/.jshintrc new file mode 100644 index 000000000..99247999e --- /dev/null +++ b/.jshintrc @@ -0,0 +1,36 @@ +{ + "browser": true, + + "bitwise":false, + "curly": true, + "eqnull": true, + "globalstrict": true, + "devel": true, + "eqeqeq": true, + "forin": false, + "immed": true, + "supernew": true, + "expr": true, + "indent": 2, + "latedef": true, + "newcap": true, + "noarg": true, + "noempty": true, + "undef": true, + "boss": true, + "trailing": true, + "laxbreak": true, + "laxcomma": true, + "sub": true, + "unused": true, + "maxdepth": 6, + "maxlen": 140, + + "globals": { + "System": true, + "define": true, + "require": true, + "Chromath": false, + "setImmediate": true + } +} From 700ce47d5cb39b2fadf53340c1421cc289945591 Mon Sep 17 00:00:00 2001 From: Alexander Zobnin Date: Sat, 16 Jan 2016 16:34:59 +0300 Subject: [PATCH 035/205] Fixed datasources getting in trigger panel. --- plugins/panel-triggers/module.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/plugins/panel-triggers/module.js b/plugins/panel-triggers/module.js index 2b6b076f4..0894f1bab 100644 --- a/plugins/panel-triggers/module.js +++ b/plugins/panel-triggers/module.js @@ -94,7 +94,7 @@ function (angular, app, _, $, config, PanelMeta) { // Get zabbix data sources var datasources = _.filter(datasourceSrv.getMetricSources(), function(datasource) { - return datasource.meta.type === 'zabbix'; + return datasource.meta.id === 'zabbix'; }); $scope.datasources = _.map(datasources, 'name'); From 8d3a00d7837a04b221665a7044e524c616d0b51e Mon Sep 17 00:00:00 2001 From: Alexander Zobnin Date: Sat, 16 Jan 2016 19:34:45 +0300 Subject: [PATCH 036/205] Format api response. --- plugins/datasource-zabbix/zabbixAPIWrapper.js | 20 ++++------ plugins/datasource-zabbix/zabbixCacheSrv.js | 38 ++++++++++++++----- 2 files changed, 35 insertions(+), 23 deletions(-) diff --git a/plugins/datasource-zabbix/zabbixAPIWrapper.js b/plugins/datasource-zabbix/zabbixAPIWrapper.js index 51feb763f..9fe2ff23c 100644 --- a/plugins/datasource-zabbix/zabbixAPIWrapper.js +++ b/plugins/datasource-zabbix/zabbixAPIWrapper.js @@ -120,8 +120,7 @@ function (angular, _) { p.getGroups = function() { var params = { output: ['name'], - sortfield: 'name', - selectHosts: [] + sortfield: 'name' }; return this.performZabbixAPIRequest('hostgroup.get', params); @@ -130,7 +129,8 @@ function (angular, _) { p.getHosts = function() { var params = { output: ['name', 'host'], - sortfield: 'name' + sortfield: 'name', + selectGroups: [] }; return this.performZabbixAPIRequest('host.get', params); @@ -139,7 +139,8 @@ function (angular, _) { p.getApplications = function() { var params = { output: ['name'], - sortfield: 'name' + sortfield: 'name', + selectHosts: [] }; return this.performZabbixAPIRequest('application.get', params); @@ -147,16 +148,9 @@ function (angular, _) { p.getItems = function() { var params = { - output: ['name', 'key_', 'value_type'], + output: ['name', 'key_', 'value_type', 'hostid', 'status', 'state'], sortfield: 'name', - //Include web items in the result - webitems: true, - // Return only numeric items - filter: { - value_type: [0, 3] - }, - // Return only enabled items - monitored: true + selectApplications: [] }; return this.performZabbixAPIRequest('item.get', params); diff --git a/plugins/datasource-zabbix/zabbixCacheSrv.js b/plugins/datasource-zabbix/zabbixCacheSrv.js index 75d92bcb2..d65212a8a 100644 --- a/plugins/datasource-zabbix/zabbixCacheSrv.js +++ b/plugins/datasource-zabbix/zabbixCacheSrv.js @@ -1,7 +1,7 @@ define([ 'angular', 'lodash' - ], +], function (angular, _) { 'use strict'; @@ -11,9 +11,22 @@ function (angular, _) { function ZabbixCache(zabbixAPI, lifetime) { var self = this; + this.zabbixAPI = zabbixAPI; this.lifetime = lifetime; + this._groups = []; + this._hosts = []; + this._applications = []; + this._items = []; + + this.refresh(); + } + + var p = ZabbixCache.prototype; + + p.refresh = function () { + var self = this; var promises = [ this.zabbixAPI.getGroups(), this.zabbixAPI.getHosts(), @@ -22,17 +35,23 @@ function (angular, _) { ]; $q.all(promises).then(function (results) { - console.log(results); if (results.length) { self._groups = results[0]; - self._hosts = results[1]; + + self._hosts = _.forEach(results[1], function(host) { + host.groups = _.map(host.groups, 'groupid'); + return host; + }); + self._applications = groupApplications(results[2]); - self._items = results[3]; + + self._items = _.forEach(results[3], function(item) { + item.applications = _.map(item.applications, 'applicationid'); + return item; + }); } }); - } - - var p = ZabbixCache.prototype; + }; p.getGroups = function() { return this._groups; @@ -52,14 +71,13 @@ function (angular, _) { /** * Group Zabbix applications by name - * @param {[type]} applications [description] - * @return {[type]} [description] */ function groupApplications(applications) { return _.map(_.groupBy(applications, 'name'), function (value, key) { return { name: key, - ids: _.map(value, 'applicationid') + applicationids: _.map(value, 'applicationid'), + hostids: _.uniq(_.map(_.flatten(value, 'hosts'), 'hostid')) }; }); } From 48816ecccb771f02ef93f6b72c977e2002bf2a36 Mon Sep 17 00:00:00 2001 From: Alexander Zobnin Date: Sat, 16 Jan 2016 20:35:11 +0300 Subject: [PATCH 037/205] Fixed linter errors. --- plugins/datasource-zabbix/datasource.js | 1 + plugins/datasource-zabbix/zabbixCacheSrv.js | 4 +--- 2 files changed, 2 insertions(+), 3 deletions(-) diff --git a/plugins/datasource-zabbix/datasource.js b/plugins/datasource-zabbix/datasource.js index 0cf5c94c0..e4b446771 100644 --- a/plugins/datasource-zabbix/datasource.js +++ b/plugins/datasource-zabbix/datasource.js @@ -34,6 +34,7 @@ function (angular, _, dateMath) { // Initialize cache service this.zabbixCache = new ZabbixCache(this.zabbixAPI); + console.log(this.zabbixCache); /** * Test connection to Zabbix API diff --git a/plugins/datasource-zabbix/zabbixCacheSrv.js b/plugins/datasource-zabbix/zabbixCacheSrv.js index d65212a8a..0c6aed13f 100644 --- a/plugins/datasource-zabbix/zabbixCacheSrv.js +++ b/plugins/datasource-zabbix/zabbixCacheSrv.js @@ -7,11 +7,9 @@ function (angular, _) { var module = angular.module('grafana.services'); - module.factory('ZabbixCache', function($q, backendSrv) { + module.factory('ZabbixCache', function($q) { function ZabbixCache(zabbixAPI, lifetime) { - var self = this; - this.zabbixAPI = zabbixAPI; this.lifetime = lifetime; From d9bc27c4afd77d726899b083c426c53f682598db Mon Sep 17 00:00:00 2001 From: Alexander Zobnin Date: Sat, 16 Jan 2016 21:16:22 +0300 Subject: [PATCH 038/205] Working on new query editor - changed dropdown to input with autocomplete. --- .../partials/query.editor.html | 111 ++++++------------ plugins/datasource-zabbix/queryCtrl.js | 45 +++++-- plugins/datasource-zabbix/zabbixCacheSrv.js | 18 ++- 3 files changed, 82 insertions(+), 92 deletions(-) diff --git a/plugins/datasource-zabbix/partials/query.editor.html b/plugins/datasource-zabbix/partials/query.editor.html index 383d9651a..76823112e 100644 --- a/plugins/datasource-zabbix/partials/query.editor.html +++ b/plugins/datasource-zabbix/partials/query.editor.html @@ -76,7 +76,7 @@
    +
    + +
    +
    +
      +
    • + Cache update interval +
    • +
    • + +
    • Trends   Date: Mon, 1 Feb 2016 23:52:33 +0300 Subject: [PATCH 115/205] Fixed #101 - Grafana sends 50 requests per second to Zabbix when the configured Zabbix username stops being valid. --- plugins/datasource-zabbix/zabbixAPI.js | 45 ++++++++++++++++++-------- 1 file changed, 32 insertions(+), 13 deletions(-) diff --git a/plugins/datasource-zabbix/zabbixAPI.js b/plugins/datasource-zabbix/zabbixAPI.js index fb1efca20..098e09708 100644 --- a/plugins/datasource-zabbix/zabbixAPI.js +++ b/plugins/datasource-zabbix/zabbixAPI.js @@ -13,7 +13,7 @@ function (angular, _) { * Creates Zabbix API instance with given parameters (url, credentials and other). * Wraps API calls and provides high-level methods. */ - module.factory('ZabbixAPI', function($q, backendSrv, ZabbixAPIService) { + module.factory('ZabbixAPI', function($q, backendSrv, alertSrv, ZabbixAPIService) { // Initialize Zabbix API. function ZabbixAPI(api_url, username, password, basicAuth, withCredentials) { @@ -38,22 +38,35 @@ function (angular, _) { p.request = function(method, params) { var self = this; + return ZabbixAPIService.request(this.url, method, params, this.requestOptions, this.auth) .then(function(result) { return result; }, - - // Handle errors + // Handle API errors function(error) { - if (isAuthError(error.data)) { - return self.loginOnce().then(function() { - return self.request(method, params); - }); + if (isNotAuthorized(error.data)) { + return self.loginOnce().then( + function() { + return self.request(method, params); + }, + // Handle user.login method errors + function(error) { + self.alertAPIError(error.data); + }); } }); }; - function isAuthError(message) { + p.alertAPIError = function(message) { + alertSrv.set( + "Zabbix API Error", + message, + 'error' + ); + }; + + function isNotAuthorized(message) { return ( message === "Session terminated, re-login, please." || message === "Not authorised." || @@ -72,11 +85,17 @@ function (angular, _) { var deferred = $q.defer(); if (!self.loginPromise) { self.loginPromise = deferred.promise; - self.login().then(function(auth) { - self.loginPromise = null; - self.auth = auth; - deferred.resolve(auth); - }); + self.login().then( + function(auth) { + self.loginPromise = null; + self.auth = auth; + deferred.resolve(auth); + }, + function(error) { + self.loginPromise = null; + deferred.reject(error); + } + ); } else { return self.loginPromise; } From 5f0f79da0f4ee6747f07074522e8cd86f86d9636 Mon Sep 17 00:00:00 2001 From: Alexander Zobnin Date: Tue, 2 Feb 2016 12:04:06 +0300 Subject: [PATCH 116/205] Don't run duplicated history requests. --- plugins/datasource-zabbix/zabbixCache.js | 47 ++++++++++++++++++++---- 1 file changed, 39 insertions(+), 8 deletions(-) diff --git a/plugins/datasource-zabbix/zabbixCache.js b/plugins/datasource-zabbix/zabbixCache.js index f30e65524..1806ec5ca 100644 --- a/plugins/datasource-zabbix/zabbixCache.js +++ b/plugins/datasource-zabbix/zabbixCache.js @@ -30,12 +30,17 @@ function (angular, _, utils) { this._initialized = undefined; this.refreshPromise = false; + this.historyPromises = {}; // Wrap _refresh() method to call it once. this.refresh = callOnce(p._refresh, this.refreshPromise); // Update cache periodically $interval(_.bind(this.refresh, this), this.ttl); + + // Don't run duplicated history requests + this.getHistory = callHistoryOnce(_.bind(this.zabbixAPI.getHistory, this.zabbixAPI), + this.historyPromises); } var p = ZabbixCachingProxy.prototype; @@ -122,14 +127,7 @@ function (angular, _, utils) { } } - p.getHistory = function(items, time_from, time_till) { - var itemids = _.map(arguments[0], 'itemid'); - var stamp = itemids.join() + arguments[1] + arguments[2]; - //console.log(arguments, stamp); - return this.zabbixAPI.getHistory(items, time_from, time_till); - }; - - p.getHistory_ = function(items, time_from, time_till) { + p.getHistoryFromCache = function(items, time_from, time_till) { var deferred = $q.defer(); var historyStorage = this.storage.history; var full_history; @@ -212,6 +210,39 @@ function (angular, _, utils) { }); } + String.prototype.getHash = function() { + var hash = 0, i, chr, len; + if (this.length === 0) { + return hash; + } + for (i = 0, len = this.length; i < len; i++) { + chr = this.charCodeAt(i); + hash = ((hash << 5) - hash) + chr; + hash |= 0; // Convert to 32bit integer + } + return hash; + }; + + function callHistoryOnce(func, promiseKeeper) { + return function() { + var itemids = _.map(arguments[0], 'itemid'); + var stamp = itemids.join() + arguments[1] + arguments[2]; + var hash = stamp.getHash(); + + var deferred = $q.defer(); + if (!promiseKeeper[hash]) { + promiseKeeper[hash] = deferred.promise; + func.apply(this, arguments).then(function(result) { + deferred.resolve(result); + promiseKeeper[hash] = null; + }); + } else { + return promiseKeeper[hash]; + } + return deferred.promise; + }; + } + function callOnce(func, promiseKeeper) { return function() { var deferred = $q.defer(); From fa10d76e2b5278b62843a0f1cb82ef197f384fc4 Mon Sep 17 00:00:00 2001 From: Alexander Zobnin Date: Tue, 2 Feb 2016 14:45:03 +0300 Subject: [PATCH 117/205] Added methods for getting trend from different api types. Added some hacks for getting applications from different apis. --- plugins/datasource-zabbix/datasource.js | 2 +- plugins/datasource-zabbix/zabbixAPI.js | 29 +++++++++++++++++++++++- plugins/datasource-zabbix/zabbixCache.js | 6 ++++- 3 files changed, 34 insertions(+), 3 deletions(-) diff --git a/plugins/datasource-zabbix/datasource.js b/plugins/datasource-zabbix/datasource.js index 8ca918a27..6417b7ea2 100644 --- a/plugins/datasource-zabbix/datasource.js +++ b/plugins/datasource-zabbix/datasource.js @@ -138,7 +138,7 @@ function (angular, _, dateMath, utils, metricFunctions) { // Use trends var valueType = target.downsampleFunction ? target.downsampleFunction.value : "avg"; - getHistory = self.zabbixAPI.getTrends(items, from, to).then(function(history) { + getHistory = self.zabbixAPI.getTrend(items, from, to).then(function(history) { return self.queryProcessor.handleTrends(history, addHostName, valueType); }); } else { diff --git a/plugins/datasource-zabbix/zabbixAPI.js b/plugins/datasource-zabbix/zabbixAPI.js index 098e09708..f8e10e4a8 100644 --- a/plugins/datasource-zabbix/zabbixAPI.js +++ b/plugins/datasource-zabbix/zabbixAPI.js @@ -143,6 +143,9 @@ function (angular, _) { var params = { output: ['name'], sortfield: 'name', + + // Hack for supporting different apis (2.2 vs 2.4 vs 3.0) + selectHost: [], selectHosts: [] }; @@ -223,7 +226,7 @@ function (angular, _) { * @param {Number} time_till Time in seconds * @return {Array} Array of Zabbix trend objects */ - p.getTrends = function(items, time_from, time_till) { + p.getTrend_ZBXNEXT1193 = function(items, time_from, time_till) { var self = this; // Group items by value type @@ -250,6 +253,30 @@ function (angular, _) { })).then(_.flatten); }; + p.getTrend_30 = function(items, time_from, time_till, value_type) { + var self = this; + var itemids = _.map(items, 'itemid'); + + var params = { + output: ["itemid", + "clock", + value_type + ], + itemids: itemids, + time_from: time_from + }; + + // Relative queries (e.g. last hour) don't include an end time + if (time_till) { + params.time_till = time_till; + } + + return self.request('trend.get', params); + }; + + p.getTrend = p.getTrend_ZBXNEXT1193; + //p.getTrend = p.getTrend_30; + p.getITService = function(/* optional */ serviceids) { var params = { output: 'extend', diff --git a/plugins/datasource-zabbix/zabbixCache.js b/plugins/datasource-zabbix/zabbixCache.js index 1806ec5ca..abe4fe397 100644 --- a/plugins/datasource-zabbix/zabbixCache.js +++ b/plugins/datasource-zabbix/zabbixCache.js @@ -187,10 +187,14 @@ function (angular, _, utils) { */ function convertApplications(applications) { return _.map(_.groupBy(applications, 'name'), function(value, key) { + + // Hack for supporting different apis (2.2 vs 2.4 vs 3.0) + var hostField = value['hosts'] ? 'hosts' : 'host'; + return { name: key, applicationids: _.map(value, 'applicationid'), - hosts: _.uniq(_.map(_.flatten(value, 'hosts'), 'hostid')) + hosts: _.uniq(_.map(_.flatten(value, hostField), 'hostid')) }; }); } From 38b83eee292e0887622fe889947b2d53dc59c833 Mon Sep 17 00:00:00 2001 From: Alexander Zobnin Date: Tue, 2 Feb 2016 17:15:56 +0300 Subject: [PATCH 118/205] Fixed templates. --- plugins/datasource-zabbix/datasource.js | 11 +++++------ 1 file changed, 5 insertions(+), 6 deletions(-) diff --git a/plugins/datasource-zabbix/datasource.js b/plugins/datasource-zabbix/datasource.js index 6417b7ea2..18ee0218d 100644 --- a/plugins/datasource-zabbix/datasource.js +++ b/plugins/datasource-zabbix/datasource.js @@ -285,13 +285,12 @@ function (angular, _, dateMath, utils, metricFunctions) { var parts = []; _.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(zabbixHelperSrv.splitMetrics(part)); - } else { - parts.push(part); + + // Replace wildcard to regex + if (part === '*') { + part = '/.*/'; } + parts.push(part); }); var template = _.object(['group', 'host', 'app', 'item'], parts); From 5a2acc66339399c66532b26ded02798f240f4470 Mon Sep 17 00:00:00 2001 From: Alexander Zobnin Date: Tue, 2 Feb 2016 16:06:43 +0300 Subject: [PATCH 119/205] Trigger panel: fixed Select triggers section. --- plugins/panel-triggers/editor.html | 52 +++++---- plugins/panel-triggers/module.js | 167 ++++++++++++++++------------- 2 files changed, 124 insertions(+), 95 deletions(-) diff --git a/plugins/panel-triggers/editor.html b/plugins/panel-triggers/editor.html index c7695fe0d..cee033808 100644 --- a/plugins/panel-triggers/editor.html +++ b/plugins/panel-triggers/editor.html @@ -7,21 +7,29 @@
      Select triggers
      Group
    • - +
    • Host
    • - +
    @@ -32,22 +40,28 @@
    Select triggers
    Application
  • - +
  • Trigger
  • + class="input-large tight-form-input last" + ng-style="panel.triggers.trigger.style" + empty-to-null">
  • diff --git a/plugins/panel-triggers/module.js b/plugins/panel-triggers/module.js index 27dd7c1f4..d4d6778db 100644 --- a/plugins/panel-triggers/module.js +++ b/plugins/panel-triggers/module.js @@ -17,8 +17,7 @@ define([ 'lodash', 'jquery', 'app/core/config', - 'app/features/panel/panel_meta', - '../zabbix/helperFunctions', + 'app/features/panel/panel_meta' ], function (angular, app, _, $, config, PanelMeta) { 'use strict'; @@ -27,7 +26,7 @@ function (angular, app, _, $, config, PanelMeta) { app.useModule(module); /** @ngInject */ - function TriggerPanelCtrl($q, $scope, $element, datasourceSrv, panelSrv, templateSrv, zabbixHelperSrv, popoverSrv) { + function TriggerPanelCtrl($q, $scope, $element, datasourceSrv, panelSrv, templateSrv, popoverSrv) { $scope.panelMeta = new PanelMeta({ panelName: 'Zabbix triggers', @@ -66,9 +65,10 @@ function (angular, app, _, $, config, PanelMeta) { var panelDefaults = { datasource: null, triggers: { - group: {name: 'All', groupid: null}, - host: {name: 'All', hostid: null}, - application: {name: 'All', value: null} + group: {filter: ""}, + host: {filter: ""}, + application: {filter: ""}, + trigger: {filter: ""} }, hostField: true, severityField: false, @@ -91,13 +91,13 @@ function (angular, app, _, $, config, PanelMeta) { $scope.panel.title = "Zabbix Triggers"; } - if (!$scope.metric) { - $scope.metric = { - groupList: [{name: 'All', groupid: null}], - hostList: [{name: 'All', hostid: null}], - applicationList: [{name: 'All', applicationid: null}] - }; - } + // Load scope defaults + var scopeDefaults = { + metric: {}, + inputStyles: {}, + oldTarget: _.cloneDeep($scope.panel.triggers) + }; + _.defaults($scope, scopeDefaults); // Get zabbix data sources var datasources = _.filter(datasourceSrv.getMetricSources(), function(datasource) { @@ -109,11 +109,11 @@ function (angular, app, _, $, config, PanelMeta) { if (!$scope.panel.datasource) { $scope.panel.datasource = $scope.datasources[0]; } - - // Update lists of groups, hosts and applications - $scope.updateGroups() - .then($scope.updateHosts) - .then($scope.updateApplications); + // Load datasource + datasourceSrv.get($scope.panel.datasource).then(function (datasource) { + $scope.datasource = datasource; + $scope.initFilters(); + }); }; $scope.refreshData = function() { @@ -142,7 +142,7 @@ function (angular, app, _, $, config, PanelMeta) { // Consider local time offset var ageUnix = now - lastchange + now.getTimezoneOffset() * 60000; - var age = zabbixHelperSrv.toZabbixAgeFormat(ageUnix); + var age = toZabbixAgeFormat(ageUnix); var triggerObj = trigger; triggerObj.lastchangeUnix = lastchangeUnix; triggerObj.lastchange = lastchange.toLocaleString(); @@ -191,44 +191,65 @@ function (angular, app, _, $, config, PanelMeta) { }); }; - $scope.groupChanged = function() { - return $scope.updateHosts() - .then($scope.updateApplications) - .then($scope.refreshData); + $scope.initFilters = function () { + $scope.filterGroups(); + $scope.filterHosts(); + $scope.filterApplications(); + //$scope.filterItems(); }; - $scope.hostChanged = function() { - return $scope.updateApplications() - .then($scope.refreshData); - }; + // Get list of metric names for bs-typeahead directive + function getMetricNames(scope, metricList) { + return _.uniq(_.map(scope.metric[metricList], 'name')); + } - $scope.appChanged = function() { - var app = $scope.panel.triggers.application.name; + // Map functions for bs-typeahead + $scope.getGroupNames = _.partial(getMetricNames, $scope, 'groupList'); + $scope.getHostNames = _.partial(getMetricNames, $scope, 'filteredHosts'); + $scope.getApplicationNames = _.partial(getMetricNames, $scope, 'filteredApplications'); + $scope.getItemNames = _.partial(getMetricNames, $scope, 'filteredItems'); - return datasourceSrv.get($scope.panel.datasource).then(function (datasource) { - return datasource.zabbixAPI.getAppByName(app).then(function (applications) { - var appids = _.map(applications, 'applicationid'); - $scope.panel.triggers.application.value = appids.length ? appids : null; - }); - }).then($scope.refreshData); + $scope.filterGroups = function() { + $scope.datasource.queryProcessor.filterGroups().then(function(groups) { + $scope.metric.groupList = groups; + }); }; - $scope.updateGroups = function() { - return datasourceSrv.get($scope.panel.datasource).then(function (datasource) { - return $scope.updateGroupList(datasource); + $scope.filterHosts = function () { + var groupFilter = templateSrv.replace($scope.panel.triggers.group.filter); + $scope.datasource.queryProcessor.filterHosts(groupFilter).then(function(hosts) { + $scope.metric.filteredHosts = hosts; }); }; - $scope.updateHosts = function() { - return datasourceSrv.get($scope.panel.datasource).then(function (datasource) { - return $scope.updateHostList(datasource); - }); + $scope.filterApplications = function () { + var groupFilter = templateSrv.replace($scope.panel.triggers.group.filter); + var hostFilter = templateSrv.replace($scope.panel.triggers.host.filter); + $scope.datasource.queryProcessor.filterApplications(groupFilter, hostFilter) + .then(function(apps) { + $scope.metric.filteredApplications = apps; + }); }; - $scope.updateApplications = function() { - return datasourceSrv.get($scope.panel.datasource).then(function (datasource) { - return $scope.updateAppList(datasource); - }); + $scope.onTargetPartChange = function (targetPart) { + var regexStyle = {'color': '#CCA300'}; + targetPart.isRegex = isRegex(targetPart.filter); + targetPart.style = targetPart.isRegex ? regexStyle : {}; + }; + + function isRegex(str) { + // Pattern for testing regex + var regexPattern = /^\/(.*)\/([gmi]*)$/m; + return regexPattern.test(str); + } + + $scope.parseTarget = function() { + $scope.initFilters(); + var newTarget = _.cloneDeep($scope.panel.triggers); + if (!_.isEqual($scope.oldTarget, $scope.panel.triggers)) { + $scope.oldTarget = newTarget; + $scope.get_data(); + } }; $scope.refreshTriggerSeverity = function() { @@ -266,38 +287,32 @@ function (angular, app, _, $, config, PanelMeta) { }); }; - $scope.updateGroupList = function (datasource) { - datasource.zabbixAPI.performHostGroupSuggestQuery().then(function (groups) { - $scope.metric.groupList = $scope.metric.groupList.concat(groups); - }); - }; - - $scope.updateHostList = function (datasource) { - var groups = $scope.panel.triggers.group.groupid ? $scope.panel.triggers.group.name : '*'; - if (groups) { - datasource.zabbixAPI.hostFindQuery(groups).then(function (hosts) { - $scope.metric.hostList = [{name: 'All', hostid: null}]; - $scope.metric.hostList = $scope.metric.hostList.concat(hosts); - }); + /** + * Convert event age from Unix format (milliseconds sins 1970) + * to Zabbix format (like at Last 20 issues panel). + * @param {Date} AgeUnix time in Unix format + * @return {string} Formatted time + */ + function toZabbixAgeFormat(ageUnix) { + var age = new Date(+ageUnix); + var ageZabbix = age.getSeconds() + 's'; + if (age.getMinutes()) { + ageZabbix = age.getMinutes() + 'm ' + ageZabbix; } - }; - - $scope.updateAppList = function (datasource) { - var groups = $scope.panel.triggers.group.groupid ? $scope.panel.triggers.group.name : '*'; - var hosts = $scope.panel.triggers.host.hostid ? $scope.panel.triggers.host.name : '*'; - if (groups && hosts) { - datasource.zabbixAPI.appFindQuery(hosts, groups).then(function (apps) { - apps = _.map(_.uniq(_.map(apps, 'name')), function (appname) { - return { - name: appname, - value: appname - }; - }); - $scope.metric.applicationList = [{name: 'All', value: null}]; - $scope.metric.applicationList = $scope.metric.applicationList.concat(apps); - }); + if (age.getHours()) { + ageZabbix = age.getHours() + 'h ' + ageZabbix; } - }; + if (age.getDate() - 1) { + ageZabbix = age.getDate() - 1 + 'd ' + ageZabbix; + } + if (age.getMonth()) { + ageZabbix = age.getMonth() + 'M ' + ageZabbix; + } + if (age.getYear() - 70) { + ageZabbix = age.getYear() -70 + 'y ' + ageZabbix; + } + return ageZabbix; + } $scope.init(); } From 840f07dc7e9e93229becb746a123a375c9eeef16 Mon Sep 17 00:00:00 2001 From: Alexander Zobnin Date: Wed, 3 Feb 2016 10:50:09 +0300 Subject: [PATCH 120/205] Trigger panel: improved work with triggers via api. --- plugins/datasource-zabbix/zabbixAPI.js | 34 +++++------- plugins/panel-triggers/module.js | 77 +++++++++++++------------- 2 files changed, 52 insertions(+), 59 deletions(-) diff --git a/plugins/datasource-zabbix/zabbixAPI.js b/plugins/datasource-zabbix/zabbixAPI.js index f8e10e4a8..fe9db0603 100644 --- a/plugins/datasource-zabbix/zabbixAPI.js +++ b/plugins/datasource-zabbix/zabbixAPI.js @@ -296,49 +296,41 @@ function (angular, _) { return this.request('service.getsla', params); }; - p.getTriggers = function(limit, sortfield, groupids, hostids, applicationids, name) { + p.getTriggers = function() { var params = { output: 'extend', expandDescription: true, expandData: true, monitored: true, + skipDependent: true, //only_true: true, filter: { value: 1 }, - search : { - description: name - }, - searchWildcardsEnabled: false, - groupids: groupids, - hostids: hostids, - applicationids: applicationids, - limit: limit, - sortfield: 'lastchange', - sortorder: 'DESC' + selectGroups: ['name'], + selectHosts: ['name'], + selectItems: ['name', 'key_', 'lastvalue'], + selectLastEvent: 'extend' }; - if (sortfield) { - params.sortfield = sortfield; - } - return this.request('trigger.get', params); }; - p.getAcknowledges = function(triggerids, from) { + p.getAcknowledges = function(eventids) { var params = { output: 'extend', - objectids: triggerids, - acknowledged: true, + eventids: eventids, + preservekeys: true, select_acknowledges: 'extend', sortfield: 'clock', - sortorder: 'DESC', - time_from: from + sortorder: 'DESC' }; return this.request('event.get', params) .then(function (events) { - return _.flatten(_.map(events, 'acknowledges')); + return _.filter(events, function(event) { + return event.acknowledges.length; + }); }); }; diff --git a/plugins/panel-triggers/module.js b/plugins/panel-triggers/module.js index d4d6778db..74f568523 100644 --- a/plugins/panel-triggers/module.js +++ b/plugins/panel-triggers/module.js @@ -122,20 +122,10 @@ function (angular, app, _, $, config, PanelMeta) { return datasourceSrv.get($scope.panel.datasource).then(function (datasource) { var zabbix = datasource.zabbixAPI; - var groupid = $scope.panel.triggers.group.groupid; - var hostid = $scope.panel.triggers.host.hostid; - var applicationids = $scope.panel.triggers.application.value; - // Get triggers - return zabbix.getTriggers(null, - $scope.panel.sortTriggersBy.value, - groupid, - hostid, - applicationids, - $scope.panel.triggers.name, - $scope.panel.showEvents.value) + return zabbix.getTriggers($scope.panel.showEvents.value) .then(function(triggers) { - var promises = _.map(triggers, function (trigger) { + return _.map(triggers, function (trigger) { var lastchange = new Date(trigger.lastchange * 1000); var lastchangeUnix = trigger.lastchange; var now = new Date(); @@ -149,44 +139,55 @@ function (angular, app, _, $, config, PanelMeta) { triggerObj.age = age.toLocaleString(); triggerObj.color = $scope.panel.triggerSeverity[trigger.priority].color; triggerObj.severity = $scope.panel.triggerSeverity[trigger.priority].severity; + return triggerObj; + }); + }) + .then(function (triggerList) { + + // Request acknowledges for trigger + var eventids = _.map(triggerList, function(trigger) { + return trigger.lastEvent.eventid; + }); + return zabbix.getAcknowledges(eventids) + .then(function (events) { + + // Map events to triggers + _.each(triggerList, function(trigger) { + var event = _.find(events, function(event) { + return event.eventid === trigger.lastEvent.eventid; + }); - // Request acknowledges for trigger - return zabbix.getAcknowledges(trigger.triggerid, lastchangeUnix) - .then(function (acknowledges) { - if (acknowledges.length) { - triggerObj.acknowledges = _.map(acknowledges, function (ack) { + if (event) { + trigger.acknowledges = _.map(event.acknowledges, function (ack) { var time = new Date(+ack.clock * 1000); ack.time = time.toLocaleString(); ack.user = ack.alias + ' (' + ack.name + ' ' + ack.surname + ')'; return ack; }); } - return triggerObj; }); - }); - return $q.all(promises).then(function (triggerList) { - // Filter acknowledged triggers - if ($scope.panel.showTriggers === 'unacknowledged') { - $scope.triggerList = _.filter(triggerList, function (trigger) { - return !trigger.acknowledges; + // Filter acknowledged triggers + if ($scope.panel.showTriggers === 'unacknowledged') { + $scope.triggerList = _.filter(triggerList, function (trigger) { + return !trigger.acknowledges; + }); + } else if ($scope.panel.showTriggers === 'acknowledged') { + $scope.triggerList = _.filter(triggerList, 'acknowledges'); + } else { + $scope.triggerList = triggerList; + } + + // Filter triggers by severity + $scope.triggerList = _.filter($scope.triggerList, function (trigger) { + return $scope.panel.triggerSeverity[trigger.priority].show; }); - } else if ($scope.panel.showTriggers === 'acknowledged') { - $scope.triggerList = _.filter(triggerList, 'acknowledges'); - } else { - $scope.triggerList = triggerList; - } - - // Filter triggers by severity - $scope.triggerList = _.filter($scope.triggerList, function (trigger) { - return $scope.panel.triggerSeverity[trigger.priority].show; - }); - // Limit triggers number - $scope.triggerList = _.first($scope.triggerList, $scope.panel.limit); + // Limit triggers number + $scope.triggerList = _.first($scope.triggerList, $scope.panel.limit); - $scope.panelRenderingComplete(); - }); + $scope.panelRenderingComplete(); + }); }); }); }; From d4ea5d5b32ae1af8718126ab2db88b16f8b91ca3 Mon Sep 17 00:00:00 2001 From: Alexander Zobnin Date: Wed, 3 Feb 2016 11:45:42 +0300 Subject: [PATCH 121/205] Trigger panel: fixed trigger filtering by group, host and app. --- plugins/datasource-zabbix/queryProcessor.js | 67 +++++++++++ plugins/datasource-zabbix/zabbixAPI.js | 5 +- plugins/panel-triggers/module.js | 122 +++++++++++--------- 3 files changed, 136 insertions(+), 58 deletions(-) diff --git a/plugins/datasource-zabbix/queryProcessor.js b/plugins/datasource-zabbix/queryProcessor.js index d28e2f49f..97f128a2e 100644 --- a/plugins/datasource-zabbix/queryProcessor.js +++ b/plugins/datasource-zabbix/queryProcessor.js @@ -28,6 +28,19 @@ function (angular, _, utils) { } }; + /** + * Build trigger query in asynchronous manner + */ + this.buildTriggerQuery = function (groupFilter, hostFilter, appFilter) { + if (this.cache._initialized) { + return $q.when(self.buildTriggerQueryFromCache(groupFilter, hostFilter, appFilter)); + } else { + return this.cache.refresh().then(function() { + return self.buildTriggerQueryFromCache(groupFilter, hostFilter, appFilter); + }); + } + }; + this.filterGroups = function(groupFilter) { return self.cache.getGroups().then(function(groupList) { return groupList; @@ -309,6 +322,60 @@ function (angular, _, utils) { }); }; + /** + * Build query - convert target filters to array of Zabbix items + */ + this.buildTriggerQueryFromCache = function (groupFilter, hostFilter, appFilter) { + var promises = [ + this.filterGroups(groupFilter).then(function(groups) { + return _.filter(groups, function(group) { + if (utils.isRegex(groupFilter)) { + return utils.buildRegex(groupFilter).test(group.name); + } else { + return group.name === groupFilter; + } + }); + }), + this.filterHosts(groupFilter).then(function(hosts) { + return _.filter(hosts, function(host) { + if (utils.isRegex(hostFilter)) { + return utils.buildRegex(hostFilter).test(host.name); + } else { + return host.name === hostFilter; + } + }); + }), + this.filterApplications(groupFilter, hostFilter).then(function(apps) { + return _.filter(apps, function(app) { + if (utils.isRegex(appFilter)) { + return utils.buildRegex(appFilter).test(app.name); + } else { + return app.name === appFilter; + } + }); + }) + ]; + + return $q.all(promises).then(function(results) { + var filteredGroups = results[0]; + var filteredHosts = results[1]; + var filteredApps = results[2]; + var query = {}; + + if (appFilter) { + query.applicationids = _.flatten(_.map(filteredApps, 'applicationids')); + } + if (hostFilter) { + query.hostids = _.map(filteredHosts, 'hostid'); + } + if (groupFilter) { + query.groupids = _.map(filteredGroups, 'groupid'); + } + + return query; + }); + }; + /** * Convert Zabbix API history.get response to Grafana format * diff --git a/plugins/datasource-zabbix/zabbixAPI.js b/plugins/datasource-zabbix/zabbixAPI.js index fe9db0603..113687425 100644 --- a/plugins/datasource-zabbix/zabbixAPI.js +++ b/plugins/datasource-zabbix/zabbixAPI.js @@ -296,9 +296,12 @@ function (angular, _) { return this.request('service.getsla', params); }; - p.getTriggers = function() { + p.getTriggers = function(groupids, hostids, applicationids) { var params = { output: 'extend', + groupids: groupids, + hostids: hostids, + applicationids: applicationids, expandDescription: true, expandData: true, monitored: true, diff --git a/plugins/panel-triggers/module.js b/plugins/panel-triggers/module.js index 74f568523..17f46ea4c 100644 --- a/plugins/panel-triggers/module.js +++ b/plugins/panel-triggers/module.js @@ -121,74 +121,82 @@ function (angular, app, _, $, config, PanelMeta) { // Load datasource return datasourceSrv.get($scope.panel.datasource).then(function (datasource) { var zabbix = datasource.zabbixAPI; + var queryProcessor = datasource.queryProcessor; + var triggerFilter = $scope.panel.triggers; + var buildQuery = queryProcessor.buildTriggerQuery(triggerFilter.group.filter, + triggerFilter.host.filter, + triggerFilter.application.filter); + return buildQuery.then(function(query) { + return zabbix.getTriggers(query.groupids, + query.hostids, + query.applicationids, + $scope.panel.showEvents.value) + .then(function(triggers) { + return _.map(triggers, function (trigger) { + var lastchange = new Date(trigger.lastchange * 1000); + var lastchangeUnix = trigger.lastchange; + var now = new Date(); + + // Consider local time offset + var ageUnix = now - lastchange + now.getTimezoneOffset() * 60000; + var age = toZabbixAgeFormat(ageUnix); + var triggerObj = trigger; + triggerObj.lastchangeUnix = lastchangeUnix; + triggerObj.lastchange = lastchange.toLocaleString(); + triggerObj.age = age.toLocaleString(); + triggerObj.color = $scope.panel.triggerSeverity[trigger.priority].color; + triggerObj.severity = $scope.panel.triggerSeverity[trigger.priority].severity; + return triggerObj; + }); + }) + .then(function (triggerList) { - // Get triggers - return zabbix.getTriggers($scope.panel.showEvents.value) - .then(function(triggers) { - return _.map(triggers, function (trigger) { - var lastchange = new Date(trigger.lastchange * 1000); - var lastchangeUnix = trigger.lastchange; - var now = new Date(); - - // Consider local time offset - var ageUnix = now - lastchange + now.getTimezoneOffset() * 60000; - var age = toZabbixAgeFormat(ageUnix); - var triggerObj = trigger; - triggerObj.lastchangeUnix = lastchangeUnix; - triggerObj.lastchange = lastchange.toLocaleString(); - triggerObj.age = age.toLocaleString(); - triggerObj.color = $scope.panel.triggerSeverity[trigger.priority].color; - triggerObj.severity = $scope.panel.triggerSeverity[trigger.priority].severity; - return triggerObj; - }); - }) - .then(function (triggerList) { + // Request acknowledges for trigger + var eventids = _.map(triggerList, function(trigger) { + return trigger.lastEvent.eventid; + }); + return zabbix.getAcknowledges(eventids) + .then(function (events) { - // Request acknowledges for trigger - var eventids = _.map(triggerList, function(trigger) { - return trigger.lastEvent.eventid; - }); - return zabbix.getAcknowledges(eventids) - .then(function (events) { + // Map events to triggers + _.each(triggerList, function(trigger) { + var event = _.find(events, function(event) { + return event.eventid === trigger.lastEvent.eventid; + }); - // Map events to triggers - _.each(triggerList, function(trigger) { - var event = _.find(events, function(event) { - return event.eventid === trigger.lastEvent.eventid; + if (event) { + trigger.acknowledges = _.map(event.acknowledges, function (ack) { + var time = new Date(+ack.clock * 1000); + ack.time = time.toLocaleString(); + ack.user = ack.alias + ' (' + ack.name + ' ' + ack.surname + ')'; + return ack; + }); + } }); - if (event) { - trigger.acknowledges = _.map(event.acknowledges, function (ack) { - var time = new Date(+ack.clock * 1000); - ack.time = time.toLocaleString(); - ack.user = ack.alias + ' (' + ack.name + ' ' + ack.surname + ')'; - return ack; + // Filter acknowledged triggers + if ($scope.panel.showTriggers === 'unacknowledged') { + $scope.triggerList = _.filter(triggerList, function (trigger) { + return !trigger.acknowledges; }); + } else if ($scope.panel.showTriggers === 'acknowledged') { + $scope.triggerList = _.filter(triggerList, 'acknowledges'); + } else { + $scope.triggerList = triggerList; } - }); - // Filter acknowledged triggers - if ($scope.panel.showTriggers === 'unacknowledged') { - $scope.triggerList = _.filter(triggerList, function (trigger) { - return !trigger.acknowledges; + // Filter triggers by severity + $scope.triggerList = _.filter($scope.triggerList, function (trigger) { + return $scope.panel.triggerSeverity[trigger.priority].show; }); - } else if ($scope.panel.showTriggers === 'acknowledged') { - $scope.triggerList = _.filter(triggerList, 'acknowledges'); - } else { - $scope.triggerList = triggerList; - } - - // Filter triggers by severity - $scope.triggerList = _.filter($scope.triggerList, function (trigger) { - return $scope.panel.triggerSeverity[trigger.priority].show; - }); - // Limit triggers number - $scope.triggerList = _.first($scope.triggerList, $scope.panel.limit); + // Limit triggers number + $scope.triggerList = _.first($scope.triggerList, $scope.panel.limit); - $scope.panelRenderingComplete(); - }); - }); + $scope.panelRenderingComplete(); + }); + }); + }); }); }; From a08529faa12bdc860b7b3ce333bae9cf7477e6e0 Mon Sep 17 00:00:00 2001 From: Alexander Zobnin Date: Wed, 3 Feb 2016 14:31:39 +0300 Subject: [PATCH 122/205] Filter triggers by description. --- plugins/panel-triggers/module.js | 33 ++++++++++++++++++++++++++++---- 1 file changed, 29 insertions(+), 4 deletions(-) diff --git a/plugins/panel-triggers/module.js b/plugins/panel-triggers/module.js index 17f46ea4c..049759e01 100644 --- a/plugins/panel-triggers/module.js +++ b/plugins/panel-triggers/module.js @@ -174,6 +174,12 @@ function (angular, app, _, $, config, PanelMeta) { } }); + // Filter triggers by description + var triggerFilter = $scope.panel.triggers.trigger.filter; + if (triggerFilter) { + triggerList = filterTriggers(triggerList, triggerFilter); + } + // Filter acknowledged triggers if ($scope.panel.showTriggers === 'unacknowledged') { $scope.triggerList = _.filter(triggerList, function (trigger) { @@ -204,7 +210,6 @@ function (angular, app, _, $, config, PanelMeta) { $scope.filterGroups(); $scope.filterHosts(); $scope.filterApplications(); - //$scope.filterItems(); }; // Get list of metric names for bs-typeahead directive @@ -224,14 +229,14 @@ function (angular, app, _, $, config, PanelMeta) { }); }; - $scope.filterHosts = function () { + $scope.filterHosts = function() { var groupFilter = templateSrv.replace($scope.panel.triggers.group.filter); $scope.datasource.queryProcessor.filterHosts(groupFilter).then(function(hosts) { $scope.metric.filteredHosts = hosts; }); }; - $scope.filterApplications = function () { + $scope.filterApplications = function() { var groupFilter = templateSrv.replace($scope.panel.triggers.group.filter); var hostFilter = templateSrv.replace($scope.panel.triggers.host.filter); $scope.datasource.queryProcessor.filterApplications(groupFilter, hostFilter) @@ -240,7 +245,19 @@ function (angular, app, _, $, config, PanelMeta) { }); }; - $scope.onTargetPartChange = function (targetPart) { + function filterTriggers(triggers, triggerFilter) { + if (isRegex(triggerFilter)) { + return _.filter(triggers, function(trigger) { + return buildRegex(triggerFilter).test(trigger.description); + }); + } else { + return _.filter(triggers, function(trigger) { + return trigger.description === triggerFilter; + }); + } + } + + $scope.onTargetPartChange = function(targetPart) { var regexStyle = {'color': '#CCA300'}; targetPart.isRegex = isRegex(targetPart.filter); targetPart.style = targetPart.isRegex ? regexStyle : {}; @@ -252,6 +269,14 @@ function (angular, app, _, $, config, PanelMeta) { return regexPattern.test(str); } + function buildRegex(str) { + var regexPattern = /^\/(.*)\/([gmi]*)$/m; + var matches = str.match(regexPattern); + var pattern = matches[1]; + var flags = matches[2] !== "" ? matches[2] : undefined; + return new RegExp(pattern, flags); + } + $scope.parseTarget = function() { $scope.initFilters(); var newTarget = _.cloneDeep($scope.panel.triggers); From de7c4bf7118d4e7cf0e7dae6588638cb5221d9bf Mon Sep 17 00:00:00 2001 From: Alexander Zobnin Date: Wed, 3 Feb 2016 14:44:13 +0300 Subject: [PATCH 123/205] Sort triggers by severity. --- plugins/panel-triggers/module.js | 15 ++++++++++----- 1 file changed, 10 insertions(+), 5 deletions(-) diff --git a/plugins/panel-triggers/module.js b/plugins/panel-triggers/module.js index 049759e01..9c71ead4b 100644 --- a/plugins/panel-triggers/module.js +++ b/plugins/panel-triggers/module.js @@ -182,22 +182,27 @@ function (angular, app, _, $, config, PanelMeta) { // Filter acknowledged triggers if ($scope.panel.showTriggers === 'unacknowledged') { - $scope.triggerList = _.filter(triggerList, function (trigger) { + triggerList = _.filter(triggerList, function (trigger) { return !trigger.acknowledges; }); } else if ($scope.panel.showTriggers === 'acknowledged') { - $scope.triggerList = _.filter(triggerList, 'acknowledges'); + triggerList = _.filter(triggerList, 'acknowledges'); } else { - $scope.triggerList = triggerList; + triggerList = triggerList; } // Filter triggers by severity - $scope.triggerList = _.filter($scope.triggerList, function (trigger) { + triggerList = _.filter(triggerList, function (trigger) { return $scope.panel.triggerSeverity[trigger.priority].show; }); + // Sort triggers + if ($scope.panel.sortTriggersBy.value === 'priority') { + triggerList = _.sortBy(triggerList, 'priority').reverse(); + } + // Limit triggers number - $scope.triggerList = _.first($scope.triggerList, $scope.panel.limit); + $scope.triggerList = _.first(triggerList, $scope.panel.limit); $scope.panelRenderingComplete(); }); From 99965296027f95a7596d9e07d7b25d27e9e403ae Mon Sep 17 00:00:00 2001 From: Alexander Zobnin Date: Wed, 3 Feb 2016 14:50:09 +0300 Subject: [PATCH 124/205] Trigger panel: fixed show triggers option. --- plugins/datasource-zabbix/zabbixAPI.js | 4 ++-- plugins/panel-triggers/module.js | 3 ++- 2 files changed, 4 insertions(+), 3 deletions(-) diff --git a/plugins/datasource-zabbix/zabbixAPI.js b/plugins/datasource-zabbix/zabbixAPI.js index 113687425..bdda740fb 100644 --- a/plugins/datasource-zabbix/zabbixAPI.js +++ b/plugins/datasource-zabbix/zabbixAPI.js @@ -296,7 +296,7 @@ function (angular, _) { return this.request('service.getsla', params); }; - p.getTriggers = function(groupids, hostids, applicationids) { + p.getTriggers = function(groupids, hostids, applicationids, showEvents) { var params = { output: 'extend', groupids: groupids, @@ -308,7 +308,7 @@ function (angular, _) { skipDependent: true, //only_true: true, filter: { - value: 1 + value: showEvents }, selectGroups: ['name'], selectHosts: ['name'], diff --git a/plugins/panel-triggers/module.js b/plugins/panel-triggers/module.js index 9c71ead4b..43d2eaba4 100644 --- a/plugins/panel-triggers/module.js +++ b/plugins/panel-triggers/module.js @@ -123,6 +123,7 @@ function (angular, app, _, $, config, PanelMeta) { var zabbix = datasource.zabbixAPI; var queryProcessor = datasource.queryProcessor; var triggerFilter = $scope.panel.triggers; + var showEvents = $scope.panel.showEvents.value; var buildQuery = queryProcessor.buildTriggerQuery(triggerFilter.group.filter, triggerFilter.host.filter, triggerFilter.application.filter); @@ -130,7 +131,7 @@ function (angular, app, _, $, config, PanelMeta) { return zabbix.getTriggers(query.groupids, query.hostids, query.applicationids, - $scope.panel.showEvents.value) + showEvents) .then(function(triggers) { return _.map(triggers, function (trigger) { var lastchange = new Date(trigger.lastchange * 1000); From f1adb7f5154f8bc0701aa589c6e9527fefba5a08 Mon Sep 17 00:00:00 2001 From: Alexander Zobnin Date: Wed, 3 Feb 2016 15:30:50 +0300 Subject: [PATCH 125/205] Trigger panel: set color for Ok events. --- plugins/panel-triggers/editor.html | 28 +++++++++++++++++++++++++--- plugins/panel-triggers/module.js | 29 +++++++++++++++++++++++++++-- 2 files changed, 52 insertions(+), 5 deletions(-) diff --git a/plugins/panel-triggers/editor.html b/plugins/panel-triggers/editor.html index cee033808..bed41ebff 100644 --- a/plugins/panel-triggers/editor.html +++ b/plugins/panel-triggers/editor.html @@ -105,7 +105,7 @@
    Options
    Limit triggers number to
  • - Options Show events
  • - +
  • + +
    +
    diff --git a/plugins/panel-triggers/module.js b/plugins/panel-triggers/module.js index 43d2eaba4..50be62ca2 100644 --- a/plugins/panel-triggers/module.js +++ b/plugins/panel-triggers/module.js @@ -79,7 +79,8 @@ function (angular, app, _, $, config, PanelMeta) { showTriggers: 'all triggers', sortTriggersBy: { text: 'last change', value: 'lastchange' }, showEvents: { text: 'Problem events', value: '1' }, - triggerSeverity: grafanaDefaultSeverity + triggerSeverity: grafanaDefaultSeverity, + okEventColor: '#890F02' }; _.defaults($scope.panel, panelDefaults); @@ -145,7 +146,14 @@ function (angular, app, _, $, config, PanelMeta) { triggerObj.lastchangeUnix = lastchangeUnix; triggerObj.lastchange = lastchange.toLocaleString(); triggerObj.age = age.toLocaleString(); - triggerObj.color = $scope.panel.triggerSeverity[trigger.priority].color; + + // Set color + if (trigger.value === '1') { + triggerObj.color = $scope.panel.triggerSeverity[trigger.priority].color; + } else { + triggerObj.color = $scope.panel.okEventColor; + } + triggerObj.severity = $scope.panel.triggerSeverity[trigger.priority].severity; return triggerObj; }); @@ -327,6 +335,23 @@ function (angular, app, _, $, config, PanelMeta) { }); }; + $scope.openOkEventColorSelector = function(event) { + var el = $(event.currentTarget); + var popoverScope = $scope.$new(); + popoverScope.trigger = {color: $scope.panel.okEventColor}; + popoverScope.changeTriggerSeverityColor = function(trigger, color) { + $scope.panel.okEventColor = color; + $scope.refreshTriggerSeverity(); + }; + + popoverSrv.show({ + element: el, + placement: 'top', + templateUrl: 'public/plugins/triggers/trigger.colorpicker.html', + scope: popoverScope + }); + }; + /** * Convert event age from Unix format (milliseconds sins 1970) * to Zabbix format (like at Last 20 issues panel). From 6f9443d5ced63517fbc0f02894c6a7cf544ec7af Mon Sep 17 00:00:00 2001 From: Alexander Zobnin Date: Wed, 3 Feb 2016 15:45:13 +0300 Subject: [PATCH 126/205] Trigger panel: fixed triggers sorting. --- plugins/panel-triggers/module.js | 2 ++ 1 file changed, 2 insertions(+) diff --git a/plugins/panel-triggers/module.js b/plugins/panel-triggers/module.js index 50be62ca2..c52e36d90 100644 --- a/plugins/panel-triggers/module.js +++ b/plugins/panel-triggers/module.js @@ -208,6 +208,8 @@ function (angular, app, _, $, config, PanelMeta) { // Sort triggers if ($scope.panel.sortTriggersBy.value === 'priority') { triggerList = _.sortBy(triggerList, 'priority').reverse(); + } else { + triggerList = _.sortBy(triggerList, 'lastchangeUnix').reverse(); } // Limit triggers number From 4c73f741597bc12e1caeec6c5cb9be32327aeae9 Mon Sep 17 00:00:00 2001 From: Alexander Zobnin Date: Wed, 3 Feb 2016 19:03:37 +0300 Subject: [PATCH 127/205] Trigger panel: handle time with moment.js --- plugins/panel-triggers/module.html | 2 +- plugins/panel-triggers/module.js | 19 ++++++++----------- 2 files changed, 9 insertions(+), 12 deletions(-) diff --git a/plugins/panel-triggers/module.html b/plugins/panel-triggers/module.html index d9a972497..0d0783ce8 100644 --- a/plugins/panel-triggers/module.html +++ b/plugins/panel-triggers/module.html @@ -17,7 +17,7 @@
    Issue
    - +
    Last change
    diff --git a/plugins/panel-triggers/module.js b/plugins/panel-triggers/module.js index c52e36d90..6bf8cffcc 100644 --- a/plugins/panel-triggers/module.js +++ b/plugins/panel-triggers/module.js @@ -16,10 +16,11 @@ define([ 'app/app', 'lodash', 'jquery', + 'moment', 'app/core/config', 'app/features/panel/panel_meta' ], -function (angular, app, _, $, config, PanelMeta) { +function (angular, app, _, $, moment, config, PanelMeta) { 'use strict'; var module = angular.module('grafana.panels.triggers', []); @@ -135,17 +136,13 @@ function (angular, app, _, $, config, PanelMeta) { showEvents) .then(function(triggers) { return _.map(triggers, function (trigger) { - var lastchange = new Date(trigger.lastchange * 1000); - var lastchangeUnix = trigger.lastchange; - var now = new Date(); - - // Consider local time offset - var ageUnix = now - lastchange + now.getTimezoneOffset() * 60000; - var age = toZabbixAgeFormat(ageUnix); var triggerObj = trigger; - triggerObj.lastchangeUnix = lastchangeUnix; - triggerObj.lastchange = lastchange.toLocaleString(); - triggerObj.age = age.toLocaleString(); + + // Format last change and age + trigger.lastchangeUnix = Number(trigger.lastchange); + var timestamp = moment.unix(trigger.lastchangeUnix); + triggerObj.lastchange = timestamp.format("DD MMM YYYY, HH:mm:ss"); + triggerObj.age = timestamp.fromNow(true); // Set color if (trigger.value === '1') { From 19d1cc315e7827406c75f3b7a9ce996fd5bf7870 Mon Sep 17 00:00:00 2001 From: Alexander Zobnin Date: Wed, 3 Feb 2016 19:54:44 +0300 Subject: [PATCH 128/205] Trigger panel: able to set custom last change time format (based on moment.js). --- plugins/panel-triggers/editor.html | 34 +++++++++++++++++++++++++++++- plugins/panel-triggers/module.js | 12 ++++++++--- 2 files changed, 42 insertions(+), 4 deletions(-) diff --git a/plugins/panel-triggers/editor.html b/plugins/panel-triggers/editor.html index bed41ebff..d92105ca9 100644 --- a/plugins/panel-triggers/editor.html +++ b/plugins/panel-triggers/editor.html @@ -61,7 +61,7 @@
    Select triggers
    placeholder="trigger name" class="input-large tight-form-input last" ng-style="panel.triggers.trigger.style" - empty-to-null"> + empty-to-null>
    @@ -192,6 +192,38 @@
    Options
    +
    + +
    +
    diff --git a/plugins/panel-triggers/module.js b/plugins/panel-triggers/module.js index 6bf8cffcc..8f780bda1 100644 --- a/plugins/panel-triggers/module.js +++ b/plugins/panel-triggers/module.js @@ -81,7 +81,7 @@ function (angular, app, _, $, moment, config, PanelMeta) { sortTriggersBy: { text: 'last change', value: 'lastchange' }, showEvents: { text: 'Problem events', value: '1' }, triggerSeverity: grafanaDefaultSeverity, - okEventColor: '#890F02' + okEventColor: '#890F02', }; _.defaults($scope.panel, panelDefaults); @@ -97,7 +97,8 @@ function (angular, app, _, $, moment, config, PanelMeta) { var scopeDefaults = { metric: {}, inputStyles: {}, - oldTarget: _.cloneDeep($scope.panel.triggers) + oldTarget: _.cloneDeep($scope.panel.triggers), + defaultTimeFormat: "DD MMM YYYY HH:mm:ss" }; _.defaults($scope, scopeDefaults); @@ -141,7 +142,12 @@ function (angular, app, _, $, moment, config, PanelMeta) { // Format last change and age trigger.lastchangeUnix = Number(trigger.lastchange); var timestamp = moment.unix(trigger.lastchangeUnix); - triggerObj.lastchange = timestamp.format("DD MMM YYYY, HH:mm:ss"); + if ($scope.panel.customLastChangeFormat) { + // User defined format + triggerObj.lastchange = timestamp.format($scope.panel.lastChangeFormat); + } else { + triggerObj.lastchange = timestamp.format($scope.defaultTimeFormat); + } triggerObj.age = timestamp.fromNow(true); // Set color From a23dbe6aa71b3918d65e3cdc97ecd42683471e27 Mon Sep 17 00:00:00 2001 From: Alexander Zobnin Date: Wed, 3 Feb 2016 20:47:24 +0300 Subject: [PATCH 129/205] Trigger panel: color picker changed to spectrum picker. --- plugins/panel-triggers/editor.html | 46 ++++++++---------------------- 1 file changed, 12 insertions(+), 34 deletions(-) diff --git a/plugins/panel-triggers/editor.html b/plugins/panel-triggers/editor.html index d92105ca9..a4986f0c9 100644 --- a/plugins/panel-triggers/editor.html +++ b/plugins/panel-triggers/editor.html @@ -229,35 +229,23 @@
    Options
    Customize triggers severity and colors
    + ng-repeat="trigger in panel.triggerSeverity">
      -
    • +
    • {{ trigger.priority }}
    • -
    • -   -   - +
    • +
    • @@ -272,25 +260,15 @@
      Customize triggers severity and colors
    -
    +
      -
    • +
    • OK event color
    • -
    • -   -   - +
    • +
    From 80022e85988e5a5befd7c8fd6bf9a6328e248eb3 Mon Sep 17 00:00:00 2001 From: Alexander Zobnin Date: Wed, 3 Feb 2016 21:04:10 +0300 Subject: [PATCH 130/205] Trigger panel: added custom trigger status field (OK or Problem). --- plugins/panel-triggers/editor.html | 11 ++++++++++- plugins/panel-triggers/module.html | 8 ++++++++ plugins/panel-triggers/module.js | 6 ++++++ 3 files changed, 24 insertions(+), 1 deletion(-) diff --git a/plugins/panel-triggers/editor.html b/plugins/panel-triggers/editor.html index a4986f0c9..1bb0e3b58 100644 --- a/plugins/panel-triggers/editor.html +++ b/plugins/panel-triggers/editor.html @@ -130,7 +130,7 @@
    Options
    Show events
  • - + +
  • + +
    Status
    +
    Severity
    @@ -35,6 +38,11 @@ {{trigger.host}}
  • + +
    + {{triggerStatusMap[trigger.value]}} +
    +
    {{trigger.severity}} diff --git a/plugins/panel-triggers/module.js b/plugins/panel-triggers/module.js index 8f780bda1..197e86582 100644 --- a/plugins/panel-triggers/module.js +++ b/plugins/panel-triggers/module.js @@ -54,6 +54,11 @@ function (angular, app, _, $, moment, config, PanelMeta) { { text: 'Problem events', value: 1 } ]; + $scope.triggerStatusMap = { + '0': 'OK', + '1': 'Problem' + }; + var grafanaDefaultSeverity = [ { priority: 0, severity: 'Not classified', color: '#B7DBAB', show: true }, { priority: 1, severity: 'Information', color: '#82B5D8', show: true }, @@ -72,6 +77,7 @@ function (angular, app, _, $, moment, config, PanelMeta) { trigger: {filter: ""} }, hostField: true, + statusField: false, severityField: false, lastChangeField: true, ageField: true, From 4bcf741d66aeb51f7576df87ab630cb6af17c2bd Mon Sep 17 00:00:00 2001 From: Alexander Zobnin Date: Thu, 4 Feb 2016 19:49:21 +0300 Subject: [PATCH 131/205] Fixed annotations. --- plugins/datasource-zabbix/datasource.js | 95 +++++++++--------- .../partials/annotations.editor.html | 96 ++++++++++--------- plugins/datasource-zabbix/utils.js | 24 +++++ plugins/datasource-zabbix/zabbixAPI.js | 6 +- 4 files changed, 129 insertions(+), 92 deletions(-) diff --git a/plugins/datasource-zabbix/datasource.js b/plugins/datasource-zabbix/datasource.js index 18ee0218d..611922980 100644 --- a/plugins/datasource-zabbix/datasource.js +++ b/plugins/datasource-zabbix/datasource.js @@ -344,75 +344,76 @@ function (angular, _, dateMath, utils, metricFunctions) { var to = Math.ceil(dateMath.parse(options.rangeRaw.to) / 1000); var annotation = options.annotation; var self = this; + var showEvents = annotation.showOkEvents ? [0, 1] : 1; + + var buildQuery = self.queryProcessor.buildTriggerQuery(templateSrv.replace(annotation.group), + templateSrv.replace(annotation.host), + templateSrv.replace(annotation.application)); + return buildQuery.then(function(query) { + return self.zabbixAPI.getTriggers(query.groupids, + query.hostids, + query.applicationids, + showEvents) + .then(function(triggers) { + + // Filter triggers by description + if (utils.isRegex(annotation.trigger)) { + triggers = _.filter(triggers, function(trigger) { + return utils.buildRegex(annotation.trigger).test(trigger.description); + }); + } else { + triggers = _.filter(triggers, function(trigger) { + return trigger.description === annotation.trigger; + }); + } - // Remove events below the chose severity - var severities = []; - for (var i = 5; i >= annotation.minseverity; i--) { - severities.push(i); - } - var params = { - output: ['triggerid', 'description', 'priority'], - preservekeys: 1, - filter: { 'priority': severities }, - search: { - 'description': annotation.trigger - }, - searchWildcardsEnabled: true, - expandDescription: true - }; - if (annotation.host) { - params.host = templateSrv.replace(annotation.host); - } - else if (annotation.group) { - params.group = templateSrv.replace(annotation.group); - } + // Remove events below the chose severity + triggers = _.filter(triggers, function(trigger) { + return Number(trigger.priority) >= Number(annotation.minseverity); + }); - return this.zabbixAPI.performZabbixAPIRequest('trigger.get', params) - .then(function (result) { - if(result) { - var objects = result; + var objectids = _.map(triggers, 'triggerid'); var params = { output: 'extend', time_from: from, time_till: to, - objectids: _.keys(objects), + objectids: objectids, select_acknowledges: 'extend', - selectHosts: 'extend' + selectHosts: 'extend', + value: showEvents }; - // Show problem events only - if (!annotation.showOkEvents) { - params.value = 1; - } + return self.zabbixAPI.request('event.get', params) + .then(function (events) { + var indexedTriggers = _.indexBy(triggers, 'triggerid'); - return self.zabbixAPI.performZabbixAPIRequest('event.get', params) - .then(function (result) { - var events = []; + // Hide acknowledged events if option enabled + if (annotation.hideAcknowledged) { + events = _.filter(events, function(event) { + return !event.acknowledges.length; + }); + } - _.each(result, function(e) { + return _.map(events, function(e) { var title =''; if (annotation.showHostname) { title += e.hosts[0].name + ': '; } - title += Number(e.value) ? 'Problem' : 'OK'; - // Hide acknowledged events - if (e.acknowledges.length > 0 && annotation.showAcknowledged) { return; } + // Show event type (OK or Problem) + title += Number(e.value) ? 'Problem' : 'OK'; - var formatted_acknowledges = zabbixHelperSrv.formatAcknowledges(e.acknowledges); - events.push({ + var formatted_acknowledges = utils.formatAcknowledges(e.acknowledges); + return { annotation: annotation, time: e.clock * 1000, title: title, - text: objects[e.objectid].description + formatted_acknowledges - }); + text: indexedTriggers[e.objectid].description + formatted_acknowledges + }; }); - return events; }); - } else { - return []; - } - }); + }); + }); }; } diff --git a/plugins/datasource-zabbix/partials/annotations.editor.html b/plugins/datasource-zabbix/partials/annotations.editor.html index 7dc8fb8c3..42e05179b 100644 --- a/plugins/datasource-zabbix/partials/annotations.editor.html +++ b/plugins/datasource-zabbix/partials/annotations.editor.html @@ -1,53 +1,61 @@
    -
    -
    Zabbix trigger -
    -
    -
    - -
    -
    - - -
    -
    - - -
    -
    - - -
    -
    - - +
    +
    Filter Triggers
    +
    +
      +
    • + Group +
    • +
    • + +
    • +
    • + Host +
    • +
    • + +
    • +
    +
    +
    +
    +
      +
    • + Application +
    • +
    • + +
    • +
    • + Trigger +
    • +
    • + +
    • +
    +
    +
    Options
    - - - - - - +
    + + +
    + + +
    diff --git a/plugins/datasource-zabbix/utils.js b/plugins/datasource-zabbix/utils.js index c2ba09c37..c92c616d4 100644 --- a/plugins/datasource-zabbix/utils.js +++ b/plugins/datasource-zabbix/utils.js @@ -47,6 +47,30 @@ function (_, moment) { return moment.duration(Number(momentInterval[1]), momentInterval[2]).valueOf(); }; + /** + * Format acknowledges. + * + * @param {array} acknowledges array of Zabbix acknowledge objects + * @return {string} HTML-formatted table + */ + this.formatAcknowledges = function(acknowledges) { + if (acknowledges.length) { + var formatted_acknowledges = '

    Acknowledges:
    ' + + ''; + _.each(_.map(acknowledges, function (ack) { + var timestamp = moment.unix(ack.clock); + return ''; + }), function (ack) { + formatted_acknowledges = formatted_acknowledges.concat(ack); + }); + formatted_acknowledges = formatted_acknowledges.concat('
    TimeUserComments
    ' + timestamp.format("DD MMM YYYY HH:mm:ss") + '' + ack.alias + + ' (' + ack.name + ' ' + ack.surname + ')' + '' + ack.message + '
    '); + return formatted_acknowledges; + } else { + return ''; + } + }; + } return new Utils(); diff --git a/plugins/datasource-zabbix/zabbixAPI.js b/plugins/datasource-zabbix/zabbixAPI.js index bdda740fb..5c1b61dbe 100644 --- a/plugins/datasource-zabbix/zabbixAPI.js +++ b/plugins/datasource-zabbix/zabbixAPI.js @@ -308,7 +308,7 @@ function (angular, _) { skipDependent: true, //only_true: true, filter: { - value: showEvents + value: 1 }, selectGroups: ['name'], selectHosts: ['name'], @@ -316,6 +316,10 @@ function (angular, _) { selectLastEvent: 'extend' }; + if (showEvents) { + params.filter.value = showEvents; + } + return this.request('trigger.get', params); }; From 5a77fafc6101d8f7c1a69ce3b6f97074877e16c6 Mon Sep 17 00:00:00 2001 From: Alexander Zobnin Date: Thu, 4 Feb 2016 22:54:15 +0300 Subject: [PATCH 132/205] Fixed annotation filter. Show all events when filter is empty. --- plugins/datasource-zabbix/datasource.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/plugins/datasource-zabbix/datasource.js b/plugins/datasource-zabbix/datasource.js index 611922980..5aaaf5cee 100644 --- a/plugins/datasource-zabbix/datasource.js +++ b/plugins/datasource-zabbix/datasource.js @@ -361,7 +361,7 @@ function (angular, _, dateMath, utils, metricFunctions) { triggers = _.filter(triggers, function(trigger) { return utils.buildRegex(annotation.trigger).test(trigger.description); }); - } else { + } else if (annotation.trigger) { triggers = _.filter(triggers, function(trigger) { return trigger.description === annotation.trigger; }); From 16a77862473068c2166b556349fa0f892629d1f1 Mon Sep 17 00:00:00 2001 From: Alexander Zobnin Date: Fri, 5 Feb 2016 12:11:23 +0300 Subject: [PATCH 133/205] Able to specify trend value (min, max or avg by default). --- plugins/datasource-zabbix/datasource.js | 12 +++++++++--- plugins/datasource-zabbix/metricFunctions.js | 10 ++++++++++ 2 files changed, 19 insertions(+), 3 deletions(-) diff --git a/plugins/datasource-zabbix/datasource.js b/plugins/datasource-zabbix/datasource.js index 5aaaf5cee..d08944471 100644 --- a/plugins/datasource-zabbix/datasource.js +++ b/plugins/datasource-zabbix/datasource.js @@ -132,12 +132,18 @@ function (angular, _, dateMath, utils, metricFunctions) { .then(function(items) { // Add hostname for items from multiple hosts var addHostName = target.host.isRegex; - var getHistory; + + // Use trends if ((from < useTrendsFrom) && self.trends) { - // Use trends - var valueType = target.downsampleFunction ? target.downsampleFunction.value : "avg"; + // Find trendValue() function and get specified trend value + var trendFunctions = _.map(metricFunctions.getCategories()['Trends'], 'name'); + var trendValueFunc = _.find(target.functions, function(func) { + return _.contains(trendFunctions, func.def.name); + }); + var valueType = trendValueFunc ? trendValueFunc.params[0] : "avg"; + getHistory = self.zabbixAPI.getTrend(items, from, to).then(function(history) { return self.queryProcessor.handleTrends(history, addHostName, valueType); }); diff --git a/plugins/datasource-zabbix/metricFunctions.js b/plugins/datasource-zabbix/metricFunctions.js index cf5509211..c0cc2b36e 100644 --- a/plugins/datasource-zabbix/metricFunctions.js +++ b/plugins/datasource-zabbix/metricFunctions.js @@ -9,6 +9,7 @@ function (_, $) { var categories = { Transform: [], Aggregate: [], + Trends: [], Alias: [] }; @@ -76,6 +77,15 @@ function (_, $) { defaultParams: ['1m'], }); + addFuncDef({ + name: 'trendValue', + category: 'Trends', + params: [ + { name: 'type', type: 'string', options: ['avg', 'min', 'max'] } + ], + defaultParams: ['avg'], + }); + addFuncDef({ name: 'setAlias', category: 'Alias', From 91c9d8118a277f55640daf6de1f67c40042b76b3 Mon Sep 17 00:00:00 2001 From: Alexander Zobnin Date: Sat, 6 Feb 2016 18:06:36 +0300 Subject: [PATCH 134/205] New Grafana plugin api: fixed module.js. --- plugins/datasource-zabbix/module.js | 18 +++++++++--------- 1 file changed, 9 insertions(+), 9 deletions(-) diff --git a/plugins/datasource-zabbix/module.js b/plugins/datasource-zabbix/module.js index ce52d1c8f..f79b3c674 100644 --- a/plugins/datasource-zabbix/module.js +++ b/plugins/datasource-zabbix/module.js @@ -1,30 +1,30 @@ define([ - './datasource', + './datasource' ], function (ZabbixAPIDatasource) { 'use strict'; - function metricsQueryEditor() { + function ZabbixQueryCtrl() { return {controller: 'ZabbixAPIQueryCtrl', templateUrl: 'public/plugins/zabbix/partials/query.editor.html'}; } - function metricsQueryOptions() { + function ZabbixQueryOptionsCtrl() { return {templateUrl: 'public/plugins/zabbix/partials/query.options.html'}; } - function annotationsQueryEditor() { + function ZabbixAnnotationsQueryCtrl() { return {templateUrl: 'public/plugins/zabbix/partials/annotations.editor.html'}; } - function configView() { + function ZabbixConfigCtrl() { return {templateUrl: 'public/plugins/zabbix/partials/config.html'}; } return { Datasource: ZabbixAPIDatasource, - configView: configView, - annotationsQueryEditor: annotationsQueryEditor, - metricsQueryEditor: metricsQueryEditor, - metricsQueryOptions: metricsQueryOptions, + ConfigCtrl: ZabbixConfigCtrl, + QueryCtrl: ZabbixQueryCtrl, + QueryOptionsCtrl: ZabbixQueryOptionsCtrl, + AnnotationsQueryCtrl: ZabbixAnnotationsQueryCtrl }; }); From bc042bd1d959b801806dc4b274e35a2faeb4154e Mon Sep 17 00:00:00 2001 From: Alexander Zobnin Date: Wed, 10 Feb 2016 13:44:20 +0300 Subject: [PATCH 135/205] Iss #152 - fixed query editor. Query editor now use app/plugins/sdk for work. Fixed controller and template. --- .../datasource-zabbix/addMetricFunction.js | 2 +- .../datasource-zabbix/metricFunctionEditor.js | 17 +- plugins/datasource-zabbix/module.js | 11 +- .../partials/query.editor.html | 147 ++++--- plugins/datasource-zabbix/queryCtrl.js | 404 ++++++++---------- 5 files changed, 274 insertions(+), 307 deletions(-) diff --git a/plugins/datasource-zabbix/addMetricFunction.js b/plugins/datasource-zabbix/addMetricFunction.js index 4bc4014b6..2001265b5 100644 --- a/plugins/datasource-zabbix/addMetricFunction.js +++ b/plugins/datasource-zabbix/addMetricFunction.js @@ -98,7 +98,7 @@ function (angular, _, $, metricFunctions) { submenu: _.map(list, function(value) { return { text: value.name, - click: "addFunction('" + value.name + "')", + click: "ctrl.addFunction('" + value.name + "')", }; }) }; diff --git a/plugins/datasource-zabbix/metricFunctionEditor.js b/plugins/datasource-zabbix/metricFunctionEditor.js index 2b14d69ee..a7af58c54 100644 --- a/plugins/datasource-zabbix/metricFunctionEditor.js +++ b/plugins/datasource-zabbix/metricFunctionEditor.js @@ -27,6 +27,7 @@ function (angular, _, $) { link: function postLink($scope, elem) { var $funcLink = $(funcSpanTemplate); var $funcControls = $(funcControlsTemplate); + var ctrl = $scope.ctrl; var func = $scope.func; var funcDef = func.def; var scheduledRelink = false; @@ -79,11 +80,13 @@ function (angular, _, $) { func.updateParam($input.val(), paramIndex); scheduledRelinkIfNeeded(); - $scope.$apply($scope.targetChanged); - } + $scope.$apply(function() { + ctrl.targetChanged(); + }); - $input.hide(); - $link.show(); + $input.hide(); + $link.show(); + } } function inputKeyPress(paramIndex, e) { @@ -198,7 +201,7 @@ function (angular, _, $) { if ($target.hasClass('fa-remove')) { toggleFuncControls(); $scope.$apply(function() { - $scope.removeFunction($scope.func); + ctrl.removeFunction($scope.func); }); return; } @@ -206,7 +209,7 @@ function (angular, _, $) { if ($target.hasClass('fa-arrow-left')) { $scope.$apply(function() { _.move($scope.target.functions, $scope.$index, $scope.$index - 1); - $scope.targetChanged(); + ctrl.targetChanged(); }); return; } @@ -214,7 +217,7 @@ function (angular, _, $) { if ($target.hasClass('fa-arrow-right')) { $scope.$apply(function() { _.move($scope.target.functions, $scope.$index, $scope.$index + 1); - $scope.targetChanged(); + ctrl.targetChanged(); }); return; } diff --git a/plugins/datasource-zabbix/module.js b/plugins/datasource-zabbix/module.js index f79b3c674..846f37e64 100644 --- a/plugins/datasource-zabbix/module.js +++ b/plugins/datasource-zabbix/module.js @@ -1,13 +1,10 @@ define([ - './datasource' + './datasource', + './queryCtrl' ], -function (ZabbixAPIDatasource) { +function (ZabbixAPIDatasource, ZabbixQueryCtrl) { 'use strict'; - function ZabbixQueryCtrl() { - return {controller: 'ZabbixAPIQueryCtrl', templateUrl: 'public/plugins/zabbix/partials/query.editor.html'}; - } - function ZabbixQueryOptionsCtrl() { return {templateUrl: 'public/plugins/zabbix/partials/query.options.html'}; } @@ -22,8 +19,8 @@ function (ZabbixAPIDatasource) { return { Datasource: ZabbixAPIDatasource, - ConfigCtrl: ZabbixConfigCtrl, QueryCtrl: ZabbixQueryCtrl, + ConfigCtrl: ZabbixConfigCtrl, QueryOptionsCtrl: ZabbixQueryOptionsCtrl, AnnotationsQueryCtrl: ZabbixAnnotationsQueryCtrl }; diff --git a/plugins/datasource-zabbix/partials/query.editor.html b/plugins/datasource-zabbix/partials/query.editor.html index 0bed21c58..48504eb7d 100644 --- a/plugins/datasource-zabbix/partials/query.editor.html +++ b/plugins/datasource-zabbix/partials/query.editor.html @@ -1,7 +1,7 @@
    • - {{target.refId}} + {{ctrl.target.refId}}
    • - +
    -
    -
    +
    -
    +
    -
    +
    diff --git a/plugins/datasource-zabbix/queryCtrl.js b/plugins/datasource-zabbix/queryCtrl.js index 56d5401ac..6f927c31f 100644 --- a/plugins/datasource-zabbix/queryCtrl.js +++ b/plugins/datasource-zabbix/queryCtrl.js @@ -1,26 +1,42 @@ define([ - 'angular', - 'lodash', - './metricFunctions', - './utils' - ], - function (angular, _, metricFunctions, Utils) { - 'use strict'; + 'app/plugins/sdk', + 'angular', + 'lodash', + './metricFunctions', + './utils' +], +function (sdk, angular, _, metricFunctions, utils) { + 'use strict'; - var module = angular.module('grafana.controllers'); - var targetLetters = 'ABCDEFGHIJKLMNOPQRSTUVWXYZ'; + var ZabbixQueryCtrl = (function(_super) { - module.controller('ZabbixAPIQueryCtrl', function ($scope, $sce, $q, templateSrv) { + // ZabbixQueryCtrl constructor + function ZabbixQueryCtrl($scope, $injector, $sce, $q, templateSrv) { - var zabbixCache = $scope.datasource.zabbixCache; + // Call superclass constructor + _super.call(this, $scope, $injector); - $scope.init = function () { - $scope.targetLetters = targetLetters; + this.editorModes = { + 0: 'num', + 1: 'itservice', + 2: 'text' + }; + + // Map functions for bs-typeahead + this.getGroupNames = _.partial(getMetricNames, this, 'groupList'); + this.getHostNames = _.partial(getMetricNames, this, 'filteredHosts'); + this.getApplicationNames = _.partial(getMetricNames, this, 'filteredApplications'); + this.getItemNames = _.partial(getMetricNames, this, 'filteredItems'); + + this.init = function() { + + this.templateSrv = templateSrv; + var target = this.target; var scopeDefaults = { metric: {} }; - _.defaults($scope, scopeDefaults); + _.defaults(this, scopeDefaults); // Load default values var targetDefaults = { @@ -31,250 +47,196 @@ define([ item: { filter: "" }, functions: [], }; - _.defaults($scope.target, targetDefaults); + _.defaults(target, targetDefaults); // Create function instances from saved JSON - $scope.target.functions = _.map($scope.target.functions, function(func) { + target.functions = _.map(target.functions, function(func) { return metricFunctions.createFuncInstance(func.def, func.params); }); - if ($scope.target.mode === 0 || - $scope.target.mode === 2) { + if (target.mode === 0 || + target.mode === 2) { - $scope.downsampleFunctionList = [ + this.downsampleFunctionList = [ {name: "avg", value: "avg"}, {name: "min", value: "min"}, {name: "max", value: "max"} ]; // Set avg by default - if (!$scope.target.downsampleFunction) { - $scope.target.downsampleFunction = $scope.downsampleFunctionList[0]; + if (!target.downsampleFunction) { + target.downsampleFunction = this.downsampleFunctionList[0]; } - $scope.initFilters(); + this.initFilters(); } - else if ($scope.target.mode === 1) { - $scope.slaPropertyList = [ + else if (target.mode === 1) { + this.slaPropertyList = [ {name: "Status", property: "status"}, {name: "SLA", property: "sla"}, {name: "OK time", property: "okTime"}, {name: "Problem time", property: "problemTime"}, {name: "Down time", property: "downtimeTime"} ]; - $scope.itserviceList = [{name: "test"}]; - $scope.updateITServiceList(); + this.itserviceList = [{name: "test"}]; + this.updateITServiceList(); } }; - $scope.initFilters = function () { - $scope.filterGroups(); - $scope.filterHosts(); - $scope.filterApplications(); - $scope.filterItems(); - }; - - // Get list of metric names for bs-typeahead directive - function getMetricNames(scope, metricList) { - return _.uniq(_.map(scope.metric[metricList], 'name')); - } - - // Map functions for bs-typeahead - $scope.getGroupNames = _.partial(getMetricNames, $scope, 'groupList'); - $scope.getHostNames = _.partial(getMetricNames, $scope, 'filteredHosts'); - $scope.getApplicationNames = _.partial(getMetricNames, $scope, 'filteredApplications'); - $scope.getItemNames = _.partial(getMetricNames, $scope, 'filteredItems'); - - $scope.filterHosts = function () { - var groupFilter = templateSrv.replace($scope.target.group.filter); - $scope.datasource.queryProcessor.filterHosts(groupFilter).then(function(hosts) { - $scope.metric.filteredHosts = hosts; + this.init(); + } + + ZabbixQueryCtrl.templateUrl = 'partials/query.editor.html'; + + ZabbixQueryCtrl.prototype = Object.create(_super.prototype); + ZabbixQueryCtrl.prototype.constructor = ZabbixQueryCtrl; + + var p = ZabbixQueryCtrl.prototype; + + p.initFilters = function () { + this.filterGroups(); + this.filterHosts(); + this.filterApplications(); + this.filterItems(); + }; + + p.filterHosts = function () { + var self = this; + var groupFilter = this.templateSrv.replace(this.target.group.filter); + this.datasource.queryProcessor.filterHosts(groupFilter).then(function(hosts) { + self.metric.filteredHosts = hosts; + }); + }; + + p.filterGroups = function() { + var self = this; + this.datasource.queryProcessor.filterGroups().then(function(groups) { + self.metric.groupList = groups; + }); + }; + + p.filterApplications = function () { + var self = this; + var groupFilter = this.templateSrv.replace(this.target.group.filter); + var hostFilter = this.templateSrv.replace(this.target.host.filter); + this.datasource.queryProcessor.filterApplications(groupFilter, hostFilter) + .then(function(apps) { + self.metric.filteredApplications = apps; }); - }; - - $scope.filterGroups = function() { - $scope.datasource.queryProcessor.filterGroups().then(function(groups) { - $scope.metric.groupList = groups; + }; + + p.filterItems = function () { + var self = this; + var item_type = this.editorModes[this.target.mode]; + var groupFilter = this.templateSrv.replace(this.target.group.filter); + var hostFilter = this.templateSrv.replace(this.target.host.filter); + var appFilter = this.templateSrv.replace(this.target.application.filter); + this.datasource.queryProcessor.filterItems(groupFilter, hostFilter, appFilter, + item_type, this.target.showDisabledItems).then(function(items) { + self.metric.filteredItems = items; }); - }; + }; - $scope.filterApplications = function () { - var groupFilter = templateSrv.replace($scope.target.group.filter); - var hostFilter = templateSrv.replace($scope.target.host.filter); - $scope.datasource.queryProcessor.filterApplications(groupFilter, hostFilter) - .then(function(apps) { - $scope.metric.filteredApplications = apps; - }); - }; + p.onTargetPartChange = function (targetPart) { + var regexStyle = {'color': '#CCA300'}; + targetPart.isRegex = utils.isRegex(targetPart.filter); + targetPart.style = targetPart.isRegex ? regexStyle : {}; + }; - $scope.filterItems = function () { - var item_type = $scope.editorModes[$scope.target.mode]; - var groupFilter = templateSrv.replace($scope.target.group.filter); - var hostFilter = templateSrv.replace($scope.target.host.filter); - var appFilter = templateSrv.replace($scope.target.application.filter); - $scope.datasource.queryProcessor.filterItems(groupFilter, hostFilter, appFilter, - item_type, $scope.target.showDisabledItems).then(function(items) { - $scope.metric.filteredItems = items; - }); - }; - - $scope.onTargetPartChange = function (targetPart) { - var regexStyle = {'color': '#CCA300'}; - targetPart.isRegex = Utils.isRegex(targetPart.filter); - targetPart.style = targetPart.isRegex ? regexStyle : {}; - }; - - // Handle group blur and filter hosts - $scope.onGroupBlur = function() { - $scope.initFilters(); - $scope.parseTarget(); - $scope.get_data(); - }; - - // Handle host blur and filter applications - $scope.onHostBlur = function() { - $scope.initFilters(); - $scope.parseTarget(); - $scope.get_data(); - }; - - // Handle application blur and filter items - $scope.onApplicationBlur = function() { - $scope.initFilters(); - $scope.parseTarget(); - $scope.get_data(); - }; - - $scope.onItemBlur = function () { - $scope.parseTarget(); - $scope.get_data(); - }; - - $scope.parseTarget = function() { - // Parse target - }; + p.onTargetBlur = function() { + this.initFilters(); + this.parseTarget(); + this.panelCtrl.refresh(); + }; - $scope.targetChanged = function() { - //console.log($scope.target); - $scope.get_data(); - }; + p.parseTarget = function() { + // Parse target + }; - // Validate target and set validation info - $scope.validateTarget = function () {}; + // Validate target and set validation info + p.validateTarget = function () {}; - $scope.addFunction = function(funcDef) { - var newFunc = metricFunctions.createFuncInstance(funcDef); - newFunc.added = true; - $scope.target.functions.push(newFunc); + p.targetChanged = function() { + this.panelCtrl.refresh(); + }; - $scope.moveAliasFuncLast(); + p.addFunction = function(funcDef) { + var newFunc = metricFunctions.createFuncInstance(funcDef); + newFunc.added = true; + this.target.functions.push(newFunc); - if (newFunc.params.length && newFunc.added || - newFunc.def.params.length === 0) { - $scope.targetChanged(); - } - }; + this.moveAliasFuncLast(); - $scope.removeFunction = function(func) { - $scope.target.functions = _.without($scope.target.functions, func); - $scope.targetChanged(); - }; - - $scope.moveAliasFuncLast = function() { - var aliasFunc = _.find($scope.target.functions, function(func) { - return func.def.name === 'alias' || - func.def.name === 'aliasByNode' || - func.def.name === 'aliasByMetric'; - }); - - if (aliasFunc) { - $scope.target.functions = _.without($scope.target.functions, aliasFunc); - $scope.target.functions.push(aliasFunc); - } - }; - - $scope.functionChanged = function() {}; - - $scope.editorModes = { - 0: 'num', - 1: 'itservice', - 2: 'text' - }; - - /** - * Switch query editor to specified mode. - * Modes: - * 0 - items - * 1 - IT services - * 2 - Text metrics - */ - $scope.switchEditorMode = function (mode) { - $scope.target.mode = mode; - $scope.init(); - }; - - /** - * Take alias from item name by default - */ - function setItemAlias() { - if (!$scope.target.alias && $scope.target.item) { - $scope.target.alias = $scope.target.item.name; - } + if (newFunc.params.length && newFunc.added || + newFunc.def.params.length === 0) { + this.targetChanged(); } + }; + + p.removeFunction = function(func) { + this.target.functions = _.without(this.target.functions, func); + this.targetChanged(); + }; + + p.moveAliasFuncLast = function() { + var aliasFunc = _.find(this.target.functions, function(func) { + return func.def.name === 'alias' || + func.def.name === 'aliasByNode' || + func.def.name === 'aliasByMetric'; + }); + + if (aliasFunc) { + this.target.functions = _.without(this.target.functions, aliasFunc); + this.target.functions.push(aliasFunc); + } + }; + + /** + * Switch query editor to specified mode. + * Modes: + * 0 - items + * 1 - IT services + * 2 - Text metrics + */ + p.switchEditorMode = function (mode) { + this.target.mode = mode; + this.init(); + }; + + ///////////////// + // IT Services // + ///////////////// + + /** + * Update list of IT services + */ + p.updateITServiceList = function () { + var self = this; + this.datasource.zabbixAPI.getITService().then(function (iteservices) { + self.itserviceList = []; + self.itserviceList = self.itserviceList.concat(iteservices); + }); + }; + + /** + * Call when IT service is selected. + */ + p.selectITService = function () { + if (!_.isEqual(this.oldTarget, this.target) && _.isEmpty(this.target.errors)) { + this.oldTarget = angular.copy(this.target); + this.panelCtrl.refresh(); + } + }; - $scope.targetBlur = function () { - setItemAlias(); - if (!_.isEqual($scope.oldTarget, $scope.target) && _.isEmpty($scope.target.errors)) { - $scope.oldTarget = angular.copy($scope.target); - $scope.get_data(); - } - }; - - /** - * Call when IT service is selected. - */ - $scope.selectITService = function () { - if (!_.isEqual($scope.oldTarget, $scope.target) && _.isEmpty($scope.target.errors)) { - $scope.oldTarget = angular.copy($scope.target); - $scope.get_data(); - } - }; - - $scope.duplicate = function () { - var clone = angular.copy($scope.target); - $scope.panel.targets.push(clone); - }; - - $scope.moveMetricQuery = function (fromIndex, toIndex) { - _.move($scope.panel.targets, fromIndex, toIndex); - }; - - /** - * Update list of IT services - */ - $scope.updateITServiceList = function () { - $scope.datasource.zabbixAPI.getITService().then(function (iteservices) { - $scope.itserviceList = []; - $scope.itserviceList = $scope.itserviceList.concat(iteservices); - }); - }; + return ZabbixQueryCtrl; - /** - * 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({ - name: '$' + variable.name, - templated: true - }); - }); - } + })(sdk.QueryCtrl); - $scope.init(); + return ZabbixQueryCtrl; - }); + // Get list of metric names for bs-typeahead directive + function getMetricNames(scope, metricList) { + return _.uniq(_.map(scope.metric[metricList], 'name')); + } - }); +}); \ No newline at end of file From c7b1c1a0dc902932e52aaef68fcce3422e21cd94 Mon Sep 17 00:00:00 2001 From: Alexander Zobnin Date: Thu, 11 Feb 2016 20:55:38 +0300 Subject: [PATCH 136/205] New plugin API - fixed module.js --- plugins/datasource-zabbix/module.js | 15 ++++++--------- 1 file changed, 6 insertions(+), 9 deletions(-) diff --git a/plugins/datasource-zabbix/module.js b/plugins/datasource-zabbix/module.js index 846f37e64..38722d92a 100644 --- a/plugins/datasource-zabbix/module.js +++ b/plugins/datasource-zabbix/module.js @@ -5,17 +5,14 @@ define([ function (ZabbixAPIDatasource, ZabbixQueryCtrl) { 'use strict'; - function ZabbixQueryOptionsCtrl() { - return {templateUrl: 'public/plugins/zabbix/partials/query.options.html'}; - } + function ZabbixQueryOptionsCtrl() {} + ZabbixQueryOptionsCtrl.templateUrl = 'partials/query.options.html'; - function ZabbixAnnotationsQueryCtrl() { - return {templateUrl: 'public/plugins/zabbix/partials/annotations.editor.html'}; - } + function ZabbixAnnotationsQueryCtrl() {} + ZabbixAnnotationsQueryCtrl.templateUrl = 'partials/annotations.editor.html'; - function ZabbixConfigCtrl() { - return {templateUrl: 'public/plugins/zabbix/partials/config.html'}; - } + function ZabbixConfigCtrl() {} + ZabbixConfigCtrl.templateUrl = 'partials/config.html'; return { Datasource: ZabbixAPIDatasource, From 7aa1d5bee82c7f89faab375d71d67c68de560660 Mon Sep 17 00:00:00 2001 From: Alexander Zobnin Date: Thu, 11 Feb 2016 20:57:46 +0300 Subject: [PATCH 137/205] New plugin API - fixed data source config page. --- .../datasource-zabbix/partials/config.html | 23 ++++++++++--------- 1 file changed, 12 insertions(+), 11 deletions(-) diff --git a/plugins/datasource-zabbix/partials/config.html b/plugins/datasource-zabbix/partials/config.html index 0b42568d6..e57efb04d 100644 --- a/plugins/datasource-zabbix/partials/config.html +++ b/plugins/datasource-zabbix/partials/config.html @@ -1,4 +1,5 @@ - + +
    Zabbix API details
    @@ -9,7 +10,7 @@
    Zabbix API details
  • @@ -17,7 +18,7 @@
    Zabbix API details
  • @@ -30,24 +31,24 @@
    Zabbix API details
  • Trends   - - + +
  • -
  • Use trends from
  • -
  • +
  • From f6979a919f2e2d7566cf143db730815c52d3e02a Mon Sep 17 00:00:00 2001 From: Alexander Zobnin Date: Thu, 11 Feb 2016 21:03:22 +0300 Subject: [PATCH 138/205] New plugin API - fixed query options. --- .../partials/query.options.html | 20 +++++++++---------- 1 file changed, 10 insertions(+), 10 deletions(-) diff --git a/plugins/datasource-zabbix/partials/query.options.html b/plugins/datasource-zabbix/partials/query.options.html index a131c83b9..fa58b313d 100644 --- a/plugins/datasource-zabbix/partials/query.options.html +++ b/plugins/datasource-zabbix/partials/query.options.html @@ -11,10 +11,10 @@
  • @@ -27,22 +27,22 @@
  • - + Max data points
  • - + IT services
  • - + IT service property
  • - + Text filter
  • @@ -54,7 +54,7 @@
    -
    +
    Max data points
    • Grafana-Zabbix plugin uses maxDataPoints parameter to consolidate the real number of values down to this @@ -67,14 +67,14 @@
      Max data points
    -
    +
    IT services
    • Select "IT services" in targets menu to activate IT services mode.
    -
    +
    IT service property
    • Zabbix returns the following availability information about IT service
    • @@ -86,7 +86,7 @@
      IT service property
    -
    +
    Text filter
    • Use regex to extract a part of the returned value.
    • From ee02c78cf88bedc9aaf065038151d511261cbbce Mon Sep 17 00:00:00 2001 From: Alexander Zobnin Date: Thu, 11 Feb 2016 21:21:05 +0300 Subject: [PATCH 139/205] Fixed stacking bar chart with groupBy() function. --- plugins/datasource-zabbix/dataProcessingService.js | 10 ++-------- 1 file changed, 2 insertions(+), 8 deletions(-) diff --git a/plugins/datasource-zabbix/dataProcessingService.js b/plugins/datasource-zabbix/dataProcessingService.js index b976b4a25..5bf73a320 100644 --- a/plugins/datasource-zabbix/dataProcessingService.js +++ b/plugins/datasource-zabbix/dataProcessingService.js @@ -71,15 +71,9 @@ function (angular, _, moment, utils) { var ms_interval = utils.parseInterval(interval); // Calculate frame timestamps - var min_timestamp = datapoints[0][1]; var frames = _.groupBy(datapoints, function(point) { - var group_time = Math.floor(point[1] / ms_interval) * ms_interval; - - // Prevent points outside of time range - if (group_time < min_timestamp) { - group_time = min_timestamp; - } - return group_time; + // Calculate time for group of points + return Math.floor(point[1] / ms_interval) * ms_interval; }); // frame: { '': [[, ], ...] } From f2201f946193f9f753b084c87f2a8bfd13ffa78c Mon Sep 17 00:00:00 2001 From: Alexander Zobnin Date: Sat, 13 Feb 2016 00:34:17 +0300 Subject: [PATCH 140/205] Iss #148 - fixed applications handling for Zabbix 2.2. --- plugins/datasource-zabbix/zabbixCache.js | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/plugins/datasource-zabbix/zabbixCache.js b/plugins/datasource-zabbix/zabbixCache.js index abe4fe397..8c8aafe66 100644 --- a/plugins/datasource-zabbix/zabbixCache.js +++ b/plugins/datasource-zabbix/zabbixCache.js @@ -189,7 +189,11 @@ function (angular, _, utils) { return _.map(_.groupBy(applications, 'name'), function(value, key) { // Hack for supporting different apis (2.2 vs 2.4 vs 3.0) - var hostField = value['hosts'] ? 'hosts' : 'host'; + var hostField = 'host'; + if (value[0] && value[0]['hosts']) { + // For Zabbix 2.2 + hostField = 'hosts'; + } return { name: key, From 637cf4c63d30b1bd5228300f907755c640b57f87 Mon Sep 17 00:00:00 2001 From: Alexander Zobnin Date: Sun, 14 Feb 2016 18:04:30 +0300 Subject: [PATCH 141/205] Iss #152 - fixed trigger panel (used new plugin API). --- plugins/panel-triggers/editor.html | 162 +++++++------- plugins/panel-triggers/editor.js | 205 ++++++++++++++++++ plugins/panel-triggers/module.html | 264 +++++++++++------------ plugins/panel-triggers/module.js | 333 +++++++---------------------- 4 files changed, 495 insertions(+), 469 deletions(-) create mode 100644 plugins/panel-triggers/editor.js diff --git a/plugins/panel-triggers/editor.html b/plugins/panel-triggers/editor.html index 1bb0e3b58..9a0c00c28 100644 --- a/plugins/panel-triggers/editor.html +++ b/plugins/panel-triggers/editor.html @@ -8,28 +8,28 @@
      Select triggers
    • + ng-style="editor.panel.triggers.group.style">
    • Host
    • + ng-style="editor.panel.triggers.host.style">
    @@ -41,26 +41,26 @@
    Select triggers
  • + ng-style="editor.panel.triggers.application.style">
  • Trigger
  • @@ -74,9 +74,9 @@
    Data source
    @@ -96,9 +96,9 @@
    Options
  • @@ -107,9 +107,9 @@
    Options
  • + ng-change="editor.panelCtrl.refreshData()">
  • @@ -121,9 +121,9 @@
    Options
  • @@ -131,9 +131,9 @@
    Options
  • @@ -145,58 +145,58 @@
    Options
    Show fields
  • - + - + ng-model="editor.panel.hostField" + ng-checked="editor.panel.hostField"> +
  • - + - + ng-model="editor.panel.statusField" + ng-checked="editor.panel.statusField"> +
  • - + - + ng-model="editor.panel.severityField" + ng-checked="editor.panel.severityField"> +
  • - + - + ng-model="editor.panel.lastChangeField" + ng-checked="editor.panel.lastChangeField"> +
  • - + - + ng-model="editor.panel.ageField" + ng-checked="editor.panel.ageField"> +
  • - + - + ng-model="editor.panel.infoField" + ng-checked="editor.panel.infoField"> +
  • @@ -205,25 +205,25 @@
    Options
    • Custom Last change format - + - + ng-change="editor.panelCtrl.refreshData()" + ng-model="editor.panel.customLastChangeFormat" + ng-checked="editor.panel.customLastChangeFormat"> +
    • -
    • +
    • -
    • +
    • @@ -237,8 +237,7 @@
      Options
    Customize triggers severity and colors
    -
    +
    • {{ trigger.priority }} @@ -251,10 +250,10 @@
      Customize triggers severity and colors
      style="color: white" ng-style="{background: trigger.color}" ng-model-onblur - ng-change="get_data()"> + ng-change="editor.panelCtrl.refreshData()">
    • - +
    • @@ -263,7 +262,7 @@
      Customize triggers severity and colors
      type="checkbox" ng-model="trigger.show" ng-checked="trigger.show" - ng-change="get_data()"> + ng-change="editor.panelCtrl.refreshData()">
    @@ -272,12 +271,15 @@
    Customize triggers severity and colors
    • OK event color
    • - + +
    diff --git a/plugins/panel-triggers/editor.js b/plugins/panel-triggers/editor.js new file mode 100644 index 000000000..55c77a7ed --- /dev/null +++ b/plugins/panel-triggers/editor.js @@ -0,0 +1,205 @@ +/** + * Grafana-Zabbix + * Zabbix plugin for Grafana. + * http://github.com/alexanderzobnin/grafana-zabbix + * + * Trigger panel. + * This feature sponsored by CORE IT + * http://www.coreit.fr + * + * Copyright 2015 Alexander Zobnin alexanderzobnin@gmail.com + * Licensed under the Apache License, Version 2.0 + */ + +define([ + 'angular', + 'lodash', + 'jquery' +], +function (angular, _, $) { + 'use strict'; + + function TriggerPanelEditorCtrl($scope, $q, uiSegmentSrv, datasourceSrv, templateSrv, popoverSrv) { + $scope.editor = this; + this.panelCtrl = $scope.ctrl; + this.panel = this.panelCtrl.panel; + + this.datasourceSrv = datasourceSrv; + this.templateSrv = templateSrv; + this.popoverSrv = popoverSrv; + + // Map functions for bs-typeahead + this.getGroupNames = _.partial(getMetricNames, this, 'groupList'); + this.getHostNames = _.partial(getMetricNames, this, 'filteredHosts'); + this.getApplicationNames = _.partial(getMetricNames, this, 'filteredApplications'); + this.getItemNames = _.partial(getMetricNames, this, 'filteredItems'); + + this.ackFilters = [ + 'all triggers', + 'unacknowledged', + 'acknowledged' + ]; + + this.sortByFields = [ + { text: 'last change', value: 'lastchange' }, + { text: 'severity', value: 'priority' } + ]; + + this.showEventsFields = [ + { text: 'all events', value: [0,1] }, + { text: 'Ok events', value: 0 }, + { text: 'Problem events', value: 1 } + ]; + + // Load scope defaults + var scopeDefaults = { + metric: {}, + inputStyles: {}, + oldTarget: _.cloneDeep(this.panel.triggers), + defaultTimeFormat: "DD MMM YYYY HH:mm:ss" + }; + _.defaults(this, scopeDefaults); + + var self = this; + + // Get zabbix data sources + var datasources = _.filter(this.datasourceSrv.getMetricSources(), function(datasource) { + return datasource.meta.id === 'zabbix'; + }); + this.datasources = _.map(datasources, 'name'); + + // Set default datasource + if (!this.panel.datasource) { + this.panel.datasource = this.datasources[0]; + } + // Load datasource + this.datasourceSrv.get(this.panel.datasource).then(function (datasource) { + self.datasource = datasource; + self.initFilters(); + self.panelCtrl.refreshData(); + }); + } + + var p = TriggerPanelEditorCtrl.prototype; + + // Get list of metric names for bs-typeahead directive + function getMetricNames(scope, metricList) { + return _.uniq(_.map(scope.metric[metricList], 'name')); + } + + p.initFilters = function () { + this.filterGroups(); + this.filterHosts(); + this.filterApplications(); + }; + + p.filterGroups = function() { + var self = this; + this.datasource.queryProcessor.filterGroups().then(function(groups) { + self.metric.groupList = groups; + }); + }; + + p.filterHosts = function() { + var self = this; + var groupFilter = this.templateSrv.replace(this.panel.triggers.group.filter); + this.datasource.queryProcessor.filterHosts(groupFilter).then(function(hosts) { + self.metric.filteredHosts = hosts; + }); + }; + + p.filterApplications = function() { + var self = this; + var groupFilter = this.templateSrv.replace(this.panel.triggers.group.filter); + var hostFilter = this.templateSrv.replace(this.panel.triggers.host.filter); + this.datasource.queryProcessor.filterApplications(groupFilter, hostFilter) + .then(function(apps) { + self.metric.filteredApplications = apps; + }); + }; + + p.onTargetPartChange = function(targetPart) { + var regexStyle = {'color': '#CCA300'}; + targetPart.isRegex = isRegex(targetPart.filter); + targetPart.style = targetPart.isRegex ? regexStyle : {}; + }; + + function isRegex(str) { + // Pattern for testing regex + var regexPattern = /^\/(.*)\/([gmi]*)$/m; + return regexPattern.test(str); + } + + p.parseTarget = function() { + this.initFilters(); + var newTarget = _.cloneDeep(this.panel.triggers); + if (!_.isEqual(this.oldTarget, this.panel.triggers)) { + this.oldTarget = newTarget; + this.panelCtrl.refreshData(); + } + }; + + p.refreshTriggerSeverity = function() { + _.each(this.triggerList, function(trigger) { + trigger.color = this.panel.triggerSeverity[trigger.priority].color; + trigger.severity = this.panel.triggerSeverity[trigger.priority].severity; + }); + this.panelCtrl.refreshData(); + }; + + p.datasourceChanged = function() { + this.panelCtrl.refreshData(); + }; + + p.changeTriggerSeverityColor = function(trigger, color) { + this.panel.triggerSeverity[trigger.priority].color = color; + this.refreshTriggerSeverity(); + }; + + function getTriggerIndexForElement(el) { + return el.parents('[data-trigger-index]').data('trigger-index'); + } + + p.openTriggerColorSelector = function(event) { + var el = $(event.currentTarget); + var index = getTriggerIndexForElement(el); + var popoverScope = this.$new(); + popoverScope.trigger = this.panel.triggerSeverity[index]; + popoverScope.changeTriggerSeverityColor = this.changeTriggerSeverityColor; + + this.popoverSrv.show({ + element: el, + placement: 'top', + templateUrl: 'public/plugins/triggers/trigger.colorpicker.html', + scope: popoverScope + }); + }; + + p.openOkEventColorSelector = function(event) { + var el = $(event.currentTarget); + var popoverScope = this.$new(); + popoverScope.trigger = {color: this.panel.okEventColor}; + popoverScope.changeTriggerSeverityColor = function(trigger, color) { + this.panel.okEventColor = color; + this.refreshTriggerSeverity(); + }; + + this.popoverSrv.show({ + element: el, + placement: 'top', + templateUrl: 'public/plugins/triggers/trigger.colorpicker.html', + scope: popoverScope + }); + }; + + var triggerPanelEditor = function() { + return { + restrict: 'E', + scope: true, + templateUrl: 'public/plugins/triggers/editor.html', + controller: TriggerPanelEditorCtrl, + }; + }; + + return triggerPanelEditor; +}); \ No newline at end of file diff --git a/plugins/panel-triggers/module.html b/plugins/panel-triggers/module.html index 8c194e592..b8d2eb3a0 100644 --- a/plugins/panel-triggers/module.html +++ b/plugins/panel-triggers/module.html @@ -1,140 +1,136 @@ -
    - -
    -
    -
    - - - - - - - - - - - - - - - - - - + + +
    -
    - Host -
    -
    -
    Status
    -
    -
    Severity
    -
    -
    Issue
    -
    -
    Last change
    -
    -
    Age
    -
    -
    Info
    -
    -
    - {{trigger.host}} -
    -
    -
    - {{triggerStatusMap[trigger.value]}} -
    -
    -
    - {{trigger.severity}} -
    -
    -
    - {{trigger.description}} - - - -
    +
    +
    +
    + + + + + + + + + + + + + + + + + + - - - + + + - - -
    +
    + Host +
    +
    +
    Status
    +
    +
    Severity
    +
    +
    Issue
    +
    +
    Last change
    +
    +
    Age
    +
    +
    Info
    +
    +
    + {{trigger.host}} +
    +
    +
    + {{ctrl.triggerStatusMap[trigger.value]}} +
    +
    +
    + {{trigger.severity}} +
    +
    +
    + {{trigger.description}} + + + +
    - -
    -
    - {{trigger.comments}} -
    -
    + +
    +
    + {{trigger.comments}} +
    +
    - -
    -
    - - - - - - - - - - - - - - - -
    TimeUserComments
    - {{ack.time}} - - {{ack.user}} - - {{ack.message}} -
    -
    -
    -
    - {{trigger.lastchange}} - - {{trigger.age}} - + +
    +
    + + + + + + + + + + + + + + + +
    TimeUserComments
    + {{ack.time}} + + {{ack.user}} + + {{ack.message}} +
    +
    +
    +
    + {{trigger.lastchange}} + + {{trigger.age}} + - - - - + + + + - - - - + + + + - - - - -
    -
    -
    - - + + + + +
    +
    + diff --git a/plugins/panel-triggers/module.js b/plugins/panel-triggers/module.js index 197e86582..47d1b045b 100644 --- a/plugins/panel-triggers/module.js +++ b/plugins/panel-triggers/module.js @@ -12,127 +12,88 @@ */ define([ + 'app/plugins/sdk', 'angular', - 'app/app', 'lodash', 'jquery', 'moment', - 'app/core/config', - 'app/features/panel/panel_meta' + './editor' ], -function (angular, app, _, $, moment, config, PanelMeta) { +function (sdk, angular, _, $, moment, triggerPanelEditor) { 'use strict'; - var module = angular.module('grafana.panels.triggers', []); - app.useModule(module); - - /** @ngInject */ - function TriggerPanelCtrl($q, $scope, $element, datasourceSrv, panelSrv, templateSrv, popoverSrv) { - - $scope.panelMeta = new PanelMeta({ - panelName: 'Zabbix triggers', - editIcon: "fa fa-lightbulb-o", - fullscreen: true, - }); - - $scope.panelMeta.addEditorTab('Options', 'public/plugins/triggers/editor.html'); - - $scope.ackFilters = [ - 'all triggers', - 'unacknowledged', - 'acknowledged' - ]; + var defaultSeverity = [ + { priority: 0, severity: 'Not classified', color: '#B7DBAB', show: true }, + { priority: 1, severity: 'Information', color: '#82B5D8', show: true }, + { priority: 2, severity: 'Warning', color: '#E5AC0E', show: true }, + { priority: 3, severity: 'Average', color: '#C15C17', show: true }, + { priority: 4, severity: 'High', color: '#BF1B00', show: true }, + { priority: 5, severity: 'Disaster', color: '#890F02', show: true } + ]; + + var panelDefaults = { + datasource: null, + triggers: { + group: {filter: ""}, + host: {filter: ""}, + application: {filter: ""}, + trigger: {filter: ""} + }, + hostField: true, + statusField: false, + severityField: false, + lastChangeField: true, + ageField: true, + infoField: true, + limit: 10, + showTriggers: 'all triggers', + sortTriggersBy: { text: 'last change', value: 'lastchange' }, + showEvents: { text: 'Problem events', value: '1' }, + triggerSeverity: defaultSeverity, + okEventColor: '#890F02', + }; - $scope.sortByFields = [ - { text: 'last change', value: 'lastchange' }, - { text: 'severity', value: 'priority' } - ]; + var triggerStatusMap = { + '0': 'OK', + '1': 'Problem' + }; - $scope.showEventsFields = [ - { text: 'all events', value: [0,1] }, - { text: 'Ok events', value: 0 }, - { text: 'Problem events', value: 1 } - ]; + var TriggerPanelCtrl = (function(_super) { - $scope.triggerStatusMap = { - '0': 'OK', - '1': 'Problem' - }; + /** @ngInject */ + function TriggerPanelCtrl($scope, $injector, $q, $element, datasourceSrv) { + _super.call(this, $scope, $injector); + this.datasourceSrv = datasourceSrv; + this.triggerStatusMap = triggerStatusMap; - var grafanaDefaultSeverity = [ - { priority: 0, severity: 'Not classified', color: '#B7DBAB', show: true }, - { priority: 1, severity: 'Information', color: '#82B5D8', show: true }, - { priority: 2, severity: 'Warning', color: '#E5AC0E', show: true }, - { priority: 3, severity: 'Average', color: '#C15C17', show: true }, - { priority: 4, severity: 'High', color: '#BF1B00', show: true }, - { priority: 5, severity: 'Disaster', color: '#890F02', show: true } - ]; - - var panelDefaults = { - datasource: null, - triggers: { - group: {filter: ""}, - host: {filter: ""}, - application: {filter: ""}, - trigger: {filter: ""} - }, - hostField: true, - statusField: false, - severityField: false, - lastChangeField: true, - ageField: true, - infoField: true, - limit: 10, - showTriggers: 'all triggers', - sortTriggersBy: { text: 'last change', value: 'lastchange' }, - showEvents: { text: 'Problem events', value: '1' }, - triggerSeverity: grafanaDefaultSeverity, - okEventColor: '#890F02', - }; + // Load panel defaults + _.defaults(this.panel, panelDefaults); - _.defaults($scope.panel, panelDefaults); - $scope.triggerList = []; + this.triggerList = []; + this.refreshData(); + } - $scope.init = function() { - panelSrv.init($scope); - if ($scope.isNewPanel()) { - $scope.panel.title = "Zabbix Triggers"; - } + TriggerPanelCtrl.templateUrl = 'module.html'; - // Load scope defaults - var scopeDefaults = { - metric: {}, - inputStyles: {}, - oldTarget: _.cloneDeep($scope.panel.triggers), - defaultTimeFormat: "DD MMM YYYY HH:mm:ss" - }; - _.defaults($scope, scopeDefaults); - - // Get zabbix data sources - var datasources = _.filter(datasourceSrv.getMetricSources(), function(datasource) { - return datasource.meta.id === 'zabbix'; - }); - $scope.datasources = _.map(datasources, 'name'); + TriggerPanelCtrl.prototype = Object.create(_super.prototype); + TriggerPanelCtrl.prototype.constructor = TriggerPanelCtrl; - // Set default datasource - if (!$scope.panel.datasource) { - $scope.panel.datasource = $scope.datasources[0]; - } - // Load datasource - datasourceSrv.get($scope.panel.datasource).then(function (datasource) { - $scope.datasource = datasource; - $scope.initFilters(); - }); + // Add panel editor + TriggerPanelCtrl.prototype.initEditMode = function() { + _super.prototype.initEditMode(); + this.icon = "fa fa-lightbulb-o"; + this.addEditorTab('Options', triggerPanelEditor, 2); }; - $scope.refreshData = function() { + TriggerPanelCtrl.prototype.refreshData = function() { + var self = this; // Load datasource - return datasourceSrv.get($scope.panel.datasource).then(function (datasource) { + return this.datasourceSrv.get(this.panel.datasource).then(function (datasource) { var zabbix = datasource.zabbixAPI; var queryProcessor = datasource.queryProcessor; - var triggerFilter = $scope.panel.triggers; - var showEvents = $scope.panel.showEvents.value; + var triggerFilter = self.panel.triggers; + var showEvents = self.panel.showEvents.value; var buildQuery = queryProcessor.buildTriggerQuery(triggerFilter.group.filter, triggerFilter.host.filter, triggerFilter.application.filter); @@ -148,22 +109,22 @@ function (angular, app, _, $, moment, config, PanelMeta) { // Format last change and age trigger.lastchangeUnix = Number(trigger.lastchange); var timestamp = moment.unix(trigger.lastchangeUnix); - if ($scope.panel.customLastChangeFormat) { + if (self.panel.customLastChangeFormat) { // User defined format - triggerObj.lastchange = timestamp.format($scope.panel.lastChangeFormat); + triggerObj.lastchange = timestamp.format(self.panel.lastChangeFormat); } else { - triggerObj.lastchange = timestamp.format($scope.defaultTimeFormat); + triggerObj.lastchange = timestamp.format(self.defaultTimeFormat); } triggerObj.age = timestamp.fromNow(true); // Set color if (trigger.value === '1') { - triggerObj.color = $scope.panel.triggerSeverity[trigger.priority].color; + triggerObj.color = self.panel.triggerSeverity[trigger.priority].color; } else { - triggerObj.color = $scope.panel.okEventColor; + triggerObj.color = self.panel.okEventColor; } - triggerObj.severity = $scope.panel.triggerSeverity[trigger.priority].severity; + triggerObj.severity = self.panel.triggerSeverity[trigger.priority].severity; return triggerObj; }); }) @@ -173,6 +134,7 @@ function (angular, app, _, $, moment, config, PanelMeta) { var eventids = _.map(triggerList, function(trigger) { return trigger.lastEvent.eventid; }); + return zabbix.getAcknowledges(eventids) .then(function (events) { @@ -193,17 +155,17 @@ function (angular, app, _, $, moment, config, PanelMeta) { }); // Filter triggers by description - var triggerFilter = $scope.panel.triggers.trigger.filter; + var triggerFilter = self.panel.triggers.trigger.filter; if (triggerFilter) { triggerList = filterTriggers(triggerList, triggerFilter); } // Filter acknowledged triggers - if ($scope.panel.showTriggers === 'unacknowledged') { + if (self.panel.showTriggers === 'unacknowledged') { triggerList = _.filter(triggerList, function (trigger) { return !trigger.acknowledges; }); - } else if ($scope.panel.showTriggers === 'acknowledged') { + } else if (self.panel.showTriggers === 'acknowledged') { triggerList = _.filter(triggerList, 'acknowledges'); } else { triggerList = triggerList; @@ -211,65 +173,26 @@ function (angular, app, _, $, moment, config, PanelMeta) { // Filter triggers by severity triggerList = _.filter(triggerList, function (trigger) { - return $scope.panel.triggerSeverity[trigger.priority].show; + return self.panel.triggerSeverity[trigger.priority].show; }); // Sort triggers - if ($scope.panel.sortTriggersBy.value === 'priority') { + if (self.panel.sortTriggersBy.value === 'priority') { triggerList = _.sortBy(triggerList, 'priority').reverse(); } else { triggerList = _.sortBy(triggerList, 'lastchangeUnix').reverse(); } // Limit triggers number - $scope.triggerList = _.first(triggerList, $scope.panel.limit); + self.triggerList = _.first(triggerList, self.panel.limit); - $scope.panelRenderingComplete(); + self.renderingCompleted(); }); }); }); }); }; - $scope.initFilters = function () { - $scope.filterGroups(); - $scope.filterHosts(); - $scope.filterApplications(); - }; - - // Get list of metric names for bs-typeahead directive - function getMetricNames(scope, metricList) { - return _.uniq(_.map(scope.metric[metricList], 'name')); - } - - // Map functions for bs-typeahead - $scope.getGroupNames = _.partial(getMetricNames, $scope, 'groupList'); - $scope.getHostNames = _.partial(getMetricNames, $scope, 'filteredHosts'); - $scope.getApplicationNames = _.partial(getMetricNames, $scope, 'filteredApplications'); - $scope.getItemNames = _.partial(getMetricNames, $scope, 'filteredItems'); - - $scope.filterGroups = function() { - $scope.datasource.queryProcessor.filterGroups().then(function(groups) { - $scope.metric.groupList = groups; - }); - }; - - $scope.filterHosts = function() { - var groupFilter = templateSrv.replace($scope.panel.triggers.group.filter); - $scope.datasource.queryProcessor.filterHosts(groupFilter).then(function(hosts) { - $scope.metric.filteredHosts = hosts; - }); - }; - - $scope.filterApplications = function() { - var groupFilter = templateSrv.replace($scope.panel.triggers.group.filter); - var hostFilter = templateSrv.replace($scope.panel.triggers.host.filter); - $scope.datasource.queryProcessor.filterApplications(groupFilter, hostFilter) - .then(function(apps) { - $scope.metric.filteredApplications = apps; - }); - }; - function filterTriggers(triggers, triggerFilter) { if (isRegex(triggerFilter)) { return _.filter(triggers, function(trigger) { @@ -282,12 +205,6 @@ function (angular, app, _, $, moment, config, PanelMeta) { } } - $scope.onTargetPartChange = function(targetPart) { - var regexStyle = {'color': '#CCA300'}; - targetPart.isRegex = isRegex(targetPart.filter); - targetPart.style = targetPart.isRegex ? regexStyle : {}; - }; - function isRegex(str) { // Pattern for testing regex var regexPattern = /^\/(.*)\/([gmi]*)$/m; @@ -302,106 +219,12 @@ function (angular, app, _, $, moment, config, PanelMeta) { return new RegExp(pattern, flags); } - $scope.parseTarget = function() { - $scope.initFilters(); - var newTarget = _.cloneDeep($scope.panel.triggers); - if (!_.isEqual($scope.oldTarget, $scope.panel.triggers)) { - $scope.oldTarget = newTarget; - $scope.get_data(); - } - }; - - $scope.refreshTriggerSeverity = function() { - _.each($scope.triggerList, function(trigger) { - trigger.color = $scope.panel.triggerSeverity[trigger.priority].color; - trigger.severity = $scope.panel.triggerSeverity[trigger.priority].severity; - }); - }; - - $scope.datasourceChanged = function() { - $scope.refreshData(); - }; - - $scope.changeTriggerSeverityColor = function(trigger, color) { - $scope.panel.triggerSeverity[trigger.priority].color = color; - $scope.refreshTriggerSeverity(); - }; - - function getTriggerIndexForElement(el) { - return el.parents('[data-trigger-index]').data('trigger-index'); - } - - $scope.openTriggerColorSelector = function(event) { - var el = $(event.currentTarget); - var index = getTriggerIndexForElement(el); - var popoverScope = $scope.$new(); - popoverScope.trigger = $scope.panel.triggerSeverity[index]; - popoverScope.changeTriggerSeverityColor = $scope.changeTriggerSeverityColor; - - popoverSrv.show({ - element: el, - placement: 'top', - templateUrl: 'public/plugins/triggers/trigger.colorpicker.html', - scope: popoverScope - }); - }; - - $scope.openOkEventColorSelector = function(event) { - var el = $(event.currentTarget); - var popoverScope = $scope.$new(); - popoverScope.trigger = {color: $scope.panel.okEventColor}; - popoverScope.changeTriggerSeverityColor = function(trigger, color) { - $scope.panel.okEventColor = color; - $scope.refreshTriggerSeverity(); - }; - - popoverSrv.show({ - element: el, - placement: 'top', - templateUrl: 'public/plugins/triggers/trigger.colorpicker.html', - scope: popoverScope - }); - }; - - /** - * Convert event age from Unix format (milliseconds sins 1970) - * to Zabbix format (like at Last 20 issues panel). - * @param {Date} AgeUnix time in Unix format - * @return {string} Formatted time - */ - function toZabbixAgeFormat(ageUnix) { - var age = new Date(+ageUnix); - var ageZabbix = age.getSeconds() + 's'; - if (age.getMinutes()) { - ageZabbix = age.getMinutes() + 'm ' + ageZabbix; - } - if (age.getHours()) { - ageZabbix = age.getHours() + 'h ' + ageZabbix; - } - if (age.getDate() - 1) { - ageZabbix = age.getDate() - 1 + 'd ' + ageZabbix; - } - if (age.getMonth()) { - ageZabbix = age.getMonth() + 'M ' + ageZabbix; - } - if (age.getYear() - 70) { - ageZabbix = age.getYear() -70 + 'y ' + ageZabbix; - } - return ageZabbix; - } - - $scope.init(); - } + return TriggerPanelCtrl; - function triggerPanelDirective() { - return { - controller: TriggerPanelCtrl, - templateUrl: 'public/plugins/triggers/module.html', - }; - } + })(sdk.PanelCtrl); return { - panel: triggerPanelDirective + PanelCtrl: TriggerPanelCtrl }; }); From 50c0764d0154d2bff9ff1688fd4dcb4d4d9e407c Mon Sep 17 00:00:00 2001 From: Alexander Zobnin Date: Thu, 18 Feb 2016 23:47:31 +0300 Subject: [PATCH 142/205] Cache and queryProcessor refactor. --- plugins/datasource-zabbix/queryProcessor.js | 28 ++++++---------- plugins/datasource-zabbix/zabbixAPI.js | 24 ++++++++++++- plugins/datasource-zabbix/zabbixCache.js | 37 +++++++++++++++++++-- 3 files changed, 69 insertions(+), 20 deletions(-) diff --git a/plugins/datasource-zabbix/queryProcessor.js b/plugins/datasource-zabbix/queryProcessor.js index 97f128a2e..c5a78ecad 100644 --- a/plugins/datasource-zabbix/queryProcessor.js +++ b/plugins/datasource-zabbix/queryProcessor.js @@ -49,7 +49,6 @@ function (angular, _, utils) { this.filterHosts = function(groupFilter) { var groups = []; - var hosts = []; return self.cache.getGroups().then(function(groupList) { // Filter groups by regex @@ -69,15 +68,15 @@ function (angular, _, utils) { } } - if (groups) { - var groupids = _.map(groups, 'groupid'); - return self.cache.getHosts().then(function(hosts) { - return _.filter(hosts, function (hostObj) { - return _.intersection(groupids, hostObj.groups).length; + var hostids = _.flatten(_.map(groups, 'hosts')); + if (hostids.length) { + return self.cache.getHostsExtend().then(function(hosts) { + return _.map(hostids, function(hostid) { + return hosts[hostid]; }); }); } else { - return hosts; + return []; } }); }; @@ -129,14 +128,12 @@ function (angular, _, utils) { var promises = [ this.filterHosts(groupFilter), - this.filterApplications(groupFilter, hostFilter), - this.cache.getItems(itemType), + this.filterApplications(groupFilter, hostFilter) ]; return $q.all(promises).then(function(results) { var hostList = results[0]; var applicationList = results[1]; - var cachedItems = results[2]; // Filter hosts by regex if (utils.isRegex(hostFilter)) { @@ -144,8 +141,7 @@ function (angular, _, utils) { hosts = _.filter(hostList, function (hostObj) { return hostFilterPattern.test(hostObj.name); }); - } - else { + } else { var findedHosts = _.find(hostList, {'name': hostFilter}); if (findedHosts) { hosts.push(findedHosts); @@ -172,20 +168,18 @@ function (angular, _, utils) { } else { apps = undefined; if (hosts) { - items = _.filter(cachedItems, function (itemObj) { - return _.find(hosts, {'hostid': itemObj.hostid }); - }); + items = _.flatten(_.map(hosts, 'items'), true); } } if (apps) { - var appids = _.flatten(_.map(apps, 'applicationids')); + /*var appids = _.flatten(_.map(apps, 'applicationids')); items = _.filter(cachedItems, function (itemObj) { return _.intersection(appids, itemObj.applications).length; }); items = _.filter(items, function (itemObj) { return _.find(hosts, {'hostid': itemObj.hostid }); - }); + });*/ } if (!showDisabledItems) { diff --git a/plugins/datasource-zabbix/zabbixAPI.js b/plugins/datasource-zabbix/zabbixAPI.js index 5c1b61dbe..eac75890e 100644 --- a/plugins/datasource-zabbix/zabbixAPI.js +++ b/plugins/datasource-zabbix/zabbixAPI.js @@ -123,7 +123,8 @@ function (angular, _) { p.getGroups = function() { var params = { output: ['name'], - sortfield: 'name' + sortfield: 'name', + selectHosts: [] }; return this.request('hostgroup.get', params); @@ -168,6 +169,27 @@ function (angular, _) { return this.request('item.get', params); }; + /** + * Get Hosts list with host's items. + * @return {[type]} [description] + */ + p.getHostsExtend = function() { + var params = { + output: ['name', 'host'], + sortfield: 'name', + selectGroups: [], + selectItems: [ + 'name', 'key_', + 'value_type', + 'hostid', + 'status', + 'state' + ] + }; + + return this.request('host.get', params); + }; + p.getLastValue = function(itemid) { var params = { output: ['lastvalue'], diff --git a/plugins/datasource-zabbix/zabbixCache.js b/plugins/datasource-zabbix/zabbixCache.js index 8c8aafe66..6427d07b8 100644 --- a/plugins/datasource-zabbix/zabbixCache.js +++ b/plugins/datasource-zabbix/zabbixCache.js @@ -21,6 +21,7 @@ function (angular, _, utils) { this._hosts = undefined; this._applications = undefined; this._items = undefined; + this._hostsExtend = undefined; this.storage = { history: {}, trends: {} @@ -51,15 +52,17 @@ function (angular, _, utils) { this.zabbixAPI.getGroups(), this.zabbixAPI.getHosts(), this.zabbixAPI.getApplications(), - this.zabbixAPI.getItems() + this.zabbixAPI.getItems(), + this.zabbixAPI.getHostsExtend() ]; return $q.all(promises).then(function(results) { if (results.length) { - self._groups = results[0]; + self._groups = convertGroups(results[0]); self._hosts = convertHosts(results[1]); self._applications = convertApplications(results[2]); self._items = convertItems(results[3]); + self._hostsExtend = convertHostsExtend(results[4]); } self._initialized = true; }); @@ -87,6 +90,17 @@ function (angular, _, utils) { } }; + p.getHostsExtend = function() { + var self = this; + if (this._hostsExtend) { + return $q.when(self._hostsExtend); + } else { + return this.refresh().then(function() { + return self._hostsExtend; + }); + } + }; + p.getApplications = function() { var self = this; if (this._applications) { @@ -181,6 +195,25 @@ function (angular, _, utils) { }); } + function convertGroups(groups) { + return _.forEach(groups, function(group) { + group.hosts = _.map(group.hosts, 'hostid'); + return group; + }); + } + + function convertHostsExtend(hosts) { + return _.indexBy(_.map(hosts, function(host) { + host.items = _.forEach(host.items, function(item) { + item.applications = _.map(item.applications, 'applicationid'); + item.item = item.name; + item.name = utils.expandItemName(item.item, item.key_); + return item; + }); + return host; + }), 'hostid'); + } + /** * Group Zabbix applications by name * host.hosts - array of host ids From 8b374781318052e8da39d59cf2f174d457f19549 Mon Sep 17 00:00:00 2001 From: Alexander Zobnin Date: Mon, 14 Mar 2016 23:42:24 +0300 Subject: [PATCH 143/205] Initial ES6 migration. module.js and datasource.js are migrated to ES6. --- .gitignore | 15 + .jshintrc | 6 +- Gruntfile.js | 62 +++ package.json | 37 ++ plugins/datasource-zabbix/datasource.js | 429 ----------------- plugins/datasource-zabbix/module.js | 24 - .../datasource-zabbix/addMetricFunction.js | 0 .../dataProcessingService.js | 0 src/datasource-zabbix/datasource.js | 430 ++++++++++++++++++ .../datasource-zabbix/directives.js | 0 .../datasource-zabbix/helperFunctions.js | 0 .../datasource-zabbix/metricFunctionEditor.js | 0 .../datasource-zabbix/metricFunctions.js | 0 src/datasource-zabbix/module.js | 19 + .../partials/annotations.editor.html | 0 .../datasource-zabbix/partials/config.html | 0 .../partials/query.editor.html | 0 .../partials/query.options.html | 0 .../datasource-zabbix/plugin.json | 0 .../datasource-zabbix/queryCtrl.js | 0 .../datasource-zabbix/queryProcessor.js | 0 {plugins => src}/datasource-zabbix/utils.js | 0 .../datasource-zabbix/zabbixAPI.js | 0 .../datasource-zabbix/zabbixAPIService.js | 0 .../datasource-zabbix/zabbixCache.js | 0 {plugins => src}/panel-triggers/editor.html | 0 {plugins => src}/panel-triggers/editor.js | 0 {plugins => src}/panel-triggers/module.html | 0 {plugins => src}/panel-triggers/module.js | 0 {plugins => src}/panel-triggers/plugin.json | 0 .../panel-triggers/trigger.colorpicker.html | 0 31 files changed, 567 insertions(+), 455 deletions(-) create mode 100644 Gruntfile.js create mode 100644 package.json delete mode 100644 plugins/datasource-zabbix/datasource.js delete mode 100644 plugins/datasource-zabbix/module.js rename {plugins => src}/datasource-zabbix/addMetricFunction.js (100%) rename {plugins => src}/datasource-zabbix/dataProcessingService.js (100%) create mode 100644 src/datasource-zabbix/datasource.js rename {plugins => src}/datasource-zabbix/directives.js (100%) rename {plugins => src}/datasource-zabbix/helperFunctions.js (100%) rename {plugins => src}/datasource-zabbix/metricFunctionEditor.js (100%) rename {plugins => src}/datasource-zabbix/metricFunctions.js (100%) create mode 100644 src/datasource-zabbix/module.js rename {plugins => src}/datasource-zabbix/partials/annotations.editor.html (100%) rename {plugins => src}/datasource-zabbix/partials/config.html (100%) rename {plugins => src}/datasource-zabbix/partials/query.editor.html (100%) rename {plugins => src}/datasource-zabbix/partials/query.options.html (100%) rename {plugins => src}/datasource-zabbix/plugin.json (100%) rename {plugins => src}/datasource-zabbix/queryCtrl.js (100%) rename {plugins => src}/datasource-zabbix/queryProcessor.js (100%) rename {plugins => src}/datasource-zabbix/utils.js (100%) rename {plugins => src}/datasource-zabbix/zabbixAPI.js (100%) rename {plugins => src}/datasource-zabbix/zabbixAPIService.js (100%) rename {plugins => src}/datasource-zabbix/zabbixCache.js (100%) rename {plugins => src}/panel-triggers/editor.html (100%) rename {plugins => src}/panel-triggers/editor.js (100%) rename {plugins => src}/panel-triggers/module.html (100%) rename {plugins => src}/panel-triggers/module.js (100%) rename {plugins => src}/panel-triggers/plugin.json (100%) rename {plugins => src}/panel-triggers/trigger.colorpicker.html (100%) diff --git a/.gitignore b/.gitignore index e368599a3..5593838a0 100644 --- a/.gitignore +++ b/.gitignore @@ -12,3 +12,18 @@ # Builded docs docs/site/ + +node_modules +npm-debug.log +coverage/ +.aws-config.json +awsconfig +/emails/dist +/public_gen +/tmp +vendor/phantomjs/phantomjs + +dist/ + +# locally required config files +public/css/*.min.css diff --git a/.jshintrc b/.jshintrc index 99247999e..36c9c9fd8 100644 --- a/.jshintrc +++ b/.jshintrc @@ -4,7 +4,7 @@ "bitwise":false, "curly": true, "eqnull": true, - "globalstrict": true, + "strict": true, "devel": true, "eqeqeq": true, "forin": false, @@ -12,7 +12,7 @@ "supernew": true, "expr": true, "indent": 2, - "latedef": true, + "latedef": false, "newcap": true, "noarg": true, "noempty": true, @@ -25,9 +25,11 @@ "unused": true, "maxdepth": 6, "maxlen": 140, + "esnext": true, "globals": { "System": true, + "Promise": true, "define": true, "require": true, "Chromath": false, diff --git a/Gruntfile.js b/Gruntfile.js new file mode 100644 index 000000000..2c8f35ae4 --- /dev/null +++ b/Gruntfile.js @@ -0,0 +1,62 @@ +module.exports = function(grunt) { + + require('load-grunt-tasks')(grunt); + + grunt.loadNpmTasks('grunt-execute'); + grunt.loadNpmTasks('grunt-contrib-clean'); + + grunt.initConfig({ + + clean: ["dist"], + + copy: { + src_to_dist: { + cwd: 'src', + expand: true, + src: [ + '**/*', + '!**/datasource.js', + '!**/module.js', + '!**/*.scss' + ], + dest: 'dist/src' + }, + pluginDef: { + expand: true, + src: ['plugin.json', 'README.md'], + dest: 'dist', + } + }, + + watch: { + rebuild_all: { + files: ['src/**/*', 'plugin.json'], + tasks: ['default'], + options: {spawn: false} + }, + }, + + babel: { + options: { + sourceMap: true, + presets: ["es2015"], + plugins: ['transform-es2015-modules-systemjs', "transform-es2015-for-of"], + }, + dist: { + files: [{ + cwd: 'src', + expand: true, + src: [ + '**/**/datasource.js', + '**/**/module.js', + ], + dest: 'dist/src', + ext:'.js' + }] + }, + }, + + }); + + grunt.registerTask('default', ['clean', 'copy:src_to_dist', 'copy:pluginDef', 'babel']); +}; diff --git a/package.json b/package.json new file mode 100644 index 000000000..64cde5b86 --- /dev/null +++ b/package.json @@ -0,0 +1,37 @@ +{ + "name": "grafana-zabbix", + "private": false, + "version": "3.0.0", + "description": "", + "scripts": { + "test": "echo \"Error: no test specified\" && exit 1" + }, + "repository": { + "type": "git", + "url": "git+https://github.com/alexanderzobnin/grafana-zabbix.git" + }, + "author": "Alexander Zobnin", + "license": "Apache-2.0", + "bugs": { + "url": "https://github.com/alexanderzobnin/grafana-zabbix/issues" + }, + "devDependencies": { + "grunt": "~0.4.5", + "babel": "~6.5.1", + "grunt-babel": "~6.0.0", + "grunt-contrib-copy": "~0.8.2", + "grunt-contrib-watch": "^0.6.1", + "grunt-contrib-uglify": "~0.11.0", + "grunt-systemjs-builder": "^0.2.5", + "load-grunt-tasks": "~3.2.0", + "grunt-execute": "~0.2.2", + "grunt-contrib-clean": "~0.6.0" + }, + "dependencies": { + "babel-plugin-transform-es2015-modules-systemjs": "^6.5.0", + "babel-plugin-transform-es2015-for-of": "^6.5.0", + "babel-preset-es2015": "^6.5.0", + "lodash": "~4.0.0" + }, + "homepage": "http://grafana-zabbix.org" +} diff --git a/plugins/datasource-zabbix/datasource.js b/plugins/datasource-zabbix/datasource.js deleted file mode 100644 index d08944471..000000000 --- a/plugins/datasource-zabbix/datasource.js +++ /dev/null @@ -1,429 +0,0 @@ -define([ - 'angular', - 'lodash', - 'app/core/utils/datemath', - './utils', - './metricFunctions', - './queryProcessor', - './directives', - './zabbixAPI', - './helperFunctions', - './dataProcessingService', - './zabbixCache', - './queryCtrl', - './addMetricFunction', - './metricFunctionEditor' -], -function (angular, _, dateMath, utils, metricFunctions) { - 'use strict'; - - /** @ngInject */ - function ZabbixAPIDatasource(instanceSettings, $q, templateSrv, alertSrv, zabbixHelperSrv, - ZabbixAPI, ZabbixCachingProxy, QueryProcessor, DataProcessingService) { - - // General data source settings - this.name = instanceSettings.name; - this.url = instanceSettings.url; - this.basicAuth = instanceSettings.basicAuth; - this.withCredentials = instanceSettings.withCredentials; - - // Zabbix API credentials - this.username = instanceSettings.jsonData.username; - this.password = instanceSettings.jsonData.password; - - // Use trends instead history since specified time - this.trends = instanceSettings.jsonData.trends; - this.trendsFrom = instanceSettings.jsonData.trendsFrom || '7d'; - - // Set cache update interval - var ttl = instanceSettings.jsonData.cacheTTL || '1h'; - this.cacheTTL = utils.parseInterval(ttl); - - // Initialize Zabbix API - this.zabbixAPI = new ZabbixAPI(this.url, this.username, this.password, this.basicAuth, this.withCredentials); - - // Initialize cache service - this.zabbixCache = new ZabbixCachingProxy(this.zabbixAPI, this.cacheTTL); - - // Initialize query builder - this.queryProcessor = new QueryProcessor(this.zabbixCache); - - console.log(this.zabbixCache); - - //////////////////////// - // Datasource methods // - //////////////////////// - - /** - * Test connection to Zabbix API - * @return {object} Connection status and Zabbix API version - */ - this.testDatasource = function() { - var self = this; - return this.zabbixAPI.getVersion().then(function (version) { - return self.zabbixAPI.login().then(function (auth) { - if (auth) { - return { - status: "success", - title: "Success", - message: "Zabbix API version: " + version - }; - } else { - return { - status: "error", - title: "Invalid user name or password", - message: "Zabbix API version: " + version - }; - } - }, function(error) { - console.log(error); - return { - status: "error", - title: "Connection failed", - message: error - }; - }); - }, - function(error) { - console.log(error); - return { - status: "error", - title: "Connection failed", - message: "Could not connect to given url" - }; - }); - }; - - /** - * Query panel data. Calls for each panel in dashboard. - * @param {Object} options Contains time range, targets and other info. - * @return {Object} Grafana metrics object with timeseries data for each target. - */ - this.query = function(options) { - var self = this; - - // get from & to in seconds - var from = Math.ceil(dateMath.parse(options.range.from) / 1000); - var to = Math.ceil(dateMath.parse(options.range.to) / 1000); - var useTrendsFrom = Math.ceil(dateMath.parse('now-' + this.trendsFrom) / 1000); - - // Create request for each target - var promises = _.map(options.targets, function(target) { - - if (target.mode !== 1) { - - // Don't request undefined and hidden targets - if (target.hide || !target.group || - !target.host || !target.item) { - return []; - } - - // Replace templated variables - var groupFilter = templateSrv.replace(target.group.filter, options.scopedVars); - var hostFilter = templateSrv.replace(target.host.filter, options.scopedVars); - var appFilter = templateSrv.replace(target.application.filter, options.scopedVars); - var itemFilter = templateSrv.replace(target.item.filter, options.scopedVars); - - // Query numeric data - if (!target.mode || target.mode === 0) { - - // Build query in asynchronous manner - return self.queryProcessor.build(groupFilter, hostFilter, appFilter, itemFilter) - .then(function(items) { - // Add hostname for items from multiple hosts - var addHostName = target.host.isRegex; - var getHistory; - - // Use trends - if ((from < useTrendsFrom) && self.trends) { - - // Find trendValue() function and get specified trend value - var trendFunctions = _.map(metricFunctions.getCategories()['Trends'], 'name'); - var trendValueFunc = _.find(target.functions, function(func) { - return _.contains(trendFunctions, func.def.name); - }); - var valueType = trendValueFunc ? trendValueFunc.params[0] : "avg"; - - getHistory = self.zabbixAPI.getTrend(items, from, to).then(function(history) { - return self.queryProcessor.handleTrends(history, addHostName, valueType); - }); - } else { - - // Use history - getHistory = self.zabbixCache.getHistory(items, from, to).then(function(history) { - return self.queryProcessor.handleHistory(history, addHostName); - }); - } - - return getHistory.then(function (timeseries_data) { - timeseries_data = _.map(timeseries_data, function (timeseries) { - - // Filter only transform functions - var transformFunctions = bindFunctionDefs(target.functions, 'Transform'); - - // Metric data processing - var dp = timeseries.datapoints; - for (var i = 0; i < transformFunctions.length; i++) { - dp = transformFunctions[i](dp); - } - timeseries.datapoints = dp; - - return timeseries; - }); - - // Aggregations - var aggregationFunctions = bindFunctionDefs(target.functions, 'Aggregate'); - var dp = _.map(timeseries_data, 'datapoints'); - if (aggregationFunctions.length) { - for (var i = 0; i < aggregationFunctions.length; i++) { - dp = aggregationFunctions[i](dp); - } - var lastAgg = _.findLast(target.functions, function(func) { - return _.contains( - _.map(metricFunctions.getCategories()['Aggregate'], 'name'), func.def.name); - }); - timeseries_data = [{ - target: lastAgg.text, - datapoints: dp - }]; - } - - // Apply alias functions - var aliasFunctions = bindFunctionDefs(target.functions, 'Alias'); - for (var j = 0; j < aliasFunctions.length; j++) { - _.each(timeseries_data, aliasFunctions[j]); - } - - return timeseries_data; - }); - }); - } - - // Query text data - else if (target.mode === 2) { - return self.queryProcessor.build(groupFilter, hostFilter, appFilter, itemFilter) - .then(function(items) { - var deferred = $q.defer(); - if (items.length) { - self.zabbixAPI.getLastValue(items[0].itemid).then(function(lastvalue) { - if (target.textFilter) { - var text_extract_pattern = new RegExp(templateSrv.replace(target.textFilter, options.scopedVars)); - var result = text_extract_pattern.exec(lastvalue); - if (result) { - if (target.useCaptureGroups) { - result = result[1]; - } else { - result = result[0]; - } - } - deferred.resolve(result); - } else { - deferred.resolve(lastvalue); - } - }); - } else { - deferred.resolve(null); - } - return deferred.promise.then(function(text) { - return { - target: target.item.name, - datapoints: [[text, to * 1000]] - }; - }); - }); - } - } - - // IT services mode - else if (target.mode === 1) { - // Don't show undefined and hidden targets - if (target.hide || !target.itservice || !target.slaProperty) { - return []; - } else { - return this.zabbixAPI.getSLA(target.itservice.serviceid, from, to) - .then(_.bind(zabbixHelperSrv.handleSLAResponse, zabbixHelperSrv, target.itservice, target.slaProperty)); - } - } - }, this); - - // Data for panel (all targets) - return $q.all(_.flatten(promises)) - .then(_.flatten) - .then(function (timeseries_data) { - - // Series downsampling - var data = _.map(timeseries_data, function(timeseries) { - var DPS = DataProcessingService; - if (timeseries.datapoints.length > options.maxDataPoints) { - timeseries.datapoints = DPS.groupBy(options.interval, DPS.AVERAGE, timeseries.datapoints); - } - return timeseries; - }); - return { data: data }; - }); - }; - - function bindFunctionDefs(functionDefs, category) { - var aggregationFunctions = _.map(metricFunctions.getCategories()[category], 'name'); - var aggFuncDefs = _.filter(functionDefs, function(func) { - return _.contains(aggregationFunctions, func.def.name); - }); - return _.map(aggFuncDefs, function(func) { - var funcInstance = metricFunctions.createFuncInstance(func.def, func.params); - return funcInstance.bindFunction(DataProcessingService.metricFunctions); - }); - } - - //////////////// - // Templating // - //////////////// - - /** - * Find metrics from templated request. - * - * @param {string} query Query from Templating - * @return {string} Metric name - group, host, app or item or list - * of metrics in "{metric1,metcic2,...,metricN}" format. - */ - this.metricFindQuery = function (query) { - // Split query. Query structure: - // group.host.app.item - var parts = []; - _.each(query.split('.'), function (part) { - part = templateSrv.replace(part); - - // Replace wildcard to regex - if (part === '*') { - part = '/.*/'; - } - parts.push(part); - }); - var template = _.object(['group', 'host', 'app', 'item'], parts); - - // Get items - if (parts.length === 4) { - return this.queryProcessor.filterItems(template.group, template.host, - template.app, 'all', true) - .then(function(items) { - return _.map(items, formatMetric); - }); - } - // Get applications - else if (parts.length === 3) { - return this.queryProcessor.filterApplications(template.group, template.host) - .then(function(apps) { - return _.map(apps, formatMetric); - }); - } - // Get hosts - else if (parts.length === 2) { - return this.queryProcessor.filterHosts(template.group) - .then(function(hosts) { - return _.map(hosts, formatMetric); - }); - } - // Get groups - else if (parts.length === 1) { - return this.zabbixCache.getGroups(template.group).then(function(groups) { - return _.map(groups, formatMetric); - }); - } - // Return empty object for invalid request - else { - return $q.when([]); - } - }; - - function formatMetric(metricObj) { - return { - text: metricObj.name, - expandable: false - }; - } - - ///////////////// - // Annotations // - ///////////////// - - this.annotationQuery = function(options) { - var from = Math.ceil(dateMath.parse(options.rangeRaw.from) / 1000); - var to = Math.ceil(dateMath.parse(options.rangeRaw.to) / 1000); - var annotation = options.annotation; - var self = this; - var showEvents = annotation.showOkEvents ? [0, 1] : 1; - - var buildQuery = self.queryProcessor.buildTriggerQuery(templateSrv.replace(annotation.group), - templateSrv.replace(annotation.host), - templateSrv.replace(annotation.application)); - return buildQuery.then(function(query) { - return self.zabbixAPI.getTriggers(query.groupids, - query.hostids, - query.applicationids, - showEvents) - .then(function(triggers) { - - // Filter triggers by description - if (utils.isRegex(annotation.trigger)) { - triggers = _.filter(triggers, function(trigger) { - return utils.buildRegex(annotation.trigger).test(trigger.description); - }); - } else if (annotation.trigger) { - triggers = _.filter(triggers, function(trigger) { - return trigger.description === annotation.trigger; - }); - } - - // Remove events below the chose severity - triggers = _.filter(triggers, function(trigger) { - return Number(trigger.priority) >= Number(annotation.minseverity); - }); - - var objectids = _.map(triggers, 'triggerid'); - var params = { - output: 'extend', - time_from: from, - time_till: to, - objectids: objectids, - select_acknowledges: 'extend', - selectHosts: 'extend', - value: showEvents - }; - - return self.zabbixAPI.request('event.get', params) - .then(function (events) { - var indexedTriggers = _.indexBy(triggers, 'triggerid'); - - // Hide acknowledged events if option enabled - if (annotation.hideAcknowledged) { - events = _.filter(events, function(event) { - return !event.acknowledges.length; - }); - } - - return _.map(events, function(e) { - var title =''; - if (annotation.showHostname) { - title += e.hosts[0].name + ': '; - } - - // Show event type (OK or Problem) - title += Number(e.value) ? 'Problem' : 'OK'; - - var formatted_acknowledges = utils.formatAcknowledges(e.acknowledges); - return { - annotation: annotation, - time: e.clock * 1000, - title: title, - text: indexedTriggers[e.objectid].description + formatted_acknowledges - }; - }); - }); - }); - }); - }; - - } - - return ZabbixAPIDatasource; - -}); diff --git a/plugins/datasource-zabbix/module.js b/plugins/datasource-zabbix/module.js deleted file mode 100644 index 38722d92a..000000000 --- a/plugins/datasource-zabbix/module.js +++ /dev/null @@ -1,24 +0,0 @@ -define([ - './datasource', - './queryCtrl' -], -function (ZabbixAPIDatasource, ZabbixQueryCtrl) { - 'use strict'; - - function ZabbixQueryOptionsCtrl() {} - ZabbixQueryOptionsCtrl.templateUrl = 'partials/query.options.html'; - - function ZabbixAnnotationsQueryCtrl() {} - ZabbixAnnotationsQueryCtrl.templateUrl = 'partials/annotations.editor.html'; - - function ZabbixConfigCtrl() {} - ZabbixConfigCtrl.templateUrl = 'partials/config.html'; - - return { - Datasource: ZabbixAPIDatasource, - QueryCtrl: ZabbixQueryCtrl, - ConfigCtrl: ZabbixConfigCtrl, - QueryOptionsCtrl: ZabbixQueryOptionsCtrl, - AnnotationsQueryCtrl: ZabbixAnnotationsQueryCtrl - }; -}); diff --git a/plugins/datasource-zabbix/addMetricFunction.js b/src/datasource-zabbix/addMetricFunction.js similarity index 100% rename from plugins/datasource-zabbix/addMetricFunction.js rename to src/datasource-zabbix/addMetricFunction.js diff --git a/plugins/datasource-zabbix/dataProcessingService.js b/src/datasource-zabbix/dataProcessingService.js similarity index 100% rename from plugins/datasource-zabbix/dataProcessingService.js rename to src/datasource-zabbix/dataProcessingService.js diff --git a/src/datasource-zabbix/datasource.js b/src/datasource-zabbix/datasource.js new file mode 100644 index 000000000..f9699f380 --- /dev/null +++ b/src/datasource-zabbix/datasource.js @@ -0,0 +1,430 @@ +//import angular from 'angular'; +import _ from 'lodash'; +import {parse as dateMathParse} from 'app/core/utils/datemath'; +import Utils from './utils'; +import metricFunctions from './metricFunctions'; +import {zabbixHelperSrv} from './helperFunctions'; +import {ZabbixAPI} from './zabbixAPI'; +import {ZabbixCachingProxy} from './zabbixCache'; +import {QueryProcessor} from './queryProcessor'; +import {DataProcessingService} from './dataProcessingService'; + +export class ZabbixAPIDatasource { + + constructor(instanceSettings, $q, templateSrv, alertSrv, ZabbixAPI, ZabbixCachingProxy, QueryProcessor, zabbixHelperSrv, DataProcessingService) { + + // General data source settings + this.name = instanceSettings.name; + this.url = instanceSettings.url; + this.basicAuth = instanceSettings.basicAuth; + this.withCredentials = instanceSettings.withCredentials; + + // Zabbix API credentials + this.username = instanceSettings.jsonData.username; + this.password = instanceSettings.jsonData.password; + + // Use trends instead history since specified time + this.trends = instanceSettings.jsonData.trends; + this.trendsFrom = instanceSettings.jsonData.trendsFrom || '7d'; + + // Set cache update interval + var ttl = instanceSettings.jsonData.cacheTTL || '1h'; + this.cacheTTL = Utils.parseInterval(ttl); + + // Initialize Zabbix API + this.zabbixAPI = new ZabbixAPI(this.url, this.username, this.password, this.basicAuth, this.withCredentials); + + // Initialize cache service + this.zabbixCache = new ZabbixCachingProxy(this.zabbixAPI, this.cacheTTL); + + // Initialize query builder + this.queryProcessor = new QueryProcessor(this.zabbixCache); + + // Dependencies + this.q = $q; + this.templateSrv = templateSrv; + this.alertSrv = alertSrv; + this.zabbixHelperSrv = zabbixHelperSrv; + this.DataProcessingService = DataProcessingService; + + console.log(this.zabbixCache); + } + + //////////////////////// + // Datasource methods // + //////////////////////// + + /** + * Test connection to Zabbix API + * @return {object} Connection status and Zabbix API version + */ + testDatasource() { + var self = this; + return this.zabbixAPI.getVersion().then(function (version) { + return self.zabbixAPI.login().then(function (auth) { + if (auth) { + return { + status: "success", + title: "Success", + message: "Zabbix API version: " + version + }; + } else { + return { + status: "error", + title: "Invalid user name or password", + message: "Zabbix API version: " + version + }; + } + }, function(error) { + console.log(error); + return { + status: "error", + title: "Connection failed", + message: error + }; + }); + }, + function(error) { + console.log(error); + return { + status: "error", + title: "Connection failed", + message: "Could not connect to given url" + }; + }); + } + + /** + * Query panel data. Calls for each panel in dashboard. + * @param {Object} options Contains time range, targets and other info. + * @return {Object} Grafana metrics object with timeseries data for each target. + */ + query(options) { + var self = this; + + // get from & to in seconds + var from = Math.ceil(dateMathParse(options.range.from) / 1000); + var to = Math.ceil(dateMathParse(options.range.to) / 1000); + var useTrendsFrom = Math.ceil(dateMathParse('now-' + this.trendsFrom) / 1000); + + // Create request for each target + var promises = _.map(options.targets, function(target) { + + if (target.mode !== 1) { + + // Don't request undefined and hidden targets + if (target.hide || !target.group || + !target.host || !target.item) { + return []; + } + + // Replace templated variables + var groupFilter = this.templateSrv.replace(target.group.filter, options.scopedVars); + var hostFilter = this.templateSrv.replace(target.host.filter, options.scopedVars); + var appFilter = this.templateSrv.replace(target.application.filter, options.scopedVars); + var itemFilter = this.templateSrv.replace(target.item.filter, options.scopedVars); + + // Query numeric data + if (!target.mode || target.mode === 0) { + + // Build query in asynchronous manner + return self.queryProcessor.build(groupFilter, hostFilter, appFilter, itemFilter) + .then(function(items) { + // Add hostname for items from multiple hosts + var addHostName = target.host.isRegex; + var getHistory; + + // Use trends + if ((from < useTrendsFrom) && self.trends) { + + // Find trendValue() function and get specified trend value + var trendFunctions = _.map(metricFunctions.getCategories()['Trends'], 'name'); + var trendValueFunc = _.find(target.functions, function(func) { + return _.contains(trendFunctions, func.def.name); + }); + var valueType = trendValueFunc ? trendValueFunc.params[0] : "avg"; + + getHistory = self.zabbixAPI.getTrend(items, from, to).then(function(history) { + return self.queryProcessor.handleTrends(history, addHostName, valueType); + }); + } else { + + // Use history + getHistory = self.zabbixCache.getHistory(items, from, to).then(function(history) { + return self.queryProcessor.handleHistory(history, addHostName); + }); + } + + return getHistory.then(function (timeseries_data) { + timeseries_data = _.map(timeseries_data, function (timeseries) { + + // Filter only transform functions + var transformFunctions = bindFunctionDefs(target.functions, 'Transform', self.DataProcessingService); + + // Metric data processing + var dp = timeseries.datapoints; + for (var i = 0; i < transformFunctions.length; i++) { + dp = transformFunctions[i](dp); + } + timeseries.datapoints = dp; + + return timeseries; + }); + + // Aggregations + var aggregationFunctions = bindFunctionDefs(target.functions, 'Aggregate', self.DataProcessingService); + var dp = _.map(timeseries_data, 'datapoints'); + if (aggregationFunctions.length) { + for (var i = 0; i < aggregationFunctions.length; i++) { + dp = aggregationFunctions[i](dp); + } + var lastAgg = _.findLast(target.functions, function(func) { + return _.contains( + _.map(metricFunctions.getCategories()['Aggregate'], 'name'), func.def.name); + }); + timeseries_data = [{ + target: lastAgg.text, + datapoints: dp + }]; + } + + // Apply alias functions + var aliasFunctions = bindFunctionDefs(target.functions, 'Alias', self.DataProcessingService); + for (var j = 0; j < aliasFunctions.length; j++) { + _.each(timeseries_data, aliasFunctions[j]); + } + + return timeseries_data; + }); + }); + } + + // Query text data + else if (target.mode === 2) { + return self.queryProcessor.build(groupFilter, hostFilter, appFilter, itemFilter) + .then(function(items) { + var deferred = self.q.defer(); + if (items.length) { + self.zabbixAPI.getLastValue(items[0].itemid).then(function(lastvalue) { + if (target.textFilter) { + var text_extract_pattern = new RegExp(self.templateSrv.replace(target.textFilter, options.scopedVars)); + var result = text_extract_pattern.exec(lastvalue); + if (result) { + if (target.useCaptureGroups) { + result = result[1]; + } else { + result = result[0]; + } + } + deferred.resolve(result); + } else { + deferred.resolve(lastvalue); + } + }); + } else { + deferred.resolve(null); + } + return deferred.promise.then(function(text) { + return { + target: target.item.name, + datapoints: [[text, to * 1000]] + }; + }); + }); + } + } + + // IT services mode + else if (target.mode === 1) { + // Don't show undefined and hidden targets + if (target.hide || !target.itservice || !target.slaProperty) { + return []; + } else { + return this.zabbixAPI.getSLA(target.itservice.serviceid, from, to) + .then(_.bind(zabbixHelperSrv.handleSLAResponse, zabbixHelperSrv, target.itservice, target.slaProperty)); + } + } + }, this); + + var self = this; + + // Data for panel (all targets) + return this.q.all(_.flatten(promises)) + .then(_.flatten) + .then(function (timeseries_data) { + + // Series downsampling + var data = _.map(timeseries_data, function(timeseries) { + var DPS = self.DataProcessingService; + if (timeseries.datapoints.length > options.maxDataPoints) { + timeseries.datapoints = DPS.groupBy(options.interval, DPS.AVERAGE, timeseries.datapoints); + } + return timeseries; + }); + return { data: data }; + }); + } + + //////////////// + // Templating // + //////////////// + + /** + * Find metrics from templated request. + * + * @param {string} query Query from Templating + * @return {string} Metric name - group, host, app or item or list + * of metrics in "{metric1,metcic2,...,metricN}" format. + */ + metricFindQuery(query) { + // Split query. Query structure: + // group.host.app.item + var parts = []; + _.each(query.split('.'), function (part) { + part = this.templateSrv.replace(part); + + // Replace wildcard to regex + if (part === '*') { + part = '/.*/'; + } + parts.push(part); + }); + var template = _.object(['group', 'host', 'app', 'item'], parts); + + // Get items + if (parts.length === 4) { + return this.queryProcessor.filterItems(template.group, template.host, + template.app, 'all', true) + .then(function(items) { + return _.map(items, formatMetric); + }); + } + // Get applications + else if (parts.length === 3) { + return this.queryProcessor.filterApplications(template.group, template.host) + .then(function(apps) { + return _.map(apps, formatMetric); + }); + } + // Get hosts + else if (parts.length === 2) { + return this.queryProcessor.filterHosts(template.group) + .then(function(hosts) { + return _.map(hosts, formatMetric); + }); + } + // Get groups + else if (parts.length === 1) { + return this.zabbixCache.getGroups(template.group).then(function(groups) { + return _.map(groups, formatMetric); + }); + } + // Return empty object for invalid request + else { + return this.q.when([]); + } + } + + ///////////////// + // Annotations // + ///////////////// + + annotationQuery(options) { + var from = Math.ceil(dateMathParse(options.rangeRaw.from) / 1000); + var to = Math.ceil(dateMathParse(options.rangeRaw.to) / 1000); + var annotation = options.annotation; + var self = this; + var showEvents = annotation.showOkEvents ? [0, 1] : 1; + + var buildQuery = self.queryProcessor.buildTriggerQuery(this.templateSrv.replace(annotation.group), + this.templateSrv.replace(annotation.host), + this.templateSrv.replace(annotation.application)); + return buildQuery.then(function(query) { + return self.zabbixAPI.getTriggers(query.groupids, + query.hostids, + query.applicationids, + showEvents) + .then(function(triggers) { + + // Filter triggers by description + if (Utils.isRegex(annotation.trigger)) { + triggers = _.filter(triggers, function(trigger) { + return Utils.buildRegex(annotation.trigger).test(trigger.description); + }); + } else if (annotation.trigger) { + triggers = _.filter(triggers, function(trigger) { + return trigger.description === annotation.trigger; + }); + } + + // Remove events below the chose severity + triggers = _.filter(triggers, function(trigger) { + return Number(trigger.priority) >= Number(annotation.minseverity); + }); + + var objectids = _.map(triggers, 'triggerid'); + var params = { + output: 'extend', + time_from: from, + time_till: to, + objectids: objectids, + select_acknowledges: 'extend', + selectHosts: 'extend', + value: showEvents + }; + + return self.zabbixAPI.request('event.get', params) + .then(function (events) { + var indexedTriggers = _.indexBy(triggers, 'triggerid'); + + // Hide acknowledged events if option enabled + if (annotation.hideAcknowledged) { + events = _.filter(events, function(event) { + return !event.acknowledges.length; + }); + } + + return _.map(events, function(e) { + var title =''; + if (annotation.showHostname) { + title += e.hosts[0].name + ': '; + } + + // Show event type (OK or Problem) + title += Number(e.value) ? 'Problem' : 'OK'; + + var formatted_acknowledges = Utils.formatAcknowledges(e.acknowledges); + return { + annotation: annotation, + time: e.clock * 1000, + title: title, + text: indexedTriggers[e.objectid].description + formatted_acknowledges + }; + }); + }); + }); + }); + } + +} + +function bindFunctionDefs(functionDefs, category, DataProcessingService) { + 'use strict'; + var aggregationFunctions = _.map(metricFunctions.getCategories()[category], 'name'); + var aggFuncDefs = _.filter(functionDefs, function(func) { + return _.contains(aggregationFunctions, func.def.name); + }); + + return _.map(aggFuncDefs, function(func) { + var funcInstance = metricFunctions.createFuncInstance(func.def, func.params); + return funcInstance.bindFunction(DataProcessingService.metricFunctions); + }); +} + +function formatMetric(metricObj) { + 'use strict'; + return { + text: metricObj.name, + expandable: false + }; +} \ No newline at end of file diff --git a/plugins/datasource-zabbix/directives.js b/src/datasource-zabbix/directives.js similarity index 100% rename from plugins/datasource-zabbix/directives.js rename to src/datasource-zabbix/directives.js diff --git a/plugins/datasource-zabbix/helperFunctions.js b/src/datasource-zabbix/helperFunctions.js similarity index 100% rename from plugins/datasource-zabbix/helperFunctions.js rename to src/datasource-zabbix/helperFunctions.js diff --git a/plugins/datasource-zabbix/metricFunctionEditor.js b/src/datasource-zabbix/metricFunctionEditor.js similarity index 100% rename from plugins/datasource-zabbix/metricFunctionEditor.js rename to src/datasource-zabbix/metricFunctionEditor.js diff --git a/plugins/datasource-zabbix/metricFunctions.js b/src/datasource-zabbix/metricFunctions.js similarity index 100% rename from plugins/datasource-zabbix/metricFunctions.js rename to src/datasource-zabbix/metricFunctions.js diff --git a/src/datasource-zabbix/module.js b/src/datasource-zabbix/module.js new file mode 100644 index 000000000..d94721401 --- /dev/null +++ b/src/datasource-zabbix/module.js @@ -0,0 +1,19 @@ +import {ZabbixAPIDatasource} from './datasource'; +import {ZabbixQueryCtrl} from './queryCtrl'; + +class ZabbixConfigCtrl {} +ZabbixConfigCtrl.templateUrl = 'partials/config.html'; + +class ZabbixQueryOptionsCtrl {} +ZabbixQueryOptionsCtrl.templateUrl = 'partials/query.options.html'; + +class ZabbixAnnotationsQueryCtrl {} +ZabbixAnnotationsQueryCtrl.templateUrl = 'partials/annotations.editor.html'; + +export { + ZabbixAPIDatasource as Datasource, + ZabbixQueryCtrl as QueryCtrl, + ZabbixConfigCtrl as ConfigCtrl, + ZabbixQueryOptionsCtrl as QueryOptionsCtrl, + ZabbixAnnotationsQueryCtrl as AnnotationsQueryCtrl +}; diff --git a/plugins/datasource-zabbix/partials/annotations.editor.html b/src/datasource-zabbix/partials/annotations.editor.html similarity index 100% rename from plugins/datasource-zabbix/partials/annotations.editor.html rename to src/datasource-zabbix/partials/annotations.editor.html diff --git a/plugins/datasource-zabbix/partials/config.html b/src/datasource-zabbix/partials/config.html similarity index 100% rename from plugins/datasource-zabbix/partials/config.html rename to src/datasource-zabbix/partials/config.html diff --git a/plugins/datasource-zabbix/partials/query.editor.html b/src/datasource-zabbix/partials/query.editor.html similarity index 100% rename from plugins/datasource-zabbix/partials/query.editor.html rename to src/datasource-zabbix/partials/query.editor.html diff --git a/plugins/datasource-zabbix/partials/query.options.html b/src/datasource-zabbix/partials/query.options.html similarity index 100% rename from plugins/datasource-zabbix/partials/query.options.html rename to src/datasource-zabbix/partials/query.options.html diff --git a/plugins/datasource-zabbix/plugin.json b/src/datasource-zabbix/plugin.json similarity index 100% rename from plugins/datasource-zabbix/plugin.json rename to src/datasource-zabbix/plugin.json diff --git a/plugins/datasource-zabbix/queryCtrl.js b/src/datasource-zabbix/queryCtrl.js similarity index 100% rename from plugins/datasource-zabbix/queryCtrl.js rename to src/datasource-zabbix/queryCtrl.js diff --git a/plugins/datasource-zabbix/queryProcessor.js b/src/datasource-zabbix/queryProcessor.js similarity index 100% rename from plugins/datasource-zabbix/queryProcessor.js rename to src/datasource-zabbix/queryProcessor.js diff --git a/plugins/datasource-zabbix/utils.js b/src/datasource-zabbix/utils.js similarity index 100% rename from plugins/datasource-zabbix/utils.js rename to src/datasource-zabbix/utils.js diff --git a/plugins/datasource-zabbix/zabbixAPI.js b/src/datasource-zabbix/zabbixAPI.js similarity index 100% rename from plugins/datasource-zabbix/zabbixAPI.js rename to src/datasource-zabbix/zabbixAPI.js diff --git a/plugins/datasource-zabbix/zabbixAPIService.js b/src/datasource-zabbix/zabbixAPIService.js similarity index 100% rename from plugins/datasource-zabbix/zabbixAPIService.js rename to src/datasource-zabbix/zabbixAPIService.js diff --git a/plugins/datasource-zabbix/zabbixCache.js b/src/datasource-zabbix/zabbixCache.js similarity index 100% rename from plugins/datasource-zabbix/zabbixCache.js rename to src/datasource-zabbix/zabbixCache.js diff --git a/plugins/panel-triggers/editor.html b/src/panel-triggers/editor.html similarity index 100% rename from plugins/panel-triggers/editor.html rename to src/panel-triggers/editor.html diff --git a/plugins/panel-triggers/editor.js b/src/panel-triggers/editor.js similarity index 100% rename from plugins/panel-triggers/editor.js rename to src/panel-triggers/editor.js diff --git a/plugins/panel-triggers/module.html b/src/panel-triggers/module.html similarity index 100% rename from plugins/panel-triggers/module.html rename to src/panel-triggers/module.html diff --git a/plugins/panel-triggers/module.js b/src/panel-triggers/module.js similarity index 100% rename from plugins/panel-triggers/module.js rename to src/panel-triggers/module.js diff --git a/plugins/panel-triggers/plugin.json b/src/panel-triggers/plugin.json similarity index 100% rename from plugins/panel-triggers/plugin.json rename to src/panel-triggers/plugin.json diff --git a/plugins/panel-triggers/trigger.colorpicker.html b/src/panel-triggers/trigger.colorpicker.html similarity index 100% rename from plugins/panel-triggers/trigger.colorpicker.html rename to src/panel-triggers/trigger.colorpicker.html From 775c2ddac1e6a1fdd0632c3853df6be8e8e1f8fd Mon Sep 17 00:00:00 2001 From: Alexander Zobnin Date: Tue, 15 Mar 2016 20:14:23 +0300 Subject: [PATCH 144/205] queryCtrl and utils module refactor. Suppress "use strict" warnings in modules. --- .jshintrc | 1 + Gruntfile.js | 6 +- src/datasource-zabbix/datasource.js | 22 +- src/datasource-zabbix/queryCtrl.js | 399 ++++++++++++++-------------- src/datasource-zabbix/utils.js | 124 ++++----- 5 files changed, 271 insertions(+), 281 deletions(-) diff --git a/.jshintrc b/.jshintrc index 36c9c9fd8..fe8638292 100644 --- a/.jshintrc +++ b/.jshintrc @@ -5,6 +5,7 @@ "curly": true, "eqnull": true, "strict": true, + "module": true, "devel": true, "eqeqeq": true, "forin": false, diff --git a/Gruntfile.js b/Gruntfile.js index 2c8f35ae4..98231763e 100644 --- a/Gruntfile.js +++ b/Gruntfile.js @@ -17,6 +17,8 @@ module.exports = function(grunt) { '**/*', '!**/datasource.js', '!**/module.js', + '!**/queryCtrl.js', + '!**/utils.js', '!**/*.scss' ], dest: 'dist/src' @@ -47,8 +49,10 @@ module.exports = function(grunt) { cwd: 'src', expand: true, src: [ - '**/**/datasource.js', '**/**/module.js', + '**/**/datasource.js', + '**/**/queryCtrl.js', + '**/**/utils.js', ], dest: 'dist/src', ext:'.js' diff --git a/src/datasource-zabbix/datasource.js b/src/datasource-zabbix/datasource.js index f9699f380..24e755d75 100644 --- a/src/datasource-zabbix/datasource.js +++ b/src/datasource-zabbix/datasource.js @@ -1,7 +1,7 @@ //import angular from 'angular'; import _ from 'lodash'; -import {parse as dateMathParse} from 'app/core/utils/datemath'; -import Utils from './utils'; +import * as dateMath from 'app/core/utils/datemath'; +import * as utils from './utils'; import metricFunctions from './metricFunctions'; import {zabbixHelperSrv} from './helperFunctions'; import {ZabbixAPI} from './zabbixAPI'; @@ -29,7 +29,7 @@ export class ZabbixAPIDatasource { // Set cache update interval var ttl = instanceSettings.jsonData.cacheTTL || '1h'; - this.cacheTTL = Utils.parseInterval(ttl); + this.cacheTTL = utils.parseInterval(ttl); // Initialize Zabbix API this.zabbixAPI = new ZabbixAPI(this.url, this.username, this.password, this.basicAuth, this.withCredentials); @@ -103,9 +103,9 @@ export class ZabbixAPIDatasource { var self = this; // get from & to in seconds - var from = Math.ceil(dateMathParse(options.range.from) / 1000); - var to = Math.ceil(dateMathParse(options.range.to) / 1000); - var useTrendsFrom = Math.ceil(dateMathParse('now-' + this.trendsFrom) / 1000); + var from = Math.ceil(dateMath.parse(options.range.from) / 1000); + var to = Math.ceil(dateMath.parse(options.range.to) / 1000); + var useTrendsFrom = Math.ceil(dateMath.parse('now-' + this.trendsFrom) / 1000); // Create request for each target var promises = _.map(options.targets, function(target) { @@ -330,8 +330,8 @@ export class ZabbixAPIDatasource { ///////////////// annotationQuery(options) { - var from = Math.ceil(dateMathParse(options.rangeRaw.from) / 1000); - var to = Math.ceil(dateMathParse(options.rangeRaw.to) / 1000); + var from = Math.ceil(dateMath.parse(options.rangeRaw.from) / 1000); + var to = Math.ceil(dateMath.parse(options.rangeRaw.to) / 1000); var annotation = options.annotation; var self = this; var showEvents = annotation.showOkEvents ? [0, 1] : 1; @@ -347,9 +347,9 @@ export class ZabbixAPIDatasource { .then(function(triggers) { // Filter triggers by description - if (Utils.isRegex(annotation.trigger)) { + if (utils.isRegex(annotation.trigger)) { triggers = _.filter(triggers, function(trigger) { - return Utils.buildRegex(annotation.trigger).test(trigger.description); + return utils.buildRegex(annotation.trigger).test(trigger.description); }); } else if (annotation.trigger) { triggers = _.filter(triggers, function(trigger) { @@ -393,7 +393,7 @@ export class ZabbixAPIDatasource { // Show event type (OK or Problem) title += Number(e.value) ? 'Problem' : 'OK'; - var formatted_acknowledges = Utils.formatAcknowledges(e.acknowledges); + var formatted_acknowledges = utils.formatAcknowledges(e.acknowledges); return { annotation: annotation, time: e.clock * 1000, diff --git a/src/datasource-zabbix/queryCtrl.js b/src/datasource-zabbix/queryCtrl.js index 6f927c31f..935fc7a47 100644 --- a/src/datasource-zabbix/queryCtrl.js +++ b/src/datasource-zabbix/queryCtrl.js @@ -1,242 +1,237 @@ -define([ +/*define([ 'app/plugins/sdk', 'angular', 'lodash', './metricFunctions', './utils' -], -function (sdk, angular, _, metricFunctions, utils) { - 'use strict'; +],*/ - var ZabbixQueryCtrl = (function(_super) { +import {QueryCtrl} from 'app/plugins/sdk'; +import _ from 'lodash'; +import * as utils from './utils'; +import metricFunctions from './metricFunctions'; - // ZabbixQueryCtrl constructor - function ZabbixQueryCtrl($scope, $injector, $sce, $q, templateSrv) { +export class ZabbixQueryCtrl extends QueryCtrl { - // Call superclass constructor - _super.call(this, $scope, $injector); + // ZabbixQueryCtrl constructor + constructor($scope, $injector, $sce, $q, templateSrv) { - this.editorModes = { - 0: 'num', - 1: 'itservice', - 2: 'text' - }; - - // Map functions for bs-typeahead - this.getGroupNames = _.partial(getMetricNames, this, 'groupList'); - this.getHostNames = _.partial(getMetricNames, this, 'filteredHosts'); - this.getApplicationNames = _.partial(getMetricNames, this, 'filteredApplications'); - this.getItemNames = _.partial(getMetricNames, this, 'filteredItems'); - - this.init = function() { - - this.templateSrv = templateSrv; - var target = this.target; - - var scopeDefaults = { - metric: {} - }; - _.defaults(this, scopeDefaults); - - // Load default values - var targetDefaults = { - mode: 0, - group: { filter: "" }, - host: { filter: "" }, - application: { filter: "" }, - item: { filter: "" }, - functions: [], - }; - _.defaults(target, targetDefaults); - - // Create function instances from saved JSON - target.functions = _.map(target.functions, function(func) { - return metricFunctions.createFuncInstance(func.def, func.params); - }); - - if (target.mode === 0 || - target.mode === 2) { - - this.downsampleFunctionList = [ - {name: "avg", value: "avg"}, - {name: "min", value: "min"}, - {name: "max", value: "max"} - ]; - - // Set avg by default - if (!target.downsampleFunction) { - target.downsampleFunction = this.downsampleFunctionList[0]; - } - - this.initFilters(); - } - else if (target.mode === 1) { - this.slaPropertyList = [ - {name: "Status", property: "status"}, - {name: "SLA", property: "sla"}, - {name: "OK time", property: "okTime"}, - {name: "Problem time", property: "problemTime"}, - {name: "Down time", property: "downtimeTime"} - ]; - this.itserviceList = [{name: "test"}]; - this.updateITServiceList(); - } - }; + // Call superclass constructor + super($scope, $injector); - this.init(); - } + this.editorModes = { + 0: 'num', + 1: 'itservice', + 2: 'text' + }; - ZabbixQueryCtrl.templateUrl = 'partials/query.editor.html'; + // Map functions for bs-typeahead + this.getGroupNames = _.partial(getMetricNames, this, 'groupList'); + this.getHostNames = _.partial(getMetricNames, this, 'filteredHosts'); + this.getApplicationNames = _.partial(getMetricNames, this, 'filteredApplications'); + this.getItemNames = _.partial(getMetricNames, this, 'filteredItems'); - ZabbixQueryCtrl.prototype = Object.create(_super.prototype); - ZabbixQueryCtrl.prototype.constructor = ZabbixQueryCtrl; + this.init = function() { - var p = ZabbixQueryCtrl.prototype; + this.templateSrv = templateSrv; + var target = this.target; - p.initFilters = function () { - this.filterGroups(); - this.filterHosts(); - this.filterApplications(); - this.filterItems(); - }; + var scopeDefaults = { + metric: {} + }; + _.defaults(this, scopeDefaults); + + // Load default values + var targetDefaults = { + mode: 0, + group: { filter: "" }, + host: { filter: "" }, + application: { filter: "" }, + item: { filter: "" }, + functions: [], + }; + _.defaults(target, targetDefaults); - p.filterHosts = function () { - var self = this; - var groupFilter = this.templateSrv.replace(this.target.group.filter); - this.datasource.queryProcessor.filterHosts(groupFilter).then(function(hosts) { - self.metric.filteredHosts = hosts; + // Create function instances from saved JSON + target.functions = _.map(target.functions, function(func) { + return metricFunctions.createFuncInstance(func.def, func.params); }); - }; - p.filterGroups = function() { - var self = this; - this.datasource.queryProcessor.filterGroups().then(function(groups) { - self.metric.groupList = groups; - }); - }; + if (target.mode === 0 || + target.mode === 2) { - p.filterApplications = function () { - var self = this; - var groupFilter = this.templateSrv.replace(this.target.group.filter); - var hostFilter = this.templateSrv.replace(this.target.host.filter); - this.datasource.queryProcessor.filterApplications(groupFilter, hostFilter) - .then(function(apps) { - self.metric.filteredApplications = apps; - }); - }; + this.downsampleFunctionList = [ + {name: "avg", value: "avg"}, + {name: "min", value: "min"}, + {name: "max", value: "max"} + ]; - p.filterItems = function () { - var self = this; - var item_type = this.editorModes[this.target.mode]; - var groupFilter = this.templateSrv.replace(this.target.group.filter); - var hostFilter = this.templateSrv.replace(this.target.host.filter); - var appFilter = this.templateSrv.replace(this.target.application.filter); - this.datasource.queryProcessor.filterItems(groupFilter, hostFilter, appFilter, - item_type, this.target.showDisabledItems).then(function(items) { - self.metric.filteredItems = items; - }); - }; + // Set avg by default + if (!target.downsampleFunction) { + target.downsampleFunction = this.downsampleFunctionList[0]; + } - p.onTargetPartChange = function (targetPart) { - var regexStyle = {'color': '#CCA300'}; - targetPart.isRegex = utils.isRegex(targetPart.filter); - targetPart.style = targetPart.isRegex ? regexStyle : {}; + this.initFilters(); + } + else if (target.mode === 1) { + this.slaPropertyList = [ + {name: "Status", property: "status"}, + {name: "SLA", property: "sla"}, + {name: "OK time", property: "okTime"}, + {name: "Problem time", property: "problemTime"}, + {name: "Down time", property: "downtimeTime"} + ]; + this.itserviceList = [{name: "test"}]; + this.updateITServiceList(); + } }; - p.onTargetBlur = function() { - this.initFilters(); - this.parseTarget(); - this.panelCtrl.refresh(); - }; + this.init(); + } - p.parseTarget = function() { - // Parse target - }; + initFilters() { + this.filterGroups(); + this.filterHosts(); + this.filterApplications(); + this.filterItems(); + } - // Validate target and set validation info - p.validateTarget = function () {}; + filterHosts() { + var self = this; + var groupFilter = this.templateSrv.replace(this.target.group.filter); + this.datasource.queryProcessor.filterHosts(groupFilter).then(function(hosts) { + self.metric.filteredHosts = hosts; + }); + } - p.targetChanged = function() { - this.panelCtrl.refresh(); - }; + filterGroups() { + var self = this; + this.datasource.queryProcessor.filterGroups().then(function(groups) { + self.metric.groupList = groups; + }); + } - p.addFunction = function(funcDef) { - var newFunc = metricFunctions.createFuncInstance(funcDef); - newFunc.added = true; - this.target.functions.push(newFunc); + filterApplications() { + var self = this; + var groupFilter = this.templateSrv.replace(this.target.group.filter); + var hostFilter = this.templateSrv.replace(this.target.host.filter); + this.datasource.queryProcessor.filterApplications(groupFilter, hostFilter) + .then(function(apps) { + self.metric.filteredApplications = apps; + }); + } - this.moveAliasFuncLast(); + filterItems() { + var self = this; + var item_type = this.editorModes[this.target.mode]; + var groupFilter = this.templateSrv.replace(this.target.group.filter); + var hostFilter = this.templateSrv.replace(this.target.host.filter); + var appFilter = this.templateSrv.replace(this.target.application.filter); + this.datasource.queryProcessor.filterItems(groupFilter, hostFilter, appFilter, + item_type, this.target.showDisabledItems).then(function(items) { + self.metric.filteredItems = items; + }); + } - if (newFunc.params.length && newFunc.added || - newFunc.def.params.length === 0) { - this.targetChanged(); - } - }; + onTargetPartChange(targetPart) { + var regexStyle = {'color': '#CCA300'}; + targetPart.isRegex = utils.isRegex(targetPart.filter); + targetPart.style = targetPart.isRegex ? regexStyle : {}; + } - p.removeFunction = function(func) { - this.target.functions = _.without(this.target.functions, func); - this.targetChanged(); - }; + onTargetBlur() { + this.initFilters(); + this.parseTarget(); + this.panelCtrl.refresh(); + } - p.moveAliasFuncLast = function() { - var aliasFunc = _.find(this.target.functions, function(func) { - return func.def.name === 'alias' || - func.def.name === 'aliasByNode' || - func.def.name === 'aliasByMetric'; - }); + parseTarget() { + // Parse target + } - if (aliasFunc) { - this.target.functions = _.without(this.target.functions, aliasFunc); - this.target.functions.push(aliasFunc); - } - }; + // Validate target and set validation info + validateTarget() { + // validate + } - /** - * Switch query editor to specified mode. - * Modes: - * 0 - items - * 1 - IT services - * 2 - Text metrics - */ - p.switchEditorMode = function (mode) { - this.target.mode = mode; - this.init(); - }; + targetChanged() { + this.panelCtrl.refresh(); + } - ///////////////// - // IT Services // - ///////////////// - - /** - * Update list of IT services - */ - p.updateITServiceList = function () { - var self = this; - this.datasource.zabbixAPI.getITService().then(function (iteservices) { - self.itserviceList = []; - self.itserviceList = self.itserviceList.concat(iteservices); - }); - }; + addFunction(funcDef) { + var newFunc = metricFunctions.createFuncInstance(funcDef); + newFunc.added = true; + this.target.functions.push(newFunc); - /** - * Call when IT service is selected. - */ - p.selectITService = function () { - if (!_.isEqual(this.oldTarget, this.target) && _.isEmpty(this.target.errors)) { - this.oldTarget = angular.copy(this.target); - this.panelCtrl.refresh(); - } - }; + this.moveAliasFuncLast(); - return ZabbixQueryCtrl; + if (newFunc.params.length && newFunc.added || + newFunc.def.params.length === 0) { + this.targetChanged(); + } + } + + removeFunction(func) { + this.target.functions = _.without(this.target.functions, func); + this.targetChanged(); + } - })(sdk.QueryCtrl); + moveAliasFuncLast() { + var aliasFunc = _.find(this.target.functions, function(func) { + return func.def.name === 'alias' || + func.def.name === 'aliasByNode' || + func.def.name === 'aliasByMetric'; + }); - return ZabbixQueryCtrl; + if (aliasFunc) { + this.target.functions = _.without(this.target.functions, aliasFunc); + this.target.functions.push(aliasFunc); + } + } - // Get list of metric names for bs-typeahead directive - function getMetricNames(scope, metricList) { - return _.uniq(_.map(scope.metric[metricList], 'name')); + /** + * Switch query editor to specified mode. + * Modes: + * 0 - items + * 1 - IT services + * 2 - Text metrics + */ + switchEditorMode(mode) { + this.target.mode = mode; + this.init(); } -}); \ No newline at end of file + ///////////////// + // IT Services // + ///////////////// + + /** + * Update list of IT services + */ + updateITServiceList() { + var self = this; + this.datasource.zabbixAPI.getITService().then(function (iteservices) { + self.itserviceList = []; + self.itserviceList = self.itserviceList.concat(iteservices); + }); + } + + /** + * Call when IT service is selected. + */ + selectITService() { + if (!_.isEqual(this.oldTarget, this.target) && _.isEmpty(this.target.errors)) { + this.oldTarget = angular.copy(this.target); + this.panelCtrl.refresh(); + } + } + +} + +// Set templateUrl as static property +ZabbixQueryCtrl.templateUrl = 'partials/query.editor.html'; + +// Get list of metric names for bs-typeahead directive +function getMetricNames(scope, metricList) { + return _.uniq(_.map(scope.metric[metricList], 'name')); +} \ No newline at end of file diff --git a/src/datasource-zabbix/utils.js b/src/datasource-zabbix/utils.js index c92c616d4..ff36135be 100644 --- a/src/datasource-zabbix/utils.js +++ b/src/datasource-zabbix/utils.js @@ -1,77 +1,67 @@ -define([ - 'lodash', - 'moment' -], -function (_, moment) { - 'use strict'; +import _ from 'lodash'; +import moment from 'moment'; - function Utils() { - /** - * Expand Zabbix item name - * - * @param {string} name item name, ie "CPU $2 time" - * @param {string} key item key, ie system.cpu.util[,system,avg1] - * @return {string} expanded name, ie "CPU system time" - */ - this.expandItemName = function(name, key) { +/** + * Expand Zabbix item name + * + * @param {string} name item name, ie "CPU $2 time" + * @param {string} key item key, ie system.cpu.util[,system,avg1] + * @return {string} expanded name, ie "CPU system time" + */ +export function expandItemName(name, key) { - // extract params from key: - // "system.cpu.util[,system,avg1]" --> ["", "system", "avg1"] - var key_params = key.substring(key.indexOf('[') + 1, key.lastIndexOf(']')).split(','); + // 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; - }; - - // Pattern for testing regex - var regexPattern = /^\/(.*)\/([gmi]*)$/m; + // replace item parameters + for (var i = key_params.length; i >= 1; i--) { + name = name.replace('$' + i, key_params[i - 1]); + } + return name; +} - this.isRegex = function (str) { - return regexPattern.test(str); - }; +// Pattern for testing regex +var regexPattern = /^\/(.*)\/([gmi]*)$/m; - this.buildRegex = function (str) { - var matches = str.match(regexPattern); - var pattern = matches[1]; - var flags = matches[2] !== "" ? matches[2] : undefined; - return new RegExp(pattern, flags); - }; +export function isRegex(str) { + return regexPattern.test(str); +} - this.parseInterval = function(interval) { - var intervalPattern = /(^[\d]+)(y|M|w|d|h|m|s)/g; - var momentInterval = intervalPattern.exec(interval); - return moment.duration(Number(momentInterval[1]), momentInterval[2]).valueOf(); - }; +export function buildRegex(str) { + var matches = str.match(regexPattern); + var pattern = matches[1]; + var flags = matches[2] !== "" ? matches[2] : undefined; + return new RegExp(pattern, flags); +} - /** - * Format acknowledges. - * - * @param {array} acknowledges array of Zabbix acknowledge objects - * @return {string} HTML-formatted table - */ - this.formatAcknowledges = function(acknowledges) { - if (acknowledges.length) { - var formatted_acknowledges = '

    Acknowledges:
    ' - + ''; - _.each(_.map(acknowledges, function (ack) { - var timestamp = moment.unix(ack.clock); - return ''; - }), function (ack) { - formatted_acknowledges = formatted_acknowledges.concat(ack); - }); - formatted_acknowledges = formatted_acknowledges.concat('
    TimeUserComments
    ' + timestamp.format("DD MMM YYYY HH:mm:ss") + '' + ack.alias - + ' (' + ack.name + ' ' + ack.surname + ')' + '' + ack.message + '
    '); - return formatted_acknowledges; - } else { - return ''; - } - }; +export function parseInterval(interval) { + var intervalPattern = /(^[\d]+)(y|M|w|d|h|m|s)/g; + var momentInterval = intervalPattern.exec(interval); + return moment.duration(Number(momentInterval[1]), momentInterval[2]).valueOf(); +} +/** + * Format acknowledges. + * + * @param {array} acknowledges array of Zabbix acknowledge objects + * @return {string} HTML-formatted table + */ +export function formatAcknowledges(acknowledges) { + if (acknowledges.length) { + var formatted_acknowledges = '

    Acknowledges:
    ' + + ''; + _.each(_.map(acknowledges, function (ack) { + var timestamp = moment.unix(ack.clock); + return ''; + }), function (ack) { + formatted_acknowledges = formatted_acknowledges.concat(ack); + }); + formatted_acknowledges = formatted_acknowledges.concat('
    TimeUserComments
    ' + timestamp.format("DD MMM YYYY HH:mm:ss") + '' + ack.alias + + ' (' + ack.name + ' ' + ack.surname + ')' + '' + ack.message + '
    '); + return formatted_acknowledges; + } else { + return ''; } - - return new Utils(); -}); \ No newline at end of file +} From de8a1ff621594d96a801fdf19ca3ccbe0f034acd Mon Sep 17 00:00:00 2001 From: Alexander Zobnin Date: Tue, 15 Mar 2016 22:54:02 +0300 Subject: [PATCH 145/205] zabbixAPI (-> zabbixAPIService) and zabbixAPIService (-> zabbixAPICoreService) refactor. --- Gruntfile.js | 4 + src/datasource-zabbix/datasource.js | 8 +- src/datasource-zabbix/zabbixAPI.js | 370 --------------- src/datasource-zabbix/zabbixAPICoreService.js | 104 +++++ src/datasource-zabbix/zabbixAPIService.js | 420 ++++++++++++++---- 5 files changed, 457 insertions(+), 449 deletions(-) delete mode 100644 src/datasource-zabbix/zabbixAPI.js create mode 100644 src/datasource-zabbix/zabbixAPICoreService.js diff --git a/Gruntfile.js b/Gruntfile.js index 98231763e..4b8522287 100644 --- a/Gruntfile.js +++ b/Gruntfile.js @@ -19,6 +19,8 @@ module.exports = function(grunt) { '!**/module.js', '!**/queryCtrl.js', '!**/utils.js', + '!**/zabbixAPICoreService.js', + '!**/zabbixAPIService.js', '!**/*.scss' ], dest: 'dist/src' @@ -53,6 +55,8 @@ module.exports = function(grunt) { '**/**/datasource.js', '**/**/queryCtrl.js', '**/**/utils.js', + '**/**/zabbixAPICoreService.js', + '**/**/zabbixAPIService.js', ], dest: 'dist/src', ext:'.js' diff --git a/src/datasource-zabbix/datasource.js b/src/datasource-zabbix/datasource.js index 24e755d75..77eddbaa5 100644 --- a/src/datasource-zabbix/datasource.js +++ b/src/datasource-zabbix/datasource.js @@ -4,14 +4,15 @@ import * as dateMath from 'app/core/utils/datemath'; import * as utils from './utils'; import metricFunctions from './metricFunctions'; import {zabbixHelperSrv} from './helperFunctions'; -import {ZabbixAPI} from './zabbixAPI'; +import './zabbixAPIService'; import {ZabbixCachingProxy} from './zabbixCache'; import {QueryProcessor} from './queryProcessor'; import {DataProcessingService} from './dataProcessingService'; export class ZabbixAPIDatasource { - constructor(instanceSettings, $q, templateSrv, alertSrv, ZabbixAPI, ZabbixCachingProxy, QueryProcessor, zabbixHelperSrv, DataProcessingService) { + /** @ngInject */ + constructor(instanceSettings, $q, templateSrv, alertSrv, zabbixAPIService, ZabbixCachingProxy, QueryProcessor, zabbixHelperSrv, DataProcessingService) { // General data source settings this.name = instanceSettings.name; @@ -32,6 +33,7 @@ export class ZabbixAPIDatasource { this.cacheTTL = utils.parseInterval(ttl); // Initialize Zabbix API + var ZabbixAPI = zabbixAPIService; this.zabbixAPI = new ZabbixAPI(this.url, this.username, this.password, this.basicAuth, this.withCredentials); // Initialize cache service @@ -427,4 +429,4 @@ function formatMetric(metricObj) { text: metricObj.name, expandable: false }; -} \ No newline at end of file +} diff --git a/src/datasource-zabbix/zabbixAPI.js b/src/datasource-zabbix/zabbixAPI.js deleted file mode 100644 index eac75890e..000000000 --- a/src/datasource-zabbix/zabbixAPI.js +++ /dev/null @@ -1,370 +0,0 @@ -define([ - 'angular', - 'lodash', - './zabbixAPIService' - ], -function (angular, _) { - 'use strict'; - - var module = angular.module('grafana.services'); - - /** - * Zabbix API Wrapper. - * Creates Zabbix API instance with given parameters (url, credentials and other). - * Wraps API calls and provides high-level methods. - */ - module.factory('ZabbixAPI', function($q, backendSrv, alertSrv, ZabbixAPIService) { - - // Initialize Zabbix API. - function ZabbixAPI(api_url, username, password, basicAuth, withCredentials) { - this.url = api_url; - this.username = username; - this.password = password; - this.auth = ""; - - this.requestOptions = { - basicAuth: basicAuth, - withCredentials: withCredentials - }; - - this.loginPromise = null; - } - - var p = ZabbixAPI.prototype; - - ////////////////// - // Core methods // - ////////////////// - - p.request = function(method, params) { - var self = this; - - return ZabbixAPIService.request(this.url, method, params, this.requestOptions, this.auth) - .then(function(result) { - return result; - }, - // Handle API errors - function(error) { - if (isNotAuthorized(error.data)) { - return self.loginOnce().then( - function() { - return self.request(method, params); - }, - // Handle user.login method errors - function(error) { - self.alertAPIError(error.data); - }); - } - }); - }; - - p.alertAPIError = function(message) { - alertSrv.set( - "Zabbix API Error", - message, - 'error' - ); - }; - - function isNotAuthorized(message) { - return ( - message === "Session terminated, re-login, please." || - message === "Not authorised." || - message === "Not authorized." - ); - } - - /** - * When API unauthenticated or auth token expired each request produce login() - * call. But auth token is common to all requests. This function wraps login() method - * and call it once. If login() already called just wait for it (return its promise). - * @return login promise - */ - p.loginOnce = function() { - var self = this; - var deferred = $q.defer(); - if (!self.loginPromise) { - self.loginPromise = deferred.promise; - self.login().then( - function(auth) { - self.loginPromise = null; - self.auth = auth; - deferred.resolve(auth); - }, - function(error) { - self.loginPromise = null; - deferred.reject(error); - } - ); - } else { - return self.loginPromise; - } - return deferred.promise; - }; - - /** - * Get authentication token. - */ - p.login = function() { - return ZabbixAPIService.login(this.url, this.username, this.password, this.requestOptions); - }; - - /** - * Get Zabbix API version - */ - p.getVersion = function() { - return ZabbixAPIService.getVersion(this.url, this.requestOptions); - }; - - ///////////////// - // API methods // - ///////////////// - - p.getGroups = function() { - var params = { - output: ['name'], - sortfield: 'name', - selectHosts: [] - }; - - return this.request('hostgroup.get', params); - }; - - p.getHosts = function() { - var params = { - output: ['name', 'host'], - sortfield: 'name', - selectGroups: [] - }; - - return this.request('host.get', params); - }; - - p.getApplications = function() { - var params = { - output: ['name'], - sortfield: 'name', - - // Hack for supporting different apis (2.2 vs 2.4 vs 3.0) - selectHost: [], - selectHosts: [] - }; - - return this.request('application.get', params); - }; - - p.getItems = function() { - var params = { - output: [ - 'name', 'key_', - 'value_type', - 'hostid', - 'status', - 'state' - ], - sortfield: 'name', - selectApplications: [] - }; - - return this.request('item.get', params); - }; - - /** - * Get Hosts list with host's items. - * @return {[type]} [description] - */ - p.getHostsExtend = function() { - var params = { - output: ['name', 'host'], - sortfield: 'name', - selectGroups: [], - selectItems: [ - 'name', 'key_', - 'value_type', - 'hostid', - 'status', - 'state' - ] - }; - - return this.request('host.get', params); - }; - - p.getLastValue = function(itemid) { - var params = { - output: ['lastvalue'], - itemids: itemid - }; - return this.request('item.get', params).then(function(items) { - if (items.length) { - return items[0].lastvalue; - } else { - return null; - } - }); - }; - - /** - * Perform history query from Zabbix API - * - * @param {Array} items Array of Zabbix item objects - * @param {Number} time_from Time in seconds - * @param {Number} time_till Time in seconds - * @return {Array} Array of Zabbix history objects - */ - p.getHistory = function(items, time_from, time_till) { - var self = this; - - // 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: time_from - }; - - // Relative queries (e.g. last hour) don't include an end time - if (time_till) { - params.time_till = time_till; - } - - return self.request('history.get', params); - })).then(_.flatten); - }; - - /** - * Perform trends query from Zabbix API - * Use trends api extension from ZBXNEXT-1193 patch. - * - * @param {Array} items Array of Zabbix item objects - * @param {Number} time_from Time in seconds - * @param {Number} time_till Time in seconds - * @return {Array} Array of Zabbix trend objects - */ - p.getTrend_ZBXNEXT1193 = function(items, time_from, time_till) { - var self = this; - - // 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', - trend: value_type, - itemids: itemids, - sortfield: 'clock', - sortorder: 'ASC', - time_from: time_from - }; - - // Relative queries (e.g. last hour) don't include an end time - if (time_till) { - params.time_till = time_till; - } - - return self.request('trend.get', params); - })).then(_.flatten); - }; - - p.getTrend_30 = function(items, time_from, time_till, value_type) { - var self = this; - var itemids = _.map(items, 'itemid'); - - var params = { - output: ["itemid", - "clock", - value_type - ], - itemids: itemids, - time_from: time_from - }; - - // Relative queries (e.g. last hour) don't include an end time - if (time_till) { - params.time_till = time_till; - } - - return self.request('trend.get', params); - }; - - p.getTrend = p.getTrend_ZBXNEXT1193; - //p.getTrend = p.getTrend_30; - - p.getITService = function(/* optional */ serviceids) { - var params = { - output: 'extend', - serviceids: serviceids - }; - return this.request('service.get', params); - }; - - p.getSLA = function(serviceids, from, to) { - var params = { - serviceids: serviceids, - intervals: [{ - from: from, - to: to - }] - }; - return this.request('service.getsla', params); - }; - - p.getTriggers = function(groupids, hostids, applicationids, showEvents) { - var params = { - output: 'extend', - groupids: groupids, - hostids: hostids, - applicationids: applicationids, - expandDescription: true, - expandData: true, - monitored: true, - skipDependent: true, - //only_true: true, - filter: { - value: 1 - }, - selectGroups: ['name'], - selectHosts: ['name'], - selectItems: ['name', 'key_', 'lastvalue'], - selectLastEvent: 'extend' - }; - - if (showEvents) { - params.filter.value = showEvents; - } - - return this.request('trigger.get', params); - }; - - p.getAcknowledges = function(eventids) { - var params = { - output: 'extend', - eventids: eventids, - preservekeys: true, - select_acknowledges: 'extend', - sortfield: 'clock', - sortorder: 'DESC' - }; - - return this.request('event.get', params) - .then(function (events) { - return _.filter(events, function(event) { - return event.acknowledges.length; - }); - }); - }; - - return ZabbixAPI; - - }); - -}); diff --git a/src/datasource-zabbix/zabbixAPICoreService.js b/src/datasource-zabbix/zabbixAPICoreService.js new file mode 100644 index 000000000..a1623e60f --- /dev/null +++ b/src/datasource-zabbix/zabbixAPICoreService.js @@ -0,0 +1,104 @@ +/** + * General Zabbix API methods + */ + +import angular from 'angular'; + +class ZabbixAPICoreService { + + /** @ngInject */ + constructor($q, backendSrv) { + this.$q = $q; + this.backendSrv = backendSrv; + } + + /** + * Request data from Zabbix API + * @return {object} response.result + */ + request(api_url, method, params, options, auth) { + var deferred = this.$q.defer(); + var requestData = { + jsonrpc: '2.0', + method: method, + params: params, + id: 1 + }; + + if (auth === "") { + // Reject immediately if not authenticated + deferred.reject({data: "Not authorised."}); + return deferred.promise; + } else if (auth) { + // Set auth parameter only if it needed + requestData.auth = auth; + } + + var requestOptions = { + method: 'POST', + headers: { + 'Content-Type': 'application/json' + }, + url: api_url, + data: requestData + }; + + // Set request options for basic auth + if (options.basicAuth || options.withCredentials) { + requestOptions.withCredentials = true; + } + if (options.basicAuth) { + requestOptions.headers.Authorization = options.basicAuth; + } + + this.backendSrv.datasourceRequest(requestOptions).then(function (response) { + // General connection issues + if (!response.data) { + deferred.reject(response); + } + + // Handle Zabbix API errors + else if (response.data.error) { + deferred.reject(response.data.error); + } + + deferred.resolve(response.data.result); + }); + return deferred.promise; + } + + /** + * Get authentication token. + * @return {string} auth token + */ + login(api_url, username, password, options) { + var params = { + user: username, + password: password + }; + return this.request(api_url, 'user.login', params, options, null); + } + + /** + * Get Zabbix API version + * Matches the version of Zabbix starting from Zabbix 2.0.4 + */ + getVersion(api_url, options) { + return this.request(api_url, 'apiinfo.version', [], options); + } +} + +// Define zabbix API exception type +function ZabbixException(error) { + this.code = error.code; + this.errorType = error.message; + this.message = error.data; +} + +ZabbixException.prototype.toString = function() { + return this.errorType + ": " + this.message; +}; + +angular + .module('grafana.services') + .service('zabbixAPICoreService', ZabbixAPICoreService); diff --git a/src/datasource-zabbix/zabbixAPIService.js b/src/datasource-zabbix/zabbixAPIService.js index 03f7789db..d2f89f7b2 100644 --- a/src/datasource-zabbix/zabbixAPIService.js +++ b/src/datasource-zabbix/zabbixAPIService.js @@ -1,103 +1,371 @@ -/** - * General Zabbix API methods - */ +import angular from 'angular'; +import _ from 'lodash'; +import './zabbixAPICoreService'; -define([ - 'angular', -], -function (angular) { - 'use strict'; +/** @ngInject */ +function ZabbixAPIService($q, alertSrv, zabbixAPICoreService) { - var module = angular.module('grafana.services'); + /** + * Zabbix API Wrapper. + * Creates Zabbix API instance with given parameters (url, credentials and other). + * Wraps API calls and provides high-level methods. + */ + class ZabbixAPI { - module.service('ZabbixAPIService', function($q, backendSrv) { + constructor(api_url, username, password, basicAuth, withCredentials) { + this.url = api_url; + this.username = username; + this.password = password; + this.auth = ""; + + this.requestOptions = { + basicAuth: basicAuth, + withCredentials: withCredentials + }; + + this.loginPromise = null; + + this.$q = $q; + this.alertSrv = alertSrv; + this.zabbixAPICore = zabbixAPICoreService; + + this.getTrend = this.getTrend_ZBXNEXT1193; + //getTrend = getTrend_30; + } + + ////////////////////////// + // Core method wrappers // + ////////////////////////// + + request(method, params) { + var self = this; + + return this.zabbixAPICore.request(this.url, method, params, this.requestOptions, this.auth) + .then(function(result) { + return result; + }, + // Handle API errors + function(error) { + if (isNotAuthorized(error.data)) { + return self.loginOnce().then( + function() { + return self.request(method, params); + }, + // Handle user.login method errors + function(error) { + self.alertAPIError(error.data); + }); + } + }); + } + + alertAPIError(message) { + this.alertSrv.set( + "Zabbix API Error", + message, + 'error' + ); + } /** - * Request data from Zabbix API - * @return {object} response.result + * When API unauthenticated or auth token expired each request produce login() + * call. But auth token is common to all requests. This function wraps login() method + * and call it once. If login() already called just wait for it (return its promise). + * @return login promise */ - this.request = function(api_url, method, params, options, auth) { - var deferred = $q.defer(); - var requestData = { - jsonrpc: '2.0', - method: method, - params: params, - id: 1 + loginOnce() { + var self = this; + var deferred = this.$q.defer(); + if (!self.loginPromise) { + self.loginPromise = deferred.promise; + self.login().then( + function(auth) { + self.loginPromise = null; + self.auth = auth; + deferred.resolve(auth); + }, + function(error) { + self.loginPromise = null; + deferred.reject(error); + } + ); + } else { + return self.loginPromise; + } + return deferred.promise; + } + + /** + * Get authentication token. + */ + login() { + return this.zabbixAPICore.login(this.url, this.username, this.password, this.requestOptions); + } + + /** + * Get Zabbix API version + */ + getVersion() { + return this.zabbixAPICore.getVersion(this.url, this.requestOptions); + } + + //////////////////////////////// + // Zabbix API method wrappers // + //////////////////////////////// + + getGroups() { + var params = { + output: ['name'], + sortfield: 'name', + selectHosts: [] }; - if (auth === "") { - // Reject immediately if not authenticated - deferred.reject({data: "Not authorised."}); - return deferred.promise; - } else if (auth) { - // Set auth parameter only if it needed - requestData.auth = auth; - } + return this.request('hostgroup.get', params); + } - var requestOptions = { - method: 'POST', - headers: { - 'Content-Type': 'application/json' - }, - url: api_url, - data: requestData + getHosts() { + var params = { + output: ['name', 'host'], + sortfield: 'name', + selectGroups: [] }; - // Set request options for basic auth - if (options.basicAuth || options.withCredentials) { - requestOptions.withCredentials = true; - } - if (options.basicAuth) { - requestOptions.headers.Authorization = options.basicAuth; - } + return this.request('host.get', params); + } - backendSrv.datasourceRequest(requestOptions).then(function (response) { - // General connection issues - if (!response.data) { - deferred.reject(response); - } + getApplications() { + var params = { + output: ['name'], + sortfield: 'name', - // Handle Zabbix API errors - else if (response.data.error) { - deferred.reject(response.data.error); - } + // Hack for supporting different apis (2.2 vs 2.4 vs 3.0) + selectHost: [], + selectHosts: [] + }; - deferred.resolve(response.data.result); - }); - return deferred.promise; - }; + return this.request('application.get', params); + } + + getItems() { + var params = { + output: [ + 'name', 'key_', + 'value_type', + 'hostid', + 'status', + 'state' + ], + sortfield: 'name', + selectApplications: [] + }; + + return this.request('item.get', params); + } /** - * Get authentication token. - * @return {string} auth token + * Get Hosts list with host's items. + * @return {[type]} [description] */ - this.login = function(api_url, username, password, options) { + getHostsExtend() { var params = { - user: username, - password: password + output: ['name', 'host'], + sortfield: 'name', + selectGroups: [], + selectItems: [ + 'name', 'key_', + 'value_type', + 'hostid', + 'status', + 'state' + ] }; - return this.request(api_url, 'user.login', params, options, null); - }; + + return this.request('host.get', params); + } + + getLastValue(itemid) { + var params = { + output: ['lastvalue'], + itemids: itemid + }; + return this.request('item.get', params).then(function(items) { + if (items.length) { + return items[0].lastvalue; + } else { + return null; + } + }); + } /** - * Get Zabbix API version - * Matches the version of Zabbix starting from Zabbix 2.0.4 + * Perform history query from Zabbix API + * + * @param {Array} items Array of Zabbix item objects + * @param {Number} time_from Time in seconds + * @param {Number} time_till Time in seconds + * @return {Array} Array of Zabbix history objects */ - this.getVersion = function(api_url, options) { - return this.request(api_url, 'apiinfo.version', [], options); - }; + getHistory(items, time_from, time_till) { + var self = this; + + // Group items by value type + var grouped_items = _.groupBy(items, 'value_type'); + + // Perform request for each value type + return this.$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: time_from + }; - }); + // Relative queries (e.g. last hour) don't include an end time + if (time_till) { + params.time_till = time_till; + } + + return self.request('history.get', params); + })).then(_.flatten); + } + + /** + * Perform trends query from Zabbix API + * Use trends api extension from ZBXNEXT-1193 patch. + * + * @param {Array} items Array of Zabbix item objects + * @param {Number} time_from Time in seconds + * @param {Number} time_till Time in seconds + * @return {Array} Array of Zabbix trend objects + */ + getTrend_ZBXNEXT1193(items, time_from, time_till) { + var self = this; + + // Group items by value type + var grouped_items = _.groupBy(items, 'value_type'); + + // Perform request for each value type + return this.$q.all(_.map(grouped_items, function (items, value_type) { + var itemids = _.map(items, 'itemid'); + var params = { + output: 'extend', + trend: value_type, + itemids: itemids, + sortfield: 'clock', + sortorder: 'ASC', + time_from: time_from + }; + + // Relative queries (e.g. last hour) don't include an end time + if (time_till) { + params.time_till = time_till; + } + + return self.request('trend.get', params); + })).then(_.flatten); + } + + getTrend_30(items, time_from, time_till, value_type) { + var self = this; + var itemids = _.map(items, 'itemid'); + + var params = { + output: ["itemid", + "clock", + value_type + ], + itemids: itemids, + time_from: time_from + }; + + // Relative queries (e.g. last hour) don't include an end time + if (time_till) { + params.time_till = time_till; + } + + return self.request('trend.get', params); + } + + getITService(/* optional */ serviceids) { + var params = { + output: 'extend', + serviceids: serviceids + }; + return this.request('service.get', params); + } + + getSLA(serviceids, from, to) { + var params = { + serviceids: serviceids, + intervals: [{ + from: from, + to: to + }] + }; + return this.request('service.getsla', params); + } + + getTriggers(groupids, hostids, applicationids, showEvents) { + var params = { + output: 'extend', + groupids: groupids, + hostids: hostids, + applicationids: applicationids, + expandDescription: true, + expandData: true, + monitored: true, + skipDependent: true, + //only_true: true, + filter: { + value: 1 + }, + selectGroups: ['name'], + selectHosts: ['name'], + selectItems: ['name', 'key_', 'lastvalue'], + selectLastEvent: 'extend' + }; + + if (showEvents) { + params.filter.value = showEvents; + } + + return this.request('trigger.get', params); + } + + getAcknowledges(eventids) { + var params = { + output: 'extend', + eventids: eventids, + preservekeys: true, + select_acknowledges: 'extend', + sortfield: 'clock', + sortorder: 'DESC' + }; + + return this.request('event.get', params) + .then(function (events) { + return _.filter(events, function(event) { + return event.acknowledges.length; + }); + }); + } - // Define zabbix API exception type - function ZabbixException(error) { - this.code = error.code; - this.errorType = error.message; - this.message = error.data; } - ZabbixException.prototype.toString = function() { - return this.errorType + ": " + this.message; - }; + return ZabbixAPI; +} + +function isNotAuthorized(message) { + return ( + message === "Session terminated, re-login, please." || + message === "Not authorised." || + message === "Not authorized." + ); +} -}); \ No newline at end of file +angular + .module('grafana.services') + .factory('zabbixAPIService', ZabbixAPIService); From def3f9115d2f2cfdfbd5354b4c1a249917a69d06 Mon Sep 17 00:00:00 2001 From: Alexander Zobnin Date: Wed, 16 Mar 2016 21:00:11 +0300 Subject: [PATCH 146/205] Removed unused files. --- src/datasource-zabbix/directives.js | 21 -- src/datasource-zabbix/helperFunctions.js | 311 ----------------------- 2 files changed, 332 deletions(-) delete mode 100644 src/datasource-zabbix/directives.js delete mode 100644 src/datasource-zabbix/helperFunctions.js diff --git a/src/datasource-zabbix/directives.js b/src/datasource-zabbix/directives.js deleted file mode 100644 index 8d274f532..000000000 --- a/src/datasource-zabbix/directives.js +++ /dev/null @@ -1,21 +0,0 @@ -define([ - 'angular' - ], - function (angular) { - 'use strict'; - - var module = angular.module('grafana.directives'); - - module.directive('metricQueryEditorZabbix', function() { - return {controller: 'ZabbixAPIQueryCtrl', templateUrl: 'public/plugins/zabbix/partials/query.editor.html'}; - }); - - module.directive('metricQueryOptionsZabbix', function() { - return {templateUrl: 'public/plugins/zabbix/partials/query.options.html'}; - }); - - module.directive('annotationsQueryEditorZabbix', function() { - return {templateUrl: 'public/plugins/zabbix/partials/annotations.editor.html'}; - }); - - }); diff --git a/src/datasource-zabbix/helperFunctions.js b/src/datasource-zabbix/helperFunctions.js deleted file mode 100644 index d844d9238..000000000 --- a/src/datasource-zabbix/helperFunctions.js +++ /dev/null @@ -1,311 +0,0 @@ -define([ - 'angular', - 'lodash' -], -function (angular, _) { - 'use strict'; - - var module = angular.module('grafana.services'); - - module.service('zabbixHelperSrv', function($q) { - var self = this; - - /** - * Convert Zabbix API history.get response to Grafana format - * - * @param {Array} items Array of Zabbix Items - * @param alias - * @param scale - * @param {Array} history Array of Zabbix History - * - * @return {Array} Array of timeseries in Grafana format - * { - * target: "Metric name", - * datapoints: [[, ], ...] - * } - */ - this.handleHistoryResponse = function(items, alias, scale, history) { - /** - * Response should be in the format: - * data: [ - * { - * target: "Metric name", - * datapoints: [[, ], ...] - * }, - * { - * target: "Metric name", - * datapoints: [[, ], ...] - * }, - * ] - */ - - // Group items and history by itemid - var indexed_items = _.indexBy(items, 'itemid'); - var grouped_history = _.groupBy(history, 'itemid'); - - var self = this; - return $q.when(_.map(grouped_history, function (history, itemid) { - var item = indexed_items[itemid]; - return { - target: (item.host ? item.host + ': ' : '') - + (alias ? alias : self.expandItemName(item)), - datapoints: _.map(history, function (p) { - - // Value must be a number for properly work - var value = Number(p.value); - - // Apply scale - if (scale) { - value *= scale; - } - return [value, p.clock * 1000]; - }) - }; - })).then(function (result) { - return _.sortBy(result, 'target'); - }); - }; - - /** - * Convert Zabbix API trends.get response to Grafana format - * - * @param {Array} items Array of Zabbix Items - * @param alias - * @param scale - * @param {string} points Point value to return: min, max or avg - * @param {Array} trends Array of Zabbix Trends - * - * @return {Array} Array of timeseries in Grafana format - * { - * target: "Metric name", - * datapoints: [[, ], ...] - * } - */ - this.handleTrendResponse = function (items, alias, scale, points, trends) { - - // Group items and trends by itemid - var indexed_items = _.indexBy(items, 'itemid'); - var grouped_trends = _.groupBy(trends, 'itemid'); - - var self = this; - return $q.when(_.map(grouped_trends, function (trends, itemid) { - var item = indexed_items[itemid]; - return { - target: (item.hosts ? item.hosts[0].name+': ' : '') - + (alias ? alias : self.expandItemName(item)), - datapoints: _.map(trends, function (p) { - - // Value must be a number for properly work - var value; - if (points === "min") { - value = Number(p.value_min); - } - else if (points === "max") { - value = Number(p.value_max); - } - else { - value = Number(p.value_avg); - } - - // Apply scale - if (scale) { - value *= scale; - } - return [value, p.clock * 1000]; - }) - }; - })).then(function (result) { - return _.sortBy(result, 'target'); - }); - }; - - /** - * Convert Zabbix API service.getsla response to Grafana format - * - * @param itservice - * @param slaProperty - * @param slaObject - * @returns {{target: *, datapoints: *[]}} - */ - this.handleSLAResponse = function (itservice, slaProperty, slaObject) { - var targetSLA = slaObject[itservice.serviceid].sla[0]; - if (slaProperty.property === 'status') { - var targetStatus = parseInt(slaObject[itservice.serviceid].status); - return { - target: itservice.name + ' ' + slaProperty.name, - datapoints: [ - [targetStatus, targetSLA.to * 1000] - ] - }; - } else { - return { - target: itservice.name + ' ' + slaProperty.name, - datapoints: [ - [targetSLA[slaProperty.property], targetSLA.from * 1000], - [targetSLA[slaProperty.property], targetSLA.to * 1000] - ] - }; - } - }; - - /** - * Expand item parameters, for example: - * CPU $2 time ($3) --> CPU system time (avg1) - * - * @param item: zabbix api item object - * @return {string} expanded item name (string) - */ - this.expandItemName = function(item) { - var name = item.name; - var key = item.key_; - - // extract params from key: - // "system.cpu.util[,system,avg1]" --> ["", "system", "avg1"] - var key_params = key.substring(key.indexOf('[') + 1, key.lastIndexOf(']')).split(','); - - // replace item parameters - for (var i = key_params.length; i >= 1; i--) { - name = name.replace('$' + i, key_params[i - 1]); - } - return name; - }; - - /** - * Convert multiple mettrics to array - * "{metric1,metcic2,...,metricN}" --> [metric1, metcic2,..., metricN] - * - * @param {string} metrics "{metric1,metcic2,...,metricN}" - * @return {Array} [metric1, metcic2,..., metricN] - */ - this.splitMetrics = function(metrics) { - var remove_brackets_pattern = /^{|}$/g; - var metric_split_pattern = /,(?!\s)/g; - return metrics.replace(remove_brackets_pattern, '').split(metric_split_pattern); - }; - - /** - * Convert Date object to local time in format - * YYYY-MM-DD HH:mm:ss - * - * @param {Date} date Date object - * @return {string} formatted local time YYYY-MM-DD HH:mm:ss - */ - this.getShortTime = function(date) { - var MM = date.getMonth() < 10 ? '0' + date.getMonth() : date.getMonth(); - var DD = date.getDate() < 10 ? '0' + date.getDate() : date.getDate(); - var HH = date.getHours() < 10 ? '0' + date.getHours() : date.getHours(); - var mm = date.getMinutes() < 10 ? '0' + date.getMinutes() : date.getMinutes(); - var ss = date.getSeconds() < 10 ? '0' + date.getSeconds() : date.getSeconds(); - return date.getFullYear() + '-' + MM + '-' + DD + ' ' + HH + ':' + mm + ':' + ss; - }; - - /** - * Format acknowledges. - * - * @param {array} acknowledges array of Zabbix acknowledge objects - * @return {string} HTML-formatted table - */ - this.formatAcknowledges = function(acknowledges) { - if (acknowledges.length) { - var formatted_acknowledges = '

    Acknowledges:
    ' - + ''; - _.each(_.map(acknowledges, function (ack) { - var time = new Date(ack.clock * 1000); - return ''; - }), function (ack) { - formatted_acknowledges = formatted_acknowledges.concat(ack); - }); - formatted_acknowledges = formatted_acknowledges.concat('
    TimeUserComments
    ' + self.getShortTime(time) + '' + ack.alias - + ' (' + ack.name + ' ' + ack.surname + ')' + '' + ack.message + '
    '); - return formatted_acknowledges; - } else { - return ''; - } - }; - - /** - * Downsample datapoints series - * - * @param {Object[]} datapoints [[, ], ...] - * @param {integer} time_to Panel time to - * @param {integer} ms_interval Interval in milliseconds for grouping datapoints - * @param {string} func Value to return: min, max or avg - * @return {Object[]} [[, ], ...] - */ - this.downsampleSeries = function(datapoints, time_to, ms_interval, func) { - var downsampledSeries = []; - var timeWindow = { - from: time_to * 1000 - ms_interval, - to: time_to * 1000 - }; - - var points_sum = 0; - var points_num = 0; - var value_avg = 0; - var frame = []; - - for (var i = datapoints.length - 1; i >= 0; i -= 1) { - if (timeWindow.from < datapoints[i][1] && datapoints[i][1] <= timeWindow.to) { - points_sum += datapoints[i][0]; - points_num++; - frame.push(datapoints[i][0]); - } - else { - value_avg = points_num ? points_sum / points_num : 0; - - if (func === "max") { - downsampledSeries.push([_.max(frame), timeWindow.to]); - } - else if (func === "min") { - downsampledSeries.push([_.min(frame), timeWindow.to]); - } - - // avg by default - else { - downsampledSeries.push([value_avg, timeWindow.to]); - } - - // Shift time window - timeWindow.to = timeWindow.from; - timeWindow.from -= ms_interval; - - points_sum = 0; - points_num = 0; - frame = []; - - // Process point again - i++; - } - } - return downsampledSeries.reverse(); - }; - - /** - * Convert event age from Unix format (milliseconds sins 1970) - * to Zabbix format (like at Last 20 issues panel). - * @param {Date} AgeUnix time in Unix format - * @return {string} Formatted time - */ - this.toZabbixAgeFormat = function(ageUnix) { - var age = new Date(+ageUnix); - var ageZabbix = age.getSeconds() + 's'; - if (age.getMinutes()) { - ageZabbix = age.getMinutes() + 'm ' + ageZabbix; - } - if (age.getHours()) { - ageZabbix = age.getHours() + 'h ' + ageZabbix; - } - if (age.getDate() - 1) { - ageZabbix = age.getDate() - 1 + 'd ' + ageZabbix; - } - if (age.getMonth()) { - ageZabbix = age.getMonth() + 'M ' + ageZabbix; - } - if (age.getYear() - 70) { - ageZabbix = age.getYear() -70 + 'y ' + ageZabbix; - } - return ageZabbix; - }; - - }); -}); \ No newline at end of file From 816870136c14e053b46fc795b6bb5508100038e4 Mon Sep 17 00:00:00 2001 From: Alexander Zobnin Date: Wed, 16 Mar 2016 21:15:46 +0300 Subject: [PATCH 147/205] Modules renamed using John Papa Angular Style Guide. --- Gruntfile.js | 23 +- ...on.js => add-metric-function.directive.js} | 0 ...ngService.js => dataProcessing.service.js} | 2 +- src/datasource-zabbix/datasource.js | 18 +- ...js => metric-function-editor.directive.js} | 0 src/datasource-zabbix/metricFunctions.js | 254 +++++++++--------- src/datasource-zabbix/module.js | 22 +- .../{queryCtrl.js => query.controller.js} | 14 +- ...Processor.js => queryProcessor.service.js} | 23 +- ...bbixAPIService.js => zabbixAPI.service.js} | 2 +- ...oreService.js => zabbixAPICore.service.js} | 0 ...{zabbixCache.js => zabbixCache.service.js} | 0 12 files changed, 183 insertions(+), 175 deletions(-) rename src/datasource-zabbix/{addMetricFunction.js => add-metric-function.directive.js} (100%) rename src/datasource-zabbix/{dataProcessingService.js => dataProcessing.service.js} (99%) rename src/datasource-zabbix/{metricFunctionEditor.js => metric-function-editor.directive.js} (100%) rename src/datasource-zabbix/{queryCtrl.js => query.controller.js} (96%) rename src/datasource-zabbix/{queryProcessor.js => queryProcessor.service.js} (94%) rename src/datasource-zabbix/{zabbixAPIService.js => zabbixAPI.service.js} (99%) rename src/datasource-zabbix/{zabbixAPICoreService.js => zabbixAPICore.service.js} (100%) rename src/datasource-zabbix/{zabbixCache.js => zabbixCache.service.js} (100%) diff --git a/Gruntfile.js b/Gruntfile.js index 4b8522287..9b20b764c 100644 --- a/Gruntfile.js +++ b/Gruntfile.js @@ -17,18 +17,19 @@ module.exports = function(grunt) { '**/*', '!**/datasource.js', '!**/module.js', - '!**/queryCtrl.js', + '!**/query.controller.js', '!**/utils.js', - '!**/zabbixAPICoreService.js', - '!**/zabbixAPIService.js', + '!**/zabbixAPICore.service.js', + '!**/zabbixAPI.service.js', + '!**/metricFunctions.js', '!**/*.scss' ], - dest: 'dist/src' + dest: 'dist/' }, pluginDef: { expand: true, src: ['plugin.json', 'README.md'], - dest: 'dist', + dest: 'dist/', } }, @@ -42,7 +43,7 @@ module.exports = function(grunt) { babel: { options: { - sourceMap: true, + sourceMap: false, presets: ["es2015"], plugins: ['transform-es2015-modules-systemjs', "transform-es2015-for-of"], }, @@ -53,13 +54,13 @@ module.exports = function(grunt) { src: [ '**/**/module.js', '**/**/datasource.js', - '**/**/queryCtrl.js', + '**/**/query.controller.js', '**/**/utils.js', - '**/**/zabbixAPICoreService.js', - '**/**/zabbixAPIService.js', + '**/**/zabbixAPICore.service.js', + '**/**/zabbixAPI.service.js', + '**/**/metricFunctions.js' ], - dest: 'dist/src', - ext:'.js' + dest: 'dist/' }] }, }, diff --git a/src/datasource-zabbix/addMetricFunction.js b/src/datasource-zabbix/add-metric-function.directive.js similarity index 100% rename from src/datasource-zabbix/addMetricFunction.js rename to src/datasource-zabbix/add-metric-function.directive.js diff --git a/src/datasource-zabbix/dataProcessingService.js b/src/datasource-zabbix/dataProcessing.service.js similarity index 99% rename from src/datasource-zabbix/dataProcessingService.js rename to src/datasource-zabbix/dataProcessing.service.js index 5bf73a320..b7958ea0b 100644 --- a/src/datasource-zabbix/dataProcessingService.js +++ b/src/datasource-zabbix/dataProcessing.service.js @@ -238,4 +238,4 @@ function (angular, _, moment, utils) { }; }); -}); \ No newline at end of file +}); diff --git a/src/datasource-zabbix/datasource.js b/src/datasource-zabbix/datasource.js index 77eddbaa5..dce090ae7 100644 --- a/src/datasource-zabbix/datasource.js +++ b/src/datasource-zabbix/datasource.js @@ -3,16 +3,15 @@ import _ from 'lodash'; import * as dateMath from 'app/core/utils/datemath'; import * as utils from './utils'; import metricFunctions from './metricFunctions'; -import {zabbixHelperSrv} from './helperFunctions'; -import './zabbixAPIService'; -import {ZabbixCachingProxy} from './zabbixCache'; -import {QueryProcessor} from './queryProcessor'; -import {DataProcessingService} from './dataProcessingService'; +import './zabbixAPI.service.js'; +import './zabbixCache.service.js'; +import './queryProcessor.service.js'; +import './dataProcessing.service'; export class ZabbixAPIDatasource { /** @ngInject */ - constructor(instanceSettings, $q, templateSrv, alertSrv, zabbixAPIService, ZabbixCachingProxy, QueryProcessor, zabbixHelperSrv, DataProcessingService) { + constructor(instanceSettings, $q, templateSrv, alertSrv, zabbixAPIService, ZabbixCachingProxy, QueryProcessor, DataProcessingService) { // General data source settings this.name = instanceSettings.name; @@ -46,7 +45,6 @@ export class ZabbixAPIDatasource { this.q = $q; this.templateSrv = templateSrv; this.alertSrv = alertSrv; - this.zabbixHelperSrv = zabbixHelperSrv; this.DataProcessingService = DataProcessingService; console.log(this.zabbixCache); @@ -243,13 +241,13 @@ export class ZabbixAPIDatasource { return []; } else { return this.zabbixAPI.getSLA(target.itservice.serviceid, from, to) - .then(_.bind(zabbixHelperSrv.handleSLAResponse, zabbixHelperSrv, target.itservice, target.slaProperty)); + .then(slaObject => { + return self.queryProcessor.handleSLAResponse(target.itservice, target.slaProperty, slaObject); + }); } } }, this); - var self = this; - // Data for panel (all targets) return this.q.all(_.flatten(promises)) .then(_.flatten) diff --git a/src/datasource-zabbix/metricFunctionEditor.js b/src/datasource-zabbix/metric-function-editor.directive.js similarity index 100% rename from src/datasource-zabbix/metricFunctionEditor.js rename to src/datasource-zabbix/metric-function-editor.directive.js diff --git a/src/datasource-zabbix/metricFunctions.js b/src/datasource-zabbix/metricFunctions.js index c0cc2b36e..acfef29c0 100644 --- a/src/datasource-zabbix/metricFunctions.js +++ b/src/datasource-zabbix/metricFunctions.js @@ -1,105 +1,102 @@ -define([ - 'lodash', - 'jquery' -], -function (_, $) { - 'use strict'; - - var index = []; - var categories = { - Transform: [], - Aggregate: [], - Trends: [], - Alias: [] - }; - - function addFuncDef(funcDef) { - funcDef.params = funcDef.params || []; - funcDef.defaultParams = funcDef.defaultParams || []; - - if (funcDef.category) { - categories[funcDef.category].push(funcDef); - } - index[funcDef.name] = funcDef; - index[funcDef.shortName || funcDef.name] = funcDef; +import _ from 'lodash'; +import $ from 'jquery'; + +var index = []; +var categories = { + Transform: [], + Aggregate: [], + Trends: [], + Alias: [] +}; + +function addFuncDef(funcDef) { + funcDef.params = funcDef.params || []; + funcDef.defaultParams = funcDef.defaultParams || []; + + if (funcDef.category) { + categories[funcDef.category].push(funcDef); } + index[funcDef.name] = funcDef; + index[funcDef.shortName || funcDef.name] = funcDef; +} + +addFuncDef({ + name: 'groupBy', + category: 'Transform', + params: [ + { name: 'interval', type: 'string'}, + { name: 'function', type: 'string', options: ['avg', 'min', 'max', 'median'] } + ], + defaultParams: ['1m', 'avg'], +}); + +addFuncDef({ + name: 'sumSeries', + category: 'Aggregate', + params: [], + defaultParams: [], +}); + +addFuncDef({ + name: 'median', + category: 'Aggregate', + params: [ + { name: 'interval', type: 'string'} + ], + defaultParams: ['1m'], +}); + +addFuncDef({ + name: 'average', + category: 'Aggregate', + params: [ + { name: 'interval', type: 'string' } + ], + defaultParams: ['1m'], +}); - addFuncDef({ - name: 'groupBy', - category: 'Transform', - params: [ - { name: 'interval', type: 'string'}, - { name: 'function', type: 'string', options: ['avg', 'min', 'max', 'median'] } - ], - defaultParams: ['1m', 'avg'], - }); - - addFuncDef({ - name: 'sumSeries', - category: 'Aggregate', - params: [], - defaultParams: [], - }); - - addFuncDef({ - name: 'median', - category: 'Aggregate', - params: [ - { name: 'interval', type: 'string'} - ], - defaultParams: ['1m'], - }); - - addFuncDef({ - name: 'average', - category: 'Aggregate', - params: [ - { name: 'interval', type: 'string' } - ], - defaultParams: ['1m'], - }); - - addFuncDef({ - name: 'min', - category: 'Aggregate', - params: [ - { name: 'interval', type: 'string' } - ], - defaultParams: ['1m'], - }); - - addFuncDef({ - name: 'max', - category: 'Aggregate', - params: [ - { name: 'interval', type: 'string' } - ], - defaultParams: ['1m'], - }); - - addFuncDef({ - name: 'trendValue', - category: 'Trends', - params: [ - { name: 'type', type: 'string', options: ['avg', 'min', 'max'] } - ], - defaultParams: ['avg'], - }); - - addFuncDef({ - name: 'setAlias', - category: 'Alias', - params: [ - { name: 'alias', type: 'string'} - ], - defaultParams: [], - }); - - _.each(categories, function(funcList, catName) { - categories[catName] = _.sortBy(funcList, 'name'); - }); - - function FuncInstance(funcDef, params) { +addFuncDef({ + name: 'min', + category: 'Aggregate', + params: [ + { name: 'interval', type: 'string' } + ], + defaultParams: ['1m'], +}); + +addFuncDef({ + name: 'max', + category: 'Aggregate', + params: [ + { name: 'interval', type: 'string' } + ], + defaultParams: ['1m'], +}); + +addFuncDef({ + name: 'trendValue', + category: 'Trends', + params: [ + { name: 'type', type: 'string', options: ['avg', 'min', 'max'] } + ], + defaultParams: ['avg'], +}); + +addFuncDef({ + name: 'setAlias', + category: 'Alias', + params: [ + { name: 'alias', type: 'string'} + ], + defaultParams: [], +}); + +_.each(categories, function(funcList, catName) { + categories[catName] = _.sortBy(funcList, 'name'); +}); + +class FuncInstance { + constructor(funcDef, params) { this.def = funcDef; if (params) { @@ -113,7 +110,7 @@ function (_, $) { this.updateText(); } - FuncInstance.prototype.bindFunction = function(metricFunctions) { + bindFunction(metricFunctions) { var func = metricFunctions[this.def.name]; if (func) { @@ -126,9 +123,9 @@ function (_, $) { } else { throw { message: 'Method not found ' + this.def.name }; } - }; + } - FuncInstance.prototype.render = function(metricExp) { + render(metricExp) { var str = this.def.name + '('; var parameters = _.map(this.params, function(value, index) { @@ -149,17 +146,17 @@ function (_, $) { } return str + parameters.join(', ') + ')'; - }; + } - FuncInstance.prototype._hasMultipleParamsInString = function(strValue, index) { + _hasMultipleParamsInString(strValue, index) { if (strValue.indexOf(',') === -1) { return false; } return this.def.params[index + 1] && this.def.params[index + 1].optional; - }; + } - FuncInstance.prototype.updateParam = function(strValue, index) { + updateParam(strValue, index) { // handle optional parameters // if string contains ',' and next param is optional, split and update both if (this._hasMultipleParamsInString(strValue, index)) { @@ -177,9 +174,9 @@ function (_, $) { } this.updateText(); - }; + } - FuncInstance.prototype.updateText = function () { + updateText() { if (this.params.length === 0) { this.text = this.def.name + '()'; return; @@ -189,26 +186,25 @@ function (_, $) { text += this.params.join(', '); text += ')'; this.text = text; - }; - - return { - createFuncInstance: function(funcDef, params) { - if (_.isString(funcDef)) { - if (!index[funcDef]) { - throw { message: 'Method not found ' + name }; - } - funcDef = index[funcDef]; - } - return new FuncInstance(funcDef, params); - }, - - getFuncDef: function(name) { - return index[name]; - }, + } +} - getCategories: function() { - return categories; +export default { + createFuncInstance: function(funcDef, params) { + if (_.isString(funcDef)) { + if (!index[funcDef]) { + throw { message: 'Method not found ' + name }; + } + funcDef = index[funcDef]; } - }; + return new FuncInstance(funcDef, params); + }, -}); + getFuncDef: function(name) { + return index[name]; + }, + + getCategories: function() { + return categories; + } +}; diff --git a/src/datasource-zabbix/module.js b/src/datasource-zabbix/module.js index d94721401..a7cc1d9ce 100644 --- a/src/datasource-zabbix/module.js +++ b/src/datasource-zabbix/module.js @@ -1,19 +1,19 @@ import {ZabbixAPIDatasource} from './datasource'; -import {ZabbixQueryCtrl} from './queryCtrl'; +import {ZabbixQueryController} from './query.controller'; -class ZabbixConfigCtrl {} -ZabbixConfigCtrl.templateUrl = 'partials/config.html'; +class ZabbixConfigController {} +ZabbixConfigController.templateUrl = 'partials/config.html'; -class ZabbixQueryOptionsCtrl {} -ZabbixQueryOptionsCtrl.templateUrl = 'partials/query.options.html'; +class ZabbixQueryOptionsController {} +ZabbixQueryOptionsController.templateUrl = 'partials/query.options.html'; -class ZabbixAnnotationsQueryCtrl {} -ZabbixAnnotationsQueryCtrl.templateUrl = 'partials/annotations.editor.html'; +class ZabbixAnnotationsQueryController {} +ZabbixAnnotationsQueryController.templateUrl = 'partials/annotations.editor.html'; export { ZabbixAPIDatasource as Datasource, - ZabbixQueryCtrl as QueryCtrl, - ZabbixConfigCtrl as ConfigCtrl, - ZabbixQueryOptionsCtrl as QueryOptionsCtrl, - ZabbixAnnotationsQueryCtrl as AnnotationsQueryCtrl + ZabbixConfigController as ConfigCtrl, + ZabbixQueryController as QueryCtrl, + ZabbixQueryOptionsController as QueryOptionsCtrl, + ZabbixAnnotationsQueryController as AnnotationsQueryCtrl }; diff --git a/src/datasource-zabbix/queryCtrl.js b/src/datasource-zabbix/query.controller.js similarity index 96% rename from src/datasource-zabbix/queryCtrl.js rename to src/datasource-zabbix/query.controller.js index 935fc7a47..25f8ac9a2 100644 --- a/src/datasource-zabbix/queryCtrl.js +++ b/src/datasource-zabbix/query.controller.js @@ -1,17 +1,9 @@ -/*define([ - 'app/plugins/sdk', - 'angular', - 'lodash', - './metricFunctions', - './utils' -],*/ - import {QueryCtrl} from 'app/plugins/sdk'; import _ from 'lodash'; import * as utils from './utils'; import metricFunctions from './metricFunctions'; -export class ZabbixQueryCtrl extends QueryCtrl { +export class ZabbixQueryController extends QueryCtrl { // ZabbixQueryCtrl constructor constructor($scope, $injector, $sce, $q, templateSrv) { @@ -229,9 +221,9 @@ export class ZabbixQueryCtrl extends QueryCtrl { } // Set templateUrl as static property -ZabbixQueryCtrl.templateUrl = 'partials/query.editor.html'; +ZabbixQueryController.templateUrl = 'partials/query.editor.html'; // Get list of metric names for bs-typeahead directive function getMetricNames(scope, metricList) { return _.uniq(_.map(scope.metric[metricList], 'name')); -} \ No newline at end of file +} diff --git a/src/datasource-zabbix/queryProcessor.js b/src/datasource-zabbix/queryProcessor.service.js similarity index 94% rename from src/datasource-zabbix/queryProcessor.js rename to src/datasource-zabbix/queryProcessor.service.js index c5a78ecad..ecedaeeaa 100644 --- a/src/datasource-zabbix/queryProcessor.js +++ b/src/datasource-zabbix/queryProcessor.service.js @@ -416,6 +416,27 @@ function (angular, _, utils) { return this.convertHistory(history, addHostName, convertPointCallback); }; + this.handleSLAResponse = function (itservice, slaProperty, slaObject) { + var targetSLA = slaObject[itservice.serviceid].sla[0]; + if (slaProperty.property === 'status') { + var targetStatus = parseInt(slaObject[itservice.serviceid].status); + return { + target: itservice.name + ' ' + slaProperty.name, + datapoints: [ + [targetStatus, targetSLA.to * 1000] + ] + }; + } else { + return { + target: itservice.name + ' ' + slaProperty.name, + datapoints: [ + [targetSLA[slaProperty.property], targetSLA.from * 1000], + [targetSLA[slaProperty.property], targetSLA.to * 1000] + ] + }; + } + }; + function convertHistoryPoint(point) { // Value must be a number for properly work return [ @@ -450,4 +471,4 @@ function (angular, _, utils) { return QueryProcessor; }); -}); \ No newline at end of file +}); diff --git a/src/datasource-zabbix/zabbixAPIService.js b/src/datasource-zabbix/zabbixAPI.service.js similarity index 99% rename from src/datasource-zabbix/zabbixAPIService.js rename to src/datasource-zabbix/zabbixAPI.service.js index d2f89f7b2..ca74d1161 100644 --- a/src/datasource-zabbix/zabbixAPIService.js +++ b/src/datasource-zabbix/zabbixAPI.service.js @@ -1,6 +1,6 @@ import angular from 'angular'; import _ from 'lodash'; -import './zabbixAPICoreService'; +import './zabbixAPICore.service'; /** @ngInject */ function ZabbixAPIService($q, alertSrv, zabbixAPICoreService) { diff --git a/src/datasource-zabbix/zabbixAPICoreService.js b/src/datasource-zabbix/zabbixAPICore.service.js similarity index 100% rename from src/datasource-zabbix/zabbixAPICoreService.js rename to src/datasource-zabbix/zabbixAPICore.service.js diff --git a/src/datasource-zabbix/zabbixCache.js b/src/datasource-zabbix/zabbixCache.service.js similarity index 100% rename from src/datasource-zabbix/zabbixCache.js rename to src/datasource-zabbix/zabbixCache.service.js From 17e85291275407db54a94e193cfe168bcf782dfc Mon Sep 17 00:00:00 2001 From: Alexander Zobnin Date: Wed, 16 Mar 2016 21:41:19 +0300 Subject: [PATCH 148/205] Fixed directives. --- src/datasource-zabbix/datasource.js | 2 +- src/datasource-zabbix/metricFunctions.js | 30 +++++++++++------------ src/datasource-zabbix/query.controller.js | 5 +++- 3 files changed, 19 insertions(+), 18 deletions(-) diff --git a/src/datasource-zabbix/datasource.js b/src/datasource-zabbix/datasource.js index dce090ae7..11183c78e 100644 --- a/src/datasource-zabbix/datasource.js +++ b/src/datasource-zabbix/datasource.js @@ -2,7 +2,7 @@ import _ from 'lodash'; import * as dateMath from 'app/core/utils/datemath'; import * as utils from './utils'; -import metricFunctions from './metricFunctions'; +import * as metricFunctions from './metricFunctions'; import './zabbixAPI.service.js'; import './zabbixCache.service.js'; import './queryProcessor.service.js'; diff --git a/src/datasource-zabbix/metricFunctions.js b/src/datasource-zabbix/metricFunctions.js index acfef29c0..aa0351ce7 100644 --- a/src/datasource-zabbix/metricFunctions.js +++ b/src/datasource-zabbix/metricFunctions.js @@ -189,22 +189,20 @@ class FuncInstance { } } -export default { - createFuncInstance: function(funcDef, params) { - if (_.isString(funcDef)) { - if (!index[funcDef]) { - throw { message: 'Method not found ' + name }; - } - funcDef = index[funcDef]; +export function createFuncInstance(funcDef, params) { + if (_.isString(funcDef)) { + if (!index[funcDef]) { + throw { message: 'Method not found ' + name }; } - return new FuncInstance(funcDef, params); - }, + funcDef = index[funcDef]; + } + return new FuncInstance(funcDef, params); +} - getFuncDef: function(name) { - return index[name]; - }, +export function getFuncDef(name) { + return index[name]; +} - getCategories: function() { - return categories; - } -}; +export function getCategories() { + return categories; +} diff --git a/src/datasource-zabbix/query.controller.js b/src/datasource-zabbix/query.controller.js index 25f8ac9a2..98354e23a 100644 --- a/src/datasource-zabbix/query.controller.js +++ b/src/datasource-zabbix/query.controller.js @@ -1,7 +1,10 @@ import {QueryCtrl} from 'app/plugins/sdk'; import _ from 'lodash'; import * as utils from './utils'; -import metricFunctions from './metricFunctions'; +import * as metricFunctions from './metricFunctions'; + +import './add-metric-function.directive'; +import './metric-function-editor.directive'; export class ZabbixQueryController extends QueryCtrl { From fbe7480c073101a5a0fd7bf2fda75a8913de1a35 Mon Sep 17 00:00:00 2001 From: Alexander Zobnin Date: Thu, 17 Mar 2016 22:36:22 +0300 Subject: [PATCH 149/205] Improved performance of metric filtering and building history query. --- Gruntfile.js | 4 +- .../queryProcessor.service.js | 127 ++++++++++++------ src/datasource-zabbix/zabbixAPI.service.js | 13 +- src/datasource-zabbix/zabbixCache.service.js | 71 +++++++--- 4 files changed, 149 insertions(+), 66 deletions(-) diff --git a/Gruntfile.js b/Gruntfile.js index 9b20b764c..672b82ddf 100644 --- a/Gruntfile.js +++ b/Gruntfile.js @@ -21,6 +21,7 @@ module.exports = function(grunt) { '!**/utils.js', '!**/zabbixAPICore.service.js', '!**/zabbixAPI.service.js', + //'!**/dataProcessing.service.js', '!**/metricFunctions.js', '!**/*.scss' ], @@ -43,7 +44,7 @@ module.exports = function(grunt) { babel: { options: { - sourceMap: false, + sourceMap: true, presets: ["es2015"], plugins: ['transform-es2015-modules-systemjs', "transform-es2015-for-of"], }, @@ -58,6 +59,7 @@ module.exports = function(grunt) { '**/**/utils.js', '**/**/zabbixAPICore.service.js', '**/**/zabbixAPI.service.js', + //'**/**/dataProcessing.service.js', '**/**/metricFunctions.js' ], dest: 'dist/' diff --git a/src/datasource-zabbix/queryProcessor.service.js b/src/datasource-zabbix/queryProcessor.service.js index ecedaeeaa..05f5cd165 100644 --- a/src/datasource-zabbix/queryProcessor.service.js +++ b/src/datasource-zabbix/queryProcessor.service.js @@ -70,7 +70,7 @@ function (angular, _, utils) { var hostids = _.flatten(_.map(groups, 'hosts')); if (hostids.length) { - return self.cache.getHostsExtend().then(function(hosts) { + return self.cache.getIndexedHosts().then(function(hosts) { return _.map(hostids, function(hostid) { return hosts[hostid]; }); @@ -121,65 +121,91 @@ function (angular, _, utils) { }); }; + /** + * Find group, host, app or item by given name. + * @param list list of groups, apps or other + * @param name visible name + * @return array with finded element or undefined + */ + function findByName(list, name) { + var finded = _.find(list, {'name': name}); + if (finded) { + return [finded]; + } else { + return undefined; + } + } + + function findByRegex(list, regex) { + var filterPattern = utils.buildRegex(regex); + return _.filter(list, function (zbx_obj) { + return filterPattern.test(zbx_obj.name); + }); + } + + function findByFilter(list, filter) { + if (utils.isRegex(filter)) { + return findByRegex(list, filter); + } else { + return findByName(list, filter); + } + } + + function getFromIndex(index, objids) { + return _.map(objids, function(id) { + return index[id]; + }); + } + this.filterItems = function (groupFilter, hostFilter, appFilter, itemType, showDisabledItems) { - var hosts = []; - var apps = []; - var items = []; + var hosts; + var apps; + var items; var promises = [ this.filterHosts(groupFilter), - this.filterApplications(groupFilter, hostFilter) + this.filterApplications(groupFilter, hostFilter), + this.cache.getIndexedHosts(), + this.cache.getIndexedApplications() ]; return $q.all(promises).then(function(results) { var hostList = results[0]; var applicationList = results[1]; + var idx_hosts = results[2]; + var idx_apps = results[3]; - // Filter hosts by regex - if (utils.isRegex(hostFilter)) { - var hostFilterPattern = utils.buildRegex(hostFilter); - hosts = _.filter(hostList, function (hostObj) { - return hostFilterPattern.test(hostObj.name); - }); - } else { - var findedHosts = _.find(hostList, {'name': hostFilter}); - if (findedHosts) { - hosts.push(findedHosts); - } else { - hosts = undefined; - } - } + // Filter hosts + hosts = findByFilter(hostList, hostFilter); + idx_hosts = getFromIndex(idx_hosts, _.map(hosts, 'hostid')); - // Filter applications by regex - if (utils.isRegex(appFilter)) { - var filterPattern = utils.buildRegex(appFilter); - apps = _.filter(applicationList, function (appObj) { - return filterPattern.test(appObj.name); - }); - } - // Find items in selected application - else if (appFilter) { - var finded = _.find(applicationList, {'name': appFilter}); - if (finded) { - apps.push(finded); - } else { - apps = undefined; - } - } else { + // Filter applications + if (appFilter === "") { + // Get all items apps = undefined; if (hosts) { - items = _.flatten(_.map(hosts, 'items'), true); + // Get all items in given hosts + items = _.flatten(_.map(idx_hosts, function(host) { + return _.values(host.idx_items); + }), true); } + } else { + apps = findByFilter(applicationList, appFilter); } if (apps) { - /*var appids = _.flatten(_.map(apps, 'applicationids')); - items = _.filter(cachedItems, function (itemObj) { - return _.intersection(appids, itemObj.applications).length; - }); - items = _.filter(items, function (itemObj) { - return _.find(hosts, {'hostid': itemObj.hostid }); - });*/ + // Get ids for finded applications + var appids = _.flatten(_.map(apps, 'applicationids')); + appids = _.flatten(_.map(_.map(hosts, 'applications'), function(apps) { + return _.intersection(apps, appids); + })); + + // For each finded host get list of items in finded applications + items = _.flatten(_.map(idx_hosts, function(host) { + var host_apps = _.intersection(appids, host.applications); + var host_itemids = _.flatten(_.map(getFromIndex(idx_apps, host_apps), 'itemids')); + return _.values(getFromIndex(host.idx_items, host_itemids)); + }), true); } if (!showDisabledItems) { @@ -194,6 +220,21 @@ function (angular, _, utils) { * Build query - convert target filters to array of Zabbix items */ this.buildFromCache = function (groupFilter, hostFilter, appFilter, itemFilter) { + return this.filterItems(groupFilter, hostFilter, appFilter).then(function(items) { + if (items.length) { + if (utils.isRegex(itemFilter)) { + return findByFilter(items, itemFilter); + } else { + return _.filter(items, {'name': itemFilter}); + } + } else { + return []; + } + }); + }; + + // DEPRECATED + this._buildFromCache = function (groupFilter, hostFilter, appFilter, itemFilter) { // Find items by item names and perform queries var groups = []; diff --git a/src/datasource-zabbix/zabbixAPI.service.js b/src/datasource-zabbix/zabbixAPI.service.js index ca74d1161..3047a7ada 100644 --- a/src/datasource-zabbix/zabbixAPI.service.js +++ b/src/datasource-zabbix/zabbixAPI.service.js @@ -127,7 +127,8 @@ function ZabbixAPIService($q, alertSrv, zabbixAPICoreService) { var params = { output: ['name', 'host'], sortfield: 'name', - selectGroups: [] + selectGroups: [], + selectApplications: ['applicationid'] }; return this.request('host.get', params); @@ -140,7 +141,8 @@ function ZabbixAPIService($q, alertSrv, zabbixAPICoreService) { // Hack for supporting different apis (2.2 vs 2.4 vs 3.0) selectHost: [], - selectHosts: [] + selectHosts: [], + selectItems: ['itemid'] }; return this.request('application.get', params); @@ -170,9 +172,12 @@ function ZabbixAPIService($q, alertSrv, zabbixAPICoreService) { var params = { output: ['name', 'host'], sortfield: 'name', - selectGroups: [], + selectGroups: ['groupid'], + selectApplications: ['applicationid'], selectItems: [ - 'name', 'key_', + 'itemid', + 'name', + 'key_', 'value_type', 'hostid', 'status', diff --git a/src/datasource-zabbix/zabbixCache.service.js b/src/datasource-zabbix/zabbixCache.service.js index 6427d07b8..4369d470f 100644 --- a/src/datasource-zabbix/zabbixCache.service.js +++ b/src/datasource-zabbix/zabbixCache.service.js @@ -62,7 +62,8 @@ function (angular, _, utils) { self._hosts = convertHosts(results[1]); self._applications = convertApplications(results[2]); self._items = convertItems(results[3]); - self._hostsExtend = convertHostsExtend(results[4]); + self._idx_apps = indexApps(results[2]); + self._idx_hosts = indexHosts(results[4]); } self._initialized = true; }); @@ -90,13 +91,24 @@ function (angular, _, utils) { } }; - p.getHostsExtend = function() { + p.getIndexedHosts = function() { var self = this; - if (this._hostsExtend) { - return $q.when(self._hostsExtend); + if (this._idx_hosts) { + return $q.when(self._idx_hosts); } else { return this.refresh().then(function() { - return self._hostsExtend; + return self._idx_hosts; + }); + } + }; + + p.getIndexedApplications = function() { + var self = this; + if (this._idx_apps) { + return $q.when(self._idx_apps); + } else { + return this.refresh().then(function() { + return self._idx_apps; }); } }; @@ -202,25 +214,13 @@ function (angular, _, utils) { }); } - function convertHostsExtend(hosts) { - return _.indexBy(_.map(hosts, function(host) { - host.items = _.forEach(host.items, function(item) { - item.applications = _.map(item.applications, 'applicationid'); - item.item = item.name; - item.name = utils.expandItemName(item.item, item.key_); - return item; - }); - return host; - }), 'hostid'); - } - /** * Group Zabbix applications by name * host.hosts - array of host ids */ function convertApplications(applications) { return _.map(_.groupBy(applications, 'name'), function(value, key) { - + //console.log(value); // Hack for supporting different apis (2.2 vs 2.4 vs 3.0) var hostField = 'host'; if (value[0] && value[0]['hosts']) { @@ -231,11 +231,46 @@ function (angular, _, utils) { return { name: key, applicationids: _.map(value, 'applicationid'), + itemids: _.uniq(_.map(_.flatten(value, 'items'), 'itemid')), hosts: _.uniq(_.map(_.flatten(value, hostField), 'hostid')) }; }); } + function indexHosts(hosts) { + return _.indexBy(_.map(hosts, function(host) { + + // Expand item names + host.items = _.forEach(host.items, function(item) { + item.item = item.name; + item.name = utils.expandItemName(item.item, item.key_); + return item; + }); + + host.applications = _.map(host.applications, 'applicationid'); + host.idx_items = indexItems(host.items); + host.items = _.map(host.items, 'itemid'); + return host; + }), 'hostid'); + } + + function indexApps(applications) { + return _.indexBy(_.map(applications, function(app) { + return { + name: app.name, + applicationid: app.applicationid, + host: _.first(_.map(app.hosts, 'hostid')), + itemids: _.map(app.items, 'itemid') + }; + }), 'applicationid'); + } + + function indexItems(items) { + return _.indexBy(_.map(items, function(item) { + return item; + }), 'itemid'); + } + /** * Convert item.get response to cache format * item.applications - array of application ids From 1ba30bc902edaca476b4e0908c9c8f17df7c4b20 Mon Sep 17 00:00:00 2001 From: Alexander Zobnin Date: Sat, 19 Mar 2016 10:42:45 +0300 Subject: [PATCH 150/205] Fixed templateSrv error in datasource.js --- src/datasource-zabbix/datasource.js | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/src/datasource-zabbix/datasource.js b/src/datasource-zabbix/datasource.js index 11183c78e..0d4c1c2a0 100644 --- a/src/datasource-zabbix/datasource.js +++ b/src/datasource-zabbix/datasource.js @@ -279,9 +279,10 @@ export class ZabbixAPIDatasource { metricFindQuery(query) { // Split query. Query structure: // group.host.app.item + var self = this; var parts = []; _.each(query.split('.'), function (part) { - part = this.templateSrv.replace(part); + part = self.templateSrv.replace(part); // Replace wildcard to regex if (part === '*') { From 30ae203f7387dc4e063c3d7ef51459fcd18d811b Mon Sep 17 00:00:00 2001 From: Alexander Zobnin Date: Sat, 19 Mar 2016 11:22:44 +0300 Subject: [PATCH 151/205] Hosts and applications filtering refactor. --- .../queryProcessor.service.js | 316 +++++------------- 1 file changed, 82 insertions(+), 234 deletions(-) diff --git a/src/datasource-zabbix/queryProcessor.service.js b/src/datasource-zabbix/queryProcessor.service.js index 05f5cd165..4794bef22 100644 --- a/src/datasource-zabbix/queryProcessor.service.js +++ b/src/datasource-zabbix/queryProcessor.service.js @@ -42,32 +42,18 @@ function (angular, _, utils) { }; this.filterGroups = function(groupFilter) { - return self.cache.getGroups().then(function(groupList) { + return this.cache.getGroups().then(function(groupList) { return groupList; }); }; + /** + * Get list of host belonging to given groups. + * @return list of hosts + */ this.filterHosts = function(groupFilter) { - var groups = []; - - return self.cache.getGroups().then(function(groupList) { - // Filter groups by regex - if (utils.isRegex(groupFilter)) { - var filterPattern = utils.buildRegex(groupFilter); - groups = _.filter(groupList, function (groupObj) { - return filterPattern.test(groupObj.name); - }); - } - // Find hosts in selected group - else { - var finded = _.find(groupList, {'name': groupFilter}); - if (finded) { - groups.push(finded); - } else { - groups = undefined; - } - } - + return this.cache.getGroups().then(function(groups) { + groups = findByFilter(groups, groupFilter); var hostids = _.flatten(_.map(groups, 'hosts')); if (hostids.length) { return self.cache.getIndexedHosts().then(function(hosts) { @@ -81,10 +67,11 @@ function (angular, _, utils) { }); }; + /** + * Get list of applications belonging to given groups and hosts. + * @return list of applications belonging to given hosts + */ this.filterApplications = function(groupFilter, hostFilter) { - var hosts = []; - var apps = []; - var promises = [ this.filterHosts(groupFilter), this.cache.getApplications() @@ -94,69 +81,18 @@ function (angular, _, utils) { var hostList = results[0]; var applicationList = results[1]; - // Filter hosts by regex - if (utils.isRegex(hostFilter)) { - var filterPattern = utils.buildRegex(hostFilter); - hosts = _.filter(hostList, function (hostObj) { - return filterPattern.test(hostObj.name); - }); - } - // Find applications in selected host - else { - var finded = _.find(hostList, {'name': hostFilter}); - if (finded) { - hosts.push(finded); - } else { - hosts = undefined; - } - } - + var hosts = findByFilter(hostList, hostFilter); if (hosts) { var hostsids = _.map(hosts, 'hostid'); - apps = _.filter(applicationList, function (appObj) { + return _.filter(applicationList, function (appObj) { return _.intersection(hostsids, appObj.hosts).length; }); + } else { + return []; } - return apps; }); }; - /** - * Find group, host, app or item by given name. - * @param list list of groups, apps or other - * @param name visible name - * @return array with finded element or undefined - */ - function findByName(list, name) { - var finded = _.find(list, {'name': name}); - if (finded) { - return [finded]; - } else { - return undefined; - } - } - - function findByRegex(list, regex) { - var filterPattern = utils.buildRegex(regex); - return _.filter(list, function (zbx_obj) { - return filterPattern.test(zbx_obj.name); - }); - } - - function findByFilter(list, filter) { - if (utils.isRegex(filter)) { - return findByRegex(list, filter); - } else { - return findByName(list, filter); - } - } - - function getFromIndex(index, objids) { - return _.map(objids, function(id) { - return index[id]; - }); - } - this.filterItems = function (groupFilter, hostFilter, appFilter, itemType, showDisabledItems) { var hosts; var apps; @@ -233,136 +169,12 @@ function (angular, _, utils) { }); }; - // DEPRECATED - this._buildFromCache = function (groupFilter, hostFilter, appFilter, itemFilter) { - - // Find items by item names and perform queries - var groups = []; - var hosts = []; - var apps = []; - var items = []; - var promises = [ - this.cache.getGroups(), - this.cache.getHosts(), - this.cache.getApplications(), - this.cache.getItems() - ]; - - return $q.all(promises).then(function(results) { - var cachedGroups = results[0]; - var cachedHosts = results[1]; - var cachedApps = results[2]; - var cachedItems = results[3]; - - if (utils.isRegex(hostFilter)) { - - // Filter groups - if (utils.isRegex(groupFilter)) { - var groupPattern = utils.buildRegex(groupFilter); - groups = _.filter(cachedGroups, function (groupObj) { - return groupPattern.test(groupObj.name); - }); - } else { - var findedGroup = _.find(cachedGroups, {'name': groupFilter}); - if (findedGroup) { - groups.push(findedGroup); - } else { - groups = undefined; - } - } - if (groups) { - var groupids = _.map(groups, 'groupid'); - hosts = _.filter(cachedHosts, function (hostObj) { - return _.intersection(groupids, hostObj.groups).length; - }); - } else { - // No groups finded - return []; - } - - // Filter hosts - var hostPattern = utils.buildRegex(hostFilter); - hosts = _.filter(hosts, function (hostObj) { - return hostPattern.test(hostObj.name); - }); - } else { - var findedHost = _.find(cachedHosts, {'name': hostFilter}); - if (findedHost) { - hosts.push(findedHost); - } else { - // No hosts finded - return []; - } - } - - // Find items belongs to selected hosts - items = _.filter(cachedItems, function (itemObj) { - return _.contains(_.map(hosts, 'hostid'), itemObj.hostid); - }); - - if (utils.isRegex(itemFilter)) { - - // Filter applications - if (utils.isRegex(appFilter)) { - var appPattern = utils.buildRegex(appFilter); - apps = _.filter(cachedApps, function (appObj) { - return appPattern.test(appObj.name); - }); - } - // Don't use application filter if it empty - else if (appFilter === "") { - apps = undefined; - } - else { - var findedApp = _.find(cachedApps, {'name': appFilter}); - if (findedApp) { - apps.push(findedApp); - } else { - // No applications finded - return []; - } - } - - // Find items belongs to selected applications - if (apps) { - var appids = _.flatten(_.map(apps, 'applicationids')); - items = _.filter(items, function (itemObj) { - return _.intersection(appids, itemObj.applications).length; - }); - } - - if (items) { - var itemPattern = utils.buildRegex(itemFilter); - items = _.filter(items, function (itemObj) { - return itemPattern.test(itemObj.name); - }); - } else { - // No items finded - return []; - } - } else { - items = _.filter(items, {'name': itemFilter}); - if (!items.length) { - // No items finded - return []; - } - } - - // Set host as host name for each item - items = _.each(items, function (itemObj) { - itemObj.host = _.find(hosts, {'hostid': itemObj.hostid}).name; - }); - - return items; - }); - }; - /** * Build query - convert target filters to array of Zabbix items */ this.buildTriggerQueryFromCache = function (groupFilter, hostFilter, appFilter) { var promises = [ - this.filterGroups(groupFilter).then(function(groups) { + this.cache.getGroups().then(function(groups) { return _.filter(groups, function(group) { if (utils.isRegex(groupFilter)) { return utils.buildRegex(groupFilter).test(group.name); @@ -477,39 +289,75 @@ function (angular, _, utils) { }; } }; - - function convertHistoryPoint(point) { - // Value must be a number for properly work - return [ - Number(point.value), - point.clock * 1000 - ]; - } - - function convertTrendPoint(valueType, point) { - var value; - switch (valueType) { - case "min": - value = point.value_min; - break; - case "max": - value = point.value_max; - break; - case "avg": - value = point.value_avg; - break; - default: - value = point.value_avg; - } - - return [ - Number(value), - point.clock * 1000 - ]; - } } return QueryProcessor; }); + /** + * Find group, host, app or item by given name. + * @param list list of groups, apps or other + * @param name visible name + * @return array with finded element or undefined + */ + function findByName(list, name) { + var finded = _.find(list, {'name': name}); + if (finded) { + return [finded]; + } else { + return undefined; + } + } + + function findByRegex(list, regex) { + var filterPattern = utils.buildRegex(regex); + return _.filter(list, function (zbx_obj) { + return filterPattern.test(zbx_obj.name); + }); + } + + function findByFilter(list, filter) { + if (utils.isRegex(filter)) { + return findByRegex(list, filter); + } else { + return findByName(list, filter); + } + } + + function getFromIndex(index, objids) { + return _.map(objids, function(id) { + return index[id]; + }); + } + + function convertHistoryPoint(point) { + // Value must be a number for properly work + return [ + Number(point.value), + point.clock * 1000 + ]; + } + + function convertTrendPoint(valueType, point) { + var value; + switch (valueType) { + case "min": + value = point.value_min; + break; + case "max": + value = point.value_max; + break; + case "avg": + value = point.value_avg; + break; + default: + value = point.value_avg; + } + + return [ + Number(value), + point.clock * 1000 + ]; + } + }); From d1c503e71afa091e750ede619082942bf231c87e Mon Sep 17 00:00:00 2001 From: Alexander Zobnin Date: Sat, 19 Mar 2016 11:56:21 +0300 Subject: [PATCH 152/205] Fixed application.get for 2.2 --- src/datasource-zabbix/zabbixAPI.service.js | 7 +++---- 1 file changed, 3 insertions(+), 4 deletions(-) diff --git a/src/datasource-zabbix/zabbixAPI.service.js b/src/datasource-zabbix/zabbixAPI.service.js index 3047a7ada..e57a436e0 100644 --- a/src/datasource-zabbix/zabbixAPI.service.js +++ b/src/datasource-zabbix/zabbixAPI.service.js @@ -136,12 +136,11 @@ function ZabbixAPIService($q, alertSrv, zabbixAPICoreService) { getApplications() { var params = { - output: ['name'], - sortfield: 'name', + output: ['applicationid', 'name'], // Hack for supporting different apis (2.2 vs 2.4 vs 3.0) - selectHost: [], - selectHosts: [], + selectHost: ['hostid'], + selectHosts: ['hostid'], selectItems: ['itemid'] }; From e8b4a4319c930b0badd1f8502c969cb25927b112 Mon Sep 17 00:00:00 2001 From: Alexander Zobnin Date: Sat, 19 Mar 2016 14:07:21 +0300 Subject: [PATCH 153/205] Services refactor - using ES6 modules. --- Gruntfile.js | 8 +- src/datasource-zabbix/DataProcessor.js | 234 +++++++ .../dataProcessing.service.js | 241 ------- src/datasource-zabbix/datasource.js | 19 +- .../queryProcessor.service.js | 638 +++++++++--------- src/datasource-zabbix/zabbixCache.service.js | 386 ++++++----- 6 files changed, 757 insertions(+), 769 deletions(-) create mode 100644 src/datasource-zabbix/DataProcessor.js delete mode 100644 src/datasource-zabbix/dataProcessing.service.js diff --git a/Gruntfile.js b/Gruntfile.js index 672b82ddf..58c59c045 100644 --- a/Gruntfile.js +++ b/Gruntfile.js @@ -19,9 +19,11 @@ module.exports = function(grunt) { '!**/module.js', '!**/query.controller.js', '!**/utils.js', + '!**/DataProcessor.js', '!**/zabbixAPICore.service.js', '!**/zabbixAPI.service.js', - //'!**/dataProcessing.service.js', + '!**/queryProcessor.service.js', + '!**/zabbixCache.service.js', '!**/metricFunctions.js', '!**/*.scss' ], @@ -57,9 +59,11 @@ module.exports = function(grunt) { '**/**/datasource.js', '**/**/query.controller.js', '**/**/utils.js', + '**/**/DataProcessor.js', '**/**/zabbixAPICore.service.js', '**/**/zabbixAPI.service.js', - //'**/**/dataProcessing.service.js', + '**/**/queryProcessor.service.js', + '**/**/zabbixCache.service.js', '**/**/metricFunctions.js' ], dest: 'dist/' diff --git a/src/datasource-zabbix/DataProcessor.js b/src/datasource-zabbix/DataProcessor.js new file mode 100644 index 000000000..c47965e8d --- /dev/null +++ b/src/datasource-zabbix/DataProcessor.js @@ -0,0 +1,234 @@ +import _ from 'lodash'; +import * as utils from './utils'; + +export default class DataProcessor { + + /** + * Downsample datapoints series + */ + static downsampleSeries(datapoints, time_to, ms_interval, func) { + var downsampledSeries = []; + var timeWindow = { + from: time_to * 1000 - ms_interval, + to: time_to * 1000 + }; + + var points_sum = 0; + var points_num = 0; + var value_avg = 0; + var frame = []; + + for (var i = datapoints.length - 1; i >= 0; i -= 1) { + if (timeWindow.from < datapoints[i][1] && datapoints[i][1] <= timeWindow.to) { + points_sum += datapoints[i][0]; + points_num++; + frame.push(datapoints[i][0]); + } + else { + value_avg = points_num ? points_sum / points_num : 0; + + if (func === "max") { + downsampledSeries.push([_.max(frame), timeWindow.to]); + } + else if (func === "min") { + downsampledSeries.push([_.min(frame), timeWindow.to]); + } + + // avg by default + else { + downsampledSeries.push([value_avg, timeWindow.to]); + } + + // Shift time window + timeWindow.to = timeWindow.from; + timeWindow.from -= ms_interval; + + points_sum = 0; + points_num = 0; + frame = []; + + // Process point again + i++; + } + } + return downsampledSeries.reverse(); + } + + /** + * Group points by given time interval + * datapoints: [[, ], ...] + */ + static groupBy(interval, groupByCallback, datapoints) { + var ms_interval = utils.parseInterval(interval); + + // Calculate frame timestamps + var frames = _.groupBy(datapoints, function(point) { + // Calculate time for group of points + return Math.floor(point[1] / ms_interval) * ms_interval; + }); + + // frame: { '': [[, ], ...] } + // return [{ '': }, { '': }, ...] + var grouped = _.mapValues(frames, function(frame) { + var points = _.map(frame, function(point) { + return point[0]; + }); + return groupByCallback(points); + }); + + // Convert points to Grafana format + return sortByTime(_.map(grouped, function(value, timestamp) { + return [Number(value), Number(timestamp)]; + })); + } + + static sumSeries(timeseries) { + + // Calculate new points for interpolation + var new_timestamps = _.uniq(_.map(_.flatten(timeseries, true), function(point) { + return point[1]; + })); + new_timestamps = _.sortBy(new_timestamps); + + var interpolated_timeseries = _.map(timeseries, function(series) { + var timestamps = _.map(series, function(point) { + return point[1]; + }); + var new_points = _.map(_.difference(new_timestamps, timestamps), function(timestamp) { + return [null, timestamp]; + }); + var new_series = series.concat(new_points); + return sortByTime(new_series); + }); + + _.each(interpolated_timeseries, interpolateSeries); + + var new_timeseries = []; + var sum; + for (var i = new_timestamps.length - 1; i >= 0; i--) { + sum = 0; + for (var j = interpolated_timeseries.length - 1; j >= 0; j--) { + sum += interpolated_timeseries[j][i][0]; + } + new_timeseries.push([sum, new_timestamps[i]]); + } + + return sortByTime(new_timeseries); + } + + static AVERAGE(values) { + var sum = 0; + _.each(values, function(value) { + sum += value; + }); + return sum / values.length; + } + + static MIN(values) { + return _.min(values); + } + + static MAX(values) { + return _.max(values); + } + + static MEDIAN(values) { + var sorted = _.sortBy(values); + return sorted[Math.floor(sorted.length / 2)]; + } + + static setAlias(alias, timeseries) { + timeseries.target = alias; + return timeseries; + } + + static groupByWrapper(interval, groupFunc, datapoints) { + var groupByCallback = DataProcessor.aggregationFunctions[groupFunc]; + return DataProcessor.groupBy(interval, groupByCallback, datapoints); + } + + static aggregateWrapper(groupByCallback, interval, datapoints) { + var flattenedPoints = _.flatten(datapoints, true); + return DataProcessor.groupBy(interval, groupByCallback, flattenedPoints); + } + + static get aggregationFunctions() { + return { + avg: this.AVERAGE, + min: this.MIN, + max: this.MAX, + median: this.MEDIAN + }; + } + + static get metricFunctions() { + return { + groupBy: this.groupByWrapper, + average: _.partial(this.aggregateWrapper, this.AVERAGE), + min: _.partial(this.aggregateWrapper, this.MIN), + max: _.partial(this.aggregateWrapper, this.MAX), + median: _.partial(this.aggregateWrapper, this.MEDIAN), + sumSeries: this.sumSeries, + setAlias: this.setAlias, + }; + } +} + +function sortByTime(series) { + return _.sortBy(series, function(point) { + return point[1]; + }); +} + +/** + * Interpolate series with gaps + */ +function interpolateSeries(series) { + var left, right; + + // Interpolate series + for (var i = series.length - 1; i >= 0; i--) { + if (!series[i][0]) { + left = findNearestLeft(series, series[i]); + right = findNearestRight(series, series[i]); + if (!left) { + left = right; + } + if (!right) { + right = left; + } + series[i][0] = linearInterpolation(series[i][1], left, right); + } + } + return series; +} + +function linearInterpolation(timestamp, left, right) { + if (left[1] === right[1]) { + return (left[0] + right[0]) / 2; + } else { + return (left[0] + (right[0] - left[0]) / (right[1] - left[1]) * (timestamp - left[1])); + } +} + +function findNearestRight(series, point) { + var point_index = _.indexOf(series, point); + var nearestRight; + for (var i = point_index; i < series.length; i++) { + if (series[i][0]) { + return series[i]; + } + } + return nearestRight; +} + +function findNearestLeft(series, point) { + var point_index = _.indexOf(series, point); + var nearestLeft; + for (var i = point_index; i > 0; i--) { + if (series[i][0]) { + return series[i]; + } + } + return nearestLeft; +} diff --git a/src/datasource-zabbix/dataProcessing.service.js b/src/datasource-zabbix/dataProcessing.service.js deleted file mode 100644 index b7958ea0b..000000000 --- a/src/datasource-zabbix/dataProcessing.service.js +++ /dev/null @@ -1,241 +0,0 @@ -define([ - 'angular', - 'lodash', - 'moment', - './utils' -], -function (angular, _, moment, utils) { - 'use strict'; - - var module = angular.module('grafana.services'); - - module.service('DataProcessingService', function() { - var self = this; - - /** - * Downsample datapoints series - */ - this.downsampleSeries = function(datapoints, time_to, ms_interval, func) { - var downsampledSeries = []; - var timeWindow = { - from: time_to * 1000 - ms_interval, - to: time_to * 1000 - }; - - var points_sum = 0; - var points_num = 0; - var value_avg = 0; - var frame = []; - - for (var i = datapoints.length - 1; i >= 0; i -= 1) { - if (timeWindow.from < datapoints[i][1] && datapoints[i][1] <= timeWindow.to) { - points_sum += datapoints[i][0]; - points_num++; - frame.push(datapoints[i][0]); - } - else { - value_avg = points_num ? points_sum / points_num : 0; - - if (func === "max") { - downsampledSeries.push([_.max(frame), timeWindow.to]); - } - else if (func === "min") { - downsampledSeries.push([_.min(frame), timeWindow.to]); - } - - // avg by default - else { - downsampledSeries.push([value_avg, timeWindow.to]); - } - - // Shift time window - timeWindow.to = timeWindow.from; - timeWindow.from -= ms_interval; - - points_sum = 0; - points_num = 0; - frame = []; - - // Process point again - i++; - } - } - return downsampledSeries.reverse(); - }; - - /** - * Group points by given time interval - * datapoints: [[, ], ...] - */ - this.groupBy = function(interval, groupByCallback, datapoints) { - var ms_interval = utils.parseInterval(interval); - - // Calculate frame timestamps - var frames = _.groupBy(datapoints, function(point) { - // Calculate time for group of points - return Math.floor(point[1] / ms_interval) * ms_interval; - }); - - // frame: { '': [[, ], ...] } - // return [{ '': }, { '': }, ...] - var grouped = _.mapValues(frames, function(frame) { - var points = _.map(frame, function(point) { - return point[0]; - }); - return groupByCallback(points); - }); - - // Convert points to Grafana format - return sortByTime(_.map(grouped, function(value, timestamp) { - return [Number(value), Number(timestamp)]; - })); - }; - - this.sumSeries = function(timeseries) { - - // Calculate new points for interpolation - var new_timestamps = _.uniq(_.map(_.flatten(timeseries, true), function(point) { - return point[1]; - })); - new_timestamps = _.sortBy(new_timestamps); - - var interpolated_timeseries = _.map(timeseries, function(series) { - var timestamps = _.map(series, function(point) { - return point[1]; - }); - var new_points = _.map(_.difference(new_timestamps, timestamps), function(timestamp) { - return [null, timestamp]; - }); - var new_series = series.concat(new_points); - return sortByTime(new_series); - }); - - _.each(interpolated_timeseries, interpolateSeries); - - var new_timeseries = []; - var sum; - for (var i = new_timestamps.length - 1; i >= 0; i--) { - sum = 0; - for (var j = interpolated_timeseries.length - 1; j >= 0; j--) { - sum += interpolated_timeseries[j][i][0]; - } - new_timeseries.push([sum, new_timestamps[i]]); - } - - return sortByTime(new_timeseries); - }; - - function sortByTime(series) { - return _.sortBy(series, function(point) { - return point[1]; - }); - } - - /** - * Interpolate series with gaps - */ - function interpolateSeries(series) { - var left, right; - - // Interpolate series - for (var i = series.length - 1; i >= 0; i--) { - if (!series[i][0]) { - left = findNearestLeft(series, series[i]); - right = findNearestRight(series, series[i]); - if (!left) { - left = right; - } - if (!right) { - right = left; - } - series[i][0] = linearInterpolation(series[i][1], left, right); - } - } - return series; - } - - function linearInterpolation(timestamp, left, right) { - if (left[1] === right[1]) { - return (left[0] + right[0]) / 2; - } else { - return (left[0] + (right[0] - left[0]) / (right[1] - left[1]) * (timestamp - left[1])); - } - } - - function findNearestRight(series, point) { - var point_index = _.indexOf(series, point); - var nearestRight; - for (var i = point_index; i < series.length; i++) { - if (series[i][0]) { - return series[i]; - } - } - return nearestRight; - } - - function findNearestLeft(series, point) { - var point_index = _.indexOf(series, point); - var nearestLeft; - for (var i = point_index; i > 0; i--) { - if (series[i][0]) { - return series[i]; - } - } - return nearestLeft; - } - - this.AVERAGE = function(values) { - var sum = 0; - _.each(values, function(value) { - sum += value; - }); - return sum / values.length; - }; - - this.MIN = function(values) { - return _.min(values); - }; - - this.MAX = function(values) { - return _.max(values); - }; - - this.MEDIAN = function(values) { - var sorted = _.sortBy(values); - return sorted[Math.floor(sorted.length / 2)]; - }; - - this.setAlias = function(alias, timeseries) { - timeseries.target = alias; - return timeseries; - }; - - this.aggregationFunctions = { - avg: this.AVERAGE, - min: this.MIN, - max: this.MAX, - median: this.MEDIAN - }; - - this.groupByWrapper = function(interval, groupFunc, datapoints) { - var groupByCallback = self.aggregationFunctions[groupFunc]; - return self.groupBy(interval, groupByCallback, datapoints); - }; - - this.aggregateWrapper = function(groupByCallback, interval, datapoints) { - var flattenedPoints = _.flatten(datapoints, true); - return self.groupBy(interval, groupByCallback, flattenedPoints); - }; - - this.metricFunctions = { - groupBy: this.groupByWrapper, - average: _.partial(this.aggregateWrapper, this.AVERAGE), - min: _.partial(this.aggregateWrapper, this.MIN), - max: _.partial(this.aggregateWrapper, this.MAX), - median: _.partial(this.aggregateWrapper, this.MEDIAN), - sumSeries: this.sumSeries, - setAlias: this.setAlias, - }; - - }); -}); diff --git a/src/datasource-zabbix/datasource.js b/src/datasource-zabbix/datasource.js index 0d4c1c2a0..8e5d250c7 100644 --- a/src/datasource-zabbix/datasource.js +++ b/src/datasource-zabbix/datasource.js @@ -3,15 +3,15 @@ import _ from 'lodash'; import * as dateMath from 'app/core/utils/datemath'; import * as utils from './utils'; import * as metricFunctions from './metricFunctions'; +import DataProcessor from './DataProcessor'; import './zabbixAPI.service.js'; import './zabbixCache.service.js'; import './queryProcessor.service.js'; -import './dataProcessing.service'; export class ZabbixAPIDatasource { /** @ngInject */ - constructor(instanceSettings, $q, templateSrv, alertSrv, zabbixAPIService, ZabbixCachingProxy, QueryProcessor, DataProcessingService) { + constructor(instanceSettings, $q, templateSrv, alertSrv, zabbixAPIService, ZabbixCachingProxy, QueryProcessor) { // General data source settings this.name = instanceSettings.name; @@ -45,7 +45,6 @@ export class ZabbixAPIDatasource { this.q = $q; this.templateSrv = templateSrv; this.alertSrv = alertSrv; - this.DataProcessingService = DataProcessingService; console.log(this.zabbixCache); } @@ -159,7 +158,7 @@ export class ZabbixAPIDatasource { timeseries_data = _.map(timeseries_data, function (timeseries) { // Filter only transform functions - var transformFunctions = bindFunctionDefs(target.functions, 'Transform', self.DataProcessingService); + var transformFunctions = bindFunctionDefs(target.functions, 'Transform', DataProcessor); // Metric data processing var dp = timeseries.datapoints; @@ -172,7 +171,7 @@ export class ZabbixAPIDatasource { }); // Aggregations - var aggregationFunctions = bindFunctionDefs(target.functions, 'Aggregate', self.DataProcessingService); + var aggregationFunctions = bindFunctionDefs(target.functions, 'Aggregate', DataProcessor); var dp = _.map(timeseries_data, 'datapoints'); if (aggregationFunctions.length) { for (var i = 0; i < aggregationFunctions.length; i++) { @@ -189,7 +188,7 @@ export class ZabbixAPIDatasource { } // Apply alias functions - var aliasFunctions = bindFunctionDefs(target.functions, 'Alias', self.DataProcessingService); + var aliasFunctions = bindFunctionDefs(target.functions, 'Alias', DataProcessor); for (var j = 0; j < aliasFunctions.length; j++) { _.each(timeseries_data, aliasFunctions[j]); } @@ -255,9 +254,9 @@ export class ZabbixAPIDatasource { // Series downsampling var data = _.map(timeseries_data, function(timeseries) { - var DPS = self.DataProcessingService; if (timeseries.datapoints.length > options.maxDataPoints) { - timeseries.datapoints = DPS.groupBy(options.interval, DPS.AVERAGE, timeseries.datapoints); + timeseries.datapoints = + DataProcessor.groupBy(options.interval, DataProcessor.AVERAGE, timeseries.datapoints); } return timeseries; }); @@ -409,7 +408,7 @@ export class ZabbixAPIDatasource { } -function bindFunctionDefs(functionDefs, category, DataProcessingService) { +function bindFunctionDefs(functionDefs, category, DataProcessor) { 'use strict'; var aggregationFunctions = _.map(metricFunctions.getCategories()[category], 'name'); var aggFuncDefs = _.filter(functionDefs, function(func) { @@ -418,7 +417,7 @@ function bindFunctionDefs(functionDefs, category, DataProcessingService) { return _.map(aggFuncDefs, function(func) { var funcInstance = metricFunctions.createFuncInstance(func.def, func.params); - return funcInstance.bindFunction(DataProcessingService.metricFunctions); + return funcInstance.bindFunction(DataProcessor.metricFunctions); }); } diff --git a/src/datasource-zabbix/queryProcessor.service.js b/src/datasource-zabbix/queryProcessor.service.js index 4794bef22..d447e5534 100644 --- a/src/datasource-zabbix/queryProcessor.service.js +++ b/src/datasource-zabbix/queryProcessor.service.js @@ -1,363 +1,361 @@ -define([ - 'angular', - 'lodash', - './utils' -], -function (angular, _, utils) { - 'use strict'; +import angular from 'angular'; +import _ from 'lodash'; +import * as utils from './utils'; - var module = angular.module('grafana.services'); +/** @ngInject */ +angular.module('grafana.services').factory('QueryProcessor', function($q) { - module.factory('QueryProcessor', function($q) { + class QueryProcessor { + constructor(zabbixCacheInstance) { + this.cache = zabbixCacheInstance; + this.$q = $q; + } - function QueryProcessor(zabbixCacheInstance) { + /** + * Build query in asynchronous manner + */ + build(groupFilter, hostFilter, appFilter, itemFilter) { var self = this; + if (this.cache._initialized) { + return this.$q.when(self.buildFromCache(groupFilter, hostFilter, appFilter, itemFilter)); + } else { + return this.cache.refresh().then(function() { + return self.buildFromCache(groupFilter, hostFilter, appFilter, itemFilter); + }); + } + } - this.cache = zabbixCacheInstance; + /** + * Build trigger query in asynchronous manner + */ + buildTriggerQuery(groupFilter, hostFilter, appFilter) { + var self = this; + if (this.cache._initialized) { + return this.$q.when(self.buildTriggerQueryFromCache(groupFilter, hostFilter, appFilter)); + } else { + return this.cache.refresh().then(function() { + return self.buildTriggerQueryFromCache(groupFilter, hostFilter, appFilter); + }); + } + } - /** - * Build query in asynchronous manner - */ - this.build = function (groupFilter, hostFilter, appFilter, itemFilter) { - if (this.cache._initialized) { - return $q.when(self.buildFromCache(groupFilter, hostFilter, appFilter, itemFilter)); - } else { - return this.cache.refresh().then(function() { - return self.buildFromCache(groupFilter, hostFilter, appFilter, itemFilter); + filterGroups(groupFilter) { + return this.cache.getGroups().then(function(groupList) { + return groupList; + }); + } + + /** + * Get list of host belonging to given groups. + * @return list of hosts + */ + filterHosts(groupFilter) { + var self = this; + return this.cache.getGroups().then(function(groups) { + groups = findByFilter(groups, groupFilter); + var hostids = _.flatten(_.map(groups, 'hosts')); + if (hostids.length) { + return self.cache.getIndexedHosts().then(function(hosts) { + return _.map(hostids, function(hostid) { + return hosts[hostid]; + }); }); + } else { + return []; } - }; + }); + } - /** - * Build trigger query in asynchronous manner - */ - this.buildTriggerQuery = function (groupFilter, hostFilter, appFilter) { - if (this.cache._initialized) { - return $q.when(self.buildTriggerQueryFromCache(groupFilter, hostFilter, appFilter)); - } else { - return this.cache.refresh().then(function() { - return self.buildTriggerQueryFromCache(groupFilter, hostFilter, appFilter); + /** + * Get list of applications belonging to given groups and hosts. + * @return list of applications belonging to given hosts + */ + filterApplications(groupFilter, hostFilter) { + var promises = [ + this.filterHosts(groupFilter), + this.cache.getApplications() + ]; + + return this.$q.all(promises).then(function(results) { + var hostList = results[0]; + var applicationList = results[1]; + + var hosts = findByFilter(hostList, hostFilter); + if (hosts) { + var hostsids = _.map(hosts, 'hostid'); + return _.filter(applicationList, function (appObj) { + return _.intersection(hostsids, appObj.hosts).length; }); + } else { + return []; } - }; - - this.filterGroups = function(groupFilter) { - return this.cache.getGroups().then(function(groupList) { - return groupList; - }); - }; - - /** - * Get list of host belonging to given groups. - * @return list of hosts - */ - this.filterHosts = function(groupFilter) { - return this.cache.getGroups().then(function(groups) { - groups = findByFilter(groups, groupFilter); - var hostids = _.flatten(_.map(groups, 'hosts')); - if (hostids.length) { - return self.cache.getIndexedHosts().then(function(hosts) { - return _.map(hostids, function(hostid) { - return hosts[hostid]; - }); - }); - } else { - return []; - } - }); - }; - - /** - * Get list of applications belonging to given groups and hosts. - * @return list of applications belonging to given hosts - */ - this.filterApplications = function(groupFilter, hostFilter) { - var promises = [ - this.filterHosts(groupFilter), - this.cache.getApplications() - ]; - - return $q.all(promises).then(function(results) { - var hostList = results[0]; - var applicationList = results[1]; + }); + } - var hosts = findByFilter(hostList, hostFilter); + filterItems(groupFilter, hostFilter, appFilter, itemType, showDisabledItems) { + var hosts; + var apps; + var items; + + var promises = [ + this.filterHosts(groupFilter), + this.filterApplications(groupFilter, hostFilter), + this.cache.getIndexedHosts(), + this.cache.getIndexedApplications() + ]; + + return this.$q.all(promises).then(function(results) { + var hostList = results[0]; + var applicationList = results[1]; + var idx_hosts = results[2]; + var idx_apps = results[3]; + + // Filter hosts + hosts = findByFilter(hostList, hostFilter); + idx_hosts = getFromIndex(idx_hosts, _.map(hosts, 'hostid')); + + // Filter applications + if (appFilter === "") { + // Get all items + apps = undefined; if (hosts) { - var hostsids = _.map(hosts, 'hostid'); - return _.filter(applicationList, function (appObj) { - return _.intersection(hostsids, appObj.hosts).length; - }); - } else { - return []; - } - }); - }; - - this.filterItems = function (groupFilter, hostFilter, appFilter, itemType, showDisabledItems) { - var hosts; - var apps; - var items; - - var promises = [ - this.filterHosts(groupFilter), - this.filterApplications(groupFilter, hostFilter), - this.cache.getIndexedHosts(), - this.cache.getIndexedApplications() - ]; - - return $q.all(promises).then(function(results) { - var hostList = results[0]; - var applicationList = results[1]; - var idx_hosts = results[2]; - var idx_apps = results[3]; - - // Filter hosts - hosts = findByFilter(hostList, hostFilter); - idx_hosts = getFromIndex(idx_hosts, _.map(hosts, 'hostid')); - - // Filter applications - if (appFilter === "") { - // Get all items - apps = undefined; - if (hosts) { - // Get all items in given hosts - items = _.flatten(_.map(idx_hosts, function(host) { - return _.values(host.idx_items); - }), true); - } - } else { - apps = findByFilter(applicationList, appFilter); - } - - if (apps) { - // Get ids for finded applications - var appids = _.flatten(_.map(apps, 'applicationids')); - appids = _.flatten(_.map(_.map(hosts, 'applications'), function(apps) { - return _.intersection(apps, appids); - })); - - // For each finded host get list of items in finded applications + // Get all items in given hosts items = _.flatten(_.map(idx_hosts, function(host) { - var host_apps = _.intersection(appids, host.applications); - var host_itemids = _.flatten(_.map(getFromIndex(idx_apps, host_apps), 'itemids')); - return _.values(getFromIndex(host.idx_items, host_itemids)); + return _.values(host.idx_items); }), true); } + } else { + apps = findByFilter(applicationList, appFilter); + } - if (!showDisabledItems) { - items = _.filter(items, {'status': '0'}); - } + if (apps) { + // Get ids for finded applications + var appids = _.flatten(_.map(apps, 'applicationids')); + appids = _.flatten(_.map(_.map(hosts, 'applications'), function(apps) { + return _.intersection(apps, appids); + })); + + // For each finded host get list of items in finded applications + items = _.flatten(_.map(idx_hosts, function(host) { + var host_apps = _.intersection(appids, host.applications); + var host_itemids = _.flatten(_.map(getFromIndex(idx_apps, host_apps), 'itemids')); + return _.values(getFromIndex(host.idx_items, host_itemids)); + }), true); + } - return items; - }); - }; + if (!showDisabledItems) { + items = _.filter(items, {'status': '0'}); + } - /** - * Build query - convert target filters to array of Zabbix items - */ - this.buildFromCache = function (groupFilter, hostFilter, appFilter, itemFilter) { - return this.filterItems(groupFilter, hostFilter, appFilter).then(function(items) { - if (items.length) { - if (utils.isRegex(itemFilter)) { - return findByFilter(items, itemFilter); - } else { - return _.filter(items, {'name': itemFilter}); - } + return items; + }); + } + + /** + * Build query - convert target filters to array of Zabbix items + */ + buildFromCache(groupFilter, hostFilter, appFilter, itemFilter) { + return this.filterItems(groupFilter, hostFilter, appFilter).then(function(items) { + if (items.length) { + if (utils.isRegex(itemFilter)) { + return findByFilter(items, itemFilter); } else { - return []; + return _.filter(items, {'name': itemFilter}); } - }); - }; + } else { + return []; + } + }); + } - /** - * Build query - convert target filters to array of Zabbix items - */ - this.buildTriggerQueryFromCache = function (groupFilter, hostFilter, appFilter) { - var promises = [ - this.cache.getGroups().then(function(groups) { - return _.filter(groups, function(group) { - if (utils.isRegex(groupFilter)) { - return utils.buildRegex(groupFilter).test(group.name); - } else { - return group.name === groupFilter; - } - }); - }), - this.filterHosts(groupFilter).then(function(hosts) { - return _.filter(hosts, function(host) { - if (utils.isRegex(hostFilter)) { - return utils.buildRegex(hostFilter).test(host.name); - } else { - return host.name === hostFilter; - } - }); - }), - this.filterApplications(groupFilter, hostFilter).then(function(apps) { - return _.filter(apps, function(app) { - if (utils.isRegex(appFilter)) { - return utils.buildRegex(appFilter).test(app.name); - } else { - return app.name === appFilter; - } - }); - }) - ]; + /** + * Build query - convert target filters to array of Zabbix items + */ + buildTriggerQueryFromCache(groupFilter, hostFilter, appFilter) { + var promises = [ + this.cache.getGroups().then(function(groups) { + return _.filter(groups, function(group) { + if (utils.isRegex(groupFilter)) { + return utils.buildRegex(groupFilter).test(group.name); + } else { + return group.name === groupFilter; + } + }); + }), + this.filterHosts(groupFilter).then(function(hosts) { + return _.filter(hosts, function(host) { + if (utils.isRegex(hostFilter)) { + return utils.buildRegex(hostFilter).test(host.name); + } else { + return host.name === hostFilter; + } + }); + }), + this.filterApplications(groupFilter, hostFilter).then(function(apps) { + return _.filter(apps, function(app) { + if (utils.isRegex(appFilter)) { + return utils.buildRegex(appFilter).test(app.name); + } else { + return app.name === appFilter; + } + }); + }) + ]; - return $q.all(promises).then(function(results) { - var filteredGroups = results[0]; - var filteredHosts = results[1]; - var filteredApps = results[2]; - var query = {}; + return this.$q.all(promises).then(function(results) { + var filteredGroups = results[0]; + var filteredHosts = results[1]; + var filteredApps = results[2]; + var query = {}; - if (appFilter) { - query.applicationids = _.flatten(_.map(filteredApps, 'applicationids')); - } - if (hostFilter) { - query.hostids = _.map(filteredHosts, 'hostid'); - } - if (groupFilter) { - query.groupids = _.map(filteredGroups, 'groupid'); - } + if (appFilter) { + query.applicationids = _.flatten(_.map(filteredApps, 'applicationids')); + } + if (hostFilter) { + query.hostids = _.map(filteredHosts, 'hostid'); + } + if (groupFilter) { + query.groupids = _.map(filteredGroups, 'groupid'); + } - return query; - }); - }; + return query; + }); + } + /** + * Convert Zabbix API history.get response to Grafana format + * + * @return {Array} Array of timeseries in Grafana format + * { + * target: "Metric name", + * datapoints: [[, ], ...] + * } + */ + convertHistory(history, addHostName, convertPointCallback) { /** - * Convert Zabbix API history.get response to Grafana format - * - * @return {Array} Array of timeseries in Grafana format - * { - * target: "Metric name", - * datapoints: [[, ], ...] - * } + * Response should be in the format: + * data: [ + * { + * target: "Metric name", + * datapoints: [[, ], ...] + * }, ... + * ] */ - this.convertHistory = function(history, addHostName, convertPointCallback) { - /** - * Response should be in the format: - * data: [ - * { - * target: "Metric name", - * datapoints: [[, ], ...] - * }, ... - * ] - */ - - // Group history by itemid - var grouped_history = _.groupBy(history, 'itemid'); - - return _.map(grouped_history, function(hist, itemid) { - var item = self.cache.getItem(itemid); - var alias = item.name; - if (addHostName) { - var host = self.cache.getHost(item.hostid); - alias = host.name + ": " + alias; - } - return { - target: alias, - datapoints: _.map(hist, convertPointCallback) - }; - }); - }; - - this.handleHistory = function(history, addHostName) { - return this.convertHistory(history, addHostName, convertHistoryPoint); - }; - - this.handleTrends = function(history, addHostName, valueType) { - var convertPointCallback = _.partial(convertTrendPoint, valueType); - return this.convertHistory(history, addHostName, convertPointCallback); - }; - - this.handleSLAResponse = function (itservice, slaProperty, slaObject) { - var targetSLA = slaObject[itservice.serviceid].sla[0]; - if (slaProperty.property === 'status') { - var targetStatus = parseInt(slaObject[itservice.serviceid].status); - return { - target: itservice.name + ' ' + slaProperty.name, - datapoints: [ - [targetStatus, targetSLA.to * 1000] - ] - }; - } else { - return { - target: itservice.name + ' ' + slaProperty.name, - datapoints: [ - [targetSLA[slaProperty.property], targetSLA.from * 1000], - [targetSLA[slaProperty.property], targetSLA.to * 1000] - ] - }; + var self = this; + + // Group history by itemid + var grouped_history = _.groupBy(history, 'itemid'); + + return _.map(grouped_history, function(hist, itemid) { + var item = self.cache.getItem(itemid); + var alias = item.name; + if (addHostName) { + var host = self.cache.getHost(item.hostid); + alias = host.name + ": " + alias; } - }; + return { + target: alias, + datapoints: _.map(hist, convertPointCallback) + }; + }); } - return QueryProcessor; - }); - - /** - * Find group, host, app or item by given name. - * @param list list of groups, apps or other - * @param name visible name - * @return array with finded element or undefined - */ - function findByName(list, name) { - var finded = _.find(list, {'name': name}); - if (finded) { - return [finded]; - } else { - return undefined; + handleHistory(history, addHostName) { + return this.convertHistory(history, addHostName, convertHistoryPoint); } - } - function findByRegex(list, regex) { - var filterPattern = utils.buildRegex(regex); - return _.filter(list, function (zbx_obj) { - return filterPattern.test(zbx_obj.name); - }); - } + handleTrends(history, addHostName, valueType) { + var convertPointCallback = _.partial(convertTrendPoint, valueType); + return this.convertHistory(history, addHostName, convertPointCallback); + } - function findByFilter(list, filter) { - if (utils.isRegex(filter)) { - return findByRegex(list, filter); - } else { - return findByName(list, filter); + handleSLAResponse(itservice, slaProperty, slaObject) { + var targetSLA = slaObject[itservice.serviceid].sla[0]; + if (slaProperty.property === 'status') { + var targetStatus = parseInt(slaObject[itservice.serviceid].status); + return { + target: itservice.name + ' ' + slaProperty.name, + datapoints: [ + [targetStatus, targetSLA.to * 1000] + ] + }; + } else { + return { + target: itservice.name + ' ' + slaProperty.name, + datapoints: [ + [targetSLA[slaProperty.property], targetSLA.from * 1000], + [targetSLA[slaProperty.property], targetSLA.to * 1000] + ] + }; + } } } - function getFromIndex(index, objids) { - return _.map(objids, function(id) { - return index[id]; - }); - } + return QueryProcessor; +}); - function convertHistoryPoint(point) { - // Value must be a number for properly work - return [ - Number(point.value), - point.clock * 1000 - ]; +/** + * Find group, host, app or item by given name. + * @param list list of groups, apps or other + * @param name visible name + * @return array with finded element or undefined + */ +function findByName(list, name) { + var finded = _.find(list, {'name': name}); + if (finded) { + return [finded]; + } else { + return undefined; } +} - function convertTrendPoint(valueType, point) { - var value; - switch (valueType) { - case "min": - value = point.value_min; - break; - case "max": - value = point.value_max; - break; - case "avg": - value = point.value_avg; - break; - default: - value = point.value_avg; - } +function findByRegex(list, regex) { + var filterPattern = utils.buildRegex(regex); + return _.filter(list, function (zbx_obj) { + return filterPattern.test(zbx_obj.name); + }); +} - return [ - Number(value), - point.clock * 1000 - ]; +function findByFilter(list, filter) { + if (utils.isRegex(filter)) { + return findByRegex(list, filter); + } else { + return findByName(list, filter); } +} -}); +function getFromIndex(index, objids) { + return _.map(objids, function(id) { + return index[id]; + }); +} + +function convertHistoryPoint(point) { + // Value must be a number for properly work + return [ + Number(point.value), + point.clock * 1000 + ]; +} + +function convertTrendPoint(valueType, point) { + var value; + switch (valueType) { + case "min": + value = point.value_min; + break; + case "max": + value = point.value_max; + break; + case "avg": + value = point.value_avg; + break; + default: + value = point.value_avg; + } + + return [ + Number(value), + point.clock * 1000 + ]; +} diff --git a/src/datasource-zabbix/zabbixCache.service.js b/src/datasource-zabbix/zabbixCache.service.js index 4369d470f..65a00cf7f 100644 --- a/src/datasource-zabbix/zabbixCache.service.js +++ b/src/datasource-zabbix/zabbixCache.service.js @@ -1,27 +1,25 @@ -define([ - 'angular', - 'lodash', - './utils' -], -function (angular, _, utils) { - 'use strict'; +import angular from 'angular'; +import _ from 'lodash'; +import * as utils from './utils'; - var module = angular.module('grafana.services'); +// Use factory() instead service() for multiple datasources support. +// Each datasource instance must initialize its own cache. - // Use factory() instead service() for multiple datasources support. - // Each datasource instance must initialize its own cache. - module.factory('ZabbixCachingProxy', function($q, $interval) { +/** @ngInject */ +angular.module('grafana.services').factory('ZabbixCachingProxy', function($q, $interval) { - function ZabbixCachingProxy(zabbixAPI, ttl) { + class ZabbixCachingProxy { + constructor(zabbixAPI, ttl) { this.zabbixAPI = zabbixAPI; this.ttl = ttl; + this.$q = $q; + // Internal objects for data storing this._groups = undefined; this._hosts = undefined; this._applications = undefined; this._items = undefined; - this._hostsExtend = undefined; this.storage = { history: {}, trends: {} @@ -34,7 +32,7 @@ function (angular, _, utils) { this.historyPromises = {}; // Wrap _refresh() method to call it once. - this.refresh = callOnce(p._refresh, this.refreshPromise); + this.refresh = callOnce(this._refresh, this.refreshPromise); // Update cache periodically $interval(_.bind(this.refresh, this), this.ttl); @@ -44,9 +42,7 @@ function (angular, _, utils) { this.historyPromises); } - var p = ZabbixCachingProxy.prototype; - - p._refresh = function() { + _refresh() { var self = this; var promises = [ this.zabbixAPI.getGroups(), @@ -56,7 +52,7 @@ function (angular, _, utils) { this.zabbixAPI.getHostsExtend() ]; - return $q.all(promises).then(function(results) { + return this.$q.all(promises).then(function(results) { if (results.length) { self._groups = convertGroups(results[0]); self._hosts = convertHosts(results[1]); @@ -67,94 +63,76 @@ function (angular, _, utils) { } self._initialized = true; }); - }; + } - p.getGroups = function() { + getGroups() { var self = this; if (this._groups) { - return $q.when(self._groups); + return this.$q.when(self._groups); } else { return this.refresh().then(function() { return self._groups; }); } - }; + } - p.getHosts = function() { + getHosts() { var self = this; if (this._hosts) { - return $q.when(self._hosts); + return this.$q.when(self._hosts); } else { return this.refresh().then(function() { return self._hosts; }); } - }; + } - p.getIndexedHosts = function() { + getIndexedHosts() { var self = this; if (this._idx_hosts) { - return $q.when(self._idx_hosts); + return this.$q.when(self._idx_hosts); } else { return this.refresh().then(function() { return self._idx_hosts; }); } - }; + } - p.getIndexedApplications = function() { + getIndexedApplications() { var self = this; if (this._idx_apps) { - return $q.when(self._idx_apps); + return this.$q.when(self._idx_apps); } else { return this.refresh().then(function() { return self._idx_apps; }); } - }; + } - p.getApplications = function() { + getApplications() { var self = this; if (this._applications) { - return $q.when(self._applications); + return this.$q.when(self._applications); } else { return this.refresh().then(function() { return self._applications; }); } - }; + } - p.getItems = function(type) { + getItems(type) { var self = this; if (this._items) { - return $q.when(filterItems(self._items, type)); + return this.$q.when(filterItems(self._items, type)); } else { return this.refresh().then(function() { return filterItems(self._items, type); }); } - }; - - function filterItems(items, type) { - switch (type) { - case 'num': - return _.filter(items, function(item) { - return (item.value_type === '0' || - item.value_type === '3'); - }); - case 'text': - return _.filter(items, function(item) { - return (item.value_type === '1' || - item.value_type === '2' || - item.value_type === '4'); - }); - default: - return items; - } } - p.getHistoryFromCache = function(items, time_from, time_till) { - var deferred = $q.defer(); + getHistoryFromCache(items, time_from, time_till) { + var deferred = this.$q.defer(); var historyStorage = this.storage.history; var full_history; var expired = _.filter(_.indexBy(items, 'itemid'), function(item, itemid) { @@ -182,161 +160,177 @@ function (angular, _, utils) { deferred.resolve(_.flatten(full_history, true)); } return deferred.promise; - }; + } - p.getHistoryFromAPI = function(items, time_from, time_till) { + getHistoryFromAPI(items, time_from, time_till) { return this.zabbixAPI.getHistory(items, time_from, time_till); - }; - - p.getHost = function(hostid) { - return _.find(this._hosts, {'hostid': hostid}); - }; - - p.getItem = function(itemid) { - return _.find(this._items, {'itemid': itemid}); - }; - - /** - * Convert host.get response to cache format - * host.groups - array of group ids - */ - function convertHosts(hosts) { - return _.forEach(hosts, function(host) { - host.groups = _.map(host.groups, 'groupid'); - return host; - }); } - function convertGroups(groups) { - return _.forEach(groups, function(group) { - group.hosts = _.map(group.hosts, 'hostid'); - return group; - }); + getHost(hostid) { + return _.find(this._hosts, {'hostid': hostid}); } - /** - * Group Zabbix applications by name - * host.hosts - array of host ids - */ - function convertApplications(applications) { - return _.map(_.groupBy(applications, 'name'), function(value, key) { - //console.log(value); - // Hack for supporting different apis (2.2 vs 2.4 vs 3.0) - var hostField = 'host'; - if (value[0] && value[0]['hosts']) { - // For Zabbix 2.2 - hostField = 'hosts'; - } - - return { - name: key, - applicationids: _.map(value, 'applicationid'), - itemids: _.uniq(_.map(_.flatten(value, 'items'), 'itemid')), - hosts: _.uniq(_.map(_.flatten(value, hostField), 'hostid')) - }; - }); + getItem(itemid) { + return _.find(this._items, {'itemid': itemid}); } + } - function indexHosts(hosts) { - return _.indexBy(_.map(hosts, function(host) { + function callHistoryOnce(func, promiseKeeper) { + return function() { + var itemids = _.map(arguments[0], 'itemid'); + var stamp = itemids.join() + arguments[1] + arguments[2]; + var hash = stamp.getHash(); - // Expand item names - host.items = _.forEach(host.items, function(item) { - item.item = item.name; - item.name = utils.expandItemName(item.item, item.key_); - return item; + var deferred = $q.defer(); + if (!promiseKeeper[hash]) { + promiseKeeper[hash] = deferred.promise; + func.apply(this, arguments).then(function(result) { + deferred.resolve(result); + promiseKeeper[hash] = null; }); - - host.applications = _.map(host.applications, 'applicationid'); - host.idx_items = indexItems(host.items); - host.items = _.map(host.items, 'itemid'); - return host; - }), 'hostid'); - } - - function indexApps(applications) { - return _.indexBy(_.map(applications, function(app) { - return { - name: app.name, - applicationid: app.applicationid, - host: _.first(_.map(app.hosts, 'hostid')), - itemids: _.map(app.items, 'itemid') - }; - }), 'applicationid'); - } - - function indexItems(items) { - return _.indexBy(_.map(items, function(item) { - return item; - }), 'itemid'); - } - - /** - * Convert item.get response to cache format - * item.applications - array of application ids - * item.item - original item name returned by api (ie "CPU $2 time") - * item.name - expanded name (ie "CPU system time") - */ - function convertItems(items) { - return _.forEach(items, function(item) { - item.applications = _.map(item.applications, 'applicationid'); - item.item = item.name; - item.name = utils.expandItemName(item.item, item.key_); - return item; - }); - } - - String.prototype.getHash = function() { - var hash = 0, i, chr, len; - if (this.length === 0) { - return hash; - } - for (i = 0, len = this.length; i < len; i++) { - chr = this.charCodeAt(i); - hash = ((hash << 5) - hash) + chr; - hash |= 0; // Convert to 32bit integer + } else { + return promiseKeeper[hash]; } - return hash; + return deferred.promise; }; + } - function callHistoryOnce(func, promiseKeeper) { - return function() { - var itemids = _.map(arguments[0], 'itemid'); - var stamp = itemids.join() + arguments[1] + arguments[2]; - var hash = stamp.getHash(); - - var deferred = $q.defer(); - if (!promiseKeeper[hash]) { - promiseKeeper[hash] = deferred.promise; - func.apply(this, arguments).then(function(result) { - deferred.resolve(result); - promiseKeeper[hash] = null; - }); - } else { - return promiseKeeper[hash]; - } - return deferred.promise; - }; - } + function callOnce(func, promiseKeeper) { + return function() { + var deferred = $q.defer(); + if (!promiseKeeper) { + promiseKeeper = deferred.promise; + func.apply(this, arguments).then(function(result) { + deferred.resolve(result); + promiseKeeper = null; + }); + } else { + return promiseKeeper; + } + return deferred.promise; + }; + } - function callOnce(func, promiseKeeper) { - return function() { - var deferred = $q.defer(); - if (!promiseKeeper) { - promiseKeeper = deferred.promise; - func.apply(this, arguments).then(function(result) { - deferred.resolve(result); - promiseKeeper = null; - }); - } else { - return promiseKeeper; - } - return deferred.promise; - }; - } + return ZabbixCachingProxy; +}); - return ZabbixCachingProxy; +/** + * Convert host.get response to cache format + * host.groups - array of group ids + */ +function convertHosts(hosts) { + return _.forEach(hosts, function(host) { + host.groups = _.map(host.groups, 'groupid'); + return host; + }); +} +function convertGroups(groups) { + return _.forEach(groups, function(group) { + group.hosts = _.map(group.hosts, 'hostid'); + return group; }); +} + +/** + * Group Zabbix applications by name + * host.hosts - array of host ids + */ +function convertApplications(applications) { + return _.map(_.groupBy(applications, 'name'), function(value, key) { + //console.log(value); + // Hack for supporting different apis (2.2 vs 2.4 vs 3.0) + var hostField = 'host'; + if (value[0] && value[0]['hosts']) { + // For Zabbix 2.2 + hostField = 'hosts'; + } -}); + return { + name: key, + applicationids: _.map(value, 'applicationid'), + itemids: _.uniq(_.map(_.flatten(value, 'items'), 'itemid')), + hosts: _.uniq(_.map(_.flatten(value, hostField), 'hostid')) + }; + }); +} + +function indexHosts(hosts) { + return _.indexBy(_.map(hosts, function(host) { + + // Expand item names + host.items = _.forEach(host.items, function(item) { + item.item = item.name; + item.name = utils.expandItemName(item.item, item.key_); + return item; + }); + + host.applications = _.map(host.applications, 'applicationid'); + host.idx_items = indexItems(host.items); + host.items = _.map(host.items, 'itemid'); + return host; + }), 'hostid'); +} + +function indexApps(applications) { + return _.indexBy(_.map(applications, function(app) { + return { + name: app.name, + applicationid: app.applicationid, + host: _.first(_.map(app.hosts, 'hostid')), + itemids: _.map(app.items, 'itemid') + }; + }), 'applicationid'); +} + +function indexItems(items) { + return _.indexBy(_.map(items, function(item) { + return item; + }), 'itemid'); +} + +/** + * Convert item.get response to cache format + * item.applications - array of application ids + * item.item - original item name returned by api (ie "CPU $2 time") + * item.name - expanded name (ie "CPU system time") + */ +function convertItems(items) { + return _.forEach(items, function(item) { + item.applications = _.map(item.applications, 'applicationid'); + item.item = item.name; + item.name = utils.expandItemName(item.item, item.key_); + return item; + }); +} + +function filterItems(items, type) { + switch (type) { + case 'num': + return _.filter(items, function(item) { + return (item.value_type === '0' || + item.value_type === '3'); + }); + case 'text': + return _.filter(items, function(item) { + return (item.value_type === '1' || + item.value_type === '2' || + item.value_type === '4'); + }); + default: + return items; + } +} + +String.prototype.getHash = function() { + var hash = 0, i, chr, len; + if (this.length === 0) { + return hash; + } + for (i = 0, len = this.length; i < len; i++) { + chr = this.charCodeAt(i); + hash = ((hash << 5) - hash) + chr; + hash |= 0; // Convert to 32bit integer + } + return hash; +}; From e74824f840a2dbcdbbfe290019924ec410f53535 Mon Sep 17 00:00:00 2001 From: Alexander Zobnin Date: Sat, 19 Mar 2016 14:20:55 +0300 Subject: [PATCH 154/205] Directives refactor. --- Gruntfile.js | 22 +- .../add-metric-function.directive.js | 201 +++++---- .../metric-function-editor.directive.js | 401 +++++++++--------- 3 files changed, 299 insertions(+), 325 deletions(-) diff --git a/Gruntfile.js b/Gruntfile.js index 58c59c045..f9b527399 100644 --- a/Gruntfile.js +++ b/Gruntfile.js @@ -15,16 +15,7 @@ module.exports = function(grunt) { expand: true, src: [ '**/*', - '!**/datasource.js', - '!**/module.js', - '!**/query.controller.js', - '!**/utils.js', - '!**/DataProcessor.js', - '!**/zabbixAPICore.service.js', - '!**/zabbixAPI.service.js', - '!**/queryProcessor.service.js', - '!**/zabbixCache.service.js', - '!**/metricFunctions.js', + '!**/*.js', '!**/*.scss' ], dest: 'dist/' @@ -55,16 +46,7 @@ module.exports = function(grunt) { cwd: 'src', expand: true, src: [ - '**/**/module.js', - '**/**/datasource.js', - '**/**/query.controller.js', - '**/**/utils.js', - '**/**/DataProcessor.js', - '**/**/zabbixAPICore.service.js', - '**/**/zabbixAPI.service.js', - '**/**/queryProcessor.service.js', - '**/**/zabbixCache.service.js', - '**/**/metricFunctions.js' + '**/**/*.js' ], dest: 'dist/' }] diff --git a/src/datasource-zabbix/add-metric-function.directive.js b/src/datasource-zabbix/add-metric-function.directive.js index 2001265b5..a3676ee17 100644 --- a/src/datasource-zabbix/add-metric-function.directive.js +++ b/src/datasource-zabbix/add-metric-function.directive.js @@ -1,107 +1,104 @@ -define([ - 'angular', - 'lodash', - 'jquery', - './metricFunctions' -], -function (angular, _, $, metricFunctions) { - 'use strict'; - - angular - .module('grafana.directives') - .directive('addMetricFunction', function($compile) { - var inputTemplate = ''; - - var buttonTemplate = '
    ' + - ''; - - return { - link: function($scope, elem) { - var categories = metricFunctions.getCategories(); - var allFunctions = getAllFunctionNames(categories); - - $scope.functionMenu = createFunctionDropDownMenu(categories); - - var $input = $(inputTemplate); - var $button = $(buttonTemplate); - $input.appendTo(elem); - $button.appendTo(elem); - - $input.attr('data-provide', 'typeahead'); - $input.typeahead({ - source: allFunctions, - minLength: 1, - items: 10, - updater: function (value) { - var funcDef = metricFunctions.getFuncDef(value); - if (!funcDef) { - // try find close match - value = value.toLowerCase(); - funcDef = _.find(allFunctions, function(funcName) { - return funcName.toLowerCase().indexOf(value) === 0; - }); - - if (!funcDef) { return; } - } - - $scope.$apply(function() { - $scope.addFunction(funcDef); +import angular from 'angular'; +import _ from 'lodash'; +import $ from 'jquery'; +import * as metricFunctions from './metricFunctions'; + +/** @ngInject */ +angular + .module('grafana.directives') + .directive('addMetricFunction', function($compile) { + var inputTemplate = ''; + + var buttonTemplate = '' + + ''; + + return { + link: function($scope, elem) { + var categories = metricFunctions.getCategories(); + var allFunctions = getAllFunctionNames(categories); + + $scope.functionMenu = createFunctionDropDownMenu(categories); + + var $input = $(inputTemplate); + var $button = $(buttonTemplate); + $input.appendTo(elem); + $button.appendTo(elem); + + $input.attr('data-provide', 'typeahead'); + $input.typeahead({ + source: allFunctions, + minLength: 1, + items: 10, + updater: function (value) { + var funcDef = metricFunctions.getFuncDef(value); + if (!funcDef) { + // try find close match + value = value.toLowerCase(); + funcDef = _.find(allFunctions, function(funcName) { + return funcName.toLowerCase().indexOf(value) === 0; }); - $input.trigger('blur'); - return ''; + if (!funcDef) { return; } } - }); - - $button.click(function() { - $button.hide(); - $input.show(); - $input.focus(); - }); - - $input.keyup(function() { - elem.toggleClass('open', $input.val() === ''); - }); - - $input.blur(function() { - // clicking the function dropdown menu wont - // work if you remove class at once - setTimeout(function() { - $input.val(''); - $input.hide(); - $button.show(); - elem.removeClass('open'); - }, 200); - }); - - $compile(elem.contents())($scope); - } - }; - }); - function getAllFunctionNames(categories) { - return _.reduce(categories, function(list, category) { - _.each(category, function(func) { - list.push(func.name); - }); - return list; - }, []); - } - - function createFunctionDropDownMenu(categories) { - return _.map(categories, function(list, category) { - return { - text: category, - submenu: _.map(list, function(value) { - return { - text: value.name, - click: "ctrl.addFunction('" + value.name + "')", - }; - }) - }; + $scope.$apply(function() { + $scope.addFunction(funcDef); + }); + + $input.trigger('blur'); + return ''; + } + }); + + $button.click(function() { + $button.hide(); + $input.show(); + $input.focus(); + }); + + $input.keyup(function() { + elem.toggleClass('open', $input.val() === ''); + }); + + $input.blur(function() { + // clicking the function dropdown menu wont + // work if you remove class at once + setTimeout(function() { + $input.val(''); + $input.hide(); + $button.show(); + elem.removeClass('open'); + }, 200); + }); + + $compile(elem.contents())($scope); + } + }; + }); + +function getAllFunctionNames(categories) { + return _.reduce(categories, function(list, category) { + _.each(category, function(func) { + list.push(func.name); }); - } -}); + return list; + }, []); +} + +function createFunctionDropDownMenu(categories) { + return _.map(categories, function(list, category) { + return { + text: category, + submenu: _.map(list, function(value) { + return { + text: value.name, + click: "ctrl.addFunction('" + value.name + "')", + }; + }) + }; + }); +} + diff --git a/src/datasource-zabbix/metric-function-editor.directive.js b/src/datasource-zabbix/metric-function-editor.directive.js index a7af58c54..e00119448 100644 --- a/src/datasource-zabbix/metric-function-editor.directive.js +++ b/src/datasource-zabbix/metric-function-editor.directive.js @@ -1,247 +1,242 @@ -define([ - 'angular', - 'lodash', - 'jquery', -], -function (angular, _, $) { - 'use strict'; - - angular - .module('grafana.directives') - .directive('metricFunctionEditor', function($compile, templateSrv) { - - var funcSpanTemplate = '{{func.def.name}}('; - var paramTemplate = ''; - - var funcControlsTemplate = - '
    ' + - '' + - '' + - '' + - '' + - '
    '; - - return { - restrict: 'A', - link: function postLink($scope, elem) { - var $funcLink = $(funcSpanTemplate); - var $funcControls = $(funcControlsTemplate); - var ctrl = $scope.ctrl; - var func = $scope.func; - var funcDef = func.def; - var scheduledRelink = false; - var paramCountAtLink = 0; - - function clickFuncParam(paramIndex) { - /*jshint validthis:true */ - - var $link = $(this); - var $input = $link.next(); - - $input.val(func.params[paramIndex]); - $input.css('width', ($link.width() + 16) + 'px'); - - $link.hide(); - $input.show(); - $input.focus(); - $input.select(); - - var typeahead = $input.data('typeahead'); - if (typeahead) { - $input.val(''); - typeahead.lookup(); - } +import angular from 'angular'; +import _ from 'lodash'; +import $ from 'jquery'; + +/** @ngInject */ +angular + .module('grafana.directives') + .directive('metricFunctionEditor', function($compile, templateSrv) { + + var funcSpanTemplate = '{{func.def.name}}('; + var paramTemplate = ''; + + var funcControlsTemplate = + '
    ' + + '' + + '' + + '' + + '' + + '
    '; + + return { + restrict: 'A', + link: function postLink($scope, elem) { + var $funcLink = $(funcSpanTemplate); + var $funcControls = $(funcControlsTemplate); + var ctrl = $scope.ctrl; + var func = $scope.func; + var funcDef = func.def; + var scheduledRelink = false; + var paramCountAtLink = 0; + + function clickFuncParam(paramIndex) { + /*jshint validthis:true */ + + var $link = $(this); + var $input = $link.next(); + + $input.val(func.params[paramIndex]); + $input.css('width', ($link.width() + 16) + 'px'); + + $link.hide(); + $input.show(); + $input.focus(); + $input.select(); + + var typeahead = $input.data('typeahead'); + if (typeahead) { + $input.val(''); + typeahead.lookup(); } + } - function scheduledRelinkIfNeeded() { - if (paramCountAtLink === func.params.length) { - return; - } + function scheduledRelinkIfNeeded() { + if (paramCountAtLink === func.params.length) { + return; + } - if (!scheduledRelink) { - scheduledRelink = true; - setTimeout(function() { - relink(); - scheduledRelink = false; - }, 200); - } + if (!scheduledRelink) { + scheduledRelink = true; + setTimeout(function() { + relink(); + scheduledRelink = false; + }, 200); } + } - function inputBlur(paramIndex) { - /*jshint validthis:true */ - var $input = $(this); - var $link = $input.prev(); - var newValue = $input.val(); + function inputBlur(paramIndex) { + /*jshint validthis:true */ + var $input = $(this); + var $link = $input.prev(); + var newValue = $input.val(); - if (newValue !== '' || func.def.params[paramIndex].optional) { - $link.html(templateSrv.highlightVariablesAsHtml(newValue)); + if (newValue !== '' || func.def.params[paramIndex].optional) { + $link.html(templateSrv.highlightVariablesAsHtml(newValue)); - func.updateParam($input.val(), paramIndex); - scheduledRelinkIfNeeded(); + func.updateParam($input.val(), paramIndex); + scheduledRelinkIfNeeded(); - $scope.$apply(function() { - ctrl.targetChanged(); - }); + $scope.$apply(function() { + ctrl.targetChanged(); + }); - $input.hide(); - $link.show(); - } + $input.hide(); + $link.show(); } + } - function inputKeyPress(paramIndex, e) { - /*jshint validthis:true */ - if(e.which === 13) { - inputBlur.call(this, paramIndex); - } + function inputKeyPress(paramIndex, e) { + /*jshint validthis:true */ + if(e.which === 13) { + inputBlur.call(this, paramIndex); } + } - function inputKeyDown() { - /*jshint validthis:true */ - this.style.width = (3 + this.value.length) * 8 + 'px'; - } + function inputKeyDown() { + /*jshint validthis:true */ + this.style.width = (3 + this.value.length) * 8 + 'px'; + } + + function addTypeahead($input, paramIndex) { + $input.attr('data-provide', 'typeahead'); - function addTypeahead($input, paramIndex) { - $input.attr('data-provide', 'typeahead'); + var options = funcDef.params[paramIndex].options; + if (funcDef.params[paramIndex].type === 'int') { + options = _.map(options, function(val) { return val.toString(); }); + } - var options = funcDef.params[paramIndex].options; - if (funcDef.params[paramIndex].type === 'int') { - options = _.map(options, function(val) { return val.toString(); }); + $input.typeahead({ + source: options, + minLength: 0, + items: 20, + updater: function (value) { + setTimeout(function() { + inputBlur.call($input[0], paramIndex); + }, 0); + return value; } + }); - $input.typeahead({ - source: options, - minLength: 0, - items: 20, - updater: function (value) { - setTimeout(function() { - inputBlur.call($input[0], paramIndex); - }, 0); - return value; - } - }); + var typeahead = $input.data('typeahead'); + typeahead.lookup = function () { + this.query = this.$element.val() || ''; + return this.process(this.source); + }; + } - var typeahead = $input.data('typeahead'); - typeahead.lookup = function () { - this.query = this.$element.val() || ''; - return this.process(this.source); - }; - } + function toggleFuncControls() { + var targetDiv = elem.closest('.tight-form'); - function toggleFuncControls() { - var targetDiv = elem.closest('.tight-form'); + if (elem.hasClass('show-function-controls')) { + elem.removeClass('show-function-controls'); + targetDiv.removeClass('has-open-function'); + $funcControls.hide(); + return; + } - if (elem.hasClass('show-function-controls')) { - elem.removeClass('show-function-controls'); - targetDiv.removeClass('has-open-function'); - $funcControls.hide(); - return; - } + elem.addClass('show-function-controls'); + targetDiv.addClass('has-open-function'); - elem.addClass('show-function-controls'); - targetDiv.addClass('has-open-function'); + $funcControls.show(); + } - $funcControls.show(); - } + function addElementsAndCompile() { + $funcControls.appendTo(elem); + $funcLink.appendTo(elem); - function addElementsAndCompile() { - $funcControls.appendTo(elem); - $funcLink.appendTo(elem); + _.each(funcDef.params, function(param, index) { + if (param.optional && func.params.length <= index) { + return; + } - _.each(funcDef.params, function(param, index) { - if (param.optional && func.params.length <= index) { - return; - } + if (index > 0) { + $(', ').appendTo(elem); + } - if (index > 0) { - $(', ').appendTo(elem); - } + var paramValue = templateSrv.highlightVariablesAsHtml(func.params[index]); + var $paramLink = $('' + paramValue + ''); + var $input = $(paramTemplate); - var paramValue = templateSrv.highlightVariablesAsHtml(func.params[index]); - var $paramLink = $('' + paramValue + ''); - var $input = $(paramTemplate); + paramCountAtLink++; - paramCountAtLink++; + $paramLink.appendTo(elem); + $input.appendTo(elem); - $paramLink.appendTo(elem); - $input.appendTo(elem); + $input.blur(_.partial(inputBlur, index)); + $input.keyup(inputKeyDown); + $input.keypress(_.partial(inputKeyPress, index)); + $paramLink.click(_.partial(clickFuncParam, index)); - $input.blur(_.partial(inputBlur, index)); - $input.keyup(inputKeyDown); - $input.keypress(_.partial(inputKeyPress, index)); - $paramLink.click(_.partial(clickFuncParam, index)); + if (funcDef.params[index].options) { + addTypeahead($input, index); + } - if (funcDef.params[index].options) { - addTypeahead($input, index); - } + }); - }); + $(')').appendTo(elem); - $(')').appendTo(elem); + $compile(elem.contents())($scope); + } - $compile(elem.contents())($scope); + function ifJustAddedFocusFistParam() { + if ($scope.func.added) { + $scope.func.added = false; + setTimeout(function() { + elem.find('.graphite-func-param-link').first().click(); + }, 10); } + } - function ifJustAddedFocusFistParam() { - if ($scope.func.added) { - $scope.func.added = false; - setTimeout(function() { - elem.find('.graphite-func-param-link').first().click(); - }, 10); + function registerFuncControlsToggle() { + $funcLink.click(toggleFuncControls); + } + + function registerFuncControlsActions() { + $funcControls.click(function(e) { + var $target = $(e.target); + if ($target.hasClass('fa-remove')) { + toggleFuncControls(); + $scope.$apply(function() { + ctrl.removeFunction($scope.func); + }); + return; } - } - function registerFuncControlsToggle() { - $funcLink.click(toggleFuncControls); - } + if ($target.hasClass('fa-arrow-left')) { + $scope.$apply(function() { + _.move($scope.target.functions, $scope.$index, $scope.$index - 1); + ctrl.targetChanged(); + }); + return; + } - function registerFuncControlsActions() { - $funcControls.click(function(e) { - var $target = $(e.target); - if ($target.hasClass('fa-remove')) { - toggleFuncControls(); - $scope.$apply(function() { - ctrl.removeFunction($scope.func); - }); - return; - } - - if ($target.hasClass('fa-arrow-left')) { - $scope.$apply(function() { - _.move($scope.target.functions, $scope.$index, $scope.$index - 1); - ctrl.targetChanged(); - }); - return; - } - - if ($target.hasClass('fa-arrow-right')) { - $scope.$apply(function() { - _.move($scope.target.functions, $scope.$index, $scope.$index + 1); - ctrl.targetChanged(); - }); - return; - } - - if ($target.hasClass('fa-question-circle')) { - window.open("http://graphite.readthedocs.org/en/latest/functions.html#graphite.render.functions." + funcDef.name,'_blank'); - return; - } - }); - } + if ($target.hasClass('fa-arrow-right')) { + $scope.$apply(function() { + _.move($scope.target.functions, $scope.$index, $scope.$index + 1); + ctrl.targetChanged(); + }); + return; + } - function relink() { - elem.children().remove(); + if ($target.hasClass('fa-question-circle')) { + window.open("http://graphite.readthedocs.org/en/latest/functions.html#graphite.render.functions." + funcDef.name,'_blank'); + return; + } + }); + } - addElementsAndCompile(); - ifJustAddedFocusFistParam(); - registerFuncControlsToggle(); - registerFuncControlsActions(); - } + function relink() { + elem.children().remove(); - relink(); + addElementsAndCompile(); + ifJustAddedFocusFistParam(); + registerFuncControlsToggle(); + registerFuncControlsActions(); } - }; - }); + relink(); + } + }; -}); + }); From af59f87246ebbbcd522950d618d982a1ba59dda1 Mon Sep 17 00:00:00 2001 From: Alexander Zobnin Date: Sat, 19 Mar 2016 15:09:13 +0300 Subject: [PATCH 155/205] Fixed annotations. --- src/datasource-zabbix/datasource.js | 16 +++------------ .../partials/annotations.editor.html | 16 +++++++-------- src/datasource-zabbix/zabbixAPI.service.js | 20 ++++++++++++++++--- 3 files changed, 28 insertions(+), 24 deletions(-) diff --git a/src/datasource-zabbix/datasource.js b/src/datasource-zabbix/datasource.js index 8e5d250c7..1291c931c 100644 --- a/src/datasource-zabbix/datasource.js +++ b/src/datasource-zabbix/datasource.js @@ -334,7 +334,7 @@ export class ZabbixAPIDatasource { var to = Math.ceil(dateMath.parse(options.rangeRaw.to) / 1000); var annotation = options.annotation; var self = this; - var showEvents = annotation.showOkEvents ? [0, 1] : 1; + var showOkEvents = annotation.showOkEvents ? [0, 1] : 1; var buildQuery = self.queryProcessor.buildTriggerQuery(this.templateSrv.replace(annotation.group), this.templateSrv.replace(annotation.host), @@ -343,7 +343,7 @@ export class ZabbixAPIDatasource { return self.zabbixAPI.getTriggers(query.groupids, query.hostids, query.applicationids, - showEvents) + true) .then(function(triggers) { // Filter triggers by description @@ -363,17 +363,7 @@ export class ZabbixAPIDatasource { }); var objectids = _.map(triggers, 'triggerid'); - var params = { - output: 'extend', - time_from: from, - time_till: to, - objectids: objectids, - select_acknowledges: 'extend', - selectHosts: 'extend', - value: showEvents - }; - - return self.zabbixAPI.request('event.get', params) + return self.zabbixAPI.getEvents(objectids, from, to, showOkEvents) .then(function (events) { var indexedTriggers = _.indexBy(triggers, 'triggerid'); diff --git a/src/datasource-zabbix/partials/annotations.editor.html b/src/datasource-zabbix/partials/annotations.editor.html index 42e05179b..c7676be2b 100644 --- a/src/datasource-zabbix/partials/annotations.editor.html +++ b/src/datasource-zabbix/partials/annotations.editor.html @@ -8,7 +8,7 @@
    Filter Triggers
  • @@ -16,7 +16,7 @@
    Filter Triggers
  • @@ -29,7 +29,7 @@
    Filter Triggers
  • @@ -37,7 +37,7 @@
    Filter Triggers
  • @@ -52,10 +52,10 @@
    Options
    - +
    - - - + + +
    diff --git a/src/datasource-zabbix/zabbixAPI.service.js b/src/datasource-zabbix/zabbixAPI.service.js index e57a436e0..7661c0c84 100644 --- a/src/datasource-zabbix/zabbixAPI.service.js +++ b/src/datasource-zabbix/zabbixAPI.service.js @@ -312,7 +312,7 @@ function ZabbixAPIService($q, alertSrv, zabbixAPICoreService) { return this.request('service.getsla', params); } - getTriggers(groupids, hostids, applicationids, showEvents) { + getTriggers(groupids, hostids, applicationids, showAll) { var params = { output: 'extend', groupids: groupids, @@ -332,13 +332,27 @@ function ZabbixAPIService($q, alertSrv, zabbixAPICoreService) { selectLastEvent: 'extend' }; - if (showEvents) { - params.filter.value = showEvents; + if (showAll) { + params.filter = {}; } return this.request('trigger.get', params); } + getEvents(objectids, from, to, showOkEvents) { + var params = { + output: 'extend', + time_from: from, + time_till: to, + objectids: objectids, + select_acknowledges: 'extend', + selectHosts: 'extend', + value: showOkEvents + }; + + return this.request('event.get', params); + } + getAcknowledges(eventids) { var params = { output: 'extend', From c6f854e9489467a14eacb9ad9b82b15b81871758 Mon Sep 17 00:00:00 2001 From: Alexander Zobnin Date: Sat, 19 Mar 2016 15:28:24 +0300 Subject: [PATCH 156/205] Fixed grunt tasks (copy files for trigger panel). --- Gruntfile.js | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/Gruntfile.js b/Gruntfile.js index f9b527399..766f87e20 100644 --- a/Gruntfile.js +++ b/Gruntfile.js @@ -15,7 +15,7 @@ module.exports = function(grunt) { expand: true, src: [ '**/*', - '!**/*.js', + '!datasource-zabbix/*.js', '!**/*.scss' ], dest: 'dist/' @@ -46,7 +46,7 @@ module.exports = function(grunt) { cwd: 'src', expand: true, src: [ - '**/**/*.js' + 'datasource-zabbix/*.js' ], dest: 'dist/' }] From a533f7f6b3ef4d8c69dc903fba1eb2ead0ce516c Mon Sep 17 00:00:00 2001 From: Alexander Zobnin Date: Sat, 19 Mar 2016 17:15:48 +0300 Subject: [PATCH 157/205] Trigger panel refactor. Migrated to ES6. --- Gruntfile.js | 4 +- src/panel-triggers/editor.js | 106 +++++----- src/panel-triggers/module.js | 395 +++++++++++++++++------------------ 3 files changed, 245 insertions(+), 260 deletions(-) diff --git a/Gruntfile.js b/Gruntfile.js index 766f87e20..1211d9d9e 100644 --- a/Gruntfile.js +++ b/Gruntfile.js @@ -16,6 +16,7 @@ module.exports = function(grunt) { src: [ '**/*', '!datasource-zabbix/*.js', + '!panel-triggers/*.js', '!**/*.scss' ], dest: 'dist/' @@ -46,7 +47,8 @@ module.exports = function(grunt) { cwd: 'src', expand: true, src: [ - 'datasource-zabbix/*.js' + 'datasource-zabbix/*.js', + 'panel-triggers/*.js', ], dest: 'dist/' }] diff --git a/src/panel-triggers/editor.js b/src/panel-triggers/editor.js index 55c77a7ed..160ce4fd4 100644 --- a/src/panel-triggers/editor.js +++ b/src/panel-triggers/editor.js @@ -11,15 +11,13 @@ * Licensed under the Apache License, Version 2.0 */ -define([ - 'angular', - 'lodash', - 'jquery' -], -function (angular, _, $) { - 'use strict'; +import _ from 'lodash'; +import $ from 'jquery'; + +class TriggerPanelEditorCtrl{ - function TriggerPanelEditorCtrl($scope, $q, uiSegmentSrv, datasourceSrv, templateSrv, popoverSrv) { + /** @ngInject */ + constructor($scope, $q, uiSegmentSrv, datasourceSrv, templateSrv, popoverSrv) { $scope.editor = this; this.panelCtrl = $scope.ctrl; this.panel = this.panelCtrl.panel; @@ -80,35 +78,28 @@ function (angular, _, $) { }); } - var p = TriggerPanelEditorCtrl.prototype; - - // Get list of metric names for bs-typeahead directive - function getMetricNames(scope, metricList) { - return _.uniq(_.map(scope.metric[metricList], 'name')); - } - - p.initFilters = function () { + initFilters() { this.filterGroups(); this.filterHosts(); this.filterApplications(); - }; + } - p.filterGroups = function() { + filterGroups() { var self = this; this.datasource.queryProcessor.filterGroups().then(function(groups) { self.metric.groupList = groups; }); - }; + } - p.filterHosts = function() { + filterHosts() { var self = this; var groupFilter = this.templateSrv.replace(this.panel.triggers.group.filter); this.datasource.queryProcessor.filterHosts(groupFilter).then(function(hosts) { self.metric.filteredHosts = hosts; }); - }; + } - p.filterApplications = function() { + filterApplications() { var self = this; var groupFilter = this.templateSrv.replace(this.panel.triggers.group.filter); var hostFilter = this.templateSrv.replace(this.panel.triggers.host.filter); @@ -116,51 +107,41 @@ function (angular, _, $) { .then(function(apps) { self.metric.filteredApplications = apps; }); - }; + } - p.onTargetPartChange = function(targetPart) { + onTargetPartChange(targetPart) { var regexStyle = {'color': '#CCA300'}; targetPart.isRegex = isRegex(targetPart.filter); targetPart.style = targetPart.isRegex ? regexStyle : {}; - }; - - function isRegex(str) { - // Pattern for testing regex - var regexPattern = /^\/(.*)\/([gmi]*)$/m; - return regexPattern.test(str); } - p.parseTarget = function() { + parseTarget() { this.initFilters(); var newTarget = _.cloneDeep(this.panel.triggers); if (!_.isEqual(this.oldTarget, this.panel.triggers)) { this.oldTarget = newTarget; this.panelCtrl.refreshData(); } - }; + } - p.refreshTriggerSeverity = function() { + refreshTriggerSeverity() { _.each(this.triggerList, function(trigger) { trigger.color = this.panel.triggerSeverity[trigger.priority].color; trigger.severity = this.panel.triggerSeverity[trigger.priority].severity; }); this.panelCtrl.refreshData(); - }; + } - p.datasourceChanged = function() { + datasourceChanged() { this.panelCtrl.refreshData(); - }; + } - p.changeTriggerSeverityColor = function(trigger, color) { + changeTriggerSeverityColor(trigger, color) { this.panel.triggerSeverity[trigger.priority].color = color; this.refreshTriggerSeverity(); - }; - - function getTriggerIndexForElement(el) { - return el.parents('[data-trigger-index]').data('trigger-index'); } - p.openTriggerColorSelector = function(event) { + openTriggerColorSelector(event) { var el = $(event.currentTarget); var index = getTriggerIndexForElement(el); var popoverScope = this.$new(); @@ -173,9 +154,9 @@ function (angular, _, $) { templateUrl: 'public/plugins/triggers/trigger.colorpicker.html', scope: popoverScope }); - }; + } - p.openOkEventColorSelector = function(event) { + openOkEventColorSelector(event) { var el = $(event.currentTarget); var popoverScope = this.$new(); popoverScope.trigger = {color: this.panel.okEventColor}; @@ -190,16 +171,31 @@ function (angular, _, $) { templateUrl: 'public/plugins/triggers/trigger.colorpicker.html', scope: popoverScope }); - }; + } +} - var triggerPanelEditor = function() { - return { - restrict: 'E', - scope: true, - templateUrl: 'public/plugins/triggers/editor.html', - controller: TriggerPanelEditorCtrl, - }; - }; +// Get list of metric names for bs-typeahead directive +function getMetricNames(scope, metricList) { + return _.uniq(_.map(scope.metric[metricList], 'name')); +} - return triggerPanelEditor; -}); \ No newline at end of file +function getTriggerIndexForElement(el) { + return el.parents('[data-trigger-index]').data('trigger-index'); +} + +function isRegex(str) { + // Pattern for testing regex + var regexPattern = /^\/(.*)\/([gmi]*)$/m; + return regexPattern.test(str); +} + +/** @ngInject */ +export function triggerPanelEditor($q, uiSegmentSrv, datasourceSrv, templateSrv, popoverSrv) { + 'use strict'; + return { + restrict: 'E', + scope: true, + templateUrl: 'public/plugins/triggers/editor.html', + controller: TriggerPanelEditorCtrl, + }; +} diff --git a/src/panel-triggers/module.js b/src/panel-triggers/module.js index 47d1b045b..fa35e3254 100644 --- a/src/panel-triggers/module.js +++ b/src/panel-triggers/module.js @@ -11,220 +11,207 @@ * Licensed under the Apache License, Version 2.0 */ -define([ - 'app/plugins/sdk', - 'angular', - 'lodash', - 'jquery', - 'moment', - './editor' -], -function (sdk, angular, _, $, moment, triggerPanelEditor) { - 'use strict'; - - var defaultSeverity = [ - { priority: 0, severity: 'Not classified', color: '#B7DBAB', show: true }, - { priority: 1, severity: 'Information', color: '#82B5D8', show: true }, - { priority: 2, severity: 'Warning', color: '#E5AC0E', show: true }, - { priority: 3, severity: 'Average', color: '#C15C17', show: true }, - { priority: 4, severity: 'High', color: '#BF1B00', show: true }, - { priority: 5, severity: 'Disaster', color: '#890F02', show: true } - ]; - - var panelDefaults = { - datasource: null, - triggers: { - group: {filter: ""}, - host: {filter: ""}, - application: {filter: ""}, - trigger: {filter: ""} - }, - hostField: true, - statusField: false, - severityField: false, - lastChangeField: true, - ageField: true, - infoField: true, - limit: 10, - showTriggers: 'all triggers', - sortTriggersBy: { text: 'last change', value: 'lastchange' }, - showEvents: { text: 'Problem events', value: '1' }, - triggerSeverity: defaultSeverity, - okEventColor: '#890F02', - }; - - var triggerStatusMap = { - '0': 'OK', - '1': 'Problem' - }; - - var TriggerPanelCtrl = (function(_super) { - - /** @ngInject */ - function TriggerPanelCtrl($scope, $injector, $q, $element, datasourceSrv) { - _super.call(this, $scope, $injector); - this.datasourceSrv = datasourceSrv; - this.triggerStatusMap = triggerStatusMap; - - // Load panel defaults - _.defaults(this.panel, panelDefaults); - - this.triggerList = []; - this.refreshData(); - } - - TriggerPanelCtrl.templateUrl = 'module.html'; - - TriggerPanelCtrl.prototype = Object.create(_super.prototype); - TriggerPanelCtrl.prototype.constructor = TriggerPanelCtrl; - - // Add panel editor - TriggerPanelCtrl.prototype.initEditMode = function() { - _super.prototype.initEditMode(); - this.icon = "fa fa-lightbulb-o"; - this.addEditorTab('Options', triggerPanelEditor, 2); - }; - - TriggerPanelCtrl.prototype.refreshData = function() { - var self = this; - - // Load datasource - return this.datasourceSrv.get(this.panel.datasource).then(function (datasource) { - var zabbix = datasource.zabbixAPI; - var queryProcessor = datasource.queryProcessor; - var triggerFilter = self.panel.triggers; - var showEvents = self.panel.showEvents.value; - var buildQuery = queryProcessor.buildTriggerQuery(triggerFilter.group.filter, - triggerFilter.host.filter, - triggerFilter.application.filter); - return buildQuery.then(function(query) { - return zabbix.getTriggers(query.groupids, - query.hostids, - query.applicationids, - showEvents) - .then(function(triggers) { - return _.map(triggers, function (trigger) { - var triggerObj = trigger; - - // Format last change and age - trigger.lastchangeUnix = Number(trigger.lastchange); - var timestamp = moment.unix(trigger.lastchangeUnix); - if (self.panel.customLastChangeFormat) { - // User defined format - triggerObj.lastchange = timestamp.format(self.panel.lastChangeFormat); - } else { - triggerObj.lastchange = timestamp.format(self.defaultTimeFormat); - } - triggerObj.age = timestamp.fromNow(true); - - // Set color - if (trigger.value === '1') { - triggerObj.color = self.panel.triggerSeverity[trigger.priority].color; - } else { - triggerObj.color = self.panel.okEventColor; - } - - triggerObj.severity = self.panel.triggerSeverity[trigger.priority].severity; - return triggerObj; - }); - }) - .then(function (triggerList) { +import _ from 'lodash'; +import moment from 'moment'; +import {PanelCtrl} from 'app/plugins/sdk'; +import {triggerPanelEditor} from './editor'; + +var defaultSeverity = [ + { priority: 0, severity: 'Not classified', color: '#B7DBAB', show: true }, + { priority: 1, severity: 'Information', color: '#82B5D8', show: true }, + { priority: 2, severity: 'Warning', color: '#E5AC0E', show: true }, + { priority: 3, severity: 'Average', color: '#C15C17', show: true }, + { priority: 4, severity: 'High', color: '#BF1B00', show: true }, + { priority: 5, severity: 'Disaster', color: '#890F02', show: true } +]; + +var panelDefaults = { + datasource: null, + triggers: { + group: {filter: ""}, + host: {filter: ""}, + application: {filter: ""}, + trigger: {filter: ""} + }, + hostField: true, + statusField: false, + severityField: false, + lastChangeField: true, + ageField: true, + infoField: true, + limit: 10, + showTriggers: 'all triggers', + sortTriggersBy: { text: 'last change', value: 'lastchange' }, + showEvents: { text: 'Problem events', value: '1' }, + triggerSeverity: defaultSeverity, + okEventColor: '#890F02', +}; + +var triggerStatusMap = { + '0': 'OK', + '1': 'Problem' +}; + +class TriggerPanelCtrl extends PanelCtrl { + + /** @ngInject */ + constructor($scope, $injector, $q, $element, datasourceSrv) { + super($scope, $injector); + this.datasourceSrv = datasourceSrv; + this.triggerStatusMap = triggerStatusMap; + + // Load panel defaults + _.defaults(this.panel, panelDefaults); + + this.triggerList = []; + this.refreshData(); + } + + // Add panel editor + initEditMode() { + super.initEditMode(); + this.icon = "fa fa-lightbulb-o"; + this.addEditorTab('Options', triggerPanelEditor, 2); + } + + refreshData() { + var self = this; + + // Load datasource + return this.datasourceSrv.get(this.panel.datasource).then(datasource => { + var zabbix = datasource.zabbixAPI; + var queryProcessor = datasource.queryProcessor; + var triggerFilter = self.panel.triggers; + var showEvents = self.panel.showEvents.value; + var buildQuery = queryProcessor.buildTriggerQuery(triggerFilter.group.filter, + triggerFilter.host.filter, + triggerFilter.application.filter); + return buildQuery.then(query => { + return zabbix.getTriggers(query.groupids, + query.hostids, + query.applicationids, + showEvents) + .then(triggers => { + return _.map(triggers, trigger => { + var triggerObj = trigger; + + // Format last change and age + trigger.lastchangeUnix = Number(trigger.lastchange); + var timestamp = moment.unix(trigger.lastchangeUnix); + if (self.panel.customLastChangeFormat) { + // User defined format + triggerObj.lastchange = timestamp.format(self.panel.lastChangeFormat); + } else { + triggerObj.lastchange = timestamp.format(self.defaultTimeFormat); + } + triggerObj.age = timestamp.fromNow(true); + + // Set color + if (trigger.value === '1') { + triggerObj.color = self.panel.triggerSeverity[trigger.priority].color; + } else { + triggerObj.color = self.panel.okEventColor; + } + + triggerObj.severity = self.panel.triggerSeverity[trigger.priority].severity; + return triggerObj; + }); + }) + .then(triggerList => { - // Request acknowledges for trigger - var eventids = _.map(triggerList, function(trigger) { - return trigger.lastEvent.eventid; - }); + // Request acknowledges for trigger + var eventids = _.map(triggerList, trigger => { + return trigger.lastEvent.eventid; + }); - return zabbix.getAcknowledges(eventids) - .then(function (events) { + return zabbix.getAcknowledges(eventids) + .then(events => { - // Map events to triggers - _.each(triggerList, function(trigger) { - var event = _.find(events, function(event) { - return event.eventid === trigger.lastEvent.eventid; - }); - - if (event) { - trigger.acknowledges = _.map(event.acknowledges, function (ack) { - var time = new Date(+ack.clock * 1000); - ack.time = time.toLocaleString(); - ack.user = ack.alias + ' (' + ack.name + ' ' + ack.surname + ')'; - return ack; - }); - } + // Map events to triggers + _.each(triggerList, trigger => { + var event = _.find(events, event => { + return event.eventid === trigger.lastEvent.eventid; }); - // Filter triggers by description - var triggerFilter = self.panel.triggers.trigger.filter; - if (triggerFilter) { - triggerList = filterTriggers(triggerList, triggerFilter); - } - - // Filter acknowledged triggers - if (self.panel.showTriggers === 'unacknowledged') { - triggerList = _.filter(triggerList, function (trigger) { - return !trigger.acknowledges; + if (event) { + trigger.acknowledges = _.map(event.acknowledges, ack => { + var time = new Date(+ack.clock * 1000); + ack.time = time.toLocaleString(); + ack.user = ack.alias + ' (' + ack.name + ' ' + ack.surname + ')'; + return ack; }); - } else if (self.panel.showTriggers === 'acknowledged') { - triggerList = _.filter(triggerList, 'acknowledges'); - } else { - triggerList = triggerList; } + }); + + // Filter triggers by description + var triggerFilter = self.panel.triggers.trigger.filter; + if (triggerFilter) { + triggerList = filterTriggers(triggerList, triggerFilter); + } - // Filter triggers by severity - triggerList = _.filter(triggerList, function (trigger) { - return self.panel.triggerSeverity[trigger.priority].show; + // Filter acknowledged triggers + if (self.panel.showTriggers === 'unacknowledged') { + triggerList = _.filter(triggerList, trigger => { + return !trigger.acknowledges; }); + } else if (self.panel.showTriggers === 'acknowledged') { + triggerList = _.filter(triggerList, 'acknowledges'); + } else { + triggerList = triggerList; + } - // Sort triggers - if (self.panel.sortTriggersBy.value === 'priority') { - triggerList = _.sortBy(triggerList, 'priority').reverse(); - } else { - triggerList = _.sortBy(triggerList, 'lastchangeUnix').reverse(); - } + // Filter triggers by severity + triggerList = _.filter(triggerList, trigger => { + return self.panel.triggerSeverity[trigger.priority].show; + }); - // Limit triggers number - self.triggerList = _.first(triggerList, self.panel.limit); + // Sort triggers + if (self.panel.sortTriggersBy.value === 'priority') { + triggerList = _.sortBy(triggerList, 'priority').reverse(); + } else { + triggerList = _.sortBy(triggerList, 'lastchangeUnix').reverse(); + } - self.renderingCompleted(); - }); - }); - }); + // Limit triggers number + self.triggerList = _.first(triggerList, self.panel.limit); + + self.renderingCompleted(); + }); + }); }); - }; - - function filterTriggers(triggers, triggerFilter) { - if (isRegex(triggerFilter)) { - return _.filter(triggers, function(trigger) { - return buildRegex(triggerFilter).test(trigger.description); - }); - } else { - return _.filter(triggers, function(trigger) { - return trigger.description === triggerFilter; - }); - } - } - - function isRegex(str) { - // Pattern for testing regex - var regexPattern = /^\/(.*)\/([gmi]*)$/m; - return regexPattern.test(str); - } - - function buildRegex(str) { - var regexPattern = /^\/(.*)\/([gmi]*)$/m; - var matches = str.match(regexPattern); - var pattern = matches[1]; - var flags = matches[2] !== "" ? matches[2] : undefined; - return new RegExp(pattern, flags); - } - - return TriggerPanelCtrl; - - })(sdk.PanelCtrl); - - return { - PanelCtrl: TriggerPanelCtrl - }; - -}); + }); + } +} + +TriggerPanelCtrl.templateUrl = 'module.html'; + +function filterTriggers(triggers, triggerFilter) { + if (isRegex(triggerFilter)) { + return _.filter(triggers, function(trigger) { + return buildRegex(triggerFilter).test(trigger.description); + }); + } else { + return _.filter(triggers, function(trigger) { + return trigger.description === triggerFilter; + }); + } +} + +function isRegex(str) { + // Pattern for testing regex + var regexPattern = /^\/(.*)\/([gmi]*)$/m; + return regexPattern.test(str); +} + +function buildRegex(str) { + var regexPattern = /^\/(.*)\/([gmi]*)$/m; + var matches = str.match(regexPattern); + var pattern = matches[1]; + var flags = matches[2] !== "" ? matches[2] : undefined; + return new RegExp(pattern, flags); +} + +export { + TriggerPanelCtrl, + TriggerPanelCtrl as PanelCtrl +}; From 29fbfbe2f8258eb4fabe9ef1ae0f9593bf3248bc Mon Sep 17 00:00:00 2001 From: Alexander Zobnin Date: Sat, 19 Mar 2016 17:27:32 +0300 Subject: [PATCH 158/205] Fixed default last change time format. --- src/panel-triggers/editor.js | 3 +-- src/panel-triggers/module.js | 3 +++ 2 files changed, 4 insertions(+), 2 deletions(-) diff --git a/src/panel-triggers/editor.js b/src/panel-triggers/editor.js index 160ce4fd4..8a16b6990 100644 --- a/src/panel-triggers/editor.js +++ b/src/panel-triggers/editor.js @@ -53,8 +53,7 @@ class TriggerPanelEditorCtrl{ var scopeDefaults = { metric: {}, inputStyles: {}, - oldTarget: _.cloneDeep(this.panel.triggers), - defaultTimeFormat: "DD MMM YYYY HH:mm:ss" + oldTarget: _.cloneDeep(this.panel.triggers) }; _.defaults(this, scopeDefaults); diff --git a/src/panel-triggers/module.js b/src/panel-triggers/module.js index fa35e3254..b263c7ae6 100644 --- a/src/panel-triggers/module.js +++ b/src/panel-triggers/module.js @@ -52,6 +52,8 @@ var triggerStatusMap = { '1': 'Problem' }; +var defaultTimeFormat = "DD MMM YYYY HH:mm:ss"; + class TriggerPanelCtrl extends PanelCtrl { /** @ngInject */ @@ -59,6 +61,7 @@ class TriggerPanelCtrl extends PanelCtrl { super($scope, $injector); this.datasourceSrv = datasourceSrv; this.triggerStatusMap = triggerStatusMap; + this.defaultTimeFormat = defaultTimeFormat; // Load panel defaults _.defaults(this.panel, panelDefaults); From 0aa425b33cf1fa0d50e437e9477ae48b3040bf24 Mon Sep 17 00:00:00 2001 From: Alexander Zobnin Date: Sat, 19 Mar 2016 17:31:17 +0300 Subject: [PATCH 159/205] Removed angular dependencies injection from triggerPanelEditor(). --- src/panel-triggers/editor.js | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/src/panel-triggers/editor.js b/src/panel-triggers/editor.js index 8a16b6990..82745709e 100644 --- a/src/panel-triggers/editor.js +++ b/src/panel-triggers/editor.js @@ -188,9 +188,7 @@ function isRegex(str) { return regexPattern.test(str); } -/** @ngInject */ -export function triggerPanelEditor($q, uiSegmentSrv, datasourceSrv, templateSrv, popoverSrv) { - 'use strict'; +export function triggerPanelEditor() { return { restrict: 'E', scope: true, From 404c2afd461b50d6f1f28e86dc0f94f8d99e84a2 Mon Sep 17 00:00:00 2001 From: Alexander Zobnin Date: Sat, 19 Mar 2016 18:09:40 +0300 Subject: [PATCH 160/205] Fixed annotation editor styles (used native Grafana 3.0). --- .../partials/annotations.editor.html | 112 +++++++++--------- 1 file changed, 58 insertions(+), 54 deletions(-) diff --git a/src/datasource-zabbix/partials/annotations.editor.html b/src/datasource-zabbix/partials/annotations.editor.html index c7676be2b..6e2e4954e 100644 --- a/src/datasource-zabbix/partials/annotations.editor.html +++ b/src/datasource-zabbix/partials/annotations.editor.html @@ -1,61 +1,65 @@ -
    -
    -
    Filter Triggers
    -
    -
      -
    • - Group -
    • -
    • - -
    • -
    • - Host -
    • -
    • - -
    • -
    -
    +
    +
    Filter Triggers
    +
    +
    + Group + +
    -
    -
      -
    • - Application -
    • -
    • - -
    • -
    • - Trigger -
    • -
    • - -
    • -
    -
    + +
    + Host + +
    -
    +
    +
    + Application + + +
    -
    -
    -
    Options
    -
    - - +
    + Trigger + + +
    +
    +
    +
    +
    + Minimum severity +
    +
    - - - +
    +
    +
    +
    Options
    +
    + + +
    From c9dfbe646c3a5f7aa1c90abd3d17c5dee28b25bb Mon Sep 17 00:00:00 2001 From: Alexander Zobnin Date: Sat, 19 Mar 2016 19:17:21 +0300 Subject: [PATCH 161/205] Added custom sass styles for triggers panel. --- Gruntfile.js | 19 +++- package.json | 1 + src/panel-triggers/module.html | 24 ++--- src/panel-triggers/module.js | 1 + src/panel-triggers/sass/panel_triggers.scss | 108 ++++++++++++++++++++ 5 files changed, 140 insertions(+), 13 deletions(-) create mode 100644 src/panel-triggers/sass/panel_triggers.scss diff --git a/Gruntfile.js b/Gruntfile.js index 1211d9d9e..3e7ff367a 100644 --- a/Gruntfile.js +++ b/Gruntfile.js @@ -55,7 +55,24 @@ module.exports = function(grunt) { }, }, + sass: { + options: { + sourceMap: true + }, + dist: { + files: { + 'dist/panel-triggers/css/panel_triggers.css' : 'src/panel-triggers/sass/panel_triggers.scss', + } + } + } + }); - grunt.registerTask('default', ['clean', 'copy:src_to_dist', 'copy:pluginDef', 'babel']); + grunt.registerTask('default', [ + 'clean', + 'copy:src_to_dist', + 'copy:pluginDef', + 'babel', + 'sass' + ]); }; diff --git a/package.json b/package.json index 64cde5b86..b4b96f1d2 100644 --- a/package.json +++ b/package.json @@ -19,6 +19,7 @@ "grunt": "~0.4.5", "babel": "~6.5.1", "grunt-babel": "~6.0.0", + "grunt-sass": "^1.1.0", "grunt-contrib-copy": "~0.8.2", "grunt-contrib-watch": "^0.6.1", "grunt-contrib-uglify": "~0.11.0", diff --git a/src/panel-triggers/module.html b/src/panel-triggers/module.html index b8d2eb3a0..4aad09a29 100644 --- a/src/panel-triggers/module.html +++ b/src/panel-triggers/module.html @@ -1,31 +1,31 @@ -
    -
    -
    - +
    +
    +
    +
    @@ -133,4 +133,4 @@
    -
    +
    Host
    -
    Status
    +
    Status
    -
    Severity
    +
    Severity
    -
    Issue
    +
    Issue
    -
    Last change
    +
    Last change
    -
    Age
    +
    Age
    -
    Info
    +
    Info
    - + diff --git a/src/panel-triggers/module.js b/src/panel-triggers/module.js index b263c7ae6..72cfdc9c1 100644 --- a/src/panel-triggers/module.js +++ b/src/panel-triggers/module.js @@ -15,6 +15,7 @@ import _ from 'lodash'; import moment from 'moment'; import {PanelCtrl} from 'app/plugins/sdk'; import {triggerPanelEditor} from './editor'; +import './css/panel_triggers.css!'; var defaultSeverity = [ { priority: 0, severity: 'Not classified', color: '#B7DBAB', show: true }, diff --git a/src/panel-triggers/sass/panel_triggers.scss b/src/panel-triggers/sass/panel_triggers.scss new file mode 100644 index 000000000..478f7dd2a --- /dev/null +++ b/src/panel-triggers/sass/panel_triggers.scss @@ -0,0 +1,108 @@ +$tight-form-func-bg: #333; +$blue: #33B5E5; +$dark-2: #1f1d1d; +$body-bg: rgb(20,20,20); + +$grafanaListAccent: lighten($dark-2, 2%); + +.triggers-panel-wrapper { + .panel-content { + padding: 0; + } + .panel-title-container { + padding-bottom: 4px; + } +} + +.triggers-panel-scroll { + overflow: auto; +} + +.triggers-panel-container { + padding-top: 2.2em; + position: relative; +} + +.triggers-panel-footer { + text-align: center; + font-size: 90%; + line-height: 2px; + + ul { + position: relative; + display: inline-block; + margin-left: 0; + margin-bottom: 0; + } + ul > li { + display: inline; // Remove list-style and block-level defaults + } + ul > li > a { + float: left; // Collapse white-space + padding: 4px 12px; + text-decoration: none; + border-left-width: 0; + + &:hover { + background-color: $tight-form-func-bg; + } + + &.active { + font-weight: bold; + color: $blue; + } + } +} + +.triggers-panel-table { + width: 100%; + border-collapse: collapse; + + th { + padding: 0; + + &:first-child { + .triggers-panel-table-header-inner { + padding-left: 15px; + } + } + } + + td { + padding: 0.45em 0 0.45em 1.1em; + border-bottom: 2px solid $body-bg; + border-right: 2px solid $body-bg; + + &:first-child { + padding-left: 15px; + } + &:last-child { + border-right: none; + } + } +} + +.triggers-panel-header-bg { + background: $grafanaListAccent; + border-top: 2px solid $body-bg; + border-bottom: 2px solid $body-bg; + height: 2.0em; + position: absolute; + top: 0; + right: 0; + left: 0; +} + +.triggers-panel-table-header-inner { + padding: 0.45em 0 0.45em 1.1em; + text-align: left; + color: $blue; + position: absolute; + top: 0; +} + +.triggers-panel-width-hack { + visibility: hidden; + height: 0px; + line-height: 0px; +} From cf07d7c2dfd58a5e704b2db6b97b075ce971c8d9 Mon Sep 17 00:00:00 2001 From: Alexander Zobnin Date: Fri, 25 Mar 2016 19:40:22 +0300 Subject: [PATCH 162/205] Working on cache rewriting (remove config caching). --- src/datasource-zabbix/query.controller.js | 110 ++++++++++++----- .../queryProcessor.service.js | 115 +++++++++++++++--- src/datasource-zabbix/zabbixAPI.service.js | 49 ++++++-- src/datasource-zabbix/zabbixCache.service.js | 54 +++++--- 4 files changed, 251 insertions(+), 77 deletions(-) diff --git a/src/datasource-zabbix/query.controller.js b/src/datasource-zabbix/query.controller.js index 98354e23a..921da2fbe 100644 --- a/src/datasource-zabbix/query.controller.js +++ b/src/datasource-zabbix/query.controller.js @@ -14,6 +14,10 @@ export class ZabbixQueryController extends QueryCtrl { // Call superclass constructor super($scope, $injector); + this.zabbix = this.datasource.zabbixAPI; + this.cache = this.datasource.zabbixCache; + this.$q = $q; + this.editorModes = { 0: 'num', 1: 'itservice', @@ -22,9 +26,9 @@ export class ZabbixQueryController extends QueryCtrl { // Map functions for bs-typeahead this.getGroupNames = _.partial(getMetricNames, this, 'groupList'); - this.getHostNames = _.partial(getMetricNames, this, 'filteredHosts'); - this.getApplicationNames = _.partial(getMetricNames, this, 'filteredApplications'); - this.getItemNames = _.partial(getMetricNames, this, 'filteredItems'); + this.getHostNames = _.partial(getMetricNames, this, 'hostList'); + this.getApplicationNames = _.partial(getMetricNames, this, 'appList'); + this.getItemNames = _.partial(getMetricNames, this, 'itemList'); this.init = function() { @@ -32,7 +36,8 @@ export class ZabbixQueryController extends QueryCtrl { var target = this.target; var scopeDefaults = { - metric: {} + metric: {}, + oldTarget: _.cloneDeep(this.target) }; _.defaults(this, scopeDefaults); @@ -85,49 +90,86 @@ export class ZabbixQueryController extends QueryCtrl { } initFilters() { - this.filterGroups(); - this.filterHosts(); - this.filterApplications(); - this.filterItems(); - } - - filterHosts() { var self = this; - var groupFilter = this.templateSrv.replace(this.target.group.filter); - this.datasource.queryProcessor.filterHosts(groupFilter).then(function(hosts) { - self.metric.filteredHosts = hosts; - }); + return this.$q.when(this.suggestGroups()) + .then(() => {return self.suggestHosts();}) + .then(() => {return self.suggestApps();}) + .then(() => {return self.suggestItems();}); } - filterGroups() { + suggestGroups() { var self = this; - this.datasource.queryProcessor.filterGroups().then(function(groups) { + return this.cache.getGroups().then(groups => { self.metric.groupList = groups; + return groups; }); } - filterApplications() { + suggestHosts() { var self = this; var groupFilter = this.templateSrv.replace(this.target.group.filter); - var hostFilter = this.templateSrv.replace(this.target.host.filter); - this.datasource.queryProcessor.filterApplications(groupFilter, hostFilter) - .then(function(apps) { - self.metric.filteredApplications = apps; + return this.datasource.queryProcessor + .filterGroups(self.metric.groupList, groupFilter) + .then(groups => { + var groupids = _.map(groups, 'groupid'); + return self.zabbix + .getHostsByGroups(groupids) + .then(hosts => { + self.metric.hostList = hosts; + return hosts; + }); }); } - filterItems() { + suggestApps() { var self = this; - var item_type = this.editorModes[this.target.mode]; - var groupFilter = this.templateSrv.replace(this.target.group.filter); var hostFilter = this.templateSrv.replace(this.target.host.filter); - var appFilter = this.templateSrv.replace(this.target.application.filter); - this.datasource.queryProcessor.filterItems(groupFilter, hostFilter, appFilter, - item_type, this.target.showDisabledItems).then(function(items) { - self.metric.filteredItems = items; + return this.datasource.queryProcessor + .filterHosts(self.metric.hostList, hostFilter) + .then(hosts => { + var hostids = _.map(hosts, 'hostid'); + return self.zabbix + .getApps(hostids) + .then(apps => { + return self.metric.appList = apps; + }); }); } + suggestItems() { + var self = this; + var appFilter = this.templateSrv.replace(this.target.application.filter); + if (appFilter) { + // Filter by applications + return this.datasource.queryProcessor + .filterApps(self.metric.appList, appFilter) + .then(apps => { + var appids = _.map(apps, 'applicationid'); + return self.zabbix + .getItems(undefined, appids) + .then(items => { + if (!self.target.showDisabledItems) { + items = _.filter(items, {'status': '0'}); + } + self.metric.itemList = items; + return items; + }); + }); + } else { + // Return all items belonged to selected hosts + var hostids = _.map(self.metric.hostList, 'hostid'); + return self.zabbix + .getItems(hostids) + .then(items => { + if (!self.target.showDisabledItems) { + items = _.filter(items, {'status': '0'}); + } + self.metric.itemList = items; + return items; + }); + } + } + onTargetPartChange(targetPart) { var regexStyle = {'color': '#CCA300'}; targetPart.isRegex = utils.isRegex(targetPart.filter); @@ -135,9 +177,13 @@ export class ZabbixQueryController extends QueryCtrl { } onTargetBlur() { - this.initFilters(); - this.parseTarget(); - this.panelCtrl.refresh(); + var newTarget = _.cloneDeep(this.target); + if (!_.isEqual(this.oldTarget, this.target)) { + this.oldTarget = newTarget; + this.initFilters(); + this.parseTarget(); + this.panelCtrl.refresh(); + } } parseTarget() { diff --git a/src/datasource-zabbix/queryProcessor.service.js b/src/datasource-zabbix/queryProcessor.service.js index d447e5534..b229190a4 100644 --- a/src/datasource-zabbix/queryProcessor.service.js +++ b/src/datasource-zabbix/queryProcessor.service.js @@ -39,31 +39,26 @@ angular.module('grafana.services').factory('QueryProcessor', function($q) { } } - filterGroups(groupFilter) { - return this.cache.getGroups().then(function(groupList) { - return groupList; - }); + filterGroups(groups, groupFilter) { + return this.$q.when( + findByFilter(groups, groupFilter) + ); } /** * Get list of host belonging to given groups. * @return list of hosts */ - filterHosts(groupFilter) { - var self = this; - return this.cache.getGroups().then(function(groups) { - groups = findByFilter(groups, groupFilter); - var hostids = _.flatten(_.map(groups, 'hosts')); - if (hostids.length) { - return self.cache.getIndexedHosts().then(function(hosts) { - return _.map(hostids, function(hostid) { - return hosts[hostid]; - }); - }); - } else { - return []; - } - }); + filterHosts(hosts, hostFilter) { + return this.$q.when( + findByFilter(hosts, hostFilter) + ); + } + + filterApps(apps, appFilter) { + return this.$q.when( + findByFilter(apps, appFilter) + ); } /** @@ -151,10 +146,65 @@ angular.module('grafana.services').factory('QueryProcessor', function($q) { }); } + buildFromCache(groupFilter, hostFilter, appFilter, itemFilter, showDisabledItems) { + var self = this; + return this.cache + .getGroups() + .then(groups => { + return findByFilter(groups, groupFilter); + }) + .then(groups => { + var groupids = _.map(groups, 'groupid'); + return self.cache + .getHosts(groupids) + .then(hosts => { + return findByFilter(hosts, hostFilter); + }); + }) + .then(hosts => { + var hostids = _.map(hosts, 'hostid'); + if (appFilter) { + return self.cache + .getApps(hostids) + .then(apps => { + // Use getByFilter for proper item filtering + return getByFilter(apps, appFilter); + }); + } else { + return { + appFilterEmpty: true, + hostids: hostids + }; + } + }) + .then(apps => { + if (apps.appFilterEmpty) { + return self.cache + .getItems(apps.hostids) + .then(items => { + if (showDisabledItems) { + items = _.filter(items, {'status': '0'}); + } + return getByFilter(items, itemFilter); + }); + } else { + var appids = _.map(apps, 'applicationid'); + return self.cache + .getItems(undefined, appids) + .then(items => { + if (showDisabledItems) { + items = _.filter(items, {'status': '0'}); + } + return getByFilter(items, itemFilter); + }); + } + }); + } + /** * Build query - convert target filters to array of Zabbix items */ - buildFromCache(groupFilter, hostFilter, appFilter, itemFilter) { + _buildFromCache(groupFilter, hostFilter, appFilter, itemFilter) { return this.filterItems(groupFilter, hostFilter, appFilter).then(function(items) { if (items.length) { if (utils.isRegex(itemFilter)) { @@ -309,6 +359,23 @@ function findByName(list, name) { } } +/** + * Different hosts can contains applications and items with same name. + * For this reason use _.filter, which return all elements instead _.find, + * which return only first finded. + * @param {[type]} list list of elements + * @param {[type]} name app name + * @return {[type]} array with finded element or undefined + */ +function filterByName(list, name) { + var finded = _.filter(list, {'name': name}); + if (finded) { + return finded; + } else { + return undefined; + } +} + function findByRegex(list, regex) { var filterPattern = utils.buildRegex(regex); return _.filter(list, function (zbx_obj) { @@ -324,6 +391,14 @@ function findByFilter(list, filter) { } } +function getByFilter(list, filter) { + if (utils.isRegex(filter)) { + return findByRegex(list, filter); + } else { + return filterByName(list, filter); + } +} + function getFromIndex(index, objids) { return _.map(objids, function(id) { return index[id]; diff --git a/src/datasource-zabbix/zabbixAPI.service.js b/src/datasource-zabbix/zabbixAPI.service.js index 7661c0c84..b3cf98bb3 100644 --- a/src/datasource-zabbix/zabbixAPI.service.js +++ b/src/datasource-zabbix/zabbixAPI.service.js @@ -1,5 +1,6 @@ import angular from 'angular'; import _ from 'lodash'; +import * as utils from './utils'; import './zabbixAPICore.service'; /** @ngInject */ @@ -116,24 +117,42 @@ function ZabbixAPIService($q, alertSrv, zabbixAPICoreService) { getGroups() { var params = { output: ['name'], - sortfield: 'name', - selectHosts: [] + sortfield: 'name' }; return this.request('hostgroup.get', params); } - getHosts() { + getHosts(groupids) { var params = { output: ['name', 'host'], - sortfield: 'name', - selectGroups: [], - selectApplications: ['applicationid'] + sortfield: 'name' }; + if (groupids) { + params.groupids = groupids; + } return this.request('host.get', params); } + getHostsByGroups(groupids) { + var params = { + output: ['name', 'host'], + groupids: groupids + }; + + return this.request('host.get', params); + } + + getApps(hostids) { + var params = { + output: ['applicationid', 'name'], + hostids: hostids + }; + + return this.request('application.get', params); + } + getApplications() { var params = { output: ['applicationid', 'name'], @@ -147,7 +166,7 @@ function ZabbixAPIService($q, alertSrv, zabbixAPICoreService) { return this.request('application.get', params); } - getItems() { + getItems(hostids, appids) { var params = { output: [ 'name', 'key_', @@ -157,10 +176,22 @@ function ZabbixAPIService($q, alertSrv, zabbixAPICoreService) { 'state' ], sortfield: 'name', - selectApplications: [] }; + if (hostids) { + params.hostids = hostids; + } + if (appids) { + params.applicationids = appids; + } - return this.request('item.get', params); + return this.request('item.get', params) + .then(items => { + return _.forEach(items, item => { + item.item = item.name; + item.name = utils.expandItemName(item.item, item.key_); + return item; + }); + }); } /** diff --git a/src/datasource-zabbix/zabbixCache.service.js b/src/datasource-zabbix/zabbixCache.service.js index 65a00cf7f..ebac8e95e 100644 --- a/src/datasource-zabbix/zabbixCache.service.js +++ b/src/datasource-zabbix/zabbixCache.service.js @@ -45,21 +45,12 @@ angular.module('grafana.services').factory('ZabbixCachingProxy', function($q, $i _refresh() { var self = this; var promises = [ - this.zabbixAPI.getGroups(), - this.zabbixAPI.getHosts(), - this.zabbixAPI.getApplications(), - this.zabbixAPI.getItems(), - this.zabbixAPI.getHostsExtend() + this.zabbixAPI.getGroups() ]; return this.$q.all(promises).then(function(results) { if (results.length) { - self._groups = convertGroups(results[0]); - self._hosts = convertHosts(results[1]); - self._applications = convertApplications(results[2]); - self._items = convertItems(results[3]); - self._idx_apps = indexApps(results[2]); - self._idx_hosts = indexHosts(results[4]); + self._groups = results[0]; } self._initialized = true; }); @@ -70,13 +61,44 @@ angular.module('grafana.services').factory('ZabbixCachingProxy', function($q, $i if (this._groups) { return this.$q.when(self._groups); } else { - return this.refresh().then(function() { - return self._groups; - }); + return this.zabbixAPI + .getGroups() + .then(groups => { + self._groups = groups; + return self._groups; + }); } } - getHosts() { + getApps(hostids) { + return this.zabbixAPI + .getApps(hostids) + .then(apps => { + return apps; + }); + } + + getHosts(groupids) { + var self = this; + return this.zabbixAPI + .getHosts(groupids) + .then(hosts => { + self._hosts = _.union(self._hosts, hosts); + return hosts; + }); + } + + getItems(hostids, appids) { + var self = this; + return this.zabbixAPI + .getItems(hostids, appids) + .then(items => { + self._items = _.union(self._items, items); + return items; + }); + } + + _getHosts() { var self = this; if (this._hosts) { return this.$q.when(self._hosts); @@ -120,7 +142,7 @@ angular.module('grafana.services').factory('ZabbixCachingProxy', function($q, $i } } - getItems(type) { + _getItems(type) { var self = this; if (this._items) { return this.$q.when(filterItems(self._items, type)); From d15ffd4cd08339283c3b15641c60543b594ef1c6 Mon Sep 17 00:00:00 2001 From: Alexander Zobnin Date: Sun, 27 Mar 2016 18:57:20 +0300 Subject: [PATCH 163/205] Fixed template variable query with new cache model. --- src/datasource-zabbix/datasource.js | 17 +++-- .../queryProcessor.service.js | 73 +++++++++++++++++++ 2 files changed, 83 insertions(+), 7 deletions(-) diff --git a/src/datasource-zabbix/datasource.js b/src/datasource-zabbix/datasource.js index 1291c931c..8f9a08844 100644 --- a/src/datasource-zabbix/datasource.js +++ b/src/datasource-zabbix/datasource.js @@ -293,22 +293,25 @@ export class ZabbixAPIDatasource { // Get items if (parts.length === 4) { - return this.queryProcessor.filterItems(template.group, template.host, - template.app, 'all', true) - .then(function(items) { - return _.map(items, formatMetric); - }); + // Search for all items, even it's not belong to any application + if (template.app === '/.*/') { + template.app = ''; + } + return this.queryProcessor.getItems(template.group, template.host, template.app) + .then(function(items) { + return _.map(items, formatMetric); + }); } // Get applications else if (parts.length === 3) { - return this.queryProcessor.filterApplications(template.group, template.host) + return this.queryProcessor.getApps(template.group, template.host) .then(function(apps) { return _.map(apps, formatMetric); }); } // Get hosts else if (parts.length === 2) { - return this.queryProcessor.filterHosts(template.group) + return this.queryProcessor.getHosts(template.group) .then(function(hosts) { return _.map(hosts, formatMetric); }); diff --git a/src/datasource-zabbix/queryProcessor.service.js b/src/datasource-zabbix/queryProcessor.service.js index b229190a4..79047c331 100644 --- a/src/datasource-zabbix/queryProcessor.service.js +++ b/src/datasource-zabbix/queryProcessor.service.js @@ -201,6 +201,79 @@ angular.module('grafana.services').factory('QueryProcessor', function($q) { }); } + getHosts(groupFilter) { + var self = this; + return this.cache + .getGroups() + .then(groups => { + return findByFilter(groups, groupFilter); + }) + .then(groups => { + var groupids = _.map(groups, 'groupid'); + return self.cache.getHosts(groupids); + }); + } + + getApps(groupFilter, hostFilter) { + var self = this; + return this + .getHosts(groupFilter) + .then(hosts => { + return findByFilter(hosts, hostFilter); + }) + .then(hosts => { + var hostids = _.map(hosts, 'hostid'); + return self.cache.getApps(hostids); + }); + } + + getItems(groupFilter, hostFilter, appFilter, showDisabledItems) { + var self = this; + return this + .getHosts(groupFilter) + .then(hosts => { + return findByFilter(hosts, hostFilter); + }) + .then(hosts => { + var hostids = _.map(hosts, 'hostid'); + if (appFilter) { + return self.cache + .getApps(hostids) + .then(apps => { + // Use getByFilter for proper item filtering + return getByFilter(apps, appFilter); + }); + } else { + return { + appFilterEmpty: true, + hostids: hostids + }; + } + }) + .then(apps => { + if (apps.appFilterEmpty) { + return self.cache + .getItems(apps.hostids, undefined) + .then(items => { + if (showDisabledItems) { + items = _.filter(items, {'status': '0'}); + } + return items; + }); + } else { + var appids = _.map(apps, 'applicationid'); + return self.cache + .getItems(undefined, appids) + .then(items => { + if (showDisabledItems) { + items = _.filter(items, {'status': '0'}); + } + return items; + }); + } + }); + } + /** * Build query - convert target filters to array of Zabbix items */ From 539444b22de40c9122e845ed11297a51b93c08c4 Mon Sep 17 00:00:00 2001 From: Alexander Zobnin Date: Sun, 27 Mar 2016 19:02:56 +0300 Subject: [PATCH 164/205] buildFromCache method refactor. --- .../queryProcessor.service.js | 60 ++----------------- 1 file changed, 5 insertions(+), 55 deletions(-) diff --git a/src/datasource-zabbix/queryProcessor.service.js b/src/datasource-zabbix/queryProcessor.service.js index 79047c331..98a898200 100644 --- a/src/datasource-zabbix/queryProcessor.service.js +++ b/src/datasource-zabbix/queryProcessor.service.js @@ -147,57 +147,9 @@ angular.module('grafana.services').factory('QueryProcessor', function($q) { } buildFromCache(groupFilter, hostFilter, appFilter, itemFilter, showDisabledItems) { - var self = this; - return this.cache - .getGroups() - .then(groups => { - return findByFilter(groups, groupFilter); - }) - .then(groups => { - var groupids = _.map(groups, 'groupid'); - return self.cache - .getHosts(groupids) - .then(hosts => { - return findByFilter(hosts, hostFilter); - }); - }) - .then(hosts => { - var hostids = _.map(hosts, 'hostid'); - if (appFilter) { - return self.cache - .getApps(hostids) - .then(apps => { - // Use getByFilter for proper item filtering - return getByFilter(apps, appFilter); - }); - } else { - return { - appFilterEmpty: true, - hostids: hostids - }; - } - }) - .then(apps => { - if (apps.appFilterEmpty) { - return self.cache - .getItems(apps.hostids) - .then(items => { - if (showDisabledItems) { - items = _.filter(items, {'status': '0'}); - } - return getByFilter(items, itemFilter); - }); - } else { - var appids = _.map(apps, 'applicationid'); - return self.cache - .getItems(undefined, appids) - .then(items => { - if (showDisabledItems) { - items = _.filter(items, {'status': '0'}); - } - return getByFilter(items, itemFilter); - }); - } + return this.getItems(groupFilter, hostFilter, appFilter, showDisabledItems) + .then(items => { + return getByFilter(items, itemFilter); }); } @@ -216,8 +168,7 @@ angular.module('grafana.services').factory('QueryProcessor', function($q) { getApps(groupFilter, hostFilter) { var self = this; - return this - .getHosts(groupFilter) + return this.getHosts(groupFilter) .then(hosts => { return findByFilter(hosts, hostFilter); }) @@ -229,8 +180,7 @@ angular.module('grafana.services').factory('QueryProcessor', function($q) { getItems(groupFilter, hostFilter, appFilter, showDisabledItems) { var self = this; - return this - .getHosts(groupFilter) + return this.getHosts(groupFilter) .then(hosts => { return findByFilter(hosts, hostFilter); }) From c5e8776936e54152dd5c7626ffa60c0646f5bbf0 Mon Sep 17 00:00:00 2001 From: Alexander Zobnin Date: Sun, 27 Mar 2016 19:08:56 +0300 Subject: [PATCH 165/205] Removed unused functions. --- .../queryProcessor.service.js | 113 ++---------------- 1 file changed, 11 insertions(+), 102 deletions(-) diff --git a/src/datasource-zabbix/queryProcessor.service.js b/src/datasource-zabbix/queryProcessor.service.js index 98a898200..392d2e22c 100644 --- a/src/datasource-zabbix/queryProcessor.service.js +++ b/src/datasource-zabbix/queryProcessor.service.js @@ -62,90 +62,8 @@ angular.module('grafana.services').factory('QueryProcessor', function($q) { } /** - * Get list of applications belonging to given groups and hosts. - * @return list of applications belonging to given hosts + * Build query - convert target filters to array of Zabbix items */ - filterApplications(groupFilter, hostFilter) { - var promises = [ - this.filterHosts(groupFilter), - this.cache.getApplications() - ]; - - return this.$q.all(promises).then(function(results) { - var hostList = results[0]; - var applicationList = results[1]; - - var hosts = findByFilter(hostList, hostFilter); - if (hosts) { - var hostsids = _.map(hosts, 'hostid'); - return _.filter(applicationList, function (appObj) { - return _.intersection(hostsids, appObj.hosts).length; - }); - } else { - return []; - } - }); - } - - filterItems(groupFilter, hostFilter, appFilter, itemType, showDisabledItems) { - var hosts; - var apps; - var items; - - var promises = [ - this.filterHosts(groupFilter), - this.filterApplications(groupFilter, hostFilter), - this.cache.getIndexedHosts(), - this.cache.getIndexedApplications() - ]; - - return this.$q.all(promises).then(function(results) { - var hostList = results[0]; - var applicationList = results[1]; - var idx_hosts = results[2]; - var idx_apps = results[3]; - - // Filter hosts - hosts = findByFilter(hostList, hostFilter); - idx_hosts = getFromIndex(idx_hosts, _.map(hosts, 'hostid')); - - // Filter applications - if (appFilter === "") { - // Get all items - apps = undefined; - if (hosts) { - // Get all items in given hosts - items = _.flatten(_.map(idx_hosts, function(host) { - return _.values(host.idx_items); - }), true); - } - } else { - apps = findByFilter(applicationList, appFilter); - } - - if (apps) { - // Get ids for finded applications - var appids = _.flatten(_.map(apps, 'applicationids')); - appids = _.flatten(_.map(_.map(hosts, 'applications'), function(apps) { - return _.intersection(apps, appids); - })); - - // For each finded host get list of items in finded applications - items = _.flatten(_.map(idx_hosts, function(host) { - var host_apps = _.intersection(appids, host.applications); - var host_itemids = _.flatten(_.map(getFromIndex(idx_apps, host_apps), 'itemids')); - return _.values(getFromIndex(host.idx_items, host_itemids)); - }), true); - } - - if (!showDisabledItems) { - items = _.filter(items, {'status': '0'}); - } - - return items; - }); - } - buildFromCache(groupFilter, hostFilter, appFilter, itemFilter, showDisabledItems) { return this.getItems(groupFilter, hostFilter, appFilter, showDisabledItems) .then(items => { @@ -153,6 +71,10 @@ angular.module('grafana.services').factory('QueryProcessor', function($q) { }); } + /** + * Get list of host belonging to given groups. + * @return list of hosts + */ getHosts(groupFilter) { var self = this; return this.cache @@ -166,6 +88,10 @@ angular.module('grafana.services').factory('QueryProcessor', function($q) { }); } + /** + * Get list of applications belonging to given groups and hosts. + * @return list of applications belonging to given hosts + */ getApps(groupFilter, hostFilter) { var self = this; return this.getHosts(groupFilter) @@ -224,23 +150,6 @@ angular.module('grafana.services').factory('QueryProcessor', function($q) { }); } - /** - * Build query - convert target filters to array of Zabbix items - */ - _buildFromCache(groupFilter, hostFilter, appFilter, itemFilter) { - return this.filterItems(groupFilter, hostFilter, appFilter).then(function(items) { - if (items.length) { - if (utils.isRegex(itemFilter)) { - return findByFilter(items, itemFilter); - } else { - return _.filter(items, {'name': itemFilter}); - } - } else { - return []; - } - }); - } - /** * Build query - convert target filters to array of Zabbix items */ @@ -255,7 +164,7 @@ angular.module('grafana.services').factory('QueryProcessor', function($q) { } }); }), - this.filterHosts(groupFilter).then(function(hosts) { + this.getHosts(groupFilter).then(function(hosts) { return _.filter(hosts, function(host) { if (utils.isRegex(hostFilter)) { return utils.buildRegex(hostFilter).test(host.name); @@ -264,7 +173,7 @@ angular.module('grafana.services').factory('QueryProcessor', function($q) { } }); }), - this.filterApplications(groupFilter, hostFilter).then(function(apps) { + this.getApps(groupFilter, hostFilter).then(function(apps) { return _.filter(apps, function(app) { if (utils.isRegex(appFilter)) { return utils.buildRegex(appFilter).test(app.name); From 935f43ae1b9e006a59efa9c21daca0ebc5fb0a48 Mon Sep 17 00:00:00 2001 From: Alexander Zobnin Date: Sun, 27 Mar 2016 19:13:01 +0300 Subject: [PATCH 166/205] Fixed metric editor in triggers panel. --- src/datasource-zabbix/queryProcessor.service.js | 4 ++++ src/panel-triggers/editor.js | 6 +++--- 2 files changed, 7 insertions(+), 3 deletions(-) diff --git a/src/datasource-zabbix/queryProcessor.service.js b/src/datasource-zabbix/queryProcessor.service.js index 392d2e22c..49ab271c1 100644 --- a/src/datasource-zabbix/queryProcessor.service.js +++ b/src/datasource-zabbix/queryProcessor.service.js @@ -71,6 +71,10 @@ angular.module('grafana.services').factory('QueryProcessor', function($q) { }); } + getGroups() { + return this.cache.getGroups(); + } + /** * Get list of host belonging to given groups. * @return list of hosts diff --git a/src/panel-triggers/editor.js b/src/panel-triggers/editor.js index 82745709e..186307e20 100644 --- a/src/panel-triggers/editor.js +++ b/src/panel-triggers/editor.js @@ -85,7 +85,7 @@ class TriggerPanelEditorCtrl{ filterGroups() { var self = this; - this.datasource.queryProcessor.filterGroups().then(function(groups) { + this.datasource.queryProcessor.getGroups().then(function(groups) { self.metric.groupList = groups; }); } @@ -93,7 +93,7 @@ class TriggerPanelEditorCtrl{ filterHosts() { var self = this; var groupFilter = this.templateSrv.replace(this.panel.triggers.group.filter); - this.datasource.queryProcessor.filterHosts(groupFilter).then(function(hosts) { + this.datasource.queryProcessor.getHosts(groupFilter).then(function(hosts) { self.metric.filteredHosts = hosts; }); } @@ -102,7 +102,7 @@ class TriggerPanelEditorCtrl{ var self = this; var groupFilter = this.templateSrv.replace(this.panel.triggers.group.filter); var hostFilter = this.templateSrv.replace(this.panel.triggers.host.filter); - this.datasource.queryProcessor.filterApplications(groupFilter, hostFilter) + this.datasource.queryProcessor.getApps(groupFilter, hostFilter) .then(function(apps) { self.metric.filteredApplications = apps; }); From ca3a0c22ac308cbfdf01367c5af942f55e5b9641 Mon Sep 17 00:00:00 2001 From: Alexander Zobnin Date: Sun, 27 Mar 2016 20:17:53 +0300 Subject: [PATCH 167/205] Optimized groups, hosts, apps and items requests (call once). --- src/datasource-zabbix/zabbixCache.service.js | 60 +++++++++++++++----- 1 file changed, 46 insertions(+), 14 deletions(-) diff --git a/src/datasource-zabbix/zabbixCache.service.js b/src/datasource-zabbix/zabbixCache.service.js index ebac8e95e..af0f62363 100644 --- a/src/datasource-zabbix/zabbixCache.service.js +++ b/src/datasource-zabbix/zabbixCache.service.js @@ -40,6 +40,22 @@ angular.module('grafana.services').factory('ZabbixCachingProxy', function($q, $i // Don't run duplicated history requests this.getHistory = callHistoryOnce(_.bind(this.zabbixAPI.getHistory, this.zabbixAPI), this.historyPromises); + + this.groupPromises = {}; + this.getGroupsOnce = callAPIRequestOnce(_.bind(this.zabbixAPI.getGroups, this.zabbixAPI), + this.groupPromises); + + this.hostPromises = {}; + this.getHostsOnce = callAPIRequestOnce(_.bind(this.zabbixAPI.getHosts, this.zabbixAPI), + this.hostPromises); + + this.appPromises = {}; + this.getAppsOnce = callAPIRequestOnce(_.bind(this.zabbixAPI.getApps, this.zabbixAPI), + this.appPromises); + + this.itemPromises = {}; + this.getItemsOnce = callAPIRequestOnce(_.bind(this.zabbixAPI.getItems, this.zabbixAPI), + this.itemPromises); } _refresh() { @@ -61,8 +77,7 @@ angular.module('grafana.services').factory('ZabbixCachingProxy', function($q, $i if (this._groups) { return this.$q.when(self._groups); } else { - return this.zabbixAPI - .getGroups() + return this.getGroupsOnce() .then(groups => { self._groups = groups; return self._groups; @@ -70,28 +85,25 @@ angular.module('grafana.services').factory('ZabbixCachingProxy', function($q, $i } } - getApps(hostids) { - return this.zabbixAPI - .getApps(hostids) - .then(apps => { - return apps; - }); - } - getHosts(groupids) { var self = this; - return this.zabbixAPI - .getHosts(groupids) + return this.getHostsOnce(groupids) .then(hosts => { self._hosts = _.union(self._hosts, hosts); return hosts; }); } + getApps(hostids) { + return this.getAppsOnce(hostids) + .then(apps => { + return apps; + }); + } + getItems(hostids, appids) { var self = this; - return this.zabbixAPI - .getItems(hostids, appids) + return this.getItemsOnce(hostids, appids) .then(items => { self._items = _.union(self._items, items); return items; @@ -197,6 +209,26 @@ angular.module('grafana.services').factory('ZabbixCachingProxy', function($q, $i } } + function callAPIRequestOnce(func, promiseKeeper) { + return function() { + var itemids = _.map(arguments[0], 'itemid'); + var stamp = itemids.join() + arguments[1] + arguments[2]; + var hash = stamp.getHash(); + + var deferred = $q.defer(); + if (!promiseKeeper[hash]) { + promiseKeeper[hash] = deferred.promise; + func.apply(this, arguments).then(function(result) { + deferred.resolve(result); + promiseKeeper[hash] = null; + }); + } else { + return promiseKeeper[hash]; + } + return deferred.promise; + }; + } + function callHistoryOnce(func, promiseKeeper) { return function() { var itemids = _.map(arguments[0], 'itemid'); From b83100815c581b84f88d94a2a527ce7a2522560e Mon Sep 17 00:00:00 2001 From: Alexander Zobnin Date: Sun, 27 Mar 2016 20:22:23 +0300 Subject: [PATCH 168/205] Removed unused functions from zabbixCache.service.js --- src/datasource-zabbix/zabbixCache.service.js | 163 ------------------- 1 file changed, 163 deletions(-) diff --git a/src/datasource-zabbix/zabbixCache.service.js b/src/datasource-zabbix/zabbixCache.service.js index af0f62363..2ba8ef9c7 100644 --- a/src/datasource-zabbix/zabbixCache.service.js +++ b/src/datasource-zabbix/zabbixCache.service.js @@ -110,61 +110,6 @@ angular.module('grafana.services').factory('ZabbixCachingProxy', function($q, $i }); } - _getHosts() { - var self = this; - if (this._hosts) { - return this.$q.when(self._hosts); - } else { - return this.refresh().then(function() { - return self._hosts; - }); - } - } - - getIndexedHosts() { - var self = this; - if (this._idx_hosts) { - return this.$q.when(self._idx_hosts); - } else { - return this.refresh().then(function() { - return self._idx_hosts; - }); - } - } - - getIndexedApplications() { - var self = this; - if (this._idx_apps) { - return this.$q.when(self._idx_apps); - } else { - return this.refresh().then(function() { - return self._idx_apps; - }); - } - } - - getApplications() { - var self = this; - if (this._applications) { - return this.$q.when(self._applications); - } else { - return this.refresh().then(function() { - return self._applications; - }); - } - } - - _getItems(type) { - var self = this; - if (this._items) { - return this.$q.when(filterItems(self._items, type)); - } else { - return this.refresh().then(function() { - return filterItems(self._items, type); - }); - } - } - getHistoryFromCache(items, time_from, time_till) { var deferred = this.$q.defer(); var historyStorage = this.storage.history; @@ -268,114 +213,6 @@ angular.module('grafana.services').factory('ZabbixCachingProxy', function($q, $i return ZabbixCachingProxy; }); -/** - * Convert host.get response to cache format - * host.groups - array of group ids - */ -function convertHosts(hosts) { - return _.forEach(hosts, function(host) { - host.groups = _.map(host.groups, 'groupid'); - return host; - }); -} - -function convertGroups(groups) { - return _.forEach(groups, function(group) { - group.hosts = _.map(group.hosts, 'hostid'); - return group; - }); -} - -/** - * Group Zabbix applications by name - * host.hosts - array of host ids - */ -function convertApplications(applications) { - return _.map(_.groupBy(applications, 'name'), function(value, key) { - //console.log(value); - // Hack for supporting different apis (2.2 vs 2.4 vs 3.0) - var hostField = 'host'; - if (value[0] && value[0]['hosts']) { - // For Zabbix 2.2 - hostField = 'hosts'; - } - - return { - name: key, - applicationids: _.map(value, 'applicationid'), - itemids: _.uniq(_.map(_.flatten(value, 'items'), 'itemid')), - hosts: _.uniq(_.map(_.flatten(value, hostField), 'hostid')) - }; - }); -} - -function indexHosts(hosts) { - return _.indexBy(_.map(hosts, function(host) { - - // Expand item names - host.items = _.forEach(host.items, function(item) { - item.item = item.name; - item.name = utils.expandItemName(item.item, item.key_); - return item; - }); - - host.applications = _.map(host.applications, 'applicationid'); - host.idx_items = indexItems(host.items); - host.items = _.map(host.items, 'itemid'); - return host; - }), 'hostid'); -} - -function indexApps(applications) { - return _.indexBy(_.map(applications, function(app) { - return { - name: app.name, - applicationid: app.applicationid, - host: _.first(_.map(app.hosts, 'hostid')), - itemids: _.map(app.items, 'itemid') - }; - }), 'applicationid'); -} - -function indexItems(items) { - return _.indexBy(_.map(items, function(item) { - return item; - }), 'itemid'); -} - -/** - * Convert item.get response to cache format - * item.applications - array of application ids - * item.item - original item name returned by api (ie "CPU $2 time") - * item.name - expanded name (ie "CPU system time") - */ -function convertItems(items) { - return _.forEach(items, function(item) { - item.applications = _.map(item.applications, 'applicationid'); - item.item = item.name; - item.name = utils.expandItemName(item.item, item.key_); - return item; - }); -} - -function filterItems(items, type) { - switch (type) { - case 'num': - return _.filter(items, function(item) { - return (item.value_type === '0' || - item.value_type === '3'); - }); - case 'text': - return _.filter(items, function(item) { - return (item.value_type === '1' || - item.value_type === '2' || - item.value_type === '4'); - }); - default: - return items; - } -} - String.prototype.getHash = function() { var hash = 0, i, chr, len; if (this.length === 0) { From c00132ff0451ddb1806123f3267c23f1fa91ac5c Mon Sep 17 00:00:00 2001 From: Alexander Zobnin Date: Sun, 27 Mar 2016 22:10:35 +0300 Subject: [PATCH 169/205] Added template variables support for Triggers panel. Fixed panel refreshing. --- src/panel-triggers/module.js | 21 +++++++++++++-------- 1 file changed, 13 insertions(+), 8 deletions(-) diff --git a/src/panel-triggers/module.js b/src/panel-triggers/module.js index 72cfdc9c1..a7749f89b 100644 --- a/src/panel-triggers/module.js +++ b/src/panel-triggers/module.js @@ -13,7 +13,7 @@ import _ from 'lodash'; import moment from 'moment'; -import {PanelCtrl} from 'app/plugins/sdk'; +import {MetricsPanelCtrl} from 'app/plugins/sdk'; import {triggerPanelEditor} from './editor'; import './css/panel_triggers.css!'; @@ -55,12 +55,13 @@ var triggerStatusMap = { var defaultTimeFormat = "DD MMM YYYY HH:mm:ss"; -class TriggerPanelCtrl extends PanelCtrl { +class TriggerPanelCtrl extends MetricsPanelCtrl { /** @ngInject */ - constructor($scope, $injector, $q, $element, datasourceSrv) { + constructor($scope, $injector, $q, $element, datasourceSrv, templateSrv) { super($scope, $injector); this.datasourceSrv = datasourceSrv; + this.templateSrv = templateSrv; this.triggerStatusMap = triggerStatusMap; this.defaultTimeFormat = defaultTimeFormat; @@ -73,9 +74,12 @@ class TriggerPanelCtrl extends PanelCtrl { // Add panel editor initEditMode() { - super.initEditMode(); + //super.initEditMode(); + this.editorTabs = []; + this.addEditorTab('General', 'public/app/partials/panelgeneral.html'); this.icon = "fa fa-lightbulb-o"; this.addEditorTab('Options', triggerPanelEditor, 2); + this.editModeInitiated = true; } refreshData() { @@ -85,11 +89,12 @@ class TriggerPanelCtrl extends PanelCtrl { return this.datasourceSrv.get(this.panel.datasource).then(datasource => { var zabbix = datasource.zabbixAPI; var queryProcessor = datasource.queryProcessor; - var triggerFilter = self.panel.triggers; var showEvents = self.panel.showEvents.value; - var buildQuery = queryProcessor.buildTriggerQuery(triggerFilter.group.filter, - triggerFilter.host.filter, - triggerFilter.application.filter); + var triggerFilter = self.panel.triggers; + var groupFilter = self.templateSrv.replace(triggerFilter.group.filter); + var hostFilter = self.templateSrv.replace(triggerFilter.host.filter); + var appFilter = self.templateSrv.replace(triggerFilter.application.filter); + var buildQuery = queryProcessor.buildTriggerQuery(groupFilter, hostFilter, appFilter); return buildQuery.then(query => { return zabbix.getTriggers(query.groupids, query.hostids, From 358d5fc1ac2f07cd881a8bf62edaff92c97253a2 Mon Sep 17 00:00:00 2001 From: Alexander Zobnin Date: Sun, 27 Mar 2016 22:39:42 +0300 Subject: [PATCH 170/205] Added initEditMode() explanations. --- src/panel-triggers/module.js | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/src/panel-triggers/module.js b/src/panel-triggers/module.js index a7749f89b..1c0b71c0c 100644 --- a/src/panel-triggers/module.js +++ b/src/panel-triggers/module.js @@ -74,9 +74,11 @@ class TriggerPanelCtrl extends MetricsPanelCtrl { // Add panel editor initEditMode() { - //super.initEditMode(); + // Use initialize method from PanelCtrl instead MetricsPanelCtrl. + // We don't need metric editor from Metrics Panel. this.editorTabs = []; this.addEditorTab('General', 'public/app/partials/panelgeneral.html'); + this.icon = "fa fa-lightbulb-o"; this.addEditorTab('Options', triggerPanelEditor, 2); this.editModeInitiated = true; From ac7bb9de3885c490fe6009891c88220a71c55d94 Mon Sep 17 00:00:00 2001 From: Alexander Zobnin Date: Sun, 27 Mar 2016 22:49:28 +0300 Subject: [PATCH 171/205] Changed default OK event color. --- src/panel-triggers/module.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/panel-triggers/module.js b/src/panel-triggers/module.js index 1c0b71c0c..9655facc0 100644 --- a/src/panel-triggers/module.js +++ b/src/panel-triggers/module.js @@ -45,7 +45,7 @@ var panelDefaults = { sortTriggersBy: { text: 'last change', value: 'lastchange' }, showEvents: { text: 'Problem events', value: '1' }, triggerSeverity: defaultSeverity, - okEventColor: '#890F02', + okEventColor: 'rgba(0, 245, 153, 0.45)', }; var triggerStatusMap = { From 223b0d1089aec11040d426ecaa75c04ec1ac9287 Mon Sep 17 00:00:00 2001 From: Alexander Zobnin Date: Mon, 28 Mar 2016 19:49:15 +0300 Subject: [PATCH 172/205] Fixed trigger comments and acknowledges show/hide. --- src/panel-triggers/module.html | 14 +++++++------- src/panel-triggers/module.js | 8 ++++++++ 2 files changed, 15 insertions(+), 7 deletions(-) diff --git a/src/panel-triggers/module.html b/src/panel-triggers/module.html index 4aad09a29..fddc00481 100644 --- a/src/panel-triggers/module.html +++ b/src/panel-triggers/module.html @@ -50,10 +50,10 @@
    {{trigger.description}} @@ -63,7 +63,7 @@
    + ng-if="trigger.showComment">
    {{trigger.comments}}
    @@ -72,7 +72,7 @@
    + ng-if="trigger.showAcknowledges">
    @@ -122,9 +122,9 @@ + role="button" + ng-click="ctrl.switchAcknowledges(trigger)" + bs-tooltip="'Acknowledges ({{trigger.acknowledges.length}})'"> diff --git a/src/panel-triggers/module.js b/src/panel-triggers/module.js index 9655facc0..1019dbe43 100644 --- a/src/panel-triggers/module.js +++ b/src/panel-triggers/module.js @@ -192,6 +192,14 @@ class TriggerPanelCtrl extends MetricsPanelCtrl { }); }); } + + switchComment(trigger) { + trigger.showComment = !trigger.showComment; + } + + switchAcknowledges(trigger) { + trigger.showAcknowledges = !trigger.showAcknowledges; + } } TriggerPanelCtrl.templateUrl = 'module.html'; From 4b91f620bcbc539e77e970da2a691eee2d2d4352 Mon Sep 17 00:00:00 2001 From: Alexander Zobnin Date: Mon, 28 Mar 2016 21:31:19 +0300 Subject: [PATCH 173/205] Fixed calculating api request hash. --- src/datasource-zabbix/zabbixCache.service.js | 17 +++++++++++++---- 1 file changed, 13 insertions(+), 4 deletions(-) diff --git a/src/datasource-zabbix/zabbixCache.service.js b/src/datasource-zabbix/zabbixCache.service.js index 2ba8ef9c7..fe5e59340 100644 --- a/src/datasource-zabbix/zabbixCache.service.js +++ b/src/datasource-zabbix/zabbixCache.service.js @@ -41,6 +41,7 @@ angular.module('grafana.services').factory('ZabbixCachingProxy', function($q, $i this.getHistory = callHistoryOnce(_.bind(this.zabbixAPI.getHistory, this.zabbixAPI), this.historyPromises); + // Don't run duplicated requests this.groupPromises = {}; this.getGroupsOnce = callAPIRequestOnce(_.bind(this.zabbixAPI.getGroups, this.zabbixAPI), this.groupPromises); @@ -156,10 +157,7 @@ angular.module('grafana.services').factory('ZabbixCachingProxy', function($q, $i function callAPIRequestOnce(func, promiseKeeper) { return function() { - var itemids = _.map(arguments[0], 'itemid'); - var stamp = itemids.join() + arguments[1] + arguments[2]; - var hash = stamp.getHash(); - + var hash = getAPIRequestHash(arguments); var deferred = $q.defer(); if (!promiseKeeper[hash]) { promiseKeeper[hash] = deferred.promise; @@ -213,6 +211,17 @@ angular.module('grafana.services').factory('ZabbixCachingProxy', function($q, $i return ZabbixCachingProxy; }); +function getAPIRequestHash(args) { + var requestStamp = _.map(args, arg => { + if (arg === undefined) { + return 'undefined'; + } else { + return arg.toString(); + } + }).join(); + return requestStamp.getHash(); +} + String.prototype.getHash = function() { var hash = 0, i, chr, len; if (this.length === 0) { From 8a4e0b76c37ba493452282e53828fbe538449d16 Mon Sep 17 00:00:00 2001 From: Alexander Zobnin Date: Mon, 28 Mar 2016 21:54:00 +0300 Subject: [PATCH 174/205] Show only host groups that contain hosts. --- src/datasource-zabbix/zabbixAPI.service.js | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/src/datasource-zabbix/zabbixAPI.service.js b/src/datasource-zabbix/zabbixAPI.service.js index b3cf98bb3..9ce52d7ed 100644 --- a/src/datasource-zabbix/zabbixAPI.service.js +++ b/src/datasource-zabbix/zabbixAPI.service.js @@ -117,7 +117,8 @@ function ZabbixAPIService($q, alertSrv, zabbixAPICoreService) { getGroups() { var params = { output: ['name'], - sortfield: 'name' + sortfield: 'name', + real_hosts: true }; return this.request('hostgroup.get', params); From 5e307838594ff77f3bccc1ad031e39503e2f8d46 Mon Sep 17 00:00:00 2001 From: Alexander Zobnin Date: Mon, 28 Mar 2016 21:57:27 +0300 Subject: [PATCH 175/205] Removed unused methods from zabbixAPI.service.js --- src/datasource-zabbix/query.controller.js | 2 +- src/datasource-zabbix/zabbixAPI.service.js | 46 ---------------------- 2 files changed, 1 insertion(+), 47 deletions(-) diff --git a/src/datasource-zabbix/query.controller.js b/src/datasource-zabbix/query.controller.js index 921da2fbe..c2c2bd5ea 100644 --- a/src/datasource-zabbix/query.controller.js +++ b/src/datasource-zabbix/query.controller.js @@ -113,7 +113,7 @@ export class ZabbixQueryController extends QueryCtrl { .then(groups => { var groupids = _.map(groups, 'groupid'); return self.zabbix - .getHostsByGroups(groupids) + .getHosts(groupids) .then(hosts => { self.metric.hostList = hosts; return hosts; diff --git a/src/datasource-zabbix/zabbixAPI.service.js b/src/datasource-zabbix/zabbixAPI.service.js index 9ce52d7ed..5c3181cb7 100644 --- a/src/datasource-zabbix/zabbixAPI.service.js +++ b/src/datasource-zabbix/zabbixAPI.service.js @@ -136,15 +136,6 @@ function ZabbixAPIService($q, alertSrv, zabbixAPICoreService) { return this.request('host.get', params); } - getHostsByGroups(groupids) { - var params = { - output: ['name', 'host'], - groupids: groupids - }; - - return this.request('host.get', params); - } - getApps(hostids) { var params = { output: ['applicationid', 'name'], @@ -154,19 +145,6 @@ function ZabbixAPIService($q, alertSrv, zabbixAPICoreService) { return this.request('application.get', params); } - getApplications() { - var params = { - output: ['applicationid', 'name'], - - // Hack for supporting different apis (2.2 vs 2.4 vs 3.0) - selectHost: ['hostid'], - selectHosts: ['hostid'], - selectItems: ['itemid'] - }; - - return this.request('application.get', params); - } - getItems(hostids, appids) { var params = { output: [ @@ -195,30 +173,6 @@ function ZabbixAPIService($q, alertSrv, zabbixAPICoreService) { }); } - /** - * Get Hosts list with host's items. - * @return {[type]} [description] - */ - getHostsExtend() { - var params = { - output: ['name', 'host'], - sortfield: 'name', - selectGroups: ['groupid'], - selectApplications: ['applicationid'], - selectItems: [ - 'itemid', - 'name', - 'key_', - 'value_type', - 'hostid', - 'status', - 'state' - ] - }; - - return this.request('host.get', params); - } - getLastValue(itemid) { var params = { output: ['lastvalue'], From 6f2db1c2fb506d5898e8f76ae757c5b4972f0c82 Mon Sep 17 00:00:00 2001 From: Alexander Zobnin Date: Mon, 28 Mar 2016 23:28:04 +0300 Subject: [PATCH 176/205] Initial Zabbix app for Grafana 3.0. --- Gruntfile.js | 4 +++ src/components/config.html | 13 ++++++++++ src/components/config.js | 4 +++ src/datasource-zabbix/plugin.json | 19 +++++++++----- src/module.js | 5 ++++ src/panel-triggers/plugin.json | 14 ++++++++--- src/plugin.json | 42 +++++++++++++++++++++++++++++++ 7 files changed, 92 insertions(+), 9 deletions(-) create mode 100644 src/components/config.html create mode 100644 src/components/config.js create mode 100644 src/module.js create mode 100644 src/plugin.json diff --git a/Gruntfile.js b/Gruntfile.js index 3e7ff367a..85b623df3 100644 --- a/Gruntfile.js +++ b/Gruntfile.js @@ -17,6 +17,8 @@ module.exports = function(grunt) { '**/*', '!datasource-zabbix/*.js', '!panel-triggers/*.js', + '!components/*.js', + '!module.js', '!**/*.scss' ], dest: 'dist/' @@ -49,6 +51,8 @@ module.exports = function(grunt) { src: [ 'datasource-zabbix/*.js', 'panel-triggers/*.js', + 'components/*.js', + 'module.js', ], dest: 'dist/' }] diff --git a/src/components/config.html b/src/components/config.html new file mode 100644 index 000000000..89eea1a69 --- /dev/null +++ b/src/components/config.html @@ -0,0 +1,13 @@ +

    Zabbix App Config

    + +
    +
    +
    + json Data property + +
    +
    + +
    +
    +
    diff --git a/src/components/config.js b/src/components/config.js new file mode 100644 index 000000000..fe9c1ef3c --- /dev/null +++ b/src/components/config.js @@ -0,0 +1,4 @@ +export class ZabbixAppConfigCtrl { + constructor() { } +} +ZabbixAppConfigCtrl.templateUrl = 'components/config.html'; diff --git a/src/datasource-zabbix/plugin.json b/src/datasource-zabbix/plugin.json index 0e75c1b6f..cd456d87b 100644 --- a/src/datasource-zabbix/plugin.json +++ b/src/datasource-zabbix/plugin.json @@ -1,12 +1,19 @@ { "type": "datasource", - "name": "Zabbix", + "name": "Zabbix Datasource", "id": "zabbix", - "module": "plugins/zabbix/module", - - "staticRoot": ".", - "metrics": true, - "annotations": true + "annotations": true, + + "info": { + "author": { + "name": "Alexander Zobnin", + "url": "http://grafana-zabbix.org" + }, + "logos": { + "small": "img/zabbix_logo.svg", + "large": "img/zabbix_logo.svg" + } + } } diff --git a/src/module.js b/src/module.js new file mode 100644 index 000000000..34ff163f9 --- /dev/null +++ b/src/module.js @@ -0,0 +1,5 @@ +import {ZabbixAppConfigCtrl} from './components/config'; + +export { + ZabbixAppConfigCtrl as ConfigCtrl +}; diff --git a/src/panel-triggers/plugin.json b/src/panel-triggers/plugin.json index 191cacdd0..5cb617e85 100644 --- a/src/panel-triggers/plugin.json +++ b/src/panel-triggers/plugin.json @@ -1,8 +1,16 @@ { "type": "panel", - "name": "Zabbix triggers", + "name": "Zabbix Triggers", "id": "triggers", - "module": "plugins/triggers/module", - "staticRoot": "." + "info": { + "author": { + "name": "Alexander Zobnin", + "url": "http://grafana-zabbix.org" + }, + "logos": { + "small": "img/zabbix_logo.svg", + "large": "img/zabbix_logo.svg" + } + } } diff --git a/src/plugin.json b/src/plugin.json new file mode 100644 index 000000000..fc354021a --- /dev/null +++ b/src/plugin.json @@ -0,0 +1,42 @@ +{ + "type": "app", + "name": "Zabbix App", + "id": "zabbix-app", + + "pages": [], + + "css": { + "dark": "css/dark.css", + "light": "css/light.css" + }, + + "info": { + "description": "Zabbix bundle for Grafana", + "author": { + "name": "Alexander Zobnin", + "url": "http://grafana-zabbix.org" + }, + "keywords": ["zabbix"], + "logos": { + "small": "/src/img/small_logo.png", + "large": "/src/img/large_logo.png" + }, + "links": [ + {"name": "Project site", "url": "https://github.com/alexanderzobnin/grafana-zabbix"}, + {"name": "License & Terms", "url": "https://github.com/alexanderzobnin/grafana-zabbix/blob/master/LICENSE.md"} + ], + "version": "1.0.0", + "updated": "2016-03-28" + }, + + "includes": [ + {"type": "dashboard", "name": "Example Zabbix Dashboard", "path": "dashboards/example_zabbix_dashboard.json"}, + {"type": "panel", "name": "Triggers Panel"}, + {"type": "datasource", "name": "Zabbix Datasource"} + ], + + "dependencies": { + "grafanaVersion": "3.x.x", + "plugins": [] + } +} From 99e6a9ba6c7cfbf7b33075a8516d85d7af4857ff Mon Sep 17 00:00:00 2001 From: Alexander Zobnin Date: Tue, 29 Mar 2016 10:04:13 +0300 Subject: [PATCH 177/205] Fixed templates paths. --- src/datasource-zabbix/module.js | 6 +++--- src/datasource-zabbix/plugin.json | 4 ++-- src/datasource-zabbix/query.controller.js | 2 +- src/panel-triggers/editor.js | 6 +++--- src/panel-triggers/module.js | 2 +- src/panel-triggers/plugin.json | 2 +- 6 files changed, 11 insertions(+), 11 deletions(-) diff --git a/src/datasource-zabbix/module.js b/src/datasource-zabbix/module.js index a7cc1d9ce..35bb3af72 100644 --- a/src/datasource-zabbix/module.js +++ b/src/datasource-zabbix/module.js @@ -2,13 +2,13 @@ import {ZabbixAPIDatasource} from './datasource'; import {ZabbixQueryController} from './query.controller'; class ZabbixConfigController {} -ZabbixConfigController.templateUrl = 'partials/config.html'; +ZabbixConfigController.templateUrl = 'datasource-zabbix/partials/config.html'; class ZabbixQueryOptionsController {} -ZabbixQueryOptionsController.templateUrl = 'partials/query.options.html'; +ZabbixQueryOptionsController.templateUrl = 'datasource-zabbix/partials/query.options.html'; class ZabbixAnnotationsQueryController {} -ZabbixAnnotationsQueryController.templateUrl = 'partials/annotations.editor.html'; +ZabbixAnnotationsQueryController.templateUrl = 'datasource-zabbix/partials/annotations.editor.html'; export { ZabbixAPIDatasource as Datasource, diff --git a/src/datasource-zabbix/plugin.json b/src/datasource-zabbix/plugin.json index cd456d87b..c05751d2d 100644 --- a/src/datasource-zabbix/plugin.json +++ b/src/datasource-zabbix/plugin.json @@ -1,7 +1,7 @@ { "type": "datasource", - "name": "Zabbix Datasource", - "id": "zabbix", + "name": "Zabbix", + "id": "zabbix-datasource", "metrics": true, "annotations": true, diff --git a/src/datasource-zabbix/query.controller.js b/src/datasource-zabbix/query.controller.js index c2c2bd5ea..928cb5714 100644 --- a/src/datasource-zabbix/query.controller.js +++ b/src/datasource-zabbix/query.controller.js @@ -270,7 +270,7 @@ export class ZabbixQueryController extends QueryCtrl { } // Set templateUrl as static property -ZabbixQueryController.templateUrl = 'partials/query.editor.html'; +ZabbixQueryController.templateUrl = 'datasource-zabbix/partials/query.editor.html'; // Get list of metric names for bs-typeahead directive function getMetricNames(scope, metricList) { diff --git a/src/panel-triggers/editor.js b/src/panel-triggers/editor.js index 186307e20..4a0420597 100644 --- a/src/panel-triggers/editor.js +++ b/src/panel-triggers/editor.js @@ -150,7 +150,7 @@ class TriggerPanelEditorCtrl{ this.popoverSrv.show({ element: el, placement: 'top', - templateUrl: 'public/plugins/triggers/trigger.colorpicker.html', + templateUrl: 'public/plugins/zabbix-triggers-panel/trigger.colorpicker.html', scope: popoverScope }); } @@ -167,7 +167,7 @@ class TriggerPanelEditorCtrl{ this.popoverSrv.show({ element: el, placement: 'top', - templateUrl: 'public/plugins/triggers/trigger.colorpicker.html', + templateUrl: 'public/plugins/zabbix-triggers-panel/trigger.colorpicker.html', scope: popoverScope }); } @@ -192,7 +192,7 @@ export function triggerPanelEditor() { return { restrict: 'E', scope: true, - templateUrl: 'public/plugins/triggers/editor.html', + templateUrl: 'public/plugins/zabbix-triggers-panel/editor.html', controller: TriggerPanelEditorCtrl, }; } diff --git a/src/panel-triggers/module.js b/src/panel-triggers/module.js index 1019dbe43..dd2c737fe 100644 --- a/src/panel-triggers/module.js +++ b/src/panel-triggers/module.js @@ -202,7 +202,7 @@ class TriggerPanelCtrl extends MetricsPanelCtrl { } } -TriggerPanelCtrl.templateUrl = 'module.html'; +TriggerPanelCtrl.templateUrl = 'panel-triggers/module.html'; function filterTriggers(triggers, triggerFilter) { if (isRegex(triggerFilter)) { diff --git a/src/panel-triggers/plugin.json b/src/panel-triggers/plugin.json index 5cb617e85..4e22da535 100644 --- a/src/panel-triggers/plugin.json +++ b/src/panel-triggers/plugin.json @@ -1,7 +1,7 @@ { "type": "panel", "name": "Zabbix Triggers", - "id": "triggers", + "id": "zabbix-triggers-panel", "info": { "author": { From a917b917d71bbf9e8797c8421658d9f090267f5c Mon Sep 17 00:00:00 2001 From: Alexander Zobnin Date: Tue, 29 Mar 2016 10:47:21 +0300 Subject: [PATCH 178/205] Added function for zabbix api url verifying. --- src/datasource-zabbix/utils.js | 10 ++++++++++ 1 file changed, 10 insertions(+) diff --git a/src/datasource-zabbix/utils.js b/src/datasource-zabbix/utils.js index ff36135be..cf89a7879 100644 --- a/src/datasource-zabbix/utils.js +++ b/src/datasource-zabbix/utils.js @@ -65,3 +65,13 @@ export function formatAcknowledges(acknowledges) { return ''; } } + +export function convertToZabbixAPIUrl(url) { + var zabbixAPIUrlPattern = /.*api_jsonrpc.php$/; + var trimSlashPattern = /(.*?)[\/]*$/; + if (url.match(zabbixAPIUrlPattern)) { + return url; + } else { + return url.replace(trimSlashPattern, "$1"); + } +} From ce6991cd3e3f13437d72fec58b9a232ba7f65057 Mon Sep 17 00:00:00 2001 From: Alexander Zobnin Date: Tue, 29 Mar 2016 11:07:48 +0300 Subject: [PATCH 179/205] Updated datasource edit page style. --- src/datasource-zabbix/partials/config.html | 101 +++++++++++---------- 1 file changed, 53 insertions(+), 48 deletions(-) diff --git a/src/datasource-zabbix/partials/config.html b/src/datasource-zabbix/partials/config.html index e57efb04d..85a6fe250 100644 --- a/src/datasource-zabbix/partials/config.html +++ b/src/datasource-zabbix/partials/config.html @@ -1,56 +1,61 @@ -
    Zabbix API details
    +
    +

    Zabbix API details

    -
    -
      -
    • - User -
    • -
    • - -
    • -
    • +
      + + Username + + + +
      + +
      + Password -
    • -
    • - -
    • -
    -
    -
    -
    -
      -
    • - Cache update interval -
    • -
    • - -
    • -
    • - Trends   - - -
    • -
    • - Use trends from -
    • -
    • - + + +
    + +
    +
    + +
    + + +
    + + Use from + + - - -
    + +
    +
    + +
    + + Cache update interval + + + +
    From 3f028c7e2d7017a4caa88f3e341e775a87a47b82 Mon Sep 17 00:00:00 2001 From: Alexander Zobnin Date: Tue, 29 Mar 2016 12:12:49 +0300 Subject: [PATCH 180/205] Added migrations module for target format converting. --- src/datasource-zabbix/datasource.js | 3 ++ src/datasource-zabbix/migrations.js | 53 +++++++++++++++++++++++ src/datasource-zabbix/query.controller.js | 3 ++ 3 files changed, 59 insertions(+) create mode 100644 src/datasource-zabbix/migrations.js diff --git a/src/datasource-zabbix/datasource.js b/src/datasource-zabbix/datasource.js index 8f9a08844..6e8805de4 100644 --- a/src/datasource-zabbix/datasource.js +++ b/src/datasource-zabbix/datasource.js @@ -2,6 +2,7 @@ import _ from 'lodash'; import * as dateMath from 'app/core/utils/datemath'; import * as utils from './utils'; +import * as migrations from './migrations'; import * as metricFunctions from './metricFunctions'; import DataProcessor from './DataProcessor'; import './zabbixAPI.service.js'; @@ -110,6 +111,8 @@ export class ZabbixAPIDatasource { var promises = _.map(options.targets, function(target) { if (target.mode !== 1) { + //console.log(migrations.isGrafana2target(target), target); + target = migrations.migrate(target); // Don't request undefined and hidden targets if (target.hide || !target.group || diff --git a/src/datasource-zabbix/migrations.js b/src/datasource-zabbix/migrations.js new file mode 100644 index 000000000..38717828c --- /dev/null +++ b/src/datasource-zabbix/migrations.js @@ -0,0 +1,53 @@ +/** + * Query format migration. + * This module can detect query format version and make migration. + */ + +import * as utils from './utils'; + +export function isGrafana2target(target) { + if ((target.hostFilter || target.itemFilter || target.downsampleFunction || target.host.host) && + (!target.functions && !target.host.filter)) { + return true; + } else { + return false; + } +} + +export function migrateFrom2To3version(target) { + utils.isRegex(target.host.name); + var newTarget = { + group: { + filter: target.group.name === "*" ? "/.*/" : target.group.name, + isRegex: target.group.name === "*" + }, + host: { + filter: target.host.name === "*" ? convertToRegex(target.hostFilter) : target.host.name, + isRegex: target.host.name === "*" + }, + application: { + filter: target.application.name === "*" ? "" : target.application.name, + isRegex: target.application.name === "*" + }, + item: { + filter: target.item.name === "All" ? convertToRegex(target.itemFilter) : target.item.name, + isRegex: target.item.name === "All" + }, + functions: [], + mode: target.mode, + hide: target.hide, + }; + return newTarget; +} + +export function migrate(target) { + if (isGrafana2target(target)) { + return migrateFrom2To3version(target); + } else { + return target; + } +} + +function convertToRegex(str) { + return '/' + str + '/'; +} diff --git a/src/datasource-zabbix/query.controller.js b/src/datasource-zabbix/query.controller.js index c2c2bd5ea..1a8a64b1d 100644 --- a/src/datasource-zabbix/query.controller.js +++ b/src/datasource-zabbix/query.controller.js @@ -2,6 +2,7 @@ import {QueryCtrl} from 'app/plugins/sdk'; import _ from 'lodash'; import * as utils from './utils'; import * as metricFunctions from './metricFunctions'; +import * as migrations from './migrations'; import './add-metric-function.directive'; import './metric-function-editor.directive'; @@ -32,6 +33,8 @@ export class ZabbixQueryController extends QueryCtrl { this.init = function() { + this.target = migrations.migrate(this.target); + this.templateSrv = templateSrv; var target = this.target; From 7f75abb2d3beb152d6701453f0adb2bbb269bedf Mon Sep 17 00:00:00 2001 From: Alexander Zobnin Date: Tue, 29 Mar 2016 13:50:05 +0300 Subject: [PATCH 181/205] Fixed migration issues. --- src/datasource-zabbix/datasource.js | 3 +- src/datasource-zabbix/migrations.js | 46 +++++++++-------------- src/datasource-zabbix/query.controller.js | 10 ++--- 3 files changed, 23 insertions(+), 36 deletions(-) diff --git a/src/datasource-zabbix/datasource.js b/src/datasource-zabbix/datasource.js index 6e8805de4..a259c73bd 100644 --- a/src/datasource-zabbix/datasource.js +++ b/src/datasource-zabbix/datasource.js @@ -111,7 +111,8 @@ export class ZabbixAPIDatasource { var promises = _.map(options.targets, function(target) { if (target.mode !== 1) { - //console.log(migrations.isGrafana2target(target), target); + + // Migrate old targets target = migrations.migrate(target); // Don't request undefined and hidden targets diff --git a/src/datasource-zabbix/migrations.js b/src/datasource-zabbix/migrations.js index 38717828c..43431494d 100644 --- a/src/datasource-zabbix/migrations.js +++ b/src/datasource-zabbix/migrations.js @@ -3,41 +3,31 @@ * This module can detect query format version and make migration. */ -import * as utils from './utils'; - export function isGrafana2target(target) { - if ((target.hostFilter || target.itemFilter || target.downsampleFunction || target.host.host) && - (!target.functions && !target.host.filter)) { - return true; + if (!target.mode || target.mode === 0) { + if ((target.hostFilter || target.itemFilter || target.downsampleFunction || target.host.host) && + (target.item.filter === undefined && target.host.filter === undefined)) { + return true; + } else { + return false; + } } else { return false; } } export function migrateFrom2To3version(target) { - utils.isRegex(target.host.name); - var newTarget = { - group: { - filter: target.group.name === "*" ? "/.*/" : target.group.name, - isRegex: target.group.name === "*" - }, - host: { - filter: target.host.name === "*" ? convertToRegex(target.hostFilter) : target.host.name, - isRegex: target.host.name === "*" - }, - application: { - filter: target.application.name === "*" ? "" : target.application.name, - isRegex: target.application.name === "*" - }, - item: { - filter: target.item.name === "All" ? convertToRegex(target.itemFilter) : target.item.name, - isRegex: target.item.name === "All" - }, - functions: [], - mode: target.mode, - hide: target.hide, - }; - return newTarget; + target.group.filter = target.group.name === "*" ? "/.*/" : target.group.name; + target.group.isRegex = target.group.name === "*"; + target.host.filter = target.host.name === "*" ? convertToRegex(target.hostFilter) : target.host.name; + target.host.isRegex = target.host.name === "*"; + target.application.filter = target.application.name === "*" ? "" : target.application.name; + target.application.isRegex = target.application.name === "*"; + target.item.filter = target.item.name === "All" ? convertToRegex(target.itemFilter) : target.item.name; + target.item.isRegex = target.item.name === "All"; + target.functions = []; + target.refId = target.refId ? target.refId : "A"; + return target; } export function migrate(target) { diff --git a/src/datasource-zabbix/query.controller.js b/src/datasource-zabbix/query.controller.js index 1a8a64b1d..daeda878a 100644 --- a/src/datasource-zabbix/query.controller.js +++ b/src/datasource-zabbix/query.controller.js @@ -33,11 +33,12 @@ export class ZabbixQueryController extends QueryCtrl { this.init = function() { - this.target = migrations.migrate(this.target); - this.templateSrv = templateSrv; var target = this.target; + // Migrate old targets + target = migrations.migrate(target); + var scopeDefaults = { metric: {}, oldTarget: _.cloneDeep(this.target) @@ -69,11 +70,6 @@ export class ZabbixQueryController extends QueryCtrl { {name: "max", value: "max"} ]; - // Set avg by default - if (!target.downsampleFunction) { - target.downsampleFunction = this.downsampleFunctionList[0]; - } - this.initFilters(); } else if (target.mode === 1) { From 38f4870e28fef2ba21556da759f4397f70badfcb Mon Sep 17 00:00:00 2001 From: Alexander Zobnin Date: Wed, 30 Mar 2016 09:58:21 +0300 Subject: [PATCH 182/205] Removed defaults properties from migrations. --- src/datasource-zabbix/migrations.js | 2 -- src/datasource-zabbix/query.controller.js | 1 + 2 files changed, 1 insertion(+), 2 deletions(-) diff --git a/src/datasource-zabbix/migrations.js b/src/datasource-zabbix/migrations.js index 43431494d..538d84389 100644 --- a/src/datasource-zabbix/migrations.js +++ b/src/datasource-zabbix/migrations.js @@ -25,8 +25,6 @@ export function migrateFrom2To3version(target) { target.application.isRegex = target.application.name === "*"; target.item.filter = target.item.name === "All" ? convertToRegex(target.itemFilter) : target.item.name; target.item.isRegex = target.item.name === "All"; - target.functions = []; - target.refId = target.refId ? target.refId : "A"; return target; } diff --git a/src/datasource-zabbix/query.controller.js b/src/datasource-zabbix/query.controller.js index daeda878a..a6fd9bf69 100644 --- a/src/datasource-zabbix/query.controller.js +++ b/src/datasource-zabbix/query.controller.js @@ -53,6 +53,7 @@ export class ZabbixQueryController extends QueryCtrl { application: { filter: "" }, item: { filter: "" }, functions: [], + refId: "A" }; _.defaults(target, targetDefaults); From 7c198ae7128448dabe6a31bd65adc147bffab142 Mon Sep 17 00:00:00 2001 From: Alexander Zobnin Date: Wed, 30 Mar 2016 10:02:43 +0300 Subject: [PATCH 183/205] Added text metrics migrations. --- src/datasource-zabbix/migrations.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/datasource-zabbix/migrations.js b/src/datasource-zabbix/migrations.js index 538d84389..264360568 100644 --- a/src/datasource-zabbix/migrations.js +++ b/src/datasource-zabbix/migrations.js @@ -4,7 +4,7 @@ */ export function isGrafana2target(target) { - if (!target.mode || target.mode === 0) { + if (!target.mode || target.mode === 0 || target.mode === 2) { if ((target.hostFilter || target.itemFilter || target.downsampleFunction || target.host.host) && (target.item.filter === undefined && target.host.filter === undefined)) { return true; From c4570d053dcd41146144fb2dfcb9098cffa7bb9e Mon Sep 17 00:00:00 2001 From: Alexander Zobnin Date: Wed, 30 Mar 2016 10:04:14 +0300 Subject: [PATCH 184/205] Changed datasource id to zabbix-datasource. --- src/datasource-zabbix/plugin.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/datasource-zabbix/plugin.json b/src/datasource-zabbix/plugin.json index 0e75c1b6f..1233eea0a 100644 --- a/src/datasource-zabbix/plugin.json +++ b/src/datasource-zabbix/plugin.json @@ -1,7 +1,7 @@ { "type": "datasource", "name": "Zabbix", - "id": "zabbix", + "id": "zabbix-datasource", "module": "plugins/zabbix/module", From b83006fcecf033d056cc46be47eddbc12bb30e12 Mon Sep 17 00:00:00 2001 From: Alexander Zobnin Date: Wed, 30 Mar 2016 10:12:22 +0300 Subject: [PATCH 185/205] Added info section to plugin.json. --- src/datasource-zabbix/plugin.json | 17 ++++++++++++----- src/panel-triggers/plugin.json | 16 ++++++++++++---- 2 files changed, 24 insertions(+), 9 deletions(-) diff --git a/src/datasource-zabbix/plugin.json b/src/datasource-zabbix/plugin.json index 1233eea0a..c05751d2d 100644 --- a/src/datasource-zabbix/plugin.json +++ b/src/datasource-zabbix/plugin.json @@ -3,10 +3,17 @@ "name": "Zabbix", "id": "zabbix-datasource", - "module": "plugins/zabbix/module", - - "staticRoot": ".", - "metrics": true, - "annotations": true + "annotations": true, + + "info": { + "author": { + "name": "Alexander Zobnin", + "url": "http://grafana-zabbix.org" + }, + "logos": { + "small": "img/zabbix_logo.svg", + "large": "img/zabbix_logo.svg" + } + } } diff --git a/src/panel-triggers/plugin.json b/src/panel-triggers/plugin.json index 191cacdd0..80bfd5903 100644 --- a/src/panel-triggers/plugin.json +++ b/src/panel-triggers/plugin.json @@ -1,8 +1,16 @@ { "type": "panel", - "name": "Zabbix triggers", - "id": "triggers", + "name": "Zabbix Triggers", + "id": "zabbix-triggers-panel", - "module": "plugins/triggers/module", - "staticRoot": "." + "info": { + "author": { + "name": "Alexander Zobnin", + "url": "http://grafana-zabbix.org" + }, + "logos": { + "small": "img/zabbix_triggers_logo.svg", + "large": "img/zabbix_triggers_logo.svg" + } + } } From 8de79a14d5421eb1ada9e6b84d3e4c4b09935006 Mon Sep 17 00:00:00 2001 From: Alexander Zobnin Date: Wed, 30 Mar 2016 10:25:32 +0300 Subject: [PATCH 186/205] Fixed migrations error in triggers panel. --- src/datasource-zabbix/migrations.js | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/src/datasource-zabbix/migrations.js b/src/datasource-zabbix/migrations.js index 264360568..b917f114b 100644 --- a/src/datasource-zabbix/migrations.js +++ b/src/datasource-zabbix/migrations.js @@ -5,7 +5,8 @@ export function isGrafana2target(target) { if (!target.mode || target.mode === 0 || target.mode === 2) { - if ((target.hostFilter || target.itemFilter || target.downsampleFunction || target.host.host) && + if ((target.hostFilter || target.itemFilter || target.downsampleFunction || + (target.host && target.host.host)) && (target.item.filter === undefined && target.host.filter === undefined)) { return true; } else { From 19541cf55fcac09f31362ee7d9e49ca82f3cb0b9 Mon Sep 17 00:00:00 2001 From: Alexander Zobnin Date: Wed, 30 Mar 2016 10:49:35 +0300 Subject: [PATCH 187/205] Temporary removed logos section from plugin.json. --- src/datasource-zabbix/plugin.json | 4 ---- src/panel-triggers/plugin.json | 4 ---- 2 files changed, 8 deletions(-) diff --git a/src/datasource-zabbix/plugin.json b/src/datasource-zabbix/plugin.json index c05751d2d..940df269f 100644 --- a/src/datasource-zabbix/plugin.json +++ b/src/datasource-zabbix/plugin.json @@ -10,10 +10,6 @@ "author": { "name": "Alexander Zobnin", "url": "http://grafana-zabbix.org" - }, - "logos": { - "small": "img/zabbix_logo.svg", - "large": "img/zabbix_logo.svg" } } } diff --git a/src/panel-triggers/plugin.json b/src/panel-triggers/plugin.json index 80bfd5903..0e8bdbd92 100644 --- a/src/panel-triggers/plugin.json +++ b/src/panel-triggers/plugin.json @@ -7,10 +7,6 @@ "author": { "name": "Alexander Zobnin", "url": "http://grafana-zabbix.org" - }, - "logos": { - "small": "img/zabbix_triggers_logo.svg", - "large": "img/zabbix_triggers_logo.svg" } } } From 94e493a3bf5969acdf1dd2f4f66db2763af1c886 Mon Sep 17 00:00:00 2001 From: Alexander Zobnin Date: Wed, 30 Mar 2016 20:02:58 +0300 Subject: [PATCH 188/205] Fixed triggers panel template urls. --- src/panel-triggers/editor.js | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/src/panel-triggers/editor.js b/src/panel-triggers/editor.js index 186307e20..91ddbe963 100644 --- a/src/panel-triggers/editor.js +++ b/src/panel-triggers/editor.js @@ -60,8 +60,8 @@ class TriggerPanelEditorCtrl{ var self = this; // Get zabbix data sources - var datasources = _.filter(this.datasourceSrv.getMetricSources(), function(datasource) { - return datasource.meta.id === 'zabbix'; + var datasources = _.filter(this.datasourceSrv.getMetricSources(), datasource => { + return datasource.meta.id === 'zabbix-datasource'; }); this.datasources = _.map(datasources, 'name'); @@ -150,7 +150,7 @@ class TriggerPanelEditorCtrl{ this.popoverSrv.show({ element: el, placement: 'top', - templateUrl: 'public/plugins/triggers/trigger.colorpicker.html', + templateUrl: 'public/plugins/zabbix-triggers-panel/trigger.colorpicker.html', scope: popoverScope }); } @@ -167,7 +167,7 @@ class TriggerPanelEditorCtrl{ this.popoverSrv.show({ element: el, placement: 'top', - templateUrl: 'public/plugins/triggers/trigger.colorpicker.html', + templateUrl: 'public/plugins/zabbix-triggers-panel/trigger.colorpicker.html', scope: popoverScope }); } @@ -192,7 +192,7 @@ export function triggerPanelEditor() { return { restrict: 'E', scope: true, - templateUrl: 'public/plugins/triggers/editor.html', + templateUrl: 'public/plugins/zabbix-triggers-panel/editor.html', controller: TriggerPanelEditorCtrl, }; } From 8e44587594e2a36310e00b04df32ddfe1fc428a8 Mon Sep 17 00:00:00 2001 From: Alexander Zobnin Date: Wed, 30 Mar 2016 20:03:30 +0300 Subject: [PATCH 189/205] Fixed panel refreshing. --- src/panel-triggers/module.js | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/src/panel-triggers/module.js b/src/panel-triggers/module.js index 1019dbe43..b40a33b9f 100644 --- a/src/panel-triggers/module.js +++ b/src/panel-triggers/module.js @@ -82,6 +82,11 @@ class TriggerPanelCtrl extends MetricsPanelCtrl { this.icon = "fa fa-lightbulb-o"; this.addEditorTab('Options', triggerPanelEditor, 2); this.editModeInitiated = true; + this.events.emit('init-edit-mode', null); + } + + onMetricsPanelRefresh() { + this.refreshData(); } refreshData() { From 14d8caf61ef6edb06c05fd5fcc610167adcda674 Mon Sep 17 00:00:00 2001 From: Alexander Zobnin Date: Wed, 30 Mar 2016 20:33:50 +0300 Subject: [PATCH 190/205] Fixed triggers filtering by value (All, OK, Problems). --- src/datasource-zabbix/datasource.js | 5 ++++- src/datasource-zabbix/zabbixAPI.service.js | 10 +++++----- src/panel-triggers/editor.js | 6 +++--- src/panel-triggers/module.js | 2 +- 4 files changed, 13 insertions(+), 10 deletions(-) diff --git a/src/datasource-zabbix/datasource.js b/src/datasource-zabbix/datasource.js index a259c73bd..bb5cb0e3a 100644 --- a/src/datasource-zabbix/datasource.js +++ b/src/datasource-zabbix/datasource.js @@ -343,6 +343,9 @@ export class ZabbixAPIDatasource { var self = this; var showOkEvents = annotation.showOkEvents ? [0, 1] : 1; + // Show all triggers + var showTriggers = [0, 1]; + var buildQuery = self.queryProcessor.buildTriggerQuery(this.templateSrv.replace(annotation.group), this.templateSrv.replace(annotation.host), this.templateSrv.replace(annotation.application)); @@ -350,7 +353,7 @@ export class ZabbixAPIDatasource { return self.zabbixAPI.getTriggers(query.groupids, query.hostids, query.applicationids, - true) + showTriggers) .then(function(triggers) { // Filter triggers by description diff --git a/src/datasource-zabbix/zabbixAPI.service.js b/src/datasource-zabbix/zabbixAPI.service.js index 5c3181cb7..ab35f0a78 100644 --- a/src/datasource-zabbix/zabbixAPI.service.js +++ b/src/datasource-zabbix/zabbixAPI.service.js @@ -298,7 +298,7 @@ function ZabbixAPIService($q, alertSrv, zabbixAPICoreService) { return this.request('service.getsla', params); } - getTriggers(groupids, hostids, applicationids, showAll) { + getTriggers(groupids, hostids, applicationids, showTriggers) { var params = { output: 'extend', groupids: groupids, @@ -318,14 +318,14 @@ function ZabbixAPIService($q, alertSrv, zabbixAPICoreService) { selectLastEvent: 'extend' }; - if (showAll) { - params.filter = {}; + if (showTriggers) { + params.filter.value = showTriggers; } return this.request('trigger.get', params); } - getEvents(objectids, from, to, showOkEvents) { + getEvents(objectids, from, to, showEvents) { var params = { output: 'extend', time_from: from, @@ -333,7 +333,7 @@ function ZabbixAPIService($q, alertSrv, zabbixAPICoreService) { objectids: objectids, select_acknowledges: 'extend', selectHosts: 'extend', - value: showOkEvents + value: showEvents }; return this.request('event.get', params); diff --git a/src/panel-triggers/editor.js b/src/panel-triggers/editor.js index 91ddbe963..8c2f61102 100644 --- a/src/panel-triggers/editor.js +++ b/src/panel-triggers/editor.js @@ -44,9 +44,9 @@ class TriggerPanelEditorCtrl{ ]; this.showEventsFields = [ - { text: 'all events', value: [0,1] }, - { text: 'Ok events', value: 0 }, - { text: 'Problem events', value: 1 } + { text: 'All', value: [0,1] }, + { text: 'OK', value: [0] }, + { text: 'Problems', value: 1 } ]; // Load scope defaults diff --git a/src/panel-triggers/module.js b/src/panel-triggers/module.js index b40a33b9f..7467ae37c 100644 --- a/src/panel-triggers/module.js +++ b/src/panel-triggers/module.js @@ -43,7 +43,7 @@ var panelDefaults = { limit: 10, showTriggers: 'all triggers', sortTriggersBy: { text: 'last change', value: 'lastchange' }, - showEvents: { text: 'Problem events', value: '1' }, + showEvents: { text: 'Problems', value: '1' }, triggerSeverity: defaultSeverity, okEventColor: 'rgba(0, 245, 153, 0.45)', }; From 98f77ee6617456c1af913e5b5ea65c3fd67850c7 Mon Sep 17 00:00:00 2001 From: Alexander Zobnin Date: Wed, 30 Mar 2016 20:55:07 +0300 Subject: [PATCH 191/205] Fixed host name displaying (not worked in Zabbix 3.0). --- src/datasource-zabbix/zabbixAPI.service.js | 2 +- src/panel-triggers/module.js | 5 +++++ 2 files changed, 6 insertions(+), 1 deletion(-) diff --git a/src/datasource-zabbix/zabbixAPI.service.js b/src/datasource-zabbix/zabbixAPI.service.js index ab35f0a78..beb4eecb2 100644 --- a/src/datasource-zabbix/zabbixAPI.service.js +++ b/src/datasource-zabbix/zabbixAPI.service.js @@ -313,7 +313,7 @@ function ZabbixAPIService($q, alertSrv, zabbixAPICoreService) { value: 1 }, selectGroups: ['name'], - selectHosts: ['name'], + selectHosts: ['name', 'host'], selectItems: ['name', 'key_', 'lastvalue'], selectLastEvent: 'extend' }; diff --git a/src/panel-triggers/module.js b/src/panel-triggers/module.js index 7467ae37c..103b281b8 100644 --- a/src/panel-triggers/module.js +++ b/src/panel-triggers/module.js @@ -122,6 +122,11 @@ class TriggerPanelCtrl extends MetricsPanelCtrl { } triggerObj.age = timestamp.fromNow(true); + // Set host that the trigger belongs + if (trigger.hosts.length) { + triggerObj.host = trigger.hosts[0].name; + } + // Set color if (trigger.value === '1') { triggerObj.color = self.panel.triggerSeverity[trigger.priority].color; From 2ffc5ca297f2070b0e12072d24dafb00cbdaea6e Mon Sep 17 00:00:00 2001 From: Alexander Zobnin Date: Wed, 30 Mar 2016 21:41:37 +0300 Subject: [PATCH 192/205] Improved regex highlighting in query editor (using custom css). Removed unused fields from targets ('isRegex' and 'style'). --- src/datasource-zabbix/css/query-editor.css | 3 +++ src/datasource-zabbix/datasource.js | 2 +- src/datasource-zabbix/migrations.js | 4 ---- src/datasource-zabbix/partials/query.editor.html | 8 ++++---- src/datasource-zabbix/query.controller.js | 10 ++++++++-- 5 files changed, 16 insertions(+), 11 deletions(-) create mode 100644 src/datasource-zabbix/css/query-editor.css diff --git a/src/datasource-zabbix/css/query-editor.css b/src/datasource-zabbix/css/query-editor.css new file mode 100644 index 000000000..5bf1cdb1c --- /dev/null +++ b/src/datasource-zabbix/css/query-editor.css @@ -0,0 +1,3 @@ +.zbx-regex { + color: #CCA300; +} diff --git a/src/datasource-zabbix/datasource.js b/src/datasource-zabbix/datasource.js index bb5cb0e3a..faeda51eb 100644 --- a/src/datasource-zabbix/datasource.js +++ b/src/datasource-zabbix/datasource.js @@ -134,7 +134,7 @@ export class ZabbixAPIDatasource { return self.queryProcessor.build(groupFilter, hostFilter, appFilter, itemFilter) .then(function(items) { // Add hostname for items from multiple hosts - var addHostName = target.host.isRegex; + var addHostName = utils.isRegex(target.host.filter); var getHistory; // Use trends diff --git a/src/datasource-zabbix/migrations.js b/src/datasource-zabbix/migrations.js index b917f114b..0bbd737c6 100644 --- a/src/datasource-zabbix/migrations.js +++ b/src/datasource-zabbix/migrations.js @@ -19,13 +19,9 @@ export function isGrafana2target(target) { export function migrateFrom2To3version(target) { target.group.filter = target.group.name === "*" ? "/.*/" : target.group.name; - target.group.isRegex = target.group.name === "*"; target.host.filter = target.host.name === "*" ? convertToRegex(target.hostFilter) : target.host.name; - target.host.isRegex = target.host.name === "*"; target.application.filter = target.application.name === "*" ? "" : target.application.name; - target.application.isRegex = target.application.name === "*"; target.item.filter = target.item.name === "All" ? convertToRegex(target.itemFilter) : target.item.name; - target.item.isRegex = target.item.name === "All"; return target; } diff --git a/src/datasource-zabbix/partials/query.editor.html b/src/datasource-zabbix/partials/query.editor.html index 48504eb7d..5c89d6e33 100644 --- a/src/datasource-zabbix/partials/query.editor.html +++ b/src/datasource-zabbix/partials/query.editor.html @@ -94,7 +94,7 @@ data-min-length=0 data-items=100 class="input-medium tight-form-input" - ng-style="ctrl.target.group.style"> + ng-class="{'zbx-regex': ctrl.isRegex(ctrl.target.group.filter)}"> @@ -108,7 +108,7 @@ data-min-length=0 data-items=100 class="input-large tight-form-input" - ng-style="ctrl.target.host.style"> + ng-class="{'zbx-regex': ctrl.isRegex(ctrl.target.host.filter)}">
  • Show disabled items  @@ -155,7 +155,7 @@ data-min-length=0 data-items=100 class="input-medium tight-form-input" - ng-style="ctrl.target.application.style"> + ng-class="{'zbx-regex': ctrl.isRegex(ctrl.target.application.filter)}">
  • @@ -169,7 +169,7 @@ data-min-length=0 data-items=100 class="input-large tight-form-input" - ng-style="ctrl.target.item.style"> + ng-class="{'zbx-regex': ctrl.isRegex(ctrl.target.item.filter)}">
  • Options
  • diff --git a/src/datasource-zabbix/query.controller.js b/src/datasource-zabbix/query.controller.js index a6fd9bf69..cd06a2c65 100644 --- a/src/datasource-zabbix/query.controller.js +++ b/src/datasource-zabbix/query.controller.js @@ -7,6 +7,8 @@ import * as migrations from './migrations'; import './add-metric-function.directive'; import './metric-function-editor.directive'; +import './css/query-editor.css!'; + export class ZabbixQueryController extends QueryCtrl { // ZabbixQueryCtrl constructor @@ -171,9 +173,13 @@ export class ZabbixQueryController extends QueryCtrl { } onTargetPartChange(targetPart) { - var regexStyle = {'color': '#CCA300'}; + /*var regexStyle = {'color': '#CCA300'}; targetPart.isRegex = utils.isRegex(targetPart.filter); - targetPart.style = targetPart.isRegex ? regexStyle : {}; + targetPart.style = targetPart.isRegex ? regexStyle : {};*/ + } + + isRegex(str) { + return utils.isRegex(str); } onTargetBlur() { From c5a421c933cba71fc24c6105edb6d077fd195e9f Mon Sep 17 00:00:00 2001 From: Alexander Zobnin Date: Wed, 30 Mar 2016 23:04:17 +0300 Subject: [PATCH 193/205] Added variables highlight in query editor. --- src/datasource-zabbix/css/query-editor.css | 4 ++++ .../partials/query.editor.html | 20 +++++++++++++++---- src/datasource-zabbix/query.controller.js | 12 +++++++++++ 3 files changed, 32 insertions(+), 4 deletions(-) diff --git a/src/datasource-zabbix/css/query-editor.css b/src/datasource-zabbix/css/query-editor.css index 5bf1cdb1c..3edd27d6c 100644 --- a/src/datasource-zabbix/css/query-editor.css +++ b/src/datasource-zabbix/css/query-editor.css @@ -1,3 +1,7 @@ .zbx-regex { color: #CCA300; } + +.zbx-variable { + color: #33B5E5; +} diff --git a/src/datasource-zabbix/partials/query.editor.html b/src/datasource-zabbix/partials/query.editor.html index 5c89d6e33..72f6b7388 100644 --- a/src/datasource-zabbix/partials/query.editor.html +++ b/src/datasource-zabbix/partials/query.editor.html @@ -94,7 +94,10 @@ data-min-length=0 data-items=100 class="input-medium tight-form-input" - ng-class="{'zbx-regex': ctrl.isRegex(ctrl.target.group.filter)}"> + ng-class="{ + 'zbx-variable': ctrl.isVariable(ctrl.target.group.filter), + 'zbx-regex': ctrl.isRegex(ctrl.target.group.filter) + }">
  • @@ -108,7 +111,10 @@ data-min-length=0 data-items=100 class="input-large tight-form-input" - ng-class="{'zbx-regex': ctrl.isRegex(ctrl.target.host.filter)}"> + ng-class="{ + 'zbx-variable': ctrl.isVariable(ctrl.target.host.filter), + 'zbx-regex': ctrl.isRegex(ctrl.target.host.filter) + }">
  • Show disabled items  @@ -155,7 +161,10 @@ data-min-length=0 data-items=100 class="input-medium tight-form-input" - ng-class="{'zbx-regex': ctrl.isRegex(ctrl.target.application.filter)}"> + ng-class="{ + 'zbx-variable': ctrl.isVariable(ctrl.target.application.filter), + 'zbx-regex': ctrl.isRegex(ctrl.target.application.filter) + }">
  • @@ -169,7 +178,10 @@ data-min-length=0 data-items=100 class="input-large tight-form-input" - ng-class="{'zbx-regex': ctrl.isRegex(ctrl.target.item.filter)}"> + ng-class="{ + 'zbx-variable': ctrl.isVariable(ctrl.target.item.filter), + 'zbx-regex': ctrl.isRegex(ctrl.target.item.filter) + }">
  • Options
  • diff --git a/src/datasource-zabbix/query.controller.js b/src/datasource-zabbix/query.controller.js index cd06a2c65..3e270e16f 100644 --- a/src/datasource-zabbix/query.controller.js +++ b/src/datasource-zabbix/query.controller.js @@ -182,6 +182,18 @@ export class ZabbixQueryController extends QueryCtrl { return utils.isRegex(str); } + isVariable(str) { + var variablePattern = /^\$\w+/; + if (variablePattern.test(str)) { + var variables = _.map(this.templateSrv.variables, variable => { + return '$' + variable.name; + }); + return _.contains(variables, str); + } else { + return false; + } + } + onTargetBlur() { var newTarget = _.cloneDeep(this.target); if (!_.isEqual(this.oldTarget, this.target)) { From 5266751dea1cf26ef5dd87b8cb264223f9a71b2e Mon Sep 17 00:00:00 2001 From: Alexander Zobnin Date: Thu, 31 Mar 2016 19:30:01 +0300 Subject: [PATCH 194/205] Temporary removed logos section from config. --- src/plugin.json | 4 ---- 1 file changed, 4 deletions(-) diff --git a/src/plugin.json b/src/plugin.json index fc354021a..c07b14f1d 100644 --- a/src/plugin.json +++ b/src/plugin.json @@ -17,10 +17,6 @@ "url": "http://grafana-zabbix.org" }, "keywords": ["zabbix"], - "logos": { - "small": "/src/img/small_logo.png", - "large": "/src/img/large_logo.png" - }, "links": [ {"name": "Project site", "url": "https://github.com/alexanderzobnin/grafana-zabbix"}, {"name": "License & Terms", "url": "https://github.com/alexanderzobnin/grafana-zabbix/blob/master/LICENSE.md"} From b3b3c7bb0bc80602d582b335c49ea6caa2710b89 Mon Sep 17 00:00:00 2001 From: Alexander Zobnin Date: Thu, 31 Mar 2016 20:58:02 +0300 Subject: [PATCH 195/205] Added dashboard example (Zabbix Server Dashboard). --- src/dashboards/zabbix_server_dashboard.json | 416 ++++++++++++++++++++ src/plugin.json | 19 +- 2 files changed, 430 insertions(+), 5 deletions(-) create mode 100644 src/dashboards/zabbix_server_dashboard.json diff --git a/src/dashboards/zabbix_server_dashboard.json b/src/dashboards/zabbix_server_dashboard.json new file mode 100644 index 000000000..172fcc9ff --- /dev/null +++ b/src/dashboards/zabbix_server_dashboard.json @@ -0,0 +1,416 @@ +{ + "id": null, + "title": "Zabbix Server Dashboard", + "originalTitle": "Zabbix Server Dashboard", + "tags": [ + "zabbix", + "example" + ], + "style": "dark", + "timezone": "browser", + "editable": true, + "hideControls": false, + "sharedCrosshair": false, + "rows": [ + { + "collapse": false, + "editable": true, + "height": "100px", + "panels": [ + { + "cacheTimeout": null, + "colorBackground": false, + "colorValue": false, + "colors": [ + "rgba(245, 54, 54, 0.9)", + "rgba(237, 129, 40, 0.89)", + "rgba(50, 172, 45, 0.97)" + ], + "datasource": null, + "editable": true, + "error": false, + "format": "none", + "id": 3, + "interval": null, + "isNew": true, + "links": [], + "maxDataPoints": 100, + "nullPointMode": "connected", + "nullText": null, + "postfix": "", + "postfixFontSize": "50%", + "prefix": "", + "prefixFontSize": "50%", + "span": 4, + "sparkline": { + "fillColor": "rgba(31, 118, 189, 0.18)", + "full": false, + "lineColor": "rgb(31, 120, 193)", + "show": false + }, + "targets": [ + { + "application": { + "filter": "General" + }, + "functions": [], + "group": { + "filter": "Linux servers" + }, + "host": { + "filter": "Zabbix server" + }, + "item": { + "filter": "Host name" + }, + "mode": 2, + "refId": "A" + } + ], + "thresholds": "", + "title": "Host name", + "type": "singlestat", + "valueFontSize": "80%", + "valueMaps": [ + { + "op": "=", + "text": "N/A", + "value": "null" + } + ], + "valueName": "avg" + }, + { + "cacheTimeout": null, + "colorBackground": false, + "colorValue": false, + "colors": [ + "rgba(245, 54, 54, 0.9)", + "rgba(237, 129, 40, 0.89)", + "rgba(50, 172, 45, 0.97)" + ], + "datasource": null, + "decimals": 0, + "editable": true, + "error": false, + "format": "s", + "id": 4, + "interval": null, + "isNew": true, + "links": [], + "maxDataPoints": "", + "nullPointMode": "connected", + "nullText": null, + "postfix": "", + "postfixFontSize": "50%", + "prefix": "", + "prefixFontSize": "50%", + "span": 4, + "sparkline": { + "fillColor": "rgba(31, 118, 189, 0.18)", + "full": false, + "lineColor": "rgb(31, 120, 193)", + "show": false + }, + "targets": [ + { + "application": { + "filter": "General" + }, + "functions": [], + "group": { + "filter": "Zabbix servers" + }, + "host": { + "filter": "Zabbix server" + }, + "item": { + "filter": "System uptime" + }, + "mode": 0, + "refId": "A" + } + ], + "thresholds": "", + "title": "Uptime", + "type": "singlestat", + "valueFontSize": "80%", + "valueMaps": [ + { + "op": "=", + "text": "N/A", + "value": "null" + } + ], + "valueName": "current" + }, + { + "cacheTimeout": null, + "colorBackground": false, + "colorValue": false, + "colors": [ + "rgba(245, 54, 54, 0.9)", + "rgba(237, 129, 40, 0.89)", + "rgba(50, 172, 45, 0.97)" + ], + "datasource": null, + "editable": true, + "error": false, + "format": "none", + "id": 5, + "interval": null, + "isNew": true, + "links": [], + "maxDataPoints": "", + "nullPointMode": "connected", + "nullText": null, + "postfix": "", + "postfixFontSize": "50%", + "prefix": "", + "prefixFontSize": "50%", + "span": 4, + "sparkline": { + "fillColor": "rgba(31, 118, 189, 0.18)", + "full": false, + "lineColor": "rgb(31, 120, 193)", + "show": false + }, + "targets": [ + { + "application": { + "filter": "Zabbix server" + }, + "functions": [], + "group": { + "filter": "Zabbix servers" + }, + "host": { + "filter": "Zabbix server" + }, + "item": { + "filter": "Required performance of Zabbix server, new values per second" + }, + "mode": 0, + "refId": "A" + } + ], + "thresholds": "", + "title": "Required performance, NVPS", + "type": "singlestat", + "valueFontSize": "80%", + "valueMaps": [ + { + "op": "=", + "text": "N/A", + "value": "null" + } + ], + "valueName": "current" + } + ], + "title": "General" + }, + { + "collapse": false, + "editable": true, + "height": "300px", + "panels": [ + { + "aliasColors": {}, + "bars": false, + "datasource": null, + "editable": true, + "error": false, + "fill": 1, + "grid": { + "threshold1": null, + "threshold1Color": "rgba(216, 200, 27, 0.27)", + "threshold2": null, + "threshold2Color": "rgba(234, 112, 112, 0.22)" + }, + "id": 1, + "isNew": true, + "legend": { + "alignAsTable": true, + "avg": false, + "current": false, + "max": false, + "min": false, + "rightSide": true, + "show": true, + "total": false, + "values": false + }, + "lines": true, + "linewidth": 2, + "links": [], + "nullPointMode": "connected", + "percentage": false, + "pointradius": 5, + "points": false, + "renderer": "flot", + "seriesOverrides": [], + "span": 7, + "stack": true, + "steppedLine": false, + "targets": [ + { + "application": { + "filter": "CPU" + }, + "functions": [], + "group": { + "filter": "Zabbix servers" + }, + "host": { + "filter": "Zabbix server" + }, + "item": { + "filter": "/CPU (?!idle)/" + }, + "mode": 0, + "refId": "A" + } + ], + "timeFrom": null, + "timeShift": null, + "title": "CPU", + "tooltip": { + "msResolution": false, + "shared": true, + "value_type": "individual" + }, + "type": "graph", + "xaxis": { + "show": true + }, + "yaxes": [ + { + "format": "short", + "label": null, + "logBase": 1, + "max": null, + "min": null, + "show": true + }, + { + "format": "short", + "label": null, + "logBase": 1, + "max": null, + "min": null, + "show": true + } + ] + }, + { + "columns": [ + { + "text": "Current", + "value": "current" + }, + { + "text": "Avg", + "value": "avg" + } + ], + "editable": true, + "error": false, + "fontSize": "100%", + "id": 2, + "isNew": true, + "links": [], + "pageSize": null, + "scroll": true, + "showHeader": true, + "sort": { + "col": 0, + "desc": true + }, + "span": 5, + "styles": [ + { + "dateFormat": "YYYY-MM-DD HH:mm:ss", + "pattern": "Time", + "type": "date" + }, + { + "colorMode": null, + "colors": [ + "rgba(245, 54, 54, 0.9)", + "rgba(237, 129, 40, 0.89)", + "rgba(50, 172, 45, 0.97)" + ], + "decimals": 2, + "pattern": "/.*/", + "thresholds": [], + "type": "number", + "unit": "short" + } + ], + "targets": [ + { + "application": { + "filter": "Zabbix server" + }, + "functions": [], + "group": { + "filter": "Zabbix servers" + }, + "host": { + "filter": "Zabbix server" + }, + "item": { + "filter": "/Zabbix busy/" + }, + "mode": 0, + "refId": "A" + } + ], + "title": "Zabbix processes", + "transform": "timeseries_aggregations", + "type": "table" + } + ], + "title": "Row" + } + ], + "time": { + "from": "now-6h", + "to": "now" + }, + "timepicker": { + "refresh_intervals": [ + "5s", + "10s", + "30s", + "1m", + "5m", + "15m", + "30m", + "1h", + "2h", + "1d" + ], + "time_options": [ + "5m", + "15m", + "1h", + "6h", + "12h", + "24h", + "2d", + "7d", + "30d" + ] + }, + "templating": { + "list": [] + }, + "annotations": { + "list": [] + }, + "schemaVersion": 12, + "version": 5, + "links": [] +} diff --git a/src/plugin.json b/src/plugin.json index c07b14f1d..fdc403db5 100644 --- a/src/plugin.json +++ b/src/plugin.json @@ -3,8 +3,6 @@ "name": "Zabbix App", "id": "zabbix-app", - "pages": [], - "css": { "dark": "css/dark.css", "light": "css/light.css" @@ -26,9 +24,20 @@ }, "includes": [ - {"type": "dashboard", "name": "Example Zabbix Dashboard", "path": "dashboards/example_zabbix_dashboard.json"}, - {"type": "panel", "name": "Triggers Panel"}, - {"type": "datasource", "name": "Zabbix Datasource"} + { + "type": "datasource", + "name": "Zabbix Datasource" + }, + { + "type": "panel", + "name": "Triggers Panel" + }, + { + "type": "dashboard", + "name": "Zabbix Server Dashboard", + "path": "dashboards/zabbix_server_dashboard.json", + "addToNav": true + } ], "dependencies": { From de191c475e86006482d582b23edadc5e8e376bd4 Mon Sep 17 00:00:00 2001 From: Alexander Zobnin Date: Thu, 31 Mar 2016 21:04:24 +0300 Subject: [PATCH 196/205] Fixed templateUrl path (use public/plugins/zabbix-app prefix) --- src/panel-triggers/editor.js | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/src/panel-triggers/editor.js b/src/panel-triggers/editor.js index 8c2f61102..65cbecc39 100644 --- a/src/panel-triggers/editor.js +++ b/src/panel-triggers/editor.js @@ -150,7 +150,7 @@ class TriggerPanelEditorCtrl{ this.popoverSrv.show({ element: el, placement: 'top', - templateUrl: 'public/plugins/zabbix-triggers-panel/trigger.colorpicker.html', + templateUrl: 'public/plugins/zabbix-app/panel-triggers/trigger.colorpicker.html', scope: popoverScope }); } @@ -167,7 +167,7 @@ class TriggerPanelEditorCtrl{ this.popoverSrv.show({ element: el, placement: 'top', - templateUrl: 'public/plugins/zabbix-triggers-panel/trigger.colorpicker.html', + templateUrl: 'public/plugins/zabbix-app/panel-triggers/trigger.colorpicker.html', scope: popoverScope }); } @@ -192,7 +192,7 @@ export function triggerPanelEditor() { return { restrict: 'E', scope: true, - templateUrl: 'public/plugins/zabbix-triggers-panel/editor.html', + templateUrl: 'public/plugins/zabbix-app/panel-triggers/editor.html', controller: TriggerPanelEditorCtrl, }; } From bb7420af1496aec49a08ec9926b3e156cc83b153 Mon Sep 17 00:00:00 2001 From: Alexander Zobnin Date: Thu, 31 Mar 2016 21:17:11 +0300 Subject: [PATCH 197/205] Override onInitMetricsPanelEditMode() method from MetricsPanelCtrl. --- src/panel-triggers/module.js | 15 +++++---------- 1 file changed, 5 insertions(+), 10 deletions(-) diff --git a/src/panel-triggers/module.js b/src/panel-triggers/module.js index 73d6afaa9..253222de0 100644 --- a/src/panel-triggers/module.js +++ b/src/panel-triggers/module.js @@ -72,17 +72,12 @@ class TriggerPanelCtrl extends MetricsPanelCtrl { this.refreshData(); } - // Add panel editor - initEditMode() { - // Use initialize method from PanelCtrl instead MetricsPanelCtrl. - // We don't need metric editor from Metrics Panel. - this.editorTabs = []; - this.addEditorTab('General', 'public/app/partials/panelgeneral.html'); - - this.icon = "fa fa-lightbulb-o"; + /** + * Override onInitMetricsPanelEditMode() method from MetricsPanelCtrl. + * We don't need metric editor from Metrics Panel. + */ + onInitMetricsPanelEditMode() { this.addEditorTab('Options', triggerPanelEditor, 2); - this.editModeInitiated = true; - this.events.emit('init-edit-mode', null); } onMetricsPanelRefresh() { From 75f4a8eeb4da65b5dc30beef25596cac314e46b9 Mon Sep 17 00:00:00 2001 From: Alexander Zobnin Date: Thu, 31 Mar 2016 21:24:44 +0300 Subject: [PATCH 198/205] Fixed triggers filtering by application. --- src/datasource-zabbix/queryProcessor.service.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/datasource-zabbix/queryProcessor.service.js b/src/datasource-zabbix/queryProcessor.service.js index 49ab271c1..abeffdb17 100644 --- a/src/datasource-zabbix/queryProcessor.service.js +++ b/src/datasource-zabbix/queryProcessor.service.js @@ -195,7 +195,7 @@ angular.module('grafana.services').factory('QueryProcessor', function($q) { var query = {}; if (appFilter) { - query.applicationids = _.flatten(_.map(filteredApps, 'applicationids')); + query.applicationids = _.flatten(_.map(filteredApps, 'applicationid')); } if (hostFilter) { query.hostids = _.map(filteredHosts, 'hostid'); From f5896a963fa22b168523860e2060eeab69a9f6da Mon Sep 17 00:00:00 2001 From: Alexander Zobnin Date: Thu, 31 Mar 2016 21:25:49 +0300 Subject: [PATCH 199/205] Small panel-triggers/module.js refactor. --- src/panel-triggers/module.js | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/src/panel-triggers/module.js b/src/panel-triggers/module.js index 253222de0..7d791fe90 100644 --- a/src/panel-triggers/module.js +++ b/src/panel-triggers/module.js @@ -93,9 +93,12 @@ class TriggerPanelCtrl extends MetricsPanelCtrl { var queryProcessor = datasource.queryProcessor; var showEvents = self.panel.showEvents.value; var triggerFilter = self.panel.triggers; + + // Replace template variables var groupFilter = self.templateSrv.replace(triggerFilter.group.filter); var hostFilter = self.templateSrv.replace(triggerFilter.host.filter); var appFilter = self.templateSrv.replace(triggerFilter.application.filter); + var buildQuery = queryProcessor.buildTriggerQuery(groupFilter, hostFilter, appFilter); return buildQuery.then(query => { return zabbix.getTriggers(query.groupids, @@ -124,8 +127,10 @@ class TriggerPanelCtrl extends MetricsPanelCtrl { // Set color if (trigger.value === '1') { + // Problem state triggerObj.color = self.panel.triggerSeverity[trigger.priority].color; } else { + // OK state triggerObj.color = self.panel.okEventColor; } From c40b53eb575e62f216ff38c1b4c77106e70dbdb7 Mon Sep 17 00:00:00 2001 From: Alexander Zobnin Date: Thu, 31 Mar 2016 21:40:02 +0300 Subject: [PATCH 200/205] Fixed Triggers panel refreshing. --- src/panel-triggers/editor.html | 20 ++++++++++---------- src/panel-triggers/editor.js | 8 ++++---- src/panel-triggers/module.js | 15 ++++++++++++++- 3 files changed, 28 insertions(+), 15 deletions(-) diff --git a/src/panel-triggers/editor.html b/src/panel-triggers/editor.html index 9a0c00c28..fc55d3e45 100644 --- a/src/panel-triggers/editor.html +++ b/src/panel-triggers/editor.html @@ -98,7 +98,7 @@
    Options
  • @@ -109,7 +109,7 @@
    Options
    type="number" ng-model="editor.panel.limit" ng-model-onblur - ng-change="editor.panelCtrl.refreshData()"> + ng-change="editor.panelCtrl.refresh()">
  • @@ -123,7 +123,7 @@
    Options
  • @@ -133,7 +133,7 @@
    Options
  • @@ -209,7 +209,7 @@
    Options
    @@ -217,7 +217,7 @@
    Options
  • Customize triggers severity and colors style="color: white" ng-style="{background: trigger.color}" ng-model-onblur - ng-change="editor.panelCtrl.refreshData()"> + ng-change="editor.panelCtrl.refresh()">
  • - +
  • @@ -262,7 +262,7 @@
    Customize triggers severity and colors
    type="checkbox" ng-model="trigger.show" ng-checked="trigger.show" - ng-change="editor.panelCtrl.refreshData()"> + ng-change="editor.panelCtrl.refresh()">
  • @@ -278,7 +278,7 @@
    Customize triggers severity and colors
  • + ng-change="editor.panelCtrl.refresh()">
  • diff --git a/src/panel-triggers/editor.js b/src/panel-triggers/editor.js index 65cbecc39..df53ff536 100644 --- a/src/panel-triggers/editor.js +++ b/src/panel-triggers/editor.js @@ -73,7 +73,7 @@ class TriggerPanelEditorCtrl{ this.datasourceSrv.get(this.panel.datasource).then(function (datasource) { self.datasource = datasource; self.initFilters(); - self.panelCtrl.refreshData(); + self.panelCtrl.refresh(); }); } @@ -119,7 +119,7 @@ class TriggerPanelEditorCtrl{ var newTarget = _.cloneDeep(this.panel.triggers); if (!_.isEqual(this.oldTarget, this.panel.triggers)) { this.oldTarget = newTarget; - this.panelCtrl.refreshData(); + this.panelCtrl.refresh(); } } @@ -128,11 +128,11 @@ class TriggerPanelEditorCtrl{ trigger.color = this.panel.triggerSeverity[trigger.priority].color; trigger.severity = this.panel.triggerSeverity[trigger.priority].severity; }); - this.panelCtrl.refreshData(); + this.panelCtrl.refresh(); } datasourceChanged() { - this.panelCtrl.refreshData(); + this.panelCtrl.refresh(); } changeTriggerSeverityColor(trigger, color) { diff --git a/src/panel-triggers/module.js b/src/panel-triggers/module.js index 7d791fe90..b29c0b048 100644 --- a/src/panel-triggers/module.js +++ b/src/panel-triggers/module.js @@ -80,7 +80,19 @@ class TriggerPanelCtrl extends MetricsPanelCtrl { this.addEditorTab('Options', triggerPanelEditor, 2); } + refresh() { + this.onMetricsPanelRefresh(); + } + onMetricsPanelRefresh() { + // ignore fetching data if another panel is in fullscreen + if (this.otherPanelInFullscreenMode()) { return; } + + // clear loading/error state + delete this.error; + this.loading = true; + this.setTimeQueryStart(); + this.refreshData(); } @@ -196,7 +208,8 @@ class TriggerPanelCtrl extends MetricsPanelCtrl { // Limit triggers number self.triggerList = _.first(triggerList, self.panel.limit); - self.renderingCompleted(); + this.setTimeQueryEnd(); + this.loading = false; }); }); }); From 2de0756d4532fd973f56832700047a8c84b552e5 Mon Sep 17 00:00:00 2001 From: Alexander Zobnin Date: Thu, 31 Mar 2016 21:57:13 +0300 Subject: [PATCH 201/205] Changed Introduction section. --- docs/sources/index.md | 17 ++++++++--------- 1 file changed, 8 insertions(+), 9 deletions(-) diff --git a/docs/sources/index.md b/docs/sources/index.md index baf5a1714..bd9ed6ba2 100644 --- a/docs/sources/index.md +++ b/docs/sources/index.md @@ -1,11 +1,11 @@ page_title: About Grafana-Zabbix -page_description: Introduction to Grafana-Zabbix. +page_description: Introduction to Grafana-Zabbix plugin. -# About Grafana-Zabbix +# About Grafana-Zabbix plugin -Grafana-Zabbix is a plugin bundle for Grafana allowing to visualize monitoring data from Zabbix -and create dashboards for analyzing metrics and realtime monitoring. The main goals of this project -is extend Zabbix capabilities for monitoring data visualization and provide quick and powerful way +Grafana-Zabbix is a plugin for Grafana allowing to visualize monitoring data from Zabbix +and create dashboards for analyzing metrics and realtime monitoring. Main goals of this project +are extend Zabbix capabilities for monitoring data visualization and provide quick and powerful way to create dashboards. It is possible due both Grafana and Grafana-Zabbix plugin features. ## Community Resources, Feedback, and Support @@ -22,18 +22,17 @@ a number of ways to get help: - [Gitter room](https://gitter.im/alexanderzobnin/grafana-zabbix) - [Twitter](https://twitter.com/AlexanderZobnin) -Or you can just send me [emai](mailto:alexanderzobnin@gmail.com). +Or you can just send me [email](mailto:alexanderzobnin@gmail.com). ## Support Project I develop this project in my free time, but if you really find it helpful and promising, you can support me. There are some ways to do this. You can [donate](https://www.paypal.me/alexanderzobnin) any reasonable amount, or you can request a feature development, interesting for you (for example, -Triggers panel was sponsored by [Core IT Project](http://coreit.fr/)). Or you can even hire me if -you want to maintaining this project in your company. +Triggers panel was sponsored by [Core IT Project](http://coreit.fr/)). ## License -By utilizing this software, you agree to the terms of the included license. Grafana-Zabbix is +By utilizing this software, you agree to the terms of the included license. Grafana-Zabbix plugin is licensed under the Apache 2.0 agreement. See [LICENSE](https://github.com/alexanderzobnin/grafana-zabbix/blob/master/LICENSE.md) for the full license terms. From 3a015c8d4d9488cb7335dcb76417c7be37ee5216 Mon Sep 17 00:00:00 2001 From: Alexander Zobnin Date: Thu, 31 Mar 2016 22:36:03 +0300 Subject: [PATCH 202/205] Updated installation docs for version 3.0 --- docs/mkdocs.yml | 3 +- docs/sources/installation/configuration.md | 75 +++++++++++++ docs/sources/installation/index.md | 117 ++++++--------------- 3 files changed, 109 insertions(+), 86 deletions(-) create mode 100644 docs/sources/installation/configuration.md diff --git a/docs/mkdocs.yml b/docs/mkdocs.yml index 7397aaaff..a11d34689 100644 --- a/docs/mkdocs.yml +++ b/docs/mkdocs.yml @@ -13,7 +13,8 @@ pages: - 'About Grafana-Zabbix': 'index.md' - 'Feature Highlights': 'features.md' - Installation: - - 'Installation and Configuration': 'installation/index.md' + - 'Installation': 'installation/index.md' + - 'Configuration': 'installation/configuration.md' - 'Troubleshooting': 'installation/troubleshooting.md' - User Guides: - 'Getting Started': 'guides/gettingstarted.md' diff --git a/docs/sources/installation/configuration.md b/docs/sources/installation/configuration.md new file mode 100644 index 000000000..824668dd5 --- /dev/null +++ b/docs/sources/installation/configuration.md @@ -0,0 +1,75 @@ +# Configuration + +To add new Zabbix data source open _Data Sources_ in side panel, click _Add new_ and select Zabbix +from dropdown list. + +![Add zabbix data source](../img/installation-add_datasource.png) + +Then configure a data source + +![Configure zabbix data source](../img/installation-datasource_config.png) + +### Http settings + +- **Url**: set Zabbix API url (full path with `api_jsonrpc.php`). +- **Access**: can be either _proxy_ or _direct_. +- **Http Auth**: configure if you use proxy authentication. + - **Basic Auth**: + - **With Credentials**: + +### Zabbiz API details + +- **User** and **Password**: setup login for access to Zabbix API. Also check user's permissions +in Zabbix if you can't get any groups and hosts in Grafana. +- **Trends**: enable if you use patch for trends +support in Zabbix 2.x ([ZBXNEXT-1193](https://support.zabbix.com/browse/ZBXNEXT-1193)). This is +strictly recommended for displaying long time periods (more than few days, depending of your item's +updating interval in Zabbix) because few days of item history contains tons of points. Using trends +can increase Grafana performance. + - **Use trends from**: time after which trends will be used. Default is **7d** (7 days). + You can set the time in Grafana format. Valid time specificators are: + - **h** - hours + - **d** - days + - **M** - months +- **Metrics limit**: maximum items number which can be returned by one request. Helps to prevent +Grafana slowdown due wrong request. + +Then click _Add_ - datasource will be added and you can check connection using _Test Connection_ button. + +![Test Connection](../img/installation-test_connection.png) + +This feature can help to find some mistakes like invalid user name or password, wrong api url. + +![Test Connection - Error](../img/installation-test_connection_error.png) + + +## Note about Zabbix 2.2 or less +Zabbix API (api_jsonrpc.php) before zabbix 2.4 don't allow cross-domain requests (CORS). And you +can get HTTP error 412 (Precondition Failed). +To fix it add this code to api_jsonrpc.php immediately after the copyright: + +```php +header('Access-Control-Allow-Origin: *'); +header('Access-Control-Allow-Headers: Content-Type'); +header('Access-Control-Allow-Methods: POST'); +header('Access-Control-Max-Age: 1000'); + +if ($_SERVER['REQUEST_METHOD'] === 'OPTIONS') { + return; +} +``` +before + +```php +require_once dirname(__FILE__).'/include/func.inc.php'; +require_once dirname(__FILE__).'/include/classes/core/CHttpRequest.php'; +``` +[Full fix listing](https://gist.github.com/alexanderzobnin/f2348f318d7a93466a0c). +For more details see zabbix issues [ZBXNEXT-1377](https://support.zabbix.com/browse/ZBXNEXT-1377) +and [ZBX-8459](https://support.zabbix.com/browse/ZBX-8459). + +## Note about Browser Cache +After updating plugin, clear browser cache and reload application page. See details +for [Chrome](https://support.google.com/chrome/answer/95582), +[Firefox](https://support.mozilla.org/en-US/kb/how-clear-firefox-cache). You need to clear cache +only, not cookies, history and other data. diff --git a/docs/sources/installation/index.md b/docs/sources/installation/index.md index dea39fc8b..5246a6165 100644 --- a/docs/sources/installation/index.md +++ b/docs/sources/installation/index.md @@ -2,117 +2,64 @@ page_title: Grafana-Zabbix Installation page_description: Installation instructions for Grafana-Zabbix. # Installation + +## From release package Download [latest release](https://github.com/alexanderzobnin/grafana-zabbix/releases/latest) for relative Grafana version or just clone a repo: ``` git clone https://github.com/alexanderzobnin/grafana-zabbix.git ``` -## Copy into plugins directory Copy content of `plugins` into your grafana plugins directory (default `/var/lib/grafana/plugins` if your installing grafana with package). Restart grafana-server and the plugin should be automatically detected and used. +## Building from sources +You need NodeJS, npm and Grunt for building plugin from sources. Read more about required versions +in [Grafana docs](http://docs.grafana.org/project/building_from_source/). + ```sh git clone https://github.com/alexanderzobnin/grafana-zabbix.git -cp -r ./grafana-zabbix/plugins/* /your/grafana/plugins/directory/ -sudo service grafana-server restart +cd grafana-zabbix +npm install +npm install -g grunt-cli +grunt ``` - -## Use directory of your choice - -By this way you can update plugin by simple `git pull` command. - -Edit your grafana.ini config file (Default location is at `/etc/grafana/grafana.ini`) -and add paths to plugins: +Plugin will built into *dist/* directory. Then you can copy it into your grafana plugins directory +or set path to compiled plugin in grafana config: ```ini [plugin.zabbix] -path = /home/your/clone/dir/plugins/datasource-zabbix - -[plugin.triggers] -path = /home/your/clone/dir/plugins/panel-triggers +path = /home/your/clone/dir/grafana-zabbix/dist ``` -Note that if you copy it into the grafana plugins directory you do not need to add the above -config option. That is only -if you want to place the plugin in a directory outside the standard plugins directory. -Be aware that grafana-server -needs read access to the directory. - -# Configuration - -To add new Zabbix data source open _Data Sources_ in side panel, click _Add new_ and select Zabbix -from dropdown list. - -![Add zabbix data source](../img/installation-add_datasource.png) - -Then configure a data source - -![Configure zabbix data source](../img/installation-datasource_config.png) - -### Http settings +If you need to upgrade plugin use -- **Url**: set Zabbix API url (full path with `api_jsonrpc.php`). -- **Access**: can be either _proxy_ or _direct_. -- **Http Auth**: configure if you use proxy authentication. - - **Basic Auth**: - - **With Credentials**: - -### Zabbiz API details - -- **User** and **Password**: setup login for access to Zabbix API. Also check user's permissions -in Zabbix if you can't get any groups and hosts in Grafana. -- **Trends**: enable if you use patch for trends -support in Zabbix 2.x ([ZBXNEXT-1193](https://support.zabbix.com/browse/ZBXNEXT-1193)). This is -strictly recommended for displaying long time periods (more than few days, depending of your item's -updating interval in Zabbix) because few days of item history contains tons of points. Using trends -can increase Grafana performance. - - **Use trends from**: time after which trends will be used. Default is **7d** (7 days). - You can set the time in Grafana format. Valid time specificators are: - - **h** - hours - - **d** - days - - **M** - months -- **Metrics limit**: maximum items number which can be returned by one request. Helps to prevent -Grafana slowdown due wrong request. - -Then click _Add_ - datasource will be added and you can check connection using _Test Connection_ button. +```sh +git pull +grunt +``` -![Test Connection](../img/installation-test_connection.png) +Restart Grafana server -This feature can help to find some mistakes like invalid user name or password, wrong api url. +```sh +sudo service grafana-server restart +systemctl restart grafana-server +``` -![Test Connection - Error](../img/installation-test_connection_error.png) +## Using grafana-cli tool +Get list of available plugins -## Note about Zabbix 2.2 or less -Zabbix API (api_jsonrpc.php) before zabbix 2.4 don't allow cross-domain requests (CORS). And you -can get HTTP error 412 (Precondition Failed). -To fix it add this code to api_jsonrpc.php immediately after the copyright: +```sh +grafana-cli plugins list-remote +``` -```php -header('Access-Control-Allow-Origin: *'); -header('Access-Control-Allow-Headers: Content-Type'); -header('Access-Control-Allow-Methods: POST'); -header('Access-Control-Max-Age: 1000'); +Install zabbix plugin -if ($_SERVER['REQUEST_METHOD'] === 'OPTIONS') { - return; -} +```sh +grafana-cli plugins install zabbix-app ``` -before -```php -require_once dirname(__FILE__).'/include/func.inc.php'; -require_once dirname(__FILE__).'/include/classes/core/CHttpRequest.php'; -``` -[Full fix listing](https://gist.github.com/alexanderzobnin/f2348f318d7a93466a0c). -For more details see zabbix issues [ZBXNEXT-1377](https://support.zabbix.com/browse/ZBXNEXT-1377) -and [ZBX-8459](https://support.zabbix.com/browse/ZBX-8459). - -## Note about Browser Cache -After updating plugin, clear browser cache and reload application page. See details -for [Chrome](https://support.google.com/chrome/answer/95582), -[Firefox](https://support.mozilla.org/en-US/kb/how-clear-firefox-cache). You need to clear cache -only, not cookies, history and other data. +Read more in [Grafana docs](http://docs.grafana.org/plugins/installation/) From c19ea6336379664c4f2d077937c162a926da2854 Mon Sep 17 00:00:00 2001 From: Alexander Zobnin Date: Thu, 31 Mar 2016 23:16:06 +0300 Subject: [PATCH 203/205] Update README.md --- README.md | 34 ++++++++++++---------------------- 1 file changed, 12 insertions(+), 22 deletions(-) diff --git a/README.md b/README.md index 5fe60de4d..4a369f85d 100644 --- a/README.md +++ b/README.md @@ -1,45 +1,35 @@ -# Grafana-Zabbix +# Zabbix plugin for Grafana -#### Zabbix datasource for Grafana dashboard +Zabbix datasource, Triggers panel and more. [![Gitter](https://badges.gitter.im/Join%20Chat.svg)](https://gitter.im/alexanderzobnin/grafana-zabbix?utm_source=badge&utm_medium=badge&utm_campaign=pr-badge) -##### [Donate](https://www.paypal.me/alexanderzobnin) - ##### See features overview and dashboards examples at Grafana-Zabbix [Live demo](http://play.grafana-zabbix.org) site. ##### Download [latest release](https://github.com/alexanderzobnin/grafana-zabbix/releases/latest) -Display your Zabbix data directly in [Grafana](http://grafana.org) dashboards! +### Meet grafana-zabbix 3.0 +Download [grafana-zabbix 3.0 beta](https://github.com/alexanderzobnin/grafana-zabbix/releases/latest) -![Dashboard](https://cloud.githubusercontent.com/assets/4932851/8269101/9e6ee67e-17a3-11e5-85de-fe9dcc2dd375.png) +[Documentation](http://docs.grafana-zabbix.org) +Read [installation instruction](http://docs.grafana-zabbix.org/installation/) for version 3.0. + +Display your Zabbix data with powerful [Grafana](http://grafana.org) dashboards! -#### [Documentation](https://github.com/alexanderzobnin/grafana-zabbix/wiki) -1. [**Overview**](https://github.com/alexanderzobnin/grafana-zabbix/wiki/Overview) -2. [**Installation**](https://github.com/alexanderzobnin/grafana-zabbix/wiki/Installation#grafana-21x-and-25x) -3. [**User’s Guide**](https://github.com/alexanderzobnin/grafana-zabbix/wiki/Usage) -4. [**Troubleshooting**](https://github.com/alexanderzobnin/grafana-zabbix/wiki/Troubleshooting) +![Dashboard](https://cloud.githubusercontent.com/assets/4932851/8269101/9e6ee67e-17a3-11e5-85de-fe9dcc2dd375.png) ## Features #### Flexible metric editor * hosts and items filtering: - -[![regex_filter](https://cloud.githubusercontent.com/assets/4932851/8312766/5eb34480-19e7-11e5-925f-452a99ec0ab6.gif)](https://cloud.githubusercontent.com/assets/4932851/8312766/5eb34480-19e7-11e5-925f-452a99ec0ab6.gif) - - * Custom scale for each target: - -![Scale](https://cloud.githubusercontent.com/assets/4932851/8269207/212549be-17a9-11e5-9e33-90deb90ddc13.png) + * Custom scale for each target #### Templated dashboards support Group, host, application or item names can be replaced with a template variable. This allows you to create generic dashboards that can quickly be changed to show stats for a specific cluster, server or application. -[![templated_dashboard](https://cloud.githubusercontent.com/assets/4932851/8312492/7f286c38-19e5-11e5-8c19-1b9e97292b06.gif)](https://cloud.githubusercontent.com/assets/4932851/8312492/7f286c38-19e5-11e5-8c19-1b9e97292b06.gif) #### Annotations support - * Display zabbix events on graphs: -![Annotations](https://cloud.githubusercontent.com/assets/4932851/8269358/622ec3be-17ad-11e5-8023-eba137369cfe.png) - * Show acknowledges for problems: -![Acknowledges](https://cloud.githubusercontent.com/assets/4932851/8269375/e6d8706a-17ad-11e5-8e2d-2d707d8ee67f.png) + * Display zabbix events on graphs + * Show acknowledges for problems Read more about Grafana features at [grafana.org](http://grafana.org) From de1b8edf3550111aafd5138c18a419023af30a36 Mon Sep 17 00:00:00 2001 From: Alexander Zobnin Date: Thu, 31 Mar 2016 23:20:46 +0300 Subject: [PATCH 204/205] Fixed installation docs. --- docs/sources/installation/index.md | 9 ++------- 1 file changed, 2 insertions(+), 7 deletions(-) diff --git a/docs/sources/installation/index.md b/docs/sources/installation/index.md index 5246a6165..3f10253d0 100644 --- a/docs/sources/installation/index.md +++ b/docs/sources/installation/index.md @@ -5,13 +5,8 @@ page_description: Installation instructions for Grafana-Zabbix. ## From release package Download [latest release](https://github.com/alexanderzobnin/grafana-zabbix/releases/latest) -for relative Grafana version or just clone a repo: -``` -git clone https://github.com/alexanderzobnin/grafana-zabbix.git -``` - -Copy content of `plugins` into your grafana plugins directory (default `/var/lib/grafana/plugins` -if your installing grafana with package). +for relative Grafana version. Unpack archive and copy *grafana-zabbix* into your grafana +plugins directory (default `/var/lib/grafana/plugins` if your installing grafana with package). Restart grafana-server and the plugin should be automatically detected and used. ## Building from sources From d9f5d5f146e5aa8d1fa057b588dffe84941fbf39 Mon Sep 17 00:00:00 2001 From: Alexander Zobnin Date: Fri, 1 Apr 2016 00:04:36 +0300 Subject: [PATCH 205/205] Changed plugin version to beta. --- src/plugin.json | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/src/plugin.json b/src/plugin.json index fdc403db5..1f030c984 100644 --- a/src/plugin.json +++ b/src/plugin.json @@ -9,7 +9,7 @@ }, "info": { - "description": "Zabbix bundle for Grafana", + "description": "Zabbix plugin for Grafana", "author": { "name": "Alexander Zobnin", "url": "http://grafana-zabbix.org" @@ -19,8 +19,8 @@ {"name": "Project site", "url": "https://github.com/alexanderzobnin/grafana-zabbix"}, {"name": "License & Terms", "url": "https://github.com/alexanderzobnin/grafana-zabbix/blob/master/LICENSE.md"} ], - "version": "1.0.0", - "updated": "2016-03-28" + "version": "1.0.0-beta1", + "updated": "2016-03-31" }, "includes": [