diff --git a/pkg/datasource/zabbix.go b/pkg/datasource/zabbix.go index 7ac18a182..9972a8e82 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 ec91acd77..a3bb8cdf5 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 f1ab2dd20..7cd65d320 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 00e86aa39..392858579 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 {