mirror of
https://github.com/grafana/grafana.git
synced 2025-02-25 18:55:37 -06:00
Merge branch 'influxdb' Issue #103, influxdb support ready for testing and feedback
This commit is contained in:
@@ -52,14 +52,17 @@ function (_, crypto) {
|
|||||||
if (options.graphiteUrl) {
|
if (options.graphiteUrl) {
|
||||||
settings.datasources = {
|
settings.datasources = {
|
||||||
graphite: {
|
graphite: {
|
||||||
name: 'default',
|
type: 'graphite',
|
||||||
url: options.graphiteUrl,
|
url: options.graphiteUrl,
|
||||||
default: true
|
default: true
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
_.map(settings.datasources, parseBasicAuth);
|
_.each(settings.datasources, function(datasource, key) {
|
||||||
|
datasource.name = key;
|
||||||
|
parseBasicAuth(datasource);
|
||||||
|
});
|
||||||
|
|
||||||
var elasticParsed = parseBasicAuth({ url: settings.elasticsearch });
|
var elasticParsed = parseBasicAuth({ url: settings.elasticsearch });
|
||||||
settings.elasticsearchBasicAuth = elasticParsed.basicAuth;
|
settings.elasticsearchBasicAuth = elasticParsed.basicAuth;
|
||||||
|
|||||||
@@ -8,4 +8,5 @@ define([
|
|||||||
'./metricKeys',
|
'./metricKeys',
|
||||||
'./graphiteTarget',
|
'./graphiteTarget',
|
||||||
'./graphiteImport',
|
'./graphiteImport',
|
||||||
|
'./influxTargetCtrl',
|
||||||
], function () {});
|
], function () {});
|
||||||
24
src/app/controllers/influxTargetCtrl.js
Normal file
24
src/app/controllers/influxTargetCtrl.js
Normal file
@@ -0,0 +1,24 @@
|
|||||||
|
define([
|
||||||
|
'angular'
|
||||||
|
],
|
||||||
|
function (angular) {
|
||||||
|
'use strict';
|
||||||
|
|
||||||
|
var module = angular.module('kibana.controllers');
|
||||||
|
|
||||||
|
module.controller('InfluxTargetCtrl', function($scope) {
|
||||||
|
|
||||||
|
$scope.init = function() {
|
||||||
|
if (!$scope.target.function) {
|
||||||
|
$scope.target.function = 'mean';
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
$scope.duplicate = function() {
|
||||||
|
var clone = angular.copy($scope.target);
|
||||||
|
$scope.panel.targets.push(clone);
|
||||||
|
};
|
||||||
|
|
||||||
|
});
|
||||||
|
|
||||||
|
});
|
||||||
@@ -63,6 +63,27 @@ function (angular, app, _) {
|
|||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
|
$scope.duplicatePanel = function(panel, row) {
|
||||||
|
row = row || $scope.row;
|
||||||
|
var currentRowSpan = $scope.rowSpan(row);
|
||||||
|
if (currentRowSpan <= 9) {
|
||||||
|
row.panels.push(angular.copy(panel));
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
var rowsList = $scope.dashboard.current.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.duplicatePanel(panel, newRow);
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
$scope.duplicatePanel(panel, rowsList[rowIndex+1]);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
/** @scratch /panels/0
|
/** @scratch /panels/0
|
||||||
* [[panels]]
|
* [[panels]]
|
||||||
* = Panels
|
* = Panels
|
||||||
@@ -111,7 +132,6 @@ function (angular, app, _) {
|
|||||||
|
|
||||||
$scope.init();
|
$scope.init();
|
||||||
|
|
||||||
}
|
});
|
||||||
);
|
|
||||||
|
|
||||||
});
|
});
|
||||||
@@ -69,8 +69,7 @@
|
|||||||
"nullPointMode": "connected",
|
"nullPointMode": "connected",
|
||||||
"steppedLine": false,
|
"steppedLine": false,
|
||||||
"tooltip": {
|
"tooltip": {
|
||||||
"value_type": "cumulative",
|
"value_type": "cumulative"
|
||||||
"query_as_alias": true
|
|
||||||
},
|
},
|
||||||
"targets": [
|
"targets": [
|
||||||
{
|
{
|
||||||
|
|||||||
@@ -32,5 +32,4 @@ function (angular, app, _) {
|
|||||||
}
|
}
|
||||||
};
|
};
|
||||||
});
|
});
|
||||||
|
|
||||||
});
|
});
|
||||||
48
src/app/directives/bootstrap-tagsinput.js
vendored
48
src/app/directives/bootstrap-tagsinput.js
vendored
@@ -82,4 +82,52 @@ function (angular, $) {
|
|||||||
}
|
}
|
||||||
};
|
};
|
||||||
});
|
});
|
||||||
|
|
||||||
|
angular
|
||||||
|
.module('kibana.directives')
|
||||||
|
.directive('gfDropdown', function ($parse, $compile, $timeout) {
|
||||||
|
|
||||||
|
function buildTemplate(items, ul) {
|
||||||
|
if (!ul) {
|
||||||
|
ul = [
|
||||||
|
'<ul class="dropdown-menu" role="menu" aria-labelledby="drop1">',
|
||||||
|
'</ul>'
|
||||||
|
];
|
||||||
|
}
|
||||||
|
|
||||||
|
angular.forEach(items, function (item, index) {
|
||||||
|
if (item.divider) {
|
||||||
|
return ul.splice(index + 1, 0, '<li class="divider"></li>');
|
||||||
|
}
|
||||||
|
|
||||||
|
var li = '<li' + (item.submenu && item.submenu.length ? ' class="dropdown-submenu"' : '') + '>' +
|
||||||
|
'<a tabindex="-1" ng-href="' + (item.href || '') + '"' + (item.click ? '" ng-click="' + item.click + '"' : '') +
|
||||||
|
(item.target ? '" target="' + item.target + '"' : '') + (item.method ? '" data-method="' + item.method + '"' : '') +
|
||||||
|
(item.configModal ? ' config-modal="' + item.configModal + '"' : "") +
|
||||||
|
'>' + (item.text || '') + '</a>';
|
||||||
|
|
||||||
|
if (item.submenu && item.submenu.length) {
|
||||||
|
li += buildTemplate(item.submenu).join('\n');
|
||||||
|
}
|
||||||
|
|
||||||
|
li += '</li>';
|
||||||
|
ul.splice(index + 1, 0, li);
|
||||||
|
});
|
||||||
|
return ul;
|
||||||
|
}
|
||||||
|
|
||||||
|
return {
|
||||||
|
restrict: 'EA',
|
||||||
|
scope: true,
|
||||||
|
link: function postLink(scope, iElement, iAttrs) {
|
||||||
|
var getter = $parse(iAttrs.gfDropdown), items = getter(scope);
|
||||||
|
$timeout(function () {
|
||||||
|
var dropdown = angular.element(buildTemplate(items).join(''));
|
||||||
|
dropdown.insertAfter(iElement);
|
||||||
|
$compile(iElement.next('ul.dropdown-menu'))(scope);
|
||||||
|
});
|
||||||
|
iElement.addClass('dropdown-toggle').attr('data-toggle', 'dropdown');
|
||||||
|
}
|
||||||
|
};
|
||||||
|
});
|
||||||
});
|
});
|
||||||
@@ -75,9 +75,6 @@ function (angular, $, kbn, moment, _) {
|
|||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
// Set barwidth based on specified interval
|
|
||||||
var barwidth = kbn.interval_to_ms(scope.interval);
|
|
||||||
|
|
||||||
var stack = panel.stack ? true : null;
|
var stack = panel.stack ? true : null;
|
||||||
|
|
||||||
// Populate element
|
// Populate element
|
||||||
@@ -96,7 +93,7 @@ function (angular, $, kbn, moment, _) {
|
|||||||
bars: {
|
bars: {
|
||||||
show: panel.bars,
|
show: panel.bars,
|
||||||
fill: 1,
|
fill: 1,
|
||||||
barWidth: barwidth/1.5,
|
barWidth: 1,
|
||||||
zero: false,
|
zero: false,
|
||||||
lineWidth: 0
|
lineWidth: 0
|
||||||
},
|
},
|
||||||
@@ -128,6 +125,10 @@ function (angular, $, kbn, moment, _) {
|
|||||||
data[i].data = _d;
|
data[i].data = _d;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (panel.bars && data.length && data[0].info.timeStep) {
|
||||||
|
options.series.bars.barWidth = data[0].info.timeStep / 1.5;
|
||||||
|
}
|
||||||
|
|
||||||
addTimeAxis(options);
|
addTimeAxis(options);
|
||||||
addGridThresholds(options, panel);
|
addGridThresholds(options, panel);
|
||||||
addAnnotations(options);
|
addAnnotations(options);
|
||||||
@@ -289,7 +290,7 @@ function (angular, $, kbn, moment, _) {
|
|||||||
seriesInfo = item.series.info;
|
seriesInfo = item.series.info;
|
||||||
format = scope.panel.y_formats[seriesInfo.yaxis - 1];
|
format = scope.panel.y_formats[seriesInfo.yaxis - 1];
|
||||||
|
|
||||||
if (seriesInfo.alias || scope.panel.tooltip.query_as_alias) {
|
if (seriesInfo.alias) {
|
||||||
group = '<small style="font-size:0.9em;">' +
|
group = '<small style="font-size:0.9em;">' +
|
||||||
'<i class="icon-circle" style="color:'+item.series.color+';"></i>' + ' ' +
|
'<i class="icon-circle" style="color:'+item.series.color+';"></i>' + ' ' +
|
||||||
(seriesInfo.alias || seriesInfo.query)+
|
(seriesInfo.alias || seriesInfo.query)+
|
||||||
|
|||||||
@@ -1,13 +1,14 @@
|
|||||||
define([
|
define([
|
||||||
'angular',
|
'angular',
|
||||||
'jquery'
|
'jquery',
|
||||||
|
'underscore'
|
||||||
],
|
],
|
||||||
function (angular, $) {
|
function (angular, $, _) {
|
||||||
'use strict';
|
'use strict';
|
||||||
|
|
||||||
angular
|
angular
|
||||||
.module('kibana.directives')
|
.module('kibana.directives')
|
||||||
.directive('kibanaPanel', function($compile) {
|
.directive('kibanaPanel', function($compile, $timeout, $rootScope) {
|
||||||
|
|
||||||
var container = '<div class="panel-container"></div>';
|
var container = '<div class="panel-container"></div>';
|
||||||
var content = '<div class="panel-content"></div>';
|
var content = '<div class="panel-content"></div>';
|
||||||
@@ -28,8 +29,8 @@ function (angular, $) {
|
|||||||
'<i class="icon-spinner icon-spin icon-large"></i>' +
|
'<i class="icon-spinner icon-spin icon-large"></i>' +
|
||||||
'</span>' +
|
'</span>' +
|
||||||
|
|
||||||
'<span ng-if="panelMeta.menuItems" class="dropdown">' +
|
'<span class="dropdown">' +
|
||||||
'<span class="panel-text panel-title pointer" bs-dropdown="panelMeta.menuItems" tabindex="1" ' +
|
'<span class="panel-text panel-title pointer" gf-dropdown="panelMeta.menu" tabindex="1" ' +
|
||||||
'data-drag=true data-jqyoui-options="kbnJqUiDraggableOptions"'+
|
'data-drag=true data-jqyoui-options="kbnJqUiDraggableOptions"'+
|
||||||
' jqyoui-draggable="'+
|
' jqyoui-draggable="'+
|
||||||
'{'+
|
'{'+
|
||||||
@@ -44,11 +45,6 @@ function (angular, $) {
|
|||||||
'</span>' +
|
'</span>' +
|
||||||
'</span>'+
|
'</span>'+
|
||||||
|
|
||||||
'<span ng-if="!panelMeta.menuItems" config-modal="./app/partials/paneleditor.html" ' +
|
|
||||||
' class="panel-text panel-title pointer" >' +
|
|
||||||
'{{panel.title}}' +
|
|
||||||
'</span>'+
|
|
||||||
|
|
||||||
'</div>'+
|
'</div>'+
|
||||||
'</div>\n'+
|
'</div>\n'+
|
||||||
'</div>';
|
'</div>';
|
||||||
@@ -88,8 +84,7 @@ function (angular, $) {
|
|||||||
var nameAsPath = name.replace(".", "/");
|
var nameAsPath = name.replace(".", "/");
|
||||||
$scope.require([
|
$scope.require([
|
||||||
'jquery',
|
'jquery',
|
||||||
'text!panels/'+nameAsPath+'/module.html',
|
'text!panels/'+nameAsPath+'/module.html'
|
||||||
'text!panels/'+nameAsPath+'/editor.html'
|
|
||||||
], function ($, moduleTemplate) {
|
], function ($, moduleTemplate) {
|
||||||
var $module = $(moduleTemplate);
|
var $module = $(moduleTemplate);
|
||||||
// top level controllers
|
// top level controllers
|
||||||
@@ -101,9 +96,7 @@ function (angular, $) {
|
|||||||
$controllers.first().prepend(panelHeader);
|
$controllers.first().prepend(panelHeader);
|
||||||
$controllers.first().find('.panel-header').nextAll().wrapAll(content);
|
$controllers.first().find('.panel-header').nextAll().wrapAll(content);
|
||||||
|
|
||||||
$scope.require([
|
$scope.require(['panels/' + nameAsPath + '/module'], function() {
|
||||||
'panels/'+nameAsPath+'/module'
|
|
||||||
], function() {
|
|
||||||
loadModule($module);
|
loadModule($module);
|
||||||
});
|
});
|
||||||
} else {
|
} else {
|
||||||
@@ -111,6 +104,126 @@ function (angular, $) {
|
|||||||
}
|
}
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
|
|
||||||
|
/*
|
||||||
|
/* Panel base functionality
|
||||||
|
/* */
|
||||||
|
newScope.initPanel = function(scope) {
|
||||||
|
|
||||||
|
scope.updateColumnSpan = function(span) {
|
||||||
|
scope.panel.span = span;
|
||||||
|
|
||||||
|
$timeout(function() {
|
||||||
|
scope.$emit('render');
|
||||||
|
});
|
||||||
|
};
|
||||||
|
|
||||||
|
function enterFullscreenMode(options) {
|
||||||
|
var docHeight = $(window).height();
|
||||||
|
var editHeight = Math.floor(docHeight * 0.3);
|
||||||
|
var fullscreenHeight = Math.floor(docHeight * 0.7);
|
||||||
|
var oldTimeRange = scope.range;
|
||||||
|
|
||||||
|
scope.height = options.edit ? editHeight : fullscreenHeight;
|
||||||
|
scope.editMode = options.edit;
|
||||||
|
|
||||||
|
if (!scope.fullscreen) {
|
||||||
|
var closeEditMode = $rootScope.$on('panel-fullscreen-exit', function() {
|
||||||
|
scope.editMode = false;
|
||||||
|
scope.fullscreen = false;
|
||||||
|
delete scope.height;
|
||||||
|
|
||||||
|
closeEditMode();
|
||||||
|
|
||||||
|
$timeout(function() {
|
||||||
|
if (oldTimeRange !== $scope.range) {
|
||||||
|
scope.dashboard.refresh();
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
scope.$emit('render');
|
||||||
|
}
|
||||||
|
});
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
$(window).scrollTop(0);
|
||||||
|
|
||||||
|
scope.fullscreen = true;
|
||||||
|
|
||||||
|
$rootScope.$emit('panel-fullscreen-enter');
|
||||||
|
|
||||||
|
$timeout(function() {
|
||||||
|
scope.$emit('render');
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
scope.toggleFullscreenEdit = function() {
|
||||||
|
if (scope.editMode) {
|
||||||
|
$rootScope.$emit('panel-fullscreen-exit');
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
enterFullscreenMode({edit: true});
|
||||||
|
};
|
||||||
|
|
||||||
|
$scope.toggleFullscreen = function() {
|
||||||
|
if (scope.fullscreen && !scope.editMode) {
|
||||||
|
$rootScope.$emit('panel-fullscreen-exit');
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
enterFullscreenMode({ edit: false });
|
||||||
|
};
|
||||||
|
|
||||||
|
var menu = [
|
||||||
|
{
|
||||||
|
text: 'Edit',
|
||||||
|
configModal: "app/partials/paneleditor.html",
|
||||||
|
condition: !scope.panelMeta.fullscreenEdit
|
||||||
|
},
|
||||||
|
{
|
||||||
|
text: 'Edit',
|
||||||
|
click: "toggleFullscreenEdit()",
|
||||||
|
condition: scope.panelMeta.fullscreenEdit
|
||||||
|
},
|
||||||
|
{
|
||||||
|
text: "Fullscreen",
|
||||||
|
click: 'toggleFullscreen()',
|
||||||
|
condition: scope.panelMeta.fullscreenView
|
||||||
|
},
|
||||||
|
{
|
||||||
|
text: 'Duplicate',
|
||||||
|
click: 'duplicatePanel(panel)',
|
||||||
|
condition: true
|
||||||
|
},
|
||||||
|
{
|
||||||
|
text: 'Span',
|
||||||
|
submenu: [
|
||||||
|
{ text: '1', click: 'updateColumnSpan(1)' },
|
||||||
|
{ text: '2', click: 'updateColumnSpan(2)' },
|
||||||
|
{ text: '3', click: 'updateColumnSpan(3)' },
|
||||||
|
{ text: '4', click: 'updateColumnSpan(4)' },
|
||||||
|
{ text: '5', click: 'updateColumnSpan(5)' },
|
||||||
|
{ text: '6', click: 'updateColumnSpan(6)' },
|
||||||
|
{ text: '7', click: 'updateColumnSpan(7)' },
|
||||||
|
{ text: '8', click: 'updateColumnSpan(8)' },
|
||||||
|
{ text: '9', click: 'updateColumnSpan(9)' },
|
||||||
|
{ text: '10', click: 'updateColumnSpan(10)' },
|
||||||
|
{ text: '11', click: 'updateColumnSpan(11)' },
|
||||||
|
{ text: '12', click: 'updateColumnSpan(12)' },
|
||||||
|
],
|
||||||
|
condition: true
|
||||||
|
},
|
||||||
|
{
|
||||||
|
text: 'Remove',
|
||||||
|
click: 'remove_panel_from_row(row, panel)',
|
||||||
|
condition: true
|
||||||
|
}
|
||||||
|
];
|
||||||
|
|
||||||
|
scope.panelMeta.menu = _.where(menu, { condition: true });
|
||||||
|
};
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
});
|
});
|
||||||
|
|||||||
@@ -2,13 +2,6 @@
|
|||||||
ng-init="init()"
|
ng-init="init()"
|
||||||
style="min-height:{{panel.height || row.height}}"
|
style="min-height:{{panel.height || row.height}}"
|
||||||
ng-class="{'panel-fullscreen': fullscreen}">
|
ng-class="{'panel-fullscreen': fullscreen}">
|
||||||
<div>
|
|
||||||
<span ng-show='panel.options'>
|
|
||||||
<a class="link underline small" ng-show='panel.options' ng-click="options=!options">
|
|
||||||
<i ng-show="!options" class="icon-caret-right"></i><i ng-show="options" class="icon-caret-down"></i> View
|
|
||||||
</a> | 
|
|
||||||
</span>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<div style="position: relative">
|
<div style="position: relative">
|
||||||
|
|
||||||
@@ -32,22 +25,8 @@
|
|||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div class="tab-content" ng-show="editorTabs[editor.index] == 'General'">
|
|
||||||
<div ng-include src="'app/partials/panelgeneral.html'"></div>
|
|
||||||
|
|
||||||
<div class="editor-row" ng-show="datasources.length > 0">
|
|
||||||
<div class="section">
|
|
||||||
<div class="editor-option">
|
|
||||||
<label class="small">Datasource</label>
|
|
||||||
<select class="input-large" ng-options="obj.value as obj.name for obj in datasources" ng-model="panel.datasource" ng-change="datasourceChanged()"></select>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<div class="tab-content" ng-repeat="tab in panelMeta.fullEditorTabs" ng-show="editorTabs[editor.index] == tab.title">
|
<div class="tab-content" ng-repeat="tab in panelMeta.fullEditorTabs" ng-show="editorTabs[editor.index] == tab.title">
|
||||||
<div ng-include src="tab.src"></div>
|
<div ng-include src="tab.src"></div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
@@ -39,11 +39,14 @@ function (angular, app, $, _, kbn, moment, timeSeries) {
|
|||||||
$scope.panelMeta = {
|
$scope.panelMeta = {
|
||||||
modals : [],
|
modals : [],
|
||||||
editorTabs: [],
|
editorTabs: [],
|
||||||
|
|
||||||
fullEditorTabs : [
|
fullEditorTabs : [
|
||||||
{
|
{
|
||||||
title:'Targets',
|
title: 'General',
|
||||||
src:'app/panels/graphite/editor.html'
|
src:'app/partials/panelgeneral.html'
|
||||||
|
},
|
||||||
|
{
|
||||||
|
title: 'Metrics',
|
||||||
|
src:'app/partials/metrics.html'
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
title:'Axes & Grid',
|
title:'Axes & Grid',
|
||||||
@@ -54,30 +57,9 @@ function (angular, app, $, _, kbn, moment, timeSeries) {
|
|||||||
src:'app/panels/graphite/styleEditor.html'
|
src:'app/panels/graphite/styleEditor.html'
|
||||||
}
|
}
|
||||||
],
|
],
|
||||||
|
fullscreenEdit: true,
|
||||||
menuItems: [
|
fullscreenView: true,
|
||||||
{ text: 'Edit', click: 'openConfigureModal()' },
|
description : "Graphing"
|
||||||
{ text: 'Fullscreen', click: 'toggleFullscreen()' },
|
|
||||||
{ text: 'Duplicate', click: 'duplicate()' },
|
|
||||||
{ text: 'Span', submenu: [
|
|
||||||
{ text: '1', click: 'updateColumnSpan(1)' },
|
|
||||||
{ text: '2', click: 'updateColumnSpan(2)' },
|
|
||||||
{ text: '3', click: 'updateColumnSpan(3)' },
|
|
||||||
{ text: '4', click: 'updateColumnSpan(4)' },
|
|
||||||
{ text: '5', click: 'updateColumnSpan(5)' },
|
|
||||||
{ text: '6', click: 'updateColumnSpan(6)' },
|
|
||||||
{ text: '7', click: 'updateColumnSpan(7)' },
|
|
||||||
{ text: '8', click: 'updateColumnSpan(8)' },
|
|
||||||
{ text: '9', click: 'updateColumnSpan(9)' },
|
|
||||||
{ text: '10', click: 'updateColumnSpan(10)' },
|
|
||||||
{ text: '11', click: 'updateColumnSpan(11)' },
|
|
||||||
{ text: '12', click: 'updateColumnSpan(12)' },
|
|
||||||
]},
|
|
||||||
{ text: 'Remove', click: 'remove_panel_from_row(row, panel)' }
|
|
||||||
],
|
|
||||||
|
|
||||||
status : "Unstable",
|
|
||||||
description : "Graphite graphing panel <br /><br />"
|
|
||||||
};
|
};
|
||||||
|
|
||||||
// Set and populate defaults
|
// Set and populate defaults
|
||||||
@@ -217,35 +199,27 @@ function (angular, app, $, _, kbn, moment, timeSeries) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
$scope.init = function() {
|
$scope.init = function() {
|
||||||
// Hide view options by default
|
$scope.initPanel($scope);
|
||||||
|
|
||||||
$scope.fullscreen = false;
|
$scope.fullscreen = false;
|
||||||
$scope.options = false;
|
$scope.editor = { index: 1 };
|
||||||
$scope.editor = {index: 1};
|
$scope.editorTabs = _.pluck($scope.panelMeta.fullEditorTabs,'title');
|
||||||
$scope.editorTabs = _.union(['General'],_.pluck($scope.panelMeta.fullEditorTabs,'title'));
|
|
||||||
$scope.hiddenSeries = {};
|
$scope.hiddenSeries = {};
|
||||||
|
|
||||||
$scope.datasources = datasourceSrv.listOptions();
|
$scope.datasources = datasourceSrv.listOptions();
|
||||||
$scope.datasource = datasourceSrv.get($scope.panel.datasource);
|
$scope.setDatasource($scope.panel.datasource);
|
||||||
|
|
||||||
// Always show the query if an alias isn't set. Users can set an alias if the query is too
|
|
||||||
// long
|
|
||||||
$scope.panel.tooltip.query_as_alias = true;
|
|
||||||
|
|
||||||
$scope.get_data();
|
|
||||||
};
|
};
|
||||||
|
|
||||||
$scope.datasourceChanged = function() {
|
$scope.setDatasource = function(datasource) {
|
||||||
$scope.datasource = datasourceSrv.get($scope.panel.datasource);
|
$scope.panel.datasource = datasource;
|
||||||
$scope.get_data();
|
$scope.datasource = datasourceSrv.get(datasource);
|
||||||
};
|
|
||||||
|
|
||||||
$scope.remove_panel_from_row = function(row, panel) {
|
if (!$scope.datasource) {
|
||||||
if ($scope.fullscreen) {
|
$scope.panel.error = "Cannot find datasource " + datasource;
|
||||||
$rootScope.$emit('panel-fullscreen-exit');
|
return;
|
||||||
}
|
|
||||||
else {
|
|
||||||
$scope.$parent.remove_panel_from_row(row, panel);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
$scope.get_data();
|
||||||
};
|
};
|
||||||
|
|
||||||
$scope.removeTarget = function (target) {
|
$scope.removeTarget = function (target) {
|
||||||
@@ -256,25 +230,17 @@ function (angular, app, $, _, kbn, moment, timeSeries) {
|
|||||||
$scope.updateTimeRange = function () {
|
$scope.updateTimeRange = function () {
|
||||||
$scope.range = filterSrv.timeRange();
|
$scope.range = filterSrv.timeRange();
|
||||||
$scope.rangeUnparsed = filterSrv.timeRange(false);
|
$scope.rangeUnparsed = filterSrv.timeRange(false);
|
||||||
|
$scope.resolution = ($(window).width() / ($scope.panel.span / 12)) / 2;
|
||||||
|
|
||||||
$scope.interval = '10m';
|
$scope.interval = '10m';
|
||||||
|
|
||||||
if ($scope.range) {
|
if ($scope.range) {
|
||||||
$scope.interval = kbn.secondsToHms(
|
$scope.interval = kbn.secondsToHms(
|
||||||
kbn.calculate_interval($scope.range.from, $scope.range.to, $scope.panel.resolution, 0) / 1000
|
kbn.calculate_interval($scope.range.from, $scope.range.to, $scope.resolution, 0) / 1000
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
/**
|
|
||||||
* Fetch the data for a chunk of a queries results. Multiple segments occur when several indicies
|
|
||||||
* need to be consulted (like timestamped logstash indicies)
|
|
||||||
*
|
|
||||||
* The results of this function are stored on the scope's data property. This property will be an
|
|
||||||
* array of objects with the properties info, time_series, and hits. These objects are used in the
|
|
||||||
* render_panel function to create the historgram.
|
|
||||||
*
|
|
||||||
*/
|
|
||||||
$scope.get_data = function() {
|
$scope.get_data = function() {
|
||||||
delete $scope.panel.error;
|
delete $scope.panel.error;
|
||||||
|
|
||||||
@@ -284,22 +250,23 @@ function (angular, app, $, _, kbn, moment, timeSeries) {
|
|||||||
|
|
||||||
var graphiteQuery = {
|
var graphiteQuery = {
|
||||||
range: $scope.rangeUnparsed,
|
range: $scope.rangeUnparsed,
|
||||||
|
interval: $scope.interval,
|
||||||
targets: $scope.panel.targets,
|
targets: $scope.panel.targets,
|
||||||
format: $scope.panel.renderer === 'png' ? 'png' : 'json',
|
format: $scope.panel.renderer === 'png' ? 'png' : 'json',
|
||||||
maxDataPoints: $scope.panel.span * 50,
|
maxDataPoints: $scope.resolution,
|
||||||
datasource: $scope.panel.datasource
|
datasource: $scope.panel.datasource
|
||||||
};
|
};
|
||||||
|
|
||||||
$scope.annotationsPromise = annotationsSrv.getAnnotations($scope.rangeUnparsed);
|
$scope.annotationsPromise = annotationsSrv.getAnnotations($scope.rangeUnparsed);
|
||||||
|
|
||||||
return $scope.datasource.query(graphiteQuery)
|
return $scope.datasource.query(graphiteQuery)
|
||||||
.then($scope.receiveGraphiteData)
|
.then($scope.dataHandler)
|
||||||
.then(null, function(err) {
|
.then(null, function(err) {
|
||||||
$scope.panel.error = err.message || "Graphite HTTP Request Error";
|
$scope.panel.error = err.message || "Graphite HTTP Request Error";
|
||||||
});
|
});
|
||||||
};
|
};
|
||||||
|
|
||||||
$scope.receiveGraphiteData = function(results) {
|
$scope.dataHandler = function(results) {
|
||||||
$scope.panelMeta.loading = false;
|
$scope.panelMeta.loading = false;
|
||||||
$scope.legend = [];
|
$scope.legend = [];
|
||||||
|
|
||||||
@@ -309,44 +276,11 @@ function (angular, app, $, _, kbn, moment, timeSeries) {
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
results = results.data;
|
|
||||||
var data = [];
|
|
||||||
|
|
||||||
$scope.datapointsWarning = false;
|
$scope.datapointsWarning = false;
|
||||||
$scope.datapointsCount = 0;
|
$scope.datapointsCount = 0;
|
||||||
$scope.datapointsOutside = false;
|
$scope.datapointsOutside = false;
|
||||||
|
|
||||||
_.each(results, function(targetData) {
|
var data = _.map(results.data, $scope.seriesHandler);
|
||||||
var datapoints = targetData.datapoints;
|
|
||||||
var alias = targetData.target;
|
|
||||||
var color = $scope.panel.aliasColors[alias] || $scope.colors[data.length];
|
|
||||||
var yaxis = $scope.panel.aliasYAxis[alias] || 1;
|
|
||||||
|
|
||||||
var seriesInfo = {
|
|
||||||
alias: alias,
|
|
||||||
color: color,
|
|
||||||
enable: true,
|
|
||||||
yaxis: yaxis
|
|
||||||
};
|
|
||||||
|
|
||||||
var series = new timeSeries.ZeroFilled({
|
|
||||||
datapoints: datapoints,
|
|
||||||
info: seriesInfo,
|
|
||||||
});
|
|
||||||
|
|
||||||
if (datapoints && datapoints.length > 0) {
|
|
||||||
var last = moment.utc(datapoints[datapoints.length - 1][1] * 1000);
|
|
||||||
var from = moment.utc($scope.range.from);
|
|
||||||
if (last - from < -1000) {
|
|
||||||
$scope.datapointsOutside = true;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
$scope.datapointsCount += datapoints.length;
|
|
||||||
|
|
||||||
$scope.legend.push(seriesInfo);
|
|
||||||
data.push(series);
|
|
||||||
});
|
|
||||||
|
|
||||||
$scope.datapointsWarning = $scope.datapointsCount || !$scope.datapointsOutside;
|
$scope.datapointsWarning = $scope.datapointsCount || !$scope.datapointsOutside;
|
||||||
|
|
||||||
@@ -359,54 +293,43 @@ function (angular, app, $, _, kbn, moment, timeSeries) {
|
|||||||
});
|
});
|
||||||
};
|
};
|
||||||
|
|
||||||
|
$scope.seriesHandler = function(seriesData, index) {
|
||||||
|
var datapoints = seriesData.datapoints;
|
||||||
|
var alias = seriesData.target;
|
||||||
|
var color = $scope.panel.aliasColors[alias] || $scope.colors[index];
|
||||||
|
var yaxis = $scope.panel.aliasYAxis[alias] || 1;
|
||||||
|
|
||||||
|
var seriesInfo = {
|
||||||
|
alias: alias,
|
||||||
|
color: color,
|
||||||
|
enable: true,
|
||||||
|
yaxis: yaxis
|
||||||
|
};
|
||||||
|
|
||||||
|
$scope.legend.push(seriesInfo);
|
||||||
|
|
||||||
|
var series = new timeSeries.ZeroFilled({
|
||||||
|
datapoints: datapoints,
|
||||||
|
info: seriesInfo,
|
||||||
|
});
|
||||||
|
|
||||||
|
if (datapoints && datapoints.length > 0) {
|
||||||
|
var last = moment.utc(datapoints[datapoints.length - 1][1] * 1000);
|
||||||
|
var from = moment.utc($scope.range.from);
|
||||||
|
if (last - from < -10000) {
|
||||||
|
$scope.datapointsOutside = true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
$scope.datapointsCount += datapoints.length;
|
||||||
|
|
||||||
|
return series;
|
||||||
|
};
|
||||||
|
|
||||||
$scope.add_target = function() {
|
$scope.add_target = function() {
|
||||||
$scope.panel.targets.push({target: ''});
|
$scope.panel.targets.push({target: ''});
|
||||||
};
|
};
|
||||||
|
|
||||||
$scope.enterFullscreenMode = function(options) {
|
|
||||||
var docHeight = $(window).height();
|
|
||||||
var editHeight = Math.floor(docHeight * 0.3);
|
|
||||||
var fullscreenHeight = Math.floor(docHeight * 0.7);
|
|
||||||
var oldTimeRange = $scope.range;
|
|
||||||
|
|
||||||
$scope.height = options.edit ? editHeight : fullscreenHeight;
|
|
||||||
$scope.editMode = options.edit;
|
|
||||||
|
|
||||||
if (!$scope.fullscreen) {
|
|
||||||
var closeEditMode = $rootScope.$on('panel-fullscreen-exit', function() {
|
|
||||||
$scope.editMode = false;
|
|
||||||
$scope.fullscreen = false;
|
|
||||||
delete $scope.height;
|
|
||||||
|
|
||||||
closeEditMode();
|
|
||||||
|
|
||||||
$timeout(function() {
|
|
||||||
$scope.$emit('render');
|
|
||||||
|
|
||||||
if (oldTimeRange !== $scope.range) {
|
|
||||||
$scope.dashboard.refresh();
|
|
||||||
}
|
|
||||||
});
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
$(window).scrollTop(0);
|
|
||||||
|
|
||||||
$scope.fullscreen = true;
|
|
||||||
$rootScope.$emit('panel-fullscreen-enter');
|
|
||||||
|
|
||||||
$timeout($scope.render);
|
|
||||||
};
|
|
||||||
|
|
||||||
$scope.openConfigureModal = function() {
|
|
||||||
if ($scope.editMode) {
|
|
||||||
$rootScope.$emit('panel-fullscreen-exit');
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
$scope.enterFullscreenMode({edit: true});
|
|
||||||
};
|
|
||||||
|
|
||||||
$scope.otherPanelInFullscreenMode = function() {
|
$scope.otherPanelInFullscreenMode = function() {
|
||||||
return $rootScope.fullscreen && !$scope.fullscreen;
|
return $rootScope.fullscreen && !$scope.fullscreen;
|
||||||
};
|
};
|
||||||
@@ -421,36 +344,6 @@ function (angular, app, $, _, kbn, moment, timeSeries) {
|
|||||||
$scope.render();
|
$scope.render();
|
||||||
};
|
};
|
||||||
|
|
||||||
$scope.duplicate = function(addToRow) {
|
|
||||||
addToRow = addToRow || $scope.row;
|
|
||||||
var currentRowSpan = $scope.rowSpan(addToRow);
|
|
||||||
if (currentRowSpan <= 9) {
|
|
||||||
addToRow.panels.push(angular.copy($scope.panel));
|
|
||||||
}
|
|
||||||
else {
|
|
||||||
var rowsList = $scope.dashboard.current.rows;
|
|
||||||
var rowIndex = _.indexOf(rowsList, addToRow);
|
|
||||||
if (rowIndex === rowsList.length - 1) {
|
|
||||||
var newRow = angular.copy($scope.row);
|
|
||||||
newRow.panels = [];
|
|
||||||
$scope.dashboard.current.rows.push(newRow);
|
|
||||||
$scope.duplicate(newRow);
|
|
||||||
}
|
|
||||||
else {
|
|
||||||
$scope.duplicate(rowsList[rowIndex+1]);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
$scope.toggleFullscreen = function() {
|
|
||||||
if ($scope.fullscreen && !$scope.editMode) {
|
|
||||||
$rootScope.$emit('panel-fullscreen-exit');
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
$scope.enterFullscreenMode({edit: false});
|
|
||||||
};
|
|
||||||
|
|
||||||
$scope.toggleSeries = function(info) {
|
$scope.toggleSeries = function(info) {
|
||||||
if ($scope.hiddenSeries[info.alias]) {
|
if ($scope.hiddenSeries[info.alias]) {
|
||||||
delete $scope.hiddenSeries[info.alias];
|
delete $scope.hiddenSeries[info.alias];
|
||||||
@@ -473,11 +366,6 @@ function (angular, app, $, _, kbn, moment, timeSeries) {
|
|||||||
$scope.render();
|
$scope.render();
|
||||||
};
|
};
|
||||||
|
|
||||||
$scope.updateColumnSpan = function(span) {
|
|
||||||
$scope.panel.span = span;
|
|
||||||
$timeout($scope.render);
|
|
||||||
};
|
|
||||||
|
|
||||||
});
|
});
|
||||||
|
|
||||||
|
|
||||||
|
|||||||
@@ -50,7 +50,12 @@ function (_, kbn) {
|
|||||||
result.push([currentTime * 1000, currentValue]);
|
result.push([currentTime * 1000, currentValue]);
|
||||||
}, this);
|
}, this);
|
||||||
|
|
||||||
|
if (result.length > 2) {
|
||||||
|
this.info.timeStep = result[1][0] - result[0][0];
|
||||||
|
}
|
||||||
|
|
||||||
if (result.length) {
|
if (result.length) {
|
||||||
|
|
||||||
this.info.avg = (this.info.total / result.length);
|
this.info.avg = (this.info.total / result.length);
|
||||||
this.info.current = result[result.length-1][1];
|
this.info.current = result[result.length-1][1];
|
||||||
|
|
||||||
|
|||||||
@@ -24,7 +24,6 @@ function (angular, app, _, require) {
|
|||||||
|
|
||||||
module.controller('text', function($scope) {
|
module.controller('text', function($scope) {
|
||||||
$scope.panelMeta = {
|
$scope.panelMeta = {
|
||||||
status : "Stable",
|
|
||||||
description : "A static text panel that can use plain text, markdown, or (sanitized) HTML"
|
description : "A static text panel that can use plain text, markdown, or (sanitized) HTML"
|
||||||
};
|
};
|
||||||
|
|
||||||
@@ -45,6 +44,7 @@ function (angular, app, _, require) {
|
|||||||
_.defaults($scope.panel,_d);
|
_.defaults($scope.panel,_d);
|
||||||
|
|
||||||
$scope.init = function() {
|
$scope.init = function() {
|
||||||
|
$scope.initPanel($scope);
|
||||||
$scope.ready = false;
|
$scope.ready = false;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|||||||
@@ -1,4 +1,5 @@
|
|||||||
<div class="editor-row">
|
|
||||||
|
<div class="editor-row" style="margin-top: 10px;">
|
||||||
|
|
||||||
<div ng-repeat="target in panel.targets"
|
<div ng-repeat="target in panel.targets"
|
||||||
class="grafana-target"
|
class="grafana-target"
|
||||||
@@ -52,7 +53,7 @@
|
|||||||
</ul>
|
</ul>
|
||||||
|
|
||||||
<input type="text"
|
<input type="text"
|
||||||
class="grafana-target-text-input"
|
class="grafana-target-text-input span12"
|
||||||
ng-model="target.target"
|
ng-model="target.target"
|
||||||
focus-me="showTextEditor"
|
focus-me="showTextEditor"
|
||||||
spellcheck='false'
|
spellcheck='false'
|
||||||
@@ -75,7 +76,7 @@
|
|||||||
</ul>
|
</ul>
|
||||||
</li>
|
</li>
|
||||||
<li ng-repeat="func in functions">
|
<li ng-repeat="func in functions">
|
||||||
<a class="grafana-target-segment grafana-target-function dropdown-toggle" bs-popover="'app/panels/graphite/funcEditor.html'" data-placement="bottom">
|
<a class="grafana-target-segment grafana-target-function dropdown-toggle" bs-popover="'app/partials/graphite/funcEditor.html'" data-placement="bottom">
|
||||||
{{func.text}}
|
{{func.text}}
|
||||||
</a>
|
</a>
|
||||||
</li>
|
</li>
|
||||||
@@ -111,6 +112,3 @@
|
|||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div class="editor-row" style="margin-top: 20px" ng-show="editor.index == 1">
|
|
||||||
<button class="btn btn-success pull-right" ng-click="add_target(panel.target)">Add target</button>
|
|
||||||
</div>
|
|
||||||
93
src/app/partials/influxdb/editor.html
Normal file
93
src/app/partials/influxdb/editor.html
Normal file
@@ -0,0 +1,93 @@
|
|||||||
|
|
||||||
|
<div class="editor-row" style="margin-top: 10px;">
|
||||||
|
|
||||||
|
<div ng-repeat="target in panel.targets"
|
||||||
|
class="grafana-target"
|
||||||
|
ng-class="{'grafana-target-hidden': target.hide}"
|
||||||
|
ng-controller="InfluxTargetCtrl"
|
||||||
|
ng-init="init()">
|
||||||
|
|
||||||
|
<div class="grafana-target-inner-wrapper">
|
||||||
|
<div class="grafana-target-inner">
|
||||||
|
<ul class="grafana-target-controls">
|
||||||
|
<li class="dropdown">
|
||||||
|
<a class="pointer dropdown-toggle"
|
||||||
|
data-toggle="dropdown"
|
||||||
|
tabindex="1">
|
||||||
|
<i class="icon-cog"></i>
|
||||||
|
</a>
|
||||||
|
<ul class="dropdown-menu pull-right" role="menu">
|
||||||
|
<li role="menuitem">
|
||||||
|
<a tabindex="1"
|
||||||
|
ng-click="duplicate()">
|
||||||
|
Duplicate
|
||||||
|
</a>
|
||||||
|
</li>
|
||||||
|
</ul>
|
||||||
|
</li>
|
||||||
|
<li>
|
||||||
|
<a class="pointer" tabindex="1" ng-click="removeTarget(target)">
|
||||||
|
<i class="icon-remove"></i>
|
||||||
|
</a>
|
||||||
|
</li>
|
||||||
|
</ul>
|
||||||
|
|
||||||
|
<ul class="grafana-target-controls-left">
|
||||||
|
<li>
|
||||||
|
<a class="grafana-target-segment"
|
||||||
|
ng-click="target.hide = !target.hide; get_data();"
|
||||||
|
role="menuitem">
|
||||||
|
<i class="icon-eye-open"></i>
|
||||||
|
</a>
|
||||||
|
</li>
|
||||||
|
</ul>
|
||||||
|
|
||||||
|
<ul class="grafana-segment-list" role="menu">
|
||||||
|
<li class="grafana-target-segment">
|
||||||
|
from series
|
||||||
|
</li>
|
||||||
|
<li>
|
||||||
|
<input type="text"
|
||||||
|
class="input-medium grafana-target-segment-input"
|
||||||
|
ng-model="target.series"
|
||||||
|
spellcheck='false'
|
||||||
|
placeholder="series name"
|
||||||
|
ng-model-onblur ng-change="get_data()" >
|
||||||
|
</li>
|
||||||
|
<li class="grafana-target-segment">
|
||||||
|
select
|
||||||
|
</li>
|
||||||
|
<li>
|
||||||
|
<input type="text"
|
||||||
|
class="input-medium grafana-target-segment-input"
|
||||||
|
ng-model="target.column"
|
||||||
|
placeholder="value column"
|
||||||
|
spellcheck='false'
|
||||||
|
ng-model-onblur ng-change="get_data()" >
|
||||||
|
</li>
|
||||||
|
<li class="grafana-target-segment">
|
||||||
|
function
|
||||||
|
</li>
|
||||||
|
<li>
|
||||||
|
<select class="input-medium grafana-target-segment-input" ng-change="get_data()" ng-model="target.function" ng-options="f for f in ['mean', 'sum', 'min', 'max', 'median', 'derivative', 'stddev']" ></select>
|
||||||
|
</li>
|
||||||
|
<li class="grafana-target-segment">
|
||||||
|
group by time
|
||||||
|
</li>
|
||||||
|
<li>
|
||||||
|
<input type="text"
|
||||||
|
class="input-mini grafana-target-segment-input"
|
||||||
|
ng-model="target.interval"
|
||||||
|
placeholder="{{interval}}"
|
||||||
|
bs-tooltip="'Leave blank for auto handling based on time range and panel width'"
|
||||||
|
spellcheck='false'
|
||||||
|
ng-model-onblur ng-change="get_data()" >
|
||||||
|
</li>
|
||||||
|
</ul>
|
||||||
|
|
||||||
|
<div class="clearfix"></div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
17
src/app/partials/metrics.html
Normal file
17
src/app/partials/metrics.html
Normal file
@@ -0,0 +1,17 @@
|
|||||||
|
<div ng-include src="datasource.editorSrc"></div>
|
||||||
|
|
||||||
|
|
||||||
|
<div class="editor-row" style="margin-top: 20px">
|
||||||
|
<button class="btn btn-success pull-right" ng-click="add_target(panel.target)">Add query</button>
|
||||||
|
|
||||||
|
<div class="btn-group pull-right" style="margin-right: 10px;">
|
||||||
|
<button class="btn btn-info dropdown-toggle" data-toggle="dropdown" bs-tooltip="'Datasource'">{{datasource.name}} <span class="caret"></span></button>
|
||||||
|
|
||||||
|
<ul class="dropdown-menu" role="menu">
|
||||||
|
<li ng-repeat="datasource in datasources" role="menuitem">
|
||||||
|
<a ng-click="setDatasource(datasource.value);">{{datasource.name}}</a>
|
||||||
|
</li>
|
||||||
|
</ul>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
</div>
|
||||||
@@ -1,6 +1,6 @@
|
|||||||
<div ng-include="'app/partials/panelgeneral.html'"></div>
|
<div ng-include="'app/partials/panelgeneral.html'"></div>
|
||||||
<div ng-include="edit_path(panel.type)"></div>
|
<div ng-if="!panelMeta.fullEditorTabs" ng-include="edit_path(panel.type)"></div>
|
||||||
<div ng-repeat="tab in panelMeta.editorTabs">
|
<div ng-repeat="tab in panelMeta.editorTabs">
|
||||||
<h5>{{tab.title}}</h5>
|
<h5>{{tab.title}}</h5>
|
||||||
<div ng-include="tab.src"></div>
|
<div ng-include="tab.src"></div>
|
||||||
</div>
|
</div>
|
||||||
@@ -1,10 +1,6 @@
|
|||||||
<div class="editor-row">
|
<div class="editor-row">
|
||||||
<div class="section">
|
|
||||||
<strong>{{panelMeta.status}}</strong> // <span ng-bind-html="panelMeta.description"></span>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
<div class="editor-row">
|
|
||||||
<div class="section">
|
<div class="section">
|
||||||
|
<h5>General options</h5>
|
||||||
<div class="editor-option">
|
<div class="editor-option">
|
||||||
<label class="small">Title</label><input type="text" class="input-medium" ng-model='panel.title'></input>
|
<label class="small">Title</label><input type="text" class="input-medium" ng-model='panel.title'></input>
|
||||||
</div>
|
</div>
|
||||||
@@ -21,4 +17,4 @@
|
|||||||
<input type="checkbox" ng-model="panel.spyable" ng-checked="panel.spyable">
|
<input type="checkbox" ng-model="panel.spyable" ng-checked="panel.spyable">
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
@@ -7,6 +7,5 @@ define([
|
|||||||
'./datasourceSrv',
|
'./datasourceSrv',
|
||||||
'./keyboardManager',
|
'./keyboardManager',
|
||||||
'./annotationsSrv',
|
'./annotationsSrv',
|
||||||
'./graphite/graphiteDatasource',
|
|
||||||
],
|
],
|
||||||
function () {});
|
function () {});
|
||||||
@@ -1,25 +1,35 @@
|
|||||||
define([
|
define([
|
||||||
'angular',
|
'angular',
|
||||||
'underscore',
|
'underscore',
|
||||||
'config'
|
'config',
|
||||||
|
'./graphite/graphiteDatasource',
|
||||||
|
'./influxdb/influxdbDatasource',
|
||||||
],
|
],
|
||||||
function (angular, _, config) {
|
function (angular, _, config) {
|
||||||
'use strict';
|
'use strict';
|
||||||
|
|
||||||
var module = angular.module('kibana.services');
|
var module = angular.module('kibana.services');
|
||||||
|
|
||||||
module.service('datasourceSrv', function($q, filterSrv, $http, GraphiteDatasource) {
|
module.service('datasourceSrv', function($q, filterSrv, $http, GraphiteDatasource, InfluxDatasource) {
|
||||||
|
|
||||||
var defaultDatasource = _.findWhere(_.values(config.datasources), { default: true } );
|
var defaultDatasource = _.findWhere(_.values(config.datasources), { default: true } );
|
||||||
|
|
||||||
this.default = new GraphiteDatasource(defaultDatasource);
|
this.default = new GraphiteDatasource(defaultDatasource);
|
||||||
|
|
||||||
this.get = function(name) {
|
this.get = function(name) {
|
||||||
if (!name) {
|
if (!name) { return this.default; }
|
||||||
return this.default;
|
|
||||||
|
var ds = config.datasources[name];
|
||||||
|
if (!ds) {
|
||||||
|
return null;
|
||||||
}
|
}
|
||||||
|
|
||||||
return new GraphiteDatasource(config.datasources[name]);
|
switch(ds.type) {
|
||||||
|
case 'graphite':
|
||||||
|
return new GraphiteDatasource(ds);
|
||||||
|
case 'influxdb':
|
||||||
|
return new InfluxDatasource(ds);
|
||||||
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
this.listOptions = function() {
|
this.listOptions = function() {
|
||||||
|
|||||||
@@ -17,6 +17,8 @@ function (angular, _, $, config, kbn, moment) {
|
|||||||
this.type = 'graphite';
|
this.type = 'graphite';
|
||||||
this.basicAuth = datasource.basicAuth;
|
this.basicAuth = datasource.basicAuth;
|
||||||
this.url = datasource.url;
|
this.url = datasource.url;
|
||||||
|
this.editorSrc = 'app/partials/graphite/editor.html';
|
||||||
|
this.name = datasource.name;
|
||||||
}
|
}
|
||||||
|
|
||||||
GraphiteDatasource.prototype.query = function(options) {
|
GraphiteDatasource.prototype.query = function(options) {
|
||||||
|
|||||||
137
src/app/services/influxdb/influxdbDatasource.js
Normal file
137
src/app/services/influxdb/influxdbDatasource.js
Normal file
@@ -0,0 +1,137 @@
|
|||||||
|
define([
|
||||||
|
'angular',
|
||||||
|
'underscore',
|
||||||
|
'kbn'
|
||||||
|
],
|
||||||
|
function (angular, _, kbn) {
|
||||||
|
'use strict';
|
||||||
|
|
||||||
|
var module = angular.module('kibana.services');
|
||||||
|
|
||||||
|
module.factory('InfluxDatasource', function($q, $http) {
|
||||||
|
|
||||||
|
function InfluxDatasource(datasource) {
|
||||||
|
this.type = 'influxDB';
|
||||||
|
this.editorSrc = 'app/partials/influxDB/editor.html';
|
||||||
|
this.url = datasource.url;
|
||||||
|
this.username = datasource.username;
|
||||||
|
this.password = datasource.password;
|
||||||
|
this.name = datasource.name;
|
||||||
|
|
||||||
|
this.templateSettings = {
|
||||||
|
interpolate : /\[\[([\s\S]+?)\]\]/g,
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
InfluxDatasource.prototype.query = function(options) {
|
||||||
|
|
||||||
|
var promises = _.map(options.targets, function(target) {
|
||||||
|
if (!target.series || !target.column || target.hide) {
|
||||||
|
return [];
|
||||||
|
}
|
||||||
|
|
||||||
|
var template = "select [[func]]([[column]]) from [[series]] where [[timeFilter]] group by time([[interval]]) order asc";
|
||||||
|
|
||||||
|
var templateData = {
|
||||||
|
series: target.series,
|
||||||
|
column: target.column,
|
||||||
|
func: target.function,
|
||||||
|
timeFilter: getTimeFilter(options),
|
||||||
|
interval: target.interval || options.interval
|
||||||
|
};
|
||||||
|
|
||||||
|
var query = _.template(template, templateData, this.templateSettings);
|
||||||
|
console.log(query);
|
||||||
|
|
||||||
|
return this.doInfluxRequest(query).then(handleInfluxQueryResponse);
|
||||||
|
|
||||||
|
}, this);
|
||||||
|
|
||||||
|
return $q.all(promises).then(function(results) {
|
||||||
|
|
||||||
|
return { data: _.flatten(results) };
|
||||||
|
});
|
||||||
|
|
||||||
|
};
|
||||||
|
|
||||||
|
InfluxDatasource.prototype.doInfluxRequest = function(query) {
|
||||||
|
var params = {
|
||||||
|
u: this.username,
|
||||||
|
p: this.password,
|
||||||
|
q: query
|
||||||
|
};
|
||||||
|
|
||||||
|
var options = {
|
||||||
|
method: 'GET',
|
||||||
|
url: this.url + '/series',
|
||||||
|
params: params,
|
||||||
|
};
|
||||||
|
|
||||||
|
return $http(options);
|
||||||
|
};
|
||||||
|
|
||||||
|
function handleInfluxQueryResponse(results) {
|
||||||
|
var output = [];
|
||||||
|
|
||||||
|
_.each(results.data, function(series) {
|
||||||
|
var timeCol = series.columns.indexOf('time');
|
||||||
|
|
||||||
|
_.each(series.columns, function(column, index) {
|
||||||
|
if (column === "time" || column === "sequence_number") {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
console.log("series:"+series.name + ": "+series.points.length + " points");
|
||||||
|
|
||||||
|
var target = series.name + "." + column;
|
||||||
|
var datapoints = [];
|
||||||
|
|
||||||
|
for(var i = 0; i < series.points.length; i++) {
|
||||||
|
var t = Math.floor(series.points[i][timeCol] / 1000);
|
||||||
|
var v = series.points[i][index];
|
||||||
|
datapoints[i] = [v,t];
|
||||||
|
}
|
||||||
|
|
||||||
|
output.push({ target:target, datapoints:datapoints });
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
return output;
|
||||||
|
}
|
||||||
|
|
||||||
|
function getTimeFilter(options) {
|
||||||
|
var from = getInfluxTime(options.range.from);
|
||||||
|
var until = getInfluxTime(options.range.to);
|
||||||
|
|
||||||
|
if (until === 'now()') {
|
||||||
|
return 'time > now() - ' + from;
|
||||||
|
}
|
||||||
|
|
||||||
|
return 'time > ' + from + ' and time < ' + until;
|
||||||
|
}
|
||||||
|
|
||||||
|
function getInfluxTime(date) {
|
||||||
|
if (_.isString(date)) {
|
||||||
|
if (date === 'now') {
|
||||||
|
return 'now()';
|
||||||
|
}
|
||||||
|
else if (date.indexOf('now') >= 0) {
|
||||||
|
return date.substring(4);
|
||||||
|
}
|
||||||
|
|
||||||
|
date = kbn.parseDate(date);
|
||||||
|
}
|
||||||
|
|
||||||
|
return to_utc_epoch_seconds(date);
|
||||||
|
}
|
||||||
|
|
||||||
|
function to_utc_epoch_seconds(date) {
|
||||||
|
return (date.getTime() / 1000).toFixed(0) + 's';
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
return InfluxDatasource;
|
||||||
|
|
||||||
|
});
|
||||||
|
|
||||||
|
});
|
||||||
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
@@ -318,15 +318,32 @@
|
|||||||
|
|
||||||
input[type=text].grafana-target-text-input {
|
input[type=text].grafana-target-text-input {
|
||||||
padding: 5px 7px;
|
padding: 5px 7px;
|
||||||
border: 1px solid @grafanaTargetSegmentBorder;
|
border: none;
|
||||||
margin: 0px;
|
margin: 0px;
|
||||||
background: transparent;
|
background: transparent;
|
||||||
width: 80%;
|
|
||||||
float: left;
|
float: left;
|
||||||
color: @grafanaTargetColor;
|
color: @grafanaTargetColor;
|
||||||
border-radius: 0;
|
border-radius: 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
input[type=text].grafana-target-segment-input {
|
||||||
|
border: none;
|
||||||
|
border-right: 1px solid @grafanaTargetSegmentBorder;
|
||||||
|
margin: 0px;
|
||||||
|
border-radius: 0;
|
||||||
|
height: 22px;
|
||||||
|
line-height: 22px;
|
||||||
|
}
|
||||||
|
|
||||||
|
select.grafana-target-segment-input {
|
||||||
|
border: none;
|
||||||
|
border-right: 1px solid @grafanaTargetSegmentBorder;
|
||||||
|
margin: 0px;
|
||||||
|
border-radius: 0;
|
||||||
|
height: 30px;
|
||||||
|
line-height: 30px;
|
||||||
|
}
|
||||||
|
|
||||||
.grafana-target .dropdown {
|
.grafana-target .dropdown {
|
||||||
padding: 0; margin: 0;
|
padding: 0; margin: 0;
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user