From 3410593f9eaf5f10bc1b111b6f6bea4489a82279 Mon Sep 17 00:00:00 2001 From: Alexander Zobnin Date: Tue, 18 May 2021 17:22:49 +0300 Subject: [PATCH] Fix compatibility with Zabbix 5.4 (skip applications filter) (#1214) * Fix queries in Zabbix 5.4 (applications not supported) * Fix alerting queries in Zabbix 5.4 --- pkg/datasource/zabbix.go | 14 ++++++++- .../partials/query.editor.html | 1 + src/datasource-zabbix/query.controller.ts | 4 +++ .../zabbix_api/zabbixAPIConnector.ts | 21 ++++++++----- src/datasource-zabbix/zabbix/types.ts | 2 ++ src/datasource-zabbix/zabbix/zabbix.ts | 30 +++++++++++++++---- 6 files changed, 59 insertions(+), 13 deletions(-) diff --git a/pkg/datasource/zabbix.go b/pkg/datasource/zabbix.go index 7ac18a1..9972a8e 100644 --- a/pkg/datasource/zabbix.go +++ b/pkg/datasource/zabbix.go @@ -167,7 +167,10 @@ func (ds *ZabbixDatasourceInstance) getItems(ctx context.Context, groupFilter st } apps, err := ds.getApps(ctx, groupFilter, hostFilter, appFilter) - if err != nil { + // Apps not supported in Zabbix 5.4 and higher + if isAppMethodNotFoundError(err) { + apps = []map[string]interface{}{} + } else if err != nil { return nil, err } var appids []string @@ -505,3 +508,12 @@ func isNotAuthorized(err error) bool { strings.Contains(message, "Not authorised.") || strings.Contains(message, "Not authorized.") } + +func isAppMethodNotFoundError(err error) bool { + if err == nil { + return false + } + + message := err.Error() + return message == `Method not found. Incorrect API "application".` +} diff --git a/src/datasource-zabbix/partials/query.editor.html b/src/datasource-zabbix/partials/query.editor.html index ec91acd..a3bb8cd 100644 --- a/src/datasource-zabbix/partials/query.editor.html +++ b/src/datasource-zabbix/partials/query.editor.html @@ -122,6 +122,7 @@ { - return response?.data?.result; - }); + if (!this.version) { + return this.initVersion().then(() => this.request(method, params)); + } + + return this.backendAPIRequest(method, params); } - backendAPIRequest(method: string, params: any = {}) { + async backendAPIRequest(method: string, params: any = {}) { const requestOptions: BackendSrvRequest = { url: this.backendAPIUrl, method: 'POST', @@ -74,14 +76,15 @@ export class ZabbixAPIConnector { requestOptions.headers.Authorization = this.requestOptions.basicAuth; } - return getBackendSrv().datasourceRequest(requestOptions); + const response = await getBackendSrv().datasourceRequest(requestOptions); + return response?.data?.result; } /** * Get Zabbix API version */ getVersion() { - return this.request('apiinfo.version'); + return this.backendAPIRequest('apiinfo.version'); } initVersion(): Promise { @@ -147,7 +150,11 @@ export class ZabbixAPIConnector { return this.request('host.get', params); } - getApps(hostids): Promise { + async getApps(hostids): Promise { + if (semver.gte(this.version, '5.4.0')) { + return []; + } + const params = { output: 'extend', hostids: hostids diff --git a/src/datasource-zabbix/zabbix/types.ts b/src/datasource-zabbix/zabbix/types.ts index f1ab2dd..7cd65d3 100644 --- a/src/datasource-zabbix/zabbix/types.ts +++ b/src/datasource-zabbix/zabbix/types.ts @@ -19,4 +19,6 @@ export interface ZabbixConnector { getApps: (groupFilter?, hostFilter?, appFilter?) => any; getItems: (groupFilter?, hostFilter?, appFilter?, itemFilter?, options?) => any; getSLA: (itservices, timeRange, target, options?) => any; + + supportsApplications: () => boolean; } diff --git a/src/datasource-zabbix/zabbix/zabbix.ts b/src/datasource-zabbix/zabbix/zabbix.ts index 00e86aa..3928585 100644 --- a/src/datasource-zabbix/zabbix/zabbix.ts +++ b/src/datasource-zabbix/zabbix/zabbix.ts @@ -1,5 +1,6 @@ import _ from 'lodash'; import moment from 'moment'; +import semver from 'semver'; import * as utils from '../utils'; import responseHandler from '../responseHandler'; import { CachingProxy } from './proxy/cachingProxy'; @@ -29,7 +30,7 @@ const REQUESTS_TO_CACHE = [ const REQUESTS_TO_BIND = [ 'getHistory', 'getTrend', 'getMacros', 'getItemsByIDs', 'getEvents', 'getAlerts', 'getHostAlerts', - 'getAcknowledges', 'getITService', 'getVersion', 'acknowledgeEvent', 'getProxies', 'getEventAlerts', + 'getAcknowledges', 'getITService', 'acknowledgeEvent', 'getProxies', 'getEventAlerts', 'getExtendedEventData', 'getScripts', 'executeScript', 'getValueMappings' ]; @@ -40,6 +41,7 @@ export class Zabbix implements ZabbixConnector { getHistoryDB: any; dbConnector: any; getTrendsDB: any; + version: string; getHistory: (items, timeFrom, timeTill) => Promise; getTrend: (items, timeFrom, timeTill) => Promise; @@ -54,7 +56,6 @@ export class Zabbix implements ZabbixConnector { getEventAlerts: (eventids) => Promise; getExtendedEventData: (eventids) => Promise; getMacros: (hostids: any[]) => Promise; - getVersion: () => Promise; getValueMappings: () => Promise; constructor(options) { @@ -168,6 +169,17 @@ export class Zabbix implements ZabbixConnector { }); } + async getVersion() { + if (!this.version) { + this.version = await this.zabbixAPI.initVersion(); + } + return this.version; + } + + supportsApplications() { + return this.version ? semver.lt(this.version, '5.4.0') : true; + } + getItemsFromTarget(target, options) { const parts = ['group', 'host', 'application', 'item']; const filters = _.map(parts, p => target[p].filter); @@ -218,7 +230,12 @@ export class Zabbix implements ZabbixConnector { /** * Get list of applications belonging to given groups and hosts. */ - getAllApps(groupFilter, hostFilter) { + async getAllApps(groupFilter, hostFilter) { + await this.getVersion(); + if (!this.supportsApplications()) { + return []; + } + return this.getHosts(groupFilter, hostFilter) .then(hosts => { const hostids = _.map(hosts, 'hostid'); @@ -226,11 +243,14 @@ export class Zabbix implements ZabbixConnector { }); } - getApps(groupFilter?, hostFilter?, appFilter?): Promise { + async getApps(groupFilter?, hostFilter?, appFilter?): Promise { + await this.getVersion(); + const skipAppFilter = !this.supportsApplications(); + return this.getHosts(groupFilter, hostFilter) .then(hosts => { const hostids = _.map(hosts, 'hostid'); - if (appFilter) { + if (appFilter && !skipAppFilter) { return this.zabbixAPI.getApps(hostids) .then(apps => filterByQuery(apps, appFilter)); } else {