influxdbDatasource.js 7.6 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245
  1. define([
  2. 'angular',
  3. 'underscore',
  4. 'kbn'
  5. ],
  6. function (angular, _, kbn) {
  7. 'use strict';
  8. var module = angular.module('kibana.services');
  9. module.factory('InfluxDatasource', function($q, $http) {
  10. function InfluxDatasource(datasource) {
  11. this.type = 'influxDB';
  12. this.editorSrc = 'app/partials/influxdb/editor.html';
  13. this.urls = datasource.urls;
  14. this.username = datasource.username;
  15. this.password = datasource.password;
  16. this.name = datasource.name;
  17. this.templateSettings = {
  18. interpolate : /\[\[([\s\S]+?)\]\]/g,
  19. };
  20. }
  21. InfluxDatasource.prototype.query = function(options) {
  22. var promises = _.map(options.targets, function(target) {
  23. var query;
  24. if (target.hide || !((target.series && target.column) || target.query)) {
  25. return [];
  26. }
  27. var timeFilter = getTimeFilter(options);
  28. var additionalGroups = [];
  29. if (target.rawQuery) {
  30. query = target.query;
  31. query = query.replace(";", "");
  32. var queryElements = query.split(" ");
  33. var lowerCaseQueryElements = query.toLowerCase().split(" ");
  34. var whereIndex = lowerCaseQueryElements.indexOf("where");
  35. var groupByIndex = lowerCaseQueryElements.indexOf("group");
  36. var orderIndex = lowerCaseQueryElements.indexOf("order");
  37. var afterGroup = _.rest(lowerCaseQueryElements, groupByIndex);
  38. for (var i = 0; i < afterGroup.length; i++) {
  39. var el = afterGroup[i];
  40. if (el === "order") break;
  41. if ( /,$/.test(el) &&
  42. _.size(afterGroup) > i &&
  43. ! /^time\(/.test(afterGroup[i + 1])) {
  44. additionalGroups.push(queryElements[groupByIndex + i + 1]);
  45. }
  46. }
  47. if (whereIndex !== -1) {
  48. queryElements.splice(whereIndex+1, 0, timeFilter, "and");
  49. }
  50. else {
  51. if (groupByIndex !== -1) {
  52. queryElements.splice(groupByIndex, 0, "where", timeFilter);
  53. }
  54. else if (orderIndex !== -1) {
  55. queryElements.splice(orderIndex, 0, "where", timeFilter);
  56. }
  57. else {
  58. queryElements.push("where");
  59. queryElements.push(timeFilter);
  60. }
  61. }
  62. query = queryElements.join(" ");
  63. }
  64. else {
  65. var template = "select [[func]]([[column]]) as [[column]]_[[func]] from [[series]] " +
  66. "where [[timeFilter]] [[condition_add]] [[condition_key]] [[condition_op]] [[condition_value]] " +
  67. "group by time([[interval]]) order asc";
  68. if (target.column.indexOf('-') !== -1 || target.column.indexOf('.') !== -1) {
  69. template = "select [[func]](\"[[column]]\") as \"[[column]]_[[func]]\" from [[series]] " +
  70. "where [[timeFilter]] [[condition_add]] [[condition_key]] [[condition_op]] [[condition_value]] " +
  71. "group by time([[interval]]) order asc";
  72. }
  73. var templateData = {
  74. series: target.series,
  75. column: target.column,
  76. func: target.function,
  77. timeFilter: timeFilter,
  78. interval: target.interval || options.interval,
  79. condition_add: target.condiction_filter ? target.condition_add : '',
  80. condition_key: target.condiction_filter ? target.condition_key : '',
  81. condition_op: target.condiction_filter ? target.condition_op : '',
  82. condition_value: target.condiction_filter ? target.condition_value: ''
  83. };
  84. query = _.template(template, templateData, this.templateSettings);
  85. target.query = query;
  86. }
  87. return this.doInfluxRequest(query, target.alias).then(handleInfluxQueryResponse(additionalGroups));
  88. }, this);
  89. return $q.all(promises).then(function(results) {
  90. return { data: _.flatten(results) };
  91. });
  92. };
  93. InfluxDatasource.prototype.listColumns = function(seriesName) {
  94. return this.doInfluxRequest('select * from ' + seriesName + ' limit 1').then(function(data) {
  95. if (!data) {
  96. return [];
  97. }
  98. return data[0].columns;
  99. });
  100. };
  101. InfluxDatasource.prototype.listSeries = function() {
  102. return this.doInfluxRequest('list series').then(function(data) {
  103. return _.map(data, function(series) {
  104. return series.name;
  105. });
  106. });
  107. };
  108. function retry(deferred, callback, delay) {
  109. return callback().then(undefined, function(reason) {
  110. if (reason.status !== 0) {
  111. deferred.reject(reason);
  112. }
  113. setTimeout(function() {
  114. return retry(deferred, callback, Math.min(delay * 2, 30000));
  115. }, delay);
  116. });
  117. }
  118. InfluxDatasource.prototype.doInfluxRequest = function(query, alias) {
  119. var _this = this;
  120. var deferred = $q.defer();
  121. retry(deferred, function() {
  122. var currentUrl = _this.urls.shift();
  123. _this.urls.push(currentUrl);
  124. var params = {
  125. u: _this.username,
  126. p: _this.password,
  127. time_precision: 's',
  128. q: query
  129. };
  130. var options = {
  131. method: 'GET',
  132. url: currentUrl + '/series',
  133. params: params,
  134. };
  135. return $http(options).success(function (data) {
  136. data.alias = alias;
  137. deferred.resolve(data);
  138. });
  139. }, 10);
  140. return deferred.promise;
  141. };
  142. function handleInfluxQueryResponse(additionalGroup) {
  143. return function(data) {
  144. var output = [];
  145. _.each(data, function(series) {
  146. var timeCol = series.columns.indexOf('time');
  147. var groupCols = _.map(additionalGroup, function(col) {
  148. return series.columns.indexOf(col);
  149. });
  150. var groupByColumn = _.find(groupCols, function(col) { return col > -1; });
  151. _.each(series.columns, function(column, index) {
  152. if (column === "time" || column === "sequence_number" || _.contains(additionalGroup, column)) {
  153. return;
  154. }
  155. var target = data.alias || series.name + "." + column;
  156. var datapoints = _.groupBy(series.points, function (point) {
  157. if (groupByColumn == undefined) return null;
  158. else return point[groupByColumn];
  159. });
  160. datapoints = _.map(_.pairs(datapoints), function(values) {
  161. return [values[0], _.map(values[1], function (point) { return [point[index], point[timeCol]]; }) ];
  162. });
  163. _.each(datapoints, function(values) {
  164. if (values[0] == null) {
  165. output.push({ target: target, datapoints: values[1]});
  166. } else {
  167. output.push({ target: values[0] + "-" + target, datapoints: values[1] });
  168. }
  169. });
  170. });
  171. });
  172. return output;
  173. }
  174. }
  175. function getTimeFilter(options) {
  176. var from = getInfluxTime(options.range.from);
  177. var until = getInfluxTime(options.range.to);
  178. if (until === 'now()') {
  179. return 'time > now() - ' + from;
  180. }
  181. return 'time > ' + from + ' and time < ' + until;
  182. }
  183. function getInfluxTime(date) {
  184. if (_.isString(date)) {
  185. if (date === 'now') {
  186. return 'now()';
  187. }
  188. else if (date.indexOf('now') >= 0) {
  189. return date.substring(4);
  190. }
  191. date = kbn.parseDate(date);
  192. }
  193. return to_utc_epoch_seconds(date);
  194. }
  195. function to_utc_epoch_seconds(date) {
  196. return (date.getTime() / 1000).toFixed(0) + 's';
  197. }
  198. return InfluxDatasource;
  199. });
  200. });