Merge branch 'develop'

This commit is contained in:
Alexander Zobnin
2016-04-14 21:46:28 +03:00
12 changed files with 1149 additions and 364 deletions

View File

@@ -2,11 +2,9 @@
Zabbix plugin allows to show different type of data from [Zabbix](http://www.zabbix.com/)
monitoring system.
![Dashboard](https://cloud.githubusercontent.com/assets/4932851/8269101/9e6ee67e-17a3-11e5-85de-fe9dcc2dd375.png)
### Live Demo
Check out the [live demo](http://play.grafana-zabbix.org/)
Check out the [live demo](http://play.grafana-zabbix.org/) with dashboard examples.
### Features

View File

@@ -0,0 +1,569 @@
{
"id": null,
"title": "Template Linux Server",
"originalTitle": "Template Linux Server",
"tags": [
"zabbix",
"example"
],
"style": "dark",
"timezone": "browser",
"editable": true,
"hideControls": false,
"sharedCrosshair": false,
"rows": [
{
"collapse": false,
"editable": true,
"height": "250px",
"panels": [
{
"aliasColors": {
"CPU iowait time": "#B7DBAB",
"CPU system time": "#BF1B00",
"CPU user time": "#EAB839"
},
"bars": false,
"datasource": null,
"editable": true,
"error": false,
"fill": 3,
"grid": {
"threshold1": null,
"threshold1Color": "rgba(216, 200, 27, 0.27)",
"threshold2": null,
"threshold2Color": "rgba(234, 112, 112, 0.22)"
},
"id": 1,
"legend": {
"avg": false,
"current": false,
"max": false,
"min": false,
"show": true,
"total": false,
"values": false
},
"lines": true,
"linewidth": 1,
"links": [],
"nullPointMode": "connected",
"percentage": false,
"pointradius": 2,
"points": false,
"renderer": "flot",
"seriesOverrides": [],
"span": 6,
"stack": true,
"steppedLine": false,
"targets": [
{
"application": {
"filter": "CPU"
},
"functions": [],
"group": {
"filter": "$group"
},
"host": {
"filter": "$host"
},
"item": {
"filter": "/CPU/"
},
"mode": 0,
"refId": "A"
}
],
"timeFrom": null,
"timeShift": null,
"title": "CPU",
"tooltip": {
"msResolution": false,
"shared": true,
"value_type": "individual"
},
"type": "graph",
"xaxis": {
"show": true
},
"yaxes": [
{
"format": "percent",
"logBase": 1,
"max": 100,
"min": 0,
"show": true
},
{
"format": "short",
"logBase": 1,
"max": null,
"min": null,
"show": true
}
]
},
{
"aliasColors": {
"Processor load (1 min average per core)": "#1F78C1"
},
"bars": false,
"datasource": null,
"editable": true,
"error": false,
"fill": 1,
"grid": {
"threshold1": null,
"threshold1Color": "rgba(216, 200, 27, 0.27)",
"threshold2": null,
"threshold2Color": "rgba(234, 112, 112, 0.22)"
},
"id": 2,
"legend": {
"avg": false,
"current": false,
"max": false,
"min": false,
"show": true,
"total": false,
"values": false
},
"lines": true,
"linewidth": 2,
"links": [],
"nullPointMode": "connected",
"percentage": false,
"pointradius": 5,
"points": false,
"renderer": "flot",
"seriesOverrides": [],
"span": 6,
"stack": false,
"steppedLine": false,
"targets": [
{
"application": {
"filter": "CPU"
},
"functions": [],
"group": {
"filter": "$group"
},
"host": {
"filter": "$host"
},
"item": {
"filter": "Processor load (15 min average per core)"
},
"mode": 0,
"refId": "A"
}
],
"timeFrom": null,
"timeShift": null,
"title": "System load",
"tooltip": {
"msResolution": false,
"shared": true,
"value_type": "cumulative"
},
"type": "graph",
"xaxis": {
"show": true
},
"yaxes": [
{
"format": "short",
"logBase": 1,
"max": null,
"min": 0,
"show": true
},
{
"format": "short",
"logBase": 1,
"max": null,
"min": null,
"show": true
}
]
}
],
"showTitle": true,
"title": "CPU"
},
{
"collapse": false,
"editable": true,
"height": "250px",
"panels": [
{
"aliasColors": {},
"bars": false,
"datasource": null,
"editable": true,
"error": false,
"fill": 3,
"grid": {
"threshold1": null,
"threshold1Color": "rgba(216, 200, 27, 0.27)",
"threshold2": null,
"threshold2Color": "rgba(234, 112, 112, 0.22)"
},
"id": 3,
"legend": {
"alignAsTable": false,
"avg": false,
"current": false,
"max": false,
"min": false,
"rightSide": false,
"show": true,
"total": false,
"values": false
},
"lines": true,
"linewidth": 2,
"links": [],
"minSpan": 4,
"nullPointMode": "connected",
"percentage": false,
"pointradius": 5,
"points": false,
"renderer": "flot",
"repeat": "netif",
"scopedVars": {
"netif": {
"text": "eth0",
"value": "eth0",
"selected": false
}
},
"seriesOverrides": [
{
"alias": "/Incoming/",
"transform": "negative-Y"
}
],
"span": 6,
"stack": false,
"steppedLine": false,
"targets": [
{
"application": {
"filter": ""
},
"functions": [],
"group": {
"filter": "$group"
},
"host": {
"filter": "$host"
},
"item": {
"filter": "/$netif/"
},
"mode": 0,
"refId": "A"
}
],
"timeFrom": null,
"timeShift": null,
"title": "Network traffic on $netif",
"tooltip": {
"msResolution": false,
"shared": true,
"value_type": "cumulative"
},
"type": "graph",
"xaxis": {
"show": true
},
"yaxes": [
{
"format": "bps",
"logBase": 1,
"max": null,
"min": null,
"show": true
},
{
"format": "short",
"logBase": 1,
"max": null,
"min": null,
"show": true
}
]
},
{
"aliasColors": {},
"bars": false,
"datasource": null,
"editable": true,
"error": false,
"fill": 3,
"grid": {
"threshold1": null,
"threshold1Color": "rgba(216, 200, 27, 0.27)",
"threshold2": null,
"threshold2Color": "rgba(234, 112, 112, 0.22)"
},
"id": 4,
"legend": {
"alignAsTable": false,
"avg": false,
"current": false,
"max": false,
"min": false,
"rightSide": false,
"show": true,
"total": false,
"values": false
},
"lines": true,
"linewidth": 2,
"links": [],
"minSpan": 4,
"nullPointMode": "connected",
"percentage": false,
"pointradius": 5,
"points": false,
"renderer": "flot",
"repeat": null,
"scopedVars": {
"netif": {
"text": "eth1",
"value": "eth1",
"selected": false
}
},
"seriesOverrides": [
{
"alias": "/Incoming/",
"transform": "negative-Y"
}
],
"span": 6,
"stack": false,
"steppedLine": false,
"targets": [
{
"application": {
"filter": ""
},
"functions": [],
"group": {
"filter": "$group"
},
"host": {
"filter": "$host"
},
"item": {
"filter": "/$netif/"
},
"mode": 0,
"refId": "A"
}
],
"timeFrom": null,
"timeShift": null,
"title": "Network traffic on $netif",
"tooltip": {
"msResolution": false,
"shared": true,
"value_type": "cumulative"
},
"type": "graph",
"xaxis": {
"show": true
},
"yaxes": [
{
"format": "bps",
"logBase": 1,
"max": null,
"min": null,
"show": true
},
{
"format": "short",
"logBase": 1,
"max": null,
"min": null,
"show": true
}
],
"repeatIteration": 1460635040618,
"repeatPanelId": 3
}
],
"showTitle": true,
"title": "Network"
}
],
"time": {
"from": "now-3h",
"to": "now"
},
"timepicker": {
"now": true,
"refresh_intervals": [
"30s",
"1m",
"5m",
"15m",
"30m",
"1h",
"3h",
"2h",
"1d"
],
"time_options": [
"5m",
"15m",
"1h",
"6h",
"12h",
"24h",
"2d",
"7d",
"30d"
]
},
"templating": {
"list": [
{
"allFormat": "regex values",
"current": {
"text": "Frontend",
"value": "Frontend"
},
"datasource": null,
"hide": 0,
"includeAll": false,
"label": "Group",
"multi": false,
"multiFormat": "glob",
"name": "group",
"options": [
{
"text": "Backend",
"value": "Backend",
"selected": false
},
{
"text": "Database servers",
"value": "Database servers",
"selected": false
},
{
"text": "Frontend",
"value": "Frontend",
"selected": true
},
{
"text": "Linux servers",
"value": "Linux servers",
"selected": false
},
{
"text": "Network",
"value": "Network",
"selected": false
},
{
"text": "Workstations",
"value": "Workstations",
"selected": false
},
{
"text": "Zabbix servers",
"value": "Zabbix servers",
"selected": false
}
],
"query": "*",
"refresh": 1,
"refresh_on_load": false,
"regex": "",
"type": "query"
},
{
"allFormat": "glob",
"current": {
"text": "frontend01",
"value": "frontend01"
},
"datasource": null,
"hide": 0,
"includeAll": false,
"label": "Host",
"multi": false,
"multiFormat": "glob",
"name": "host",
"options": [
{
"text": "frontend01",
"value": "frontend01",
"selected": true
},
{
"text": "frontend02",
"value": "frontend02",
"selected": false
}
],
"query": "$group.*",
"refresh": 1,
"refresh_on_load": false,
"regex": "",
"type": "query"
},
{
"allFormat": "regex values",
"current": {
"text": "All",
"value": "$__all"
},
"datasource": null,
"hide": 0,
"hideLabel": false,
"includeAll": true,
"label": "Network interface",
"multi": true,
"multiFormat": "regex values",
"name": "netif",
"options": [
{
"text": "All",
"value": "$__all",
"selected": true
},
{
"text": "eth0",
"value": "eth0",
"selected": false
},
{
"text": "eth1",
"value": "eth1",
"selected": false
}
],
"query": "*.$host.Network interfaces.*",
"refresh": 1,
"refresh_on_load": false,
"regex": "/(?:Incoming|Outgoing) network traffic on (.*)/",
"type": "query"
}
]
},
"annotations": {
"list": []
},
"schemaVersion": 12,
"version": 8,
"links": []
}

View File

@@ -249,7 +249,20 @@
"pointradius": 5,
"points": false,
"renderer": "flot",
"seriesOverrides": [],
"seriesOverrides": [
{
"alias": "/user/",
"color": "#1F78C1"
},
{
"alias": "/system/",
"color": "#BF1B00"
},
{
"alias": "/iowait/",
"color": "#E5AC0E"
}
],
"span": 7,
"stack": true,
"steppedLine": false,
@@ -286,7 +299,7 @@
},
"yaxes": [
{
"format": "short",
"format": "percent",
"label": null,
"logBase": 1,
"max": null,
@@ -324,7 +337,7 @@
"scroll": true,
"showHeader": true,
"sort": {
"col": 0,
"col": 2,
"desc": true
},
"span": 5,
@@ -335,17 +348,20 @@
"type": "date"
},
{
"colorMode": null,
"colorMode": "cell",
"colors": [
"rgba(245, 54, 54, 0.9)",
"rgba(237, 129, 40, 0.89)",
"rgba(50, 172, 45, 0.97)"
"rgb(41, 170, 106)",
"rgba(239, 148, 21, 0.89)",
"rgba(239, 10, 10, 0.9)"
],
"decimals": 2,
"decimals": 1,
"pattern": "/.*/",
"thresholds": [],
"thresholds": [
"50",
"80"
],
"type": "number",
"unit": "short"
"unit": "percent"
}
],
"targets": [
@@ -373,6 +389,207 @@
}
],
"title": "Row"
},
{
"title": "New row",
"height": "380",
"editable": true,
"collapse": false,
"panels": [
{
"title": "Zabbix busy processes",
"error": false,
"span": 7.069277691711851,
"editable": true,
"type": "graph",
"isNew": true,
"id": 6,
"targets": [
{
"refId": "A",
"mode": 0,
"group": {
"filter": "Zabbix servers"
},
"host": {
"filter": "Zabbix server"
},
"application": {
"filter": "Zabbix server"
},
"item": {
"filter": "/Zabbix busy/"
},
"functions": []
}
],
"datasource": null,
"renderer": "flot",
"yaxes": [
{
"label": null,
"show": true,
"logBase": 1,
"min": null,
"max": null,
"format": "percent"
},
{
"label": null,
"show": true,
"logBase": 1,
"min": null,
"max": null,
"format": "short"
}
],
"xaxis": {
"show": true
},
"grid": {
"threshold1": null,
"threshold2": null,
"threshold1Color": "rgba(216, 200, 27, 0.27)",
"threshold2Color": "rgba(234, 112, 112, 0.22)"
},
"lines": true,
"fill": 0,
"linewidth": 2,
"points": false,
"pointradius": 5,
"bars": false,
"stack": false,
"percentage": false,
"legend": {
"show": true,
"values": false,
"min": false,
"max": false,
"current": false,
"total": false,
"avg": false,
"hideEmpty": true,
"hideZero": true,
"alignAsTable": true,
"rightSide": true
},
"nullPointMode": "connected",
"steppedLine": false,
"tooltip": {
"value_type": "cumulative",
"shared": true,
"msResolution": false
},
"timeFrom": null,
"timeShift": null,
"aliasColors": {},
"seriesOverrides": [],
"links": []
},
{
"title": "Zabbix Queue",
"error": false,
"span": 4.930722308288148,
"editable": true,
"type": "graph",
"isNew": true,
"id": 7,
"targets": [
{
"refId": "A",
"mode": 0,
"group": {
"filter": "Zabbix servers"
},
"host": {
"filter": "Zabbix server"
},
"application": {
"filter": "Zabbix server"
},
"item": {
"filter": "Zabbix queue"
},
"functions": []
},
{
"refId": "B",
"mode": 0,
"group": {
"filter": "Zabbix servers"
},
"host": {
"filter": "Zabbix server"
},
"application": {
"filter": "Zabbix server"
},
"item": {
"filter": "/Values processed/"
},
"functions": []
}
],
"datasource": null,
"renderer": "flot",
"yaxes": [
{
"label": null,
"show": true,
"logBase": 1,
"min": null,
"max": null,
"format": "short"
},
{
"label": null,
"show": true,
"logBase": 1,
"min": null,
"max": null,
"format": "short"
}
],
"xaxis": {
"show": true
},
"grid": {
"threshold1": null,
"threshold2": null,
"threshold1Color": "rgba(216, 200, 27, 0.27)",
"threshold2Color": "rgba(234, 112, 112, 0.22)"
},
"lines": true,
"fill": 0,
"linewidth": 2,
"points": false,
"pointradius": 5,
"bars": false,
"stack": false,
"percentage": false,
"legend": {
"show": true,
"values": false,
"min": false,
"max": false,
"current": false,
"total": false,
"avg": false
},
"nullPointMode": "connected",
"steppedLine": false,
"tooltip": {
"value_type": "cumulative",
"shared": true,
"msResolution": false
},
"timeFrom": null,
"timeShift": null,
"aliasColors": {},
"seriesOverrides": [],
"links": []
}
]
}
],
"time": {
@@ -411,6 +628,6 @@
"list": []
},
"schemaVersion": 12,
"version": 5,
"version": 6,
"links": []
}

View File

@@ -57,46 +57,6 @@ export class ZabbixAPIDatasource {
// Datasource methods //
////////////////////////
/**
* Test connection to Zabbix API
* @return {object} Connection status and Zabbix API version
*/
testDatasource() {
var self = this;
return this.zabbixAPI.getVersion().then(function (version) {
return self.zabbixAPI.login().then(function (auth) {
if (auth) {
return {
status: "success",
title: "Success",
message: "Zabbix API version: " + version
};
} else {
return {
status: "error",
title: "Invalid user name or password",
message: "Zabbix API version: " + version
};
}
}, function(error) {
console.log(error);
return {
status: "error",
title: "Connection failed",
message: error
};
});
},
function(error) {
console.log(error);
return {
status: "error",
title: "Connection failed",
message: "Could not connect to given url"
};
});
}
/**
* Query panel data. Calls for each panel in dashboard.
* @param {Object} options Contains time range, targets and other info.
@@ -106,12 +66,13 @@ export class ZabbixAPIDatasource {
var self = this;
// get from & to in seconds
var from = Math.ceil(dateMath.parse(options.range.from) / 1000);
var to = Math.ceil(dateMath.parse(options.range.to) / 1000);
var 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) {
var promises = _.map(options.targets, target => {
if (target.mode !== 1) {
@@ -119,8 +80,7 @@ export class ZabbixAPIDatasource {
target = migrations.migrate(target);
// Don't request undefined and hidden targets
if (target.hide || !target.group ||
!target.host || !target.item) {
if (target.hide || !target.group || !target.host || !target.item) {
return [];
}
@@ -132,116 +92,14 @@ export class ZabbixAPIDatasource {
// Query numeric data
if (!target.mode || target.mode === 0) {
// Build query in asynchronous manner
return self.queryProcessor
.build(groupFilter, hostFilter, appFilter, itemFilter, 'num')
.then(items => {
// Add hostname for items from multiple hosts
var addHostName = utils.isRegex(target.host.filter);
var getHistory;
// Use trends
if ((from < useTrendsFrom) && self.trends) {
// Find trendValue() function and get specified trend value
var trendFunctions = _.map(metricFunctions.getCategories()['Trends'], 'name');
var trendValueFunc = _.find(target.functions, function(func) {
return _.contains(trendFunctions, func.def.name);
});
var valueType = trendValueFunc ? trendValueFunc.params[0] : "avg";
getHistory = self.zabbixAPI.getTrend(items, from, to).then(function(history) {
return self.queryProcessor.handleTrends(history, items, addHostName, valueType);
});
} else {
// Use history
getHistory = self.zabbixCache.getHistory(items, from, to).then(function(history) {
return self.queryProcessor.handleHistory(history, items, addHostName);
});
}
return getHistory.then(function (timeseries_data) {
timeseries_data = _.map(timeseries_data, function (timeseries) {
// Filter only transform functions
var transformFunctions = bindFunctionDefs(target.functions, 'Transform', DataProcessor);
// Metric data processing
var dp = timeseries.datapoints;
for (var i = 0; i < transformFunctions.length; i++) {
dp = transformFunctions[i](dp);
}
timeseries.datapoints = dp;
return timeseries;
});
// Aggregations
var aggregationFunctions = bindFunctionDefs(target.functions, 'Aggregate', DataProcessor);
var dp = _.map(timeseries_data, 'datapoints');
if (aggregationFunctions.length) {
for (var i = 0; i < aggregationFunctions.length; i++) {
dp = aggregationFunctions[i](dp);
}
var lastAgg = _.findLast(target.functions, function(func) {
return _.contains(
_.map(metricFunctions.getCategories()['Aggregate'], 'name'), func.def.name);
});
timeseries_data = [{
target: lastAgg.text,
datapoints: dp
}];
}
// Apply alias functions
var aliasFunctions = bindFunctionDefs(target.functions, 'Alias', DataProcessor);
for (var j = 0; j < aliasFunctions.length; j++) {
_.each(timeseries_data, aliasFunctions[j]);
}
return timeseries_data;
});
});
return self.queryNumericData(target, groupFilter, hostFilter, appFilter, itemFilter,
timeFrom, timeTo, useTrends, options, self);
}
// Query text data
else if (target.mode === 2) {
return self.queryProcessor
.build(groupFilter, hostFilter, appFilter, itemFilter, 'text')
.then(items => {
if (items.length) {
var textItemsPromises = _.map(items, item => {
return self.zabbixAPI.getLastValue(item.itemid);
});
return self.q.all(textItemsPromises)
.then(result => {
return _.map(result, (lastvalue, index) => {
var extractedValue;
if (target.textFilter) {
var text_extract_pattern = new RegExp(self.replaceTemplateVars(target.textFilter, options.scopedVars));
extractedValue = text_extract_pattern.exec(lastvalue);
if (extractedValue) {
if (target.useCaptureGroups) {
extractedValue = extractedValue[1];
} else {
extractedValue = extractedValue[0];
}
}
} else {
extractedValue = lastvalue;
}
return {
target: items[index].name,
datapoints: [[extractedValue, to * 1000]]
};
});
});
} else {
return self.q.when([]);
}
});
return self.queryTextData(target, groupFilter, hostFilter, appFilter, itemFilter,
timeFrom, timeTo, options, self);
}
}
@@ -250,25 +108,27 @@ export class ZabbixAPIDatasource {
// Don't show undefined and hidden targets
if (target.hide || !target.itservice || !target.slaProperty) {
return [];
} else {
return this.zabbixAPI.getSLA(target.itservice.serviceid, from, to)
.then(slaObject => {
return self.queryProcessor.handleSLAResponse(target.itservice, target.slaProperty, slaObject);
});
}
return this.zabbixAPI
.getSLA(target.itservice.serviceid, timeFrom, timeTo)
.then(slaObject => {
return self.queryProcessor
.handleSLAResponse(target.itservice, target.slaProperty, slaObject);
});
}
}, this);
// Data for panel (all targets)
return this.q.all(_.flatten(promises))
.then(_.flatten)
.then(function (timeseries_data) {
.then(timeseries_data => {
// Series downsampling
var data = _.map(timeseries_data, function(timeseries) {
var data = _.map(timeseries_data, timeseries => {
if (timeseries.datapoints.length > options.maxDataPoints) {
timeseries.datapoints =
DataProcessor.groupBy(options.interval, DataProcessor.AVERAGE, timeseries.datapoints);
timeseries.datapoints = DataProcessor
.groupBy(options.interval, DataProcessor.AVERAGE, timeseries.datapoints);
}
return timeseries;
});
@@ -276,6 +136,166 @@ export class ZabbixAPIDatasource {
});
}
queryNumericData(target, groupFilter, hostFilter, appFilter, itemFilter, timeFrom, timeTo, useTrends, options, self) {
// Build query in asynchronous manner
return self.queryProcessor
.build(groupFilter, hostFilter, appFilter, itemFilter, 'num')
.then(items => {
// Add hostname for items from multiple hosts
var addHostName = utils.isRegex(target.host.filter);
var getHistory;
// Use trends
if (useTrends) {
// Find trendValue() function and get specified trend value
var trendFunctions = _.map(metricFunctions.getCategories()['Trends'], 'name');
var trendValueFunc = _.find(target.functions, func => {
return _.contains(trendFunctions, func.def.name);
});
var valueType = trendValueFunc ? trendValueFunc.params[0] : "avg";
getHistory = self.zabbixAPI
.getTrend(items, timeFrom, timeTo)
.then(history => {
return self.queryProcessor.handleTrends(history, items, addHostName, valueType);
});
}
// Use history
else {
getHistory = self.zabbixCache
.getHistory(items, timeFrom, timeTo)
.then(history => {
return self.queryProcessor.handleHistory(history, items, addHostName);
});
}
return getHistory.then(timeseries_data => {
// Apply transformation functions
timeseries_data = _.map(timeseries_data, timeseries => {
// Filter only transform functions
var transformFunctions = bindFunctionDefs(target.functions, 'Transform', DataProcessor);
// Metric data processing
var dp = timeseries.datapoints;
for (var i = 0; i < transformFunctions.length; i++) {
dp = transformFunctions[i](dp);
}
timeseries.datapoints = dp;
return timeseries;
});
// Apply aggregations
var aggregationFunctions = bindFunctionDefs(target.functions, 'Aggregate', DataProcessor);
var dp = _.map(timeseries_data, 'datapoints');
if (aggregationFunctions.length) {
for (var i = 0; i < aggregationFunctions.length; i++) {
dp = aggregationFunctions[i](dp);
}
var lastAgg = _.findLast(target.functions, func => {
return _.contains(
_.map(metricFunctions.getCategories()['Aggregate'], 'name'), func.def.name);
});
timeseries_data = [
{
target: lastAgg.text,
datapoints: dp
}
];
}
// Apply alias functions
var aliasFunctions = bindFunctionDefs(target.functions, 'Alias', DataProcessor);
for (var j = 0; j < aliasFunctions.length; j++) {
_.each(timeseries_data, aliasFunctions[j]);
}
return timeseries_data;
});
});
}
queryTextData(target, groupFilter, hostFilter, appFilter, itemFilter, timeFrom, timeTo, options, self) {
return self.queryProcessor
.build(groupFilter, hostFilter, appFilter, itemFilter, 'text')
.then(items => {
if (items.length) {
var textItemsPromises = _.map(items, item => {
return self.zabbixAPI.getLastValue(item.itemid);
});
return self.q.all(textItemsPromises)
.then(result => {
return _.map(result, (lastvalue, index) => {
var extractedValue;
if (target.textFilter) {
var text_extract_pattern = new RegExp(self.replaceTemplateVars(target.textFilter, options.scopedVars));
extractedValue = text_extract_pattern.exec(lastvalue);
if (extractedValue) {
if (target.useCaptureGroups) {
extractedValue = extractedValue[1];
} else {
extractedValue = extractedValue[0];
}
}
} else {
extractedValue = lastvalue;
}
return {
target: items[index].name,
datapoints: [[extractedValue, timeTo * 1000]]
};
});
});
} else {
return self.q.when([]);
}
});
}
/**
* Test connection to Zabbix API
* @return {object} Connection status and Zabbix API version
*/
testDatasource() {
var self = this;
return this.zabbixAPI.getVersion()
.then(version => {
return self.zabbixAPI.login()
.then(auth => {
if (auth) {
return {
status: "success",
title: "Success",
message: "Zabbix API version: " + version
};
} else {
return {
status: "error",
title: "Invalid user name or password",
message: "Zabbix API version: " + version
};
}
}, error => {
return {
status: "error",
title: error.message,
message: error.data
};
});
}, error => {
console.log(error);
return {
status: "error",
title: "Connection failed",
message: "Could not connect to given url"
};
});
}
////////////////
// Templating //
////////////////
@@ -309,30 +329,35 @@ export class ZabbixAPIDatasource {
if (template.app === '/.*/') {
template.app = '';
}
return this.queryProcessor.getItems(template.group, template.host, template.app)
.then(function(items) {
return this.queryProcessor
.getItems(template.group, template.host, template.app)
.then(items => {
return _.map(items, formatMetric);
});
}
// Get applications
else if (parts.length === 3) {
return this.queryProcessor.getApps(template.group, template.host)
.then(function(apps) {
return this.queryProcessor
.getApps(template.group, template.host)
.then(apps => {
return _.map(apps, formatMetric);
});
}
// Get hosts
else if (parts.length === 2) {
return this.queryProcessor.getHosts(template.group)
.then(function(hosts) {
return this.queryProcessor
.getHosts(template.group)
.then(hosts => {
return _.map(hosts, formatMetric);
});
}
// Get groups
else if (parts.length === 1) {
return this.zabbixCache.getGroups(template.group).then(function(groups) {
return _.map(groups, formatMetric);
});
return this.zabbixCache
.getGroups(template.group)
.then(groups => {
return _.map(groups, formatMetric);
});
}
// Return empty object for invalid request
else {
@@ -348,66 +373,66 @@ export class ZabbixAPIDatasource {
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 self = this;
var showOkEvents = annotation.showOkEvents ? [0, 1] : 1;
// Show all triggers
var showTriggers = [0, 1];
var buildQuery = self.queryProcessor.buildTriggerQuery(this.replaceTemplateVars(annotation.group, {}),
this.replaceTemplateVars(annotation.host, {}),
this.replaceTemplateVars(annotation.application, {}));
return buildQuery.then(function(query) {
return self.zabbixAPI.getTriggers(query.groupids,
query.hostids,
query.applicationids,
showTriggers,
timeFrom, timeTo)
.then(function(triggers) {
var buildQuery = this.queryProcessor
.buildTriggerQuery(this.replaceTemplateVars(annotation.group, {}),
this.replaceTemplateVars(annotation.host, {}),
this.replaceTemplateVars(annotation.application, {}));
var self = this;
return buildQuery.then(query => {
return self.zabbixAPI
.getTriggers(query.groupids, query.hostids, query.applicationids,
showTriggers, timeFrom, timeTo)
.then(triggers => {
// Filter triggers by description
if (utils.isRegex(annotation.trigger)) {
triggers = _.filter(triggers, function(trigger) {
triggers = _.filter(triggers, trigger => {
return utils.buildRegex(annotation.trigger).test(trigger.description);
});
} else if (annotation.trigger) {
triggers = _.filter(triggers, function(trigger) {
triggers = _.filter(triggers, trigger => {
return trigger.description === annotation.trigger;
});
}
// Remove events below the chose severity
triggers = _.filter(triggers, function(trigger) {
triggers = _.filter(triggers, trigger => {
return Number(trigger.priority) >= Number(annotation.minseverity);
});
var objectids = _.map(triggers, 'triggerid');
return self.zabbixAPI.getEvents(objectids, timeFrom, timeTo, showOkEvents)
.then(function (events) {
return self.zabbixAPI
.getEvents(objectids, timeFrom, timeTo, showOkEvents)
.then(events => {
var indexedTriggers = _.indexBy(triggers, 'triggerid');
// Hide acknowledged events if option enabled
if (annotation.hideAcknowledged) {
events = _.filter(events, function(event) {
events = _.filter(events, event => {
return !event.acknowledges.length;
});
}
return _.map(events, function(e) {
return _.map(events, event => {
var title ='';
if (annotation.showHostname) {
title += e.hosts[0].name + ': ';
title += event.hosts[0].name + ': ';
}
// Show event type (OK or Problem)
title += Number(e.value) ? 'Problem' : 'OK';
title += Number(event.value) ? 'Problem' : 'OK';
var formatted_acknowledges = utils.formatAcknowledges(e.acknowledges);
var formatted_acknowledges = utils.formatAcknowledges(event.acknowledges);
return {
annotation: annotation,
time: e.clock * 1000,
time: event.clock * 1000,
title: title,
text: indexedTriggers[e.objectid].description + formatted_acknowledges
text: indexedTriggers[event.objectid].description + formatted_acknowledges
};
});
});

View File

@@ -89,7 +89,6 @@
<input type="text"
ng-model="ctrl.target.group.filter"
bs-typeahead="ctrl.getGroupNames"
ng-change="ctrl.onTargetPartChange(ctrl.target.group)"
ng-blur="ctrl.onTargetBlur()"
data-min-length=0
data-items=100
@@ -106,7 +105,6 @@
<input type="text"
ng-model="ctrl.target.host.filter"
bs-typeahead="ctrl.getHostNames"
ng-change="ctrl.onTargetPartChange(ctrl.target.host)"
ng-blur="ctrl.onTargetBlur()"
data-min-length=0
data-items=100
@@ -123,26 +121,7 @@
change="ctrl.onTargetBlur()">
</editor-checkbox>
</li>
<!-- Downsampling function -->
<!-- <li class="tight-form-item input-medium" ng-hide="ctrl.target.mode == 2">
Downsampling
</li>
<li ng-hide="ctrl.target.mode == 2">
<select class="tight-form-input input-small"
ng-change="ctrl.targetBlur()"
ng-model="ctrl.target.downsampleFunction"
bs-tooltip="'Downsampling function'"
ng-options="func.name for func in downsampleFunctionList track by func.name">
</select>
<a bs-tooltip="ctrl.target.errors.metric"
style="color: rgb(229, 189, 28)"
ng-show="ctrl.target.errors.metric">
<i class="icon-warning-sign"></i>
</a>
</li> -->
</ul>
<div class="clearfix"></div>
</div>
@@ -156,7 +135,6 @@
<input type="text"
ng-model="ctrl.target.application.filter"
bs-typeahead="ctrl.getApplicationNames"
ng-change="ctrl.onTargetPartChange(ctrl.target.application)"
ng-blur="ctrl.onTargetBlur()"
data-min-length=0
data-items=100
@@ -173,7 +151,6 @@
<input type="text"
ng-model="ctrl.target.item.filter"
bs-typeahead="ctrl.getItemNames"
ng-change="ctrl.onTargetPartChange(ctrl.target.item)"
ng-blur="ctrl.onTargetBlur()"
data-min-length=0
data-items=100

View File

@@ -178,26 +178,12 @@ export class ZabbixQueryController extends QueryCtrl {
}
}
onTargetPartChange(targetPart) {
/*var regexStyle = {'color': '#CCA300'};
targetPart.isRegex = utils.isRegex(targetPart.filter);
targetPart.style = targetPart.isRegex ? regexStyle : {};*/
}
isRegex(str) {
return utils.isRegex(str);
}
isVariable(str) {
var variablePattern = /^\$\w+/;
if (variablePattern.test(str)) {
var variables = _.map(this.templateSrv.variables, variable => {
return '$' + variable.name;
});
return _.contains(variables, str);
} else {
return false;
}
return utils.isTemplateVariable(str, this.templateSrv.variables);
}
onTargetBlur() {

View File

@@ -29,6 +29,18 @@ export function isRegex(str) {
return regexPattern.test(str);
}
export function isTemplateVariable(str, templateVariables) {
var variablePattern = /^\$\w+/;
if (variablePattern.test(str)) {
var variables = _.map(templateVariables, variable => {
return '$' + variable.name;
});
return _.contains(variables, str);
} else {
return false;
}
}
export function buildRegex(str) {
var matches = str.match(regexPattern);
var pattern = matches[1];

View File

@@ -10,12 +10,14 @@
<input type="text"
ng-model="editor.panel.triggers.group.filter"
bs-typeahead="editor.getGroupNames"
ng-change="editor.onTargetPartChange(editor.panel.triggers.group)"
ng-blur="editor.parseTarget()"
data-min-length=0
data-items=100
class="input-large tight-form-input"
ng-style="editor.panel.triggers.group.style">
ng-class="{
'zbx-variable': editor.isVariable(editor.panel.triggers.group.filter),
'zbx-regex': editor.isRegex(editor.panel.triggers.group.filter)
}">
</li>
<li class="tight-form-item" style="width: 50px">
Host
@@ -24,12 +26,14 @@
<input type="text"
ng-model="editor.panel.triggers.host.filter"
bs-typeahead="editor.getHostNames"
ng-change="editor.onTargetPartChange(editor.panel.triggers.host)"
ng-blur="editor.parseTarget()"
data-min-length=0
data-items=100
class="input-large tight-form-input last"
ng-style="editor.panel.triggers.host.style">
ng-class="{
'zbx-variable': editor.isVariable(editor.panel.triggers.host.filter),
'zbx-regex': editor.isRegex(editor.panel.triggers.host.filter)
}">
</li>
</ul>
<div class="clearfix"></div>
@@ -43,12 +47,14 @@
<input type="text"
ng-model="editor.panel.triggers.application.filter"
bs-typeahead="editor.getApplicationNames"
ng-change="editor.onTargetPartChange(editor.panel.triggers.application)"
ng-blur="editor.parseTarget()"
data-min-length=0
data-items=100
class="input-large tight-form-input"
ng-style="editor.panel.triggers.application.style">
ng-class="{
'zbx-variable': editor.isVariable(editor.panel.triggers.application.filter),
'zbx-regex': editor.isRegex(editor.panel.triggers.application.filter)
}">
</li>
<li class="tight-form-item" style="width: 50px">
Trigger
@@ -56,7 +62,6 @@
<li>
<input type="text"
ng-model="editor.panel.triggers.trigger.filter"
ng-change="editor.onTargetPartChange(editor.panel.triggers.trigger)"
ng-blur="editor.parseTarget()"
placeholder="trigger name"
class="input-large tight-form-input last"

View File

@@ -12,25 +12,30 @@
*/
import _ from 'lodash';
import $ from 'jquery';
import * as utils from '../datasource-zabbix/utils';
class TriggerPanelEditorCtrl{
import '../datasource-zabbix/css/query-editor.css!';
class TriggerPanelEditorCtrl {
/** @ngInject */
constructor($scope, $q, uiSegmentSrv, datasourceSrv, templateSrv, popoverSrv) {
constructor($scope, $rootScope, $q, uiSegmentSrv, datasourceSrv, templateSrv, popoverSrv) {
$scope.editor = this;
this.panelCtrl = $scope.ctrl;
this.panel = this.panelCtrl.panel;
this.$q = $q;
this.datasourceSrv = datasourceSrv;
this.templateSrv = templateSrv;
this.popoverSrv = popoverSrv;
// Map functions for bs-typeahead
this.getGroupNames = _.partial(getMetricNames, this, 'groupList');
this.getHostNames = _.partial(getMetricNames, this, 'filteredHosts');
this.getApplicationNames = _.partial(getMetricNames, this, 'filteredApplications');
this.getItemNames = _.partial(getMetricNames, this, 'filteredItems');
this.getHostNames = _.partial(getMetricNames, this, 'hostList');
this.getApplicationNames = _.partial(getMetricNames, this, 'appList');
// Update metric suggestion when template variable was changed
$rootScope.$on('template-variable-value-updated', () => this.onVariableChange());
this.ackFilters = [
'all triggers',
@@ -78,40 +83,75 @@ class TriggerPanelEditorCtrl{
}
initFilters() {
this.filterGroups();
this.filterHosts();
this.filterApplications();
var self = this;
return this.$q
.when(this.suggestGroups())
.then(() => {return self.suggestHosts();})
.then(() => {return self.suggestApps();});
}
filterGroups() {
suggestGroups() {
var self = this;
this.datasource.queryProcessor.getGroups().then(function(groups) {
self.metric.groupList = groups;
});
}
filterHosts() {
var self = this;
var groupFilter = this.templateSrv.replace(this.panel.triggers.group.filter);
this.datasource.queryProcessor.getHosts(groupFilter).then(function(hosts) {
self.metric.filteredHosts = hosts;
});
}
filterApplications() {
var self = this;
var groupFilter = this.templateSrv.replace(this.panel.triggers.group.filter);
var hostFilter = this.templateSrv.replace(this.panel.triggers.host.filter);
this.datasource.queryProcessor.getApps(groupFilter, hostFilter)
.then(function(apps) {
self.metric.filteredApplications = apps;
return this.datasource.zabbixCache
.getGroups()
.then(groups => {
self.metric.groupList = groups;
return groups;
});
}
onTargetPartChange(targetPart) {
var regexStyle = {'color': '#CCA300'};
targetPart.isRegex = isRegex(targetPart.filter);
targetPart.style = targetPart.isRegex ? regexStyle : {};
suggestHosts() {
var self = this;
var groupFilter = this.datasource.replaceTemplateVars(this.panel.triggers.group.filter);
return this.datasource.queryProcessor
.filterGroups(self.metric.groupList, groupFilter)
.then(groups => {
var groupids = _.map(groups, 'groupid');
return self.datasource.zabbixAPI
.getHosts(groupids)
.then(hosts => {
self.metric.hostList = hosts;
return hosts;
});
});
}
suggestApps() {
var self = this;
var hostFilter = this.datasource.replaceTemplateVars(this.panel.triggers.host.filter);
return this.datasource.queryProcessor
.filterHosts(self.metric.hostList, hostFilter)
.then(hosts => {
var hostids = _.map(hosts, 'hostid');
return self.datasource.zabbixAPI
.getApps(hostids)
.then(apps => {
return self.metric.appList = apps;
});
});
}
onVariableChange() {
if (this.isContainsVariables()) {
this.targetChanged();
}
}
/**
* Check query for template variables
*/
isContainsVariables() {
var self = this;
return _.some(self.templateSrv.variables, variable => {
return _.some(['group', 'host', 'application'], field => {
return self.templateSrv.containsVariable(self.panel.triggers[field].filter, variable.name);
});
});
}
targetChanged() {
this.initFilters();
this.panelCtrl.refresh();
}
parseTarget() {
@@ -140,36 +180,12 @@ class TriggerPanelEditorCtrl{
this.refreshTriggerSeverity();
}
openTriggerColorSelector(event) {
var el = $(event.currentTarget);
var index = getTriggerIndexForElement(el);
var popoverScope = this.$new();
popoverScope.trigger = this.panel.triggerSeverity[index];
popoverScope.changeTriggerSeverityColor = this.changeTriggerSeverityColor;
this.popoverSrv.show({
element: el,
placement: 'top',
templateUrl: 'public/plugins/alexanderzobnin-zabbix-app/panel-triggers/trigger.colorpicker.html',
scope: popoverScope
});
isRegex(str) {
return utils.isRegex(str);
}
openOkEventColorSelector(event) {
var el = $(event.currentTarget);
var popoverScope = this.$new();
popoverScope.trigger = {color: this.panel.okEventColor};
popoverScope.changeTriggerSeverityColor = function(trigger, color) {
this.panel.okEventColor = color;
this.refreshTriggerSeverity();
};
this.popoverSrv.show({
element: el,
placement: 'top',
templateUrl: 'public/plugins/alexanderzobnin-zabbix-app/panel-triggers/trigger.colorpicker.html',
scope: popoverScope
});
isVariable(str) {
return utils.isTemplateVariable(str, this.templateSrv.variables);
}
}
@@ -178,16 +194,6 @@ function getMetricNames(scope, metricList) {
return _.uniq(_.map(scope.metric[metricList], 'name'));
}
function getTriggerIndexForElement(el) {
return el.parents('[data-trigger-index]').data('trigger-index');
}
function isRegex(str) {
// Pattern for testing regex
var regexPattern = /^\/(.*)\/([gmi]*)$/m;
return regexPattern.test(str);
}
export function triggerPanelEditor() {
return {
restrict: 'E',

View File

@@ -13,6 +13,7 @@
import _ from 'lodash';
import moment from 'moment';
import * as utils from '../datasource-zabbix/utils';
import {MetricsPanelCtrl} from 'app/plugins/sdk';
import {triggerPanelEditor} from './editor';
import './css/panel_triggers.css!';
@@ -66,7 +67,9 @@ class TriggerPanelCtrl extends MetricsPanelCtrl {
this.defaultTimeFormat = defaultTimeFormat;
// Load panel defaults
_.defaults(this.panel, panelDefaults);
// _.cloneDeep() need for prevent changing shared defaultSeverity.
// Load object "by value" istead "by reference".
_.defaults(this.panel, _.cloneDeep(panelDefaults));
this.triggerList = [];
this.refreshData();
@@ -88,15 +91,15 @@ class TriggerPanelCtrl extends MetricsPanelCtrl {
// ignore fetching data if another panel is in fullscreen
if (this.otherPanelInFullscreenMode()) { return; }
this.refreshData();
}
refreshData() {
// clear loading/error state
delete this.error;
this.loading = true;
this.setTimeQueryStart();
this.refreshData();
}
refreshData() {
var self = this;
// Load datasource
@@ -107,9 +110,9 @@ class TriggerPanelCtrl extends MetricsPanelCtrl {
var triggerFilter = self.panel.triggers;
// Replace template variables
var groupFilter = self.templateSrv.replace(triggerFilter.group.filter);
var hostFilter = self.templateSrv.replace(triggerFilter.host.filter);
var appFilter = self.templateSrv.replace(triggerFilter.application.filter);
var groupFilter = datasource.replaceTemplateVars(triggerFilter.group.filter);
var hostFilter = datasource.replaceTemplateVars(triggerFilter.host.filter);
var appFilter = datasource.replaceTemplateVars(triggerFilter.application.filter);
var buildQuery = queryProcessor.buildTriggerQuery(groupFilter, hostFilter, appFilter);
return buildQuery.then(query => {
@@ -208,8 +211,9 @@ class TriggerPanelCtrl extends MetricsPanelCtrl {
// Limit triggers number
self.triggerList = _.first(triggerList, self.panel.limit);
this.setTimeQueryEnd();
this.loading = false;
// Notify panel that request is finished
self.setTimeQueryEnd();
self.loading = false;
});
});
});
@@ -228,9 +232,9 @@ class TriggerPanelCtrl extends MetricsPanelCtrl {
TriggerPanelCtrl.templateUrl = 'panel-triggers/module.html';
function filterTriggers(triggers, triggerFilter) {
if (isRegex(triggerFilter)) {
if (utils.isRegex(triggerFilter)) {
return _.filter(triggers, function(trigger) {
return buildRegex(triggerFilter).test(trigger.description);
return utils.buildRegex(triggerFilter).test(trigger.description);
});
} else {
return _.filter(triggers, function(trigger) {
@@ -239,20 +243,6 @@ function filterTriggers(triggers, triggerFilter) {
}
}
function isRegex(str) {
// Pattern for testing regex
var regexPattern = /^\/(.*)\/([gmi]*)$/m;
return regexPattern.test(str);
}
function buildRegex(str) {
var regexPattern = /^\/(.*)\/([gmi]*)$/m;
var matches = str.match(regexPattern);
var pattern = matches[1];
var flags = matches[2] !== "" ? matches[2] : undefined;
return new RegExp(pattern, flags);
}
export {
TriggerPanelCtrl,
TriggerPanelCtrl as PanelCtrl

View File

@@ -1,13 +0,0 @@
<div class="graph-legend-popover">
<a class="close"
href=""
ng-click="dismiss();">×</a>
<div class="editor-row">
<i ng-repeat="color in colors" class="pointer"
ng-class="{'fa fa-circle-o': color === trigger.color,'fa fa-circle': color !== trigger.color}"
ng-style="{color:color}"
ng-click="changeTriggerSeverityColor(trigger, color);dismiss();">&nbsp;</i>
</div>
</div>

View File

@@ -24,8 +24,15 @@
{"name": "Docs", "url": "http://docs.grafana-zabbix.org"},
{"name": "License", "url": "https://github.com/alexanderzobnin/grafana-zabbix/blob/master/LICENSE.md"}
],
"version": "3.0.0-beta6",
"updated": "2016-04-13"
"screenshots": [
{"name": "Showcase", "path": "https://cloud.githubusercontent.com/assets/4932851/8269101/9e6ee67e-17a3-11e5-85de-fe9dcc2dd375.png"},
{"name": "Dashboard", "path": "https://cloud.githubusercontent.com/assets/4932851/14539264/5ff70b58-0288-11e6-9de3-8aabe8c85e4c.png"},
{"name": "Annotations", "path": "https://cloud.githubusercontent.com/assets/4932851/14539263/5fd8abd6-0288-11e6-9c87-b960c654ab29.png"},
{"name": "Metric Editor", "path": "https://cloud.githubusercontent.com/assets/4932851/14539267/6004cb8a-0288-11e6-9b1f-78ec54409835.png"},
{"name": "Triggers", "path": "https://cloud.githubusercontent.com/assets/4932851/14539266/600495fc-0288-11e6-8d15-308bbef16535.png"}
],
"version": "3.0.0-beta7",
"updated": "2016-04-14"
},
"includes": [
@@ -42,6 +49,12 @@
"name": "Zabbix Server Dashboard",
"path": "dashboards/zabbix_server_dashboard.json",
"addToNav": true
},
{
"type": "dashboard",
"name": "Template Linux Server",
"path": "dashboards/template_linux_server.json",
"addToNav": true
}
],