572 lines
14 KiB
JavaScript
572 lines
14 KiB
JavaScript
import _ from 'lodash';
|
|
import kbn from 'grafana/app/core/utils/kbn';
|
|
import * as utils from '../../../utils';
|
|
import { ZabbixAPICore } from './zabbixAPICore';
|
|
import { ZBX_ACK_ACTION_NONE, ZBX_ACK_ACTION_ACK, ZBX_ACK_ACTION_ADD_MESSAGE, MIN_SLA_INTERVAL } from '../../../constants';
|
|
|
|
/**
|
|
* Zabbix API Wrapper.
|
|
* Creates Zabbix API instance with given parameters (url, credentials and other).
|
|
* Wraps API calls and provides high-level methods.
|
|
*/
|
|
export class ZabbixAPIConnector {
|
|
constructor(api_url, username, password, version, basicAuth, withCredentials, backendSrv, datasourceId) {
|
|
this.url = api_url;
|
|
this.username = username;
|
|
this.password = password;
|
|
this.auth = '';
|
|
this.version = version;
|
|
|
|
this.requestOptions = {
|
|
basicAuth: basicAuth,
|
|
withCredentials: withCredentials
|
|
};
|
|
|
|
this.datasourceId = datasourceId;
|
|
this.backendSrv = backendSrv;
|
|
|
|
this.loginPromise = null;
|
|
this.loginErrorCount = 0;
|
|
this.maxLoginAttempts = 3;
|
|
|
|
this.zabbixAPICore = new ZabbixAPICore(backendSrv);
|
|
|
|
this.getTrend = this.getTrend_ZBXNEXT1193;
|
|
//getTrend = getTrend_30;
|
|
}
|
|
|
|
//////////////////////////
|
|
// Core method wrappers //
|
|
//////////////////////////
|
|
|
|
request(method, params) {
|
|
return this.tsdbRequest(method, params).then(response => {
|
|
const result = this.handleTsdbResponse(response);
|
|
|
|
return result;
|
|
});
|
|
}
|
|
|
|
tsdbRequest(method, params) {
|
|
const tsdbRequestData = {
|
|
queries: [{
|
|
datasourceId: this.datasourceId,
|
|
queryType: 'zabbixAPI',
|
|
target: {
|
|
method,
|
|
params,
|
|
},
|
|
}],
|
|
};
|
|
|
|
return this.backendSrv.datasourceRequest({
|
|
url: '/api/tsdb/query',
|
|
method: 'POST',
|
|
data: tsdbRequestData
|
|
});
|
|
}
|
|
|
|
handleTsdbResponse(response) {
|
|
if (!response || !response.data || !response.data.results) {
|
|
return [];
|
|
}
|
|
|
|
return response.data.results['zabbixAPI'].meta;
|
|
}
|
|
|
|
/**
|
|
* When API unauthenticated or auth token expired each request produce login()
|
|
* call. But auth token is common to all requests. This function wraps login() method
|
|
* and call it once. If login() already called just wait for it (return its promise).
|
|
* @return login promise
|
|
*/
|
|
loginOnce() {
|
|
if (!this.loginPromise) {
|
|
this.loginPromise = Promise.resolve(
|
|
this.login().then(auth => {
|
|
this.auth = auth;
|
|
this.loginPromise = null;
|
|
return auth;
|
|
})
|
|
);
|
|
}
|
|
return this.loginPromise;
|
|
}
|
|
|
|
/**
|
|
* Get authentication token.
|
|
*/
|
|
login() {
|
|
return this.zabbixAPICore.login(this.url, this.username, this.password, this.requestOptions);
|
|
}
|
|
|
|
/**
|
|
* Get Zabbix API version
|
|
*/
|
|
getVersion() {
|
|
return this.zabbixAPICore.getVersion(this.url, this.requestOptions);
|
|
}
|
|
|
|
////////////////////////////////
|
|
// Zabbix API method wrappers //
|
|
////////////////////////////////
|
|
|
|
acknowledgeEvent(eventid, message) {
|
|
const action = this.version >= 4 ? ZBX_ACK_ACTION_ACK + ZBX_ACK_ACTION_ADD_MESSAGE : ZBX_ACK_ACTION_NONE;
|
|
const params = {
|
|
eventids: eventid,
|
|
message: message,
|
|
action: action
|
|
};
|
|
|
|
return this.request('event.acknowledge', params);
|
|
}
|
|
|
|
getGroups() {
|
|
var params = {
|
|
output: ['name'],
|
|
sortfield: 'name',
|
|
real_hosts: true
|
|
};
|
|
|
|
return this.request('hostgroup.get', params);
|
|
}
|
|
|
|
getHosts(groupids) {
|
|
var params = {
|
|
output: ['name', 'host'],
|
|
sortfield: 'name'
|
|
};
|
|
if (groupids) {
|
|
params.groupids = groupids;
|
|
}
|
|
|
|
return this.request('host.get', params);
|
|
}
|
|
|
|
getApps(hostids) {
|
|
var params = {
|
|
output: 'extend',
|
|
hostids: hostids
|
|
};
|
|
|
|
return this.request('application.get', params);
|
|
}
|
|
|
|
/**
|
|
* Get Zabbix items
|
|
* @param {[type]} hostids host ids
|
|
* @param {[type]} appids application ids
|
|
* @param {String} itemtype 'num' or 'text'
|
|
* @return {[type]} array of items
|
|
*/
|
|
getItems(hostids, appids, itemtype) {
|
|
var params = {
|
|
output: [
|
|
'name', 'key_',
|
|
'value_type',
|
|
'hostid',
|
|
'status',
|
|
'state'
|
|
],
|
|
sortfield: 'name',
|
|
webitems: true,
|
|
filter: {},
|
|
selectHosts: ['hostid', 'name']
|
|
};
|
|
if (hostids) {
|
|
params.hostids = hostids;
|
|
}
|
|
if (appids) {
|
|
params.applicationids = appids;
|
|
}
|
|
if (itemtype === 'num') {
|
|
// Return only numeric metrics
|
|
params.filter.value_type = [0, 3];
|
|
}
|
|
if (itemtype === 'text') {
|
|
// Return only text metrics
|
|
params.filter.value_type = [1, 2, 4];
|
|
}
|
|
|
|
return this.request('item.get', params)
|
|
.then(utils.expandItems);
|
|
}
|
|
|
|
getItemsByIDs(itemids) {
|
|
var params = {
|
|
itemids: itemids,
|
|
output: [
|
|
'name', 'key_',
|
|
'value_type',
|
|
'hostid',
|
|
'status',
|
|
'state'
|
|
],
|
|
webitems: true,
|
|
selectHosts: ['hostid', 'name']
|
|
};
|
|
|
|
return this.request('item.get', params)
|
|
.then(utils.expandItems);
|
|
}
|
|
|
|
getMacros(hostids) {
|
|
var params = {
|
|
output: 'extend',
|
|
hostids: hostids
|
|
};
|
|
|
|
return this.request('usermacro.get', params);
|
|
}
|
|
|
|
getGlobalMacros() {
|
|
var params = {
|
|
output: 'extend',
|
|
globalmacro: true
|
|
};
|
|
|
|
return this.request('usermacro.get', params);
|
|
}
|
|
|
|
getLastValue(itemid) {
|
|
var params = {
|
|
output: ['lastvalue'],
|
|
itemids: itemid
|
|
};
|
|
return this.request('item.get', params)
|
|
.then(items => items.length ? items[0].lastvalue : null);
|
|
}
|
|
|
|
/**
|
|
* Perform history query from Zabbix API
|
|
*
|
|
* @param {Array} items Array of Zabbix item objects
|
|
* @param {Number} timeFrom Time in seconds
|
|
* @param {Number} timeTill Time in seconds
|
|
* @return {Array} Array of Zabbix history objects
|
|
*/
|
|
getHistory(items, timeFrom, timeTill) {
|
|
|
|
// Group items by value type and perform request for each value type
|
|
let grouped_items = _.groupBy(items, 'value_type');
|
|
let promises = _.map(grouped_items, (items, value_type) => {
|
|
let itemids = _.map(items, 'itemid');
|
|
let params = {
|
|
output: 'extend',
|
|
history: value_type,
|
|
itemids: itemids,
|
|
sortfield: 'clock',
|
|
sortorder: 'ASC',
|
|
time_from: timeFrom
|
|
};
|
|
|
|
// Relative queries (e.g. last hour) don't include an end time
|
|
if (timeTill) {
|
|
params.time_till = timeTill;
|
|
}
|
|
|
|
return this.request('history.get', params);
|
|
});
|
|
|
|
return Promise.all(promises).then(_.flatten);
|
|
}
|
|
|
|
/**
|
|
* Perform trends query from Zabbix API
|
|
* Use trends api extension from ZBXNEXT-1193 patch.
|
|
*
|
|
* @param {Array} items Array of Zabbix item objects
|
|
* @param {Number} time_from Time in seconds
|
|
* @param {Number} time_till Time in seconds
|
|
* @return {Array} Array of Zabbix trend objects
|
|
*/
|
|
getTrend_ZBXNEXT1193(items, timeFrom, timeTill) {
|
|
|
|
// Group items by value type and perform request for each value type
|
|
let grouped_items = _.groupBy(items, 'value_type');
|
|
let promises = _.map(grouped_items, (items, value_type) => {
|
|
let itemids = _.map(items, 'itemid');
|
|
let params = {
|
|
output: 'extend',
|
|
trend: value_type,
|
|
itemids: itemids,
|
|
sortfield: 'clock',
|
|
sortorder: 'ASC',
|
|
time_from: timeFrom
|
|
};
|
|
|
|
// Relative queries (e.g. last hour) don't include an end time
|
|
if (timeTill) {
|
|
params.time_till = timeTill;
|
|
}
|
|
|
|
return this.request('trend.get', params);
|
|
});
|
|
|
|
return Promise.all(promises).then(_.flatten);
|
|
}
|
|
|
|
getTrend_30(items, time_from, time_till, value_type) {
|
|
var self = this;
|
|
var itemids = _.map(items, 'itemid');
|
|
|
|
var params = {
|
|
output: ["itemid",
|
|
"clock",
|
|
value_type
|
|
],
|
|
itemids: itemids,
|
|
time_from: time_from
|
|
};
|
|
|
|
// Relative queries (e.g. last hour) don't include an end time
|
|
if (time_till) {
|
|
params.time_till = time_till;
|
|
}
|
|
|
|
return self.request('trend.get', params);
|
|
}
|
|
|
|
getITService(serviceids) {
|
|
var params = {
|
|
output: 'extend',
|
|
serviceids: serviceids
|
|
};
|
|
return this.request('service.get', params);
|
|
}
|
|
|
|
getSLA(serviceids, timeRange, options) {
|
|
const intervals = buildSLAIntervals(timeRange, options.intervalMs);
|
|
const params = {
|
|
serviceids,
|
|
intervals
|
|
};
|
|
return this.request('service.getsla', params);
|
|
}
|
|
|
|
getTriggers(groupids, hostids, applicationids, options) {
|
|
let {showTriggers, maintenance, timeFrom, timeTo} = options;
|
|
|
|
let params = {
|
|
output: 'extend',
|
|
groupids: groupids,
|
|
hostids: hostids,
|
|
applicationids: applicationids,
|
|
expandDescription: true,
|
|
expandData: true,
|
|
expandComment: true,
|
|
monitored: true,
|
|
skipDependent: true,
|
|
//only_true: true,
|
|
filter: {
|
|
value: 1
|
|
},
|
|
selectGroups: ['name'],
|
|
selectHosts: ['name', 'host', 'maintenance_status', 'proxy_hostid'],
|
|
selectItems: ['name', 'key_', 'lastvalue'],
|
|
selectLastEvent: 'extend',
|
|
selectTags: 'extend'
|
|
};
|
|
|
|
if (showTriggers) {
|
|
params.filter.value = showTriggers;
|
|
}
|
|
|
|
if (maintenance) {
|
|
params.maintenance = true;
|
|
}
|
|
|
|
if (timeFrom || timeTo) {
|
|
params.lastChangeSince = timeFrom;
|
|
params.lastChangeTill = timeTo;
|
|
}
|
|
|
|
return this.request('trigger.get', params);
|
|
}
|
|
|
|
getEvents(objectids, timeFrom, timeTo, showEvents, limit) {
|
|
var params = {
|
|
output: 'extend',
|
|
time_from: timeFrom,
|
|
time_till: timeTo,
|
|
objectids: objectids,
|
|
select_acknowledges: 'extend',
|
|
selectHosts: 'extend',
|
|
value: showEvents,
|
|
};
|
|
|
|
if (limit) {
|
|
params.limit = limit;
|
|
params.sortfield = 'clock';
|
|
params.sortorder = 'DESC';
|
|
}
|
|
|
|
return this.request('event.get', params);
|
|
}
|
|
|
|
getAcknowledges(eventids) {
|
|
var params = {
|
|
output: 'extend',
|
|
eventids: eventids,
|
|
preservekeys: true,
|
|
select_acknowledges: 'extend',
|
|
sortfield: 'clock',
|
|
sortorder: 'DESC'
|
|
};
|
|
|
|
return this.request('event.get', params)
|
|
.then(events => {
|
|
return _.filter(events, (event) => event.acknowledges.length);
|
|
});
|
|
}
|
|
|
|
getExtendedEventData(eventids) {
|
|
var params = {
|
|
output: 'extend',
|
|
eventids: eventids,
|
|
preservekeys: true,
|
|
select_acknowledges: 'extend',
|
|
selectTags: 'extend',
|
|
sortfield: 'clock',
|
|
sortorder: 'DESC'
|
|
};
|
|
|
|
return this.request('event.get', params);
|
|
}
|
|
|
|
getEventAlerts(eventids) {
|
|
const params = {
|
|
eventids: eventids,
|
|
output: [
|
|
'eventid',
|
|
'message',
|
|
'clock',
|
|
'error'
|
|
],
|
|
selectUsers: true,
|
|
};
|
|
|
|
return this.request('alert.get', params);
|
|
}
|
|
|
|
getAlerts(itemids, timeFrom, timeTo) {
|
|
var params = {
|
|
output: 'extend',
|
|
itemids: itemids,
|
|
expandDescription: true,
|
|
expandData: true,
|
|
expandComment: true,
|
|
monitored: true,
|
|
skipDependent: true,
|
|
//only_true: true,
|
|
// filter: {
|
|
// value: 1
|
|
// },
|
|
selectLastEvent: 'extend'
|
|
};
|
|
|
|
if (timeFrom || timeTo) {
|
|
params.lastChangeSince = timeFrom;
|
|
params.lastChangeTill = timeTo;
|
|
}
|
|
|
|
return this.request('trigger.get', params);
|
|
}
|
|
|
|
getHostAlerts(hostids, applicationids, options) {
|
|
let {minSeverity, acknowledged, count, timeFrom, timeTo} = options;
|
|
let params = {
|
|
output: 'extend',
|
|
hostids: hostids,
|
|
min_severity: minSeverity,
|
|
filter: { value: 1 },
|
|
expandDescription: true,
|
|
expandData: true,
|
|
expandComment: true,
|
|
monitored: true,
|
|
skipDependent: true,
|
|
selectLastEvent: 'extend',
|
|
selectGroups: 'extend',
|
|
selectHosts: ['host', 'name']
|
|
};
|
|
|
|
if (count && acknowledged !== 0 && acknowledged !== 1) {
|
|
params.countOutput = true;
|
|
}
|
|
|
|
if (applicationids && applicationids.length) {
|
|
params.applicationids = applicationids;
|
|
}
|
|
|
|
if (timeFrom || timeTo) {
|
|
params.lastChangeSince = timeFrom;
|
|
params.lastChangeTill = timeTo;
|
|
}
|
|
|
|
return this.request('trigger.get', params)
|
|
.then((triggers) => {
|
|
if (!count || acknowledged === 0 || acknowledged === 1) {
|
|
triggers = filterTriggersByAcknowledge(triggers, acknowledged);
|
|
if (count) {
|
|
triggers = triggers.length;
|
|
}
|
|
}
|
|
return triggers;
|
|
});
|
|
}
|
|
|
|
getProxies() {
|
|
var params = {
|
|
output: ['proxyid', 'host'],
|
|
};
|
|
|
|
return this.request('proxy.get', params);
|
|
}
|
|
}
|
|
|
|
function filterTriggersByAcknowledge(triggers, acknowledged) {
|
|
if (acknowledged === 0) {
|
|
return _.filter(triggers, (trigger) => trigger.lastEvent.acknowledged === "0");
|
|
} else if (acknowledged === 1) {
|
|
return _.filter(triggers, (trigger) => trigger.lastEvent.acknowledged === "1");
|
|
} else {
|
|
return triggers;
|
|
}
|
|
}
|
|
|
|
function isNotAuthorized(message) {
|
|
return (
|
|
message === "Session terminated, re-login, please." ||
|
|
message === "Not authorised." ||
|
|
message === "Not authorized."
|
|
);
|
|
}
|
|
|
|
function getSLAInterval(intervalMs) {
|
|
// Too many intervals may cause significant load on the database, so decrease number of resulting points
|
|
const resolutionRatio = 100;
|
|
const interval = kbn.round_interval(intervalMs * resolutionRatio) / 1000;
|
|
return Math.max(interval, MIN_SLA_INTERVAL);
|
|
}
|
|
|
|
function buildSLAIntervals(timeRange, intervalMs) {
|
|
let [timeFrom, timeTo] = timeRange;
|
|
const slaInterval = getSLAInterval(intervalMs);
|
|
const intervals = [];
|
|
|
|
// Align time range with calculated interval
|
|
timeFrom = Math.floor(timeFrom / slaInterval) * slaInterval;
|
|
timeTo = Math.ceil(timeTo / slaInterval) * slaInterval;
|
|
|
|
for (let i = timeFrom; i <= timeTo - slaInterval; i += slaInterval) {
|
|
intervals.push({
|
|
from : i,
|
|
to : (i + slaInterval)
|
|
});
|
|
|
|
}
|
|
|
|
return intervals;
|
|
}
|