Problems count mode (#1493)

* Problems count mode

* Use tooltip from grafana ui

* Add editors for new modes

* Fix macro mode

* Fix bugs

* Unify editors to use one Triggers editor for all count queries

* Use time range toggle for triggers query, #918

* Add item tags suport for triggers count mode

* Fix triggers count by items

* Use data frames for triggers data, #1441

* Return empty result if no items found

* Add migration for problems count mode

* bump version to 4.3.0-pre

* Add zip task to makefile

* Add schema to query model

* Minor refactor

* Refactor: move components to separate files

* Minor refactor

* Support url in event tags

* Add tooltip with link url

* Update grafana packages

* Fix adding new problems panel

* ProblemDetails: rewrite as a functional component

* minor refactor
This commit is contained in:
Alexander Zobnin
2023-01-20 14:23:46 +01:00
committed by GitHub
parent 445b46a6aa
commit a5c239f77b
31 changed files with 2216 additions and 514 deletions

View File

@@ -241,10 +241,13 @@ export class ZabbixDatasource extends DataSourceApi<ZabbixMetricsQuery, ZabbixDS
return this.queryITServiceData(target, timeRange, request);
} else if (target.queryType === c.MODE_TRIGGERS) {
// Triggers query
return this.queryTriggersData(target, timeRange);
return this.queryTriggersData(target, timeRange, request);
} else if (target.queryType === c.MODE_PROBLEMS) {
// Problems query
return this.queryProblems(target, timeRange, request);
} else if (target.queryType === c.MODE_MACROS) {
// UserMacro query
return this.queryUserMacrosData(target);
} else {
return [];
}
@@ -254,7 +257,14 @@ export class ZabbixDatasource extends DataSourceApi<ZabbixMetricsQuery, ZabbixDS
return Promise.all(_.flatten(promises))
.then(_.flatten)
.then((data) => {
if (data && data.length > 0 && isDataFrame(data[0]) && !utils.isProblemsDataFrame(data[0])) {
if (
data &&
data.length > 0 &&
isDataFrame(data[0]) &&
!utils.isProblemsDataFrame(data[0]) &&
!utils.isMacrosDataFrame(data[0]) &&
!utils.nonTimeSeriesDataFrame(data[0])
) {
data = responseHandler.alignFrames(data);
if (responseHandler.isConvertibleToWide(data)) {
console.log('Converting response to the wide format');
@@ -504,31 +514,120 @@ export class ZabbixDatasource extends DataSourceApi<ZabbixMetricsQuery, ZabbixDS
return this.handleBackendPostProcessingResponse(processedResponse, request, target);
}
queryTriggersData(target, timeRange) {
async queryUserMacrosData(target) {
const groupFilter = target.group.filter;
const hostFilter = target.host.filter;
const macroFilter = target.macro.filter;
const macros = await this.zabbix.getUMacros(groupFilter, hostFilter, macroFilter);
const hostmacroids = _.map(macros, 'hostmacroid');
const userMacros = await this.zabbix.getUserMacros(hostmacroids);
return responseHandler.handleMacro(userMacros, target);
}
async queryTriggersData(target: ZabbixMetricsQuery, timeRange, request) {
if (target.countTriggersBy === 'items') {
return this.queryTriggersICData(target, timeRange);
} else if (target.countTriggersBy === 'problems') {
return this.queryTriggersPCData(target, timeRange, request);
}
const [hosts, apps] = await this.zabbix.getHostsApsFromTarget(target);
if (!hosts.length) {
return Promise.resolve([]);
}
const groupFilter = target.group.filter;
const groups = await this.zabbix.getGroups(groupFilter);
const hostids = hosts?.map((h) => h.hostid);
const appids = apps?.map((a) => a.applicationid);
const options = getTriggersOptions(target, timeRange);
const alerts = await this.zabbix.getHostAlerts(hostids, appids, options);
return responseHandler.handleTriggersResponse(alerts, groups, timeRange, target);
}
async queryTriggersICData(target, timeRange) {
const getItemOptions = { itemtype: 'num' };
const [hosts, apps, items] = await this.zabbix.getHostsFromICTarget(target, getItemOptions);
if (!hosts.length) {
return Promise.resolve([]);
}
const groupFilter = target.group.filter;
const groups = await this.zabbix.getGroups(groupFilter);
const hostids = hosts?.map((h) => h.hostid);
const appids = apps?.map((a) => a.applicationid);
const itemids = items?.map((i) => i.itemid);
if (!itemids.length) {
return Promise.resolve([]);
}
const options = getTriggersOptions(target, timeRange);
const alerts = await this.zabbix.getHostICAlerts(hostids, appids, itemids, options);
return responseHandler.handleTriggersResponse(alerts, groups, timeRange, target);
}
async queryTriggersPCData(target: ZabbixMetricsQuery, timeRange, request) {
const [timeFrom, timeTo] = timeRange;
return this.zabbix.getHostsFromTarget(target).then((results) => {
const [hosts, apps] = results;
if (hosts.length) {
const hostids = _.map(hosts, 'hostid');
const appids = _.map(apps, 'applicationid');
const options = {
minSeverity: target.triggers.minSeverity,
acknowledged: target.triggers.acknowledged,
count: target.triggers.count,
timeFrom: timeFrom,
timeTo: timeTo,
};
const groupFilter = target.group.filter;
return Promise.all([
this.zabbix.getHostAlerts(hostids, appids, options),
this.zabbix.getGroups(groupFilter),
]).then(([triggers, groups]) => {
return responseHandler.handleTriggersResponse(triggers, groups, timeRange);
});
} else {
return Promise.resolve([]);
}
const tagsFilter = this.replaceTemplateVars(target.tags?.filter, request.scopedVars);
// replaceTemplateVars() builds regex-like string, so we should trim it.
const tagsFilterStr = tagsFilter.replace('/^', '').replace('$/', '');
const tags = utils.parseTags(tagsFilterStr);
tags.forEach((tag) => {
// Zabbix uses {"tag": "<tag>", "value": "<value>", "operator": "<operator>"} format, where 1 means Equal
tag.operator = 1;
});
const problemsOptions: any = {
minSeverity: target.options?.minSeverity,
limit: target.options?.limit,
};
if (tags && tags.length) {
problemsOptions.tags = tags;
}
if (target.options?.acknowledged === 0 || target.options?.acknowledged === 1) {
problemsOptions.acknowledged = target.options?.acknowledged ? true : false;
}
if (target.options?.minSeverity) {
let severities = [0, 1, 2, 3, 4, 5].filter((v) => v >= target.options?.minSeverity);
if (target.options?.severities) {
severities = severities.filter((v) => target.options?.severities.includes(v));
}
problemsOptions.severities = severities;
}
if (target.options.useTimeRange) {
problemsOptions.timeFrom = timeFrom;
problemsOptions.timeTo = timeTo;
}
const [hosts, apps, triggers] = await this.zabbix.getHostsFromPCTarget(target, problemsOptions);
if (!hosts.length) {
return Promise.resolve([]);
}
const groupFilter = target.group.filter;
const groups = await this.zabbix.getGroups(groupFilter);
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,
count: target.options.count,
};
if (target.options.useTimeRange) {
options.timeFrom = timeFrom;
options.timeTo = timeTo;
}
const alerts = await this.zabbix.getHostPCAlerts(hostids, appids, triggerids, options);
return responseHandler.handleTriggersResponse(alerts, groups, timeRange, target);
}
queryProblems(target: ZabbixMetricsQuery, timeRange, options) {
@@ -742,6 +841,7 @@ export class ZabbixDatasource extends DataSourceApi<ZabbixMetricsQuery, ZabbixDS
templateSrv.variableExists(target.application?.filter) ||
templateSrv.variableExists(target.itemTag?.filter) ||
templateSrv.variableExists(target.item?.filter) ||
templateSrv.variableExists(target.macro?.filter) ||
templateSrv.variableExists(target.proxy?.filter) ||
templateSrv.variableExists(target.trigger?.filter) ||
templateSrv.variableExists(target.textFilter) ||
@@ -972,3 +1072,17 @@ function getRequestTarget(request: DataQueryRequest<any>, refId: string): any {
}
return null;
}
const getTriggersOptions = (target: ZabbixMetricsQuery, timeRange) => {
const [timeFrom, timeTo] = timeRange;
const options: any = {
minSeverity: target.options?.minSeverity,
acknowledged: target.options?.acknowledged,
count: target.options?.count,
};
if (target.options?.useTimeRange) {
options.timeFrom = timeFrom;
options.timeTo = timeTo;
}
return options;
};