Apply data processing for the IT Service query on the backend

This commit is contained in:
Alexander Zobnin
2021-08-05 17:59:58 +03:00
parent 6c1722d2ef
commit 62f38df28a
2 changed files with 82 additions and 96 deletions

View File

@@ -313,22 +313,7 @@ export class ZabbixDatasource extends DataSourceApi<ZabbixMetricsQuery, ZabbixDS
console.log(`Datasource::Performance Query Time (${this.name}): ${queryEnd - queryStart}`);
}
const frames = [];
for (const frameJSON of result) {
const frame = dataFrameFromJSON(frameJSON);
frame.refId = target.refId;
frames.push(frame);
}
const resp = { data: frames };
this.sortByRefId(resp);
this.applyFrontendFunctions(resp, request);
if (responseHandler.isConvertibleToWide(resp.data)) {
console.log('Converting response to the wide format');
resp.data = responseHandler.convertToWide(resp.data);
}
return resp.data;
return this.handleBackendPostProcessingResponse(result, request, target);
}
/**
@@ -345,6 +330,10 @@ export class ZabbixDatasource extends DataSourceApi<ZabbixMetricsQuery, ZabbixDS
history = await this.zabbix.getHistoryTS(items, timeRange, options);
}
return await this.invokeDataProcessingQuery(history, target);
}
async invokeDataProcessingQuery(timeSeriesData, query) {
// Request backend for data processing
const requestOptions: BackendSrvRequest = {
url: `/api/datasources/${this.datasourceId}/resources/db-connection-post`,
@@ -354,8 +343,8 @@ export class ZabbixDatasource extends DataSourceApi<ZabbixMetricsQuery, ZabbixDS
},
hideFromInspector: false,
data: {
query: target,
series: history,
series: timeSeriesData,
query,
},
};
@@ -363,6 +352,25 @@ export class ZabbixDatasource extends DataSourceApi<ZabbixMetricsQuery, ZabbixDS
return response.data;
}
handleBackendPostProcessingResponse(response, request, target) {
const frames = [];
for (const frameJSON of response) {
const frame = dataFrameFromJSON(frameJSON);
frame.refId = target.refId;
frames.push(frame);
}
const resp = { data: frames };
this.sortByRefId(resp);
this.applyFrontendFunctions(resp, request);
if (responseHandler.isConvertibleToWide(resp.data)) {
console.log('Converting response to the wide format');
resp.data = responseHandler.convertToWide(resp.data);
}
return resp.data;
}
getTrendValueType(target) {
// Find trendValue() function and get specified trend value
const trendFunctions = _.map(metricFunctions.getCategories()['Trends'], 'name');
@@ -395,62 +403,6 @@ export class ZabbixDatasource extends DataSourceApi<ZabbixMetricsQuery, ZabbixDS
return response;
}
applyDataProcessingFunctions(timeseries_data, target) {
const transformFunctions = bindFunctionDefs(target.functions, 'Transform');
const aggregationFunctions = bindFunctionDefs(target.functions, 'Aggregate');
const filterFunctions = bindFunctionDefs(target.functions, 'Filter');
const aliasFunctions = bindFunctionDefs(target.functions, 'Alias');
// Apply transformation functions
timeseries_data = _.cloneDeep(_.map(timeseries_data, timeseries => {
timeseries.datapoints = utils.sequence(transformFunctions)(timeseries.datapoints);
return timeseries;
}));
// Apply filter functions
if (filterFunctions.length) {
timeseries_data = utils.sequence(filterFunctions)(timeseries_data);
}
// Apply aggregations
if (aggregationFunctions.length) {
let dp = _.map(timeseries_data, 'datapoints');
dp = utils.sequence(aggregationFunctions)(dp);
const aggFuncNames = _.map(metricFunctions.getCategories()['Aggregate'], 'name');
const lastAgg = _.findLast(target.functions, func => {
return _.includes(aggFuncNames, func.def.name);
});
timeseries_data = [{
target: lastAgg.text,
datapoints: dp
}];
}
// Apply alias functions
_.forEach(timeseries_data, utils.sequence(aliasFunctions).bind(this));
// Apply Time-related functions (timeShift(), etc)
// Find timeShift() function and get specified trend value
this.applyTimeShiftFunction(timeseries_data, target);
return timeseries_data;
}
applyTimeShiftFunction(timeseries_data, target) {
// Find timeShift() function and get specified interval
const timeShiftFunc = _.find(target.functions, (func) => {
return func.def.name === 'timeShift';
});
if (timeShiftFunc) {
const shift = timeShiftFunc.params[0];
_.forEach(timeseries_data, (series) => {
series.datapoints = dataProcessor.unShiftTimeSeries(shift, series.datapoints);
});
}
}
/**
* Query target data for Text
*/
@@ -491,33 +443,32 @@ export class ZabbixDatasource extends DataSourceApi<ZabbixMetricsQuery, ZabbixDS
/**
* Query target data for IT Services
*/
queryITServiceData(target, timeRange, options) {
async queryITServiceData(target, timeRange, request) {
// Don't show undefined and hidden targets
if (target.hide || (!target.itservice && !target.itServiceFilter) || !target.slaProperty) {
return [];
}
let itServiceFilter;
options.isOldVersion = target.itservice && !target.itServiceFilter;
request.isOldVersion = target.itservice && !target.itServiceFilter;
if (options.isOldVersion) {
if (request.isOldVersion) {
// Backward compatibility
itServiceFilter = '/.*/';
} else {
itServiceFilter = this.replaceTemplateVars(target.itServiceFilter, options.scopedVars);
itServiceFilter = this.replaceTemplateVars(target.itServiceFilter, request.scopedVars);
}
options.slaInterval = target.slaInterval;
request.slaInterval = target.slaInterval;
return this.zabbix.getITServices(itServiceFilter)
.then(itservices => {
if (options.isOldVersion) {
itservices = _.filter(itservices, { 'serviceid': target.itservice?.serviceid });
}
return this.zabbix.getSLA(itservices, timeRange, target, options);
})
.then(itservicesdp => this.applyDataProcessingFunctions(itservicesdp, target))
.then(result => result.map(s => responseHandler.seriesToDataFrame(s, target)));
let itservices = await this.zabbix.getITServices(itServiceFilter);
if (request.isOldVersion) {
itservices = _.filter(itservices, { 'serviceid': target.itservice?.serviceid });
}
const itservicesdp = await this.zabbix.getSLA(itservices, timeRange, target, request);
const backendRequest = responseHandler.itServiceResponseToTimeSeries(itservicesdp, target.slaInterval);
const processedResponse = await this.invokeDataProcessingQuery(backendRequest, target);
return this.handleBackendPostProcessingResponse(processedResponse, request, target);
}
queryTriggersData(target, timeRange) {
@@ -596,7 +547,7 @@ export class ZabbixDatasource extends DataSourceApi<ZabbixMetricsQuery, ZabbixDS
}
if (target.options?.acknowledged === 0 || target.options?.acknowledged === 1) {
problemsOptions.acknowledged = target.options?.acknowledged ? true : false;
problemsOptions.acknowledged = !!target.options?.acknowledged;
}
if (target.options?.minSeverity) {
@@ -690,8 +641,9 @@ export class ZabbixDatasource extends DataSourceApi<ZabbixMetricsQuery, ZabbixDS
* Find metrics from templated request.
*
* @param {string} query Query from Templating
* @param options
* @return {string} Metric name - group, host, app or item or list
* of metrics in "{metric1,metcic2,...,metricN}" format.
* of metrics in "{metric1, metric2,..., metricN}" format.
*/
metricFindQuery(query, options) {
let resultPromise;
@@ -947,12 +899,6 @@ function replaceTemplateVars(templateSrv, target, scopedVars) {
return replacedTarget;
}
function filterEnabledTargets(targets) {
return _.filter(targets, target => {
return !(target.hide || !target.group || !target.host || !target.item);
});
}
export function base64StringToArrowTable(text: string) {
const b64 = atob(text);
const arr = Uint8Array.from(b64, (c) => {

View File

@@ -153,6 +153,7 @@ export function seriesToDataFrame(timeseries, target: ZabbixMetricsQuery, valueM
return mutableFrame;
}
// Converts DataResponse to the format which backend works with (for data processing)
export function dataResponseToTimeSeries(response: DataFrameJSON[], items) {
const series = [];
if (response.length === 0) {
@@ -198,6 +199,44 @@ export function dataResponseToTimeSeries(response: DataFrameJSON[], items) {
return series;
}
export function itServiceResponseToTimeSeries(response: any, interval) {
const series = [];
if (response.length === 0) {
return [];
}
for (const s of response) {
const ts = [];
if (!s.datapoints) {
continue;
}
const dp = s.datapoints;
for (let i = 0; i < dp.length; i++) {
ts.push({ time: dp[i][1] / 1000, value: dp[i][0] });
}
let intervalS = utils.parseItemInterval(interval);
if (intervalS === 0) {
intervalS = null;
}
const timeSeriesData = {
ts: ts,
meta: {
name: s.target,
interval: intervalS,
item: {},
}
};
series.push(timeSeriesData);
}
return series;
}
export function isConvertibleToWide(data: DataFrame[]): boolean {
if (!data || data.length < 2) {
return false;
@@ -499,6 +538,7 @@ export default {
sortTimeseries,
seriesToDataFrame,
dataResponseToTimeSeries,
itServiceResponseToTimeSeries,
isConvertibleToWide,
convertToWide,
alignFrames,