diff --git a/dist/panel-triggers/editor.html b/dist/panel-triggers/editor.html
index 39e96b8..b0510db 100644
--- a/dist/panel-triggers/editor.html
+++ b/dist/panel-triggers/editor.html
@@ -137,6 +137,17 @@
ng-change="editor.panelCtrl.refresh()">
+
diff --git a/dist/panel-triggers/editor.js b/dist/panel-triggers/editor.js
index ea74521..8cf9d99 100644
--- a/dist/panel-triggers/editor.js
+++ b/dist/panel-triggers/editor.js
@@ -78,10 +78,9 @@ System.register(['lodash', '../datasource-zabbix/utils', '../datasource-zabbix/c
return _this.onVariableChange();
});
+ this.fontSizes = ['80%', '90%', '100%', '110%', '120%', '130%', '150%', '160%', '180%', '200%', '220%', '250%'];
this.ackFilters = ['all triggers', 'unacknowledged', 'acknowledged'];
-
this.sortByFields = [{ text: 'last change', value: 'lastchange' }, { text: 'severity', value: 'priority' }];
-
this.showEventsFields = [{ text: 'All', value: [0, 1] }, { text: 'OK', value: [0] }, { text: 'Problems', value: 1 }];
// Load scope defaults
diff --git a/dist/panel-triggers/editor.js.map b/dist/panel-triggers/editor.js.map
index d016fb8..f3f2d45 100644
--- a/dist/panel-triggers/editor.js.map
+++ b/dist/panel-triggers/editor.js.map
@@ -1 +1 @@
-{"version":3,"sources":["../../src/panel-triggers/editor.js"],"names":["getMetricNames","scope","metricList","_","uniq","map","metric","triggerPanelEditor","restrict","templateUrl","controller","TriggerPanelEditorCtrl","utils","$scope","$rootScope","uiSegmentSrv","datasourceSrv","templateSrv","popoverSrv","editor","panelCtrl","ctrl","panel","getGroupNames","partial","getHostNames","getApplicationNames","$on","onVariableChange","ackFilters","sortByFields","text","value","showEventsFields","scopeDefaults","inputStyles","oldTarget","cloneDeep","triggers","defaults","datasources","getZabbixDataSources","datasource","get","then","zabbix","queryBuilder","initFilters","refresh","Promise","all","suggestGroups","suggestHosts","suggestApps","getAllGroups","groupList","groups","groupFilter","replaceTemplateVars","group","filter","getAllHosts","hostList","hosts","hostFilter","host","getAllApps","appList","apps","isContainsVariables","targetChanged","some","isTemplateVariable","field","variables","newTarget","isEqual","each","triggerList","trigger","color","triggerSeverity","priority","severity","refreshTriggerSeverity","str","isRegex","ZABBIX_DS_ID","getMetricSources","meta","id"],"mappings":"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","file":"editor.js","sourcesContent":["/**\n * Grafana-Zabbix\n * Zabbix plugin for Grafana.\n * http://github.com/alexanderzobnin/grafana-zabbix\n *\n * Trigger panel.\n * This feature sponsored by CORE IT\n * http://www.coreit.fr\n *\n * Copyright 2015 Alexander Zobnin alexanderzobnin@gmail.com\n * Licensed under the Apache License, Version 2.0\n */\n\nimport _ from 'lodash';\nimport * as utils from '../datasource-zabbix/utils';\n\nimport '../datasource-zabbix/css/query-editor.css!';\n\nclass TriggerPanelEditorCtrl {\n\n /** @ngInject */\n constructor($scope, $rootScope, uiSegmentSrv, datasourceSrv, templateSrv, popoverSrv) {\n $scope.editor = this;\n this.panelCtrl = $scope.ctrl;\n this.panel = this.panelCtrl.panel;\n\n this.datasourceSrv = datasourceSrv;\n this.templateSrv = templateSrv;\n this.popoverSrv = popoverSrv;\n\n // Map functions for bs-typeahead\n this.getGroupNames = _.partial(getMetricNames, this, 'groupList');\n this.getHostNames = _.partial(getMetricNames, this, 'hostList');\n this.getApplicationNames = _.partial(getMetricNames, this, 'appList');\n\n // Update metric suggestion when template variable was changed\n $rootScope.$on('template-variable-value-updated', () => this.onVariableChange());\n\n this.ackFilters = [\n 'all triggers',\n 'unacknowledged',\n 'acknowledged'\n ];\n\n this.sortByFields = [\n { text: 'last change', value: 'lastchange' },\n { text: 'severity', value: 'priority' }\n ];\n\n this.showEventsFields = [\n { text: 'All', value: [0,1] },\n { text: 'OK', value: [0] },\n { text: 'Problems', value: 1 }\n ];\n\n // Load scope defaults\n var scopeDefaults = {\n metric: {},\n inputStyles: {},\n oldTarget: _.cloneDeep(this.panel.triggers)\n };\n _.defaults(this, scopeDefaults);\n\n // Set default datasource\n this.datasources = _.map(this.getZabbixDataSources(), 'name');\n if (!this.panel.datasource) {\n this.panel.datasource = this.datasources[0];\n }\n // Load datasource\n this.datasourceSrv.get(this.panel.datasource)\n .then(datasource => {\n this.datasource = datasource;\n this.zabbix = datasource.zabbix;\n this.queryBuilder = datasource.queryBuilder;\n this.initFilters();\n this.panelCtrl.refresh();\n });\n }\n\n initFilters() {\n return Promise.all([\n this.suggestGroups(),\n this.suggestHosts(),\n this.suggestApps()\n ]);\n }\n\n suggestGroups() {\n return this.zabbix.getAllGroups()\n .then(groups => {\n this.metric.groupList = groups;\n return groups;\n });\n }\n\n suggestHosts() {\n let groupFilter = this.datasource.replaceTemplateVars(this.panel.triggers.group.filter);\n return this.zabbix.getAllHosts(groupFilter)\n .then(hosts => {\n this.metric.hostList = hosts;\n return hosts;\n });\n }\n\n suggestApps() {\n let groupFilter = this.datasource.replaceTemplateVars(this.panel.triggers.group.filter);\n let hostFilter = this.datasource.replaceTemplateVars(this.panel.triggers.host.filter);\n return this.zabbix.getAllApps(groupFilter, hostFilter)\n .then(apps => {\n this.metric.appList = apps;\n return apps;\n });\n }\n\n onVariableChange() {\n if (this.isContainsVariables()) {\n this.targetChanged();\n }\n }\n\n /**\n * Check query for template variables\n */\n isContainsVariables() {\n return _.some(['group', 'host', 'application'], field => {\n return utils.isTemplateVariable(this.panel.triggers[field].filter, this.templateSrv.variables);\n });\n }\n\n targetChanged() {\n this.initFilters();\n this.panelCtrl.refresh();\n }\n\n parseTarget() {\n this.initFilters();\n var newTarget = _.cloneDeep(this.panel.triggers);\n if (!_.isEqual(this.oldTarget, this.panel.triggers)) {\n this.oldTarget = newTarget;\n this.panelCtrl.refresh();\n }\n }\n\n refreshTriggerSeverity() {\n _.each(this.triggerList, function(trigger) {\n trigger.color = this.panel.triggerSeverity[trigger.priority].color;\n trigger.severity = this.panel.triggerSeverity[trigger.priority].severity;\n });\n this.panelCtrl.refresh();\n }\n\n datasourceChanged() {\n this.panelCtrl.refresh();\n }\n\n changeTriggerSeverityColor(trigger, color) {\n this.panel.triggerSeverity[trigger.priority].color = color;\n this.refreshTriggerSeverity();\n }\n\n isRegex(str) {\n return utils.isRegex(str);\n }\n\n isVariable(str) {\n return utils.isTemplateVariable(str, this.templateSrv.variables);\n }\n\n getZabbixDataSources() {\n let ZABBIX_DS_ID = 'alexanderzobnin-zabbix-datasource';\n return _.filter(this.datasourceSrv.getMetricSources(), datasource => {\n return datasource.meta.id === ZABBIX_DS_ID && datasource.value;\n });\n }\n}\n\n// Get list of metric names for bs-typeahead directive\nfunction getMetricNames(scope, metricList) {\n return _.uniq(_.map(scope.metric[metricList], 'name'));\n}\n\nexport function triggerPanelEditor() {\n return {\n restrict: 'E',\n scope: true,\n templateUrl: 'public/plugins/alexanderzobnin-zabbix-app/panel-triggers/editor.html',\n controller: TriggerPanelEditorCtrl,\n };\n}\n"]}
\ No newline at end of file
+{"version":3,"sources":["../../src/panel-triggers/editor.js"],"names":["getMetricNames","scope","metricList","_","uniq","map","metric","triggerPanelEditor","restrict","templateUrl","controller","TriggerPanelEditorCtrl","utils","$scope","$rootScope","uiSegmentSrv","datasourceSrv","templateSrv","popoverSrv","editor","panelCtrl","ctrl","panel","getGroupNames","partial","getHostNames","getApplicationNames","$on","onVariableChange","fontSizes","ackFilters","sortByFields","text","value","showEventsFields","scopeDefaults","inputStyles","oldTarget","cloneDeep","triggers","defaults","datasources","getZabbixDataSources","datasource","get","then","zabbix","queryBuilder","initFilters","refresh","Promise","all","suggestGroups","suggestHosts","suggestApps","getAllGroups","groupList","groups","groupFilter","replaceTemplateVars","group","filter","getAllHosts","hostList","hosts","hostFilter","host","getAllApps","appList","apps","isContainsVariables","targetChanged","some","isTemplateVariable","field","variables","newTarget","isEqual","each","triggerList","trigger","color","triggerSeverity","priority","severity","refreshTriggerSeverity","str","isRegex","ZABBIX_DS_ID","getMetricSources","meta","id"],"mappings":"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","file":"editor.js","sourcesContent":["/**\n * Grafana-Zabbix\n * Zabbix plugin for Grafana.\n * http://github.com/alexanderzobnin/grafana-zabbix\n *\n * Trigger panel.\n * This feature sponsored by CORE IT\n * http://www.coreit.fr\n *\n * Copyright 2015 Alexander Zobnin alexanderzobnin@gmail.com\n * Licensed under the Apache License, Version 2.0\n */\n\nimport _ from 'lodash';\nimport * as utils from '../datasource-zabbix/utils';\n\nimport '../datasource-zabbix/css/query-editor.css!';\n\nclass TriggerPanelEditorCtrl {\n\n /** @ngInject */\n constructor($scope, $rootScope, uiSegmentSrv, datasourceSrv, templateSrv, popoverSrv) {\n $scope.editor = this;\n this.panelCtrl = $scope.ctrl;\n this.panel = this.panelCtrl.panel;\n\n this.datasourceSrv = datasourceSrv;\n this.templateSrv = templateSrv;\n this.popoverSrv = popoverSrv;\n\n // Map functions for bs-typeahead\n this.getGroupNames = _.partial(getMetricNames, this, 'groupList');\n this.getHostNames = _.partial(getMetricNames, this, 'hostList');\n this.getApplicationNames = _.partial(getMetricNames, this, 'appList');\n\n // Update metric suggestion when template variable was changed\n $rootScope.$on('template-variable-value-updated', () => this.onVariableChange());\n\n this.fontSizes = ['80%', '90%', '100%', '110%', '120%', '130%', '150%', '160%', '180%', '200%', '220%', '250%'];\n this.ackFilters = [\n 'all triggers',\n 'unacknowledged',\n 'acknowledged'\n ];\n this.sortByFields = [\n { text: 'last change', value: 'lastchange' },\n { text: 'severity', value: 'priority' }\n ];\n this.showEventsFields = [\n { text: 'All', value: [0,1] },\n { text: 'OK', value: [0] },\n { text: 'Problems', value: 1 }\n ];\n\n // Load scope defaults\n var scopeDefaults = {\n metric: {},\n inputStyles: {},\n oldTarget: _.cloneDeep(this.panel.triggers)\n };\n _.defaults(this, scopeDefaults);\n\n // Set default datasource\n this.datasources = _.map(this.getZabbixDataSources(), 'name');\n if (!this.panel.datasource) {\n this.panel.datasource = this.datasources[0];\n }\n // Load datasource\n this.datasourceSrv.get(this.panel.datasource)\n .then(datasource => {\n this.datasource = datasource;\n this.zabbix = datasource.zabbix;\n this.queryBuilder = datasource.queryBuilder;\n this.initFilters();\n this.panelCtrl.refresh();\n });\n }\n\n initFilters() {\n return Promise.all([\n this.suggestGroups(),\n this.suggestHosts(),\n this.suggestApps()\n ]);\n }\n\n suggestGroups() {\n return this.zabbix.getAllGroups()\n .then(groups => {\n this.metric.groupList = groups;\n return groups;\n });\n }\n\n suggestHosts() {\n let groupFilter = this.datasource.replaceTemplateVars(this.panel.triggers.group.filter);\n return this.zabbix.getAllHosts(groupFilter)\n .then(hosts => {\n this.metric.hostList = hosts;\n return hosts;\n });\n }\n\n suggestApps() {\n let groupFilter = this.datasource.replaceTemplateVars(this.panel.triggers.group.filter);\n let hostFilter = this.datasource.replaceTemplateVars(this.panel.triggers.host.filter);\n return this.zabbix.getAllApps(groupFilter, hostFilter)\n .then(apps => {\n this.metric.appList = apps;\n return apps;\n });\n }\n\n onVariableChange() {\n if (this.isContainsVariables()) {\n this.targetChanged();\n }\n }\n\n /**\n * Check query for template variables\n */\n isContainsVariables() {\n return _.some(['group', 'host', 'application'], field => {\n return utils.isTemplateVariable(this.panel.triggers[field].filter, this.templateSrv.variables);\n });\n }\n\n targetChanged() {\n this.initFilters();\n this.panelCtrl.refresh();\n }\n\n parseTarget() {\n this.initFilters();\n var newTarget = _.cloneDeep(this.panel.triggers);\n if (!_.isEqual(this.oldTarget, this.panel.triggers)) {\n this.oldTarget = newTarget;\n this.panelCtrl.refresh();\n }\n }\n\n refreshTriggerSeverity() {\n _.each(this.triggerList, function(trigger) {\n trigger.color = this.panel.triggerSeverity[trigger.priority].color;\n trigger.severity = this.panel.triggerSeverity[trigger.priority].severity;\n });\n this.panelCtrl.refresh();\n }\n\n datasourceChanged() {\n this.panelCtrl.refresh();\n }\n\n changeTriggerSeverityColor(trigger, color) {\n this.panel.triggerSeverity[trigger.priority].color = color;\n this.refreshTriggerSeverity();\n }\n\n isRegex(str) {\n return utils.isRegex(str);\n }\n\n isVariable(str) {\n return utils.isTemplateVariable(str, this.templateSrv.variables);\n }\n\n getZabbixDataSources() {\n let ZABBIX_DS_ID = 'alexanderzobnin-zabbix-datasource';\n return _.filter(this.datasourceSrv.getMetricSources(), datasource => {\n return datasource.meta.id === ZABBIX_DS_ID && datasource.value;\n });\n }\n}\n\n// Get list of metric names for bs-typeahead directive\nfunction getMetricNames(scope, metricList) {\n return _.uniq(_.map(scope.metric[metricList], 'name'));\n}\n\nexport function triggerPanelEditor() {\n return {\n restrict: 'E',\n scope: true,\n templateUrl: 'public/plugins/alexanderzobnin-zabbix-app/panel-triggers/editor.html',\n controller: TriggerPanelEditorCtrl,\n };\n}\n"]}
\ No newline at end of file
diff --git a/dist/panel-triggers/module.js b/dist/panel-triggers/module.js
index e3c079d..1742270 100644
--- a/dist/panel-triggers/module.js
+++ b/dist/panel-triggers/module.js
@@ -103,7 +103,8 @@ System.register(['lodash', 'jquery', 'moment', '../datasource-zabbix/utils', 'ap
okEventColor: 'rgba(0, 245, 153, 0.45)',
ackEventColor: 'rgba(0, 0, 0, 0)',
scroll: true,
- pageSize: 10
+ pageSize: 10,
+ fontSize: '100%'
};
triggerStatusMap = {
'0': 'OK',
diff --git a/dist/panel-triggers/module.js.map b/dist/panel-triggers/module.js.map
index 5b9cbc2..a78c48b 100644
--- a/dist/panel-triggers/module.js.map
+++ b/dist/panel-triggers/module.js.map
@@ -1 +1 @@
-{"version":3,"sources":["../../src/panel-triggers/module.js"],"names":["filterTriggers","triggers","triggerFilter","utils","isRegex","_","filter","trigger","buildRegex","test","description","$","moment","PanelCtrl","triggerPanelEditor","defaultSeverity","priority","severity","color","show","panelDefaults","datasource","group","host","application","hostField","statusField","severityField","lastChangeField","ageField","infoField","limit","showTriggers","sortTriggersBy","text","value","showEvents","triggerSeverity","okEventColor","ackEventColor","scroll","pageSize","triggerStatusMap","defaultTimeFormat","TriggerPanelCtrl","$scope","$injector","$element","datasourceSrv","templateSrv","contextSrv","pageIndex","triggerList","currentTriggersPage","defaults","panel","cloneDeep","events","on","onInitEditMode","bind","onRefresh","addEditorTab","error","loading","refreshData","then","slice","getCurrentTriggersPage","render","getTriggers","getAcknowledges","get","zabbix","groupFilter","replaceTemplateVars","hostFilter","appFilter","map","formatTrigger","eventids","lastEvent","eventid","each","event","find","acknowledges","timestamp","unix","ack","clock","customLastChangeFormat","time","format","lastChangeFormat","user","alias","name","surname","markAckEvents","length","sortBy","reverse","triggerObj","lastchangeUnix","Number","lastchange","age","fromNow","hosts","hostTechName","showComment","message","grafana_user","ack_message","zabbixAPI","acknowledgeEvent","startPos","endPos","Math","min","scope","elem","attrs","ctrl","data","pageCount","getTableHeight","panelHeight","height","switchPage","e","el","currentTarget","parseInt","$apply","renderPanel","appendPaginationControls","footerElem","empty","ceil","startPage","max","endPage","paginationList","i","activeClass","pageLinkElem","append","panelElem","parents","rootElem","css","fontSize","addClass","unbindDestroy","$on","off","renderData","renderingCompleted","templateUrl"],"mappings":"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","file":"module.js","sourcesContent":["/**\n * Grafana-Zabbix\n * Zabbix plugin for Grafana.\n * http://github.com/alexanderzobnin/grafana-zabbix\n *\n * Trigger panel.\n * This feature sponsored by CORE IT\n * http://www.coreit.fr\n *\n * Copyright 2015 Alexander Zobnin alexanderzobnin@gmail.com\n * Licensed under the Apache License, Version 2.0\n */\n\nimport _ from 'lodash';\nimport $ from 'jquery';\nimport moment from 'moment';\nimport * as utils from '../datasource-zabbix/utils';\nimport {PanelCtrl} from 'app/plugins/sdk';\nimport {triggerPanelEditor} from './editor';\nimport './ack-tooltip.directive';\nimport './css/panel_triggers.css!';\n\nvar defaultSeverity = [\n { priority: 0, severity: 'Not classified', color: '#B7DBAB', show: true },\n { priority: 1, severity: 'Information', color: '#82B5D8', show: true },\n { priority: 2, severity: 'Warning', color: '#E5AC0E', show: true },\n { priority: 3, severity: 'Average', color: '#C15C17', show: true },\n { priority: 4, severity: 'High', color: '#BF1B00', show: true },\n { priority: 5, severity: 'Disaster', color: '#890F02', show: true }\n];\n\nvar panelDefaults = {\n datasource: null,\n triggers: {\n group: {filter: \"\"},\n host: {filter: \"\"},\n application: {filter: \"\"},\n trigger: {filter: \"\"}\n },\n hostField: true,\n statusField: false,\n severityField: false,\n lastChangeField: true,\n ageField: true,\n infoField: true,\n limit: 10,\n showTriggers: 'all triggers',\n sortTriggersBy: { text: 'last change', value: 'lastchange' },\n showEvents: { text: 'Problems', value: '1' },\n triggerSeverity: defaultSeverity,\n okEventColor: 'rgba(0, 245, 153, 0.45)',\n ackEventColor: 'rgba(0, 0, 0, 0)',\n scroll: true,\n pageSize: 10\n};\n\nvar triggerStatusMap = {\n '0': 'OK',\n '1': 'Problem'\n};\n\nvar defaultTimeFormat = \"DD MMM YYYY HH:mm:ss\";\n\nclass TriggerPanelCtrl extends PanelCtrl {\n\n /** @ngInject */\n constructor($scope, $injector, $element, datasourceSrv, templateSrv, contextSrv) {\n super($scope, $injector);\n this.datasourceSrv = datasourceSrv;\n this.templateSrv = templateSrv;\n this.contextSrv = contextSrv;\n this.triggerStatusMap = triggerStatusMap;\n this.defaultTimeFormat = defaultTimeFormat;\n this.pageIndex = 0;\n this.triggerList = [];\n this.currentTriggersPage = [];\n\n // Load panel defaults\n // _.cloneDeep() need for prevent changing shared defaultSeverity.\n // Load object \"by value\" istead \"by reference\".\n _.defaults(this.panel, _.cloneDeep(panelDefaults));\n\n this.events.on('init-edit-mode', this.onInitEditMode.bind(this));\n this.events.on('refresh', this.onRefresh.bind(this));\n }\n\n onInitEditMode() {\n this.addEditorTab('Options', triggerPanelEditor, 2);\n }\n\n onRefresh() {\n // clear loading/error state\n delete this.error;\n this.loading = true;\n\n return this.refreshData()\n .then(triggerList => {\n // Limit triggers number\n this.triggerList = triggerList.slice(0, this.panel.limit);\n\n this.getCurrentTriggersPage();\n\n // Notify panel that request is finished\n this.loading = false;\n\n this.render(this.triggerList);\n });\n }\n\n refreshData() {\n return this.getTriggers()\n .then(this.getAcknowledges.bind(this))\n .then(this.filterTriggers.bind(this));\n }\n\n getTriggers() {\n return this.datasourceSrv.get(this.panel.datasource)\n .then(datasource => {\n var zabbix = datasource.zabbix;\n this.zabbix = zabbix;\n var showEvents = this.panel.showEvents.value;\n var triggerFilter = this.panel.triggers;\n\n // Replace template variables\n var groupFilter = datasource.replaceTemplateVars(triggerFilter.group.filter);\n var hostFilter = datasource.replaceTemplateVars(triggerFilter.host.filter);\n var appFilter = datasource.replaceTemplateVars(triggerFilter.application.filter);\n\n var getTriggers = zabbix.getTriggers(groupFilter, hostFilter, appFilter, showEvents);\n return getTriggers.then(triggers => {\n return _.map(triggers, this.formatTrigger.bind(this));\n });\n });\n }\n\n getAcknowledges(triggerList) {\n // Request acknowledges for trigger\n var eventids = _.map(triggerList, trigger => {\n return trigger.lastEvent.eventid;\n });\n\n return this.zabbix.getAcknowledges(eventids)\n .then(events => {\n\n // Map events to triggers\n _.each(triggerList, trigger => {\n var event = _.find(events, event => {\n return event.eventid === trigger.lastEvent.eventid;\n });\n\n if (event) {\n trigger.acknowledges = _.map(event.acknowledges, ack => {\n let timestamp = moment.unix(ack.clock);\n if (this.panel.customLastChangeFormat) {\n ack.time = timestamp.format(this.panel.lastChangeFormat);\n } else {\n ack.time = timestamp.format(this.defaultTimeFormat);\n }\n ack.user = ack.alias + ' (' + ack.name + ' ' + ack.surname + ')';\n return ack;\n });\n\n // Mark acknowledged triggers with different color\n if (this.panel.markAckEvents && trigger.acknowledges.length) {\n trigger.color = this.panel.ackEventColor;\n }\n }\n });\n\n return triggerList;\n });\n }\n\n filterTriggers(triggerList) {\n // Filter triggers by description\n var triggerFilter = this.panel.triggers.trigger.filter;\n if (triggerFilter) {\n triggerList = filterTriggers(triggerList, triggerFilter);\n }\n\n // Filter acknowledged triggers\n if (this.panel.showTriggers === 'unacknowledged') {\n triggerList = _.filter(triggerList, trigger => {\n return !trigger.acknowledges;\n });\n } else if (this.panel.showTriggers === 'acknowledged') {\n triggerList = _.filter(triggerList, 'acknowledges');\n } else {\n triggerList = triggerList;\n }\n\n // Filter triggers by severity\n triggerList = _.filter(triggerList, trigger => {\n return this.panel.triggerSeverity[trigger.priority].show;\n });\n\n // Sort triggers\n if (this.panel.sortTriggersBy.value === 'priority') {\n triggerList = _.sortBy(triggerList, 'priority').reverse();\n } else {\n triggerList = _.sortBy(triggerList, 'lastchangeUnix').reverse();\n }\n\n return triggerList;\n }\n\n formatTrigger(trigger) {\n let triggerObj = trigger;\n\n // Format last change and age\n trigger.lastchangeUnix = Number(trigger.lastchange);\n let timestamp = moment.unix(trigger.lastchangeUnix);\n if (this.panel.customLastChangeFormat) {\n // User defined format\n triggerObj.lastchange = timestamp.format(this.panel.lastChangeFormat);\n } else {\n triggerObj.lastchange = timestamp.format(this.defaultTimeFormat);\n }\n triggerObj.age = timestamp.fromNow(true);\n\n // Set host that the trigger belongs\n if (trigger.hosts.length) {\n triggerObj.host = trigger.hosts[0].name;\n triggerObj.hostTechName = trigger.hosts[0].host;\n }\n\n // Set color\n if (trigger.value === '1') {\n // Problem state\n triggerObj.color = this.panel.triggerSeverity[trigger.priority].color;\n } else {\n // OK state\n triggerObj.color = this.panel.okEventColor;\n }\n\n triggerObj.severity = this.panel.triggerSeverity[trigger.priority].severity;\n return triggerObj;\n }\n\n switchComment(trigger) {\n trigger.showComment = !trigger.showComment;\n }\n\n acknowledgeTrigger(trigger, message) {\n let eventid = trigger.lastEvent.eventid;\n let grafana_user = this.contextSrv.user.name;\n let ack_message = grafana_user + ' (Grafana): ' + message;\n return this.datasourceSrv.get(this.panel.datasource)\n .then(datasource => {\n let zabbixAPI = datasource.zabbix.zabbixAPI;\n return zabbixAPI.acknowledgeEvent(eventid, ack_message);\n })\n .then(this.onRefresh.bind(this));\n }\n\n getCurrentTriggersPage() {\n let pageSize = this.panel.pageSize || 10;\n let startPos = this.pageIndex * pageSize;\n let endPos = Math.min(startPos + pageSize, this.triggerList.length);\n this.currentTriggersPage = this.triggerList.slice(startPos, endPos);\n return this.currentTriggersPage;\n }\n\n link(scope, elem, attrs, ctrl) {\n var data;\n var panel = ctrl.panel;\n var pageCount = 0;\n data = ctrl.triggerList;\n\n function getTableHeight() {\n var panelHeight = ctrl.height;\n\n if (pageCount > 1) {\n panelHeight -= 26;\n }\n\n return (panelHeight - 31) + 'px';\n }\n\n function switchPage(e) {\n let el = $(e.currentTarget);\n ctrl.pageIndex = (parseInt(el.text(), 10)-1);\n\n let pageSize = ctrl.panel.pageSize || 10;\n let startPos = ctrl.pageIndex * pageSize;\n let endPos = Math.min(startPos + pageSize, ctrl.triggerList.length);\n ctrl.currentTriggersPage = ctrl.triggerList.slice(startPos, endPos);\n\n scope.$apply();\n renderPanel();\n }\n\n function appendPaginationControls(footerElem) {\n footerElem.empty();\n\n var pageSize = ctrl.panel.pageSize || 5;\n pageCount = Math.ceil(data.length / pageSize);\n if (pageCount === 1) {\n return;\n }\n\n var startPage = Math.max(ctrl.pageIndex - 3, 0);\n var endPage = Math.min(pageCount, startPage + 9);\n\n var paginationList = $('
');\n\n for (var i = startPage; i < endPage; i++) {\n var activeClass = i === ctrl.pageIndex ? 'active' : '';\n var pageLinkElem = $('
' + (i+1) + ' ');\n paginationList.append(pageLinkElem);\n }\n\n footerElem.append(paginationList);\n }\n\n function renderPanel() {\n var panelElem = elem.parents('.panel');\n var rootElem = elem.find('.triggers-panel-scroll');\n var footerElem = elem.find('.triggers-panel-footer');\n\n elem.css({'font-size': panel.fontSize});\n panelElem.addClass('triggers-panel-wrapper');\n appendPaginationControls(footerElem);\n\n rootElem.css({'max-height': panel.scroll ? getTableHeight() : '' });\n }\n\n elem.on('click', '.triggers-panel-page-link', switchPage);\n\n var unbindDestroy = scope.$on('$destroy', function() {\n elem.off('click', '.triggers-panel-page-link');\n unbindDestroy();\n });\n\n ctrl.events.on('render', (renderData) => {\n data = renderData || data;\n if (data) {\n renderPanel();\n }\n ctrl.renderingCompleted();\n });\n }\n}\n\nTriggerPanelCtrl.templateUrl = 'panel-triggers/module.html';\n\nfunction filterTriggers(triggers, triggerFilter) {\n if (utils.isRegex(triggerFilter)) {\n return _.filter(triggers, function(trigger) {\n return utils.buildRegex(triggerFilter).test(trigger.description);\n });\n } else {\n return _.filter(triggers, function(trigger) {\n return trigger.description === triggerFilter;\n });\n }\n}\n\nexport {\n TriggerPanelCtrl,\n TriggerPanelCtrl as PanelCtrl\n};\n"]}
\ No newline at end of file
+{"version":3,"sources":["../../src/panel-triggers/module.js"],"names":["filterTriggers","triggers","triggerFilter","utils","isRegex","_","filter","trigger","buildRegex","test","description","$","moment","PanelCtrl","triggerPanelEditor","defaultSeverity","priority","severity","color","show","panelDefaults","datasource","group","host","application","hostField","statusField","severityField","lastChangeField","ageField","infoField","limit","showTriggers","sortTriggersBy","text","value","showEvents","triggerSeverity","okEventColor","ackEventColor","scroll","pageSize","fontSize","triggerStatusMap","defaultTimeFormat","TriggerPanelCtrl","$scope","$injector","$element","datasourceSrv","templateSrv","contextSrv","pageIndex","triggerList","currentTriggersPage","defaults","panel","cloneDeep","events","on","onInitEditMode","bind","onRefresh","addEditorTab","error","loading","refreshData","then","slice","getCurrentTriggersPage","render","getTriggers","getAcknowledges","get","zabbix","groupFilter","replaceTemplateVars","hostFilter","appFilter","map","formatTrigger","eventids","lastEvent","eventid","each","event","find","acknowledges","timestamp","unix","ack","clock","customLastChangeFormat","time","format","lastChangeFormat","user","alias","name","surname","markAckEvents","length","sortBy","reverse","triggerObj","lastchangeUnix","Number","lastchange","age","fromNow","hosts","hostTechName","showComment","message","grafana_user","ack_message","zabbixAPI","acknowledgeEvent","startPos","endPos","Math","min","scope","elem","attrs","ctrl","data","pageCount","getTableHeight","panelHeight","height","switchPage","e","el","currentTarget","parseInt","$apply","renderPanel","appendPaginationControls","footerElem","empty","ceil","startPage","max","endPage","paginationList","i","activeClass","pageLinkElem","append","panelElem","parents","rootElem","css","addClass","unbindDestroy","$on","off","renderData","renderingCompleted","templateUrl"],"mappings":"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","file":"module.js","sourcesContent":["/**\n * Grafana-Zabbix\n * Zabbix plugin for Grafana.\n * http://github.com/alexanderzobnin/grafana-zabbix\n *\n * Trigger panel.\n * This feature sponsored by CORE IT\n * http://www.coreit.fr\n *\n * Copyright 2015 Alexander Zobnin alexanderzobnin@gmail.com\n * Licensed under the Apache License, Version 2.0\n */\n\nimport _ from 'lodash';\nimport $ from 'jquery';\nimport moment from 'moment';\nimport * as utils from '../datasource-zabbix/utils';\nimport {PanelCtrl} from 'app/plugins/sdk';\nimport {triggerPanelEditor} from './editor';\nimport './ack-tooltip.directive';\nimport './css/panel_triggers.css!';\n\nvar defaultSeverity = [\n { priority: 0, severity: 'Not classified', color: '#B7DBAB', show: true },\n { priority: 1, severity: 'Information', color: '#82B5D8', show: true },\n { priority: 2, severity: 'Warning', color: '#E5AC0E', show: true },\n { priority: 3, severity: 'Average', color: '#C15C17', show: true },\n { priority: 4, severity: 'High', color: '#BF1B00', show: true },\n { priority: 5, severity: 'Disaster', color: '#890F02', show: true }\n];\n\nvar panelDefaults = {\n datasource: null,\n triggers: {\n group: {filter: \"\"},\n host: {filter: \"\"},\n application: {filter: \"\"},\n trigger: {filter: \"\"}\n },\n hostField: true,\n statusField: false,\n severityField: false,\n lastChangeField: true,\n ageField: true,\n infoField: true,\n limit: 10,\n showTriggers: 'all triggers',\n sortTriggersBy: { text: 'last change', value: 'lastchange' },\n showEvents: { text: 'Problems', value: '1' },\n triggerSeverity: defaultSeverity,\n okEventColor: 'rgba(0, 245, 153, 0.45)',\n ackEventColor: 'rgba(0, 0, 0, 0)',\n scroll: true,\n pageSize: 10,\n fontSize: '100%',\n};\n\nvar triggerStatusMap = {\n '0': 'OK',\n '1': 'Problem'\n};\n\nvar defaultTimeFormat = \"DD MMM YYYY HH:mm:ss\";\n\nclass TriggerPanelCtrl extends PanelCtrl {\n\n /** @ngInject */\n constructor($scope, $injector, $element, datasourceSrv, templateSrv, contextSrv) {\n super($scope, $injector);\n this.datasourceSrv = datasourceSrv;\n this.templateSrv = templateSrv;\n this.contextSrv = contextSrv;\n this.triggerStatusMap = triggerStatusMap;\n this.defaultTimeFormat = defaultTimeFormat;\n this.pageIndex = 0;\n this.triggerList = [];\n this.currentTriggersPage = [];\n\n // Load panel defaults\n // _.cloneDeep() need for prevent changing shared defaultSeverity.\n // Load object \"by value\" istead \"by reference\".\n _.defaults(this.panel, _.cloneDeep(panelDefaults));\n\n this.events.on('init-edit-mode', this.onInitEditMode.bind(this));\n this.events.on('refresh', this.onRefresh.bind(this));\n }\n\n onInitEditMode() {\n this.addEditorTab('Options', triggerPanelEditor, 2);\n }\n\n onRefresh() {\n // clear loading/error state\n delete this.error;\n this.loading = true;\n\n return this.refreshData()\n .then(triggerList => {\n // Limit triggers number\n this.triggerList = triggerList.slice(0, this.panel.limit);\n\n this.getCurrentTriggersPage();\n\n // Notify panel that request is finished\n this.loading = false;\n\n this.render(this.triggerList);\n });\n }\n\n refreshData() {\n return this.getTriggers()\n .then(this.getAcknowledges.bind(this))\n .then(this.filterTriggers.bind(this));\n }\n\n getTriggers() {\n return this.datasourceSrv.get(this.panel.datasource)\n .then(datasource => {\n var zabbix = datasource.zabbix;\n this.zabbix = zabbix;\n var showEvents = this.panel.showEvents.value;\n var triggerFilter = this.panel.triggers;\n\n // Replace template variables\n var groupFilter = datasource.replaceTemplateVars(triggerFilter.group.filter);\n var hostFilter = datasource.replaceTemplateVars(triggerFilter.host.filter);\n var appFilter = datasource.replaceTemplateVars(triggerFilter.application.filter);\n\n var getTriggers = zabbix.getTriggers(groupFilter, hostFilter, appFilter, showEvents);\n return getTriggers.then(triggers => {\n return _.map(triggers, this.formatTrigger.bind(this));\n });\n });\n }\n\n getAcknowledges(triggerList) {\n // Request acknowledges for trigger\n var eventids = _.map(triggerList, trigger => {\n return trigger.lastEvent.eventid;\n });\n\n return this.zabbix.getAcknowledges(eventids)\n .then(events => {\n\n // Map events to triggers\n _.each(triggerList, trigger => {\n var event = _.find(events, event => {\n return event.eventid === trigger.lastEvent.eventid;\n });\n\n if (event) {\n trigger.acknowledges = _.map(event.acknowledges, ack => {\n let timestamp = moment.unix(ack.clock);\n if (this.panel.customLastChangeFormat) {\n ack.time = timestamp.format(this.panel.lastChangeFormat);\n } else {\n ack.time = timestamp.format(this.defaultTimeFormat);\n }\n ack.user = ack.alias + ' (' + ack.name + ' ' + ack.surname + ')';\n return ack;\n });\n\n // Mark acknowledged triggers with different color\n if (this.panel.markAckEvents && trigger.acknowledges.length) {\n trigger.color = this.panel.ackEventColor;\n }\n }\n });\n\n return triggerList;\n });\n }\n\n filterTriggers(triggerList) {\n // Filter triggers by description\n var triggerFilter = this.panel.triggers.trigger.filter;\n if (triggerFilter) {\n triggerList = filterTriggers(triggerList, triggerFilter);\n }\n\n // Filter acknowledged triggers\n if (this.panel.showTriggers === 'unacknowledged') {\n triggerList = _.filter(triggerList, trigger => {\n return !trigger.acknowledges;\n });\n } else if (this.panel.showTriggers === 'acknowledged') {\n triggerList = _.filter(triggerList, 'acknowledges');\n } else {\n triggerList = triggerList;\n }\n\n // Filter triggers by severity\n triggerList = _.filter(triggerList, trigger => {\n return this.panel.triggerSeverity[trigger.priority].show;\n });\n\n // Sort triggers\n if (this.panel.sortTriggersBy.value === 'priority') {\n triggerList = _.sortBy(triggerList, 'priority').reverse();\n } else {\n triggerList = _.sortBy(triggerList, 'lastchangeUnix').reverse();\n }\n\n return triggerList;\n }\n\n formatTrigger(trigger) {\n let triggerObj = trigger;\n\n // Format last change and age\n trigger.lastchangeUnix = Number(trigger.lastchange);\n let timestamp = moment.unix(trigger.lastchangeUnix);\n if (this.panel.customLastChangeFormat) {\n // User defined format\n triggerObj.lastchange = timestamp.format(this.panel.lastChangeFormat);\n } else {\n triggerObj.lastchange = timestamp.format(this.defaultTimeFormat);\n }\n triggerObj.age = timestamp.fromNow(true);\n\n // Set host that the trigger belongs\n if (trigger.hosts.length) {\n triggerObj.host = trigger.hosts[0].name;\n triggerObj.hostTechName = trigger.hosts[0].host;\n }\n\n // Set color\n if (trigger.value === '1') {\n // Problem state\n triggerObj.color = this.panel.triggerSeverity[trigger.priority].color;\n } else {\n // OK state\n triggerObj.color = this.panel.okEventColor;\n }\n\n triggerObj.severity = this.panel.triggerSeverity[trigger.priority].severity;\n return triggerObj;\n }\n\n switchComment(trigger) {\n trigger.showComment = !trigger.showComment;\n }\n\n acknowledgeTrigger(trigger, message) {\n let eventid = trigger.lastEvent.eventid;\n let grafana_user = this.contextSrv.user.name;\n let ack_message = grafana_user + ' (Grafana): ' + message;\n return this.datasourceSrv.get(this.panel.datasource)\n .then(datasource => {\n let zabbixAPI = datasource.zabbix.zabbixAPI;\n return zabbixAPI.acknowledgeEvent(eventid, ack_message);\n })\n .then(this.onRefresh.bind(this));\n }\n\n getCurrentTriggersPage() {\n let pageSize = this.panel.pageSize || 10;\n let startPos = this.pageIndex * pageSize;\n let endPos = Math.min(startPos + pageSize, this.triggerList.length);\n this.currentTriggersPage = this.triggerList.slice(startPos, endPos);\n return this.currentTriggersPage;\n }\n\n link(scope, elem, attrs, ctrl) {\n var data;\n var panel = ctrl.panel;\n var pageCount = 0;\n data = ctrl.triggerList;\n\n function getTableHeight() {\n var panelHeight = ctrl.height;\n\n if (pageCount > 1) {\n panelHeight -= 26;\n }\n\n return (panelHeight - 31) + 'px';\n }\n\n function switchPage(e) {\n let el = $(e.currentTarget);\n ctrl.pageIndex = (parseInt(el.text(), 10)-1);\n\n let pageSize = ctrl.panel.pageSize || 10;\n let startPos = ctrl.pageIndex * pageSize;\n let endPos = Math.min(startPos + pageSize, ctrl.triggerList.length);\n ctrl.currentTriggersPage = ctrl.triggerList.slice(startPos, endPos);\n\n scope.$apply();\n renderPanel();\n }\n\n function appendPaginationControls(footerElem) {\n footerElem.empty();\n\n var pageSize = ctrl.panel.pageSize || 5;\n pageCount = Math.ceil(data.length / pageSize);\n if (pageCount === 1) {\n return;\n }\n\n var startPage = Math.max(ctrl.pageIndex - 3, 0);\n var endPage = Math.min(pageCount, startPage + 9);\n\n var paginationList = $('
');\n\n for (var i = startPage; i < endPage; i++) {\n var activeClass = i === ctrl.pageIndex ? 'active' : '';\n var pageLinkElem = $('
' + (i+1) + ' ');\n paginationList.append(pageLinkElem);\n }\n\n footerElem.append(paginationList);\n }\n\n function renderPanel() {\n var panelElem = elem.parents('.panel');\n var rootElem = elem.find('.triggers-panel-scroll');\n var footerElem = elem.find('.triggers-panel-footer');\n\n elem.css({'font-size': panel.fontSize});\n panelElem.addClass('triggers-panel-wrapper');\n appendPaginationControls(footerElem);\n\n rootElem.css({'max-height': panel.scroll ? getTableHeight() : '' });\n }\n\n elem.on('click', '.triggers-panel-page-link', switchPage);\n\n var unbindDestroy = scope.$on('$destroy', function() {\n elem.off('click', '.triggers-panel-page-link');\n unbindDestroy();\n });\n\n ctrl.events.on('render', (renderData) => {\n data = renderData || data;\n if (data) {\n renderPanel();\n }\n ctrl.renderingCompleted();\n });\n }\n}\n\nTriggerPanelCtrl.templateUrl = 'panel-triggers/module.html';\n\nfunction filterTriggers(triggers, triggerFilter) {\n if (utils.isRegex(triggerFilter)) {\n return _.filter(triggers, function(trigger) {\n return utils.buildRegex(triggerFilter).test(trigger.description);\n });\n } else {\n return _.filter(triggers, function(trigger) {\n return trigger.description === triggerFilter;\n });\n }\n}\n\nexport {\n TriggerPanelCtrl,\n TriggerPanelCtrl as PanelCtrl\n};\n"]}
\ No newline at end of file
diff --git a/dist/test/components/config.js b/dist/test/components/config.js
new file mode 100644
index 0000000..ad0987c
--- /dev/null
+++ b/dist/test/components/config.js
@@ -0,0 +1,13 @@
+'use strict';
+
+Object.defineProperty(exports, "__esModule", {
+ value: true
+});
+
+function _classCallCheck(instance, Constructor) { if (!(instance instanceof Constructor)) { throw new TypeError("Cannot call a class as a function"); } }
+
+var ZabbixAppConfigCtrl = exports.ZabbixAppConfigCtrl = function ZabbixAppConfigCtrl() {
+ _classCallCheck(this, ZabbixAppConfigCtrl);
+};
+
+ZabbixAppConfigCtrl.templateUrl = 'components/config.html';
diff --git a/dist/test/datasource-zabbix/add-metric-function.directive.js b/dist/test/datasource-zabbix/add-metric-function.directive.js
new file mode 100644
index 0000000..26b55cc
--- /dev/null
+++ b/dist/test/datasource-zabbix/add-metric-function.directive.js
@@ -0,0 +1,116 @@
+'use strict';
+
+var _angular = require('angular');
+
+var _angular2 = _interopRequireDefault(_angular);
+
+var _lodash = require('lodash');
+
+var _lodash2 = _interopRequireDefault(_lodash);
+
+var _jquery = require('jquery');
+
+var _jquery2 = _interopRequireDefault(_jquery);
+
+var _metricFunctions = require('./metricFunctions');
+
+var metricFunctions = _interopRequireWildcard(_metricFunctions);
+
+function _interopRequireWildcard(obj) { if (obj && obj.__esModule) { return obj; } else { var newObj = {}; if (obj != null) { for (var key in obj) { if (Object.prototype.hasOwnProperty.call(obj, key)) newObj[key] = obj[key]; } } newObj.default = obj; return newObj; } }
+
+function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; }
+
+/** @ngInject */
+_angular2.default.module('grafana.directives').directive('addMetricFunction', function ($compile) {
+ var inputTemplate = '
';
+
+ var buttonTemplate = '
' + ' ';
+
+ return {
+ link: function link($scope, elem) {
+ var categories = metricFunctions.getCategories();
+ var allFunctions = getAllFunctionNames(categories);
+
+ $scope.functionMenu = createFunctionDropDownMenu(categories);
+
+ var $input = (0, _jquery2.default)(inputTemplate);
+ var $button = (0, _jquery2.default)(buttonTemplate);
+ $input.appendTo(elem);
+ $button.appendTo(elem);
+
+ $input.attr('data-provide', 'typeahead');
+ $input.typeahead({
+ source: allFunctions,
+ minLength: 1,
+ items: 10,
+ updater: function updater(value) {
+ var funcDef = metricFunctions.getFuncDef(value);
+ if (!funcDef) {
+ // try find close match
+ value = value.toLowerCase();
+ funcDef = _lodash2.default.find(allFunctions, function (funcName) {
+ return funcName.toLowerCase().indexOf(value) === 0;
+ });
+
+ if (!funcDef) {
+ return;
+ }
+ }
+
+ $scope.$apply(function () {
+ $scope.addFunction(funcDef);
+ });
+
+ $input.trigger('blur');
+ return '';
+ }
+ });
+
+ $button.click(function () {
+ $button.hide();
+ $input.show();
+ $input.focus();
+ });
+
+ $input.keyup(function () {
+ elem.toggleClass('open', $input.val() === '');
+ });
+
+ $input.blur(function () {
+ // clicking the function dropdown menu wont
+ // work if you remove class at once
+ setTimeout(function () {
+ $input.val('');
+ $input.hide();
+ $button.show();
+ elem.removeClass('open');
+ }, 200);
+ });
+
+ $compile(elem.contents())($scope);
+ }
+ };
+});
+
+function getAllFunctionNames(categories) {
+ return _lodash2.default.reduce(categories, function (list, category) {
+ _lodash2.default.each(category, function (func) {
+ list.push(func.name);
+ });
+ return list;
+ }, []);
+}
+
+function createFunctionDropDownMenu(categories) {
+ return _lodash2.default.map(categories, function (list, category) {
+ return {
+ text: category,
+ submenu: _lodash2.default.map(list, function (value) {
+ return {
+ text: value.name,
+ click: "ctrl.addFunction('" + value.name + "')"
+ };
+ })
+ };
+ });
+}
diff --git a/dist/test/datasource-zabbix/dataProcessor.js b/dist/test/datasource-zabbix/dataProcessor.js
new file mode 100644
index 0000000..2ce1585
--- /dev/null
+++ b/dist/test/datasource-zabbix/dataProcessor.js
@@ -0,0 +1,330 @@
+'use strict';
+
+Object.defineProperty(exports, "__esModule", {
+ value: true
+});
+
+var _lodash = require('lodash');
+
+var _lodash2 = _interopRequireDefault(_lodash);
+
+var _utils = require('./utils');
+
+var utils = _interopRequireWildcard(_utils);
+
+function _interopRequireWildcard(obj) { if (obj && obj.__esModule) { return obj; } else { var newObj = {}; if (obj != null) { for (var key in obj) { if (Object.prototype.hasOwnProperty.call(obj, key)) newObj[key] = obj[key]; } } newObj.default = obj; return newObj; } }
+
+function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; }
+
+/**
+ * Downsample datapoints series
+ */
+function downsampleSeries(datapoints, time_to, ms_interval, func) {
+ var downsampledSeries = [];
+ var timeWindow = {
+ from: time_to * 1000 - ms_interval,
+ to: time_to * 1000
+ };
+
+ var points_sum = 0;
+ var points_num = 0;
+ var value_avg = 0;
+ var frame = [];
+
+ for (var i = datapoints.length - 1; i >= 0; i -= 1) {
+ if (timeWindow.from < datapoints[i][1] && datapoints[i][1] <= timeWindow.to) {
+ points_sum += datapoints[i][0];
+ points_num++;
+ frame.push(datapoints[i][0]);
+ } else {
+ value_avg = points_num ? points_sum / points_num : 0;
+
+ if (func === "max") {
+ downsampledSeries.push([_lodash2.default.max(frame), timeWindow.to]);
+ } else if (func === "min") {
+ downsampledSeries.push([_lodash2.default.min(frame), timeWindow.to]);
+ }
+
+ // avg by default
+ else {
+ downsampledSeries.push([value_avg, timeWindow.to]);
+ }
+
+ // Shift time window
+ timeWindow.to = timeWindow.from;
+ timeWindow.from -= ms_interval;
+
+ points_sum = 0;
+ points_num = 0;
+ frame = [];
+
+ // Process point again
+ i++;
+ }
+ }
+ return downsampledSeries.reverse();
+}
+
+/**
+ * Group points by given time interval
+ * datapoints: [[
, ], ...]
+ */
+function groupBy(interval, groupByCallback, datapoints) {
+ var ms_interval = utils.parseInterval(interval);
+
+ // Calculate frame timestamps
+ var frames = _lodash2.default.groupBy(datapoints, function (point) {
+ // Calculate time for group of points
+ return Math.floor(point[1] / ms_interval) * ms_interval;
+ });
+
+ // frame: { '': [[, ], ...] }
+ // return [{ '': }, { '': }, ...]
+ var grouped = _lodash2.default.mapValues(frames, function (frame) {
+ var points = _lodash2.default.map(frame, function (point) {
+ return point[0];
+ });
+ return groupByCallback(points);
+ });
+
+ // Convert points to Grafana format
+ return sortByTime(_lodash2.default.map(grouped, function (value, timestamp) {
+ return [Number(value), Number(timestamp)];
+ }));
+}
+
+function sumSeries(timeseries) {
+
+ // Calculate new points for interpolation
+ var new_timestamps = _lodash2.default.uniq(_lodash2.default.map(_lodash2.default.flatten(timeseries, true), function (point) {
+ return point[1];
+ }));
+ new_timestamps = _lodash2.default.sortBy(new_timestamps);
+
+ var interpolated_timeseries = _lodash2.default.map(timeseries, function (series) {
+ var timestamps = _lodash2.default.map(series, function (point) {
+ return point[1];
+ });
+ var new_points = _lodash2.default.map(_lodash2.default.difference(new_timestamps, timestamps), function (timestamp) {
+ return [null, timestamp];
+ });
+ var new_series = series.concat(new_points);
+ return sortByTime(new_series);
+ });
+
+ _lodash2.default.each(interpolated_timeseries, interpolateSeries);
+
+ var new_timeseries = [];
+ var sum;
+ for (var i = new_timestamps.length - 1; i >= 0; i--) {
+ sum = 0;
+ for (var j = interpolated_timeseries.length - 1; j >= 0; j--) {
+ sum += interpolated_timeseries[j][i][0];
+ }
+ new_timeseries.push([sum, new_timestamps[i]]);
+ }
+
+ return sortByTime(new_timeseries);
+}
+
+function limit(order, n, orderByFunc, timeseries) {
+ var orderByCallback = aggregationFunctions[orderByFunc];
+ var sortByIteratee = function sortByIteratee(ts) {
+ var values = _lodash2.default.map(ts.datapoints, function (point) {
+ return point[0];
+ });
+ return orderByCallback(values);
+ };
+ var sortedTimeseries = _lodash2.default.sortBy(timeseries, sortByIteratee);
+ if (order === 'bottom') {
+ return sortedTimeseries.slice(0, n);
+ } else {
+ return sortedTimeseries.slice(-n);
+ }
+}
+
+function AVERAGE(values) {
+ var sum = 0;
+ _lodash2.default.each(values, function (value) {
+ sum += value;
+ });
+ return sum / values.length;
+}
+
+function MIN(values) {
+ return _lodash2.default.min(values);
+}
+
+function MAX(values) {
+ return _lodash2.default.max(values);
+}
+
+function MEDIAN(values) {
+ var sorted = _lodash2.default.sortBy(values);
+ return sorted[Math.floor(sorted.length / 2)];
+}
+
+function setAlias(alias, timeseries) {
+ timeseries.target = alias;
+ return timeseries;
+}
+
+function setAliasByRegex(alias, timeseries) {
+ timeseries.target = extractText(timeseries.target, alias);
+ return timeseries;
+}
+
+function extractText(str, pattern) {
+ var extractPattern = new RegExp(pattern);
+ var extractedValue = extractPattern.exec(str);
+ extractedValue = extractedValue[0];
+ return extractedValue;
+}
+
+function scale(factor, datapoints) {
+ return _lodash2.default.map(datapoints, function (point) {
+ return [point[0] * factor, point[1]];
+ });
+}
+
+function delta(datapoints) {
+ var newSeries = [];
+ var deltaValue = void 0;
+ for (var i = 1; i < datapoints.length; i++) {
+ deltaValue = datapoints[i][0] - datapoints[i - 1][0];
+ newSeries.push([deltaValue, datapoints[i][1]]);
+ }
+ return newSeries;
+}
+
+function groupByWrapper(interval, groupFunc, datapoints) {
+ var groupByCallback = aggregationFunctions[groupFunc];
+ return groupBy(interval, groupByCallback, datapoints);
+}
+
+function aggregateByWrapper(interval, aggregateFunc, datapoints) {
+ // Flatten all points in frame and then just use groupBy()
+ var flattenedPoints = _lodash2.default.flatten(datapoints, true);
+ var groupByCallback = aggregationFunctions[aggregateFunc];
+ return groupBy(interval, groupByCallback, flattenedPoints);
+}
+
+function aggregateWrapper(groupByCallback, interval, datapoints) {
+ var flattenedPoints = _lodash2.default.flatten(datapoints, true);
+ return groupBy(interval, groupByCallback, flattenedPoints);
+}
+
+function sortByTime(series) {
+ return _lodash2.default.sortBy(series, function (point) {
+ return point[1];
+ });
+}
+
+/**
+ * Interpolate series with gaps
+ */
+function interpolateSeries(series) {
+ var left, right;
+
+ // Interpolate series
+ for (var i = series.length - 1; i >= 0; i--) {
+ if (!series[i][0]) {
+ left = findNearestLeft(series, series[i]);
+ right = findNearestRight(series, series[i]);
+ if (!left) {
+ left = right;
+ }
+ if (!right) {
+ right = left;
+ }
+ series[i][0] = linearInterpolation(series[i][1], left, right);
+ }
+ }
+ return series;
+}
+
+function linearInterpolation(timestamp, left, right) {
+ if (left[1] === right[1]) {
+ return (left[0] + right[0]) / 2;
+ } else {
+ return left[0] + (right[0] - left[0]) / (right[1] - left[1]) * (timestamp - left[1]);
+ }
+}
+
+function findNearestRight(series, point) {
+ var point_index = _lodash2.default.indexOf(series, point);
+ var nearestRight;
+ for (var i = point_index; i < series.length; i++) {
+ if (series[i][0] !== null) {
+ return series[i];
+ }
+ }
+ return nearestRight;
+}
+
+function findNearestLeft(series, point) {
+ var point_index = _lodash2.default.indexOf(series, point);
+ var nearestLeft;
+ for (var i = point_index; i > 0; i--) {
+ if (series[i][0] !== null) {
+ return series[i];
+ }
+ }
+ return nearestLeft;
+}
+
+function timeShift(interval, range) {
+ var shift = utils.parseTimeShiftInterval(interval) / 1000;
+ return range.map(function (time) {
+ return time - shift;
+ });
+}
+
+function unShiftTimeSeries(interval, datapoints) {
+ var unshift = utils.parseTimeShiftInterval(interval);
+ return datapoints.map(function (dp) {
+ return [dp[0], dp[1] + unshift];
+ });
+}
+
+var metricFunctions = {
+ groupBy: groupByWrapper,
+ scale: scale,
+ delta: delta,
+ aggregateBy: aggregateByWrapper,
+ average: _lodash2.default.partial(aggregateWrapper, AVERAGE),
+ min: _lodash2.default.partial(aggregateWrapper, MIN),
+ max: _lodash2.default.partial(aggregateWrapper, MAX),
+ median: _lodash2.default.partial(aggregateWrapper, MEDIAN),
+ sumSeries: sumSeries,
+ top: _lodash2.default.partial(limit, 'top'),
+ bottom: _lodash2.default.partial(limit, 'bottom'),
+ timeShift: timeShift,
+ setAlias: setAlias,
+ setAliasByRegex: setAliasByRegex
+};
+
+var aggregationFunctions = {
+ avg: AVERAGE,
+ min: MIN,
+ max: MAX,
+ median: MEDIAN
+};
+
+exports.default = {
+ downsampleSeries: downsampleSeries,
+ groupBy: groupBy,
+ AVERAGE: AVERAGE,
+ MIN: MIN,
+ MAX: MAX,
+ MEDIAN: MEDIAN,
+ unShiftTimeSeries: unShiftTimeSeries,
+
+ get aggregationFunctions() {
+ return aggregationFunctions;
+ },
+
+ get metricFunctions() {
+ return metricFunctions;
+ }
+};
diff --git a/dist/test/datasource-zabbix/datasource.js b/dist/test/datasource-zabbix/datasource.js
new file mode 100644
index 0000000..2ecc64a
--- /dev/null
+++ b/dist/test/datasource-zabbix/datasource.js
@@ -0,0 +1,582 @@
+'use strict';
+
+Object.defineProperty(exports, "__esModule", {
+ value: true
+});
+exports.zabbixTemplateFormat = exports.ZabbixAPIDatasource = undefined;
+
+var _slicedToArray = function () { function sliceIterator(arr, i) { var _arr = []; var _n = true; var _d = false; var _e = undefined; try { for (var _i = arr[Symbol.iterator](), _s; !(_n = (_s = _i.next()).done); _n = true) { _arr.push(_s.value); if (i && _arr.length === i) break; } } catch (err) { _d = true; _e = err; } finally { try { if (!_n && _i["return"]) _i["return"](); } finally { if (_d) throw _e; } } return _arr; } return function (arr, i) { if (Array.isArray(arr)) { return arr; } else if (Symbol.iterator in Object(arr)) { return sliceIterator(arr, i); } else { throw new TypeError("Invalid attempt to destructure non-iterable instance"); } }; }();
+
+var _createClass = function () { function defineProperties(target, props) { for (var i = 0; i < props.length; i++) { var descriptor = props[i]; descriptor.enumerable = descriptor.enumerable || false; descriptor.configurable = true; if ("value" in descriptor) descriptor.writable = true; Object.defineProperty(target, descriptor.key, descriptor); } } return function (Constructor, protoProps, staticProps) { if (protoProps) defineProperties(Constructor.prototype, protoProps); if (staticProps) defineProperties(Constructor, staticProps); return Constructor; }; }(); //import angular from 'angular';
+
+
+var _lodash = require('lodash');
+
+var _lodash2 = _interopRequireDefault(_lodash);
+
+var _datemath = require('app/core/utils/datemath');
+
+var dateMath = _interopRequireWildcard(_datemath);
+
+var _utils = require('./utils');
+
+var utils = _interopRequireWildcard(_utils);
+
+var _migrations = require('./migrations');
+
+var migrations = _interopRequireWildcard(_migrations);
+
+var _metricFunctions = require('./metricFunctions');
+
+var metricFunctions = _interopRequireWildcard(_metricFunctions);
+
+var _dataProcessor = require('./dataProcessor');
+
+var _dataProcessor2 = _interopRequireDefault(_dataProcessor);
+
+var _responseHandler = require('./responseHandler');
+
+var _responseHandler2 = _interopRequireDefault(_responseHandler);
+
+require('./zabbix.js');
+
+var _zabbixAPICoreService = require('./zabbixAPICore.service.js');
+
+function _interopRequireWildcard(obj) { if (obj && obj.__esModule) { return obj; } else { var newObj = {}; if (obj != null) { for (var key in obj) { if (Object.prototype.hasOwnProperty.call(obj, key)) newObj[key] = obj[key]; } } newObj.default = obj; return newObj; } }
+
+function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; }
+
+function _classCallCheck(instance, Constructor) { if (!(instance instanceof Constructor)) { throw new TypeError("Cannot call a class as a function"); } }
+
+var ZabbixAPIDatasource = function () {
+
+ /** @ngInject */
+ function ZabbixAPIDatasource(instanceSettings, templateSrv, alertSrv, Zabbix) {
+ _classCallCheck(this, ZabbixAPIDatasource);
+
+ this.templateSrv = templateSrv;
+ this.alertSrv = alertSrv;
+
+ // 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);
+
+ this.zabbix = new Zabbix(this.url, this.username, this.password, this.basicAuth, this.withCredentials, this.cacheTTL);
+
+ // Use custom format for template variables
+ this.replaceTemplateVars = _lodash2.default.partial(replaceTemplateVars, this.templateSrv);
+ }
+
+ ////////////////////////
+ // Datasource methods //
+ ////////////////////////
+
+ /**
+ * 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.
+ */
+
+
+ _createClass(ZabbixAPIDatasource, [{
+ key: 'query',
+ value: function query(options) {
+ var _this = this;
+
+ 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 = _lodash2.default.map(options.targets, function (target) {
+ // Prevent changes of original object
+ target = _lodash2.default.cloneDeep(target);
+ _this.replaceTargetVariables(target, options);
+
+ // Apply Time-related functions (timeShift(), etc)
+ var timeFunctions = bindFunctionDefs(target.functions, 'Time');
+ if (timeFunctions.length) {
+ var _sequence = sequence(timeFunctions)([timeFrom, timeTo]),
+ _sequence2 = _slicedToArray(_sequence, 2),
+ time_from = _sequence2[0],
+ time_to = _sequence2[1];
+
+ timeFrom = time_from;
+ timeTo = time_to;
+ }
+
+ // Metrics or Text query mode
+ if (target.mode !== 1) {
+ // Migrate old targets
+ target = migrations.migrate(target);
+
+ // Don't request undefined and hidden targets
+ if (target.hide || !target.group || !target.host || !target.item) {
+ return [];
+ }
+
+ if (!target.mode || target.mode === 0) {
+ return _this.queryNumericData(target, timeFrom, timeTo, useTrends);
+ } else if (target.mode === 2) {
+ return _this.queryTextData(target, timeFrom, timeTo);
+ }
+ }
+
+ // IT services mode
+ else if (target.mode === 1) {
+ // Don't show undefined and hidden targets
+ if (target.hide || !target.itservice || !target.slaProperty) {
+ return [];
+ }
+
+ return _this.zabbix.getSLA(target.itservice.serviceid, timeFrom, timeTo).then(function (slaObject) {
+ return _responseHandler2.default.handleSLAResponse(target.itservice, target.slaProperty, slaObject);
+ });
+ }
+ });
+
+ // Data for panel (all targets)
+ return Promise.all(_lodash2.default.flatten(promises)).then(_lodash2.default.flatten).then(function (timeseries_data) {
+ return downsampleSeries(timeseries_data, options);
+ }).then(function (data) {
+ return { data: data };
+ });
+ }
+ }, {
+ key: 'queryNumericData',
+ value: function queryNumericData(target, timeFrom, timeTo, useTrends) {
+ var _this2 = this;
+
+ var options = {
+ itemtype: 'num'
+ };
+ return this.zabbix.getItemsFromTarget(target, options).then(function (items) {
+ var getHistoryPromise = void 0;
+
+ if (useTrends) {
+ (function () {
+ var valueType = _this2.getTrendValueType(target);
+ getHistoryPromise = _this2.zabbix.getTrend(items, timeFrom, timeTo).then(function (history) {
+ return _responseHandler2.default.handleTrends(history, items, valueType);
+ });
+ })();
+ } else {
+ // Use history
+ getHistoryPromise = _this2.zabbix.getHistory(items, timeFrom, timeTo).then(function (history) {
+ return _responseHandler2.default.handleHistory(history, items);
+ });
+ }
+
+ return getHistoryPromise.then(function (timeseries_data) {
+ return _this2.applyDataProcessingFunctions(timeseries_data, target);
+ });
+ });
+ }
+ }, {
+ key: 'getTrendValueType',
+ value: function getTrendValueType(target) {
+ // Find trendValue() function and get specified trend value
+ var trendFunctions = _lodash2.default.map(metricFunctions.getCategories()['Trends'], 'name');
+ var trendValueFunc = _lodash2.default.find(target.functions, function (func) {
+ return _lodash2.default.includes(trendFunctions, func.def.name);
+ });
+ return trendValueFunc ? trendValueFunc.params[0] : "avg";
+ }
+ }, {
+ key: 'applyDataProcessingFunctions',
+ value: function applyDataProcessingFunctions(timeseries_data, target) {
+ var transformFunctions = bindFunctionDefs(target.functions, 'Transform');
+ var aggregationFunctions = bindFunctionDefs(target.functions, 'Aggregate');
+ var filterFunctions = bindFunctionDefs(target.functions, 'Filter');
+ var aliasFunctions = bindFunctionDefs(target.functions, 'Alias');
+
+ // Apply transformation functions
+ timeseries_data = _lodash2.default.map(timeseries_data, function (timeseries) {
+ timeseries.datapoints = sequence(transformFunctions)(timeseries.datapoints);
+ return timeseries;
+ });
+
+ // Apply filter functions
+ if (filterFunctions.length) {
+ timeseries_data = sequence(filterFunctions)(timeseries_data);
+ }
+
+ // Apply aggregations
+ if (aggregationFunctions.length) {
+ (function () {
+ var dp = _lodash2.default.map(timeseries_data, 'datapoints');
+ dp = sequence(aggregationFunctions)(dp);
+
+ var aggFuncNames = _lodash2.default.map(metricFunctions.getCategories()['Aggregate'], 'name');
+ var lastAgg = _lodash2.default.findLast(target.functions, function (func) {
+ return _lodash2.default.includes(aggFuncNames, func.def.name);
+ });
+
+ timeseries_data = [{
+ target: lastAgg.text,
+ datapoints: dp
+ }];
+ })();
+ }
+
+ // Apply alias functions
+ _lodash2.default.forEach(timeseries_data, sequence(aliasFunctions));
+
+ // Apply Time-related functions (timeShift(), etc)
+ // Find timeShift() function and get specified trend value
+ this.applyTimeShiftFunction(timeseries_data, target);
+
+ return timeseries_data;
+ }
+ }, {
+ key: 'applyTimeShiftFunction',
+ value: function applyTimeShiftFunction(timeseries_data, target) {
+ // Find timeShift() function and get specified interval
+ var timeShiftFunc = _lodash2.default.find(target.functions, function (func) {
+ return func.def.name === 'timeShift';
+ });
+ if (timeShiftFunc) {
+ (function () {
+ var shift = timeShiftFunc.params[0];
+ _lodash2.default.forEach(timeseries_data, function (series) {
+ series.datapoints = _dataProcessor2.default.unShiftTimeSeries(shift, series.datapoints);
+ });
+ })();
+ }
+ }
+ }, {
+ key: 'queryTextData',
+ value: function queryTextData(target, timeFrom, timeTo) {
+ var _this3 = this;
+
+ var options = {
+ itemtype: 'text'
+ };
+ return this.zabbix.getItemsFromTarget(target, options).then(function (items) {
+ if (items.length) {
+ return _this3.zabbix.getHistory(items, timeFrom, timeTo).then(function (history) {
+ return _responseHandler2.default.convertHistory(history, items, false, function (point) {
+ var value = point.value;
+
+ // Regex-based extractor
+ if (target.textFilter) {
+ value = extractText(point.value, target.textFilter, target.useCaptureGroups);
+ }
+
+ return [value, point.clock * 1000];
+ });
+ });
+ } else {
+ return Promise.resolve([]);
+ }
+ });
+ }
+
+ /**
+ * Test connection to Zabbix API
+ * @return {object} Connection status and Zabbix API version
+ */
+
+ }, {
+ key: 'testDatasource',
+ value: function testDatasource() {
+ var _this4 = this;
+
+ var zabbixVersion = void 0;
+ return this.zabbix.getVersion().then(function (version) {
+ zabbixVersion = version;
+ return _this4.zabbix.login();
+ }).then(function () {
+ return {
+ status: "success",
+ title: "Success",
+ message: "Zabbix API version: " + zabbixVersion
+ };
+ }).catch(function (error) {
+ if (error instanceof _zabbixAPICoreService.ZabbixAPIError) {
+ return {
+ status: "error",
+ title: error.message,
+ message: error.data
+ };
+ } else {
+ return {
+ status: "error",
+ title: "Connection failed",
+ message: "Could not connect to given url"
+ };
+ }
+ });
+ }
+
+ ////////////////
+ // 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.
+ */
+
+ }, {
+ key: 'metricFindQuery',
+ value: function metricFindQuery(query) {
+ var _this5 = this;
+
+ var result = void 0;
+ var parts = [];
+
+ // Split query. Query structure: group.host.app.item
+ _lodash2.default.each(query.split('.'), function (part) {
+ part = _this5.replaceTemplateVars(part, {});
+
+ // Replace wildcard to regex
+ if (part === '*') {
+ part = '/.*/';
+ }
+ parts.push(part);
+ });
+ var template = _lodash2.default.zipObject(['group', 'host', 'app', 'item'], parts);
+
+ // Get items
+ if (parts.length === 4) {
+ // Search for all items, even it's not belong to any application
+ if (template.app === '/.*/') {
+ template.app = '';
+ }
+ result = this.zabbix.getItems(template.group, template.host, template.app, template.item);
+ } else if (parts.length === 3) {
+ // Get applications
+ result = this.zabbix.getApps(template.group, template.host, template.app);
+ } else if (parts.length === 2) {
+ // Get hosts
+ result = this.zabbix.getHosts(template.group, template.host);
+ } else if (parts.length === 1) {
+ // Get groups
+ result = this.zabbix.getGroups(template.group);
+ } else {
+ result = Promise.resolve([]);
+ }
+
+ return result.then(function (metrics) {
+ return metrics.map(formatMetric);
+ });
+ }
+
+ /////////////////
+ // Annotations //
+ /////////////////
+
+ }, {
+ key: 'annotationQuery',
+ value: function annotationQuery(options) {
+ var _this6 = this;
+
+ 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 showOkEvents = annotation.showOkEvents ? [0, 1] : 1;
+
+ // Show all triggers
+ var showTriggers = [0, 1];
+
+ var getTriggers = this.zabbix.getTriggers(this.replaceTemplateVars(annotation.group, {}), this.replaceTemplateVars(annotation.host, {}), this.replaceTemplateVars(annotation.application, {}), showTriggers);
+
+ return getTriggers.then(function (triggers) {
+
+ // Filter triggers by description
+ if (utils.isRegex(annotation.trigger)) {
+ triggers = _lodash2.default.filter(triggers, function (trigger) {
+ return utils.buildRegex(annotation.trigger).test(trigger.description);
+ });
+ } else if (annotation.trigger) {
+ triggers = _lodash2.default.filter(triggers, function (trigger) {
+ return trigger.description === annotation.trigger;
+ });
+ }
+
+ // Remove events below the chose severity
+ triggers = _lodash2.default.filter(triggers, function (trigger) {
+ return Number(trigger.priority) >= Number(annotation.minseverity);
+ });
+
+ var objectids = _lodash2.default.map(triggers, 'triggerid');
+ return _this6.zabbix.getEvents(objectids, timeFrom, timeTo, showOkEvents).then(function (events) {
+ var indexedTriggers = _lodash2.default.keyBy(triggers, 'triggerid');
+
+ // Hide acknowledged events if option enabled
+ if (annotation.hideAcknowledged) {
+ events = _lodash2.default.filter(events, function (event) {
+ return !event.acknowledges.length;
+ });
+ }
+
+ return _lodash2.default.map(events, function (event) {
+ var tags = void 0;
+ if (annotation.showHostname) {
+ tags = _lodash2.default.map(event.hosts, 'name');
+ }
+
+ // Show event type (OK or Problem)
+ var title = Number(event.value) ? 'Problem' : 'OK';
+
+ var formatted_acknowledges = utils.formatAcknowledges(event.acknowledges);
+ return {
+ annotation: annotation,
+ time: event.clock * 1000,
+ title: title,
+ tags: tags,
+ text: indexedTriggers[event.objectid].description + formatted_acknowledges
+ };
+ });
+ });
+ });
+ }
+
+ // Replace template variables
+
+ }, {
+ key: 'replaceTargetVariables',
+ value: function replaceTargetVariables(target, options) {
+ var _this7 = this;
+
+ var parts = ['group', 'host', 'application', 'item'];
+ parts.forEach(function (p) {
+ if (target[p] && target[p].filter) {
+ target[p].filter = _this7.replaceTemplateVars(target[p].filter, options.scopedVars);
+ }
+ });
+ target.textFilter = this.replaceTemplateVars(target.textFilter, options.scopedVars);
+
+ _lodash2.default.forEach(target.functions, function (func) {
+ func.params = func.params.map(function (param) {
+ if (typeof param === 'number') {
+ return +_this7.templateSrv.replace(param.toString(), options.scopedVars);
+ } else {
+ return _this7.templateSrv.replace(param, options.scopedVars);
+ }
+ });
+ });
+ }
+ }]);
+
+ return ZabbixAPIDatasource;
+}();
+
+function bindFunctionDefs(functionDefs, category) {
+ var aggregationFunctions = _lodash2.default.map(metricFunctions.getCategories()[category], 'name');
+ var aggFuncDefs = _lodash2.default.filter(functionDefs, function (func) {
+ return _lodash2.default.includes(aggregationFunctions, func.def.name);
+ });
+
+ return _lodash2.default.map(aggFuncDefs, function (func) {
+ var funcInstance = metricFunctions.createFuncInstance(func.def, func.params);
+ return funcInstance.bindFunction(_dataProcessor2.default.metricFunctions);
+ });
+}
+
+function downsampleSeries(timeseries_data, options) {
+ return _lodash2.default.map(timeseries_data, function (timeseries) {
+ if (timeseries.datapoints.length > options.maxDataPoints) {
+ timeseries.datapoints = _dataProcessor2.default.groupBy(options.interval, _dataProcessor2.default.AVERAGE, timeseries.datapoints);
+ }
+ return timeseries;
+ });
+}
+
+function formatMetric(metricObj) {
+ return {
+ text: metricObj.name,
+ expandable: false
+ };
+}
+
+/**
+ * Custom formatter for template variables.
+ * Default Grafana "regex" formatter returns
+ * value1|value2
+ * This formatter returns
+ * (value1|value2)
+ * This format needed for using in complex regex with
+ * template variables, for example
+ * /CPU $cpu_item.*time/ where $cpu_item is system,user,iowait
+ */
+function zabbixTemplateFormat(value) {
+ if (typeof value === 'string') {
+ return utils.escapeRegex(value);
+ }
+
+ var escapedValues = _lodash2.default.map(value, utils.escapeRegex);
+ return '(' + escapedValues.join('|') + ')';
+}
+
+/**
+ * If template variables are used in request, replace it using regex format
+ * and wrap with '/' for proper multi-value work. Example:
+ * $variable selected as a, b, c
+ * We use filter $variable
+ * $variable -> a|b|c -> /a|b|c/
+ * /$variable/ -> /a|b|c/ -> /a|b|c/
+ */
+function replaceTemplateVars(templateSrv, target, scopedVars) {
+ var replacedTarget = templateSrv.replace(target, scopedVars, zabbixTemplateFormat);
+ if (target !== replacedTarget && !utils.isRegex(replacedTarget)) {
+ replacedTarget = '/^' + replacedTarget + '$/';
+ }
+ return replacedTarget;
+}
+
+function extractText(str, pattern, useCaptureGroups) {
+ var extractPattern = new RegExp(pattern);
+ var extractedValue = extractPattern.exec(str);
+ if (extractedValue) {
+ if (useCaptureGroups) {
+ extractedValue = extractedValue[1];
+ } else {
+ extractedValue = extractedValue[0];
+ }
+ }
+ return extractedValue;
+}
+
+// Apply function one by one:
+// sequence([a(), b(), c()]) = c(b(a()));
+function sequence(funcsArray) {
+ return function (result) {
+ for (var i = 0; i < funcsArray.length; i++) {
+ result = funcsArray[i].call(this, result);
+ }
+ return result;
+ };
+}
+
+exports.ZabbixAPIDatasource = ZabbixAPIDatasource;
+exports.zabbixTemplateFormat = zabbixTemplateFormat;
+
+// Fix for backward compatibility with lodash 2.4
+
+if (!_lodash2.default.includes) {
+ _lodash2.default.includes = _lodash2.default.contains;
+}
+if (!_lodash2.default.keyBy) {
+ _lodash2.default.keyBy = _lodash2.default.indexBy;
+}
diff --git a/dist/test/datasource-zabbix/metric-function-editor.directive.js b/dist/test/datasource-zabbix/metric-function-editor.directive.js
new file mode 100644
index 0000000..02135e9
--- /dev/null
+++ b/dist/test/datasource-zabbix/metric-function-editor.directive.js
@@ -0,0 +1,246 @@
+'use strict';
+
+var _angular = require('angular');
+
+var _angular2 = _interopRequireDefault(_angular);
+
+var _lodash = require('lodash');
+
+var _lodash2 = _interopRequireDefault(_lodash);
+
+var _jquery = require('jquery');
+
+var _jquery2 = _interopRequireDefault(_jquery);
+
+function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; }
+
+/** @ngInject */
+_angular2.default.module('grafana.directives').directive('metricFunctionEditor', function ($compile, templateSrv) {
+
+ var funcSpanTemplate = '{{func.def.name}} ( ';
+ var paramTemplate = ' ';
+
+ var funcControlsTemplate = '' + ' ' + ' ' + ' ' + ' ' + '
';
+
+ return {
+ restrict: 'A',
+ link: function postLink($scope, elem) {
+ var $funcLink = (0, _jquery2.default)(funcSpanTemplate);
+ var $funcControls = (0, _jquery2.default)(funcControlsTemplate);
+ var ctrl = $scope.ctrl;
+ var func = $scope.func;
+ var funcDef = func.def;
+ var scheduledRelink = false;
+ var paramCountAtLink = 0;
+
+ function clickFuncParam(paramIndex) {
+ /*jshint validthis:true */
+
+ var $link = (0, _jquery2.default)(this);
+ var $input = $link.next();
+
+ $input.val(func.params[paramIndex]);
+ $input.css('width', $link.width() + 16 + 'px');
+
+ $link.hide();
+ $input.show();
+ $input.focus();
+ $input.select();
+
+ var typeahead = $input.data('typeahead');
+ if (typeahead) {
+ $input.val('');
+ typeahead.lookup();
+ }
+ }
+
+ function scheduledRelinkIfNeeded() {
+ if (paramCountAtLink === func.params.length) {
+ return;
+ }
+
+ if (!scheduledRelink) {
+ scheduledRelink = true;
+ setTimeout(function () {
+ relink();
+ scheduledRelink = false;
+ }, 200);
+ }
+ }
+
+ function inputBlur(paramIndex) {
+ /*jshint validthis:true */
+ var $input = (0, _jquery2.default)(this);
+ var $link = $input.prev();
+ var newValue = $input.val();
+
+ if (newValue !== '' || func.def.params[paramIndex].optional) {
+ $link.html(templateSrv.highlightVariablesAsHtml(newValue));
+
+ func.updateParam($input.val(), paramIndex);
+ scheduledRelinkIfNeeded();
+
+ $scope.$apply(function () {
+ ctrl.targetChanged();
+ });
+
+ $input.hide();
+ $link.show();
+ }
+ }
+
+ function inputKeyPress(paramIndex, e) {
+ /*jshint validthis:true */
+ if (e.which === 13) {
+ inputBlur.call(this, paramIndex);
+ }
+ }
+
+ function inputKeyDown() {
+ /*jshint validthis:true */
+ this.style.width = (3 + this.value.length) * 8 + 'px';
+ }
+
+ function addTypeahead($input, paramIndex) {
+ $input.attr('data-provide', 'typeahead');
+
+ var options = funcDef.params[paramIndex].options;
+ if (funcDef.params[paramIndex].type === 'int' || funcDef.params[paramIndex].type === 'float') {
+ options = _lodash2.default.map(options, function (val) {
+ return val.toString();
+ });
+ }
+
+ $input.typeahead({
+ source: options,
+ minLength: 0,
+ items: 20,
+ updater: function updater(value) {
+ setTimeout(function () {
+ inputBlur.call($input[0], paramIndex);
+ }, 0);
+ return value;
+ }
+ });
+
+ var typeahead = $input.data('typeahead');
+ typeahead.lookup = function () {
+ this.query = this.$element.val() || '';
+ return this.process(this.source);
+ };
+ }
+
+ function toggleFuncControls() {
+ var targetDiv = elem.closest('.tight-form');
+
+ if (elem.hasClass('show-function-controls')) {
+ elem.removeClass('show-function-controls');
+ targetDiv.removeClass('has-open-function');
+ $funcControls.hide();
+ return;
+ }
+
+ elem.addClass('show-function-controls');
+ targetDiv.addClass('has-open-function');
+
+ $funcControls.show();
+ }
+
+ function addElementsAndCompile() {
+ $funcControls.appendTo(elem);
+ $funcLink.appendTo(elem);
+
+ _lodash2.default.each(funcDef.params, function (param, index) {
+ if (param.optional && func.params.length <= index) {
+ return;
+ }
+
+ if (index > 0) {
+ (0, _jquery2.default)(', ').appendTo(elem);
+ }
+
+ var paramValue = templateSrv.highlightVariablesAsHtml(func.params[index]);
+ var $paramLink = (0, _jquery2.default)('' + paramValue + ' ');
+ var $input = (0, _jquery2.default)(paramTemplate);
+
+ paramCountAtLink++;
+
+ $paramLink.appendTo(elem);
+ $input.appendTo(elem);
+
+ $input.blur(_lodash2.default.partial(inputBlur, index));
+ $input.keyup(inputKeyDown);
+ $input.keypress(_lodash2.default.partial(inputKeyPress, index));
+ $paramLink.click(_lodash2.default.partial(clickFuncParam, index));
+
+ if (funcDef.params[index].options) {
+ addTypeahead($input, index);
+ }
+ });
+
+ (0, _jquery2.default)(') ').appendTo(elem);
+
+ $compile(elem.contents())($scope);
+ }
+
+ function ifJustAddedFocusFistParam() {
+ if ($scope.func.added) {
+ $scope.func.added = false;
+ setTimeout(function () {
+ elem.find('.graphite-func-param-link').first().click();
+ }, 10);
+ }
+ }
+
+ function registerFuncControlsToggle() {
+ $funcLink.click(toggleFuncControls);
+ }
+
+ function registerFuncControlsActions() {
+ $funcControls.click(function (e) {
+ var $target = (0, _jquery2.default)(e.target);
+ if ($target.hasClass('fa-remove')) {
+ toggleFuncControls();
+ $scope.$apply(function () {
+ ctrl.removeFunction($scope.func);
+ });
+ return;
+ }
+
+ if ($target.hasClass('fa-arrow-left')) {
+ $scope.$apply(function () {
+ _lodash2.default.move($scope.target.functions, $scope.$index, $scope.$index - 1);
+ ctrl.targetChanged();
+ });
+ return;
+ }
+
+ if ($target.hasClass('fa-arrow-right')) {
+ $scope.$apply(function () {
+ _lodash2.default.move($scope.target.functions, $scope.$index, $scope.$index + 1);
+ ctrl.targetChanged();
+ });
+ return;
+ }
+
+ if ($target.hasClass('fa-question-circle')) {
+ var docSite = "http://docs.grafana-zabbix.org/reference/functions/";
+ window.open(docSite + '#' + funcDef.name.toLowerCase(), '_blank');
+ return;
+ }
+ });
+ }
+
+ function relink() {
+ elem.children().remove();
+
+ addElementsAndCompile();
+ ifJustAddedFocusFistParam();
+ registerFuncControlsToggle();
+ registerFuncControlsActions();
+ }
+
+ relink();
+ }
+ };
+});
diff --git a/dist/test/datasource-zabbix/metricFunctions.js b/dist/test/datasource-zabbix/metricFunctions.js
new file mode 100644
index 0000000..1a54a57
--- /dev/null
+++ b/dist/test/datasource-zabbix/metricFunctions.js
@@ -0,0 +1,292 @@
+'use strict';
+
+Object.defineProperty(exports, "__esModule", {
+ value: true
+});
+
+var _createClass = function () { function defineProperties(target, props) { for (var i = 0; i < props.length; i++) { var descriptor = props[i]; descriptor.enumerable = descriptor.enumerable || false; descriptor.configurable = true; if ("value" in descriptor) descriptor.writable = true; Object.defineProperty(target, descriptor.key, descriptor); } } return function (Constructor, protoProps, staticProps) { if (protoProps) defineProperties(Constructor.prototype, protoProps); if (staticProps) defineProperties(Constructor, staticProps); return Constructor; }; }();
+
+exports.createFuncInstance = createFuncInstance;
+exports.getFuncDef = getFuncDef;
+exports.getCategories = getCategories;
+
+var _lodash = require('lodash');
+
+var _lodash2 = _interopRequireDefault(_lodash);
+
+var _jquery = require('jquery');
+
+var _jquery2 = _interopRequireDefault(_jquery);
+
+function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; }
+
+function _classCallCheck(instance, Constructor) { if (!(instance instanceof Constructor)) { throw new TypeError("Cannot call a class as a function"); } }
+
+var index = [];
+var categories = {
+ Transform: [],
+ Aggregate: [],
+ Filter: [],
+ Trends: [],
+ Time: [],
+ Alias: []
+};
+
+function addFuncDef(funcDef) {
+ funcDef.params = funcDef.params || [];
+ funcDef.defaultParams = funcDef.defaultParams || [];
+
+ if (funcDef.category) {
+ categories[funcDef.category].push(funcDef);
+ }
+ index[funcDef.name] = funcDef;
+ index[funcDef.shortName || funcDef.name] = funcDef;
+}
+
+// Transform
+
+addFuncDef({
+ name: 'groupBy',
+ category: 'Transform',
+ params: [{ name: 'interval', type: 'string' }, { name: 'function', type: 'string', options: ['avg', 'min', 'max', 'median'] }],
+ defaultParams: ['1m', 'avg']
+});
+
+addFuncDef({
+ name: 'scale',
+ category: 'Transform',
+ params: [{ name: 'factor', type: 'float', options: [100, 0.01, 10, -1] }],
+ defaultParams: [100]
+});
+
+addFuncDef({
+ name: 'delta',
+ category: 'Transform',
+ params: [],
+ defaultParams: []
+});
+
+// Aggregate
+
+addFuncDef({
+ name: 'sumSeries',
+ category: 'Aggregate',
+ params: [],
+ defaultParams: []
+});
+
+addFuncDef({
+ name: 'median',
+ category: 'Aggregate',
+ params: [{ name: 'interval', type: 'string' }],
+ defaultParams: ['1m']
+});
+
+addFuncDef({
+ name: 'average',
+ category: 'Aggregate',
+ params: [{ name: 'interval', type: 'string' }],
+ defaultParams: ['1m']
+});
+
+addFuncDef({
+ name: 'min',
+ category: 'Aggregate',
+ params: [{ name: 'interval', type: 'string' }],
+ defaultParams: ['1m']
+});
+
+addFuncDef({
+ name: 'max',
+ category: 'Aggregate',
+ params: [{ name: 'interval', type: 'string' }],
+ defaultParams: ['1m']
+});
+
+addFuncDef({
+ name: 'aggregateBy',
+ category: 'Aggregate',
+ params: [{ name: 'interval', type: 'string' }, { name: 'function', type: 'string', options: ['avg', 'min', 'max', 'median'] }],
+ defaultParams: ['1m', 'avg']
+});
+
+// Filter
+
+addFuncDef({
+ name: 'top',
+ category: 'Filter',
+ params: [{ name: 'number', type: 'int' }, { name: 'value', type: 'string', options: ['avg', 'min', 'max', 'median'] }],
+ defaultParams: [5, 'avg']
+});
+
+addFuncDef({
+ name: 'bottom',
+ category: 'Filter',
+ params: [{ name: 'number', type: 'int' }, { name: 'value', type: 'string', options: ['avg', 'min', 'max', 'median'] }],
+ defaultParams: [5, 'avg']
+});
+
+// Trends
+
+addFuncDef({
+ name: 'trendValue',
+ category: 'Trends',
+ params: [{ name: 'type', type: 'string', options: ['avg', 'min', 'max'] }],
+ defaultParams: ['avg']
+});
+
+// Time
+
+addFuncDef({
+ name: 'timeShift',
+ category: 'Time',
+ params: [{ name: 'interval', type: 'string', options: ['24h', '7d', '1M', '+24h', '-24h'] }],
+ defaultParams: ['24h']
+});
+
+//Alias
+
+addFuncDef({
+ name: 'setAlias',
+ category: 'Alias',
+ params: [{ name: 'alias', type: 'string' }],
+ defaultParams: []
+});
+
+addFuncDef({
+ name: 'setAliasByRegex',
+ category: 'Alias',
+ params: [{ name: 'aliasByRegex', type: 'string' }],
+ defaultParams: []
+});
+
+_lodash2.default.each(categories, function (funcList, catName) {
+ categories[catName] = _lodash2.default.sortBy(funcList, 'name');
+});
+
+var FuncInstance = function () {
+ function FuncInstance(funcDef, params) {
+ _classCallCheck(this, FuncInstance);
+
+ this.def = funcDef;
+
+ if (params) {
+ this.params = params;
+ } else {
+ // Create with default params
+ this.params = [];
+ this.params = funcDef.defaultParams.slice(0);
+ }
+
+ this.updateText();
+ }
+
+ _createClass(FuncInstance, [{
+ key: 'bindFunction',
+ value: function bindFunction(metricFunctions) {
+ var func = metricFunctions[this.def.name];
+ if (func) {
+
+ // Bind function arguments
+ var bindedFunc = func;
+ var param;
+ for (var i = 0; i < this.params.length; i++) {
+ param = this.params[i];
+
+ // Convert numeric params
+ if (this.def.params[i].type === 'int' || this.def.params[i].type === 'float') {
+ param = Number(param);
+ }
+ bindedFunc = _lodash2.default.partial(bindedFunc, param);
+ }
+ return bindedFunc;
+ } else {
+ throw { message: 'Method not found ' + this.def.name };
+ }
+ }
+ }, {
+ key: 'render',
+ value: function render(metricExp) {
+ var str = this.def.name + '(';
+ var parameters = _lodash2.default.map(this.params, function (value, index) {
+
+ var paramType = this.def.params[index].type;
+ if (paramType === 'int' || paramType === 'float' || paramType === 'value_or_series' || paramType === 'boolean') {
+ return value;
+ } else if (paramType === 'int_or_interval' && _jquery2.default.isNumeric(value)) {
+ return value;
+ }
+
+ return "'" + value + "'";
+ }, this);
+
+ if (metricExp) {
+ parameters.unshift(metricExp);
+ }
+
+ return str + parameters.join(', ') + ')';
+ }
+ }, {
+ key: '_hasMultipleParamsInString',
+ value: function _hasMultipleParamsInString(strValue, index) {
+ if (strValue.indexOf(',') === -1) {
+ return false;
+ }
+
+ return this.def.params[index + 1] && this.def.params[index + 1].optional;
+ }
+ }, {
+ key: 'updateParam',
+ value: function updateParam(strValue, index) {
+ // handle optional parameters
+ // if string contains ',' and next param is optional, split and update both
+ if (this._hasMultipleParamsInString(strValue, index)) {
+ _lodash2.default.each(strValue.split(','), function (partVal, idx) {
+ this.updateParam(partVal.trim(), idx);
+ }, this);
+ return;
+ }
+
+ if (strValue === '' && this.def.params[index].optional) {
+ this.params.splice(index, 1);
+ } else {
+ this.params[index] = strValue;
+ }
+
+ this.updateText();
+ }
+ }, {
+ key: 'updateText',
+ value: function updateText() {
+ if (this.params.length === 0) {
+ this.text = this.def.name + '()';
+ return;
+ }
+
+ var text = this.def.name + '(';
+ text += this.params.join(', ');
+ text += ')';
+ this.text = text;
+ }
+ }]);
+
+ return FuncInstance;
+}();
+
+function createFuncInstance(funcDef, params) {
+ if (_lodash2.default.isString(funcDef)) {
+ if (!index[funcDef]) {
+ throw { message: 'Method not found ' + name };
+ }
+ funcDef = index[funcDef];
+ }
+ return new FuncInstance(funcDef, params);
+}
+
+function getFuncDef(name) {
+ return index[name];
+}
+
+function getCategories() {
+ return categories;
+}
diff --git a/dist/test/datasource-zabbix/migrations.js b/dist/test/datasource-zabbix/migrations.js
new file mode 100644
index 0000000..dc422c0
--- /dev/null
+++ b/dist/test/datasource-zabbix/migrations.js
@@ -0,0 +1,48 @@
+"use strict";
+
+Object.defineProperty(exports, "__esModule", {
+ value: true
+});
+exports.isGrafana2target = isGrafana2target;
+exports.migrateFrom2To3version = migrateFrom2To3version;
+exports.migrate = migrate;
+/**
+ * Query format migration.
+ * This module can detect query format version and make migration.
+ */
+
+function isGrafana2target(target) {
+ if (!target.mode || target.mode === 0 || target.mode === 2) {
+ if ((target.hostFilter || target.itemFilter || target.downsampleFunction || target.host && target.host.host) && target.item.filter === undefined && target.host.filter === undefined) {
+ return true;
+ } else {
+ return false;
+ }
+ } else {
+ return false;
+ }
+}
+
+function migrateFrom2To3version(target) {
+ target.group.filter = target.group.name === "*" ? "/.*/" : target.group.name;
+ target.host.filter = target.host.name === "*" ? convertToRegex(target.hostFilter) : target.host.name;
+ target.application.filter = target.application.name === "*" ? "" : target.application.name;
+ target.item.filter = target.item.name === "All" ? convertToRegex(target.itemFilter) : target.item.name;
+ return target;
+}
+
+function migrate(target) {
+ if (isGrafana2target(target)) {
+ return migrateFrom2To3version(target);
+ } else {
+ return target;
+ }
+}
+
+function convertToRegex(str) {
+ if (str) {
+ return '/' + str + '/';
+ } else {
+ return '/.*/';
+ }
+}
diff --git a/dist/test/datasource-zabbix/module.js b/dist/test/datasource-zabbix/module.js
new file mode 100644
index 0000000..10f88d0
--- /dev/null
+++ b/dist/test/datasource-zabbix/module.js
@@ -0,0 +1,36 @@
+'use strict';
+
+Object.defineProperty(exports, "__esModule", {
+ value: true
+});
+exports.AnnotationsQueryCtrl = exports.QueryOptionsCtrl = exports.QueryCtrl = exports.ConfigCtrl = exports.Datasource = undefined;
+
+var _datasource = require('./datasource');
+
+var _query = require('./query.controller');
+
+function _classCallCheck(instance, Constructor) { if (!(instance instanceof Constructor)) { throw new TypeError("Cannot call a class as a function"); } }
+
+var ZabbixConfigController = function ZabbixConfigController() {
+ _classCallCheck(this, ZabbixConfigController);
+};
+
+ZabbixConfigController.templateUrl = 'datasource-zabbix/partials/config.html';
+
+var ZabbixQueryOptionsController = function ZabbixQueryOptionsController() {
+ _classCallCheck(this, ZabbixQueryOptionsController);
+};
+
+ZabbixQueryOptionsController.templateUrl = 'datasource-zabbix/partials/query.options.html';
+
+var ZabbixAnnotationsQueryController = function ZabbixAnnotationsQueryController() {
+ _classCallCheck(this, ZabbixAnnotationsQueryController);
+};
+
+ZabbixAnnotationsQueryController.templateUrl = 'datasource-zabbix/partials/annotations.editor.html';
+
+exports.Datasource = _datasource.ZabbixAPIDatasource;
+exports.ConfigCtrl = ZabbixConfigController;
+exports.QueryCtrl = _query.ZabbixQueryController;
+exports.QueryOptionsCtrl = ZabbixQueryOptionsController;
+exports.AnnotationsQueryCtrl = ZabbixAnnotationsQueryController;
diff --git a/dist/test/datasource-zabbix/query.controller.js b/dist/test/datasource-zabbix/query.controller.js
new file mode 100644
index 0000000..24542f5
--- /dev/null
+++ b/dist/test/datasource-zabbix/query.controller.js
@@ -0,0 +1,378 @@
+'use strict';
+
+Object.defineProperty(exports, "__esModule", {
+ value: true
+});
+exports.ZabbixQueryController = undefined;
+
+var _createClass = function () { function defineProperties(target, props) { for (var i = 0; i < props.length; i++) { var descriptor = props[i]; descriptor.enumerable = descriptor.enumerable || false; descriptor.configurable = true; if ("value" in descriptor) descriptor.writable = true; Object.defineProperty(target, descriptor.key, descriptor); } } return function (Constructor, protoProps, staticProps) { if (protoProps) defineProperties(Constructor.prototype, protoProps); if (staticProps) defineProperties(Constructor, staticProps); return Constructor; }; }();
+
+var _sdk = require('app/plugins/sdk');
+
+var _angular = require('angular');
+
+var _angular2 = _interopRequireDefault(_angular);
+
+var _lodash = require('lodash');
+
+var _lodash2 = _interopRequireDefault(_lodash);
+
+var _utils = require('./utils');
+
+var utils = _interopRequireWildcard(_utils);
+
+var _metricFunctions = require('./metricFunctions');
+
+var metricFunctions = _interopRequireWildcard(_metricFunctions);
+
+var _migrations = require('./migrations');
+
+var migrations = _interopRequireWildcard(_migrations);
+
+require('./add-metric-function.directive');
+
+require('./metric-function-editor.directive');
+
+require('./css/query-editor.css!');
+
+function _interopRequireWildcard(obj) { if (obj && obj.__esModule) { return obj; } else { var newObj = {}; if (obj != null) { for (var key in obj) { if (Object.prototype.hasOwnProperty.call(obj, key)) newObj[key] = obj[key]; } } newObj.default = obj; return newObj; } }
+
+function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; }
+
+function _classCallCheck(instance, Constructor) { if (!(instance instanceof Constructor)) { throw new TypeError("Cannot call a class as a function"); } }
+
+function _possibleConstructorReturn(self, call) { if (!self) { throw new ReferenceError("this hasn't been initialised - super() hasn't been called"); } return call && (typeof call === "object" || typeof call === "function") ? call : self; }
+
+function _inherits(subClass, superClass) { if (typeof superClass !== "function" && superClass !== null) { throw new TypeError("Super expression must either be null or a function, not " + typeof superClass); } subClass.prototype = Object.create(superClass && superClass.prototype, { constructor: { value: subClass, enumerable: false, writable: true, configurable: true } }); if (superClass) Object.setPrototypeOf ? Object.setPrototypeOf(subClass, superClass) : subClass.__proto__ = superClass; }
+
+var ZabbixQueryController = exports.ZabbixQueryController = function (_QueryCtrl) {
+ _inherits(ZabbixQueryController, _QueryCtrl);
+
+ // ZabbixQueryCtrl constructor
+ function ZabbixQueryController($scope, $injector, $rootScope, $sce, templateSrv) {
+ _classCallCheck(this, ZabbixQueryController);
+
+ var _this = _possibleConstructorReturn(this, (ZabbixQueryController.__proto__ || Object.getPrototypeOf(ZabbixQueryController)).call(this, $scope, $injector));
+
+ _this.zabbix = _this.datasource.zabbix;
+
+ // Use custom format for template variables
+ _this.replaceTemplateVars = _this.datasource.replaceTemplateVars;
+ _this.templateSrv = templateSrv;
+
+ _this.editorModes = {
+ 0: { value: 'num', text: 'Metrics', mode: 0 },
+ 1: { value: 'itservice', text: 'IT Services', mode: 1 },
+ 2: { value: 'text', text: 'Text', mode: 2 }
+ };
+
+ // Map functions for bs-typeahead
+ _this.getGroupNames = _lodash2.default.partial(getMetricNames, _this, 'groupList');
+ _this.getHostNames = _lodash2.default.partial(getMetricNames, _this, 'hostList');
+ _this.getApplicationNames = _lodash2.default.partial(getMetricNames, _this, 'appList');
+ _this.getItemNames = _lodash2.default.partial(getMetricNames, _this, 'itemList');
+
+ // Update metric suggestion when template variable was changed
+ $rootScope.$on('template-variable-value-updated', function () {
+ return _this.onVariableChange();
+ });
+
+ // Update metrics when item selected from dropdown
+ $scope.$on('typeahead-updated', function () {
+ _this.onTargetBlur();
+ });
+
+ _this.init = function () {
+ var target = this.target;
+
+ // Migrate old targets
+ target = migrations.migrate(target);
+
+ var scopeDefaults = {
+ metric: {},
+ oldTarget: _lodash2.default.cloneDeep(this.target),
+ queryOptionsText: this.renderQueryOptionsText()
+ };
+ _lodash2.default.defaults(this, scopeDefaults);
+
+ // Load default values
+ var targetDefaults = {
+ mode: 0,
+ group: { filter: "" },
+ host: { filter: "" },
+ application: { filter: "" },
+ item: { filter: "" },
+ functions: [],
+ options: {
+ showDisabledItems: false
+ }
+ };
+ _lodash2.default.defaults(target, targetDefaults);
+
+ // Create function instances from saved JSON
+ target.functions = _lodash2.default.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" }];
+
+ 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();
+ }
+ };
+
+ _this.init();
+ return _this;
+ }
+
+ _createClass(ZabbixQueryController, [{
+ key: 'initFilters',
+ value: function initFilters() {
+ var itemtype = this.editorModes[this.target.mode].value;
+ return Promise.all([this.suggestGroups(), this.suggestHosts(), this.suggestApps(), this.suggestItems(itemtype)]);
+ }
+ }, {
+ key: 'suggestGroups',
+ value: function suggestGroups() {
+ var _this2 = this;
+
+ return this.zabbix.getAllGroups().then(function (groups) {
+ _this2.metric.groupList = groups;
+ return groups;
+ });
+ }
+ }, {
+ key: 'suggestHosts',
+ value: function suggestHosts() {
+ var _this3 = this;
+
+ var groupFilter = this.replaceTemplateVars(this.target.group.filter);
+ return this.zabbix.getAllHosts(groupFilter).then(function (hosts) {
+ _this3.metric.hostList = hosts;
+ return hosts;
+ });
+ }
+ }, {
+ key: 'suggestApps',
+ value: function suggestApps() {
+ var _this4 = this;
+
+ var groupFilter = this.replaceTemplateVars(this.target.group.filter);
+ var hostFilter = this.replaceTemplateVars(this.target.host.filter);
+ return this.zabbix.getAllApps(groupFilter, hostFilter).then(function (apps) {
+ _this4.metric.appList = apps;
+ return apps;
+ });
+ }
+ }, {
+ key: 'suggestItems',
+ value: function suggestItems() {
+ var _this5 = this;
+
+ var itemtype = arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : 'num';
+
+ var groupFilter = this.replaceTemplateVars(this.target.group.filter);
+ var hostFilter = this.replaceTemplateVars(this.target.host.filter);
+ var appFilter = this.replaceTemplateVars(this.target.application.filter);
+ var options = {
+ itemtype: itemtype,
+ showDisabledItems: this.target.options.showDisabledItems
+ };
+
+ return this.zabbix.getAllItems(groupFilter, hostFilter, appFilter, options).then(function (items) {
+ _this5.metric.itemList = items;
+ return items;
+ });
+ }
+ }, {
+ key: 'isRegex',
+ value: function isRegex(str) {
+ return utils.isRegex(str);
+ }
+ }, {
+ key: 'isVariable',
+ value: function isVariable(str) {
+ return utils.isTemplateVariable(str, this.templateSrv.variables);
+ }
+ }, {
+ key: 'onTargetBlur',
+ value: function onTargetBlur() {
+ var newTarget = _lodash2.default.cloneDeep(this.target);
+ if (!_lodash2.default.isEqual(this.oldTarget, this.target)) {
+ this.oldTarget = newTarget;
+ this.targetChanged();
+ }
+ }
+ }, {
+ key: 'onVariableChange',
+ value: function onVariableChange() {
+ if (this.isContainsVariables()) {
+ this.targetChanged();
+ }
+ }
+
+ /**
+ * Check query for template variables
+ */
+
+ }, {
+ key: 'isContainsVariables',
+ value: function isContainsVariables() {
+ var _this6 = this;
+
+ return _lodash2.default.some(['group', 'host', 'application'], function (field) {
+ if (_this6.target[field] && _this6.target[field].filter) {
+ return utils.isTemplateVariable(_this6.target[field].filter, _this6.templateSrv.variables);
+ } else {
+ return false;
+ }
+ });
+ }
+ }, {
+ key: 'parseTarget',
+ value: function parseTarget() {}
+ // Parse target
+
+
+ // Validate target and set validation info
+
+ }, {
+ key: 'validateTarget',
+ value: function validateTarget() {
+ // validate
+ }
+ }, {
+ key: 'targetChanged',
+ value: function targetChanged() {
+ this.initFilters();
+ this.parseTarget();
+ this.panelCtrl.refresh();
+ }
+ }, {
+ key: 'addFunction',
+ value: function 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();
+ }
+ }
+ }, {
+ key: 'removeFunction',
+ value: function removeFunction(func) {
+ this.target.functions = _lodash2.default.without(this.target.functions, func);
+ this.targetChanged();
+ }
+ }, {
+ key: 'moveAliasFuncLast',
+ value: function moveAliasFuncLast() {
+ var aliasFunc = _lodash2.default.find(this.target.functions, function (func) {
+ return func.def.name === 'alias' || func.def.name === 'aliasByNode' || func.def.name === 'aliasByMetric';
+ });
+
+ if (aliasFunc) {
+ this.target.functions = _lodash2.default.without(this.target.functions, aliasFunc);
+ this.target.functions.push(aliasFunc);
+ }
+ }
+ }, {
+ key: 'toggleQueryOptions',
+ value: function toggleQueryOptions() {
+ this.showQueryOptions = !this.showQueryOptions;
+ }
+ }, {
+ key: 'onQueryOptionChange',
+ value: function onQueryOptionChange() {
+ this.queryOptionsText = this.renderQueryOptionsText();
+ this.onTargetBlur();
+ }
+ }, {
+ key: 'renderQueryOptionsText',
+ value: function renderQueryOptionsText() {
+ var optionsMap = {
+ showDisabledItems: "Show disabled items"
+ };
+ var options = [];
+ _lodash2.default.forOwn(this.target.options, function (value, key) {
+ if (value) {
+ if (value === true) {
+ // Show only option name (if enabled) for boolean options
+ options.push(optionsMap[key]);
+ } else {
+ // Show "option = value" for another options
+ options.push(optionsMap[key] + " = " + value);
+ }
+ }
+ });
+ return "Options: " + options.join(', ');
+ }
+
+ /**
+ * Switch query editor to specified mode.
+ * Modes:
+ * 0 - items
+ * 1 - IT services
+ * 2 - Text metrics
+ */
+
+ }, {
+ key: 'switchEditorMode',
+ value: function switchEditorMode(mode) {
+ this.target.mode = mode;
+ this.init();
+ }
+
+ /////////////////
+ // IT Services //
+ /////////////////
+
+ /**
+ * Update list of IT services
+ */
+
+ }, {
+ key: 'updateITServiceList',
+ value: function updateITServiceList() {
+ var _this7 = this;
+
+ this.zabbix.getITService().then(function (iteservices) {
+ _this7.itserviceList = [];
+ _this7.itserviceList = _this7.itserviceList.concat(iteservices);
+ });
+ }
+
+ /**
+ * Call when IT service is selected.
+ */
+
+ }, {
+ key: 'selectITService',
+ value: function selectITService() {
+ if (!_lodash2.default.isEqual(this.oldTarget, this.target) && _lodash2.default.isEmpty(this.target.errors)) {
+ this.oldTarget = _angular2.default.copy(this.target);
+ this.panelCtrl.refresh();
+ }
+ }
+ }]);
+
+ return ZabbixQueryController;
+}(_sdk.QueryCtrl);
+
+// Set templateUrl as static property
+
+
+ZabbixQueryController.templateUrl = 'datasource-zabbix/partials/query.editor.html';
+
+// Get list of metric names for bs-typeahead directive
+function getMetricNames(scope, metricList) {
+ return _lodash2.default.uniq(_lodash2.default.map(scope.metric[metricList], 'name'));
+}
diff --git a/dist/test/datasource-zabbix/responseHandler.js b/dist/test/datasource-zabbix/responseHandler.js
new file mode 100644
index 0000000..8bad6f4
--- /dev/null
+++ b/dist/test/datasource-zabbix/responseHandler.js
@@ -0,0 +1,116 @@
+'use strict';
+
+Object.defineProperty(exports, "__esModule", {
+ value: true
+});
+
+var _lodash = require('lodash');
+
+var _lodash2 = _interopRequireDefault(_lodash);
+
+function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; }
+
+/**
+ * Convert Zabbix API history.get response to Grafana format
+ *
+ * @return {Array} Array of timeseries in Grafana format
+ * {
+ * target: "Metric name",
+ * datapoints: [[, ], ...]
+ * }
+ */
+function convertHistory(history, items, addHostName, convertPointCallback) {
+ /**
+ * Response should be in the format:
+ * data: [
+ * {
+ * target: "Metric name",
+ * datapoints: [[, ], ...]
+ * }, ...
+ * ]
+ */
+
+ // Group history by itemid
+ var grouped_history = _lodash2.default.groupBy(history, 'itemid');
+ var hosts = _lodash2.default.uniqBy(_lodash2.default.flatten(_lodash2.default.map(items, 'hosts')), 'hostid'); //uniqBy is needed to deduplicate
+
+ return _lodash2.default.map(grouped_history, function (hist, itemid) {
+ var item = _lodash2.default.find(items, { 'itemid': itemid });
+ var alias = item.name;
+ if (_lodash2.default.keys(hosts).length > 1 && addHostName) {
+ //only when actual multi hosts selected
+ var host = _lodash2.default.find(hosts, { 'hostid': item.hostid });
+ alias = host.name + ": " + alias;
+ }
+ return {
+ target: alias,
+ datapoints: _lodash2.default.map(hist, convertPointCallback)
+ };
+ });
+}
+
+function handleHistory(history, items) {
+ var addHostName = arguments.length > 2 && arguments[2] !== undefined ? arguments[2] : true;
+
+ return convertHistory(history, items, addHostName, convertHistoryPoint);
+}
+
+function handleTrends(history, items, valueType) {
+ var addHostName = arguments.length > 3 && arguments[3] !== undefined ? arguments[3] : true;
+
+ var convertPointCallback = _lodash2.default.partial(convertTrendPoint, valueType);
+ return convertHistory(history, items, addHostName, convertPointCallback);
+}
+
+function handleSLAResponse(itservice, slaProperty, slaObject) {
+ var targetSLA = slaObject[itservice.serviceid].sla[0];
+ if (slaProperty.property === 'status') {
+ var targetStatus = parseInt(slaObject[itservice.serviceid].status);
+ return {
+ target: itservice.name + ' ' + slaProperty.name,
+ datapoints: [[targetStatus, targetSLA.to * 1000]]
+ };
+ } else {
+ return {
+ target: itservice.name + ' ' + slaProperty.name,
+ datapoints: [[targetSLA[slaProperty.property], targetSLA.from * 1000], [targetSLA[slaProperty.property], targetSLA.to * 1000]]
+ };
+ }
+}
+
+function convertHistoryPoint(point) {
+ // Value must be a number for properly work
+ return [Number(point.value), point.clock * 1000];
+}
+
+function convertTrendPoint(valueType, point) {
+ var value;
+ switch (valueType) {
+ case "min":
+ value = point.value_min;
+ break;
+ case "max":
+ value = point.value_max;
+ break;
+ case "avg":
+ value = point.value_avg;
+ break;
+ default:
+ value = point.value_avg;
+ }
+
+ return [Number(value), point.clock * 1000];
+}
+
+exports.default = {
+ handleHistory: handleHistory,
+ convertHistory: convertHistory,
+ handleTrends: handleTrends,
+ handleSLAResponse: handleSLAResponse
+};
+
+// Fix for backward compatibility with lodash 2.4
+
+if (!_lodash2.default.uniqBy) {
+ _lodash2.default.uniqBy = _lodash2.default.uniq;
+}
diff --git a/dist/test/datasource-zabbix/specs/datasource_specs.js b/dist/test/datasource-zabbix/specs/datasource_specs.js
new file mode 100644
index 0000000..f34a105
--- /dev/null
+++ b/dist/test/datasource-zabbix/specs/datasource_specs.js
@@ -0,0 +1,317 @@
+"use strict";
+
+var _module = require("../module");
+
+var _datasource = require("../datasource");
+
+var _q = require("q");
+
+var _q2 = _interopRequireDefault(_q);
+
+var _sinon = require("sinon");
+
+var _sinon2 = _interopRequireDefault(_sinon);
+
+var _lodash = require("lodash");
+
+var _lodash2 = _interopRequireDefault(_lodash);
+
+function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; }
+
+describe('ZabbixDatasource', function () {
+ var ctx = {};
+ var defined = _sinon2.default.match.defined;
+
+ beforeEach(function () {
+ ctx.instanceSettings = {
+ jsonData: {
+ username: 'zabbix',
+ password: 'zabbix',
+ trends: true,
+ trendsFrom: '7d'
+ }
+ };
+ ctx.templateSrv = {};
+ ctx.alertSrv = {};
+ ctx.zabbix = function () {};
+
+ ctx.ds = new _module.Datasource(ctx.instanceSettings, ctx.templateSrv, ctx.alertSrv, ctx.zabbix);
+ });
+
+ describe('When querying data', function () {
+ beforeEach(function () {
+ ctx.ds.replaceTemplateVars = function (str) {
+ return str;
+ };
+ });
+
+ ctx.options = {
+ targets: [{
+ group: { filter: "" },
+ host: { filter: "" },
+ application: { filter: "" },
+ item: { filter: "" }
+ }],
+ range: { from: 'now-7d', to: 'now' }
+ };
+
+ it('should return an empty array when no targets are set', function (done) {
+ var options = {
+ targets: [],
+ range: { from: 'now-6h', to: 'now' }
+ };
+ ctx.ds.query(options).then(function (result) {
+ expect(result.data).to.have.length(0);
+ done();
+ });
+ });
+
+ it('should use trends if it enabled and time more than trendsFrom', function (done) {
+ var ranges = ['now-7d', 'now-168h', 'now-1M', 'now-1y'];
+
+ _lodash2.default.forEach(ranges, function (range) {
+ ctx.options.range.from = range;
+ ctx.ds.queryNumericData = _sinon2.default.spy();
+ ctx.ds.query(ctx.options);
+
+ // Check that useTrends options is true
+ expect(ctx.ds.queryNumericData).to.have.been.calledWith(defined, defined, defined, true);
+ });
+
+ done();
+ });
+
+ it('shouldnt use trends if it enabled and time less than trendsFrom', function (done) {
+ var ranges = ['now-6d', 'now-167h', 'now-1h', 'now-30m', 'now-30s'];
+
+ _lodash2.default.forEach(ranges, function (range) {
+ ctx.options.range.from = range;
+ ctx.ds.queryNumericData = _sinon2.default.spy();
+ ctx.ds.query(ctx.options);
+
+ // Check that useTrends options is false
+ expect(ctx.ds.queryNumericData).to.have.been.calledWith(defined, defined, defined, false);
+ });
+ done();
+ });
+ });
+
+ describe('When replacing template variables', function () {
+
+ function testReplacingVariable(target, varValue, expectedResult, done) {
+ ctx.ds.templateSrv.replace = function () {
+ return (0, _datasource.zabbixTemplateFormat)(varValue);
+ };
+
+ var result = ctx.ds.replaceTemplateVars(target);
+ expect(result).to.equal(expectedResult);
+ done();
+ }
+
+ /*
+ * Alphanumerics, spaces, dots, dashes and underscores
+ * are allowed in Zabbix host name.
+ * 'AaBbCc0123 .-_'
+ */
+ it('should return properly escaped regex', function (done) {
+ var target = '$host';
+ var template_var_value = 'AaBbCc0123 .-_';
+ var expected_result = '/^AaBbCc0123 \\.-_$/';
+
+ testReplacingVariable(target, template_var_value, expected_result, done);
+ });
+
+ /*
+ * Single-value variable
+ * $host = backend01
+ * $host => /^backend01|backend01$/
+ */
+ it('should return proper regex for single value', function (done) {
+ var target = '$host';
+ var template_var_value = 'backend01';
+ var expected_result = '/^backend01$/';
+
+ testReplacingVariable(target, template_var_value, expected_result, done);
+ });
+
+ /*
+ * Multi-value variable
+ * $host = [backend01, backend02]
+ * $host => /^(backend01|backend01)$/
+ */
+ it('should return proper regex for multi-value', function (done) {
+ var target = '$host';
+ var template_var_value = ['backend01', 'backend02'];
+ var expected_result = '/^(backend01|backend02)$/';
+
+ testReplacingVariable(target, template_var_value, expected_result, done);
+ });
+ });
+
+ describe('When invoking metricFindQuery()', function () {
+ beforeEach(function () {
+ ctx.ds.replaceTemplateVars = function (str) {
+ return str;
+ };
+ ctx.ds.zabbix = {
+ getGroups: function getGroups() {
+ return _q2.default.when([]);
+ },
+ getHosts: function getHosts() {
+ return _q2.default.when([]);
+ },
+ getApps: function getApps() {
+ return _q2.default.when([]);
+ },
+ getItems: function getItems() {
+ return _q2.default.when([]);
+ }
+ };
+ });
+
+ it('should return groups', function (done) {
+ var tests = [{ query: '*', expect: '/.*/' }, { query: '', expect: '' }, { query: 'Backend', expect: 'Backend' }, { query: 'Back*', expect: 'Back*' }];
+
+ var getGroups = _sinon2.default.spy(ctx.ds.zabbix, 'getGroups');
+ var _iteratorNormalCompletion = true;
+ var _didIteratorError = false;
+ var _iteratorError = undefined;
+
+ try {
+ for (var _iterator = tests[Symbol.iterator](), _step; !(_iteratorNormalCompletion = (_step = _iterator.next()).done); _iteratorNormalCompletion = true) {
+ var test = _step.value;
+
+ ctx.ds.metricFindQuery(test.query);
+ expect(getGroups).to.have.been.calledWith(test.expect);
+ getGroups.reset();
+ }
+ } catch (err) {
+ _didIteratorError = true;
+ _iteratorError = err;
+ } finally {
+ try {
+ if (!_iteratorNormalCompletion && _iterator.return) {
+ _iterator.return();
+ }
+ } finally {
+ if (_didIteratorError) {
+ throw _iteratorError;
+ }
+ }
+ }
+
+ done();
+ });
+
+ it('should return hosts', function (done) {
+ var tests = [{ query: '*.*', expect: '/.*/' }, { query: '.', expect: '' }, { query: 'Backend.*', expect: 'Backend' }, { query: 'Back*.', expect: 'Back*' }];
+
+ var getHosts = _sinon2.default.spy(ctx.ds.zabbix, 'getHosts');
+ var _iteratorNormalCompletion2 = true;
+ var _didIteratorError2 = false;
+ var _iteratorError2 = undefined;
+
+ try {
+ for (var _iterator2 = tests[Symbol.iterator](), _step2; !(_iteratorNormalCompletion2 = (_step2 = _iterator2.next()).done); _iteratorNormalCompletion2 = true) {
+ var test = _step2.value;
+
+ ctx.ds.metricFindQuery(test.query);
+ expect(getHosts).to.have.been.calledWith(test.expect);
+ getHosts.reset();
+ }
+ } catch (err) {
+ _didIteratorError2 = true;
+ _iteratorError2 = err;
+ } finally {
+ try {
+ if (!_iteratorNormalCompletion2 && _iterator2.return) {
+ _iterator2.return();
+ }
+ } finally {
+ if (_didIteratorError2) {
+ throw _iteratorError2;
+ }
+ }
+ }
+
+ done();
+ });
+
+ it('should return applications', function (done) {
+ var tests = [{ query: '*.*.*', expect: ['/.*/', '/.*/'] }, { query: '.*.', expect: ['', '/.*/'] }, { query: 'Backend.backend01.*', expect: ['Backend', 'backend01'] }, { query: 'Back*.*.', expect: ['Back*', '/.*/'] }];
+
+ var getApps = _sinon2.default.spy(ctx.ds.zabbix, 'getApps');
+ var _iteratorNormalCompletion3 = true;
+ var _didIteratorError3 = false;
+ var _iteratorError3 = undefined;
+
+ try {
+ for (var _iterator3 = tests[Symbol.iterator](), _step3; !(_iteratorNormalCompletion3 = (_step3 = _iterator3.next()).done); _iteratorNormalCompletion3 = true) {
+ var test = _step3.value;
+
+ ctx.ds.metricFindQuery(test.query);
+ expect(getApps).to.have.been.calledWith(test.expect[0], test.expect[1]);
+ getApps.reset();
+ }
+ } catch (err) {
+ _didIteratorError3 = true;
+ _iteratorError3 = err;
+ } finally {
+ try {
+ if (!_iteratorNormalCompletion3 && _iterator3.return) {
+ _iterator3.return();
+ }
+ } finally {
+ if (_didIteratorError3) {
+ throw _iteratorError3;
+ }
+ }
+ }
+
+ done();
+ });
+
+ it('should return items', function (done) {
+ var tests = [{ query: '*.*.*.*', expect: ['/.*/', '/.*/', ''] }, { query: '.*.*.*', expect: ['', '/.*/', ''] }, { query: 'Backend.backend01.*.*', expect: ['Backend', 'backend01', ''] }, { query: 'Back*.*.cpu.*', expect: ['Back*', '/.*/', 'cpu'] }];
+
+ var getItems = _sinon2.default.spy(ctx.ds.zabbix, 'getItems');
+ var _iteratorNormalCompletion4 = true;
+ var _didIteratorError4 = false;
+ var _iteratorError4 = undefined;
+
+ try {
+ for (var _iterator4 = tests[Symbol.iterator](), _step4; !(_iteratorNormalCompletion4 = (_step4 = _iterator4.next()).done); _iteratorNormalCompletion4 = true) {
+ var test = _step4.value;
+
+ ctx.ds.metricFindQuery(test.query);
+ expect(getItems).to.have.been.calledWith(test.expect[0], test.expect[1], test.expect[2]);
+ getItems.reset();
+ }
+ } catch (err) {
+ _didIteratorError4 = true;
+ _iteratorError4 = err;
+ } finally {
+ try {
+ if (!_iteratorNormalCompletion4 && _iterator4.return) {
+ _iterator4.return();
+ }
+ } finally {
+ if (_didIteratorError4) {
+ throw _iteratorError4;
+ }
+ }
+ }
+
+ done();
+ });
+
+ it('should invoke method with proper arguments', function (done) {
+ var query = '*.*';
+
+ var getHosts = _sinon2.default.spy(ctx.ds.zabbix, 'getHosts');
+ ctx.ds.metricFindQuery(query);
+ expect(getHosts).to.have.been.calledWith('/.*/');
+ done();
+ });
+ });
+});
diff --git a/dist/test/datasource-zabbix/specs/modules/datemath.js b/dist/test/datasource-zabbix/specs/modules/datemath.js
new file mode 100644
index 0000000..063ad04
--- /dev/null
+++ b/dist/test/datasource-zabbix/specs/modules/datemath.js
@@ -0,0 +1,135 @@
+'use strict';
+
+Object.defineProperty(exports, "__esModule", {
+ value: true
+});
+exports.parse = parse;
+exports.isValid = isValid;
+exports.parseDateMath = parseDateMath;
+
+var _lodash = require('lodash');
+
+var _lodash2 = _interopRequireDefault(_lodash);
+
+var _moment = require('moment');
+
+var _moment2 = _interopRequireDefault(_moment);
+
+function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; }
+
+var units = ['y', 'M', 'w', 'd', 'h', 'm', 's'];
+
+function parse(text, roundUp) {
+ if (!text) {
+ return undefined;
+ }
+ if (_moment2.default.isMoment(text)) {
+ return text;
+ }
+ if (_lodash2.default.isDate(text)) {
+ return (0, _moment2.default)(text);
+ }
+
+ var time;
+ var mathString = '';
+ var index;
+ var parseString;
+
+ if (text.substring(0, 3) === 'now') {
+ time = (0, _moment2.default)();
+ mathString = text.substring('now'.length);
+ } else {
+ index = text.indexOf('||');
+ if (index === -1) {
+ parseString = text;
+ mathString = ''; // nothing else
+ } else {
+ parseString = text.substring(0, index);
+ mathString = text.substring(index + 2);
+ }
+ // We're going to just require ISO8601 timestamps, k?
+ time = (0, _moment2.default)(parseString, _moment2.default.ISO_8601);
+ }
+
+ if (!mathString.length) {
+ return time;
+ }
+
+ return parseDateMath(mathString, time, roundUp);
+}
+
+function isValid(text) {
+ var date = parse(text);
+ if (!date) {
+ return false;
+ }
+
+ if (_moment2.default.isMoment(date)) {
+ return date.isValid();
+ }
+
+ return false;
+}
+
+function parseDateMath(mathString, time, roundUp) {
+ var dateTime = time;
+ var i = 0;
+ var len = mathString.length;
+
+ while (i < len) {
+ var c = mathString.charAt(i++);
+ var type;
+ var num;
+ var unit;
+
+ if (c === '/') {
+ type = 0;
+ } else if (c === '+') {
+ type = 1;
+ } else if (c === '-') {
+ type = 2;
+ } else {
+ return undefined;
+ }
+
+ if (isNaN(mathString.charAt(i))) {
+ num = 1;
+ } else if (mathString.length === 2) {
+ num = mathString.charAt(i);
+ } else {
+ var numFrom = i;
+ while (!isNaN(mathString.charAt(i))) {
+ i++;
+ if (i > 10) {
+ return undefined;
+ }
+ }
+ num = parseInt(mathString.substring(numFrom, i), 10);
+ }
+
+ if (type === 0) {
+ // rounding is only allowed on whole, single, units (eg M or 1M, not 0.5M or 2M)
+ if (num !== 1) {
+ return undefined;
+ }
+ }
+ unit = mathString.charAt(i++);
+
+ if (!_lodash2.default.includes(units, unit)) {
+ return undefined;
+ } else {
+ if (type === 0) {
+ if (roundUp) {
+ dateTime.endOf(unit);
+ } else {
+ dateTime.startOf(unit);
+ }
+ } else if (type === 1) {
+ dateTime.add(num, unit);
+ } else if (type === 2) {
+ dateTime.subtract(num, unit);
+ }
+ }
+ }
+ return dateTime;
+}
diff --git a/dist/test/datasource-zabbix/specs/test-main.js b/dist/test/datasource-zabbix/specs/test-main.js
new file mode 100644
index 0000000..a80d33a
--- /dev/null
+++ b/dist/test/datasource-zabbix/specs/test-main.js
@@ -0,0 +1,66 @@
+'use strict';
+
+var _prunk = require('prunk');
+
+var _prunk2 = _interopRequireDefault(_prunk);
+
+var _jsdom = require('jsdom');
+
+var _chai = require('chai');
+
+var _chai2 = _interopRequireDefault(_chai);
+
+var _sinonChai = require('sinon-chai');
+
+var _sinonChai2 = _interopRequireDefault(_sinonChai);
+
+var _datemath = require('./modules/datemath');
+
+var dateMath = _interopRequireWildcard(_datemath);
+
+function _interopRequireWildcard(obj) { if (obj && obj.__esModule) { return obj; } else { var newObj = {}; if (obj != null) { for (var key in obj) { if (Object.prototype.hasOwnProperty.call(obj, key)) newObj[key] = obj[key]; } } newObj.default = obj; return newObj; } }
+
+function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; }
+
+// Mock angular module
+
+// import sinon from 'sinon';
+var angularMocks = {
+ module: function module() {
+ return {
+ directive: function directive() {},
+ service: function service() {},
+ factory: function factory() {}
+ };
+ }
+}; // JSHint options
+/* globals global: false */
+
+var datemathMock = {
+ parse: dateMath.parse,
+ parseDateMath: dateMath.parseDateMath,
+ isValid: dateMath.isValid
+};
+
+// Mock Grafana modules that are not available outside of the core project
+// Required for loading module.js
+_prunk2.default.mock('./css/query-editor.css!', 'no css, dude.');
+_prunk2.default.mock('app/plugins/sdk', {
+ QueryCtrl: null
+});
+_prunk2.default.mock('app/core/utils/datemath', datemathMock);
+_prunk2.default.mock('angular', angularMocks);
+_prunk2.default.mock('jquery', 'module not found');
+
+// Setup jsdom
+// Required for loading angularjs
+global.document = (0, _jsdom.jsdom)('');
+global.window = global.document.parentWindow;
+global.navigator = window.navigator = {};
+global.Node = window.Node;
+
+// Setup Chai
+_chai2.default.should();
+_chai2.default.use(_sinonChai2.default);
+global.assert = _chai2.default.assert;
+global.expect = _chai2.default.expect;
diff --git a/dist/test/datasource-zabbix/utils.js b/dist/test/datasource-zabbix/utils.js
new file mode 100644
index 0000000..36eceb9
--- /dev/null
+++ b/dist/test/datasource-zabbix/utils.js
@@ -0,0 +1,151 @@
+'use strict';
+
+Object.defineProperty(exports, "__esModule", {
+ value: true
+});
+exports.regexPattern = undefined;
+exports.expandItemName = expandItemName;
+exports.isRegex = isRegex;
+exports.isTemplateVariable = isTemplateVariable;
+exports.buildRegex = buildRegex;
+exports.escapeRegex = escapeRegex;
+exports.parseInterval = parseInterval;
+exports.parseTimeShiftInterval = parseTimeShiftInterval;
+exports.formatAcknowledges = formatAcknowledges;
+exports.convertToZabbixAPIUrl = convertToZabbixAPIUrl;
+exports.callOnce = callOnce;
+
+var _lodash = require('lodash');
+
+var _lodash2 = _interopRequireDefault(_lodash);
+
+var _moment = require('moment');
+
+var _moment2 = _interopRequireDefault(_moment);
+
+function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; }
+
+/**
+ * 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"
+ */
+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 = exports.regexPattern = /^\/(.*)\/([gmi]*)$/m;
+
+function isRegex(str) {
+ return regexPattern.test(str);
+}
+
+function isTemplateVariable(str, templateVariables) {
+ var variablePattern = /^\$\w+/;
+ if (variablePattern.test(str)) {
+ var variables = _lodash2.default.map(templateVariables, function (variable) {
+ return '$' + variable.name;
+ });
+ return _lodash2.default.includes(variables, str);
+ } else {
+ return false;
+ }
+}
+
+function buildRegex(str) {
+ var matches = str.match(regexPattern);
+ var pattern = matches[1];
+ var flags = matches[2] !== "" ? matches[2] : undefined;
+ return new RegExp(pattern, flags);
+}
+
+// Need for template variables replace
+// From Grafana's templateSrv.js
+function escapeRegex(value) {
+ return value.replace(/[\\^$*+?.()|[\]{}\/]/g, '\\$&');
+}
+
+function parseInterval(interval) {
+ var intervalPattern = /(^[\d]+)(y|M|w|d|h|m|s)/g;
+ var momentInterval = intervalPattern.exec(interval);
+ return _moment2.default.duration(Number(momentInterval[1]), momentInterval[2]).valueOf();
+}
+
+function parseTimeShiftInterval(interval) {
+ var intervalPattern = /^([\+\-]*)([\d]+)(y|M|w|d|h|m|s)/g;
+ var momentInterval = intervalPattern.exec(interval);
+ var duration = 0;
+
+ if (momentInterval[1] === '+') {
+ duration = 0 - _moment2.default.duration(Number(momentInterval[2]), momentInterval[3]).valueOf();
+ } else {
+ duration = _moment2.default.duration(Number(momentInterval[2]), momentInterval[3]).valueOf();
+ }
+
+ return duration;
+}
+
+/**
+ * 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:Time ' + 'User Comments ';
+ _lodash2.default.each(_lodash2.default.map(acknowledges, function (ack) {
+ var timestamp = _moment2.default.unix(ack.clock);
+ return '' + timestamp.format("DD MMM YYYY HH:mm:ss") + ' ' + ack.alias + ' (' + ack.name + ' ' + ack.surname + ')' + ' ' + ack.message + ' ';
+ }), function (ack) {
+ formatted_acknowledges = formatted_acknowledges.concat(ack);
+ });
+ formatted_acknowledges = formatted_acknowledges.concat('
');
+ return formatted_acknowledges;
+ } else {
+ return '';
+ }
+}
+
+function convertToZabbixAPIUrl(url) {
+ var zabbixAPIUrlPattern = /.*api_jsonrpc.php$/;
+ var trimSlashPattern = /(.*?)[\/]*$/;
+ if (url.match(zabbixAPIUrlPattern)) {
+ return url;
+ } else {
+ return url.replace(trimSlashPattern, "$1");
+ }
+}
+
+/**
+ * Wrap function to prevent multiple calls
+ * when waiting for result.
+ */
+function callOnce(func, promiseKeeper) {
+ return function () {
+ if (!promiseKeeper) {
+ promiseKeeper = Promise.resolve(func.apply(this, arguments).then(function (result) {
+ promiseKeeper = null;
+ return result;
+ }));
+ }
+ return promiseKeeper;
+ };
+}
+
+// Fix for backward compatibility with lodash 2.4
+if (!_lodash2.default.includes) {
+ _lodash2.default.includes = _lodash2.default.contains;
+}
diff --git a/dist/test/datasource-zabbix/zabbix.js b/dist/test/datasource-zabbix/zabbix.js
new file mode 100644
index 0000000..627d107
--- /dev/null
+++ b/dist/test/datasource-zabbix/zabbix.js
@@ -0,0 +1,266 @@
+'use strict';
+
+var _createClass = function () { function defineProperties(target, props) { for (var i = 0; i < props.length; i++) { var descriptor = props[i]; descriptor.enumerable = descriptor.enumerable || false; descriptor.configurable = true; if ("value" in descriptor) descriptor.writable = true; Object.defineProperty(target, descriptor.key, descriptor); } } return function (Constructor, protoProps, staticProps) { if (protoProps) defineProperties(Constructor.prototype, protoProps); if (staticProps) defineProperties(Constructor, staticProps); return Constructor; }; }();
+
+var _angular = require('angular');
+
+var _angular2 = _interopRequireDefault(_angular);
+
+var _lodash = require('lodash');
+
+var _lodash2 = _interopRequireDefault(_lodash);
+
+var _utils = require('./utils');
+
+var utils = _interopRequireWildcard(_utils);
+
+require('./zabbixAPI.service.js');
+
+require('./zabbixCachingProxy.service.js');
+
+function _interopRequireWildcard(obj) { if (obj && obj.__esModule) { return obj; } else { var newObj = {}; if (obj != null) { for (var key in obj) { if (Object.prototype.hasOwnProperty.call(obj, key)) newObj[key] = obj[key]; } } newObj.default = obj; return newObj; } }
+
+function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; }
+
+function _toConsumableArray(arr) { if (Array.isArray(arr)) { for (var i = 0, arr2 = Array(arr.length); i < arr.length; i++) { arr2[i] = arr[i]; } return arr2; } else { return Array.from(arr); } }
+
+function _classCallCheck(instance, Constructor) { if (!(instance instanceof Constructor)) { throw new TypeError("Cannot call a class as a function"); } }
+
+// Use factory() instead service() for multiple data sources support.
+// Each Zabbix data source instance should initialize its own API instance.
+
+/** @ngInject */
+function ZabbixFactory(zabbixAPIService, ZabbixCachingProxy) {
+ var Zabbix = function () {
+ function Zabbix(url, username, password, basicAuth, withCredentials, cacheTTL) {
+ _classCallCheck(this, Zabbix);
+
+ // Initialize Zabbix API
+ var ZabbixAPI = zabbixAPIService;
+ this.zabbixAPI = new ZabbixAPI(url, username, password, basicAuth, withCredentials);
+
+ // Initialize caching proxy for requests
+ var cacheOptions = {
+ enabled: true,
+ ttl: cacheTTL
+ };
+ this.cachingProxy = new ZabbixCachingProxy(this.zabbixAPI, cacheOptions);
+
+ // Proxy methods
+ this.getHistory = this.cachingProxy.getHistory.bind(this.cachingProxy);
+
+ 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.getITService = this.zabbixAPI.getITService.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);
+ }
+
+ _createClass(Zabbix, [{
+ key: 'getItemsFromTarget',
+ value: function getItemsFromTarget(target, options) {
+ var parts = ['group', 'host', 'application', 'item'];
+ var filters = _lodash2.default.map(parts, function (p) {
+ return target[p].filter;
+ });
+ return this.getItems.apply(this, _toConsumableArray(filters).concat([options]));
+ }
+ }, {
+ key: 'getAllGroups',
+ value: function getAllGroups() {
+ return this.cachingProxy.getGroups();
+ }
+ }, {
+ key: 'getGroups',
+ value: function getGroups(groupFilter) {
+ return this.getAllGroups().then(function (groups) {
+ return findByFilter(groups, groupFilter);
+ });
+ }
+
+ /**
+ * Get list of host belonging to given groups.
+ */
+
+ }, {
+ key: 'getAllHosts',
+ value: function getAllHosts(groupFilter) {
+ var _this = this;
+
+ return this.getGroups(groupFilter).then(function (groups) {
+ var groupids = _lodash2.default.map(groups, 'groupid');
+ return _this.cachingProxy.getHosts(groupids);
+ });
+ }
+ }, {
+ key: 'getHosts',
+ value: function getHosts(groupFilter, hostFilter) {
+ return this.getAllHosts(groupFilter).then(function (hosts) {
+ return findByFilter(hosts, hostFilter);
+ });
+ }
+
+ /**
+ * Get list of applications belonging to given groups and hosts.
+ */
+
+ }, {
+ key: 'getAllApps',
+ value: function getAllApps(groupFilter, hostFilter) {
+ var _this2 = this;
+
+ return this.getHosts(groupFilter, hostFilter).then(function (hosts) {
+ var hostids = _lodash2.default.map(hosts, 'hostid');
+ return _this2.cachingProxy.getApps(hostids);
+ });
+ }
+ }, {
+ key: 'getApps',
+ value: function getApps(groupFilter, hostFilter, appFilter) {
+ var _this3 = this;
+
+ return this.getHosts(groupFilter, hostFilter).then(function (hosts) {
+ var hostids = _lodash2.default.map(hosts, 'hostid');
+ if (appFilter) {
+ return _this3.cachingProxy.getApps(hostids).then(function (apps) {
+ return filterByQuery(apps, appFilter);
+ });
+ } else {
+ return {
+ appFilterEmpty: true,
+ hostids: hostids
+ };
+ }
+ });
+ }
+ }, {
+ key: 'getAllItems',
+ value: function getAllItems(groupFilter, hostFilter, appFilter) {
+ var _this4 = this;
+
+ var options = arguments.length > 3 && arguments[3] !== undefined ? arguments[3] : {};
+
+ return this.getApps(groupFilter, hostFilter, appFilter).then(function (apps) {
+ if (apps.appFilterEmpty) {
+ return _this4.cachingProxy.getItems(apps.hostids, undefined, options.itemtype);
+ } else {
+ var appids = _lodash2.default.map(apps, 'applicationid');
+ return _this4.cachingProxy.getItems(undefined, appids, options.itemtype);
+ }
+ }).then(function (items) {
+ if (!options.showDisabledItems) {
+ items = _lodash2.default.filter(items, { 'status': '0' });
+ }
+ return items;
+ });
+ }
+ }, {
+ key: 'getItems',
+ value: function getItems(groupFilter, hostFilter, appFilter, itemFilter) {
+ var options = arguments.length > 4 && arguments[4] !== undefined ? arguments[4] : {};
+
+ return this.getAllItems(groupFilter, hostFilter, appFilter, options).then(function (items) {
+ return filterByQuery(items, itemFilter);
+ });
+ }
+
+ /**
+ * Build query - convert target filters to array of Zabbix items
+ */
+
+ }, {
+ key: 'getTriggers',
+ value: function getTriggers(groupFilter, hostFilter, appFilter, showTriggers) {
+ var _this5 = this;
+
+ var promises = [this.getGroups(groupFilter), this.getHosts(groupFilter, hostFilter), this.getApps(groupFilter, hostFilter, appFilter)];
+
+ return Promise.all(promises).then(function (results) {
+ var filteredGroups = results[0];
+ var filteredHosts = results[1];
+ var filteredApps = results[2];
+ var query = {};
+
+ if (appFilter) {
+ query.applicationids = _lodash2.default.flatten(_lodash2.default.map(filteredApps, 'applicationid'));
+ }
+ if (hostFilter) {
+ query.hostids = _lodash2.default.map(filteredHosts, 'hostid');
+ }
+ if (groupFilter) {
+ query.groupids = _lodash2.default.map(filteredGroups, 'groupid');
+ }
+
+ return query;
+ }).then(function (query) {
+ return _this5.zabbixAPI.getTriggers(query.groupids, query.hostids, query.applicationids, showTriggers);
+ });
+ }
+ }]);
+
+ return Zabbix;
+ }();
+
+ return Zabbix;
+}
+
+_angular2.default.module('grafana.services').factory('Zabbix', ZabbixFactory);
+
+///////////////////////////////////////////////////////////////////////////////
+
+/**
+ * Find group, host, app or item by given name.
+ * @param list list of groups, apps or other
+ * @param name visible name
+ * @return array with finded element or undefined
+ */
+function findByName(list, name) {
+ var finded = _lodash2.default.find(list, { 'name': name });
+ if (finded) {
+ return [finded];
+ } else {
+ return undefined;
+ }
+}
+
+/**
+ * Different hosts can contains applications and items with same name.
+ * For this reason use _.filter, which return all elements instead _.find,
+ * which return only first finded.
+ * @param {[type]} list list of elements
+ * @param {[type]} name app name
+ * @return {[type]} array with finded element or undefined
+ */
+function filterByName(list, name) {
+ var finded = _lodash2.default.filter(list, { 'name': name });
+ if (finded) {
+ return finded;
+ } else {
+ return undefined;
+ }
+}
+
+function filterByRegex(list, regex) {
+ var filterPattern = utils.buildRegex(regex);
+ return _lodash2.default.filter(list, function (zbx_obj) {
+ return filterPattern.test(zbx_obj.name);
+ });
+}
+
+function findByFilter(list, filter) {
+ if (utils.isRegex(filter)) {
+ return filterByRegex(list, filter);
+ } else {
+ return findByName(list, filter);
+ }
+}
+
+function filterByQuery(list, filter) {
+ if (utils.isRegex(filter)) {
+ return filterByRegex(list, filter);
+ } else {
+ return filterByName(list, filter);
+ }
+}
diff --git a/dist/test/datasource-zabbix/zabbixAPI.service.js b/dist/test/datasource-zabbix/zabbixAPI.service.js
new file mode 100644
index 0000000..cd82a13
--- /dev/null
+++ b/dist/test/datasource-zabbix/zabbixAPI.service.js
@@ -0,0 +1,436 @@
+'use strict';
+
+var _createClass = function () { function defineProperties(target, props) { for (var i = 0; i < props.length; i++) { var descriptor = props[i]; descriptor.enumerable = descriptor.enumerable || false; descriptor.configurable = true; if ("value" in descriptor) descriptor.writable = true; Object.defineProperty(target, descriptor.key, descriptor); } } return function (Constructor, protoProps, staticProps) { if (protoProps) defineProperties(Constructor.prototype, protoProps); if (staticProps) defineProperties(Constructor, staticProps); return Constructor; }; }();
+
+var _angular = require('angular');
+
+var _angular2 = _interopRequireDefault(_angular);
+
+var _lodash = require('lodash');
+
+var _lodash2 = _interopRequireDefault(_lodash);
+
+var _utils = require('./utils');
+
+var utils = _interopRequireWildcard(_utils);
+
+require('./zabbixAPICore.service');
+
+function _interopRequireWildcard(obj) { if (obj && obj.__esModule) { return obj; } else { var newObj = {}; if (obj != null) { for (var key in obj) { if (Object.prototype.hasOwnProperty.call(obj, key)) newObj[key] = obj[key]; } } newObj.default = obj; return newObj; } }
+
+function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; }
+
+function _classCallCheck(instance, Constructor) { if (!(instance instanceof Constructor)) { throw new TypeError("Cannot call a class as a function"); } }
+
+/** @ngInject */
+function ZabbixAPIServiceFactory(alertSrv, zabbixAPICoreService) {
+
+ /**
+ * Zabbix API Wrapper.
+ * Creates Zabbix API instance with given parameters (url, credentials and other).
+ * Wraps API calls and provides high-level methods.
+ */
+ var ZabbixAPI = function () {
+ function ZabbixAPI(api_url, username, password, basicAuth, withCredentials) {
+ _classCallCheck(this, ZabbixAPI);
+
+ this.url = api_url;
+ this.username = username;
+ this.password = password;
+ this.auth = "";
+
+ this.requestOptions = {
+ basicAuth: basicAuth,
+ withCredentials: withCredentials
+ };
+
+ this.loginPromise = null;
+ this.loginErrorCount = 0;
+ this.maxLoginAttempts = 3;
+
+ this.alertSrv = alertSrv;
+ this.zabbixAPICore = zabbixAPICoreService;
+
+ this.getTrend = this.getTrend_ZBXNEXT1193;
+ //getTrend = getTrend_30;
+ }
+
+ //////////////////////////
+ // Core method wrappers //
+ //////////////////////////
+
+ _createClass(ZabbixAPI, [{
+ key: 'request',
+ value: function request(method, params) {
+ var _this = this;
+
+ return this.zabbixAPICore.request(this.url, method, params, this.requestOptions, this.auth).catch(function (error) {
+ if (isNotAuthorized(error.data)) {
+ // Handle auth errors
+ _this.loginErrorCount++;
+ if (_this.loginErrorCount > _this.maxLoginAttempts) {
+ _this.loginErrorCount = 0;
+ return null;
+ } else {
+ return _this.loginOnce().then(function () {
+ return _this.request(method, params);
+ });
+ }
+ } else {
+ // Handle API errors
+ var message = error.data ? error.data : error.statusText;
+ _this.alertAPIError(message);
+ }
+ });
+ }
+ }, {
+ key: 'alertAPIError',
+ value: function alertAPIError(message) {
+ var timeout = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : 5000;
+
+ this.alertSrv.set("Zabbix API Error", message, 'error', timeout);
+ }
+
+ /**
+ * When API unauthenticated or auth token expired each request produce login()
+ * call. But auth token is common to all requests. This function wraps login() method
+ * and call it once. If login() already called just wait for it (return its promise).
+ * @return login promise
+ */
+
+ }, {
+ key: 'loginOnce',
+ value: function loginOnce() {
+ var _this2 = this;
+
+ if (!this.loginPromise) {
+ this.loginPromise = Promise.resolve(this.login().then(function (auth) {
+ _this2.auth = auth;
+ _this2.loginPromise = null;
+ return auth;
+ }));
+ }
+ return this.loginPromise;
+ }
+
+ /**
+ * Get authentication token.
+ */
+
+ }, {
+ key: 'login',
+ value: function login() {
+ return this.zabbixAPICore.login(this.url, this.username, this.password, this.requestOptions);
+ }
+
+ /**
+ * Get Zabbix API version
+ */
+
+ }, {
+ key: 'getVersion',
+ value: function getVersion() {
+ return this.zabbixAPICore.getVersion(this.url, this.requestOptions);
+ }
+
+ ////////////////////////////////
+ // Zabbix API method wrappers //
+ ////////////////////////////////
+
+ }, {
+ key: 'acknowledgeEvent',
+ value: function acknowledgeEvent(eventid, message) {
+ var params = {
+ eventids: eventid,
+ message: message
+ };
+
+ return this.request('event.acknowledge', params);
+ }
+ }, {
+ key: 'getGroups',
+ value: function getGroups() {
+ var params = {
+ output: ['name'],
+ sortfield: 'name',
+ real_hosts: true
+ };
+
+ return this.request('hostgroup.get', params);
+ }
+ }, {
+ key: 'getHosts',
+ value: function getHosts(groupids) {
+ var params = {
+ output: ['name', 'host'],
+ sortfield: 'name'
+ };
+ if (groupids) {
+ params.groupids = groupids;
+ }
+
+ return this.request('host.get', params);
+ }
+ }, {
+ key: 'getApps',
+ value: function getApps(hostids) {
+ var params = {
+ output: ['applicationid', 'name'],
+ hostids: hostids
+ };
+
+ return this.request('application.get', params);
+ }
+
+ /**
+ * Get Zabbix items
+ * @param {[type]} hostids host ids
+ * @param {[type]} appids application ids
+ * @param {String} itemtype 'num' or 'text'
+ * @return {[type]} array of items
+ */
+
+ }, {
+ key: 'getItems',
+ value: function getItems(hostids, appids, itemtype) {
+ var params = {
+ output: ['name', 'key_', 'value_type', 'hostid', 'status', 'state'],
+ sortfield: 'name',
+ webitems: true,
+ filter: {},
+ selectHosts: ['hostid', 'name']
+ };
+ if (hostids) {
+ params.hostids = hostids;
+ }
+ if (appids) {
+ params.applicationids = appids;
+ }
+ if (itemtype === 'num') {
+ // Return only numeric metrics
+ params.filter.value_type = [0, 3];
+ }
+ if (itemtype === 'text') {
+ // Return only text metrics
+ params.filter.value_type = [1, 2, 4];
+ }
+
+ return this.request('item.get', params).then(expandItems);
+
+ function expandItems(items) {
+ items.forEach(function (item) {
+ item.item = item.name;
+ item.name = utils.expandItemName(item.item, item.key_);
+ return item;
+ });
+ return items;
+ }
+ }
+ }, {
+ key: 'getLastValue',
+ value: function getLastValue(itemid) {
+ var params = {
+ output: ['lastvalue'],
+ itemids: itemid
+ };
+ return this.request('item.get', params).then(function (items) {
+ return items.length ? items[0].lastvalue : null;
+ });
+ }
+
+ /**
+ * Perform history query from Zabbix API
+ *
+ * @param {Array} items Array of Zabbix item objects
+ * @param {Number} timeFrom Time in seconds
+ * @param {Number} timeTill Time in seconds
+ * @return {Array} Array of Zabbix history objects
+ */
+
+ }, {
+ key: 'getHistory',
+ value: function getHistory(items, timeFrom, timeTill) {
+ var _this3 = this;
+
+ // Group items by value type and perform request for each value type
+ var grouped_items = _lodash2.default.groupBy(items, 'value_type');
+ var promises = _lodash2.default.map(grouped_items, function (items, value_type) {
+ var itemids = _lodash2.default.map(items, 'itemid');
+ var params = {
+ output: 'extend',
+ history: value_type,
+ itemids: itemids,
+ sortfield: 'clock',
+ sortorder: 'ASC',
+ time_from: timeFrom
+ };
+
+ // Relative queries (e.g. last hour) don't include an end time
+ if (timeTill) {
+ params.time_till = timeTill;
+ }
+
+ return _this3.request('history.get', params);
+ });
+
+ return Promise.all(promises).then(_lodash2.default.flatten);
+ }
+
+ /**
+ * Perform trends query from Zabbix API
+ * Use trends api extension from ZBXNEXT-1193 patch.
+ *
+ * @param {Array} items Array of Zabbix item objects
+ * @param {Number} time_from Time in seconds
+ * @param {Number} time_till Time in seconds
+ * @return {Array} Array of Zabbix trend objects
+ */
+
+ }, {
+ key: 'getTrend_ZBXNEXT1193',
+ value: function getTrend_ZBXNEXT1193(items, timeFrom, timeTill) {
+ var _this4 = this;
+
+ // Group items by value type and perform request for each value type
+ var grouped_items = _lodash2.default.groupBy(items, 'value_type');
+ var promises = _lodash2.default.map(grouped_items, function (items, value_type) {
+ var itemids = _lodash2.default.map(items, 'itemid');
+ var params = {
+ output: 'extend',
+ trend: value_type,
+ itemids: itemids,
+ sortfield: 'clock',
+ sortorder: 'ASC',
+ time_from: timeFrom
+ };
+
+ // Relative queries (e.g. last hour) don't include an end time
+ if (timeTill) {
+ params.time_till = timeTill;
+ }
+
+ return _this4.request('trend.get', params);
+ });
+
+ return Promise.all(promises).then(_lodash2.default.flatten);
+ }
+ }, {
+ key: 'getTrend_30',
+ value: function getTrend_30(items, time_from, time_till, value_type) {
+ var self = this;
+ var itemids = _lodash2.default.map(items, 'itemid');
+
+ var params = {
+ output: ["itemid", "clock", value_type],
+ itemids: itemids,
+ time_from: time_from
+ };
+
+ // Relative queries (e.g. last hour) don't include an end time
+ if (time_till) {
+ params.time_till = time_till;
+ }
+
+ return self.request('trend.get', params);
+ }
+ }, {
+ key: 'getITService',
+ value: function getITService(serviceids) {
+ var params = {
+ output: 'extend',
+ serviceids: serviceids
+ };
+ return this.request('service.get', params);
+ }
+ }, {
+ key: 'getSLA',
+ value: function getSLA(serviceids, timeFrom, timeTo) {
+ var params = {
+ serviceids: serviceids,
+ intervals: [{
+ from: timeFrom,
+ to: timeTo
+ }]
+ };
+ return this.request('service.getsla', params);
+ }
+ }, {
+ key: 'getTriggers',
+ value: function getTriggers(groupids, hostids, applicationids, showTriggers, timeFrom, timeTo) {
+ var params = {
+ output: 'extend',
+ groupids: groupids,
+ hostids: hostids,
+ applicationids: applicationids,
+ expandDescription: true,
+ expandData: true,
+ expandComment: true,
+ monitored: true,
+ skipDependent: true,
+ //only_true: true,
+ filter: {
+ value: 1
+ },
+ selectGroups: ['name'],
+ selectHosts: ['name', 'host'],
+ selectItems: ['name', 'key_', 'lastvalue'],
+ selectLastEvent: 'extend'
+ };
+
+ if (showTriggers) {
+ params.filter.value = showTriggers;
+ }
+
+ if (timeFrom || timeTo) {
+ params.lastChangeSince = timeFrom;
+ params.lastChangeTill = timeTo;
+ }
+
+ return this.request('trigger.get', params);
+ }
+ }, {
+ key: 'getEvents',
+ value: function getEvents(objectids, timeFrom, timeTo, showEvents) {
+ var params = {
+ output: 'extend',
+ time_from: timeFrom,
+ time_till: timeTo,
+ objectids: objectids,
+ select_acknowledges: 'extend',
+ selectHosts: 'extend',
+ value: showEvents
+ };
+
+ return this.request('event.get', params);
+ }
+ }, {
+ key: 'getAcknowledges',
+ value: function getAcknowledges(eventids) {
+ var params = {
+ output: 'extend',
+ eventids: eventids,
+ preservekeys: true,
+ select_acknowledges: 'extend',
+ sortfield: 'clock',
+ sortorder: 'DESC'
+ };
+
+ return this.request('event.get', params).then(function (events) {
+ return _lodash2.default.filter(events, function (event) {
+ return event.acknowledges.length;
+ });
+ });
+ }
+ }]);
+
+ return ZabbixAPI;
+ }();
+
+ return ZabbixAPI;
+}
+
+function isNotAuthorized(message) {
+ return message === "Session terminated, re-login, please." || message === "Not authorised." || message === "Not authorized.";
+}
+
+_angular2.default.module('grafana.services').factory('zabbixAPIService', ZabbixAPIServiceFactory);
diff --git a/dist/test/datasource-zabbix/zabbixAPICore.service.js b/dist/test/datasource-zabbix/zabbixAPICore.service.js
new file mode 100644
index 0000000..075f149
--- /dev/null
+++ b/dist/test/datasource-zabbix/zabbixAPICore.service.js
@@ -0,0 +1,142 @@
+'use strict';
+
+Object.defineProperty(exports, "__esModule", {
+ value: true
+});
+exports.ZabbixAPIError = undefined;
+
+var _createClass = function () { function defineProperties(target, props) { for (var i = 0; i < props.length; i++) { var descriptor = props[i]; descriptor.enumerable = descriptor.enumerable || false; descriptor.configurable = true; if ("value" in descriptor) descriptor.writable = true; Object.defineProperty(target, descriptor.key, descriptor); } } return function (Constructor, protoProps, staticProps) { if (protoProps) defineProperties(Constructor.prototype, protoProps); if (staticProps) defineProperties(Constructor, staticProps); return Constructor; }; }(); /**
+ * General Zabbix API methods
+ */
+
+var _angular = require('angular');
+
+var _angular2 = _interopRequireDefault(_angular);
+
+function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; }
+
+function _classCallCheck(instance, Constructor) { if (!(instance instanceof Constructor)) { throw new TypeError("Cannot call a class as a function"); } }
+
+var ZabbixAPICoreService = function () {
+
+ /** @ngInject */
+ function ZabbixAPICoreService(backendSrv) {
+ _classCallCheck(this, ZabbixAPICoreService);
+
+ this.backendSrv = backendSrv;
+ }
+
+ /**
+ * Request data from Zabbix API
+ * @return {object} response.result
+ */
+
+
+ _createClass(ZabbixAPICoreService, [{
+ key: 'request',
+ value: function request(api_url, method, params, options, auth) {
+ var requestData = {
+ jsonrpc: '2.0',
+ method: method,
+ params: params,
+ id: 1
+ };
+
+ if (auth === "") {
+ // Reject immediately if not authenticated
+ return Promise.reject(new ZabbixAPIError({ data: "Not authorised." }));
+ } else if (auth) {
+ // Set auth parameter only if it needed
+ requestData.auth = auth;
+ }
+
+ var requestOptions = {
+ method: 'POST',
+ url: api_url,
+ data: requestData,
+ headers: {
+ 'Content-Type': 'application/json'
+ }
+ };
+
+ // Set request options for basic auth
+ if (options.basicAuth || options.withCredentials) {
+ requestOptions.withCredentials = true;
+ }
+ if (options.basicAuth) {
+ requestOptions.headers.Authorization = options.basicAuth;
+ }
+
+ return this.datasourceRequest(requestOptions);
+ }
+ }, {
+ key: 'datasourceRequest',
+ value: function datasourceRequest(requestOptions) {
+ return this.backendSrv.datasourceRequest(requestOptions).then(function (response) {
+ if (!response.data) {
+ return Promise.reject(new ZabbixAPIError({ data: "General Error, no data" }));
+ } else if (response.data.error) {
+
+ // Handle Zabbix API errors
+ return Promise.reject(new ZabbixAPIError(response.data.error));
+ }
+
+ // Success
+ return response.data.result;
+ });
+ }
+
+ /**
+ * Get authentication token.
+ * @return {string} auth token
+ */
+
+ }, {
+ key: 'login',
+ value: function login(api_url, username, password, options) {
+ var params = {
+ user: username,
+ password: password
+ };
+ return this.request(api_url, 'user.login', params, options, null);
+ }
+
+ /**
+ * Get Zabbix API version
+ * Matches the version of Zabbix starting from Zabbix 2.0.4
+ */
+
+ }, {
+ key: 'getVersion',
+ value: function getVersion(api_url, options) {
+ return this.request(api_url, 'apiinfo.version', [], options);
+ }
+ }]);
+
+ return ZabbixAPICoreService;
+}();
+
+// Define zabbix API exception type
+
+
+var ZabbixAPIError = exports.ZabbixAPIError = function () {
+ function ZabbixAPIError(error) {
+ _classCallCheck(this, ZabbixAPIError);
+
+ this.code = error.code;
+ this.name = error.data;
+ this.message = error.data;
+ this.data = error.data;
+ }
+
+ _createClass(ZabbixAPIError, [{
+ key: 'toString',
+ value: function toString() {
+ return this.name + ": " + this.message;
+ }
+ }]);
+
+ return ZabbixAPIError;
+}();
+
+_angular2.default.module('grafana.services').service('zabbixAPICoreService', ZabbixAPICoreService);
diff --git a/dist/test/datasource-zabbix/zabbixCachingProxy.service.js b/dist/test/datasource-zabbix/zabbixCachingProxy.service.js
new file mode 100644
index 0000000..a378045
--- /dev/null
+++ b/dist/test/datasource-zabbix/zabbixCachingProxy.service.js
@@ -0,0 +1,215 @@
+'use strict';
+
+var _createClass = function () { function defineProperties(target, props) { for (var i = 0; i < props.length; i++) { var descriptor = props[i]; descriptor.enumerable = descriptor.enumerable || false; descriptor.configurable = true; if ("value" in descriptor) descriptor.writable = true; Object.defineProperty(target, descriptor.key, descriptor); } } return function (Constructor, protoProps, staticProps) { if (protoProps) defineProperties(Constructor.prototype, protoProps); if (staticProps) defineProperties(Constructor, staticProps); return Constructor; }; }();
+
+var _angular = require('angular');
+
+var _angular2 = _interopRequireDefault(_angular);
+
+var _lodash = require('lodash');
+
+var _lodash2 = _interopRequireDefault(_lodash);
+
+function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; }
+
+function _toConsumableArray(arr) { if (Array.isArray(arr)) { for (var i = 0, arr2 = Array(arr.length); i < arr.length; i++) { arr2[i] = arr[i]; } return arr2; } else { return Array.from(arr); } }
+
+function _classCallCheck(instance, Constructor) { if (!(instance instanceof Constructor)) { throw new TypeError("Cannot call a class as a function"); } }
+
+// Use factory() instead service() for multiple datasources support.
+// Each datasource instance must initialize its own cache.
+
+/** @ngInject */
+function ZabbixCachingProxyFactory() {
+ var ZabbixCachingProxy = function () {
+ function ZabbixCachingProxy(zabbixAPI, cacheOptions) {
+ _classCallCheck(this, ZabbixCachingProxy);
+
+ this.zabbixAPI = zabbixAPI;
+ this.cacheEnabled = cacheOptions.enabled;
+ this.ttl = cacheOptions.ttl || 600000; // 10 minutes by default
+
+ // Internal objects for data storing
+ this.cache = {
+ groups: {},
+ hosts: {},
+ applications: {},
+ items: {},
+ history: {},
+ trends: {}
+ };
+
+ this.historyPromises = {};
+
+ // Don't run duplicated history requests
+ this.getHistory = callAPIRequestOnce(_lodash2.default.bind(this.zabbixAPI.getHistory, this.zabbixAPI), this.historyPromises, getHistoryRequestHash);
+
+ // Don't run duplicated requests
+ this.groupPromises = {};
+ this.getGroupsOnce = callAPIRequestOnce(_lodash2.default.bind(this.zabbixAPI.getGroups, this.zabbixAPI), this.groupPromises, getRequestHash);
+
+ this.hostPromises = {};
+ this.getHostsOnce = callAPIRequestOnce(_lodash2.default.bind(this.zabbixAPI.getHosts, this.zabbixAPI), this.hostPromises, getRequestHash);
+
+ this.appPromises = {};
+ this.getAppsOnce = callAPIRequestOnce(_lodash2.default.bind(this.zabbixAPI.getApps, this.zabbixAPI), this.appPromises, getRequestHash);
+
+ this.itemPromises = {};
+ this.getItemsOnce = callAPIRequestOnce(_lodash2.default.bind(this.zabbixAPI.getItems, this.zabbixAPI), this.itemPromises, getRequestHash);
+ }
+
+ _createClass(ZabbixCachingProxy, [{
+ key: 'isExpired',
+ value: function isExpired(cacheObject) {
+ if (cacheObject) {
+ var object_age = Date.now() - cacheObject.timestamp;
+ return !(cacheObject.timestamp && object_age < this.ttl);
+ } else {
+ return true;
+ }
+ }
+
+ /**
+ * Check that result is present in cache and up to date
+ * or send request to API.
+ */
+
+ }, {
+ key: 'proxyRequest',
+ value: function proxyRequest(request, params, cacheObject) {
+ var hash = getRequestHash(params);
+ if (this.cacheEnabled && !this.isExpired(cacheObject[hash])) {
+ return Promise.resolve(cacheObject[hash].value);
+ } else {
+ return request.apply(undefined, _toConsumableArray(params)).then(function (result) {
+ cacheObject[hash] = {
+ value: result,
+ timestamp: Date.now()
+ };
+ return result;
+ });
+ }
+ }
+ }, {
+ key: 'getGroups',
+ value: function getGroups() {
+ return this.proxyRequest(this.getGroupsOnce, [], this.cache.groups);
+ }
+ }, {
+ key: 'getHosts',
+ value: function getHosts(groupids) {
+ return this.proxyRequest(this.getHostsOnce, [groupids], this.cache.hosts);
+ }
+ }, {
+ key: 'getApps',
+ value: function getApps(hostids) {
+ return this.proxyRequest(this.getAppsOnce, [hostids], this.cache.applications);
+ }
+ }, {
+ key: 'getItems',
+ value: function getItems(hostids, appids, itemtype) {
+ var params = [hostids, appids, itemtype];
+ return this.proxyRequest(this.getItemsOnce, params, this.cache.items);
+ }
+ }, {
+ key: 'getHistoryFromCache',
+ value: function getHistoryFromCache(items, time_from, time_till) {
+ var historyStorage = this.cache.history;
+ var full_history;
+ var expired = _lodash2.default.filter(_lodash2.default.keyBy(items, 'itemid'), function (item, itemid) {
+ return !historyStorage[itemid];
+ });
+ if (expired.length) {
+ return this.zabbixAPI.getHistory(expired, time_from, time_till).then(function (history) {
+ var grouped_history = _lodash2.default.groupBy(history, 'itemid');
+ _lodash2.default.forEach(expired, function (item) {
+ var itemid = item.itemid;
+ historyStorage[itemid] = item;
+ historyStorage[itemid].time_from = time_from;
+ historyStorage[itemid].time_till = time_till;
+ historyStorage[itemid].history = grouped_history[itemid];
+ });
+ full_history = _lodash2.default.map(items, function (item) {
+ return historyStorage[item.itemid].history;
+ });
+ return _lodash2.default.flatten(full_history, true);
+ });
+ } else {
+ full_history = _lodash2.default.map(items, function (item) {
+ return historyStorage[item.itemid].history;
+ });
+ return Promise.resolve(_lodash2.default.flatten(full_history, true));
+ }
+ }
+ }, {
+ key: 'getHistoryFromAPI',
+ value: function getHistoryFromAPI(items, time_from, time_till) {
+ return this.zabbixAPI.getHistory(items, time_from, time_till);
+ }
+ }]);
+
+ return ZabbixCachingProxy;
+ }();
+
+ return ZabbixCachingProxy;
+}
+
+_angular2.default.module('grafana.services').factory('ZabbixCachingProxy', ZabbixCachingProxyFactory);
+
+/**
+ * Wrap zabbix API request to prevent multiple calls
+ * with same params when waiting for result.
+ */
+function callAPIRequestOnce(func, promiseKeeper, argsHashFunc) {
+ return function () {
+ var hash = argsHashFunc(arguments);
+ if (!promiseKeeper[hash]) {
+ promiseKeeper[hash] = Promise.resolve(func.apply(this, arguments).then(function (result) {
+ promiseKeeper[hash] = null;
+ return result;
+ }));
+ }
+ return promiseKeeper[hash];
+ };
+}
+
+function getRequestHash(args) {
+ var requestStamp = _lodash2.default.map(args, function (arg) {
+ if (arg === undefined) {
+ return 'undefined';
+ } else {
+ if (_lodash2.default.isArray(arg)) {
+ return arg.sort().toString();
+ } else {
+ return arg.toString();
+ }
+ }
+ }).join();
+ return requestStamp.getHash();
+}
+
+function getHistoryRequestHash(args) {
+ var itemids = _lodash2.default.map(args[0], 'itemid');
+ var stamp = itemids.join() + args[1] + args[2];
+ return stamp.getHash();
+}
+
+String.prototype.getHash = function () {
+ var hash = 0,
+ i,
+ chr,
+ len;
+ if (this.length !== 0) {
+ for (i = 0, len = this.length; i < len; i++) {
+ chr = this.charCodeAt(i);
+ hash = (hash << 5) - hash + chr;
+ hash |= 0; // Convert to 32bit integer
+ }
+ }
+ return hash;
+};
+
+// Fix for backward compatibility with lodash 2.4
+if (!_lodash2.default.keyBy) {
+ _lodash2.default.keyBy = _lodash2.default.indexBy;
+}
diff --git a/dist/test/module.js b/dist/test/module.js
new file mode 100644
index 0000000..ac26ed7
--- /dev/null
+++ b/dist/test/module.js
@@ -0,0 +1,10 @@
+'use strict';
+
+Object.defineProperty(exports, "__esModule", {
+ value: true
+});
+exports.ConfigCtrl = undefined;
+
+var _config = require('./components/config');
+
+exports.ConfigCtrl = _config.ZabbixAppConfigCtrl;
diff --git a/dist/test/panel-triggers/ack-tooltip.directive.js b/dist/test/panel-triggers/ack-tooltip.directive.js
new file mode 100644
index 0000000..e74683e
--- /dev/null
+++ b/dist/test/panel-triggers/ack-tooltip.directive.js
@@ -0,0 +1,122 @@
+'use strict';
+
+var _angular = require('angular');
+
+var _angular2 = _interopRequireDefault(_angular);
+
+var _jquery = require('jquery');
+
+var _jquery2 = _interopRequireDefault(_jquery);
+
+var _tetherDrop = require('tether-drop');
+
+var _tetherDrop2 = _interopRequireDefault(_tetherDrop);
+
+function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; }
+
+/** @ngInject */
+_angular2.default.module('grafana.directives').directive('ackTooltip', function ($sanitize, $compile) {
+ var buttonTemplate = ' ';
+
+ return {
+ scope: {
+ ack: "=",
+ trigger: "=",
+ onAck: "=",
+ context: "="
+ },
+ link: function link(scope, element) {
+ var acknowledges = scope.ack;
+ var $button = (0, _jquery2.default)(buttonTemplate);
+ $button.appendTo(element);
+
+ $button.click(function () {
+ var tooltip = '';
+
+ if (acknowledges && acknowledges.length) {
+ tooltip += '
' + 'Time ' + 'User ' + '' + ' ';
+ var _iteratorNormalCompletion = true;
+ var _didIteratorError = false;
+ var _iteratorError = undefined;
+
+ try {
+ for (var _iterator = acknowledges[Symbol.iterator](), _step; !(_iteratorNormalCompletion = (_step = _iterator.next()).done); _iteratorNormalCompletion = true) {
+ var ack = _step.value;
+
+ tooltip += '' + ack.time + ' ' + '' + ack.user + ' ' + '' + ack.message + ' ';
+ }
+ } catch (err) {
+ _didIteratorError = true;
+ _iteratorError = err;
+ } finally {
+ try {
+ if (!_iteratorNormalCompletion && _iterator.return) {
+ _iterator.return();
+ }
+ } finally {
+ if (_didIteratorError) {
+ throw _iteratorError;
+ }
+ }
+ }
+
+ tooltip += '
';
+ } else {
+ tooltip += 'Add acknowledge';
+ }
+
+ var addAckButtonTemplate = '
' + '' + ' ' + '
';
+ tooltip += addAckButtonTemplate;
+ tooltip += '
';
+
+ var drop = new _tetherDrop2.default({
+ target: element[0],
+ content: tooltip,
+ position: "bottom left",
+ classes: 'drop-popover ack-tooltip',
+ openOn: 'hover',
+ hoverCloseDelay: 500,
+ tetherOptions: {
+ constraints: [{ to: 'window', pin: true, attachment: "both" }]
+ }
+ });
+
+ drop.open();
+ drop.on('close', closeDrop);
+
+ (0, _jquery2.default)('#add-acknowledge-btn').on('click', onAddAckButtonClick);
+
+ function onAddAckButtonClick() {
+ var inputTemplate = '' + ' ' + '' + 'Acknowledge ' + '' + 'Cancel' + '
';
+
+ var $input = (0, _jquery2.default)(inputTemplate);
+ var $addAckButton = (0, _jquery2.default)('.ack-tooltip .ack-add-button');
+ $addAckButton.replaceWith($input);
+ (0, _jquery2.default)('.ack-tooltip #cancel-ack-button').on('click', onAckCancelButtonClick);
+ (0, _jquery2.default)('.ack-tooltip #send-ack-button').on('click', onAckSendlButtonClick);
+ }
+
+ function onAckCancelButtonClick() {
+ (0, _jquery2.default)('.ack-tooltip .ack-input-group').replaceWith(addAckButtonTemplate);
+ (0, _jquery2.default)('#add-acknowledge-btn').on('click', onAddAckButtonClick);
+ }
+
+ function onAckSendlButtonClick() {
+ var message = (0, _jquery2.default)('.ack-tooltip #ack-message')[0].value;
+ var onAck = scope.onAck.bind(scope.context);
+ onAck(scope.trigger, message).then(function () {
+ closeDrop();
+ });
+ }
+
+ function closeDrop() {
+ setTimeout(function () {
+ drop.destroy();
+ });
+ }
+ });
+
+ $compile(element.contents())(scope);
+ }
+ };
+});
diff --git a/dist/test/panel-triggers/editor.js b/dist/test/panel-triggers/editor.js
new file mode 100644
index 0000000..1403a32
--- /dev/null
+++ b/dist/test/panel-triggers/editor.js
@@ -0,0 +1,224 @@
+'use strict';
+
+Object.defineProperty(exports, "__esModule", {
+ value: true
+});
+
+var _createClass = function () { function defineProperties(target, props) { for (var i = 0; i < props.length; i++) { var descriptor = props[i]; descriptor.enumerable = descriptor.enumerable || false; descriptor.configurable = true; if ("value" in descriptor) descriptor.writable = true; Object.defineProperty(target, descriptor.key, descriptor); } } return function (Constructor, protoProps, staticProps) { if (protoProps) defineProperties(Constructor.prototype, protoProps); if (staticProps) defineProperties(Constructor, staticProps); return Constructor; }; }(); /**
+ * Grafana-Zabbix
+ * Zabbix plugin for Grafana.
+ * http://github.com/alexanderzobnin/grafana-zabbix
+ *
+ * Trigger panel.
+ * This feature sponsored by CORE IT
+ * http://www.coreit.fr
+ *
+ * Copyright 2015 Alexander Zobnin alexanderzobnin@gmail.com
+ * Licensed under the Apache License, Version 2.0
+ */
+
+exports.triggerPanelEditor = triggerPanelEditor;
+
+var _lodash = require('lodash');
+
+var _lodash2 = _interopRequireDefault(_lodash);
+
+var _utils = require('../datasource-zabbix/utils');
+
+var utils = _interopRequireWildcard(_utils);
+
+require('../datasource-zabbix/css/query-editor.css!');
+
+function _interopRequireWildcard(obj) { if (obj && obj.__esModule) { return obj; } else { var newObj = {}; if (obj != null) { for (var key in obj) { if (Object.prototype.hasOwnProperty.call(obj, key)) newObj[key] = obj[key]; } } newObj.default = obj; return newObj; } }
+
+function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; }
+
+function _classCallCheck(instance, Constructor) { if (!(instance instanceof Constructor)) { throw new TypeError("Cannot call a class as a function"); } }
+
+var TriggerPanelEditorCtrl = function () {
+
+ /** @ngInject */
+ function TriggerPanelEditorCtrl($scope, $rootScope, uiSegmentSrv, datasourceSrv, templateSrv, popoverSrv) {
+ var _this = this;
+
+ _classCallCheck(this, TriggerPanelEditorCtrl);
+
+ $scope.editor = this;
+ this.panelCtrl = $scope.ctrl;
+ this.panel = this.panelCtrl.panel;
+
+ this.datasourceSrv = datasourceSrv;
+ this.templateSrv = templateSrv;
+ this.popoverSrv = popoverSrv;
+
+ // Map functions for bs-typeahead
+ this.getGroupNames = _lodash2.default.partial(getMetricNames, this, 'groupList');
+ this.getHostNames = _lodash2.default.partial(getMetricNames, this, 'hostList');
+ this.getApplicationNames = _lodash2.default.partial(getMetricNames, this, 'appList');
+
+ // Update metric suggestion when template variable was changed
+ $rootScope.$on('template-variable-value-updated', function () {
+ return _this.onVariableChange();
+ });
+
+ this.fontSizes = ['80%', '90%', '100%', '110%', '120%', '130%', '150%', '160%', '180%', '200%', '220%', '250%'];
+ this.ackFilters = ['all triggers', 'unacknowledged', 'acknowledged'];
+ this.sortByFields = [{ text: 'last change', value: 'lastchange' }, { text: 'severity', value: 'priority' }];
+ this.showEventsFields = [{ text: 'All', value: [0, 1] }, { text: 'OK', value: [0] }, { text: 'Problems', value: 1 }];
+
+ // Load scope defaults
+ var scopeDefaults = {
+ metric: {},
+ inputStyles: {},
+ oldTarget: _lodash2.default.cloneDeep(this.panel.triggers)
+ };
+ _lodash2.default.defaults(this, scopeDefaults);
+
+ // Set default datasource
+ this.datasources = _lodash2.default.map(this.getZabbixDataSources(), 'name');
+ if (!this.panel.datasource) {
+ this.panel.datasource = this.datasources[0];
+ }
+ // Load datasource
+ this.datasourceSrv.get(this.panel.datasource).then(function (datasource) {
+ _this.datasource = datasource;
+ _this.zabbix = datasource.zabbix;
+ _this.queryBuilder = datasource.queryBuilder;
+ _this.initFilters();
+ _this.panelCtrl.refresh();
+ });
+ }
+
+ _createClass(TriggerPanelEditorCtrl, [{
+ key: 'initFilters',
+ value: function initFilters() {
+ return Promise.all([this.suggestGroups(), this.suggestHosts(), this.suggestApps()]);
+ }
+ }, {
+ key: 'suggestGroups',
+ value: function suggestGroups() {
+ var _this2 = this;
+
+ return this.zabbix.getAllGroups().then(function (groups) {
+ _this2.metric.groupList = groups;
+ return groups;
+ });
+ }
+ }, {
+ key: 'suggestHosts',
+ value: function suggestHosts() {
+ var _this3 = this;
+
+ var groupFilter = this.datasource.replaceTemplateVars(this.panel.triggers.group.filter);
+ return this.zabbix.getAllHosts(groupFilter).then(function (hosts) {
+ _this3.metric.hostList = hosts;
+ return hosts;
+ });
+ }
+ }, {
+ key: 'suggestApps',
+ value: function suggestApps() {
+ var _this4 = this;
+
+ var groupFilter = this.datasource.replaceTemplateVars(this.panel.triggers.group.filter);
+ var hostFilter = this.datasource.replaceTemplateVars(this.panel.triggers.host.filter);
+ return this.zabbix.getAllApps(groupFilter, hostFilter).then(function (apps) {
+ _this4.metric.appList = apps;
+ return apps;
+ });
+ }
+ }, {
+ key: 'onVariableChange',
+ value: function onVariableChange() {
+ if (this.isContainsVariables()) {
+ this.targetChanged();
+ }
+ }
+
+ /**
+ * Check query for template variables
+ */
+
+ }, {
+ key: 'isContainsVariables',
+ value: function isContainsVariables() {
+ var _this5 = this;
+
+ return _lodash2.default.some(['group', 'host', 'application'], function (field) {
+ return utils.isTemplateVariable(_this5.panel.triggers[field].filter, _this5.templateSrv.variables);
+ });
+ }
+ }, {
+ key: 'targetChanged',
+ value: function targetChanged() {
+ this.initFilters();
+ this.panelCtrl.refresh();
+ }
+ }, {
+ key: 'parseTarget',
+ value: function parseTarget() {
+ this.initFilters();
+ var newTarget = _lodash2.default.cloneDeep(this.panel.triggers);
+ if (!_lodash2.default.isEqual(this.oldTarget, this.panel.triggers)) {
+ this.oldTarget = newTarget;
+ this.panelCtrl.refresh();
+ }
+ }
+ }, {
+ key: 'refreshTriggerSeverity',
+ value: function refreshTriggerSeverity() {
+ _lodash2.default.each(this.triggerList, function (trigger) {
+ trigger.color = this.panel.triggerSeverity[trigger.priority].color;
+ trigger.severity = this.panel.triggerSeverity[trigger.priority].severity;
+ });
+ this.panelCtrl.refresh();
+ }
+ }, {
+ key: 'datasourceChanged',
+ value: function datasourceChanged() {
+ this.panelCtrl.refresh();
+ }
+ }, {
+ key: 'changeTriggerSeverityColor',
+ value: function changeTriggerSeverityColor(trigger, color) {
+ this.panel.triggerSeverity[trigger.priority].color = color;
+ this.refreshTriggerSeverity();
+ }
+ }, {
+ key: 'isRegex',
+ value: function isRegex(str) {
+ return utils.isRegex(str);
+ }
+ }, {
+ key: 'isVariable',
+ value: function isVariable(str) {
+ return utils.isTemplateVariable(str, this.templateSrv.variables);
+ }
+ }, {
+ key: 'getZabbixDataSources',
+ value: function getZabbixDataSources() {
+ var ZABBIX_DS_ID = 'alexanderzobnin-zabbix-datasource';
+ return _lodash2.default.filter(this.datasourceSrv.getMetricSources(), function (datasource) {
+ return datasource.meta.id === ZABBIX_DS_ID && datasource.value;
+ });
+ }
+ }]);
+
+ return TriggerPanelEditorCtrl;
+}();
+
+// Get list of metric names for bs-typeahead directive
+
+
+function getMetricNames(scope, metricList) {
+ return _lodash2.default.uniq(_lodash2.default.map(scope.metric[metricList], 'name'));
+}
+
+function triggerPanelEditor() {
+ return {
+ restrict: 'E',
+ scope: true,
+ templateUrl: 'public/plugins/alexanderzobnin-zabbix-app/panel-triggers/editor.html',
+ controller: TriggerPanelEditorCtrl
+ };
+}
diff --git a/dist/test/panel-triggers/module.js b/dist/test/panel-triggers/module.js
new file mode 100644
index 0000000..201cd1d
--- /dev/null
+++ b/dist/test/panel-triggers/module.js
@@ -0,0 +1,407 @@
+'use strict';
+
+Object.defineProperty(exports, "__esModule", {
+ value: true
+});
+exports.PanelCtrl = exports.TriggerPanelCtrl = undefined;
+
+var _createClass = function () { function defineProperties(target, props) { for (var i = 0; i < props.length; i++) { var descriptor = props[i]; descriptor.enumerable = descriptor.enumerable || false; descriptor.configurable = true; if ("value" in descriptor) descriptor.writable = true; Object.defineProperty(target, descriptor.key, descriptor); } } return function (Constructor, protoProps, staticProps) { if (protoProps) defineProperties(Constructor.prototype, protoProps); if (staticProps) defineProperties(Constructor, staticProps); return Constructor; }; }();
+
+var _lodash = require('lodash');
+
+var _lodash2 = _interopRequireDefault(_lodash);
+
+var _jquery = require('jquery');
+
+var _jquery2 = _interopRequireDefault(_jquery);
+
+var _moment = require('moment');
+
+var _moment2 = _interopRequireDefault(_moment);
+
+var _utils = require('../datasource-zabbix/utils');
+
+var utils = _interopRequireWildcard(_utils);
+
+var _sdk = require('app/plugins/sdk');
+
+var _editor = require('./editor');
+
+require('./ack-tooltip.directive');
+
+require('./css/panel_triggers.css!');
+
+function _interopRequireWildcard(obj) { if (obj && obj.__esModule) { return obj; } else { var newObj = {}; if (obj != null) { for (var key in obj) { if (Object.prototype.hasOwnProperty.call(obj, key)) newObj[key] = obj[key]; } } newObj.default = obj; return newObj; } }
+
+function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; }
+
+function _classCallCheck(instance, Constructor) { if (!(instance instanceof Constructor)) { throw new TypeError("Cannot call a class as a function"); } }
+
+function _possibleConstructorReturn(self, call) { if (!self) { throw new ReferenceError("this hasn't been initialised - super() hasn't been called"); } return call && (typeof call === "object" || typeof call === "function") ? call : self; }
+
+function _inherits(subClass, superClass) { if (typeof superClass !== "function" && superClass !== null) { throw new TypeError("Super expression must either be null or a function, not " + typeof superClass); } subClass.prototype = Object.create(superClass && superClass.prototype, { constructor: { value: subClass, enumerable: false, writable: true, configurable: true } }); if (superClass) Object.setPrototypeOf ? Object.setPrototypeOf(subClass, superClass) : subClass.__proto__ = superClass; } /**
+ * Grafana-Zabbix
+ * Zabbix plugin for Grafana.
+ * http://github.com/alexanderzobnin/grafana-zabbix
+ *
+ * Trigger panel.
+ * This feature sponsored by CORE IT
+ * http://www.coreit.fr
+ *
+ * Copyright 2015 Alexander Zobnin alexanderzobnin@gmail.com
+ * Licensed under the Apache License, Version 2.0
+ */
+
+var defaultSeverity = [{ priority: 0, severity: 'Not classified', color: '#B7DBAB', show: true }, { priority: 1, severity: 'Information', color: '#82B5D8', show: true }, { priority: 2, severity: 'Warning', color: '#E5AC0E', show: true }, { priority: 3, severity: 'Average', color: '#C15C17', show: true }, { priority: 4, severity: 'High', color: '#BF1B00', show: true }, { priority: 5, severity: 'Disaster', color: '#890F02', show: true }];
+
+var panelDefaults = {
+ datasource: null,
+ triggers: {
+ group: { filter: "" },
+ host: { filter: "" },
+ application: { filter: "" },
+ trigger: { filter: "" }
+ },
+ hostField: true,
+ statusField: false,
+ severityField: false,
+ lastChangeField: true,
+ ageField: true,
+ infoField: true,
+ limit: 10,
+ showTriggers: 'all triggers',
+ sortTriggersBy: { text: 'last change', value: 'lastchange' },
+ showEvents: { text: 'Problems', value: '1' },
+ triggerSeverity: defaultSeverity,
+ okEventColor: 'rgba(0, 245, 153, 0.45)',
+ ackEventColor: 'rgba(0, 0, 0, 0)',
+ scroll: true,
+ pageSize: 10,
+ fontSize: '100%'
+};
+
+var triggerStatusMap = {
+ '0': 'OK',
+ '1': 'Problem'
+};
+
+var defaultTimeFormat = "DD MMM YYYY HH:mm:ss";
+
+var TriggerPanelCtrl = function (_PanelCtrl) {
+ _inherits(TriggerPanelCtrl, _PanelCtrl);
+
+ /** @ngInject */
+ function TriggerPanelCtrl($scope, $injector, $element, datasourceSrv, templateSrv, contextSrv) {
+ _classCallCheck(this, TriggerPanelCtrl);
+
+ var _this = _possibleConstructorReturn(this, (TriggerPanelCtrl.__proto__ || Object.getPrototypeOf(TriggerPanelCtrl)).call(this, $scope, $injector));
+
+ _this.datasourceSrv = datasourceSrv;
+ _this.templateSrv = templateSrv;
+ _this.contextSrv = contextSrv;
+ _this.triggerStatusMap = triggerStatusMap;
+ _this.defaultTimeFormat = defaultTimeFormat;
+ _this.pageIndex = 0;
+ _this.triggerList = [];
+ _this.currentTriggersPage = [];
+
+ // Load panel defaults
+ // _.cloneDeep() need for prevent changing shared defaultSeverity.
+ // Load object "by value" istead "by reference".
+ _lodash2.default.defaults(_this.panel, _lodash2.default.cloneDeep(panelDefaults));
+
+ _this.events.on('init-edit-mode', _this.onInitEditMode.bind(_this));
+ _this.events.on('refresh', _this.onRefresh.bind(_this));
+ return _this;
+ }
+
+ _createClass(TriggerPanelCtrl, [{
+ key: 'onInitEditMode',
+ value: function onInitEditMode() {
+ this.addEditorTab('Options', _editor.triggerPanelEditor, 2);
+ }
+ }, {
+ key: 'onRefresh',
+ value: function onRefresh() {
+ var _this2 = this;
+
+ // clear loading/error state
+ delete this.error;
+ this.loading = true;
+
+ return this.refreshData().then(function (triggerList) {
+ // Limit triggers number
+ _this2.triggerList = triggerList.slice(0, _this2.panel.limit);
+
+ _this2.getCurrentTriggersPage();
+
+ // Notify panel that request is finished
+ _this2.loading = false;
+
+ _this2.render(_this2.triggerList);
+ });
+ }
+ }, {
+ key: 'refreshData',
+ value: function refreshData() {
+ return this.getTriggers().then(this.getAcknowledges.bind(this)).then(this.filterTriggers.bind(this));
+ }
+ }, {
+ key: 'getTriggers',
+ value: function getTriggers() {
+ var _this3 = this;
+
+ return this.datasourceSrv.get(this.panel.datasource).then(function (datasource) {
+ var zabbix = datasource.zabbix;
+ _this3.zabbix = zabbix;
+ var showEvents = _this3.panel.showEvents.value;
+ var triggerFilter = _this3.panel.triggers;
+
+ // Replace template variables
+ var groupFilter = datasource.replaceTemplateVars(triggerFilter.group.filter);
+ var hostFilter = datasource.replaceTemplateVars(triggerFilter.host.filter);
+ var appFilter = datasource.replaceTemplateVars(triggerFilter.application.filter);
+
+ var getTriggers = zabbix.getTriggers(groupFilter, hostFilter, appFilter, showEvents);
+ return getTriggers.then(function (triggers) {
+ return _lodash2.default.map(triggers, _this3.formatTrigger.bind(_this3));
+ });
+ });
+ }
+ }, {
+ key: 'getAcknowledges',
+ value: function getAcknowledges(triggerList) {
+ var _this4 = this;
+
+ // Request acknowledges for trigger
+ var eventids = _lodash2.default.map(triggerList, function (trigger) {
+ return trigger.lastEvent.eventid;
+ });
+
+ return this.zabbix.getAcknowledges(eventids).then(function (events) {
+
+ // Map events to triggers
+ _lodash2.default.each(triggerList, function (trigger) {
+ var event = _lodash2.default.find(events, function (event) {
+ return event.eventid === trigger.lastEvent.eventid;
+ });
+
+ if (event) {
+ trigger.acknowledges = _lodash2.default.map(event.acknowledges, function (ack) {
+ var timestamp = _moment2.default.unix(ack.clock);
+ if (_this4.panel.customLastChangeFormat) {
+ ack.time = timestamp.format(_this4.panel.lastChangeFormat);
+ } else {
+ ack.time = timestamp.format(_this4.defaultTimeFormat);
+ }
+ ack.user = ack.alias + ' (' + ack.name + ' ' + ack.surname + ')';
+ return ack;
+ });
+
+ // Mark acknowledged triggers with different color
+ if (_this4.panel.markAckEvents && trigger.acknowledges.length) {
+ trigger.color = _this4.panel.ackEventColor;
+ }
+ }
+ });
+
+ return triggerList;
+ });
+ }
+ }, {
+ key: 'filterTriggers',
+ value: function filterTriggers(triggerList) {
+ var _this5 = this;
+
+ // Filter triggers by description
+ var triggerFilter = this.panel.triggers.trigger.filter;
+ if (triggerFilter) {
+ triggerList = _filterTriggers(triggerList, triggerFilter);
+ }
+
+ // Filter acknowledged triggers
+ if (this.panel.showTriggers === 'unacknowledged') {
+ triggerList = _lodash2.default.filter(triggerList, function (trigger) {
+ return !trigger.acknowledges;
+ });
+ } else if (this.panel.showTriggers === 'acknowledged') {
+ triggerList = _lodash2.default.filter(triggerList, 'acknowledges');
+ } else {
+ triggerList = triggerList;
+ }
+
+ // Filter triggers by severity
+ triggerList = _lodash2.default.filter(triggerList, function (trigger) {
+ return _this5.panel.triggerSeverity[trigger.priority].show;
+ });
+
+ // Sort triggers
+ if (this.panel.sortTriggersBy.value === 'priority') {
+ triggerList = _lodash2.default.sortBy(triggerList, 'priority').reverse();
+ } else {
+ triggerList = _lodash2.default.sortBy(triggerList, 'lastchangeUnix').reverse();
+ }
+
+ return triggerList;
+ }
+ }, {
+ key: 'formatTrigger',
+ value: function formatTrigger(trigger) {
+ var triggerObj = trigger;
+
+ // Format last change and age
+ trigger.lastchangeUnix = Number(trigger.lastchange);
+ var timestamp = _moment2.default.unix(trigger.lastchangeUnix);
+ if (this.panel.customLastChangeFormat) {
+ // User defined format
+ triggerObj.lastchange = timestamp.format(this.panel.lastChangeFormat);
+ } else {
+ triggerObj.lastchange = timestamp.format(this.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 color
+ if (trigger.value === '1') {
+ // Problem state
+ triggerObj.color = this.panel.triggerSeverity[trigger.priority].color;
+ } else {
+ // OK state
+ triggerObj.color = this.panel.okEventColor;
+ }
+
+ triggerObj.severity = this.panel.triggerSeverity[trigger.priority].severity;
+ return triggerObj;
+ }
+ }, {
+ key: 'switchComment',
+ value: function switchComment(trigger) {
+ trigger.showComment = !trigger.showComment;
+ }
+ }, {
+ key: 'acknowledgeTrigger',
+ value: function acknowledgeTrigger(trigger, message) {
+ var eventid = trigger.lastEvent.eventid;
+ var grafana_user = this.contextSrv.user.name;
+ var ack_message = grafana_user + ' (Grafana): ' + message;
+ return this.datasourceSrv.get(this.panel.datasource).then(function (datasource) {
+ var zabbixAPI = datasource.zabbix.zabbixAPI;
+ return zabbixAPI.acknowledgeEvent(eventid, ack_message);
+ }).then(this.onRefresh.bind(this));
+ }
+ }, {
+ key: 'getCurrentTriggersPage',
+ value: function getCurrentTriggersPage() {
+ var pageSize = this.panel.pageSize || 10;
+ var startPos = this.pageIndex * pageSize;
+ var endPos = Math.min(startPos + pageSize, this.triggerList.length);
+ this.currentTriggersPage = this.triggerList.slice(startPos, endPos);
+ return this.currentTriggersPage;
+ }
+ }, {
+ key: 'link',
+ value: function link(scope, elem, attrs, ctrl) {
+ var data;
+ var panel = ctrl.panel;
+ var pageCount = 0;
+ data = ctrl.triggerList;
+
+ function getTableHeight() {
+ var panelHeight = ctrl.height;
+
+ if (pageCount > 1) {
+ panelHeight -= 26;
+ }
+
+ return panelHeight - 31 + 'px';
+ }
+
+ function switchPage(e) {
+ var el = (0, _jquery2.default)(e.currentTarget);
+ ctrl.pageIndex = parseInt(el.text(), 10) - 1;
+
+ var pageSize = ctrl.panel.pageSize || 10;
+ var startPos = ctrl.pageIndex * pageSize;
+ var endPos = Math.min(startPos + pageSize, ctrl.triggerList.length);
+ ctrl.currentTriggersPage = ctrl.triggerList.slice(startPos, endPos);
+
+ scope.$apply();
+ renderPanel();
+ }
+
+ function appendPaginationControls(footerElem) {
+ footerElem.empty();
+
+ var pageSize = ctrl.panel.pageSize || 5;
+ pageCount = Math.ceil(data.length / pageSize);
+ if (pageCount === 1) {
+ return;
+ }
+
+ var startPage = Math.max(ctrl.pageIndex - 3, 0);
+ var endPage = Math.min(pageCount, startPage + 9);
+
+ var paginationList = (0, _jquery2.default)('');
+
+ for (var i = startPage; i < endPage; i++) {
+ var activeClass = i === ctrl.pageIndex ? 'active' : '';
+ var pageLinkElem = (0, _jquery2.default)('' + (i + 1) + ' ');
+ paginationList.append(pageLinkElem);
+ }
+
+ footerElem.append(paginationList);
+ }
+
+ function renderPanel() {
+ var panelElem = elem.parents('.panel');
+ var rootElem = elem.find('.triggers-panel-scroll');
+ var footerElem = elem.find('.triggers-panel-footer');
+
+ elem.css({ 'font-size': panel.fontSize });
+ panelElem.addClass('triggers-panel-wrapper');
+ appendPaginationControls(footerElem);
+
+ rootElem.css({ 'max-height': panel.scroll ? getTableHeight() : '' });
+ }
+
+ elem.on('click', '.triggers-panel-page-link', switchPage);
+
+ var unbindDestroy = scope.$on('$destroy', function () {
+ elem.off('click', '.triggers-panel-page-link');
+ unbindDestroy();
+ });
+
+ ctrl.events.on('render', function (renderData) {
+ data = renderData || data;
+ if (data) {
+ renderPanel();
+ }
+ ctrl.renderingCompleted();
+ });
+ }
+ }]);
+
+ return TriggerPanelCtrl;
+}(_sdk.PanelCtrl);
+
+TriggerPanelCtrl.templateUrl = 'panel-triggers/module.html';
+
+function _filterTriggers(triggers, triggerFilter) {
+ if (utils.isRegex(triggerFilter)) {
+ return _lodash2.default.filter(triggers, function (trigger) {
+ return utils.buildRegex(triggerFilter).test(trigger.description);
+ });
+ } else {
+ return _lodash2.default.filter(triggers, function (trigger) {
+ return trigger.description === triggerFilter;
+ });
+ }
+}
+
+exports.TriggerPanelCtrl = TriggerPanelCtrl;
+exports.PanelCtrl = TriggerPanelCtrl;
diff --git a/src/panel-triggers/editor.html b/src/panel-triggers/editor.html
index 39e96b8..b0510db 100644
--- a/src/panel-triggers/editor.html
+++ b/src/panel-triggers/editor.html
@@ -137,6 +137,17 @@
ng-change="editor.panelCtrl.refresh()">
+
diff --git a/src/panel-triggers/editor.js b/src/panel-triggers/editor.js
index d8f2736..9bdf60f 100644
--- a/src/panel-triggers/editor.js
+++ b/src/panel-triggers/editor.js
@@ -36,17 +36,16 @@ class TriggerPanelEditorCtrl {
// Update metric suggestion when template variable was changed
$rootScope.$on('template-variable-value-updated', () => this.onVariableChange());
+ this.fontSizes = ['80%', '90%', '100%', '110%', '120%', '130%', '150%', '160%', '180%', '200%', '220%', '250%'];
this.ackFilters = [
'all triggers',
'unacknowledged',
'acknowledged'
];
-
this.sortByFields = [
{ text: 'last change', value: 'lastchange' },
{ text: 'severity', value: 'priority' }
];
-
this.showEventsFields = [
{ text: 'All', value: [0,1] },
{ text: 'OK', value: [0] },
diff --git a/src/panel-triggers/module.js b/src/panel-triggers/module.js
index 56b967c..1d9eab1 100644
--- a/src/panel-triggers/module.js
+++ b/src/panel-triggers/module.js
@@ -51,7 +51,8 @@ var panelDefaults = {
okEventColor: 'rgba(0, 245, 153, 0.45)',
ackEventColor: 'rgba(0, 0, 0, 0)',
scroll: true,
- pageSize: 10
+ pageSize: 10,
+ fontSize: '100%',
};
var triggerStatusMap = {