Merge branch 'postgres-support'

This commit is contained in:
Alexander Zobnin
2017-10-17 19:52:23 +03:00
9 changed files with 261 additions and 45 deletions

View File

@@ -6,7 +6,15 @@ The format is based on [Keep a Changelog](http://keepachangelog.com/)
and this project adheres to [Semantic Versioning](http://semver.org/). and this project adheres to [Semantic Versioning](http://semver.org/).
## [Unreleased] ## [Unreleased]
### Added
- PostgreSQL support for Direct DB Connection.
- `sortSeries()` function that allows to sort multiple timeseries by name, [#447](https://github.com/alexanderzobnin/grafana-zabbix/issues/447)
- `percentil()` function
### Fixed
- Direct DB connection doesn't work with `ONLY_FULL_GROUP_BY` option enabled, [#445](https://github.com/alexanderzobnin/grafana-zabbix/issues/445)
- Application selection doesn't work, [#352](https://github.com/alexanderzobnin/grafana-zabbix/issues/352)
- "data points outside time range" error when there is no datapoints and aggregation function is used
## [3.6.1] - 2017-07-26 ## [3.6.1] - 2017-07-26
### Fixed ### Fixed

View File

@@ -34,7 +34,7 @@ System.register(['lodash'], function (_export, _context) {
}; };
}(); }();
SUPPORTED_SQL_DS = ['mysql']; SUPPORTED_SQL_DS = ['mysql', 'postgres'];
defaultConfig = { defaultConfig = {
dbConnection: { dbConnection: {
enable: false enable: false

View File

@@ -1 +1 @@
{"version":3,"sources":["../../src/datasource-zabbix/config.controller.js"],"names":["_","SUPPORTED_SQL_DS","defaultConfig","dbConnection","enable","ZabbixDSConfigController","$scope","$injector","datasourceSrv","defaults","current","jsonData","sqlDataSources","getSupportedSQLDataSources","datasources","getAll","filter","includes","ds","type","templateUrl"],"mappings":";;;;;;;;;;;;;;;AAAOA,O;;;;;;;;;;;;;;;;;;;;;AAEDC,sB,GAAmB,CAAC,OAAD,C;AAEnBC,mB,GAAgB;AACpBC,sBAAc;AACZC,kBAAQ;AADI;AADM,O;;0CAMTC,wB;AACX;AACA,0CAAYC,MAAZ,EAAoBC,SAApB,EAA+BC,aAA/B,EAA8C;AAAA;;AAC5C,eAAKA,aAAL,GAAqBA,aAArB;;AAEAR,YAAES,QAAF,CAAW,KAAKC,OAAL,CAAaC,QAAxB,EAAkCT,aAAlC;AACA,eAAKU,cAAL,GAAsB,KAAKC,0BAAL,EAAtB;AACD;;;;uDAE4B;AAC3B,gBAAIC,cAAc,KAAKN,aAAL,CAAmBO,MAAnB,EAAlB;AACA,mBAAOf,EAAEgB,MAAF,CAASF,WAAT,EAAsB,cAAM;AACjC,qBAAOd,EAAEiB,QAAF,CAAWhB,gBAAX,EAA6BiB,GAAGC,IAAhC,CAAP;AACD,aAFM,CAAP;AAGD;;;;;;;;AAGHd,+BAAyBe,WAAzB,GAAuC,wCAAvC","file":"config.controller.js","sourcesContent":["import _ from 'lodash';\n\nconst SUPPORTED_SQL_DS = ['mysql'];\n\nconst defaultConfig = {\n dbConnection: {\n enable: false,\n }\n};\n\nexport class ZabbixDSConfigController {\n /** @ngInject */\n constructor($scope, $injector, datasourceSrv) {\n this.datasourceSrv = datasourceSrv;\n\n _.defaults(this.current.jsonData, defaultConfig);\n this.sqlDataSources = this.getSupportedSQLDataSources();\n }\n\n getSupportedSQLDataSources() {\n let datasources = this.datasourceSrv.getAll();\n return _.filter(datasources, ds => {\n return _.includes(SUPPORTED_SQL_DS, ds.type);\n });\n }\n}\n\nZabbixDSConfigController.templateUrl = 'datasource-zabbix/partials/config.html';\n"]} {"version":3,"sources":["../../src/datasource-zabbix/config.controller.js"],"names":["_","SUPPORTED_SQL_DS","defaultConfig","dbConnection","enable","ZabbixDSConfigController","$scope","$injector","datasourceSrv","defaults","current","jsonData","sqlDataSources","getSupportedSQLDataSources","datasources","getAll","filter","includes","ds","type","templateUrl"],"mappings":";;;;;;;;;;;;;;;AAAOA,O;;;;;;;;;;;;;;;;;;;;;AAEDC,sB,GAAmB,CAAC,OAAD,EAAU,UAAV,C;AAEnBC,mB,GAAgB;AACpBC,sBAAc;AACZC,kBAAQ;AADI;AADM,O;;0CAMTC,wB;AACX;AACA,0CAAYC,MAAZ,EAAoBC,SAApB,EAA+BC,aAA/B,EAA8C;AAAA;;AAC5C,eAAKA,aAAL,GAAqBA,aAArB;;AAEAR,YAAES,QAAF,CAAW,KAAKC,OAAL,CAAaC,QAAxB,EAAkCT,aAAlC;AACA,eAAKU,cAAL,GAAsB,KAAKC,0BAAL,EAAtB;AACD;;;;uDAE4B;AAC3B,gBAAIC,cAAc,KAAKN,aAAL,CAAmBO,MAAnB,EAAlB;AACA,mBAAOf,EAAEgB,MAAF,CAASF,WAAT,EAAsB,cAAM;AACjC,qBAAOd,EAAEiB,QAAF,CAAWhB,gBAAX,EAA6BiB,GAAGC,IAAhC,CAAP;AACD,aAFM,CAAP;AAGD;;;;;;;;AAGHd,+BAAyBe,WAAzB,GAAuC,wCAAvC","file":"config.controller.js","sourcesContent":["import _ from 'lodash';\n\nconst SUPPORTED_SQL_DS = ['mysql', 'postgres'];\n\nconst defaultConfig = {\n dbConnection: {\n enable: false,\n }\n};\n\nexport class ZabbixDSConfigController {\n /** @ngInject */\n constructor($scope, $injector, datasourceSrv) {\n this.datasourceSrv = datasourceSrv;\n\n _.defaults(this.current.jsonData, defaultConfig);\n this.sqlDataSources = this.getSupportedSQLDataSources();\n }\n\n getSupportedSQLDataSources() {\n let datasources = this.datasourceSrv.getAll();\n return _.filter(datasources, ds => {\n return _.includes(SUPPORTED_SQL_DS, ds.type);\n });\n }\n}\n\nZabbixDSConfigController.templateUrl = 'datasource-zabbix/partials/config.html';\n"]}

View File

@@ -3,7 +3,7 @@
System.register(['angular', 'lodash'], function (_export, _context) { System.register(['angular', 'lodash'], function (_export, _context) {
"use strict"; "use strict";
var angular, _, _createClass, DEFAULT_QUERY_LIMIT, HISTORY_TO_TABLE_MAP, TREND_TO_TABLE_MAP, consolidateByFunc, consolidateByTrendColumns; var angular, _, _createClass, DEFAULT_QUERY_LIMIT, HISTORY_TO_TABLE_MAP, TREND_TO_TABLE_MAP, consolidateByFunc, consolidateByTrendColumns, TEST_MYSQL_QUERY, itemid_format, TEST_POSTGRES_QUERY;
function _classCallCheck(instance, Constructor) { function _classCallCheck(instance, Constructor) {
if (!(instance instanceof Constructor)) { if (!(instance instanceof Constructor)) {
@@ -24,6 +24,8 @@ System.register(['angular', 'lodash'], function (_export, _context) {
this.sqlDataSourceId = sqlDataSourceId; this.sqlDataSourceId = sqlDataSourceId;
this.limit = limit || DEFAULT_QUERY_LIMIT; this.limit = limit || DEFAULT_QUERY_LIMIT;
this.loadSQLDataSource(sqlDataSourceId);
} }
/** /**
@@ -35,10 +37,13 @@ System.register(['angular', 'lodash'], function (_export, _context) {
_createClass(ZabbixDBConnector, [{ _createClass(ZabbixDBConnector, [{
key: 'loadSQLDataSource', key: 'loadSQLDataSource',
value: function loadSQLDataSource(datasourceId) { value: function loadSQLDataSource(datasourceId) {
var _this = this;
var ds = _.find(datasourceSrv.getAll(), { 'id': datasourceId }); var ds = _.find(datasourceSrv.getAll(), { 'id': datasourceId });
if (ds) { if (ds) {
return datasourceSrv.loadDatasource(ds.name).then(function (ds) { return datasourceSrv.loadDatasource(ds.name).then(function (ds) {
console.log('SQL data source loaded', ds); _this.sqlDataSourceType = ds.meta.id;
return ds;
}); });
} else { } else {
return Promise.reject('SQL Data Source with ID ' + datasourceId + ' not found'); return Promise.reject('SQL Data Source with ID ' + datasourceId + ' not found');
@@ -47,13 +52,16 @@ System.register(['angular', 'lodash'], function (_export, _context) {
}, { }, {
key: 'testSQLDataSource', key: 'testSQLDataSource',
value: function testSQLDataSource() { value: function testSQLDataSource() {
var testQuery = 'SELECT itemid AS metric, clock AS time_sec, value_avg AS value FROM trends_uint LIMIT 1'; var testQuery = TEST_MYSQL_QUERY;
if (this.sqlDataSourceType === 'postgres') {
testQuery = TEST_POSTGRES_QUERY;
}
return this.invokeSQLQuery(testQuery); return this.invokeSQLQuery(testQuery);
} }
}, { }, {
key: 'getHistory', key: 'getHistory',
value: function getHistory(items, timeFrom, timeTill, options) { value: function getHistory(items, timeFrom, timeTill, options) {
var _this = this; var _this2 = this;
var intervalMs = options.intervalMs, var intervalMs = options.intervalMs,
consolidateBy = options.consolidateBy; consolidateBy = options.consolidateBy;
@@ -69,11 +77,11 @@ System.register(['angular', 'lodash'], function (_export, _context) {
var itemids = _.map(items, 'itemid').join(', '); var itemids = _.map(items, 'itemid').join(', ');
var table = HISTORY_TO_TABLE_MAP[value_type]; var table = HISTORY_TO_TABLE_MAP[value_type];
var time_expression = 'clock DIV ' + intervalSec + ' * ' + intervalSec; var dialect = _this2.sqlDataSourceType;
var query = '\n SELECT itemid AS metric, ' + time_expression + ' AS time_sec, ' + aggFunction + '(value) AS value\n FROM ' + table + '\n WHERE itemid IN (' + itemids + ')\n AND clock > ' + timeFrom + ' AND clock < ' + timeTill + '\n GROUP BY ' + time_expression + ', metric\n '; var query = buildSQLHistoryQuery(itemids, table, timeFrom, timeTill, intervalSec, aggFunction, dialect);
query = compactSQLQuery(query); query = compactSQLQuery(query);
return _this.invokeSQLQuery(query); return _this2.invokeSQLQuery(query);
}); });
return Promise.all(promises).then(function (results) { return Promise.all(promises).then(function (results) {
@@ -83,7 +91,7 @@ System.register(['angular', 'lodash'], function (_export, _context) {
}, { }, {
key: 'getTrends', key: 'getTrends',
value: function getTrends(items, timeFrom, timeTill, options) { value: function getTrends(items, timeFrom, timeTill, options) {
var _this2 = this; var _this3 = this;
var intervalMs = options.intervalMs, var intervalMs = options.intervalMs,
consolidateBy = options.consolidateBy; consolidateBy = options.consolidateBy;
@@ -101,11 +109,11 @@ System.register(['angular', 'lodash'], function (_export, _context) {
var valueColumn = _.includes(['avg', 'min', 'max'], consolidateBy) ? consolidateBy : 'avg'; var valueColumn = _.includes(['avg', 'min', 'max'], consolidateBy) ? consolidateBy : 'avg';
valueColumn = consolidateByTrendColumns[valueColumn]; valueColumn = consolidateByTrendColumns[valueColumn];
var time_expression = 'clock DIV ' + intervalSec + ' * ' + intervalSec; var dialect = _this3.sqlDataSourceType;
var query = '\n SELECT itemid AS metric, ' + time_expression + ' AS time_sec, ' + aggFunction + '(' + valueColumn + ') AS value\n FROM ' + table + '\n WHERE itemid IN (' + itemids + ')\n AND clock > ' + timeFrom + ' AND clock < ' + timeTill + '\n GROUP BY ' + time_expression + ', metric\n '; var query = buildSQLTrendsQuery(itemids, table, timeFrom, timeTill, intervalSec, aggFunction, valueColumn, dialect);
query = compactSQLQuery(query); query = compactSQLQuery(query);
return _this2.invokeSQLQuery(query); return _this3.invokeSQLQuery(query);
}); });
return Promise.all(promises).then(function (results) { return Promise.all(promises).then(function (results) {
@@ -181,6 +189,55 @@ System.register(['angular', 'lodash'], function (_export, _context) {
function compactSQLQuery(query) { function compactSQLQuery(query) {
return query.replace(/\s+/g, ' '); return query.replace(/\s+/g, ' ');
} }
function buildSQLHistoryQuery(itemids, table, timeFrom, timeTill, intervalSec, aggFunction) {
var dialect = arguments.length > 6 && arguments[6] !== undefined ? arguments[6] : 'mysql';
if (dialect === 'postgres') {
return buildPostgresHistoryQuery(itemids, table, timeFrom, timeTill, intervalSec, aggFunction);
} else {
return buildMysqlHistoryQuery(itemids, table, timeFrom, timeTill, intervalSec, aggFunction);
}
}
function buildSQLTrendsQuery(itemids, table, timeFrom, timeTill, intervalSec, aggFunction, valueColumn) {
var dialect = arguments.length > 7 && arguments[7] !== undefined ? arguments[7] : 'mysql';
if (dialect === 'postgres') {
return buildPostgresTrendsQuery(itemids, table, timeFrom, timeTill, intervalSec, aggFunction, valueColumn);
} else {
return buildMysqlTrendsQuery(itemids, table, timeFrom, timeTill, intervalSec, aggFunction, valueColumn);
}
}
///////////
// MySQL //
///////////
function buildMysqlHistoryQuery(itemids, table, timeFrom, timeTill, intervalSec, aggFunction) {
var time_expression = 'clock DIV ' + intervalSec + ' * ' + intervalSec;
var query = '\n SELECT itemid AS metric, ' + time_expression + ' AS time_sec, ' + aggFunction + '(value) AS value\n FROM ' + table + '\n WHERE itemid IN (' + itemids + ')\n AND clock > ' + timeFrom + ' AND clock < ' + timeTill + '\n GROUP BY ' + time_expression + ', metric\n ORDER BY time_sec ASC\n ';
return query;
}
function buildMysqlTrendsQuery(itemids, table, timeFrom, timeTill, intervalSec, aggFunction, valueColumn) {
var time_expression = 'clock DIV ' + intervalSec + ' * ' + intervalSec;
var query = '\n SELECT itemid AS metric, ' + time_expression + ' AS time_sec, ' + aggFunction + '(' + valueColumn + ') AS value\n FROM ' + table + '\n WHERE itemid IN (' + itemids + ')\n AND clock > ' + timeFrom + ' AND clock < ' + timeTill + '\n GROUP BY ' + time_expression + ', metric\n ORDER BY time_sec ASC\n ';
return query;
}
function buildPostgresHistoryQuery(itemids, table, timeFrom, timeTill, intervalSec, aggFunction) {
var time_expression = 'clock / ' + intervalSec + ' * ' + intervalSec;
var query = '\n SELECT DISTINCT to_char(itemid, \'' + itemid_format + '\') AS metric,\n ' + time_expression + ' AS time,\n ' + aggFunction + '(value) OVER (PARTITION BY clock / ' + intervalSec + ') AS value\n FROM ' + table + '\n WHERE itemid IN (' + itemids + ')\n AND clock > ' + timeFrom + ' AND clock < ' + timeTill + '\n GROUP BY metric, clock, value\n ORDER BY time ASC\n ';
return query;
}
function buildPostgresTrendsQuery(itemids, table, timeFrom, timeTill, intervalSec, aggFunction, valueColumn) {
var time_expression = 'clock / ' + intervalSec + ' * ' + intervalSec;
var query = '\n SELECT DISTINCT to_char(itemid, \'' + itemid_format + '\') AS metric,\n ' + time_expression + ' AS time,\n ' + aggFunction + '(' + valueColumn + ') OVER (PARTITION BY clock / ' + intervalSec + ') AS value\n FROM ' + table + '\n WHERE itemid IN (' + itemids + ')\n AND clock > ' + timeFrom + ' AND clock < ' + timeTill + '\n GROUP BY metric, clock, ' + valueColumn + '\n ORDER BY time ASC\n ';
return query;
}
return { return {
setters: [function (_angular) { setters: [function (_angular) {
angular = _angular.default; angular = _angular.default;
@@ -230,7 +287,9 @@ System.register(['angular', 'lodash'], function (_export, _context) {
'min': 'value_min', 'min': 'value_min',
'max': 'value_max' 'max': 'value_max'
}; };
angular.module('grafana.services').factory('ZabbixDBConnector', ZabbixDBConnectorFactory); angular.module('grafana.services').factory('ZabbixDBConnector', ZabbixDBConnectorFactory);TEST_MYSQL_QUERY = 'SELECT itemid AS metric, clock AS time_sec, value_avg AS value FROM trends_uint LIMIT 1';
itemid_format = 'FM99999999999999999999';
TEST_POSTGRES_QUERY = '\n SELECT to_char(itemid, \'' + itemid_format + '\') AS metric, clock AS time, value_avg AS value\n FROM trends_uint LIMIT 1\n';
} }
}; };
}); });

File diff suppressed because one or more lines are too long

View File

@@ -15,7 +15,7 @@ function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { de
function _classCallCheck(instance, Constructor) { if (!(instance instanceof Constructor)) { throw new TypeError("Cannot call a class as a function"); } } function _classCallCheck(instance, Constructor) { if (!(instance instanceof Constructor)) { throw new TypeError("Cannot call a class as a function"); } }
var SUPPORTED_SQL_DS = ['mysql']; var SUPPORTED_SQL_DS = ['mysql', 'postgres'];
var defaultConfig = { var defaultConfig = {
dbConnection: { dbConnection: {

View File

@@ -55,6 +55,8 @@ function ZabbixDBConnectorFactory(datasourceSrv, backendSrv) {
this.sqlDataSourceId = sqlDataSourceId; this.sqlDataSourceId = sqlDataSourceId;
this.limit = limit || DEFAULT_QUERY_LIMIT; this.limit = limit || DEFAULT_QUERY_LIMIT;
this.loadSQLDataSource(sqlDataSourceId);
} }
/** /**
@@ -66,10 +68,13 @@ function ZabbixDBConnectorFactory(datasourceSrv, backendSrv) {
_createClass(ZabbixDBConnector, [{ _createClass(ZabbixDBConnector, [{
key: 'loadSQLDataSource', key: 'loadSQLDataSource',
value: function loadSQLDataSource(datasourceId) { value: function loadSQLDataSource(datasourceId) {
var _this = this;
var ds = _lodash2.default.find(datasourceSrv.getAll(), { 'id': datasourceId }); var ds = _lodash2.default.find(datasourceSrv.getAll(), { 'id': datasourceId });
if (ds) { if (ds) {
return datasourceSrv.loadDatasource(ds.name).then(function (ds) { return datasourceSrv.loadDatasource(ds.name).then(function (ds) {
console.log('SQL data source loaded', ds); _this.sqlDataSourceType = ds.meta.id;
return ds;
}); });
} else { } else {
return Promise.reject('SQL Data Source with ID ' + datasourceId + ' not found'); return Promise.reject('SQL Data Source with ID ' + datasourceId + ' not found');
@@ -83,13 +88,16 @@ function ZabbixDBConnectorFactory(datasourceSrv, backendSrv) {
}, { }, {
key: 'testSQLDataSource', key: 'testSQLDataSource',
value: function testSQLDataSource() { value: function testSQLDataSource() {
var testQuery = 'SELECT itemid AS metric, clock AS time_sec, value_avg AS value FROM trends_uint LIMIT 1'; var testQuery = TEST_MYSQL_QUERY;
if (this.sqlDataSourceType === 'postgres') {
testQuery = TEST_POSTGRES_QUERY;
}
return this.invokeSQLQuery(testQuery); return this.invokeSQLQuery(testQuery);
} }
}, { }, {
key: 'getHistory', key: 'getHistory',
value: function getHistory(items, timeFrom, timeTill, options) { value: function getHistory(items, timeFrom, timeTill, options) {
var _this = this; var _this2 = this;
var intervalMs = options.intervalMs, var intervalMs = options.intervalMs,
consolidateBy = options.consolidateBy; consolidateBy = options.consolidateBy;
@@ -105,11 +113,11 @@ function ZabbixDBConnectorFactory(datasourceSrv, backendSrv) {
var itemids = _lodash2.default.map(items, 'itemid').join(', '); var itemids = _lodash2.default.map(items, 'itemid').join(', ');
var table = HISTORY_TO_TABLE_MAP[value_type]; var table = HISTORY_TO_TABLE_MAP[value_type];
var time_expression = 'clock DIV ' + intervalSec + ' * ' + intervalSec; var dialect = _this2.sqlDataSourceType;
var query = '\n SELECT itemid AS metric, ' + time_expression + ' AS time_sec, ' + aggFunction + '(value) AS value\n FROM ' + table + '\n WHERE itemid IN (' + itemids + ')\n AND clock > ' + timeFrom + ' AND clock < ' + timeTill + '\n GROUP BY ' + time_expression + ', metric\n '; var query = buildSQLHistoryQuery(itemids, table, timeFrom, timeTill, intervalSec, aggFunction, dialect);
query = compactSQLQuery(query); query = compactSQLQuery(query);
return _this.invokeSQLQuery(query); return _this2.invokeSQLQuery(query);
}); });
return Promise.all(promises).then(function (results) { return Promise.all(promises).then(function (results) {
@@ -119,7 +127,7 @@ function ZabbixDBConnectorFactory(datasourceSrv, backendSrv) {
}, { }, {
key: 'getTrends', key: 'getTrends',
value: function getTrends(items, timeFrom, timeTill, options) { value: function getTrends(items, timeFrom, timeTill, options) {
var _this2 = this; var _this3 = this;
var intervalMs = options.intervalMs, var intervalMs = options.intervalMs,
consolidateBy = options.consolidateBy; consolidateBy = options.consolidateBy;
@@ -137,11 +145,11 @@ function ZabbixDBConnectorFactory(datasourceSrv, backendSrv) {
var valueColumn = _lodash2.default.includes(['avg', 'min', 'max'], consolidateBy) ? consolidateBy : 'avg'; var valueColumn = _lodash2.default.includes(['avg', 'min', 'max'], consolidateBy) ? consolidateBy : 'avg';
valueColumn = consolidateByTrendColumns[valueColumn]; valueColumn = consolidateByTrendColumns[valueColumn];
var time_expression = 'clock DIV ' + intervalSec + ' * ' + intervalSec; var dialect = _this3.sqlDataSourceType;
var query = '\n SELECT itemid AS metric, ' + time_expression + ' AS time_sec, ' + aggFunction + '(' + valueColumn + ') AS value\n FROM ' + table + '\n WHERE itemid IN (' + itemids + ')\n AND clock > ' + timeFrom + ' AND clock < ' + timeTill + '\n GROUP BY ' + time_expression + ', metric\n '; var query = buildSQLTrendsQuery(itemids, table, timeFrom, timeTill, intervalSec, aggFunction, valueColumn, dialect);
query = compactSQLQuery(query); query = compactSQLQuery(query);
return _this2.invokeSQLQuery(query); return _this3.invokeSQLQuery(query);
}); });
return Promise.all(promises).then(function (results) { return Promise.all(promises).then(function (results) {
@@ -219,3 +227,61 @@ function convertGrafanaTSResponse(time_series, items, addHostName) {
function compactSQLQuery(query) { function compactSQLQuery(query) {
return query.replace(/\s+/g, ' '); return query.replace(/\s+/g, ' ');
} }
function buildSQLHistoryQuery(itemids, table, timeFrom, timeTill, intervalSec, aggFunction) {
var dialect = arguments.length > 6 && arguments[6] !== undefined ? arguments[6] : 'mysql';
if (dialect === 'postgres') {
return buildPostgresHistoryQuery(itemids, table, timeFrom, timeTill, intervalSec, aggFunction);
} else {
return buildMysqlHistoryQuery(itemids, table, timeFrom, timeTill, intervalSec, aggFunction);
}
}
function buildSQLTrendsQuery(itemids, table, timeFrom, timeTill, intervalSec, aggFunction, valueColumn) {
var dialect = arguments.length > 7 && arguments[7] !== undefined ? arguments[7] : 'mysql';
if (dialect === 'postgres') {
return buildPostgresTrendsQuery(itemids, table, timeFrom, timeTill, intervalSec, aggFunction, valueColumn);
} else {
return buildMysqlTrendsQuery(itemids, table, timeFrom, timeTill, intervalSec, aggFunction, valueColumn);
}
}
///////////
// MySQL //
///////////
function buildMysqlHistoryQuery(itemids, table, timeFrom, timeTill, intervalSec, aggFunction) {
var time_expression = 'clock DIV ' + intervalSec + ' * ' + intervalSec;
var query = '\n SELECT itemid AS metric, ' + time_expression + ' AS time_sec, ' + aggFunction + '(value) AS value\n FROM ' + table + '\n WHERE itemid IN (' + itemids + ')\n AND clock > ' + timeFrom + ' AND clock < ' + timeTill + '\n GROUP BY ' + time_expression + ', metric\n ORDER BY time_sec ASC\n ';
return query;
}
function buildMysqlTrendsQuery(itemids, table, timeFrom, timeTill, intervalSec, aggFunction, valueColumn) {
var time_expression = 'clock DIV ' + intervalSec + ' * ' + intervalSec;
var query = '\n SELECT itemid AS metric, ' + time_expression + ' AS time_sec, ' + aggFunction + '(' + valueColumn + ') AS value\n FROM ' + table + '\n WHERE itemid IN (' + itemids + ')\n AND clock > ' + timeFrom + ' AND clock < ' + timeTill + '\n GROUP BY ' + time_expression + ', metric\n ORDER BY time_sec ASC\n ';
return query;
}
var TEST_MYSQL_QUERY = 'SELECT itemid AS metric, clock AS time_sec, value_avg AS value FROM trends_uint LIMIT 1';
////////////////
// PostgreSQL //
////////////////
var itemid_format = 'FM99999999999999999999';
function buildPostgresHistoryQuery(itemids, table, timeFrom, timeTill, intervalSec, aggFunction) {
var time_expression = 'clock / ' + intervalSec + ' * ' + intervalSec;
var query = '\n SELECT DISTINCT to_char(itemid, \'' + itemid_format + '\') AS metric,\n ' + time_expression + ' AS time,\n ' + aggFunction + '(value) OVER (PARTITION BY clock / ' + intervalSec + ') AS value\n FROM ' + table + '\n WHERE itemid IN (' + itemids + ')\n AND clock > ' + timeFrom + ' AND clock < ' + timeTill + '\n GROUP BY metric, clock, value\n ORDER BY time ASC\n ';
return query;
}
function buildPostgresTrendsQuery(itemids, table, timeFrom, timeTill, intervalSec, aggFunction, valueColumn) {
var time_expression = 'clock / ' + intervalSec + ' * ' + intervalSec;
var query = '\n SELECT DISTINCT to_char(itemid, \'' + itemid_format + '\') AS metric,\n ' + time_expression + ' AS time,\n ' + aggFunction + '(' + valueColumn + ') OVER (PARTITION BY clock / ' + intervalSec + ') AS value\n FROM ' + table + '\n WHERE itemid IN (' + itemids + ')\n AND clock > ' + timeFrom + ' AND clock < ' + timeTill + '\n GROUP BY metric, clock, ' + valueColumn + '\n ORDER BY time ASC\n ';
return query;
}
var TEST_POSTGRES_QUERY = '\n SELECT to_char(itemid, \'' + itemid_format + '\') AS metric, clock AS time, value_avg AS value\n FROM trends_uint LIMIT 1\n';

View File

@@ -1,6 +1,6 @@
import _ from 'lodash'; import _ from 'lodash';
const SUPPORTED_SQL_DS = ['mysql']; const SUPPORTED_SQL_DS = ['mysql', 'postgres'];
const defaultConfig = { const defaultConfig = {
dbConnection: { dbConnection: {

View File

@@ -39,6 +39,8 @@ function ZabbixDBConnectorFactory(datasourceSrv, backendSrv) {
this.sqlDataSourceId = sqlDataSourceId; this.sqlDataSourceId = sqlDataSourceId;
this.limit = limit || DEFAULT_QUERY_LIMIT; this.limit = limit || DEFAULT_QUERY_LIMIT;
this.loadSQLDataSource(sqlDataSourceId);
} }
/** /**
@@ -50,7 +52,8 @@ function ZabbixDBConnectorFactory(datasourceSrv, backendSrv) {
if (ds) { if (ds) {
return datasourceSrv.loadDatasource(ds.name) return datasourceSrv.loadDatasource(ds.name)
.then(ds => { .then(ds => {
console.log('SQL data source loaded', ds); this.sqlDataSourceType = ds.meta.id;
return ds;
}); });
} else { } else {
return Promise.reject(`SQL Data Source with ID ${datasourceId} not found`); return Promise.reject(`SQL Data Source with ID ${datasourceId} not found`);
@@ -61,7 +64,10 @@ function ZabbixDBConnectorFactory(datasourceSrv, backendSrv) {
* Try to invoke test query for one of Zabbix database tables. * Try to invoke test query for one of Zabbix database tables.
*/ */
testSQLDataSource() { testSQLDataSource() {
let testQuery = `SELECT itemid AS metric, clock AS time_sec, value_avg AS value FROM trends_uint LIMIT 1`; let testQuery = TEST_MYSQL_QUERY;
if (this.sqlDataSourceType === 'postgres') {
testQuery = TEST_POSTGRES_QUERY;
}
return this.invokeSQLQuery(testQuery); return this.invokeSQLQuery(testQuery);
} }
@@ -78,14 +84,8 @@ function ZabbixDBConnectorFactory(datasourceSrv, backendSrv) {
let itemids = _.map(items, 'itemid').join(', '); let itemids = _.map(items, 'itemid').join(', ');
let table = HISTORY_TO_TABLE_MAP[value_type]; let table = HISTORY_TO_TABLE_MAP[value_type];
let time_expression = `clock DIV ${intervalSec} * ${intervalSec}`; let dialect = this.sqlDataSourceType;
let query = ` let query = buildSQLHistoryQuery(itemids, table, timeFrom, timeTill, intervalSec, aggFunction, dialect);
SELECT itemid AS metric, ${time_expression} AS time_sec, ${aggFunction}(value) AS value
FROM ${table}
WHERE itemid IN (${itemids})
AND clock > ${timeFrom} AND clock < ${timeTill}
GROUP BY ${time_expression}, metric
`;
query = compactSQLQuery(query); query = compactSQLQuery(query);
return this.invokeSQLQuery(query); return this.invokeSQLQuery(query);
@@ -111,14 +111,8 @@ function ZabbixDBConnectorFactory(datasourceSrv, backendSrv) {
let valueColumn = _.includes(['avg', 'min', 'max'], consolidateBy) ? consolidateBy : 'avg'; let valueColumn = _.includes(['avg', 'min', 'max'], consolidateBy) ? consolidateBy : 'avg';
valueColumn = consolidateByTrendColumns[valueColumn]; valueColumn = consolidateByTrendColumns[valueColumn];
let time_expression = `clock DIV ${intervalSec} * ${intervalSec}`; let dialect = this.sqlDataSourceType;
let query = ` let query = buildSQLTrendsQuery(itemids, table, timeFrom, timeTill, intervalSec, aggFunction, valueColumn, dialect);
SELECT itemid AS metric, ${time_expression} AS time_sec, ${aggFunction}(${valueColumn}) AS value
FROM ${table}
WHERE itemid IN (${itemids})
AND clock > ${timeFrom} AND clock < ${timeTill}
GROUP BY ${time_expression}, metric
`;
query = compactSQLQuery(query); query = compactSQLQuery(query);
return this.invokeSQLQuery(query); return this.invokeSQLQuery(query);
@@ -194,3 +188,92 @@ function convertGrafanaTSResponse(time_series, items, addHostName) {
function compactSQLQuery(query) { function compactSQLQuery(query) {
return query.replace(/\s+/g, ' '); return query.replace(/\s+/g, ' ');
} }
function buildSQLHistoryQuery(itemids, table, timeFrom, timeTill, intervalSec, aggFunction, dialect = 'mysql') {
if (dialect === 'postgres') {
return buildPostgresHistoryQuery(itemids, table, timeFrom, timeTill, intervalSec, aggFunction);
} else {
return buildMysqlHistoryQuery(itemids, table, timeFrom, timeTill, intervalSec, aggFunction);
}
}
function buildSQLTrendsQuery(itemids, table, timeFrom, timeTill, intervalSec, aggFunction, valueColumn, dialect = 'mysql') {
if (dialect === 'postgres') {
return buildPostgresTrendsQuery(itemids, table, timeFrom, timeTill, intervalSec, aggFunction, valueColumn);
} else {
return buildMysqlTrendsQuery(itemids, table, timeFrom, timeTill, intervalSec, aggFunction, valueColumn);
}
}
///////////
// MySQL //
///////////
function buildMysqlHistoryQuery(itemids, table, timeFrom, timeTill, intervalSec, aggFunction) {
let time_expression = `clock DIV ${intervalSec} * ${intervalSec}`;
let query = `
SELECT itemid AS metric, ${time_expression} AS time_sec, ${aggFunction}(value) AS value
FROM ${table}
WHERE itemid IN (${itemids})
AND clock > ${timeFrom} AND clock < ${timeTill}
GROUP BY ${time_expression}, metric
ORDER BY time_sec ASC
`;
return query;
}
function buildMysqlTrendsQuery(itemids, table, timeFrom, timeTill, intervalSec, aggFunction, valueColumn) {
let time_expression = `clock DIV ${intervalSec} * ${intervalSec}`;
let query = `
SELECT itemid AS metric, ${time_expression} AS time_sec, ${aggFunction}(${valueColumn}) AS value
FROM ${table}
WHERE itemid IN (${itemids})
AND clock > ${timeFrom} AND clock < ${timeTill}
GROUP BY ${time_expression}, metric
ORDER BY time_sec ASC
`;
return query;
}
const TEST_MYSQL_QUERY = `SELECT itemid AS metric, clock AS time_sec, value_avg AS value FROM trends_uint LIMIT 1`;
////////////////
// PostgreSQL //
////////////////
const itemid_format = 'FM99999999999999999999';
function buildPostgresHistoryQuery(itemids, table, timeFrom, timeTill, intervalSec, aggFunction) {
let time_expression = `clock / ${intervalSec} * ${intervalSec}`;
let query = `
SELECT DISTINCT to_char(itemid, '${itemid_format}') AS metric,
${time_expression} AS time,
${aggFunction}(value) OVER (PARTITION BY clock / ${intervalSec}) AS value
FROM ${table}
WHERE itemid IN (${itemids})
AND clock > ${timeFrom} AND clock < ${timeTill}
GROUP BY metric, clock, value
ORDER BY time ASC
`;
return query;
}
function buildPostgresTrendsQuery(itemids, table, timeFrom, timeTill, intervalSec, aggFunction, valueColumn) {
let time_expression = `clock / ${intervalSec} * ${intervalSec}`;
let query = `
SELECT DISTINCT to_char(itemid, '${itemid_format}') AS metric,
${time_expression} AS time,
${aggFunction}(${valueColumn}) OVER (PARTITION BY clock / ${intervalSec}) AS value
FROM ${table}
WHERE itemid IN (${itemids})
AND clock > ${timeFrom} AND clock < ${timeTill}
GROUP BY metric, clock, ${valueColumn}
ORDER BY time ASC
`;
return query;
}
const TEST_POSTGRES_QUERY = `
SELECT to_char(itemid, '${itemid_format}') AS metric, clock AS time, value_avg AS value
FROM trends_uint LIMIT 1
`;