Add dist/ directory to repo to correspond development guide.

http://docs.grafana.org/plugins/development/
This commit is contained in:
Alexander Zobnin
2017-02-02 11:33:54 +03:00
parent 658e436198
commit 0b3f537341
88 changed files with 11482 additions and 1 deletions

View File

@@ -0,0 +1,116 @@
'use strict';
System.register(['angular', 'lodash', 'jquery', './metricFunctions'], function (_export, _context) {
"use strict";
var angular, _, $, metricFunctions;
function getAllFunctionNames(categories) {
return _.reduce(categories, function (list, category) {
_.each(category, function (func) {
list.push(func.name);
});
return list;
}, []);
}
function createFunctionDropDownMenu(categories) {
return _.map(categories, function (list, category) {
return {
text: category,
submenu: _.map(list, function (value) {
return {
text: value.name,
click: "ctrl.addFunction('" + value.name + "')"
};
})
};
});
}
return {
setters: [function (_angular) {
angular = _angular.default;
}, function (_lodash) {
_ = _lodash.default;
}, function (_jquery) {
$ = _jquery.default;
}, function (_metricFunctions) {
metricFunctions = _metricFunctions;
}],
execute: function () {
/** @ngInject */
angular.module('grafana.directives').directive('addMetricFunction', function ($compile) {
var inputTemplate = '<input type="text"' + ' class="gf-form-input"' + ' spellcheck="false" style="display:none"></input>';
var buttonTemplate = '<a class="gf-form-label tight-form-func dropdown-toggle query-part"' + ' tabindex="1" gf-dropdown="functionMenu" data-toggle="dropdown">' + '<i class="fa fa-plus"></i></a>';
return {
link: function link($scope, elem) {
var categories = metricFunctions.getCategories();
var allFunctions = getAllFunctionNames(categories);
$scope.functionMenu = createFunctionDropDownMenu(categories);
var $input = $(inputTemplate);
var $button = $(buttonTemplate);
$input.appendTo(elem);
$button.appendTo(elem);
$input.attr('data-provide', 'typeahead');
$input.typeahead({
source: allFunctions,
minLength: 1,
items: 10,
updater: function updater(value) {
var funcDef = metricFunctions.getFuncDef(value);
if (!funcDef) {
// try find close match
value = value.toLowerCase();
funcDef = _.find(allFunctions, function (funcName) {
return funcName.toLowerCase().indexOf(value) === 0;
});
if (!funcDef) {
return;
}
}
$scope.$apply(function () {
$scope.addFunction(funcDef);
});
$input.trigger('blur');
return '';
}
});
$button.click(function () {
$button.hide();
$input.show();
$input.focus();
});
$input.keyup(function () {
elem.toggleClass('open', $input.val() === '');
});
$input.blur(function () {
// clicking the function dropdown menu wont
// work if you remove class at once
setTimeout(function () {
$input.val('');
$input.hide();
$button.show();
elem.removeClass('open');
}, 200);
});
$compile(elem.contents())($scope);
}
};
});
}
};
});
//# sourceMappingURL=add-metric-function.directive.js.map

File diff suppressed because one or more lines are too long

View File

@@ -0,0 +1,7 @@
.zbx-regex {
color: #CCA300;
}
.zbx-variable {
color: #33B5E5;
}

287
dist/datasource-zabbix/dataProcessor.js vendored Normal file
View File

@@ -0,0 +1,287 @@
'use strict';
System.register(['lodash', './utils'], function (_export, _context) {
"use strict";
var _, utils, metricFunctions, aggregationFunctions;
/**
* 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) {
var orderByCallback = aggregationFunctions[orderByFunc];
var sortByIteratee = function sortByIteratee(ts) {
var values = _.map(ts.datapoints, function (point) {
return point[0];
});
return orderByCallback(values);
};
var 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 setAliasByRegex(alias, timeseries) {
timeseries.target = extractText(timeseries.target, alias);
return timeseries;
}function extractText(str, pattern) {
var extractPattern = new RegExp(pattern);
var extractedValue = extractPattern.exec(str);
extractedValue = extractedValue[0];
return extractedValue;
}function scale(factor, datapoints) {
return _.map(datapoints, function (point) {
return [point[0] * factor, point[1]];
});
}function delta(datapoints) {
var newSeries = [];
var deltaValue = void 0;
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;
}function timeShift(interval, range) {
var shift = utils.parseTimeShiftInterval(interval) / 1000;
return range.map(function (time) {
return time - shift;
});
}function unShiftTimeSeries(interval, datapoints) {
var unshift = utils.parseTimeShiftInterval(interval);
return datapoints.map(function (dp) {
return [dp[0], dp[1] + unshift];
});
}return {
setters: [function (_lodash) {
_ = _lodash.default;
}, function (_utils) {
utils = _utils;
}],
execute: function () {
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'),
timeShift: timeShift,
setAlias: setAlias,
setAliasByRegex: setAliasByRegex
};
aggregationFunctions = {
avg: AVERAGE,
min: MIN,
max: MAX,
median: MEDIAN
};
_export('default', {
downsampleSeries: downsampleSeries,
groupBy: groupBy,
AVERAGE: AVERAGE,
MIN: MIN,
MAX: MAX,
MEDIAN: MEDIAN,
unShiftTimeSeries: unShiftTimeSeries,
get aggregationFunctions() {
return aggregationFunctions;
},
get metricFunctions() {
return metricFunctions;
}
});
}
};
});
//# sourceMappingURL=dataProcessor.js.map

File diff suppressed because one or more lines are too long

597
dist/datasource-zabbix/datasource.js vendored Normal file
View File

@@ -0,0 +1,597 @@
'use strict';
System.register(['lodash', 'app/core/utils/datemath', './utils', './migrations', './metricFunctions', './dataProcessor', './responseHandler', './zabbix.js', './zabbixAPICore.service.js'], function (_export, _context) {
"use strict";
var _, dateMath, utils, migrations, metricFunctions, dataProcessor, responseHandler, ZabbixAPIError, _slicedToArray, _createClass, ZabbixAPIDatasource;
function _classCallCheck(instance, Constructor) {
if (!(instance instanceof Constructor)) {
throw new TypeError("Cannot call a class as a function");
}
}
function bindFunctionDefs(functionDefs, category) {
var aggregationFunctions = _.map(metricFunctions.getCategories()[category], 'name');
var aggFuncDefs = _.filter(functionDefs, function (func) {
return _.includes(aggregationFunctions, func.def.name);
});
return _.map(aggFuncDefs, function (func) {
var funcInstance = metricFunctions.createFuncInstance(func.def, func.params);
return funcInstance.bindFunction(dataProcessor.metricFunctions);
});
}
function downsampleSeries(timeseries_data, options) {
return _.map(timeseries_data, function (timeseries) {
if (timeseries.datapoints.length > options.maxDataPoints) {
timeseries.datapoints = dataProcessor.groupBy(options.interval, dataProcessor.AVERAGE, timeseries.datapoints);
}
return timeseries;
});
}
function formatMetric(metricObj) {
return {
text: metricObj.name,
expandable: false
};
}
/**
* Custom formatter for template variables.
* Default Grafana "regex" formatter returns
* value1|value2
* This formatter returns
* (value1|value2)
* This format needed for using in complex regex with
* template variables, for example
* /CPU $cpu_item.*time/ where $cpu_item is system,user,iowait
*/
function zabbixTemplateFormat(value) {
if (typeof value === 'string') {
return utils.escapeRegex(value);
}
var escapedValues = _.map(value, utils.escapeRegex);
return '(' + escapedValues.join('|') + ')';
}
/**
* If template variables are used in request, replace it using regex format
* and wrap with '/' for proper multi-value work. Example:
* $variable selected as a, b, c
* We use filter $variable
* $variable -> a|b|c -> /a|b|c/
* /$variable/ -> /a|b|c/ -> /a|b|c/
*/
function replaceTemplateVars(templateSrv, target, scopedVars) {
var replacedTarget = templateSrv.replace(target, scopedVars, zabbixTemplateFormat);
if (target !== replacedTarget && !utils.isRegex(replacedTarget)) {
replacedTarget = '/^' + replacedTarget + '$/';
}
return replacedTarget;
}
function extractText(str, pattern, useCaptureGroups) {
var extractPattern = new RegExp(pattern);
var extractedValue = extractPattern.exec(str);
if (extractedValue) {
if (useCaptureGroups) {
extractedValue = extractedValue[1];
} else {
extractedValue = extractedValue[0];
}
}
return extractedValue;
}
// Apply function one by one:
// sequence([a(), b(), c()]) = c(b(a()));
function sequence(funcsArray) {
return function (result) {
for (var i = 0; i < funcsArray.length; i++) {
result = funcsArray[i].call(this, result);
}
return result;
};
}
return {
setters: [function (_lodash) {
_ = _lodash.default;
}, function (_appCoreUtilsDatemath) {
dateMath = _appCoreUtilsDatemath;
}, function (_utils) {
utils = _utils;
}, function (_migrations) {
migrations = _migrations;
}, function (_metricFunctions) {
metricFunctions = _metricFunctions;
}, function (_dataProcessor) {
dataProcessor = _dataProcessor.default;
}, function (_responseHandler) {
responseHandler = _responseHandler.default;
}, function (_zabbixJs) {}, function (_zabbixAPICoreServiceJs) {
ZabbixAPIError = _zabbixAPICoreServiceJs.ZabbixAPIError;
}],
execute: function () {
_slicedToArray = function () {
function sliceIterator(arr, i) {
var _arr = [];
var _n = true;
var _d = false;
var _e = undefined;
try {
for (var _i = arr[Symbol.iterator](), _s; !(_n = (_s = _i.next()).done); _n = true) {
_arr.push(_s.value);
if (i && _arr.length === i) break;
}
} catch (err) {
_d = true;
_e = err;
} finally {
try {
if (!_n && _i["return"]) _i["return"]();
} finally {
if (_d) throw _e;
}
}
return _arr;
}
return function (arr, i) {
if (Array.isArray(arr)) {
return arr;
} else if (Symbol.iterator in Object(arr)) {
return sliceIterator(arr, i);
} else {
throw new TypeError("Invalid attempt to destructure non-iterable instance");
}
};
}();
_createClass = function () {
function defineProperties(target, props) {
for (var i = 0; i < props.length; i++) {
var descriptor = props[i];
descriptor.enumerable = descriptor.enumerable || false;
descriptor.configurable = true;
if ("value" in descriptor) descriptor.writable = true;
Object.defineProperty(target, descriptor.key, descriptor);
}
}
return function (Constructor, protoProps, staticProps) {
if (protoProps) defineProperties(Constructor.prototype, protoProps);
if (staticProps) defineProperties(Constructor, staticProps);
return Constructor;
};
}();
_export('ZabbixAPIDatasource', ZabbixAPIDatasource = function () {
/** @ngInject */
function ZabbixAPIDatasource(instanceSettings, templateSrv, alertSrv, Zabbix) {
_classCallCheck(this, ZabbixAPIDatasource);
this.templateSrv = templateSrv;
this.alertSrv = alertSrv;
// General data source settings
this.name = instanceSettings.name;
this.url = instanceSettings.url;
this.basicAuth = instanceSettings.basicAuth;
this.withCredentials = instanceSettings.withCredentials;
// Zabbix API credentials
this.username = instanceSettings.jsonData.username;
this.password = instanceSettings.jsonData.password;
// Use trends instead history since specified time
this.trends = instanceSettings.jsonData.trends;
this.trendsFrom = instanceSettings.jsonData.trendsFrom || '7d';
// Set cache update interval
var ttl = instanceSettings.jsonData.cacheTTL || '1h';
this.cacheTTL = utils.parseInterval(ttl);
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);
}
////////////////////////
// Datasource methods //
////////////////////////
/**
* Query panel data. Calls for each panel in dashboard.
* @param {Object} options Contains time range, targets and other info.
* @return {Object} Grafana metrics object with timeseries data for each target.
*/
_createClass(ZabbixAPIDatasource, [{
key: 'query',
value: function query(options) {
var _this = this;
var timeFrom = Math.ceil(dateMath.parse(options.range.from) / 1000);
var timeTo = Math.ceil(dateMath.parse(options.range.to) / 1000);
var useTrendsFrom = Math.ceil(dateMath.parse('now-' + this.trendsFrom) / 1000);
var useTrends = timeFrom <= useTrendsFrom && this.trends;
// Create request for each target
var promises = _.map(options.targets, function (target) {
// Prevent changes of original object
target = _.cloneDeep(target);
_this.replaceTargetVariables(target, options);
// Apply Time-related functions (timeShift(), etc)
var timeFunctions = bindFunctionDefs(target.functions, 'Time');
if (timeFunctions.length) {
var _sequence = sequence(timeFunctions)([timeFrom, timeTo]),
_sequence2 = _slicedToArray(_sequence, 2),
time_from = _sequence2[0],
time_to = _sequence2[1];
timeFrom = time_from;
timeTo = time_to;
}
// Metrics or Text query mode
if (target.mode !== 1) {
// Migrate old targets
target = migrations.migrate(target);
// Don't request undefined and hidden targets
if (target.hide || !target.group || !target.host || !target.item) {
return [];
}
if (!target.mode || target.mode === 0) {
return _this.queryNumericData(target, timeFrom, timeTo, useTrends);
} else if (target.mode === 2) {
return _this.queryTextData(target, timeFrom, timeTo);
}
}
// IT services mode
else if (target.mode === 1) {
// Don't show undefined and hidden targets
if (target.hide || !target.itservice || !target.slaProperty) {
return [];
}
return _this.zabbix.getSLA(target.itservice.serviceid, timeFrom, timeTo).then(function (slaObject) {
return responseHandler.handleSLAResponse(target.itservice, target.slaProperty, slaObject);
});
}
});
// Data for panel (all targets)
return Promise.all(_.flatten(promises)).then(_.flatten).then(function (timeseries_data) {
return downsampleSeries(timeseries_data, options);
}).then(function (data) {
return { data: data };
});
}
}, {
key: 'queryNumericData',
value: function queryNumericData(target, timeFrom, timeTo, useTrends) {
var _this2 = this;
var options = {
itemtype: 'num'
};
return this.zabbix.getItemsFromTarget(target, options).then(function (items) {
var getHistoryPromise = void 0;
if (useTrends) {
(function () {
var valueType = _this2.getTrendValueType(target);
getHistoryPromise = _this2.zabbix.getTrend(items, timeFrom, timeTo).then(function (history) {
return responseHandler.handleTrends(history, items, valueType);
});
})();
} else {
// Use history
getHistoryPromise = _this2.zabbix.getHistory(items, timeFrom, timeTo).then(function (history) {
return responseHandler.handleHistory(history, items);
});
}
return getHistoryPromise.then(function (timeseries_data) {
return _this2.applyDataProcessingFunctions(timeseries_data, target);
});
});
}
}, {
key: 'getTrendValueType',
value: function getTrendValueType(target) {
// Find trendValue() function and get specified trend value
var trendFunctions = _.map(metricFunctions.getCategories()['Trends'], 'name');
var trendValueFunc = _.find(target.functions, function (func) {
return _.includes(trendFunctions, func.def.name);
});
return trendValueFunc ? trendValueFunc.params[0] : "avg";
}
}, {
key: 'applyDataProcessingFunctions',
value: function applyDataProcessingFunctions(timeseries_data, target) {
var transformFunctions = bindFunctionDefs(target.functions, 'Transform');
var aggregationFunctions = bindFunctionDefs(target.functions, 'Aggregate');
var filterFunctions = bindFunctionDefs(target.functions, 'Filter');
var aliasFunctions = bindFunctionDefs(target.functions, 'Alias');
// Apply transformation functions
timeseries_data = _.map(timeseries_data, function (timeseries) {
timeseries.datapoints = sequence(transformFunctions)(timeseries.datapoints);
return timeseries;
});
// Apply filter functions
if (filterFunctions.length) {
timeseries_data = sequence(filterFunctions)(timeseries_data);
}
// Apply aggregations
if (aggregationFunctions.length) {
(function () {
var dp = _.map(timeseries_data, 'datapoints');
dp = sequence(aggregationFunctions)(dp);
var aggFuncNames = _.map(metricFunctions.getCategories()['Aggregate'], 'name');
var lastAgg = _.findLast(target.functions, function (func) {
return _.includes(aggFuncNames, func.def.name);
});
timeseries_data = [{
target: lastAgg.text,
datapoints: dp
}];
})();
}
// Apply alias functions
_.forEach(timeseries_data, sequence(aliasFunctions));
// Apply Time-related functions (timeShift(), etc)
// Find timeShift() function and get specified trend value
this.applyTimeShiftFunction(timeseries_data, target);
return timeseries_data;
}
}, {
key: 'applyTimeShiftFunction',
value: function applyTimeShiftFunction(timeseries_data, target) {
// Find timeShift() function and get specified interval
var timeShiftFunc = _.find(target.functions, function (func) {
return func.def.name === 'timeShift';
});
if (timeShiftFunc) {
(function () {
var shift = timeShiftFunc.params[0];
_.forEach(timeseries_data, function (series) {
series.datapoints = dataProcessor.unShiftTimeSeries(shift, series.datapoints);
});
})();
}
}
}, {
key: 'queryTextData',
value: function queryTextData(target, timeFrom, timeTo) {
var _this3 = this;
var options = {
itemtype: 'text'
};
return this.zabbix.getItemsFromTarget(target, options).then(function (items) {
if (items.length) {
return _this3.zabbix.getHistory(items, timeFrom, timeTo).then(function (history) {
return responseHandler.convertHistory(history, items, false, function (point) {
var value = point.value;
// Regex-based extractor
if (target.textFilter) {
value = extractText(point.value, target.textFilter, target.useCaptureGroups);
}
return [value, point.clock * 1000];
});
});
} else {
return Promise.resolve([]);
}
});
}
}, {
key: 'testDatasource',
value: function testDatasource() {
var _this4 = this;
var zabbixVersion = void 0;
return this.zabbix.getVersion().then(function (version) {
zabbixVersion = version;
return _this4.zabbix.login();
}).then(function () {
return {
status: "success",
title: "Success",
message: "Zabbix API version: " + zabbixVersion
};
}).catch(function (error) {
if (error instanceof ZabbixAPIError) {
return {
status: "error",
title: error.message,
message: error.data
};
} else {
return {
status: "error",
title: "Connection failed",
message: "Could not connect to given url"
};
}
});
}
}, {
key: 'metricFindQuery',
value: function metricFindQuery(query) {
var _this5 = this;
var result = void 0;
var parts = [];
// Split query. Query structure: group.host.app.item
_.each(query.split('.'), function (part) {
part = _this5.replaceTemplateVars(part, {});
// Replace wildcard to regex
if (part === '*') {
part = '/.*/';
}
parts.push(part);
});
var template = _.zipObject(['group', 'host', 'app', 'item'], parts);
// Get items
if (parts.length === 4) {
// Search for all items, even it's not belong to any application
if (template.app === '/.*/') {
template.app = '';
}
result = this.zabbix.getItems(template.group, template.host, template.app, template.item);
} else if (parts.length === 3) {
// Get applications
result = this.zabbix.getApps(template.group, template.host, template.app);
} else if (parts.length === 2) {
// Get hosts
result = this.zabbix.getHosts(template.group, template.host);
} else if (parts.length === 1) {
// Get groups
result = this.zabbix.getGroups(template.group);
} else {
result = Promise.resolve([]);
}
return result.then(function (metrics) {
return metrics.map(formatMetric);
});
}
}, {
key: 'annotationQuery',
value: function annotationQuery(options) {
var _this6 = this;
var timeFrom = Math.ceil(dateMath.parse(options.rangeRaw.from) / 1000);
var timeTo = Math.ceil(dateMath.parse(options.rangeRaw.to) / 1000);
var annotation = options.annotation;
var showOkEvents = annotation.showOkEvents ? [0, 1] : 1;
// Show all triggers
var showTriggers = [0, 1];
var getTriggers = this.zabbix.getTriggers(this.replaceTemplateVars(annotation.group, {}), this.replaceTemplateVars(annotation.host, {}), this.replaceTemplateVars(annotation.application, {}), showTriggers);
return getTriggers.then(function (triggers) {
// Filter triggers by description
if (utils.isRegex(annotation.trigger)) {
triggers = _.filter(triggers, function (trigger) {
return utils.buildRegex(annotation.trigger).test(trigger.description);
});
} else if (annotation.trigger) {
triggers = _.filter(triggers, function (trigger) {
return trigger.description === annotation.trigger;
});
}
// Remove events below the chose severity
triggers = _.filter(triggers, function (trigger) {
return Number(trigger.priority) >= Number(annotation.minseverity);
});
var objectids = _.map(triggers, 'triggerid');
return _this6.zabbix.getEvents(objectids, timeFrom, timeTo, showOkEvents).then(function (events) {
var indexedTriggers = _.keyBy(triggers, 'triggerid');
// Hide acknowledged events if option enabled
if (annotation.hideAcknowledged) {
events = _.filter(events, function (event) {
return !event.acknowledges.length;
});
}
return _.map(events, function (event) {
var tags = void 0;
if (annotation.showHostname) {
tags = _.map(event.hosts, 'name');
}
// Show event type (OK or Problem)
var title = Number(event.value) ? 'Problem' : 'OK';
var formatted_acknowledges = utils.formatAcknowledges(event.acknowledges);
return {
annotation: annotation,
time: event.clock * 1000,
title: title,
tags: tags,
text: indexedTriggers[event.objectid].description + formatted_acknowledges
};
});
});
});
}
}, {
key: 'replaceTargetVariables',
value: function replaceTargetVariables(target, options) {
var _this7 = this;
var parts = ['group', 'host', 'application', 'item'];
parts.forEach(function (p) {
if (target[p] && target[p].filter) {
target[p].filter = _this7.replaceTemplateVars(target[p].filter, options.scopedVars);
}
});
target.textFilter = this.replaceTemplateVars(target.textFilter, options.scopedVars);
_.forEach(target.functions, function (func) {
func.params = func.params.map(function (param) {
if (typeof param === 'number') {
return +_this7.templateSrv.replace(param.toString(), options.scopedVars);
} else {
return _this7.templateSrv.replace(param, options.scopedVars);
}
});
});
}
}]);
return ZabbixAPIDatasource;
}());
_export('ZabbixAPIDatasource', ZabbixAPIDatasource);
_export('zabbixTemplateFormat', zabbixTemplateFormat);
// Fix for backward compatibility with lodash 2.4
if (!_.includes) {
_.includes = _.contains;
}
if (!_.keyBy) {
_.keyBy = _.indexBy;
}
}
};
});
//# sourceMappingURL=datasource.js.map

File diff suppressed because one or more lines are too long

View File

@@ -0,0 +1,107 @@
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
<!-- Generator: Adobe Illustrator 19.1.0, SVG Export Plug-In . SVG Version: 6.00 Build 0) -->
<svg
xmlns:dc="http://purl.org/dc/elements/1.1/"
xmlns:cc="http://creativecommons.org/ns#"
xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#"
xmlns:svg="http://www.w3.org/2000/svg"
xmlns="http://www.w3.org/2000/svg"
xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd"
xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape"
version="1.0"
id="Layer_1"
x="0px"
y="0px"
width="100px"
height="100px"
viewBox="692 0 100 100"
style="enable-background:new 692 0 100 100;"
xml:space="preserve"
inkscape:version="0.91 r"
sodipodi:docname="zabbix_app_logo.svg"
enable-background="new"><metadata
id="metadata13"><rdf:RDF><cc:Work
rdf:about=""><dc:format>image/svg+xml</dc:format><dc:type
rdf:resource="http://purl.org/dc/dcmitype/StillImage" /><dc:title></dc:title></cc:Work></rdf:RDF></metadata><defs
id="defs11"><linearGradient
id="SVGID_1_"
gradientUnits="userSpaceOnUse"
x1="2.6005001"
y1="65.475197"
x2="94.377701"
y2="30.245199"><stop
id="stop34"
style="stop-color:#58595B"
offset="0.2583" /><stop
id="stop32"
style="stop-color:#646C70"
offset="0.2917" /><stop
id="stop30"
style="stop-color:#6C8087"
offset="0.3398" /><stop
id="stop28"
style="stop-color:#6D8F9B"
offset="0.3927" /><stop
id="stop26"
style="stop-color:#689BAA"
offset="0.4499" /><stop
id="stop24"
style="stop-color:#5FA3B5"
offset="0.5128" /><stop
id="stop22"
style="stop-color:#53A8BD"
offset="0.5837" /><stop
id="stop20"
style="stop-color:#47ABC2"
offset="0.6674" /><stop
id="stop18"
style="stop-color:#3FAEC5"
offset="0.7759" /><stop
id="stop16"
style="stop-color:#3CAFC7"
offset="1" /><stop
id="stop14"
style="stop-color:#3BB0C9"
offset="1" /></linearGradient></defs><sodipodi:namedview
pagecolor="#ffffff"
bordercolor="#666666"
borderopacity="1"
objecttolerance="10"
gridtolerance="10"
guidetolerance="10"
inkscape:pageopacity="0"
inkscape:pageshadow="2"
inkscape:window-width="1615"
inkscape:window-height="1026"
id="namedview9"
showgrid="false"
inkscape:zoom="4.285"
inkscape:cx="50.424685"
inkscape:cy="23.581186"
inkscape:window-x="65"
inkscape:window-y="24"
inkscape:window-maximized="1"
inkscape:current-layer="g5194" /><style
type="text/css"
id="style3">
.st0{fill:#787878;}
</style><g
inkscape:groupmode="layer"
id="g5194"
inkscape:label="Zabbix BG Original"
style="display:inline"><rect
style="fill:#d40000;fill-opacity:1"
id="rect5196"
width="100"
height="100"
x="692"
y="0" /></g><g
inkscape:groupmode="layer"
id="layer6"
inkscape:label="Zabbix Original Z"
style="display:inline"><path
d="m 715.54426,16.689227 52.91147,0 0,6.87033 -42.58255,52.167008 43.62047,0 0,7.584207 -54.9873,0 0,-6.871516 42.58255,-52.166552 -41.54464,0 0,-7.583477 z"
style="display:inline;fill:#ffffff;fill-opacity:1;fill-rule:nonzero;stroke:none"
id="path4169-6"
inkscape:connector-curvature="0" /></g></svg>

After

Width:  |  Height:  |  Size: 3.5 KiB

View File

@@ -0,0 +1,251 @@
'use strict';
System.register(['angular', 'lodash', 'jquery'], function (_export, _context) {
"use strict";
var angular, _, $;
return {
setters: [function (_angular) {
angular = _angular.default;
}, function (_lodash) {
_ = _lodash.default;
}, function (_jquery) {
$ = _jquery.default;
}],
execute: function () {
/** @ngInject */
angular.module('grafana.directives').directive('metricFunctionEditor', function ($compile, templateSrv) {
var funcSpanTemplate = '<a ng-click="">{{func.def.name}}</a><span>(</span>';
var paramTemplate = '<input type="text" style="display:none"' + ' class="input-mini tight-form-func-param"></input>';
var funcControlsTemplate = '<div class="tight-form-func-controls">' + '<span class="pointer fa fa-arrow-left"></span>' + '<span class="pointer fa fa-question-circle"></span>' + '<span class="pointer fa fa-remove" ></span>' + '<span class="pointer fa fa-arrow-right"></span>' + '</div>';
return {
restrict: 'A',
link: function postLink($scope, elem) {
var $funcLink = $(funcSpanTemplate);
var $funcControls = $(funcControlsTemplate);
var ctrl = $scope.ctrl;
var func = $scope.func;
var funcDef = func.def;
var scheduledRelink = false;
var paramCountAtLink = 0;
function clickFuncParam(paramIndex) {
/*jshint validthis:true */
var $link = $(this);
var $input = $link.next();
$input.val(func.params[paramIndex]);
$input.css('width', $link.width() + 16 + 'px');
$link.hide();
$input.show();
$input.focus();
$input.select();
var typeahead = $input.data('typeahead');
if (typeahead) {
$input.val('');
typeahead.lookup();
}
}
function scheduledRelinkIfNeeded() {
if (paramCountAtLink === func.params.length) {
return;
}
if (!scheduledRelink) {
scheduledRelink = true;
setTimeout(function () {
relink();
scheduledRelink = false;
}, 200);
}
}
function inputBlur(paramIndex) {
/*jshint validthis:true */
var $input = $(this);
var $link = $input.prev();
var newValue = $input.val();
if (newValue !== '' || func.def.params[paramIndex].optional) {
$link.html(templateSrv.highlightVariablesAsHtml(newValue));
func.updateParam($input.val(), paramIndex);
scheduledRelinkIfNeeded();
$scope.$apply(function () {
ctrl.targetChanged();
});
$input.hide();
$link.show();
}
}
function inputKeyPress(paramIndex, e) {
/*jshint validthis:true */
if (e.which === 13) {
inputBlur.call(this, paramIndex);
}
}
function inputKeyDown() {
/*jshint validthis:true */
this.style.width = (3 + this.value.length) * 8 + 'px';
}
function addTypeahead($input, paramIndex) {
$input.attr('data-provide', 'typeahead');
var options = funcDef.params[paramIndex].options;
if (funcDef.params[paramIndex].type === 'int' || funcDef.params[paramIndex].type === 'float') {
options = _.map(options, function (val) {
return val.toString();
});
}
$input.typeahead({
source: options,
minLength: 0,
items: 20,
updater: function updater(value) {
setTimeout(function () {
inputBlur.call($input[0], paramIndex);
}, 0);
return value;
}
});
var typeahead = $input.data('typeahead');
typeahead.lookup = function () {
this.query = this.$element.val() || '';
return this.process(this.source);
};
}
function toggleFuncControls() {
var targetDiv = elem.closest('.tight-form');
if (elem.hasClass('show-function-controls')) {
elem.removeClass('show-function-controls');
targetDiv.removeClass('has-open-function');
$funcControls.hide();
return;
}
elem.addClass('show-function-controls');
targetDiv.addClass('has-open-function');
$funcControls.show();
}
function addElementsAndCompile() {
$funcControls.appendTo(elem);
$funcLink.appendTo(elem);
_.each(funcDef.params, function (param, index) {
if (param.optional && func.params.length <= index) {
return;
}
if (index > 0) {
$('<span>, </span>').appendTo(elem);
}
var paramValue = templateSrv.highlightVariablesAsHtml(func.params[index]);
var $paramLink = $('<a ng-click="" class="graphite-func-param-link">' + paramValue + '</a>');
var $input = $(paramTemplate);
paramCountAtLink++;
$paramLink.appendTo(elem);
$input.appendTo(elem);
$input.blur(_.partial(inputBlur, index));
$input.keyup(inputKeyDown);
$input.keypress(_.partial(inputKeyPress, index));
$paramLink.click(_.partial(clickFuncParam, index));
if (funcDef.params[index].options) {
addTypeahead($input, index);
}
});
$('<span>)</span>').appendTo(elem);
$compile(elem.contents())($scope);
}
function ifJustAddedFocusFistParam() {
if ($scope.func.added) {
$scope.func.added = false;
setTimeout(function () {
elem.find('.graphite-func-param-link').first().click();
}, 10);
}
}
function registerFuncControlsToggle() {
$funcLink.click(toggleFuncControls);
}
function registerFuncControlsActions() {
$funcControls.click(function (e) {
var $target = $(e.target);
if ($target.hasClass('fa-remove')) {
toggleFuncControls();
$scope.$apply(function () {
ctrl.removeFunction($scope.func);
});
return;
}
if ($target.hasClass('fa-arrow-left')) {
$scope.$apply(function () {
_.move($scope.target.functions, $scope.$index, $scope.$index - 1);
ctrl.targetChanged();
});
return;
}
if ($target.hasClass('fa-arrow-right')) {
$scope.$apply(function () {
_.move($scope.target.functions, $scope.$index, $scope.$index + 1);
ctrl.targetChanged();
});
return;
}
if ($target.hasClass('fa-question-circle')) {
var docSite = "http://docs.grafana-zabbix.org/reference/functions/";
window.open(docSite + '#' + funcDef.name.toLowerCase(), '_blank');
return;
}
});
}
function relink() {
elem.children().remove();
addElementsAndCompile();
ifJustAddedFocusFistParam();
registerFuncControlsToggle();
registerFuncControlsActions();
}
relink();
}
};
});
}
};
});
//# sourceMappingURL=metric-function-editor.directive.js.map

File diff suppressed because one or more lines are too long

View File

@@ -0,0 +1,315 @@
'use strict';
System.register(['lodash', 'jquery'], function (_export, _context) {
"use strict";
var _, $, _createClass, index, categories, FuncInstance;
function _classCallCheck(instance, Constructor) {
if (!(instance instanceof Constructor)) {
throw new TypeError("Cannot call a class as a function");
}
}
function addFuncDef(funcDef) {
funcDef.params = funcDef.params || [];
funcDef.defaultParams = funcDef.defaultParams || [];
if (funcDef.category) {
categories[funcDef.category].push(funcDef);
}
index[funcDef.name] = funcDef;
index[funcDef.shortName || funcDef.name] = funcDef;
}
// Transform
function createFuncInstance(funcDef, params) {
if (_.isString(funcDef)) {
if (!index[funcDef]) {
throw { message: 'Method not found ' + name };
}
funcDef = index[funcDef];
}
return new FuncInstance(funcDef, params);
}
_export('createFuncInstance', createFuncInstance);
function getFuncDef(name) {
return index[name];
}
_export('getFuncDef', getFuncDef);
function getCategories() {
return categories;
}
_export('getCategories', getCategories);
return {
setters: [function (_lodash) {
_ = _lodash.default;
}, function (_jquery) {
$ = _jquery.default;
}],
execute: function () {
_createClass = function () {
function defineProperties(target, props) {
for (var i = 0; i < props.length; i++) {
var descriptor = props[i];
descriptor.enumerable = descriptor.enumerable || false;
descriptor.configurable = true;
if ("value" in descriptor) descriptor.writable = true;
Object.defineProperty(target, descriptor.key, descriptor);
}
}
return function (Constructor, protoProps, staticProps) {
if (protoProps) defineProperties(Constructor.prototype, protoProps);
if (staticProps) defineProperties(Constructor, staticProps);
return Constructor;
};
}();
index = [];
categories = {
Transform: [],
Aggregate: [],
Filter: [],
Trends: [],
Time: [],
Alias: []
};
addFuncDef({
name: 'groupBy',
category: 'Transform',
params: [{ name: 'interval', type: 'string' }, { name: 'function', type: 'string', options: ['avg', 'min', 'max', 'median'] }],
defaultParams: ['1m', 'avg']
});
addFuncDef({
name: 'scale',
category: 'Transform',
params: [{ name: 'factor', type: 'float', options: [100, 0.01, 10, -1] }],
defaultParams: [100]
});
addFuncDef({
name: 'delta',
category: 'Transform',
params: [],
defaultParams: []
});
// Aggregate
addFuncDef({
name: 'sumSeries',
category: 'Aggregate',
params: [],
defaultParams: []
});
addFuncDef({
name: 'median',
category: 'Aggregate',
params: [{ name: 'interval', type: 'string' }],
defaultParams: ['1m']
});
addFuncDef({
name: 'average',
category: 'Aggregate',
params: [{ name: 'interval', type: 'string' }],
defaultParams: ['1m']
});
addFuncDef({
name: 'min',
category: 'Aggregate',
params: [{ name: 'interval', type: 'string' }],
defaultParams: ['1m']
});
addFuncDef({
name: 'max',
category: 'Aggregate',
params: [{ name: 'interval', type: 'string' }],
defaultParams: ['1m']
});
addFuncDef({
name: 'aggregateBy',
category: 'Aggregate',
params: [{ name: 'interval', type: 'string' }, { name: 'function', type: 'string', options: ['avg', 'min', 'max', 'median'] }],
defaultParams: ['1m', 'avg']
});
// Filter
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']
});
// Trends
addFuncDef({
name: 'trendValue',
category: 'Trends',
params: [{ name: 'type', type: 'string', options: ['avg', 'min', 'max'] }],
defaultParams: ['avg']
});
// Time
addFuncDef({
name: 'timeShift',
category: 'Time',
params: [{ name: 'interval', type: 'string', options: ['24h', '7d', '1M', '+24h', '-24h'] }],
defaultParams: ['24h']
});
//Alias
addFuncDef({
name: 'setAlias',
category: 'Alias',
params: [{ name: 'alias', type: 'string' }],
defaultParams: []
});
addFuncDef({
name: 'setAliasByRegex',
category: 'Alias',
params: [{ name: 'aliasByRegex', type: 'string' }],
defaultParams: []
});
_.each(categories, function (funcList, catName) {
categories[catName] = _.sortBy(funcList, 'name');
});
FuncInstance = function () {
function FuncInstance(funcDef, params) {
_classCallCheck(this, FuncInstance);
this.def = funcDef;
if (params) {
this.params = params;
} else {
// Create with default params
this.params = [];
this.params = funcDef.defaultParams.slice(0);
}
this.updateText();
}
_createClass(FuncInstance, [{
key: 'bindFunction',
value: function bindFunction(metricFunctions) {
var func = metricFunctions[this.def.name];
if (func) {
// Bind function arguments
var bindedFunc = func;
var param;
for (var i = 0; i < this.params.length; i++) {
param = this.params[i];
// Convert numeric params
if (this.def.params[i].type === 'int' || this.def.params[i].type === 'float') {
param = Number(param);
}
bindedFunc = _.partial(bindedFunc, param);
}
return bindedFunc;
} else {
throw { message: 'Method not found ' + this.def.name };
}
}
}, {
key: 'render',
value: function render(metricExp) {
var str = this.def.name + '(';
var parameters = _.map(this.params, function (value, index) {
var paramType = this.def.params[index].type;
if (paramType === 'int' || paramType === 'float' || paramType === 'value_or_series' || paramType === 'boolean') {
return value;
} else if (paramType === 'int_or_interval' && $.isNumeric(value)) {
return value;
}
return "'" + value + "'";
}, this);
if (metricExp) {
parameters.unshift(metricExp);
}
return str + parameters.join(', ') + ')';
}
}, {
key: '_hasMultipleParamsInString',
value: function _hasMultipleParamsInString(strValue, index) {
if (strValue.indexOf(',') === -1) {
return false;
}
return this.def.params[index + 1] && this.def.params[index + 1].optional;
}
}, {
key: 'updateParam',
value: function updateParam(strValue, index) {
// handle optional parameters
// if string contains ',' and next param is optional, split and update both
if (this._hasMultipleParamsInString(strValue, index)) {
_.each(strValue.split(','), function (partVal, idx) {
this.updateParam(partVal.trim(), idx);
}, this);
return;
}
if (strValue === '' && this.def.params[index].optional) {
this.params.splice(index, 1);
} else {
this.params[index] = strValue;
}
this.updateText();
}
}, {
key: 'updateText',
value: function updateText() {
if (this.params.length === 0) {
this.text = this.def.name + '()';
return;
}
var text = this.def.name + '(';
text += this.params.join(', ');
text += ')';
this.text = text;
}
}]);
return FuncInstance;
}();
}
};
});
//# sourceMappingURL=metricFunctions.js.map

File diff suppressed because one or more lines are too long

55
dist/datasource-zabbix/migrations.js vendored Normal file
View File

@@ -0,0 +1,55 @@
"use strict";
System.register([], function (_export, _context) {
"use strict";
/**
* Query format migration.
* This module can detect query format version and make migration.
*/
function isGrafana2target(target) {
if (!target.mode || target.mode === 0 || target.mode === 2) {
if ((target.hostFilter || target.itemFilter || target.downsampleFunction || target.host && target.host.host) && target.item.filter === undefined && target.host.filter === undefined) {
return true;
} else {
return false;
}
} else {
return false;
}
}
_export("isGrafana2target", isGrafana2target);
function migrateFrom2To3version(target) {
target.group.filter = target.group.name === "*" ? "/.*/" : target.group.name;
target.host.filter = target.host.name === "*" ? convertToRegex(target.hostFilter) : target.host.name;
target.application.filter = target.application.name === "*" ? "" : target.application.name;
target.item.filter = target.item.name === "All" ? convertToRegex(target.itemFilter) : target.item.name;
return target;
}
_export("migrateFrom2To3version", migrateFrom2To3version);
function migrate(target) {
if (isGrafana2target(target)) {
return migrateFrom2To3version(target);
} else {
return target;
}
}
_export("migrate", migrate);
function convertToRegex(str) {
if (str) {
return '/' + str + '/';
} else {
return '/.*/';
}
}return {
setters: [],
execute: function () {}
};
});
//# sourceMappingURL=migrations.js.map

View File

@@ -0,0 +1 @@
{"version":3,"sources":["../../src/datasource-zabbix/migrations.js"],"names":["isGrafana2target","target","mode","hostFilter","itemFilter","downsampleFunction","host","item","filter","undefined","migrateFrom2To3version","group","name","convertToRegex","application","migrate","str"],"mappings":";;;;;AAAA;;;;;AAKO,WAASA,gBAAT,CAA0BC,MAA1B,EAAkC;AACvC,QAAI,CAACA,OAAOC,IAAR,IAAgBD,OAAOC,IAAP,KAAgB,CAAhC,IAAqCD,OAAOC,IAAP,KAAgB,CAAzD,EAA4D;AAC1D,UAAI,CAACD,OAAOE,UAAP,IAAqBF,OAAOG,UAA5B,IAA0CH,OAAOI,kBAAjD,IACAJ,OAAOK,IAAP,IAAeL,OAAOK,IAAP,CAAYA,IAD5B,KAECL,OAAOM,IAAP,CAAYC,MAAZ,KAAuBC,SAAvB,IAAoCR,OAAOK,IAAP,CAAYE,MAAZ,KAAuBC,SAFhE,EAE4E;AAC1E,eAAO,IAAP;AACD,OAJD,MAIO;AACL,eAAO,KAAP;AACD;AACF,KARD,MAQO;AACL,aAAO,KAAP;AACD;AACF;;8BAZeT,gB;;AAcT,WAASU,sBAAT,CAAgCT,MAAhC,EAAwC;AAC7CA,WAAOU,KAAP,CAAaH,MAAb,GAAsBP,OAAOU,KAAP,CAAaC,IAAb,KAAsB,GAAtB,GAA4B,MAA5B,GAAqCX,OAAOU,KAAP,CAAaC,IAAxE;AACAX,WAAOK,IAAP,CAAYE,MAAZ,GAAqBP,OAAOK,IAAP,CAAYM,IAAZ,KAAqB,GAArB,GAA2BC,eAAeZ,OAAOE,UAAtB,CAA3B,GAA+DF,OAAOK,IAAP,CAAYM,IAAhG;AACAX,WAAOa,WAAP,CAAmBN,MAAnB,GAA4BP,OAAOa,WAAP,CAAmBF,IAAnB,KAA4B,GAA5B,GAAkC,EAAlC,GAAuCX,OAAOa,WAAP,CAAmBF,IAAtF;AACAX,WAAOM,IAAP,CAAYC,MAAZ,GAAqBP,OAAOM,IAAP,CAAYK,IAAZ,KAAqB,KAArB,GAA6BC,eAAeZ,OAAOG,UAAtB,CAA7B,GAAiEH,OAAOM,IAAP,CAAYK,IAAlG;AACA,WAAOX,MAAP;AACD;oCANeS,sB;;AAQT,WAASK,OAAT,CAAiBd,MAAjB,EAAyB;AAC9B,QAAID,iBAAiBC,MAAjB,CAAJ,EAA8B;AAC5B,aAAOS,uBAAuBT,MAAvB,CAAP;AACD,KAFD,MAEO;AACL,aAAOA,MAAP;AACD;AACF;;qBANec,O;;AAQhB,WAASF,cAAT,CAAwBG,GAAxB,EAA6B;AAC3B,QAAIA,GAAJ,EAAS;AACP,aAAO,MAAMA,GAAN,GAAY,GAAnB;AACD,KAFD,MAEO;AACL,aAAO,MAAP;AACD;AACF,G","file":"migrations.js","sourcesContent":["/**\n * Query format migration.\n * This module can detect query format version and make migration.\n */\n\nexport function isGrafana2target(target) {\n if (!target.mode || target.mode === 0 || target.mode === 2) {\n if ((target.hostFilter || target.itemFilter || target.downsampleFunction ||\n (target.host && target.host.host)) &&\n (target.item.filter === undefined && target.host.filter === undefined)) {\n return true;\n } else {\n return false;\n }\n } else {\n return false;\n }\n}\n\nexport function migrateFrom2To3version(target) {\n target.group.filter = target.group.name === \"*\" ? \"/.*/\" : target.group.name;\n target.host.filter = target.host.name === \"*\" ? convertToRegex(target.hostFilter) : target.host.name;\n target.application.filter = target.application.name === \"*\" ? \"\" : target.application.name;\n target.item.filter = target.item.name === \"All\" ? convertToRegex(target.itemFilter) : target.item.name;\n return target;\n}\n\nexport function migrate(target) {\n if (isGrafana2target(target)) {\n return migrateFrom2To3version(target);\n } else {\n return target;\n }\n}\n\nfunction convertToRegex(str) {\n if (str) {\n return '/' + str + '/';\n } else {\n return '/.*/';\n }\n}\n"]}

51
dist/datasource-zabbix/module.js vendored Normal file
View File

@@ -0,0 +1,51 @@
'use strict';
System.register(['./datasource', './query.controller'], function (_export, _context) {
"use strict";
var ZabbixAPIDatasource, ZabbixQueryController, ZabbixConfigController, ZabbixQueryOptionsController, ZabbixAnnotationsQueryController;
function _classCallCheck(instance, Constructor) {
if (!(instance instanceof Constructor)) {
throw new TypeError("Cannot call a class as a function");
}
}
return {
setters: [function (_datasource) {
ZabbixAPIDatasource = _datasource.ZabbixAPIDatasource;
}, function (_queryController) {
ZabbixQueryController = _queryController.ZabbixQueryController;
}],
execute: function () {
_export('ConfigCtrl', ZabbixConfigController = function ZabbixConfigController() {
_classCallCheck(this, ZabbixConfigController);
});
ZabbixConfigController.templateUrl = 'datasource-zabbix/partials/config.html';
_export('QueryOptionsCtrl', ZabbixQueryOptionsController = function ZabbixQueryOptionsController() {
_classCallCheck(this, ZabbixQueryOptionsController);
});
ZabbixQueryOptionsController.templateUrl = 'datasource-zabbix/partials/query.options.html';
_export('AnnotationsQueryCtrl', ZabbixAnnotationsQueryController = function ZabbixAnnotationsQueryController() {
_classCallCheck(this, ZabbixAnnotationsQueryController);
});
ZabbixAnnotationsQueryController.templateUrl = 'datasource-zabbix/partials/annotations.editor.html';
_export('Datasource', ZabbixAPIDatasource);
_export('ConfigCtrl', ZabbixConfigController);
_export('QueryCtrl', ZabbixQueryController);
_export('QueryOptionsCtrl', ZabbixQueryOptionsController);
_export('AnnotationsQueryCtrl', ZabbixAnnotationsQueryController);
}
};
});
//# sourceMappingURL=module.js.map

1
dist/datasource-zabbix/module.js.map vendored Normal file
View File

@@ -0,0 +1 @@
{"version":3,"sources":["../../src/datasource-zabbix/module.js"],"names":["ZabbixAPIDatasource","ZabbixQueryController","ZabbixConfigController","templateUrl","ZabbixQueryOptionsController","ZabbixAnnotationsQueryController"],"mappings":";;;;;;;;;;;;;;;AAAQA,yB,eAAAA,mB;;AACAC,2B,oBAAAA,qB;;;4BAEFC,sB;;;;AACNA,6BAAuBC,WAAvB,GAAqC,wCAArC;;kCAEMC,4B;;;;AACNA,mCAA6BD,WAA7B,GAA2C,+CAA3C;;sCAEME,gC;;;;AACNA,uCAAiCF,WAAjC,GAA+C,oDAA/C;;4BAGEH,mB;;4BACAE,sB;;2BACAD,qB;;kCACAG,4B;;sCACAC,gC","file":"module.js","sourcesContent":["import {ZabbixAPIDatasource} from './datasource';\nimport {ZabbixQueryController} from './query.controller';\n\nclass ZabbixConfigController {}\nZabbixConfigController.templateUrl = 'datasource-zabbix/partials/config.html';\n\nclass ZabbixQueryOptionsController {}\nZabbixQueryOptionsController.templateUrl = 'datasource-zabbix/partials/query.options.html';\n\nclass ZabbixAnnotationsQueryController {}\nZabbixAnnotationsQueryController.templateUrl = 'datasource-zabbix/partials/annotations.editor.html';\n\nexport {\n ZabbixAPIDatasource as Datasource,\n ZabbixConfigController as ConfigCtrl,\n ZabbixQueryController as QueryCtrl,\n ZabbixQueryOptionsController as QueryOptionsCtrl,\n ZabbixAnnotationsQueryController as AnnotationsQueryCtrl\n};\n"]}

View File

@@ -0,0 +1,65 @@
<div class="gf-form-group">
<h6>Filter Triggers</h6>
<div class="gf-form-inline">
<div class="gf-form">
<span class="gf-form-label width-10">Group</span>
<input type="text"
class="gf-form-input max-width-16"
ng-model="ctrl.annotation.group">
</input>
</div>
<div class="gf-form">
<span class="gf-form-label width-10">Host</span>
<input type="text"
class="gf-form-input max-width-16"
ng-model="ctrl.annotation.host">
</input>
</div>
</div>
<div class="gf-form-inline">
<div class="gf-form">
<span class="gf-form-label width-10">Application</span>
<input type="text"
class="gf-form-input max-width-16"
ng-model="ctrl.annotation.application">
</input>
</div>
<div class="gf-form">
<span class="gf-form-label width-10">Trigger</span>
<input type="text"
class="gf-form-input max-width-16"
ng-model="ctrl.annotation.trigger">
</input>
</div>
</div>
</div>
<div class="gf-form-group">
<div class="gf-form">
<span class="gf-form-label width-10">Minimum severity</span>
<div class="gf-form-select-wrapper">
<select class="gf-form-input gf-size-auto"
ng-init='ctrl.annotation.minseverity = ctrl.annotation.minseverity || 0'
ng-model='ctrl.annotation.minseverity'
ng-options="v as k for (k, v) in {
'Not classified': 0,
'Information': 1,
'Warning': 2,
'Average': 3,
'High': 4,
'Disaster': 5
}"
ng-change="render()">
</select>
</div>
</div>
</div>
<div class="gf-form-group">
<h6>Options</h6>
<div class="gf-form">
<editor-checkbox text="Show OK events" model="ctrl.annotation.showOkEvents"></editor-checkbox>
<editor-checkbox text="Hide acknowledged events" model="ctrl.annotation.hideAcknowledged"></editor-checkbox>
<editor-checkbox text="Show hostname" model="ctrl.annotation.showHostname"></editor-checkbox>
</div>
</div>

View File

@@ -0,0 +1,60 @@
<datasource-http-settings current="ctrl.current">
</datasource-http-settings>
<div class="gf-form-group">
<h3 class="page-heading">Zabbix API details</h3>
<div class="gf-form">
<span class="gf-form-label width-7">
Username
</span>
<input class="gf-form-input max-width-21"
type="text"
ng-model='ctrl.current.jsonData.username'
placeholder="user"
required>
</input>
</div>
<div class="gf-form">
<span class="gf-form-label width-7">
Password
</span>
<input class="gf-form-input max-width-21"
type="password"
ng-model='ctrl.current.jsonData.password'
placeholder="password">
</input>
</div>
<div class="gf-form-inline">
<div class="gf-form">
<label class="gf-form-label width-7">Trends</label>
</div>
<gf-form-switch class="gf-form"
label="Enable"
checked="ctrl.current.jsonData.trends" switch-class="max-width-6">
</gf-form-switch>
<div class="gf-form" ng-if="ctrl.current.jsonData.trends">
<span class="gf-form-label width-7">
Use from
</span>
<input class="gf-form-input max-width-5"
type="text"
ng-model='ctrl.current.jsonData.trendsFrom'
placeholder="7d">
</input>
</div>
</div>
<div class="gf-form">
<span class="gf-form-label width-12">
Cache update interval
</span>
<input class="gf-form-input max-width-4"
type="text"
ng-model='ctrl.current.jsonData.cacheTTL'
placeholder="1h">
</input>
</div>
</div>

View File

@@ -0,0 +1,170 @@
<query-editor-row query-ctrl="ctrl" can-collapse="false">
<div class="gf-form-inline">
<div class="gf-form max-width-20">
<label class="gf-form-label width-7">Query Mode</label>
<div class="gf-form-select-wrapper max-width-20">
<select class="gf-form-input"
ng-change="ctrl.switchEditorMode(ctrl.target.mode)"
ng-model="ctrl.target.mode"
ng-options="v.mode as v.text for (k, v) in ctrl.editorModes">
</select>
</div>
</div>
<div class="gf-form gf-form--grow">
<div class="gf-form-label gf-form-label--grow"></div>
</div>
</div>
<!-- IT Service editor -->
<div class="gf-form-inline" ng-show="ctrl.target.mode == 1">
<div class="gf-form max-width-20">
<label class="gf-form-label query-keyword width-7">IT Service</label>
<div class="gf-form-select-wrapper max-width-20">
<select class="gf-form-input"
ng-change="ctrl.selectITService()"
ng-model="ctrl.target.itservice"
bs-tooltip="ctrl.target.itservice.name.length > 25 ? ctrl.target.itservice.name : ''"
ng-options="itservice.name for itservice in ctrl.itserviceList track by itservice.name">
<option value="">-- Select IT service --</option>
</select>
</div>
</div>
<div class="gf-form">
<label class="gf-form-label query-keyword">IT service property</label>
<div class="gf-form-select-wrapper">
<select class="gf-form-input"
ng-change="ctrl.selectITService()"
ng-model="ctrl.target.slaProperty"
ng-options="slaProperty.name for slaProperty in ctrl.slaPropertyList track by slaProperty.name">
<option value="">-- Property --</option>
</select>
</div>
</div>
<div class="gf-form gf-form--grow">
<div class="gf-form-label gf-form-label--grow"></div>
</div>
</div>
<div class="gf-form-inline" ng-hide="ctrl.target.mode == 1">
<!-- Select Group -->
<div class="gf-form max-width-20">
<label class="gf-form-label query-keyword width-7">Group</label>
<input type="text"
ng-model="ctrl.target.group.filter"
bs-typeahead="ctrl.getGroupNames"
ng-blur="ctrl.onTargetBlur()"
data-min-length=0
data-items=100
class="gf-form-input"
ng-class="{
'zbx-variable': ctrl.isVariable(ctrl.target.group.filter),
'zbx-regex': ctrl.isRegex(ctrl.target.group.filter)
}"></input>
</div>
<!-- Select Host -->
<div class="gf-form">
<label class="gf-form-label query-keyword width-7">Host</label>
<input type="text"
ng-model="ctrl.target.host.filter"
bs-typeahead="ctrl.getHostNames"
ng-blur="ctrl.onTargetBlur()"
data-min-length=0
data-items=100
class="gf-form-input"
ng-class="{
'zbx-variable': ctrl.isVariable(ctrl.target.host.filter),
'zbx-regex': ctrl.isRegex(ctrl.target.host.filter)
}">
</div>
<div class="gf-form gf-form--grow">
<div class="gf-form-label gf-form-label--grow"></div>
</div>
</div>
<div class="gf-form-inline" ng-hide="ctrl.target.mode == 1">
<!-- Select Application -->
<div class="gf-form max-width-20">
<label class="gf-form-label query-keyword width-7">Application</label>
<input type="text"
ng-model="ctrl.target.application.filter"
bs-typeahead="ctrl.getApplicationNames"
ng-blur="ctrl.onTargetBlur()"
data-min-length=0
data-items=100
class="gf-form-input"
ng-class="{
'zbx-variable': ctrl.isVariable(ctrl.target.application.filter),
'zbx-regex': ctrl.isRegex(ctrl.target.application.filter)
}">
</div>
<!-- Select Item -->
<div class="gf-form">
<label class="gf-form-label query-keyword width-7">Item</label>
<input type="text"
ng-model="ctrl.target.item.filter"
bs-typeahead="ctrl.getItemNames"
ng-blur="ctrl.onTargetBlur()"
data-min-length=0
data-items=100
class="gf-form-input"
ng-class="{
'zbx-variable': ctrl.isVariable(ctrl.target.item.filter),
'zbx-regex': ctrl.isRegex(ctrl.target.item.filter)
}">
</div>
<div class="gf-form gf-form--grow">
<label class="gf-form-label gf-form-label--grow">
<a ng-click="ctrl.toggleQueryOptions()">
<i class="fa fa-caret-down" ng-show="ctrl.showQueryOptions"></i>
<i class="fa fa-caret-right" ng-hide="ctrl.showQueryOptions"></i>
{{ctrl.queryOptionsText}}
</a>
</label>
</div>
</div>
<!-- Query options -->
<div class="gf-form-group" ng-if="ctrl.showQueryOptions">
<div class="gf-form offset-width-7">
<gf-form-switch class="gf-form" ng-hide="ctrl.target.mode == 2"
label="Show disabled items"
checked="ctrl.target.options.showDisabledItems"
on-change="ctrl.onQueryOptionChange()">
</gf-form-switch>
</div>
</div>
<!-- Metric processing functions -->
<div class="gf-form-inline" ng-hide="ctrl.target.mode">
<div class="gf-form">
<label class="gf-form-label query-keyword width-7">Functions</label>
<div ng-repeat="func in ctrl.target.functions" class="gf-form-label query-part" metric-function-editor></div>
</div>
<div class="gf-form dropdown" add-metric-function>
</div>
<div class="gf-form gf-form--grow">
<div class="gf-form-label gf-form-label--grow"></div>
</div>
</div>
<!-- Text mode options -->
<div class="gf-form-inline" ng-show="ctrl.target.mode == 2">
<!-- Text metric regex -->
<div class="gf-form max-width-20">
<label class="gf-form-label query-keyword width-7">Text filter</label>
<input type="text"
class="gf-form-input"
ng-model="ctrl.target.textFilter"
spellcheck='false'
placeholder="Text filter (regex)"
ng-blur="ctrl.onTargetBlur()">
</div>
<gf-form-switch class="gf-form" label="Use capture groups" checked="ctrl.target.useCaptureGroups" on-change="ctrl.onTargetBlur()">
</gf-form-switch>
</div>
</query-editor-row>

View File

@@ -0,0 +1,88 @@
<section class="grafana-metric-options gf-form-group">
<div class="gf-form-inline">
<div class="gf-form max-width-15">
<span class="gf-form-label">Max data points</span>
<input type="text"
class="gf-form-input"
ng-model="ctrl.panelCtrl.panel.maxDataPoints"
bs-tooltip="'Override max data points, automatically set to graph width in pixels.'"
data-placement="right"
ng-model-onblur ng-change="ctrl.panelCtrl.refresh()"
spellcheck='false'
placeholder="auto">
</input>
</div>
</div>
<div class="gf-form-inline">
<div class="gf-form">
<span class="gf-form-label width-10">
<i class="fa fa-info-circle"></i>
<a ng-click="ctrl.panelCtrl.toggleEditorHelp(1);" bs-tooltip="'click to show helpful info'" data-placement="bottom">
Max data points
</a>
</span>
<span class="gf-form-label width-10">
<i class="fa fa-info-circle"></i>
<a ng-click="ctrl.panelCtrl.toggleEditorHelp(2);" bs-tooltip="'click to show helpful info'" data-placement="bottom">
IT services
</a>
</span>
<span class="gf-form-label width-12">
<i class="fa fa-info-circle"></i>
<a ng-click="ctrl.panelCtrl.toggleEditorHelp(3)" bs-tooltip="'click to show helpful info'" data-placement="bottom">
IT service property
</a>
</span>
<span class="gf-form-label width-8">
<i class="fa fa-info-circle"></i>
<a ng-click="ctrl.panelCtrl.toggleEditorHelp(4)" bs-tooltip="'click to show helpful info'" data-placement="bottom">
Text filter
</a>
</span>
</div>
</div>
</section>
<div class="editor-row">
<div class="pull-left">
<div class="grafana-info-box span8" ng-if="ctrl.panelCtrl.editorHelpIndex === 1">
<h5>Max data points</h5>
<ul>
<li>Grafana-Zabbix plugin uses maxDataPoints parameter to consolidate the real number of values down to this
number
</li>
<li>If there are more real values, then by default they will be consolidated using averages</li>
<li>This could hide real peaks and max values in your series</li>
<li>Point consolidation will effect series legend values (min,max,total,current)</li>
<li>If you override maxDataPoint and set a high value performance can be severely effected</li>
</ul>
</div>
<div class="grafana-info-box span8" ng-if="ctrl.panelCtrl.editorHelpIndex === 2">
<h5>IT services</h5>
<ul>
<li>Select "IT services" in targets menu to activate IT services mode.</li>
</ul>
</div>
<div class="grafana-info-box span8" ng-if="ctrl.panelCtrl.editorHelpIndex === 3">
<h5>IT service property</h5>
<ul>
<li>Zabbix returns the following availability information about IT service</li>
<li>Status - current status of the IT service</li>
<li>SLA - SLA for the given time interval</li>
<li>OK time - time the service was in OK state, in seconds</li>
<li>Problem time - time the service was in problem state, in seconds</li>
<li>Down time - time the service was in scheduled downtime, in seconds</li>
</ul>
</div>
<div class="grafana-info-box span8" ng-if="ctrl.panelCtrl.editorHelpIndex === 4">
<h5>Text filter</h5>
<ul>
<li>Use regex to extract a part of the returned value.</li>
</ul>
</div>
</div>
</div>

19
dist/datasource-zabbix/plugin.json vendored Normal file
View File

@@ -0,0 +1,19 @@
{
"type": "datasource",
"name": "Zabbix",
"id": "alexanderzobnin-zabbix-datasource",
"metrics": true,
"annotations": true,
"info": {
"author": {
"name": "Alexander Zobnin",
"url": "https://github.com/alexanderzobnin/grafana-zabbix"
},
"logos": {
"small": "img/zabbix_app_logo.svg",
"large": "img/zabbix_app_logo.svg"
}
}
}

View File

@@ -0,0 +1,376 @@
'use strict';
System.register(['app/plugins/sdk', 'angular', 'lodash', './utils', './metricFunctions', './migrations', './add-metric-function.directive', './metric-function-editor.directive', './css/query-editor.css!'], function (_export, _context) {
"use strict";
var QueryCtrl, angular, _, utils, metricFunctions, migrations, _createClass, ZabbixQueryController;
function _classCallCheck(instance, Constructor) {
if (!(instance instanceof Constructor)) {
throw new TypeError("Cannot call a class as a function");
}
}
function _possibleConstructorReturn(self, call) {
if (!self) {
throw new ReferenceError("this hasn't been initialised - super() hasn't been called");
}
return call && (typeof call === "object" || typeof call === "function") ? call : self;
}
function _inherits(subClass, superClass) {
if (typeof superClass !== "function" && superClass !== null) {
throw new TypeError("Super expression must either be null or a function, not " + typeof superClass);
}
subClass.prototype = Object.create(superClass && superClass.prototype, {
constructor: {
value: subClass,
enumerable: false,
writable: true,
configurable: true
}
});
if (superClass) Object.setPrototypeOf ? Object.setPrototypeOf(subClass, superClass) : subClass.__proto__ = superClass;
}
// Get list of metric names for bs-typeahead directive
function getMetricNames(scope, metricList) {
return _.uniq(_.map(scope.metric[metricList], 'name'));
}
return {
setters: [function (_appPluginsSdk) {
QueryCtrl = _appPluginsSdk.QueryCtrl;
}, function (_angular) {
angular = _angular.default;
}, function (_lodash) {
_ = _lodash.default;
}, function (_utils) {
utils = _utils;
}, function (_metricFunctions) {
metricFunctions = _metricFunctions;
}, function (_migrations) {
migrations = _migrations;
}, function (_addMetricFunctionDirective) {}, function (_metricFunctionEditorDirective) {}, function (_cssQueryEditorCss) {}],
execute: function () {
_createClass = function () {
function defineProperties(target, props) {
for (var i = 0; i < props.length; i++) {
var descriptor = props[i];
descriptor.enumerable = descriptor.enumerable || false;
descriptor.configurable = true;
if ("value" in descriptor) descriptor.writable = true;
Object.defineProperty(target, descriptor.key, descriptor);
}
}
return function (Constructor, protoProps, staticProps) {
if (protoProps) defineProperties(Constructor.prototype, protoProps);
if (staticProps) defineProperties(Constructor, staticProps);
return Constructor;
};
}();
_export('ZabbixQueryController', ZabbixQueryController = function (_QueryCtrl) {
_inherits(ZabbixQueryController, _QueryCtrl);
// ZabbixQueryCtrl constructor
function ZabbixQueryController($scope, $injector, $rootScope, $sce, templateSrv) {
_classCallCheck(this, ZabbixQueryController);
var _this = _possibleConstructorReturn(this, (ZabbixQueryController.__proto__ || Object.getPrototypeOf(ZabbixQueryController)).call(this, $scope, $injector));
_this.zabbix = _this.datasource.zabbix;
// Use custom format for template variables
_this.replaceTemplateVars = _this.datasource.replaceTemplateVars;
_this.templateSrv = templateSrv;
_this.editorModes = {
0: { value: 'num', text: 'Metrics', mode: 0 },
1: { value: 'itservice', text: 'IT Services', mode: 1 },
2: { value: 'text', text: 'Text', mode: 2 }
};
// Map functions for bs-typeahead
_this.getGroupNames = _.partial(getMetricNames, _this, 'groupList');
_this.getHostNames = _.partial(getMetricNames, _this, 'hostList');
_this.getApplicationNames = _.partial(getMetricNames, _this, 'appList');
_this.getItemNames = _.partial(getMetricNames, _this, 'itemList');
// Update metric suggestion when template variable was changed
$rootScope.$on('template-variable-value-updated', function () {
return _this.onVariableChange();
});
// Update metrics when item selected from dropdown
$scope.$on('typeahead-updated', function () {
_this.onTargetBlur();
});
_this.init = function () {
var target = this.target;
// Migrate old targets
target = migrations.migrate(target);
var scopeDefaults = {
metric: {},
oldTarget: _.cloneDeep(this.target),
queryOptionsText: this.renderQueryOptionsText()
};
_.defaults(this, scopeDefaults);
// Load default values
var targetDefaults = {
mode: 0,
group: { filter: "" },
host: { filter: "" },
application: { filter: "" },
item: { filter: "" },
functions: [],
options: {
showDisabledItems: false
}
};
_.defaults(target, targetDefaults);
// Create function instances from saved JSON
target.functions = _.map(target.functions, function (func) {
return metricFunctions.createFuncInstance(func.def, func.params);
});
if (target.mode === 0 || target.mode === 2) {
this.downsampleFunctionList = [{ name: "avg", value: "avg" }, { name: "min", value: "min" }, { name: "max", value: "max" }];
this.initFilters();
} else if (target.mode === 1) {
this.slaPropertyList = [{ name: "Status", property: "status" }, { name: "SLA", property: "sla" }, { name: "OK time", property: "okTime" }, { name: "Problem time", property: "problemTime" }, { name: "Down time", property: "downtimeTime" }];
this.itserviceList = [{ name: "test" }];
this.updateITServiceList();
}
};
_this.init();
return _this;
}
_createClass(ZabbixQueryController, [{
key: 'initFilters',
value: function initFilters() {
var itemtype = this.editorModes[this.target.mode].value;
return Promise.all([this.suggestGroups(), this.suggestHosts(), this.suggestApps(), this.suggestItems(itemtype)]);
}
}, {
key: 'suggestGroups',
value: function suggestGroups() {
var _this2 = this;
return this.zabbix.getAllGroups().then(function (groups) {
_this2.metric.groupList = groups;
return groups;
});
}
}, {
key: 'suggestHosts',
value: function suggestHosts() {
var _this3 = this;
var groupFilter = this.replaceTemplateVars(this.target.group.filter);
return this.zabbix.getAllHosts(groupFilter).then(function (hosts) {
_this3.metric.hostList = hosts;
return hosts;
});
}
}, {
key: 'suggestApps',
value: function suggestApps() {
var _this4 = this;
var groupFilter = this.replaceTemplateVars(this.target.group.filter);
var hostFilter = this.replaceTemplateVars(this.target.host.filter);
return this.zabbix.getAllApps(groupFilter, hostFilter).then(function (apps) {
_this4.metric.appList = apps;
return apps;
});
}
}, {
key: 'suggestItems',
value: function suggestItems() {
var _this5 = this;
var itemtype = arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : 'num';
var groupFilter = this.replaceTemplateVars(this.target.group.filter);
var hostFilter = this.replaceTemplateVars(this.target.host.filter);
var appFilter = this.replaceTemplateVars(this.target.application.filter);
var options = {
itemtype: itemtype,
showDisabledItems: this.target.options.showDisabledItems
};
return this.zabbix.getAllItems(groupFilter, hostFilter, appFilter, options).then(function (items) {
_this5.metric.itemList = items;
return items;
});
}
}, {
key: 'isRegex',
value: function isRegex(str) {
return utils.isRegex(str);
}
}, {
key: 'isVariable',
value: function isVariable(str) {
return utils.isTemplateVariable(str, this.templateSrv.variables);
}
}, {
key: 'onTargetBlur',
value: function onTargetBlur() {
var newTarget = _.cloneDeep(this.target);
if (!_.isEqual(this.oldTarget, this.target)) {
this.oldTarget = newTarget;
this.targetChanged();
}
}
}, {
key: 'onVariableChange',
value: function onVariableChange() {
if (this.isContainsVariables()) {
this.targetChanged();
}
}
}, {
key: 'isContainsVariables',
value: function isContainsVariables() {
var _this6 = this;
return _.some(['group', 'host', 'application'], function (field) {
if (_this6.target[field] && _this6.target[field].filter) {
return utils.isTemplateVariable(_this6.target[field].filter, _this6.templateSrv.variables);
} else {
return false;
}
});
}
}, {
key: 'parseTarget',
value: function parseTarget() {}
// Parse target
// Validate target and set validation info
}, {
key: 'validateTarget',
value: function validateTarget() {
// validate
}
}, {
key: 'targetChanged',
value: function targetChanged() {
this.initFilters();
this.parseTarget();
this.panelCtrl.refresh();
}
}, {
key: 'addFunction',
value: function addFunction(funcDef) {
var newFunc = metricFunctions.createFuncInstance(funcDef);
newFunc.added = true;
this.target.functions.push(newFunc);
this.moveAliasFuncLast();
if (newFunc.params.length && newFunc.added || newFunc.def.params.length === 0) {
this.targetChanged();
}
}
}, {
key: 'removeFunction',
value: function removeFunction(func) {
this.target.functions = _.without(this.target.functions, func);
this.targetChanged();
}
}, {
key: 'moveAliasFuncLast',
value: function moveAliasFuncLast() {
var aliasFunc = _.find(this.target.functions, function (func) {
return func.def.name === 'alias' || func.def.name === 'aliasByNode' || func.def.name === 'aliasByMetric';
});
if (aliasFunc) {
this.target.functions = _.without(this.target.functions, aliasFunc);
this.target.functions.push(aliasFunc);
}
}
}, {
key: 'toggleQueryOptions',
value: function toggleQueryOptions() {
this.showQueryOptions = !this.showQueryOptions;
}
}, {
key: 'onQueryOptionChange',
value: function onQueryOptionChange() {
this.queryOptionsText = this.renderQueryOptionsText();
this.onTargetBlur();
}
}, {
key: 'renderQueryOptionsText',
value: function renderQueryOptionsText() {
var optionsMap = {
showDisabledItems: "Show disabled items"
};
var options = [];
_.forOwn(this.target.options, function (value, key) {
if (value) {
if (value === true) {
// Show only option name (if enabled) for boolean options
options.push(optionsMap[key]);
} else {
// Show "option = value" for another options
options.push(optionsMap[key] + " = " + value);
}
}
});
return "Options: " + options.join(', ');
}
}, {
key: 'switchEditorMode',
value: function switchEditorMode(mode) {
this.target.mode = mode;
this.init();
}
}, {
key: 'updateITServiceList',
value: function updateITServiceList() {
var _this7 = this;
this.zabbix.getITService().then(function (iteservices) {
_this7.itserviceList = [];
_this7.itserviceList = _this7.itserviceList.concat(iteservices);
});
}
}, {
key: 'selectITService',
value: function selectITService() {
if (!_.isEqual(this.oldTarget, this.target) && _.isEmpty(this.target.errors)) {
this.oldTarget = angular.copy(this.target);
this.panelCtrl.refresh();
}
}
}]);
return ZabbixQueryController;
}(QueryCtrl));
_export('ZabbixQueryController', ZabbixQueryController);
// Set templateUrl as static property
ZabbixQueryController.templateUrl = 'datasource-zabbix/partials/query.editor.html';
}
};
});
//# sourceMappingURL=query.controller.js.map

File diff suppressed because one or more lines are too long

View File

@@ -0,0 +1,113 @@
'use strict';
System.register(['lodash'], function (_export, _context) {
"use strict";
var _;
/**
* 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) {
var addHostName = arguments.length > 2 && arguments[2] !== undefined ? arguments[2] : true;
return convertHistory(history, items, addHostName, convertHistoryPoint);
}function handleTrends(history, items, valueType) {
var addHostName = arguments.length > 3 && arguments[3] !== undefined ? arguments[3] : 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];
}return {
setters: [function (_lodash) {
_ = _lodash.default;
}],
execute: function () {
_export('default', {
handleHistory: handleHistory,
convertHistory: convertHistory,
handleTrends: handleTrends,
handleSLAResponse: handleSLAResponse
});
// Fix for backward compatibility with lodash 2.4
if (!_.uniqBy) {
_.uniqBy = _.uniq;
}
}
};
});
//# sourceMappingURL=responseHandler.js.map

File diff suppressed because one or more lines are too long

View File

@@ -0,0 +1,230 @@
import {Datasource} from "../module";
import {zabbixTemplateFormat} from "../datasource";
import Q from "q";
import sinon from 'sinon';
import _ from 'lodash';
describe('ZabbixDatasource', () => {
let ctx = {};
let defined = sinon.match.defined;
beforeEach(() => {
ctx.instanceSettings = {
jsonData: {
username: 'zabbix',
password: 'zabbix',
trends: true,
trendsFrom: '7d'
}
};
ctx.templateSrv = {};
ctx.alertSrv = {};
ctx.zabbix = () => {};
ctx.ds = new Datasource(ctx.instanceSettings, ctx.templateSrv, ctx.alertSrv, ctx.zabbix);
});
describe('When querying data', () => {
beforeEach(() => {
ctx.ds.replaceTemplateVars = (str) => str;
});
ctx.options = {
targets: [
{
group: {filter: ""},
host: {filter: ""},
application: {filter: ""},
item: {filter: ""}
}
],
range: {from: 'now-7d', to: 'now'}
};
it('should return an empty array when no targets are set', (done) => {
let options = {
targets: [],
range: {from: 'now-6h', to: 'now'}
};
ctx.ds.query(options).then(result => {
expect(result.data).to.have.length(0);
done();
});
});
it('should use trends if it enabled and time more than trendsFrom', (done) => {
let ranges = ['now-7d', 'now-168h', 'now-1M', 'now-1y'];
_.forEach(ranges, range => {
ctx.options.range.from = range;
ctx.ds.queryNumericData = sinon.spy();
ctx.ds.query(ctx.options);
// Check that useTrends options is true
expect(ctx.ds.queryNumericData)
.to.have.been.calledWith(defined, defined, defined, true);
});
done();
});
it('shouldnt use trends if it enabled and time less than trendsFrom', (done) => {
let ranges = ['now-6d', 'now-167h', 'now-1h', 'now-30m', 'now-30s'];
_.forEach(ranges, range => {
ctx.options.range.from = range;
ctx.ds.queryNumericData = sinon.spy();
ctx.ds.query(ctx.options);
// Check that useTrends options is false
expect(ctx.ds.queryNumericData)
.to.have.been.calledWith(defined, defined, defined, false);
});
done();
});
});
describe('When replacing template variables', () => {
function testReplacingVariable(target, varValue, expectedResult, done) {
ctx.ds.templateSrv.replace = () => {
return zabbixTemplateFormat(varValue);
};
let result = ctx.ds.replaceTemplateVars(target);
expect(result).to.equal(expectedResult);
done();
}
/*
* Alphanumerics, spaces, dots, dashes and underscores
* are allowed in Zabbix host name.
* 'AaBbCc0123 .-_'
*/
it('should return properly escaped regex', (done) => {
let target = '$host';
let template_var_value = 'AaBbCc0123 .-_';
let expected_result = '/^AaBbCc0123 \\.-_$/';
testReplacingVariable(target, template_var_value, expected_result, done);
});
/*
* Single-value variable
* $host = backend01
* $host => /^backend01|backend01$/
*/
it('should return proper regex for single value', (done) => {
let target = '$host';
let template_var_value = 'backend01';
let expected_result = '/^backend01$/';
testReplacingVariable(target, template_var_value, expected_result, done);
});
/*
* Multi-value variable
* $host = [backend01, backend02]
* $host => /^(backend01|backend01)$/
*/
it('should return proper regex for multi-value', (done) => {
let target = '$host';
let template_var_value = ['backend01', 'backend02'];
let expected_result = '/^(backend01|backend02)$/';
testReplacingVariable(target, template_var_value, expected_result, done);
});
});
describe('When invoking metricFindQuery()', () => {
beforeEach(() => {
ctx.ds.replaceTemplateVars = (str) => str;
ctx.ds.zabbix = {
getGroups: () => Q.when([]),
getHosts: () => Q.when([]),
getApps: () => Q.when([]),
getItems: () => Q.when([])
};
});
it('should return groups', (done) => {
const tests = [
{query: '*', expect: '/.*/'},
{query: '', expect: ''},
{query: 'Backend', expect: 'Backend'},
{query: 'Back*', expect: 'Back*'}
];
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);
getGroups.reset();
}
done();
});
it('should return hosts', (done) => {
const tests = [
{query: '*.*', expect: '/.*/'},
{query: '.', expect: ''},
{query: 'Backend.*', expect: 'Backend'},
{query: 'Back*.', expect: 'Back*'}
];
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);
getHosts.reset();
}
done();
});
it('should return applications', (done) => {
const tests = [
{query: '*.*.*', expect: ['/.*/', '/.*/']},
{query: '.*.', expect: ['', '/.*/']},
{query: 'Backend.backend01.*', expect: ['Backend', 'backend01']},
{query: 'Back*.*.', expect: ['Back*', '/.*/']}
];
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]);
getApps.reset();
}
done();
});
it('should return items', (done) => {
const tests = [
{query: '*.*.*.*', expect: ['/.*/', '/.*/', '']},
{query: '.*.*.*', expect: ['', '/.*/', '']},
{query: 'Backend.backend01.*.*', expect: ['Backend', 'backend01', '']},
{query: 'Back*.*.cpu.*', expect: ['Back*', '/.*/', 'cpu']}
];
let getItems = sinon.spy(ctx.ds.zabbix, 'getItems');
for (const test of tests) {
ctx.ds.metricFindQuery(test.query);
expect(getItems)
.to.have.been.calledWith(test.expect[0], test.expect[1], test.expect[2]);
getItems.reset();
}
done();
});
it('should invoke method with proper arguments', (done) => {
let query = '*.*';
let getHosts = sinon.spy(ctx.ds.zabbix, 'getHosts');
ctx.ds.metricFindQuery(query);
expect(getHosts).to.have.been.calledWith('/.*/');
done();
});
});
});

View File

@@ -0,0 +1,111 @@
import _ from 'lodash';
import moment from 'moment';
var units = ['y', 'M', 'w', 'd', 'h', 'm', 's'];
export function parse(text, roundUp) {
if (!text) { return undefined; }
if (moment.isMoment(text)) { return text; }
if (_.isDate(text)) { return moment(text); }
var time;
var mathString = '';
var index;
var parseString;
if (text.substring(0, 3) === 'now') {
time = moment();
mathString = text.substring('now'.length);
} else {
index = text.indexOf('||');
if (index === -1) {
parseString = text;
mathString = ''; // nothing else
} else {
parseString = text.substring(0, index);
mathString = text.substring(index + 2);
}
// We're going to just require ISO8601 timestamps, k?
time = moment(parseString, moment.ISO_8601);
}
if (!mathString.length) {
return time;
}
return parseDateMath(mathString, time, roundUp);
}
export function isValid(text) {
var date = parse(text);
if (!date) {
return false;
}
if (moment.isMoment(date)) {
return date.isValid();
}
return false;
}
export function parseDateMath(mathString, time, roundUp) {
var dateTime = time;
var i = 0;
var len = mathString.length;
while (i < len) {
var c = mathString.charAt(i++);
var type;
var num;
var unit;
if (c === '/') {
type = 0;
} else if (c === '+') {
type = 1;
} else if (c === '-') {
type = 2;
} else {
return undefined;
}
if (isNaN(mathString.charAt(i))) {
num = 1;
} else if (mathString.length === 2) {
num = mathString.charAt(i);
} else {
var numFrom = i;
while (!isNaN(mathString.charAt(i))) {
i++;
if (i > 10) { return undefined; }
}
num = parseInt(mathString.substring(numFrom, i), 10);
}
if (type === 0) {
// rounding is only allowed on whole, single, units (eg M or 1M, not 0.5M or 2M)
if (num !== 1) {
return undefined;
}
}
unit = mathString.charAt(i++);
if (!_.includes(units, unit)) {
return undefined;
} else {
if (type === 0) {
if (roundUp) {
dateTime.endOf(unit);
} else {
dateTime.startOf(unit);
}
} else if (type === 1) {
dateTime.add(num, unit);
} else if (type === 2) {
dateTime.subtract(num, unit);
}
}
}
return dateTime;
}

View File

@@ -0,0 +1,49 @@
// JSHint options
/* globals global: false */
import prunk from 'prunk';
import {jsdom} from 'jsdom';
import chai from 'chai';
// import sinon from 'sinon';
import sinonChai from 'sinon-chai';
import * as dateMath from './modules/datemath';
// Mock angular module
var angularMocks = {
module: function() {
return {
directive: function() {},
service: function() {},
factory: function() {}
};
}
};
var datemathMock = {
parse: dateMath.parse,
parseDateMath: dateMath.parseDateMath,
isValid: dateMath.isValid
};
// Mock Grafana modules that are not available outside of the core project
// Required for loading module.js
prunk.mock('./css/query-editor.css!', 'no css, dude.');
prunk.mock('app/plugins/sdk', {
QueryCtrl: null
});
prunk.mock('app/core/utils/datemath', datemathMock);
prunk.mock('angular', angularMocks);
prunk.mock('jquery', 'module not found');
// Setup jsdom
// Required for loading angularjs
global.document = jsdom('<html><head><script></script></head><body></body></html>');
global.window = global.document.parentWindow;
global.navigator = window.navigator = {};
global.Node = window.Node;
// Setup Chai
chai.should();
chai.use(sinonChai);
global.assert = chai.assert;
global.expect = chai.expect;

169
dist/datasource-zabbix/utils.js vendored Normal file
View File

@@ -0,0 +1,169 @@
'use strict';
System.register(['lodash', 'moment'], function (_export, _context) {
"use strict";
var _, moment, regexPattern;
/**
* Expand Zabbix item name
*
* @param {string} name item name, ie "CPU $2 time"
* @param {string} key item key, ie system.cpu.util[,system,avg1]
* @return {string} expanded name, ie "CPU system time"
*/
function expandItemName(name, key) {
// extract params from key:
// "system.cpu.util[,system,avg1]" --> ["", "system", "avg1"]
var key_params = key.substring(key.indexOf('[') + 1, key.lastIndexOf(']')).split(',');
// replace item parameters
for (var i = key_params.length; i >= 1; i--) {
name = name.replace('$' + i, key_params[i - 1]);
}
return name;
}
// Pattern for testing regex
_export('expandItemName', expandItemName);
function isRegex(str) {
return regexPattern.test(str);
}
_export('isRegex', isRegex);
function isTemplateVariable(str, templateVariables) {
var variablePattern = /^\$\w+/;
if (variablePattern.test(str)) {
var variables = _.map(templateVariables, function (variable) {
return '$' + variable.name;
});
return _.includes(variables, str);
} else {
return false;
}
}
_export('isTemplateVariable', isTemplateVariable);
function buildRegex(str) {
var matches = str.match(regexPattern);
var pattern = matches[1];
var flags = matches[2] !== "" ? matches[2] : undefined;
return new RegExp(pattern, flags);
}
// Need for template variables replace
// From Grafana's templateSrv.js
_export('buildRegex', buildRegex);
function escapeRegex(value) {
return value.replace(/[\\^$*+?.()|[\]{}\/]/g, '\\$&');
}
_export('escapeRegex', escapeRegex);
function parseInterval(interval) {
var intervalPattern = /(^[\d]+)(y|M|w|d|h|m|s)/g;
var momentInterval = intervalPattern.exec(interval);
return moment.duration(Number(momentInterval[1]), momentInterval[2]).valueOf();
}
_export('parseInterval', parseInterval);
function parseTimeShiftInterval(interval) {
var intervalPattern = /^([\+\-]*)([\d]+)(y|M|w|d|h|m|s)/g;
var momentInterval = intervalPattern.exec(interval);
var duration = 0;
if (momentInterval[1] === '+') {
duration = 0 - moment.duration(Number(momentInterval[2]), momentInterval[3]).valueOf();
} else {
duration = moment.duration(Number(momentInterval[2]), momentInterval[3]).valueOf();
}
return duration;
}
/**
* Format acknowledges.
*
* @param {array} acknowledges array of Zabbix acknowledge objects
* @return {string} HTML-formatted table
*/
_export('parseTimeShiftInterval', parseTimeShiftInterval);
function formatAcknowledges(acknowledges) {
if (acknowledges.length) {
var formatted_acknowledges = '<br><br>Acknowledges:<br><table><tr><td><b>Time</b></td>' + '<td><b>User</b></td><td><b>Comments</b></td></tr>';
_.each(_.map(acknowledges, function (ack) {
var timestamp = moment.unix(ack.clock);
return '<tr><td><i>' + timestamp.format("DD MMM YYYY HH:mm:ss") + '</i></td><td>' + ack.alias + ' (' + ack.name + ' ' + ack.surname + ')' + '</td><td>' + ack.message + '</td></tr>';
}), function (ack) {
formatted_acknowledges = formatted_acknowledges.concat(ack);
});
formatted_acknowledges = formatted_acknowledges.concat('</table>');
return formatted_acknowledges;
} else {
return '';
}
}
_export('formatAcknowledges', formatAcknowledges);
function convertToZabbixAPIUrl(url) {
var zabbixAPIUrlPattern = /.*api_jsonrpc.php$/;
var trimSlashPattern = /(.*?)[\/]*$/;
if (url.match(zabbixAPIUrlPattern)) {
return url;
} else {
return url.replace(trimSlashPattern, "$1");
}
}
/**
* Wrap function to prevent multiple calls
* when waiting for result.
*/
_export('convertToZabbixAPIUrl', convertToZabbixAPIUrl);
function callOnce(func, promiseKeeper) {
return function () {
if (!promiseKeeper) {
promiseKeeper = Promise.resolve(func.apply(this, arguments).then(function (result) {
promiseKeeper = null;
return result;
}));
}
return promiseKeeper;
};
}
// Fix for backward compatibility with lodash 2.4
_export('callOnce', callOnce);
return {
setters: [function (_lodash) {
_ = _lodash.default;
}, function (_moment) {
moment = _moment.default;
}],
execute: function () {
_export('regexPattern', regexPattern = /^\/(.*)\/([gmi]*)$/m);
_export('regexPattern', regexPattern);
if (!_.includes) {
_.includes = _.contains;
}
}
};
});
//# sourceMappingURL=utils.js.map

1
dist/datasource-zabbix/utils.js.map vendored Normal file

File diff suppressed because one or more lines are too long

278
dist/datasource-zabbix/zabbix.js vendored Normal file
View File

@@ -0,0 +1,278 @@
'use strict';
System.register(['angular', 'lodash', './utils', './zabbixAPI.service.js', './zabbixCachingProxy.service.js'], function (_export, _context) {
"use strict";
var angular, _, utils, _createClass;
function _toConsumableArray(arr) {
if (Array.isArray(arr)) {
for (var i = 0, arr2 = Array(arr.length); i < arr.length; i++) {
arr2[i] = arr[i];
}
return arr2;
} else {
return Array.from(arr);
}
}
function _classCallCheck(instance, Constructor) {
if (!(instance instanceof Constructor)) {
throw new TypeError("Cannot call a class as a function");
}
}
// 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) {
var Zabbix = function () {
function Zabbix(url, username, password, basicAuth, withCredentials, cacheTTL) {
_classCallCheck(this, Zabbix);
// Initialize Zabbix API
var ZabbixAPI = zabbixAPIService;
this.zabbixAPI = new ZabbixAPI(url, username, password, basicAuth, withCredentials);
// Initialize caching proxy for requests
var 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.getITService = this.zabbixAPI.getITService.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);
}
_createClass(Zabbix, [{
key: 'getItemsFromTarget',
value: function getItemsFromTarget(target, options) {
var parts = ['group', 'host', 'application', 'item'];
var filters = _.map(parts, function (p) {
return target[p].filter;
});
return this.getItems.apply(this, _toConsumableArray(filters).concat([options]));
}
}, {
key: 'getAllGroups',
value: function getAllGroups() {
return this.cachingProxy.getGroups();
}
}, {
key: 'getGroups',
value: function getGroups(groupFilter) {
return this.getAllGroups().then(function (groups) {
return findByFilter(groups, groupFilter);
});
}
}, {
key: 'getAllHosts',
value: function getAllHosts(groupFilter) {
var _this = this;
return this.getGroups(groupFilter).then(function (groups) {
var groupids = _.map(groups, 'groupid');
return _this.cachingProxy.getHosts(groupids);
});
}
}, {
key: 'getHosts',
value: function getHosts(groupFilter, hostFilter) {
return this.getAllHosts(groupFilter).then(function (hosts) {
return findByFilter(hosts, hostFilter);
});
}
}, {
key: 'getAllApps',
value: function getAllApps(groupFilter, hostFilter) {
var _this2 = this;
return this.getHosts(groupFilter, hostFilter).then(function (hosts) {
var hostids = _.map(hosts, 'hostid');
return _this2.cachingProxy.getApps(hostids);
});
}
}, {
key: 'getApps',
value: function getApps(groupFilter, hostFilter, appFilter) {
var _this3 = this;
return this.getHosts(groupFilter, hostFilter).then(function (hosts) {
var hostids = _.map(hosts, 'hostid');
if (appFilter) {
return _this3.cachingProxy.getApps(hostids).then(function (apps) {
return filterByQuery(apps, appFilter);
});
} else {
return {
appFilterEmpty: true,
hostids: hostids
};
}
});
}
}, {
key: 'getAllItems',
value: function getAllItems(groupFilter, hostFilter, appFilter) {
var _this4 = this;
var options = arguments.length > 3 && arguments[3] !== undefined ? arguments[3] : {};
return this.getApps(groupFilter, hostFilter, appFilter).then(function (apps) {
if (apps.appFilterEmpty) {
return _this4.cachingProxy.getItems(apps.hostids, undefined, options.itemtype);
} else {
var appids = _.map(apps, 'applicationid');
return _this4.cachingProxy.getItems(undefined, appids, options.itemtype);
}
}).then(function (items) {
if (!options.showDisabledItems) {
items = _.filter(items, { 'status': '0' });
}
return items;
});
}
}, {
key: 'getItems',
value: function getItems(groupFilter, hostFilter, appFilter, itemFilter) {
var options = arguments.length > 4 && arguments[4] !== undefined ? arguments[4] : {};
return this.getAllItems(groupFilter, hostFilter, appFilter, options).then(function (items) {
return filterByQuery(items, itemFilter);
});
}
}, {
key: 'getTriggers',
value: function getTriggers(groupFilter, hostFilter, appFilter, showTriggers) {
var _this5 = this;
var promises = [this.getGroups(groupFilter), this.getHosts(groupFilter, hostFilter), this.getApps(groupFilter, hostFilter, appFilter)];
return Promise.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;
}).then(function (query) {
return _this5.zabbixAPI.getTriggers(query.groupids, query.hostids, query.applicationids, showTriggers);
});
}
}]);
return Zabbix;
}();
return Zabbix;
}
///////////////////////////////////////////////////////////////////////////////
/**
* 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);
}
}
return {
setters: [function (_angular) {
angular = _angular.default;
}, function (_lodash) {
_ = _lodash.default;
}, function (_utils) {
utils = _utils;
}, function (_zabbixAPIServiceJs) {}, function (_zabbixCachingProxyServiceJs) {}],
execute: function () {
_createClass = function () {
function defineProperties(target, props) {
for (var i = 0; i < props.length; i++) {
var descriptor = props[i];
descriptor.enumerable = descriptor.enumerable || false;
descriptor.configurable = true;
if ("value" in descriptor) descriptor.writable = true;
Object.defineProperty(target, descriptor.key, descriptor);
}
}
return function (Constructor, protoProps, staticProps) {
if (protoProps) defineProperties(Constructor.prototype, protoProps);
if (staticProps) defineProperties(Constructor, staticProps);
return Constructor;
};
}();
angular.module('grafana.services').factory('Zabbix', ZabbixFactory);
}
};
});
//# sourceMappingURL=zabbix.js.map

1
dist/datasource-zabbix/zabbix.js.map vendored Normal file

File diff suppressed because one or more lines are too long

View File

@@ -0,0 +1,397 @@
'use strict';
System.register(['angular', 'lodash', './utils', './zabbixAPICore.service'], function (_export, _context) {
"use strict";
var angular, _, utils, _createClass;
function _classCallCheck(instance, Constructor) {
if (!(instance instanceof Constructor)) {
throw new TypeError("Cannot call a class as a function");
}
}
/** @ngInject */
function ZabbixAPIServiceFactory(alertSrv, zabbixAPICoreService) {
var ZabbixAPI = function () {
function ZabbixAPI(api_url, username, password, basicAuth, withCredentials) {
_classCallCheck(this, ZabbixAPI);
this.url = api_url;
this.username = username;
this.password = password;
this.auth = "";
this.requestOptions = {
basicAuth: basicAuth,
withCredentials: withCredentials
};
this.loginPromise = null;
this.loginErrorCount = 0;
this.maxLoginAttempts = 3;
this.alertSrv = alertSrv;
this.zabbixAPICore = zabbixAPICoreService;
this.getTrend = this.getTrend_ZBXNEXT1193;
//getTrend = getTrend_30;
}
//////////////////////////
// Core method wrappers //
//////////////////////////
_createClass(ZabbixAPI, [{
key: 'request',
value: function request(method, params) {
var _this = this;
return this.zabbixAPICore.request(this.url, method, params, this.requestOptions, this.auth).catch(function (error) {
if (isNotAuthorized(error.data)) {
// Handle auth errors
_this.loginErrorCount++;
if (_this.loginErrorCount > _this.maxLoginAttempts) {
_this.loginErrorCount = 0;
return null;
} else {
return _this.loginOnce().then(function () {
return _this.request(method, params);
});
}
} else {
// Handle API errors
var message = error.data ? error.data : error.statusText;
_this.alertAPIError(message);
}
});
}
}, {
key: 'alertAPIError',
value: function alertAPIError(message) {
var timeout = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : 5000;
this.alertSrv.set("Zabbix API Error", message, 'error', timeout);
}
}, {
key: 'loginOnce',
value: function loginOnce() {
var _this2 = this;
if (!this.loginPromise) {
this.loginPromise = Promise.resolve(this.login().then(function (auth) {
_this2.auth = auth;
_this2.loginPromise = null;
return auth;
}));
}
return this.loginPromise;
}
}, {
key: 'login',
value: function login() {
return this.zabbixAPICore.login(this.url, this.username, this.password, this.requestOptions);
}
}, {
key: 'getVersion',
value: function getVersion() {
return this.zabbixAPICore.getVersion(this.url, this.requestOptions);
}
}, {
key: 'acknowledgeEvent',
value: function acknowledgeEvent(eventid, message) {
var params = {
eventids: eventid,
message: message
};
return this.request('event.acknowledge', params);
}
}, {
key: 'getGroups',
value: function getGroups() {
var params = {
output: ['name'],
sortfield: 'name',
real_hosts: true
};
return this.request('hostgroup.get', params);
}
}, {
key: 'getHosts',
value: function getHosts(groupids) {
var params = {
output: ['name', 'host'],
sortfield: 'name'
};
if (groupids) {
params.groupids = groupids;
}
return this.request('host.get', params);
}
}, {
key: 'getApps',
value: function getApps(hostids) {
var params = {
output: ['applicationid', 'name'],
hostids: hostids
};
return this.request('application.get', params);
}
}, {
key: 'getItems',
value: function getItems(hostids, appids, itemtype) {
var params = {
output: ['name', 'key_', 'value_type', 'hostid', 'status', 'state'],
sortfield: 'name',
webitems: true,
filter: {},
selectHosts: ['hostid', 'name']
};
if (hostids) {
params.hostids = hostids;
}
if (appids) {
params.applicationids = appids;
}
if (itemtype === 'num') {
// Return only numeric metrics
params.filter.value_type = [0, 3];
}
if (itemtype === 'text') {
// Return only text metrics
params.filter.value_type = [1, 2, 4];
}
return this.request('item.get', params).then(expandItems);
function expandItems(items) {
items.forEach(function (item) {
item.item = item.name;
item.name = utils.expandItemName(item.item, item.key_);
return item;
});
return items;
}
}
}, {
key: 'getLastValue',
value: function getLastValue(itemid) {
var params = {
output: ['lastvalue'],
itemids: itemid
};
return this.request('item.get', params).then(function (items) {
return items.length ? items[0].lastvalue : null;
});
}
}, {
key: 'getHistory',
value: function getHistory(items, timeFrom, timeTill) {
var _this3 = this;
// Group items by value type and perform request for each value type
var grouped_items = _.groupBy(items, 'value_type');
var promises = _.map(grouped_items, function (items, value_type) {
var itemids = _.map(items, 'itemid');
var params = {
output: 'extend',
history: value_type,
itemids: itemids,
sortfield: 'clock',
sortorder: 'ASC',
time_from: timeFrom
};
// Relative queries (e.g. last hour) don't include an end time
if (timeTill) {
params.time_till = timeTill;
}
return _this3.request('history.get', params);
});
return Promise.all(promises).then(_.flatten);
}
}, {
key: 'getTrend_ZBXNEXT1193',
value: function getTrend_ZBXNEXT1193(items, timeFrom, timeTill) {
var _this4 = this;
// Group items by value type and perform request for each value type
var grouped_items = _.groupBy(items, 'value_type');
var promises = _.map(grouped_items, function (items, value_type) {
var itemids = _.map(items, 'itemid');
var params = {
output: 'extend',
trend: value_type,
itemids: itemids,
sortfield: 'clock',
sortorder: 'ASC',
time_from: timeFrom
};
// Relative queries (e.g. last hour) don't include an end time
if (timeTill) {
params.time_till = timeTill;
}
return _this4.request('trend.get', params);
});
return Promise.all(promises).then(_.flatten);
}
}, {
key: 'getTrend_30',
value: function getTrend_30(items, time_from, time_till, value_type) {
var self = this;
var itemids = _.map(items, 'itemid');
var params = {
output: ["itemid", "clock", value_type],
itemids: itemids,
time_from: time_from
};
// Relative queries (e.g. last hour) don't include an end time
if (time_till) {
params.time_till = time_till;
}
return self.request('trend.get', params);
}
}, {
key: 'getITService',
value: function getITService(serviceids) {
var params = {
output: 'extend',
serviceids: serviceids
};
return this.request('service.get', params);
}
}, {
key: 'getSLA',
value: function getSLA(serviceids, timeFrom, timeTo) {
var params = {
serviceids: serviceids,
intervals: [{
from: timeFrom,
to: timeTo
}]
};
return this.request('service.getsla', params);
}
}, {
key: 'getTriggers',
value: function getTriggers(groupids, hostids, applicationids, showTriggers, timeFrom, timeTo) {
var params = {
output: 'extend',
groupids: groupids,
hostids: hostids,
applicationids: applicationids,
expandDescription: true,
expandData: true,
expandComment: true,
monitored: true,
skipDependent: true,
//only_true: true,
filter: {
value: 1
},
selectGroups: ['name'],
selectHosts: ['name', 'host'],
selectItems: ['name', 'key_', 'lastvalue'],
selectLastEvent: 'extend'
};
if (showTriggers) {
params.filter.value = showTriggers;
}
if (timeFrom || timeTo) {
params.lastChangeSince = timeFrom;
params.lastChangeTill = timeTo;
}
return this.request('trigger.get', params);
}
}, {
key: 'getEvents',
value: function getEvents(objectids, timeFrom, timeTo, showEvents) {
var params = {
output: 'extend',
time_from: timeFrom,
time_till: timeTo,
objectids: objectids,
select_acknowledges: 'extend',
selectHosts: 'extend',
value: showEvents
};
return this.request('event.get', params);
}
}, {
key: 'getAcknowledges',
value: function getAcknowledges(eventids) {
var params = {
output: 'extend',
eventids: eventids,
preservekeys: true,
select_acknowledges: 'extend',
sortfield: 'clock',
sortorder: 'DESC'
};
return this.request('event.get', params).then(function (events) {
return _.filter(events, function (event) {
return event.acknowledges.length;
});
});
}
}]);
return ZabbixAPI;
}();
return ZabbixAPI;
}
function isNotAuthorized(message) {
return message === "Session terminated, re-login, please." || message === "Not authorised." || message === "Not authorized.";
}
return {
setters: [function (_angular) {
angular = _angular.default;
}, function (_lodash) {
_ = _lodash.default;
}, function (_utils) {
utils = _utils;
}, function (_zabbixAPICoreService) {}],
execute: function () {
_createClass = function () {
function defineProperties(target, props) {
for (var i = 0; i < props.length; i++) {
var descriptor = props[i];
descriptor.enumerable = descriptor.enumerable || false;
descriptor.configurable = true;
if ("value" in descriptor) descriptor.writable = true;
Object.defineProperty(target, descriptor.key, descriptor);
}
}
return function (Constructor, protoProps, staticProps) {
if (protoProps) defineProperties(Constructor.prototype, protoProps);
if (staticProps) defineProperties(Constructor, staticProps);
return Constructor;
};
}();
angular.module('grafana.services').factory('zabbixAPIService', ZabbixAPIServiceFactory);
}
};
});
//# sourceMappingURL=zabbixAPI.service.js.map

File diff suppressed because one or more lines are too long

View File

@@ -0,0 +1,150 @@
'use strict';
System.register(['angular'], function (_export, _context) {
"use strict";
var angular, _createClass, ZabbixAPICoreService, ZabbixAPIError;
function _classCallCheck(instance, Constructor) {
if (!(instance instanceof Constructor)) {
throw new TypeError("Cannot call a class as a function");
}
}
return {
setters: [function (_angular) {
angular = _angular.default;
}],
execute: function () {
_createClass = function () {
function defineProperties(target, props) {
for (var i = 0; i < props.length; i++) {
var descriptor = props[i];
descriptor.enumerable = descriptor.enumerable || false;
descriptor.configurable = true;
if ("value" in descriptor) descriptor.writable = true;
Object.defineProperty(target, descriptor.key, descriptor);
}
}
return function (Constructor, protoProps, staticProps) {
if (protoProps) defineProperties(Constructor.prototype, protoProps);
if (staticProps) defineProperties(Constructor, staticProps);
return Constructor;
};
}();
ZabbixAPICoreService = function () {
/** @ngInject */
function ZabbixAPICoreService(backendSrv) {
_classCallCheck(this, ZabbixAPICoreService);
this.backendSrv = backendSrv;
}
/**
* Request data from Zabbix API
* @return {object} response.result
*/
_createClass(ZabbixAPICoreService, [{
key: 'request',
value: function request(api_url, method, params, options, auth) {
var requestData = {
jsonrpc: '2.0',
method: method,
params: params,
id: 1
};
if (auth === "") {
// Reject immediately if not authenticated
return Promise.reject(new ZabbixAPIError({ data: "Not authorised." }));
} else if (auth) {
// Set auth parameter only if it needed
requestData.auth = auth;
}
var requestOptions = {
method: 'POST',
url: api_url,
data: requestData,
headers: {
'Content-Type': 'application/json'
}
};
// Set request options for basic auth
if (options.basicAuth || options.withCredentials) {
requestOptions.withCredentials = true;
}
if (options.basicAuth) {
requestOptions.headers.Authorization = options.basicAuth;
}
return this.datasourceRequest(requestOptions);
}
}, {
key: 'datasourceRequest',
value: function datasourceRequest(requestOptions) {
return this.backendSrv.datasourceRequest(requestOptions).then(function (response) {
if (!response.data) {
return Promise.reject(new ZabbixAPIError({ data: "General Error, no data" }));
} else if (response.data.error) {
// Handle Zabbix API errors
return Promise.reject(new ZabbixAPIError(response.data.error));
}
// Success
return response.data.result;
});
}
}, {
key: 'login',
value: function login(api_url, username, password, options) {
var params = {
user: username,
password: password
};
return this.request(api_url, 'user.login', params, options, null);
}
}, {
key: 'getVersion',
value: function getVersion(api_url, options) {
return this.request(api_url, 'apiinfo.version', [], options);
}
}]);
return ZabbixAPICoreService;
}();
_export('ZabbixAPIError', ZabbixAPIError = function () {
function ZabbixAPIError(error) {
_classCallCheck(this, ZabbixAPIError);
this.code = error.code;
this.name = error.data;
this.message = error.data;
this.data = error.data;
}
_createClass(ZabbixAPIError, [{
key: 'toString',
value: function toString() {
return this.name + ": " + this.message;
}
}]);
return ZabbixAPIError;
}());
_export('ZabbixAPIError', ZabbixAPIError);
angular.module('grafana.services').service('zabbixAPICoreService', ZabbixAPICoreService);
}
};
});
//# sourceMappingURL=zabbixAPICore.service.js.map

View File

@@ -0,0 +1 @@
{"version":3,"sources":["../../src/datasource-zabbix/zabbixAPICore.service.js"],"names":["angular","ZabbixAPICoreService","backendSrv","api_url","method","params","options","auth","requestData","jsonrpc","id","Promise","reject","ZabbixAPIError","data","requestOptions","url","headers","basicAuth","withCredentials","Authorization","datasourceRequest","then","response","error","result","username","password","user","request","code","name","message","module","service"],"mappings":";;;;;;;;;;;;;;;AAIOA,a;;;;;;;;;;;;;;;;;;;;;AAEDC,0B;;AAEJ;AACA,sCAAYC,UAAZ,EAAwB;AAAA;;AACtB,eAAKA,UAAL,GAAkBA,UAAlB;AACD;;AAED;;;;;;;;kCAIQC,O,EAASC,M,EAAQC,M,EAAQC,O,EAASC,I,EAAM;AAC9C,gBAAIC,cAAc;AAChBC,uBAAS,KADO;AAEhBL,sBAAQA,MAFQ;AAGhBC,sBAAQA,MAHQ;AAIhBK,kBAAI;AAJY,aAAlB;;AAOA,gBAAIH,SAAS,EAAb,EAAiB;AACf;AACA,qBAAOI,QAAQC,MAAR,CAAe,IAAIC,cAAJ,CAAmB,EAACC,MAAM,iBAAP,EAAnB,CAAf,CAAP;AACD,aAHD,MAGO,IAAIP,IAAJ,EAAU;AACf;AACAC,0BAAYD,IAAZ,GAAmBA,IAAnB;AACD;;AAED,gBAAIQ,iBAAiB;AACnBX,sBAAQ,MADW;AAEnBY,mBAAKb,OAFc;AAGnBW,oBAAMN,WAHa;AAInBS,uBAAS;AACP,gCAAgB;AADT;AAJU,aAArB;;AASA;AACA,gBAAIX,QAAQY,SAAR,IAAqBZ,QAAQa,eAAjC,EAAkD;AAChDJ,6BAAeI,eAAf,GAAiC,IAAjC;AACD;AACD,gBAAIb,QAAQY,SAAZ,EAAuB;AACrBH,6BAAeE,OAAf,CAAuBG,aAAvB,GAAuCd,QAAQY,SAA/C;AACD;;AAED,mBAAO,KAAKG,iBAAL,CAAuBN,cAAvB,CAAP;AACD;;;4CAEiBA,c,EAAgB;AAChC,mBAAO,KAAKb,UAAL,CAAgBmB,iBAAhB,CAAkCN,cAAlC,EACNO,IADM,CACD,oBAAY;AAChB,kBAAI,CAACC,SAAST,IAAd,EAAoB;AAClB,uBAAOH,QAAQC,MAAR,CAAe,IAAIC,cAAJ,CAAmB,EAACC,MAAM,wBAAP,EAAnB,CAAf,CAAP;AACD,eAFD,MAEO,IAAIS,SAAST,IAAT,CAAcU,KAAlB,EAAyB;;AAE9B;AACA,uBAAOb,QAAQC,MAAR,CAAe,IAAIC,cAAJ,CAAmBU,SAAST,IAAT,CAAcU,KAAjC,CAAf,CAAP;AACD;;AAED;AACA,qBAAOD,SAAST,IAAT,CAAcW,MAArB;AACD,aAZM,CAAP;AAaD;;;gCAMKtB,O,EAASuB,Q,EAAUC,Q,EAAUrB,O,EAAS;AAC1C,gBAAID,SAAS;AACXuB,oBAAMF,QADK;AAEXC,wBAAUA;AAFC,aAAb;AAIA,mBAAO,KAAKE,OAAL,CAAa1B,OAAb,EAAsB,YAAtB,EAAoCE,MAApC,EAA4CC,OAA5C,EAAqD,IAArD,CAAP;AACD;;;qCAMUH,O,EAASG,O,EAAS;AAC3B,mBAAO,KAAKuB,OAAL,CAAa1B,OAAb,EAAsB,iBAAtB,EAAyC,EAAzC,EAA6CG,OAA7C,CAAP;AACD;;;;;;gCAIUO,c;AACX,gCAAYW,KAAZ,EAAmB;AAAA;;AACjB,eAAKM,IAAL,GAAYN,MAAMM,IAAlB;AACA,eAAKC,IAAL,GAAYP,MAAMV,IAAlB;AACA,eAAKkB,OAAL,GAAeR,MAAMV,IAArB;AACA,eAAKA,IAAL,GAAYU,MAAMV,IAAlB;AACD;;;;qCAEU;AACT,mBAAO,KAAKiB,IAAL,GAAY,IAAZ,GAAmB,KAAKC,OAA/B;AACD;;;;;;;;AAGHhC,cACGiC,MADH,CACU,kBADV,EAEGC,OAFH,CAEW,sBAFX,EAEmCjC,oBAFnC","file":"zabbixAPICore.service.js","sourcesContent":["/**\n * General Zabbix API methods\n */\n\nimport angular from 'angular';\n\nclass ZabbixAPICoreService {\n\n /** @ngInject */\n constructor(backendSrv) {\n this.backendSrv = backendSrv;\n }\n\n /**\n * Request data from Zabbix API\n * @return {object} response.result\n */\n request(api_url, method, params, options, auth) {\n let requestData = {\n jsonrpc: '2.0',\n method: method,\n params: params,\n id: 1\n };\n\n if (auth === \"\") {\n // Reject immediately if not authenticated\n return Promise.reject(new ZabbixAPIError({data: \"Not authorised.\"}));\n } else if (auth) {\n // Set auth parameter only if it needed\n requestData.auth = auth;\n }\n\n let requestOptions = {\n method: 'POST',\n url: api_url,\n data: requestData,\n headers: {\n 'Content-Type': 'application/json'\n }\n };\n\n // Set request options for basic auth\n if (options.basicAuth || options.withCredentials) {\n requestOptions.withCredentials = true;\n }\n if (options.basicAuth) {\n requestOptions.headers.Authorization = options.basicAuth;\n }\n\n return this.datasourceRequest(requestOptions);\n }\n\n datasourceRequest(requestOptions) {\n return this.backendSrv.datasourceRequest(requestOptions)\n .then(response => {\n if (!response.data) {\n return Promise.reject(new ZabbixAPIError({data: \"General Error, no data\"}));\n } else if (response.data.error) {\n\n // Handle Zabbix API errors\n return Promise.reject(new ZabbixAPIError(response.data.error));\n }\n\n // Success\n return response.data.result;\n });\n }\n\n /**\n * Get authentication token.\n * @return {string} auth token\n */\n login(api_url, username, password, options) {\n let params = {\n user: username,\n password: password\n };\n return this.request(api_url, 'user.login', params, options, null);\n }\n\n /**\n * Get Zabbix API version\n * Matches the version of Zabbix starting from Zabbix 2.0.4\n */\n getVersion(api_url, options) {\n return this.request(api_url, 'apiinfo.version', [], options);\n }\n}\n\n// Define zabbix API exception type\nexport class ZabbixAPIError {\n constructor(error) {\n this.code = error.code;\n this.name = error.data;\n this.message = error.data;\n this.data = error.data;\n }\n\n toString() {\n return this.name + \": \" + this.message;\n }\n}\n\nangular\n .module('grafana.services')\n .service('zabbixAPICoreService', ZabbixAPICoreService);\n"]}

View File

@@ -0,0 +1,243 @@
'use strict';
System.register(['angular', 'lodash'], function (_export, _context) {
"use strict";
var angular, _, _createClass;
function _toConsumableArray(arr) {
if (Array.isArray(arr)) {
for (var i = 0, arr2 = Array(arr.length); i < arr.length; i++) {
arr2[i] = arr[i];
}
return arr2;
} else {
return Array.from(arr);
}
}
function _classCallCheck(instance, Constructor) {
if (!(instance instanceof Constructor)) {
throw new TypeError("Cannot call a class as a function");
}
}
// Use factory() instead service() for multiple datasources support.
// Each datasource instance must initialize its own cache.
/** @ngInject */
function ZabbixCachingProxyFactory() {
var ZabbixCachingProxy = function () {
function ZabbixCachingProxy(zabbixAPI, cacheOptions) {
_classCallCheck(this, ZabbixCachingProxy);
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);
}
_createClass(ZabbixCachingProxy, [{
key: 'isExpired',
value: function isExpired(cacheObject) {
if (cacheObject) {
var object_age = Date.now() - cacheObject.timestamp;
return !(cacheObject.timestamp && object_age < this.ttl);
} else {
return true;
}
}
}, {
key: 'proxyRequest',
value: function proxyRequest(request, params, cacheObject) {
var hash = getRequestHash(params);
if (this.cacheEnabled && !this.isExpired(cacheObject[hash])) {
return Promise.resolve(cacheObject[hash].value);
} else {
return request.apply(undefined, _toConsumableArray(params)).then(function (result) {
cacheObject[hash] = {
value: result,
timestamp: Date.now()
};
return result;
});
}
}
}, {
key: 'getGroups',
value: function getGroups() {
return this.proxyRequest(this.getGroupsOnce, [], this.cache.groups);
}
}, {
key: 'getHosts',
value: function getHosts(groupids) {
return this.proxyRequest(this.getHostsOnce, [groupids], this.cache.hosts);
}
}, {
key: 'getApps',
value: function getApps(hostids) {
return this.proxyRequest(this.getAppsOnce, [hostids], this.cache.applications);
}
}, {
key: 'getItems',
value: function getItems(hostids, appids, itemtype) {
var params = [hostids, appids, itemtype];
return this.proxyRequest(this.getItemsOnce, params, this.cache.items);
}
}, {
key: 'getHistoryFromCache',
value: function getHistoryFromCache(items, time_from, time_till) {
var historyStorage = this.cache.history;
var full_history;
var expired = _.filter(_.keyBy(items, 'itemid'), function (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, 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;
});
return _.flatten(full_history, true);
});
} else {
full_history = _.map(items, function (item) {
return historyStorage[item.itemid].history;
});
return Promise.resolve(_.flatten(full_history, true));
}
}
}, {
key: 'getHistoryFromAPI',
value: function getHistoryFromAPI(items, time_from, time_till) {
return this.zabbixAPI.getHistory(items, time_from, time_till);
}
}]);
return ZabbixCachingProxy;
}();
return ZabbixCachingProxy;
}
/**
* 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(function (result) {
promiseKeeper[hash] = null;
return result;
}));
}
return promiseKeeper[hash];
};
}
function getRequestHash(args) {
var requestStamp = _.map(args, function (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) {
var itemids = _.map(args[0], 'itemid');
var stamp = itemids.join() + args[1] + args[2];
return stamp.getHash();
}
return {
setters: [function (_angular) {
angular = _angular.default;
}, function (_lodash) {
_ = _lodash.default;
}],
execute: function () {
_createClass = function () {
function defineProperties(target, props) {
for (var i = 0; i < props.length; i++) {
var descriptor = props[i];
descriptor.enumerable = descriptor.enumerable || false;
descriptor.configurable = true;
if ("value" in descriptor) descriptor.writable = true;
Object.defineProperty(target, descriptor.key, descriptor);
}
}
return function (Constructor, protoProps, staticProps) {
if (protoProps) defineProperties(Constructor.prototype, protoProps);
if (staticProps) defineProperties(Constructor, staticProps);
return Constructor;
};
}();
angular.module('grafana.services').factory('ZabbixCachingProxy', ZabbixCachingProxyFactory);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;
}
}
};
});
//# sourceMappingURL=zabbixCachingProxy.service.js.map

File diff suppressed because one or more lines are too long