diff --git a/src/app/components/settings.js b/src/app/components/settings.js
index b483007d25e..62ba18826a9 100644
--- a/src/app/components/settings.js
+++ b/src/app/components/settings.js
@@ -14,7 +14,12 @@ function (_, crypto) {
*/
var defaults = {
elasticsearch : "http://"+window.location.hostname+":9200",
- graphiteUrl : "http://"+window.location.hostname+":8080",
+ datasources : {
+ default: {
+ url: "http://"+window.location.hostname+":8080",
+ default: true
+ }
+ },
panel_names : [],
default_route : '/dashboard/file/default.json',
grafana_index : 'grafana-dash',
@@ -40,7 +45,22 @@ function (_, crypto) {
}
};
- settings.graphiteBasicAuth = basicAuth(settings.graphiteUrl);
+ if (options.graphiteUrl) {
+ settings.datasources = {
+ graphite: {
+ name: 'default',
+ url: options.graphiteUrl,
+ default: true,
+ basicAuth: basicAuth(options.graphiteUrl)
+ }
+ };
+ }
+ else {
+ _.each(_.values(settings.datasources), function(source) {
+ source.basicAuth = basicAuth(source.url);
+ });
+ }
+
settings.elasticsearchBasicAuth = basicAuth(settings.elasticsearch);
return settings;
};
diff --git a/src/app/controllers/graphiteImport.js b/src/app/controllers/graphiteImport.js
index 87990bf2671..10f6e4d0593 100644
--- a/src/app/controllers/graphiteImport.js
+++ b/src/app/controllers/graphiteImport.js
@@ -8,7 +8,7 @@ function (angular, app, _) {
var module = angular.module('kibana.controllers');
- module.controller('GraphiteImportCtrl', function($scope, $rootScope, $timeout, graphiteSrv, dashboard) {
+ module.controller('GraphiteImportCtrl', function($scope, $rootScope, $timeout, datasourceSrv, dashboard) {
$scope.init = function() {
console.log('hej!');
@@ -17,7 +17,7 @@ function (angular, app, _) {
$scope.listAll = function(query) {
delete $scope.error;
- graphiteSrv.listDashboards(query)
+ datasourceSrv.default.listDashboards(query)
.then(function(results) {
$scope.dashboards = results;
})
@@ -29,7 +29,7 @@ function (angular, app, _) {
$scope.import = function(dashName) {
delete $scope.error;
- graphiteSrv.loadDashboard(dashName)
+ datasourceSrv.default.loadDashboard(dashName)
.then(function(results) {
if (!results.data || !results.data.state) {
throw { message: 'no dashboard state received from graphite' };
diff --git a/src/app/controllers/graphiteTarget.js b/src/app/controllers/graphiteTarget.js
index 43620c7bd87..8ec5bde50ac 100644
--- a/src/app/controllers/graphiteTarget.js
+++ b/src/app/controllers/graphiteTarget.js
@@ -10,7 +10,7 @@ function (angular, _, config, gfunc, Parser) {
var module = angular.module('kibana.controllers');
- module.controller('GraphiteTargetCtrl', function($scope, $http, filterSrv, graphiteSrv) {
+ module.controller('GraphiteTargetCtrl', function($scope, $http, filterSrv) {
$scope.init = function() {
$scope.funcCategories = gfunc.getCategories();
@@ -121,7 +121,7 @@ function (angular, _, config, gfunc, Parser) {
}
var path = getSegmentPathUpTo(fromIndex + 1);
- return graphiteSrv.metricFindQuery(path)
+ return $scope.datasource.metricFindQuery(path)
.then(function(segments) {
if (segments.length === 0) {
$scope.segments = $scope.segments.splice(0, fromIndex);
@@ -158,7 +158,7 @@ function (angular, _, config, gfunc, Parser) {
var query = index === 0 ?
'*' : getSegmentPathUpTo(index) + '.*';
- return graphiteSrv.metricFindQuery(query)
+ return $scope.datasource.metricFindQuery(query)
.then(function(segments) {
_.each(segments, function(segment) {
segment.html = segment.val = segment.text;
diff --git a/src/app/panels/filtering/module.js b/src/app/panels/filtering/module.js
index f0d69040029..9fc84471085 100644
--- a/src/app/panels/filtering/module.js
+++ b/src/app/panels/filtering/module.js
@@ -14,7 +14,7 @@ function (angular, app, _) {
var module = angular.module('kibana.panels.filtering', []);
app.useModule(module);
- module.controller('filtering', function($scope, filterSrv, graphiteSrv, $rootScope, dashboard) {
+ module.controller('filtering', function($scope, filterSrv, datasourceSrv, $rootScope, dashboard) {
$scope.panelMeta = {
status : "Stable",
@@ -35,7 +35,7 @@ function (angular, app, _) {
};
$scope.applyFilter = function(filter) {
- graphiteSrv.metricFindQuery(filter.query)
+ datasourceSrv.default.metricFindQuery(filter.query)
.then(function (results) {
filter.editing=undefined;
filter.options = _.map(results, function(node) {
diff --git a/src/app/panels/graphite/module.html b/src/app/panels/graphite/module.html
index 0812e28b678..8b14f02c220 100644
--- a/src/app/panels/graphite/module.html
+++ b/src/app/panels/graphite/module.html
@@ -27,10 +27,20 @@
+
\ No newline at end of file
diff --git a/src/app/panels/graphite/module.js b/src/app/panels/graphite/module.js
index b419cf15a47..71a33c5ca12 100644
--- a/src/app/panels/graphite/module.js
+++ b/src/app/panels/graphite/module.js
@@ -34,7 +34,7 @@ function (angular, app, $, _, kbn, moment, timeSeries) {
var module = angular.module('kibana.panels.graphite', []);
app.useModule(module);
- module.controller('graphite', function($scope, $rootScope, filterSrv, graphiteSrv, $timeout, annotationsSrv) {
+ module.controller('graphite', function($scope, $rootScope, filterSrv, datasourceSrv, $timeout, annotationsSrv) {
$scope.panelMeta = {
modals : [],
@@ -82,6 +82,9 @@ function (angular, app, $, _, kbn, moment, timeSeries) {
// Set and populate defaults
var _d = {
+
+ datasource: null,
+
/** @scratch /panels/histogram/3
* renderer:: sets client side (flot) or native graphite png renderer (png)
*/
@@ -220,6 +223,10 @@ function (angular, app, $, _, kbn, moment, timeSeries) {
$scope.editor = {index: 1};
$scope.editorTabs = _.union(['General'],_.pluck($scope.panelMeta.fullEditorTabs,'title'));
$scope.hiddenSeries = {};
+
+ $scope.datasources = datasourceSrv.listOptions();
+ $scope.datasource = datasourceSrv.get($scope.panel.datasource);
+
// Always show the query if an alias isn't set. Users can set an alias if the query is too
// long
$scope.panel.tooltip.query_as_alias = true;
@@ -227,6 +234,11 @@ function (angular, app, $, _, kbn, moment, timeSeries) {
$scope.get_data();
};
+ $scope.datasourceChanged = function() {
+ $scope.datasource = datasourceSrv.get($scope.panel.datasource);
+ $scope.get_data();
+ };
+
$scope.remove_panel_from_row = function(row, panel) {
if ($scope.fullscreen) {
$rootScope.$emit('panel-fullscreen-exit');
@@ -284,12 +296,13 @@ function (angular, app, $, _, kbn, moment, timeSeries) {
range: $scope.rangeUnparsed,
targets: $scope.panel.targets,
format: $scope.panel.renderer === 'png' ? 'png' : 'json',
- maxDataPoints: $scope.panel.span * 50
+ maxDataPoints: $scope.panel.span * 50,
+ datasource: $scope.panel.datasource
};
$scope.annotationsPromise = annotationsSrv.getAnnotations($scope.rangeUnparsed);
- return graphiteSrv.query(graphiteQuery)
+ return $scope.datasource.query(graphiteQuery)
.then($scope.receiveGraphiteData)
.then(null, function(err) {
$scope.panel.error = err.message || "Graphite HTTP Request Error";
diff --git a/src/app/services/all.js b/src/app/services/all.js
index 2f2965b83b0..6c0164a027e 100644
--- a/src/app/services/all.js
+++ b/src/app/services/all.js
@@ -4,7 +4,7 @@ define([
'./filterSrv',
'./timer',
'./panelMove',
- './graphite/graphiteSrv',
+ './datasourceSrv',
'./keyboardManager',
'./annotationsSrv',
],
diff --git a/src/app/services/annotationsSrv.js b/src/app/services/annotationsSrv.js
index bf4281745e4..441f896c862 100644
--- a/src/app/services/annotationsSrv.js
+++ b/src/app/services/annotationsSrv.js
@@ -7,7 +7,7 @@ define([
var module = angular.module('kibana.services');
- module.service('annotationsSrv', function(dashboard, graphiteSrv, $q, alertSrv) {
+ module.service('annotationsSrv', function(dashboard, datasourceSrv, $q, alertSrv) {
this.init = function() {
this.annotationList = [
@@ -43,7 +43,7 @@ define([
maxDataPoints: 100
};
- return graphiteSrv.query(graphiteQuery)
+ return datasourceSrv.default.query(graphiteQuery)
.then(function(results) {
return _.reduce(results.data, function(list, target) {
_.each(target.datapoints, function (values) {
diff --git a/src/app/services/dashboard.js b/src/app/services/dashboard.js
index 45a6fe22cd6..72bbf931a45 100644
--- a/src/app/services/dashboard.js
+++ b/src/app/services/dashboard.js
@@ -160,7 +160,6 @@ function (angular, $, kbn, _, config, moment, Modernizr) {
$timeout(function() {
// Ok, now that we've setup the current dashboard, we can inject our services
filterSrv = $injector.get('filterSrv');
- graphiteSrv = $injector.get('graphiteSrv');
filterSrv.init();
},0).then(function() {
diff --git a/src/app/services/datasourceSrv.js b/src/app/services/datasourceSrv.js
new file mode 100644
index 00000000000..db6d8a09a6b
--- /dev/null
+++ b/src/app/services/datasourceSrv.js
@@ -0,0 +1,34 @@
+define([
+ 'angular',
+ 'underscore',
+ 'config',
+ './graphite/graphiteDatasource'
+],
+function (angular, _, config, GraphiteDatasource) {
+ 'use strict';
+
+ var module = angular.module('kibana.services');
+
+ module.service('datasourceSrv', function($q, filterSrv, $http) {
+ var defaultDatasource = _.findWhere(_.values(config.datasources), { default: true } );
+
+ this.default = new GraphiteDatasource(defaultDatasource, $q, filterSrv, $http);
+
+ this.get = function(name) {
+ if (!name) {
+ return this.default;
+ }
+
+ return new GraphiteDatasource(config.datasources[name], $q, filterSrv, $http);
+ };
+
+ this.listOptions = function() {
+ return _.map(config.datasources, function(value, key) {
+ return {
+ name: value.default ? key + ' (default)' : key,
+ value: value.default ? null : key
+ };
+ });
+ };
+ });
+});
\ No newline at end of file
diff --git a/src/app/services/graphite/graphiteDatasource.js b/src/app/services/graphite/graphiteDatasource.js
new file mode 100644
index 00000000000..f15f9ef6ac9
--- /dev/null
+++ b/src/app/services/graphite/graphiteDatasource.js
@@ -0,0 +1,150 @@
+define([
+ 'angular',
+ 'underscore',
+ 'jquery',
+ 'config',
+ 'kbn',
+ 'moment'
+],
+function (angular, _, $, config, kbn, moment) {
+ 'use strict';
+
+ function GraphiteDatasource(datasource, $q, filterSrv, $http) {
+ this.url = datasource.url;
+ this.type = 'graphite';
+ this.basicAuth = datasource.basicAuth;
+ this.$q = $q;
+ this.filterSrv = filterSrv;
+ this.$http = $http;
+ };
+
+ GraphiteDatasource.prototype.query = function(options) {
+ try {
+ var graphOptions = {
+ from: this.translateTime(options.range.from),
+ until: this.translateTime(options.range.to),
+ targets: options.targets,
+ format: options.format,
+ maxDataPoints: options.maxDataPoints,
+ };
+
+ var params = this.buildGraphiteParams(graphOptions);
+
+ if (options.format === 'png') {
+ return $q.when(graphiteRenderUrl + '?' + params.join('&'));
+ }
+
+ return this.doGraphiteRequest({
+ method: 'POST',
+ url: '/render',
+ datasource: options.datasource,
+ data: params.join('&'),
+ headers: {
+ 'Content-Type': 'application/x-www-form-urlencoded',
+ }
+ });
+ }
+ catch(err) {
+ return this.$q.reject(err);
+ }
+ };
+
+ GraphiteDatasource.prototype.translateTime = function(date) {
+ if (_.isString(date)) {
+ if (date === 'now') {
+ return 'now';
+ }
+ else if (date.indexOf('now') >= 0) {
+ date = date.substring(3);
+ date = date.replace('m', 'min');
+ date = date.replace('M', 'mon');
+ return date;
+ }
+
+ date = kbn.parseDate(date);
+ }
+
+ date = moment.utc(date).local();
+
+ if (config.timezoneOffset) {
+ date = date.zone(config.timezoneOffset);
+ }
+
+ return date.format('HH:mm_YYYYMMDD');
+ };
+
+ GraphiteDatasource.prototype.metricFindQuery = function(query) {
+ var interpolated;
+ try {
+ interpolated = this.filterSrv.applyFilterToTarget(query);
+ }
+ catch(err) {
+ return $q.reject(err);
+ }
+
+ return this.doGraphiteRequest({method: 'GET', url: '/metrics/find/?query=' + interpolated })
+ .then(function(results) {
+ return _.map(results.data, function(metric) {
+ return {
+ text: metric.text,
+ expandable: metric.expandable ? true : false
+ };
+ });
+ });
+ };
+
+ GraphiteDatasource.prototype.listDashboards = function(query) {
+ return this.doGraphiteRequest({ method: 'GET', url: '/dashboard/find/', params: {query: query || ''} })
+ .then(function(results) {
+ return results.data.dashboards;
+ });
+ };
+
+ GraphiteDatasource.prototype.loadDashboard = function(dashName) {
+ return this.doGraphiteRequest({method: 'GET', url: '/dashboard/load/' + encodeURIComponent(dashName) });
+ };
+
+ GraphiteDatasource.prototype.doGraphiteRequest = function(options) {
+ if (this.basicAuth) {
+ options.withCredentials = true;
+ options.headers = options.headers || {};
+ options.headers.Authorization = 'Basic ' + config.graphiteBasicAuth;
+ }
+
+ options.url = this.url + options.url;
+
+ return this.$http(options);
+ };
+
+ GraphiteDatasource.prototype.buildGraphiteParams = function(options) {
+ var clean_options = [];
+ var graphite_options = ['target', 'targets', 'from', 'until', 'rawData', 'format', 'maxDataPoints'];
+
+ if (options.format !== 'png') {
+ options['format'] = 'json';
+ }
+
+ _.each(options, function (value, key) {
+ if ($.inArray(key, graphite_options) === -1) {
+ return;
+ }
+
+ if (key === "targets") {
+ _.each(value, function (value) {
+ if (!value.hide) {
+ var targetValue = this.filterSrv.applyFilterToTarget(value.target);
+ clean_options.push("target=" + encodeURIComponent(targetValue));
+ }
+ }, this);
+ }
+ else if (value !== null) {
+ clean_options.push(key + "=" + encodeURIComponent(value));
+ }
+ }, this);
+ return clean_options;
+ };
+
+
+ return GraphiteDatasource;
+
+});
diff --git a/src/app/services/graphite/graphiteSrv.js b/src/app/services/graphite/graphiteSrv.js
deleted file mode 100644
index f0a5642edb1..00000000000
--- a/src/app/services/graphite/graphiteSrv.js
+++ /dev/null
@@ -1,161 +0,0 @@
-define([
- 'angular',
- 'underscore',
- 'jquery',
- 'config',
- 'kbn',
- 'moment'
-],
-function (angular, _, $, config, kbn, moment) {
- 'use strict';
-
- var module = angular.module('kibana.services');
-
- module.service('graphiteSrv', function($http, $q, filterSrv) {
- var graphiteRenderUrl = config.graphiteUrl + "/render";
-
- this.query = function(options) {
- try {
- var graphOptions = {
- from: this.translateTime(options.range.from),
- until: this.translateTime(options.range.to),
- targets: options.targets,
- format: options.format,
- maxDataPoints: options.maxDataPoints
- };
-
- var params = buildGraphiteParams(graphOptions);
-
- if (options.format === 'png') {
- return $q.when(graphiteRenderUrl + '?' + params.join('&'));
- }
-
- return doGraphiteRequest({
- method: 'POST',
- url: graphiteRenderUrl,
- data: params.join('&'),
- headers: {
- 'Content-Type': 'application/x-www-form-urlencoded',
- }
- });
- }
- catch(err) {
- return $q.reject(err);
- }
- };
-
- this.translateTime = function(date) {
- if (_.isString(date)) {
- if (date === 'now') {
- return 'now';
- }
- else if (date.indexOf('now') >= 0) {
- date = date.substring(3);
- date = date.replace('m', 'min');
- date = date.replace('M', 'mon');
- return date;
- }
-
- date = kbn.parseDate(date);
- }
-
- date = moment.utc(date).local();
-
- if (config.timezoneOffset) {
- date = date.zone(config.timezoneOffset);
- }
-
- return date.format('HH:mm_YYYYMMDD');
- };
-
- this.match = function(targets, graphiteTargetStr) {
- var found = targets[0];
-
- for (var i = 0; i < targets.length; i++) {
- if (targets[i].target === graphiteTargetStr) {
- found = targets[i];
- break;
- }
- if(targets[i].target.match("'" + graphiteTargetStr + "'")) {
- found = targets[i];
- }
- }
-
- return found;
- };
-
- this.metricFindQuery = function(query) {
- var interpolated;
- try {
- interpolated = filterSrv.applyFilterToTarget(query);
- }
- catch(err) {
- return $q.reject(err);
- }
-
- var url = config.graphiteUrl + '/metrics/find/?query=' + interpolated;
- return doGraphiteRequest({method: 'GET', url: url})
- .then(function(results) {
- return _.map(results.data, function(metric) {
- return {
- text: metric.text,
- expandable: metric.expandable ? true : false
- };
- });
- });
- };
-
- this.listDashboards = function(query) {
- var url = config.graphiteUrl + '/dashboard/find/';
- return doGraphiteRequest({ method: 'GET', url: url, params: {query: query || ''} })
- .then(function(results) {
- return results.data.dashboards;
- });
- };
-
- this.loadDashboard = function(dashName) {
- var url = config.graphiteUrl + '/dashboard/load/' + encodeURIComponent(dashName);
- return doGraphiteRequest({method: 'GET', url: url});
- };
-
- function doGraphiteRequest(options) {
- if (config.graphiteBasicAuth) {
- options.withCredentials = true;
- options.headers = options.headers || {};
- options.headers.Authorization = 'Basic ' + config.graphiteBasicAuth;
- }
-
- return $http(options);
- }
-
- function buildGraphiteParams(options) {
- var clean_options = [];
- var graphite_options = ['target', 'targets', 'from', 'until', 'rawData', 'format', 'maxDataPoints'];
-
- if (options.format !== 'png') {
- options['format'] = 'json';
- }
-
- $.each(options, function (key, value) {
- if ($.inArray(key, graphite_options) === -1) {
- return;
- }
-
- if (key === "targets") {
- $.each(value, function (index, value) {
- if (!value.hide) {
- var targetValue = filterSrv.applyFilterToTarget(value.target);
- clean_options.push("target=" + encodeURIComponent(targetValue));
- }
- });
- }
- else if (value !== null) {
- clean_options.push(key + "=" + encodeURIComponent(value));
- }
- });
- return clean_options;
- }
-
- });
-
-});
diff --git a/src/config.sample.js b/src/config.sample.js
index d26bee82c69..5ec66aab2b9 100644
--- a/src/config.sample.js
+++ b/src/config.sample.js
@@ -24,6 +24,15 @@ function (Settings) {
*/
graphiteUrl: "http://"+window.location.hostname+":8080",
+ /**
+ * Multiple graphite servers? Comment out graphiteUrl and replace with
+ *
+ * datasources: {
+ * data_center_us: { type: 'graphite', url: 'http://', default: true },
+ * data_center_eu: { type: 'graphite', url: 'http://' }
+ * }
+ */
+
default_route: '/dashboard/file/default.json',
/**