diff --git a/.jshintrc b/.jshintrc index 36c9c9f..fe86382 100644 --- a/.jshintrc +++ b/.jshintrc @@ -5,6 +5,7 @@ "curly": true, "eqnull": true, "strict": true, + "module": true, "devel": true, "eqeqeq": true, "forin": false, diff --git a/Gruntfile.js b/Gruntfile.js index 2c8f35a..9823176 100644 --- a/Gruntfile.js +++ b/Gruntfile.js @@ -17,6 +17,8 @@ module.exports = function(grunt) { '**/*', '!**/datasource.js', '!**/module.js', + '!**/queryCtrl.js', + '!**/utils.js', '!**/*.scss' ], dest: 'dist/src' @@ -47,8 +49,10 @@ module.exports = function(grunt) { cwd: 'src', expand: true, src: [ - '**/**/datasource.js', '**/**/module.js', + '**/**/datasource.js', + '**/**/queryCtrl.js', + '**/**/utils.js', ], dest: 'dist/src', ext:'.js' diff --git a/src/datasource-zabbix/datasource.js b/src/datasource-zabbix/datasource.js index f9699f3..24e755d 100644 --- a/src/datasource-zabbix/datasource.js +++ b/src/datasource-zabbix/datasource.js @@ -1,7 +1,7 @@ //import angular from 'angular'; import _ from 'lodash'; -import {parse as dateMathParse} from 'app/core/utils/datemath'; -import Utils from './utils'; +import * as dateMath from 'app/core/utils/datemath'; +import * as utils from './utils'; import metricFunctions from './metricFunctions'; import {zabbixHelperSrv} from './helperFunctions'; import {ZabbixAPI} from './zabbixAPI'; @@ -29,7 +29,7 @@ export class ZabbixAPIDatasource { // Set cache update interval var ttl = instanceSettings.jsonData.cacheTTL || '1h'; - this.cacheTTL = Utils.parseInterval(ttl); + this.cacheTTL = utils.parseInterval(ttl); // Initialize Zabbix API this.zabbixAPI = new ZabbixAPI(this.url, this.username, this.password, this.basicAuth, this.withCredentials); @@ -103,9 +103,9 @@ export class ZabbixAPIDatasource { 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); + 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) { @@ -330,8 +330,8 @@ export class ZabbixAPIDatasource { ///////////////// annotationQuery(options) { - var from = Math.ceil(dateMathParse(options.rangeRaw.from) / 1000); - var to = Math.ceil(dateMathParse(options.rangeRaw.to) / 1000); + 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; @@ -347,9 +347,9 @@ export class ZabbixAPIDatasource { .then(function(triggers) { // Filter triggers by description - if (Utils.isRegex(annotation.trigger)) { + if (utils.isRegex(annotation.trigger)) { triggers = _.filter(triggers, function(trigger) { - return Utils.buildRegex(annotation.trigger).test(trigger.description); + return utils.buildRegex(annotation.trigger).test(trigger.description); }); } else if (annotation.trigger) { triggers = _.filter(triggers, function(trigger) { @@ -393,7 +393,7 @@ export class ZabbixAPIDatasource { // Show event type (OK or Problem) title += Number(e.value) ? 'Problem' : 'OK'; - var formatted_acknowledges = Utils.formatAcknowledges(e.acknowledges); + var formatted_acknowledges = utils.formatAcknowledges(e.acknowledges); return { annotation: annotation, time: e.clock * 1000, diff --git a/src/datasource-zabbix/queryCtrl.js b/src/datasource-zabbix/queryCtrl.js index 6f927c3..935fc7a 100644 --- a/src/datasource-zabbix/queryCtrl.js +++ b/src/datasource-zabbix/queryCtrl.js @@ -1,242 +1,237 @@ -define([ +/*define([ 'app/plugins/sdk', 'angular', 'lodash', './metricFunctions', './utils' -], -function (sdk, angular, _, metricFunctions, utils) { - 'use strict'; +],*/ - var ZabbixQueryCtrl = (function(_super) { +import {QueryCtrl} from 'app/plugins/sdk'; +import _ from 'lodash'; +import * as utils from './utils'; +import metricFunctions from './metricFunctions'; - // ZabbixQueryCtrl constructor - function ZabbixQueryCtrl($scope, $injector, $sce, $q, templateSrv) { +export class ZabbixQueryCtrl extends QueryCtrl { - // Call superclass constructor - _super.call(this, $scope, $injector); + // ZabbixQueryCtrl constructor + constructor($scope, $injector, $sce, $q, templateSrv) { - this.editorModes = { - 0: 'num', - 1: 'itservice', - 2: 'text' + // Call superclass constructor + super($scope, $injector); + + this.editorModes = { + 0: 'num', + 1: 'itservice', + 2: 'text' + }; + + // Map functions for bs-typeahead + this.getGroupNames = _.partial(getMetricNames, this, 'groupList'); + this.getHostNames = _.partial(getMetricNames, this, 'filteredHosts'); + this.getApplicationNames = _.partial(getMetricNames, this, 'filteredApplications'); + this.getItemNames = _.partial(getMetricNames, this, 'filteredItems'); + + this.init = function() { + + this.templateSrv = templateSrv; + var target = this.target; + + var scopeDefaults = { + metric: {} }; + _.defaults(this, scopeDefaults); - // Map functions for bs-typeahead - this.getGroupNames = _.partial(getMetricNames, this, 'groupList'); - this.getHostNames = _.partial(getMetricNames, this, 'filteredHosts'); - this.getApplicationNames = _.partial(getMetricNames, this, 'filteredApplications'); - this.getItemNames = _.partial(getMetricNames, this, 'filteredItems'); - - this.init = function() { - - this.templateSrv = templateSrv; - var target = this.target; - - var scopeDefaults = { - metric: {} - }; - _.defaults(this, scopeDefaults); - - // Load default values - var targetDefaults = { - mode: 0, - group: { filter: "" }, - host: { filter: "" }, - application: { filter: "" }, - item: { filter: "" }, - functions: [], - }; - _.defaults(target, targetDefaults); - - // Create function instances from saved JSON - target.functions = _.map(target.functions, function(func) { - return metricFunctions.createFuncInstance(func.def, func.params); - }); - - if (target.mode === 0 || - target.mode === 2) { - - this.downsampleFunctionList = [ - {name: "avg", value: "avg"}, - {name: "min", value: "min"}, - {name: "max", value: "max"} - ]; - - // Set avg by default - if (!target.downsampleFunction) { - target.downsampleFunction = this.downsampleFunctionList[0]; - } - - this.initFilters(); - } - else if (target.mode === 1) { - this.slaPropertyList = [ - {name: "Status", property: "status"}, - {name: "SLA", property: "sla"}, - {name: "OK time", property: "okTime"}, - {name: "Problem time", property: "problemTime"}, - {name: "Down time", property: "downtimeTime"} - ]; - this.itserviceList = [{name: "test"}]; - this.updateITServiceList(); - } + // Load default values + var targetDefaults = { + mode: 0, + group: { filter: "" }, + host: { filter: "" }, + application: { filter: "" }, + item: { filter: "" }, + functions: [], }; + _.defaults(target, targetDefaults); - this.init(); - } - - ZabbixQueryCtrl.templateUrl = 'partials/query.editor.html'; - - ZabbixQueryCtrl.prototype = Object.create(_super.prototype); - ZabbixQueryCtrl.prototype.constructor = ZabbixQueryCtrl; - - var p = ZabbixQueryCtrl.prototype; - - p.initFilters = function () { - this.filterGroups(); - this.filterHosts(); - this.filterApplications(); - this.filterItems(); - }; - - p.filterHosts = function () { - var self = this; - var groupFilter = this.templateSrv.replace(this.target.group.filter); - this.datasource.queryProcessor.filterHosts(groupFilter).then(function(hosts) { - self.metric.filteredHosts = hosts; + // Create function instances from saved JSON + target.functions = _.map(target.functions, function(func) { + return metricFunctions.createFuncInstance(func.def, func.params); }); - }; - p.filterGroups = function() { - var self = this; - this.datasource.queryProcessor.filterGroups().then(function(groups) { - self.metric.groupList = groups; - }); - }; + if (target.mode === 0 || + target.mode === 2) { - p.filterApplications = function () { - var self = this; - var groupFilter = this.templateSrv.replace(this.target.group.filter); - var hostFilter = this.templateSrv.replace(this.target.host.filter); - this.datasource.queryProcessor.filterApplications(groupFilter, hostFilter) - .then(function(apps) { - self.metric.filteredApplications = apps; - }); - }; + this.downsampleFunctionList = [ + {name: "avg", value: "avg"}, + {name: "min", value: "min"}, + {name: "max", value: "max"} + ]; - p.filterItems = function () { - var self = this; - var item_type = this.editorModes[this.target.mode]; - var groupFilter = this.templateSrv.replace(this.target.group.filter); - var hostFilter = this.templateSrv.replace(this.target.host.filter); - var appFilter = this.templateSrv.replace(this.target.application.filter); - this.datasource.queryProcessor.filterItems(groupFilter, hostFilter, appFilter, - item_type, this.target.showDisabledItems).then(function(items) { - self.metric.filteredItems = items; - }); - }; + // Set avg by default + if (!target.downsampleFunction) { + target.downsampleFunction = this.downsampleFunctionList[0]; + } - p.onTargetPartChange = function (targetPart) { - var regexStyle = {'color': '#CCA300'}; - targetPart.isRegex = utils.isRegex(targetPart.filter); - targetPart.style = targetPart.isRegex ? regexStyle : {}; - }; - - p.onTargetBlur = function() { - this.initFilters(); - this.parseTarget(); - this.panelCtrl.refresh(); - }; - - p.parseTarget = function() { - // Parse target - }; - - // Validate target and set validation info - p.validateTarget = function () {}; - - p.targetChanged = function() { - this.panelCtrl.refresh(); - }; - - p.addFunction = function(funcDef) { - var newFunc = metricFunctions.createFuncInstance(funcDef); - newFunc.added = true; - this.target.functions.push(newFunc); - - this.moveAliasFuncLast(); - - if (newFunc.params.length && newFunc.added || - newFunc.def.params.length === 0) { - this.targetChanged(); + this.initFilters(); + } + else if (target.mode === 1) { + this.slaPropertyList = [ + {name: "Status", property: "status"}, + {name: "SLA", property: "sla"}, + {name: "OK time", property: "okTime"}, + {name: "Problem time", property: "problemTime"}, + {name: "Down time", property: "downtimeTime"} + ]; + this.itserviceList = [{name: "test"}]; + this.updateITServiceList(); } }; - p.removeFunction = function(func) { - this.target.functions = _.without(this.target.functions, func); - this.targetChanged(); - }; - - p.moveAliasFuncLast = function() { - var aliasFunc = _.find(this.target.functions, function(func) { - return func.def.name === 'alias' || - func.def.name === 'aliasByNode' || - func.def.name === 'aliasByMetric'; - }); - - if (aliasFunc) { - this.target.functions = _.without(this.target.functions, aliasFunc); - this.target.functions.push(aliasFunc); - } - }; - - /** - * Switch query editor to specified mode. - * Modes: - * 0 - items - * 1 - IT services - * 2 - Text metrics - */ - p.switchEditorMode = function (mode) { - this.target.mode = mode; - this.init(); - }; - - ///////////////// - // IT Services // - ///////////////// - - /** - * Update list of IT services - */ - p.updateITServiceList = function () { - var self = this; - this.datasource.zabbixAPI.getITService().then(function (iteservices) { - self.itserviceList = []; - self.itserviceList = self.itserviceList.concat(iteservices); - }); - }; - - /** - * Call when IT service is selected. - */ - p.selectITService = function () { - if (!_.isEqual(this.oldTarget, this.target) && _.isEmpty(this.target.errors)) { - this.oldTarget = angular.copy(this.target); - this.panelCtrl.refresh(); - } - }; - - return ZabbixQueryCtrl; - - })(sdk.QueryCtrl); - - return ZabbixQueryCtrl; - - // Get list of metric names for bs-typeahead directive - function getMetricNames(scope, metricList) { - return _.uniq(_.map(scope.metric[metricList], 'name')); + this.init(); } -}); \ No newline at end of file + initFilters() { + this.filterGroups(); + this.filterHosts(); + this.filterApplications(); + this.filterItems(); + } + + filterHosts() { + var self = this; + var groupFilter = this.templateSrv.replace(this.target.group.filter); + this.datasource.queryProcessor.filterHosts(groupFilter).then(function(hosts) { + self.metric.filteredHosts = hosts; + }); + } + + filterGroups() { + var self = this; + this.datasource.queryProcessor.filterGroups().then(function(groups) { + self.metric.groupList = groups; + }); + } + + filterApplications() { + var self = this; + var groupFilter = this.templateSrv.replace(this.target.group.filter); + var hostFilter = this.templateSrv.replace(this.target.host.filter); + this.datasource.queryProcessor.filterApplications(groupFilter, hostFilter) + .then(function(apps) { + self.metric.filteredApplications = apps; + }); + } + + filterItems() { + var self = this; + var item_type = this.editorModes[this.target.mode]; + var groupFilter = this.templateSrv.replace(this.target.group.filter); + var hostFilter = this.templateSrv.replace(this.target.host.filter); + var appFilter = this.templateSrv.replace(this.target.application.filter); + this.datasource.queryProcessor.filterItems(groupFilter, hostFilter, appFilter, + item_type, this.target.showDisabledItems).then(function(items) { + self.metric.filteredItems = items; + }); + } + + onTargetPartChange(targetPart) { + var regexStyle = {'color': '#CCA300'}; + targetPart.isRegex = utils.isRegex(targetPart.filter); + targetPart.style = targetPart.isRegex ? regexStyle : {}; + } + + onTargetBlur() { + this.initFilters(); + this.parseTarget(); + this.panelCtrl.refresh(); + } + + parseTarget() { + // Parse target + } + + // Validate target and set validation info + validateTarget() { + // validate + } + + targetChanged() { + this.panelCtrl.refresh(); + } + + addFunction(funcDef) { + var newFunc = metricFunctions.createFuncInstance(funcDef); + newFunc.added = true; + this.target.functions.push(newFunc); + + this.moveAliasFuncLast(); + + if (newFunc.params.length && newFunc.added || + newFunc.def.params.length === 0) { + this.targetChanged(); + } + } + + removeFunction(func) { + this.target.functions = _.without(this.target.functions, func); + this.targetChanged(); + } + + moveAliasFuncLast() { + var aliasFunc = _.find(this.target.functions, function(func) { + return func.def.name === 'alias' || + func.def.name === 'aliasByNode' || + func.def.name === 'aliasByMetric'; + }); + + if (aliasFunc) { + this.target.functions = _.without(this.target.functions, aliasFunc); + this.target.functions.push(aliasFunc); + } + } + + /** + * Switch query editor to specified mode. + * Modes: + * 0 - items + * 1 - IT services + * 2 - Text metrics + */ + switchEditorMode(mode) { + this.target.mode = mode; + this.init(); + } + + ///////////////// + // IT Services // + ///////////////// + + /** + * Update list of IT services + */ + updateITServiceList() { + var self = this; + this.datasource.zabbixAPI.getITService().then(function (iteservices) { + self.itserviceList = []; + self.itserviceList = self.itserviceList.concat(iteservices); + }); + } + + /** + * Call when IT service is selected. + */ + selectITService() { + if (!_.isEqual(this.oldTarget, this.target) && _.isEmpty(this.target.errors)) { + this.oldTarget = angular.copy(this.target); + this.panelCtrl.refresh(); + } + } + +} + +// Set templateUrl as static property +ZabbixQueryCtrl.templateUrl = 'partials/query.editor.html'; + +// Get list of metric names for bs-typeahead directive +function getMetricNames(scope, metricList) { + return _.uniq(_.map(scope.metric[metricList], 'name')); +} \ No newline at end of file diff --git a/src/datasource-zabbix/utils.js b/src/datasource-zabbix/utils.js index c92c616..ff36135 100644 --- a/src/datasource-zabbix/utils.js +++ b/src/datasource-zabbix/utils.js @@ -1,77 +1,67 @@ -define([ - 'lodash', - 'moment' -], -function (_, moment) { - 'use strict'; +import _ from 'lodash'; +import moment from 'moment'; - function Utils() { - /** - * Expand Zabbix item name - * - * @param {string} name item name, ie "CPU $2 time" - * @param {string} key item key, ie system.cpu.util[,system,avg1] - * @return {string} expanded name, ie "CPU system time" - */ - this.expandItemName = function(name, key) { +/** + * Expand Zabbix item name + * + * @param {string} name item name, ie "CPU $2 time" + * @param {string} key item key, ie system.cpu.util[,system,avg1] + * @return {string} expanded name, ie "CPU system time" + */ +export function expandItemName(name, key) { - // extract params from key: - // "system.cpu.util[,system,avg1]" --> ["", "system", "avg1"] - var key_params = key.substring(key.indexOf('[') + 1, key.lastIndexOf(']')).split(','); - - // replace item parameters - for (var i = key_params.length; i >= 1; i--) { - name = name.replace('$' + i, key_params[i - 1]); - } - return name; - }; - - // Pattern for testing regex - var regexPattern = /^\/(.*)\/([gmi]*)$/m; - - this.isRegex = function (str) { - return regexPattern.test(str); - }; - - this.buildRegex = function (str) { - var matches = str.match(regexPattern); - var pattern = matches[1]; - var flags = matches[2] !== "" ? matches[2] : undefined; - return new RegExp(pattern, flags); - }; - - this.parseInterval = function(interval) { - var intervalPattern = /(^[\d]+)(y|M|w|d|h|m|s)/g; - var momentInterval = intervalPattern.exec(interval); - return moment.duration(Number(momentInterval[1]), momentInterval[2]).valueOf(); - }; - - /** - * Format acknowledges. - * - * @param {array} acknowledges array of Zabbix acknowledge objects - * @return {string} HTML-formatted table - */ - this.formatAcknowledges = function(acknowledges) { - if (acknowledges.length) { - var formatted_acknowledges = '

Acknowledges:
' - + ''; - _.each(_.map(acknowledges, function (ack) { - var timestamp = moment.unix(ack.clock); - return ''; - }), function (ack) { - formatted_acknowledges = formatted_acknowledges.concat(ack); - }); - formatted_acknowledges = formatted_acknowledges.concat('
TimeUserComments
' + timestamp.format("DD MMM YYYY HH:mm:ss") + '' + ack.alias - + ' (' + ack.name + ' ' + ack.surname + ')' + '' + ack.message + '
'); - return formatted_acknowledges; - } else { - return ''; - } - }; + // extract params from key: + // "system.cpu.util[,system,avg1]" --> ["", "system", "avg1"] + var key_params = key.substring(key.indexOf('[') + 1, key.lastIndexOf(']')).split(','); + // replace item parameters + for (var i = key_params.length; i >= 1; i--) { + name = name.replace('$' + i, key_params[i - 1]); } + return name; +} - return new Utils(); -}); \ No newline at end of file +// Pattern for testing regex +var regexPattern = /^\/(.*)\/([gmi]*)$/m; + +export function isRegex(str) { + return regexPattern.test(str); +} + +export function buildRegex(str) { + var matches = str.match(regexPattern); + var pattern = matches[1]; + var flags = matches[2] !== "" ? matches[2] : undefined; + return new RegExp(pattern, flags); +} + +export function parseInterval(interval) { + var intervalPattern = /(^[\d]+)(y|M|w|d|h|m|s)/g; + var momentInterval = intervalPattern.exec(interval); + return moment.duration(Number(momentInterval[1]), momentInterval[2]).valueOf(); +} + +/** + * Format acknowledges. + * + * @param {array} acknowledges array of Zabbix acknowledge objects + * @return {string} HTML-formatted table + */ +export function formatAcknowledges(acknowledges) { + if (acknowledges.length) { + var formatted_acknowledges = '

Acknowledges:
' + + ''; + _.each(_.map(acknowledges, function (ack) { + var timestamp = moment.unix(ack.clock); + return ''; + }), function (ack) { + formatted_acknowledges = formatted_acknowledges.concat(ack); + }); + formatted_acknowledges = formatted_acknowledges.concat('
TimeUserComments
' + timestamp.format("DD MMM YYYY HH:mm:ss") + '' + ack.alias + + ' (' + ack.name + ' ' + ack.surname + ')' + '' + ack.message + '
'); + return formatted_acknowledges; + } else { + return ''; + } +}