Commit c472054f by Torkel Ödegaard

Merge branch 'master' into dashboard-export

parents df1e90c7 e61d0496
...@@ -8,6 +8,9 @@ ...@@ -8,6 +8,9 @@
* **Graph**: Fixed graph legend table mode and always visible scrollbar [#6828](https://github.com/grafana/grafana/issues/6828) * **Graph**: Fixed graph legend table mode and always visible scrollbar [#6828](https://github.com/grafana/grafana/issues/6828)
* **Templating**: Fixed template variable value groups/tags feature [#6752](https://github.com/grafana/grafana/issues/6752) * **Templating**: Fixed template variable value groups/tags feature [#6752](https://github.com/grafana/grafana/issues/6752)
## Enhancements
* **Elasticsearch**: Added support for all moving average options [#7154](https://github.com/grafana/grafana/pull/7154), thx [@vaibhavinbayarea](https://github.com/vaibhavinbayarea)
# 4.1-beta1 (2016-12-21) # 4.1-beta1 (2016-12-21)
### Enhancements ### Enhancements
......
...@@ -4,7 +4,7 @@ deps-go: ...@@ -4,7 +4,7 @@ deps-go:
go run build.go setup go run build.go setup
deps-js: deps-js:
npm install yarn install
deps: deps-go deps-js deps: deps-go deps-js
......
...@@ -114,7 +114,8 @@ To build less to css for the frontend you will need a recent version of **node ( ...@@ -114,7 +114,8 @@ To build less to css for the frontend you will need a recent version of **node (
npm (v2.5.0) and grunt (v0.4.5). Run the following: npm (v2.5.0) and grunt (v0.4.5). Run the following:
```bash ```bash
npm install npm install -g yarn
yarn install
npm run build npm run build
``` ```
......
...@@ -11,7 +11,8 @@ environment: ...@@ -11,7 +11,8 @@ environment:
install: install:
# install nodejs and npm # install nodejs and npm
- ps: Install-Product node $env:nodejs_version - ps: Install-Product node $env:nodejs_version
- npm install - npm install -g yarn
- yarn install
- npm install -g grunt-cli - npm install -g grunt-cli
# install gcc (needed for sqlite3) # install gcc (needed for sqlite3)
- choco install -y --limit-output mingw - choco install -y --limit-output mingw
......
machine: machine:
node: node:
version: 5.11.1 version: 6.9.2
environment: environment:
GOPATH: "/home/ubuntu/.go_workspace" GOPATH: "/home/ubuntu/.go_workspace"
ORG_PATH: "github.com/grafana" ORG_PATH: "github.com/grafana"
...@@ -22,10 +22,10 @@ test: ...@@ -22,10 +22,10 @@ test:
override: override:
- bash scripts/circle-test.sh - bash scripts/circle-test.sh
deployment: # deployment:
master: # master:
branch: master # branch: master
owner: grafana # owner: grafana
commands: # commands:
- ./scripts/trigger_grafana_packer.sh ${TRIGGER_GRAFANA_PACKER_CIRCLECI_TOKEN} # - ./scripts/trigger_grafana_packer.sh ${TRIGGER_GRAFANA_PACKER_CIRCLECI_TOKEN}
- ./scripts/trigger_windows_build.sh ${APPVEYOR_TOKEN} # - ./scripts/trigger_windows_build.sh ${APPVEYOR_TOKEN}
...@@ -40,7 +40,8 @@ To build less to css for the frontend you will need a recent version of node (v0 ...@@ -40,7 +40,8 @@ To build less to css for the frontend you will need a recent version of node (v0
npm (v2.5.0) and grunt (v0.4.5). Run the following: npm (v2.5.0) and grunt (v0.4.5). Run the following:
``` ```
npm install npm install -g yarn
yarn install
npm install -g grunt-cli npm install -g grunt-cli
grunt grunt
``` ```
......
...@@ -5,6 +5,7 @@ export class ConfigCtrl { ...@@ -5,6 +5,7 @@ export class ConfigCtrl {
appEditCtrl: any; appEditCtrl: any;
/** @ngInject **/
constructor(private backendSrv) { constructor(private backendSrv) {
this.appEditCtrl.setPreUpdateHook(this.initDatasource.bind(this)); this.appEditCtrl.setPreUpdateHook(this.initDatasource.bind(this));
} }
......
...@@ -73,7 +73,7 @@ function (angular, _, queryDef) { ...@@ -73,7 +73,7 @@ function (angular, _, queryDef) {
$scope.validateModel = function() { $scope.validateModel = function() {
$scope.index = _.indexOf(bucketAggs, $scope.agg); $scope.index = _.indexOf(bucketAggs, $scope.agg);
$scope.isFirst = $scope.index === 0; $scope.isFirst = $scope.index === 0;
$scope.isLast = $scope.index === bucketAggs.length - 1; $scope.bucketAggCount = bucketAggs.length;
var settingsLinkText = ""; var settingsLinkText = "";
var settings = $scope.agg.settings || {}; var settings = $scope.agg.settings || {};
......
...@@ -231,6 +231,7 @@ function (angular, _, moment, kbn, ElasticQueryBuilder, IndexPattern, ElasticRes ...@@ -231,6 +231,7 @@ function (angular, _, moment, kbn, ElasticQueryBuilder, IndexPattern, ElasticRes
this.getFields = function(query) { this.getFields = function(query) {
return this._get('/_mapping').then(function(result) { return this._get('/_mapping').then(function(result) {
var typeMap = { var typeMap = {
'float': 'number', 'float': 'number',
'double': 'number', 'double': 'number',
...@@ -238,13 +239,28 @@ function (angular, _, moment, kbn, ElasticQueryBuilder, IndexPattern, ElasticRes ...@@ -238,13 +239,28 @@ function (angular, _, moment, kbn, ElasticQueryBuilder, IndexPattern, ElasticRes
'long': 'number', 'long': 'number',
'date': 'date', 'date': 'date',
'string': 'string', 'string': 'string',
'text': 'text', 'text': 'string',
'scaled_float': 'number',
'nested': 'nested' 'nested': 'nested'
}; };
function shouldAddField(obj, key, query) {
if (key[0] === '_') {
return false;
}
if (!query.type) {
return true;
}
// equal query type filter, or via typemap translation
return query.type === obj.type || query.type === typeMap[obj.type];
}
// Store subfield names: [system, process, cpu, total] -> system.process.cpu.total // Store subfield names: [system, process, cpu, total] -> system.process.cpu.total
var fieldNameParts = []; var fieldNameParts = [];
var fields = {}; var fields = {};
function getFieldsRecursively(obj) { function getFieldsRecursively(obj) {
for (var key in obj) { for (var key in obj) {
var subObj = obj[key]; var subObj = obj[key];
...@@ -257,10 +273,7 @@ function (angular, _, moment, kbn, ElasticQueryBuilder, IndexPattern, ElasticRes ...@@ -257,10 +273,7 @@ function (angular, _, moment, kbn, ElasticQueryBuilder, IndexPattern, ElasticRes
var fieldName = fieldNameParts.concat(key).join('.'); var fieldName = fieldNameParts.concat(key).join('.');
// Hide meta-fields and check field type // Hide meta-fields and check field type
if (key[0] !== '_' && if (shouldAddField(subObj, key, query)) {
(!query.type ||
query.type && typeMap[subObj.type] === query.type)) {
fields[fieldName] = { fields[fieldName] = {
text: fieldName, text: fieldName,
type: subObj.type type: subObj.type
......
...@@ -29,6 +29,7 @@ function (angular, _, queryDef) { ...@@ -29,6 +29,7 @@ function (angular, _, queryDef) {
$scope.metricAggTypes = queryDef.getMetricAggTypes($scope.esVersion); $scope.metricAggTypes = queryDef.getMetricAggTypes($scope.esVersion);
$scope.extendedStats = queryDef.extendedStats; $scope.extendedStats = queryDef.extendedStats;
$scope.pipelineAggOptions = []; $scope.pipelineAggOptions = [];
$scope.modelSettingsValues = {};
$scope.init = function() { $scope.init = function() {
$scope.agg = metricAggs[$scope.index]; $scope.agg = metricAggs[$scope.index];
...@@ -95,6 +96,12 @@ function (angular, _, queryDef) { ...@@ -95,6 +96,12 @@ function (angular, _, queryDef) {
$scope.settingsLinkText = 'Stats: ' + stats.join(', '); $scope.settingsLinkText = 'Stats: ' + stats.join(', ');
break; break;
} }
case 'moving_avg': {
$scope.movingAvgModelTypes = queryDef.movingAvgModelOptions;
$scope.modelSettings = queryDef.getMovingAvgSettings($scope.agg.settings.model, true);
$scope.updateMovingAvgModelSettings();
break;
}
case 'raw_document': { case 'raw_document': {
$scope.target.metrics = [$scope.agg]; $scope.target.metrics = [$scope.agg];
$scope.target.bucketAggs = []; $scope.target.bucketAggs = [];
...@@ -127,6 +134,25 @@ function (angular, _, queryDef) { ...@@ -127,6 +134,25 @@ function (angular, _, queryDef) {
$scope.onChange(); $scope.onChange();
}; };
$scope.updateMovingAvgModelSettings = function () {
var modelSettingsKeys = [];
var modelSettings = queryDef.getMovingAvgSettings($scope.agg.settings.model, false);
for (var i=0; i < modelSettings.length; i++) {
modelSettingsKeys.push(modelSettings[i].value);
}
for (var key in $scope.agg.settings.settings) {
if (($scope.agg.settings.settings[key] === null) || (modelSettingsKeys.indexOf(key) === -1)) {
delete $scope.agg.settings.settings[key];
}
}
};
$scope.onChangeClearInternal = function() {
delete $scope.agg.settings.minimize;
$scope.onChange();
};
$scope.onTypeChange = function() { $scope.onTypeChange = function() {
$scope.agg.settings = {}; $scope.agg.settings = {};
$scope.agg.meta = {}; $scope.agg.meta = {};
......
...@@ -23,7 +23,7 @@ ...@@ -23,7 +23,7 @@
<label class="gf-form-label" ng-if="isFirst"> <label class="gf-form-label" ng-if="isFirst">
<a class="pointer" ng-click="addBucketAgg()"><i class="fa fa-plus"></i></a> <a class="pointer" ng-click="addBucketAgg()"><i class="fa fa-plus"></i></a>
</label> </label>
<label class="gf-form-label" ng-if="!isFirst"> <label class="gf-form-label" ng-if="bucketAggCount > 1">
<a class="pointer" ng-click="removeBucketAgg()"><i class="fa fa-minus"></i></a> <a class="pointer" ng-click="removeBucketAgg()"><i class="fa fa-minus"></i></a>
</label> </label>
</div> </div>
......
...@@ -37,52 +37,62 @@ ...@@ -37,52 +37,62 @@
</div> </div>
<div class="gf-form-group" ng-if="showOptions"> <div class="gf-form-group" ng-if="showOptions">
<div class="gf-form offset-width-7" ng-if="agg.type === 'derivative'"> <div class="gf-form offset-width-7" ng-if="agg.type === 'derivative'">
<label class="gf-form-label width-10">Unit</label> <label class="gf-form-label width-10">Unit</label>
<input type="text" class="gf-form-input max-width-12" ng-model="agg.settings.unit" ng-blur="onChangeInternal()" spellcheck='false'> <input type="text" class="gf-form-input max-width-12" ng-model="agg.settings.unit" ng-blur="onChangeInternal()" spellcheck='false'>
</div> </div>
<div class="gf-form offset-width-7" ng-if="agg.type === 'moving_avg'"> <div ng-if="agg.type === 'moving_avg'">
<label class="gf-form-label width-10">Window</label> <div class="gf-form offset-width-7">
<input type="number" class="gf-form-input max-width-12" ng-model="agg.settings.window" ng-blur="onChangeInternal()" spellcheck='false'> <label class="gf-form-label width-10">Model</label>
</div> <metric-segment-model property="agg.settings.model" options="movingAvgModelTypes" on-change="onChangeClearInternal()" custom="false" css-class="width-12"></metric-segment-model>
</div>
<div class="gf-form offset-width-7" ng-if="agg.type === 'moving_avg'"> <div class="gf-form offset-width-7">
<label class="gf-form-label width-10">Model</label> <label class="gf-form-label width-10">Window</label>
<input type="text" class="gf-form-input max-width-12" ng-change="onChangeInternal()" ng-model="agg.settings.model" blur="onChange()" spellcheck='false'> <input type="number" class="gf-form-input max-width-12" ng-model="agg.settings.window" ng-blur="onChangeInternal()" spellcheck='false'>
</div> </div>
<div class="gf-form offset-width-7" ng-if="agg.type === 'moving_avg'"> <div class="gf-form offset-width-7">
<label class="gf-form-label width-10">Predict</label> <label class="gf-form-label width-10">Predict</label>
<input type="number" class="gf-form-input max-width-12" ng-model="agg.settings.predict" ng-blur="onChangeInternal()" spellcheck='false'> <input type="number" class="gf-form-input max-width-12" ng-model="agg.settings.predict" ng-blur="onChangeInternal()" spellcheck='false'>
</div> </div>
<div class="gf-form offset-width-7" ng-if="agg.type === 'percentiles'">
<label class="gf-form-label width-10">Percentiles</label>
<input type="text" class="gf-form-input max-width-12" ng-model="agg.settings.percents" array-join ng-blur="onChange()"></input>
</div>
<div class="gf-form offset-width-7" ng-if="agg.type === 'cardinality'"> <div class="gf-form offset-width-7" ng-repeat="setting in modelSettings">
<label class="gf-form-label width-10">Precision threshold</label> <label class="gf-form-label width-10">{{setting.text}}</label>
<input type="number" class="gf-form-input max-width-12" ng-model="agg.settings.precision_threshold" ng-blur="onChange()"></input> <input type="number" class="gf-form-input max-width-12" ng-model="agg.settings.settings[setting.value]" ng-blur="onChangeInternal()" spellcheck='false'>
</div> </div>
<div ng-if="agg.type === 'extended_stats'"> <gf-form-switch ng-if="agg.settings.model == 'holt_winters'" class="gf-form offset-width-7" label="Pad" label-class="width-10" checked="agg.settings.settings.pad" on-change="onChangeInternal()"></gf-form-switch>
<gf-form-switch ng-repeat="stat in extendedStats" class="gf-form offset-width-7" label="{{stat.text}}" label-class="width-10" checked="agg.meta[stat.value]" on-change="onChangeInternal()"></gf-form-switch> <gf-form-switch ng-if="agg.settings.model.match('ewma|holt_winters|holt') !== null" class="gf-form offset-width-7" label="Minimize" label-class="width-10" checked="agg.settings.minimize" on-change="onChangeInternal()"></gf-form-switch>
</div>
<div class="gf-form offset-width-7"> <div class="gf-form offset-width-7" ng-if="agg.type === 'percentiles'">
<label class="gf-form-label width-10">Sigma</label> <label class="gf-form-label width-10">Percentiles</label>
<input type="number" class="gf-form-input max-width-12" placeholder="3" ng-model="agg.settings.sigma" ng-blur="onChange()"></input> <input type="text" class="gf-form-input max-width-12" ng-model="agg.settings.percents" array-join ng-blur="onChange()"></input>
</div> </div>
</div>
<div class="gf-form offset-width-7" ng-if="aggDef.supportsInlineScript"> <div class="gf-form offset-width-7" ng-if="agg.type === 'cardinality'">
<label class="gf-form-label width-10">Script</label> <label class="gf-form-label width-10">Precision threshold</label>
<input type="text" class="gf-form-input max-width-12" empty-to-null ng-model="agg.inlineScript" ng-blur="onChangeInternal()" spellcheck='false' placeholder="_value * 1"> <input type="number" class="gf-form-input max-width-12" ng-model="agg.settings.precision_threshold" ng-blur="onChange()"></input>
</div> </div>
<div ng-if="agg.type === 'extended_stats'">
<gf-form-switch ng-repeat="stat in extendedStats" class="gf-form offset-width-7" label="{{stat.text}}" label-class="width-10" checked="agg.meta[stat.value]" on-change="onChangeInternal()"></gf-form-switch>
<div class="gf-form offset-width-7">
<label class="gf-form-label width-10">Sigma</label>
<input type="number" class="gf-form-input max-width-12" placeholder="3" ng-model="agg.settings.sigma" ng-blur="onChange()"></input>
</div>
</div>
<div class="gf-form offset-width-7" ng-if="aggDef.supportsInlineScript">
<label class="gf-form-label width-10">Script</label>
<input type="text" class="gf-form-input max-width-12" empty-to-null ng-model="agg.inlineScript" ng-blur="onChangeInternal()" spellcheck='false' placeholder="_value * 1">
</div>
<div class="gf-form offset-width-7" ng-if="aggDef.supportsMissing"> <div class="gf-form offset-width-7" ng-if="aggDef.supportsMissing">
<label class="gf-form-label width-10"> <label class="gf-form-label width-10">
Missing Missing
<tip>The missing parameter defines how documents that are missing a value should be treated. By default they will be ignored but it is also possible to treat them as if they had a value</tip> <tip>The missing parameter defines how documents that are missing a value should be treated. By default they will be ignored but it is also possible to treat them as if they had a value</tip>
......
...@@ -69,17 +69,44 @@ function (_) { ...@@ -69,17 +69,44 @@ function (_) {
{text: '1d', value: '1d'}, {text: '1d', value: '1d'},
], ],
movingAvgModelOptions: [
{text: 'Simple', value: 'simple'},
{text: 'Linear', value: 'linear'},
{text: 'Exponentially Weighted', value: 'ewma'},
{text: 'Holt Linear', value: 'holt'},
{text: 'Holt Winters', value: 'holt_winters'},
],
pipelineOptions: { pipelineOptions: {
'moving_avg' : [ 'moving_avg' : [
{text: 'window', default: 5}, {text: 'window', default: 5},
{text: 'model', default: 'simple'}, {text: 'model', default: 'simple'},
{text: 'predict', default: 0} {text: 'predict', default: undefined},
{text: 'minimize', default: false},
], ],
'derivative': [ 'derivative': [
{text: 'unit', default: undefined}, {text: 'unit', default: undefined},
] ]
}, },
movingAvgModelSettings: {
'simple' : [],
'linear' : [],
'ewma' : [
{text: "Alpha", value: "alpha", default: undefined}],
'holt' : [
{text: "Alpha", value: "alpha", default: undefined},
{text: "Beta", value: "beta", default: undefined},
],
'holt_winters' : [
{text: "Alpha", value: "alpha", default: undefined},
{text: "Beta", value: "beta", default: undefined},
{text: "Gamma", value: "gamma", default: undefined},
{text: "Period", value: "period", default: undefined},
{text: "Pad", value: "pad", default: undefined, isCheckbox: true},
],
},
getMetricAggTypes: function(esVersion) { getMetricAggTypes: function(esVersion) {
return _.filter(this.metricAggTypes, function(f) { return _.filter(this.metricAggTypes, function(f) {
if (f.minVersion) { if (f.minVersion) {
...@@ -119,6 +146,19 @@ function (_) { ...@@ -119,6 +146,19 @@ function (_) {
return result; return result;
}, },
getMovingAvgSettings: function(model, filtered) {
var filteredResult = [];
if (filtered) {
_.each(this.movingAvgModelSettings[model], function(setting) {
if (!(setting.isCheckbox)) {
filteredResult.push(setting);
}
});
return filteredResult;
}
return this.movingAvgModelSettings[model];
},
getOrderByOptions: function(target) { getOrderByOptions: function(target) {
var self = this; var self = this;
var metricRefs = []; var metricRefs = [];
......
...@@ -13,8 +13,8 @@ function exit_if_fail { ...@@ -13,8 +13,8 @@ function exit_if_fail {
cd /home/ubuntu/.go_workspace/src/github.com/grafana/grafana cd /home/ubuntu/.go_workspace/src/github.com/grafana/grafana
rm -rf node_modules rm -rf node_modules
npm install -g npm npm install -g yarn
npm install yarn install
exit_if_fail npm test exit_if_fail npm test
exit_if_fail npm run coveralls exit_if_fail npm run coveralls
......
This source diff could not be displayed because it is too large. You can view the blob instead.
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