Migrate from DatasourceAPI to DatasourceWithBackend (#2123)
This PR migrates the use of `DatasourceApi` to `DatasourceWithBackend`, with this a couple additional improvements were made: 1. Migrate to use `interpolateVariablesInQuery` everywhere instead of the custom `replaceTemplateVariables` we were using 2. Moves util functions out of `datasource.ts` and into the existing `utils.ts` <img width="1261" height="406" alt="Screenshot 2025-11-20 at 11 37 56 AM" src="https://github.com/user-attachments/assets/9e396cf2-eab0-49d1-958c-963a2e896eba" /> Now we can see the `query` calls being made to the backend: <img width="367" height="102" alt="Screenshot 2025-11-20 at 11 38 18 AM" src="https://github.com/user-attachments/assets/a5a9a337-7f19-4f7c-9d04-9d30c0216fb2" /> Tested: - By running queries from Explore and Dashboards (with and without variables) - By interacting with all the different Editors to make sure `ComboBox` was working as expected Next: Once this is merged, we will next be able to slowly move away from using the `ZabbixConnector` to make backend datasource calls. Fixes: [#131](https://github.com/orgs/grafana/projects/457/views/40?pane=issue&itemId=139450234&issue=grafana%7Coss-big-tent-squad%7C131)
This commit is contained in:
committed by
GitHub
parent
cc492b916d
commit
ce4a8d3e19
@@ -6,7 +6,6 @@ import * as utils from './utils';
|
||||
import * as migrations from './migrations';
|
||||
import * as metricFunctions from './metricFunctions';
|
||||
import * as c from './constants';
|
||||
import dataProcessor from './dataProcessor';
|
||||
import responseHandler from './responseHandler';
|
||||
import problemsHandler from './problemsHandler';
|
||||
import { Zabbix } from './zabbix/zabbix';
|
||||
@@ -18,28 +17,27 @@ import {
|
||||
BackendSrvRequest,
|
||||
getBackendSrv,
|
||||
getTemplateSrv,
|
||||
toDataQueryResponse,
|
||||
getDataSourceSrv,
|
||||
HealthCheckError,
|
||||
DataSourceWithBackend,
|
||||
TemplateSrv,
|
||||
} from '@grafana/runtime';
|
||||
import {
|
||||
DataFrame,
|
||||
dataFrameFromJSON,
|
||||
DataQueryRequest,
|
||||
DataQueryResponse,
|
||||
DataSourceApi,
|
||||
DataSourceInstanceSettings,
|
||||
FieldType,
|
||||
isDataFrame,
|
||||
LoadingState,
|
||||
ScopedVars,
|
||||
toDataFrame,
|
||||
} from '@grafana/data';
|
||||
import { AnnotationQueryEditor } from './components/AnnotationQueryEditor';
|
||||
import { trackRequest } from './tracking';
|
||||
import { lastValueFrom } from 'rxjs';
|
||||
import { lastValueFrom, map, Observable } from 'rxjs';
|
||||
|
||||
export class ZabbixDatasource extends DataSourceApi<ZabbixMetricsQuery, ZabbixDSOptions> {
|
||||
export class ZabbixDatasource extends DataSourceWithBackend<ZabbixMetricsQuery, ZabbixDSOptions> {
|
||||
name: string;
|
||||
basicAuth: any;
|
||||
withCredentials: any;
|
||||
@@ -59,9 +57,10 @@ export class ZabbixDatasource extends DataSourceApi<ZabbixMetricsQuery, ZabbixDS
|
||||
instanceSettings: DataSourceInstanceSettings<ZabbixDSOptions>;
|
||||
zabbix: Zabbix;
|
||||
|
||||
replaceTemplateVars: (target: any, scopedVars?: any) => any;
|
||||
|
||||
constructor(instanceSettings: DataSourceInstanceSettings<ZabbixDSOptions>) {
|
||||
constructor(
|
||||
instanceSettings: DataSourceInstanceSettings<ZabbixDSOptions>,
|
||||
private readonly templateSrv: TemplateSrv = getTemplateSrv()
|
||||
) {
|
||||
super(instanceSettings);
|
||||
|
||||
this.instanceSettings = instanceSettings;
|
||||
@@ -72,10 +71,6 @@ export class ZabbixDatasource extends DataSourceApi<ZabbixMetricsQuery, ZabbixDS
|
||||
prepareAnnotation: migrations.prepareAnnotation,
|
||||
};
|
||||
|
||||
// Use custom format for template variables
|
||||
const templateSrv = getTemplateSrv();
|
||||
this.replaceTemplateVars = _.partial(replaceTemplateVars, templateSrv);
|
||||
|
||||
// General data source settings
|
||||
this.datasourceId = instanceSettings.id;
|
||||
this.name = instanceSettings.name;
|
||||
@@ -120,13 +115,12 @@ export class ZabbixDatasource extends DataSourceApi<ZabbixMetricsQuery, ZabbixDS
|
||||
////////////////////////
|
||||
// Datasource methods //
|
||||
////////////////////////
|
||||
|
||||
/**
|
||||
* Query panel data. Calls for each panel in dashboard.
|
||||
* @param {Object} request Contains time range, targets and other info.
|
||||
* @return {Object} Grafana metrics object with timeseries data for each target.
|
||||
*/
|
||||
query(request: DataQueryRequest<ZabbixMetricsQuery>) {
|
||||
query(request: DataQueryRequest<ZabbixMetricsQuery>): Observable<DataQueryResponse> {
|
||||
trackRequest(request);
|
||||
|
||||
// Migrate old targets
|
||||
@@ -144,104 +138,33 @@ export class ZabbixDatasource extends DataSourceApi<ZabbixMetricsQuery, ZabbixDS
|
||||
return target;
|
||||
});
|
||||
|
||||
const backendResponsePromise = this.backendQuery({ ...request, targets: requestTargets });
|
||||
const dbConnectionResponsePromise = this.dbConnectionQuery({ ...request, targets: requestTargets });
|
||||
const frontendResponsePromise = this.frontendQuery({ ...request, targets: requestTargets });
|
||||
const annotationResposePromise = this.annotationRequest({ ...request, targets: requestTargets });
|
||||
const interpolatedTargets = this.interpolateVariablesInQueries(requestTargets, request.scopedVars);
|
||||
const backendResponse = super.query({ ...request, targets: interpolatedTargets.filter(this.isBackendTarget) });
|
||||
const dbConnectionResponsePromise = this.dbConnectionQuery({ ...request, targets: interpolatedTargets });
|
||||
const frontendResponsePromise = this.frontendQuery({ ...request, targets: interpolatedTargets });
|
||||
const annotationResposePromise = this.annotationRequest({ ...request, targets: interpolatedTargets });
|
||||
|
||||
return Promise.all([
|
||||
backendResponsePromise,
|
||||
dbConnectionResponsePromise,
|
||||
frontendResponsePromise,
|
||||
annotationResposePromise,
|
||||
]).then((rsp) => {
|
||||
// Merge backend and frontend queries results
|
||||
const [backendRes, dbConnectionRes, frontendRes, annotationRes] = rsp;
|
||||
if (dbConnectionRes.data) {
|
||||
backendRes.data = backendRes.data.concat(dbConnectionRes.data);
|
||||
}
|
||||
if (frontendRes.data) {
|
||||
backendRes.data = backendRes.data.concat(frontendRes.data);
|
||||
}
|
||||
const applyMergeQueries = (queryResponse: DataQueryResponse) =>
|
||||
this.mergeQueries(queryResponse, dbConnectionResponsePromise, frontendResponsePromise, annotationResposePromise);
|
||||
const applyFEFuncs = (queryResponse: DataQueryResponse) =>
|
||||
this.applyFrontendFunctions(queryResponse, {
|
||||
...request,
|
||||
targets: interpolatedTargets.filter(this.isBackendTarget),
|
||||
});
|
||||
|
||||
if (annotationRes.data) {
|
||||
backendRes.data = backendRes.data.concat(annotationRes.data);
|
||||
}
|
||||
|
||||
return {
|
||||
data: backendRes.data,
|
||||
state: LoadingState.Done,
|
||||
key: request.requestId,
|
||||
};
|
||||
});
|
||||
}
|
||||
|
||||
async backendQuery(request: DataQueryRequest<any>): Promise<DataQueryResponse> {
|
||||
const { intervalMs, maxDataPoints, range, requestId } = request;
|
||||
const targets = request.targets.filter(this.isBackendTarget);
|
||||
|
||||
// Add range variables
|
||||
request.scopedVars = Object.assign({}, request.scopedVars, utils.getRangeScopedVars(request.range));
|
||||
|
||||
const queries = _.compact(
|
||||
targets.map((query) => {
|
||||
// Don't request for hidden targets
|
||||
if (query.hide) {
|
||||
return null;
|
||||
}
|
||||
|
||||
this.replaceTargetVariables(query, request);
|
||||
|
||||
return {
|
||||
...query,
|
||||
datasourceId: this.datasourceId,
|
||||
intervalMs,
|
||||
maxDataPoints,
|
||||
};
|
||||
})
|
||||
return backendResponse.pipe(
|
||||
map(applyFEFuncs),
|
||||
map(responseHandler.convertZabbixUnits),
|
||||
map(this.convertToWide),
|
||||
map(applyMergeQueries)
|
||||
);
|
||||
|
||||
// Return early if no queries exist
|
||||
if (!queries.length) {
|
||||
return Promise.resolve({ data: [] });
|
||||
}
|
||||
|
||||
const body: any = { queries };
|
||||
|
||||
if (range) {
|
||||
body.range = range;
|
||||
body.from = range.from.valueOf().toString();
|
||||
body.to = range.to.valueOf().toString();
|
||||
}
|
||||
|
||||
let rsp: any;
|
||||
try {
|
||||
rsp = await lastValueFrom(
|
||||
getBackendSrv().fetch({
|
||||
url: '/api/ds/query',
|
||||
method: 'POST',
|
||||
data: body,
|
||||
requestId,
|
||||
})
|
||||
);
|
||||
} catch (err) {
|
||||
return toDataQueryResponse(err);
|
||||
}
|
||||
|
||||
const resp = toDataQueryResponse(rsp);
|
||||
this.sortByRefId(resp);
|
||||
this.applyFrontendFunctions(resp, request);
|
||||
responseHandler.convertZabbixUnits(resp);
|
||||
if (responseHandler.isConvertibleToWide(resp.data)) {
|
||||
console.log('Converting response to the wide format');
|
||||
resp.data = responseHandler.convertToWide(resp.data);
|
||||
}
|
||||
|
||||
return resp;
|
||||
}
|
||||
|
||||
async frontendQuery(request: DataQueryRequest<any>): Promise<DataQueryResponse> {
|
||||
async frontendQuery(request: DataQueryRequest<ZabbixMetricsQuery>): Promise<DataQueryResponse> {
|
||||
const frontendTargets = request.targets.filter((t) => !(this.isBackendTarget(t) || this.isDBConnectionTarget(t)));
|
||||
const oldVersionTargets = frontendTargets
|
||||
.filter((target) => (target as any).itservice && !target.itServiceFilter)
|
||||
.map((t) => t.refId);
|
||||
const promises = _.map(frontendTargets, (target) => {
|
||||
// Don't request for hidden targets
|
||||
if (target.hide) {
|
||||
@@ -250,7 +173,6 @@ export class ZabbixDatasource extends DataSourceApi<ZabbixMetricsQuery, ZabbixDS
|
||||
|
||||
// Add range variables
|
||||
request.scopedVars = Object.assign({}, request.scopedVars, utils.getRangeScopedVars(request.range));
|
||||
this.replaceTargetVariables(target, request);
|
||||
const timeRange = this.buildTimeRange(request, target);
|
||||
|
||||
if (target.queryType === c.MODE_TEXT) {
|
||||
@@ -262,7 +184,8 @@ export class ZabbixDatasource extends DataSourceApi<ZabbixMetricsQuery, ZabbixDS
|
||||
return this.queryTextData(target, timeRange);
|
||||
} else if (target.queryType === c.MODE_ITSERVICE) {
|
||||
// IT services query
|
||||
return this.queryITServiceData(target, timeRange, request);
|
||||
const isOldVersion = oldVersionTargets.includes(target.refId);
|
||||
return this.queryITServiceData(target, timeRange, request, isOldVersion);
|
||||
} else if (target.queryType === c.MODE_TRIGGERS) {
|
||||
// Triggers query
|
||||
return this.queryTriggersData(target, timeRange, request);
|
||||
@@ -311,7 +234,6 @@ export class ZabbixDatasource extends DataSourceApi<ZabbixMetricsQuery, ZabbixDS
|
||||
|
||||
// Add range variables
|
||||
request.scopedVars = Object.assign({}, request.scopedVars, utils.getRangeScopedVars(request.range));
|
||||
this.replaceTargetVariables(target, request);
|
||||
const timeRange = this.buildTimeRange(request, target);
|
||||
const useTrends = this.isUseTrends(timeRange, target);
|
||||
|
||||
@@ -341,7 +263,7 @@ export class ZabbixDatasource extends DataSourceApi<ZabbixMetricsQuery, ZabbixDS
|
||||
let timeTo = Math.ceil(dateMath.parse(request.range.to) / 1000);
|
||||
|
||||
// Apply Time-related functions (timeShift(), etc)
|
||||
const timeFunctions = bindFunctionDefs(target.functions, 'Time');
|
||||
const timeFunctions = utils.bindFunctionDefs(target.functions, 'Time');
|
||||
if (timeFunctions.length) {
|
||||
const [time_from, time_to] = utils.sequence(timeFunctions)([timeFrom, timeTo]);
|
||||
timeFrom = time_from;
|
||||
@@ -377,7 +299,7 @@ export class ZabbixDatasource extends DataSourceApi<ZabbixMetricsQuery, ZabbixDS
|
||||
async queryNumericDataForItems(items, target: ZabbixMetricsQuery, timeRange, useTrends, request) {
|
||||
let history;
|
||||
request.valueType = this.getTrendValueType(target);
|
||||
request.consolidateBy = getConsolidateBy(target) || request.valueType;
|
||||
request.consolidateBy = utils.getConsolidateBy(target) || request.valueType;
|
||||
|
||||
if (useTrends) {
|
||||
history = await this.zabbix.getTrends(items, timeRange, request);
|
||||
@@ -454,10 +376,10 @@ export class ZabbixDatasource extends DataSourceApi<ZabbixMetricsQuery, ZabbixDS
|
||||
applyFrontendFunctions(response: DataQueryResponse, request: DataQueryRequest<any>) {
|
||||
for (let i = 0; i < response.data.length; i++) {
|
||||
const frame: DataFrame = response.data[i];
|
||||
const target = getRequestTarget(request, frame.refId);
|
||||
const target = utils.getRequestTarget(request, frame.refId);
|
||||
|
||||
// Apply alias functions
|
||||
const aliasFunctions = bindFunctionDefs(target.functions, 'Alias');
|
||||
const aliasFunctions = utils.bindFunctionDefs(target.functions, 'Alias');
|
||||
utils.sequence(aliasFunctions)(frame);
|
||||
}
|
||||
return response;
|
||||
@@ -489,7 +411,7 @@ export class ZabbixDatasource extends DataSourceApi<ZabbixMetricsQuery, ZabbixDS
|
||||
queryItemIdData(target, timeRange, useTrends, options) {
|
||||
let itemids = target.itemids;
|
||||
const templateSrv = getTemplateSrv();
|
||||
itemids = templateSrv.replace(itemids, options.scopedVars, zabbixItemIdsTemplateFormat);
|
||||
itemids = templateSrv.replace(itemids, options.scopedVars, utils.zabbixItemIdsTemplateFormat);
|
||||
itemids = _.map(itemids.split(','), (itemid) => itemid.trim());
|
||||
|
||||
if (!itemids) {
|
||||
@@ -504,34 +426,26 @@ export class ZabbixDatasource extends DataSourceApi<ZabbixMetricsQuery, ZabbixDS
|
||||
/**
|
||||
* Query target data for IT Services
|
||||
*/
|
||||
async queryITServiceData(target: ZabbixMetricsQuery, timeRange, request) {
|
||||
async queryITServiceData(
|
||||
target: ZabbixMetricsQuery,
|
||||
timeRange: number[],
|
||||
request: DataQueryRequest<ZabbixMetricsQuery>,
|
||||
isOldVersion: boolean
|
||||
) {
|
||||
// Don't show undefined and hidden targets
|
||||
if (target.hide || (!(target as any).itservice && !target.itServiceFilter) || !target.slaProperty) {
|
||||
return [];
|
||||
}
|
||||
|
||||
let itServiceFilter;
|
||||
request.isOldVersion = (target as any).itservice && !target.itServiceFilter;
|
||||
|
||||
if (request.isOldVersion) {
|
||||
// Backward compatibility
|
||||
itServiceFilter = '/.*/';
|
||||
} else {
|
||||
itServiceFilter = this.replaceTemplateVars(target.itServiceFilter, request.scopedVars);
|
||||
}
|
||||
|
||||
request.slaInterval = target.slaInterval;
|
||||
|
||||
let itservices = await this.zabbix.getITServices(itServiceFilter);
|
||||
if (request.isOldVersion) {
|
||||
let itservices = await this.zabbix.getITServices(target.itServiceFilter);
|
||||
if (isOldVersion) {
|
||||
itservices = _.filter(itservices, { serviceid: (target as any).itservice?.serviceid });
|
||||
}
|
||||
if (target.slaFilter !== undefined) {
|
||||
const slaFilter = this.replaceTemplateVars(target.slaFilter, request.scopedVars);
|
||||
const slas = await this.zabbix.getSLAs(slaFilter);
|
||||
const slas = await this.zabbix.getSLAs(target.slaFilter);
|
||||
const result = await this.zabbix.getSLI(itservices, slas, timeRange, target, request);
|
||||
// Apply alias functions
|
||||
const aliasFunctions = bindFunctionDefs(target.functions, 'Alias');
|
||||
const aliasFunctions = utils.bindFunctionDefs(target.functions, 'Alias');
|
||||
utils.sequence(aliasFunctions)(result);
|
||||
return result;
|
||||
}
|
||||
@@ -568,11 +482,10 @@ export class ZabbixDatasource extends DataSourceApi<ZabbixMetricsQuery, ZabbixDS
|
||||
|
||||
const hostids = hosts?.map((h) => h.hostid);
|
||||
const appids = apps?.map((a) => a.applicationid);
|
||||
const options = getTriggersOptions(target, timeRange);
|
||||
const options = utils.getTriggersOptions(target, timeRange);
|
||||
|
||||
const tagsFilter = this.replaceTemplateVars(target.tags?.filter, request.scopedVars);
|
||||
// replaceTemplateVars() builds regex-like string, so we should trim it.
|
||||
const tagsFilterStr = tagsFilter.replace('/^', '').replace('$/', '');
|
||||
// variable interpolation builds regex-like string, so we should trim it.
|
||||
const tagsFilterStr = target.tags.filter.replace('/^', '').replace('$/', '');
|
||||
const tags = utils.parseTags(tagsFilterStr);
|
||||
tags.forEach((tag) => {
|
||||
// Zabbix uses {"tag": "<tag>", "value": "<value>", "operator": "<operator>"} format, where 1 means Equal
|
||||
@@ -603,16 +516,15 @@ export class ZabbixDatasource extends DataSourceApi<ZabbixMetricsQuery, ZabbixDS
|
||||
return Promise.resolve([]);
|
||||
}
|
||||
|
||||
const options = getTriggersOptions(target, timeRange);
|
||||
const options = utils.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;
|
||||
const tagsFilter = this.replaceTemplateVars(target.tags?.filter, request.scopedVars);
|
||||
// replaceTemplateVars() builds regex-like string, so we should trim it.
|
||||
const tagsFilterStr = tagsFilter.replace('/^', '').replace('$/', '');
|
||||
// variable interpolation builds regex-like string, so we should trim it.
|
||||
const tagsFilterStr = target.tags.filter.replace('/^', '').replace('$/', '');
|
||||
const tags = utils.parseTags(tagsFilterStr);
|
||||
tags.forEach((tag) => {
|
||||
// Zabbix uses {"tag": "<tag>", "value": "<value>", "operator": "<operator>"} format, where 1 means Equal
|
||||
@@ -682,23 +594,8 @@ export class ZabbixDatasource extends DataSourceApi<ZabbixMetricsQuery, ZabbixDS
|
||||
const getProxiesPromise = showProxy ? this.zabbix.getProxies() : () => [];
|
||||
showAckButton = !this.disableReadOnlyUsersAck || userIsEditor;
|
||||
|
||||
// Replace template variables
|
||||
const groupFilter = this.replaceTemplateVars(target.group?.filter, options.scopedVars);
|
||||
const hostFilter = this.replaceTemplateVars(target.host?.filter, options.scopedVars);
|
||||
const appFilter = this.replaceTemplateVars(target.application?.filter, options.scopedVars);
|
||||
const proxyFilter = this.replaceTemplateVars(target.proxy?.filter, options.scopedVars);
|
||||
|
||||
const triggerFilter = this.replaceTemplateVars(target.trigger?.filter, options.scopedVars);
|
||||
const tagsFilter = this.replaceTemplateVars(target.tags?.filter, options.scopedVars);
|
||||
|
||||
const replacedTarget = {
|
||||
...target,
|
||||
trigger: { filter: triggerFilter },
|
||||
tags: { filter: tagsFilter },
|
||||
};
|
||||
|
||||
// replaceTemplateVars() builds regex-like string, so we should trim it.
|
||||
const tagsFilterStr = tagsFilter.replace('/^', '').replace('$/', '');
|
||||
const tagsFilterStr = target.tags.filter.replace('/^', '').replace('$/', '');
|
||||
const tags = utils.parseTags(tagsFilterStr);
|
||||
tags.forEach((tag) => {
|
||||
// Zabbix uses {"tag": "<tag>", "value": "<value>", "operator": "<operator>"} format, where 1 means Equal
|
||||
@@ -733,14 +630,20 @@ export class ZabbixDatasource extends DataSourceApi<ZabbixMetricsQuery, ZabbixDS
|
||||
problemsOptions.timeFrom = timeFrom;
|
||||
problemsOptions.timeTo = timeTo;
|
||||
getProblemsPromise = this.zabbix.getProblemsHistory(
|
||||
groupFilter,
|
||||
hostFilter,
|
||||
appFilter,
|
||||
proxyFilter,
|
||||
target.group.filter,
|
||||
target.host.filter,
|
||||
target.application.filter,
|
||||
target.proxy.filter,
|
||||
problemsOptions
|
||||
);
|
||||
} else {
|
||||
getProblemsPromise = this.zabbix.getProblems(groupFilter, hostFilter, appFilter, proxyFilter, problemsOptions);
|
||||
getProblemsPromise = this.zabbix.getProblems(
|
||||
target.group.filter,
|
||||
target.host.filter,
|
||||
target.application.filter,
|
||||
target.proxy.filter,
|
||||
problemsOptions
|
||||
);
|
||||
}
|
||||
const getUsersPromise = this.zabbix.getUsers();
|
||||
|
||||
@@ -754,7 +657,7 @@ export class ZabbixDatasource extends DataSourceApi<ZabbixMetricsQuery, ZabbixDS
|
||||
})
|
||||
.then((problems) => problemsHandler.setMaintenanceStatus(problems))
|
||||
.then((problems) => problemsHandler.setAckButtonStatus(problems, showAckButton))
|
||||
.then((problems) => problemsHandler.filterTriggersPre(problems, replacedTarget))
|
||||
.then((problems) => problemsHandler.filterTriggersPre(problems, target))
|
||||
.then((problems) => problemsHandler.sortProblems(problems, target))
|
||||
.then((problems) => problemsHandler.addTriggerDataSource(problems, target))
|
||||
.then((problems) => problemsHandler.formatAcknowledges(problems, zabbixUsers))
|
||||
@@ -770,9 +673,8 @@ export class ZabbixDatasource extends DataSourceApi<ZabbixMetricsQuery, ZabbixDS
|
||||
* Test connection to Zabbix API and external history DB.
|
||||
*/
|
||||
async testDatasource() {
|
||||
const backendDS = new DataSourceWithBackend(this.instanceSettings);
|
||||
try {
|
||||
const testResult = await backendDS.testDatasource();
|
||||
const testResult = await super.testDatasource();
|
||||
return this.zabbix.testDataSource().then((dbConnectorStatus) => {
|
||||
let message = testResult.message;
|
||||
if (dbConnectorStatus) {
|
||||
@@ -839,7 +741,7 @@ export class ZabbixDatasource extends DataSourceApi<ZabbixMetricsQuery, ZabbixDS
|
||||
}
|
||||
|
||||
for (const prop of ['group', 'host', 'application', 'itemTag', 'item']) {
|
||||
queryModel[prop] = this.replaceTemplateVars(queryModel[prop], {});
|
||||
queryModel[prop] = utils.replaceTemplateVars(this.templateSrv, queryModel[prop], {});
|
||||
}
|
||||
|
||||
queryModel = queryModel as VariableQuery;
|
||||
@@ -878,7 +780,7 @@ export class ZabbixDatasource extends DataSourceApi<ZabbixMetricsQuery, ZabbixDS
|
||||
}
|
||||
|
||||
return resultPromise.then((metrics) => {
|
||||
return _.map(metrics, formatMetric);
|
||||
return _.map(metrics, utils.formatMetric);
|
||||
});
|
||||
}
|
||||
|
||||
@@ -931,16 +833,16 @@ export class ZabbixDatasource extends DataSourceApi<ZabbixMetricsQuery, ZabbixDS
|
||||
problemsOptions.severities = severities;
|
||||
}
|
||||
|
||||
const groupFilter = this.replaceTemplateVars(annotation.group.filter, {});
|
||||
const hostFilter = this.replaceTemplateVars(annotation.host.filter, {});
|
||||
const appFilter = this.replaceTemplateVars(annotation.application.filter, {});
|
||||
const groupFilter = annotation.group.filter;
|
||||
const hostFilter = annotation.host.filter;
|
||||
const appFilter = annotation.application.filter;
|
||||
const proxyFilter = undefined;
|
||||
|
||||
return this.zabbix
|
||||
.getProblemsHistory(groupFilter, hostFilter, appFilter, proxyFilter, problemsOptions)
|
||||
.then((problems) => {
|
||||
// Filter triggers by description
|
||||
const problemName = this.replaceTemplateVars(annotation.trigger.filter, {});
|
||||
const problemName = annotation.trigger.filter;
|
||||
if (utils.isRegex(problemName)) {
|
||||
problems = _.filter(problems, (p) => {
|
||||
return utils.buildRegex(problemName).test(p.description);
|
||||
@@ -977,35 +879,6 @@ export class ZabbixDatasource extends DataSourceApi<ZabbixMetricsQuery, ZabbixDS
|
||||
});
|
||||
}
|
||||
|
||||
// Replace template variables
|
||||
replaceTargetVariables(target, options) {
|
||||
const templateSrv = getTemplateSrv();
|
||||
const parts = ['group', 'host', 'application', 'itemTag', 'item'];
|
||||
_.forEach(parts, (p) => {
|
||||
if (target[p] && target[p].filter) {
|
||||
target[p].filter = this.replaceTemplateVars(target[p].filter, options.scopedVars);
|
||||
}
|
||||
});
|
||||
|
||||
if (target.textFilter) {
|
||||
target.textFilter = this.replaceTemplateVars(target.textFilter, options.scopedVars);
|
||||
}
|
||||
|
||||
if (target.itemids) {
|
||||
target.itemids = templateSrv.replace(target.itemids, options.scopedVars, zabbixItemIdsTemplateFormat);
|
||||
}
|
||||
|
||||
_.forEach(target.functions, (func) => {
|
||||
func.params = _.map(func.params, (param) => {
|
||||
if (typeof param === 'number') {
|
||||
return +templateSrv.replace(param.toString(), options.scopedVars);
|
||||
} else {
|
||||
return templateSrv.replace(param, options.scopedVars);
|
||||
}
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
isUseTrends(timeRange, target: ZabbixMetricsQuery) {
|
||||
if (target.options.useTrends === 'false') {
|
||||
return false;
|
||||
@@ -1030,108 +903,89 @@ export class ZabbixDatasource extends DataSourceApi<ZabbixMetricsQuery, ZabbixDS
|
||||
isDBConnectionTarget = (target: any): boolean => {
|
||||
return this.enableDirectDBConnection && (target.queryType === c.MODE_METRICS || target.queryType === c.MODE_ITEMID);
|
||||
};
|
||||
}
|
||||
|
||||
function bindFunctionDefs(functionDefs, category) {
|
||||
const aggregationFunctions = _.map(metricFunctions.getCategories()[category], 'name');
|
||||
const aggFuncDefs = _.filter(functionDefs, (func) => {
|
||||
return _.includes(aggregationFunctions, func.def.name) && func.params.length > 0;
|
||||
});
|
||||
mergeQueries(
|
||||
queryResponse: DataQueryResponse,
|
||||
dbConnectionResponsePromise: Promise<DataQueryResponse>,
|
||||
frontendResponsePromise: Promise<DataQueryResponse>,
|
||||
annotationResposePromise: Promise<DataQueryResponse>
|
||||
): DataQueryResponse {
|
||||
Promise.all([dbConnectionResponsePromise, frontendResponsePromise, annotationResposePromise]).then((resp) => {
|
||||
const [dbConnectionRes, frontendRes, annotationRes] = resp;
|
||||
if (dbConnectionRes.data) {
|
||||
queryResponse.data = queryResponse.data.concat(dbConnectionRes.data);
|
||||
}
|
||||
if (frontendRes.data) {
|
||||
queryResponse.data = queryResponse.data.concat(frontendRes.data);
|
||||
}
|
||||
|
||||
return _.map(aggFuncDefs, (func) => {
|
||||
const funcInstance = metricFunctions.createFuncInstance(func.def, func.params);
|
||||
return funcInstance.bindFunction(dataProcessor.metricFunctions);
|
||||
});
|
||||
}
|
||||
|
||||
function getConsolidateBy(target) {
|
||||
let consolidateBy;
|
||||
const funcDef = _.find(target.functions, (func) => {
|
||||
return func.def.name === 'consolidateBy';
|
||||
});
|
||||
if (funcDef && funcDef.params && funcDef.params.length) {
|
||||
consolidateBy = funcDef.params[0];
|
||||
}
|
||||
return consolidateBy;
|
||||
}
|
||||
|
||||
function formatMetric(metricObj) {
|
||||
return {
|
||||
text: metricObj.name,
|
||||
expandable: false,
|
||||
};
|
||||
}
|
||||
|
||||
/**
|
||||
* Custom formatter for template variables.
|
||||
* Default Grafana "regex" formatter returns
|
||||
* value1|value2
|
||||
* This formatter returns
|
||||
* (value1|value2)
|
||||
* This format needed for using in complex regex with
|
||||
* template variables, for example
|
||||
* /CPU $cpu_item.*time/ where $cpu_item is system,user,iowait
|
||||
*/
|
||||
export function zabbixTemplateFormat(value) {
|
||||
if (typeof value === 'string') {
|
||||
return utils.escapeRegex(value);
|
||||
if (annotationRes.data) {
|
||||
queryResponse.data = queryResponse.data.concat(annotationRes.data);
|
||||
}
|
||||
});
|
||||
return queryResponse;
|
||||
}
|
||||
|
||||
const escapedValues = _.map(value, utils.escapeRegex);
|
||||
return '(' + escapedValues.join('|') + ')';
|
||||
}
|
||||
|
||||
function zabbixItemIdsTemplateFormat(value) {
|
||||
if (typeof value === 'string') {
|
||||
return value;
|
||||
}
|
||||
return value.join(',');
|
||||
}
|
||||
|
||||
/**
|
||||
* If template variables are used in request, replace it using regex format
|
||||
* and wrap with '/' for proper multi-value work. Example:
|
||||
* $variable selected as a, b, c
|
||||
* We use filter $variable
|
||||
* $variable -> a|b|c -> /a|b|c/
|
||||
* /$variable/ -> /a|b|c/ -> /a|b|c/
|
||||
*/
|
||||
export function replaceTemplateVars(templateSrv, target, scopedVars) {
|
||||
let replacedTarget = templateSrv.replace(target, scopedVars, zabbixTemplateFormat);
|
||||
if (target && target !== replacedTarget && !utils.isRegex(replacedTarget)) {
|
||||
replacedTarget = '/^' + replacedTarget + '$/';
|
||||
}
|
||||
return replacedTarget;
|
||||
}
|
||||
|
||||
export function base64StringToArrowTable(text: string) {
|
||||
const b64 = atob(text);
|
||||
const arr = Uint8Array.from(b64, (c) => {
|
||||
return c.charCodeAt(0);
|
||||
});
|
||||
return arr;
|
||||
}
|
||||
|
||||
function getRequestTarget(request: DataQueryRequest<any>, refId: string): any {
|
||||
for (let i = 0; i < request.targets.length; i++) {
|
||||
const target = request.targets[i];
|
||||
if (target.refId === refId) {
|
||||
return target;
|
||||
convertToWide(response: DataQueryResponse) {
|
||||
if (responseHandler.isConvertibleToWide(response.data)) {
|
||||
response.data = responseHandler.convertToWide(response.data);
|
||||
}
|
||||
return response;
|
||||
}
|
||||
interpolateVariablesInQueries(queries: ZabbixMetricsQuery[], scopedVars: ScopedVars): ZabbixMetricsQuery[] {
|
||||
if (!queries || queries.length === 0) {
|
||||
return [];
|
||||
}
|
||||
return queries.map((query) => {
|
||||
// backwardsCompatibility
|
||||
const isOldVersion: boolean = (query as any).itservice && !query.itServiceFilter;
|
||||
return {
|
||||
...query,
|
||||
itServiceFilter: isOldVersion
|
||||
? '/.*/'
|
||||
: utils.replaceTemplateVars(this.templateSrv, query.itServiceFilter, scopedVars),
|
||||
slaFilter: utils.replaceTemplateVars(this.templateSrv, query.slaFilter, scopedVars),
|
||||
itemids: utils.replaceTemplateVars(
|
||||
this.templateSrv,
|
||||
query.itemids,
|
||||
scopedVars,
|
||||
utils.zabbixItemIdsTemplateFormat
|
||||
),
|
||||
textFilter: utils.replaceTemplateVars(this.templateSrv, query.textFilter, scopedVars),
|
||||
functions: utils.replaceVariablesInFuncParams(this.templateSrv, query.functions, scopedVars),
|
||||
tags: {
|
||||
...query.tags,
|
||||
filter: utils.replaceTemplateVars(this.templateSrv, query.tags?.filter, scopedVars),
|
||||
},
|
||||
group: {
|
||||
...query.group,
|
||||
filter: utils.replaceTemplateVars(this.templateSrv, query.group?.filter, scopedVars),
|
||||
},
|
||||
host: {
|
||||
...query.host,
|
||||
filter: utils.replaceTemplateVars(this.templateSrv, query.host?.filter, scopedVars),
|
||||
},
|
||||
application: {
|
||||
...query.application,
|
||||
filter: utils.replaceTemplateVars(this.templateSrv, query.application?.filter, scopedVars),
|
||||
},
|
||||
proxy: {
|
||||
...query.proxy,
|
||||
filter: utils.replaceTemplateVars(this.templateSrv, query.proxy?.filter, scopedVars),
|
||||
},
|
||||
trigger: {
|
||||
...query.trigger,
|
||||
filter: utils.replaceTemplateVars(this.templateSrv, query.trigger?.filter, scopedVars),
|
||||
},
|
||||
itemTag: {
|
||||
...query.itemTag,
|
||||
filter: utils.replaceTemplateVars(this.templateSrv, query.itemTag?.filter, scopedVars),
|
||||
},
|
||||
item: {
|
||||
...query.item,
|
||||
filter: utils.replaceTemplateVars(this.templateSrv, query.item?.filter, scopedVars),
|
||||
},
|
||||
};
|
||||
});
|
||||
}
|
||||
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;
|
||||
};
|
||||
|
||||
Reference in New Issue
Block a user