Skip to content

Commit

Permalink
Use data frames for triggers data, #1441
Browse files Browse the repository at this point in the history
  • Loading branch information
alexanderzobnin committed Dec 16, 2022
1 parent cca7a7e commit 8908c72
Show file tree
Hide file tree
Showing 5 changed files with 110 additions and 79 deletions.
22 changes: 11 additions & 11 deletions src/datasource/datasource.ts
Original file line number Diff line number Diff line change
Expand Up @@ -262,7 +262,8 @@ export class ZabbixDatasource extends DataSourceApi<ZabbixMetricsQuery, ZabbixDS
data.length > 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)) {
Expand Down Expand Up @@ -546,15 +547,14 @@ export class ZabbixDatasource extends DataSourceApi<ZabbixMetricsQuery, ZabbixDS
}

const alerts = await this.zabbix.getHostAlerts(hostids, appids, options);
return responseHandler.handleTriggersResponse(alerts, groups, timeRange);
return responseHandler.handleTriggersResponse(alerts, groups, timeRange, target);
}

async queryTriggersICData(target, timeRange) {
const [timeFrom, timeTo] = timeRange;
const getItemOptions = {
itemtype: 'num',
};

const [hosts, apps, items] = await this.zabbix.getHostsFromICTarget(target, getItemOptions);
if (!hosts.length) {
return Promise.resolve([]);
Expand All @@ -563,9 +563,9 @@ export class ZabbixDatasource extends DataSourceApi<ZabbixMetricsQuery, ZabbixDS
const groupFilter = target.group.filter;
const groups = await this.zabbix.getGroups(groupFilter);

const hostids = _.map(hosts, 'hostid');
const appids = _.map(apps, 'applicationid');
const itemids = _.map(items, 'itemid');
const hostids = hosts?.map((h) => 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,
Expand All @@ -577,7 +577,7 @@ export class ZabbixDatasource extends DataSourceApi<ZabbixMetricsQuery, ZabbixDS
}

const alerts = await this.zabbix.getHostICAlerts(hostids, appids, itemids, options);
return responseHandler.handleTriggersResponse(alerts, groups, timeRange);
return responseHandler.handleTriggersResponse(alerts, groups, timeRange, target);
}

async queryTriggersPCData(target, timeRange, request) {
Expand Down Expand Up @@ -625,9 +625,9 @@ export class ZabbixDatasource extends DataSourceApi<ZabbixMetricsQuery, ZabbixDS
const groupFilter = target.group.filter;
const groups = await this.zabbix.getGroups(groupFilter);

const hostids = _.map(hosts, 'hostid');
const appids = _.map(apps, 'applicationid');
const triggerids = _.map(triggers, 'triggerid');
const hostids = hosts?.map((h) => 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,
Expand All @@ -639,7 +639,7 @@ export class ZabbixDatasource extends DataSourceApi<ZabbixMetricsQuery, ZabbixDS
}

const alerts = await this.zabbix.getHostPCAlerts(hostids, appids, triggerids, options);
return responseHandler.handleTriggersResponse(alerts, groups, timeRange);
return responseHandler.handleTriggersResponse(alerts, groups, timeRange, target);
}

queryProblems(target: ZabbixMetricsQuery, timeRange, options) {
Expand Down
61 changes: 41 additions & 20 deletions src/datasource/responseHandler.ts
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,7 @@ import {
TIME_SERIES_TIME_FIELD_NAME,
TIME_SERIES_VALUE_FIELD_NAME,
} from '@grafana/data';
import { ZabbixMetricsQuery } from './types';
import { ZabbixMetricsQuery, ZBXGroup, ZBXTrigger } from './types';

/**
* Convert Zabbix API history.get response to Grafana format
Expand Down Expand Up @@ -514,37 +514,58 @@ function handleSLAResponse(itservice, slaProperty, slaObject) {
}
}

function handleTriggersResponse(triggers, groups, timeRange) {
function handleTriggersResponse(triggers: ZBXTrigger[], groups: ZBXGroup[], timeRange: number[], target) {
if (!_.isArray(triggers)) {
let triggersCount = null;
try {
triggersCount = Number(triggers);
} catch (err) {
console.log('Error when handling triggers count: ', err);
}
return {
target: 'triggers count',
datapoints: [[triggersCount, timeRange[1] * 1000]],
};

const frame = new MutableDataFrame({
refId: target.refId,
fields: [
{ name: TIME_SERIES_TIME_FIELD_NAME, type: FieldType.time, values: new ArrayVector([timeRange[1] * 1000]) },
{ name: TIME_SERIES_VALUE_FIELD_NAME, type: FieldType.number, values: new ArrayVector([triggersCount]) },
],
length: 1,
});

return frame;
} else {
const stats = getTriggerStats(triggers);
const groupNames = _.map(groups, 'name');
const table: any = new TableModel();
table.addColumn({ text: 'Host group' });
_.each(_.orderBy(c.TRIGGER_SEVERITY, ['val'], ['desc']), (severity) => {
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;
}
}

Expand Down
14 changes: 13 additions & 1 deletion src/datasource/utils.ts
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down Expand Up @@ -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<T>(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) {
Expand Down
19 changes: 9 additions & 10 deletions src/datasource/zabbix/connectors/zabbix_api/zabbixAPIConnector.ts
Original file line number Diff line number Diff line change
Expand Up @@ -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';
Expand Down Expand Up @@ -651,7 +651,7 @@ export class ZabbixAPIConnector {
return this.request('trigger.get', params);
}

getHostAlerts(hostids, applicationids, options) {
async getHostAlerts(hostids, applicationids, options): Promise<ZBXTrigger[]> {
const { minSeverity, acknowledged, count, timeFrom, timeTo } = options;
const params: any = {
output: 'extend',
Expand Down Expand Up @@ -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) {
Expand Down
73 changes: 36 additions & 37 deletions src/datasource/zabbix/zabbix.ts
Original file line number Diff line number Diff line change
Expand Up @@ -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<any> {
appFilterEmpty?: boolean;
Expand Down Expand Up @@ -97,7 +97,7 @@ export class Zabbix implements ZabbixConnector {
getItemsByIDs: (itemids) => Promise<any>;
getEvents: (objectids, timeFrom, timeTo, showEvents, limit?) => Promise<any>;
getAlerts: (itemids, timeFrom?, timeTo?) => Promise<any>;
getHostAlerts: (hostids, applicationids, options?) => Promise<any>;
getHostAlerts: (hostids, applicationids, options?) => Promise<ZBXTrigger[]>;
getHostICAlerts: (hostids, applicationids, itemids, options?) => Promise<any>;
getHostPCAlerts: (hostids, applicationids, triggerids, options?) => Promise<any>;
getAcknowledges: (eventids) => Promise<any>;
Expand Down Expand Up @@ -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() {
Expand Down

0 comments on commit 8908c72

Please sign in to comment.