From 8908c7236f2e73099604c80bd13765c50aa51403 Mon Sep 17 00:00:00 2001 From: Alexander Zobnin Date: Fri, 16 Dec 2022 12:07:02 +0100 Subject: [PATCH] Use data frames for triggers data, #1441 --- src/datasource/datasource.ts | 22 +++--- src/datasource/responseHandler.ts | 61 +++++++++++----- src/datasource/utils.ts | 14 +++- .../zabbix_api/zabbixAPIConnector.ts | 19 +++-- src/datasource/zabbix/zabbix.ts | 73 +++++++++---------- 5 files changed, 110 insertions(+), 79 deletions(-) diff --git a/src/datasource/datasource.ts b/src/datasource/datasource.ts index d8d8ea01e..96a5cc99a 100644 --- a/src/datasource/datasource.ts +++ b/src/datasource/datasource.ts @@ -262,7 +262,8 @@ export class ZabbixDatasource extends DataSourceApi 0 && isDataFrame(data[0]) && !utils.isProblemsDataFrame(data[0]) && - !utils.isMacrosDataFrame(data[0]) + !utils.isMacrosDataFrame(data[0]) && + !utils.nonTimeSeriesDataFrame(data[0]) ) { data = responseHandler.alignFrames(data); if (responseHandler.isConvertibleToWide(data)) { @@ -546,7 +547,7 @@ export class ZabbixDatasource extends DataSourceApi h.hostid); + const appids = apps?.map((a) => a.applicationid); + const itemids = items?.map((i) => i.itemid); const options: any = { minSeverity: target.options.minSeverity, acknowledged: target.options.acknowledged, @@ -577,7 +577,7 @@ export class ZabbixDatasource extends DataSourceApi h.hostid); + const appids = apps?.map((a) => a.applicationid); + const triggerids = triggers.map((t) => t.triggerid); const options: any = { minSeverity: target.options?.minSeverity, acknowledged: target.options?.acknowledged, @@ -639,7 +639,7 @@ export class ZabbixDatasource extends DataSourceApi { - table.addColumn({ text: severity.text }); + const frame = new MutableDataFrame({ + refId: target.refId, + fields: [{ name: 'Host group', type: FieldType.string, values: new ArrayVector() }], }); - _.each(stats, (severity_stats, group) => { - if (_.includes(groupNames, group)) { - let row = _.map( - _.orderBy(_.toPairs(severity_stats), (s) => s[0], ['desc']), - (s) => s[1] - ); - row = _.concat([group], ...row); - table.rows.push(row); + + for (let i = c.TRIGGER_SEVERITY.length - 1; i >= 0; i--) { + frame.fields.push({ + name: c.TRIGGER_SEVERITY[i].text, + type: FieldType.number, + config: { unit: 'none', decimals: 0 }, + values: new ArrayVector(), + }); + } + + const groupNames = groups?.map((g) => g.name); + groupNames?.forEach((group) => { + if (!stats[group]) { + return; } + frame.add({ + 'Host group': group, + Disaster: stats[group][5], + High: stats[group][4], + Average: stats[group][3], + Warning: stats[group][2], + Information: stats[group][1], + 'Not classified': stats[group][0], + }); }); - return table; + + return frame; } } diff --git a/src/datasource/utils.ts b/src/datasource/utils.ts index 205a54492..e7bf8304f 100644 --- a/src/datasource/utils.ts +++ b/src/datasource/utils.ts @@ -3,7 +3,15 @@ import _ from 'lodash'; import moment from 'moment'; import * as c from './constants'; import { VariableQuery, VariableQueryTypes, ZBXItemTag } from './types'; -import { DataFrame, FieldType, getValueFormats, MappingType, rangeUtil, ValueMapping } from '@grafana/data'; +import { + DataFrame, + FieldType, + getValueFormats, + MappingType, + rangeUtil, + TIME_SERIES_TIME_FIELD_NAME, + ValueMapping, +} from '@grafana/data'; /* * This regex matches 3 types of variable reference with an optional format specifier @@ -511,6 +519,10 @@ export function isMacrosDataFrame(data: DataFrame): boolean { return data.name === 'macros'; } +export function nonTimeSeriesDataFrame(data: DataFrame): boolean { + return !data.fields.find((f) => f.type === FieldType.time || f.name === TIME_SERIES_TIME_FIELD_NAME); +} + // Swap n and k elements. export function swap(list: T[], n: number, k: number): T[] { if (list === null || list.length < 2 || k > list.length - 1 || k < 0 || n > list.length - 1 || n < 0) { diff --git a/src/datasource/zabbix/connectors/zabbix_api/zabbixAPIConnector.ts b/src/datasource/zabbix/connectors/zabbix_api/zabbixAPIConnector.ts index b648240f2..199ed0105 100644 --- a/src/datasource/zabbix/connectors/zabbix_api/zabbixAPIConnector.ts +++ b/src/datasource/zabbix/connectors/zabbix_api/zabbixAPIConnector.ts @@ -3,7 +3,7 @@ import semver from 'semver'; import kbn from 'grafana/app/core/utils/kbn'; import * as utils from '../../../utils'; import { MIN_SLA_INTERVAL, ZBX_ACK_ACTION_ADD_MESSAGE, ZBX_ACK_ACTION_NONE } from '../../../constants'; -import { ShowProblemTypes, ZBXProblem } from '../../../types'; +import { ShowProblemTypes, ZBXProblem, ZBXTrigger } from '../../../types'; import { APIExecuteScriptResponse, JSONRPCError, ZBXScript } from './types'; import { BackendSrvRequest, getBackendSrv } from '@grafana/runtime'; import { rangeUtil } from '@grafana/data'; @@ -651,7 +651,7 @@ export class ZabbixAPIConnector { return this.request('trigger.get', params); } - getHostAlerts(hostids, applicationids, options) { + async getHostAlerts(hostids, applicationids, options): Promise { const { minSeverity, acknowledged, count, timeFrom, timeTo } = options; const params: any = { output: 'extend', @@ -684,15 +684,14 @@ export class ZabbixAPIConnector { params.lastChangeTill = timeTo; } - return this.request('trigger.get', params).then((triggers) => { - if (!count || acknowledged === 1) { - triggers = filterTriggersByAcknowledge(triggers, acknowledged); - if (count) { - triggers = triggers.length; - } + let triggers = await this.request('trigger.get', params); + if (!count || acknowledged === 1) { + triggers = filterTriggersByAcknowledge(triggers, acknowledged); + if (count) { + triggers = triggers.length; } - return triggers; - }); + } + return triggers; } getHostICAlerts(hostids, applicationids, itemids, options) { diff --git a/src/datasource/zabbix/zabbix.ts b/src/datasource/zabbix/zabbix.ts index 9ce8e7146..9487b61bd 100644 --- a/src/datasource/zabbix/zabbix.ts +++ b/src/datasource/zabbix/zabbix.ts @@ -11,7 +11,7 @@ import { SQLConnector } from './connectors/sql/sqlConnector'; import { InfluxDBConnector } from './connectors/influxdb/influxdbConnector'; import { ZabbixConnector } from './types'; import { joinTriggersWithEvents, joinTriggersWithProblems } from '../problemsHandler'; -import { ProblemDTO, ZBXApp, ZBXHost, ZBXItem, ZBXItemTag } from '../types'; +import { ProblemDTO, ZBXApp, ZBXHost, ZBXItem, ZBXItemTag, ZBXTrigger } from '../types'; interface AppsResponse extends Array { appFilterEmpty?: boolean; @@ -97,7 +97,7 @@ export class Zabbix implements ZabbixConnector { getItemsByIDs: (itemids) => Promise; getEvents: (objectids, timeFrom, timeTo, showEvents, limit?) => Promise; getAlerts: (itemids, timeFrom?, timeTo?) => Promise; - getHostAlerts: (hostids, applicationids, options?) => Promise; + getHostAlerts: (hostids, applicationids, options?) => Promise; getHostICAlerts: (hostids, applicationids, itemids, options?) => Promise; getHostPCAlerts: (hostids, applicationids, triggerids, options?) => Promise; getAcknowledges: (eventids) => Promise; @@ -265,51 +265,50 @@ export class Zabbix implements ZabbixConnector { return this.getUMacros(...filters); } - getHostsApsFromTarget(target): Promise<[ZBXHost[], ZBXApp[]]> { + async getHostsApsFromTarget(target): Promise<[ZBXHost[], ZBXApp[]]> { const parts = ['group', 'host', 'application']; - const filters = _.map(parts, (p) => target[p].filter); - return Promise.all([this.getHosts(...filters), this.getApps(...filters)]).then((results) => { - const hosts = results[0]; - let apps: AppsResponse = results[1]; - if (apps.appFilterEmpty) { - apps = []; - } - return [hosts, apps]; - }); + const filters = parts.map((p) => target[p].filter); + const results = await Promise.all([this.getHosts(...filters), this.getApps(...filters)]); + const hosts = results[0]; + let apps: AppsResponse = results[1]; + if (apps.appFilterEmpty) { + apps = []; + } + return [hosts, apps]; } - getHostsFromICTarget(target, options) { + async getHostsFromICTarget(target, options): Promise<[ZBXHost[], ZBXApp[], ZBXItem[]]> { const parts = ['group', 'host', 'application', 'itemTag', 'item']; - const filters = _.map(parts, (p) => target[p].filter); - return Promise.all([this.getHosts(...filters), this.getApps(...filters), this.getItems(...filters, options)]).then( - (results) => { - const hosts = results[0]; - let apps: AppsResponse = results[1]; - if (apps.appFilterEmpty) { - apps = []; - } - const items = results[2]; - return [hosts, apps, items]; - } - ); + const filters = parts.map((p) => target[p].filter); + const results = await Promise.all([ + this.getHosts(...filters), + this.getApps(...filters), + this.getItems(...filters, options), + ]); + const hosts = results[0]; + let apps: AppsResponse = results[1]; + if (apps.appFilterEmpty) { + apps = []; + } + const items = results[2]; + return [hosts, apps, items]; } - getHostsFromPCTarget(target, options) { + async getHostsFromPCTarget(target, options): Promise<[ZBXHost[], ZBXApp[], ProblemDTO[]]> { const parts = ['group', 'host', 'application', 'proxy', 'trigger']; - const filters = _.map(parts, (p) => target[p].filter); - return Promise.all([ + const filters = parts.map((p) => target[p].filter); + const results = await Promise.all([ this.getHosts(...filters), this.getApps(...filters), this.getCProblems(...filters, options), - ]).then((results) => { - const hosts = results[0]; - let apps: AppsResponse = results[1]; - if (apps.appFilterEmpty) { - apps = []; - } - const triggers = results[2]; - return [hosts, apps, triggers]; - }); + ]); + const hosts = results[0]; + let apps: AppsResponse = results[1]; + if (apps.appFilterEmpty) { + apps = []; + } + const problems = results[2]; + return [hosts, apps, problems]; } getAllGroups() {