diff --git a/src/datasource-zabbix/datasource.js b/src/datasource-zabbix/datasource.js index 4c7ccfd..a3f1f12 100644 --- a/src/datasource-zabbix/datasource.js +++ b/src/datasource-zabbix/datasource.js @@ -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 }; }); });