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
This commit is contained in:
Alexander Zobnin
2021-05-18 17:22:49 +03:00
committed by GitHub
parent 29a8bde236
commit 3410593f9e
6 changed files with 59 additions and 13 deletions

View File

@@ -167,7 +167,10 @@ func (ds *ZabbixDatasourceInstance) getItems(ctx context.Context, groupFilter st
} }
apps, err := ds.getApps(ctx, groupFilter, hostFilter, appFilter) 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 return nil, err
} }
var appids []string var appids []string
@@ -505,3 +508,12 @@ func isNotAuthorized(err error) bool {
strings.Contains(message, "Not authorised.") || strings.Contains(message, "Not authorised.") ||
strings.Contains(message, "Not authorized.") 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".`
}

View File

@@ -122,6 +122,7 @@
<label class="gf-form-label query-keyword width-7">Application</label> <label class="gf-form-label query-keyword width-7">Application</label>
<input type="text" <input type="text"
ng-model="ctrl.target.application.filter" ng-model="ctrl.target.application.filter"
ng-disabled="ctrl.appFilterDisabled()"
bs-typeahead="ctrl.getApplicationNames" bs-typeahead="ctrl.getApplicationNames"
ng-blur="ctrl.onTargetBlur()" ng-blur="ctrl.onTargetBlur()"
data-min-length=0 data-min-length=0

View File

@@ -512,4 +512,8 @@ export class ZabbixQueryController extends QueryCtrl {
this.init(); this.init();
this.targetChanged(); this.targetChanged();
} }
appFilterDisabled() {
return !this.zabbix.supportsApplications();
}
} }

View File

@@ -46,12 +46,14 @@ export class ZabbixAPIConnector {
////////////////////////// //////////////////////////
request(method: string, params?: any) { request(method: string, params?: any) {
return this.backendAPIRequest(method, params).then(response => { if (!this.version) {
return response?.data?.result; 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 = { const requestOptions: BackendSrvRequest = {
url: this.backendAPIUrl, url: this.backendAPIUrl,
method: 'POST', method: 'POST',
@@ -74,14 +76,15 @@ export class ZabbixAPIConnector {
requestOptions.headers.Authorization = this.requestOptions.basicAuth; requestOptions.headers.Authorization = this.requestOptions.basicAuth;
} }
return getBackendSrv().datasourceRequest(requestOptions); const response = await getBackendSrv().datasourceRequest(requestOptions);
return response?.data?.result;
} }
/** /**
* Get Zabbix API version * Get Zabbix API version
*/ */
getVersion() { getVersion() {
return this.request('apiinfo.version'); return this.backendAPIRequest('apiinfo.version');
} }
initVersion(): Promise<string> { initVersion(): Promise<string> {
@@ -147,7 +150,11 @@ export class ZabbixAPIConnector {
return this.request('host.get', params); return this.request('host.get', params);
} }
getApps(hostids): Promise<any[]> { async getApps(hostids): Promise<any[]> {
if (semver.gte(this.version, '5.4.0')) {
return [];
}
const params = { const params = {
output: 'extend', output: 'extend',
hostids: hostids hostids: hostids

View File

@@ -19,4 +19,6 @@ export interface ZabbixConnector {
getApps: (groupFilter?, hostFilter?, appFilter?) => any; getApps: (groupFilter?, hostFilter?, appFilter?) => any;
getItems: (groupFilter?, hostFilter?, appFilter?, itemFilter?, options?) => any; getItems: (groupFilter?, hostFilter?, appFilter?, itemFilter?, options?) => any;
getSLA: (itservices, timeRange, target, options?) => any; getSLA: (itservices, timeRange, target, options?) => any;
supportsApplications: () => boolean;
} }

View File

@@ -1,5 +1,6 @@
import _ from 'lodash'; import _ from 'lodash';
import moment from 'moment'; import moment from 'moment';
import semver from 'semver';
import * as utils from '../utils'; import * as utils from '../utils';
import responseHandler from '../responseHandler'; import responseHandler from '../responseHandler';
import { CachingProxy } from './proxy/cachingProxy'; import { CachingProxy } from './proxy/cachingProxy';
@@ -29,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', 'getVersion', 'acknowledgeEvent', 'getProxies', 'getEventAlerts', 'getAcknowledges', 'getITService', 'acknowledgeEvent', 'getProxies', 'getEventAlerts',
'getExtendedEventData', 'getScripts', 'executeScript', 'getValueMappings' 'getExtendedEventData', 'getScripts', 'executeScript', 'getValueMappings'
]; ];
@@ -40,6 +41,7 @@ export class Zabbix implements ZabbixConnector {
getHistoryDB: any; getHistoryDB: any;
dbConnector: any; dbConnector: any;
getTrendsDB: any; getTrendsDB: any;
version: string;
getHistory: (items, timeFrom, timeTill) => Promise<any>; getHistory: (items, timeFrom, timeTill) => Promise<any>;
getTrend: (items, timeFrom, timeTill) => Promise<any>; getTrend: (items, timeFrom, timeTill) => Promise<any>;
@@ -54,7 +56,6 @@ export class Zabbix implements ZabbixConnector {
getEventAlerts: (eventids) => Promise<any>; getEventAlerts: (eventids) => Promise<any>;
getExtendedEventData: (eventids) => Promise<any>; getExtendedEventData: (eventids) => Promise<any>;
getMacros: (hostids: any[]) => Promise<any>; getMacros: (hostids: any[]) => Promise<any>;
getVersion: () => Promise<string>;
getValueMappings: () => Promise<any>; getValueMappings: () => Promise<any>;
constructor(options) { 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) { getItemsFromTarget(target, options) {
const parts = ['group', 'host', 'application', 'item']; const parts = ['group', 'host', 'application', 'item'];
const filters = _.map(parts, p => target[p].filter); 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. * 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) return this.getHosts(groupFilter, hostFilter)
.then(hosts => { .then(hosts => {
const hostids = _.map(hosts, 'hostid'); const hostids = _.map(hosts, 'hostid');
@@ -226,11 +243,14 @@ export class Zabbix implements ZabbixConnector {
}); });
} }
getApps(groupFilter?, hostFilter?, appFilter?): Promise<AppsResponse> { async getApps(groupFilter?, hostFilter?, appFilter?): Promise<AppsResponse> {
await this.getVersion();
const skipAppFilter = !this.supportsApplications();
return this.getHosts(groupFilter, hostFilter) return this.getHosts(groupFilter, hostFilter)
.then(hosts => { .then(hosts => {
const hostids = _.map(hosts, 'hostid'); const hostids = _.map(hosts, 'hostid');
if (appFilter) { if (appFilter && !skipAppFilter) {
return this.zabbixAPI.getApps(hostids) return this.zabbixAPI.getApps(hostids)
.then(apps => filterByQuery(apps, appFilter)); .then(apps => filterByQuery(apps, appFilter));
} else { } else {