datasource.js 8.3 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288
  1. define([
  2. 'angular',
  3. 'lodash',
  4. 'app/core/utils/datemath',
  5. './influx_series',
  6. './query_builder',
  7. './directives',
  8. './query_ctrl',
  9. './func_editor',
  10. ],
  11. function (angular, _, dateMath, InfluxSeries, InfluxQueryBuilder) {
  12. 'use strict';
  13. var module = angular.module('grafana.services');
  14. module.factory('InfluxDatasource_08', function($q, backendSrv, templateSrv) {
  15. function InfluxDatasource(datasource) {
  16. this.urls = _.map(datasource.url.split(','), function(url) {
  17. return url.trim();
  18. });
  19. this.username = datasource.username;
  20. this.password = datasource.password;
  21. this.name = datasource.name;
  22. this.basicAuth = datasource.basicAuth;
  23. }
  24. InfluxDatasource.prototype.query = function(options) {
  25. var timeFilter = getTimeFilter(options);
  26. var promises = _.map(options.targets, function(target) {
  27. if (target.hide || !((target.series && target.column) || target.query)) {
  28. return [];
  29. }
  30. // build query
  31. var queryBuilder = new InfluxQueryBuilder(target);
  32. var query = queryBuilder.build();
  33. // replace grafana variables
  34. query = query.replace('$timeFilter', timeFilter);
  35. query = query.replace(/\$interval/g, (target.interval || options.interval));
  36. // replace templated variables
  37. query = templateSrv.replace(query, options.scopedVars);
  38. var alias = target.alias ? templateSrv.replace(target.alias, options.scopedVars) : '';
  39. var handleResponse = _.partial(handleInfluxQueryResponse, alias, queryBuilder.groupByField);
  40. return this._seriesQuery(query).then(handleResponse);
  41. }, this);
  42. return $q.all(promises).then(function(results) {
  43. return { data: _.flatten(results) };
  44. });
  45. };
  46. InfluxDatasource.prototype.annotationQuery = function(options) {
  47. var timeFilter = getTimeFilter({rangeRaw: options.rangeRaw});
  48. var query = options.annotation.query.replace('$timeFilter', timeFilter);
  49. query = templateSrv.replace(query);
  50. return this._seriesQuery(query).then(function(results) {
  51. return new InfluxSeries({seriesList: results, annotation: options.annotation}).getAnnotations();
  52. });
  53. };
  54. InfluxDatasource.prototype.listColumns = function(seriesName) {
  55. seriesName = templateSrv.replace(seriesName);
  56. if(!seriesName.match('^/.*/') && !seriesName.match(/^merge\(.*\)/)) {
  57. seriesName = '"' + seriesName+ '"';
  58. }
  59. return this._seriesQuery('select * from ' + seriesName + ' limit 1').then(function(data) {
  60. if (!data) {
  61. return [];
  62. }
  63. return data[0].columns.map(function(item) {
  64. return /^\w+$/.test(item) ? item : ('"' + item + '"');
  65. });
  66. });
  67. };
  68. InfluxDatasource.prototype.listSeries = function(query) {
  69. // wrap in regex
  70. if (query && query.length > 0 && query[0] !== '/') {
  71. query = '/' + query + '/';
  72. }
  73. return this._seriesQuery('list series ' + query).then(function(data) {
  74. if (!data || data.length === 0) {
  75. return [];
  76. }
  77. return _.map(data[0].points, function(point) {
  78. return point[1];
  79. });
  80. });
  81. };
  82. InfluxDatasource.prototype.testDatasource = function() {
  83. return this.metricFindQuery('list series').then(function () {
  84. return { status: "success", message: "Data source is working", title: "Success" };
  85. });
  86. };
  87. InfluxDatasource.prototype.metricFindQuery = function (query) {
  88. var interpolated;
  89. try {
  90. interpolated = templateSrv.replace(query);
  91. }
  92. catch (err) {
  93. return $q.reject(err);
  94. }
  95. return this._seriesQuery(interpolated)
  96. .then(function (results) {
  97. if (!results || results.length === 0) { return []; }
  98. return _.map(results[0].points, function (metric) {
  99. return {
  100. text: metric[1],
  101. expandable: false
  102. };
  103. });
  104. });
  105. };
  106. function retry(deferred, callback, delay) {
  107. return callback().then(undefined, function(reason) {
  108. if (reason.status !== 0 || reason.status >= 300) {
  109. reason.message = 'InfluxDB Error: <br/>' + reason.data;
  110. deferred.reject(reason);
  111. }
  112. else {
  113. setTimeout(function() {
  114. return retry(deferred, callback, Math.min(delay * 2, 30000));
  115. }, delay);
  116. }
  117. });
  118. }
  119. InfluxDatasource.prototype._seriesQuery = function(query) {
  120. return this._influxRequest('GET', '/series', {
  121. q: query,
  122. });
  123. };
  124. InfluxDatasource.prototype._influxRequest = function(method, url, data) {
  125. var _this = this;
  126. var deferred = $q.defer();
  127. retry(deferred, function() {
  128. var currentUrl = _this.urls.shift();
  129. _this.urls.push(currentUrl);
  130. var params = {
  131. u: _this.username,
  132. p: _this.password,
  133. };
  134. if (method === 'GET') {
  135. _.extend(params, data);
  136. data = null;
  137. }
  138. var options = {
  139. method: method,
  140. url: currentUrl + url,
  141. params: params,
  142. data: data,
  143. inspect: { type: 'influxdb' },
  144. };
  145. options.headers = options.headers || {};
  146. if (_this.basicAuth) {
  147. options.headers.Authorization = 'Basic ' + _this.basicAuth;
  148. }
  149. return backendSrv.datasourceRequest(options).then(function(response) {
  150. deferred.resolve(response.data);
  151. });
  152. }, 10);
  153. return deferred.promise;
  154. };
  155. InfluxDatasource.prototype._getDashboardInternal = function(id) {
  156. var queryString = 'select dashboard from "grafana.dashboard_' + btoa(id) + '"';
  157. return this._seriesQuery(queryString).then(function(results) {
  158. if (!results || !results.length) {
  159. return null;
  160. }
  161. var dashCol = _.indexOf(results[0].columns, 'dashboard');
  162. var dashJson = results[0].points[0][dashCol];
  163. return angular.fromJson(dashJson);
  164. }, function() {
  165. return null;
  166. });
  167. };
  168. InfluxDatasource.prototype.getDashboard = function(id) {
  169. return this._getDashboardInternal(id).then(function(dashboard) {
  170. if (dashboard !== null) {
  171. return dashboard;
  172. }
  173. throw "Dashboard not found";
  174. }, function(err) {
  175. throw "Could not load dashboard, " + err.data;
  176. });
  177. };
  178. InfluxDatasource.prototype.searchDashboards = function() {
  179. var influxQuery = 'select * from /grafana.dashboard_.*/ ';
  180. return this._seriesQuery(influxQuery).then(function(results) {
  181. var hits = { dashboards: [], tags: [], tagsOnly: false };
  182. if (!results || !results.length) {
  183. return hits;
  184. }
  185. for (var i = 0; i < results.length; i++) {
  186. var dashCol = _.indexOf(results[i].columns, 'title');
  187. var idCol = _.indexOf(results[i].columns, 'id');
  188. var hit = {
  189. id: results[i].points[0][dashCol],
  190. title: results[i].points[0][dashCol],
  191. };
  192. if (idCol !== -1) {
  193. hit.id = results[i].points[0][idCol];
  194. }
  195. hits.dashboards.push(hit);
  196. }
  197. return hits;
  198. });
  199. };
  200. function handleInfluxQueryResponse(alias, groupByField, seriesList) {
  201. var influxSeries = new InfluxSeries({
  202. seriesList: seriesList,
  203. alias: alias,
  204. groupByField: groupByField
  205. });
  206. return influxSeries.getTimeSeries();
  207. }
  208. function getTimeFilter(options) {
  209. var from = getInfluxTime(options.rangeRaw.from, false);
  210. var until = getInfluxTime(options.rangeRaw.to, true);
  211. var fromIsAbsolute = from[from.length-1] === 's';
  212. if (until === 'now()' && !fromIsAbsolute) {
  213. return 'time > ' + from;
  214. }
  215. return 'time > ' + from + ' and time < ' + until;
  216. }
  217. function getInfluxTime(date, roundUp) {
  218. if (_.isString(date)) {
  219. if (date === 'now') {
  220. return 'now()';
  221. }
  222. var parts = /^now-(\d+)([d|h|m|s])$/.exec(date);
  223. if (parts) {
  224. var amount = parseInt(parts[1]);
  225. var unit = parts[2];
  226. return 'now()-' + amount + unit;
  227. }
  228. date = dateMath.parse(date, roundUp);
  229. }
  230. return (date.valueOf() / 1000).toFixed(0) + 's';
  231. }
  232. return InfluxDatasource;
  233. });
  234. });