diff --git a/.gitignore b/.gitignore index e368599..5593838 100644 --- a/.gitignore +++ b/.gitignore @@ -12,3 +12,18 @@ # Builded docs docs/site/ + +node_modules +npm-debug.log +coverage/ +.aws-config.json +awsconfig +/emails/dist +/public_gen +/tmp +vendor/phantomjs/phantomjs + +dist/ + +# locally required config files +public/css/*.min.css diff --git a/.jshintrc b/.jshintrc index 9924799..36c9c9f 100644 --- a/.jshintrc +++ b/.jshintrc @@ -4,7 +4,7 @@ "bitwise":false, "curly": true, "eqnull": true, - "globalstrict": true, + "strict": true, "devel": true, "eqeqeq": true, "forin": false, @@ -12,7 +12,7 @@ "supernew": true, "expr": true, "indent": 2, - "latedef": true, + "latedef": false, "newcap": true, "noarg": true, "noempty": true, @@ -25,9 +25,11 @@ "unused": true, "maxdepth": 6, "maxlen": 140, + "esnext": true, "globals": { "System": true, + "Promise": true, "define": true, "require": true, "Chromath": false, diff --git a/Gruntfile.js b/Gruntfile.js new file mode 100644 index 0000000..2c8f35a --- /dev/null +++ b/Gruntfile.js @@ -0,0 +1,62 @@ +module.exports = function(grunt) { + + require('load-grunt-tasks')(grunt); + + grunt.loadNpmTasks('grunt-execute'); + grunt.loadNpmTasks('grunt-contrib-clean'); + + grunt.initConfig({ + + clean: ["dist"], + + copy: { + src_to_dist: { + cwd: 'src', + expand: true, + src: [ + '**/*', + '!**/datasource.js', + '!**/module.js', + '!**/*.scss' + ], + dest: 'dist/src' + }, + pluginDef: { + expand: true, + src: ['plugin.json', 'README.md'], + dest: 'dist', + } + }, + + watch: { + rebuild_all: { + files: ['src/**/*', 'plugin.json'], + tasks: ['default'], + options: {spawn: false} + }, + }, + + babel: { + options: { + sourceMap: true, + presets: ["es2015"], + plugins: ['transform-es2015-modules-systemjs', "transform-es2015-for-of"], + }, + dist: { + files: [{ + cwd: 'src', + expand: true, + src: [ + '**/**/datasource.js', + '**/**/module.js', + ], + dest: 'dist/src', + ext:'.js' + }] + }, + }, + + }); + + grunt.registerTask('default', ['clean', 'copy:src_to_dist', 'copy:pluginDef', 'babel']); +}; diff --git a/package.json b/package.json new file mode 100644 index 0000000..64cde5b --- /dev/null +++ b/package.json @@ -0,0 +1,37 @@ +{ + "name": "grafana-zabbix", + "private": false, + "version": "3.0.0", + "description": "", + "scripts": { + "test": "echo \"Error: no test specified\" && exit 1" + }, + "repository": { + "type": "git", + "url": "git+https://github.com/alexanderzobnin/grafana-zabbix.git" + }, + "author": "Alexander Zobnin", + "license": "Apache-2.0", + "bugs": { + "url": "https://github.com/alexanderzobnin/grafana-zabbix/issues" + }, + "devDependencies": { + "grunt": "~0.4.5", + "babel": "~6.5.1", + "grunt-babel": "~6.0.0", + "grunt-contrib-copy": "~0.8.2", + "grunt-contrib-watch": "^0.6.1", + "grunt-contrib-uglify": "~0.11.0", + "grunt-systemjs-builder": "^0.2.5", + "load-grunt-tasks": "~3.2.0", + "grunt-execute": "~0.2.2", + "grunt-contrib-clean": "~0.6.0" + }, + "dependencies": { + "babel-plugin-transform-es2015-modules-systemjs": "^6.5.0", + "babel-plugin-transform-es2015-for-of": "^6.5.0", + "babel-preset-es2015": "^6.5.0", + "lodash": "~4.0.0" + }, + "homepage": "http://grafana-zabbix.org" +} diff --git a/plugins/datasource-zabbix/datasource.js b/plugins/datasource-zabbix/datasource.js deleted file mode 100644 index d089444..0000000 --- a/plugins/datasource-zabbix/datasource.js +++ /dev/null @@ -1,429 +0,0 @@ -define([ - 'angular', - 'lodash', - 'app/core/utils/datemath', - './utils', - './metricFunctions', - './queryProcessor', - './directives', - './zabbixAPI', - './helperFunctions', - './dataProcessingService', - './zabbixCache', - './queryCtrl', - './addMetricFunction', - './metricFunctionEditor' -], -function (angular, _, dateMath, utils, metricFunctions) { - 'use strict'; - - /** @ngInject */ - function ZabbixAPIDatasource(instanceSettings, $q, templateSrv, alertSrv, zabbixHelperSrv, - ZabbixAPI, ZabbixCachingProxy, QueryProcessor, DataProcessingService) { - - // General data source settings - this.name = instanceSettings.name; - this.url = instanceSettings.url; - this.basicAuth = instanceSettings.basicAuth; - this.withCredentials = instanceSettings.withCredentials; - - // Zabbix API credentials - this.username = instanceSettings.jsonData.username; - this.password = instanceSettings.jsonData.password; - - // Use trends instead history since specified time - this.trends = instanceSettings.jsonData.trends; - this.trendsFrom = instanceSettings.jsonData.trendsFrom || '7d'; - - // Set cache update interval - var ttl = instanceSettings.jsonData.cacheTTL || '1h'; - this.cacheTTL = utils.parseInterval(ttl); - - // Initialize Zabbix API - 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.queryProcessor = new QueryProcessor(this.zabbixCache); - - console.log(this.zabbixCache); - - //////////////////////// - // Datasource methods // - //////////////////////// - - /** - * Test connection to Zabbix API - * @return {object} Connection status and Zabbix API version - */ - this.testDatasource = function() { - 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. - * @return {Object} Grafana metrics object with timeseries data for each target. - */ - this.query = function(options) { - 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 useTrendsFrom = Math.ceil(dateMath.parse('now-' + this.trendsFrom) / 1000); - - // Create request for each target - var promises = _.map(options.targets, function(target) { - - if (target.mode !== 1) { - - // Don't request undefined and hidden targets - if (target.hide || !target.group || - !target.host || !target.item) { - return []; - } - - // Replace templated variables - var groupFilter = templateSrv.replace(target.group.filter, options.scopedVars); - var hostFilter = templateSrv.replace(target.host.filter, options.scopedVars); - var appFilter = templateSrv.replace(target.application.filter, options.scopedVars); - var itemFilter = templateSrv.replace(target.item.filter, options.scopedVars); - - // Query numeric data - if (!target.mode || target.mode === 0) { - - // Build query in asynchronous manner - return self.queryProcessor.build(groupFilter, hostFilter, appFilter, itemFilter) - .then(function(items) { - // Add hostname for items from multiple hosts - var addHostName = target.host.isRegex; - 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, addHostName, valueType); - }); - } else { - - // Use history - getHistory = self.zabbixCache.getHistory(items, from, to).then(function(history) { - return self.queryProcessor.handleHistory(history, addHostName); - }); - } - - return getHistory.then(function (timeseries_data) { - timeseries_data = _.map(timeseries_data, function (timeseries) { - - // Filter only transform functions - var transformFunctions = bindFunctionDefs(target.functions, 'Transform'); - - // 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'); - 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'); - for (var j = 0; j < aliasFunctions.length; j++) { - _.each(timeseries_data, aliasFunctions[j]); - } - - return timeseries_data; - }); - }); - } - - // Query text data - else if (target.mode === 2) { - return self.queryProcessor.build(groupFilter, hostFilter, appFilter, itemFilter) - .then(function(items) { - var deferred = $q.defer(); - if (items.length) { - self.zabbixAPI.getLastValue(items[0].itemid).then(function(lastvalue) { - if (target.textFilter) { - var text_extract_pattern = new RegExp(templateSrv.replace(target.textFilter, options.scopedVars)); - var result = text_extract_pattern.exec(lastvalue); - if (result) { - if (target.useCaptureGroups) { - result = result[1]; - } else { - result = result[0]; - } - } - deferred.resolve(result); - } else { - deferred.resolve(lastvalue); - } - }); - } else { - deferred.resolve(null); - } - return deferred.promise.then(function(text) { - return { - target: target.item.name, - datapoints: [[text, to * 1000]] - }; - }); - }); - } - } - - // IT services mode - else if (target.mode === 1) { - // 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(_.bind(zabbixHelperSrv.handleSLAResponse, zabbixHelperSrv, target.itservice, target.slaProperty)); - } - } - }, this); - - // Data for panel (all targets) - return $q.all(_.flatten(promises)) - .then(_.flatten) - .then(function (timeseries_data) { - - // Series downsampling - var data = _.map(timeseries_data, function(timeseries) { - var DPS = DataProcessingService; - if (timeseries.datapoints.length > options.maxDataPoints) { - timeseries.datapoints = DPS.groupBy(options.interval, DPS.AVERAGE, timeseries.datapoints); - } - return timeseries; - }); - return { data: data }; - }); - }; - - function bindFunctionDefs(functionDefs, category) { - var aggregationFunctions = _.map(metricFunctions.getCategories()[category], 'name'); - var aggFuncDefs = _.filter(functionDefs, function(func) { - return _.contains(aggregationFunctions, func.def.name); - }); - return _.map(aggFuncDefs, function(func) { - var funcInstance = metricFunctions.createFuncInstance(func.def, func.params); - return funcInstance.bindFunction(DataProcessingService.metricFunctions); - }); - } - - //////////////// - // Templating // - //////////////// - - /** - * Find metrics from templated request. - * - * @param {string} query Query from Templating - * @return {string} Metric name - group, host, app or item or list - * of metrics in "{metric1,metcic2,...,metricN}" format. - */ - this.metricFindQuery = function (query) { - // Split query. Query structure: - // group.host.app.item - var parts = []; - _.each(query.split('.'), function (part) { - part = templateSrv.replace(part); - - // Replace wildcard to regex - if (part === '*') { - part = '/.*/'; - } - parts.push(part); - }); - var template = _.object(['group', 'host', 'app', 'item'], parts); - - // Get items - if (parts.length === 4) { - return this.queryProcessor.filterItems(template.group, template.host, - template.app, 'all', true) - .then(function(items) { - return _.map(items, formatMetric); - }); - } - // Get applications - else if (parts.length === 3) { - return this.queryProcessor.filterApplications(template.group, template.host) - .then(function(apps) { - return _.map(apps, formatMetric); - }); - } - // Get hosts - else if (parts.length === 2) { - return this.queryProcessor.filterHosts(template.group) - .then(function(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 empty object for invalid request - else { - return $q.when([]); - } - }; - - function formatMetric(metricObj) { - return { - text: metricObj.name, - expandable: false - }; - } - - ///////////////// - // Annotations // - ///////////////// - - this.annotationQuery = function(options) { - var from = Math.ceil(dateMath.parse(options.rangeRaw.from) / 1000); - var to = Math.ceil(dateMath.parse(options.rangeRaw.to) / 1000); - var annotation = options.annotation; - var self = this; - var showEvents = annotation.showOkEvents ? [0, 1] : 1; - - var buildQuery = self.queryProcessor.buildTriggerQuery(templateSrv.replace(annotation.group), - templateSrv.replace(annotation.host), - templateSrv.replace(annotation.application)); - return buildQuery.then(function(query) { - return self.zabbixAPI.getTriggers(query.groupids, - query.hostids, - query.applicationids, - showEvents) - .then(function(triggers) { - - // Filter triggers by description - if (utils.isRegex(annotation.trigger)) { - triggers = _.filter(triggers, function(trigger) { - return utils.buildRegex(annotation.trigger).test(trigger.description); - }); - } else if (annotation.trigger) { - triggers = _.filter(triggers, function(trigger) { - return trigger.description === annotation.trigger; - }); - } - - // Remove events below the chose severity - triggers = _.filter(triggers, function(trigger) { - return Number(trigger.priority) >= Number(annotation.minseverity); - }); - - var objectids = _.map(triggers, 'triggerid'); - var params = { - output: 'extend', - time_from: from, - time_till: to, - objectids: objectids, - select_acknowledges: 'extend', - selectHosts: 'extend', - value: showEvents - }; - - return self.zabbixAPI.request('event.get', params) - .then(function (events) { - var indexedTriggers = _.indexBy(triggers, 'triggerid'); - - // Hide acknowledged events if option enabled - if (annotation.hideAcknowledged) { - events = _.filter(events, function(event) { - return !event.acknowledges.length; - }); - } - - return _.map(events, function(e) { - var title =''; - if (annotation.showHostname) { - title += e.hosts[0].name + ': '; - } - - // Show event type (OK or Problem) - title += Number(e.value) ? 'Problem' : 'OK'; - - var formatted_acknowledges = utils.formatAcknowledges(e.acknowledges); - return { - annotation: annotation, - time: e.clock * 1000, - title: title, - text: indexedTriggers[e.objectid].description + formatted_acknowledges - }; - }); - }); - }); - }); - }; - - } - - return ZabbixAPIDatasource; - -}); diff --git a/plugins/datasource-zabbix/module.js b/plugins/datasource-zabbix/module.js deleted file mode 100644 index 38722d9..0000000 --- a/plugins/datasource-zabbix/module.js +++ /dev/null @@ -1,24 +0,0 @@ -define([ - './datasource', - './queryCtrl' -], -function (ZabbixAPIDatasource, ZabbixQueryCtrl) { - 'use strict'; - - function ZabbixQueryOptionsCtrl() {} - ZabbixQueryOptionsCtrl.templateUrl = 'partials/query.options.html'; - - function ZabbixAnnotationsQueryCtrl() {} - ZabbixAnnotationsQueryCtrl.templateUrl = 'partials/annotations.editor.html'; - - function ZabbixConfigCtrl() {} - ZabbixConfigCtrl.templateUrl = 'partials/config.html'; - - return { - Datasource: ZabbixAPIDatasource, - QueryCtrl: ZabbixQueryCtrl, - ConfigCtrl: ZabbixConfigCtrl, - QueryOptionsCtrl: ZabbixQueryOptionsCtrl, - AnnotationsQueryCtrl: ZabbixAnnotationsQueryCtrl - }; -}); diff --git a/plugins/datasource-zabbix/addMetricFunction.js b/src/datasource-zabbix/addMetricFunction.js similarity index 100% rename from plugins/datasource-zabbix/addMetricFunction.js rename to src/datasource-zabbix/addMetricFunction.js diff --git a/plugins/datasource-zabbix/dataProcessingService.js b/src/datasource-zabbix/dataProcessingService.js similarity index 100% rename from plugins/datasource-zabbix/dataProcessingService.js rename to src/datasource-zabbix/dataProcessingService.js diff --git a/src/datasource-zabbix/datasource.js b/src/datasource-zabbix/datasource.js new file mode 100644 index 0000000..f9699f3 --- /dev/null +++ b/src/datasource-zabbix/datasource.js @@ -0,0 +1,430 @@ +//import angular from 'angular'; +import _ from 'lodash'; +import {parse as dateMathParse} from 'app/core/utils/datemath'; +import Utils from './utils'; +import metricFunctions from './metricFunctions'; +import {zabbixHelperSrv} from './helperFunctions'; +import {ZabbixAPI} from './zabbixAPI'; +import {ZabbixCachingProxy} from './zabbixCache'; +import {QueryProcessor} from './queryProcessor'; +import {DataProcessingService} from './dataProcessingService'; + +export class ZabbixAPIDatasource { + + constructor(instanceSettings, $q, templateSrv, alertSrv, ZabbixAPI, ZabbixCachingProxy, QueryProcessor, zabbixHelperSrv, DataProcessingService) { + + // General data source settings + this.name = instanceSettings.name; + this.url = instanceSettings.url; + this.basicAuth = instanceSettings.basicAuth; + this.withCredentials = instanceSettings.withCredentials; + + // Zabbix API credentials + this.username = instanceSettings.jsonData.username; + this.password = instanceSettings.jsonData.password; + + // Use trends instead history since specified time + this.trends = instanceSettings.jsonData.trends; + this.trendsFrom = instanceSettings.jsonData.trendsFrom || '7d'; + + // Set cache update interval + var ttl = instanceSettings.jsonData.cacheTTL || '1h'; + this.cacheTTL = Utils.parseInterval(ttl); + + // Initialize Zabbix API + 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.queryProcessor = new QueryProcessor(this.zabbixCache); + + // Dependencies + this.q = $q; + this.templateSrv = templateSrv; + this.alertSrv = alertSrv; + this.zabbixHelperSrv = zabbixHelperSrv; + this.DataProcessingService = DataProcessingService; + + console.log(this.zabbixCache); + } + + //////////////////////// + // 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. + * @return {Object} Grafana metrics object with timeseries data for each target. + */ + query(options) { + var self = this; + + // get from & to in seconds + var from = Math.ceil(dateMathParse(options.range.from) / 1000); + var to = Math.ceil(dateMathParse(options.range.to) / 1000); + var useTrendsFrom = Math.ceil(dateMathParse('now-' + this.trendsFrom) / 1000); + + // Create request for each target + var promises = _.map(options.targets, function(target) { + + if (target.mode !== 1) { + + // Don't request undefined and hidden targets + if (target.hide || !target.group || + !target.host || !target.item) { + return []; + } + + // Replace templated variables + var groupFilter = this.templateSrv.replace(target.group.filter, options.scopedVars); + var hostFilter = this.templateSrv.replace(target.host.filter, options.scopedVars); + var appFilter = this.templateSrv.replace(target.application.filter, options.scopedVars); + var itemFilter = this.templateSrv.replace(target.item.filter, options.scopedVars); + + // Query numeric data + if (!target.mode || target.mode === 0) { + + // Build query in asynchronous manner + return self.queryProcessor.build(groupFilter, hostFilter, appFilter, itemFilter) + .then(function(items) { + // Add hostname for items from multiple hosts + var addHostName = target.host.isRegex; + 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, addHostName, valueType); + }); + } else { + + // Use history + getHistory = self.zabbixCache.getHistory(items, from, to).then(function(history) { + return self.queryProcessor.handleHistory(history, addHostName); + }); + } + + return getHistory.then(function (timeseries_data) { + timeseries_data = _.map(timeseries_data, function (timeseries) { + + // Filter only transform functions + var transformFunctions = bindFunctionDefs(target.functions, 'Transform', self.DataProcessingService); + + // 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', self.DataProcessingService); + 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', self.DataProcessingService); + for (var j = 0; j < aliasFunctions.length; j++) { + _.each(timeseries_data, aliasFunctions[j]); + } + + return timeseries_data; + }); + }); + } + + // Query text data + else if (target.mode === 2) { + return self.queryProcessor.build(groupFilter, hostFilter, appFilter, itemFilter) + .then(function(items) { + var deferred = self.q.defer(); + if (items.length) { + self.zabbixAPI.getLastValue(items[0].itemid).then(function(lastvalue) { + if (target.textFilter) { + var text_extract_pattern = new RegExp(self.templateSrv.replace(target.textFilter, options.scopedVars)); + var result = text_extract_pattern.exec(lastvalue); + if (result) { + if (target.useCaptureGroups) { + result = result[1]; + } else { + result = result[0]; + } + } + deferred.resolve(result); + } else { + deferred.resolve(lastvalue); + } + }); + } else { + deferred.resolve(null); + } + return deferred.promise.then(function(text) { + return { + target: target.item.name, + datapoints: [[text, to * 1000]] + }; + }); + }); + } + } + + // IT services mode + else if (target.mode === 1) { + // 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(_.bind(zabbixHelperSrv.handleSLAResponse, zabbixHelperSrv, target.itservice, target.slaProperty)); + } + } + }, this); + + var self = this; + + // Data for panel (all targets) + return this.q.all(_.flatten(promises)) + .then(_.flatten) + .then(function (timeseries_data) { + + // Series downsampling + var data = _.map(timeseries_data, function(timeseries) { + var DPS = self.DataProcessingService; + if (timeseries.datapoints.length > options.maxDataPoints) { + timeseries.datapoints = DPS.groupBy(options.interval, DPS.AVERAGE, timeseries.datapoints); + } + return timeseries; + }); + return { data: data }; + }); + } + + //////////////// + // Templating // + //////////////// + + /** + * Find metrics from templated request. + * + * @param {string} query Query from Templating + * @return {string} Metric name - group, host, app or item or list + * of metrics in "{metric1,metcic2,...,metricN}" format. + */ + metricFindQuery(query) { + // Split query. Query structure: + // group.host.app.item + var parts = []; + _.each(query.split('.'), function (part) { + part = this.templateSrv.replace(part); + + // Replace wildcard to regex + if (part === '*') { + part = '/.*/'; + } + parts.push(part); + }); + var template = _.object(['group', 'host', 'app', 'item'], parts); + + // Get items + if (parts.length === 4) { + return this.queryProcessor.filterItems(template.group, template.host, + template.app, 'all', true) + .then(function(items) { + return _.map(items, formatMetric); + }); + } + // Get applications + else if (parts.length === 3) { + return this.queryProcessor.filterApplications(template.group, template.host) + .then(function(apps) { + return _.map(apps, formatMetric); + }); + } + // Get hosts + else if (parts.length === 2) { + return this.queryProcessor.filterHosts(template.group) + .then(function(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 empty object for invalid request + else { + return this.q.when([]); + } + } + + ///////////////// + // Annotations // + ///////////////// + + annotationQuery(options) { + var from = Math.ceil(dateMathParse(options.rangeRaw.from) / 1000); + var to = Math.ceil(dateMathParse(options.rangeRaw.to) / 1000); + var annotation = options.annotation; + var self = this; + var showEvents = annotation.showOkEvents ? [0, 1] : 1; + + var buildQuery = self.queryProcessor.buildTriggerQuery(this.templateSrv.replace(annotation.group), + this.templateSrv.replace(annotation.host), + this.templateSrv.replace(annotation.application)); + return buildQuery.then(function(query) { + return self.zabbixAPI.getTriggers(query.groupids, + query.hostids, + query.applicationids, + showEvents) + .then(function(triggers) { + + // Filter triggers by description + if (Utils.isRegex(annotation.trigger)) { + triggers = _.filter(triggers, function(trigger) { + return Utils.buildRegex(annotation.trigger).test(trigger.description); + }); + } else if (annotation.trigger) { + triggers = _.filter(triggers, function(trigger) { + return trigger.description === annotation.trigger; + }); + } + + // Remove events below the chose severity + triggers = _.filter(triggers, function(trigger) { + return Number(trigger.priority) >= Number(annotation.minseverity); + }); + + var objectids = _.map(triggers, 'triggerid'); + var params = { + output: 'extend', + time_from: from, + time_till: to, + objectids: objectids, + select_acknowledges: 'extend', + selectHosts: 'extend', + value: showEvents + }; + + return self.zabbixAPI.request('event.get', params) + .then(function (events) { + var indexedTriggers = _.indexBy(triggers, 'triggerid'); + + // Hide acknowledged events if option enabled + if (annotation.hideAcknowledged) { + events = _.filter(events, function(event) { + return !event.acknowledges.length; + }); + } + + return _.map(events, function(e) { + var title =''; + if (annotation.showHostname) { + title += e.hosts[0].name + ': '; + } + + // Show event type (OK or Problem) + title += Number(e.value) ? 'Problem' : 'OK'; + + var formatted_acknowledges = Utils.formatAcknowledges(e.acknowledges); + return { + annotation: annotation, + time: e.clock * 1000, + title: title, + text: indexedTriggers[e.objectid].description + formatted_acknowledges + }; + }); + }); + }); + }); + } + +} + +function bindFunctionDefs(functionDefs, category, DataProcessingService) { + 'use strict'; + var aggregationFunctions = _.map(metricFunctions.getCategories()[category], 'name'); + var aggFuncDefs = _.filter(functionDefs, function(func) { + return _.contains(aggregationFunctions, func.def.name); + }); + + return _.map(aggFuncDefs, function(func) { + var funcInstance = metricFunctions.createFuncInstance(func.def, func.params); + return funcInstance.bindFunction(DataProcessingService.metricFunctions); + }); +} + +function formatMetric(metricObj) { + 'use strict'; + return { + text: metricObj.name, + expandable: false + }; +} \ No newline at end of file diff --git a/plugins/datasource-zabbix/directives.js b/src/datasource-zabbix/directives.js similarity index 100% rename from plugins/datasource-zabbix/directives.js rename to src/datasource-zabbix/directives.js diff --git a/plugins/datasource-zabbix/helperFunctions.js b/src/datasource-zabbix/helperFunctions.js similarity index 100% rename from plugins/datasource-zabbix/helperFunctions.js rename to src/datasource-zabbix/helperFunctions.js diff --git a/plugins/datasource-zabbix/metricFunctionEditor.js b/src/datasource-zabbix/metricFunctionEditor.js similarity index 100% rename from plugins/datasource-zabbix/metricFunctionEditor.js rename to src/datasource-zabbix/metricFunctionEditor.js diff --git a/plugins/datasource-zabbix/metricFunctions.js b/src/datasource-zabbix/metricFunctions.js similarity index 100% rename from plugins/datasource-zabbix/metricFunctions.js rename to src/datasource-zabbix/metricFunctions.js diff --git a/src/datasource-zabbix/module.js b/src/datasource-zabbix/module.js new file mode 100644 index 0000000..d947214 --- /dev/null +++ b/src/datasource-zabbix/module.js @@ -0,0 +1,19 @@ +import {ZabbixAPIDatasource} from './datasource'; +import {ZabbixQueryCtrl} from './queryCtrl'; + +class ZabbixConfigCtrl {} +ZabbixConfigCtrl.templateUrl = 'partials/config.html'; + +class ZabbixQueryOptionsCtrl {} +ZabbixQueryOptionsCtrl.templateUrl = 'partials/query.options.html'; + +class ZabbixAnnotationsQueryCtrl {} +ZabbixAnnotationsQueryCtrl.templateUrl = 'partials/annotations.editor.html'; + +export { + ZabbixAPIDatasource as Datasource, + ZabbixQueryCtrl as QueryCtrl, + ZabbixConfigCtrl as ConfigCtrl, + ZabbixQueryOptionsCtrl as QueryOptionsCtrl, + ZabbixAnnotationsQueryCtrl as AnnotationsQueryCtrl +}; diff --git a/plugins/datasource-zabbix/partials/annotations.editor.html b/src/datasource-zabbix/partials/annotations.editor.html similarity index 100% rename from plugins/datasource-zabbix/partials/annotations.editor.html rename to src/datasource-zabbix/partials/annotations.editor.html diff --git a/plugins/datasource-zabbix/partials/config.html b/src/datasource-zabbix/partials/config.html similarity index 100% rename from plugins/datasource-zabbix/partials/config.html rename to src/datasource-zabbix/partials/config.html diff --git a/plugins/datasource-zabbix/partials/query.editor.html b/src/datasource-zabbix/partials/query.editor.html similarity index 100% rename from plugins/datasource-zabbix/partials/query.editor.html rename to src/datasource-zabbix/partials/query.editor.html diff --git a/plugins/datasource-zabbix/partials/query.options.html b/src/datasource-zabbix/partials/query.options.html similarity index 100% rename from plugins/datasource-zabbix/partials/query.options.html rename to src/datasource-zabbix/partials/query.options.html diff --git a/plugins/datasource-zabbix/plugin.json b/src/datasource-zabbix/plugin.json similarity index 100% rename from plugins/datasource-zabbix/plugin.json rename to src/datasource-zabbix/plugin.json diff --git a/plugins/datasource-zabbix/queryCtrl.js b/src/datasource-zabbix/queryCtrl.js similarity index 100% rename from plugins/datasource-zabbix/queryCtrl.js rename to src/datasource-zabbix/queryCtrl.js diff --git a/plugins/datasource-zabbix/queryProcessor.js b/src/datasource-zabbix/queryProcessor.js similarity index 100% rename from plugins/datasource-zabbix/queryProcessor.js rename to src/datasource-zabbix/queryProcessor.js diff --git a/plugins/datasource-zabbix/utils.js b/src/datasource-zabbix/utils.js similarity index 100% rename from plugins/datasource-zabbix/utils.js rename to src/datasource-zabbix/utils.js diff --git a/plugins/datasource-zabbix/zabbixAPI.js b/src/datasource-zabbix/zabbixAPI.js similarity index 100% rename from plugins/datasource-zabbix/zabbixAPI.js rename to src/datasource-zabbix/zabbixAPI.js diff --git a/plugins/datasource-zabbix/zabbixAPIService.js b/src/datasource-zabbix/zabbixAPIService.js similarity index 100% rename from plugins/datasource-zabbix/zabbixAPIService.js rename to src/datasource-zabbix/zabbixAPIService.js diff --git a/plugins/datasource-zabbix/zabbixCache.js b/src/datasource-zabbix/zabbixCache.js similarity index 100% rename from plugins/datasource-zabbix/zabbixCache.js rename to src/datasource-zabbix/zabbixCache.js diff --git a/plugins/panel-triggers/editor.html b/src/panel-triggers/editor.html similarity index 100% rename from plugins/panel-triggers/editor.html rename to src/panel-triggers/editor.html diff --git a/plugins/panel-triggers/editor.js b/src/panel-triggers/editor.js similarity index 100% rename from plugins/panel-triggers/editor.js rename to src/panel-triggers/editor.js diff --git a/plugins/panel-triggers/module.html b/src/panel-triggers/module.html similarity index 100% rename from plugins/panel-triggers/module.html rename to src/panel-triggers/module.html diff --git a/plugins/panel-triggers/module.js b/src/panel-triggers/module.js similarity index 100% rename from plugins/panel-triggers/module.js rename to src/panel-triggers/module.js diff --git a/plugins/panel-triggers/plugin.json b/src/panel-triggers/plugin.json similarity index 100% rename from plugins/panel-triggers/plugin.json rename to src/panel-triggers/plugin.json diff --git a/plugins/panel-triggers/trigger.colorpicker.html b/src/panel-triggers/trigger.colorpicker.html similarity index 100% rename from plugins/panel-triggers/trigger.colorpicker.html rename to src/panel-triggers/trigger.colorpicker.html