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/) Zabbix plugin allows to show different type of data from [Zabbix](http://www.zabbix.com/)
monitoring system. monitoring system.
![Dashboard](https://cloud.githubusercontent.com/assets/4932851/8269101/9e6ee67e-17a3-11e5-85de-fe9dcc2dd375.png)
### Live Demo ### 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 ### 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, "pointradius": 5,
"points": false, "points": false,
"renderer": "flot", "renderer": "flot",
"seriesOverrides": [], "seriesOverrides": [
{
"alias": "/user/",
"color": "#1F78C1"
},
{
"alias": "/system/",
"color": "#BF1B00"
},
{
"alias": "/iowait/",
"color": "#E5AC0E"
}
],
"span": 7, "span": 7,
"stack": true, "stack": true,
"steppedLine": false, "steppedLine": false,
@@ -286,7 +299,7 @@
}, },
"yaxes": [ "yaxes": [
{ {
"format": "short", "format": "percent",
"label": null, "label": null,
"logBase": 1, "logBase": 1,
"max": null, "max": null,
@@ -324,7 +337,7 @@
"scroll": true, "scroll": true,
"showHeader": true, "showHeader": true,
"sort": { "sort": {
"col": 0, "col": 2,
"desc": true "desc": true
}, },
"span": 5, "span": 5,
@@ -335,17 +348,20 @@
"type": "date" "type": "date"
}, },
{ {
"colorMode": null, "colorMode": "cell",
"colors": [ "colors": [
"rgba(245, 54, 54, 0.9)", "rgb(41, 170, 106)",
"rgba(237, 129, 40, 0.89)", "rgba(239, 148, 21, 0.89)",
"rgba(50, 172, 45, 0.97)" "rgba(239, 10, 10, 0.9)"
], ],
"decimals": 2, "decimals": 1,
"pattern": "/.*/", "pattern": "/.*/",
"thresholds": [], "thresholds": [
"50",
"80"
],
"type": "number", "type": "number",
"unit": "short" "unit": "percent"
} }
], ],
"targets": [ "targets": [
@@ -373,6 +389,207 @@
} }
], ],
"title": "Row" "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": { "time": {
@@ -411,6 +628,6 @@
"list": [] "list": []
}, },
"schemaVersion": 12, "schemaVersion": 12,
"version": 5, "version": 6,
"links": [] "links": []
} }

View File

@@ -57,46 +57,6 @@ export class ZabbixAPIDatasource {
// Datasource methods // // 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. * Query panel data. Calls for each panel in dashboard.
* @param {Object} options Contains time range, targets and other info. * @param {Object} options Contains time range, targets and other info.
@@ -106,12 +66,13 @@ export class ZabbixAPIDatasource {
var self = this; var self = this;
// get from & to in seconds // get from & to in seconds
var from = Math.ceil(dateMath.parse(options.range.from) / 1000); var timeFrom = Math.ceil(dateMath.parse(options.range.from) / 1000);
var to = Math.ceil(dateMath.parse(options.range.to) / 1000); var timeTo = Math.ceil(dateMath.parse(options.range.to) / 1000);
var useTrendsFrom = Math.ceil(dateMath.parse('now-' + this.trendsFrom) / 1000); var useTrendsFrom = Math.ceil(dateMath.parse('now-' + this.trendsFrom) / 1000);
var useTrends = (timeFrom < useTrendsFrom) && this.trends;
// Create request for each target // Create request for each target
var promises = _.map(options.targets, function(target) { var promises = _.map(options.targets, target => {
if (target.mode !== 1) { if (target.mode !== 1) {
@@ -119,8 +80,7 @@ export class ZabbixAPIDatasource {
target = migrations.migrate(target); target = migrations.migrate(target);
// Don't request undefined and hidden targets // Don't request undefined and hidden targets
if (target.hide || !target.group || if (target.hide || !target.group || !target.host || !target.item) {
!target.host || !target.item) {
return []; return [];
} }
@@ -132,7 +92,51 @@ export class ZabbixAPIDatasource {
// Query numeric data // Query numeric data
if (!target.mode || target.mode === 0) { if (!target.mode || target.mode === 0) {
return self.queryNumericData(target, groupFilter, hostFilter, appFilter, itemFilter,
timeFrom, timeTo, useTrends, options, self);
}
// Query text data
else if (target.mode === 2) {
return self.queryTextData(target, groupFilter, hostFilter, appFilter, itemFilter,
timeFrom, timeTo, options, self);
}
}
// 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.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(timeseries_data => {
// Series downsampling
var data = _.map(timeseries_data, timeseries => {
if (timeseries.datapoints.length > options.maxDataPoints) {
timeseries.datapoints = DataProcessor
.groupBy(options.interval, DataProcessor.AVERAGE, timeseries.datapoints);
}
return timeseries;
});
return { data: data };
});
}
queryNumericData(target, groupFilter, hostFilter, appFilter, itemFilter, timeFrom, timeTo, useTrends, options, self) {
// Build query in asynchronous manner // Build query in asynchronous manner
return self.queryProcessor return self.queryProcessor
.build(groupFilter, hostFilter, appFilter, itemFilter, 'num') .build(groupFilter, hostFilter, appFilter, itemFilter, 'num')
@@ -142,28 +146,35 @@ export class ZabbixAPIDatasource {
var getHistory; var getHistory;
// Use trends // Use trends
if ((from < useTrendsFrom) && self.trends) { if (useTrends) {
// Find trendValue() function and get specified trend value // Find trendValue() function and get specified trend value
var trendFunctions = _.map(metricFunctions.getCategories()['Trends'], 'name'); var trendFunctions = _.map(metricFunctions.getCategories()['Trends'], 'name');
var trendValueFunc = _.find(target.functions, function(func) { var trendValueFunc = _.find(target.functions, func => {
return _.contains(trendFunctions, func.def.name); return _.contains(trendFunctions, func.def.name);
}); });
var valueType = trendValueFunc ? trendValueFunc.params[0] : "avg"; var valueType = trendValueFunc ? trendValueFunc.params[0] : "avg";
getHistory = self.zabbixAPI.getTrend(items, from, to).then(function(history) { getHistory = self.zabbixAPI
.getTrend(items, timeFrom, timeTo)
.then(history => {
return self.queryProcessor.handleTrends(history, items, addHostName, valueType); return self.queryProcessor.handleTrends(history, items, addHostName, valueType);
}); });
} else { }
// Use history // Use history
getHistory = self.zabbixCache.getHistory(items, from, to).then(function(history) { else {
getHistory = self.zabbixCache
.getHistory(items, timeFrom, timeTo)
.then(history => {
return self.queryProcessor.handleHistory(history, items, addHostName); return self.queryProcessor.handleHistory(history, items, addHostName);
}); });
} }
return getHistory.then(function (timeseries_data) { return getHistory.then(timeseries_data => {
timeseries_data = _.map(timeseries_data, function (timeseries) {
// Apply transformation functions
timeseries_data = _.map(timeseries_data, timeseries => {
// Filter only transform functions // Filter only transform functions
var transformFunctions = bindFunctionDefs(target.functions, 'Transform', DataProcessor); var transformFunctions = bindFunctionDefs(target.functions, 'Transform', DataProcessor);
@@ -178,21 +189,23 @@ export class ZabbixAPIDatasource {
return timeseries; return timeseries;
}); });
// Aggregations // Apply aggregations
var aggregationFunctions = bindFunctionDefs(target.functions, 'Aggregate', DataProcessor); var aggregationFunctions = bindFunctionDefs(target.functions, 'Aggregate', DataProcessor);
var dp = _.map(timeseries_data, 'datapoints'); var dp = _.map(timeseries_data, 'datapoints');
if (aggregationFunctions.length) { if (aggregationFunctions.length) {
for (var i = 0; i < aggregationFunctions.length; i++) { for (var i = 0; i < aggregationFunctions.length; i++) {
dp = aggregationFunctions[i](dp); dp = aggregationFunctions[i](dp);
} }
var lastAgg = _.findLast(target.functions, function(func) { var lastAgg = _.findLast(target.functions, func => {
return _.contains( return _.contains(
_.map(metricFunctions.getCategories()['Aggregate'], 'name'), func.def.name); _.map(metricFunctions.getCategories()['Aggregate'], 'name'), func.def.name);
}); });
timeseries_data = [{ timeseries_data = [
{
target: lastAgg.text, target: lastAgg.text,
datapoints: dp datapoints: dp
}]; }
];
} }
// Apply alias functions // Apply alias functions
@@ -206,8 +219,7 @@ export class ZabbixAPIDatasource {
}); });
} }
// Query text data queryTextData(target, groupFilter, hostFilter, appFilter, itemFilter, timeFrom, timeTo, options, self) {
else if (target.mode === 2) {
return self.queryProcessor return self.queryProcessor
.build(groupFilter, hostFilter, appFilter, itemFilter, 'text') .build(groupFilter, hostFilter, appFilter, itemFilter, 'text')
.then(items => { .then(items => {
@@ -234,7 +246,7 @@ export class ZabbixAPIDatasource {
} }
return { return {
target: items[index].name, target: items[index].name,
datapoints: [[extractedValue, to * 1000]] datapoints: [[extractedValue, timeTo * 1000]]
}; };
}); });
}); });
@@ -243,36 +255,44 @@ export class ZabbixAPIDatasource {
} }
}); });
} }
}
// IT services mode /**
else if (target.mode === 1) { * Test connection to Zabbix API
// Don't show undefined and hidden targets * @return {object} Connection status and Zabbix API version
if (target.hide || !target.itservice || !target.slaProperty) { */
return []; 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 { } else {
return this.zabbixAPI.getSLA(target.itservice.serviceid, from, to) return {
.then(slaObject => { status: "error",
return self.queryProcessor.handleSLAResponse(target.itservice, target.slaProperty, slaObject); 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);
}, this); return {
status: "error",
// Data for panel (all targets) title: "Connection failed",
return this.q.all(_.flatten(promises)) message: "Could not connect to given url"
.then(_.flatten) };
.then(function (timeseries_data) {
// Series downsampling
var data = _.map(timeseries_data, function(timeseries) {
if (timeseries.datapoints.length > options.maxDataPoints) {
timeseries.datapoints =
DataProcessor.groupBy(options.interval, DataProcessor.AVERAGE, timeseries.datapoints);
}
return timeseries;
});
return { data: data };
}); });
} }
@@ -309,28 +329,33 @@ export class ZabbixAPIDatasource {
if (template.app === '/.*/') { if (template.app === '/.*/') {
template.app = ''; template.app = '';
} }
return this.queryProcessor.getItems(template.group, template.host, template.app) return this.queryProcessor
.then(function(items) { .getItems(template.group, template.host, template.app)
.then(items => {
return _.map(items, formatMetric); return _.map(items, formatMetric);
}); });
} }
// Get applications // Get applications
else if (parts.length === 3) { else if (parts.length === 3) {
return this.queryProcessor.getApps(template.group, template.host) return this.queryProcessor
.then(function(apps) { .getApps(template.group, template.host)
.then(apps => {
return _.map(apps, formatMetric); return _.map(apps, formatMetric);
}); });
} }
// Get hosts // Get hosts
else if (parts.length === 2) { else if (parts.length === 2) {
return this.queryProcessor.getHosts(template.group) return this.queryProcessor
.then(function(hosts) { .getHosts(template.group)
.then(hosts => {
return _.map(hosts, formatMetric); return _.map(hosts, formatMetric);
}); });
} }
// Get groups // Get groups
else if (parts.length === 1) { else if (parts.length === 1) {
return this.zabbixCache.getGroups(template.group).then(function(groups) { return this.zabbixCache
.getGroups(template.group)
.then(groups => {
return _.map(groups, formatMetric); return _.map(groups, formatMetric);
}); });
} }
@@ -348,66 +373,66 @@ export class ZabbixAPIDatasource {
var timeFrom = Math.ceil(dateMath.parse(options.rangeRaw.from) / 1000); var timeFrom = Math.ceil(dateMath.parse(options.rangeRaw.from) / 1000);
var timeTo = Math.ceil(dateMath.parse(options.rangeRaw.to) / 1000); var timeTo = Math.ceil(dateMath.parse(options.rangeRaw.to) / 1000);
var annotation = options.annotation; var annotation = options.annotation;
var self = this;
var showOkEvents = annotation.showOkEvents ? [0, 1] : 1; var showOkEvents = annotation.showOkEvents ? [0, 1] : 1;
// Show all triggers // Show all triggers
var showTriggers = [0, 1]; var showTriggers = [0, 1];
var buildQuery = self.queryProcessor.buildTriggerQuery(this.replaceTemplateVars(annotation.group, {}), var buildQuery = this.queryProcessor
.buildTriggerQuery(this.replaceTemplateVars(annotation.group, {}),
this.replaceTemplateVars(annotation.host, {}), this.replaceTemplateVars(annotation.host, {}),
this.replaceTemplateVars(annotation.application, {})); this.replaceTemplateVars(annotation.application, {}));
return buildQuery.then(function(query) { var self = this;
return self.zabbixAPI.getTriggers(query.groupids, return buildQuery.then(query => {
query.hostids, return self.zabbixAPI
query.applicationids, .getTriggers(query.groupids, query.hostids, query.applicationids,
showTriggers, showTriggers, timeFrom, timeTo)
timeFrom, timeTo) .then(triggers => {
.then(function(triggers) {
// Filter triggers by description // Filter triggers by description
if (utils.isRegex(annotation.trigger)) { if (utils.isRegex(annotation.trigger)) {
triggers = _.filter(triggers, function(trigger) { triggers = _.filter(triggers, trigger => {
return utils.buildRegex(annotation.trigger).test(trigger.description); return utils.buildRegex(annotation.trigger).test(trigger.description);
}); });
} else if (annotation.trigger) { } else if (annotation.trigger) {
triggers = _.filter(triggers, function(trigger) { triggers = _.filter(triggers, trigger => {
return trigger.description === annotation.trigger; return trigger.description === annotation.trigger;
}); });
} }
// Remove events below the chose severity // Remove events below the chose severity
triggers = _.filter(triggers, function(trigger) { triggers = _.filter(triggers, trigger => {
return Number(trigger.priority) >= Number(annotation.minseverity); return Number(trigger.priority) >= Number(annotation.minseverity);
}); });
var objectids = _.map(triggers, 'triggerid'); var objectids = _.map(triggers, 'triggerid');
return self.zabbixAPI.getEvents(objectids, timeFrom, timeTo, showOkEvents) return self.zabbixAPI
.then(function (events) { .getEvents(objectids, timeFrom, timeTo, showOkEvents)
.then(events => {
var indexedTriggers = _.indexBy(triggers, 'triggerid'); var indexedTriggers = _.indexBy(triggers, 'triggerid');
// Hide acknowledged events if option enabled // Hide acknowledged events if option enabled
if (annotation.hideAcknowledged) { if (annotation.hideAcknowledged) {
events = _.filter(events, function(event) { events = _.filter(events, event => {
return !event.acknowledges.length; return !event.acknowledges.length;
}); });
} }
return _.map(events, function(e) { return _.map(events, event => {
var title =''; var title ='';
if (annotation.showHostname) { if (annotation.showHostname) {
title += e.hosts[0].name + ': '; title += event.hosts[0].name + ': ';
} }
// Show event type (OK or Problem) // 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 { return {
annotation: annotation, annotation: annotation,
time: e.clock * 1000, time: event.clock * 1000,
title: title, 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" <input type="text"
ng-model="ctrl.target.group.filter" ng-model="ctrl.target.group.filter"
bs-typeahead="ctrl.getGroupNames" bs-typeahead="ctrl.getGroupNames"
ng-change="ctrl.onTargetPartChange(ctrl.target.group)"
ng-blur="ctrl.onTargetBlur()" ng-blur="ctrl.onTargetBlur()"
data-min-length=0 data-min-length=0
data-items=100 data-items=100
@@ -106,7 +105,6 @@
<input type="text" <input type="text"
ng-model="ctrl.target.host.filter" ng-model="ctrl.target.host.filter"
bs-typeahead="ctrl.getHostNames" bs-typeahead="ctrl.getHostNames"
ng-change="ctrl.onTargetPartChange(ctrl.target.host)"
ng-blur="ctrl.onTargetBlur()" ng-blur="ctrl.onTargetBlur()"
data-min-length=0 data-min-length=0
data-items=100 data-items=100
@@ -123,26 +121,7 @@
change="ctrl.onTargetBlur()"> change="ctrl.onTargetBlur()">
</editor-checkbox> </editor-checkbox>
</li> </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> </ul>
<div class="clearfix"></div> <div class="clearfix"></div>
</div> </div>
@@ -156,7 +135,6 @@
<input type="text" <input type="text"
ng-model="ctrl.target.application.filter" ng-model="ctrl.target.application.filter"
bs-typeahead="ctrl.getApplicationNames" bs-typeahead="ctrl.getApplicationNames"
ng-change="ctrl.onTargetPartChange(ctrl.target.application)"
ng-blur="ctrl.onTargetBlur()" ng-blur="ctrl.onTargetBlur()"
data-min-length=0 data-min-length=0
data-items=100 data-items=100
@@ -173,7 +151,6 @@
<input type="text" <input type="text"
ng-model="ctrl.target.item.filter" ng-model="ctrl.target.item.filter"
bs-typeahead="ctrl.getItemNames" bs-typeahead="ctrl.getItemNames"
ng-change="ctrl.onTargetPartChange(ctrl.target.item)"
ng-blur="ctrl.onTargetBlur()" ng-blur="ctrl.onTargetBlur()"
data-min-length=0 data-min-length=0
data-items=100 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) { isRegex(str) {
return utils.isRegex(str); return utils.isRegex(str);
} }
isVariable(str) { isVariable(str) {
var variablePattern = /^\$\w+/; return utils.isTemplateVariable(str, this.templateSrv.variables);
if (variablePattern.test(str)) {
var variables = _.map(this.templateSrv.variables, variable => {
return '$' + variable.name;
});
return _.contains(variables, str);
} else {
return false;
}
} }
onTargetBlur() { onTargetBlur() {

View File

@@ -29,6 +29,18 @@ export function isRegex(str) {
return regexPattern.test(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) { export function buildRegex(str) {
var matches = str.match(regexPattern); var matches = str.match(regexPattern);
var pattern = matches[1]; var pattern = matches[1];

View File

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

View File

@@ -12,25 +12,30 @@
*/ */
import _ from 'lodash'; import _ from 'lodash';
import $ from 'jquery'; import * as utils from '../datasource-zabbix/utils';
import '../datasource-zabbix/css/query-editor.css!';
class TriggerPanelEditorCtrl { class TriggerPanelEditorCtrl {
/** @ngInject */ /** @ngInject */
constructor($scope, $q, uiSegmentSrv, datasourceSrv, templateSrv, popoverSrv) { constructor($scope, $rootScope, $q, uiSegmentSrv, datasourceSrv, templateSrv, popoverSrv) {
$scope.editor = this; $scope.editor = this;
this.panelCtrl = $scope.ctrl; this.panelCtrl = $scope.ctrl;
this.panel = this.panelCtrl.panel; this.panel = this.panelCtrl.panel;
this.$q = $q;
this.datasourceSrv = datasourceSrv; this.datasourceSrv = datasourceSrv;
this.templateSrv = templateSrv; this.templateSrv = templateSrv;
this.popoverSrv = popoverSrv; this.popoverSrv = popoverSrv;
// Map functions for bs-typeahead // Map functions for bs-typeahead
this.getGroupNames = _.partial(getMetricNames, this, 'groupList'); this.getGroupNames = _.partial(getMetricNames, this, 'groupList');
this.getHostNames = _.partial(getMetricNames, this, 'filteredHosts'); this.getHostNames = _.partial(getMetricNames, this, 'hostList');
this.getApplicationNames = _.partial(getMetricNames, this, 'filteredApplications'); this.getApplicationNames = _.partial(getMetricNames, this, 'appList');
this.getItemNames = _.partial(getMetricNames, this, 'filteredItems');
// Update metric suggestion when template variable was changed
$rootScope.$on('template-variable-value-updated', () => this.onVariableChange());
this.ackFilters = [ this.ackFilters = [
'all triggers', 'all triggers',
@@ -78,40 +83,75 @@ class TriggerPanelEditorCtrl{
} }
initFilters() { initFilters() {
this.filterGroups(); var self = this;
this.filterHosts(); return this.$q
this.filterApplications(); .when(this.suggestGroups())
.then(() => {return self.suggestHosts();})
.then(() => {return self.suggestApps();});
} }
filterGroups() { suggestGroups() {
var self = this; var self = this;
this.datasource.queryProcessor.getGroups().then(function(groups) { return this.datasource.zabbixCache
.getGroups()
.then(groups => {
self.metric.groupList = groups; self.metric.groupList = groups;
return groups;
}); });
} }
filterHosts() { suggestHosts() {
var self = this; var self = this;
var groupFilter = this.templateSrv.replace(this.panel.triggers.group.filter); var groupFilter = this.datasource.replaceTemplateVars(this.panel.triggers.group.filter);
this.datasource.queryProcessor.getHosts(groupFilter).then(function(hosts) { return this.datasource.queryProcessor
self.metric.filteredHosts = hosts; .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;
});
}); });
} }
filterApplications() { suggestApps() {
var self = this; var self = this;
var groupFilter = this.templateSrv.replace(this.panel.triggers.group.filter); var hostFilter = this.datasource.replaceTemplateVars(this.panel.triggers.host.filter);
var hostFilter = this.templateSrv.replace(this.panel.triggers.host.filter); return this.datasource.queryProcessor
this.datasource.queryProcessor.getApps(groupFilter, hostFilter) .filterHosts(self.metric.hostList, hostFilter)
.then(function(apps) { .then(hosts => {
self.metric.filteredApplications = apps; var hostids = _.map(hosts, 'hostid');
return self.datasource.zabbixAPI
.getApps(hostids)
.then(apps => {
return self.metric.appList = apps;
});
}); });
} }
onTargetPartChange(targetPart) { onVariableChange() {
var regexStyle = {'color': '#CCA300'}; if (this.isContainsVariables()) {
targetPart.isRegex = isRegex(targetPart.filter); this.targetChanged();
targetPart.style = targetPart.isRegex ? regexStyle : {}; }
}
/**
* 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() { parseTarget() {
@@ -140,36 +180,12 @@ class TriggerPanelEditorCtrl{
this.refreshTriggerSeverity(); this.refreshTriggerSeverity();
} }
openTriggerColorSelector(event) { isRegex(str) {
var el = $(event.currentTarget); return utils.isRegex(str);
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
});
} }
openOkEventColorSelector(event) { isVariable(str) {
var el = $(event.currentTarget); return utils.isTemplateVariable(str, this.templateSrv.variables);
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
});
} }
} }
@@ -178,16 +194,6 @@ function getMetricNames(scope, metricList) {
return _.uniq(_.map(scope.metric[metricList], 'name')); 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() { export function triggerPanelEditor() {
return { return {
restrict: 'E', restrict: 'E',

View File

@@ -13,6 +13,7 @@
import _ from 'lodash'; import _ from 'lodash';
import moment from 'moment'; import moment from 'moment';
import * as utils from '../datasource-zabbix/utils';
import {MetricsPanelCtrl} from 'app/plugins/sdk'; import {MetricsPanelCtrl} from 'app/plugins/sdk';
import {triggerPanelEditor} from './editor'; import {triggerPanelEditor} from './editor';
import './css/panel_triggers.css!'; import './css/panel_triggers.css!';
@@ -66,7 +67,9 @@ class TriggerPanelCtrl extends MetricsPanelCtrl {
this.defaultTimeFormat = defaultTimeFormat; this.defaultTimeFormat = defaultTimeFormat;
// Load panel defaults // 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.triggerList = [];
this.refreshData(); this.refreshData();
@@ -88,15 +91,15 @@ class TriggerPanelCtrl extends MetricsPanelCtrl {
// ignore fetching data if another panel is in fullscreen // ignore fetching data if another panel is in fullscreen
if (this.otherPanelInFullscreenMode()) { return; } if (this.otherPanelInFullscreenMode()) { return; }
this.refreshData();
}
refreshData() {
// clear loading/error state // clear loading/error state
delete this.error; delete this.error;
this.loading = true; this.loading = true;
this.setTimeQueryStart(); this.setTimeQueryStart();
this.refreshData();
}
refreshData() {
var self = this; var self = this;
// Load datasource // Load datasource
@@ -107,9 +110,9 @@ class TriggerPanelCtrl extends MetricsPanelCtrl {
var triggerFilter = self.panel.triggers; var triggerFilter = self.panel.triggers;
// Replace template variables // Replace template variables
var groupFilter = self.templateSrv.replace(triggerFilter.group.filter); var groupFilter = datasource.replaceTemplateVars(triggerFilter.group.filter);
var hostFilter = self.templateSrv.replace(triggerFilter.host.filter); var hostFilter = datasource.replaceTemplateVars(triggerFilter.host.filter);
var appFilter = self.templateSrv.replace(triggerFilter.application.filter); var appFilter = datasource.replaceTemplateVars(triggerFilter.application.filter);
var buildQuery = queryProcessor.buildTriggerQuery(groupFilter, hostFilter, appFilter); var buildQuery = queryProcessor.buildTriggerQuery(groupFilter, hostFilter, appFilter);
return buildQuery.then(query => { return buildQuery.then(query => {
@@ -208,8 +211,9 @@ class TriggerPanelCtrl extends MetricsPanelCtrl {
// Limit triggers number // Limit triggers number
self.triggerList = _.first(triggerList, self.panel.limit); self.triggerList = _.first(triggerList, self.panel.limit);
this.setTimeQueryEnd(); // Notify panel that request is finished
this.loading = false; self.setTimeQueryEnd();
self.loading = false;
}); });
}); });
}); });
@@ -228,9 +232,9 @@ class TriggerPanelCtrl extends MetricsPanelCtrl {
TriggerPanelCtrl.templateUrl = 'panel-triggers/module.html'; TriggerPanelCtrl.templateUrl = 'panel-triggers/module.html';
function filterTriggers(triggers, triggerFilter) { function filterTriggers(triggers, triggerFilter) {
if (isRegex(triggerFilter)) { if (utils.isRegex(triggerFilter)) {
return _.filter(triggers, function(trigger) { return _.filter(triggers, function(trigger) {
return buildRegex(triggerFilter).test(trigger.description); return utils.buildRegex(triggerFilter).test(trigger.description);
}); });
} else { } else {
return _.filter(triggers, function(trigger) { 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 { export {
TriggerPanelCtrl, TriggerPanelCtrl,
TriggerPanelCtrl as PanelCtrl 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": "Docs", "url": "http://docs.grafana-zabbix.org"},
{"name": "License", "url": "https://github.com/alexanderzobnin/grafana-zabbix/blob/master/LICENSE.md"} {"name": "License", "url": "https://github.com/alexanderzobnin/grafana-zabbix/blob/master/LICENSE.md"}
], ],
"version": "3.0.0-beta6", "screenshots": [
"updated": "2016-04-13" {"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": [ "includes": [
@@ -42,6 +49,12 @@
"name": "Zabbix Server Dashboard", "name": "Zabbix Server Dashboard",
"path": "dashboards/zabbix_server_dashboard.json", "path": "dashboards/zabbix_server_dashboard.json",
"addToNav": true "addToNav": true
},
{
"type": "dashboard",
"name": "Template Linux Server",
"path": "dashboards/template_linux_server.json",
"addToNav": true
} }
], ],