mirror of
https://github.com/grafana/grafana.git
synced 2025-02-25 18:55:37 -06:00
Merge branch 'develop'
This commit is contained in:
@@ -7,6 +7,7 @@
|
||||
- [Issue #525](https://github.com/grafana/grafana/issues/525). InfluxDB: Enhanced series aliasing (legend names) with pattern replacements
|
||||
- [Issue #581](https://github.com/grafana/grafana/issues/581). InfluxDB: Add continuous query in series results (series typeahead).
|
||||
- [Issue #584](https://github.com/grafana/grafana/issues/584). InfluxDB: Support for alias & alias patterns when using raw query mode
|
||||
- [Issue #394](https://github.com/grafana/grafana/issues/394). InfluxDB: Annotation support
|
||||
- [Issue #610](https://github.com/grafana/grafana/issues/610). InfluxDB: Support for InfluxdB v0.8 list series response schemea (series typeahead)
|
||||
- [Issue #604](https://github.com/grafana/grafana/issues/604). Chart: New axis format, 'bps' (SI unit in steps of 1000) useful for network gear metics
|
||||
|
||||
|
||||
@@ -6,15 +6,15 @@ define([
|
||||
'jquery',
|
||||
'underscore',
|
||||
'require',
|
||||
'elasticjs',
|
||||
'config',
|
||||
'bootstrap',
|
||||
'angular-sanitize',
|
||||
'angular-strap',
|
||||
'angular-dragdrop',
|
||||
'extend-jquery',
|
||||
'bindonce'
|
||||
'bindonce',
|
||||
],
|
||||
function (angular, $, _, appLevelRequire) {
|
||||
function (angular, $, _, appLevelRequire, config) {
|
||||
|
||||
"use strict";
|
||||
|
||||
@@ -48,38 +48,9 @@ function (angular, $, _, appLevelRequire) {
|
||||
return module;
|
||||
};
|
||||
|
||||
app.safeApply = function ($scope, fn) {
|
||||
switch($scope.$$phase) {
|
||||
case '$apply':
|
||||
// $digest hasn't started, we should be good
|
||||
$scope.$eval(fn);
|
||||
break;
|
||||
case '$digest':
|
||||
// waiting to $apply the changes
|
||||
setTimeout(function () { app.safeApply($scope, fn); }, 10);
|
||||
break;
|
||||
default:
|
||||
// clear to begin an $apply $$phase
|
||||
$scope.$apply(fn);
|
||||
break;
|
||||
}
|
||||
};
|
||||
|
||||
app.config(function ($routeProvider, $controllerProvider, $compileProvider, $filterProvider, $provide) {
|
||||
|
||||
$routeProvider
|
||||
.when('/dashboard', {
|
||||
templateUrl: 'app/partials/dashboard.html',
|
||||
})
|
||||
.when('/dashboard/:kbnType/:kbnId', {
|
||||
templateUrl: 'app/partials/dashboard.html',
|
||||
})
|
||||
.when('/dashboard/:kbnType/:kbnId/:params', {
|
||||
templateUrl: 'app/partials/dashboard.html'
|
||||
})
|
||||
.otherwise({
|
||||
redirectTo: 'dashboard'
|
||||
});
|
||||
$routeProvider.otherwise({ redirectTo: config.default_route });
|
||||
|
||||
// this is how the internet told me to dynamically add modules :/
|
||||
register_fns.controller = $controllerProvider.register;
|
||||
@@ -90,7 +61,6 @@ function (angular, $, _, appLevelRequire) {
|
||||
});
|
||||
|
||||
var apps_deps = [
|
||||
'elasticjs.service',
|
||||
'$strap.directives',
|
||||
'ngSanitize',
|
||||
'ngDragDrop',
|
||||
@@ -98,7 +68,7 @@ function (angular, $, _, appLevelRequire) {
|
||||
'pasvaz.bindonce'
|
||||
];
|
||||
|
||||
var module_types = ['controllers', 'directives', 'factories', 'services', 'services.dashboard', 'filters'];
|
||||
var module_types = ['controllers', 'directives', 'factories', 'services', 'filters', 'routes'];
|
||||
|
||||
_.each(module_types, function (type) {
|
||||
var module_name = 'kibana.'+type;
|
||||
@@ -114,13 +84,13 @@ function (angular, $, _, appLevelRequire) {
|
||||
'directives/all',
|
||||
'filters/all',
|
||||
'components/partials',
|
||||
'routes/all',
|
||||
], function () {
|
||||
|
||||
// bootstrap the app
|
||||
angular
|
||||
.element(document)
|
||||
.ready(function() {
|
||||
$('body').attr('ng-controller', 'DashCtrl');
|
||||
angular.bootstrap(document, apps_deps)
|
||||
.invoke(['$rootScope', function ($rootScope) {
|
||||
_.each(pre_boot_modules, function (module) {
|
||||
|
||||
@@ -10,18 +10,6 @@ function ($) {
|
||||
$.fn.place_tt = (function () {
|
||||
var defaults = {
|
||||
offset: 5,
|
||||
css: {
|
||||
position : 'absolute',
|
||||
top : -1000,
|
||||
left : 0,
|
||||
color : "#c8c8c8",
|
||||
padding : '10px',
|
||||
'font-size': '11pt',
|
||||
'font-weight' : 200,
|
||||
'background-color': '#1f1f1f',
|
||||
'border-radius': '5px',
|
||||
'z-index': 9999
|
||||
}
|
||||
};
|
||||
|
||||
return function (x, y, opts) {
|
||||
@@ -29,7 +17,8 @@ function ($) {
|
||||
return this.each(function () {
|
||||
var $tooltip = $(this), width, height;
|
||||
|
||||
$tooltip.css(opts.css);
|
||||
$tooltip.addClass('grafana-tooltip');
|
||||
|
||||
if (!$.contains(document.body, $tooltip[0])) {
|
||||
$tooltip.appendTo(document.body);
|
||||
}
|
||||
@@ -44,4 +33,4 @@ function ($) {
|
||||
})();
|
||||
|
||||
return $;
|
||||
});
|
||||
});
|
||||
|
||||
@@ -41,7 +41,6 @@ require.config({
|
||||
'jquery.flot.time': '../vendor/jquery/jquery.flot.time',
|
||||
|
||||
modernizr: '../vendor/modernizr-2.6.1',
|
||||
elasticjs: '../vendor/elasticjs/elastic-angular-client',
|
||||
|
||||
'bootstrap-tagsinput': '../vendor/tagsinput/bootstrap-tagsinput',
|
||||
|
||||
@@ -101,8 +100,6 @@ require.config({
|
||||
timepicker: ['jquery', 'bootstrap'],
|
||||
datepicker: ['jquery', 'bootstrap'],
|
||||
|
||||
elasticjs: ['angular', '../vendor/elasticjs/elastic'],
|
||||
|
||||
'bootstrap-tagsinput': ['jquery'],
|
||||
},
|
||||
waitSeconds: 60,
|
||||
|
||||
@@ -25,7 +25,6 @@ function (_, crypto) {
|
||||
default_route : '/dashboard/file/default.json',
|
||||
grafana_index : 'grafana-dash',
|
||||
elasticsearch_all_disabled : false,
|
||||
timezoneOffset : null,
|
||||
playlist_timespan : "1m",
|
||||
unsaved_changes_warning : true
|
||||
};
|
||||
|
||||
@@ -1,4 +1,5 @@
|
||||
define([
|
||||
'./grafanaCtrl',
|
||||
'./dash',
|
||||
'./dashLoader',
|
||||
'./row',
|
||||
|
||||
@@ -23,7 +23,6 @@ define([
|
||||
'config',
|
||||
'underscore',
|
||||
'services/all',
|
||||
'services/dashboard/all'
|
||||
],
|
||||
function (angular, $, config, _) {
|
||||
"use strict";
|
||||
@@ -31,50 +30,40 @@ function (angular, $, config, _) {
|
||||
var module = angular.module('kibana.controllers');
|
||||
|
||||
module.controller('DashCtrl', function(
|
||||
$scope, $rootScope, $timeout, ejsResource, dashboard, filterSrv, dashboardKeybindings,
|
||||
alertSrv, panelMove, keyboardManager, grafanaVersion) {
|
||||
$scope, $rootScope, dashboardKeybindings, filterSrv, dashboard, panelMoveSrv, timer) {
|
||||
|
||||
$scope.requiredElasticSearchVersion = ">=0.90.3";
|
||||
|
||||
$scope.editor = {
|
||||
index: 0
|
||||
};
|
||||
|
||||
$scope.grafanaVersion = grafanaVersion[0] === '@' ? 'master' : grafanaVersion;
|
||||
|
||||
// For moving stuff around the dashboard.
|
||||
$scope.panelMoveDrop = panelMove.onDrop;
|
||||
$scope.panelMoveStart = panelMove.onStart;
|
||||
$scope.panelMoveStop = panelMove.onStop;
|
||||
$scope.panelMoveOver = panelMove.onOver;
|
||||
$scope.panelMoveOut = panelMove.onOut;
|
||||
$scope.editor = { index: 0 };
|
||||
|
||||
$scope.init = function() {
|
||||
$scope.config = config;
|
||||
|
||||
// Make stuff, including underscore.js available to views
|
||||
$scope._ = _;
|
||||
$scope.dashboard = dashboard;
|
||||
$scope.dashAlerts = alertSrv;
|
||||
|
||||
$scope.filter = filterSrv;
|
||||
$scope.filter.init(dashboard.current);
|
||||
|
||||
$rootScope.$on("dashboard-loaded", function(event, dashboard) {
|
||||
$scope.filter.init(dashboard);
|
||||
});
|
||||
|
||||
// Clear existing alerts
|
||||
alertSrv.clearAll();
|
||||
|
||||
$scope.reset_row();
|
||||
|
||||
$scope.ejs = ejsResource(config.elasticsearch, config.elasticsearchBasicAuth);
|
||||
|
||||
$scope.bindKeyboardShortcuts();
|
||||
$scope.availablePanels = config.panels;
|
||||
$scope.onAppEvent('setup-dashboard', $scope.setupDashboard);
|
||||
};
|
||||
|
||||
$scope.bindKeyboardShortcuts = dashboardKeybindings.shortcuts;
|
||||
$scope.setupDashboard = function(event, dashboardData) {
|
||||
timer.cancel_all();
|
||||
|
||||
$rootScope.fullscreen = false;
|
||||
|
||||
$scope.dashboard = dashboard.create(dashboardData);
|
||||
$scope.grafana.style = $scope.dashboard.style;
|
||||
|
||||
$scope.filter = filterSrv;
|
||||
$scope.filter.init($scope.dashboard);
|
||||
|
||||
var panelMove = panelMoveSrv.create($scope.dashboard);
|
||||
|
||||
$scope.panelMoveDrop = panelMove.onDrop;
|
||||
$scope.panelMoveStart = panelMove.onStart;
|
||||
$scope.panelMoveStop = panelMove.onStop;
|
||||
$scope.panelMoveOver = panelMove.onOver;
|
||||
$scope.panelMoveOut = panelMove.onOut;
|
||||
|
||||
window.document.title = 'Grafana - ' + $scope.dashboard.title;
|
||||
|
||||
dashboardKeybindings.shortcuts($scope);
|
||||
|
||||
$scope.emitAppEvent("dashboard-loaded", $scope.dashboard);
|
||||
};
|
||||
|
||||
$scope.isPanel = function(obj) {
|
||||
if(!_.isNull(obj) && !_.isUndefined(obj) && !_.isUndefined(obj.type)) {
|
||||
@@ -91,7 +80,7 @@ function (angular, $, config, _) {
|
||||
$scope.add_row_default = function() {
|
||||
$scope.reset_row();
|
||||
$scope.row.title = 'New row';
|
||||
$scope.add_row(dashboard.current, $scope.row);
|
||||
$scope.add_row($scope.dashboard, $scope.row);
|
||||
};
|
||||
|
||||
$scope.reset_row = function() {
|
||||
@@ -131,12 +120,6 @@ function (angular, $, config, _) {
|
||||
return $scope.editorTabs;
|
||||
};
|
||||
|
||||
// This is whoafully incomplete, but will do for now
|
||||
$scope.parse_error = function(data) {
|
||||
var _error = data.match("nested: (.*?);");
|
||||
return _.isNull(_error) ? data : _error[1];
|
||||
};
|
||||
|
||||
$scope.colors = [
|
||||
"#7EB26D","#EAB839","#6ED0E0","#EF843C","#E24D42","#1F78C1","#BA43A9","#705DA0", //1
|
||||
"#508642","#CCA300","#447EBC","#C15C17","#890F02","#0A437C","#6D1F62","#584477", //2
|
||||
|
||||
@@ -1,135 +1,106 @@
|
||||
define([
|
||||
'angular',
|
||||
'underscore',
|
||||
'moment'
|
||||
'moment',
|
||||
'filesaver'
|
||||
],
|
||||
function (angular, _, moment) {
|
||||
'use strict';
|
||||
|
||||
var module = angular.module('kibana.controllers');
|
||||
|
||||
module.controller('dashLoader', function($scope, $rootScope, $http, dashboard, alertSrv, $location, playlistSrv) {
|
||||
$scope.loader = dashboard.current.loader;
|
||||
module.controller('dashLoader', function($scope, $rootScope, $http, alertSrv, $location, playlistSrv, elastic) {
|
||||
|
||||
$scope.init = function() {
|
||||
$scope.gist_pattern = /(^\d{5,}$)|(^[a-z0-9]{10,}$)|(gist.github.com(\/*.*)\/[a-z0-9]{5,}\/*$)/;
|
||||
$scope.gist = $scope.gist || {};
|
||||
$scope.elasticsearch = $scope.elasticsearch || {};
|
||||
|
||||
$rootScope.$on('save-dashboard', function() {
|
||||
$scope.elasticsearch_save('dashboard', false);
|
||||
$scope.onAppEvent('save-dashboard', function() {
|
||||
$scope.saveDashboard();
|
||||
});
|
||||
|
||||
$rootScope.$on('zoom-out', function() {
|
||||
$scope.onAppEvent('zoom-out', function() {
|
||||
$scope.zoom(2);
|
||||
});
|
||||
|
||||
};
|
||||
|
||||
$scope.exitFullscreen = function() {
|
||||
$rootScope.$emit('panel-fullscreen-exit');
|
||||
$scope.emitAppEvent('panel-fullscreen-exit');
|
||||
};
|
||||
|
||||
$scope.showDropdown = function(type) {
|
||||
if(_.isUndefined(dashboard.current.loader)) {
|
||||
if(_.isUndefined($scope.dashboard)) {
|
||||
return true;
|
||||
}
|
||||
|
||||
var _l = dashboard.current.loader;
|
||||
var _l = $scope.dashboard.loader;
|
||||
if(type === 'load') {
|
||||
return (_l.load_elasticsearch || _l.load_gist || _l.load_local);
|
||||
return (_l.load_elasticsearch || _l.load_gist);
|
||||
}
|
||||
if(type === 'save') {
|
||||
return (_l.save_elasticsearch || _l.save_gist || _l.save_local || _l.save_default);
|
||||
}
|
||||
if(type === 'share') {
|
||||
return (_l.save_temp);
|
||||
return (_l.save_elasticsearch || _l.save_gist);
|
||||
}
|
||||
return false;
|
||||
};
|
||||
|
||||
$scope.set_default = function() {
|
||||
if(dashboard.set_default($location.path())) {
|
||||
alertSrv.set('Home Set','This page has been set as your default dashboard','success',5000);
|
||||
} else {
|
||||
alertSrv.set('Incompatible Browser','Sorry, your browser is too old for this feature','error',5000);
|
||||
}
|
||||
window.localStorage.grafanaDashboardDefault = $location.path();
|
||||
alertSrv.set('Home Set','This page has been set as your default dashboard','success',5000);
|
||||
};
|
||||
|
||||
$scope.purge_default = function() {
|
||||
if(dashboard.purge_default()) {
|
||||
alertSrv.set('Local Default Clear','Your default dashboard has been reset to the default',
|
||||
'success',5000);
|
||||
} else {
|
||||
alertSrv.set('Incompatible Browser','Sorry, your browser is too old for this feature','error',5000);
|
||||
}
|
||||
delete window.localStorage.grafanaDashboardDefault;
|
||||
alertSrv.set('Local Default Clear','Your default dashboard has been reset to the default','success', 5000);
|
||||
};
|
||||
|
||||
$scope.elasticsearch_save = function(type,ttl) {
|
||||
dashboard.elasticsearch_save(type, dashboard.current.title, ttl)
|
||||
$scope.saveForSharing = function() {
|
||||
elastic.saveForSharing($scope.dashboard)
|
||||
.then(function(result) {
|
||||
if(_.isUndefined(result._id)) {
|
||||
alertSrv.set('Save failed','Dashboard could not be saved to Elasticsearch','error',5000);
|
||||
return;
|
||||
}
|
||||
|
||||
alertSrv.set('Dashboard Saved', 'Dashboard has been saved to Elasticsearch as "' + result._id + '"','success', 5000);
|
||||
if(type === 'temp') {
|
||||
$scope.share = dashboard.share_link(dashboard.current.title,'temp',result._id);
|
||||
}
|
||||
$scope.share = { url: result.url, title: result.title };
|
||||
|
||||
$rootScope.$emit('dashboard-saved', dashboard.current);
|
||||
}, function(err) {
|
||||
alertSrv.set('Save for sharing failed', err, 'error',5000);
|
||||
});
|
||||
};
|
||||
|
||||
$scope.elasticsearch_delete = function(id) {
|
||||
$scope.saveDashboard = function() {
|
||||
elastic.saveDashboard($scope.dashboard, $scope.dashboard.title)
|
||||
.then(function(result) {
|
||||
alertSrv.set('Dashboard Saved', 'Dashboard has been saved to Elasticsearch as "' + result.title + '"','success', 5000);
|
||||
|
||||
$location.path(result.url);
|
||||
|
||||
$rootScope.$emit('dashboard-saved', $scope.dashboard);
|
||||
|
||||
}, function(err) {
|
||||
alertSrv.set('Save failed', err, 'error',5000);
|
||||
});
|
||||
};
|
||||
|
||||
$scope.deleteDashboard = function(id) {
|
||||
if (!confirm('Are you sure you want to delete dashboard?')) {
|
||||
return;
|
||||
}
|
||||
|
||||
dashboard.elasticsearch_delete(id).then(
|
||||
function(result) {
|
||||
if(!_.isUndefined(result)) {
|
||||
if(result.found) {
|
||||
alertSrv.set('Dashboard Deleted',id+' has been deleted','success',5000);
|
||||
// Find the deleted dashboard in the cached list and remove it
|
||||
var toDelete = _.where($scope.elasticsearch.dashboards,{_id:id})[0];
|
||||
$scope.elasticsearch.dashboards = _.without($scope.elasticsearch.dashboards,toDelete);
|
||||
} else {
|
||||
alertSrv.set('Dashboard Not Found','Could not find '+id+' in Elasticsearch','warning',5000);
|
||||
}
|
||||
} else {
|
||||
alertSrv.set('Dashboard Not Deleted','An error occurred deleting the dashboard','error',5000);
|
||||
}
|
||||
}
|
||||
);
|
||||
};
|
||||
|
||||
$scope.save_gist = function() {
|
||||
dashboard.save_gist($scope.gist.title).then(function(link) {
|
||||
if (!_.isUndefined(link)) {
|
||||
$scope.gist.last = link;
|
||||
alertSrv.set('Gist saved','You will be able to access your exported dashboard file at '+
|
||||
'<a href="'+link+'">'+link+'</a> in a moment','success');
|
||||
} else {
|
||||
alertSrv.set('Save failed','Gist could not be saved','error',5000);
|
||||
}
|
||||
elastic.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);
|
||||
});
|
||||
};
|
||||
|
||||
$scope.gist_dblist = function(id) {
|
||||
dashboard.gist_list(id).then(function(files) {
|
||||
if (files && files.length > 0) {
|
||||
$scope.gist.files = files;
|
||||
} else {
|
||||
alertSrv.set('Gist Failed','Could not retrieve dashboard list from gist','error',5000);
|
||||
}
|
||||
});
|
||||
$scope.exportDashboard = function() {
|
||||
var blob = new Blob([angular.toJson($scope.dashboard, true)], { type: "application/json;charset=utf-8" });
|
||||
window.saveAs(blob, $scope.dashboard.title + '-' + new Date().getTime());
|
||||
};
|
||||
|
||||
// function $scope.zoom
|
||||
// factor :: Zoom factor, so 0.5 = cuts timespan in half, 2 doubles timespan
|
||||
$scope.zoom = function(factor) {
|
||||
var _range = this.filter.timeRange();
|
||||
var _range = $scope.filter.timeRange();
|
||||
var _timespan = (_range.to.valueOf() - _range.from.valueOf());
|
||||
var _center = _range.to.valueOf() - _timespan/2;
|
||||
|
||||
@@ -143,23 +114,27 @@ function (angular, _, moment) {
|
||||
_to = Date.now();
|
||||
}
|
||||
|
||||
this.filter.setTime({
|
||||
$scope.filter.setTime({
|
||||
from:moment.utc(_from).toDate(),
|
||||
to:moment.utc(_to).toDate(),
|
||||
});
|
||||
};
|
||||
|
||||
$scope.styleUpdated = function() {
|
||||
$scope.grafana.style = $scope.dashboard.style;
|
||||
};
|
||||
|
||||
$scope.openSaveDropdown = function() {
|
||||
$scope.isFavorite = playlistSrv.isCurrentFavorite();
|
||||
$scope.isFavorite = playlistSrv.isCurrentFavorite($scope.dashboard);
|
||||
};
|
||||
|
||||
$scope.markAsFavorite = function() {
|
||||
playlistSrv.markAsFavorite();
|
||||
playlistSrv.markAsFavorite($scope.dashboard);
|
||||
$scope.isFavorite = true;
|
||||
};
|
||||
|
||||
$scope.removeAsFavorite = function() {
|
||||
playlistSrv.removeAsFavorite(dashboard.current);
|
||||
playlistSrv.removeAsFavorite($scope.dashboard);
|
||||
$scope.isFavorite = false;
|
||||
};
|
||||
|
||||
|
||||
35
src/app/controllers/grafanaCtrl.js
Normal file
35
src/app/controllers/grafanaCtrl.js
Normal file
@@ -0,0 +1,35 @@
|
||||
define([
|
||||
'angular',
|
||||
'config',
|
||||
'underscore',
|
||||
],
|
||||
function (angular, config, _) {
|
||||
"use strict";
|
||||
|
||||
var module = angular.module('kibana.controllers');
|
||||
|
||||
module.controller('GrafanaCtrl', function($scope, alertSrv, grafanaVersion, $rootScope) {
|
||||
|
||||
$scope.grafanaVersion = grafanaVersion[0] === '@' ? 'master' : grafanaVersion;
|
||||
|
||||
$scope.init = function() {
|
||||
$scope._ = _;
|
||||
$scope.dashAlerts = alertSrv;
|
||||
$scope.grafana = {
|
||||
style: 'dark'
|
||||
};
|
||||
};
|
||||
|
||||
$rootScope.onAppEvent = function(name, callback) {
|
||||
var unbind = $rootScope.$on(name, callback);
|
||||
this.$on('$destroy', unbind);
|
||||
};
|
||||
|
||||
$rootScope.emitAppEvent = function(name, payload) {
|
||||
$rootScope.$emit(name, payload);
|
||||
};
|
||||
|
||||
$scope.init();
|
||||
|
||||
});
|
||||
});
|
||||
@@ -8,7 +8,7 @@ function (angular, app, _) {
|
||||
|
||||
var module = angular.module('kibana.controllers');
|
||||
|
||||
module.controller('GraphiteImportCtrl', function($scope, $rootScope, $timeout, datasourceSrv, dashboard) {
|
||||
module.controller('GraphiteImportCtrl', function($scope, $rootScope, $timeout, datasourceSrv) {
|
||||
|
||||
$scope.init = function() {
|
||||
console.log('hej!');
|
||||
@@ -68,7 +68,7 @@ function (angular, app, _) {
|
||||
|
||||
currentRow = angular.copy(rowTemplate);
|
||||
|
||||
var newDashboard = angular.copy(dashboard.current);
|
||||
var newDashboard = angular.copy($scope.dashboard);
|
||||
newDashboard.rows = [];
|
||||
newDashboard.title = state.name;
|
||||
newDashboard.rows.push(currentRow);
|
||||
@@ -96,7 +96,7 @@ function (angular, app, _) {
|
||||
currentRow.panels.push(panel);
|
||||
});
|
||||
|
||||
dashboard.dash_load(newDashboard);
|
||||
$scope.dashboard.dash_load(newDashboard);
|
||||
}
|
||||
|
||||
});
|
||||
|
||||
@@ -86,7 +86,7 @@ function (angular, _, $) {
|
||||
|
||||
$timeout(function() {
|
||||
if (oldTimeRange !== $scope.range) {
|
||||
$scope.dashboard.refresh();
|
||||
$scope.dashboard.emit_refresh();
|
||||
}
|
||||
else {
|
||||
$scope.$emit('render');
|
||||
|
||||
@@ -76,12 +76,12 @@ function (angular, app, _) {
|
||||
|
||||
$scope.delete_row = function() {
|
||||
if (confirm("Are you sure you want to delete this row?")) {
|
||||
$scope.dashboard.current.rows = _.without($scope.dashboard.current.rows, $scope.row);
|
||||
$scope.dashboard.rows = _.without($scope.dashboard.rows, $scope.row);
|
||||
}
|
||||
};
|
||||
|
||||
$scope.move_row = function(direction) {
|
||||
var rowsList = $scope.dashboard.current.rows;
|
||||
var rowsList = $scope.dashboard.rows;
|
||||
var rowIndex = _.indexOf(rowsList, $scope.row);
|
||||
var newIndex = rowIndex + direction;
|
||||
if (newIndex >= 0 && newIndex <= (rowsList.length - 1)) {
|
||||
@@ -116,12 +116,12 @@ function (angular, app, _) {
|
||||
row.panels.push(angular.copy(panel));
|
||||
}
|
||||
else {
|
||||
var rowsList = $scope.dashboard.current.rows;
|
||||
var rowsList = $scope.dashboard.rows;
|
||||
var rowIndex = _.indexOf(rowsList, row);
|
||||
if (rowIndex === rowsList.length - 1) {
|
||||
var newRow = angular.copy($scope.row);
|
||||
newRow.panels = [];
|
||||
$scope.dashboard.current.rows.push(newRow);
|
||||
$scope.dashboard.rows.push(newRow);
|
||||
$scope.duplicatePanel(panel, newRow);
|
||||
}
|
||||
else {
|
||||
|
||||
@@ -9,14 +9,14 @@ function (angular, _, config, $) {
|
||||
|
||||
var module = angular.module('kibana.controllers');
|
||||
|
||||
module.controller('SearchCtrl', function($scope, $rootScope, dashboard, $element, $location) {
|
||||
module.controller('SearchCtrl', function($scope, $rootScope, $element, $location, elastic) {
|
||||
|
||||
$scope.init = function() {
|
||||
$scope.giveSearchFocus = 0;
|
||||
$scope.selectedIndex = -1;
|
||||
$scope.results = {dashboards: [], tags: [], metrics: []};
|
||||
$scope.query = { query: 'title:' };
|
||||
$rootScope.$on('open-search', $scope.openSearch);
|
||||
$scope.onAppEvent('open-search', $scope.openSearch);
|
||||
};
|
||||
|
||||
$scope.keyDown = function (evt) {
|
||||
@@ -48,30 +48,40 @@ function (angular, _, config, $) {
|
||||
}
|
||||
};
|
||||
|
||||
$scope.searchDasboards = function(query) {
|
||||
var request = $scope.ejs.Request().indices(config.grafana_index).types('dashboard');
|
||||
var tagsOnly = query.indexOf('tags!:') === 0;
|
||||
$scope.shareDashboard = function(title, id) {
|
||||
var baseUrl = window.location.href.replace(window.location.hash,'');
|
||||
|
||||
$scope.share = {
|
||||
title: title,
|
||||
url: baseUrl + '#dashboard/elasticsearch/' + encodeURIComponent(id)
|
||||
};
|
||||
};
|
||||
|
||||
$scope.searchDasboards = function(queryString) {
|
||||
var tagsOnly = queryString.indexOf('tags!:') === 0;
|
||||
if (tagsOnly) {
|
||||
var tagsQuery = query.substring(6, query.length);
|
||||
query = 'tags:' + tagsQuery + '*';
|
||||
var tagsQuery = queryString.substring(6, queryString.length);
|
||||
queryString = 'tags:' + tagsQuery + '*';
|
||||
}
|
||||
else {
|
||||
if (query.length === 0) {
|
||||
query = 'title:';
|
||||
if (queryString.length === 0) {
|
||||
queryString = 'title:';
|
||||
}
|
||||
|
||||
if (query[query.length - 1] !== '*') {
|
||||
query += '*';
|
||||
if (queryString[queryString.length - 1] !== '*') {
|
||||
queryString += '*';
|
||||
}
|
||||
}
|
||||
|
||||
return request
|
||||
.query($scope.ejs.QueryStringQuery(query))
|
||||
.sort('_uid')
|
||||
.facet($scope.ejs.TermsFacet("tags").field("tags").order('term').size(50))
|
||||
.size(20).doSearch()
|
||||
.then(function(results) {
|
||||
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)
|
||||
.then(function(results) {
|
||||
if(_.isUndefined(results.hits)) {
|
||||
$scope.results.dashboards = [];
|
||||
$scope.results.tags = [];
|
||||
@@ -114,32 +124,6 @@ function (angular, _, config, $) {
|
||||
$scope.searchDasboards(queryStr);
|
||||
return;
|
||||
}
|
||||
|
||||
queryStr = queryStr.substring(2, queryStr.length);
|
||||
|
||||
var words = queryStr.split(' ');
|
||||
var query = $scope.ejs.BoolQuery();
|
||||
var terms = _.map(words, function(word) {
|
||||
return $scope.ejs.MatchQuery('metricPath_ng', word).boost(1.2);
|
||||
});
|
||||
|
||||
var ngramQuery = $scope.ejs.BoolQuery();
|
||||
ngramQuery.must(terms);
|
||||
|
||||
var fieldMatchQuery = $scope.ejs.FieldQuery('metricPath', queryStr + "*").boost(1.2);
|
||||
query.should([ngramQuery, fieldMatchQuery]);
|
||||
|
||||
var request = $scope.ejs.Request().indices(config.grafana_index).types('metricKey');
|
||||
var results = request.query(query).size(20).doSearch();
|
||||
|
||||
results.then(function(results) {
|
||||
if (results && results.hits && results.hits.hits.length > 0) {
|
||||
$scope.results.metrics = { metrics: results.hits.hits };
|
||||
}
|
||||
else {
|
||||
$scope.results.metrics = { metric: [] };
|
||||
}
|
||||
});
|
||||
};
|
||||
|
||||
$scope.openSearch = function (evt) {
|
||||
@@ -153,7 +137,7 @@ function (angular, _, config, $) {
|
||||
};
|
||||
|
||||
$scope.addMetricToCurrentDashboard = function (metricId) {
|
||||
dashboard.current.rows.push({
|
||||
$scope.dashboard.rows.push({
|
||||
title: '',
|
||||
height: '250px',
|
||||
editable: true,
|
||||
|
||||
@@ -92,7 +92,6 @@
|
||||
],
|
||||
"editable": true,
|
||||
"failover": false,
|
||||
"panel_hints": true,
|
||||
"style": "dark",
|
||||
"pulldowns": [
|
||||
{
|
||||
@@ -138,15 +137,12 @@
|
||||
"loader": {
|
||||
"save_gist": false,
|
||||
"save_elasticsearch": true,
|
||||
"save_local": true,
|
||||
"save_default": true,
|
||||
"save_temp": true,
|
||||
"save_temp_ttl_enable": true,
|
||||
"save_temp_ttl": "30d",
|
||||
"load_gist": false,
|
||||
"load_elasticsearch": true,
|
||||
"load_elasticsearch_size": 20,
|
||||
"load_local": false,
|
||||
"hide": false
|
||||
},
|
||||
"refresh": false
|
||||
|
||||
@@ -68,7 +68,6 @@
|
||||
"loader": {
|
||||
"save_gist": false,
|
||||
"save_elasticsearch": true,
|
||||
"save_local": true,
|
||||
"save_default": true,
|
||||
"save_temp": true,
|
||||
"save_temp_ttl_enable": true,
|
||||
@@ -76,7 +75,6 @@
|
||||
"load_gist": false,
|
||||
"load_elasticsearch": true,
|
||||
"load_elasticsearch_size": 20,
|
||||
"load_local": false,
|
||||
"hide": false
|
||||
},
|
||||
"refresh": false
|
||||
|
||||
@@ -15,8 +15,12 @@ function (angular, app, _) {
|
||||
var lastPulldownVal;
|
||||
var lastHideControlsVal;
|
||||
|
||||
$scope.$watch('dashboard.current.pulldowns', function() {
|
||||
var panel = _.find($scope.dashboard.current.pulldowns, function(pulldown) { return pulldown.enable; });
|
||||
$scope.$watch('dashboard.pulldowns', function() {
|
||||
if (!$scope.dashboard) {
|
||||
return;
|
||||
}
|
||||
|
||||
var panel = _.find($scope.dashboard.pulldowns, function(pulldown) { return pulldown.enable; });
|
||||
var panelEnabled = panel ? panel.enable : false;
|
||||
if (lastPulldownVal !== panelEnabled) {
|
||||
elem.toggleClass('submenu-controls-visible', panelEnabled);
|
||||
@@ -24,8 +28,12 @@ function (angular, app, _) {
|
||||
}
|
||||
}, true);
|
||||
|
||||
$scope.$watch('dashboard.current.hideControls', function() {
|
||||
var hideControls = $scope.dashboard.current.hideControls || $scope.playlist_active;
|
||||
$scope.$watch('dashboard.hideControls', function() {
|
||||
if (!$scope.dashboard) {
|
||||
return;
|
||||
}
|
||||
|
||||
var hideControls = $scope.dashboard.hideControls || $scope.playlist_active;
|
||||
|
||||
if (lastHideControlsVal !== hideControls) {
|
||||
elem.toggleClass('hide-controls', hideControls);
|
||||
|
||||
@@ -6,7 +6,7 @@ function (angular) {
|
||||
|
||||
var module = angular.module('kibana.directives');
|
||||
|
||||
module.directive('dashUpload', function(timer, dashboard, alertSrv) {
|
||||
module.directive('dashUpload', function(timer, alertSrv) {
|
||||
return {
|
||||
restrict: 'A',
|
||||
link: function(scope) {
|
||||
@@ -14,7 +14,8 @@ function (angular) {
|
||||
var files = evt.target.files; // FileList object
|
||||
var readerOnload = function() {
|
||||
return function(e) {
|
||||
dashboard.dash_load(JSON.parse(e.target.result));
|
||||
var dashboard = JSON.parse(e.target.result);
|
||||
scope.emitAppEvent('setup-dashboard', dashboard);
|
||||
scope.$apply();
|
||||
};
|
||||
};
|
||||
@@ -34,4 +35,4 @@ function (angular) {
|
||||
}
|
||||
};
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
@@ -10,13 +10,14 @@ function (angular, $, kbn, moment, _) {
|
||||
|
||||
var module = angular.module('kibana.directives');
|
||||
|
||||
module.directive('grafanaGraph', function($rootScope, dashboard) {
|
||||
module.directive('grafanaGraph', function($rootScope) {
|
||||
return {
|
||||
restrict: 'A',
|
||||
template: '<div> </div>',
|
||||
link: function(scope, elem) {
|
||||
var data, plot, annotations;
|
||||
var hiddenData = {};
|
||||
var dashboard = scope.dashboard;
|
||||
var legendSideLastValue = null;
|
||||
|
||||
scope.$on('refresh',function() {
|
||||
@@ -195,7 +196,7 @@ function (angular, $, kbn, moment, _) {
|
||||
var max = _.isUndefined(scope.range.to) ? null : scope.range.to.getTime();
|
||||
|
||||
options.xaxis = {
|
||||
timezone: dashboard.current.timezone,
|
||||
timezone: dashboard.timezone,
|
||||
show: scope.panel['x-axis'],
|
||||
mode: "time",
|
||||
min: min,
|
||||
@@ -354,7 +355,7 @@ function (angular, $, kbn, moment, _) {
|
||||
|
||||
value = kbn.getFormatFunction(format, 2)(value);
|
||||
|
||||
timestamp = dashboard.current.timezone === 'browser' ?
|
||||
timestamp = dashboard.timezone === 'browser' ?
|
||||
moment(item.datapoint[0]).format('YYYY-MM-DD HH:mm:ss') :
|
||||
moment.utc(item.datapoint[0]).format('YYYY-MM-DD HH:mm:ss');
|
||||
$tooltip
|
||||
|
||||
@@ -1,4 +1,5 @@
|
||||
<div bindonce class="modal-body">
|
||||
<div ng-controller="AnnotationsEditorCtrl" ng-init="init()">
|
||||
<div class="modal-body">
|
||||
<div class="pull-right editor-title">Annotations</div>
|
||||
|
||||
<div class="editor-row">
|
||||
@@ -29,42 +30,31 @@
|
||||
|
||||
<div class="editor-option">
|
||||
<label class="small">Name</label>
|
||||
<input type="text" class="input-medium" ng-model='currentAnnnotation.name' placeholder="name"></input>
|
||||
<input type="text" class="input-medium" ng-model='currentAnnotation.name' placeholder="name"></input>
|
||||
</div>
|
||||
<div class="editor-option">
|
||||
<label class="small">Type</label>
|
||||
<select ng-model="currentAnnnotation.type" ng-options="f for f in ['graphite metric', 'graphite events']"></select>
|
||||
<label class="small">Datasource</label>
|
||||
<select ng-model="currentDatasource" ng-options="f.name for f in datasources" ng-change="setDatasource()"></select>
|
||||
</div>
|
||||
<div class="editor-option">
|
||||
<label class="small">Icon color</label>
|
||||
<spectrum-picker ng-model="currentAnnnotation.iconColor"></spectrum-picker>
|
||||
<spectrum-picker ng-model="currentAnnotation.iconColor"></spectrum-picker>
|
||||
</div>
|
||||
<div class="editor-option">
|
||||
<label class="small">Icon size</label>
|
||||
<select class="input-mini" ng-model="currentAnnnotation.iconSize" ng-options="f for f in [7,8,9,10,13,15,17,20,25,30]"></select>
|
||||
<select class="input-mini" ng-model="currentAnnotation.iconSize" ng-options="f for f in [7,8,9,10,13,15,17,20,25,30]"></select>
|
||||
</div>
|
||||
<div class="editor-option">
|
||||
<label class="small">Grid line</label>
|
||||
<input type="checkbox" ng-model="currentAnnnotation.showLine" ng-checked="currentAnnnotation.showLine">
|
||||
<input type="checkbox" ng-model="currentAnnotation.showLine" ng-checked="currentAnnotation.showLine">
|
||||
</div>
|
||||
<div class="editor-option">
|
||||
<label class="small">Line color</label>
|
||||
<spectrum-picker ng-model="currentAnnnotation.lineColor"></spectrum-picker>
|
||||
<spectrum-picker ng-model="currentAnnotation.lineColor"></spectrum-picker>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="editor-row" ng-if="currentAnnnotation.type === 'graphite metric'">
|
||||
<div class="editor-option">
|
||||
<label class="small">Graphite target expression</label>
|
||||
<input type="text" class="span10" ng-model='currentAnnnotation.target' placeholder=""></input>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="editor-row" ng-if="currentAnnnotation.type === 'graphite events'">
|
||||
<div class="editor-option">
|
||||
<label class="small">Graphite event tags</label>
|
||||
<input type="text" ng-model='currentAnnnotation.tags' placeholder=""></input>
|
||||
</div>
|
||||
<div ng-include src="currentDatasource.editorSrc">
|
||||
</div>
|
||||
|
||||
</div>
|
||||
@@ -73,4 +63,5 @@
|
||||
<button ng-show="currentIsNew" type="button" class="btn btn-success" ng-click="add()">Add annotation</button>
|
||||
<button ng-show="!currentIsNew" type="button" class="btn btn-success" ng-click="update()">Update</button>
|
||||
<button type="button" class="btn btn-danger" ng-click="close_edit();dismiss();dashboard.refresh();">Close</button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
68
src/app/panels/annotations/editor.js
Normal file
68
src/app/panels/annotations/editor.js
Normal file
@@ -0,0 +1,68 @@
|
||||
/*
|
||||
|
||||
*/
|
||||
define([
|
||||
'angular',
|
||||
'app',
|
||||
'underscore'
|
||||
],
|
||||
function (angular, app, _) {
|
||||
'use strict';
|
||||
|
||||
var module = angular.module('kibana.panels.annotations', []);
|
||||
app.useModule(module);
|
||||
|
||||
module.controller('AnnotationsEditorCtrl', function($scope, datasourceSrv, $rootScope) {
|
||||
|
||||
var annotationDefaults = {
|
||||
name: '',
|
||||
datasource: null,
|
||||
showLine: true,
|
||||
iconColor: '#C0C6BE',
|
||||
lineColor: 'rgba(255, 96, 96, 0.592157)',
|
||||
iconSize: 13,
|
||||
enable: true
|
||||
};
|
||||
|
||||
$scope.init = function() {
|
||||
$scope.currentAnnotation = angular.copy(annotationDefaults);
|
||||
$scope.currentIsNew = true;
|
||||
$scope.datasources = datasourceSrv.getAnnotationSources();
|
||||
|
||||
if ($scope.datasources.length > 0) {
|
||||
$scope.currentDatasource = $scope.datasources[0];
|
||||
}
|
||||
};
|
||||
|
||||
$scope.setDatasource = function() {
|
||||
$scope.currentAnnotation.datasource = $scope.currentDatasource.name;
|
||||
};
|
||||
|
||||
$scope.edit = function(annotation) {
|
||||
$scope.currentAnnotation = annotation;
|
||||
$scope.currentIsNew = false;
|
||||
$scope.currentDatasource = _.findWhere($scope.datasources, { name: annotation.datasource });
|
||||
|
||||
if (!$scope.currentDatasource) {
|
||||
$scope.currentDatasource = $scope.datasources[0];
|
||||
}
|
||||
};
|
||||
|
||||
$scope.update = function() {
|
||||
$scope.currentAnnotation = angular.copy(annotationDefaults);
|
||||
$scope.currentIsNew = true;
|
||||
};
|
||||
|
||||
$scope.add = function() {
|
||||
$scope.currentAnnotation.datasource = $scope.currentDatasource.name;
|
||||
$scope.panel.annotations.push($scope.currentAnnotation);
|
||||
$scope.currentAnnnotation = angular.copy(annotationDefaults);
|
||||
};
|
||||
|
||||
$scope.hide = function (annotation) {
|
||||
annotation.enable = !annotation.enable;
|
||||
$rootScope.$broadcast('refresh');
|
||||
};
|
||||
|
||||
});
|
||||
});
|
||||
@@ -6,7 +6,8 @@
|
||||
define([
|
||||
'angular',
|
||||
'app',
|
||||
'underscore'
|
||||
'underscore',
|
||||
'./editor'
|
||||
],
|
||||
function (angular, app, _) {
|
||||
'use strict';
|
||||
@@ -14,7 +15,7 @@ function (angular, app, _) {
|
||||
var module = angular.module('kibana.panels.annotations', []);
|
||||
app.useModule(module);
|
||||
|
||||
module.controller('AnnotationsCtrl', function($scope, dashboard, $rootScope) {
|
||||
module.controller('AnnotationsCtrl', function($scope, datasourceSrv, $rootScope) {
|
||||
|
||||
$scope.panelMeta = {
|
||||
status : "Stable",
|
||||
@@ -26,37 +27,7 @@ function (angular, app, _) {
|
||||
annotations: []
|
||||
};
|
||||
|
||||
var annotationDefaults = {
|
||||
name: '',
|
||||
type: 'graphite metric',
|
||||
showLine: true,
|
||||
iconColor: '#C0C6BE',
|
||||
lineColor: 'rgba(255, 96, 96, 0.592157)',
|
||||
iconSize: 13,
|
||||
enable: true
|
||||
};
|
||||
|
||||
_.defaults($scope.panel,_d);
|
||||
|
||||
$scope.init = function() {
|
||||
$scope.currentAnnnotation = angular.copy(annotationDefaults);
|
||||
$scope.currentIsNew = true;
|
||||
};
|
||||
|
||||
$scope.edit = function(annotation) {
|
||||
$scope.currentAnnnotation = annotation;
|
||||
$scope.currentIsNew = false;
|
||||
};
|
||||
|
||||
$scope.update = function() {
|
||||
$scope.currentAnnnotation = angular.copy(annotationDefaults);
|
||||
$scope.currentIsNew = true;
|
||||
};
|
||||
|
||||
$scope.add = function() {
|
||||
$scope.panel.annotations.push($scope.currentAnnnotation);
|
||||
$scope.currentAnnnotation = angular.copy(annotationDefaults);
|
||||
};
|
||||
_.defaults($scope.panel, _d);
|
||||
|
||||
$scope.hide = function (annotation) {
|
||||
annotation.enable = !annotation.enable;
|
||||
@@ -64,4 +35,5 @@ function (angular, app, _) {
|
||||
};
|
||||
|
||||
});
|
||||
});
|
||||
|
||||
});
|
||||
|
||||
@@ -43,7 +43,7 @@ function (angular, app, _) {
|
||||
.then(function() {
|
||||
// only refresh in the outermost call
|
||||
if (!recursive) {
|
||||
$scope.dashboard.refresh();
|
||||
$scope.dashboard.emit_refresh();
|
||||
}
|
||||
});
|
||||
};
|
||||
|
||||
@@ -271,10 +271,10 @@ function (angular, app, $, _, kbn, moment, timeSeries) {
|
||||
targets: $scope.panel.targets,
|
||||
format: $scope.panel.renderer === 'png' ? 'png' : 'json',
|
||||
maxDataPoints: $scope.resolution,
|
||||
datasource: $scope.panel.datasource
|
||||
datasource: $scope.panel.datasource,
|
||||
};
|
||||
|
||||
$scope.annotationsPromise = annotationsSrv.getAnnotations($scope.filter, $scope.rangeUnparsed);
|
||||
$scope.annotationsPromise = annotationsSrv.getAnnotations($scope.filter, $scope.rangeUnparsed, $scope.dashboard);
|
||||
|
||||
return $scope.datasource.query($scope.filter, graphiteQuery)
|
||||
.then($scope.dataHandler)
|
||||
|
||||
@@ -25,7 +25,7 @@
|
||||
<span class="pointer" ng-show="panel.now">{{time.to.date | moment:'ago'}}</span>
|
||||
</span>
|
||||
<span ng-hide="filter.time">Time filter</span>
|
||||
<span ng-show="dashboard.current.refresh" class="text-warning">refreshed every {{dashboard.current.refresh}} </span>
|
||||
<span ng-show="dashboard.refresh" class="text-warning">refreshed every {{dashboard.refresh}} </span>
|
||||
<i class="icon-caret-down"></i>
|
||||
</a>
|
||||
|
||||
@@ -47,8 +47,8 @@
|
||||
</ul>
|
||||
|
||||
</li>
|
||||
<li ng-show="!dashboard.current.refresh" class="grafana-menu-refresh">
|
||||
<a class="icon-refresh" ng-click="dashboard.refresh()"></a>
|
||||
<li ng-show="!dashboard.refresh" class="grafana-menu-refresh">
|
||||
<a class="icon-refresh" ng-click="dashboard.emit_refresh()"></a>
|
||||
</li>
|
||||
</ul>
|
||||
|
||||
|
||||
@@ -16,7 +16,7 @@
|
||||
</a>
|
||||
</li>
|
||||
|
||||
<li ng-repeat="pulldown in dashboard.current.nav" ng-controller="PulldownCtrl" ng-show="pulldown.enable"><kibana-simple-panel type="pulldown.type" ng-cloak></kibana-simple-panel></li>
|
||||
<li ng-repeat="pulldown in dashboard.nav" ng-controller="PulldownCtrl" ng-show="pulldown.enable"><kibana-simple-panel type="pulldown.type" ng-cloak></kibana-simple-panel></li>
|
||||
|
||||
<li class="dropdown grafana-menu-save" ng-show="showDropdown('save')">
|
||||
<a href="#" bs-tooltip="'Save'" data-placement="bottom" class="dropdown-toggle" data-toggle="dropdown" ng-click="openSaveDropdown()">
|
||||
@@ -25,17 +25,17 @@
|
||||
|
||||
<ul class="save-dashboard-dropdown dropdown-menu">
|
||||
|
||||
<li ng-show="dashboard.current.loader.save_elasticsearch">
|
||||
<li ng-show="dashboard.loader.save_elasticsearch">
|
||||
<form class="input-prepend nomargin save-dashboard-dropdown-save-form">
|
||||
<input class='input-medium' ng-model="dashboard.current.title" type="text" ng-model="elasticsearch.title"/>
|
||||
<button class="btn" ng-click="elasticsearch_save('dashboard')"><i class="icon-save"></i></button>
|
||||
<input class='input-medium' ng-model="dashboard.title" type="text" ng-model="elasticsearch.title"/>
|
||||
<button class="btn" ng-click="saveDashboard()"><i class="icon-save"></i></button>
|
||||
</form>
|
||||
</li>
|
||||
|
||||
<li ng-show="dashboard.current.loader.save_default">
|
||||
<li ng-show="dashboard.loader.save_default">
|
||||
<a class="link" ng-click="set_default()">Save as Home</a>
|
||||
</li>
|
||||
<li ng-show="dashboard.current.loader.save_default">
|
||||
<li ng-show="dashboard.loader.save_default">
|
||||
<a class="link" ng-click="purge_default()">Reset Home</a>
|
||||
</li>
|
||||
<li ng-show="!isFavorite">
|
||||
@@ -44,12 +44,12 @@
|
||||
<li ng-show="isFavorite">
|
||||
<a class="link" ng-click="removeAsFavorite()">Remove as favorite</a>
|
||||
</li>
|
||||
<li ng-show="dashboard.current.loader.save_local">
|
||||
<a class="link" ng-click="dashboard.to_file()">Export dashboard</a>
|
||||
<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="elasticsearch_save('temp',dashboard.current.loader.save_temp_ttl)" 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</i></a></li>
|
||||
|
||||
<li ng-show="dashboard.current.loader.save_gist" style="margin:10px">
|
||||
<li ng-show="dashboard.loader.save_gist" style="margin:10px">
|
||||
<h6>Gist</h6>
|
||||
<form class="input-append">
|
||||
<input class='input-medium' placeholder='Title' type="text" ng-model="gist.title"/>
|
||||
@@ -65,10 +65,9 @@
|
||||
|
||||
</li>
|
||||
|
||||
<li class="grafana-menu-home"><a bs-tooltip="'Goto saved default'" data-placement="bottom" href='#/'><i class='icon-home'></i></a></li>
|
||||
|
||||
<li class="grafana-menu-home"><a bs-tooltip="'Goto saved default'" data-placement="bottom" href='#/dashboard'><i class='icon-home'></i></a></li>
|
||||
|
||||
<li class="grafana-menu-edit" ng-show="dashboard.current.editable" bs-tooltip="'Configure dashboard'" data-placement="bottom"><a class="link" config-modal="app/partials/dasheditor.html"><i class='icon-cog pointer'></i></a></li>
|
||||
<li class="grafana-menu-edit" ng-show="dashboard.editable" bs-tooltip="'Configure dashboard'" data-placement="bottom"><a class="link" config-modal="app/partials/dasheditor.html"><i class='icon-cog pointer'></i></a></li>
|
||||
|
||||
<li class="grafana-menu-stop-playlist hide">
|
||||
<a class='small' ng-click='stopPlaylist(2)'>
|
||||
|
||||
@@ -4,7 +4,7 @@
|
||||
</div>
|
||||
<div class="modal-body">
|
||||
<label>Share this dashboard with this URL</label>
|
||||
<input ng-model='share.link' type="text" style="width:90%" onclick="this.select()" onfocus="this.select()" ng-change="share = dashboard.share_link(share.title,share.type,share.id)">
|
||||
<input ng-model='share.url' type="text" style="width:90%" onclick="this.select()" onfocus="this.select()">
|
||||
</div>
|
||||
<div class="modal-footer">
|
||||
<button type="button" class="btn btn-success" ng-click="dismiss();$broadcast('render')">Close</button>
|
||||
|
||||
@@ -1,53 +1,65 @@
|
||||
<div class="submenu-controls">
|
||||
<div class="submenu-panel" ng-controller="SubmenuCtrl" ng-repeat="pulldown in dashboard.current.pulldowns | filter:{ enable: true }">
|
||||
<div class="submenu-panel-title">
|
||||
<span class="small"><strong>{{pulldown.type}}:</strong></span>
|
||||
</div>
|
||||
<div class="submenu-panel-wrapper">
|
||||
<kibana-simple-panel type="pulldown.type" ng-cloak></kibana-simple-panel>
|
||||
<div ng-controller="DashCtrl" body-class>
|
||||
|
||||
<div class="navbar navbar-static-top">
|
||||
<div class="navbar-inner">
|
||||
<div class="container-fluid">
|
||||
<span class="brand"><img src="img/small.png" bs-tooltip="'Grafana'" data-placement="bottom"> {{dashboard.title}}</span>
|
||||
<ul class="nav pull-right" ng-controller='dashLoader' ng-init="init()" ng-include="'app/partials/dashLoader.html'">
|
||||
</ul>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="clearfix"></div>
|
||||
</div>
|
||||
|
||||
<div class="clearfix"></div>
|
||||
<div class="container-fluid main">
|
||||
<div>
|
||||
<div class="grafana-container container">
|
||||
<!-- Rows -->
|
||||
<div class="kibana-row" ng-controller="RowCtrl" ng-repeat="(row_name, row) in dashboard.current.rows" ng-style="row_style(row)">
|
||||
<div class="row-control">
|
||||
<div class="grafana-row" style="padding:0px;margin:0px;position:relative;">
|
||||
<div class="row-close" ng-show="row.collapse" data-placement="bottom" >
|
||||
<span class="row-button bgWarning" config-modal="app/partials/roweditor.html" class="pointer">
|
||||
<i bs-tooltip="'Configure row'" data-placement="right" ng-show="row.editable" class="icon-cog pointer"></i>
|
||||
</span>
|
||||
<span class="row-button bgPrimary" ng-click="toggle_row(row)" ng-show="row.collapsable">
|
||||
<i bs-tooltip="'Expand row'" data-placement="right" class="icon-caret-left pointer" ></i>
|
||||
</span>
|
||||
<span class="row-button row-text" ng-click="toggle_row(row)" ng-class="{'pointer':row.collapsable}">{{row.title || 'Row '+$index}}</span>
|
||||
</div>
|
||||
<div class="row-open" ng-show="!row.collapse">
|
||||
<div ng-show="row.collapsable" class='row-tab bgPrimary' ng-click="toggle_row(row)">
|
||||
<span class="row-tab-button">
|
||||
<i class="icon-caret-right" ></i>
|
||||
<div class="submenu-controls">
|
||||
<div class="submenu-panel" ng-controller="SubmenuCtrl" ng-repeat="pulldown in dashboard.pulldowns | filter:{ enable: true }">
|
||||
<div class="submenu-panel-title">
|
||||
<span class="small"><strong>{{pulldown.type}}:</strong></span>
|
||||
</div>
|
||||
<div class="submenu-panel-wrapper">
|
||||
<kibana-simple-panel type="pulldown.type" ng-cloak></kibana-simple-panel>
|
||||
</div>
|
||||
</div>
|
||||
<div class="clearfix"></div>
|
||||
</div>
|
||||
|
||||
<div class="clearfix"></div>
|
||||
<div class="container-fluid main">
|
||||
<div>
|
||||
<div class="grafana-container container">
|
||||
<!-- Rows -->
|
||||
<div class="kibana-row" ng-controller="RowCtrl" ng-repeat="(row_name, row) in dashboard.rows" ng-style="row_style(row)">
|
||||
<div class="row-control">
|
||||
<div class="grafana-row" style="padding:0px;margin:0px;position:relative;">
|
||||
<div class="row-close" ng-show="row.collapse" data-placement="bottom" >
|
||||
<span class="row-button bgWarning" config-modal="app/partials/roweditor.html" class="pointer">
|
||||
<i bs-tooltip="'Configure row'" data-placement="right" ng-show="row.editable" class="icon-cog pointer"></i>
|
||||
</span>
|
||||
<span class="row-button bgPrimary" ng-click="toggle_row(row)" ng-show="row.collapsable">
|
||||
<i bs-tooltip="'Expand row'" data-placement="right" class="icon-caret-left pointer" ></i>
|
||||
</span>
|
||||
<span class="row-button row-text" ng-click="toggle_row(row)" ng-class="{'pointer':row.collapsable}">{{row.title || 'Row '+$index}}</span>
|
||||
</div>
|
||||
<div class='row-tab bgSuccess dropdown' ng-show="row.editable">
|
||||
<span class="row-tab-button dropdown-toggle" data-toggle="dropdown">
|
||||
<i class="icon-th-list"></i>
|
||||
</span>
|
||||
<ul class="dropdown-menu dropdown-menu-right" role="menu" aria-labelledby="drop1">
|
||||
<li class="dropdown-submenu">
|
||||
<div class="row-open" ng-show="!row.collapse">
|
||||
<div ng-show="row.collapsable" class='row-tab bgPrimary' ng-click="toggle_row(row)">
|
||||
<span class="row-tab-button">
|
||||
<i class="icon-caret-right"></i>
|
||||
</span>
|
||||
</div>
|
||||
<div class='row-tab bgSuccess dropdown' ng-show="row.editable">
|
||||
<span class="row-tab-button dropdown-toggle" data-toggle="dropdown">
|
||||
<i class="icon-th-list"></i>
|
||||
</span>
|
||||
<ul class="dropdown-menu dropdown-menu-right" role="menu" aria-labelledby="drop1">
|
||||
<li class="dropdown-submenu">
|
||||
<a href="javascript:void(0);">Add Panel</a>
|
||||
<ul class="dropdown-menu">
|
||||
<ul class="dropdown-menu">
|
||||
<li><a ng-click="add_panel_default('graph')">Graph</a></li>
|
||||
<li><a ng-click="add_panel_default('text')">Text</a></li>
|
||||
</ul>
|
||||
</li>
|
||||
<li class="dropdown-submenu">
|
||||
</ul>
|
||||
</li>
|
||||
<li class="dropdown-submenu">
|
||||
<a href="javascript:void(0);">Set height</a>
|
||||
<ul class="dropdown-menu">
|
||||
<ul class="dropdown-menu">
|
||||
<li><a ng-click="set_height('100px')">100 px</a></li>
|
||||
<li><a ng-click="set_height('150px')">150 px</a></li>
|
||||
<li><a ng-click="set_height('200px')">200 px</a></li>
|
||||
@@ -58,53 +70,54 @@
|
||||
<li><a ng-click="set_height('500px')">500 px</a></li>
|
||||
<li><a ng-click="set_height('600px')">600 px</a></li>
|
||||
<li><a ng-click="set_height('700px')">700 px</a></li>
|
||||
</ul>
|
||||
</li>
|
||||
<li class="dropdown-submenu">
|
||||
</ul>
|
||||
</li>
|
||||
<li class="dropdown-submenu">
|
||||
<a href="javascript:void(0);">Move</a>
|
||||
<ul class="dropdown-menu">
|
||||
<ul class="dropdown-menu">
|
||||
<li><a ng-click="move_row(-1)">Up</a></li>
|
||||
<li><a ng-click="move_row(1)">Down</a></li>
|
||||
</ul>
|
||||
</li>
|
||||
<li>
|
||||
<a config-modal="app/partials/roweditor.html">Row editor</a>
|
||||
</li>
|
||||
<li>
|
||||
<a ng-click="delete_row()">Delete row</a>
|
||||
</li>
|
||||
</ul>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div style="padding-top:0px" ng-if="!row.collapse">
|
||||
|
||||
<!-- Panels -->
|
||||
<div ng-repeat="(name, panel) in row.panels|filter:isPanel" ng-hide="panel.hide" class="panel nospace" ng-style="{'width':!panel.span?'100%':(panel.span/1.2)*10+'%'}" data-drop="true" ng-model="row.panels" data-jqyoui-options jqyoui-droppable="{index:$index,mutate:false,onDrop:'panelMoveDrop',onOver:'panelMoveOver(true)',onOut:'panelMoveOut'}" ng-class="{'dragInProgress':dashboard.panelDragging}">
|
||||
<!-- Content Panel -->
|
||||
<div style="position:relative">
|
||||
<kibana-panel type="panel.type" ng-cloak></kibana-panel>
|
||||
</ul>
|
||||
</li>
|
||||
<li>
|
||||
<a config-modal="app/partials/roweditor.html">Row editor</a>
|
||||
</li>
|
||||
<li>
|
||||
<a ng-click="delete_row()">Delete row</a>
|
||||
</li>
|
||||
</ul>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div ng-show="rowSpan(row) < 10 && dashboard.panelDragging" class="panel" style="margin:5px;width:30%;background:rgba(100,100,100,0.50)" ng-class="{'dragInProgress':dashboard.panelDragging}" ng-style="{height:row.height}" data-drop="true" ng-model="row.panels" data-jqyoui-options jqyoui-droppable="{index:row.panels.length,mutate:false,onDrop:'panelMoveDrop',onOver:'panelMoveOver',onOut:'panelMoveOut'}">
|
||||
</div>
|
||||
<div style="padding-top:0px" ng-if="!row.collapse">
|
||||
|
||||
<div class="clearfix"></div>
|
||||
<!-- Panels -->
|
||||
<div ng-repeat="(name, panel) in row.panels|filter:isPanel" ng-hide="panel.hide" class="panel nospace" ng-style="{'width':!panel.span?'100%':(panel.span/1.2)*10+'%'}" data-drop="true" ng-model="row.panels" data-jqyoui-options jqyoui-droppable="{index:$index,mutate:false,onDrop:'panelMoveDrop',onOver:'panelMoveOver(true)',onOut:'panelMoveOut'}" ng-class="{'dragInProgress':dashboard.panelDragging}">
|
||||
<!-- Content Panel -->
|
||||
<div style="position:relative">
|
||||
<kibana-panel type="panel.type" ng-cloak></kibana-panel>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div ng-show="rowSpan(row) < 10 && dashboard.panelDragging" class="panel" style="margin:5px;width:30%;background:rgba(100,100,100,0.50)" ng-class="{'dragInProgress':dashboard.panelDragging}" ng-style="{height:row.height}" data-drop="true" ng-model="row.panels" data-jqyoui-options jqyoui-droppable="{index:row.panels.length,mutate:false,onDrop:'panelMoveDrop',onOver:'panelMoveOver',onOut:'panelMoveOut'}">
|
||||
</div>
|
||||
|
||||
<div class="clearfix"></div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div ng-show='dashboard.current.editable && dashboard.current.panel_hints' class="row-fluid add-row-panel-hint">
|
||||
|
||||
<div ng-show='dashboard.editable' class="row-fluid add-row-panel-hint">
|
||||
<div class="span12" style="text-align:right;">
|
||||
<span style="margin-right: 10px;" ng-click="add_row_default()" class="pointer btn btn-info btn-mini">
|
||||
<span><i class="icon-plus-sign"></i> ADD A ROW</span>
|
||||
</span>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
</div>
|
||||
</div>
|
||||
|
||||
</div>
|
||||
@@ -4,7 +4,7 @@
|
||||
<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>
|
||||
<div ng-repeat="tab in dashboard.current.nav" data-title="{{tab.type}}">
|
||||
<div ng-repeat="tab in dashboard.nav" data-title="{{tab.type}}">
|
||||
</div>
|
||||
</div>
|
||||
|
||||
@@ -12,21 +12,18 @@
|
||||
<div class="editor-row">
|
||||
<div class="section">
|
||||
<div class="editor-option">
|
||||
<label class="small">Title</label><input type="text" class="input-large" ng-model='dashboard.current.title'></input>
|
||||
<label class="small">Title</label><input type="text" class="input-large" ng-model='dashboard.title'></input>
|
||||
</div>
|
||||
<div class="editor-option">
|
||||
<label class="small">Style</label><select class="input-small" ng-model="dashboard.current.style" ng-options="f for f in ['dark','light']"></select>
|
||||
<label class="small">Theme</label><select class="input-small" ng-model="dashboard.style" ng-options="f for f in ['dark','light']" ng-change="styleUpdated()"></select>
|
||||
</div>
|
||||
<div class="editor-option">
|
||||
<label class="small">Time correction</label>
|
||||
<select ng-model="dashboard.current.timezone" class='input-small' ng-options="f for f in ['browser','utc']"></select>
|
||||
<select ng-model="dashboard.timezone" class='input-small' ng-options="f for f in ['browser','utc']"></select>
|
||||
</div>
|
||||
<div class="editor-option">
|
||||
<label class="small"> Hints <tip>Show 'Add panel' hints in empty spaces</tip></label><input type="checkbox" ng-model="dashboard.current.panel_hints" ng-checked="dashboard.current.panel_hints" />
|
||||
</div>
|
||||
<div class="editor-option">
|
||||
<label class="small">Hide controls</label>
|
||||
<input type="checkbox" ng-model="dashboard.current.hideControls" ng-checked="dashboard.current.hideControls">
|
||||
<label class="small">Hide controls (CTRL+H)</label>
|
||||
<input type="checkbox" ng-model="dashboard.hideControls" ng-checked="dashboard.hideControls">
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
@@ -34,7 +31,7 @@
|
||||
<div class="section">
|
||||
<div class="editor-option">
|
||||
<label class="small">Tags</label>
|
||||
<bootstrap-tagsinput ng-model="dashboard.current.tags" tagclass="label label-tag" placeholder="add tags">
|
||||
<bootstrap-tagsinput ng-model="dashboard.tags" tagclass="label label-tag" placeholder="add tags">
|
||||
</bootstrap-tagsinput>
|
||||
<tip>Press enter to a add tag</tip>
|
||||
</div>
|
||||
@@ -53,10 +50,10 @@
|
||||
<th width="1%"></th>
|
||||
<th width="97%">Title</th>
|
||||
</thead>
|
||||
<tr ng-repeat="row in dashboard.current.rows">
|
||||
<td><i ng-click="_.move(dashboard.current.rows,$index,$index-1)" ng-hide="$first" class="pointer icon-arrow-up"></i></td>
|
||||
<td><i ng-click="_.move(dashboard.current.rows,$index,$index+1)" ng-hide="$last" class="pointer icon-arrow-down"></i></td>
|
||||
<td><i ng-click="dashboard.current.rows = _.without(dashboard.current.rows,row)" class="pointer icon-remove"></i></td>
|
||||
<tr ng-repeat="row in dashboard.rows">
|
||||
<td><i ng-click="_.move(dashboard.rows,$index,$index-1)" ng-hide="$first" class="pointer icon-arrow-up"></i></td>
|
||||
<td><i ng-click="_.move(dashboard.rows,$index,$index+1)" ng-hide="$last" class="pointer icon-arrow-down"></i></td>
|
||||
<td><i ng-click="dashboard.rows = _.without(dashboard.rows,row)" class="pointer icon-remove"></i></td>
|
||||
<td>{{row.title}}</td>
|
||||
</tr>
|
||||
</table>
|
||||
@@ -76,43 +73,31 @@
|
||||
<div class="section">
|
||||
<h5>Save to</h5>
|
||||
<div class="editor-option">
|
||||
<label class="small">Export</label><input type="checkbox" ng-model="dashboard.current.loader.save_local" ng-checked="dashboard.current.loader.save_local">
|
||||
<label class="small">Gist <tip>Requires your domain to be OAUTH registered with Github<tip></label><input type="checkbox" ng-model="dashboard.loader.save_gist" ng-checked="dashboard.loader.save_gist">
|
||||
</div>
|
||||
<div class="editor-option">
|
||||
<label class="small">Browser</label><input type="checkbox" ng-model="dashboard.current.loader.save_default" ng-checked="dashboard.current.loader.save_default">
|
||||
</div>
|
||||
<div class="editor-option">
|
||||
<label class="small">Gist <tip>Requires your domain to be OAUTH registered with Github</tip></label><input type="checkbox" ng-model="dashboard.current.loader.save_gist" ng-checked="dashboard.current.loader.save_gist">
|
||||
</div>
|
||||
<div class="editor-option">
|
||||
<label class="small">Elasticsearch</label><input type="checkbox" ng-model="dashboard.current.loader.save_elasticsearch" ng-checked="dashboard.current.loader.save_elasticsearch">
|
||||
<label class="small">Elasticsearch</label><input type="checkbox" ng-model="dashboard.loader.save_elasticsearch" ng-checked="dashboard.loader.save_elasticsearch">
|
||||
</div>
|
||||
</div>
|
||||
<div class="section">
|
||||
<h5>Load from</h5>
|
||||
<div class="editor-option">
|
||||
<label class="small">Local file</label><input type="checkbox" ng-model="dashboard.current.loader.load_local" ng-checked="dashboard.current.loader.load_local">
|
||||
<label class="small">Gist</label><input type="checkbox" ng-model="dashboard.loader.load_gist" ng-checked="dashboard.loader.load_gist">
|
||||
</div>
|
||||
<div class="editor-option">
|
||||
<label class="small">Gist</label><input type="checkbox" ng-model="dashboard.current.loader.load_gist" ng-checked="dashboard.current.loader.load_gist">
|
||||
</div>
|
||||
<div class="editor-option">
|
||||
<label class="small">Elasticsearch</label><input type="checkbox" ng-model="dashboard.current.loader.load_elasticsearch" ng-checked="dashboard.current.loader.load_elasticsearch">
|
||||
</div>
|
||||
<div class="editor-option" ng-show="dashboard.current.loader.load.elasticsearch">
|
||||
<label class="small">ES list size</label><input class="input-mini" type="number" ng-model="dashboard.current.loader.load_elasticsearch_size">
|
||||
<label class="small">Elasticsearch</label><input type="checkbox" ng-model="dashboard.loader.load_elasticsearch" ng-checked="dashboard.loader.load_elasticsearch">
|
||||
</div>
|
||||
</div>
|
||||
<div class="section">
|
||||
<h5>Sharing</h5>
|
||||
<div class="editor-option" >
|
||||
<label class="small">Allow Sharing <tip>Allow generating adhoc links to dashboards</tip></label><input type="checkbox" ng-model="dashboard.current.loader.save_temp" ng-checked="dashboard.current.loader.save_temp">
|
||||
<label class="small">Allow Sharing <tip>Allow generating adhoc links to dashboards</tip></label><input type="checkbox" ng-model="dashboard.loader.save_temp" ng-checked="dashboard.loader.save_temp">
|
||||
</div>
|
||||
<div class="editor-option" ng-show="dashboard.current.loader.save_temp">
|
||||
<label class="small">TTL <tip>Expire temp urls</tip></label><input type="checkbox" ng-model="dashboard.current.loader.save_temp_ttl_enable">
|
||||
<div class="editor-option" ng-show="dashboard.loader.save_temp">
|
||||
<label class="small">TTL <tip>Expire temp urls</tip></label><input type="checkbox" ng-model="dashboard.loader.save_temp_ttl_enable">
|
||||
</div>
|
||||
<div class="editor-option" ng-show="dashboard.current.loader.save_temp && dashboard.current.loader.save_temp_ttl_enable">
|
||||
<label class="small">TTL Duration <tip>Elasticsearch date math, eg: 1m,1d,1w,30d </tip></label><input class="input-small" type="text" ng-model="dashboard.current.loader.save_temp_ttl">
|
||||
<div class="editor-option" ng-show="dashboard.loader.save_temp && dashboard.loader.save_temp_ttl_enable">
|
||||
<label class="small">TTL Duration <tip>Elasticsearch date math, eg: 1m,1d,1w,30d </tip></label><input class="input-small" type="text" ng-model="dashboard.loader.save_temp_ttl">
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
@@ -122,10 +107,10 @@
|
||||
<div class="editor-row">
|
||||
<div class="section">
|
||||
<h5>Feature toggles</h5>
|
||||
<div class="editor-option" ng-repeat="pulldown in dashboard.current.pulldowns">
|
||||
<div class="editor-option" ng-repeat="pulldown in dashboard.pulldowns">
|
||||
<label class="small" style="text-transform:capitalize;">{{pulldown.type}}</label><input type="checkbox" ng-model="pulldown.enable" ng-checked="pulldown.enable">
|
||||
</div>
|
||||
<div class="editor-option" ng-repeat="pulldown in dashboard.current.nav">
|
||||
<div class="editor-option" ng-repeat="pulldown in dashboard.nav">
|
||||
<label class="small" style="text-transform:capitalize;">{{pulldown.type}}</label><input type="checkbox" ng-model="pulldown.enable" ng-checked="pulldown.enable">
|
||||
</div>
|
||||
</div>
|
||||
@@ -140,7 +125,7 @@
|
||||
<ng-include src="'app/partials/import.html'"></ng-include>
|
||||
</div>
|
||||
|
||||
<div ng-repeat="pulldown in dashboard.current.nav" ng-controller="SubmenuCtrl" ng-show="editor.index == 5+$index">
|
||||
<div ng-repeat="pulldown in dashboard.nav" ng-controller="SubmenuCtrl" ng-show="editor.index == 5+$index">
|
||||
<ng-include ng-show="pulldown.enable" src="edit_path(pulldown.type)"></ng-include>
|
||||
<button ng-hide="pulldown.enable" class="btn" ng-click="pulldown.enable = true">Enable the {{pulldown.type}}</button>
|
||||
</div>
|
||||
@@ -156,6 +141,6 @@
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<button ng-click="add_row(dashboard.current,row); reset_row();" class="btn btn-success" ng-show="editor.index == 1">Create Row</button>
|
||||
<button type="button" class="btn btn-info" ng-click="editor.index=0;dismiss();reset_panel();dashboard.refresh()">Close</button>
|
||||
<button ng-click="add_row(dashboard,row); reset_row();" class="btn btn-success" ng-show="editor.index == 1">Create Row</button>
|
||||
<button type="button" class="btn btn-info" ng-click="editor.index=0;dismiss();reset_panel();dashboard.emit_refresh()">Close</button>
|
||||
</div>
|
||||
15
src/app/partials/graphite/annotation_editor.html
Normal file
15
src/app/partials/graphite/annotation_editor.html
Normal file
@@ -0,0 +1,15 @@
|
||||
<div class="editor-row">
|
||||
<div class="editor-option">
|
||||
<label class="small">Graphite target expression</label>
|
||||
<input type="text" class="span10" ng-model='currentAnnotation.target' placeholder=""></input>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="editor-row">
|
||||
<div class="editor-option">
|
||||
<label class="small">Graphite event tags</label>
|
||||
<input type="text" ng-model='currentAnnotation.tags' placeholder=""></input>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
|
||||
29
src/app/partials/influxdb/annotation_editor.html
Normal file
29
src/app/partials/influxdb/annotation_editor.html
Normal file
@@ -0,0 +1,29 @@
|
||||
<div class="editor-row">
|
||||
<div class="section">
|
||||
<h5>InfluxDB Query <tip>Example: select text from events where [[timeFilter]]</tip></h5>
|
||||
<div class="editor-option">
|
||||
<input type="text" class="span10" ng-model='currentAnnotation.query' placeholder="select text from events where [[timeFilter]]"></input>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="editor-row">
|
||||
<div class="section">
|
||||
<h5>Column mappings <tip>If your influxdb query returns more than one column you need to specify the column names bellow. An annotation event is composed of a title, tags, and an additional text field.</tip></h5>
|
||||
<div class="editor-option">
|
||||
<label class="small">Title</label>
|
||||
<input type="text" class="input-small" ng-model='currentAnnotation.titleColumn' placeholder=""></input>
|
||||
</div>
|
||||
|
||||
<div class="editor-option">
|
||||
<label class="small">Tags</label>
|
||||
<input type="text" class="input-small" ng-model='currentAnnotation.tagsColumn' placeholder=""></input>
|
||||
</div>
|
||||
|
||||
<div class="editor-option">
|
||||
<label class="small">Text</label>
|
||||
<input type="text" class="input-small" ng-model='currentAnnotation.textColumn' placeholder=""></input>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
@@ -48,7 +48,7 @@
|
||||
<div class="row-fluid" ng-if="editor.index == 2">
|
||||
<h4>Select Panel Type</h4>
|
||||
<form class="form-inline">
|
||||
<select class="input-medium" ng-model="panel.type" ng-options="panelType for panelType in dashboard.availablePanels|stringSort"></select>
|
||||
<select class="input-medium" ng-model="panel.type" ng-options="panelType for panelType in availablePanels|stringSort"></select>
|
||||
<small ng-show="rowSpan(row) > 11">
|
||||
Note: This row is full, new panels will wrap to a new line. You should add another row.
|
||||
</small>
|
||||
|
||||
@@ -77,7 +77,7 @@
|
||||
<tr bindonce
|
||||
ng-repeat="row in results.dashboards"
|
||||
ng-class="{'selected': $index === selectedIndex }">
|
||||
<td><a confirm-click="elasticsearch_delete(row._id)" confirmation="Are you sure you want to delete the {{row._id}} dashboard"><i class="icon-remove"></i></a></td>
|
||||
<td><a confirm-click="deleteDashboard(row._id)" confirmation="Are you sure you want to delete the {{row._id}} dashboard"><i class="icon-remove"></i></a></td>
|
||||
<td style="width:100%">
|
||||
<a href="#/dashboard/elasticsearch/{{row._id}}" bo-text="row._id"></a>
|
||||
</td>
|
||||
@@ -86,13 +86,13 @@
|
||||
{{tag}}
|
||||
</a>
|
||||
</td>
|
||||
<td><a><i class="icon-share" ng-click="share = dashboard.share_link(row._id,'elasticsearch',row._id)" config-modal="app/partials/dashLoaderShare.html"></i></a></td>
|
||||
<td><a><i class="icon-share" ng-click="shareDashboard(row._id, row._id)" config-modal="app/partials/dashLoaderShare.html"></i></a></td>
|
||||
</tr>
|
||||
</table>
|
||||
</div>
|
||||
</li>
|
||||
|
||||
<!-- ng-show="dashboard.current.loader.load_gist || dashboard.current.loader.load_local" -->
|
||||
<!-- ng-show="dashboard.loader.load_gist || dashboard.loader.load_local" -->
|
||||
<li ng-if="showImport" style="margin: 20px;">
|
||||
<div class="editor-row">
|
||||
<div class="section">
|
||||
@@ -104,20 +104,5 @@
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="editor-row" ng-show='dashboard.current.loader.load_gist'>
|
||||
<h5>Gist <tip>Enter a gist number or url</tip></h5>
|
||||
<form>
|
||||
<input type="text" ng-model="gist.url" placeholder="Gist number or URL"><br>
|
||||
<button class="btn" ng-click="gist_dblist(dashboard.gist_id(gist.url))" ng-show="dashboard.is_gist(gist.url)"><i class="icon-github-alt"></i> Get gist:{{gist.url | gistid}}</button>
|
||||
<h6 ng-show="gist.files.length">Dashboards in gist:{{gist.url | gistid}} <small>click to load</small></h6>
|
||||
<h6 ng-hide="gist.files.length || !gist.url.length">No gist dashboards found</h6>
|
||||
<table class="table table-condensed table-striped">
|
||||
<tr ng-repeat="file in gist.files">
|
||||
<td><a ng-click="dashboard.dash_load(file)">{{file.title}}</a></td>
|
||||
</tr>
|
||||
</table>
|
||||
</form>
|
||||
</div>
|
||||
</li>
|
||||
</ul>
|
||||
</ul>
|
||||
|
||||
7
src/app/routes/all.js
Normal file
7
src/app/routes/all.js
Normal file
@@ -0,0 +1,7 @@
|
||||
define([
|
||||
'./dashboard-from-es',
|
||||
'./dashboard-from-file',
|
||||
'./dashboard-from-script',
|
||||
'./dashboard-default',
|
||||
],
|
||||
function () {});
|
||||
24
src/app/routes/dashboard-default.js
Normal file
24
src/app/routes/dashboard-default.js
Normal file
@@ -0,0 +1,24 @@
|
||||
define([
|
||||
'angular',
|
||||
'config'
|
||||
],
|
||||
function (angular, config) {
|
||||
"use strict";
|
||||
|
||||
var module = angular.module('kibana.routes');
|
||||
|
||||
module.config(function($routeProvider) {
|
||||
$routeProvider
|
||||
.when('/', {
|
||||
redirectTo: function() {
|
||||
if (window.localStorage && window.localStorage.grafanaDashboardDefault) {
|
||||
return window.localStorage.grafanaDashboardDefault;
|
||||
}
|
||||
else {
|
||||
return config.default_route;
|
||||
}
|
||||
}
|
||||
});
|
||||
});
|
||||
|
||||
});
|
||||
57
src/app/routes/dashboard-from-es.js
Normal file
57
src/app/routes/dashboard-from-es.js
Normal file
@@ -0,0 +1,57 @@
|
||||
define([
|
||||
'angular',
|
||||
'jquery',
|
||||
'config'
|
||||
],
|
||||
function (angular, $, config) {
|
||||
"use strict";
|
||||
|
||||
var module = angular.module('kibana.routes');
|
||||
|
||||
module.config(function($routeProvider) {
|
||||
$routeProvider
|
||||
.when('/dashboard/elasticsearch/:id', {
|
||||
templateUrl: 'app/partials/dashboard.html',
|
||||
controller : 'DashFromElasticProvider',
|
||||
})
|
||||
.when('/dashboard/temp/:id', {
|
||||
templateUrl: 'app/partials/dashboard.html',
|
||||
controller : 'DashFromElasticProvider',
|
||||
});
|
||||
});
|
||||
|
||||
module.controller('DashFromElasticProvider', function($scope, $rootScope, elastic, $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);
|
||||
});
|
||||
|
||||
});
|
||||
|
||||
});
|
||||
59
src/app/routes/dashboard-from-file.js
Normal file
59
src/app/routes/dashboard-from-file.js
Normal file
@@ -0,0 +1,59 @@
|
||||
define([
|
||||
'angular',
|
||||
'jquery',
|
||||
'config',
|
||||
'underscore'
|
||||
],
|
||||
function (angular, $, config, _) {
|
||||
"use strict";
|
||||
|
||||
var module = angular.module('kibana.routes');
|
||||
|
||||
module.config(function($routeProvider) {
|
||||
$routeProvider
|
||||
.when('/dashboard/file/:jsonFile', {
|
||||
templateUrl: 'app/partials/dashboard.html',
|
||||
controller : 'DashFromFileProvider',
|
||||
});
|
||||
});
|
||||
|
||||
module.controller('DashFromFileProvider', function($scope, $rootScope, $http, $routeParams, alertSrv) {
|
||||
|
||||
var renderTemplate = function(json,params) {
|
||||
var _r;
|
||||
_.templateSettings = {interpolate : /\{\{(.+?)\}\}/g};
|
||||
var template = _.template(json);
|
||||
var rendered = template({ARGS:params});
|
||||
try {
|
||||
_r = angular.fromJson(rendered);
|
||||
} catch(e) {
|
||||
_r = false;
|
||||
}
|
||||
return _r;
|
||||
};
|
||||
|
||||
var file_load = function(file) {
|
||||
return $http({
|
||||
url: "app/dashboards/"+file.replace(/\.(?!json)/,"/")+'?' + new Date().getTime(),
|
||||
method: "GET",
|
||||
transformResponse: function(response) {
|
||||
return renderTemplate(response,$routeParams);
|
||||
}
|
||||
}).then(function(result) {
|
||||
if(!result) {
|
||||
return false;
|
||||
}
|
||||
return result.data;
|
||||
},function() {
|
||||
alertSrv.set('Error',"Could not load <i>dashboards/"+file+"</i>. Please make sure it exists" ,'error');
|
||||
return false;
|
||||
});
|
||||
};
|
||||
|
||||
file_load($routeParams.jsonFile).then(function(result) {
|
||||
$scope.emitAppEvent('setup-dashboard', result);
|
||||
});
|
||||
|
||||
});
|
||||
|
||||
});
|
||||
61
src/app/routes/dashboard-from-script.js
Normal file
61
src/app/routes/dashboard-from-script.js
Normal file
@@ -0,0 +1,61 @@
|
||||
define([
|
||||
'angular',
|
||||
'jquery',
|
||||
'config',
|
||||
'underscore',
|
||||
'kbn',
|
||||
'moment'
|
||||
],
|
||||
function (angular, $, config, _, kbn, moment) {
|
||||
"use strict";
|
||||
|
||||
var module = angular.module('kibana.routes');
|
||||
|
||||
module.config(function($routeProvider) {
|
||||
$routeProvider
|
||||
.when('/dashboard/script/:jsFile', {
|
||||
templateUrl: 'app/partials/dashboard.html',
|
||||
controller : 'DashFromScriptProvider',
|
||||
});
|
||||
});
|
||||
|
||||
module.controller('DashFromScriptProvider', function($scope, $rootScope, $http, $routeParams, alertSrv, $q) {
|
||||
|
||||
var execute_script = function(result) {
|
||||
/*jshint -W054 */
|
||||
var script_func = new Function('ARGS','kbn','_','moment','window','document','$','jQuery', result.data);
|
||||
var script_result = script_func($routeParams, kbn, _ , moment, window, document, $, $);
|
||||
|
||||
// Handle async dashboard scripts
|
||||
if (_.isFunction(script_result)) {
|
||||
var deferred = $q.defer();
|
||||
script_result(function(dashboard) {
|
||||
$rootScope.$apply(function() {
|
||||
deferred.resolve({ data: dashboard });
|
||||
});
|
||||
});
|
||||
return deferred.promise;
|
||||
}
|
||||
|
||||
return { data: script_result };
|
||||
};
|
||||
|
||||
var script_load = function(file) {
|
||||
var url = 'app/dashboards/'+file.replace(/\.(?!js)/,"/") + '?' + new Date().getTime();
|
||||
|
||||
return $http({ url: url, method: "GET" })
|
||||
.then(execute_script)
|
||||
.then(null,function(err) {
|
||||
console.log('Script dashboard error '+ err);
|
||||
alertSrv.set('Error', "Could not load <i>scripts/"+file+"</i>. Please make sure it exists and returns a valid dashboard", 'error');
|
||||
return false;
|
||||
});
|
||||
};
|
||||
|
||||
script_load($routeParams.jsFile).then(function(result) {
|
||||
$scope.emitAppEvent('setup-dashboard', result.data);
|
||||
});
|
||||
|
||||
});
|
||||
|
||||
});
|
||||
@@ -1,6 +1,5 @@
|
||||
define([
|
||||
'./alertSrv',
|
||||
'./dashboard',
|
||||
'./datasourceSrv',
|
||||
'./filterSrv',
|
||||
'./timer',
|
||||
@@ -9,5 +8,8 @@ define([
|
||||
'./annotationsSrv',
|
||||
'./playlistSrv',
|
||||
'./unsavedChangesSrv',
|
||||
'./elasticsearch/es-client',
|
||||
'./dashboard/dashboardKeyBindings',
|
||||
'./dashboard/dashboardModel',
|
||||
],
|
||||
function () {});
|
||||
@@ -7,20 +7,14 @@ define([
|
||||
|
||||
var module = angular.module('kibana.services');
|
||||
|
||||
module.service('annotationsSrv', function(dashboard, datasourceSrv, $q, alertSrv, $rootScope) {
|
||||
module.service('annotationsSrv', function(datasourceSrv, $q, alertSrv, $rootScope) {
|
||||
var promiseCached;
|
||||
var annotationPanel;
|
||||
var list = [];
|
||||
var timezone;
|
||||
|
||||
this.init = function() {
|
||||
$rootScope.$on('refresh', this.clearCache);
|
||||
$rootScope.$on('dashboard-loaded', this.dashboardLoaded);
|
||||
|
||||
this.dashboardLoaded();
|
||||
};
|
||||
|
||||
this.dashboardLoaded = function () {
|
||||
annotationPanel = _.findWhere(dashboard.current.pulldowns, { type: 'annotations' });
|
||||
};
|
||||
|
||||
this.clearCache = function() {
|
||||
@@ -28,7 +22,8 @@ define([
|
||||
list = [];
|
||||
};
|
||||
|
||||
this.getAnnotations = function(filterSrv, rangeUnparsed) {
|
||||
this.getAnnotations = function(filterSrv, rangeUnparsed, dashboard) {
|
||||
annotationPanel = _.findWhere(dashboard.pulldowns, { type: 'annotations' });
|
||||
if (!annotationPanel.enable) {
|
||||
return $q.when(null);
|
||||
}
|
||||
@@ -37,10 +32,17 @@ define([
|
||||
return promiseCached;
|
||||
}
|
||||
|
||||
var graphiteMetrics = this.getGraphiteMetrics(filterSrv, rangeUnparsed);
|
||||
var graphiteEvents = this.getGraphiteEvents(rangeUnparsed);
|
||||
timezone = dashboard.timezone;
|
||||
var annotations = _.where(annotationPanel.annotations, { enable: true });
|
||||
|
||||
promiseCached = $q.all(graphiteMetrics.concat(graphiteEvents))
|
||||
var promises = _.map(annotations, function(annotation) {
|
||||
var datasource = datasourceSrv.get(annotation.datasource);
|
||||
return datasource.annotationQuery(annotation, filterSrv, rangeUnparsed)
|
||||
.then(this.receiveAnnotationResults)
|
||||
.then(null, errorHandler);
|
||||
}, this);
|
||||
|
||||
promiseCached = $q.all(promises)
|
||||
.then(function() {
|
||||
return list;
|
||||
});
|
||||
@@ -48,61 +50,10 @@ define([
|
||||
return promiseCached;
|
||||
};
|
||||
|
||||
this.getGraphiteEvents = function(rangeUnparsed) {
|
||||
var annotations = this.getAnnotationsByType('graphite events');
|
||||
if (annotations.length === 0) {
|
||||
return [];
|
||||
this.receiveAnnotationResults = function(results) {
|
||||
for (var i = 0; i < results.length; i++) {
|
||||
addAnnotation(results[i]);
|
||||
}
|
||||
|
||||
var promises = _.map(annotations, function(annotation) {
|
||||
|
||||
return datasourceSrv.default.events({ range: rangeUnparsed, tags: annotation.tags })
|
||||
.then(function(results) {
|
||||
_.each(results.data, function (event) {
|
||||
addAnnotation({
|
||||
annotation: annotation,
|
||||
time: event.when * 1000,
|
||||
description: event.what,
|
||||
tags: event.tags,
|
||||
data: event.data
|
||||
});
|
||||
});
|
||||
})
|
||||
.then(null, errorHandler);
|
||||
});
|
||||
|
||||
return promises;
|
||||
};
|
||||
|
||||
this.getAnnotationsByType = function(type) {
|
||||
return _.where(annotationPanel.annotations, {
|
||||
type: type,
|
||||
enable: true
|
||||
});
|
||||
};
|
||||
|
||||
this.getGraphiteMetrics = function(filterSrv, rangeUnparsed) {
|
||||
var annotations = this.getAnnotationsByType('graphite metric');
|
||||
if (annotations.length === 0) {
|
||||
return [];
|
||||
}
|
||||
|
||||
var promises = _.map(annotations, function(annotation) {
|
||||
var graphiteQuery = {
|
||||
range: rangeUnparsed,
|
||||
targets: [{ target: annotation.target }],
|
||||
format: 'json',
|
||||
maxDataPoints: 100
|
||||
};
|
||||
|
||||
var receiveFunc = _.partial(receiveGraphiteMetrics, annotation);
|
||||
|
||||
return datasourceSrv.default.query(filterSrv, graphiteQuery)
|
||||
.then(receiveFunc)
|
||||
.then(null, errorHandler);
|
||||
});
|
||||
|
||||
return promises;
|
||||
};
|
||||
|
||||
function errorHandler(err) {
|
||||
@@ -110,33 +61,23 @@ define([
|
||||
alertSrv.set('Annotations','Could not fetch annotations','error');
|
||||
}
|
||||
|
||||
function receiveGraphiteMetrics(annotation, results) {
|
||||
for (var i = 0; i < results.data.length; i++) {
|
||||
var target = results.data[i];
|
||||
|
||||
for (var y = 0; y < target.datapoints.length; y++) {
|
||||
var datapoint = target.datapoints[y];
|
||||
|
||||
if (datapoint[0]) {
|
||||
addAnnotation({
|
||||
annotation: annotation,
|
||||
time: datapoint[1] * 1000,
|
||||
description: target.target
|
||||
});
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
function addAnnotation(options) {
|
||||
var tooltip = "<small><b>" + options.description + "</b><br/>";
|
||||
var tooltip = "<small><b>" + options.title + "</b><br/>";
|
||||
if (options.tags) {
|
||||
tooltip += (options.tags || '') + '<br/>';
|
||||
}
|
||||
tooltip += '<i>' + moment(options.time).format('YYYY-MM-DD HH:mm:ss') + '</i><br/>';
|
||||
if (options.data) {
|
||||
tooltip += options.data.replace(/\n/g, '<br/>');
|
||||
|
||||
if (timezone === 'browser') {
|
||||
tooltip += '<i>' + moment(options.time).format('YYYY-MM-DD HH:mm:ss') + '</i><br/>';
|
||||
}
|
||||
else {
|
||||
tooltip += '<i>' + moment.utc(options.time).format('YYYY-MM-DD HH:mm:ss') + '</i><br/>';
|
||||
}
|
||||
|
||||
if (options.text) {
|
||||
tooltip += options.text.replace(/\n/g, '<br/>');
|
||||
}
|
||||
|
||||
tooltip += "</small>";
|
||||
|
||||
list.push({
|
||||
|
||||
@@ -1,472 +0,0 @@
|
||||
define([
|
||||
'angular',
|
||||
'jquery',
|
||||
'kbn',
|
||||
'underscore',
|
||||
'config',
|
||||
'moment',
|
||||
'modernizr',
|
||||
'filesaver'
|
||||
],
|
||||
function (angular, $, kbn, _, config, moment, Modernizr) {
|
||||
'use strict';
|
||||
|
||||
var module = angular.module('kibana.services');
|
||||
|
||||
module.service('dashboard', function(
|
||||
$routeParams, $http, $rootScope, $injector, $location, $timeout,
|
||||
ejsResource, timer, alertSrv, $q
|
||||
) {
|
||||
// A hash of defaults to use when loading a dashboard
|
||||
|
||||
var _dash = {
|
||||
title: "",
|
||||
tags: [],
|
||||
style: "dark",
|
||||
timezone: 'browser',
|
||||
editable: true,
|
||||
failover: false,
|
||||
panel_hints: true,
|
||||
rows: [],
|
||||
pulldowns: [{ type: 'templating' }, { type: 'annotations' }],
|
||||
nav: [{ type: 'timepicker' }],
|
||||
services: {},
|
||||
loader: {
|
||||
save_gist: false,
|
||||
save_elasticsearch: true,
|
||||
save_local: true,
|
||||
save_default: true,
|
||||
save_temp: true,
|
||||
save_temp_ttl_enable: true,
|
||||
save_temp_ttl: '30d',
|
||||
load_gist: false,
|
||||
load_elasticsearch: true,
|
||||
load_elasticsearch_size: 20,
|
||||
load_local: false,
|
||||
hide: false
|
||||
},
|
||||
refresh: false
|
||||
};
|
||||
|
||||
// An elasticJS client to use
|
||||
var ejs = ejsResource(config.elasticsearch, config.elasticsearchBasicAuth);
|
||||
var gist_pattern = /(^\d{5,}$)|(^[a-z0-9]{10,}$)|(gist.github.com(\/*.*)\/[a-z0-9]{5,}\/*$)/;
|
||||
|
||||
// Store a reference to this
|
||||
var self = this;
|
||||
|
||||
this.current = _.clone(_dash);
|
||||
this.last = {};
|
||||
this.availablePanels = [];
|
||||
|
||||
$rootScope.$on('$routeChangeSuccess',function() {
|
||||
// Clear the current dashboard to prevent reloading
|
||||
self.current = {};
|
||||
self.indices = [];
|
||||
route();
|
||||
});
|
||||
|
||||
var route = function() {
|
||||
// Is there a dashboard type and id in the URL?
|
||||
if(!(_.isUndefined($routeParams.kbnType)) && !(_.isUndefined($routeParams.kbnId))) {
|
||||
var _type = $routeParams.kbnType;
|
||||
var _id = $routeParams.kbnId;
|
||||
|
||||
switch(_type) {
|
||||
case ('elasticsearch'):
|
||||
self.elasticsearch_load('dashboard',_id);
|
||||
break;
|
||||
case ('temp'):
|
||||
self.elasticsearch_load('temp',_id);
|
||||
break;
|
||||
case ('file'):
|
||||
self.file_load(_id);
|
||||
break;
|
||||
case('script'):
|
||||
self.script_load(_id);
|
||||
break;
|
||||
case('local'):
|
||||
self.local_load();
|
||||
break;
|
||||
default:
|
||||
$location.path(config.default_route);
|
||||
}
|
||||
// No dashboard in the URL
|
||||
} else {
|
||||
// Check if browser supports localstorage, and if there's an old dashboard. If there is,
|
||||
// inform the user that they should save their dashboard to Elasticsearch and then set that
|
||||
// as their default
|
||||
if (Modernizr.localstorage) {
|
||||
if(!(_.isUndefined(window.localStorage['dashboard'])) && window.localStorage['dashboard'] !== '') {
|
||||
$location.path(config.default_route);
|
||||
alertSrv.set('Saving to browser storage has been replaced',' with saving to Elasticsearch.'+
|
||||
' Click <a href="#/dashboard/local/deprecated">here</a> to load your old dashboard anyway.');
|
||||
} else if(!(_.isUndefined(window.localStorage.grafanaDashboardDefault))) {
|
||||
$location.path(window.localStorage.grafanaDashboardDefault);
|
||||
} else {
|
||||
$location.path(config.default_route);
|
||||
}
|
||||
// No? Ok, grab the default route, its all we have now
|
||||
} else {
|
||||
$location.path(config.default_route);
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
this.refresh = function() {
|
||||
$rootScope.$broadcast('refresh');
|
||||
};
|
||||
|
||||
var dash_defaults = function(dashboard) {
|
||||
|
||||
_.defaults(dashboard, _dash);
|
||||
_.defaults(dashboard.loader,_dash.loader);
|
||||
|
||||
var filtering = _.findWhere(dashboard.pulldowns, {type: 'filtering'});
|
||||
if (!filtering) {
|
||||
dashboard.pulldowns.push({
|
||||
type: 'filtering',
|
||||
enable: false
|
||||
});
|
||||
}
|
||||
|
||||
var annotations = _.findWhere(dashboard.pulldowns, {type: 'annotations'});
|
||||
if (!annotations) {
|
||||
dashboard.pulldowns.push({
|
||||
type: 'annotations',
|
||||
enable: false
|
||||
});
|
||||
}
|
||||
|
||||
_.each(dashboard.rows, function(row) {
|
||||
_.each(row.panels, function(panel) {
|
||||
if (panel.type === 'graphite') {
|
||||
panel.type = 'graph';
|
||||
}
|
||||
});
|
||||
});
|
||||
|
||||
return dashboard;
|
||||
};
|
||||
|
||||
this.dash_load = function(dashboard) {
|
||||
// Cancel all timers
|
||||
timer.cancel_all();
|
||||
|
||||
// reset fullscreen flag
|
||||
$rootScope.fullscreen = false;
|
||||
|
||||
// Make sure the dashboard being loaded has everything required
|
||||
dashboard = dash_defaults(dashboard);
|
||||
|
||||
window.document.title = 'Grafana - ' + dashboard.title;
|
||||
|
||||
// Set the current dashboard
|
||||
self.current = angular.copy(dashboard);
|
||||
if(dashboard.refresh) {
|
||||
self.set_interval(dashboard.refresh);
|
||||
}
|
||||
|
||||
self.availablePanels = config.panels;
|
||||
|
||||
$rootScope.$emit('dashboard-loaded', self.current);
|
||||
|
||||
return true;
|
||||
};
|
||||
|
||||
this.gist_id = function(string) {
|
||||
if(self.is_gist(string)) {
|
||||
return string.match(gist_pattern)[0].replace(/.*\//, '');
|
||||
}
|
||||
};
|
||||
|
||||
this.is_gist = function(string) {
|
||||
if(!_.isUndefined(string) && string !== '' && !_.isNull(string.match(gist_pattern))) {
|
||||
return string.match(gist_pattern).length > 0 ? true : false;
|
||||
} else {
|
||||
return false;
|
||||
}
|
||||
};
|
||||
|
||||
this.to_file = function() {
|
||||
var blob = new Blob([angular.toJson(self.current,true)], {type: "application/json;charset=utf-8"});
|
||||
// from filesaver.js
|
||||
window.saveAs(blob, self.current.title+"-"+new Date().getTime());
|
||||
return true;
|
||||
};
|
||||
|
||||
this.set_default = function(route) {
|
||||
if (Modernizr.localstorage) {
|
||||
// Purge any old dashboards
|
||||
if(!_.isUndefined(window.localStorage['dashboard'])) {
|
||||
delete window.localStorage['dashboard'];
|
||||
}
|
||||
window.localStorage.grafanaDashboardDefault = route;
|
||||
return true;
|
||||
} else {
|
||||
return false;
|
||||
}
|
||||
};
|
||||
|
||||
this.purge_default = function() {
|
||||
if (Modernizr.localstorage) {
|
||||
// Purge any old dashboards
|
||||
if(!_.isUndefined(window.localStorage['dashboard'])) {
|
||||
|
||||
delete window.localStorage['dashboard'];
|
||||
}
|
||||
delete window.localStorage.grafanaDashboardDefault;
|
||||
return true;
|
||||
} else {
|
||||
return false;
|
||||
}
|
||||
};
|
||||
|
||||
// TOFIX: Pretty sure this breaks when you're on a saved dashboard already
|
||||
this.share_link = function(title,type,id) {
|
||||
return {
|
||||
location : window.location.href.replace(window.location.hash,""),
|
||||
type : type,
|
||||
id : id,
|
||||
link : window.location.href.replace(window.location.hash,"")+"#dashboard/"+type+"/"+id,
|
||||
title : title
|
||||
};
|
||||
};
|
||||
|
||||
var renderTemplate = function(json,params) {
|
||||
var _r;
|
||||
_.templateSettings = {interpolate : /\{\{(.+?)\}\}/g};
|
||||
var template = _.template(json);
|
||||
var rendered = template({ARGS:params});
|
||||
try {
|
||||
_r = angular.fromJson(rendered);
|
||||
} catch(e) {
|
||||
_r = false;
|
||||
}
|
||||
return _r;
|
||||
};
|
||||
|
||||
this.local_load = function() {
|
||||
var dashboard = JSON.parse(window.localStorage['dashboard']);
|
||||
dashboard.rows.unshift({
|
||||
height: "30",
|
||||
title: "Deprecation Notice",
|
||||
panels: [
|
||||
{
|
||||
title: 'WARNING: Legacy dashboard',
|
||||
type: 'text',
|
||||
span: 12,
|
||||
mode: 'html',
|
||||
content: 'This dashboard has been loaded from the browsers local cache. If you use '+
|
||||
'another brower or computer you will not be able to access it! '+
|
||||
'\n\n <h4>Good news!</h4> Kibana'+
|
||||
' now stores saved dashboards in Elasticsearch. Click the <i class="icon-save"></i> '+
|
||||
'button in the top left to save this dashboard. Then select "Set as Home" from'+
|
||||
' the "advanced" sub menu to automatically use the stored dashboard as your Kibana '+
|
||||
'landing page afterwards'+
|
||||
'<br><br><strong>Tip:</strong> You may with to remove this row before saving!'
|
||||
}
|
||||
]
|
||||
});
|
||||
self.dash_load(dashboard);
|
||||
};
|
||||
|
||||
this.file_load = function(file) {
|
||||
return $http({
|
||||
url: "app/dashboards/"+file.replace(/\.(?!json)/,"/")+'?' + new Date().getTime(),
|
||||
method: "GET",
|
||||
transformResponse: function(response) {
|
||||
return renderTemplate(response,$routeParams);
|
||||
}
|
||||
}).then(function(result) {
|
||||
if(!result) {
|
||||
return false;
|
||||
}
|
||||
self.dash_load(dash_defaults(result.data));
|
||||
return true;
|
||||
},function() {
|
||||
alertSrv.set('Error',"Could not load <i>dashboards/"+file+"</i>. Please make sure it exists" ,'error');
|
||||
return false;
|
||||
});
|
||||
};
|
||||
|
||||
this.elasticsearch_load = function(type,id) {
|
||||
var options = {
|
||||
url: config.elasticsearch + "/" + config.grafana_index + "/"+type+"/"+id+'?' + new Date().getTime(),
|
||||
method: "GET",
|
||||
transformResponse: function(response) {
|
||||
return renderTemplate(angular.fromJson(response)._source.dashboard, $routeParams);
|
||||
}
|
||||
};
|
||||
if (config.elasticsearchBasicAuth) {
|
||||
options.withCredentials = true;
|
||||
options.headers = {
|
||||
"Authorization": "Basic " + config.elasticsearchBasicAuth
|
||||
};
|
||||
}
|
||||
return $http(options)
|
||||
.error(function(data, status) {
|
||||
if(status === 0) {
|
||||
alertSrv.set('Error',"Could not contact Elasticsearch at "+config.elasticsearch+
|
||||
". Please ensure that Elasticsearch is reachable from your system." ,'error');
|
||||
} else {
|
||||
alertSrv.set('Error',"Could not find "+id+". If you"+
|
||||
" are using a proxy, ensure it is configured correctly",'error');
|
||||
}
|
||||
return false;
|
||||
}).success(function(data) {
|
||||
self.dash_load(data);
|
||||
});
|
||||
};
|
||||
|
||||
this.script_load = function(file) {
|
||||
return $http({
|
||||
url: "app/dashboards/"+file.replace(/\.(?!js)/,"/"),
|
||||
method: "GET"
|
||||
})
|
||||
.then(function(result) {
|
||||
/*jshint -W054 */
|
||||
var script_func = new Function('ARGS','kbn','_','moment','window','document','$','jQuery', result.data);
|
||||
var script_result = script_func($routeParams,kbn,_,moment, window, document, $, $);
|
||||
|
||||
// Handle async dashboard scripts
|
||||
if (_.isFunction(script_result)) {
|
||||
var deferred = $q.defer();
|
||||
script_result(function(dashboard) {
|
||||
$rootScope.$apply(function() {
|
||||
deferred.resolve({ data: dashboard });
|
||||
});
|
||||
});
|
||||
return deferred.promise;
|
||||
}
|
||||
|
||||
return { data: script_result };
|
||||
})
|
||||
.then(function(result) {
|
||||
if(!result) {
|
||||
return false;
|
||||
}
|
||||
self.dash_load(dash_defaults(result.data));
|
||||
return true;
|
||||
},function() {
|
||||
alertSrv.set('Error',
|
||||
"Could not load <i>scripts/"+file+"</i>. Please make sure it exists and returns a valid dashboard" ,
|
||||
'error');
|
||||
return false;
|
||||
});
|
||||
};
|
||||
|
||||
this.elasticsearch_save = function(type,title,ttl) {
|
||||
// Clone object so we can modify it without influencing the existing obejct
|
||||
var save = _.clone(self.current);
|
||||
var id;
|
||||
|
||||
// Change title on object clone
|
||||
if (type === 'dashboard') {
|
||||
id = save.title = _.isUndefined(title) ? self.current.title : title;
|
||||
}
|
||||
|
||||
// Create request with id as title. Rethink this.
|
||||
var request = ejs.Document(config.grafana_index,type,id).source({
|
||||
user: 'guest',
|
||||
group: 'guest',
|
||||
title: save.title,
|
||||
tags: save.tags,
|
||||
dashboard: angular.toJson(save)
|
||||
});
|
||||
|
||||
request = type === 'temp' && ttl ? request.ttl(ttl) : request;
|
||||
|
||||
return request.doIndex(
|
||||
// Success
|
||||
function(result) {
|
||||
if(type === 'dashboard') {
|
||||
$location.path('/dashboard/elasticsearch/'+title);
|
||||
}
|
||||
return result;
|
||||
},
|
||||
// Failure
|
||||
function() {
|
||||
return false;
|
||||
}
|
||||
);
|
||||
};
|
||||
|
||||
this.elasticsearch_delete = function(id) {
|
||||
return ejs.Document(config.grafana_index,'dashboard',id).doDelete(
|
||||
// Success
|
||||
function(result) {
|
||||
return result;
|
||||
},
|
||||
// Failure
|
||||
function() {
|
||||
return false;
|
||||
}
|
||||
);
|
||||
};
|
||||
|
||||
this.save_gist = function(title,dashboard) {
|
||||
var save = _.clone(dashboard || self.current);
|
||||
save.title = title || self.current.title;
|
||||
return $http({
|
||||
url: "https://api.github.com/gists",
|
||||
method: "POST",
|
||||
data: {
|
||||
"description": save.title,
|
||||
"public": false,
|
||||
"files": {
|
||||
"kibana-dashboard.json": {
|
||||
"content": angular.toJson(save,true)
|
||||
}
|
||||
}
|
||||
}
|
||||
}).then(function(data) {
|
||||
return data.data.html_url;
|
||||
}, function() {
|
||||
return false;
|
||||
});
|
||||
};
|
||||
|
||||
this.gist_list = function(id) {
|
||||
return $http.jsonp("https://api.github.com/gists/"+id+"?callback=JSON_CALLBACK"
|
||||
).then(function(response) {
|
||||
var files = [];
|
||||
_.each(response.data.data.files,function(v) {
|
||||
try {
|
||||
var file = JSON.parse(v.content);
|
||||
files.push(file);
|
||||
} catch(e) {
|
||||
return false;
|
||||
}
|
||||
});
|
||||
return files;
|
||||
}, function() {
|
||||
return false;
|
||||
});
|
||||
};
|
||||
|
||||
this.start_scheduled_refresh = function (after_ms) {
|
||||
this.cancel_scheduled_refresh();
|
||||
self.refresh_timer = timer.register($timeout(function () {
|
||||
self.start_scheduled_refresh(after_ms);
|
||||
self.refresh();
|
||||
}, after_ms));
|
||||
};
|
||||
|
||||
this.cancel_scheduled_refresh = function () {
|
||||
timer.cancel(self.refresh_timer);
|
||||
};
|
||||
|
||||
this.set_interval = function (interval) {
|
||||
self.current.refresh = interval;
|
||||
if (interval) {
|
||||
var _i = kbn.interval_to_ms(interval);
|
||||
this.start_scheduled_refresh(_i);
|
||||
} else {
|
||||
this.cancel_scheduled_refresh();
|
||||
}
|
||||
};
|
||||
|
||||
});
|
||||
|
||||
});
|
||||
@@ -1,4 +0,0 @@
|
||||
define([
|
||||
'./dashboardKeyBindings',
|
||||
],
|
||||
function () {});
|
||||
@@ -6,44 +6,54 @@ define([
|
||||
function(angular, $) {
|
||||
"use strict";
|
||||
|
||||
var module = angular.module('kibana.services.dashboard');
|
||||
var module = angular.module('kibana.services');
|
||||
|
||||
module.service('dashboardKeybindings', function($rootScope, keyboardManager, dashboard) {
|
||||
this.shortcuts = function() {
|
||||
$rootScope.$on('panel-fullscreen-enter', function() {
|
||||
module.service('dashboardKeybindings', function($rootScope, keyboardManager) {
|
||||
|
||||
this.shortcuts = function(scope) {
|
||||
|
||||
scope.onAppEvent('panel-fullscreen-enter', function() {
|
||||
$rootScope.fullscreen = true;
|
||||
});
|
||||
|
||||
$rootScope.$on('panel-fullscreen-exit', function() {
|
||||
scope.onAppEvent('panel-fullscreen-exit', function() {
|
||||
$rootScope.fullscreen = false;
|
||||
});
|
||||
|
||||
$rootScope.$on('dashboard-saved', function() {
|
||||
scope.onAppEvent('dashboard-saved', function() {
|
||||
if ($rootScope.fullscreen) {
|
||||
$rootScope.$emit('panel-fullscreen-exit');
|
||||
scope.emitAppEvent('panel-fullscreen-exit');
|
||||
}
|
||||
});
|
||||
|
||||
scope.$on('$destroy', function() {
|
||||
keyboardManager.unbind('ctrl+f');
|
||||
keyboardManager.unbind('ctrl+h');
|
||||
keyboardManager.unbind('ctrl+s');
|
||||
keyboardManager.unbind('ctrl+r');
|
||||
keyboardManager.unbind('ctrl+z');
|
||||
keyboardManager.unbind('esc');
|
||||
});
|
||||
|
||||
keyboardManager.bind('ctrl+f', function(evt) {
|
||||
$rootScope.$emit('open-search', evt);
|
||||
scope.emitAppEvent('open-search', evt);
|
||||
}, { inputDisabled: true });
|
||||
|
||||
keyboardManager.bind('ctrl+h', function() {
|
||||
var current = dashboard.current.hideControls;
|
||||
dashboard.current.hideControls = !current;
|
||||
dashboard.current.panel_hints = current;
|
||||
var current = scope.dashboard.hideControls;
|
||||
scope.dashboard.hideControls = !current;
|
||||
}, { inputDisabled: true });
|
||||
|
||||
keyboardManager.bind('ctrl+s', function(evt) {
|
||||
$rootScope.$emit('save-dashboard', evt);
|
||||
scope.emitAppEvent('save-dashboard', evt);
|
||||
}, { inputDisabled: true });
|
||||
|
||||
keyboardManager.bind('ctrl+r', function() {
|
||||
dashboard.refresh();
|
||||
scope.dashboard.emit_refresh();
|
||||
}, { inputDisabled: true });
|
||||
|
||||
keyboardManager.bind('ctrl+z', function(evt) {
|
||||
$rootScope.$emit('zoom-out', evt);
|
||||
scope.emitAppEvent('zoom-out', evt);
|
||||
}, { inputDisabled: true });
|
||||
|
||||
keyboardManager.bind('esc', function() {
|
||||
@@ -51,7 +61,7 @@ function(angular, $) {
|
||||
if (popups.length > 0) {
|
||||
return;
|
||||
}
|
||||
$rootScope.$emit('panel-fullscreen-exit');
|
||||
scope.emitAppEvent('panel-fullscreen-exit');
|
||||
}, { inputDisabled: true });
|
||||
};
|
||||
});
|
||||
|
||||
99
src/app/services/dashboard/dashboardModel.js
Normal file
99
src/app/services/dashboard/dashboardModel.js
Normal file
@@ -0,0 +1,99 @@
|
||||
define([
|
||||
'angular',
|
||||
'jquery',
|
||||
'kbn',
|
||||
'underscore'
|
||||
],
|
||||
function (angular, $, kbn, _) {
|
||||
'use strict';
|
||||
|
||||
var module = angular.module('kibana.services');
|
||||
|
||||
module.service('dashboard', function(timer, $rootScope, $timeout) {
|
||||
|
||||
function DashboardModel (data) {
|
||||
|
||||
if (!data) {
|
||||
data = {};
|
||||
}
|
||||
|
||||
this.title = data.title;
|
||||
this.tags = data.tags || [];
|
||||
this.style = data.style || "dark";
|
||||
this.timezone = data.timezone || 'browser';
|
||||
this.editable = data.editble || true;
|
||||
this.rows = data.rows || [];
|
||||
this.pulldowns = data.pulldowns || [];
|
||||
this.nav = data.nav || [];
|
||||
this.services = data.services || {};
|
||||
this.loader = data.loader || {};
|
||||
|
||||
_.defaults(this.loader, {
|
||||
save_gist: false,
|
||||
save_elasticsearch: true,
|
||||
save_default: true,
|
||||
save_temp: true,
|
||||
save_temp_ttl_enable: true,
|
||||
save_temp_ttl: '30d',
|
||||
load_gist: false,
|
||||
load_elasticsearch: true,
|
||||
hide: false
|
||||
});
|
||||
|
||||
if (this.nav.length === 0) {
|
||||
this.nav.push({ type: 'timepicker' });
|
||||
}
|
||||
|
||||
if (!_.findWhere(this.pulldowns, {type: 'filtering'})) {
|
||||
this.pulldowns.push({ type: 'filtering', enable: false });
|
||||
}
|
||||
|
||||
if (!_.findWhere(this.pulldowns, {type: 'annotations'})) {
|
||||
this.pulldowns.push({ type: 'annotations', enable: false });
|
||||
}
|
||||
|
||||
_.each(this.rows, function(row) {
|
||||
_.each(row.panels, function(panel) {
|
||||
if (panel.type === 'graphite') {
|
||||
panel.type = 'graph';
|
||||
}
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
var p = DashboardModel.prototype;
|
||||
|
||||
p.emit_refresh = function() {
|
||||
$rootScope.$broadcast('refresh');
|
||||
};
|
||||
|
||||
p.start_scheduled_refresh = function (after_ms) {
|
||||
this.cancel_scheduled_refresh();
|
||||
this.refresh_timer = timer.register($timeout(function () {
|
||||
this.start_scheduled_refresh(after_ms);
|
||||
this.emit_refresh();
|
||||
}.bind(this), after_ms));
|
||||
};
|
||||
|
||||
p.cancel_scheduled_refresh = function () {
|
||||
timer.cancel(this.refresh_timer);
|
||||
};
|
||||
|
||||
p.set_interval = function (interval) {
|
||||
this.refresh = interval;
|
||||
if (interval) {
|
||||
var _i = kbn.interval_to_ms(interval);
|
||||
this.start_scheduled_refresh(_i);
|
||||
} else {
|
||||
this.cancel_scheduled_refresh();
|
||||
}
|
||||
};
|
||||
|
||||
return {
|
||||
create: function(dashboard) {
|
||||
return new DashboardModel(dashboard);
|
||||
}
|
||||
};
|
||||
|
||||
});
|
||||
});
|
||||
@@ -12,13 +12,20 @@ function (angular, _, config) {
|
||||
var module = angular.module('kibana.services');
|
||||
|
||||
module.service('datasourceSrv', function($q, filterSrv, $http, GraphiteDatasource, InfluxDatasource, OpenTSDBDatasource) {
|
||||
var datasources = {};
|
||||
|
||||
this.init = function() {
|
||||
var defaultDatasource = _.findWhere(_.values(config.datasources), { default: true });
|
||||
if (!defaultDatasource) {
|
||||
defaultDatasource = config.datasources[_.keys(config.datasources)[0]];
|
||||
_.each(config.datasources, function(value, key) {
|
||||
datasources[key] = this.datasourceFactory(value);
|
||||
if (value.default) {
|
||||
this.default = datasources[key];
|
||||
}
|
||||
}, this);
|
||||
|
||||
if (!this.default) {
|
||||
this.default = datasources[_.keys(datasources)[0]];
|
||||
this.default.default = true;
|
||||
}
|
||||
this.default = this.datasourceFactory(defaultDatasource);
|
||||
};
|
||||
|
||||
this.datasourceFactory = function(ds) {
|
||||
@@ -34,13 +41,22 @@ function (angular, _, config) {
|
||||
|
||||
this.get = function(name) {
|
||||
if (!name) { return this.default; }
|
||||
if (datasources[name]) { return datasources[name]; }
|
||||
|
||||
var ds = config.datasources[name];
|
||||
if (!ds) {
|
||||
return null;
|
||||
}
|
||||
throw "Unable to find datasource: " + name;
|
||||
};
|
||||
|
||||
return this.datasourceFactory(ds);
|
||||
this.getAnnotationSources = function() {
|
||||
var results = [];
|
||||
_.each(datasources, function(value, key) {
|
||||
if (value.supportAnnotations) {
|
||||
results.push({
|
||||
name: key,
|
||||
editorSrc: value.annotationEditorSrc,
|
||||
});
|
||||
}
|
||||
});
|
||||
return results;
|
||||
};
|
||||
|
||||
this.listOptions = function() {
|
||||
|
||||
96
src/app/services/elasticsearch/es-client.js
Normal file
96
src/app/services/elasticsearch/es-client.js
Normal file
@@ -0,0 +1,96 @@
|
||||
define([
|
||||
'angular',
|
||||
'config'
|
||||
],
|
||||
function(angular, config) {
|
||||
"use strict";
|
||||
|
||||
var module = angular.module('kibana.services');
|
||||
|
||||
module.service('elastic', function($http) {
|
||||
|
||||
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) {
|
||||
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.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), data)
|
||||
.then(function() {
|
||||
return { title: title, url: '/dashboard/elasticsearch/' + title };
|
||||
}, function(err) {
|
||||
throw 'Failed to save to elasticsearch ' + err.data;
|
||||
});
|
||||
};
|
||||
|
||||
});
|
||||
});
|
||||
@@ -8,7 +8,7 @@ define([
|
||||
|
||||
var module = angular.module('kibana.services');
|
||||
|
||||
module.factory('filterSrv', function(dashboard, $rootScope, $timeout, $routeParams) {
|
||||
module.factory('filterSrv', function($rootScope, $timeout, $routeParams) {
|
||||
// defaults
|
||||
var _d = {
|
||||
templateParameters: [],
|
||||
@@ -53,16 +53,14 @@ define([
|
||||
// disable refresh if we have an absolute time
|
||||
if (time.to !== 'now') {
|
||||
this.old_refresh = this.dashboard.refresh;
|
||||
dashboard.set_interval(false);
|
||||
this.dashboard.set_interval(false);
|
||||
}
|
||||
else if (this.old_refresh && this.old_refresh !== this.dashboard.refresh) {
|
||||
dashboard.set_interval(this.old_refresh);
|
||||
this.dashboard.set_interval(this.old_refresh);
|
||||
this.old_refresh = null;
|
||||
}
|
||||
|
||||
$timeout(function() {
|
||||
dashboard.refresh();
|
||||
},0);
|
||||
$timeout(this.dashboard.emit_refresh, 0);
|
||||
},
|
||||
|
||||
timeRange: function(parse) {
|
||||
@@ -96,14 +94,22 @@ define([
|
||||
this.dashboard = dashboard;
|
||||
this.templateSettings = { interpolate : /\[\[([\s\S]+?)\]\]/g };
|
||||
|
||||
if(dashboard.services && dashboard.services.filter) {
|
||||
this.time = dashboard.services.filter.time;
|
||||
this.templateParameters = dashboard.services.filter.list || [];
|
||||
this.updateTemplateData(true);
|
||||
if (!this.dashboard.services.filter) {
|
||||
this.dashboard.services.filter = {
|
||||
list: [],
|
||||
time: {
|
||||
from: '1h',
|
||||
to: 'now'
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
this.time = dashboard.services.filter.time;
|
||||
this.templateParameters = dashboard.services.filter.list || [];
|
||||
this.updateTemplateData(true);
|
||||
}
|
||||
};
|
||||
|
||||
return result;
|
||||
});
|
||||
|
||||
|
||||
@@ -11,7 +11,7 @@ function (angular, _, $, config, kbn, moment) {
|
||||
|
||||
var module = angular.module('kibana.services');
|
||||
|
||||
module.factory('GraphiteDatasource', function(dashboard, $q, $http) {
|
||||
module.factory('GraphiteDatasource', function($q, $http) {
|
||||
|
||||
function GraphiteDatasource(datasource) {
|
||||
this.type = 'graphite';
|
||||
@@ -20,6 +20,8 @@ function (angular, _, $, config, kbn, moment) {
|
||||
this.editorSrc = 'app/partials/graphite/editor.html';
|
||||
this.name = datasource.name;
|
||||
this.render_method = datasource.render_method || 'POST';
|
||||
this.supportAnnotations = true;
|
||||
this.annotationEditorSrc = 'app/partials/graphite/annotation_editor.html';
|
||||
}
|
||||
|
||||
GraphiteDatasource.prototype.query = function(filterSrv, options) {
|
||||
@@ -55,6 +57,57 @@ function (angular, _, $, config, kbn, moment) {
|
||||
}
|
||||
};
|
||||
|
||||
GraphiteDatasource.prototype.annotationQuery = function(annotation, filterSrv, rangeUnparsed) {
|
||||
// Graphite metric as annotation
|
||||
if (annotation.target) {
|
||||
var graphiteQuery = {
|
||||
range: rangeUnparsed,
|
||||
targets: [{ target: annotation.target }],
|
||||
format: 'json',
|
||||
maxDataPoints: 100
|
||||
};
|
||||
|
||||
return this.query(filterSrv, graphiteQuery)
|
||||
.then(function(result) {
|
||||
var list = [];
|
||||
|
||||
for (var i = 0; i < result.data.length; i++) {
|
||||
var target = result.data[i];
|
||||
|
||||
for (var y = 0; y < target.datapoints.length; y++) {
|
||||
var datapoint = target.datapoints[y];
|
||||
if (!datapoint[0]) { continue; }
|
||||
|
||||
list.push({
|
||||
annotation: annotation,
|
||||
time: datapoint[1] * 1000,
|
||||
title: target.target
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
return list;
|
||||
});
|
||||
}
|
||||
// Graphite event as annotation
|
||||
else if (annotation.tags) {
|
||||
return this.events({ range: rangeUnparsed, tags: annotation.tags })
|
||||
.then(function(results) {
|
||||
var list = [];
|
||||
for (var i = 0; i < results.data; i++) {
|
||||
list.push({
|
||||
annotation: annotation,
|
||||
time: event.when * 1000,
|
||||
title: event.what,
|
||||
tags: event.tags,
|
||||
text: event.data
|
||||
});
|
||||
}
|
||||
return list;
|
||||
});
|
||||
}
|
||||
};
|
||||
|
||||
GraphiteDatasource.prototype.events = function(options) {
|
||||
try {
|
||||
var tags = '';
|
||||
|
||||
@@ -8,6 +8,7 @@ function (_) {
|
||||
this.seriesList = options.seriesList;
|
||||
this.alias = options.alias;
|
||||
this.groupByField = options.groupByField;
|
||||
this.annotation = options.annotation;
|
||||
}
|
||||
|
||||
var p = InfluxSeries.prototype;
|
||||
@@ -65,6 +66,45 @@ function (_) {
|
||||
return output;
|
||||
};
|
||||
|
||||
p.getAnnotations = function () {
|
||||
var list = [];
|
||||
var self = this;
|
||||
|
||||
_.each(this.seriesList, function (series) {
|
||||
var titleCol = null;
|
||||
var timeCol = null;
|
||||
var tagsCol = null;
|
||||
var textCol = null;
|
||||
|
||||
_.each(series.columns, function(column, index) {
|
||||
if (column === 'time') { timeCol = index; return; }
|
||||
if (column === 'sequence_number') { return; }
|
||||
if (!titleCol) { titleCol = index; }
|
||||
if (column === self.annotation.titleColumn) { titleCol = index; return; }
|
||||
if (column === self.annotation.tagsColumn) { tagsCol = index; return; }
|
||||
if (column === self.annotation.textColumn) { textCol = index; return; }
|
||||
});
|
||||
|
||||
_.each(series.points, function (point) {
|
||||
var data = {
|
||||
annotation: self.annotation,
|
||||
time: point[timeCol] * 1000,
|
||||
title: point[titleCol],
|
||||
tags: point[tagsCol],
|
||||
text: point[textCol]
|
||||
};
|
||||
|
||||
if (tagsCol) {
|
||||
data.tags = point[tagsCol];
|
||||
}
|
||||
|
||||
list.push(data);
|
||||
});
|
||||
});
|
||||
|
||||
return list;
|
||||
};
|
||||
|
||||
p.createNameForSeries = function(seriesName, groupByColValue) {
|
||||
var name = this.alias
|
||||
.replace('$s', seriesName);
|
||||
@@ -84,4 +124,4 @@ function (_) {
|
||||
};
|
||||
|
||||
return InfluxSeries;
|
||||
});
|
||||
});
|
||||
|
||||
@@ -21,6 +21,9 @@ function (angular, _, kbn, InfluxSeries) {
|
||||
this.templateSettings = {
|
||||
interpolate : /\[\[([\s\S]+?)\]\]/g,
|
||||
};
|
||||
|
||||
this.supportAnnotations = true;
|
||||
this.annotationEditorSrc = 'app/partials/influxdb/annotation_editor.html';
|
||||
}
|
||||
|
||||
InfluxDatasource.prototype.query = function(filterSrv, options) {
|
||||
@@ -116,6 +119,15 @@ function (angular, _, kbn, InfluxSeries) {
|
||||
|
||||
};
|
||||
|
||||
InfluxDatasource.prototype.annotationQuery = function(annotation, filterSrv, rangeUnparsed) {
|
||||
var timeFilter = getTimeFilter({ range: rangeUnparsed });
|
||||
var query = _.template(annotation.query, { timeFilter: timeFilter }, this.templateSettings);
|
||||
|
||||
return this.doInfluxRequest(query).then(function(results) {
|
||||
return new InfluxSeries({ seriesList: results, annotation: annotation }).getAnnotations();
|
||||
});
|
||||
};
|
||||
|
||||
InfluxDatasource.prototype.listColumns = function(seriesName) {
|
||||
return this.doInfluxRequest('select * from /' + seriesName + '/ limit 1').then(function(data) {
|
||||
if (!data) {
|
||||
|
||||
@@ -7,27 +7,33 @@ function (angular, _) {
|
||||
|
||||
var module = angular.module('kibana.services');
|
||||
|
||||
module.service('panelMove', function(dashboard, $rootScope) {
|
||||
module.service('panelMoveSrv', function($rootScope) {
|
||||
|
||||
function PanelMoveSrv(dashboard) {
|
||||
this.dashboard = dashboard;
|
||||
_.bindAll(this, 'onStart', 'onOver', 'onOut', 'onDrop', 'onStop', 'cleanup');
|
||||
}
|
||||
|
||||
var p = PanelMoveSrv.prototype;
|
||||
|
||||
/* each of these can take event,ui,data parameters */
|
||||
|
||||
this.onStart = function() {
|
||||
dashboard.panelDragging = true;
|
||||
p.onStart = function() {
|
||||
this.dashboard.panelDragging = true;
|
||||
$rootScope.$apply();
|
||||
};
|
||||
|
||||
this.onOver = function() {
|
||||
p.onOver = function() {
|
||||
$rootScope.$apply();
|
||||
};
|
||||
|
||||
this.onOut = function() {
|
||||
p.onOut = function() {
|
||||
$rootScope.$apply();
|
||||
};
|
||||
|
||||
/*
|
||||
Use our own drop logic. the $parent.$parent this is ugly.
|
||||
*/
|
||||
this.onDrop = function(event,ui,data) {
|
||||
p.onDrop = function(event,ui,data) {
|
||||
var
|
||||
dragRow = data.draggableScope.$parent.$parent.row.panels,
|
||||
dropRow = data.droppableScope.$parent.$parent.row.panels,
|
||||
@@ -42,26 +48,32 @@ function (angular, _) {
|
||||
dropRow.splice(dropIndex,0,data.dragItem);
|
||||
}
|
||||
|
||||
dashboard.panelDragging = false;
|
||||
this.dashboard.panelDragging = false;
|
||||
// Cleanup nulls/undefined left behind
|
||||
cleanup();
|
||||
this.cleanup();
|
||||
$rootScope.$apply();
|
||||
$rootScope.$broadcast('render');
|
||||
};
|
||||
|
||||
this.onStop = function() {
|
||||
dashboard.panelDragging = false;
|
||||
cleanup();
|
||||
p.onStop = function() {
|
||||
this.dashboard.panelDragging = false;
|
||||
this.cleanup();
|
||||
$rootScope.$apply();
|
||||
};
|
||||
|
||||
var cleanup = function () {
|
||||
_.each(dashboard.current.rows, function(row) {
|
||||
p.cleanup = function () {
|
||||
_.each(this.dashboard.rows, function(row) {
|
||||
row.panels = _.without(row.panels,{});
|
||||
row.panels = _.compact(row.panels);
|
||||
});
|
||||
};
|
||||
|
||||
return {
|
||||
create: function(dashboard) {
|
||||
return new PanelMoveSrv(dashboard);
|
||||
}
|
||||
};
|
||||
|
||||
});
|
||||
|
||||
});
|
||||
@@ -8,7 +8,7 @@ function (angular, _, kbn) {
|
||||
|
||||
var module = angular.module('kibana.services');
|
||||
|
||||
module.service('playlistSrv', function(dashboard, $location, $rootScope) {
|
||||
module.service('playlistSrv', function($location, $rootScope) {
|
||||
var timerInstance;
|
||||
var favorites = { dashboards: [] };
|
||||
|
||||
@@ -33,17 +33,17 @@ function (angular, _, kbn) {
|
||||
}
|
||||
};
|
||||
|
||||
this.isCurrentFavorite = function() {
|
||||
return this._find(dashboard.current.title) ? true : false;
|
||||
this.isCurrentFavorite = function(dashboard) {
|
||||
return this._find(dashboard.title) ? true : false;
|
||||
};
|
||||
|
||||
this.markAsFavorite = function() {
|
||||
var existing = this._find(dashboard.current.title);
|
||||
this.markAsFavorite = function(dashboard) {
|
||||
var existing = this._find(dashboard.title);
|
||||
this._remove(existing);
|
||||
|
||||
favorites.dashboards.push({
|
||||
url: $location.path(),
|
||||
title: dashboard.current.title
|
||||
title: dashboard.title
|
||||
});
|
||||
|
||||
this._save();
|
||||
|
||||
@@ -12,16 +12,22 @@ function(angular, _, config) {
|
||||
|
||||
var module = angular.module('kibana.services');
|
||||
|
||||
module.service('unsavedChangesSrv', function($rootScope, $modal, dashboard, $q, $location, $timeout) {
|
||||
module.service('unsavedChangesSrv', function($rootScope, $modal, $q, $location, $timeout) {
|
||||
|
||||
var self = this;
|
||||
var modalScope = $rootScope.$new();
|
||||
|
||||
$rootScope.$on("dashboard-loaded", function(event, newDashboard) {
|
||||
self.original = angular.copy(newDashboard);
|
||||
// wait for different services to patch the dashboard (missing properties)
|
||||
$timeout(function() {
|
||||
self.original = angular.copy(newDashboard);
|
||||
self.current = newDashboard;
|
||||
}, 1000);
|
||||
});
|
||||
|
||||
$rootScope.$on("dashboard-saved", function(event, savedDashboard) {
|
||||
self.original = angular.copy(savedDashboard);
|
||||
self.current = savedDashboard;
|
||||
});
|
||||
|
||||
$rootScope.$on("$routeChangeSuccess", function() {
|
||||
@@ -39,19 +45,20 @@ function(angular, _, config) {
|
||||
if (self.has_unsaved_changes()) {
|
||||
event.preventDefault();
|
||||
self.next = next;
|
||||
self.open_modal();
|
||||
|
||||
$timeout(self.open_modal);
|
||||
}
|
||||
});
|
||||
};
|
||||
|
||||
this.open_modal = function() {
|
||||
var confirmModal = $modal({
|
||||
template: './app/partials/unsaved-changes.html',
|
||||
persist: true,
|
||||
show: false,
|
||||
scope: modalScope,
|
||||
keyboard: false
|
||||
});
|
||||
template: './app/partials/unsaved-changes.html',
|
||||
persist: true,
|
||||
show: false,
|
||||
scope: modalScope,
|
||||
keyboard: false
|
||||
});
|
||||
|
||||
$q.when(confirmModal).then(function(modalEl) {
|
||||
modalEl.modal('show');
|
||||
@@ -63,7 +70,7 @@ function(angular, _, config) {
|
||||
return false;
|
||||
}
|
||||
|
||||
var current = angular.copy(dashboard.current);
|
||||
var current = angular.copy(self.current);
|
||||
var original = self.original;
|
||||
|
||||
// ignore timespan changes
|
||||
|
||||
@@ -35,18 +35,6 @@ function (Settings) {
|
||||
// Elasticsearch index for storing dashboards
|
||||
grafana_index: "grafana-dash",
|
||||
|
||||
// timezoneOFfset:
|
||||
// If you experiance problems with zoom, it is probably caused by timezone diff between
|
||||
// your browser and the graphite-web application. timezoneOffset setting can be used to have Grafana
|
||||
// translate absolute time ranges to the graphite-web timezone.
|
||||
// Example:
|
||||
// If TIME_ZONE in graphite-web config file local_settings.py is set to America/New_York, then set
|
||||
// timezoneOffset to "-0500" (for UTC - 5 hours)
|
||||
// Example:
|
||||
// If TIME_ZONE is set to UTC, set this to "0000"
|
||||
//
|
||||
timezoneOffset: null,
|
||||
|
||||
// set to false to disable unsaved changes warning
|
||||
unsaved_changes_warning: true,
|
||||
|
||||
|
||||
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
@@ -460,7 +460,6 @@ select.grafana-target-segment-input {
|
||||
width: 200px;
|
||||
text-align: center;
|
||||
cursor: auto;
|
||||
//background: rgba(50,50,50,0.8);
|
||||
padding: 10px;
|
||||
}
|
||||
|
||||
@@ -474,4 +473,17 @@ select.grafana-target-segment-input {
|
||||
.hide {
|
||||
display: block;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.grafana-tooltip {
|
||||
position : absolute;
|
||||
top: -1000;
|
||||
left: 0;
|
||||
color: #c8c8c8;
|
||||
padding: 10px;
|
||||
font-size: 11pt;
|
||||
font-weight : 200;
|
||||
background-color: rgb(58, 57, 57);
|
||||
border-radius: 5px;
|
||||
z-index: 9999;
|
||||
}
|
||||
|
||||
@@ -127,3 +127,5 @@
|
||||
padding: 5px;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
@@ -18,9 +18,9 @@
|
||||
|
||||
</head>
|
||||
|
||||
<body ng-cloak body-class>
|
||||
<body ng-cloak ng-controller="GrafanaCtrl">
|
||||
|
||||
<link rel="stylesheet" href="css/bootstrap.light.min.css" ng-if="dashboard.current.style === 'light'">
|
||||
<link rel="stylesheet" href="css/bootstrap.light.min.css" ng-if="grafana.style === 'light'">
|
||||
<link rel="stylesheet" href="css/bootstrap-responsive.min.css">
|
||||
<link rel="stylesheet" href="css/font-awesome.min.css">
|
||||
|
||||
@@ -28,15 +28,6 @@
|
||||
<button type="button" class="close" ng-click="dashAlerts.clear(alert)" style="padding-right:50px">×</button>
|
||||
<strong>{{alert.title}}</strong> <span ng-bind-html='alert.text'></span> <div style="padding-right:10px" class='pull-right small'> {{$index + 1}} alert(s) </div>
|
||||
</div>
|
||||
<div class="navbar navbar-static-top">
|
||||
<div class="navbar-inner">
|
||||
<div class="container-fluid">
|
||||
<span class="brand"><img src="img/small.png" bs-tooltip="'Grafana'" data-placement="bottom"> {{dashboard.current.title}}</span>
|
||||
<ul class="nav pull-right" ng-controller='dashLoader' ng-init="init()" ng-include="'app/partials/dashLoader.html'">
|
||||
</ul>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div ng-view ng-class="{'dashboard-fullscreen': fullscreen}"></div>
|
||||
|
||||
|
||||
@@ -5,37 +5,40 @@ define([],
|
||||
return {
|
||||
create: function() {
|
||||
return {
|
||||
refresh: function() {},
|
||||
set_interval: function(value) { this.current.refresh = value; },
|
||||
emit_refresh: function() {},
|
||||
set_interval: function(value) { this.refresh = value; },
|
||||
|
||||
current: {
|
||||
title: "",
|
||||
tags: [],
|
||||
style: "dark",
|
||||
timezone: 'browser',
|
||||
editable: true,
|
||||
failover: false,
|
||||
panel_hints: true,
|
||||
rows: [],
|
||||
pulldowns: [ { type: 'templating' }, { type: 'annotations' } ],
|
||||
nav: [ { type: 'timepicker' } ],
|
||||
services: {},
|
||||
loader: {
|
||||
save_gist: false,
|
||||
save_elasticsearch: true,
|
||||
save_local: true,
|
||||
save_default: true,
|
||||
save_temp: true,
|
||||
save_temp_ttl_enable: true,
|
||||
save_temp_ttl: '30d',
|
||||
load_gist: false,
|
||||
load_elasticsearch: true,
|
||||
load_elasticsearch_size: 20,
|
||||
load_local: false,
|
||||
hide: false
|
||||
},
|
||||
refresh: true
|
||||
}
|
||||
title: "",
|
||||
tags: [],
|
||||
style: "dark",
|
||||
timezone: 'browser',
|
||||
editable: true,
|
||||
failover: false,
|
||||
panel_hints: true,
|
||||
rows: [],
|
||||
pulldowns: [ { type: 'templating' }, { type: 'annotations' } ],
|
||||
nav: [ { type: 'timepicker' } ],
|
||||
services: {
|
||||
filter: {
|
||||
time: {},
|
||||
list: []
|
||||
}
|
||||
},
|
||||
loader: {
|
||||
save_gist: false,
|
||||
save_elasticsearch: true,
|
||||
save_local: true,
|
||||
save_default: true,
|
||||
save_temp: true,
|
||||
save_temp_ttl_enable: true,
|
||||
save_temp_ttl: '30d',
|
||||
load_gist: false,
|
||||
load_elasticsearch: true,
|
||||
load_elasticsearch_size: 20,
|
||||
load_local: false,
|
||||
hide: false
|
||||
},
|
||||
refresh: true
|
||||
};
|
||||
}
|
||||
};
|
||||
|
||||
@@ -10,9 +10,8 @@ define([
|
||||
var _dashboard;
|
||||
|
||||
beforeEach(module('kibana.services'));
|
||||
beforeEach(module(function($provide){
|
||||
beforeEach(module(function(){
|
||||
_dashboard = dashboardMock.create();
|
||||
$provide.value('dashboard', _dashboard);
|
||||
}));
|
||||
|
||||
beforeEach(inject(function(filterSrv) {
|
||||
@@ -20,7 +19,7 @@ define([
|
||||
}));
|
||||
|
||||
beforeEach(function() {
|
||||
_filterSrv.init(_dashboard.current);
|
||||
_filterSrv.init(_dashboard);
|
||||
});
|
||||
|
||||
describe('init', function() {
|
||||
@@ -68,18 +67,18 @@ define([
|
||||
|
||||
describe('setTime', function() {
|
||||
it('should return disable refresh for absolute times', function() {
|
||||
_dashboard.current.refresh = true;
|
||||
_dashboard.refresh = true;
|
||||
|
||||
_filterSrv.setTime({from: '2011-01-01', to: '2015-01-01' });
|
||||
expect(_dashboard.current.refresh).to.be(false);
|
||||
expect(_dashboard.refresh).to.be(false);
|
||||
});
|
||||
|
||||
it('should restore refresh after relative time range is set', function() {
|
||||
_dashboard.current.refresh = true;
|
||||
_dashboard.refresh = true;
|
||||
_filterSrv.setTime({from: '2011-01-01', to: '2015-01-01' });
|
||||
expect(_dashboard.current.refresh).to.be(false);
|
||||
expect(_dashboard.refresh).to.be(false);
|
||||
_filterSrv.setTime({from: '2011-01-01', to: 'now' });
|
||||
expect(_dashboard.current.refresh).to.be(true);
|
||||
expect(_dashboard.refresh).to.be(true);
|
||||
});
|
||||
});
|
||||
|
||||
|
||||
@@ -139,4 +139,61 @@ define([
|
||||
|
||||
});
|
||||
|
||||
describe("when creating annotations from influxdb response", function() {
|
||||
describe('given column mapping for all columns', function() {
|
||||
var series = new InfluxSeries({
|
||||
seriesList: [
|
||||
{
|
||||
columns: ['time', 'text', 'sequence_number', 'title', 'tags'],
|
||||
name: 'events1',
|
||||
points: [[1402596000, 'some text', 1, 'Hello', 'B'], [1402596001, 'asd', 2, 'Hello2', 'B']]
|
||||
}
|
||||
],
|
||||
annotation: {
|
||||
query: 'select',
|
||||
titleColumn: 'title',
|
||||
tagsColumn: 'tags',
|
||||
textColumn: 'text',
|
||||
}
|
||||
});
|
||||
|
||||
var result = series.getAnnotations();
|
||||
|
||||
it(' should generate 2 annnotations ', function() {
|
||||
expect(result.length).to.be(2);
|
||||
expect(result[0].annotation.query).to.be('select');
|
||||
expect(result[0].title).to.be('Hello');
|
||||
expect(result[0].time).to.be(1402596000000);
|
||||
expect(result[0].tags).to.be('B');
|
||||
expect(result[0].text).to.be('some text');
|
||||
});
|
||||
|
||||
});
|
||||
|
||||
describe('given no column mapping', function() {
|
||||
var series = new InfluxSeries({
|
||||
seriesList: [
|
||||
{
|
||||
columns: ['time', 'text', 'sequence_number'],
|
||||
name: 'events1',
|
||||
points: [[1402596000, 'some text', 1]]
|
||||
}
|
||||
],
|
||||
annotation: { query: 'select' }
|
||||
});
|
||||
|
||||
var result = series.getAnnotations();
|
||||
|
||||
it('should generate 1 annnotation', function() {
|
||||
expect(result.length).to.be(1);
|
||||
expect(result[0].title).to.be('some text');
|
||||
expect(result[0].time).to.be(1402596000000);
|
||||
expect(result[0].tags).to.be(undefined);
|
||||
expect(result[0].text).to.be(undefined);
|
||||
});
|
||||
|
||||
});
|
||||
|
||||
});
|
||||
|
||||
});
|
||||
|
||||
@@ -44,7 +44,6 @@ require.config({
|
||||
'jquery.flot.time': '../vendor/jquery/jquery.flot.time',
|
||||
|
||||
modernizr: '../vendor/modernizr-2.6.1',
|
||||
elasticjs: '../vendor/elasticjs/elastic-angular-client',
|
||||
},
|
||||
|
||||
shim: {
|
||||
@@ -97,8 +96,6 @@ require.config({
|
||||
|
||||
timepicker: ['jquery', 'bootstrap'],
|
||||
datepicker: ['jquery', 'bootstrap'],
|
||||
|
||||
elasticjs: ['angular', '../vendor/elasticjs/elastic'],
|
||||
}
|
||||
});
|
||||
|
||||
@@ -107,7 +104,6 @@ require([
|
||||
'angularMocks',
|
||||
'jquery',
|
||||
'underscore',
|
||||
'elasticjs',
|
||||
'bootstrap',
|
||||
'angular-sanitize',
|
||||
'angular-strap',
|
||||
|
||||
100
src/vendor/elasticjs/elastic-angular-client.js
vendored
100
src/vendor/elasticjs/elastic-angular-client.js
vendored
@@ -1,100 +0,0 @@
|
||||
/*! elastic.js - v1.1.1 - 2013-05-24
|
||||
* https://github.com/fullscale/elastic.js
|
||||
* Copyright (c) 2013 FullScale Labs, LLC; Licensed MIT */
|
||||
|
||||
/*jshint browser:true */
|
||||
/*global angular:true */
|
||||
'use strict';
|
||||
|
||||
/*
|
||||
Angular.js service wrapping the elastic.js API. This module can simply
|
||||
be injected into your angular controllers.
|
||||
*/
|
||||
angular.module('elasticjs.service', [])
|
||||
.factory('ejsResource', ['$http', function ($http) {
|
||||
|
||||
return function (config, basicAuth) {
|
||||
|
||||
var
|
||||
|
||||
// use existing ejs object if it exists
|
||||
ejs = window.ejs || {},
|
||||
|
||||
/* results are returned as a promise */
|
||||
promiseThen = function (httpPromise, successcb, errorcb) {
|
||||
return httpPromise.then(function (response) {
|
||||
(successcb || angular.noop)(response.data);
|
||||
return response.data;
|
||||
}, function (response) {
|
||||
(errorcb || angular.noop)(response.data);
|
||||
return response.data;
|
||||
});
|
||||
};
|
||||
|
||||
// check if we have a config object
|
||||
// if not, we have the server url so
|
||||
// we convert it to a config object
|
||||
if (config !== Object(config)) {
|
||||
config = {server: config};
|
||||
}
|
||||
|
||||
// set url to empty string if it was not specified
|
||||
if (config.server == null) {
|
||||
config.server = '';
|
||||
}
|
||||
|
||||
// set authentication header
|
||||
if (basicAuth || config.basicAuth) {
|
||||
config.headers = angular.extend( config.headers||{}, {
|
||||
"Authorization": "Basic " + (basicAuth||config.basicAuth)
|
||||
});
|
||||
}
|
||||
/* implement the elastic.js client interface for angular */
|
||||
ejs.client = {
|
||||
server: function (s) {
|
||||
if (s == null) {
|
||||
return config.server;
|
||||
}
|
||||
|
||||
config.server = s;
|
||||
return this;
|
||||
},
|
||||
post: function (path, data, successcb, errorcb) {
|
||||
path = config.server + path;
|
||||
var reqConfig = {url: path, data: data, method: 'POST'};
|
||||
return promiseThen($http(angular.extend(reqConfig, config)), successcb, errorcb);
|
||||
},
|
||||
get: function (path, data, successcb, errorcb) {
|
||||
path = config.server + path;
|
||||
// no body on get request, data will be request params
|
||||
var reqConfig = {url: path, params: data, method: 'GET'};
|
||||
return promiseThen($http(angular.extend(reqConfig, config)), successcb, errorcb);
|
||||
},
|
||||
put: function (path, data, successcb, errorcb) {
|
||||
path = config.server + path;
|
||||
var reqConfig = {url: path, data: data, method: 'PUT'};
|
||||
return promiseThen($http(angular.extend(reqConfig, config)), successcb, errorcb);
|
||||
},
|
||||
del: function (path, data, successcb, errorcb) {
|
||||
path = config.server + path;
|
||||
var reqConfig = {url: path, data: data, method: 'DELETE'};
|
||||
return promiseThen($http(angular.extend(reqConfig, config)), successcb, errorcb);
|
||||
},
|
||||
head: function (path, data, successcb, errorcb) {
|
||||
path = config.server + path;
|
||||
// no body on HEAD request, data will be request params
|
||||
var reqConfig = {url: path, params: data, method: 'HEAD'};
|
||||
return $http(angular.extend(reqConfig, config))
|
||||
.then(function (response) {
|
||||
(successcb || angular.noop)(response.headers());
|
||||
return response.headers();
|
||||
}, function (response) {
|
||||
(errorcb || angular.noop)(undefined);
|
||||
return undefined;
|
||||
});
|
||||
}
|
||||
};
|
||||
|
||||
return ejs;
|
||||
};
|
||||
}]);
|
||||
22268
src/vendor/elasticjs/elastic.js
vendored
22268
src/vendor/elasticjs/elastic.js
vendored
File diff suppressed because it is too large
Load Diff
@@ -49,7 +49,6 @@ module.exports = function(config,grunt) {
|
||||
'settings',
|
||||
'bootstrap',
|
||||
'modernizr',
|
||||
'elasticjs',
|
||||
'timepicker',
|
||||
'datepicker',
|
||||
'underscore',
|
||||
|
||||
Reference in New Issue
Block a user