From 1c4b84e33b39875cdd1981bae7ce798c4a768dfe Mon Sep 17 00:00:00 2001 From: Alexander Zobnin Date: Sun, 17 May 2015 16:16:18 +0300 Subject: [PATCH 1/4] Refactoring - new handleZabbixAPIResponse method. --- zabbix/datasource.js | 112 ++++++++++++++++++++----------------------- 1 file changed, 51 insertions(+), 61 deletions(-) diff --git a/zabbix/datasource.js b/zabbix/datasource.js index c41221c..0bac8a3 100644 --- a/zabbix/datasource.js +++ b/zabbix/datasource.js @@ -36,11 +36,8 @@ function (angular, _, kbn) { var from = kbn.parseDate(options.range.from).getTime(); var to = kbn.parseDate(options.range.to).getTime(); - // Need for find target alias - var targets = options.targets; - // Remove undefined and hidden targets - var displayedTargets = _.filter(targets, function (target) { + var displayedTargets = _.filter(options.targets, function (target) { return (!target.hide && target.item); }); @@ -57,68 +54,61 @@ function (angular, _, kbn) { from = Math.ceil(from/1000); to = Math.ceil(to/1000); + var self = this; return this.performTimeSeriesQuery(target_items, from, to).then(function (response) { - /** - * Response should be in the format: - * data: [ - * { - * target: "Metric name", - * datapoints: [[, ], ...] - * }, - * { - * target: "Metric name", - * datapoints: [[, ], ...] - * }, - * ] - */ - - // Index returned datapoints by item/metric id - var indexed_result = _.groupBy(response, 'itemid'); - - // TODO: realize correct timeseries reduce - /* - // Reduce timeseries to the same size for stacking and tooltip work properly - var min_length = _.min(_.map(indexed_result, function (history) { - return history.length; - })); - _.each(indexed_result, function (item) { - item.splice(0, item.length - min_length); - });*/ - - // Sort result as the same as targets for display - // stacked timeseries in proper order - var sorted_history = _.sortBy(indexed_result, function (value, key, list) { - return _.indexOf(_.map(target_items, 'itemid'), key); - }); - - var series = _.map(sorted_history, - // Foreach itemid index: iterate over the data points and - // normalize to Grafana response format. - function (history, index) { - return { - // Lookup itemid:alias map - //target: targets[itemid].alias, - target: targets[index].alias, - - datapoints: _.map(history, function (p) { - - // Value must be a number for properly work - var value = Number(p.value); - - // TODO: Correct time for proper stacking - //var clock = Math.round(Number(p.clock) / 60) * 60; - return [value, p.clock * 1000]; - }) - }; - }) - return $q.when({data: series}); + return self.handleZabbixAPIResponse(response, displayedTargets) }); }; - /////////////////////////////////////////////////////////////////////// - /// Query methods - /////////////////////////////////////////////////////////////////////// + // Request data from Zabbix API + ZabbixAPIDatasource.prototype.handleZabbixAPIResponse = function(response, targets) { + /** + * Response should be in the format: + * data: [ + * { + * target: "Metric name", + * datapoints: [[, ], ...] + * }, + * { + * target: "Metric name", + * datapoints: [[, ], ...] + * }, + * ] + */ + + // Index returned datapoints by item/metric id + var indexed_result = _.groupBy(response, 'itemid'); + + // Sort result as the same as targets + // for display stacked timeseries in proper order + var sorted_history = _.sortBy(indexed_result, function (value, key, list) { + return _.indexOf(_.map(targets, function (target) { + return target.item.itemid; + }), key); + }); + + var series = _.map(sorted_history, + // Foreach itemid index: iterate over the data points and + // normalize to Grafana response format. + function (history, index) { + return { + // Lookup itemid:alias map + target: targets[index].alias, + + datapoints: _.map(history, function (p) { + + // Value must be a number for properly work + var value = Number(p.value); + + // TODO: Correct time for proper stacking + //var clock = Math.round(Number(p.clock) / 60) * 60; + return [value, p.clock * 1000]; + }) + }; + }) + return $q.when({data: series}); + }; // Request data from Zabbix API From 11581347186c2547b700b698732cae07b832dd22 Mon Sep 17 00:00:00 2001 From: Alexander Zobnin Date: Sun, 17 May 2015 22:39:30 +0300 Subject: [PATCH 2/4] Some night refactoring. --- zabbix/datasource.js | 127 ++++++++++++++++--------------------------- 1 file changed, 47 insertions(+), 80 deletions(-) diff --git a/zabbix/datasource.js b/zabbix/datasource.js index 0bac8a3..cdadd26 100644 --- a/zabbix/datasource.js +++ b/zabbix/datasource.js @@ -36,33 +36,29 @@ function (angular, _, kbn) { var from = kbn.parseDate(options.range.from).getTime(); var to = kbn.parseDate(options.range.to).getTime(); - // Remove undefined and hidden targets - var displayedTargets = _.filter(options.targets, function (target) { - return (!target.hide && target.item); - }); - - if (displayedTargets.length) { - // Extract zabbix api item objects from targets - var target_items = _.map(displayedTargets, 'item'); - } else { - // No valid targets, return the empty dataset - var d = $q.defer(); - d.resolve({ data: [] }); - return d.promise; - } - from = Math.ceil(from/1000); to = Math.ceil(to/1000); - var self = this; - return this.performTimeSeriesQuery(target_items, from, to).then(function (response) { - return self.handleZabbixAPIResponse(response, displayedTargets) + var promises = _.map(options.targets, function(target) { + // Remove undefined and hidden targets + if (target.hide || !target.item) { + return []; + } + + var self = this; + return this.performTimeSeriesQuery(target.item, from, to).then(function (response) { + return self.handleZabbixAPIResponse(response, target) + }); + }, this); + + return $q.all(promises).then(function(results) { + return { data: _.flatten(results) }; }); }; // Request data from Zabbix API - ZabbixAPIDatasource.prototype.handleZabbixAPIResponse = function(response, targets) { + ZabbixAPIDatasource.prototype.handleZabbixAPIResponse = function(response, target) { /** * Response should be in the format: * data: [ @@ -77,37 +73,22 @@ function (angular, _, kbn) { * ] */ - // Index returned datapoints by item/metric id - var indexed_result = _.groupBy(response, 'itemid'); + var series = { + // Lookup itemid:alias map + target: target.alias, - // Sort result as the same as targets - // for display stacked timeseries in proper order - var sorted_history = _.sortBy(indexed_result, function (value, key, list) { - return _.indexOf(_.map(targets, function (target) { - return target.item.itemid; - }), key); - }); + datapoints: _.map(response, function (p) { - var series = _.map(sorted_history, - // Foreach itemid index: iterate over the data points and - // normalize to Grafana response format. - function (history, index) { - return { - // Lookup itemid:alias map - target: targets[index].alias, + // Value must be a number for properly work + var value = Number(p.value); - datapoints: _.map(history, function (p) { - - // Value must be a number for properly work - var value = Number(p.value); - - // TODO: Correct time for proper stacking - //var clock = Math.round(Number(p.clock) / 60) * 60; - return [value, p.clock * 1000]; - }) - }; + // TODO: Correct time for proper stacking + //var clock = Math.round(Number(p.clock) / 60) * 60; + return [value, p.clock * 1000]; }) - return $q.when({data: series}); + }; + + return $q.when(series); }; @@ -152,42 +133,28 @@ function (angular, _, kbn) { * @param items: array of zabbix api item objects */ ZabbixAPIDatasource.prototype.performTimeSeriesQuery = function(items, start, end) { + var data = { + jsonrpc: '2.0', + method: 'history.get', + params: { + output: 'extend', + history: items.value_type, + itemids: items.itemid, + sortfield: 'clock', + sortorder: 'ASC', + limit: this.limitmetrics, + time_from: start, + }, + auth: this.auth, + id: 1 + }; - // Group items by value type for separate requests - var items_by_value_type = _.groupBy(items, 'value_type'); + // Relative queries (e.g. last hour) don't include an end time + if (end) { + data.params.time_till = end; + } - var self = this; - var apiRequests = []; - - // Prepare requests for each value type - _.each(items_by_value_type, function (value, key, list) { - var item_ids = _.map(value, 'itemid'); - var history_type = key; - var data = { - jsonrpc: '2.0', - method: 'history.get', - params: { - output: 'extend', - history: history_type, - itemids: item_ids, - sortfield: 'clock', - sortorder: 'ASC', - limit: self.limitmetrics, - time_from: start, - }, - auth: self.auth, - id: 1 - }; - - // Relative queries (e.g. last hour) don't include an end time - if (end) { - data.params.time_till = end; - } - - apiRequests.push(self.performZabbixAPIRequest(data)); - }); - - return this.handleMultipleRequest(apiRequests); + return this.performZabbixAPIRequest(data); }; From a62d0e60fc3abe5eef4c5c8448499ea6ab13c740 Mon Sep 17 00:00:00 2001 From: Alexander Zobnin Date: Mon, 18 May 2015 11:45:10 +0300 Subject: [PATCH 3/4] Refactoring: separate api request for each target. handleMultipleRequest() no more needed. --- zabbix/datasource.js | 50 +++++++------------------------------------- 1 file changed, 8 insertions(+), 42 deletions(-) diff --git a/zabbix/datasource.js b/zabbix/datasource.js index cdadd26..599718f 100644 --- a/zabbix/datasource.js +++ b/zabbix/datasource.js @@ -33,22 +33,20 @@ function (angular, _, kbn) { ZabbixAPIDatasource.prototype.query = function(options) { // get from & to in seconds - var from = kbn.parseDate(options.range.from).getTime(); - var to = kbn.parseDate(options.range.to).getTime(); - - from = Math.ceil(from/1000); - to = Math.ceil(to/1000); + var from = Math.ceil(kbn.parseDate(options.range.from).getTime() / 1000); + var to = Math.ceil(kbn.parseDate(options.range.to).getTime() / 1000); + // Create request for each target var promises = _.map(options.targets, function(target) { + // Remove undefined and hidden targets if (target.hide || !target.item) { return []; } - var self = this; - return this.performTimeSeriesQuery(target.item, from, to).then(function (response) { - return self.handleZabbixAPIResponse(response, target) - }); + // Perform request and then handle result + return this.performTimeSeriesQuery(target.item, from, to).then(_.partial( + this.handleZabbixAPIResponse, target)); }, this); return $q.all(promises).then(function(results) { @@ -58,7 +56,7 @@ function (angular, _, kbn) { // Request data from Zabbix API - ZabbixAPIDatasource.prototype.handleZabbixAPIResponse = function(response, target) { + ZabbixAPIDatasource.prototype.handleZabbixAPIResponse = function(target, response) { /** * Response should be in the format: * data: [ @@ -74,16 +72,10 @@ function (angular, _, kbn) { */ var series = { - // Lookup itemid:alias map target: target.alias, - datapoints: _.map(response, function (p) { - // Value must be a number for properly work var value = Number(p.value); - - // TODO: Correct time for proper stacking - //var clock = Math.round(Number(p.clock) / 60) * 60; return [value, p.clock * 1000]; }) }; @@ -158,32 +150,6 @@ function (angular, _, kbn) { }; - // Handle multiple request - ZabbixAPIDatasource.prototype.handleMultipleRequest = function(apiRequests) { - var history = []; - var performedQuery = null; - - // Build chain of api requests and put all history data into single array - _.each(apiRequests, function (apiRequest) { - if(!performedQuery) { - performedQuery = apiRequest.then(function (response) { - history = history.concat(response); - return history; - }); - } else { - performedQuery = performedQuery.then(function () { - return apiRequest.then(function (response) { - history = history.concat(response); - return history; - }); - }); - } - }); - - return performedQuery; - }; - - // Get authentication token ZabbixAPIDatasource.prototype.performZabbixAPILogin = function() { var options = { From c3b64fc2b5f2a819631eaa7ac857460f4aa834fb Mon Sep 17 00:00:00 2001 From: Alexander Zobnin Date: Mon, 18 May 2015 12:36:43 +0300 Subject: [PATCH 4/4] Refactoring: change performZabbixAPIRequest()? deleted code duplication. --- zabbix/datasource.js | 115 ++++++++++++++++++------------------------- 1 file changed, 47 insertions(+), 68 deletions(-) diff --git a/zabbix/datasource.js b/zabbix/datasource.js index 599718f..a7ab033 100644 --- a/zabbix/datasource.js +++ b/zabbix/datasource.js @@ -85,14 +85,20 @@ function (angular, _, kbn) { // Request data from Zabbix API - ZabbixAPIDatasource.prototype.performZabbixAPIRequest = function(request_data) { + ZabbixAPIDatasource.prototype.performZabbixAPIRequest = function(method, params) { var options = { method: 'POST', headers: { 'Content-Type': 'application/json' }, url: this.url, - data: request_data + data: { + jsonrpc: '2.0', + method: method, + params: params, + auth: this.auth, + id: 1 + } }; var performedQuery; @@ -125,28 +131,22 @@ function (angular, _, kbn) { * @param items: array of zabbix api item objects */ ZabbixAPIDatasource.prototype.performTimeSeriesQuery = function(items, start, end) { - var data = { - jsonrpc: '2.0', - method: 'history.get', - params: { - output: 'extend', - history: items.value_type, - itemids: items.itemid, - sortfield: 'clock', - sortorder: 'ASC', - limit: this.limitmetrics, - time_from: start, - }, - auth: this.auth, - id: 1 + var params = { + output: 'extend', + history: items.value_type, + itemids: items.itemid, + sortfield: 'clock', + sortorder: 'ASC', + limit: this.limitmetrics, + time_from: start, }; // Relative queries (e.g. last hour) don't include an end time if (end) { - data.params.time_till = end; + params.time_till = end; } - return this.performZabbixAPIRequest(data); + return this.performZabbixAPIRequest('history.get', params); }; @@ -178,83 +178,62 @@ function (angular, _, kbn) { // Get the list of host groups ZabbixAPIDatasource.prototype.performHostGroupSuggestQuery = function() { - var data = { - jsonrpc: '2.0', - method: 'hostgroup.get', - params: { - output: ['name'], - real_hosts: true, //Return only host groups that contain hosts - sortfield: 'name' - }, - auth: this.auth, - id: 1 + var params = { + output: ['name'], + real_hosts: true, //Return only host groups that contain hosts + sortfield: 'name' }; - return this.performZabbixAPIRequest(data); + return this.performZabbixAPIRequest('hostgroup.get', params); }; // Get the list of hosts ZabbixAPIDatasource.prototype.performHostSuggestQuery = function(groupid) { - var data = { - jsonrpc: '2.0', - method: 'host.get', - params: { - output: ['name'], - sortfield: 'name' - }, - auth: this.auth, - id: 1 + var params = { + output: ['name'], + sortfield: 'name' }; + // Return only hosts in given group if (groupid) { - data.params.groupids = groupid; + params.groupids = groupid; } - - return this.performZabbixAPIRequest(data); + return this.performZabbixAPIRequest('host.get', params); }; // Get the list of applications ZabbixAPIDatasource.prototype.performAppSuggestQuery = function(hostid) { - var data = { - jsonrpc: '2.0', - method: 'application.get', - params: { - output: ['name'], - sortfield: 'name', - hostids: hostid - }, - auth: this.auth, - id: 1 + var params = { + output: ['name'], + sortfield: 'name', + hostids: hostid }; - return this.performZabbixAPIRequest(data); + return this.performZabbixAPIRequest('application.get', params); }; // Get the list of host items ZabbixAPIDatasource.prototype.performItemSuggestQuery = function(hostid, applicationid) { - var data = { - jsonrpc: '2.0', - method: 'item.get', - params: { - output: ['name', 'key_', 'value_type', 'delay'], - sortfield: 'name', - hostids: hostid, - webitems: true, //Include web items in the result - filter: { - value_type: [0,3] - } - }, - auth: this.auth, - id: 1 + var params = { + output: ['name', 'key_', 'value_type', 'delay'], + sortfield: 'name', + hostids: hostid, + + //Include web items in the result + webitems: true, + // Return only numeric items + filter: { + value_type: [0,3] + } }; // If application selected return only relative items if (applicationid) { - data.params.applicationids = applicationid; + params.applicationids = applicationid; } - return this.performZabbixAPIRequest(data); + return this.performZabbixAPIRequest('item.get', params); };