import angular from 'angular'; import _ from 'lodash'; import * as utils from './utils'; /** @ngInject */ angular.module('grafana.services').factory('QueryProcessor', function($q) { class QueryProcessor { constructor(zabbixCacheInstance) { this.cache = zabbixCacheInstance; this.$q = $q; } /** * Build query in asynchronous manner */ build(groupFilter, hostFilter, appFilter, itemFilter) { var self = this; if (this.cache._initialized) { return this.$q.when(self.buildFromCache(groupFilter, hostFilter, appFilter, itemFilter)); } else { return this.cache.refresh().then(function() { return self.buildFromCache(groupFilter, hostFilter, appFilter, itemFilter); }); } } /** * Build trigger query in asynchronous manner */ buildTriggerQuery(groupFilter, hostFilter, appFilter) { var self = this; if (this.cache._initialized) { return this.$q.when(self.buildTriggerQueryFromCache(groupFilter, hostFilter, appFilter)); } else { return this.cache.refresh().then(function() { return self.buildTriggerQueryFromCache(groupFilter, hostFilter, appFilter); }); } } filterGroups(groups, groupFilter) { return this.$q.when( findByFilter(groups, groupFilter) ); } /** * Get list of host belonging to given groups. * @return list of hosts */ filterHosts(hosts, hostFilter) { return this.$q.when( findByFilter(hosts, hostFilter) ); } filterApps(apps, appFilter) { return this.$q.when( findByFilter(apps, appFilter) ); } /** * Get list of applications belonging to given groups and hosts. * @return list of applications belonging to given hosts */ filterApplications(groupFilter, hostFilter) { var promises = [ this.filterHosts(groupFilter), this.cache.getApplications() ]; return this.$q.all(promises).then(function(results) { var hostList = results[0]; var applicationList = results[1]; var hosts = findByFilter(hostList, hostFilter); if (hosts) { var hostsids = _.map(hosts, 'hostid'); return _.filter(applicationList, function (appObj) { return _.intersection(hostsids, appObj.hosts).length; }); } else { return []; } }); } filterItems(groupFilter, hostFilter, appFilter, itemType, showDisabledItems) { var hosts; var apps; var items; var promises = [ this.filterHosts(groupFilter), this.filterApplications(groupFilter, hostFilter), this.cache.getIndexedHosts(), this.cache.getIndexedApplications() ]; return this.$q.all(promises).then(function(results) { var hostList = results[0]; var applicationList = results[1]; var idx_hosts = results[2]; var idx_apps = results[3]; // Filter hosts hosts = findByFilter(hostList, hostFilter); idx_hosts = getFromIndex(idx_hosts, _.map(hosts, 'hostid')); // Filter applications if (appFilter === "") { // Get all items apps = undefined; if (hosts) { // Get all items in given hosts items = _.flatten(_.map(idx_hosts, function(host) { return _.values(host.idx_items); }), true); } } else { apps = findByFilter(applicationList, appFilter); } if (apps) { // Get ids for finded applications var appids = _.flatten(_.map(apps, 'applicationids')); appids = _.flatten(_.map(_.map(hosts, 'applications'), function(apps) { return _.intersection(apps, appids); })); // For each finded host get list of items in finded applications items = _.flatten(_.map(idx_hosts, function(host) { var host_apps = _.intersection(appids, host.applications); var host_itemids = _.flatten(_.map(getFromIndex(idx_apps, host_apps), 'itemids')); return _.values(getFromIndex(host.idx_items, host_itemids)); }), true); } if (!showDisabledItems) { items = _.filter(items, {'status': '0'}); } return items; }); } buildFromCache(groupFilter, hostFilter, appFilter, itemFilter, showDisabledItems) { var self = this; return this.cache .getGroups() .then(groups => { return findByFilter(groups, groupFilter); }) .then(groups => { var groupids = _.map(groups, 'groupid'); return self.cache .getHosts(groupids) .then(hosts => { return findByFilter(hosts, hostFilter); }); }) .then(hosts => { var hostids = _.map(hosts, 'hostid'); if (appFilter) { return self.cache .getApps(hostids) .then(apps => { // Use getByFilter for proper item filtering return getByFilter(apps, appFilter); }); } else { return { appFilterEmpty: true, hostids: hostids }; } }) .then(apps => { if (apps.appFilterEmpty) { return self.cache .getItems(apps.hostids) .then(items => { if (showDisabledItems) { items = _.filter(items, {'status': '0'}); } return getByFilter(items, itemFilter); }); } else { var appids = _.map(apps, 'applicationid'); return self.cache .getItems(undefined, appids) .then(items => { if (showDisabledItems) { items = _.filter(items, {'status': '0'}); } return getByFilter(items, itemFilter); }); } }); } /** * Build query - convert target filters to array of Zabbix items */ _buildFromCache(groupFilter, hostFilter, appFilter, itemFilter) { return this.filterItems(groupFilter, hostFilter, appFilter).then(function(items) { if (items.length) { if (utils.isRegex(itemFilter)) { return findByFilter(items, itemFilter); } else { return _.filter(items, {'name': itemFilter}); } } else { return []; } }); } /** * Build query - convert target filters to array of Zabbix items */ buildTriggerQueryFromCache(groupFilter, hostFilter, appFilter) { var promises = [ this.cache.getGroups().then(function(groups) { return _.filter(groups, function(group) { if (utils.isRegex(groupFilter)) { return utils.buildRegex(groupFilter).test(group.name); } else { return group.name === groupFilter; } }); }), this.filterHosts(groupFilter).then(function(hosts) { return _.filter(hosts, function(host) { if (utils.isRegex(hostFilter)) { return utils.buildRegex(hostFilter).test(host.name); } else { return host.name === hostFilter; } }); }), this.filterApplications(groupFilter, hostFilter).then(function(apps) { return _.filter(apps, function(app) { if (utils.isRegex(appFilter)) { return utils.buildRegex(appFilter).test(app.name); } else { return app.name === appFilter; } }); }) ]; return this.$q.all(promises).then(function(results) { var filteredGroups = results[0]; var filteredHosts = results[1]; var filteredApps = results[2]; var query = {}; if (appFilter) { query.applicationids = _.flatten(_.map(filteredApps, 'applicationids')); } if (hostFilter) { query.hostids = _.map(filteredHosts, 'hostid'); } if (groupFilter) { query.groupids = _.map(filteredGroups, 'groupid'); } return query; }); } /** * Convert Zabbix API history.get response to Grafana format * * @return {Array} Array of timeseries in Grafana format * { * target: "Metric name", * datapoints: [[, ], ...] * } */ convertHistory(history, addHostName, convertPointCallback) { /** * Response should be in the format: * data: [ * { * target: "Metric name", * datapoints: [[, ], ...] * }, ... * ] */ var self = this; // Group history by itemid var grouped_history = _.groupBy(history, 'itemid'); return _.map(grouped_history, function(hist, itemid) { var item = self.cache.getItem(itemid); var alias = item.name; if (addHostName) { var host = self.cache.getHost(item.hostid); alias = host.name + ": " + alias; } return { target: alias, datapoints: _.map(hist, convertPointCallback) }; }); } handleHistory(history, addHostName) { return this.convertHistory(history, addHostName, convertHistoryPoint); } handleTrends(history, addHostName, valueType) { var convertPointCallback = _.partial(convertTrendPoint, valueType); return this.convertHistory(history, addHostName, convertPointCallback); } handleSLAResponse(itservice, slaProperty, slaObject) { var targetSLA = slaObject[itservice.serviceid].sla[0]; if (slaProperty.property === 'status') { var targetStatus = parseInt(slaObject[itservice.serviceid].status); return { target: itservice.name + ' ' + slaProperty.name, datapoints: [ [targetStatus, targetSLA.to * 1000] ] }; } else { return { target: itservice.name + ' ' + slaProperty.name, datapoints: [ [targetSLA[slaProperty.property], targetSLA.from * 1000], [targetSLA[slaProperty.property], targetSLA.to * 1000] ] }; } } } return QueryProcessor; }); /** * Find group, host, app or item by given name. * @param list list of groups, apps or other * @param name visible name * @return array with finded element or undefined */ function findByName(list, name) { var finded = _.find(list, {'name': name}); if (finded) { return [finded]; } else { return undefined; } } /** * Different hosts can contains applications and items with same name. * For this reason use _.filter, which return all elements instead _.find, * which return only first finded. * @param {[type]} list list of elements * @param {[type]} name app name * @return {[type]} array with finded element or undefined */ function filterByName(list, name) { var finded = _.filter(list, {'name': name}); if (finded) { return finded; } else { return undefined; } } function findByRegex(list, regex) { var filterPattern = utils.buildRegex(regex); return _.filter(list, function (zbx_obj) { return filterPattern.test(zbx_obj.name); }); } function findByFilter(list, filter) { if (utils.isRegex(filter)) { return findByRegex(list, filter); } else { return findByName(list, filter); } } function getByFilter(list, filter) { if (utils.isRegex(filter)) { return findByRegex(list, filter); } else { return filterByName(list, filter); } } function getFromIndex(index, objids) { return _.map(objids, function(id) { return index[id]; }); } function convertHistoryPoint(point) { // Value must be a number for properly work return [ Number(point.value), point.clock * 1000 ]; } function convertTrendPoint(valueType, point) { var value; switch (valueType) { case "min": value = point.value_min; break; case "max": value = point.value_max; break; case "avg": value = point.value_avg; break; default: value = point.value_avg; } return [ Number(value), point.clock * 1000 ]; }