mirror of
https://github.com/grafana/grafana.git
synced 2025-02-25 18:55:37 -06:00
Merge branch 'elastic_annotations'
Conflicts: src/css/bootstrap.dark.min.css src/css/bootstrap.light.min.css src/css/default.min.css
This commit is contained in:
commit
d2a342a94e
@ -12,6 +12,7 @@
|
||||
- [Issue #610](https://github.com/grafana/grafana/issues/610). InfluxDB: Support for InfluxdB v0.8 list series response schemea (series typeahead)
|
||||
- [Issue #266](https://github.com/grafana/grafana/issues/266). Graphite: New option cacheTimeout to override graphite default memcache timeout
|
||||
- [Issue #606](https://github.com/grafana/grafana/issues/606). General: New global option in config.js to specify admin password (useful to hinder users from accidentally make changes)
|
||||
- [Issue #201](https://github.com/grafana/grafana/issues/201). Annotations: Elasticsearch datasource support for events
|
||||
|
||||
**Changes**
|
||||
- [Issue #536](https://github.com/grafana/grafana/issues/536). Graphite: Use unix epoch for Graphite from/to for absolute time ranges
|
||||
|
@ -13,18 +13,10 @@ function (_, crypto) {
|
||||
* @type {Object}
|
||||
*/
|
||||
var defaults = {
|
||||
elasticsearch : "http://"+window.location.hostname+":9200",
|
||||
datasources : {
|
||||
default: {
|
||||
url: "http://"+window.location.hostname+":8080",
|
||||
default: true
|
||||
}
|
||||
},
|
||||
datasources : {},
|
||||
panels : ['graph', 'text'],
|
||||
plugins : {},
|
||||
default_route : '/dashboard/file/default.json',
|
||||
grafana_index : 'grafana-dash',
|
||||
elasticsearch_all_disabled : false,
|
||||
playlist_timespan : "1m",
|
||||
unsaved_changes_warning : true,
|
||||
admin: {}
|
||||
@ -57,13 +49,21 @@ function (_, crypto) {
|
||||
return datasource;
|
||||
};
|
||||
|
||||
// backward compatible with old config
|
||||
if (options.graphiteUrl) {
|
||||
settings.datasources = {
|
||||
graphite: {
|
||||
type: 'graphite',
|
||||
url: options.graphiteUrl,
|
||||
default: true
|
||||
}
|
||||
settings.datasources.graphite = {
|
||||
type: 'graphite',
|
||||
url: options.graphiteUrl,
|
||||
default: true
|
||||
};
|
||||
}
|
||||
|
||||
if (options.elasticsearch) {
|
||||
settings.datasources.elasticsearch = {
|
||||
type: 'elasticsearch',
|
||||
url: options.elasticsearch,
|
||||
index: options.grafana_index,
|
||||
grafanaDB: true
|
||||
};
|
||||
}
|
||||
|
||||
@ -73,10 +73,6 @@ function (_, crypto) {
|
||||
if (datasource.type === 'influxdb') { parseMultipleHosts(datasource); }
|
||||
});
|
||||
|
||||
var elasticParsed = parseBasicAuth({ url: settings.elasticsearch });
|
||||
settings.elasticsearchBasicAuth = elasticParsed.basicAuth;
|
||||
settings.elasticsearch = elasticParsed.url;
|
||||
|
||||
if (settings.plugins.panels) {
|
||||
settings.panels = _.union(settings.panels, settings.plugins.panels);
|
||||
}
|
||||
|
@ -2,16 +2,18 @@ define([
|
||||
'angular',
|
||||
'underscore',
|
||||
'moment',
|
||||
'config',
|
||||
'filesaver'
|
||||
],
|
||||
function (angular, _, moment) {
|
||||
function (angular, _, moment, config) {
|
||||
'use strict';
|
||||
|
||||
var module = angular.module('grafana.controllers');
|
||||
|
||||
module.controller('dashLoader', function($scope, $rootScope, $http, alertSrv, $location, playlistSrv, elastic) {
|
||||
module.controller('dashLoader', function($scope, $rootScope, $http, alertSrv, $location, playlistSrv, datasourceSrv) {
|
||||
|
||||
$scope.init = function() {
|
||||
$scope.db = datasourceSrv.getGrafanaDB();
|
||||
$scope.onAppEvent('save-dashboard', function() {
|
||||
$scope.saveDashboard();
|
||||
});
|
||||
@ -19,7 +21,6 @@ function (angular, _, moment) {
|
||||
$scope.onAppEvent('zoom-out', function() {
|
||||
$scope.zoom(2);
|
||||
});
|
||||
|
||||
};
|
||||
|
||||
$scope.exitFullscreen = function() {
|
||||
@ -38,6 +39,9 @@ function (angular, _, moment) {
|
||||
if(type === 'save') {
|
||||
return (_l.save_elasticsearch);
|
||||
}
|
||||
if(type === 'share') {
|
||||
return (_l.save_temp);
|
||||
}
|
||||
return false;
|
||||
};
|
||||
|
||||
@ -52,7 +56,7 @@ function (angular, _, moment) {
|
||||
};
|
||||
|
||||
$scope.saveForSharing = function() {
|
||||
elastic.saveForSharing($scope.dashboard)
|
||||
$scope.db.saveDashboardTemp($scope.dashboard)
|
||||
.then(function(result) {
|
||||
|
||||
$scope.share = { url: result.url, title: result.title };
|
||||
@ -62,10 +66,32 @@ function (angular, _, moment) {
|
||||
});
|
||||
};
|
||||
|
||||
$scope.passwordCache = function(pwd) {
|
||||
if (!window.sessionStorage) { return null; }
|
||||
if (!pwd) { return window.sessionStorage["grafanaAdminPassword"]; }
|
||||
window.sessionStorage["grafanaAdminPassword"] = pwd;
|
||||
};
|
||||
|
||||
$scope.isAdmin = function() {
|
||||
if (!config.admin || !config.admin.password) { return true; }
|
||||
if (this.passwordCache() === config.admin.password) { return true; }
|
||||
|
||||
var password = window.prompt("Admin password", "");
|
||||
this.passwordCache(password);
|
||||
|
||||
if (password === config.admin.password) { return true; }
|
||||
|
||||
alertSrv.set('Save failed', 'Password incorrect', 'error');
|
||||
|
||||
return false;
|
||||
};
|
||||
|
||||
$scope.saveDashboard = function() {
|
||||
elastic.saveDashboard($scope.dashboard, $scope.dashboard.title)
|
||||
if (!this.isAdmin()) { return false; }
|
||||
|
||||
$scope.db.saveDashboard($scope.dashboard, $scope.dashboard.title)
|
||||
.then(function(result) {
|
||||
alertSrv.set('Dashboard Saved', 'Dashboard has been saved to Elasticsearch as "' + result.title + '"','success', 5000);
|
||||
alertSrv.set('Dashboard Saved', 'Dashboard has been saved as "' + result.title + '"','success', 5000);
|
||||
|
||||
$location.path(result.url);
|
||||
|
||||
@ -81,7 +107,9 @@ function (angular, _, moment) {
|
||||
return;
|
||||
}
|
||||
|
||||
elastic.deleteDashboard(id).then(function(id) {
|
||||
if (!this.isAdmin()) { return false; }
|
||||
|
||||
$scope.db.deleteDashboard(id).then(function(id) {
|
||||
alertSrv.set('Dashboard Deleted', id + ' has been deleted', 'success', 5000);
|
||||
}, function() {
|
||||
alertSrv.set('Dashboard Not Deleted', 'An error occurred deleting the dashboard', 'error', 5000);
|
||||
|
@ -11,8 +11,7 @@ function (angular, app, _) {
|
||||
module.controller('GraphiteImportCtrl', function($scope, $rootScope, $timeout, datasourceSrv) {
|
||||
|
||||
$scope.init = function() {
|
||||
console.log('hej!');
|
||||
$scope.datasources = datasourceSrv.listOptions();
|
||||
$scope.datasources = datasourceSrv.getMetricSources();
|
||||
$scope.setDatasource(null);
|
||||
};
|
||||
|
||||
@ -96,7 +95,8 @@ function (angular, app, _) {
|
||||
currentRow.panels.push(panel);
|
||||
});
|
||||
|
||||
$scope.dashboard.dash_load(newDashboard);
|
||||
$scope.emitAppEvent('setup-dashboard', newDashboard);
|
||||
$scope.dismiss();
|
||||
}
|
||||
|
||||
});
|
||||
|
@ -9,13 +9,14 @@ function (angular, _, config, $) {
|
||||
|
||||
var module = angular.module('grafana.controllers');
|
||||
|
||||
module.controller('SearchCtrl', function($scope, $rootScope, $element, $location, elastic) {
|
||||
module.controller('SearchCtrl', function($scope, $rootScope, $element, $location, datasourceSrv) {
|
||||
|
||||
$scope.init = function() {
|
||||
$scope.giveSearchFocus = 0;
|
||||
$scope.selectedIndex = -1;
|
||||
$scope.results = {dashboards: [], tags: [], metrics: []};
|
||||
$scope.query = { query: 'title:' };
|
||||
$scope.db = datasourceSrv.getGrafanaDB();
|
||||
$scope.onAppEvent('open-search', $scope.openSearch);
|
||||
};
|
||||
|
||||
@ -57,40 +58,12 @@ function (angular, _, config, $) {
|
||||
};
|
||||
};
|
||||
|
||||
$scope.searchDasboards = function(queryString) {
|
||||
var tagsOnly = queryString.indexOf('tags!:') === 0;
|
||||
if (tagsOnly) {
|
||||
var tagsQuery = queryString.substring(6, queryString.length);
|
||||
queryString = 'tags:' + tagsQuery + '*';
|
||||
}
|
||||
else {
|
||||
if (queryString.length === 0) {
|
||||
queryString = 'title:';
|
||||
}
|
||||
|
||||
if (queryString[queryString.length - 1] !== '*') {
|
||||
queryString += '*';
|
||||
}
|
||||
}
|
||||
|
||||
var query = {
|
||||
query: { query_string: { query: queryString } },
|
||||
facets: { tags: { terms: { field: "tags", order: "term", size: 50 } } },
|
||||
size: 20,
|
||||
sort: ["_uid"]
|
||||
};
|
||||
|
||||
return elastic.post('/dashboard/_search', query)
|
||||
$scope.searchDashboards = function(queryString) {
|
||||
return $scope.db.searchDashboards(queryString)
|
||||
.then(function(results) {
|
||||
if(_.isUndefined(results.hits)) {
|
||||
$scope.results.dashboards = [];
|
||||
$scope.results.tags = [];
|
||||
return;
|
||||
}
|
||||
|
||||
$scope.tagsOnly = tagsOnly;
|
||||
$scope.results.dashboards = results.hits.hits;
|
||||
$scope.results.tags = results.facets.tags.terms;
|
||||
$scope.tagsOnly = results.dashboards.length === 0 && results.tags.length > 0;
|
||||
$scope.results.dashboards = results.dashboards;
|
||||
$scope.results.tags = results.tags;
|
||||
});
|
||||
};
|
||||
|
||||
@ -118,12 +91,9 @@ function (angular, _, config, $) {
|
||||
$scope.selectedIndex = -1;
|
||||
|
||||
var queryStr = $scope.query.query.toLowerCase();
|
||||
queryStr = queryStr.replace(' and ', ' AND ');
|
||||
|
||||
if (queryStr.indexOf('m:') !== 0) {
|
||||
queryStr = queryStr.replace(' and ', ' AND ');
|
||||
$scope.searchDasboards(queryStr);
|
||||
return;
|
||||
}
|
||||
$scope.searchDashboards(queryStr);
|
||||
};
|
||||
|
||||
$scope.openSearch = function (evt) {
|
||||
@ -177,4 +147,4 @@ function (angular, _, config, $) {
|
||||
};
|
||||
});
|
||||
|
||||
});
|
||||
});
|
||||
|
@ -12,7 +12,7 @@ function (angular, app, _) {
|
||||
var module = angular.module('grafana.panels.annotations', []);
|
||||
app.useModule(module);
|
||||
|
||||
module.controller('AnnotationsEditorCtrl', function($scope, datasourceSrv, $rootScope) {
|
||||
module.controller('AnnotationsEditorCtrl', function($scope, datasourceSrv) {
|
||||
|
||||
var annotationDefaults = {
|
||||
name: '',
|
||||
|
@ -220,7 +220,7 @@ function (angular, app, $, _, kbn, moment, timeSeries) {
|
||||
$scope.editorTabs = _.pluck($scope.panelMeta.fullEditorTabs,'title');
|
||||
$scope.hiddenSeries = {};
|
||||
|
||||
$scope.datasources = datasourceSrv.listOptions();
|
||||
$scope.datasources = datasourceSrv.getMetricSources();
|
||||
$scope.setDatasource($scope.panel.datasource);
|
||||
|
||||
if ($scope.panel.targets.length === 0) {
|
||||
|
@ -27,7 +27,7 @@
|
||||
|
||||
<li ng-show="dashboard.loader.save_elasticsearch">
|
||||
<form class="input-prepend nomargin save-dashboard-dropdown-save-form">
|
||||
<input class='input-medium' ng-model="dashboard.title" type="text" ng-model="elasticsearch.title"/>
|
||||
<input class='input-medium' ng-model="dashboard.title" type="text" />
|
||||
<button class="btn" ng-click="saveDashboard()"><i class="icon-save"></i></button>
|
||||
</form>
|
||||
</li>
|
||||
@ -47,8 +47,11 @@
|
||||
<li ng-show="dashboard.loader.save_local">
|
||||
<a class="link" ng-click="exportDashboard()">Export dashboard</a>
|
||||
</li>
|
||||
<li ng-show="showDropdown('share')"><a bs-tooltip="'Share'" data-placement="bottom" ng-click="saveForSharing()" config-modal="app/partials/dashLoaderShare.html">Share temp copy</i></a></li>
|
||||
|
||||
<li ng-show="showDropdown('share')">
|
||||
<a bs-tooltip="'Share'" data-placement="bottom" ng-click="saveForSharing()" config-modal="app/partials/dashLoaderShare.html">
|
||||
Share temp copy
|
||||
</a>
|
||||
</li>
|
||||
</ul>
|
||||
</li>
|
||||
|
||||
|
@ -2,7 +2,7 @@
|
||||
<div class="pull-right editor-title">Dashboard settings</div>
|
||||
|
||||
<div ng-model="editor.index" bs-tabs style="text-transform:capitalize;">
|
||||
<div ng-repeat="tab in ['General', 'Rows','Controls', 'Metrics', 'Import']" data-title="{{tab}}">
|
||||
<div ng-repeat="tab in ['General', 'Rows','Controls', 'Import']" data-title="{{tab}}">
|
||||
</div>
|
||||
<div ng-repeat="tab in dashboard.nav" data-title="{{tab.type}}">
|
||||
</div>
|
||||
@ -111,11 +111,7 @@
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div ng-if="editor.index == 3">
|
||||
<ng-include src="'app/partials/loadmetrics.html'"></ng-include>
|
||||
</div>
|
||||
|
||||
<div ng-if="editor.index == 4">
|
||||
<div ng-if="editor.index == 3">
|
||||
<ng-include src="'app/partials/import.html'"></ng-include>
|
||||
</div>
|
||||
|
||||
|
@ -1 +1,39 @@
|
||||
<h2>Elasticsearch</h2>
|
||||
<div class="editor-row">
|
||||
<div class="section">
|
||||
<h5>Index name</h5>
|
||||
<div class="editor-option">
|
||||
<input type="text" class="span4" ng-model='currentAnnotation.index' placeholder="events-*"></input>
|
||||
</div>
|
||||
</div>
|
||||
<div class="section">
|
||||
<h5>Search query (lucene) <tip>Use [[filterName]] in query to replace part of the query with a filter value</h5>
|
||||
<div class="editor-option">
|
||||
<input type="text" class="span6" ng-model='currentAnnotation.query' placeholder="tags:deploy"></input>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="editor-row">
|
||||
<div class="section">
|
||||
<h5>Field mappings</h5>
|
||||
<div class="editor-option">
|
||||
<label class="small">Time</label>
|
||||
<input type="text" class="input-small" ng-model='currentAnnotation.timeField' placeholder="@timestamp"></input>
|
||||
</div>
|
||||
|
||||
<div class="editor-option">
|
||||
<label class="small">Title</label>
|
||||
<input type="text" class="input-small" ng-model='currentAnnotation.titleField' placeholder="desc"></input>
|
||||
</div>
|
||||
|
||||
<div class="editor-option">
|
||||
<label class="small">Tags</label>
|
||||
<input type="text" class="input-small" ng-model='currentAnnotation.tagsField' placeholder="tags"></input>
|
||||
</div>
|
||||
|
||||
<div class="editor-option">
|
||||
<label class="small">Text</label>
|
||||
<input type="text" class="input-small" ng-model='currentAnnotation.textField' placeholder=""></input>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
@ -1,9 +1,7 @@
|
||||
define([
|
||||
'angular',
|
||||
'jquery',
|
||||
'config'
|
||||
],
|
||||
function (angular, $, config) {
|
||||
function (angular) {
|
||||
"use strict";
|
||||
|
||||
var module = angular.module('grafana.routes');
|
||||
@ -20,37 +18,15 @@ function (angular, $, config) {
|
||||
});
|
||||
});
|
||||
|
||||
module.controller('DashFromElasticProvider', function($scope, $rootScope, elastic, $routeParams, alertSrv) {
|
||||
module.controller('DashFromElasticProvider', function($scope, $rootScope, datasourceSrv, $routeParams, alertSrv) {
|
||||
|
||||
var elasticsearch_load = function(id) {
|
||||
var url = '/dashboard/' + id;
|
||||
|
||||
// hack to check if it is a temp dashboard
|
||||
if (window.location.href.indexOf('dashboard/temp') > 0) {
|
||||
url = '/temp/' + id;
|
||||
}
|
||||
|
||||
return elastic.get(url)
|
||||
.then(function(result) {
|
||||
if (result._source && result._source.dashboard) {
|
||||
return angular.fromJson(result._source.dashboard);
|
||||
} else {
|
||||
return false;
|
||||
}
|
||||
}, function(data, status) {
|
||||
if(status === 0) {
|
||||
alertSrv.set('Error',"Could not contact Elasticsearch at " +
|
||||
config.elasticsearch + ". Please ensure that Elasticsearch is reachable from your browser.",'error');
|
||||
} else {
|
||||
alertSrv.set('Error',"Could not find dashboard " + id, 'error');
|
||||
}
|
||||
return false;
|
||||
});
|
||||
};
|
||||
|
||||
elasticsearch_load($routeParams.id).then(function(dashboard) {
|
||||
$scope.emitAppEvent('setup-dashboard', dashboard);
|
||||
});
|
||||
var db = datasourceSrv.getGrafanaDB();
|
||||
db.getDashboard($routeParams.id)
|
||||
.then(function(dashboard) {
|
||||
$scope.emitAppEvent('setup-dashboard', dashboard);
|
||||
}).then(null, function(error) {
|
||||
alertSrv.set('Error', error, 'error');
|
||||
});
|
||||
|
||||
});
|
||||
|
||||
|
@ -8,8 +8,7 @@ define([
|
||||
'./annotationsSrv',
|
||||
'./playlistSrv',
|
||||
'./unsavedChangesSrv',
|
||||
'./elasticsearch/es-client',
|
||||
'./dashboard/dashboardKeyBindings',
|
||||
'./dashboard/dashboardModel',
|
||||
],
|
||||
function () {});
|
||||
function () {});
|
||||
|
@ -37,6 +37,7 @@ define([
|
||||
|
||||
var promises = _.map(annotations, function(annotation) {
|
||||
var datasource = datasourceSrv.get(annotation.datasource);
|
||||
|
||||
return datasource.annotationQuery(annotation, filterSrv, rangeUnparsed)
|
||||
.then(this.receiveAnnotationResults)
|
||||
.then(null, errorHandler);
|
||||
@ -64,7 +65,7 @@ define([
|
||||
function addAnnotation(options) {
|
||||
var tooltip = "<small><b>" + options.title + "</b><br/>";
|
||||
if (options.tags) {
|
||||
tooltip += (options.tags || '') + '<br/>';
|
||||
tooltip += '<span class="tag label label-tag">' + (options.tags || '') + '</span><br/>';
|
||||
}
|
||||
|
||||
if (timezone === 'browser') {
|
||||
|
@ -14,6 +14,9 @@ function (angular, _, config) {
|
||||
|
||||
module.service('datasourceSrv', function($q, filterSrv, $http, $injector) {
|
||||
var datasources = {};
|
||||
var metricSources = [];
|
||||
var annotationSources = [];
|
||||
var grafanaDB = {};
|
||||
|
||||
this.init = function() {
|
||||
_.each(config.datasources, function(value, key) {
|
||||
@ -27,6 +30,26 @@ function (angular, _, config) {
|
||||
this.default = datasources[_.keys(datasources)[0]];
|
||||
this.default.default = true;
|
||||
}
|
||||
|
||||
// create list of different source types
|
||||
_.each(datasources, function(value, key) {
|
||||
if (value.supportMetrics) {
|
||||
metricSources.push({
|
||||
name: value.name,
|
||||
value: value.default ? null : key,
|
||||
});
|
||||
}
|
||||
if (value.supportAnnotations) {
|
||||
annotationSources.push({
|
||||
name: key,
|
||||
editorSrc: value.annotationEditorSrc,
|
||||
});
|
||||
}
|
||||
if (value.grafanaDB) {
|
||||
grafanaDB = value;
|
||||
}
|
||||
});
|
||||
|
||||
};
|
||||
|
||||
this.datasourceFactory = function(ds) {
|
||||
@ -56,25 +79,15 @@ function (angular, _, config) {
|
||||
};
|
||||
|
||||
this.getAnnotationSources = function() {
|
||||
var results = [];
|
||||
_.each(datasources, function(value, key) {
|
||||
if (value.supportAnnotations) {
|
||||
results.push({
|
||||
name: key,
|
||||
editorSrc: value.annotationEditorSrc,
|
||||
});
|
||||
}
|
||||
});
|
||||
return results;
|
||||
return annotationSources;
|
||||
};
|
||||
|
||||
this.listOptions = function() {
|
||||
return _.map(config.datasources, function(value, key) {
|
||||
return {
|
||||
name: value.default ? key + ' (default)' : key,
|
||||
value: value.default ? null : key
|
||||
};
|
||||
});
|
||||
this.getMetricSources = function() {
|
||||
return metricSources;
|
||||
};
|
||||
|
||||
this.getGrafanaDB = function() {
|
||||
return grafanaDB;
|
||||
};
|
||||
|
||||
this.init();
|
||||
|
@ -1,116 +0,0 @@
|
||||
define([
|
||||
'angular',
|
||||
'config'
|
||||
],
|
||||
function(angular, config) {
|
||||
"use strict";
|
||||
|
||||
var module = angular.module('grafana.services');
|
||||
|
||||
module.service('elastic', function($http, $q) {
|
||||
|
||||
this._request = function(method, url, data) {
|
||||
var options = {
|
||||
url: config.elasticsearch + "/" + config.grafana_index + url,
|
||||
method: method,
|
||||
data: data
|
||||
};
|
||||
|
||||
if (config.elasticsearchBasicAuth) {
|
||||
options.headers = {
|
||||
"Authorization": "Basic " + config.elasticsearchBasicAuth
|
||||
};
|
||||
}
|
||||
|
||||
return $http(options);
|
||||
};
|
||||
|
||||
this.get = function(url) {
|
||||
return this._request('GET', url)
|
||||
.then(function(results) {
|
||||
return results.data;
|
||||
});
|
||||
};
|
||||
|
||||
this.post = function(url, data) {
|
||||
return this._request('POST', url, data)
|
||||
.then(function(results) {
|
||||
return results.data;
|
||||
});
|
||||
};
|
||||
|
||||
this.deleteDashboard = function(id) {
|
||||
if (!this.isAdmin()) { return $q.reject("Invalid admin password"); }
|
||||
|
||||
return this._request('DELETE', '/dashboard/' + id)
|
||||
.then(function(result) {
|
||||
return result.data._id;
|
||||
}, function(err) {
|
||||
throw err.data;
|
||||
});
|
||||
};
|
||||
|
||||
this.saveForSharing = function(dashboard) {
|
||||
var data = {
|
||||
user: 'guest',
|
||||
group: 'guest',
|
||||
title: dashboard.title,
|
||||
tags: dashboard.tags,
|
||||
dashboard: angular.toJson(dashboard)
|
||||
};
|
||||
|
||||
var ttl = dashboard.loader.save_temp_ttl;
|
||||
|
||||
return this._request('POST', '/temp/?ttl=' + ttl, data)
|
||||
.then(function(result) {
|
||||
|
||||
var baseUrl = window.location.href.replace(window.location.hash,'');
|
||||
var url = baseUrl + "#dashboard/temp/" + result.data._id;
|
||||
|
||||
return { title: dashboard.title, url: url };
|
||||
|
||||
}, function(err) {
|
||||
throw "Failed to save to temp dashboard to elasticsearch " + err.data;
|
||||
});
|
||||
};
|
||||
|
||||
this.passwordCache = function(pwd) {
|
||||
if (!window.sessionStorage) { return null; }
|
||||
if (!pwd) { return window.sessionStorage["grafanaAdminPassword"]; }
|
||||
window.sessionStorage["grafanaAdminPassword"] = pwd;
|
||||
};
|
||||
|
||||
this.isAdmin = function() {
|
||||
if (!config.admin || !config.admin.password) { return true; }
|
||||
if (this.passwordCache() === config.admin.password) { return true; }
|
||||
|
||||
var password = window.prompt("Admin password", "");
|
||||
this.passwordCache(password);
|
||||
|
||||
return password === config.admin.password;
|
||||
};
|
||||
|
||||
this.saveDashboard = function(dashboard, title) {
|
||||
if (!this.isAdmin()) { return $q.reject("Invalid admin password"); }
|
||||
|
||||
var dashboardClone = angular.copy(dashboard);
|
||||
title = dashboardClone.title = title ? title : dashboard.title;
|
||||
|
||||
var data = {
|
||||
user: 'guest',
|
||||
group: 'guest',
|
||||
title: title,
|
||||
tags: dashboardClone.tags,
|
||||
dashboard: angular.toJson(dashboardClone)
|
||||
};
|
||||
|
||||
return this._request('PUT', '/dashboard/' + encodeURIComponent(title), data)
|
||||
.then(function() {
|
||||
return { title: title, url: '/dashboard/elasticsearch/' + title };
|
||||
}, function(err) {
|
||||
throw 'Failed to save to elasticsearch ' + err.data;
|
||||
});
|
||||
};
|
||||
|
||||
});
|
||||
});
|
@ -19,10 +19,197 @@ function (angular, _, $, config, kbn, moment) {
|
||||
this.url = datasource.url;
|
||||
this.name = datasource.name;
|
||||
this.supportAnnotations = true;
|
||||
this.supportMetrics = false;
|
||||
this.index = datasource.index;
|
||||
this.grafanaDB = datasource.grafanaDB;
|
||||
this.annotationEditorSrc = 'app/partials/elasticsearch/annotation_editor.html';
|
||||
}
|
||||
|
||||
ElasticDatasource.prototype._request = function(method, url, index, data) {
|
||||
var options = {
|
||||
url: this.url + "/" + index + url,
|
||||
method: method,
|
||||
data: data
|
||||
};
|
||||
|
||||
if (this.basicAuth) {
|
||||
options.headers = {
|
||||
"Authorization": "Basic " + this.basicAuth
|
||||
};
|
||||
}
|
||||
|
||||
return $http(options);
|
||||
};
|
||||
|
||||
ElasticDatasource.prototype._get = function(url) {
|
||||
return this._request('GET', url, this.index)
|
||||
.then(function(results) {
|
||||
return results.data;
|
||||
});
|
||||
};
|
||||
|
||||
ElasticDatasource.prototype._post = function(url, data) {
|
||||
return this._request('POST', url, this.index, data)
|
||||
.then(function(results) {
|
||||
return results.data;
|
||||
});
|
||||
};
|
||||
|
||||
ElasticDatasource.prototype.annotationQuery = function(annotation, filterSrv, rangeUnparsed) {
|
||||
var range = {};
|
||||
var timeField = annotation.timeField || '@timestamp';
|
||||
var queryString = annotation.query || '*';
|
||||
var tagsField = annotation.tagsField || 'tags';
|
||||
var titleField = annotation.titleField || 'desc';
|
||||
var textField = annotation.textField || null;
|
||||
|
||||
range[annotation.timeField]= {
|
||||
from: rangeUnparsed.from,
|
||||
to: rangeUnparsed.to,
|
||||
};
|
||||
|
||||
var filter = { "bool": { "must": [{ "range": range }] } };
|
||||
var query = { "bool": { "should": [{ "query_string": { "query": queryString } }] } };
|
||||
var data = { "query" : { "filtered": { "query" : query, "filter": filter } }, "size": 100 };
|
||||
|
||||
return this._request('POST', '/_search', annotation.index, data).then(function(results) {
|
||||
var list = [];
|
||||
var hits = results.data.hits.hits;
|
||||
|
||||
for (var i = 0; i < hits.length; i++) {
|
||||
var source = hits[i]._source;
|
||||
var event = {
|
||||
annotation: annotation,
|
||||
time: moment.utc(source[timeField]).valueOf(),
|
||||
title: source[titleField],
|
||||
};
|
||||
|
||||
if (source[tagsField]) {
|
||||
if (_.isArray(source[tagsField])) {
|
||||
event.tags = source[tagsField].join(', ');
|
||||
}
|
||||
else {
|
||||
event.tags = source[tagsField];
|
||||
}
|
||||
}
|
||||
if (textField && source[textField]) {
|
||||
event.text = source[textField];
|
||||
}
|
||||
|
||||
list.push(event);
|
||||
}
|
||||
return list;
|
||||
});
|
||||
};
|
||||
|
||||
ElasticDatasource.prototype.getDashboard = function(id) {
|
||||
var url = '/dashboard/' + id;
|
||||
|
||||
// hack to check if it is a temp dashboard
|
||||
if (window.location.href.indexOf('dashboard/temp') > 0) {
|
||||
url = '/temp/' + id;
|
||||
}
|
||||
|
||||
return this._get(url)
|
||||
.then(function(result) {
|
||||
if (result._source && result._source.dashboard) {
|
||||
return angular.fromJson(result._source.dashboard);
|
||||
} else {
|
||||
return false;
|
||||
}
|
||||
}, function(data) {
|
||||
if(data.status === 0) {
|
||||
throw "Could not contact Elasticsearch. Please ensure that Elasticsearch is reachable from your browser.";
|
||||
} else {
|
||||
throw "Could not find dashboard " + id;
|
||||
}
|
||||
});
|
||||
};
|
||||
|
||||
ElasticDatasource.prototype.saveDashboard = function(dashboard, title) {
|
||||
var dashboardClone = angular.copy(dashboard);
|
||||
title = dashboardClone.title = title ? title : dashboard.title;
|
||||
|
||||
var data = {
|
||||
user: 'guest',
|
||||
group: 'guest',
|
||||
title: title,
|
||||
tags: dashboardClone.tags,
|
||||
dashboard: angular.toJson(dashboardClone)
|
||||
};
|
||||
|
||||
return this._request('PUT', '/dashboard/' + encodeURIComponent(title), this.index, data)
|
||||
.then(function() {
|
||||
return { title: title, url: '/dashboard/elasticsearch/' + title };
|
||||
}, function(err) {
|
||||
throw 'Failed to save to elasticsearch ' + err.data;
|
||||
});
|
||||
};
|
||||
|
||||
ElasticDatasource.prototype.saveDashboardTemp = function(dashboard) {
|
||||
var data = {
|
||||
user: 'guest',
|
||||
group: 'guest',
|
||||
title: dashboard.title,
|
||||
tags: dashboard.tags,
|
||||
dashboard: angular.toJson(dashboard)
|
||||
};
|
||||
|
||||
var ttl = dashboard.loader.save_temp_ttl;
|
||||
|
||||
return this._request('POST', '/temp/?ttl=' + ttl, this.index, data)
|
||||
.then(function(result) {
|
||||
|
||||
var baseUrl = window.location.href.replace(window.location.hash,'');
|
||||
var url = baseUrl + "#dashboard/temp/" + result.data._id;
|
||||
|
||||
return { title: dashboard.title, url: url };
|
||||
|
||||
}, function(err) {
|
||||
throw "Failed to save to temp dashboard to elasticsearch " + err.data;
|
||||
});
|
||||
};
|
||||
|
||||
ElasticDatasource.prototype.deleteDashboard = function(id) {
|
||||
return this._request('DELETE', '/dashboard/' + id, this.index)
|
||||
.then(function(result) {
|
||||
return result.data._id;
|
||||
}, function(err) {
|
||||
throw err.data;
|
||||
});
|
||||
};
|
||||
|
||||
ElasticDatasource.prototype.searchDashboards = function(queryString) {
|
||||
var tagsOnly = queryString.indexOf('tags!:') === 0;
|
||||
if (tagsOnly) {
|
||||
var tagsQuery = queryString.substring(6, queryString.length);
|
||||
queryString = 'tags:' + tagsQuery + '*';
|
||||
}
|
||||
else {
|
||||
if (queryString.length === 0) {
|
||||
queryString = 'title:';
|
||||
}
|
||||
|
||||
if (queryString[queryString.length - 1] !== '*') {
|
||||
queryString += '*';
|
||||
}
|
||||
}
|
||||
|
||||
var query = {
|
||||
query: { query_string: { query: queryString } },
|
||||
facets: { tags: { terms: { field: "tags", order: "term", size: 50 } } },
|
||||
size: 20,
|
||||
sort: ["_uid"]
|
||||
};
|
||||
|
||||
return this._post('/dashboard/_search', query)
|
||||
.then(function(results) {
|
||||
if(_.isUndefined(results.hits)) {
|
||||
return { dashboards: [], tags: [] };
|
||||
}
|
||||
|
||||
return { dashboards: results.hits.hits, tags: results.facets.terms };
|
||||
});
|
||||
};
|
||||
|
||||
return ElasticDatasource;
|
||||
|
@ -21,6 +21,7 @@ function (angular, _, $, config, kbn, moment) {
|
||||
this.name = datasource.name;
|
||||
this.render_method = datasource.render_method || 'POST';
|
||||
this.supportAnnotations = true;
|
||||
this.supportMetrics = true;
|
||||
this.annotationEditorSrc = 'app/partials/graphite/annotation_editor.html';
|
||||
this.cacheTimeout = datasource.cacheTimeout;
|
||||
}
|
||||
|
@ -23,6 +23,7 @@ function (angular, _, kbn, InfluxSeries) {
|
||||
};
|
||||
|
||||
this.supportAnnotations = true;
|
||||
this.supportMetrics = true;
|
||||
this.annotationEditorSrc = 'app/partials/influxdb/annotation_editor.html';
|
||||
}
|
||||
|
||||
|
2
src/css/bootstrap.dark.min.css
vendored
2
src/css/bootstrap.dark.min.css
vendored
File diff suppressed because one or more lines are too long
2
src/css/bootstrap.light.min.css
vendored
2
src/css/bootstrap.light.min.css
vendored
File diff suppressed because one or more lines are too long
2
src/css/default.min.css
vendored
2
src/css/default.min.css
vendored
File diff suppressed because one or more lines are too long
@ -128,4 +128,6 @@
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
.annotation-tags {
|
||||
color: @purple;
|
||||
}
|
||||
|
@ -12,7 +12,6 @@ module.exports = function(config) {
|
||||
'app/routes/**/*.js',
|
||||
'app/app.js',
|
||||
'vendor/angular/**/*.js',
|
||||
'vendor/elasticjs/elastic-angular-client.js'
|
||||
],
|
||||
dest: '<%= tempDir %>'
|
||||
}
|
||||
|
Loading…
Reference in New Issue
Block a user