Бунин Олег Александрович
2018-04-19 17:40:08 +03:00
parent f0a5e7de8d
commit e78387fcb0
72 changed files with 4110 additions and 4005 deletions

File diff suppressed because one or more lines are too long

View File

@@ -1 +1 @@
{"version":3,"sources":["../../src/panel-triggers/datasource-selector.directive.js"],"names":["angular","_","template","module","directive","scope","datasources","options","onChange","controller","DatasourceSelectorCtrl","controllerAs","$scope","dsOptions","multi","current","value","text","join","map","ds","selected","includes","updatedOptions","newDataSources","$$postDigest"],"mappings":";;;;;;;;;;;;;;;AAAOA,a;;AACAC,O;;;;;;;;;;;;;;;;;;;;;AAEDC,c;;;AAKNF,cACCG,MADD,CACQ,oBADR,EAECC,SAFD,CAEW,oBAFX,EAEiC,YAAM;AACrC,eAAO;AACLC,iBAAO;AACLC,yBAAa,GADR;AAELC,qBAAS,GAFJ;AAGLC,sBAAU;AAHL,WADF;AAMLC,sBAAYC,sBANP;AAOLC,wBAAc,MAPT;AAQLT,oBAAUA;AARL,SAAP;AAUD,OAbD;;AAeMQ,4B;;AAEJ;AACA,wCAAYE,MAAZ,EAAoB;AAAA;;AAClB,eAAKP,KAAL,GAAaO,MAAb;AACA,cAAIN,cAAcM,OAAON,WAAzB;AACA,cAAIC,UAAUK,OAAOL,OAArB;AACA,eAAKM,SAAL,GAAiB;AACfC,mBAAO,IADQ;AAEfC,qBAAS,EAACC,OAAOV,WAAR,EAAqBW,MAAMX,YAAYY,IAAZ,CAAiB,KAAjB,CAA3B,EAFM;AAGfX,qBAASN,EAAEkB,GAAF,CAAMZ,OAAN,EAAe,UAACa,EAAD,EAAQ;AAC9B,qBAAO,EAACH,MAAMG,EAAP,EAAWJ,OAAOI,EAAlB,EAAsBC,UAAUpB,EAAEqB,QAAF,CAAWhB,WAAX,EAAwBc,EAAxB,CAAhC,EAAP;AACD,aAFQ;AAHM,WAAjB;AAOD;;;;mCAEQG,c,EAAgB;AAAA;;AACvB,gBAAIC,iBAAiBD,eAAeR,OAAf,CAAuBC,KAA5C;AACA,iBAAKX,KAAL,CAAWC,WAAX,GAAyBkB,cAAzB;;AAEA;AACA,iBAAKnB,KAAL,CAAWoB,YAAX,CAAwB,YAAM;AAC5B,oBAAKpB,KAAL,CAAWG,QAAX;AACD,aAFD;AAGD","file":"datasource-selector.directive.js","sourcesContent":["import angular from 'angular';\nimport _ from 'lodash';\n\nconst template = `\n<value-select-dropdown variable=\"ctrl.dsOptions\" on-updated=\"ctrl.onChange(ctrl.dsOptions)\">\n</value-select-dropdown>\n`;\n\nangular\n.module('grafana.directives')\n.directive('datasourceSelector', () => {\n return {\n scope: {\n datasources: \"=\",\n options: \"=\",\n onChange: \"&\"\n },\n controller: DatasourceSelectorCtrl,\n controllerAs: 'ctrl',\n template: template\n };\n});\n\nclass DatasourceSelectorCtrl {\n\n /** @ngInject */\n constructor($scope) {\n this.scope = $scope;\n let datasources = $scope.datasources;\n let options = $scope.options;\n this.dsOptions = {\n multi: true,\n current: {value: datasources, text: datasources.join(\" + \")},\n options: _.map(options, (ds) => {\n return {text: ds, value: ds, selected: _.includes(datasources, ds)};\n })\n };\n }\n\n onChange(updatedOptions) {\n let newDataSources = updatedOptions.current.value;\n this.scope.datasources = newDataSources;\n\n // Run after model was changed\n this.scope.$$postDigest(() => {\n this.scope.onChange();\n });\n }\n}\n"]}
{"version":3,"sources":["../../src/panel-triggers/datasource-selector.directive.js"],"names":["angular","_","template","module","directive","scope","datasources","options","onChange","controller","DatasourceSelectorCtrl","controllerAs","$scope","dsOptions","multi","current","value","text","join","map","ds","selected","includes","updatedOptions","newDataSources","$$postDigest"],"mappings":";;;;;;;;;;;;;;;AAAOA,a;;AACAC,O;;;;;;;;;;;;;;;;;;;;;AAEDC,c;;;AAKNF,cACCG,MADD,CACQ,oBADR,EAECC,SAFD,CAEW,oBAFX,EAEiC,YAAM;AACrC,eAAO;AACLC,iBAAO;AACLC,yBAAa,GADR;AAELC,qBAAS,GAFJ;AAGLC,sBAAU;AAHL,WADF;AAMLC,sBAAYC,sBANP;AAOLC,wBAAc,MAPT;AAQLT,oBAAUA;AARL,SAAP;AAUD,OAbD;;AAeMQ,4B;;AAEJ;AACA,wCAAYE,MAAZ,EAAoB;AAAA;;AAClB,eAAKP,KAAL,GAAaO,MAAb;AACA,cAAIN,cAAcM,OAAON,WAAzB;AACA,cAAIC,UAAUK,OAAOL,OAArB;AACA,eAAKM,SAAL,GAAiB;AACfC,mBAAO,IADQ;AAEfC,qBAAS,EAACC,OAAOV,WAAR,EAAqBW,MAAMX,YAAYY,IAAZ,CAAiB,KAAjB,CAA3B,EAFM;AAGfX,qBAASN,EAAEkB,GAAF,CAAMZ,OAAN,EAAe,UAACa,EAAD,EAAQ;AAC9B,qBAAO,EAACH,MAAMG,EAAP,EAAWJ,OAAOI,EAAlB,EAAsBC,UAAUpB,EAAEqB,QAAF,CAAWhB,WAAX,EAAwBc,EAAxB,CAAhC,EAAP;AACD,aAFQ;AAHM,WAAjB;AAOD;;;;mCAEQG,c,EAAgB;AAAA;;AACvB,gBAAIC,iBAAiBD,eAAeR,OAAf,CAAuBC,KAA5C;AACA,iBAAKX,KAAL,CAAWC,WAAX,GAAyBkB,cAAzB;;AAEA;AACA,iBAAKnB,KAAL,CAAWoB,YAAX,CAAwB,YAAM;AAC5B,oBAAKpB,KAAL,CAAWG,QAAX;AACD,aAFD;AAGD","file":"datasource-selector.directive.js","sourcesContent":["import angular from 'angular';\r\nimport _ from 'lodash';\r\n\r\nconst template = `\r\n<value-select-dropdown variable=\"ctrl.dsOptions\" on-updated=\"ctrl.onChange(ctrl.dsOptions)\">\r\n</value-select-dropdown>\r\n`;\r\n\r\nangular\r\n.module('grafana.directives')\r\n.directive('datasourceSelector', () => {\r\n return {\r\n scope: {\r\n datasources: \"=\",\r\n options: \"=\",\r\n onChange: \"&\"\r\n },\r\n controller: DatasourceSelectorCtrl,\r\n controllerAs: 'ctrl',\r\n template: template\r\n };\r\n});\r\n\r\nclass DatasourceSelectorCtrl {\r\n\r\n /** @ngInject */\r\n constructor($scope) {\r\n this.scope = $scope;\r\n let datasources = $scope.datasources;\r\n let options = $scope.options;\r\n this.dsOptions = {\r\n multi: true,\r\n current: {value: datasources, text: datasources.join(\" + \")},\r\n options: _.map(options, (ds) => {\r\n return {text: ds, value: ds, selected: _.includes(datasources, ds)};\r\n })\r\n };\r\n }\r\n\r\n onChange(updatedOptions) {\r\n let newDataSources = updatedOptions.current.value;\r\n this.scope.datasources = newDataSources;\r\n\r\n // Run after model was changed\r\n this.scope.$$postDigest(() => {\r\n this.scope.onChange();\r\n });\r\n }\r\n}\r\n"]}

View File

@@ -1 +1 @@
{"version":3,"sources":["../../src/panel-triggers/migrations.js"],"names":["migratePanelSchema","panel","isEmptyPanel","schemaVersion","getSchemaVersion","CURRENT_SCHEMA_VERSION","datasources","datasource","targets","triggers","lastChangeField","ageField","infoField","scroll","hideHostsInMaintenance","_","isEmpty","each","target","defaultsDeep","DEFAULT_TARGET"],"mappings":";;;;;;;AAMO,WAASA,kBAAT,CAA4BC,KAA5B,EAAmC;AACxC,QAAIC,aAAaD,KAAb,CAAJ,EAAyB;AACvB,aAAOA,KAAP;AACD;;AAED,QAAME,gBAAgBC,iBAAiBH,KAAjB,CAAtB;AACAA,UAAME,aAAN,GAAsBE,sBAAtB;;AAEA,QAAIF,gBAAgB,CAApB,EAAuB;AACrBF,YAAMK,WAAN,GAAoB,CAACL,MAAMM,UAAP,CAApB;AACAN,YAAMO,OAAN,GAAgB,EAAhB;AACAP,YAAMO,OAAN,CAAcP,MAAMK,WAAN,CAAkB,CAAlB,CAAd,IAAsCL,MAAMQ,QAA5C;;AAEA;AACA,aAAOR,MAAMQ,QAAb;AACA,aAAOR,MAAMM,UAAb;AACD;;AAED,QAAIJ,gBAAgB,CAApB,EAAuB;AACrB;AACA,aAAOF,MAAMS,eAAb;AACA,aAAOT,MAAMU,QAAb;AACA,aAAOV,MAAMW,SAAb;AACA,aAAOX,MAAMY,MAAb;AACA,aAAOZ,MAAMa,sBAAb;AACD;;AAED,QAAIX,gBAAgB,CAApB,EAAuB;AACrB,UAAIF,MAAMO,OAAN,IAAiB,CAACO,EAAEC,OAAF,CAAUf,MAAMO,OAAhB,CAAtB,EAAgD;AAC9CO,UAAEE,IAAF,CAAOhB,MAAMO,OAAb,EAAsB,UAACU,MAAD,EAAY;AAChCH,YAAEI,YAAF,CAAeD,MAAf,EAAuBE,cAAvB;AACD,SAFD;AAGD;AACF;;AAED,WAAOnB,KAAP;AACD;;gCApCeD,kB;;AAsChB,WAASI,gBAAT,CAA0BH,KAA1B,EAAiC;AAC/B,WAAOA,MAAME,aAAN,IAAuB,CAA9B;AACD;;AAED,WAASD,YAAT,CAAsBD,KAAtB,EAA6B;AAC3B,WAAO,CAACA,MAAMM,UAAP,IAAqB,CAACN,MAAMK,WAA5B,IAA2C,CAACL,MAAMQ,QAAlD,IAA8D,CAACR,MAAMO,OAA5E;AACD;;;AAlDMO,O;;AACCK,oB,wBAAAA,c;;;wCAGKf,sB,GAAyB,C","file":"migrations.js","sourcesContent":["import _ from 'lodash';\nimport {DEFAULT_TARGET} from './triggers_panel_ctrl';\n\n// Actual schema version\nexport const CURRENT_SCHEMA_VERSION = 4;\n\nexport function migratePanelSchema(panel) {\n if (isEmptyPanel(panel)) {\n return panel;\n }\n\n const schemaVersion = getSchemaVersion(panel);\n panel.schemaVersion = CURRENT_SCHEMA_VERSION;\n\n if (schemaVersion < 2) {\n panel.datasources = [panel.datasource];\n panel.targets = {};\n panel.targets[panel.datasources[0]] = panel.triggers;\n\n // delete old props\n delete panel.triggers;\n delete panel.datasource;\n }\n\n if (schemaVersion < 3) {\n // delete old props\n delete panel.lastChangeField;\n delete panel.ageField;\n delete panel.infoField;\n delete panel.scroll;\n delete panel.hideHostsInMaintenance;\n }\n\n if (schemaVersion < 4) {\n if (panel.targets && !_.isEmpty(panel.targets)) {\n _.each(panel.targets, (target) => {\n _.defaultsDeep(target, DEFAULT_TARGET);\n });\n }\n }\n\n return panel;\n}\n\nfunction getSchemaVersion(panel) {\n return panel.schemaVersion || 1;\n}\n\nfunction isEmptyPanel(panel) {\n return !panel.datasource && !panel.datasources && !panel.triggers && !panel.targets;\n}\n"]}
{"version":3,"sources":["../../src/panel-triggers/migrations.js"],"names":["migratePanelSchema","panel","isEmptyPanel","schemaVersion","getSchemaVersion","CURRENT_SCHEMA_VERSION","datasources","datasource","targets","triggers","lastChangeField","ageField","infoField","scroll","hideHostsInMaintenance","_","isEmpty","each","target","defaultsDeep","DEFAULT_TARGET"],"mappings":";;;;;;;AAMO,WAASA,kBAAT,CAA4BC,KAA5B,EAAmC;AACxC,QAAIC,aAAaD,KAAb,CAAJ,EAAyB;AACvB,aAAOA,KAAP;AACD;;AAED,QAAME,gBAAgBC,iBAAiBH,KAAjB,CAAtB;AACAA,UAAME,aAAN,GAAsBE,sBAAtB;;AAEA,QAAIF,gBAAgB,CAApB,EAAuB;AACrBF,YAAMK,WAAN,GAAoB,CAACL,MAAMM,UAAP,CAApB;AACAN,YAAMO,OAAN,GAAgB,EAAhB;AACAP,YAAMO,OAAN,CAAcP,MAAMK,WAAN,CAAkB,CAAlB,CAAd,IAAsCL,MAAMQ,QAA5C;;AAEA;AACA,aAAOR,MAAMQ,QAAb;AACA,aAAOR,MAAMM,UAAb;AACD;;AAED,QAAIJ,gBAAgB,CAApB,EAAuB;AACrB;AACA,aAAOF,MAAMS,eAAb;AACA,aAAOT,MAAMU,QAAb;AACA,aAAOV,MAAMW,SAAb;AACA,aAAOX,MAAMY,MAAb;AACA,aAAOZ,MAAMa,sBAAb;AACD;;AAED,QAAIX,gBAAgB,CAApB,EAAuB;AACrB,UAAIF,MAAMO,OAAN,IAAiB,CAACO,EAAEC,OAAF,CAAUf,MAAMO,OAAhB,CAAtB,EAAgD;AAC9CO,UAAEE,IAAF,CAAOhB,MAAMO,OAAb,EAAsB,UAACU,MAAD,EAAY;AAChCH,YAAEI,YAAF,CAAeD,MAAf,EAAuBE,cAAvB;AACD,SAFD;AAGD;AACF;;AAED,WAAOnB,KAAP;AACD;;gCApCeD,kB;;AAsChB,WAASI,gBAAT,CAA0BH,KAA1B,EAAiC;AAC/B,WAAOA,MAAME,aAAN,IAAuB,CAA9B;AACD;;AAED,WAASD,YAAT,CAAsBD,KAAtB,EAA6B;AAC3B,WAAO,CAACA,MAAMM,UAAP,IAAqB,CAACN,MAAMK,WAA5B,IAA2C,CAACL,MAAMQ,QAAlD,IAA8D,CAACR,MAAMO,OAA5E;AACD;;;AAlDMO,O;;AACCK,oB,wBAAAA,c;;;wCAGKf,sB,GAAyB,C","file":"migrations.js","sourcesContent":["import _ from 'lodash';\r\nimport {DEFAULT_TARGET} from './triggers_panel_ctrl';\r\n\r\n// Actual schema version\r\nexport const CURRENT_SCHEMA_VERSION = 4;\r\n\r\nexport function migratePanelSchema(panel) {\r\n if (isEmptyPanel(panel)) {\r\n return panel;\r\n }\r\n\r\n const schemaVersion = getSchemaVersion(panel);\r\n panel.schemaVersion = CURRENT_SCHEMA_VERSION;\r\n\r\n if (schemaVersion < 2) {\r\n panel.datasources = [panel.datasource];\r\n panel.targets = {};\r\n panel.targets[panel.datasources[0]] = panel.triggers;\r\n\r\n // delete old props\r\n delete panel.triggers;\r\n delete panel.datasource;\r\n }\r\n\r\n if (schemaVersion < 3) {\r\n // delete old props\r\n delete panel.lastChangeField;\r\n delete panel.ageField;\r\n delete panel.infoField;\r\n delete panel.scroll;\r\n delete panel.hideHostsInMaintenance;\r\n }\r\n\r\n if (schemaVersion < 4) {\r\n if (panel.targets && !_.isEmpty(panel.targets)) {\r\n _.each(panel.targets, (target) => {\r\n _.defaultsDeep(target, DEFAULT_TARGET);\r\n });\r\n }\r\n }\r\n\r\n return panel;\r\n}\r\n\r\nfunction getSchemaVersion(panel) {\r\n return panel.schemaVersion || 1;\r\n}\r\n\r\nfunction isEmptyPanel(panel) {\r\n return !panel.datasource && !panel.datasources && !panel.triggers && !panel.targets;\r\n}\r\n"]}

View File

@@ -11,17 +11,17 @@ System.register(['./triggers_panel_ctrl', 'app/plugins/sdk', './datasource-selec
loadPluginCss = _appPluginsSdk.loadPluginCss;
}, function (_datasourceSelectorDirective) {}, function (_ackTooltipDirective) {}],
execute: function () {
/**
* 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
/**
* 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
*/
loadPluginCss({

View File

@@ -1 +1 @@
{"version":3,"sources":["../../src/panel-triggers/module.js"],"names":["TriggerPanelCtrl","loadPluginCss","dark","light"],"mappings":";;;;;;;;AAaQA,sB,wBAAAA,gB;;AACAC,mB,kBAAAA,a;;;AAdR;;;;;;;;;;;;;AAkBAA,oBAAc;AACZC,cAAM,gEADM;AAEZC,eAAO;AAFK,OAAd;;2BAMEH,gB","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 {TriggerPanelCtrl} from './triggers_panel_ctrl';\nimport {loadPluginCss} from 'app/plugins/sdk';\nimport './datasource-selector.directive';\nimport './ack-tooltip.directive';\n\nloadPluginCss({\n dark: 'plugins/alexanderzobnin-zabbix-app/css/grafana-zabbix.dark.css',\n light: 'plugins/alexanderzobnin-zabbix-app/css/grafana-zabbix.light.css'\n});\n\nexport {\n TriggerPanelCtrl as PanelCtrl\n};\n"]}
{"version":3,"sources":["../../src/panel-triggers/module.js"],"names":["TriggerPanelCtrl","loadPluginCss","dark","light"],"mappings":";;;;;;;;AAaQA,sB,wBAAAA,gB;;AACAC,mB,kBAAAA,a;;;AAdR;;;;;;;;;;;;;AAkBAA,oBAAc;AACZC,cAAM,gEADM;AAEZC,eAAO;AAFK,OAAd;;2BAMEH,gB","file":"module.js","sourcesContent":["/**\r\n * Grafana-Zabbix\r\n * Zabbix plugin for Grafana.\r\n * http://github.com/alexanderzobnin/grafana-zabbix\r\n *\r\n * Trigger panel.\r\n * This feature sponsored by CORE IT\r\n * http://www.coreit.fr\r\n *\r\n * Copyright 2015 Alexander Zobnin alexanderzobnin@gmail.com\r\n * Licensed under the Apache License, Version 2.0\r\n */\r\n\r\nimport {TriggerPanelCtrl} from './triggers_panel_ctrl';\r\nimport {loadPluginCss} from 'app/plugins/sdk';\r\nimport './datasource-selector.directive';\r\nimport './ack-tooltip.directive';\r\n\r\nloadPluginCss({\r\n dark: 'plugins/alexanderzobnin-zabbix-app/css/grafana-zabbix.dark.css',\r\n light: 'plugins/alexanderzobnin-zabbix-app/css/grafana-zabbix.light.css'\r\n});\r\n\r\nexport {\r\n TriggerPanelCtrl as PanelCtrl\r\n};\r\n"]}

View File

@@ -1 +1 @@
{"version":3,"sources":["../../src/panel-triggers/options_tab.js"],"names":["triggerPanelOptionsTab","restrict","scope","templateUrl","controller","TriggerPanelOptionsCtrl","$scope","editor","panelCtrl","ctrl","panel","fontSizes","ackFilters","sortByFields","text","value","showEventsFields"],"mappings":";;;;;;;;;;;;;AAuCO,WAASA,sBAAT,GAAkC;AACvC,WAAO;AACLC,gBAAU,GADL;AAELC,aAAO,IAFF;AAGLC,mBAAa,oFAHR;AAILC,kBAAYC;AAJP,KAAP;AAMD;;oCAPeL,sB;;;;;AA1BVK,6B;;AAEJ;AACA,uCAAYC,MAAZ,EAAoB;AAAA;;AAClBA,eAAOC,MAAP,GAAgB,IAAhB;AACA,aAAKC,SAAL,GAAiBF,OAAOG,IAAxB;AACA,aAAKC,KAAL,GAAa,KAAKF,SAAL,CAAeE,KAA5B;;AAEA,aAAKC,SAAL,GAAiB,CAAC,KAAD,EAAQ,KAAR,EAAe,MAAf,EAAuB,MAAvB,EAA+B,MAA/B,EAAuC,MAAvC,EAA+C,MAA/C,EAAuD,MAAvD,EAA+D,MAA/D,EAAuE,MAAvE,EAA+E,MAA/E,EAAuF,MAAvF,CAAjB;AACA,aAAKC,UAAL,GAAkB,CAChB,cADgB,EAEhB,gBAFgB,EAGhB,cAHgB,CAAlB;AAKA,aAAKC,YAAL,GAAoB,CAClB,EAAEC,MAAM,aAAR,EAAwBC,OAAO,YAA/B,EADkB,EAElB,EAAED,MAAM,UAAR,EAAwBC,OAAO,UAA/B,EAFkB,CAApB;AAIA,aAAKC,gBAAL,GAAwB,CACtB,EAAEF,MAAM,KAAR,EAAmBC,OAAO,CAAC,CAAD,EAAG,CAAH,CAA1B,EADsB,EAEtB,EAAED,MAAM,IAAR,EAAmBC,OAAO,CAAC,CAAD,CAA1B,EAFsB,EAGtB,EAAED,MAAM,UAAR,EAAoBC,OAAO,CAA3B,EAHsB,CAAxB;AAKD,O","file":"options_tab.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\nclass TriggerPanelOptionsCtrl {\n\n /** @ngInject */\n constructor($scope) {\n $scope.editor = this;\n this.panelCtrl = $scope.ctrl;\n this.panel = this.panelCtrl.panel;\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}\n\nexport function triggerPanelOptionsTab() {\n return {\n restrict: 'E',\n scope: true,\n templateUrl: 'public/plugins/alexanderzobnin-zabbix-app/panel-triggers/partials/options_tab.html',\n controller: TriggerPanelOptionsCtrl,\n };\n}\n"]}
{"version":3,"sources":["../../src/panel-triggers/options_tab.js"],"names":["triggerPanelOptionsTab","restrict","scope","templateUrl","controller","TriggerPanelOptionsCtrl","$scope","editor","panelCtrl","ctrl","panel","fontSizes","ackFilters","sortByFields","text","value","showEventsFields"],"mappings":";;;;;;;;;;;;;AAuCO,WAASA,sBAAT,GAAkC;AACvC,WAAO;AACLC,gBAAU,GADL;AAELC,aAAO,IAFF;AAGLC,mBAAa,oFAHR;AAILC,kBAAYC;AAJP,KAAP;AAMD;;oCAPeL,sB;;;;;AA1BVK,6B;;AAEJ;AACA,uCAAYC,MAAZ,EAAoB;AAAA;;AAClBA,eAAOC,MAAP,GAAgB,IAAhB;AACA,aAAKC,SAAL,GAAiBF,OAAOG,IAAxB;AACA,aAAKC,KAAL,GAAa,KAAKF,SAAL,CAAeE,KAA5B;;AAEA,aAAKC,SAAL,GAAiB,CAAC,KAAD,EAAQ,KAAR,EAAe,MAAf,EAAuB,MAAvB,EAA+B,MAA/B,EAAuC,MAAvC,EAA+C,MAA/C,EAAuD,MAAvD,EAA+D,MAA/D,EAAuE,MAAvE,EAA+E,MAA/E,EAAuF,MAAvF,CAAjB;AACA,aAAKC,UAAL,GAAkB,CAChB,cADgB,EAEhB,gBAFgB,EAGhB,cAHgB,CAAlB;AAKA,aAAKC,YAAL,GAAoB,CAClB,EAAEC,MAAM,aAAR,EAAwBC,OAAO,YAA/B,EADkB,EAElB,EAAED,MAAM,UAAR,EAAwBC,OAAO,UAA/B,EAFkB,CAApB;AAIA,aAAKC,gBAAL,GAAwB,CACtB,EAAEF,MAAM,KAAR,EAAmBC,OAAO,CAAC,CAAD,EAAG,CAAH,CAA1B,EADsB,EAEtB,EAAED,MAAM,IAAR,EAAmBC,OAAO,CAAC,CAAD,CAA1B,EAFsB,EAGtB,EAAED,MAAM,UAAR,EAAoBC,OAAO,CAA3B,EAHsB,CAAxB;AAKD,O","file":"options_tab.js","sourcesContent":["/**\r\n * Grafana-Zabbix\r\n * Zabbix plugin for Grafana.\r\n * http://github.com/alexanderzobnin/grafana-zabbix\r\n *\r\n * Trigger panel.\r\n * This feature sponsored by CORE IT\r\n * http://www.coreit.fr\r\n *\r\n * Copyright 2015 Alexander Zobnin alexanderzobnin@gmail.com\r\n * Licensed under the Apache License, Version 2.0\r\n */\r\n\r\nclass TriggerPanelOptionsCtrl {\r\n\r\n /** @ngInject */\r\n constructor($scope) {\r\n $scope.editor = this;\r\n this.panelCtrl = $scope.ctrl;\r\n this.panel = this.panelCtrl.panel;\r\n\r\n this.fontSizes = ['80%', '90%', '100%', '110%', '120%', '130%', '150%', '160%', '180%', '200%', '220%', '250%'];\r\n this.ackFilters = [\r\n 'all triggers',\r\n 'unacknowledged',\r\n 'acknowledged'\r\n ];\r\n this.sortByFields = [\r\n { text: 'last change', value: 'lastchange' },\r\n { text: 'severity', value: 'priority' }\r\n ];\r\n this.showEventsFields = [\r\n { text: 'All', value: [0,1] },\r\n { text: 'OK', value: [0] },\r\n { text: 'Problems', value: 1 }\r\n ];\r\n }\r\n}\r\n\r\nexport function triggerPanelOptionsTab() {\r\n return {\r\n restrict: 'E',\r\n scope: true,\r\n templateUrl: 'public/plugins/alexanderzobnin-zabbix-app/panel-triggers/partials/options_tab.html',\r\n controller: TriggerPanelOptionsCtrl,\r\n };\r\n}\r\n"]}

View File

@@ -1,97 +1,97 @@
<div class="triggers-panel-container">
<div class="triggers-panel-scroll">
<section class="card-section card-list-layout-list">
<ol class="alert-rule-list">
<!-- Trigger list item -->
<li class="alert-rule-item zbx-trigger-card" ng-repeat="trigger in ctrl.currentTriggersPage"
ng-class="{'zbx-trigger-highlighted': ctrl.panel.highlightBackground}"
ng-style="ctrl.panel.highlightBackground && {background: ctrl.getBackground(trigger)}">
<!-- Heart icon -->
<div class="alert-rule-item__icon" ng-style="!ctrl.panel.highlightBackground && {color: trigger.color}">
<i class="icon-gf" ng-class="ctrl.getAlertIconClass(trigger)"></i>
</div>
<div class="alert-rule-item__body">
<div class="alert-rule-item__header">
<p class="alert-rule-item__name">
<span class="zabbix-trigger-name">{{trigger.description}}</span>
<span class="zabbix-hostname" ng-if="ctrl.panel.hostField || ctrl.panel.hostTechNameField">
<i ng-if="trigger.maintenance" class="fa fa-wrench zbx-maintenance-icon"></i>
{{ ctrl.formatHostName(trigger) }}
</span>
<span class="zabbix-hostname" ng-if="ctrl.panel.hostGroups">
{{ ctrl.formatHostGroups(trigger) }}
</span>
<span class="zbx-trigger-tags" ng-if="ctrl.panel.showTags && trigger.tags">
<span ng-repeat="tag in trigger.tags" ng-click="ctrl.addTagFilter(tag, trigger.datasource)"
tag-color-from-name="tag.tag+tag.value" class="label label-tag zbx-tag">
{{tag.tag}}: {{tag.value}}
</span>
</span>
</p>
<div class="alert-rule-item__text">
<span ng-if="ctrl.panel.statusField" class="zbx-trigger-state"
ng-class="ctrl.getAlertStateClass(trigger)">
{{ctrl.triggerStatusMap[trigger.value]}}
</span>
<span ng-if="ctrl.panel.severityField" class="zbx-trigger-severity"
ng-class="ctrl.getAlertStateClass(trigger)"
ng-style="!ctrl.panel.highlightBackground && {color: trigger.color}">
{{trigger.severity}}
</span>
<span class="alert-rule-item__time">
{{trigger.age && "for " + trigger.age}}
</span>
<span class="zbx-description"
ng-if="ctrl.panel.descriptionField && !ctrl.panel.descriptionAtNewLine"
ng-bind-html="trigger.comments">
</span>
</div>
<!-- If description at the new line -->
<div class="alert-rule-item__text"
ng-if="trigger.comments && ctrl.panel.descriptionField && ctrl.panel.descriptionAtNewLine">
<span class="alert-rule-item__info zbx-description zbx-description--newline"
ng-bind-html="trigger.comments">
</span>
</div>
</div>
</div>
<!-- Datasource name -->
<div class="alert-rule-item__time zabbix-trigger-source" ng-if="ctrl.panel.datasources.length > 1">
<span>
<i class="fa fa-database"></i>
{{trigger.datasource}}
</span>
</div>
<div class="alert-rule-item__time zbx-trigger-lastchange">
<span>{{trigger.lastchange || "last change unknown"}}</span>
<div class="trigger-info-block zbx-status-icons">
<a ng-if="trigger.url" href="{{trigger.url}}" target="_blank">
<i class="fa fa-external-link"></i>
</a>
<span ng-if="trigger.state === '1'" bs-tooltip="'{{trigger.error}}'">
<i class="fa fa-question-circle"></i>
</span>
<ack-tooltip ng-if="trigger.lastEvent" ack="trigger.acknowledges" trigger="trigger"
on-ack="ctrl.acknowledgeTrigger" context="ctrl">
</ack-tooltip>
</div>
</div>
</li>
</ol>
</section>
</div>
</div>
<div class="triggers-panel-footer"></div>
<div class="triggers-panel-container">
<div class="triggers-panel-scroll">
<section class="card-section card-list-layout-list">
<ol class="alert-rule-list">
<!-- Trigger list item -->
<li class="alert-rule-item zbx-trigger-card" ng-repeat="trigger in ctrl.currentTriggersPage"
ng-class="{'zbx-trigger-highlighted': ctrl.panel.highlightBackground}"
ng-style="ctrl.panel.highlightBackground && {background: ctrl.getBackground(trigger)}">
<!-- Heart icon -->
<div class="alert-rule-item__icon" ng-style="!ctrl.panel.highlightBackground && {color: trigger.color}">
<i class="icon-gf" ng-class="ctrl.getAlertIconClass(trigger)"></i>
</div>
<div class="alert-rule-item__body">
<div class="alert-rule-item__header">
<p class="alert-rule-item__name">
<span class="zabbix-trigger-name">{{trigger.description}}</span>
<span class="zabbix-hostname" ng-if="ctrl.panel.hostField || ctrl.panel.hostTechNameField">
<i ng-if="trigger.maintenance" class="fa fa-wrench zbx-maintenance-icon"></i>
{{ ctrl.formatHostName(trigger) }}
</span>
<span class="zabbix-hostname" ng-if="ctrl.panel.hostGroups">
{{ ctrl.formatHostGroups(trigger) }}
</span>
<span class="zbx-trigger-tags" ng-if="ctrl.panel.showTags && trigger.tags">
<span ng-repeat="tag in trigger.tags" ng-click="ctrl.addTagFilter(tag, trigger.datasource)"
tag-color-from-name="tag.tag+tag.value" class="label label-tag zbx-tag">
{{tag.tag}}: {{tag.value}}
</span>
</span>
</p>
<div class="alert-rule-item__text">
<span ng-if="ctrl.panel.statusField" class="zbx-trigger-state"
ng-class="ctrl.getAlertStateClass(trigger)">
{{ctrl.triggerStatusMap[trigger.value]}}
</span>
<span ng-if="ctrl.panel.severityField" class="zbx-trigger-severity"
ng-class="ctrl.getAlertStateClass(trigger)"
ng-style="!ctrl.panel.highlightBackground && {color: trigger.color}">
{{trigger.severity}}
</span>
<span class="alert-rule-item__time">
{{trigger.age && "for " + trigger.age}}
</span>
<span class="zbx-description"
ng-if="ctrl.panel.descriptionField && !ctrl.panel.descriptionAtNewLine"
ng-bind-html="trigger.comments">
</span>
</div>
<!-- If description at the new line -->
<div class="alert-rule-item__text"
ng-if="trigger.comments && ctrl.panel.descriptionField && ctrl.panel.descriptionAtNewLine">
<span class="alert-rule-item__info zbx-description zbx-description--newline"
ng-bind-html="trigger.comments">
</span>
</div>
</div>
</div>
<!-- Datasource name -->
<div class="alert-rule-item__time zabbix-trigger-source" ng-if="ctrl.panel.datasources.length > 1">
<span>
<i class="fa fa-database"></i>
{{trigger.datasource}}
</span>
</div>
<div class="alert-rule-item__time zbx-trigger-lastchange">
<span>{{trigger.lastchange || "last change unknown"}}</span>
<div class="trigger-info-block zbx-status-icons">
<a ng-if="trigger.url" href="{{trigger.url}}" target="_blank">
<i class="fa fa-external-link"></i>
</a>
<span ng-if="trigger.state === '1'" bs-tooltip="'{{trigger.error}}'">
<i class="fa fa-question-circle"></i>
</span>
<ack-tooltip ng-if="trigger.lastEvent" ack="trigger.acknowledges" trigger="trigger"
on-ack="ctrl.acknowledgeTrigger" context="ctrl">
</ack-tooltip>
</div>
</div>
</li>
</ol>
</section>
</div>
</div>
<div class="triggers-panel-footer"></div>

View File

@@ -1,223 +1,223 @@
<div class="editor-row">
<div class="section gf-form-group">
<h5 class="section-heading">Show fields</h5>
<gf-form-switch class="gf-form"
label-class="width-9"
label="Host name"
checked="ctrl.panel.hostField"
on-change="ctrl.render()">
</gf-form-switch>
<gf-form-switch class="gf-form"
label-class="width-9"
label="Technical name"
checked="ctrl.panel.hostTechNameField"
on-change="ctrl.render()">
</gf-form-switch>
<gf-form-switch class="gf-form"
label-class="width-9"
label="Host groups"
checked="ctrl.panel.hostGroups"
on-change="ctrl.render()">
</gf-form-switch>
<gf-form-switch class="gf-form"
label-class="width-9"
label="Tags"
checked="ctrl.panel.showTags"
on-change="ctrl.render()">
</gf-form-switch>
<gf-form-switch class="gf-form"
label-class="width-9"
label="Status"
checked="ctrl.panel.statusField"
on-change="ctrl.render()">
</gf-form-switch>
<gf-form-switch class="gf-form"
label-class="width-9"
label="Severity"
checked="ctrl.panel.severityField"
on-change="ctrl.render()">
</gf-form-switch>
<gf-form-switch class="gf-form"
label-class="width-9"
label="Description"
checked="ctrl.panel.descriptionField"
on-change="ctrl.render()">
</gf-form-switch>
<gf-form-switch class="gf-form" ng-if="ctrl.panel.descriptionField"
label-class="width-9"
label="At the new line"
checked="ctrl.panel.descriptionAtNewLine"
on-change="ctrl.render()">
</gf-form-switch>
</div>
<div class="section gf-form-group">
<h5 class="section-heading">Options</h5>
<gf-form-switch class="gf-form"
label-class="width-15"
label="Show hosts in maintenance"
checked="ctrl.panel.hostsInMaintenance"
on-change="ctrl.render()">
</gf-form-switch>
<div class="gf-form">
<label class="gf-form-label width-8">Acknowledged</label>
<div class="gf-form-select-wrapper width-12">
<select class="gf-form-input"
ng-model="ctrl.panel.showTriggers"
ng-options="f for f in editor.ackFilters"
ng-change="ctrl.refresh()">
</select>
</div>
</div>
<div class="gf-form">
<label class="gf-form-label width-8">Sort by</label>
<div class="gf-form-select-wrapper width-12">
<select class="gf-form-input"
ng-model="ctrl.panel.sortTriggersBy"
ng-options="f.text for f in editor.sortByFields track by f.value"
ng-change="ctrl.render()">
</select>
</div>
</div>
<div class="gf-form">
<label class="gf-form-label width-8">Show events</label>
<div class="gf-form-select-wrapper width-12">
<select class="gf-form-input"
ng-model="ctrl.panel.showEvents"
ng-options="f.text for f in editor.showEventsFields track by f.value"
ng-change="ctrl.refresh()">
</select>
</div>
</div>
<div class="gf-form">
<label class="gf-form-label width-8">Limit triggers</label>
<input class="gf-form-input width-5"
type="number" placeholder="100"
ng-model="ctrl.panel.limit"
ng-model-onblur ng-change="ctrl.refresh()">
</div>
</div>
<div class="section gf-form-group">
<h5 class="section-heading">View options</h5>
<div class="gf-form">
<label class="gf-form-label width-10">Font size</label>
<div class="gf-form-select-wrapper max-width-8">
<select class="gf-form-input"
ng-model="ctrl.panel.fontSize"
ng-options="f for f in editor.fontSizes"
ng-change="ctrl.render()"></select>
</div>
</div>
<div class="gf-form">
<label class="gf-form-label width-10">Page size</label>
<input class="gf-form-input width-8"
type="number" placeholder="10"
ng-model="ctrl.panel.pageSize"
ng-model-onblur ng-change="ctrl.render()">
</div>
<gf-form-switch class="gf-form"
label-class="width-10"
label="Highlight background"
checked="ctrl.panel.highlightBackground"
on-change="ctrl.render()">
</gf-form-switch>
<gf-form-switch class="gf-form"
label-class="width-10"
label="Highlight new events"
checked="ctrl.panel.highlightNewEvents"
on-change="ctrl.render()">
</gf-form-switch>
<div class="gf-form">
<label class="gf-form-label width-10">Newer than</label>
<input class="gf-form-input width-8" placeholder="1h"
ng-model="ctrl.panel.highlightNewerThan"
ng-model-onblur ng-change="ctrl.render()">
</div>
<gf-form-switch class="gf-form"
label-class="width-16"
label="Custom Last change format"
checked="ctrl.panel.customLastChangeFormat"
on-change="ctrl.render()">
</gf-form-switch>
<div class="gf-form" ng-if="ctrl.panel.customLastChangeFormat">
<label class="gf-form-label width-3">
<a href="http://momentjs.com/docs/#/displaying/format/" target="_blank">
<tip>See moment.js dosc for time format.</tip>
</a>
</label>
<input class="gf-form-input width-18"
type="text"
placeholder="dddd, MMMM Do YYYY, h:mm:ss a"
empty-to-null
ng-model-onblur
ng-model="ctrl.panel.lastChangeFormat"
ng-change="ctrl.render()">
</div>
</div>
<div class="section gf-form-group">
<h5 class="section-heading">Triggers severity and colors</h5>
<div class="gf-form-inline" ng-repeat="trigger in ctrl.panel.triggerSeverity">
<div class="gf-form">
<label class="gf-form-label width-3">{{ trigger.priority }}</label>
<label class="gf-form-label triggers-severity-config"
ng-style="{color: trigger.color}">
<i class="icon-gf" ng-class="ctrl.getAlertIconClassBySeverity(trigger)"></i>
</label>
<input type="text"
class="gf-form-input width-12"
empty-to-null
ng-model="trigger.severity"
ng-model-onblur
ng-change="ctrl.render()">
<span class="gf-form-label">
<spectrum-picker ng-model="trigger.color" ng-change="ctrl.render()"></spectrum-picker>
</span>
</div>
<gf-form-switch class="gf-form"
label-class="width-0"
label="Show"
checked="trigger.show"
on-change="ctrl.refresh()">
</gf-form-switch>
</div>
<div class="gf-form-inline">
<div class="gf-form">
<label class="gf-form-label width-3">&nbsp;</label>
<label class="gf-form-label triggers-severity-config"
ng-style="{color: ctrl.panel.ackEventColor}">
<i class="icon-gf icon-gf-online"></i>
</label>
<label class="gf-form-label width-12">
Acknowledged color
</label>
<span class="gf-form-label">
<spectrum-picker ng-model="ctrl.panel.ackEventColor" ng-change="ctrl.render()"></spectrum-picker>
</span>
</div>
<gf-form-switch class="gf-form"
label-class="width-0"
label="Show"
checked="ctrl.panel.markAckEvents"
on-change="ctrl.refresh()">
</gf-form-switch>
</div>
<div class="gf-form-inline">
<div class="gf-form">
<label class="gf-form-label width-3">&nbsp;</label>
<label class="gf-form-label triggers-severity-config"
ng-style="{color: ctrl.panel.okEventColor}">
<i class="icon-gf icon-gf-online"></i>
</label>
<label class="gf-form-label width-12">
OK event color
</label>
<span class="gf-form-label">
<spectrum-picker ng-model="ctrl.panel.okEventColor" ng-change="ctrl.render()"></spectrum-picker>
</span>
</div>
</div>
</div>
</div>
<div class="editor-row">
<div class="section gf-form-group">
<h5 class="section-heading">Show fields</h5>
<gf-form-switch class="gf-form"
label-class="width-9"
label="Host name"
checked="ctrl.panel.hostField"
on-change="ctrl.render()">
</gf-form-switch>
<gf-form-switch class="gf-form"
label-class="width-9"
label="Technical name"
checked="ctrl.panel.hostTechNameField"
on-change="ctrl.render()">
</gf-form-switch>
<gf-form-switch class="gf-form"
label-class="width-9"
label="Host groups"
checked="ctrl.panel.hostGroups"
on-change="ctrl.render()">
</gf-form-switch>
<gf-form-switch class="gf-form"
label-class="width-9"
label="Tags"
checked="ctrl.panel.showTags"
on-change="ctrl.render()">
</gf-form-switch>
<gf-form-switch class="gf-form"
label-class="width-9"
label="Status"
checked="ctrl.panel.statusField"
on-change="ctrl.render()">
</gf-form-switch>
<gf-form-switch class="gf-form"
label-class="width-9"
label="Severity"
checked="ctrl.panel.severityField"
on-change="ctrl.render()">
</gf-form-switch>
<gf-form-switch class="gf-form"
label-class="width-9"
label="Description"
checked="ctrl.panel.descriptionField"
on-change="ctrl.render()">
</gf-form-switch>
<gf-form-switch class="gf-form" ng-if="ctrl.panel.descriptionField"
label-class="width-9"
label="At the new line"
checked="ctrl.panel.descriptionAtNewLine"
on-change="ctrl.render()">
</gf-form-switch>
</div>
<div class="section gf-form-group">
<h5 class="section-heading">Options</h5>
<gf-form-switch class="gf-form"
label-class="width-15"
label="Show hosts in maintenance"
checked="ctrl.panel.hostsInMaintenance"
on-change="ctrl.render()">
</gf-form-switch>
<div class="gf-form">
<label class="gf-form-label width-8">Acknowledged</label>
<div class="gf-form-select-wrapper width-12">
<select class="gf-form-input"
ng-model="ctrl.panel.showTriggers"
ng-options="f for f in editor.ackFilters"
ng-change="ctrl.refresh()">
</select>
</div>
</div>
<div class="gf-form">
<label class="gf-form-label width-8">Sort by</label>
<div class="gf-form-select-wrapper width-12">
<select class="gf-form-input"
ng-model="ctrl.panel.sortTriggersBy"
ng-options="f.text for f in editor.sortByFields track by f.value"
ng-change="ctrl.render()">
</select>
</div>
</div>
<div class="gf-form">
<label class="gf-form-label width-8">Show events</label>
<div class="gf-form-select-wrapper width-12">
<select class="gf-form-input"
ng-model="ctrl.panel.showEvents"
ng-options="f.text for f in editor.showEventsFields track by f.value"
ng-change="ctrl.refresh()">
</select>
</div>
</div>
<div class="gf-form">
<label class="gf-form-label width-8">Limit triggers</label>
<input class="gf-form-input width-5"
type="number" placeholder="100"
ng-model="ctrl.panel.limit"
ng-model-onblur ng-change="ctrl.refresh()">
</div>
</div>
<div class="section gf-form-group">
<h5 class="section-heading">View options</h5>
<div class="gf-form">
<label class="gf-form-label width-10">Font size</label>
<div class="gf-form-select-wrapper max-width-8">
<select class="gf-form-input"
ng-model="ctrl.panel.fontSize"
ng-options="f for f in editor.fontSizes"
ng-change="ctrl.render()"></select>
</div>
</div>
<div class="gf-form">
<label class="gf-form-label width-10">Page size</label>
<input class="gf-form-input width-8"
type="number" placeholder="10"
ng-model="ctrl.panel.pageSize"
ng-model-onblur ng-change="ctrl.render()">
</div>
<gf-form-switch class="gf-form"
label-class="width-10"
label="Highlight background"
checked="ctrl.panel.highlightBackground"
on-change="ctrl.render()">
</gf-form-switch>
<gf-form-switch class="gf-form"
label-class="width-10"
label="Highlight new events"
checked="ctrl.panel.highlightNewEvents"
on-change="ctrl.render()">
</gf-form-switch>
<div class="gf-form">
<label class="gf-form-label width-10">Newer than</label>
<input class="gf-form-input width-8" placeholder="1h"
ng-model="ctrl.panel.highlightNewerThan"
ng-model-onblur ng-change="ctrl.render()">
</div>
<gf-form-switch class="gf-form"
label-class="width-16"
label="Custom Last change format"
checked="ctrl.panel.customLastChangeFormat"
on-change="ctrl.render()">
</gf-form-switch>
<div class="gf-form" ng-if="ctrl.panel.customLastChangeFormat">
<label class="gf-form-label width-3">
<a href="http://momentjs.com/docs/#/displaying/format/" target="_blank">
<tip>See moment.js dosc for time format.</tip>
</a>
</label>
<input class="gf-form-input width-18"
type="text"
placeholder="dddd, MMMM Do YYYY, h:mm:ss a"
empty-to-null
ng-model-onblur
ng-model="ctrl.panel.lastChangeFormat"
ng-change="ctrl.render()">
</div>
</div>
<div class="section gf-form-group">
<h5 class="section-heading">Triggers severity and colors</h5>
<div class="gf-form-inline" ng-repeat="trigger in ctrl.panel.triggerSeverity">
<div class="gf-form">
<label class="gf-form-label width-3">{{ trigger.priority }}</label>
<label class="gf-form-label triggers-severity-config"
ng-style="{color: trigger.color}">
<i class="icon-gf" ng-class="ctrl.getAlertIconClassBySeverity(trigger)"></i>
</label>
<input type="text"
class="gf-form-input width-12"
empty-to-null
ng-model="trigger.severity"
ng-model-onblur
ng-change="ctrl.render()">
<span class="gf-form-label">
<spectrum-picker ng-model="trigger.color" ng-change="ctrl.render()"></spectrum-picker>
</span>
</div>
<gf-form-switch class="gf-form"
label-class="width-0"
label="Show"
checked="trigger.show"
on-change="ctrl.refresh()">
</gf-form-switch>
</div>
<div class="gf-form-inline">
<div class="gf-form">
<label class="gf-form-label width-3">&nbsp;</label>
<label class="gf-form-label triggers-severity-config"
ng-style="{color: ctrl.panel.ackEventColor}">
<i class="icon-gf icon-gf-online"></i>
</label>
<label class="gf-form-label width-12">
Acknowledged color
</label>
<span class="gf-form-label">
<spectrum-picker ng-model="ctrl.panel.ackEventColor" ng-change="ctrl.render()"></spectrum-picker>
</span>
</div>
<gf-form-switch class="gf-form"
label-class="width-0"
label="Show"
checked="ctrl.panel.markAckEvents"
on-change="ctrl.refresh()">
</gf-form-switch>
</div>
<div class="gf-form-inline">
<div class="gf-form">
<label class="gf-form-label width-3">&nbsp;</label>
<label class="gf-form-label triggers-severity-config"
ng-style="{color: ctrl.panel.okEventColor}">
<i class="icon-gf icon-gf-online"></i>
</label>
<label class="gf-form-label width-12">
OK event color
</label>
<span class="gf-form-label">
<spectrum-picker ng-model="ctrl.panel.okEventColor" ng-change="ctrl.render()"></spectrum-picker>
</span>
</div>
</div>
</div>
</div>

View File

@@ -1,90 +1,90 @@
<div class="editor-row">
<div class="section gf-form-group">
<div class="gf-form-inline">
<div class="gf-form">
<label class="gf-form-label width-9">Data sources</label>
</div>
<div class="gf-form">
<datasource-selector
datasources="ctrl.panel.datasources"
options="editor.panelCtrl.available_datasources"
on-change="editor.datasourcesChanged()">
</datasource-selector>
</div>
</div>
</div>
</div>
<div class="editor-row" ng-repeat="ds in ctrl.panel.datasources">
<div class="section gf-form-group">
<h5 class="section-heading">{{ ds }}</h5>
<div class="gf-form-inline">
<div class="gf-form max-width-20">
<label class="gf-form-label query-keyword width-7">Group</label>
<input type="text"
ng-model="ctrl.panel.targets[ds].group.filter"
bs-typeahead="editor.getGroupNames[ds]"
ng-blur="editor.parseTarget()"
data-min-length=0
data-items=100
class="gf-form-input"
ng-class="{
'zbx-variable': editor.isVariable(ctrl.panel.targets[ds].group.filter),
'zbx-regex': editor.isRegex(ctrl.panel.targets[ds].group.filter)
}">
</div>
<div class="gf-form">
<label class="gf-form-label query-keyword width-7">Host</label>
<input type="text"
ng-model="ctrl.panel.targets[ds].host.filter"
bs-typeahead="editor.getHostNames[ds]"
ng-blur="editor.parseTarget()"
data-min-length=0
data-items=100
class="gf-form-input"
ng-class="{
'zbx-variable': editor.isVariable(ctrl.panel.targets[ds].host.filter),
'zbx-regex': editor.isRegex(ctrl.panel.targets[ds].host.filter)
}">
</div>
</div>
<div class="gf-form-inline">
<div class="gf-form max-width-20">
<label class="gf-form-label query-keyword width-7">Application</label>
<input type="text"
ng-model="ctrl.panel.targets[ds].application.filter"
bs-typeahead="editor.getApplicationNames[ds]"
ng-blur="editor.parseTarget()"
data-min-length=0
data-items=100
class="gf-form-input"
ng-class="{
'zbx-variable': editor.isVariable(ctrl.panel.targets[ds].application.filter),
'zbx-regex': editor.isRegex(ctrl.panel.targets[ds].application.filter)
}">
</div>
<div class="gf-form">
<label class="gf-form-label query-keyword width-7">Trigger</label>
<input type="text"
ng-model="ctrl.panel.targets[ds].trigger.filter"
ng-blur="editor.parseTarget()"
placeholder="trigger name"
class="gf-form-input"
ng-style="ctrl.panel.targets[ds].trigger.style"
ng-class="{
'zbx-variable': editor.isVariable(ctrl.panel.targets[ds].trigger.filter),
'zbx-regex': editor.isRegex(ctrl.panel.targets[ds].trigger.filter)
}"
empty-to-null>
</div>
<div class="gf-form">
<label class="gf-form-label query-keyword width-7">Tags</label>
<input type="text" class="gf-form-input"
ng-model="ctrl.panel.targets[ds].tags.filter"
ng-blur="editor.parseTarget()"
placeholder="tag1:value1, tag2:value2">
</div>
</div>
</div>
</div>
<div class="editor-row">
<div class="section gf-form-group">
<div class="gf-form-inline">
<div class="gf-form">
<label class="gf-form-label width-9">Data sources</label>
</div>
<div class="gf-form">
<datasource-selector
datasources="ctrl.panel.datasources"
options="editor.panelCtrl.available_datasources"
on-change="editor.datasourcesChanged()">
</datasource-selector>
</div>
</div>
</div>
</div>
<div class="editor-row" ng-repeat="ds in ctrl.panel.datasources">
<div class="section gf-form-group">
<h5 class="section-heading">{{ ds }}</h5>
<div class="gf-form-inline">
<div class="gf-form max-width-20">
<label class="gf-form-label query-keyword width-7">Group</label>
<input type="text"
ng-model="ctrl.panel.targets[ds].group.filter"
bs-typeahead="editor.getGroupNames[ds]"
ng-blur="editor.parseTarget()"
data-min-length=0
data-items=100
class="gf-form-input"
ng-class="{
'zbx-variable': editor.isVariable(ctrl.panel.targets[ds].group.filter),
'zbx-regex': editor.isRegex(ctrl.panel.targets[ds].group.filter)
}">
</div>
<div class="gf-form">
<label class="gf-form-label query-keyword width-7">Host</label>
<input type="text"
ng-model="ctrl.panel.targets[ds].host.filter"
bs-typeahead="editor.getHostNames[ds]"
ng-blur="editor.parseTarget()"
data-min-length=0
data-items=100
class="gf-form-input"
ng-class="{
'zbx-variable': editor.isVariable(ctrl.panel.targets[ds].host.filter),
'zbx-regex': editor.isRegex(ctrl.panel.targets[ds].host.filter)
}">
</div>
</div>
<div class="gf-form-inline">
<div class="gf-form max-width-20">
<label class="gf-form-label query-keyword width-7">Application</label>
<input type="text"
ng-model="ctrl.panel.targets[ds].application.filter"
bs-typeahead="editor.getApplicationNames[ds]"
ng-blur="editor.parseTarget()"
data-min-length=0
data-items=100
class="gf-form-input"
ng-class="{
'zbx-variable': editor.isVariable(ctrl.panel.targets[ds].application.filter),
'zbx-regex': editor.isRegex(ctrl.panel.targets[ds].application.filter)
}">
</div>
<div class="gf-form">
<label class="gf-form-label query-keyword width-7">Trigger</label>
<input type="text"
ng-model="ctrl.panel.targets[ds].trigger.filter"
ng-blur="editor.parseTarget()"
placeholder="trigger name"
class="gf-form-input"
ng-style="ctrl.panel.targets[ds].trigger.style"
ng-class="{
'zbx-variable': editor.isVariable(ctrl.panel.targets[ds].trigger.filter),
'zbx-regex': editor.isRegex(ctrl.panel.targets[ds].trigger.filter)
}"
empty-to-null>
</div>
<div class="gf-form">
<label class="gf-form-label query-keyword width-7">Tags</label>
<input type="text" class="gf-form-input"
ng-model="ctrl.panel.targets[ds].tags.filter"
ng-blur="editor.parseTarget()"
placeholder="tag1:value1, tag2:value2">
</div>
</div>
</div>
</div>

View File

@@ -1,12 +1,12 @@
{
"type": "panel",
"name": "Zabbix Triggers",
"id": "alexanderzobnin-zabbix-triggers-panel",
"info": {
"author": {
"name": "Alexander Zobnin",
"url": "https://github.com/alexanderzobnin/grafana-zabbix"
}
}
}
{
"type": "panel",
"name": "Zabbix Triggers",
"id": "alexanderzobnin-zabbix-triggers-panel",
"info": {
"author": {
"name": "Alexander Zobnin",
"url": "https://github.com/alexanderzobnin/grafana-zabbix"
}
}
}

View File

@@ -1,77 +1,77 @@
import _ from 'lodash';
import {TriggerPanelCtrl} from '../triggers_panel_ctrl';
import {DEFAULT_TARGET, DEFAULT_SEVERITY, PANEL_DEFAULTS} from '../triggers_panel_ctrl';
import {CURRENT_SCHEMA_VERSION} from '../migrations';
describe('Triggers Panel schema migration', () => {
let ctx = {};
let datasourceSrvMock = {
getMetricSources: () => {
return [{ meta: {id: 'alexanderzobnin-zabbix-datasource'}, value: {}, name: 'zabbix_default' }];
},
get: () => Promise.resolve({})
};
let timeoutMock = () => {};
beforeEach(() => {
ctx = {
scope: {
panel: {
datasource: 'zabbix',
triggers: DEFAULT_TARGET,
hostField: true,
statusField: false,
severityField: false,
lastChangeField: true,
ageField: true,
infoField: true,
limit: 10,
showTriggers: 'all triggers',
hideHostsInMaintenance: false,
sortTriggersBy: { text: 'last change', value: 'lastchange' },
showEvents: { text: 'Problems', value: '1' },
triggerSeverity: DEFAULT_SEVERITY,
okEventColor: 'rgba(0, 245, 153, 0.45)',
ackEventColor: 'rgba(0, 0, 0, 0)',
scroll: true,
pageSize: 10,
fontSize: '100%',
}
}
};
});
it('should update old panel schema', () => {
let updatedPanelCtrl = new TriggerPanelCtrl(ctx.scope, {}, timeoutMock, datasourceSrvMock, {}, {}, {});
let expected = _.defaultsDeep({
schemaVersion: CURRENT_SCHEMA_VERSION,
datasources: ['zabbix'],
targets: {
'zabbix': DEFAULT_TARGET
},
statusField: false,
severityField: false,
limit: 10,
okEventColor: 'rgba(0, 245, 153, 0.45)',
ackEventColor: 'rgba(0, 0, 0, 0)'
}, PANEL_DEFAULTS);
expect(updatedPanelCtrl.panel).toEqual(expected);
});
it('should create new panel with default schema', () => {
ctx.scope.panel = {};
let updatedPanelCtrl = new TriggerPanelCtrl(ctx.scope, {}, {}, datasourceSrvMock, {}, {}, {});
let expected = _.defaultsDeep({
schemaVersion: CURRENT_SCHEMA_VERSION,
datasources: ['zabbix_default'],
targets: {
'zabbix_default': DEFAULT_TARGET
}
}, PANEL_DEFAULTS);
expect(updatedPanelCtrl.panel).toEqual(expected);
});
});
import _ from 'lodash';
import {TriggerPanelCtrl} from '../triggers_panel_ctrl';
import {DEFAULT_TARGET, DEFAULT_SEVERITY, PANEL_DEFAULTS} from '../triggers_panel_ctrl';
import {CURRENT_SCHEMA_VERSION} from '../migrations';
describe('Triggers Panel schema migration', () => {
let ctx = {};
let datasourceSrvMock = {
getMetricSources: () => {
return [{ meta: {id: 'alexanderzobnin-zabbix-datasource'}, value: {}, name: 'zabbix_default' }];
},
get: () => Promise.resolve({})
};
let timeoutMock = () => {};
beforeEach(() => {
ctx = {
scope: {
panel: {
datasource: 'zabbix',
triggers: DEFAULT_TARGET,
hostField: true,
statusField: false,
severityField: false,
lastChangeField: true,
ageField: true,
infoField: true,
limit: 10,
showTriggers: 'all triggers',
hideHostsInMaintenance: false,
sortTriggersBy: { text: 'last change', value: 'lastchange' },
showEvents: { text: 'Problems', value: '1' },
triggerSeverity: DEFAULT_SEVERITY,
okEventColor: 'rgba(0, 245, 153, 0.45)',
ackEventColor: 'rgba(0, 0, 0, 0)',
scroll: true,
pageSize: 10,
fontSize: '100%',
}
}
};
});
it('should update old panel schema', () => {
let updatedPanelCtrl = new TriggerPanelCtrl(ctx.scope, {}, timeoutMock, datasourceSrvMock, {}, {}, {});
let expected = _.defaultsDeep({
schemaVersion: CURRENT_SCHEMA_VERSION,
datasources: ['zabbix'],
targets: {
'zabbix': DEFAULT_TARGET
},
statusField: false,
severityField: false,
limit: 10,
okEventColor: 'rgba(0, 245, 153, 0.45)',
ackEventColor: 'rgba(0, 0, 0, 0)'
}, PANEL_DEFAULTS);
expect(updatedPanelCtrl.panel).toEqual(expected);
});
it('should create new panel with default schema', () => {
ctx.scope.panel = {};
let updatedPanelCtrl = new TriggerPanelCtrl(ctx.scope, {}, {}, datasourceSrvMock, {}, {}, {});
let expected = _.defaultsDeep({
schemaVersion: CURRENT_SCHEMA_VERSION,
datasources: ['zabbix_default'],
targets: {
'zabbix_default': DEFAULT_TARGET
}
}, PANEL_DEFAULTS);
expect(updatedPanelCtrl.panel).toEqual(expected);
});
});

View File

@@ -1,283 +1,283 @@
import _ from 'lodash';
import {TriggerPanelCtrl} from '../triggers_panel_ctrl';
import {PANEL_DEFAULTS, DEFAULT_TARGET} from '../triggers_panel_ctrl';
// import { create } from 'domain';
describe('TriggerPanelCtrl', () => {
let ctx = {};
let datasourceSrvMock, zabbixDSMock;
let timeoutMock = () => {};
let createPanelCtrl;
beforeEach(() => {
ctx = {scope: {panel: PANEL_DEFAULTS}};
zabbixDSMock = {
replaceTemplateVars: () => {},
zabbix: {
getTriggers: jest.fn().mockReturnValue([generateTrigger("1"), generateTrigger("1")]),
getAcknowledges: jest.fn().mockReturnValue(Promise.resolve([]))
}
};
datasourceSrvMock = {
getMetricSources: () => {
return [
{ meta: {id: 'alexanderzobnin-zabbix-datasource'}, value: {}, name: 'zabbix_default' },
{ meta: {id: 'alexanderzobnin-zabbix-datasource'}, value: {}, name: 'zabbix' },
{ meta: {id: 'graphite'}, value: {}, name: 'graphite' },
];
},
get: () => Promise.resolve(zabbixDSMock)
};
createPanelCtrl = () => new TriggerPanelCtrl(ctx.scope, {}, timeoutMock, datasourceSrvMock, {}, {}, {});
const getTriggersResp = [
[
createTrigger({
triggerid: "1", lastchange: "1510000010", priority: 5, lastEvent: {eventid: "11"}, hosts: [{maintenance_status: '1'}]
}),
createTrigger({
triggerid: "2", lastchange: "1510000040", priority: 3, lastEvent: {eventid: "12"}
}),
],
[
createTrigger({triggerid: "3", lastchange: "1510000020", priority: 4, lastEvent: {eventid: "13"}}),
createTrigger({triggerid: "4", lastchange: "1510000030", priority: 2, lastEvent: {eventid: "14"}}),
]
];
// Simulate 2 data sources
zabbixDSMock.zabbix.getTriggers = jest.fn()
.mockReturnValueOnce(getTriggersResp[0])
.mockReturnValueOnce(getTriggersResp[1]);
zabbixDSMock.zabbix.getAcknowledges = jest.fn()
.mockReturnValue(Promise.resolve([defaultEvent]));
ctx.panelCtrl = createPanelCtrl();
});
describe('When adding new panel', () => {
it('should suggest all zabbix data sources', () => {
ctx.scope.panel = {};
let panelCtrl = createPanelCtrl();
expect(panelCtrl.available_datasources).toEqual([
'zabbix_default', 'zabbix'
]);
});
it('should load first zabbix data source as default', () => {
ctx.scope.panel = {};
let panelCtrl = createPanelCtrl();
expect(panelCtrl.panel.datasources).toEqual([
'zabbix_default'
]);
});
});
describe('When refreshing panel', () => {
beforeEach(() => {
ctx.scope.panel.datasources = ['zabbix_default', 'zabbix'];
ctx.scope.panel.targets = {
'zabbix_default': DEFAULT_TARGET,
'zabbix': DEFAULT_TARGET
};
ctx.panelCtrl = createPanelCtrl();
});
it('should format triggers', (done) => {
ctx.panelCtrl.onRefresh().then(() => {
let formattedTrigger = _.find(ctx.panelCtrl.triggerList, {triggerid: "1"});
expect(formattedTrigger.host).toBe('backend01');
expect(formattedTrigger.hostTechName).toBe('backend01_tech');
expect(formattedTrigger.datasource).toBe('zabbix_default');
expect(formattedTrigger.severity).toBe('Disaster');
expect(formattedTrigger.maintenance).toBe(true);
expect(formattedTrigger.age).toBeTruthy();
expect(formattedTrigger.lastchange).toBeTruthy();
done();
});
});
it('should sort triggers by time by default', (done) => {
ctx.panelCtrl.onRefresh().then(() => {
let trigger_ids = _.map(ctx.panelCtrl.triggerList, 'triggerid');
expect(trigger_ids).toEqual([
'2', '4', '3', '1'
]);
done();
});
});
it('should sort triggers by severity', (done) => {
ctx.panelCtrl.panel.sortTriggersBy = { text: 'severity', value: 'priority' };
ctx.panelCtrl.onRefresh().then(() => {
let trigger_ids = _.map(ctx.panelCtrl.triggerList, 'triggerid');
expect(trigger_ids).toEqual([
'1', '3', '2', '4'
]);
done();
});
});
it('should add acknowledges to trigger', (done) => {
ctx.panelCtrl.onRefresh().then(() => {
let trigger = getTriggerById(1, ctx);
expect(trigger.acknowledges).toHaveLength(1);
expect(trigger.acknowledges[0].message).toBe("event ack");
expect(getTriggerById(2, ctx).acknowledges).toBe(undefined);
expect(getTriggerById(3, ctx).acknowledges).toBe(undefined);
expect(getTriggerById(4, ctx).acknowledges).toBe(undefined);
done();
});
});
});
describe('When formatting triggers', () => {
beforeEach(() => {
ctx.panelCtrl = createPanelCtrl();
});
it('should handle new lines in trigger description', () => {
ctx.panelCtrl.setTriggerSeverity = jest.fn((trigger) => trigger);
let trigger = {comments: "this is\ndescription"};
const formattedTrigger = ctx.panelCtrl.formatTrigger(trigger);
expect(formattedTrigger.comments).toBe("this is<br>description");
});
it('should format host name to display (default)', (done) => {
ctx.panelCtrl.onRefresh().then(() => {
let trigger = getTriggerById(1, ctx);
let hostname = ctx.panelCtrl.formatHostName(trigger);
expect(hostname).toBe('backend01');
done();
});
});
it('should format host name to display (tech name)', (done) => {
ctx.panelCtrl.panel.hostField = false;
ctx.panelCtrl.panel.hostTechNameField = true;
ctx.panelCtrl.onRefresh().then(() => {
let trigger = getTriggerById(1, ctx);
let hostname = ctx.panelCtrl.formatHostName(trigger);
expect(hostname).toBe('backend01_tech');
done();
});
});
it('should format host name to display (both tech and visible)', (done) => {
ctx.panelCtrl.panel.hostField = true;
ctx.panelCtrl.panel.hostTechNameField = true;
ctx.panelCtrl.onRefresh().then(() => {
let trigger = getTriggerById(1, ctx);
let hostname = ctx.panelCtrl.formatHostName(trigger);
expect(hostname).toBe('backend01 (backend01_tech)');
done();
});
});
it('should hide hostname if both visible and tech name checkboxes unset', (done) => {
ctx.panelCtrl.panel.hostField = false;
ctx.panelCtrl.panel.hostTechNameField = false;
ctx.panelCtrl.onRefresh().then(() => {
let trigger = getTriggerById(1, ctx);
let hostname = ctx.panelCtrl.formatHostName(trigger);
expect(hostname).toBe("");
done();
});
});
});
describe('When formatting acknowledges', () => {
beforeEach(() => {
ctx.panelCtrl = createPanelCtrl();
});
it('should build proper user name', () => {
const ack = {
alias: 'alias', name: 'name', surname: 'surname'
};
const formatted = ctx.panelCtrl.formatAcknowledge(ack);
expect(formatted.user).toBe('alias (name surname)');
});
it('should return empty name if it is not defined', () => {
const formatted = ctx.panelCtrl.formatAcknowledge({});
expect(formatted.user).toBe('');
});
});
});
const defaultTrigger = {
"triggerid": "13565",
"value": "1",
"groups": [{"groupid": "1", "name": "Backend"}] ,
"hosts": [{"host": "backend01_tech", "hostid": "10001","maintenance_status": "0", "name": "backend01"}] ,
"lastEvent": {
"eventid": "11",
"clock": "1507229064",
"ns": "556202037",
"acknowledged": "1",
"value": "1",
"object": "0",
"source": "0",
"objectid": "13565",
},
"tags": [] ,
"lastchange": "1440259530",
"priority": "2",
"description": "Lack of free swap space on server",
"comments": "It probably means that the systems requires\nmore physical memory.",
"url": "https://host.local/path",
"templateid": "0", "expression": "{13174}<50", "manual_close": "0", "correlation_mode": "0",
"correlation_tag": "", "recovery_mode": "0", "recovery_expression": "", "state": "0", "status": "0",
"flags": "0", "type": "0", "items": [] , "error": ""
};
const defaultEvent = {
"eventid": "11",
"acknowledges": [
{
"acknowledgeid": "185",
"action": "0",
"alias": "api",
"clock": "1512382246",
"eventid": "11",
"message": "event ack",
"name": "api",
"surname": "user",
"userid": "3"
}
],
"clock": "1507229064",
"ns": "556202037",
"acknowledged": "1",
"value": "1",
"object": "0",
"source": "0",
"objectid": "1",
};
function generateTrigger(id, timestamp, severity) {
let trigger = _.cloneDeep(defaultTrigger);
trigger.triggerid = id.toString();
if (severity) {
trigger.priority = severity.toString();
}
if (timestamp) {
trigger.lastchange = timestamp;
}
return trigger;
}
function createTrigger(props) {
let trigger = _.cloneDeep(defaultTrigger);
trigger = _.merge(trigger, props);
trigger.lastEvent.objectid = trigger.triggerid;
return trigger;
}
function getTriggerById(id, ctx) {
return _.find(ctx.panelCtrl.triggerList, {triggerid: id.toString()});
}
import _ from 'lodash';
import {TriggerPanelCtrl} from '../triggers_panel_ctrl';
import {PANEL_DEFAULTS, DEFAULT_TARGET} from '../triggers_panel_ctrl';
// import { create } from 'domain';
describe('TriggerPanelCtrl', () => {
let ctx = {};
let datasourceSrvMock, zabbixDSMock;
let timeoutMock = () => {};
let createPanelCtrl;
beforeEach(() => {
ctx = {scope: {panel: PANEL_DEFAULTS}};
zabbixDSMock = {
replaceTemplateVars: () => {},
zabbix: {
getTriggers: jest.fn().mockReturnValue([generateTrigger("1"), generateTrigger("1")]),
getAcknowledges: jest.fn().mockReturnValue(Promise.resolve([]))
}
};
datasourceSrvMock = {
getMetricSources: () => {
return [
{ meta: {id: 'alexanderzobnin-zabbix-datasource'}, value: {}, name: 'zabbix_default' },
{ meta: {id: 'alexanderzobnin-zabbix-datasource'}, value: {}, name: 'zabbix' },
{ meta: {id: 'graphite'}, value: {}, name: 'graphite' },
];
},
get: () => Promise.resolve(zabbixDSMock)
};
createPanelCtrl = () => new TriggerPanelCtrl(ctx.scope, {}, timeoutMock, datasourceSrvMock, {}, {}, {});
const getTriggersResp = [
[
createTrigger({
triggerid: "1", lastchange: "1510000010", priority: 5, lastEvent: {eventid: "11"}, hosts: [{maintenance_status: '1'}]
}),
createTrigger({
triggerid: "2", lastchange: "1510000040", priority: 3, lastEvent: {eventid: "12"}
}),
],
[
createTrigger({triggerid: "3", lastchange: "1510000020", priority: 4, lastEvent: {eventid: "13"}}),
createTrigger({triggerid: "4", lastchange: "1510000030", priority: 2, lastEvent: {eventid: "14"}}),
]
];
// Simulate 2 data sources
zabbixDSMock.zabbix.getTriggers = jest.fn()
.mockReturnValueOnce(getTriggersResp[0])
.mockReturnValueOnce(getTriggersResp[1]);
zabbixDSMock.zabbix.getAcknowledges = jest.fn()
.mockReturnValue(Promise.resolve([defaultEvent]));
ctx.panelCtrl = createPanelCtrl();
});
describe('When adding new panel', () => {
it('should suggest all zabbix data sources', () => {
ctx.scope.panel = {};
let panelCtrl = createPanelCtrl();
expect(panelCtrl.available_datasources).toEqual([
'zabbix_default', 'zabbix'
]);
});
it('should load first zabbix data source as default', () => {
ctx.scope.panel = {};
let panelCtrl = createPanelCtrl();
expect(panelCtrl.panel.datasources).toEqual([
'zabbix_default'
]);
});
});
describe('When refreshing panel', () => {
beforeEach(() => {
ctx.scope.panel.datasources = ['zabbix_default', 'zabbix'];
ctx.scope.panel.targets = {
'zabbix_default': DEFAULT_TARGET,
'zabbix': DEFAULT_TARGET
};
ctx.panelCtrl = createPanelCtrl();
});
it('should format triggers', (done) => {
ctx.panelCtrl.onRefresh().then(() => {
let formattedTrigger = _.find(ctx.panelCtrl.triggerList, {triggerid: "1"});
expect(formattedTrigger.host).toBe('backend01');
expect(formattedTrigger.hostTechName).toBe('backend01_tech');
expect(formattedTrigger.datasource).toBe('zabbix_default');
expect(formattedTrigger.severity).toBe('Disaster');
expect(formattedTrigger.maintenance).toBe(true);
expect(formattedTrigger.age).toBeTruthy();
expect(formattedTrigger.lastchange).toBeTruthy();
done();
});
});
it('should sort triggers by time by default', (done) => {
ctx.panelCtrl.onRefresh().then(() => {
let trigger_ids = _.map(ctx.panelCtrl.triggerList, 'triggerid');
expect(trigger_ids).toEqual([
'2', '4', '3', '1'
]);
done();
});
});
it('should sort triggers by severity', (done) => {
ctx.panelCtrl.panel.sortTriggersBy = { text: 'severity', value: 'priority' };
ctx.panelCtrl.onRefresh().then(() => {
let trigger_ids = _.map(ctx.panelCtrl.triggerList, 'triggerid');
expect(trigger_ids).toEqual([
'1', '3', '2', '4'
]);
done();
});
});
it('should add acknowledges to trigger', (done) => {
ctx.panelCtrl.onRefresh().then(() => {
let trigger = getTriggerById(1, ctx);
expect(trigger.acknowledges).toHaveLength(1);
expect(trigger.acknowledges[0].message).toBe("event ack");
expect(getTriggerById(2, ctx).acknowledges).toBe(undefined);
expect(getTriggerById(3, ctx).acknowledges).toBe(undefined);
expect(getTriggerById(4, ctx).acknowledges).toBe(undefined);
done();
});
});
});
describe('When formatting triggers', () => {
beforeEach(() => {
ctx.panelCtrl = createPanelCtrl();
});
it('should handle new lines in trigger description', () => {
ctx.panelCtrl.setTriggerSeverity = jest.fn((trigger) => trigger);
let trigger = {comments: "this is\ndescription"};
const formattedTrigger = ctx.panelCtrl.formatTrigger(trigger);
expect(formattedTrigger.comments).toBe("this is<br>description");
});
it('should format host name to display (default)', (done) => {
ctx.panelCtrl.onRefresh().then(() => {
let trigger = getTriggerById(1, ctx);
let hostname = ctx.panelCtrl.formatHostName(trigger);
expect(hostname).toBe('backend01');
done();
});
});
it('should format host name to display (tech name)', (done) => {
ctx.panelCtrl.panel.hostField = false;
ctx.panelCtrl.panel.hostTechNameField = true;
ctx.panelCtrl.onRefresh().then(() => {
let trigger = getTriggerById(1, ctx);
let hostname = ctx.panelCtrl.formatHostName(trigger);
expect(hostname).toBe('backend01_tech');
done();
});
});
it('should format host name to display (both tech and visible)', (done) => {
ctx.panelCtrl.panel.hostField = true;
ctx.panelCtrl.panel.hostTechNameField = true;
ctx.panelCtrl.onRefresh().then(() => {
let trigger = getTriggerById(1, ctx);
let hostname = ctx.panelCtrl.formatHostName(trigger);
expect(hostname).toBe('backend01 (backend01_tech)');
done();
});
});
it('should hide hostname if both visible and tech name checkboxes unset', (done) => {
ctx.panelCtrl.panel.hostField = false;
ctx.panelCtrl.panel.hostTechNameField = false;
ctx.panelCtrl.onRefresh().then(() => {
let trigger = getTriggerById(1, ctx);
let hostname = ctx.panelCtrl.formatHostName(trigger);
expect(hostname).toBe("");
done();
});
});
});
describe('When formatting acknowledges', () => {
beforeEach(() => {
ctx.panelCtrl = createPanelCtrl();
});
it('should build proper user name', () => {
const ack = {
alias: 'alias', name: 'name', surname: 'surname'
};
const formatted = ctx.panelCtrl.formatAcknowledge(ack);
expect(formatted.user).toBe('alias (name surname)');
});
it('should return empty name if it is not defined', () => {
const formatted = ctx.panelCtrl.formatAcknowledge({});
expect(formatted.user).toBe('');
});
});
});
const defaultTrigger = {
"triggerid": "13565",
"value": "1",
"groups": [{"groupid": "1", "name": "Backend"}] ,
"hosts": [{"host": "backend01_tech", "hostid": "10001","maintenance_status": "0", "name": "backend01"}] ,
"lastEvent": {
"eventid": "11",
"clock": "1507229064",
"ns": "556202037",
"acknowledged": "1",
"value": "1",
"object": "0",
"source": "0",
"objectid": "13565",
},
"tags": [] ,
"lastchange": "1440259530",
"priority": "2",
"description": "Lack of free swap space on server",
"comments": "It probably means that the systems requires\nmore physical memory.",
"url": "https://host.local/path",
"templateid": "0", "expression": "{13174}<50", "manual_close": "0", "correlation_mode": "0",
"correlation_tag": "", "recovery_mode": "0", "recovery_expression": "", "state": "0", "status": "0",
"flags": "0", "type": "0", "items": [] , "error": ""
};
const defaultEvent = {
"eventid": "11",
"acknowledges": [
{
"acknowledgeid": "185",
"action": "0",
"alias": "api",
"clock": "1512382246",
"eventid": "11",
"message": "event ack",
"name": "api",
"surname": "user",
"userid": "3"
}
],
"clock": "1507229064",
"ns": "556202037",
"acknowledged": "1",
"value": "1",
"object": "0",
"source": "0",
"objectid": "1",
};
function generateTrigger(id, timestamp, severity) {
let trigger = _.cloneDeep(defaultTrigger);
trigger.triggerid = id.toString();
if (severity) {
trigger.priority = severity.toString();
}
if (timestamp) {
trigger.lastchange = timestamp;
}
return trigger;
}
function createTrigger(props) {
let trigger = _.cloneDeep(defaultTrigger);
trigger = _.merge(trigger, props);
trigger.lastEvent.objectid = trigger.triggerid;
return trigger;
}
function getTriggerById(id, ctx) {
return _.find(ctx.panelCtrl.triggerList, {triggerid: id.toString()});
}

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long