diff --git a/docs/sources/reference/functions.md b/docs/sources/reference/functions.md index 089edac..c620ca7 100644 --- a/docs/sources/reference/functions.md +++ b/docs/sources/reference/functions.md @@ -30,6 +30,13 @@ scale(100) scale(0.01) ``` +### delta +``` +delta() +``` +Convert absolute values to delta, for example, bits to bits/sec. + + Aggregate --------- @@ -71,6 +78,38 @@ max(interval) ``` **Deprecated**, use `aggregateBy(interval, max)` instead. + +Filter +--------- + +### top + +``` +top(N, value) +``` + +Returns top N series, sorted by _value_, which can be one of: _avg_, _min_, _max_, _median_. + +Examples: +``` +top(10, avg) +top(5, max) +``` + +### bottom + +``` +bottom(N, value) +``` + +Returns bottom N series, sorted by _value_, which can be one of: _avg_, _min_, _max_, _median_. + +Examples: +``` +bottom(5, avg) +``` + + ## Trends ### trendValue diff --git a/src/datasource-zabbix/DataProcessor.js b/src/datasource-zabbix/DataProcessor.js index 0dad30d..cb6c4c2 100644 --- a/src/datasource-zabbix/DataProcessor.js +++ b/src/datasource-zabbix/DataProcessor.js @@ -116,6 +116,22 @@ export default class DataProcessor { return sortByTime(new_timeseries); } + static limit(order, n, orderByFunc, timeseries) { + let orderByCallback = DataProcessor.aggregationFunctions[orderByFunc]; + let sortByIteratee = (ts) => { + let values = _.map(ts.datapoints, (point) => { + return point[0]; + }); + return orderByCallback(values); + }; + let sortedTimeseries = _.sortBy(timeseries, sortByIteratee); + if (order === 'bottom') { + return sortedTimeseries.slice(0, n); + } else { + return sortedTimeseries.slice(-n); + } + } + static AVERAGE(values) { var sum = 0; _.each(values, function(value) { @@ -151,6 +167,16 @@ export default class DataProcessor { }); } + static delta(datapoints) { + let newSeries = []; + let deltaValue; + for (var i = 1; i < datapoints.length; i++) { + deltaValue = datapoints[i][0] - datapoints[i - 1][0]; + newSeries.push([deltaValue, datapoints[i][1]]); + } + return newSeries; + } + static groupByWrapper(interval, groupFunc, datapoints) { var groupByCallback = DataProcessor.aggregationFunctions[groupFunc]; return DataProcessor.groupBy(interval, groupByCallback, datapoints); @@ -181,12 +207,15 @@ export default class DataProcessor { return { groupBy: this.groupByWrapper, scale: this.scale, + delta: this.delta, aggregateBy: this.aggregateByWrapper, 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, + top: _.partial(this.limit, 'top'), + bottom: _.partial(this.limit, 'bottom'), setAlias: this.setAlias, }; } diff --git a/src/datasource-zabbix/datasource.js b/src/datasource-zabbix/datasource.js index 2e914a4..c9db4bb 100644 --- a/src/datasource-zabbix/datasource.js +++ b/src/datasource-zabbix/datasource.js @@ -92,7 +92,11 @@ export class ZabbixAPIDatasource { _.forEach(target.functions, func => { func.params = _.map(func.params, param => { - return this.templateSrv.replace(param, options.scopedVars); + if (typeof param === 'number') { + return +this.templateSrv.replace(param.toString(), options.scopedVars); + } else { + return this.templateSrv.replace(param, options.scopedVars); + } }); }); @@ -181,6 +185,7 @@ export class ZabbixAPIDatasource { return getHistory.then(timeseries_data => { let transformFunctions = bindFunctionDefs(target.functions, 'Transform'); let aggregationFunctions = bindFunctionDefs(target.functions, 'Aggregate'); + let filterFunctions = bindFunctionDefs(target.functions, 'Filter'); let aliasFunctions = bindFunctionDefs(target.functions, 'Alias'); // Apply transformation functions @@ -189,6 +194,11 @@ export class ZabbixAPIDatasource { return timeseries; }); + // Apply filter functions + if (filterFunctions.length) { + timeseries_data = sequence(filterFunctions)(timeseries_data); + } + // Apply aggregations if (aggregationFunctions.length) { let dp = _.map(timeseries_data, 'datapoints'); diff --git a/src/datasource-zabbix/metricFunctions.js b/src/datasource-zabbix/metricFunctions.js index 5616bc2..5d467f4 100644 --- a/src/datasource-zabbix/metricFunctions.js +++ b/src/datasource-zabbix/metricFunctions.js @@ -5,6 +5,7 @@ var index = []; var categories = { Transform: [], Aggregate: [], + Filter: [], Trends: [], Alias: [] }; @@ -39,6 +40,13 @@ addFuncDef({ defaultParams: [100], }); +addFuncDef({ + name: 'delta', + category: 'Transform', + params: [], + defaultParams: [], +}); + addFuncDef({ name: 'sumSeries', category: 'Aggregate', @@ -92,6 +100,26 @@ addFuncDef({ defaultParams: ['1m', 'avg'], }); +addFuncDef({ + name: 'top', + category: 'Filter', + params: [ + { name: 'number', type: 'int' }, + { name: 'value', type: 'string', options: ['avg', 'min', 'max', 'median'] } + ], + defaultParams: [5, 'avg'], +}); + +addFuncDef({ + name: 'bottom', + category: 'Filter', + params: [ + { name: 'number', type: 'int' }, + { name: 'value', type: 'string', options: ['avg', 'min', 'max', 'median'] } + ], + defaultParams: [5, 'avg'], +}); + addFuncDef({ name: 'trendValue', category: 'Trends', diff --git a/src/datasource-zabbix/queryProcessor.service.js b/src/datasource-zabbix/queryProcessor.service.js index 6931468..5f8e8e9 100644 --- a/src/datasource-zabbix/queryProcessor.service.js +++ b/src/datasource-zabbix/queryProcessor.service.js @@ -230,13 +230,13 @@ angular.module('grafana.services').factory('QueryProcessor', function($q) { // Group history by itemid var grouped_history = _.groupBy(history, 'itemid'); - var hosts = _.groupBy(_.flatten(_.map(items, 'hosts')), 'hostid'); + var hosts = _.flatten(_.map(items, 'hosts')); return _.map(grouped_history, function(hist, itemid) { var item = _.find(items, {'itemid': itemid}); var alias = item.name; if (_.keys(hosts).length > 1 || addHostName) { - var host = hosts[item.hostid]; + var host = _.find(hosts, {'hostid': item.hostid}); alias = host.name + ": " + alias; } return { diff --git a/src/datasource-zabbix/zabbixAPI.service.js b/src/datasource-zabbix/zabbixAPI.service.js index 0d6c82f..b8f9edb 100644 --- a/src/datasource-zabbix/zabbixAPI.service.js +++ b/src/datasource-zabbix/zabbixAPI.service.js @@ -41,12 +41,12 @@ function ZabbixAPIService($q, alertSrv, zabbixAPICoreService) { request(method, params) { var self = this; - return this.zabbixAPICore.request(this.url, method, params, this.requestOptions, this.auth) - .then(function(result) { + return this.zabbixAPICore + .request(this.url, method, params, this.requestOptions, this.auth) + .then((result) => { return result; - }, - // Handle API errors - function(error) { + }, (error) => { + // Handle API errors if (isNotAuthorized(error.data)) { return self.loginOnce().then( function() { @@ -56,15 +56,18 @@ function ZabbixAPIService($q, alertSrv, zabbixAPICoreService) { function(error) { self.alertAPIError(error.data); }); + } else { + this.alertSrv.set("Connection Error", error.data, 'error', 5000); } }); } - alertAPIError(message) { + alertAPIError(message, timeout = 5000) { this.alertSrv.set( "Zabbix API Error", message, - 'error' + 'error', + timeout ); } diff --git a/src/datasource-zabbix/zabbixAPICore.service.js b/src/datasource-zabbix/zabbixAPICore.service.js index a1623e6..2a7473c 100644 --- a/src/datasource-zabbix/zabbixAPICore.service.js +++ b/src/datasource-zabbix/zabbixAPICore.service.js @@ -51,19 +51,23 @@ class ZabbixAPICoreService { requestOptions.headers.Authorization = options.basicAuth; } - this.backendSrv.datasourceRequest(requestOptions).then(function (response) { - // General connection issues - if (!response.data) { - deferred.reject(response); - } + this.backendSrv.datasourceRequest(requestOptions) + .then((response) => { + // General connection issues + if (!response.data) { + deferred.reject(response); + } - // Handle Zabbix API errors - else if (response.data.error) { - deferred.reject(response.data.error); - } + // Handle Zabbix API errors + else if (response.data.error) { + deferred.reject(response.data.error); + } + + deferred.resolve(response.data.result); + }, (error) => { + deferred.reject(error.err); + }); - deferred.resolve(response.data.result); - }); return deferred.promise; }