Merge branch 'refactor-es6'. Datasource refactoring.

This commit is contained in:
Alexander Zobnin
2016-11-16 22:33:57 +03:00
16 changed files with 1322 additions and 1480 deletions

View File

@@ -33,7 +33,7 @@ module.exports = function(grunt) {
watch: {
rebuild_all: {
files: ['src/**/*', 'plugin.json'],
tasks: ['default'],
tasks: ['watchTask'],
options: {spawn: false}
},
},
@@ -135,4 +135,14 @@ module.exports = function(grunt) {
'babel',
'mochaTest'
]);
grunt.registerTask('watchTask', [
'clean',
'copy:src_to_dist',
'copy:pluginDef',
'jshint',
'jscs',
'sass',
'babel:dist'
]);
};

View File

@@ -1,281 +0,0 @@
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: [[<value>, <unixtime>], ...]
*/
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: { '<unixtime>': [[<value>, <unixtime>], ...] }
// return [{ '<unixtime>': <value> }, { '<unixtime>': <value> }, ...]
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 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) {
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 scale(factor, datapoints) {
return _.map(datapoints, point => {
return [
point[0] * factor,
point[1]
];
});
}
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);
}
static aggregateByWrapper(interval, aggregateFunc, datapoints) {
// Flatten all points in frame and then just use groupBy()
var flattenedPoints = _.flatten(datapoints, true);
var groupByCallback = DataProcessor.aggregationFunctions[aggregateFunc];
return DataProcessor.groupBy(interval, groupByCallback, flattenedPoints);
}
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,
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,
};
}
}
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] !== null) {
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] !== null) {
return series[i];
}
}
return nearestLeft;
}

View File

@@ -0,0 +1,291 @@
import _ from 'lodash';
import * as utils from './utils';
/**
* Downsample datapoints series
*/
function 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: [[<value>, <unixtime>], ...]
*/
function 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: { '<unixtime>': [[<value>, <unixtime>], ...] }
// return [{ '<unixtime>': <value> }, { '<unixtime>': <value> }, ...]
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)];
}));
}
function 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);
}
function limit(order, n, orderByFunc, timeseries) {
let orderByCallback = 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);
}
}
function AVERAGE(values) {
var sum = 0;
_.each(values, function(value) {
sum += value;
});
return sum / values.length;
}
function MIN(values) {
return _.min(values);
}
function MAX(values) {
return _.max(values);
}
function MEDIAN(values) {
var sorted = _.sortBy(values);
return sorted[Math.floor(sorted.length / 2)];
}
function setAlias(alias, timeseries) {
timeseries.target = alias;
return timeseries;
}
function scale(factor, datapoints) {
return _.map(datapoints, point => {
return [
point[0] * factor,
point[1]
];
});
}
function 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;
}
function groupByWrapper(interval, groupFunc, datapoints) {
var groupByCallback = aggregationFunctions[groupFunc];
return groupBy(interval, groupByCallback, datapoints);
}
function aggregateByWrapper(interval, aggregateFunc, datapoints) {
// Flatten all points in frame and then just use groupBy()
var flattenedPoints = _.flatten(datapoints, true);
var groupByCallback = aggregationFunctions[aggregateFunc];
return groupBy(interval, groupByCallback, flattenedPoints);
}
function aggregateWrapper(groupByCallback, interval, datapoints) {
var flattenedPoints = _.flatten(datapoints, true);
return groupBy(interval, groupByCallback, flattenedPoints);
}
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] !== null) {
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] !== null) {
return series[i];
}
}
return nearestLeft;
}
let metricFunctions = {
groupBy: groupByWrapper,
scale: scale,
delta: delta,
aggregateBy: aggregateByWrapper,
average: _.partial(aggregateWrapper, AVERAGE),
min: _.partial(aggregateWrapper, MIN),
max: _.partial(aggregateWrapper, MAX),
median: _.partial(aggregateWrapper, MEDIAN),
sumSeries: sumSeries,
top: _.partial(limit, 'top'),
bottom: _.partial(limit, 'bottom'),
setAlias: setAlias
};
let aggregationFunctions = {
avg: AVERAGE,
min: MIN,
max: MAX,
median: MEDIAN
};
export default {
downsampleSeries: downsampleSeries,
groupBy: groupBy,
AVERAGE: AVERAGE,
MIN: MIN,
MAX: MAX,
MEDIAN: MEDIAN,
get aggregationFunctions() {
return aggregationFunctions;
},
get metricFunctions() {
return metricFunctions;
}
};

View File

@@ -4,15 +4,17 @@ 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';
import './zabbixCache.service.js';
import './queryProcessor.service.js';
import dataProcessor from './dataProcessor';
import responseHandler from './responseHandler';
import './zabbix.js';
import {ZabbixAPIError} from './zabbixAPICore.service.js';
class ZabbixAPIDatasource {
/** @ngInject */
constructor(instanceSettings, $q, templateSrv, alertSrv, zabbixAPIService, ZabbixCachingProxy, QueryProcessor) {
constructor(instanceSettings, templateSrv, alertSrv, Zabbix) {
this.templateSrv = templateSrv;
this.alertSrv = alertSrv;
// General data source settings
this.name = instanceSettings.name;
@@ -32,20 +34,7 @@ class ZabbixAPIDatasource {
var ttl = instanceSettings.jsonData.cacheTTL || '1h';
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
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.zabbix = new Zabbix(this.url, this.username, this.password, this.basicAuth, this.withCredentials, this.cacheTTL);
// Use custom format for template variables
this.replaceTemplateVars = _.partial(replaceTemplateVars, this.templateSrv);
@@ -69,12 +58,12 @@ class ZabbixAPIDatasource {
// Create request for each target
var promises = _.map(options.targets, target => {
// Prevent changes of original object
target = _.cloneDeep(target);
this.replaceTargetVariables(target, options);
// Metrics or Text query mode
if (target.mode !== 1) {
// Migrate old targets
target = migrations.migrate(target);
@@ -83,30 +72,9 @@ class ZabbixAPIDatasource {
return [];
}
// Replace templated variables
target.group.filter = this.replaceTemplateVars(target.group.filter, options.scopedVars);
target.host.filter = this.replaceTemplateVars(target.host.filter, options.scopedVars);
target.application.filter = this.replaceTemplateVars(target.application.filter, options.scopedVars);
target.item.filter = this.replaceTemplateVars(target.item.filter, options.scopedVars);
target.textFilter = this.replaceTemplateVars(target.textFilter, options.scopedVars);
_.forEach(target.functions, func => {
func.params = _.map(func.params, param => {
if (typeof param === 'number') {
return +this.templateSrv.replace(param.toString(), options.scopedVars);
} else {
return this.templateSrv.replace(param, options.scopedVars);
}
});
});
// Query numeric data
if (!target.mode || target.mode === 0) {
return this.queryNumericData(target, timeFrom, timeTo, useTrends);
}
// Query text data
else if (target.mode === 2) {
} else if (target.mode === 2) {
return this.queryTextData(target, timeFrom, timeTo);
}
}
@@ -118,71 +86,62 @@ class ZabbixAPIDatasource {
return [];
}
return this.zabbixAPI
.getSLA(target.itservice.serviceid, timeFrom, timeTo)
return this.zabbix.getSLA(target.itservice.serviceid, timeFrom, timeTo)
.then(slaObject => {
return this.queryProcessor
.handleSLAResponse(target.itservice, target.slaProperty, slaObject);
return responseHandler.handleSLAResponse(target.itservice, target.slaProperty, slaObject);
});
}
});
// Data for panel (all targets)
return this.q.all(_.flatten(promises))
return Promise.all(_.flatten(promises))
.then(_.flatten)
.then(timeseries_data => {
// Series downsampling
var data = _.map(timeseries_data, timeseries => {
if (timeseries.datapoints.length > options.maxDataPoints) {
timeseries.datapoints = DataProcessor
.groupBy(options.interval, DataProcessor.AVERAGE, timeseries.datapoints);
}
return timeseries;
});
return downsampleSeries(timeseries_data, options);
})
.then(data => {
return { data: data };
});
}
queryNumericData(target, timeFrom, timeTo, useTrends) {
// Build query in asynchronous manner
return this.queryProcessor.build(target.group.filter,
target.host.filter,
target.application.filter,
target.item.filter,
'num')
let options = {
itemtype: 'num'
};
return this.zabbix.getItemsFromTarget(target, options)
.then(items => {
// Add hostname for items from multiple hosts
var addHostName = utils.isRegex(target.host.filter);
var getHistory;
let getHistoryPromise;
// Use trends
if (useTrends) {
let valueType = this.getTrendValueType(target);
getHistoryPromise = this.zabbix.getTrend(items, timeFrom, timeTo)
.then(history => {
return responseHandler.handleTrends(history, items, valueType);
});
} else {
// Use history
getHistoryPromise = this.zabbix.getHistory(items, timeFrom, timeTo)
.then(history => {
return responseHandler.handleHistory(history, items);
});
}
return getHistoryPromise.then(timeseries_data => {
return this.applyDataProcessingFunctions(timeseries_data, target);
});
});
}
getTrendValueType(target) {
// Find trendValue() function and get specified trend value
var trendFunctions = _.map(metricFunctions.getCategories()['Trends'], 'name');
var trendValueFunc = _.find(target.functions, func => {
return _.includes(trendFunctions, func.def.name);
});
var valueType = trendValueFunc ? trendValueFunc.params[0] : "avg";
getHistory = this.zabbixAPI
.getTrend(items, timeFrom, timeTo)
.then(history => {
return this.queryProcessor.handleTrends(history, items, addHostName, valueType);
});
return trendValueFunc ? trendValueFunc.params[0] : "avg";
}
// Use history
else {
getHistory = this.zabbixCache
.getHistory(items, timeFrom, timeTo)
.then(history => {
return this.queryProcessor.handleHistory(history, items, addHostName);
});
}
return getHistory.then(timeseries_data => {
applyDataProcessingFunctions(timeseries_data, target) {
let transformFunctions = bindFunctionDefs(target.functions, 'Transform');
let aggregationFunctions = bindFunctionDefs(target.functions, 'Aggregate');
let filterFunctions = bindFunctionDefs(target.functions, 'Filter');
@@ -209,33 +168,28 @@ class ZabbixAPIDatasource {
return _.includes(aggFuncNames, func.def.name);
});
timeseries_data = [
{
timeseries_data = [{
target: lastAgg.text,
datapoints: dp
}
];
}];
}
// Apply alias functions
_.each(timeseries_data, sequence(aliasFunctions));
return timeseries_data;
});
});
}
queryTextData(target, timeFrom, timeTo) {
return this.queryProcessor.build(target.group.filter,
target.host.filter,
target.application.filter,
target.item.filter,
'text')
let options = {
itemtype: 'text'
};
return this.zabbix.getItemsFromTarget(target, options)
.then(items => {
if (items.length) {
return this.zabbixAPI.getHistory(items, timeFrom, timeTo)
return this.zabbix.getHistory(items, timeFrom, timeTo)
.then(history => {
return this.queryProcessor.convertHistory(history, items, false, (point) => {
return responseHandler.convertHistory(history, items, false, (point) => {
let value = point.value;
// Regex-based extractor
@@ -247,7 +201,7 @@ class ZabbixAPIDatasource {
});
});
} else {
return this.q.when([]);
return Promise.resolve([]);
}
});
}
@@ -257,37 +211,33 @@ class ZabbixAPIDatasource {
* @return {object} Connection status and Zabbix API version
*/
testDatasource() {
return this.zabbixAPI.getVersion()
let zabbixVersion;
return this.zabbix.getVersion()
.then(version => {
return this.zabbixAPI.login()
.then(auth => {
if (auth) {
zabbixVersion = version;
return this.zabbix.login();
})
.then(() => {
return {
status: "success",
title: "Success",
message: "Zabbix API version: " + version
message: "Zabbix API version: " + zabbixVersion
};
} else {
return {
status: "error",
title: "Invalid user name or password",
message: "Zabbix API version: " + version
};
}
}, error => {
})
.catch(error => {
if (error instanceof ZabbixAPIError) {
return {
status: "error",
title: error.message,
message: error.data
};
});
}, error => {
console.log(error);
} else {
return {
status: "error",
title: "Connection failed",
message: "Could not connect to given url"
};
}
});
}
@@ -324,22 +274,22 @@ class ZabbixAPIDatasource {
if (template.app === '/.*/') {
template.app = '';
}
result = this.queryProcessor.getItems(template.group, template.host, template.app);
result = this.zabbix.getItems(template.group, template.host, template.app, template.item);
} else if (parts.length === 3) {
// Get applications
result = this.queryProcessor.getApps(template.group, template.host);
result = this.zabbix.getApps(template.group, template.host, template.app);
} else if (parts.length === 2) {
// Get hosts
result = this.queryProcessor.getHosts(template.group);
result = this.zabbix.getHosts(template.group, template.host);
} else if (parts.length === 1) {
// Get groups
result = this.zabbixCache.getGroups(template.group);
result = this.zabbix.getGroups(template.group);
} else {
result = this.q.when([]);
result = Promise.resolve([]);
}
return result.then(metrics => {
return _.map(metrics, formatMetric);
return metrics.map(formatMetric);
});
}
@@ -356,15 +306,13 @@ class ZabbixAPIDatasource {
// Show all triggers
var showTriggers = [0, 1];
var buildQuery = this.queryProcessor
.buildTriggerQuery(this.replaceTemplateVars(annotation.group, {}),
var getTriggers = this.zabbix
.getTriggers(this.replaceTemplateVars(annotation.group, {}),
this.replaceTemplateVars(annotation.host, {}),
this.replaceTemplateVars(annotation.application, {}));
this.replaceTemplateVars(annotation.application, {}),
showTriggers);
return buildQuery.then(query => {
return this.zabbixAPI
.getTriggers(query.groupids, query.hostids, query.applicationids, showTriggers)
.then(triggers => {
return getTriggers.then(triggers => {
// Filter triggers by description
if (utils.isRegex(annotation.trigger)) {
@@ -383,7 +331,7 @@ class ZabbixAPIDatasource {
});
var objectids = _.map(triggers, 'triggerid');
return this.zabbixAPI
return this.zabbix
.getEvents(objectids, timeFrom, timeTo, showOkEvents)
.then(events => {
var indexedTriggers = _.keyBy(triggers, 'triggerid');
@@ -415,6 +363,24 @@ class ZabbixAPIDatasource {
});
});
});
}
// Replace template variables
replaceTargetVariables(target, options) {
let parts = ['group', 'host', 'application', 'item'];
parts.forEach(p => {
target[p].filter = this.replaceTemplateVars(target[p].filter, options.scopedVars);
});
target.textFilter = this.replaceTemplateVars(target.textFilter, options.scopedVars);
_.forEach(target.functions, func => {
func.params = func.params.map(param => {
if (typeof param === 'number') {
return +this.templateSrv.replace(param.toString(), options.scopedVars);
} else {
return this.templateSrv.replace(param, options.scopedVars);
}
});
});
}
@@ -428,7 +394,17 @@ function bindFunctionDefs(functionDefs, category) {
return _.map(aggFuncDefs, function(func) {
var funcInstance = metricFunctions.createFuncInstance(func.def, func.params);
return funcInstance.bindFunction(DataProcessor.metricFunctions);
return funcInstance.bindFunction(dataProcessor.metricFunctions);
});
}
function downsampleSeries(timeseries_data, options) {
return _.map(timeseries_data, timeseries => {
if (timeseries.datapoints.length > options.maxDataPoints) {
timeseries.datapoints = dataProcessor
.groupBy(options.interval, dataProcessor.AVERAGE, timeseries.datapoints);
}
return timeseries;
});
}

View File

@@ -13,14 +13,9 @@ import './css/query-editor.css!';
export class ZabbixQueryController extends QueryCtrl {
// ZabbixQueryCtrl constructor
constructor($scope, $injector, $rootScope, $sce, $q, templateSrv) {
// Call superclass constructor
constructor($scope, $injector, $rootScope, $sce, templateSrv) {
super($scope, $injector);
this.zabbix = this.datasource.zabbixAPI;
this.cache = this.datasource.zabbixCache;
this.$q = $q;
this.zabbix = this.datasource.zabbix;
// Use custom format for template variables
this.replaceTemplateVars = this.datasource.replaceTemplateVars;
@@ -106,85 +101,57 @@ export class ZabbixQueryController extends QueryCtrl {
}
initFilters() {
var self = this;
var itemtype = self.editorModes[self.target.mode].value;
return this.$q.when(this.suggestGroups())
.then(() => {return self.suggestHosts();})
.then(() => {return self.suggestApps();})
.then(() => {return self.suggestItems(itemtype);});
let itemtype = this.editorModes[this.target.mode].value;
return Promise.all([
this.suggestGroups(),
this.suggestHosts(),
this.suggestApps(),
this.suggestItems(itemtype)
]);
}
suggestGroups() {
var self = this;
return this.cache.getGroups().then(groups => {
self.metric.groupList = groups;
return this.zabbix.getAllGroups()
.then(groups => {
this.metric.groupList = groups;
return groups;
});
}
suggestHosts() {
var self = this;
var groupFilter = this.replaceTemplateVars(this.target.group.filter);
return this.datasource.queryProcessor
.filterGroups(self.metric.groupList, groupFilter)
.then(groups => {
var groupids = _.map(groups, 'groupid');
return self.zabbix
.getHosts(groupids)
let groupFilter = this.replaceTemplateVars(this.target.group.filter);
return this.zabbix.getAllHosts(groupFilter)
.then(hosts => {
self.metric.hostList = hosts;
this.metric.hostList = hosts;
return hosts;
});
});
}
suggestApps() {
var self = this;
var hostFilter = this.replaceTemplateVars(this.target.host.filter);
return this.datasource.queryProcessor
.filterHosts(self.metric.hostList, hostFilter)
.then(hosts => {
var hostids = _.map(hosts, 'hostid');
return self.zabbix
.getApps(hostids)
let groupFilter = this.replaceTemplateVars(this.target.group.filter);
let hostFilter = this.replaceTemplateVars(this.target.host.filter);
return this.zabbix.getAllApps(groupFilter, hostFilter)
.then(apps => {
return self.metric.appList = apps;
});
this.metric.appList = apps;
return apps;
});
}
suggestItems(itemtype = 'num') {
var self = this;
var appFilter = this.replaceTemplateVars(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, itemtype)
let groupFilter = this.replaceTemplateVars(this.target.group.filter);
let hostFilter = this.replaceTemplateVars(this.target.host.filter);
let appFilter = this.replaceTemplateVars(this.target.application.filter);
let options = {
itemtype: itemtype,
showDisabledItems: this.target.options.showDisabledItems
};
return this.zabbix
.getAllItems(groupFilter, hostFilter, appFilter, options)
.then(items => {
if (!self.target.options.showDisabledItems) {
items = _.filter(items, {'status': '0'});
}
self.metric.itemList = items;
this.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, undefined, itemtype)
.then(items => {
if (!self.target.options.showDisabledItems) {
items = _.filter(items, {'status': '0'});
}
self.metric.itemList = items;
return items;
});
}
}
isRegex(str) {
@@ -331,7 +298,6 @@ export class ZabbixQueryController extends QueryCtrl {
this.panelCtrl.refresh();
}
}
}
// Set templateUrl as static property

View File

@@ -1,366 +0,0 @@
import angular from 'angular';
import _ from 'lodash';
import * as utils from './utils';
/** @ngInject */
angular.module('grafana.services').factory('QueryProcessor', function($q) {
class QueryProcessor {
constructor(zabbixCacheInstance) {
this.cache = zabbixCacheInstance;
this.$q = $q;
}
/**
* Build query in asynchronous manner
*/
build(groupFilter, hostFilter, appFilter, itemFilter, itemtype) {
var self = this;
if (this.cache._initialized) {
return this.$q.when(self.buildFromCache(groupFilter, hostFilter, appFilter, itemFilter, itemtype));
} else {
return this.cache.refresh().then(function() {
return self.buildFromCache(groupFilter, hostFilter, appFilter, itemFilter, itemtype);
});
}
}
/**
* 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);
});
}
}
filterGroups(groups, groupFilter) {
return this.$q.when(
findByFilter(groups, groupFilter)
);
}
/**
* Get list of host belonging to given groups.
* @return list of hosts
*/
filterHosts(hosts, hostFilter) {
return this.$q.when(
findByFilter(hosts, hostFilter)
);
}
filterApps(apps, appFilter) {
return this.$q.when(
findByFilter(apps, appFilter)
);
}
/**
* Build query - convert target filters to array of Zabbix items
*/
buildFromCache(groupFilter, hostFilter, appFilter, itemFilter, itemtype, showDisabledItems) {
return this.getItems(groupFilter, hostFilter, appFilter, itemtype, showDisabledItems)
.then(items => {
return getByFilter(items, itemFilter);
});
}
getGroups() {
return this.cache.getGroups();
}
/**
* Get list of host belonging to given groups.
* @return list of hosts
*/
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);
});
}
/**
* 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)
.then(hosts => {
return findByFilter(hosts, hostFilter);
})
.then(hosts => {
var hostids = _.map(hosts, 'hostid');
return self.cache.getApps(hostids);
});
}
getItems(groupFilter, hostFilter, appFilter, itemtype, 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, itemtype)
.then(items => {
if (showDisabledItems) {
items = _.filter(items, {'status': '0'});
}
return items;
});
} else {
var appids = _.map(apps, 'applicationid');
return self.cache
.getItems(undefined, appids, itemtype)
.then(items => {
if (showDisabledItems) {
items = _.filter(items, {'status': '0'});
}
return items;
});
}
});
}
/**
* 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.getHosts(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.getApps(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 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, 'applicationid'));
}
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
*
* @return {Array} Array of timeseries in Grafana format
* {
* target: "Metric name",
* datapoints: [[<value>, <unixtime>], ...]
* }
*/
convertHistory(history, items, addHostName, convertPointCallback) {
/**
* Response should be in the format:
* data: [
* {
* target: "Metric name",
* datapoints: [[<value>, <unixtime>], ...]
* }, ...
* ]
*/
// Group history by itemid
var grouped_history = _.groupBy(history, 'itemid');
var hosts = _.uniq(_.flatten(_.map(items, 'hosts')),'hostid'); //uniq is needed to deduplicate
return _.map(grouped_history, function(hist, itemid) {
var item = _.find(items, {'itemid': itemid});
var alias = item.name;
if (_.keys(hosts).length > 1 && addHostName) { //only when actual multi hosts selected
var host = _.find(hosts, {'hostid': item.hostid});
alias = host.name + ": " + alias;
}
return {
target: alias,
datapoints: _.map(hist, convertPointCallback)
};
});
}
handleHistory(history, items, addHostName) {
return this.convertHistory(history, items, addHostName, convertHistoryPoint);
}
handleTrends(history, items, addHostName, valueType) {
var convertPointCallback = _.partial(convertTrendPoint, valueType);
return this.convertHistory(history, items, addHostName, convertPointCallback);
}
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]
]
};
}
}
}
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;
}
}
/**
* 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) {
return filterPattern.test(zbx_obj.name);
});
}
function findByFilter(list, filter) {
if (utils.isRegex(filter)) {
return findByRegex(list, filter);
} else {
return findByName(list, filter);
}
}
function getByFilter(list, filter) {
if (utils.isRegex(filter)) {
return findByRegex(list, filter);
} else {
return filterByName(list, filter);
}
}
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
];
}

View File

@@ -0,0 +1,106 @@
import _ from 'lodash';
/**
* Convert Zabbix API history.get response to Grafana format
*
* @return {Array} Array of timeseries in Grafana format
* {
* target: "Metric name",
* datapoints: [[<value>, <unixtime>], ...]
* }
*/
function convertHistory(history, items, addHostName, convertPointCallback) {
/**
* Response should be in the format:
* data: [
* {
* target: "Metric name",
* datapoints: [[<value>, <unixtime>], ...]
* }, ...
* ]
*/
// Group history by itemid
var grouped_history = _.groupBy(history, 'itemid');
var hosts = _.uniqBy(_.flatten(_.map(items, 'hosts')), 'hostid'); //uniqBy is needed to deduplicate
return _.map(grouped_history, function(hist, itemid) {
var item = _.find(items, {'itemid': itemid});
var alias = item.name;
if (_.keys(hosts).length > 1 && addHostName) { //only when actual multi hosts selected
var host = _.find(hosts, {'hostid': item.hostid});
alias = host.name + ": " + alias;
}
return {
target: alias,
datapoints: _.map(hist, convertPointCallback)
};
});
}
function handleHistory(history, items, addHostName = true) {
return convertHistory(history, items, addHostName, convertHistoryPoint);
}
function handleTrends(history, items, valueType, addHostName = true) {
var convertPointCallback = _.partial(convertTrendPoint, valueType);
return convertHistory(history, items, addHostName, convertPointCallback);
}
function 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 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
];
}
export default {
handleHistory: handleHistory,
convertHistory: convertHistory,
handleTrends: handleTrends,
handleSLAResponse: handleSLAResponse
};

View File

@@ -17,15 +17,11 @@ describe('ZabbixDatasource', () => {
trendsFrom: '7d'
}
};
ctx.$q = Q;
ctx.templateSrv = {};
ctx.alertSrv = {};
ctx.zabbixAPIService = () => {};
ctx.ZabbixCachingProxy = () => {};
ctx.QueryProcessor = () => {};
ctx.zabbix = () => {};
ctx.ds = new Datasource(ctx.instanceSettings, ctx.$q, ctx.templateSrv, ctx.alertSrv,
ctx.zabbixAPIService, ctx.ZabbixCachingProxy, ctx.QueryProcessor);
ctx.ds = new Datasource(ctx.instanceSettings, ctx.templateSrv, ctx.alertSrv, ctx.zabbix);
});
describe('When querying data', () => {
@@ -144,10 +140,7 @@ describe('ZabbixDatasource', () => {
describe('When invoking metricFindQuery()', () => {
beforeEach(() => {
ctx.ds.replaceTemplateVars = (str) => str;
ctx.ds.zabbixCache = {
getGroups: () => Q.when([])
};
ctx.ds.queryProcessor = {
ctx.ds.zabbix = {
getGroups: () => Q.when([]),
getHosts: () => Q.when([]),
getApps: () => Q.when([]),
@@ -163,7 +156,7 @@ describe('ZabbixDatasource', () => {
{query: 'Back*', expect: 'Back*'}
];
let getGroups = sinon.spy(ctx.ds.zabbixCache, 'getGroups');
let getGroups = sinon.spy(ctx.ds.zabbix, 'getGroups');
for (const test of tests) {
ctx.ds.metricFindQuery(test.query);
expect(getGroups).to.have.been.calledWith(test.expect);
@@ -180,7 +173,7 @@ describe('ZabbixDatasource', () => {
{query: 'Back*.', expect: 'Back*'}
];
let getHosts = sinon.spy(ctx.ds.queryProcessor, 'getHosts');
let getHosts = sinon.spy(ctx.ds.zabbix, 'getHosts');
for (const test of tests) {
ctx.ds.metricFindQuery(test.query);
expect(getHosts).to.have.been.calledWith(test.expect);
@@ -197,7 +190,7 @@ describe('ZabbixDatasource', () => {
{query: 'Back*.*.', expect: ['Back*', '/.*/']}
];
let getApps = sinon.spy(ctx.ds.queryProcessor, 'getApps');
let getApps = sinon.spy(ctx.ds.zabbix, 'getApps');
for (const test of tests) {
ctx.ds.metricFindQuery(test.query);
expect(getApps).to.have.been.calledWith(test.expect[0], test.expect[1]);
@@ -214,7 +207,7 @@ describe('ZabbixDatasource', () => {
{query: 'Back*.*.cpu.*', expect: ['Back*', '/.*/', 'cpu']}
];
let getItems = sinon.spy(ctx.ds.queryProcessor, 'getItems');
let getItems = sinon.spy(ctx.ds.zabbix, 'getItems');
for (const test of tests) {
ctx.ds.metricFindQuery(test.query);
expect(getItems)
@@ -227,7 +220,7 @@ describe('ZabbixDatasource', () => {
it('should invoke method with proper arguments', (done) => {
let query = '*.*';
let getHosts = sinon.spy(ctx.ds.queryProcessor, 'getHosts');
let getHosts = sinon.spy(ctx.ds.zabbix, 'getHosts');
ctx.ds.metricFindQuery(query);
expect(getHosts).to.have.been.calledWith('/.*/');
done();

View File

@@ -93,6 +93,25 @@ export function convertToZabbixAPIUrl(url) {
}
}
/**
* Wrap function to prevent multiple calls
* when waiting for result.
*/
export function callOnce(func, promiseKeeper) {
return function() {
if (!promiseKeeper) {
promiseKeeper = Promise.resolve(
func.apply(this, arguments)
.then(result => {
promiseKeeper = null;
return result;
})
);
}
return promiseKeeper;
};
}
// Fix for backward compatibility with lodash 2.4
if (!_.includes) {
_.includes = _.contains;

View File

@@ -0,0 +1,216 @@
import angular from 'angular';
import _ from 'lodash';
import * as utils from './utils';
import './zabbixAPI.service.js';
import './zabbixCachingProxy.service.js';
// Use factory() instead service() for multiple data sources support.
// Each Zabbix data source instance should initialize its own API instance.
/** @ngInject */
function ZabbixFactory(zabbixAPIService, ZabbixCachingProxy) {
class Zabbix {
constructor(url, username, password, basicAuth, withCredentials, cacheTTL) {
// Initialize Zabbix API
var ZabbixAPI = zabbixAPIService;
this.zabbixAPI = new ZabbixAPI(url, username, password, basicAuth, withCredentials);
// Initialize caching proxy for requests
let cacheOptions = {
enabled: true,
ttl: cacheTTL
};
this.cachingProxy = new ZabbixCachingProxy(this.zabbixAPI, cacheOptions);
// Proxy methods
this.getHistory = this.cachingProxy.getHistory.bind(this.cachingProxy);
this.getTrend = this.zabbixAPI.getTrend.bind(this.zabbixAPI);
this.getEvents = this.zabbixAPI.getEvents.bind(this.zabbixAPI);
this.getAcknowledges = this.zabbixAPI.getAcknowledges.bind(this.zabbixAPI);
this.getSLA = this.zabbixAPI.getSLA.bind(this.zabbixAPI);
this.getVersion = this.zabbixAPI.getVersion.bind(this.zabbixAPI);
this.login = this.zabbixAPI.login.bind(this.zabbixAPI);
}
getItemsFromTarget(target, options) {
let parts = ['group', 'host', 'application', 'item'];
let filters = _.map(parts, p => target[p].filter);
return this.getItems(...filters, options);
}
getAllGroups() {
return this.cachingProxy.getGroups();
}
getGroups(groupFilter) {
return this.getAllGroups()
.then(groups => findByFilter(groups, groupFilter));
}
/**
* Get list of host belonging to given groups.
*/
getAllHosts(groupFilter) {
return this.getGroups(groupFilter)
.then(groups => {
let groupids = _.map(groups, 'groupid');
return this.cachingProxy.getHosts(groupids);
});
}
getHosts(groupFilter, hostFilter) {
return this.getAllHosts(groupFilter)
.then(hosts => findByFilter(hosts, hostFilter));
}
/**
* Get list of applications belonging to given groups and hosts.
*/
getAllApps(groupFilter, hostFilter) {
return this.getHosts(groupFilter, hostFilter)
.then(hosts => {
let hostids = _.map(hosts, 'hostid');
return this.cachingProxy.getApps(hostids);
});
}
getApps(groupFilter, hostFilter, appFilter) {
return this.getHosts(groupFilter, hostFilter)
.then(hosts => {
let hostids = _.map(hosts, 'hostid');
if (appFilter) {
return this.cachingProxy.getApps(hostids)
.then(apps => filterByQuery(apps, appFilter));
} else {
return {
appFilterEmpty: true,
hostids: hostids
};
}
});
}
getAllItems(groupFilter, hostFilter, appFilter, options = {}) {
return this.getApps(groupFilter, hostFilter, appFilter)
.then(apps => {
if (apps.appFilterEmpty) {
return this.cachingProxy.getItems(apps.hostids, undefined, options.itemtype);
} else {
let appids = _.map(apps, 'applicationid');
return this.cachingProxy.getItems(undefined, appids, options.itemtype);
}
})
.then(items => {
if (!options.showDisabledItems) {
items = _.filter(items, {'status': '0'});
}
return items;
});
}
getItems(groupFilter, hostFilter, appFilter, itemFilter, options = {}) {
return this.getAllItems(groupFilter, hostFilter, appFilter, options)
.then(items => filterByQuery(items, itemFilter));
}
/**
* Build query - convert target filters to array of Zabbix items
*/
getTriggers(groupFilter, hostFilter, appFilter, showTriggers) {
let promises = [
this.getGroups(groupFilter),
this.getHosts(groupFilter, hostFilter),
this.getApps(groupFilter, hostFilter, appFilter)
];
return Promise.all(promises)
.then(results => {
let filteredGroups = results[0];
let filteredHosts = results[1];
let filteredApps = results[2];
let query = {};
if (appFilter) {
query.applicationids = _.flatten(_.map(filteredApps, 'applicationid'));
}
if (hostFilter) {
query.hostids = _.map(filteredHosts, 'hostid');
}
if (groupFilter) {
query.groupids = _.map(filteredGroups, 'groupid');
}
return query;
}).then(query => {
return this.zabbixAPI
.getTriggers(query.groupids, query.hostids, query.applicationids, showTriggers);
});
}
}
return Zabbix;
}
angular
.module('grafana.services')
.factory('Zabbix', ZabbixFactory);
///////////////////////////////////////////////////////////////////////////////
/**
* 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;
}
}
/**
* 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 filterByRegex(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 filterByRegex(list, filter);
} else {
return findByName(list, filter);
}
}
function filterByQuery(list, filter) {
if (utils.isRegex(filter)) {
return filterByRegex(list, filter);
} else {
return filterByName(list, filter);
}
}

View File

@@ -4,7 +4,7 @@ import * as utils from './utils';
import './zabbixAPICore.service';
/** @ngInject */
function ZabbixAPIService($q, alertSrv, zabbixAPICoreService) {
function ZabbixAPIServiceFactory(alertSrv, zabbixAPICoreService) {
/**
* Zabbix API Wrapper.
@@ -25,8 +25,9 @@ function ZabbixAPIService($q, alertSrv, zabbixAPICoreService) {
};
this.loginPromise = null;
this.loginErrorCount = 0;
this.maxLoginAttempts = 3;
this.$q = $q;
this.alertSrv = alertSrv;
this.zabbixAPICore = zabbixAPICoreService;
@@ -39,25 +40,22 @@ function ZabbixAPIService($q, alertSrv, zabbixAPICoreService) {
//////////////////////////
request(method, params) {
var self = this;
return this.zabbixAPICore
.request(this.url, method, params, this.requestOptions, this.auth)
.then((result) => {
return result;
}, (error) => {
// Handle API errors
return this.zabbixAPICore.request(this.url, method, params, this.requestOptions, this.auth)
.catch(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);
});
// Handle auth errors
this.loginErrorCount++;
if (this.loginErrorCount > this.maxLoginAttempts) {
this.loginErrorCount = 0;
return null;
} else {
this.alertSrv.set("Connection Error", error.data, 'error', 5000);
return this.loginOnce()
.then(() => this.request(method, params));
}
} else {
// Handle API errors
let message = error.data ? error.data : error.statusText;
this.alertAPIError(message);
}
});
}
@@ -78,25 +76,16 @@ function ZabbixAPIService($q, alertSrv, zabbixAPICoreService) {
* @return login promise
*/
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);
}
if (!this.loginPromise) {
this.loginPromise = Promise.resolve(
this.login().then(auth => {
this.auth = auth;
this.loginPromise = null;
return auth;
})
);
} else {
return self.loginPromise;
}
return deferred.promise;
return this.loginPromise;
}
/**
@@ -197,13 +186,16 @@ function ZabbixAPIService($q, alertSrv, zabbixAPICoreService) {
}
return this.request('item.get', params)
.then(items => {
return _.forEach(items, item => {
.then(expandItems);
function expandItems(items) {
items.forEach(item => {
item.item = item.name;
item.name = utils.expandItemName(item.item, item.key_);
return item;
});
});
return items;
}
}
getLastValue(itemid) {
@@ -211,48 +203,42 @@ function ZabbixAPIService($q, alertSrv, zabbixAPICoreService) {
output: ['lastvalue'],
itemids: itemid
};
return this.request('item.get', params).then(function(items) {
if (items.length) {
return items[0].lastvalue;
} else {
return null;
}
});
return this.request('item.get', params)
.then(items => items.length ? items[0].lastvalue : 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
* @param {Number} timeFrom Time in seconds
* @param {Number} timeTill Time in seconds
* @return {Array} Array of Zabbix history objects
*/
getHistory(items, time_from, time_till) {
var self = this;
getHistory(items, timeFrom, timeTill) {
// 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 = {
// Group items by value type and perform request for each value type
let grouped_items = _.groupBy(items, 'value_type');
let promises = _.map(grouped_items, (items, value_type) => {
let itemids = _.map(items, 'itemid');
let params = {
output: 'extend',
history: value_type,
itemids: itemids,
sortfield: 'clock',
sortorder: 'ASC',
time_from: time_from
time_from: timeFrom
};
// Relative queries (e.g. last hour) don't include an end time
if (time_till) {
params.time_till = time_till;
if (timeTill) {
params.time_till = timeTill;
}
return self.request('history.get', params);
})).then(_.flatten);
return this.request('history.get', params);
});
return Promise.all(promises).then(_.flatten);
}
/**
@@ -264,31 +250,30 @@ function ZabbixAPIService($q, alertSrv, zabbixAPICoreService) {
* @param {Number} time_till Time in seconds
* @return {Array} Array of Zabbix trend objects
*/
getTrend_ZBXNEXT1193(items, time_from, time_till) {
var self = this;
getTrend_ZBXNEXT1193(items, timeFrom, timeTill) {
// 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 = {
// Group items by value type and perform request for each value type
let grouped_items = _.groupBy(items, 'value_type');
let promises = _.map(grouped_items, (items, value_type) => {
let itemids = _.map(items, 'itemid');
let params = {
output: 'extend',
trend: value_type,
itemids: itemids,
sortfield: 'clock',
sortorder: 'ASC',
time_from: time_from
time_from: timeFrom
};
// Relative queries (e.g. last hour) don't include an end time
if (time_till) {
params.time_till = time_till;
if (timeTill) {
params.time_till = timeTill;
}
return self.request('trend.get', params);
})).then(_.flatten);
return this.request('trend.get', params);
});
return Promise.all(promises).then(_.flatten);
}
getTrend_30(items, time_from, time_till, value_type) {
@@ -312,7 +297,7 @@ function ZabbixAPIService($q, alertSrv, zabbixAPICoreService) {
return self.request('trend.get', params);
}
getITService(/* optional */ serviceids) {
getITService(serviceids) {
var params = {
output: 'extend',
serviceids: serviceids
@@ -320,12 +305,12 @@ function ZabbixAPIService($q, alertSrv, zabbixAPICoreService) {
return this.request('service.get', params);
}
getSLA(serviceids, from, to) {
getSLA(serviceids, timeFrom, timeTo) {
var params = {
serviceids: serviceids,
intervals: [{
from: from,
to: to
from: timeFrom,
to: timeTo
}]
};
return this.request('service.getsla', params);
@@ -364,11 +349,11 @@ function ZabbixAPIService($q, alertSrv, zabbixAPICoreService) {
return this.request('trigger.get', params);
}
getEvents(objectids, from, to, showEvents) {
getEvents(objectids, timeFrom, timeTo, showEvents) {
var params = {
output: 'extend',
time_from: from,
time_till: to,
time_from: timeFrom,
time_till: timeTo,
objectids: objectids,
select_acknowledges: 'extend',
selectHosts: 'extend',
@@ -389,10 +374,8 @@ function ZabbixAPIService($q, alertSrv, zabbixAPICoreService) {
};
return this.request('event.get', params)
.then(function (events) {
return _.filter(events, function(event) {
return event.acknowledges.length;
});
.then(events => {
return _.filter(events, (event) => event.acknowledges.length);
});
}
@@ -411,4 +394,4 @@ function isNotAuthorized(message) {
angular
.module('grafana.services')
.factory('zabbixAPIService', ZabbixAPIService);
.factory('zabbixAPIService', ZabbixAPIServiceFactory);

View File

@@ -7,8 +7,7 @@ import angular from 'angular';
class ZabbixAPICoreService {
/** @ngInject */
constructor($q, backendSrv) {
this.$q = $q;
constructor(backendSrv) {
this.backendSrv = backendSrv;
}
@@ -17,8 +16,7 @@ class ZabbixAPICoreService {
* @return {object} response.result
*/
request(api_url, method, params, options, auth) {
var deferred = this.$q.defer();
var requestData = {
let requestData = {
jsonrpc: '2.0',
method: method,
params: params,
@@ -27,20 +25,19 @@ class ZabbixAPICoreService {
if (auth === "") {
// Reject immediately if not authenticated
deferred.reject({data: "Not authorised."});
return deferred.promise;
return Promise.reject(new ZabbixAPIError({data: "Not authorised."}));
} else if (auth) {
// Set auth parameter only if it needed
requestData.auth = auth;
}
var requestOptions = {
let requestOptions = {
method: 'POST',
url: api_url,
data: requestData,
headers: {
'Content-Type': 'application/json'
},
url: api_url,
data: requestData
}
};
// Set request options for basic auth
@@ -51,24 +48,23 @@ class ZabbixAPICoreService {
requestOptions.headers.Authorization = options.basicAuth;
}
this.backendSrv.datasourceRequest(requestOptions)
.then((response) => {
// General connection issues
if (!response.data) {
deferred.reject(response);
return this.datasourceRequest(requestOptions);
}
datasourceRequest(requestOptions) {
return this.backendSrv.datasourceRequest(requestOptions)
.then(response => {
if (!response.data) {
return Promise.reject(new ZabbixAPIError({data: "General Error, no data"}));
} else if (response.data.error) {
// Handle Zabbix API errors
else if (response.data.error) {
deferred.reject(response.data.error);
return Promise.reject(new ZabbixAPIError(response.data.error));
}
deferred.resolve(response.data.result);
}, (error) => {
deferred.reject(error.err);
// Success
return response.data.result;
});
return deferred.promise;
}
/**
@@ -76,7 +72,7 @@ class ZabbixAPICoreService {
* @return {string} auth token
*/
login(api_url, username, password, options) {
var params = {
let params = {
user: username,
password: password
};
@@ -93,15 +89,18 @@ class ZabbixAPICoreService {
}
// Define zabbix API exception type
function ZabbixException(error) {
export class ZabbixAPIError {
constructor(error) {
this.code = error.code;
this.errorType = error.message;
this.name = error.data;
this.message = error.data;
this.data = error.data;
}
ZabbixException.prototype.toString = function() {
return this.errorType + ": " + this.message;
};
toString() {
return this.name + ": " + this.message;
}
}
angular
.module('grafana.services')

View File

@@ -1,240 +0,0 @@
import angular from 'angular';
import _ from 'lodash';
// Use factory() instead service() for multiple datasources support.
// Each datasource instance must initialize its own cache.
/** @ngInject */
angular.module('grafana.services').factory('ZabbixCachingProxy', function($q, $interval) {
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.storage = {
history: {},
trends: {}
};
// Check is a service initialized or not
this._initialized = undefined;
this.refreshPromise = false;
this.historyPromises = {};
// Wrap _refresh() method to call it once.
this.refresh = callOnce(this._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);
// Don't run duplicated requests
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() {
var self = this;
var promises = [
this.zabbixAPI.getGroups()
];
return this.$q.all(promises).then(function(results) {
if (results.length) {
self._groups = results[0];
}
self._initialized = true;
});
}
getGroups() {
var self = this;
if (this._groups) {
return this.$q.when(self._groups);
} else {
return this.getGroupsOnce()
.then(groups => {
self._groups = groups;
return self._groups;
});
}
}
getHosts(groupids) {
//var self = this;
return this.getHostsOnce(groupids)
.then(hosts => {
// iss #196 - disable caching due performance issues
//self._hosts = _.union(self._hosts, hosts);
return hosts;
});
}
getApps(hostids) {
return this.getAppsOnce(hostids)
.then(apps => {
return apps;
});
}
getItems(hostids, appids, itemtype) {
//var self = this;
return this.getItemsOnce(hostids, appids, itemtype)
.then(items => {
// iss #196 - disable caching due performance issues
//self._items = _.union(self._items, items);
return items;
});
}
getHistoryFromCache(items, time_from, time_till) {
var deferred = this.$q.defer();
var historyStorage = this.storage.history;
var full_history;
var expired = _.filter(_.keyBy(items, 'itemid'), function(item, itemid) {
return !historyStorage[itemid];
});
if (expired.length) {
this.zabbixAPI.getHistory(expired, time_from, time_till).then(function(history) {
var grouped_history = _.groupBy(history, 'itemid');
_.forEach(expired, function(item) {
var itemid = item.itemid;
historyStorage[itemid] = item;
historyStorage[itemid].time_from = time_from;
historyStorage[itemid].time_till = time_till;
historyStorage[itemid].history = grouped_history[itemid];
});
full_history = _.map(items, function(item) {
return historyStorage[item.itemid].history;
});
deferred.resolve(_.flatten(full_history, true));
});
} else {
full_history = _.map(items, function(item) {
return historyStorage[item.itemid].history;
});
deferred.resolve(_.flatten(full_history, true));
}
return deferred.promise;
}
getHistoryFromAPI(items, time_from, time_till) {
return this.zabbixAPI.getHistory(items, time_from, time_till);
}
getHost(hostid) {
return _.find(this._hosts, {'hostid': hostid});
}
getItem(itemid) {
return _.find(this._items, {'itemid': itemid});
}
}
function callAPIRequestOnce(func, promiseKeeper) {
return function() {
var hash = getAPIRequestHash(arguments);
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');
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;
};
}
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) {
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;
};
// Fix for backward compatibility with lodash 2.4
if (!_.keyBy) {_.keyBy = _.indexBy;}

View File

@@ -0,0 +1,191 @@
import angular from 'angular';
import _ from 'lodash';
// Use factory() instead service() for multiple datasources support.
// Each datasource instance must initialize its own cache.
/** @ngInject */
function ZabbixCachingProxyFactory() {
class ZabbixCachingProxy {
constructor(zabbixAPI, cacheOptions) {
this.zabbixAPI = zabbixAPI;
this.cacheEnabled = cacheOptions.enabled;
this.ttl = cacheOptions.ttl || 600000; // 10 minutes by default
// Internal objects for data storing
this.cache = {
groups: {},
hosts: {},
applications: {},
items: {},
history: {},
trends: {}
};
this.historyPromises = {};
// Don't run duplicated history requests
this.getHistory = callAPIRequestOnce(_.bind(this.zabbixAPI.getHistory, this.zabbixAPI),
this.historyPromises, getHistoryRequestHash);
// Don't run duplicated requests
this.groupPromises = {};
this.getGroupsOnce = callAPIRequestOnce(_.bind(this.zabbixAPI.getGroups, this.zabbixAPI),
this.groupPromises, getRequestHash);
this.hostPromises = {};
this.getHostsOnce = callAPIRequestOnce(_.bind(this.zabbixAPI.getHosts, this.zabbixAPI),
this.hostPromises, getRequestHash);
this.appPromises = {};
this.getAppsOnce = callAPIRequestOnce(_.bind(this.zabbixAPI.getApps, this.zabbixAPI),
this.appPromises, getRequestHash);
this.itemPromises = {};
this.getItemsOnce = callAPIRequestOnce(_.bind(this.zabbixAPI.getItems, this.zabbixAPI),
this.itemPromises, getRequestHash);
}
isExpired(cacheObject) {
if (cacheObject) {
let object_age = Date.now() - cacheObject.timestamp;
return !(cacheObject.timestamp && object_age < this.ttl);
} else {
return true;
}
}
/**
* Check that result is present in cache and up to date
* or send request to API.
*/
proxyRequest(request, params, cacheObject) {
let hash = getRequestHash(params);
if (this.cacheEnabled && !this.isExpired(cacheObject[hash])) {
return Promise.resolve(cacheObject[hash].value);
} else {
return request(...params)
.then(result => {
cacheObject[hash] = {
value: result,
timestamp: Date.now()
};
return result;
});
}
}
getGroups() {
return this.proxyRequest(this.getGroupsOnce, [], this.cache.groups);
}
getHosts(groupids) {
return this.proxyRequest(this.getHostsOnce, [groupids], this.cache.hosts);
}
getApps(hostids) {
return this.proxyRequest(this.getAppsOnce, [hostids], this.cache.applications);
}
getItems(hostids, appids, itemtype) {
let params = [hostids, appids, itemtype];
return this.proxyRequest(this.getItemsOnce, params, this.cache.items);
}
getHistoryFromCache(items, time_from, time_till) {
var historyStorage = this.cache.history;
var full_history;
var expired = _.filter(_.keyBy(items, 'itemid'), (item, itemid) => {
return !historyStorage[itemid];
});
if (expired.length) {
return this.zabbixAPI.getHistory(expired, time_from, time_till).then(function(history) {
var grouped_history = _.groupBy(history, 'itemid');
_.forEach(expired, item => {
var itemid = item.itemid;
historyStorage[itemid] = item;
historyStorage[itemid].time_from = time_from;
historyStorage[itemid].time_till = time_till;
historyStorage[itemid].history = grouped_history[itemid];
});
full_history = _.map(items, item => {
return historyStorage[item.itemid].history;
});
return _.flatten(full_history, true);
});
} else {
full_history = _.map(items, function(item) {
return historyStorage[item.itemid].history;
});
return Promise.resolve(_.flatten(full_history, true));
}
}
getHistoryFromAPI(items, time_from, time_till) {
return this.zabbixAPI.getHistory(items, time_from, time_till);
}
}
return ZabbixCachingProxy;
}
angular
.module('grafana.services')
.factory('ZabbixCachingProxy', ZabbixCachingProxyFactory);
/**
* Wrap zabbix API request to prevent multiple calls
* with same params when waiting for result.
*/
function callAPIRequestOnce(func, promiseKeeper, argsHashFunc) {
return function() {
var hash = argsHashFunc(arguments);
if (!promiseKeeper[hash]) {
promiseKeeper[hash] = Promise.resolve(
func.apply(this, arguments)
.then(result => {
promiseKeeper[hash] = null;
return result;
})
);
}
return promiseKeeper[hash];
};
}
function getRequestHash(args) {
var requestStamp = _.map(args, arg => {
if (arg === undefined) {
return 'undefined';
} else {
if (_.isArray(arg)) {
return arg.sort().toString();
} else {
return arg.toString();
}
}
}).join();
return requestStamp.getHash();
}
function getHistoryRequestHash(args) {
let itemids = _.map(args[0], 'itemid');
let stamp = itemids.join() + args[1] + args[2];
return stamp.getHash();
}
String.prototype.getHash = function() {
var hash = 0, i, chr, len;
if (this.length !== 0) {
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;
};
// Fix for backward compatibility with lodash 2.4
if (!_.keyBy) {_.keyBy = _.indexBy;}

View File

@@ -19,12 +19,11 @@ import '../datasource-zabbix/css/query-editor.css!';
class TriggerPanelEditorCtrl {
/** @ngInject */
constructor($scope, $rootScope, $q, uiSegmentSrv, datasourceSrv, templateSrv, popoverSrv) {
constructor($scope, $rootScope, uiSegmentSrv, datasourceSrv, templateSrv, popoverSrv) {
$scope.editor = this;
this.panelCtrl = $scope.ctrl;
this.panel = this.panelCtrl.panel;
this.$q = $q;
this.datasourceSrv = datasourceSrv;
this.templateSrv = templateSrv;
this.popoverSrv = popoverSrv;
@@ -62,8 +61,6 @@ class TriggerPanelEditorCtrl {
};
_.defaults(this, scopeDefaults);
var self = this;
// Get zabbix data sources
var datasources = _.filter(this.datasourceSrv.getMetricSources(), datasource => {
return datasource.meta.id === 'alexanderzobnin-zabbix-datasource';
@@ -75,59 +72,47 @@ class TriggerPanelEditorCtrl {
this.panel.datasource = this.datasources[0];
}
// Load datasource
this.datasourceSrv.get(this.panel.datasource).then(function (datasource) {
self.datasource = datasource;
self.initFilters();
self.panelCtrl.refresh();
this.datasourceSrv.get(this.panel.datasource)
.then(datasource => {
this.datasource = datasource;
this.queryBuilder = datasource.queryBuilder;
this.initFilters();
this.panelCtrl.refresh();
});
}
initFilters() {
var self = this;
return this.$q
.when(this.suggestGroups())
.then(() => {return self.suggestHosts();})
.then(() => {return self.suggestApps();});
return Promise.all([
this.suggestGroups(),
this.suggestHosts(),
this.suggestApps()
]);
}
suggestGroups() {
var self = this;
return this.datasource.zabbixCache
.getGroups()
return this.queryBuilder.getAllGroups()
.then(groups => {
self.metric.groupList = groups;
this.metric.groupList = groups;
return groups;
});
}
suggestHosts() {
var self = this;
var groupFilter = this.datasource.replaceTemplateVars(this.panel.triggers.group.filter);
return this.datasource.queryProcessor
.filterGroups(self.metric.groupList, groupFilter)
.then(groups => {
var groupids = _.map(groups, 'groupid');
return self.datasource.zabbixAPI
.getHosts(groupids)
let groupFilter = this.datasource.replaceTemplateVars(this.panel.triggers.group.filter);
return this.queryBuilder.getAllHosts(groupFilter)
.then(hosts => {
self.metric.hostList = hosts;
this.metric.hostList = hosts;
return hosts;
});
});
}
suggestApps() {
var self = this;
var hostFilter = this.datasource.replaceTemplateVars(this.panel.triggers.host.filter);
return this.datasource.queryProcessor
.filterHosts(self.metric.hostList, hostFilter)
.then(hosts => {
var hostids = _.map(hosts, 'hostid');
return self.datasource.zabbixAPI
.getApps(hostids)
let groupFilter = this.datasource.replaceTemplateVars(this.panel.triggers.group.filter);
let hostFilter = this.datasource.replaceTemplateVars(this.panel.triggers.host.filter);
return this.queryBuilder.getAllApps(groupFilter, hostFilter)
.then(apps => {
return self.metric.appList = apps;
});
this.metric.appList = apps;
return apps;
});
}

View File

@@ -61,7 +61,7 @@ var defaultTimeFormat = "DD MMM YYYY HH:mm:ss";
class TriggerPanelCtrl extends MetricsPanelCtrl {
/** @ngInject */
constructor($scope, $injector, $q, $element, datasourceSrv, templateSrv, contextSrv) {
constructor($scope, $injector, $element, datasourceSrv, templateSrv, contextSrv) {
super($scope, $injector);
this.datasourceSrv = datasourceSrv;
this.templateSrv = templateSrv;
@@ -106,9 +106,9 @@ class TriggerPanelCtrl extends MetricsPanelCtrl {
var self = this;
// Load datasource
return this.datasourceSrv.get(this.panel.datasource).then(datasource => {
var zabbix = datasource.zabbixAPI;
var queryProcessor = datasource.queryProcessor;
return this.datasourceSrv.get(this.panel.datasource)
.then(datasource => {
var zabbix = datasource.zabbix;
var showEvents = self.panel.showEvents.value;
var triggerFilter = self.panel.triggers;
@@ -117,13 +117,8 @@ class TriggerPanelCtrl extends MetricsPanelCtrl {
var hostFilter = datasource.replaceTemplateVars(triggerFilter.host.filter);
var appFilter = datasource.replaceTemplateVars(triggerFilter.application.filter);
var buildQuery = queryProcessor.buildTriggerQuery(groupFilter, hostFilter, appFilter);
return buildQuery.then(query => {
return zabbix.getTriggers(query.groupids,
query.hostids,
query.applicationids,
showEvents)
.then(triggers => {
var getTriggers = zabbix.getTriggers(groupFilter, hostFilter, appFilter, showEvents);
return getTriggers.then(triggers => {
return _.map(triggers, trigger => {
let triggerObj = trigger;
@@ -230,7 +225,6 @@ class TriggerPanelCtrl extends MetricsPanelCtrl {
});
});
});
});
}
switchComment(trigger) {