Commit 125ff957 by Torkel Ödegaard

Merge branch 'influxdb' Issue #103, influxdb support ready for testing and feedback

parents 6d59d217 73dcd9e1
......@@ -52,14 +52,17 @@ function (_, crypto) {
if (options.graphiteUrl) {
settings.datasources = {
graphite: {
name: 'default',
type: 'graphite',
url: options.graphiteUrl,
default: true
}
};
}
_.map(settings.datasources, parseBasicAuth);
_.each(settings.datasources, function(datasource, key) {
datasource.name = key;
parseBasicAuth(datasource);
});
var elasticParsed = parseBasicAuth({ url: settings.elasticsearch });
settings.elasticsearchBasicAuth = elasticParsed.basicAuth;
......
......@@ -8,4 +8,5 @@ define([
'./metricKeys',
'./graphiteTarget',
'./graphiteImport',
'./influxTargetCtrl',
], function () {});
\ No newline at end of file
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);
};
});
});
\ No newline at end of file
......@@ -9,109 +9,129 @@ function (angular, app, _) {
var module = angular.module('kibana.controllers');
module.controller('RowCtrl', function($scope, $rootScope, $timeout) {
var _d = {
title: "Row",
height: "150px",
collapse: false,
collapsable: true,
editable: true,
panels: [],
notice: false
};
_.defaults($scope.row,_d);
$scope.init = function() {
$scope.reset_panel();
};
$scope.toggle_row = function(row) {
if(!row.collapsable) {
return;
}
row.collapse = row.collapse ? false : true;
if (!row.collapse) {
$timeout(function() {
$scope.$broadcast('render');
});
} else {
row.notice = false;
}
};
$scope.rowSpan = function(row) {
var panels = _.filter(row.panels, function(p) {
return $scope.isPanel(p);
var _d = {
title: "Row",
height: "150px",
collapse: false,
collapsable: true,
editable: true,
panels: [],
notice: false
};
_.defaults($scope.row,_d);
$scope.init = function() {
$scope.reset_panel();
};
$scope.toggle_row = function(row) {
if(!row.collapsable) {
return;
}
row.collapse = row.collapse ? false : true;
if (!row.collapse) {
$timeout(function() {
$scope.$broadcast('render');
});
return _.reduce(_.pluck(panels,'span'), function(p,v) {
return p+v;
},0);
};
// This can be overridden by individual panels
$scope.close_edit = function() {
$scope.$broadcast('render');
};
$scope.add_panel = function(row,panel) {
$scope.row.panels.push(panel);
};
$scope.remove_panel_from_row = function(row, panel) {
if (confirm('Are you sure you want to remove this ' + panel.type + ' panel?')) {
row.panels = _.without(row.panels,panel);
} else {
row.notice = false;
}
};
$scope.rowSpan = function(row) {
var panels = _.filter(row.panels, function(p) {
return $scope.isPanel(p);
});
return _.reduce(_.pluck(panels,'span'), function(p,v) {
return p+v;
},0);
};
// This can be overridden by individual panels
$scope.close_edit = function() {
$scope.$broadcast('render');
};
$scope.add_panel = function(row,panel) {
$scope.row.panels.push(panel);
};
$scope.remove_panel_from_row = function(row, panel) {
if (confirm('Are you sure you want to remove this ' + panel.type + ' panel?')) {
row.panels = _.without(row.panels,panel);
}
};
$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
* [[panels]]
* = Panels
*
* [partintro]
* --
* *Kibana* dashboards are made up of blocks called +panels+. Panels are organized into rows
* and can serve many purposes, though most are designed to provide the results of a query or
* multiple queries as a visualization. Other panels may show collections of documents or
* allow you to insert instructions for your users.
*
* Panels can be configured easily via the Kibana web interface. For more advanced usage, such
* as templated or scripted dashboards, documentation of panel properties is available in this
* section. You may find settings here which are not exposed via the web interface.
*
* Each panel type has its own properties, hover there are several that are shared.
*
*/
$scope.reset_panel = function(type) {
var
defaultSpan = 4,
_as = 12-$scope.rowSpan($scope.row);
$scope.panel = {
error : false,
/** @scratch /panels/1
* span:: A number, 1-12, that describes the width of the panel.
*/
span : _as < defaultSpan && _as > 0 ? _as : defaultSpan,
/** @scratch /panels/1
* editable:: Enable or disable the edit button the the panel
*/
editable: true,
/** @scratch /panels/1
* type:: The type of panel this object contains. Each panel type will require additional
* properties. See the panel types list to the right.
*/
type : type
};
};
/** @scratch /panels/0
* [[panels]]
* = Panels
*
* [partintro]
* --
* *Kibana* dashboards are made up of blocks called +panels+. Panels are organized into rows
* and can serve many purposes, though most are designed to provide the results of a query or
* multiple queries as a visualization. Other panels may show collections of documents or
* allow you to insert instructions for your users.
*
* Panels can be configured easily via the Kibana web interface. For more advanced usage, such
* as templated or scripted dashboards, documentation of panel properties is available in this
* section. You may find settings here which are not exposed via the web interface.
*
* Each panel type has its own properties, hover there are several that are shared.
*
*/
$scope.reset_panel = function(type) {
var
defaultSpan = 4,
_as = 12-$scope.rowSpan($scope.row);
$scope.panel = {
error : false,
/** @scratch /panels/1
* span:: A number, 1-12, that describes the width of the panel.
*/
span : _as < defaultSpan && _as > 0 ? _as : defaultSpan,
/** @scratch /panels/1
* editable:: Enable or disable the edit button the the panel
*/
editable: true,
/** @scratch /panels/1
* type:: The type of panel this object contains. Each panel type will require additional
* properties. See the panel types list to the right.
*/
type : type
};
};
/** @scratch /panels/2
* --
*/
/** @scratch /panels/2
* --
*/
$scope.init();
$scope.init();
}
);
});
});
\ No newline at end of file
......@@ -69,8 +69,7 @@
"nullPointMode": "connected",
"steppedLine": false,
"tooltip": {
"value_type": "cumulative",
"query_as_alias": true
"value_type": "cumulative"
},
"targets": [
{
......
......@@ -32,5 +32,4 @@ function (angular, app, _) {
}
};
});
});
\ No newline at end of file
......@@ -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');
}
};
});
});
\ No newline at end of file
......@@ -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;
// Populate element
......@@ -96,7 +93,7 @@ function (angular, $, kbn, moment, _) {
bars: {
show: panel.bars,
fill: 1,
barWidth: barwidth/1.5,
barWidth: 1,
zero: false,
lineWidth: 0
},
......@@ -128,6 +125,10 @@ function (angular, $, kbn, moment, _) {
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);
addGridThresholds(options, panel);
addAnnotations(options);
......@@ -289,7 +290,7 @@ function (angular, $, kbn, moment, _) {
seriesInfo = item.series.info;
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;">' +
'<i class="icon-circle" style="color:'+item.series.color+';"></i>' + ' ' +
(seriesInfo.alias || seriesInfo.query)+
......
define([
'angular',
'jquery'
'jquery',
'underscore'
],
function (angular, $) {
function (angular, $, _) {
'use strict';
angular
.module('kibana.directives')
.directive('kibanaPanel', function($compile) {
.directive('kibanaPanel', function($compile, $timeout, $rootScope) {
var container = '<div class="panel-container"></div>';
var content = '<div class="panel-content"></div>';
......@@ -28,8 +29,8 @@ function (angular, $) {
'<i class="icon-spinner icon-spin icon-large"></i>' +
'</span>' +
'<span ng-if="panelMeta.menuItems" class="dropdown">' +
'<span class="panel-text panel-title pointer" bs-dropdown="panelMeta.menuItems" tabindex="1" ' +
'<span class="dropdown">' +
'<span class="panel-text panel-title pointer" gf-dropdown="panelMeta.menu" tabindex="1" ' +
'data-drag=true data-jqyoui-options="kbnJqUiDraggableOptions"'+
' jqyoui-draggable="'+
'{'+
......@@ -44,11 +45,6 @@ function (angular, $) {
'</span>' +
'</span>'+
'<span ng-if="!panelMeta.menuItems" config-modal="./app/partials/paneleditor.html" ' +
' class="panel-text panel-title pointer" >' +
'{{panel.title}}' +
'</span>'+
'</div>'+
'</div>\n'+
'</div>';
......@@ -88,8 +84,7 @@ function (angular, $) {
var nameAsPath = name.replace(".", "/");
$scope.require([
'jquery',
'text!panels/'+nameAsPath+'/module.html',
'text!panels/'+nameAsPath+'/editor.html'
'text!panels/'+nameAsPath+'/module.html'
], function ($, moduleTemplate) {
var $module = $(moduleTemplate);
// top level controllers
......@@ -101,9 +96,7 @@ function (angular, $) {
$controllers.first().prepend(panelHeader);
$controllers.first().find('.panel-header').nextAll().wrapAll(content);
$scope.require([
'panels/'+nameAsPath+'/module'
], function() {
$scope.require(['panels/' + nameAsPath + '/module'], function() {
loadModule($module);
});
} 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()"
style="min-height:{{panel.height || row.height}}"
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> |&nbsp
</span>
</div>
<div style="position: relative">
......@@ -27,27 +20,13 @@
</div>
<div class="panel-full-edit-tabs" ng-if="editMode">
<div ng-model="editor.index" bs-tabs>
<div ng-repeat="tab in editorTabs" data-title="{{tab}}">
</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 ng-include src="tab.src"></div>
<div ng-model="editor.index" bs-tabs>
<div ng-repeat="tab in editorTabs" data-title="{{tab}}">
</div>
</div>
<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>
</div>
</div>
\ No newline at end of file
......@@ -50,7 +50,12 @@ function (_, kbn) {
result.push([currentTime * 1000, currentValue]);
}, this);
if (result.length > 2) {
this.info.timeStep = result[1][0] - result[0][0];
}
if (result.length) {
this.info.avg = (this.info.total / result.length);
this.info.current = result[result.length-1][1];
......
......@@ -24,7 +24,6 @@ function (angular, app, _, require) {
module.controller('text', function($scope) {
$scope.panelMeta = {
status : "Stable",
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);
$scope.init = function() {
$scope.initPanel($scope);
$scope.ready = false;
};
......
<div class="editor-row">
<div class="editor-row" style="margin-top: 10px;">
<div ng-repeat="target in panel.targets"
class="grafana-target"
......@@ -52,7 +53,7 @@
</ul>
<input type="text"
class="grafana-target-text-input"
class="grafana-target-text-input span12"
ng-model="target.target"
focus-me="showTextEditor"
spellcheck='false'
......@@ -75,7 +76,7 @@
</ul>
</li>
<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}}
</a>
</li>
......@@ -111,6 +112,3 @@
</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>
\ No newline at end of file
<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>
<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>
<div ng-include="'app/partials/panelgeneral.html'"></div>
<div ng-include="edit_path(panel.type)"></div>
<div ng-repeat="tab in panelMeta.editorTabs">
<h5>{{tab.title}}</h5>
<div ng-include="tab.src"></div>
</div>
\ No newline at end of file
<div ng-include="'app/partials/panelgeneral.html'"></div>
<div ng-if="!panelMeta.fullEditorTabs" ng-include="edit_path(panel.type)"></div>
<div ng-repeat="tab in panelMeta.editorTabs">
<h5>{{tab.title}}</h5>
<div ng-include="tab.src"></div>
</div>
\ No newline at end of file
<div class="editor-row">
<div class="section">
<strong>{{panelMeta.status}}</strong> // <span ng-bind-html="panelMeta.description"></span>
<div class="editor-row">
<div class="section">
<h5>General options</h5>
<div class="editor-option">
<label class="small">Title</label><input type="text" class="input-medium" ng-model='panel.title'></input>
</div>
</div>
<div class="editor-row">
<div class="section">
<div class="editor-option">
<label class="small">Title</label><input type="text" class="input-medium" ng-model='panel.title'></input>
</div>
<div class="editor-option" ng-hide="panel.sizeable == false">
<label class="small">Span</label> <select class="input-mini" ng-model="panel.span" ng-options="f for f in [0,1,2,3,4,5,6,7,8,9,10,11,12]"></select>
</div>
<div class="editor-option">
<label class="small">Editable</label><input type="checkbox" ng-model="panel.editable" ng-checked="panel.editable">
</div>
<div class="editor-option" ng-show="!_.isUndefined(panel.spyable)">
<label class="small">
Inspect <i class="icon-question-sign" bs-tooltip="'Allow query reveal via <i class=icon-eye-open></i>'"></i>
</label>
<input type="checkbox" ng-model="panel.spyable" ng-checked="panel.spyable">
</div>
<div class="editor-option" ng-hide="panel.sizeable == false">
<label class="small">Span</label> <select class="input-mini" ng-model="panel.span" ng-options="f for f in [0,1,2,3,4,5,6,7,8,9,10,11,12]"></select>
</div>
<div class="editor-option">
<label class="small">Editable</label><input type="checkbox" ng-model="panel.editable" ng-checked="panel.editable">
</div>
</div>
\ No newline at end of file
<div class="editor-option" ng-show="!_.isUndefined(panel.spyable)">
<label class="small">
Inspect <i class="icon-question-sign" bs-tooltip="'Allow query reveal via <i class=icon-eye-open></i>'"></i>
</label>
<input type="checkbox" ng-model="panel.spyable" ng-checked="panel.spyable">
</div>
</div>
</div>
\ No newline at end of file
......@@ -7,6 +7,5 @@ define([
'./datasourceSrv',
'./keyboardManager',
'./annotationsSrv',
'./graphite/graphiteDatasource',
],
function () {});
\ No newline at end of file
define([
'angular',
'underscore',
'config'
'config',
'./graphite/graphiteDatasource',
'./influxdb/influxdbDatasource',
],
function (angular, _, config) {
'use strict';
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 } );
this.default = new GraphiteDatasource(defaultDatasource);
this.get = function(name) {
if (!name) {
return this.default;
if (!name) { 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() {
......
......@@ -17,6 +17,8 @@ function (angular, _, $, config, kbn, moment) {
this.type = 'graphite';
this.basicAuth = datasource.basicAuth;
this.url = datasource.url;
this.editorSrc = 'app/partials/graphite/editor.html';
this.name = datasource.name;
}
GraphiteDatasource.prototype.query = function(options) {
......
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;
});
});
This source diff could not be displayed because it is too large. You can view the blob instead.
This source diff could not be displayed because it is too large. You can view the blob instead.
......@@ -318,15 +318,32 @@
input[type=text].grafana-target-text-input {
padding: 5px 7px;
border: 1px solid @grafanaTargetSegmentBorder;
border: none;
margin: 0px;
background: transparent;
width: 80%;
float: left;
color: @grafanaTargetColor;
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 {
padding: 0; margin: 0;
}
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment