Files
grafana-zabbix/src/panel-triggers/ProblemsPanel.tsx
Sriram c4065fb0f3 config updates (#1800)
* cleanup

* update create plugin config and query help fix

* query types file

* Update docker-compose.yml

Co-authored-by: Zoltán Bedi <zoltan.bedi@gmail.com>

* addressed review comments

---------

Co-authored-by: Zoltán Bedi <zoltan.bedi@gmail.com>
2024-03-13 15:20:53 +00:00

279 lines
9.5 KiB
TypeScript

import React from 'react';
import _ from 'lodash';
import { DataSourceRef, dateMath, PanelProps } from '@grafana/data';
import { getDataSourceSrv } from '@grafana/runtime';
import { contextSrv } from 'grafana/app/core/core';
import { ProblemsPanelOptions, RTResized } from './types';
import { ZabbixMetricsQuery } from '../datasource/types/query';
import { ProblemDTO, ZBXQueryUpdatedEvent, ZBXTag } from '../datasource/types';
import { APIExecuteScriptResponse } from '../datasource/zabbix/connectors/zabbix_api/types';
import ProblemList from './components/Problems/Problems';
import { AckProblemData } from './components/AckModal';
import AlertList from './components/AlertList/AlertList';
const PROBLEM_EVENTS_LIMIT = 100;
interface ProblemsPanelProps extends PanelProps<ProblemsPanelOptions> {}
export const ProblemsPanel = (props: ProblemsPanelProps): JSX.Element => {
const { data, options, timeRange, onOptionsChange } = props;
const { layout, showTriggers, triggerSeverity, sortProblems } = options;
const prepareProblems = () => {
const problems: ProblemDTO[] = [];
if (!data?.series) {
return [];
}
for (const dataFrame of data.series) {
try {
const values = dataFrame.fields[0].values;
if (values.toArray) {
problems.push(...values.toArray());
}
} catch (error) {
console.log(error);
return [];
}
}
let triggers = _.cloneDeep(problems);
triggers = triggers.map((t) => formatTrigger(t));
triggers = filterProblems(triggers);
triggers = sortTriggers(triggers);
return triggers;
};
const filterProblems = (problems: ProblemDTO[]) => {
let problemsList = _.cloneDeep(problems);
// Filter acknowledged triggers
if (showTriggers === 'unacknowledged') {
problemsList = problemsList.filter((trigger) => {
return !(trigger.acknowledges && trigger.acknowledges.length);
});
} else if (showTriggers === 'acknowledged') {
problemsList = problemsList.filter((trigger) => {
return trigger.acknowledges && trigger.acknowledges.length;
});
}
// Filter triggers by severity
problemsList = problemsList.filter((problem) => {
const severity = problem.severity !== undefined ? Number(problem.severity) : Number(problem.priority);
return triggerSeverity[severity].show;
});
return problemsList;
};
const sortTriggers = (problems: ProblemDTO[]) => {
if (sortProblems === 'priority') {
problems = _.orderBy(problems, ['severity', 'timestamp', 'eventid'], ['desc', 'desc', 'desc']);
} else if (sortProblems === 'lastchange') {
problems = _.orderBy(problems, ['timestamp', 'severity', 'eventid'], ['desc', 'desc', 'desc']);
}
return problems;
};
const formatTrigger = (zabbixTrigger: ProblemDTO) => {
const trigger = _.cloneDeep(zabbixTrigger);
// Set host and proxy that the trigger belongs
if (trigger.hosts && trigger.hosts.length) {
const host = trigger.hosts[0];
trigger.host = host.name;
trigger.hostTechName = host.host;
if (host.proxy) {
trigger.proxy = host.proxy;
}
}
// Set tags if present
if (trigger.tags && trigger.tags.length === 0) {
trigger.tags = undefined;
}
// Handle multi-line description
if (trigger.comments && options.allowDangerousHTML) {
trigger.comments = trigger.comments.replace('\n', '<br>');
}
trigger.lastchangeUnix = Number(trigger.lastchange);
return trigger;
};
const parseTags = (tagStr: string): ZBXTag[] => {
if (!tagStr) {
return [];
}
const tagStrings = _.map(tagStr.split(','), (tag) => tag.trim());
const tags = _.map(tagStrings, (tag) => {
const tagParts = tag.split(':');
return { tag: tagParts[0].trim(), value: tagParts[1].trim() };
});
return tags;
};
const tagsToString = (tags: ZBXTag[]) => {
return _.map(tags, (tag) => `${tag.tag}:${tag.value}`).join(', ');
};
const addTagFilter = (tag: ZBXTag, datasource: DataSourceRef) => {
const targets = data.request?.targets!;
let updated = false;
for (const target of targets) {
if (target.datasource?.uid === datasource?.uid || target.datasource === datasource) {
const tagFilter = (target as ZabbixMetricsQuery).tags?.filter!;
let targetTags = parseTags(tagFilter);
const newTag = { tag: tag.tag, value: tag.value };
targetTags.push(newTag);
targetTags = _.uniqWith(targetTags, _.isEqual);
const newFilter = tagsToString(targetTags);
(target as ZabbixMetricsQuery).tags!.filter = newFilter;
updated = true;
}
}
if (updated) {
// TODO: investigate is it possible to handle this event
const event = new ZBXQueryUpdatedEvent(targets);
props.eventBus.publish(event);
}
};
const removeTagFilter = (tag: ZBXTag, datasource: DataSourceRef) => {
const matchTag = (t: ZBXTag) => t.tag === tag.tag && t.value === tag.value;
const targets = data.request?.targets!;
let updated = false;
for (const target of targets) {
if (target.datasource?.uid === datasource?.uid || target.datasource === datasource) {
const tagFilter = (target as ZabbixMetricsQuery).tags?.filter!;
let targetTags = parseTags(tagFilter);
_.remove(targetTags, matchTag);
targetTags = _.uniqWith(targetTags, _.isEqual);
const newFilter = tagsToString(targetTags);
(target as ZabbixMetricsQuery).tags!.filter = newFilter;
updated = true;
}
}
if (updated) {
// TODO: investigate is it possible to handle this event
const event = new ZBXQueryUpdatedEvent(targets);
props.eventBus.publish(event);
}
};
const getProblemEvents = async (problem: ProblemDTO) => {
const triggerids = [problem.triggerid];
const timeFrom = Math.ceil(dateMath.parse(timeRange.from)!.unix());
const timeTo = Math.ceil(dateMath.parse(timeRange.to)!.unix());
const ds: any = await getDataSourceSrv().get(problem.datasource);
return ds.zabbix.getEvents(triggerids, timeFrom, timeTo, [0, 1], PROBLEM_EVENTS_LIMIT);
};
const getProblemAlerts = async (problem: ProblemDTO) => {
if (!problem.eventid) {
return Promise.resolve([]);
}
const eventids = [problem.eventid];
const ds: any = await getDataSourceSrv().get(problem.datasource);
return ds.zabbix.getEventAlerts(eventids);
};
const getScripts = async (problem: ProblemDTO) => {
const hostid = problem.hosts?.length ? problem.hosts[0].hostid : null;
const ds: any = await getDataSourceSrv().get(problem.datasource);
return ds.zabbix.getScripts([hostid]);
};
const onExecuteScript = async (problem: ProblemDTO, scriptid: string): Promise<APIExecuteScriptResponse> => {
const hostid = problem.hosts?.length ? problem.hosts[0].hostid : null;
const ds: any = await getDataSourceSrv().get(problem.datasource);
return ds.zabbix.executeScript(hostid, scriptid);
};
const onProblemAck = async (problem: ProblemDTO, data: AckProblemData) => {
const { message, action, severity } = data;
const eventid = problem.eventid;
const grafana_user = (contextSrv.user as any).name;
const ack_message = grafana_user + ' (Grafana): ' + message;
const ds: any = await getDataSourceSrv().get(problem.datasource);
const userIsEditor = contextSrv.isEditor || contextSrv.isGrafanaAdmin;
if (ds.disableReadOnlyUsersAck && !userIsEditor) {
return { message: 'You have no permissions to acknowledge events.' };
}
if (eventid) {
return ds.zabbix.acknowledgeEvent(eventid, ack_message, action, severity);
} else {
return { message: 'Trigger has no events. Nothing to acknowledge.' };
}
};
const onPageSizeChange = (pageSize: number, pageIndex?: number) => {
onOptionsChange({ ...options, pageSize });
};
const onColumnResize = (newResized: RTResized) => {
onOptionsChange({ ...options, resizedColumns: newResized });
};
const onTagClick = (tag: ZBXTag, datasource: DataSourceRef, ctrlKey?: boolean, shiftKey?: boolean) => {
if (ctrlKey || shiftKey) {
removeTagFilter(tag, datasource);
} else {
addTagFilter(tag, datasource);
}
};
const renderList = () => {
const problems = prepareProblems();
const fontSize = parseInt(options.fontSize.slice(0, options.fontSize.length - 1), 10);
const fontSizeProp = fontSize && fontSize !== 100 ? fontSize : undefined;
return (
<AlertList
problems={problems}
panelOptions={options}
pageSize={options.pageSize}
fontSize={fontSizeProp}
onProblemAck={onProblemAck}
onTagClick={onTagClick}
/>
);
};
const renderTable = () => {
const problems = prepareProblems();
const fontSize = parseInt(options.fontSize.slice(0, options.fontSize.length - 1), 10);
const fontSizeProp = fontSize && fontSize !== 100 ? fontSize : undefined;
return (
<ProblemList
problems={problems}
panelOptions={options}
pageSize={options.pageSize}
fontSize={fontSizeProp}
timeRange={timeRange}
getProblemEvents={getProblemEvents}
getProblemAlerts={getProblemAlerts}
getScripts={getScripts}
onExecuteScript={onExecuteScript}
onProblemAck={onProblemAck}
onColumnResize={onColumnResize}
onPageSizeChange={onPageSizeChange}
onTagClick={onTagClick}
/>
);
};
return (
<>
{layout === 'list' && renderList()}
{layout === 'table' && renderTable()}
</>
);
};