diff --git a/zabbix/datasource.js b/zabbix/datasource.js index bb3d3ed..ddbdc1c 100644 --- a/zabbix/datasource.js +++ b/zabbix/datasource.js @@ -25,6 +25,10 @@ function (angular, _, kbn) { this.username = datasource.meta.username; this.password = datasource.meta.password; + // Use trends instead history since specified time + this.trends = datasource.meta.trends; + this.trendsFrom = datasource.meta.trendsFrom || '7d'; + // Limit metrics per panel for templated request this.limitmetrics = datasource.meta.limitmetrics || 50; } @@ -44,6 +48,7 @@ function (angular, _, kbn) { // get from & to in seconds var from = Math.ceil(kbn.parseDate(options.range.from).getTime() / 1000); var to = Math.ceil(kbn.parseDate(options.range.to).getTime() / 1000); + var useTrendsFrom = Math.ceil(kbn.parseDate('now-' + this.trendsFrom).getTime() / 1000); // Create request for each target var promises = _.map(options.targets, function(target) { @@ -93,8 +98,14 @@ function (angular, _, kbn) { return []; } else { items = _.flatten(items); - return self.performTimeSeriesQuery(items, from, to) - .then(_.partial(self.handleHistoryResponse, items)); + + if ((from < useTrendsFrom) && self.trends) { + return self.getTrends(items, from, to) + .then(_.partial(self.handleTrendResponse, items)); + } else { + return self.performTimeSeriesQuery(items, from, to) + .then(_.partial(self.handleHistoryResponse, items)); + } } }); }, this); @@ -142,6 +153,58 @@ function (angular, _, kbn) { }; + ZabbixAPIDatasource.prototype.getTrends = function(items, start, end) { + // Group items by value type + var grouped_items = _.groupBy(items, 'value_type'); + + // Perform request for each value type + return $q.all(_.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: start + }; + + // Relative queries (e.g. last hour) don't include an end time + if (end) { + params.time_till = end; + } + + return this.performZabbixAPIRequest('trend.get', params); + }, this)).then(function (results) { + return _.flatten(results); + }); + }; + + + ZabbixAPIDatasource.prototype.handleTrendResponse = function(items, trends) { + + // Group items and trends by itemid + var indexed_items = _.indexBy(items, 'itemid'); + var grouped_history = _.groupBy(trends, 'itemid'); + + return $q.when(_.map(grouped_history, function (trends, itemid) { + var item = indexed_items[itemid]; + var series = { + target: (item.hosts ? item.hosts[0].name+': ' : '') + expandItemName(item), + datapoints: _.map(trends, function (p) { + + // Value must be a number for properly work + var value = Number(p.value_avg); + return [value, p.clock * 1000]; + }) + }; + return series; + })).then(function (result) { + return _.sortBy(result, 'target'); + }); + }; + + /** * Convert Zabbix API data to Grafana format * @@ -618,31 +681,37 @@ function (angular, _, kbn) { search: { 'description': annotation.query }, + searchWildcardsEnabled: true, + expandDescription: true }; return this.performZabbixAPIRequest('trigger.get', params) .then(function (result) { if(result) { - var obs = {}; - obs = _.indexBy(result, 'triggerid'); - + var objects = _.indexBy(result, 'triggerid'); var params = { output: 'extend', - sortorder: 'DESC', time_from: from, time_till: to, - objectids: _.keys(obs) + objectids: _.keys(objects), + select_acknowledges: 'extend' }; + // Show problem events only + if (!annotation.showOkEvents) { + params.value = 1; + } + return self.performZabbixAPIRequest('event.get', params) .then(function (result) { var events = []; _.each(result, function(e) { + var formatted_acknowledges = formatAcknowledges(e.acknowledges);; events.push({ annotation: annotation, time: e.clock * 1000, - title: obs[e.objectid].description, - text: e.eventid, + title: Number(e.value) ? 'Problem' : 'OK', + text: objects[e.objectid].description + formatted_acknowledges, }); }); return events; @@ -692,4 +761,44 @@ function expandItemName(item) { name = name.replace('$' + i, key_params[i - 1]); }; return name; -}; \ No newline at end of file +} + + +/** + * Convert Date object to local time in format + * YYYY-MM-DD HH:mm:ss + * + * @param {Date} date Date object + * @return {string} formatted local time YYYY-MM-DD HH:mm:ss + */ +function getShortTime(date) { + var MM = date.getMonth() < 10 ? '0' + date.getMonth() : date.getMonth(); + var DD = date.getDate() < 10 ? '0' + date.getDate() : date.getDate(); + var HH = date.getHours() < 10 ? '0' + date.getHours() : date.getHours(); + var mm = date.getMinutes() < 10 ? '0' + date.getMinutes() : date.getMinutes(); + var ss = date.getSeconds() < 10 ? '0' + date.getSeconds() : date.getSeconds(); + return date.getFullYear() + '-' + MM + '-' + DD + ' ' + HH + ':' + mm + ':' + ss; +} + + +/** + * Format acknowledges. + * + * @param {array} acknowledges array of Zabbix acknowledge objects + * @return {string} HTML-formatted table + */ +function formatAcknowledges(acknowledges) { + if (acknowledges.length) { + var formatted_acknowledges = '

Acknowledges:
'; + _.each(_.map(acknowledges, function (ack) { + var time = new Date(ack.clock * 1000); + return ''; + }), function (ack) { + formatted_acknowledges = formatted_acknowledges.concat(ack) + }); + formatted_acknowledges = formatted_acknowledges.concat('
TimeUserComments
' + getShortTime(time) + '' + ack.alias + ' (' + ack.name+ ' ' + ack.surname + ')' + '' + ack.message + '
') + return formatted_acknowledges; + } else { + return ''; + } +} diff --git a/zabbix/partials/annotations.editor.html b/zabbix/partials/annotations.editor.html index eb65f53..1dcfe6f 100644 --- a/zabbix/partials/annotations.editor.html +++ b/zabbix/partials/annotations.editor.html @@ -2,7 +2,15 @@
Zabbix trigger Example: Lack of free swap space
- +
+ +
+
+
Options
+ + +
+
diff --git a/zabbix/plugin.json b/zabbix/plugin.json index 75f81cf..5d10ced 100644 --- a/zabbix/plugin.json +++ b/zabbix/plugin.json @@ -16,6 +16,9 @@ "username": "guest", "password": "", + "trends": true, + "trendsFrom": "7d", + "limitmetrics": 50, "metrics": true,