Problems panel: convert to metric panel
This commit is contained in:
@@ -3,9 +3,6 @@
|
|||||||
"name": "Zabbix Problems",
|
"name": "Zabbix Problems",
|
||||||
"id": "alexanderzobnin-zabbix-triggers-panel",
|
"id": "alexanderzobnin-zabbix-triggers-panel",
|
||||||
|
|
||||||
"dataFormats": [],
|
|
||||||
"skipDataQuery": true,
|
|
||||||
|
|
||||||
"info": {
|
"info": {
|
||||||
"author": {
|
"author": {
|
||||||
"name": "Alexander Zobnin",
|
"name": "Alexander Zobnin",
|
||||||
|
|||||||
@@ -2,11 +2,11 @@ import React from 'react';
|
|||||||
import ReactDOM from 'react-dom';
|
import ReactDOM from 'react-dom';
|
||||||
import _ from 'lodash';
|
import _ from 'lodash';
|
||||||
import { getDataSourceSrv } from '@grafana/runtime';
|
import { getDataSourceSrv } from '@grafana/runtime';
|
||||||
|
import { PanelEvents } from '@grafana/data';
|
||||||
import * as dateMath from 'grafana/app/core/utils/datemath';
|
import * as dateMath from 'grafana/app/core/utils/datemath';
|
||||||
import * as utils from '../datasource-zabbix/utils';
|
import * as utils from '../datasource-zabbix/utils';
|
||||||
import { PanelCtrl } from 'grafana/app/plugins/sdk';
|
import { MetricsPanelCtrl } from 'grafana/app/plugins/sdk';
|
||||||
import { triggerPanelOptionsTab } from './options_tab';
|
import { triggerPanelOptionsTab } from './options_tab';
|
||||||
import { triggerPanelTriggersTab } from './triggers_tab';
|
|
||||||
import { migratePanelSchema, CURRENT_SCHEMA_VERSION } from './migrations';
|
import { migratePanelSchema, CURRENT_SCHEMA_VERSION } from './migrations';
|
||||||
import ProblemList from './components/Problems/Problems';
|
import ProblemList from './components/Problems/Problems';
|
||||||
import AlertList from './components/AlertList/AlertList';
|
import AlertList from './components/AlertList/AlertList';
|
||||||
@@ -51,7 +51,7 @@ const DEFAULT_TIME_FORMAT = "DD MMM YYYY HH:mm:ss";
|
|||||||
|
|
||||||
export const PANEL_DEFAULTS = {
|
export const PANEL_DEFAULTS = {
|
||||||
schemaVersion: CURRENT_SCHEMA_VERSION,
|
schemaVersion: CURRENT_SCHEMA_VERSION,
|
||||||
targets: [getDefaultTarget([])],
|
// targets: [getDefaultTarget([])],
|
||||||
// Fields
|
// Fields
|
||||||
hostField: true,
|
hostField: true,
|
||||||
hostTechNameField: false,
|
hostTechNameField: false,
|
||||||
@@ -93,7 +93,7 @@ const triggerStatusMap = {
|
|||||||
'1': 'PROBLEM'
|
'1': 'PROBLEM'
|
||||||
};
|
};
|
||||||
|
|
||||||
export class TriggerPanelCtrl extends PanelCtrl {
|
export class TriggerPanelCtrl extends MetricsPanelCtrl {
|
||||||
|
|
||||||
/** @ngInject */
|
/** @ngInject */
|
||||||
constructor($scope, $injector, $timeout, templateSrv, contextSrv, dashboardSrv, timeSrv) {
|
constructor($scope, $injector, $timeout, templateSrv, contextSrv, dashboardSrv, timeSrv) {
|
||||||
@@ -105,25 +105,59 @@ export class TriggerPanelCtrl extends PanelCtrl {
|
|||||||
this.scope = $scope;
|
this.scope = $scope;
|
||||||
this.$timeout = $timeout;
|
this.$timeout = $timeout;
|
||||||
|
|
||||||
|
// Tell Grafana do not convert data frames to table or series
|
||||||
|
this.useDataFrames = true;
|
||||||
|
|
||||||
this.editorTabIndex = 1;
|
this.editorTabIndex = 1;
|
||||||
this.triggerStatusMap = triggerStatusMap;
|
this.triggerStatusMap = triggerStatusMap;
|
||||||
this.defaultTimeFormat = DEFAULT_TIME_FORMAT;
|
this.defaultTimeFormat = DEFAULT_TIME_FORMAT;
|
||||||
this.pageIndex = 0;
|
this.pageIndex = 0;
|
||||||
this.triggerList = [];
|
|
||||||
this.datasources = {};
|
|
||||||
this.range = {};
|
this.range = {};
|
||||||
|
this.renderData = [];
|
||||||
|
|
||||||
this.panel = migratePanelSchema(this.panel);
|
// this.panel = migratePanelSchema(this.panel);
|
||||||
_.defaultsDeep(this.panel, _.cloneDeep(PANEL_DEFAULTS));
|
_.defaultsDeep(this.panel, _.cloneDeep(PANEL_DEFAULTS));
|
||||||
|
|
||||||
this.available_datasources = _.map(this.getZabbixDataSources(), 'name');
|
this.events.on(PanelEvents.render, this.onRender.bind(this));
|
||||||
if (this.panel.targets && !this.panel.targets[0].datasource) {
|
this.events.on('data-frames-received', this.onDataFramesReceived.bind(this));
|
||||||
this.panel.targets[0].datasource = this.available_datasources[0];
|
this.events.on(PanelEvents.dataSnapshotLoad, this.onDataSnapshotLoad.bind(this));
|
||||||
|
this.events.on(PanelEvents.editModeInitialized, this.onInitEditMode.bind(this));
|
||||||
}
|
}
|
||||||
|
|
||||||
this.initDatasources();
|
onInitEditMode() {
|
||||||
this.events.on('init-edit-mode', this.onInitEditMode.bind(this));
|
this.addEditorTab('Options', triggerPanelOptionsTab);
|
||||||
this.events.on('refresh', this.onRefresh.bind(this));
|
}
|
||||||
|
|
||||||
|
onDataFramesReceived(data) {
|
||||||
|
let problems = [];
|
||||||
|
|
||||||
|
if (data && data.length) {
|
||||||
|
for (const dataFrame of data) {
|
||||||
|
try {
|
||||||
|
const values = dataFrame.fields[0].values;
|
||||||
|
if (values.toArray) {
|
||||||
|
problems.push(values.toArray());
|
||||||
|
} else if (values.length > 0) {
|
||||||
|
// On snapshot mode values is a plain Array, not ArrayVector
|
||||||
|
problems.push(values);
|
||||||
|
}
|
||||||
|
} catch (error) {
|
||||||
|
console.log(error);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
this.loading = false;
|
||||||
|
problems = _.flatten(problems);
|
||||||
|
this.renderProblems(problems);
|
||||||
|
}
|
||||||
|
|
||||||
|
onDataSnapshotLoad(snapshotData) {
|
||||||
|
this.onDataFramesReceived(snapshotData);
|
||||||
|
}
|
||||||
|
|
||||||
|
onRender() {
|
||||||
|
this.range = this.timeSrv.timeRange();
|
||||||
}
|
}
|
||||||
|
|
||||||
setPanelError(err, defaultError) {
|
setPanelError(err, defaultError) {
|
||||||
@@ -139,93 +173,12 @@ export class TriggerPanelCtrl extends PanelCtrl {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
this.events.emit('data-error', err);
|
// this.events.emit(PanelEvents.dataError, err);
|
||||||
console.log('Panel data error:', err);
|
console.log('Panel data error:', err);
|
||||||
}
|
}
|
||||||
|
|
||||||
initDatasources() {
|
renderProblems(problems) {
|
||||||
if (!this.panel.targets) {
|
let triggers = _.cloneDeep(problems);
|
||||||
return;
|
|
||||||
}
|
|
||||||
const targetDatasources = _.compact(this.panel.targets.map(target => target.datasource));
|
|
||||||
let promises = targetDatasources.map(ds => {
|
|
||||||
// Load datasource
|
|
||||||
return getDataSourceSrv().get(ds)
|
|
||||||
.then(datasource => {
|
|
||||||
this.datasources[ds] = datasource;
|
|
||||||
return datasource;
|
|
||||||
});
|
|
||||||
});
|
|
||||||
return Promise.all(promises);
|
|
||||||
}
|
|
||||||
|
|
||||||
getZabbixDataSources() {
|
|
||||||
return _.filter(getDataSourceSrv().getMetricSources(), datasource => {
|
|
||||||
return datasource.meta.id === ZABBIX_DS_ID && datasource.value;
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
isEmptyTargets() {
|
|
||||||
const emptyTargets = _.isEmpty(this.panel.targets);
|
|
||||||
const emptyTarget = (this.panel.targets.length === 1 && (
|
|
||||||
_.isEmpty(this.panel.targets[0]) ||
|
|
||||||
this.panel.targets[0].target === ""
|
|
||||||
));
|
|
||||||
return emptyTargets || emptyTarget;
|
|
||||||
}
|
|
||||||
|
|
||||||
onInitEditMode() {
|
|
||||||
this.addEditorTab('Triggers', triggerPanelTriggersTab, 1);
|
|
||||||
this.addEditorTab('Options', triggerPanelOptionsTab, 2);
|
|
||||||
}
|
|
||||||
|
|
||||||
setTimeQueryStart() {
|
|
||||||
this.timing.queryStart = new Date().getTime();
|
|
||||||
}
|
|
||||||
|
|
||||||
setTimeQueryEnd() {
|
|
||||||
this.timing.queryEnd = (new Date()).getTime();
|
|
||||||
}
|
|
||||||
|
|
||||||
onRefresh() {
|
|
||||||
// ignore fetching data if another panel is in fullscreen
|
|
||||||
if (this.otherPanelInFullscreenMode()) { return; }
|
|
||||||
|
|
||||||
this.range = this.timeSrv.timeRange();
|
|
||||||
|
|
||||||
// clear loading/error state
|
|
||||||
delete this.error;
|
|
||||||
this.loading = true;
|
|
||||||
this.setTimeQueryStart();
|
|
||||||
this.pageIndex = 0;
|
|
||||||
|
|
||||||
return this.getTriggers()
|
|
||||||
.then(triggers => {
|
|
||||||
// Notify panel that request is finished
|
|
||||||
this.loading = false;
|
|
||||||
this.setTimeQueryEnd();
|
|
||||||
return this.renderTriggers(triggers);
|
|
||||||
})
|
|
||||||
.then(() => {
|
|
||||||
this.$timeout(() => {
|
|
||||||
this.renderingCompleted();
|
|
||||||
});
|
|
||||||
})
|
|
||||||
.catch(err => {
|
|
||||||
this.loading = false;
|
|
||||||
|
|
||||||
if (err.cancelled) {
|
|
||||||
console.log('Panel request cancelled', err);
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
this.setPanelError(err);
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
renderTriggers(zabbixTriggers) {
|
|
||||||
let triggers = _.cloneDeep(zabbixTriggers || this.triggerListUnfiltered);
|
|
||||||
this.triggerListUnfiltered = _.cloneDeep(triggers);
|
|
||||||
|
|
||||||
triggers = _.map(triggers, this.formatTrigger.bind(this));
|
triggers = _.map(triggers, this.formatTrigger.bind(this));
|
||||||
triggers = this.filterTriggersPost(triggers);
|
triggers = this.filterTriggersPost(triggers);
|
||||||
@@ -234,44 +187,13 @@ export class TriggerPanelCtrl extends PanelCtrl {
|
|||||||
// Limit triggers number
|
// Limit triggers number
|
||||||
triggers = triggers.slice(0, this.panel.limit || PANEL_DEFAULTS.limit);
|
triggers = triggers.slice(0, this.panel.limit || PANEL_DEFAULTS.limit);
|
||||||
|
|
||||||
this.triggerList = triggers;
|
this.renderData = triggers;
|
||||||
|
|
||||||
return this.$timeout(() => {
|
return this.$timeout(() => {
|
||||||
return super.render(this.triggerList);
|
return super.render(triggers);
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
getTriggers() {
|
|
||||||
const timeFrom = Math.ceil(dateMath.parse(this.range.from) / 1000);
|
|
||||||
const timeTo = Math.ceil(dateMath.parse(this.range.to) / 1000);
|
|
||||||
const timeRange = [timeFrom, timeTo];
|
|
||||||
|
|
||||||
const options = {
|
|
||||||
scopedVars: this.panel.scopedVars
|
|
||||||
};
|
|
||||||
|
|
||||||
let promises = _.map(this.panel.targets, (target) => {
|
|
||||||
const ds = target.datasource;
|
|
||||||
return getDataSourceSrv().get(ds)
|
|
||||||
.then(datasource => {
|
|
||||||
return datasource.queryProblems(target, timeRange, options);
|
|
||||||
})
|
|
||||||
.then(results => {
|
|
||||||
let problems = [];
|
|
||||||
if (results && results.fields && results.fields.length) {
|
|
||||||
try {
|
|
||||||
problems = results.fields[0].values.toArray();
|
|
||||||
} catch (error) {
|
|
||||||
console.log(error);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return problems;
|
|
||||||
});
|
|
||||||
});
|
|
||||||
|
|
||||||
return Promise.all(promises).then(_.flatten);
|
|
||||||
}
|
|
||||||
|
|
||||||
filterTriggersPost(triggers) {
|
filterTriggersPost(triggers) {
|
||||||
let triggerList = _.cloneDeep(triggers);
|
let triggerList = _.cloneDeep(triggers);
|
||||||
|
|
||||||
@@ -358,7 +280,6 @@ export class TriggerPanelCtrl extends PanelCtrl {
|
|||||||
|
|
||||||
addTagFilter(tag, datasource) {
|
addTagFilter(tag, datasource) {
|
||||||
const target = this.panel.targets.find(t => t.datasource === datasource);
|
const target = this.panel.targets.find(t => t.datasource === datasource);
|
||||||
console.log(target);
|
|
||||||
let tagFilter = target.tags.filter;
|
let tagFilter = target.tags.filter;
|
||||||
let targetTags = this.parseTags(tagFilter);
|
let targetTags = this.parseTags(tagFilter);
|
||||||
let newTag = {tag: tag.tag, value: tag.value};
|
let newTag = {tag: tag.tag, value: tag.value};
|
||||||
@@ -494,7 +415,7 @@ export class TriggerPanelCtrl extends PanelCtrl {
|
|||||||
return Promise.reject({message: 'Trigger has no events. Nothing to acknowledge.'});
|
return Promise.reject({message: 'Trigger has no events. Nothing to acknowledge.'});
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
.then(this.onRefresh.bind(this))
|
.then(this.onMetricsPanelRefresh.bind(this))
|
||||||
.catch((err) => {
|
.catch((err) => {
|
||||||
this.setPanelError(err);
|
this.setPanelError(err);
|
||||||
});
|
});
|
||||||
@@ -517,15 +438,15 @@ export class TriggerPanelCtrl extends PanelCtrl {
|
|||||||
|
|
||||||
link(scope, elem, attrs, ctrl) {
|
link(scope, elem, attrs, ctrl) {
|
||||||
let panel = ctrl.panel;
|
let panel = ctrl.panel;
|
||||||
let triggerList = ctrl.triggerList;
|
// let problems = ctrl.problems;
|
||||||
|
|
||||||
scope.$watchGroup(['ctrl.triggerList'], renderPanel);
|
// scope.$watchGroup(['ctrl.renderData'], renderPanel);
|
||||||
ctrl.events.on('render', (renderData) => {
|
ctrl.events.on(PanelEvents.render, (renderData) => {
|
||||||
triggerList = renderData || triggerList;
|
renderData = renderData || this.renderData;
|
||||||
renderPanel();
|
renderPanel(renderData);
|
||||||
});
|
});
|
||||||
|
|
||||||
function renderPanel() {
|
function renderPanel(problems) {
|
||||||
const timeFrom = Math.ceil(dateMath.parse(ctrl.range.from) / 1000);
|
const timeFrom = Math.ceil(dateMath.parse(ctrl.range.from) / 1000);
|
||||||
const timeTo = Math.ceil(dateMath.parse(ctrl.range.to) / 1000);
|
const timeTo = Math.ceil(dateMath.parse(ctrl.range.to) / 1000);
|
||||||
|
|
||||||
@@ -533,14 +454,14 @@ export class TriggerPanelCtrl extends PanelCtrl {
|
|||||||
const fontSizeProp = fontSize && fontSize !== 100 ? fontSize : null;
|
const fontSizeProp = fontSize && fontSize !== 100 ? fontSize : null;
|
||||||
|
|
||||||
const pageSize = panel.pageSize || 10;
|
const pageSize = panel.pageSize || 10;
|
||||||
const loading = ctrl.loading && (!ctrl.triggerList || !ctrl.triggerList.length);
|
const loading = ctrl.loading && (!problems || !problems.length);
|
||||||
|
|
||||||
let panelOptions = {};
|
let panelOptions = {};
|
||||||
for (let prop in PANEL_DEFAULTS) {
|
for (let prop in PANEL_DEFAULTS) {
|
||||||
panelOptions[prop] = ctrl.panel[prop];
|
panelOptions[prop] = ctrl.panel[prop];
|
||||||
}
|
}
|
||||||
const problemsListProps = {
|
const problemsListProps = {
|
||||||
problems: ctrl.triggerList,
|
problems,
|
||||||
panelOptions,
|
panelOptions,
|
||||||
timeRange: { timeFrom, timeTo },
|
timeRange: { timeFrom, timeTo },
|
||||||
loading,
|
loading,
|
||||||
|
|||||||
Reference in New Issue
Block a user