Support item tags as variables

This commit is contained in:
Alexander Zobnin
2021-08-10 12:34:18 +03:00
parent b79356731a
commit 21f1d87dc1
5 changed files with 101 additions and 50 deletions

View File

@@ -1,15 +1,16 @@
import React, { PureComponent } from 'react'; import React, { PureComponent } from 'react';
import { parseLegacyVariableQuery } from '../utils'; import { parseLegacyVariableQuery } from '../utils';
import { SelectableValue } from '@grafana/data'; import { SelectableValue } from '@grafana/data';
import { VariableQuery, VariableQueryTypes, VariableQueryProps, VariableQueryData } from '../types'; import { VariableQuery, VariableQueryData, VariableQueryProps, VariableQueryTypes } from '../types';
import { ZabbixInput } from './ZabbixInput'; import { ZabbixInput } from './ZabbixInput';
import { InlineFormLabel, Select, Input } from '@grafana/ui'; import { InlineFormLabel, Input, Select } from '@grafana/ui';
export class ZabbixVariableQueryEditor extends PureComponent<VariableQueryProps, VariableQueryData> { export class ZabbixVariableQueryEditor extends PureComponent<VariableQueryProps, VariableQueryData> {
queryTypes: Array<SelectableValue<VariableQueryTypes>> = [ queryTypes: Array<SelectableValue<VariableQueryTypes>> = [
{ value: VariableQueryTypes.Group, label: 'Group'}, { value: VariableQueryTypes.Group, label: 'Group' },
{ value: VariableQueryTypes.Host, label: 'Host' }, { value: VariableQueryTypes.Host, label: 'Host' },
{ value: VariableQueryTypes.Application, label: 'Application' }, { value: VariableQueryTypes.Application, label: 'Application' },
{ value: VariableQueryTypes.ItemTag, label: 'Item tag' },
{ value: VariableQueryTypes.Item, label: 'Item' }, { value: VariableQueryTypes.Item, label: 'Item' },
{ value: VariableQueryTypes.ItemValues, label: 'Item values' }, { value: VariableQueryTypes.ItemValues, label: 'Item values' },
]; ];
@@ -20,6 +21,7 @@ export class ZabbixVariableQueryEditor extends PureComponent<VariableQueryProps,
group: '/.*/', group: '/.*/',
host: '', host: '',
application: '', application: '',
itemTag: '',
item: '', item: '',
}; };
@@ -64,13 +66,13 @@ export class ZabbixVariableQueryEditor extends PureComponent<VariableQueryProps,
...newQuery, ...newQuery,
}; };
}); });
} };
handleQueryChange = () => { handleQueryChange = () => {
const { queryType, group, host, application, item } = this.state; const { queryType, group, host, application, itemTag, item } = this.state;
const queryModel = { queryType, group, host, application, item }; const queryModel = { queryType, group, host, application, itemTag, item };
this.props.onChange(queryModel, `Zabbix - ${queryType}`); this.props.onChange(queryModel, `Zabbix - ${queryType}`);
} };
handleQueryTypeChange = (selectedItem: SelectableValue<VariableQueryTypes>) => { handleQueryTypeChange = (selectedItem: SelectableValue<VariableQueryTypes>) => {
this.setState({ this.setState({
@@ -79,14 +81,16 @@ export class ZabbixVariableQueryEditor extends PureComponent<VariableQueryProps,
queryType: selectedItem.value, queryType: selectedItem.value,
}); });
const { group, host, application, item } = this.state; const { group, host, application, itemTag, item } = this.state;
const queryType = selectedItem.value; const queryType = selectedItem.value;
const queryModel = { queryType, group, host, application, item }; const queryModel = { queryType, group, host, application, itemTag, item };
this.props.onChange(queryModel, `Zabbix - ${queryType}`); this.props.onChange(queryModel, `Zabbix - ${queryType}`);
} };
render() { render() {
const { selectedQueryType, legacyQuery, group, host, application, item } = this.state; const { selectedQueryType, legacyQuery, group, host, application, itemTag, item } = this.state;
const { datasource } = this.props;
const supportsItemTags = datasource.zabbix.isZabbix54OrHigher();
return ( return (
<> <>
@@ -109,20 +113,32 @@ export class ZabbixVariableQueryEditor extends PureComponent<VariableQueryProps,
/> />
</div> </div>
{selectedQueryType.value !== VariableQueryTypes.Group && {selectedQueryType.value !== VariableQueryTypes.Group &&
<div className="gf-form max-width-30"> <div className="gf-form max-width-30">
<InlineFormLabel width={10}>Host</InlineFormLabel> <InlineFormLabel width={10}>Host</InlineFormLabel>
<ZabbixInput <ZabbixInput
value={host} value={host}
onChange={evt => this.handleQueryUpdate(evt, 'host')} onChange={evt => this.handleQueryUpdate(evt, 'host')}
onBlur={this.handleQueryChange} onBlur={this.handleQueryChange}
/> />
</div> </div>
} }
</div> </div>
{(selectedQueryType.value === VariableQueryTypes.Application || {(selectedQueryType.value === VariableQueryTypes.Application ||
selectedQueryType.value === VariableQueryTypes.ItemTag ||
selectedQueryType.value === VariableQueryTypes.Item || selectedQueryType.value === VariableQueryTypes.Item ||
selectedQueryType.value === VariableQueryTypes.ItemValues) && selectedQueryType.value === VariableQueryTypes.ItemValues) &&
<div className="gf-form-inline"> <div className="gf-form-inline">
{supportsItemTags && (
<div className="gf-form max-width-30">
<InlineFormLabel width={10}>Item tag</InlineFormLabel>
<ZabbixInput
value={itemTag}
onChange={evt => this.handleQueryUpdate(evt, 'itemTag')}
onBlur={this.handleQueryChange}
/>
</div>
)}
{!supportsItemTags && (
<div className="gf-form max-width-30"> <div className="gf-form max-width-30">
<InlineFormLabel width={10}>Application</InlineFormLabel> <InlineFormLabel width={10}>Application</InlineFormLabel>
<ZabbixInput <ZabbixInput
@@ -131,29 +147,30 @@ export class ZabbixVariableQueryEditor extends PureComponent<VariableQueryProps,
onBlur={this.handleQueryChange} onBlur={this.handleQueryChange}
/> />
</div> </div>
{(selectedQueryType.value === VariableQueryTypes.Item || )}
selectedQueryType.value === VariableQueryTypes.ItemValues) && {(selectedQueryType.value === VariableQueryTypes.Item ||
<div className="gf-form max-width-30"> selectedQueryType.value === VariableQueryTypes.ItemValues) &&
<InlineFormLabel width={10}>Item</InlineFormLabel> <div className="gf-form max-width-30">
<ZabbixInput <InlineFormLabel width={10}>Item</InlineFormLabel>
value={item} <ZabbixInput
onChange={evt => this.handleQueryUpdate(evt, 'item')} value={item}
onBlur={this.handleQueryChange} onChange={evt => this.handleQueryUpdate(evt, 'item')}
/> onBlur={this.handleQueryChange}
</div> />
}
</div> </div>
}
</div>
} }
{legacyQuery && {legacyQuery &&
<div className="gf-form"> <div className="gf-form">
<InlineFormLabel width={10} tooltip="Original query string, read-only">Legacy Query</InlineFormLabel> <InlineFormLabel width={10} tooltip="Original query string, read-only">Legacy Query</InlineFormLabel>
<Input <Input
css="" css=""
value={legacyQuery} value={legacyQuery}
readOnly={true} readOnly={true}
/> />
</div> </div>
} }
</> </>
); );

View File

@@ -663,7 +663,7 @@ export class ZabbixDatasource extends DataSourceApi<ZabbixMetricsQuery, ZabbixDS
queryModel = utils.parseLegacyVariableQuery(query); queryModel = utils.parseLegacyVariableQuery(query);
} }
for (const prop of ['group', 'host', 'application', 'item']) { for (const prop of ['group', 'host', 'application', 'itemTag', 'item']) {
queryModel[prop] = this.replaceTemplateVars(queryModel[prop], {}); queryModel[prop] = this.replaceTemplateVars(queryModel[prop], {});
} }
@@ -679,6 +679,9 @@ export class ZabbixDatasource extends DataSourceApi<ZabbixMetricsQuery, ZabbixDS
case VariableQueryTypes.Application: case VariableQueryTypes.Application:
resultPromise = this.zabbix.getApps(queryModel.group, queryModel.host, queryModel.application); resultPromise = this.zabbix.getApps(queryModel.group, queryModel.host, queryModel.application);
break; break;
case VariableQueryTypes.ItemTag:
resultPromise = this.zabbix.getItemTags(queryModel.group, queryModel.host, queryModel.itemTag);
break;
case VariableQueryTypes.Item: case VariableQueryTypes.Item:
resultPromise = this.zabbix.getItems(queryModel.group, queryModel.host, queryModel.application, null, queryModel.item); resultPromise = this.zabbix.getItems(queryModel.group, queryModel.host, queryModel.application, null, queryModel.item);
break; break;
@@ -781,7 +784,7 @@ export class ZabbixDatasource extends DataSourceApi<ZabbixMetricsQuery, ZabbixDS
// Replace template variables // Replace template variables
replaceTargetVariables(target, options) { replaceTargetVariables(target, options) {
const parts = ['group', 'host', 'application', 'item']; const parts = ['group', 'host', 'application', 'itemTag', 'item'];
_.forEach(parts, p => { _.forEach(parts, p => {
if (target[p] && target[p].filter) { if (target[p] && target[p].filter) {
target[p].filter = this.replaceTemplateVars(target[p].filter, options.scopedVars); target[p].filter = this.replaceTemplateVars(target[p].filter, options.scopedVars);

View File

@@ -315,7 +315,14 @@ export class ZabbixQueryController extends QueryCtrl {
if (!this.metric?.tagList) { if (!this.metric?.tagList) {
return []; return [];
} }
return this.metric.tagList.map(t => itemTagToString(t)); const tags = this.metric.tagList.map(t => itemTagToString(t));
// Add template variables
_.forEach(this.templateSrv.getVariables(), variable => {
tags.unshift('$' + variable.name);
});
return tags;
}; };
getTemplateVariables() { getTemplateVariables() {

View File

@@ -150,6 +150,7 @@ export interface VariableQuery {
group?: string; group?: string;
host?: string; host?: string;
application?: string; application?: string;
itemTag?: string;
item?: string; item?: string;
} }
@@ -159,6 +160,7 @@ export enum VariableQueryTypes {
Group = 'group', Group = 'group',
Host = 'host', Host = 'host',
Application = 'application', Application = 'application',
ItemTag = 'itemTag',
Item = 'item', Item = 'item',
ItemValues = 'itemValues', ItemValues = 'itemValues',
} }

View File

@@ -2,7 +2,6 @@ import _ from 'lodash';
import moment from 'moment'; import moment from 'moment';
import semver from 'semver'; import semver from 'semver';
import * as utils from '../utils'; import * as utils from '../utils';
import { itemTagToString } from '../utils';
import responseHandler from '../responseHandler'; import responseHandler from '../responseHandler';
import { CachingProxy } from './proxy/cachingProxy'; import { CachingProxy } from './proxy/cachingProxy';
import { DBConnector } from './connectors/dbConnector'; import { DBConnector } from './connectors/dbConnector';
@@ -11,7 +10,7 @@ import { SQLConnector } from './connectors/sql/sqlConnector';
import { InfluxDBConnector } from './connectors/influxdb/influxdbConnector'; import { InfluxDBConnector } from './connectors/influxdb/influxdbConnector';
import { ZabbixConnector } from './types'; import { ZabbixConnector } from './types';
import { joinTriggersWithEvents, joinTriggersWithProblems } from '../problemsHandler'; import { joinTriggersWithEvents, joinTriggersWithProblems } from '../problemsHandler';
import { ProblemDTO, ZBXItemTag } from '../types'; import { ProblemDTO, ZBXItem, ZBXItemTag } from '../types';
interface AppsResponse extends Array<any> { interface AppsResponse extends Array<any> {
appFilterEmpty?: boolean; appFilterEmpty?: boolean;
@@ -20,7 +19,7 @@ interface AppsResponse extends Array<any> {
const REQUESTS_TO_PROXYFY = [ const REQUESTS_TO_PROXYFY = [
'getHistory', 'getTrend', 'getGroups', 'getHosts', 'getApps', 'getItems', 'getMacros', 'getItemsByIDs', 'getHistory', 'getTrend', 'getGroups', 'getHosts', 'getApps', 'getItems', 'getMacros', 'getItemsByIDs',
'getEvents', 'getAlerts', 'getHostAlerts', 'getAcknowledges', 'getITService', 'getSLA', 'getVersion', 'getProxies', 'getEvents', 'getAlerts', 'getHostAlerts', 'getAcknowledges', 'getITService', 'getSLA', 'getProxies',
'getEventAlerts', 'getExtendedEventData', 'getProblems', 'getEventsHistory', 'getTriggersByIds', 'getScripts', 'getValueMappings' 'getEventAlerts', 'getExtendedEventData', 'getProblems', 'getEventsHistory', 'getTriggersByIds', 'getScripts', 'getValueMappings'
]; ];
@@ -31,7 +30,7 @@ const REQUESTS_TO_CACHE = [
const REQUESTS_TO_BIND = [ const REQUESTS_TO_BIND = [
'getHistory', 'getTrend', 'getMacros', 'getItemsByIDs', 'getEvents', 'getAlerts', 'getHostAlerts', 'getHistory', 'getTrend', 'getMacros', 'getItemsByIDs', 'getEvents', 'getAlerts', 'getHostAlerts',
'getAcknowledges', 'getITService', 'acknowledgeEvent', 'getProxies', 'getEventAlerts', 'getAcknowledges', 'getITService', 'acknowledgeEvent', 'getProxies', 'getEventAlerts',
'getExtendedEventData', 'getScripts', 'executeScript', 'getValueMappings', 'isZabbix54OrHigher' 'getExtendedEventData', 'getScripts', 'executeScript', 'getValueMappings'
]; ];
export class Zabbix implements ZabbixConnector { export class Zabbix implements ZabbixConnector {
@@ -57,7 +56,6 @@ export class Zabbix implements ZabbixConnector {
getExtendedEventData: (eventids) => Promise<any>; getExtendedEventData: (eventids) => Promise<any>;
getMacros: (hostids: any[]) => Promise<any>; getMacros: (hostids: any[]) => Promise<any>;
getValueMappings: () => Promise<any>; getValueMappings: () => Promise<any>;
isZabbix54OrHigher: () => boolean;
constructor(options) { constructor(options) {
const { const {
@@ -172,13 +170,23 @@ export class Zabbix implements ZabbixConnector {
async getVersion() { async getVersion() {
if (!this.version) { if (!this.version) {
this.version = await this.zabbixAPI.initVersion(); if (this.zabbixAPI.version) {
this.version = this.zabbixAPI.version;
} else {
this.version = await this.zabbixAPI.initVersion();
}
} }
return this.version; return this.version;
} }
supportsApplications() { supportsApplications() {
return this.version ? semver.lt(this.version, '5.4.0') : true; const version = this.version || this.zabbixAPI.version;
return version ? semver.lt(version, '5.4.0') : true;
}
isZabbix54OrHigher() {
const version = this.version || this.zabbixAPI.version;
return version ? semver.gte(version, '5.4.0') : false;
} }
getItemsFromTarget(target, options) { getItemsFromTarget(target, options) {
@@ -263,6 +271,20 @@ export class Zabbix implements ZabbixConnector {
}); });
} }
async getItemTags(groupFilter?, hostFilter?, itemTagFilter?) {
const items = await this.getAllItems(groupFilter, hostFilter, null, null, {});
let tags: ZBXItemTag[] = _.flatten(items.map((item: ZBXItem) => {
if (item.tags) {
return item.tags;
} else {
return [];
}
}));
tags = _.uniqBy(tags, t => t.tag + t.value || '');
const tagsStr = tags.map(t => ({ name: utils.itemTagToString(t) }));
return findByFilter(tagsStr, itemTagFilter);
}
async getAllItems(groupFilter, hostFilter, appFilter, itemTagFilter, options: any = {}) { async getAllItems(groupFilter, hostFilter, appFilter, itemTagFilter, options: any = {}) {
const apps = await this.getApps(groupFilter, hostFilter, appFilter); const apps = await this.getApps(groupFilter, hostFilter, appFilter);
let items: any[]; let items: any[];
@@ -272,7 +294,7 @@ export class Zabbix implements ZabbixConnector {
if (itemTagFilter) { if (itemTagFilter) {
items = items.filter(item => { items = items.filter(item => {
if (item.tags) { if (item.tags) {
const tags: ZBXItemTag[] = item.tags.map(t => itemTagToString(t)); const tags: ZBXItemTag[] = item.tags.map(t => utils.itemTagToString(t));
return tags.includes(itemTagFilter); return tags.includes(itemTagFilter);
} else { } else {
return false; return false;