Compare commits

...

25 Commits

Author SHA1 Message Date
Torkel Ödegaard
0daadc954c fix(templating): fixed issue with detecting data source variable in other template variable definition, fixes #5165 2016-05-25 10:46:55 +02:00
bergquist
8c2b6b532d fix(preferences): fixes broken default home dashboard
closes #5167
2016-05-25 09:56:45 +02:00
Torkel Ödegaard
71d62012e1 fix(panel): fixed issue with fullscreen panel view and switching to another dashboard, fixes #5163 2016-05-25 09:46:15 +02:00
Torkel Ödegaard
6f094ef215 fix(templating): fixed issue with nested template variables and multi select, the child variable selection state is now updated like single select variables, so if none matches the first option is selected, fixes #4861 2016-05-24 09:50:23 +02:00
Torkel Ödegaard
c41c771e9a fix(home dashboard): fixed handling error when default dashboard is not found, fixes #5141 2016-05-24 07:39:58 +02:00
Torkel Ödegaard
f1ec270e3e bumped version to 3.0.4 2016-05-23 21:24:02 +02:00
Torkel Ödegaard
a3f2f574c6 fix(singlestat): fixed Gauge positioning issue, fixes #5143 2016-05-23 21:23:31 +02:00
Utkarsh Bhatnagar
ec19a97156 [Bug Fix] #5136 (#5142) 2016-05-23 21:00:06 +02:00
Torkel Ödegaard
9e6056551d docs(): updated download links 2016-05-23 14:13:30 +02:00
Torkel Ödegaard
9eec68985d docs(): updated download links 2016-05-23 11:49:07 +02:00
Torkel Ödegaard
dc77d555f9 fix(elasticsearch): fixed templating issue with elasticsearch, fixes #5135 2016-05-23 10:58:31 +02:00
Torkel Ödegaard
1da149d9e1 feat(annotations): annotations can now use a template variable as data source, closes #5054 2016-05-23 09:28:14 +02:00
Torkel Ödegaard
43ba563a1c fix(logging): change log level to trace for plugin proxy logging call, fixes #5126 2016-05-23 07:55:55 +02:00
Torkel Ödegaard
e6f251011f fix(panel span): fixed issue setting panel span while in fullscren and also an issue when changing repeat variable while in fullscreen view, fixes #4957 2016-05-20 15:15:02 +02:00
Torkel Ödegaard
f00cbc0aeb fix(panel height): fixed issue with singlestat height, fixes #4679, fixes #4894, fixes #5113 2016-05-20 12:23:26 +02:00
Torkel Ödegaard
ee86d24797 fix(elasticsearch): minor editor layout fix 2016-05-20 10:31:22 +02:00
Torkel Ödegaard
e0e8fd6637 fix(templating): fixed handling of numeric values in tempalting query results, fixes #5097 2016-05-20 10:24:24 +02:00
Torkel Ödegaard
2416ee04c8 fix(templating): fixed detection of nested template variables, fixes #5103 2016-05-20 09:58:07 +02:00
Torkel Ödegaard
df0ddc0b50 fix(prometheus): fixed bug in prometheus query editor, fixes #5107 2016-05-20 09:03:52 +02:00
Torkel Ödegaard
37821e6d63 fix(share modal): fixed link in share modal when sub app url is /dashboard/, fixes #5109 2016-05-20 08:31:27 +02:00
Torkel Ödegaard
d474eba53a fix(logging): fixed reading config level from config file, fixes #5079 2016-05-18 14:18:08 +02:00
Torkel Ödegaard
cd80884b76 fix(dashboard timepicker): fixed issue with time picker and UTC when reading time from url, fixes #5078 2016-05-18 12:01:11 +02:00
Mitsuhiro Tanda
8d2f350ad1 (prometheus) fix prometheus link 2016-05-18 07:50:30 +02:00
Torkel Ödegaard
99db89067e fix(): reverted RESTART_ON_UPGRADE change 2016-05-17 08:34:34 +02:00
Torkel Ödegaard
7f0fe31881 docs(): updated download links 2016-05-16 13:41:50 +02:00
32 changed files with 242 additions and 117 deletions

View File

@@ -10,13 +10,13 @@ page_keywords: grafana, installation, debian, ubuntu, guide
Description | Download
------------ | -------------
Stable .deb for Debian-based Linux | [grafana_3.0.1_amd64.deb](https://grafanarel.s3.amazonaws.com/builds/grafana_3.0.1_amd64.deb)
Stable .deb for Debian-based Linux | [grafana_3.0.3-1463994644.deb](https://grafanarel.s3.amazonaws.com/builds/grafana_3.0.3-1463994644_amd64.deb)
## Install Stable
$ wget https://grafanarel.s3.amazonaws.com/builds/grafana_3.0.1_amd64.deb
$ wget https://grafanarel.s3.amazonaws.com/builds/grafana_3.0.3-1463994644_amd64.deb
$ sudo apt-get install -y adduser libfontconfig
$ sudo dpkg -i grafana_3.0.1_amd64.deb
$ sudo dpkg -i grafana_3.0.3-1463994644_amd64.deb
## APT Repository

View File

@@ -10,24 +10,24 @@ page_keywords: grafana, installation, centos, fedora, opensuse, redhat, guide
Description | Download
------------ | -------------
Stable .RPM for CentOS / Fedora / OpenSuse / Redhat Linux | [grafana-3.0.1-1.x86_64.rpm](https://grafanarel.s3.amazonaws.com/builds/grafana-3.0.1-1.x86_64.rpm)
Stable .RPM for CentOS / Fedora / OpenSuse / Redhat Linux | [grafana-3.0.3-1463994644.x86_64.rpm](https://grafanarel.s3.amazonaws.com/builds/grafana-3.0.3-1463994644.x86_64.rpm)
## Install Stable Release from package file
You can install Grafana using Yum directly.
$ sudo yum install https://grafanarel.s3.amazonaws.com/builds/grafana-3.0.1-1.x86_64.rpm
$ sudo yum install https://grafanarel.s3.amazonaws.com/builds/grafana-3.0.3-1463994644.x86_64.rpm
Or install manually using `rpm`.
#### On CentOS / Fedora / Redhat:
$ sudo yum install initscripts fontconfig
$ sudo rpm -Uvh grafana-3.0.1-1.x86_64.rpm
$ sudo rpm -Uvh grafana-3.0.3-1463994644.x86_64.rpm
#### On OpenSuse:
$ sudo rpm -i --nodeps grafana-3.0.1-1.x86_64.rpm
$ sudo rpm -i --nodeps grafana-3.0.3-1463994644.x86_64.rpm
## Install via YUM Repository

View File

@@ -10,7 +10,7 @@ page_keywords: grafana, installation, windows guide
Description | Download
------------ | -------------
Stable Zip package for Windows | [grafana.2.6.0.windows-x64.zip](https://grafanarel.s3.amazonaws.com/winbuilds/dist/grafana-2.5.0.windows-x64.zip)
Stable Zip package for Windows | [grafana.3.0.3.windows-x64.zip](https://grafanarel.s3.amazonaws.com/winbuilds/dist/grafana-3.0.3.windows-x64.zip)
## Configure

View File

@@ -4,7 +4,7 @@
"company": "Coding Instinct AB"
},
"name": "grafana",
"version": "3.0.2",
"version": "3.0.4",
"repository": {
"type": "git",
"url": "http://github.com/grafana/grafana.git"

View File

@@ -14,6 +14,6 @@ CONF_DIR=/etc/grafana
CONF_FILE=/etc/grafana/grafana.ini
RESTART_ON_UPGRADE=true
RESTART_ON_UPGRADE=false
PLUGINS_DIR=/var/lib/grafana/plugins

View File

@@ -1,22 +1,20 @@
#! /usr/bin/env bash
deb_ver=3.0.1
rpm_ver=3.0.1-1
deb_ver=3.0.3-1463994644
rpm_ver=3.0.3-1463994644
#rpm_ver=3.0.0-1
wget https://grafanarel.s3.amazonaws.com/builds/grafana_${deb_ver}_amd64.deb
#wget https://grafanarel.s3.amazonaws.com/builds/grafana_${deb_ver}_amd64.deb
package_cloud push grafana/stable/debian/jessie grafana_${deb_ver}_amd64.deb
package_cloud push grafana/stable/debian/wheezy grafana_${deb_ver}_amd64.deb
#package_cloud push grafana/stable/debian/jessie grafana_${deb_ver}_amd64.deb
#package_cloud push grafana/stable/debian/wheezy grafana_${deb_ver}_amd64.deb
package_cloud push grafana/testing/debian/jessie grafana_${deb_ver}_amd64.deb
package_cloud push grafana/testing/debian/wheezy grafana_${deb_ver}_amd64.deb
#package_cloud push grafana/testing/debian/jessie grafana_${deb_ver}_amd64.deb
#package_cloud push grafana/testing/debian/wheezy grafana_${deb_ver}_amd64.deb
wget https://grafanarel.s3.amazonaws.com/builds/grafana-${rpm_ver}.x86_64.rpm
#wget https://grafanarel.s3.amazonaws.com/builds/grafana-${rpm_ver}.x86_64.rpm
#package_cloud push grafana/testing/el/6 grafana-${rpm_ver}.x86_64.rpm
#package_cloud push grafana/testing/el/7 grafana-${rpm_ver}.x86_64.rpm
package_cloud push grafana/testing/el/6 grafana-${rpm_ver}.x86_64.rpm
package_cloud push grafana/testing/el/7 grafana-${rpm_ver}.x86_64.rpm
package_cloud push grafana/stable/el/7 grafana-${rpm_ver}.x86_64.rpm
package_cloud push grafana/stable/el/6 grafana-${rpm_ver}.x86_64.rpm

View File

@@ -14,6 +14,6 @@ CONF_DIR=/etc/grafana
CONF_FILE=/etc/grafana/grafana.ini
RESTART_ON_UPGRADE=true
RESTART_ON_UPGRADE=false
PLUGINS_DIR=/var/lib/grafana/plugins

View File

@@ -209,7 +209,7 @@ func Register(r *macaron.Macaron) {
r.Combo("/db/:slug").Get(GetDashboard).Delete(DeleteDashboard)
r.Post("/db", reqEditorRole, bind(m.SaveDashboardCommand{}), PostDashboard)
r.Get("/file/:file", GetDashboardFromJsonFile)
r.Get("/home", GetHomeDashboard)
r.Get("/home", wrap(GetHomeDashboard))
r.Get("/tags", GetDashboardTags)
r.Post("/import", bind(dtos.ImportDashboardCommand{}), wrap(ImportDashboard))
})

View File

@@ -8,6 +8,7 @@ import (
"github.com/grafana/grafana/pkg/api/dtos"
"github.com/grafana/grafana/pkg/bus"
"github.com/grafana/grafana/pkg/log"
"github.com/grafana/grafana/pkg/metrics"
"github.com/grafana/grafana/pkg/middleware"
m "github.com/grafana/grafana/pkg/models"
@@ -158,30 +159,27 @@ func canEditDashboard(role m.RoleType) bool {
return role == m.ROLE_ADMIN || role == m.ROLE_EDITOR || role == m.ROLE_READ_ONLY_EDITOR
}
func GetHomeDashboard(c *middleware.Context) {
func GetHomeDashboard(c *middleware.Context) Response {
prefsQuery := m.GetPreferencesWithDefaultsQuery{OrgId: c.OrgId, UserId: c.UserId}
if err := bus.Dispatch(&prefsQuery); err != nil {
c.JsonApiErr(500, "Failed to get preferences", err)
return ApiError(500, "Failed to get preferences", err)
}
if prefsQuery.Result.HomeDashboardId != 0 {
slugQuery := m.GetDashboardSlugByIdQuery{Id: prefsQuery.Result.HomeDashboardId}
err := bus.Dispatch(&slugQuery)
if err != nil {
c.JsonApiErr(500, "Failed to get slug from database", err)
return
if err == nil {
dashRedirect := dtos.DashboardRedirect{RedirectUri: "db/" + slugQuery.Result}
return Json(200, &dashRedirect)
} else {
log.Warn("Failed to get slug from database, %s", err.Error())
}
dashRedirect := dtos.DashboardRedirect{RedirectUri: "db/" + slugQuery.Result}
c.JSON(200, &dashRedirect)
return
}
filePath := path.Join(setting.StaticRootPath, "dashboards/home.json")
file, err := os.Open(filePath)
if err != nil {
c.JsonApiErr(500, "Failed to load home dashboard", err)
return
return ApiError(500, "Failed to load home dashboard", err)
}
dash := dtos.DashboardFullWithMeta{}
@@ -189,11 +187,10 @@ func GetHomeDashboard(c *middleware.Context) {
dash.Meta.CanEdit = canEditDashboard(c.OrgRole)
jsonParser := json.NewDecoder(file)
if err := jsonParser.Decode(&dash.Dashboard); err != nil {
c.JsonApiErr(500, "Failed to load home dashboard", err)
return
return ApiError(500, "Failed to load home dashboard", err)
}
c.JSON(200, &dash)
return Json(200, &dash)
}
func GetDashboardFromJsonFile(c *middleware.Context) {

View File

@@ -88,7 +88,7 @@ func NewApiPluginProxy(ctx *middleware.Context, proxyPath string, route *plugins
}
for key, value := range headers {
log.Info("setting key %v value %v", key, value[0])
log.Trace("setting key %v value %v", key, value[0])
req.Header.Set(key, value[0])
}
}

View File

@@ -526,6 +526,17 @@ var logLevels = map[string]int{
"Critical": 5,
}
func getLogLevel(key string, defaultName string) (string, int) {
levelName := Cfg.Section(key).Key("level").In(defaultName, []string{"Trace", "Debug", "Info", "Warn", "Error", "Critical"})
level, ok := logLevels[levelName]
if !ok {
log.Fatal(4, "Unknown log level: %s", levelName)
}
return levelName, level
}
func initLogging(args *CommandLineArgs) {
//close any existing log handlers.
log.Close()
@@ -533,8 +544,12 @@ func initLogging(args *CommandLineArgs) {
LogModes = strings.Split(Cfg.Section("log").Key("mode").MustString("console"), ",")
LogsPath = makeAbsolute(Cfg.Section("paths").Key("logs").String(), HomePath)
defaultLevelName, _ := getLogLevel("log", "Info")
LogConfigs = make([]util.DynMap, len(LogModes))
for i, mode := range LogModes {
mode = strings.TrimSpace(mode)
sec, err := Cfg.GetSection("log." + mode)
if err != nil {
@@ -542,12 +557,7 @@ func initLogging(args *CommandLineArgs) {
}
// Log level.
levelName := Cfg.Section("log."+mode).Key("level").In("Trace",
[]string{"Trace", "Debug", "Info", "Warn", "Error", "Critical"})
level, ok := logLevels[levelName]
if !ok {
log.Fatal(4, "Unknown log level: %s", levelName)
}
_, level := getLogLevel("log."+mode, defaultLevelName)
// Generate log configuration.
switch mode {

View File

@@ -66,14 +66,17 @@ function (angular, _, coreModule, config) {
};
this.getAnnotationSources = function() {
return _.reduce(config.datasources, function(memo, value) {
var sources = [];
this.addDataSourceVariables(sources);
_.each(config.datasources, function(value) {
if (value.meta && value.meta.annotations) {
memo.push(value);
sources.push(value);
}
});
return memo;
}, []);
return sources;
};
this.getMetricSources = function(options) {
@@ -90,24 +93,7 @@ function (angular, _, coreModule, config) {
});
if (!options || !options.skipVariables) {
// look for data source variables
for (var i = 0; i < templateSrv.variables.length; i++) {
var variable = templateSrv.variables[i];
if (variable.type !== 'datasource') {
continue;
}
var first = variable.current.value;
var ds = config.datasources[first];
if (ds) {
metricSources.push({
name: '$' + variable.name,
value: '$' + variable.name,
meta: ds.meta,
});
}
}
this.addDataSourceVariables(metricSources);
}
metricSources.sort(function(a, b) {
@@ -123,6 +109,27 @@ function (angular, _, coreModule, config) {
return metricSources;
};
this.addDataSourceVariables = function(list) {
// look for data source variables
for (var i = 0; i < templateSrv.variables.length; i++) {
var variable = templateSrv.variables[i];
if (variable.type !== 'datasource') {
continue;
}
var first = variable.current.value;
var ds = config.datasources[first];
if (ds) {
list.push({
name: '$' + variable.name,
value: '$' + variable.name,
meta: ds.meta,
});
}
}
};
this.init();
});
});

View File

@@ -173,8 +173,8 @@ export default class TimeSeries {
isMsResolutionNeeded() {
for (var i = 0; i < this.datapoints.length; i++) {
if (this.datapoints[i][0] !== null) {
var timestamp = this.datapoints[i][0].toString();
if (this.datapoints[i][1] !== null) {
var timestamp = this.datapoints[i][1].toString();
if (timestamp.length === 13 && (timestamp % 1000) !== 0) {
return true;
}

View File

@@ -30,7 +30,7 @@ function (angular, _, $) {
$scope.datasourceChanged = function() {
return datasourceSrv.get($scope.currentAnnotation.datasource).then(function(ds) {
$scope.currentDatasource = ds;
$scope.currentAnnotation.datasource = ds.name;
$scope.currentAnnotation.datasource = $scope.currentAnnotation.datasource;
});
};

View File

@@ -142,12 +142,19 @@ function (angular, _, config) {
});
module.directive('panelWidth', function() {
return function(scope, element) {
var fullscreen = false;
function updateWidth() {
element[0].style.width = ((scope.panel.span / 1.2) * 10) + '%';
if (!fullscreen) {
element[0].style.width = ((scope.panel.span / 1.2) * 10) + '%';
}
}
scope.onAppEvent('panel-fullscreen-enter', function(evt, info) {
fullscreen = true;
if (scope.panel.id !== info.panelId) {
element.hide();
} else {
@@ -156,14 +163,20 @@ function (angular, _, config) {
});
scope.onAppEvent('panel-fullscreen-exit', function(evt, info) {
fullscreen = false;
if (scope.panel.id !== info.panelId) {
element.show();
} else {
updateWidth();
}
updateWidth();
});
scope.$watch('panel.span', updateWidth);
if (fullscreen) {
element.hide();
}
};
});

View File

@@ -70,7 +70,7 @@ function (angular, _, require, config) {
$scope.shareUrl = linkSrv.addParamsToUrl(baseUrl, params);
var soloUrl = $scope.shareUrl;
soloUrl = soloUrl.replace('/dashboard/', '/dashboard-solo/');
soloUrl = soloUrl.replace(config.appSubUrl + '/dashboard/', config.appSubUrl + '/dashboard-solo/');
soloUrl = soloUrl.replace("&fullscreen", "");
$scope.iframeHtml = '<iframe src="' + soloUrl + '" width="450" height="200" frameborder="0"></iframe>';

View File

@@ -50,7 +50,7 @@ define([
if (!isNaN(value)) {
var epoch = parseInt(value);
return moment(epoch);
return moment.utc(epoch);
}
return null;

View File

@@ -8,6 +8,7 @@ import $ from 'jquery';
const TITLE_HEIGHT = 25;
const EMPTY_TITLE_HEIGHT = 9;
const PANEL_PADDING = 5;
const PANEL_BORDER = 2;
import {Emitter} from 'app/core/core';
@@ -141,7 +142,7 @@ export class PanelCtrl {
}
}
this.height = this.containerHeight - (PANEL_PADDING + (this.panel.title ? TITLE_HEIGHT : EMPTY_TITLE_HEIGHT));
this.height = this.containerHeight - (PANEL_BORDER + PANEL_PADDING + (this.panel.title ? TITLE_HEIGHT : EMPTY_TITLE_HEIGHT));
}
render(payload?) {

View File

@@ -42,6 +42,16 @@ function (angular, _) {
return value.replace(/([\!\*\+\-\=<>\s\&\|\(\)\[\]\{\}\^\~\?\:\\/"])/g, "\\$1");
}
this.luceneFormat = function(value) {
if (typeof value === 'string') {
return luceneEscape(value);
}
var quotedValues = _.map(value, function(val) {
return '\"' + luceneEscape(val) + '\"';
});
return '(' + quotedValues.join(' OR ') + ')';
};
this.formatValue = function(value, format, variable) {
// for some scopedVars there is no variable
variable = variable || {};
@@ -60,13 +70,7 @@ function (angular, _) {
return '(' + escapedValues.join('|') + ')';
}
case "lucene": {
if (typeof value === 'string') {
return luceneEscape(value);
}
var quotedValues = _.map(value, function(val) {
return '\"' + luceneEscape(val) + '\"';
});
return '(' + quotedValues.join(' OR ') + ')';
return this.luceneFormat(value, format, variable);
}
case "pipe": {
if (typeof value === 'string') {
@@ -97,8 +101,11 @@ function (angular, _) {
if (!str) {
return false;
}
var match = this._regex.exec(str);
return match && (match[1] === variableName || match[2] === variableName);
variableName = regexEscape(variableName);
var findVarRegex = new RegExp('\\$(' + variableName + ')(?:\\W|$)|\\[\\[(' + variableName + ')\\]\\]', 'g');
var match = findVarRegex.exec(str);
return match !== null;
};
this.highlightVariablesAsHtml = function(str) {

View File

@@ -244,15 +244,26 @@ function (angular, _, kbn) {
this.validateVariableSelectionState = function(variable) {
if (!variable.current) {
if (!variable.options.length) { return; }
return self.setVariableValue(variable, variable.options[0], true);
return self.setVariableValue(variable, variable.options[0], false);
}
if (_.isArray(variable.current.value)) {
self.selectOptionsForCurrentValue(variable);
// updated selected value
var selected = {
value: _.map(_.filter(variable.options, {selected: true}), function(op) {
return op.value;
})
};
// if none pick first
if (selected.value.length === 0) {
selected = variable.options[0];
}
return self.setVariableValue(variable, selected, false);
} else {
var currentOption = _.findWhere(variable.options, {text: variable.current.text});
if (currentOption) {
return self.setVariableValue(variable, currentOption, true);
return self.setVariableValue(variable, currentOption, false);
} else {
if (!variable.options.length) { return; }
return self.setVariableValue(variable, variable.options[0]);
@@ -313,6 +324,14 @@ function (angular, _, kbn) {
var value = item.value || item.text;
var text = item.text || item.value;
if (_.isNumber(value)) {
value = value.toString();
}
if (_.isNumber(text)) {
text = text.toString();
}
if (regex) {
matches = regex.exec(value);
if (!matches) { continue; }

View File

@@ -78,7 +78,7 @@ function (angular, _, moment, kbn, ElasticQueryBuilder, IndexPattern, ElasticRes
range[timeField]["format"] = "epoch_millis";
}
var queryInterpolated = templateSrv.replace(queryString);
var queryInterpolated = templateSrv.replace(queryString, {}, 'lucene');
var filter = { "bool": { "must": [{ "range": range }] } };
var query = { "bool": { "should": [{ "query_string": { "query": queryInterpolated } }] } };
var data = {
@@ -204,6 +204,14 @@ function (angular, _, moment, kbn, ElasticQueryBuilder, IndexPattern, ElasticRes
});
};
function escapeForJson(value) {
return value.replace(/\"/g, '\\"');
}
function luceneThenJsonFormat(value) {
return escapeForJson(templateSrv.luceneFormat(value));
}
this.getFields = function(query) {
return this._get('/_mapping').then(function(res) {
var fields = {};
@@ -246,7 +254,7 @@ function (angular, _, moment, kbn, ElasticQueryBuilder, IndexPattern, ElasticRes
var header = this.getQueryHeader('count', range.from, range.to);
var esQuery = angular.toJson(this.queryBuilder.getTermsQuery(queryDef));
esQuery = esQuery.replace("$lucene_query", queryDef.query || '*');
esQuery = esQuery.replace("$lucene_query", escapeForJson(queryDef.query || '*'));
esQuery = esQuery.replace(/\$timeFrom/g, range.from.valueOf());
esQuery = esQuery.replace(/\$timeTo/g, range.to.valueOf());
esQuery = header + '\n' + esQuery + '\n';
@@ -260,7 +268,7 @@ function (angular, _, moment, kbn, ElasticQueryBuilder, IndexPattern, ElasticRes
};
this.metricFindQuery = function(query) {
query = templateSrv.replace(query);
query = templateSrv.replace(query, {}, luceneThenJsonFormat);
query = angular.fromJson(query);
if (!query) {
return $q.when([]);

View File

@@ -70,9 +70,9 @@
</div>
<div ng-if="agg.type === 'filters'">
<div class="gf-form-inline" ng-repeat="filter in agg.settings.filters" ng-class="{last: $last}">
<div class="gf-form-inline offset-width-7" ng-repeat="filter in agg.settings.filters">
<div class="gf-form">
<label class="gf-form-item width-10">Query {{$index + 1}}</label>
<label class="gf-form-label width-10">Query {{$index + 1}}</label>
<input type="text" class="gf-form-input max-width-12" ng-model="filter.query" spellcheck='false' placeholder="Lucene query" ng-blur="onChangeInternal()">
</div>
<div class="gf-form">
@@ -88,7 +88,7 @@
<div ng-if="agg.type === 'geohash_grid'">
<div class="gf-form offset-width-7">
<label class="gf-form-label">Precision</label>
<label class="gf-form-label width-10">Precision</label>
<input type="number" class="gf-form-input max-width-12" ng-model="agg.settings.precision" spellcheck='false' placeholder="3" ng-blur="onChangeInternal()">
</div>
</div>

View File

@@ -43,7 +43,7 @@ export function PrometheusDatasource(instanceSettings, $q, backendSrv, templateS
return value.replace(/[\\^$*+?.()|[\]{}]/g, '\\\\$&');
}
function interpolateQueryExpr(value, variable, defaultFormatFn) {
this.interpolateQueryExpr = function(value, variable, defaultFormatFn) {
// if no multi or include all do not regexEscape
if (!variable.multi && !variable.includeAll) {
return value;
@@ -59,6 +59,7 @@ export function PrometheusDatasource(instanceSettings, $q, backendSrv, templateS
// Called once per panel (graph)
this.query = function(options) {
var self = this;
var start = getPrometheusTime(options.range.from, false);
var end = getPrometheusTime(options.range.to, true);
@@ -73,7 +74,7 @@ export function PrometheusDatasource(instanceSettings, $q, backendSrv, templateS
activeTargets.push(target);
var query: any = {};
query.expr = templateSrv.replace(target.expr, options.scopedVars, interpolateQueryExpr);
query.expr = templateSrv.replace(target.expr, options.scopedVars, self.interpolateQueryExpr);
var interval = target.interval || options.interval;
var intervalFactor = target.intervalFactor || 1;
@@ -99,7 +100,6 @@ export function PrometheusDatasource(instanceSettings, $q, backendSrv, templateS
return this.performTimeSeriesQuery(query, start, end);
}, this));
var self = this;
return $q.all(allQueryPromise)
.then(function(allResponse) {
var result = [];
@@ -160,7 +160,7 @@ export function PrometheusDatasource(instanceSettings, $q, backendSrv, templateS
var interpolated;
try {
interpolated = templateSrv.replace(expr, {}, interpolateQueryExpr);
interpolated = templateSrv.replace(expr, {}, this.interpolateQueryExpr);
} catch (err) {
return $q.reject(err);
}

View File

@@ -58,10 +58,14 @@ class PrometheusQueryCtrl extends QueryCtrl {
updateLink() {
var range = this.panelCtrl.range;
if (!range) {
return;
}
var rangeDiff = Math.ceil((range.to.valueOf() - range.from.valueOf()) / 1000);
var endTime = range.to.utc().format('YYYY-MM-DD HH:mm');
var expr = {
expr: this.templateSrv.replace(this.target.expr, this.panelCtrl.panel.scopedVars),
expr: this.templateSrv.replace(this.target.expr, this.panelCtrl.panel.scopedVars, this.datasource.interpolateQueryExpr),
range_input: rangeDiff + 's',
end_input: endTime,
step_input: '',

View File

@@ -66,7 +66,7 @@ function (angular, $, moment, _, kbn, GraphTooltip) {
function getLegendHeight(panelHeight) {
if (!panel.legend.show || panel.legend.rightSide) {
return 2;
return 0;
}
if (panel.legend.alignAsTable) {

View File

@@ -22,8 +22,8 @@ describe('GraphCtrl', function() {
describe('msResolution with second resolution timestamps', function() {
beforeEach(function() {
var data = [
{ target: 'test.cpu1', datapoints: [[1234567890, 45], [1234567899, 60]]},
{ target: 'test.cpu2', datapoints: [[1236547890, 55], [1234456709, 90]]}
{ target: 'test.cpu1', datapoints: [[45, 1234567890], [60, 1234567899]]},
{ target: 'test.cpu2', datapoints: [[55, 1236547890], [90, 1234456709]]}
];
ctx.ctrl.panel.tooltip.msResolution = false;
ctx.ctrl.onDataReceived(data);
@@ -37,8 +37,8 @@ describe('GraphCtrl', function() {
describe('msResolution with millisecond resolution timestamps', function() {
beforeEach(function() {
var data = [
{ target: 'test.cpu1', datapoints: [[1234567890000, 45], [1234567899000, 60]]},
{ target: 'test.cpu2', datapoints: [[1236547890001, 55], [1234456709000, 90]]}
{ target: 'test.cpu1', datapoints: [[45, 1234567890000], [60, 1234567899000]]},
{ target: 'test.cpu2', datapoints: [[55, 1236547890001], [90, 1234456709000]]}
];
ctx.ctrl.panel.tooltip.msResolution = false;
ctx.ctrl.onDataReceived(data);
@@ -52,8 +52,8 @@ describe('GraphCtrl', function() {
describe('msResolution with millisecond resolution timestamps but with trailing zeroes', function() {
beforeEach(function() {
var data = [
{ target: 'test.cpu1', datapoints: [[1234567890000, 45], [1234567899000, 60]]},
{ target: 'test.cpu2', datapoints: [[1236547890000, 55], [1234456709000, 90]]}
{ target: 'test.cpu1', datapoints: [[45, 1234567890000], [60, 1234567899000]]},
{ target: 'test.cpu2', datapoints: [[55, 1236547890000], [90, 1234456709000]]}
];
ctx.ctrl.panel.tooltip.msResolution = false;
ctx.ctrl.onDataReceived(data);
@@ -67,9 +67,9 @@ describe('GraphCtrl', function() {
describe('msResolution with millisecond resolution timestamps in one of the series', function() {
beforeEach(function() {
var data = [
{ target: 'test.cpu1', datapoints: [[1234567890000, 45], [1234567899000, 60]]},
{ target: 'test.cpu2', datapoints: [[1236547890010, 55], [1234456709000, 90]]},
{ target: 'test.cpu3', datapoints: [[1236547890000, 65], [1234456709000, 120]]}
{ target: 'test.cpu1', datapoints: [[45, 1234567890000], [60, 1234567899000]]},
{ target: 'test.cpu2', datapoints: [[55, 1236547890010], [90, 1234456709000]]},
{ target: 'test.cpu3', datapoints: [[65, 1236547890000], [120, 1234456709000]]}
];
ctx.ctrl.panel.tooltip.msResolution = false;
ctx.ctrl.onDataReceived(data);

View File

@@ -1,4 +1,3 @@
<div class="singlestat-panel">
</div>
<div class="clearfix"></div>

View File

@@ -284,6 +284,9 @@ class SingleStatCtrl extends MetricsPanelCtrl {
}
function addGauge() {
var width = elem.width();
var height = elem.height();
ctrl.invalidGaugeRange = false;
if (panel.gauge.minValue > panel.gauge.maxValue) {
ctrl.invalidGaugeRange = true;
@@ -291,8 +294,6 @@ class SingleStatCtrl extends MetricsPanelCtrl {
}
var plotCanvas = $('<div></div>');
var width = elem.width();
var height = elem.height();
var plotCss = {
top: '10px',
margin: 'auto',

View File

@@ -5,7 +5,6 @@
}
.singlestat-panel-value-container {
padding: 20px;
display: table-cell;
vertical-align: middle;
text-align: center;

View File

@@ -56,7 +56,7 @@ define([
});
});
describe('can detect if serie contains ms precision', function() {
describe('can detect if series contains ms precision', function() {
var fakedata;
beforeEach(function() {
@@ -64,13 +64,13 @@ define([
});
it('missing datapoint with ms precision', function() {
fakedata.datapoints[0] = [1234567890000, 1337];
fakedata.datapoints[0] = [1337, 1234567890000];
series = new TimeSeries(fakedata);
expect(series.isMsResolutionNeeded()).to.be(false);
});
it('contains datapoint with ms precision', function() {
fakedata.datapoints[0] = [1236547890001, 1337];
fakedata.datapoints[0] = [1337, 1236547890001];
series = new TimeSeries(fakedata);
expect(series.isMsResolutionNeeded()).to.be(true);
});

View File

@@ -141,8 +141,8 @@ define([
});
it('slash should be properly escaped in regex format', function() {
var result = _templateSrv.formatValue('Gi3/14', 'regex');
expect(result).to.be('Gi3\\/14');
var result = _templateSrv.formatValue('Gi3/14', 'regex');
expect(result).to.be('Gi3\\/14');
});
});
@@ -200,6 +200,15 @@ define([
expect(contains).to.be(true);
});
it('should find it when part of segment', function() {
var contains = _templateSrv.containsVariable('metrics.$env.$group-*', 'group');
expect(contains).to.be(true);
});
it('should find it its the only thing', function() {
var contains = _templateSrv.containsVariable('$env', 'env');
expect(contains).to.be(true);
});
});
describe('updateTemplateData with simple value', function() {

View File

@@ -126,6 +126,59 @@ define([
});
});
describeUpdateVariable('query variable with multi select and new options does not contain some selected values', function(scenario) {
scenario.setup(function() {
scenario.variable = {
type: 'query',
query: '',
name: 'test',
current: {
value: ['val1', 'val2', 'val3'],
text: 'val1 + val2 + val3'
}
};
scenario.queryResult = [{text: 'val2'}, {text: 'val3'}];
});
it('should update current value', function() {
expect(scenario.variable.current.value).to.eql(['val2', 'val3']);
expect(scenario.variable.current.text).to.eql('val2 + val3');
});
});
describeUpdateVariable('query variable with multi select and new options does not contain any selected values', function(scenario) {
scenario.setup(function() {
scenario.variable = {
type: 'query',
query: '',
name: 'test',
current: {
value: ['val1', 'val2', 'val3'],
text: 'val1 + val2 + val3'
}
};
scenario.queryResult = [{text: 'val5'}, {text: 'val6'}];
});
it('should update current value with first one', function() {
expect(scenario.variable.current.value).to.eql('val5');
expect(scenario.variable.current.text).to.eql('val5');
});
});
describeUpdateVariable('query variable with numeric results', function(scenario) {
scenario.setup(function() {
scenario.variable = { type: 'query', query: '', name: 'test', current: {} };
scenario.queryResult = [{text: 12, value: 12}];
});
it('should set current value to first option', function() {
expect(scenario.variable.current.value).to.be('12');
expect(scenario.variable.options[0].value).to.be('12');
expect(scenario.variable.options[0].text).to.be('12');
});
});
describeUpdateVariable('interval variable without auto', function(scenario) {
scenario.setup(function() {
scenario.variable = { type: 'interval', query: '1s,2h,5h,1d', name: 'test' };