diff --git a/src/datasource-zabbix/datasource.js b/src/datasource-zabbix/datasource.js index 0e4a419..f055406 100644 --- a/src/datasource-zabbix/datasource.js +++ b/src/datasource-zabbix/datasource.js @@ -6,15 +6,15 @@ import * as migrations from './migrations'; import * as metricFunctions from './metricFunctions'; import DataProcessor from './DataProcessor'; import responseHandler from './responseHandler'; -import './zabbixAPI.service.js'; -import './zabbixCache.service.js'; -import './queryBuilder.js'; +import './zabbix.js'; import {ZabbixAPIError} from './zabbixAPICore.service.js'; class ZabbixAPIDatasource { /** @ngInject */ - constructor(instanceSettings, templateSrv, alertSrv, zabbixAPIService, ZabbixCachingProxy, QueryBuilder) { + constructor(instanceSettings, templateSrv, alertSrv, Zabbix) { + this.templateSrv = templateSrv; + this.alertSrv = alertSrv; // General data source settings this.name = instanceSettings.name; @@ -34,19 +34,7 @@ class ZabbixAPIDatasource { var ttl = instanceSettings.jsonData.cacheTTL || '1h'; this.cacheTTL = utils.parseInterval(ttl); - // Initialize Zabbix API - var ZabbixAPI = zabbixAPIService; - this.zabbixAPI = new ZabbixAPI(this.url, this.username, this.password, this.basicAuth, this.withCredentials); - - // Initialize cache service - this.zabbixCache = new ZabbixCachingProxy(this.zabbixAPI, this.cacheTTL); - - // Initialize query builder - this.queryBuilder = new QueryBuilder(this.zabbixCache); - - // Dependencies - this.templateSrv = templateSrv; - this.alertSrv = alertSrv; + this.zabbix = new Zabbix(this.url, this.username, this.password, this.basicAuth, this.withCredentials, this.cacheTTL); // Use custom format for template variables this.replaceTemplateVars = _.partial(replaceTemplateVars, this.templateSrv); @@ -119,7 +107,7 @@ class ZabbixAPIDatasource { return []; } - return this.zabbixAPI + return this.zabbix .getSLA(target.itservice.serviceid, timeFrom, timeTo) .then(slaObject => { return responseHandler.handleSLAResponse(target.itservice, target.slaProperty, slaObject); @@ -148,7 +136,7 @@ class ZabbixAPIDatasource { let options = { itemtype: 'num' }; - return this.queryBuilder.build(target, options) + return this.zabbix.getItemsFromTarget(target, options) .then(items => { // Add hostname for items from multiple hosts var addHostName = utils.isRegex(target.host.filter); @@ -164,7 +152,7 @@ class ZabbixAPIDatasource { }); var valueType = trendValueFunc ? trendValueFunc.params[0] : "avg"; - getHistory = this.zabbixAPI + getHistory = this.zabbix .getTrend(items, timeFrom, timeTo) .then(history => { return responseHandler.handleTrends(history, items, addHostName, valueType); @@ -173,7 +161,7 @@ class ZabbixAPIDatasource { // Use history else { - getHistory = this.zabbixCache + getHistory = this.zabbix .getHistory(items, timeFrom, timeTo) .then(history => { return responseHandler.handleHistory(history, items, addHostName); @@ -227,10 +215,10 @@ class ZabbixAPIDatasource { let options = { itemtype: 'text' }; - return this.queryBuilder.build(target, options) + return this.zabbix.getItemsFromTarget(target, options) .then(items => { if (items.length) { - return this.zabbixAPI.getHistory(items, timeFrom, timeTo) + return this.zabbix.getHistory(items, timeFrom, timeTo) .then(history => { return responseHandler.convertHistory(history, items, false, (point) => { let value = point.value; @@ -255,10 +243,10 @@ class ZabbixAPIDatasource { */ testDatasource() { let zabbixVersion; - return this.zabbixAPI.getVersion() + return this.zabbix.getVersion() .then(version => { zabbixVersion = version; - return this.zabbixAPI.login(); + return this.zabbix.login(); }) .then(() => { return { @@ -317,16 +305,16 @@ class ZabbixAPIDatasource { if (template.app === '/.*/') { template.app = ''; } - result = this.queryBuilder.getItems(template.group, template.host, template.app, template.item); + result = this.zabbix.getItems(template.group, template.host, template.app, template.item); } else if (parts.length === 3) { // Get applications - result = this.queryBuilder.getApps(template.group, template.host, template.app); + result = this.zabbix.getApps(template.group, template.host, template.app); } else if (parts.length === 2) { // Get hosts - result = this.queryBuilder.getHosts(template.group, template.host); + result = this.zabbix.getHosts(template.group, template.host); } else if (parts.length === 1) { // Get groups - result = this.zabbixCache.getGroups(template.group); + result = this.zabbix.getGroups(template.group); } else { result = Promise.resolve([]); } @@ -349,64 +337,61 @@ class ZabbixAPIDatasource { // Show all triggers var showTriggers = [0, 1]; - var buildQuery = this.queryBuilder - .buildTriggerQuery(this.replaceTemplateVars(annotation.group, {}), - this.replaceTemplateVars(annotation.host, {}), - this.replaceTemplateVars(annotation.application, {})); + var getTriggers = this.zabbix + .getTriggers(this.replaceTemplateVars(annotation.group, {}), + this.replaceTemplateVars(annotation.host, {}), + this.replaceTemplateVars(annotation.application, {}), + showTriggers); - return buildQuery.then(query => { - return this.zabbixAPI - .getTriggers(query.groupids, query.hostids, query.applicationids, showTriggers) - .then(triggers => { + return getTriggers.then(triggers => { - // Filter triggers by description - if (utils.isRegex(annotation.trigger)) { - triggers = _.filter(triggers, trigger => { - return utils.buildRegex(annotation.trigger).test(trigger.description); - }); - } else if (annotation.trigger) { - triggers = _.filter(triggers, trigger => { - return trigger.description === annotation.trigger; + // Filter triggers by description + if (utils.isRegex(annotation.trigger)) { + triggers = _.filter(triggers, trigger => { + return utils.buildRegex(annotation.trigger).test(trigger.description); + }); + } else if (annotation.trigger) { + triggers = _.filter(triggers, trigger => { + return trigger.description === annotation.trigger; + }); + } + + // Remove events below the chose severity + triggers = _.filter(triggers, trigger => { + return Number(trigger.priority) >= Number(annotation.minseverity); + }); + + var objectids = _.map(triggers, 'triggerid'); + return this.zabbix + .getEvents(objectids, timeFrom, timeTo, showOkEvents) + .then(events => { + var indexedTriggers = _.keyBy(triggers, 'triggerid'); + + // Hide acknowledged events if option enabled + if (annotation.hideAcknowledged) { + events = _.filter(events, event => { + return !event.acknowledges.length; }); } - // Remove events below the chose severity - triggers = _.filter(triggers, trigger => { - return Number(trigger.priority) >= Number(annotation.minseverity); + return _.map(events, event => { + let tags; + if (annotation.showHostname) { + tags = _.map(event.hosts, 'name'); + } + + // Show event type (OK or Problem) + let title = Number(event.value) ? 'Problem' : 'OK'; + + let formatted_acknowledges = utils.formatAcknowledges(event.acknowledges); + return { + annotation: annotation, + time: event.clock * 1000, + title: title, + tags: tags, + text: indexedTriggers[event.objectid].description + formatted_acknowledges + }; }); - - var objectids = _.map(triggers, 'triggerid'); - return this.zabbixAPI - .getEvents(objectids, timeFrom, timeTo, showOkEvents) - .then(events => { - var indexedTriggers = _.keyBy(triggers, 'triggerid'); - - // Hide acknowledged events if option enabled - if (annotation.hideAcknowledged) { - events = _.filter(events, event => { - return !event.acknowledges.length; - }); - } - - return _.map(events, event => { - let tags; - if (annotation.showHostname) { - tags = _.map(event.hosts, 'name'); - } - - // Show event type (OK or Problem) - let title = Number(event.value) ? 'Problem' : 'OK'; - - let formatted_acknowledges = utils.formatAcknowledges(event.acknowledges); - return { - annotation: annotation, - time: event.clock * 1000, - title: title, - tags: tags, - text: indexedTriggers[event.objectid].description + formatted_acknowledges - }; - }); - }); }); }); } diff --git a/src/datasource-zabbix/queryBuilder.js b/src/datasource-zabbix/zabbix.js similarity index 72% rename from src/datasource-zabbix/queryBuilder.js rename to src/datasource-zabbix/zabbix.js index 8188e59..7500b91 100644 --- a/src/datasource-zabbix/queryBuilder.js +++ b/src/datasource-zabbix/zabbix.js @@ -1,45 +1,40 @@ import angular from 'angular'; import _ from 'lodash'; import * as utils from './utils'; +import './zabbixAPI.service.js'; +import './zabbixCache.service.js'; -function QueryBuilderFactory() { +// Use factory() instead service() for multiple data sources support. +// Each Zabbix data source instance should initialize its own API instance. - class QueryBuilder { - constructor(zabbixCacheInstance) { - this.cache = zabbixCacheInstance; +/** @ngInject */ +function ZabbixFactory(zabbixAPIService, ZabbixCachingProxy) { + + class Zabbix { + constructor(url, username, password, basicAuth, withCredentials, cacheTTL) { + + // Initialize Zabbix API + var ZabbixAPI = zabbixAPIService; + this.zabbixAPI = new ZabbixAPI(url, username, password, basicAuth, withCredentials); + + // Initialize cache + this.cache = new ZabbixCachingProxy(this.zabbixAPI, cacheTTL); + + // Proxy methods + this.getHistory = this.cache.getHistory.bind(this.cache); + + this.getTrend = this.zabbixAPI.getTrend.bind(this.zabbixAPI); + this.getEvents = this.zabbixAPI.getEvents.bind(this.zabbixAPI); + this.getAcknowledges = this.zabbixAPI.getAcknowledges.bind(this.zabbixAPI); + this.getSLA = this.zabbixAPI.getSLA.bind(this.zabbixAPI); + this.getVersion = this.zabbixAPI.getVersion.bind(this.zabbixAPI); + this.login = this.zabbixAPI.login.bind(this.zabbixAPI); } - initializeCache() { - if (this.cache._initialized) { - return Promise.resolve(); - } else { - return this.cache.refresh(); - } - } - - /** - * Build query - convert target filters to array of Zabbix items - */ - build(target, options) { - function getFiltersFromTarget(target) { - let parts = ['group', 'host', 'application', 'item']; - return _.map(parts, p => target[p].filter); - } - - return this.initializeCache() - .then(() => { - return this.getItems(...getFiltersFromTarget(target), options); - }); - } - - /** - * Build trigger query in asynchronous manner - */ - buildTriggerQuery(groupFilter, hostFilter, appFilter) { - return this.initializeCache() - .then(() => { - return this.buildTriggerQueryFromCache(groupFilter, hostFilter, appFilter); - }); + getItemsFromTarget(target, options) { + let parts = ['group', 'host', 'application', 'item']; + let filters = _.map(parts, p => target[p].filter); + return this.getItems(...filters, options); } getAllGroups() { @@ -120,7 +115,7 @@ function QueryBuilderFactory() { /** * Build query - convert target filters to array of Zabbix items */ - buildTriggerQueryFromCache(groupFilter, hostFilter, appFilter) { + getTriggers(groupFilter, hostFilter, appFilter, showTriggers) { let promises = [ this.getGroups(groupFilter), this.getHosts(groupFilter, hostFilter), @@ -145,16 +140,21 @@ function QueryBuilderFactory() { } return query; + }).then(query => { + return this.zabbixAPI + .getTriggers(query.groupids, query.hostids, query.applicationids, showTriggers); }); } } - return QueryBuilder; + return Zabbix; } angular .module('grafana.services') - .factory('QueryBuilder', QueryBuilderFactory); + .factory('Zabbix', ZabbixFactory); + +/////////////////////////////////////////////////////////////////////////////// /** * Find group, host, app or item by given name. diff --git a/src/panel-triggers/module.js b/src/panel-triggers/module.js index 33d7bd8..7ccbfd5 100644 --- a/src/panel-triggers/module.js +++ b/src/panel-triggers/module.js @@ -106,9 +106,9 @@ class TriggerPanelCtrl extends MetricsPanelCtrl { var self = this; // Load datasource - return this.datasourceSrv.get(this.panel.datasource).then(datasource => { - var zabbix = datasource.zabbixAPI; - var queryBuilder = datasource.queryBuilder; + return this.datasourceSrv.get(this.panel.datasource) + .then(datasource => { + var zabbix = datasource.zabbix; var showEvents = self.panel.showEvents.value; var triggerFilter = self.panel.triggers; @@ -117,118 +117,112 @@ class TriggerPanelCtrl extends MetricsPanelCtrl { var hostFilter = datasource.replaceTemplateVars(triggerFilter.host.filter); var appFilter = datasource.replaceTemplateVars(triggerFilter.application.filter); - var buildQuery = queryBuilder.buildTriggerQuery(groupFilter, hostFilter, appFilter); - return buildQuery.then(query => { - return zabbix.getTriggers(query.groupids, - query.hostids, - query.applicationids, - showEvents) - .then(triggers => { - return _.map(triggers, trigger => { - let triggerObj = trigger; + var getTriggers = zabbix.getTriggers(groupFilter, hostFilter, appFilter, showEvents); + return getTriggers.then(triggers => { + return _.map(triggers, trigger => { + let triggerObj = trigger; - // Format last change and age - trigger.lastchangeUnix = Number(trigger.lastchange); - let timestamp = moment.unix(trigger.lastchangeUnix); - if (self.panel.customLastChangeFormat) { - // User defined format - triggerObj.lastchange = timestamp.format(self.panel.lastChangeFormat); - } else { - triggerObj.lastchange = timestamp.format(self.defaultTimeFormat); - } - triggerObj.age = timestamp.fromNow(true); + // Format last change and age + trigger.lastchangeUnix = Number(trigger.lastchange); + let timestamp = moment.unix(trigger.lastchangeUnix); + if (self.panel.customLastChangeFormat) { + // User defined format + triggerObj.lastchange = timestamp.format(self.panel.lastChangeFormat); + } else { + triggerObj.lastchange = timestamp.format(self.defaultTimeFormat); + } + triggerObj.age = timestamp.fromNow(true); - // Set host that the trigger belongs - if (trigger.hosts.length) { - triggerObj.host = trigger.hosts[0].name; - triggerObj.hostTechName = trigger.hosts[0].host; - } + // Set host that the trigger belongs + if (trigger.hosts.length) { + triggerObj.host = trigger.hosts[0].name; + triggerObj.hostTechName = trigger.hosts[0].host; + } - // Set color - if (trigger.value === '1') { - // Problem state - triggerObj.color = self.panel.triggerSeverity[trigger.priority].color; - } else { - // OK state - triggerObj.color = self.panel.okEventColor; - } + // Set color + if (trigger.value === '1') { + // Problem state + triggerObj.color = self.panel.triggerSeverity[trigger.priority].color; + } else { + // OK state + triggerObj.color = self.panel.okEventColor; + } - triggerObj.severity = self.panel.triggerSeverity[trigger.priority].severity; - return triggerObj; - }); - }) - .then(triggerList => { + triggerObj.severity = self.panel.triggerSeverity[trigger.priority].severity; + return triggerObj; + }); + }) + .then(triggerList => { - // Request acknowledges for trigger - var eventids = _.map(triggerList, trigger => { - return trigger.lastEvent.eventid; + // Request acknowledges for trigger + var eventids = _.map(triggerList, trigger => { + return trigger.lastEvent.eventid; + }); + + return zabbix.getAcknowledges(eventids) + .then(events => { + + // Map events to triggers + _.each(triggerList, trigger => { + var event = _.find(events, event => { + return event.eventid === trigger.lastEvent.eventid; }); - return zabbix.getAcknowledges(eventids) - .then(events => { - - // Map events to triggers - _.each(triggerList, trigger => { - var event = _.find(events, event => { - return event.eventid === trigger.lastEvent.eventid; - }); - - if (event) { - trigger.acknowledges = _.map(event.acknowledges, ack => { - let timestamp = moment.unix(ack.clock); - if (self.panel.customLastChangeFormat) { - ack.time = timestamp.format(self.panel.lastChangeFormat); - } else { - ack.time = timestamp.format(self.defaultTimeFormat); - } - ack.user = ack.alias + ' (' + ack.name + ' ' + ack.surname + ')'; - return ack; - }); - - // Mark acknowledged triggers with different color - if (self.panel.markAckEvents && trigger.acknowledges.length) { - trigger.color = self.panel.ackEventColor; - } - } - }); - - // Filter triggers by description - var triggerFilter = self.panel.triggers.trigger.filter; - if (triggerFilter) { - triggerList = filterTriggers(triggerList, triggerFilter); - } - - // Filter acknowledged triggers - if (self.panel.showTriggers === 'unacknowledged') { - triggerList = _.filter(triggerList, trigger => { - return !trigger.acknowledges; - }); - } else if (self.panel.showTriggers === 'acknowledged') { - triggerList = _.filter(triggerList, 'acknowledges'); + if (event) { + trigger.acknowledges = _.map(event.acknowledges, ack => { + let timestamp = moment.unix(ack.clock); + if (self.panel.customLastChangeFormat) { + ack.time = timestamp.format(self.panel.lastChangeFormat); } else { - triggerList = triggerList; + ack.time = timestamp.format(self.defaultTimeFormat); } - - // Filter triggers by severity - triggerList = _.filter(triggerList, trigger => { - return self.panel.triggerSeverity[trigger.priority].show; - }); - - // Sort triggers - if (self.panel.sortTriggersBy.value === 'priority') { - triggerList = _.sortBy(triggerList, 'priority').reverse(); - } else { - triggerList = _.sortBy(triggerList, 'lastchangeUnix').reverse(); - } - - // Limit triggers number - self.triggerList = triggerList.slice(0, self.panel.limit); - - // Notify panel that request is finished - self.setTimeQueryEnd(); - self.loading = false; + ack.user = ack.alias + ' (' + ack.name + ' ' + ack.surname + ')'; + return ack; }); + + // Mark acknowledged triggers with different color + if (self.panel.markAckEvents && trigger.acknowledges.length) { + trigger.color = self.panel.ackEventColor; + } + } }); + + // Filter triggers by description + var triggerFilter = self.panel.triggers.trigger.filter; + if (triggerFilter) { + triggerList = filterTriggers(triggerList, triggerFilter); + } + + // Filter acknowledged triggers + if (self.panel.showTriggers === 'unacknowledged') { + triggerList = _.filter(triggerList, trigger => { + return !trigger.acknowledges; + }); + } else if (self.panel.showTriggers === 'acknowledged') { + triggerList = _.filter(triggerList, 'acknowledges'); + } else { + triggerList = triggerList; + } + + // Filter triggers by severity + triggerList = _.filter(triggerList, trigger => { + return self.panel.triggerSeverity[trigger.priority].show; + }); + + // Sort triggers + if (self.panel.sortTriggersBy.value === 'priority') { + triggerList = _.sortBy(triggerList, 'priority').reverse(); + } else { + triggerList = _.sortBy(triggerList, 'lastchangeUnix').reverse(); + } + + // Limit triggers number + self.triggerList = triggerList.slice(0, self.panel.limit); + + // Notify panel that request is finished + self.setTimeQueryEnd(); + self.loading = false; + }); }); }); }