From 24a3a100ed187620fce60660696b73dd637ce53a Mon Sep 17 00:00:00 2001 From: bergquist Date: Tue, 8 Nov 2016 19:22:59 +0100 Subject: [PATCH 01/28] feat(influxdb): add alias to query --- pkg/tsdb/influxdb/model_parser.go | 2 ++ pkg/tsdb/influxdb/model_parser_test.go | 2 ++ pkg/tsdb/influxdb/models.go | 1 + 3 files changed, 5 insertions(+) diff --git a/pkg/tsdb/influxdb/model_parser.go b/pkg/tsdb/influxdb/model_parser.go index ff8977f925b..cbd4b397d9d 100644 --- a/pkg/tsdb/influxdb/model_parser.go +++ b/pkg/tsdb/influxdb/model_parser.go @@ -13,6 +13,7 @@ func (qp *InfluxdbQueryParser) Parse(model *simplejson.Json, dsInfo *tsdb.DataSo policy := model.Get("policy").MustString("default") rawQuery := model.Get("query").MustString("") interval := model.Get("interval").MustString("") + alias := model.Get("alias").MustString("") measurement := model.Get("measurement").MustString("") @@ -52,6 +53,7 @@ func (qp *InfluxdbQueryParser) Parse(model *simplejson.Json, dsInfo *tsdb.DataSo Selects: selects, RawQuery: rawQuery, Interval: interval, + Alias: alias, }, nil } diff --git a/pkg/tsdb/influxdb/model_parser_test.go b/pkg/tsdb/influxdb/model_parser_test.go index 8f43cc7d70f..eae6e163cc0 100644 --- a/pkg/tsdb/influxdb/model_parser_test.go +++ b/pkg/tsdb/influxdb/model_parser_test.go @@ -90,6 +90,7 @@ func TestInfluxdbQueryParser(t *testing.T) { } ] ], + "alias": "serie alias", "tags": [ { "key": "datacenter", @@ -115,6 +116,7 @@ func TestInfluxdbQueryParser(t *testing.T) { So(len(res.Selects), ShouldEqual, 3) So(len(res.Tags), ShouldEqual, 2) So(res.Interval, ShouldEqual, ">20s") + So(res.Alias, ShouldEqual, "serie alias") }) Convey("can part raw query json model", func() { diff --git a/pkg/tsdb/influxdb/models.go b/pkg/tsdb/influxdb/models.go index 7903616ff22..0dcecd20773 100644 --- a/pkg/tsdb/influxdb/models.go +++ b/pkg/tsdb/influxdb/models.go @@ -8,6 +8,7 @@ type Query struct { GroupBy []*QueryPart Selects []*Select RawQuery string + Alias string Interval string } From c897d39d5ebc7db273a5a826e5b32436acab8f4a Mon Sep 17 00:00:00 2001 From: bergquist Date: Tue, 8 Nov 2016 19:56:57 +0100 Subject: [PATCH 02/28] feat(influxdb): add support for serie alias replacement ref #6510 --- pkg/tsdb/influxdb/influxdb.go | 27 ++-- pkg/tsdb/influxdb/response_parser.go | 53 +++++++- pkg/tsdb/influxdb/response_parser_test.go | 155 +++++++++++++++++----- pkg/tsdb/prometheus/prometheus.go | 8 +- 4 files changed, 190 insertions(+), 53 deletions(-) diff --git a/pkg/tsdb/influxdb/influxdb.go b/pkg/tsdb/influxdb/influxdb.go index 3b591342787..61be1b08556 100644 --- a/pkg/tsdb/influxdb/influxdb.go +++ b/pkg/tsdb/influxdb/influxdb.go @@ -11,6 +11,7 @@ import ( "golang.org/x/net/context/ctxhttp" "github.com/grafana/grafana/pkg/log" + "github.com/grafana/grafana/pkg/setting" "github.com/grafana/grafana/pkg/tsdb" ) @@ -50,9 +51,16 @@ func (e *InfluxDBExecutor) Execute(ctx context.Context, queries tsdb.QuerySlice, return result.WithError(err) } - glog.Debug("Influxdb query", "raw query", query) + rawQuery, err := e.QueryBuilder.Build(query, context) + if err != nil { + return result.WithError(err) + } - req, err := e.createRequest(query) + if setting.Env == setting.DEV { + glog.Debug("Influxdb query", "raw query", query) + } + + req, err := e.createRequest(rawQuery) if err != nil { return result.WithError(err) } @@ -77,28 +85,23 @@ func (e *InfluxDBExecutor) Execute(ctx context.Context, queries tsdb.QuerySlice, } result.QueryResults = make(map[string]*tsdb.QueryResult) - result.QueryResults["A"] = e.ResponseParser.Parse(&response) + result.QueryResults["A"] = e.ResponseParser.Parse(&response, query) return result } -func (e *InfluxDBExecutor) getQuery(queries tsdb.QuerySlice, context *tsdb.QueryContext) (string, error) { +func (e *InfluxDBExecutor) getQuery(queries tsdb.QuerySlice, context *tsdb.QueryContext) (*Query, error) { for _, v := range queries { query, err := e.QueryParser.Parse(v.Model, e.DataSourceInfo) if err != nil { - return "", err + return nil, err } - rawQuery, err := e.QueryBuilder.Build(query, context) - if err != nil { - return "", err - } - - return rawQuery, nil + return query, nil } - return "", fmt.Errorf("query request contains no queries") + return nil, fmt.Errorf("query request contains no queries") } func (e *InfluxDBExecutor) createRequest(query string) (*http.Request, error) { diff --git a/pkg/tsdb/influxdb/response_parser.go b/pkg/tsdb/influxdb/response_parser.go index 44afa910b27..daa08807d07 100644 --- a/pkg/tsdb/influxdb/response_parser.go +++ b/pkg/tsdb/influxdb/response_parser.go @@ -3,6 +3,7 @@ package influxdb import ( "encoding/json" "fmt" + "regexp" "strings" "github.com/grafana/grafana/pkg/tsdb" @@ -11,17 +12,25 @@ import ( type ResponseParser struct{} -func (rp *ResponseParser) Parse(response *Response) *tsdb.QueryResult { +var ( + legendFormat *regexp.Regexp +) + +func init() { + legendFormat = regexp.MustCompile(`\[\[(\w+?)*\]\]*|\$\s*(\w+?)*`) +} + +func (rp *ResponseParser) Parse(response *Response, query *Query) *tsdb.QueryResult { queryRes := tsdb.NewQueryResult() for _, result := range response.Results { - queryRes.Series = append(queryRes.Series, rp.transformRows(result.Series, queryRes)...) + queryRes.Series = append(queryRes.Series, rp.transformRows(result.Series, queryRes, query)...) } return queryRes } -func (rp *ResponseParser) transformRows(rows []Row, queryResult *tsdb.QueryResult) tsdb.TimeSeriesSlice { +func (rp *ResponseParser) transformRows(rows []Row, queryResult *tsdb.QueryResult, query *Query) tsdb.TimeSeriesSlice { var result tsdb.TimeSeriesSlice for _, row := range rows { @@ -38,7 +47,7 @@ func (rp *ResponseParser) transformRows(rows []Row, queryResult *tsdb.QueryResul } } result = append(result, &tsdb.TimeSeries{ - Name: rp.formatSerieName(row, column), + Name: rp.formatSerieName(row, column, query), Points: points, }) } @@ -47,7 +56,41 @@ func (rp *ResponseParser) transformRows(rows []Row, queryResult *tsdb.QueryResul return result } -func (rp *ResponseParser) formatSerieName(row Row, column string) string { +func (rp *ResponseParser) formatSerieName(row Row, column string, query *Query) string { + if query.Alias == "" { + return rp.buildSerieNameFromQuery(row, column) + } + + result := legendFormat.ReplaceAllFunc([]byte(query.Alias), func(in []byte) []byte { + aliasFormat := string(in) + aliasFormat = strings.Replace(aliasFormat, "[[", "", 1) + aliasFormat = strings.Replace(aliasFormat, "]]", "", 1) + aliasFormat = strings.Replace(aliasFormat, "$", "", 1) + + if aliasFormat == "m" || aliasFormat == "measurement" { + return []byte(query.Measurement) + } + if aliasFormat == "col" { + return []byte(column) + } + + if !strings.HasPrefix(aliasFormat, "tag_") { + return in + } + + tagKey := strings.Replace(aliasFormat, "tag_", "", 1) + tagValue, exist := row.Tags[tagKey] + if exist { + return []byte(tagValue) + } + + return in + }) + + return string(result) +} + +func (rp *ResponseParser) buildSerieNameFromQuery(row Row, column string) string { var tags []string for k, v := range row.Tags { diff --git a/pkg/tsdb/influxdb/response_parser_test.go b/pkg/tsdb/influxdb/response_parser_test.go index b45f98a1fff..0672667adc5 100644 --- a/pkg/tsdb/influxdb/response_parser_test.go +++ b/pkg/tsdb/influxdb/response_parser_test.go @@ -4,56 +4,147 @@ import ( "encoding/json" "testing" + "github.com/grafana/grafana/pkg/setting" . "github.com/smartystreets/goconvey/convey" ) func TestInfluxdbResponseParser(t *testing.T) { Convey("Influxdb response parser", t, func() { + Convey("Response parser", func() { + parser := &ResponseParser{} - parser := &ResponseParser{} + setting.NewConfigContext(&setting.CommandLineArgs{ + HomePath: "../../../", + }) - response := &Response{ - Results: []Result{ - Result{ - Series: []Row{ - { - Name: "cpu", - Columns: []string{"time", "mean", "sum"}, - Tags: map[string]string{"datacenter": "America"}, - Values: [][]interface{}{ - {json.Number("111"), json.Number("222"), json.Number("333")}, - {json.Number("111"), json.Number("222"), json.Number("333")}, - {json.Number("111"), json.Number("null"), json.Number("333")}, + response := &Response{ + Results: []Result{ + Result{ + Series: []Row{ + { + Name: "cpu", + Columns: []string{"time", "mean", "sum"}, + Tags: map[string]string{"datacenter": "America"}, + Values: [][]interface{}{ + {json.Number("111"), json.Number("222"), json.Number("333")}, + {json.Number("111"), json.Number("222"), json.Number("333")}, + {json.Number("111"), json.Number("null"), json.Number("333")}, + }, }, }, }, }, - }, - } + } - result := parser.Parse(response) + query := &Query{} - Convey("can parse all series", func() { - So(len(result.Series), ShouldEqual, 2) + result := parser.Parse(response, query) + + Convey("can parse all series", func() { + So(len(result.Series), ShouldEqual, 2) + }) + + Convey("can parse all points", func() { + So(len(result.Series[0].Points), ShouldEqual, 3) + So(len(result.Series[1].Points), ShouldEqual, 3) + }) + + Convey("can parse multi row result", func() { + So(result.Series[0].Points[1][0].Float64, ShouldEqual, float64(222)) + So(result.Series[1].Points[1][0].Float64, ShouldEqual, float64(333)) + }) + + Convey("can parse null points", func() { + So(result.Series[0].Points[2][0].Valid, ShouldBeFalse) + }) + + Convey("can format serie names", func() { + So(result.Series[0].Name, ShouldEqual, "cpu.mean { datacenter: America }") + So(result.Series[1].Name, ShouldEqual, "cpu.sum { datacenter: America }") + }) }) - Convey("can parse all points", func() { - So(len(result.Series[0].Points), ShouldEqual, 3) - So(len(result.Series[1].Points), ShouldEqual, 3) - }) + Convey("Response parser with alias", func() { + parser := &ResponseParser{} - Convey("can parse multi row result", func() { - So(result.Series[0].Points[1][0].Float64, ShouldEqual, float64(222)) - So(result.Series[1].Points[1][0].Float64, ShouldEqual, float64(333)) - }) + response := &Response{ + Results: []Result{ + Result{ + Series: []Row{ + { + Name: "cpu", + Columns: []string{"time", "mean", "sum"}, + Tags: map[string]string{"datacenter": "America"}, + Values: [][]interface{}{ + {json.Number("111"), json.Number("222"), json.Number("333")}, + }, + }, + }, + }, + }, + } - Convey("can parse null points", func() { - So(result.Series[0].Points[2][0].Valid, ShouldBeFalse) - }) + Convey("$ alias", func() { + Convey("simple alias", func() { + query := &Query{Alias: "serie alias"} + result := parser.Parse(response, query) - Convey("can format serie names", func() { - So(result.Series[0].Name, ShouldEqual, "cpu.mean { datacenter: America }") - So(result.Series[1].Name, ShouldEqual, "cpu.sum { datacenter: America }") + So(result.Series[0].Name, ShouldEqual, "serie alias") + }) + + Convey("measurement alias", func() { + query := &Query{Alias: "alias $m $measurement", Measurement: "10m"} + result := parser.Parse(response, query) + + So(result.Series[0].Name, ShouldEqual, "alias 10m 10m") + }) + + Convey("column alias", func() { + query := &Query{Alias: "alias $col", Measurement: "10m"} + result := parser.Parse(response, query) + + So(result.Series[0].Name, ShouldEqual, "alias mean") + So(result.Series[1].Name, ShouldEqual, "alias sum") + }) + + Convey("tag alias", func() { + query := &Query{Alias: "alias $tag_datacenter"} + result := parser.Parse(response, query) + + So(result.Series[0].Name, ShouldEqual, "alias America") + }) + }) + + Convey("[[]] alias", func() { + Convey("simple alias", func() { + query := &Query{Alias: "serie alias"} + result := parser.Parse(response, query) + + So(result.Series[0].Name, ShouldEqual, "serie alias") + }) + + Convey("measurement alias", func() { + query := &Query{Alias: "alias [[m]] [[measurement]]", Measurement: "10m"} + result := parser.Parse(response, query) + + So(result.Series[0].Name, ShouldEqual, "alias 10m 10m") + }) + + Convey("column alias", func() { + query := &Query{Alias: "alias [[col]]", Measurement: "10m"} + result := parser.Parse(response, query) + + So(result.Series[0].Name, ShouldEqual, "alias mean") + So(result.Series[1].Name, ShouldEqual, "alias sum") + }) + + Convey("tag alias", func() { + query := &Query{Alias: "alias [[tag_datacenter]]"} + result := parser.Parse(response, query) + + So(result.Series[0].Name, ShouldEqual, "alias America") + }) + }) }) }) } diff --git a/pkg/tsdb/prometheus/prometheus.go b/pkg/tsdb/prometheus/prometheus.go index ec5b098cbaf..21e1dfdef24 100644 --- a/pkg/tsdb/prometheus/prometheus.go +++ b/pkg/tsdb/prometheus/prometheus.go @@ -24,12 +24,14 @@ func NewPrometheusExecutor(dsInfo *tsdb.DataSourceInfo) tsdb.Executor { } var ( - plog log.Logger + plog log.Logger + legendFormat *regexp.Regexp ) func init() { plog = log.New("tsdb.prometheus") tsdb.RegisterExecutor("prometheus", NewPrometheusExecutor) + legendFormat = regexp.MustCompile(`\{\{\s*(.+?)\s*\}\}`) } func (e *PrometheusExecutor) getClient() (prometheus.QueryAPI, error) { @@ -79,13 +81,11 @@ func (e *PrometheusExecutor) Execute(ctx context.Context, queries tsdb.QuerySlic } func formatLegend(metric pmodel.Metric, query *PrometheusQuery) string { - reg, _ := regexp.Compile(`\{\{\s*(.+?)\s*\}\}`) - if query.LegendFormat == "" { return metric.String() } - result := reg.ReplaceAllFunc([]byte(query.LegendFormat), func(in []byte) []byte { + result := legendFormat.ReplaceAllFunc([]byte(query.LegendFormat), func(in []byte) []byte { labelName := strings.Replace(string(in), "{{", "", 1) labelName = strings.Replace(labelName, "}}", "", 1) labelName = strings.TrimSpace(labelName) From 97271514fb0d395fa772dbe956b1a814f325cbc2 Mon Sep 17 00:00:00 2001 From: bergquist Date: Wed, 9 Nov 2016 10:30:03 +0100 Subject: [PATCH 03/28] feat(influxdb): cleanup influxdb query editor options --- .../datasource/influxdb/partials/query.options.html | 9 ++++----- 1 file changed, 4 insertions(+), 5 deletions(-) diff --git a/public/app/plugins/datasource/influxdb/partials/query.options.html b/public/app/plugins/datasource/influxdb/partials/query.options.html index feb8600fcc9..2dd6b4d376b 100644 --- a/public/app/plugins/datasource/influxdb/partials/query.options.html +++ b/public/app/plugins/datasource/influxdb/partials/query.options.html @@ -4,16 +4,15 @@
Group by time interval - - + + Set a low limit by having a greater sign: example: >60s +
- From f0a0e647a0066a19c556a98a6098cf540bba2977 Mon Sep 17 00:00:00 2001 From: bergquist Date: Wed, 9 Nov 2016 12:45:58 +0100 Subject: [PATCH 04/28] feat(influxdb): backend support for alias by segment ref #6510 --- pkg/tsdb/influxdb/response_parser.go | 8 ++++++++ pkg/tsdb/influxdb/response_parser_test.go | 16 +++++++++++++++- 2 files changed, 23 insertions(+), 1 deletion(-) diff --git a/pkg/tsdb/influxdb/response_parser.go b/pkg/tsdb/influxdb/response_parser.go index daa08807d07..8e9fa0022c3 100644 --- a/pkg/tsdb/influxdb/response_parser.go +++ b/pkg/tsdb/influxdb/response_parser.go @@ -4,6 +4,7 @@ import ( "encoding/json" "fmt" "regexp" + "strconv" "strings" "github.com/grafana/grafana/pkg/tsdb" @@ -61,6 +62,8 @@ func (rp *ResponseParser) formatSerieName(row Row, column string, query *Query) return rp.buildSerieNameFromQuery(row, column) } + nameSegment := strings.Split(row.Name, ".") + result := legendFormat.ReplaceAllFunc([]byte(query.Alias), func(in []byte) []byte { aliasFormat := string(in) aliasFormat = strings.Replace(aliasFormat, "[[", "", 1) @@ -74,6 +77,11 @@ func (rp *ResponseParser) formatSerieName(row Row, column string, query *Query) return []byte(column) } + pos, err := strconv.Atoi(aliasFormat) + if err == nil && len(nameSegment) >= pos { + return []byte(nameSegment[pos]) + } + if !strings.HasPrefix(aliasFormat, "tag_") { return in } diff --git a/pkg/tsdb/influxdb/response_parser_test.go b/pkg/tsdb/influxdb/response_parser_test.go index 0672667adc5..67667f565d5 100644 --- a/pkg/tsdb/influxdb/response_parser_test.go +++ b/pkg/tsdb/influxdb/response_parser_test.go @@ -72,7 +72,7 @@ func TestInfluxdbResponseParser(t *testing.T) { Result{ Series: []Row{ { - Name: "cpu", + Name: "cpu.upc", Columns: []string{"time", "mean", "sum"}, Tags: map[string]string{"datacenter": "America"}, Values: [][]interface{}{ @@ -113,6 +113,20 @@ func TestInfluxdbResponseParser(t *testing.T) { So(result.Series[0].Name, ShouldEqual, "alias America") }) + + Convey("segment alias", func() { + query := &Query{Alias: "alias $1"} + result := parser.Parse(response, query) + + So(result.Series[0].Name, ShouldEqual, "alias upc") + }) + + Convey("segment position out of bound", func() { + query := &Query{Alias: "alias $5"} + result := parser.Parse(response, query) + + So(result.Series[0].Name, ShouldEqual, "alias $5") + }) }) Convey("[[]] alias", func() { From ef08a243c5e8a129bb771159537ab0172ef97a93 Mon Sep 17 00:00:00 2001 From: bergquist Date: Thu, 10 Nov 2016 08:23:58 +0100 Subject: [PATCH 05/28] fix(influxdb): fixes possible nil pointer closes #6531 --- pkg/tsdb/influxdb/model_parser.go | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/pkg/tsdb/influxdb/model_parser.go b/pkg/tsdb/influxdb/model_parser.go index ff8977f925b..af727771f1f 100644 --- a/pkg/tsdb/influxdb/model_parser.go +++ b/pkg/tsdb/influxdb/model_parser.go @@ -12,7 +12,6 @@ type InfluxdbQueryParser struct{} func (qp *InfluxdbQueryParser) Parse(model *simplejson.Json, dsInfo *tsdb.DataSourceInfo) (*Query, error) { policy := model.Get("policy").MustString("default") rawQuery := model.Get("query").MustString("") - interval := model.Get("interval").MustString("") measurement := model.Get("measurement").MustString("") @@ -36,7 +35,8 @@ func (qp *InfluxdbQueryParser) Parse(model *simplejson.Json, dsInfo *tsdb.DataSo return nil, err } - if interval == "" { + interval := model.Get("interval").MustString("") + if interval == "" && dsInfo.JsonData != nil { dsInterval := dsInfo.JsonData.Get("timeInterval").MustString("") if dsInterval != "" { interval = dsInterval From 24433263867183637407932573e9883a1946e278 Mon Sep 17 00:00:00 2001 From: Ben RUBSON Date: Thu, 10 Nov 2016 10:19:42 +0100 Subject: [PATCH 06/28] Add some comments about some previous modifications (#6533) --- public/app/plugins/panel/graph/graph.ts | 2 ++ .../app/plugins/panel/graph/graph_tooltip.js | 8 +++++- public/vendor/flot/jquery.flot.js | 2 +- public/vendor/flot/jquery.flot.stack.js | 25 ++++++++++++------- 4 files changed, 26 insertions(+), 11 deletions(-) diff --git a/public/app/plugins/panel/graph/graph.ts b/public/app/plugins/panel/graph/graph.ts index 239bf9ecc83..b2252978d37 100755 --- a/public/app/plugins/panel/graph/graph.ts +++ b/public/app/plugins/panel/graph/graph.ts @@ -183,6 +183,8 @@ module.directive('grafanaGraph', function($rootScope, timeSrv) { } } + // Series could have different timeSteps, + // let's find the smallest one so that bars are correctly rendered. function getMinTimeStepOfSeries(data) { var min = 100000000000; diff --git a/public/app/plugins/panel/graph/graph_tooltip.js b/public/app/plugins/panel/graph/graph_tooltip.js index 697dce0b7ee..3aec815428f 100644 --- a/public/app/plugins/panel/graph/graph_tooltip.js +++ b/public/app/plugins/panel/graph/graph_tooltip.js @@ -21,7 +21,9 @@ function ($) { var initial = last*ps; var len = series.datapoints.points.length; for (var j = initial; j < len; j += ps) { + // Special case of a non stepped line, highlight the very last point just before a null point if ((series.datapoints.points[initial] != null && series.datapoints.points[j] == null && ! series.lines.steps) + //normal case || series.datapoints.points[j] > posX) { return Math.max(j - ps, 0)/ps; } @@ -58,11 +60,13 @@ function ($) { series = seriesList[i]; if (!series.data.length || (panel.legend.hideEmpty && series.allIsNull)) { + // Init value & yaxis so that it does not brake series sorting results.push({ hidden: true, value: 0, yaxis: 0 }); continue; } if (!series.data.length || (panel.legend.hideZero && series.allIsZero)) { + // Init value & yaxis so that it does not brake series sorting results.push({ hidden: true, value: 0, yaxis: 0 }); continue; } @@ -71,6 +75,7 @@ function ($) { hoverDistance = pos.x - series.data[hoverIndex][0]; pointTime = series.data[hoverIndex][0]; + // Take the closest point before the cursor, or if it does not exist, the closest after if (! minDistance || (hoverDistance >=0 && (hoverDistance < minDistance || minDistance < 0)) || (hoverDistance < 0 && hoverDistance > minDistance)) { @@ -99,6 +104,7 @@ function ($) { hoverIndex = this.findHoverIndexFromDataPoints(pos.x, series, hoverIndex); } + // Be sure we have a yaxis so that it does not brake series sorting yaxis = 0; if (series.yaxis) { yaxis = series.yaxis.n; @@ -116,7 +122,7 @@ function ($) { }); } - // Find point which closer to pointer + // Time of the point closer to pointer results.time = minTime; return results; diff --git a/public/vendor/flot/jquery.flot.js b/public/vendor/flot/jquery.flot.js index e0941bb24c0..8d61b3e9b28 100644 --- a/public/vendor/flot/jquery.flot.js +++ b/public/vendor/flot/jquery.flot.js @@ -1210,7 +1210,7 @@ Licensed under the MIT license. // middle point has same y points[k + 1] = points[k - ps + 1] || 0; - // if series has null values, let's give the last correct value a nice step + // if series has null values, let's give the last !null value a nice step if(nullify) points[k] = p[0]; diff --git a/public/vendor/flot/jquery.flot.stack.js b/public/vendor/flot/jquery.flot.stack.js index ba2a6b86a2f..5367ff30603 100644 --- a/public/vendor/flot/jquery.flot.stack.js +++ b/public/vendor/flot/jquery.flot.stack.js @@ -78,9 +78,12 @@ charts or filled areas). i = 0, j = 0, l, m; while (true) { + // browse all points from the current series and from the previous series if (i >= points.length && j >= otherpoints.length) break; + // newpoints will replace current series with + // as many points as different timestamps we have in the 2 (current & previous) series l = newpoints.length; px = points[i + keyOffset]; py = points[i + accumulateOffset]; @@ -89,30 +92,32 @@ charts or filled areas). bottom = 0; if (i < points.length && px == null) { - // ignore point + // let's ignore null points from current series, nothing to do with them i += ps; } else if (j < otherpoints.length && qx == null) { - // ignore point + // let's ignore null points from previous series, nothing to do with them j += otherps; } else if (i >= points.length) { - // take the remaining points from the previous series + // no more points in the current series, simply take the remaining points + // from the previous series so that next series will correctly stack for (m = 0; m < ps; ++m) newpoints.push(otherpoints[j + m]); bottom = qy; j += otherps; } else if (j >= otherpoints.length) { - // take the remaining points from the current series + // no more points in the previous series, of course let's take + // the remaining points from the current series for (m = 0; m < ps; ++m) newpoints.push(points[i + m]); i += ps; } else { - // cases where we actually got two points + // next available points from current and previous series have the same timestamp if (px == qx) { - // take the point from the current series and skip the previous' one + // so take the point from the current series and skip the previous' one for (m = 0; m < ps; ++m) newpoints.push(points[i + m]); @@ -122,8 +127,9 @@ charts or filled areas). i += ps; j += otherps; } + // next available point with the smallest timestamp is from the previous series else if (px > qx) { - // take the point from the previous series so that the next series can stack over it + // so take the point from the previous series so that next series will correctly stack for (m = 0; m < ps; ++m) newpoints.push(otherpoints[j + m]); @@ -135,8 +141,9 @@ charts or filled areas). j += otherps; } - else { // px < qx - // take the point from the current series + // (px < qx) next available point with the smallest timestamp is from the current series + else { + // so of course let's take the point from the current series for (m = 0; m < ps; ++m) newpoints.push(points[i + m]); From 6495ba155af148c0a76eb5c5f254d406edb62cc7 Mon Sep 17 00:00:00 2001 From: Ben RUBSON Date: Thu, 10 Nov 2016 10:20:27 +0100 Subject: [PATCH 07/28] Correct timeStep in case of missing values (#6526) * Correct timeStep in case of missing values * Comment --- public/app/core/time_series2.ts | 13 +++++++++---- 1 file changed, 9 insertions(+), 4 deletions(-) diff --git a/public/app/core/time_series2.ts b/public/app/core/time_series2.ts index d672e0dd0dc..3d99b43704e 100644 --- a/public/app/core/time_series2.ts +++ b/public/app/core/time_series2.ts @@ -115,6 +115,15 @@ export default class TimeSeries { currentValue = this.datapoints[i][0]; currentTime = this.datapoints[i][1]; + // Due to missing values we could have different timeStep all along the series + // so we have to find the minimum one (could occur with aggregators such as ZimSum) + if (i>0) { + var previousTime = this.datapoints[i-1][1]; + if (!this.stats.timeStep || currentTime - previousTime < this.stats.timeStep) { + this.stats.timeStep = currentTime - previousTime; + } + } + if (currentValue === null) { if (ignoreNulls) { continue; } if (nullAsZero) { @@ -145,10 +154,6 @@ export default class TimeSeries { result.push([currentTime, currentValue]); } - if (this.datapoints.length >= 2) { - this.stats.timeStep = this.datapoints[1][1] - this.datapoints[0][1]; - } - if (this.stats.max === -Number.MAX_VALUE) { this.stats.max = null; } if (this.stats.min === Number.MAX_VALUE) { this.stats.min = null; } From 71eb0f3278b63579efe033b058d2363a3df76add Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Torkel=20=C3=96degaard?= Date: Thu, 10 Nov 2016 10:50:48 +0100 Subject: [PATCH 08/28] fix(graph): fixed issue with bar width when used in series override, fixes #6528 --- CHANGELOG.md | 9 +++++++-- public/app/core/time_series2.ts | 11 +++++++---- public/app/plugins/panel/graph/graph.ts | 6 ++---- 3 files changed, 16 insertions(+), 10 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 110c8015a27..55298901618 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,4 +1,9 @@ -# 4.0-pre (unreleased) +# 4.0-beta2 (unrelased) + +### Bugfixes +* **Graph Panel**: Bar width if bars was only used in series override, [#6528](https://github.com/grafana/grafana/issues/6528) + +# 4.0-beta1 (2016-11-09) ### Enhancements * **Login**: Adds option to disable username/password logins, closes [#4674](https://github.com/grafana/grafana/issues/4674) @@ -24,7 +29,7 @@ * **SystemD**: Change systemd description, closes [#5971](https://github.com/grafana/grafana/pull/5971) * **lodash upgrade**: Upgraded lodash from 2.4.2 to 4.15.0, this contains a number of breaking changes that could effect plugins. closes [#6021](https://github.com/grafana/grafana/pull/6021) -### Bugfixes +### Bug fixes * **Table Panel**: Fixed problem when switching to Mixed datasource in metrics tab, fixes [#5999](https://github.com/grafana/grafana/pull/5999) * **Playlist**: Fixed problem with play order not matching order defined in playlist, fixes [#5467](https://github.com/grafana/grafana/pull/5467) * **Graph panel**: Fixed problem with auto decimals on y axis when datamin=datamax, fixes [#6070](https://github.com/grafana/grafana/pull/6070) diff --git a/public/app/core/time_series2.ts b/public/app/core/time_series2.ts index 3d99b43704e..25a99a38ad8 100644 --- a/public/app/core/time_series2.ts +++ b/public/app/core/time_series2.ts @@ -102,6 +102,7 @@ export default class TimeSeries { this.stats.min = Number.MAX_VALUE; this.stats.avg = null; this.stats.current = null; + this.stats.timeStep = Number.MAX_VALUE; this.allIsNull = true; this.allIsZero = true; @@ -110,6 +111,7 @@ export default class TimeSeries { var currentTime; var currentValue; var nonNulls = 0; + var previousTime; for (var i = 0; i < this.datapoints.length; i++) { currentValue = this.datapoints[i][0]; @@ -117,12 +119,13 @@ export default class TimeSeries { // Due to missing values we could have different timeStep all along the series // so we have to find the minimum one (could occur with aggregators such as ZimSum) - if (i>0) { - var previousTime = this.datapoints[i-1][1]; - if (!this.stats.timeStep || currentTime - previousTime < this.stats.timeStep) { - this.stats.timeStep = currentTime - previousTime; + if (previousTime !== undefined) { + let timeStep = currentTime - previousTime; + if (timeStep < this.stats.timeStep) { + this.stats.timeStep = timeStep; } } + previousTime = currentTime; if (currentValue === null) { if (ignoreNulls) { continue; } diff --git a/public/app/plugins/panel/graph/graph.ts b/public/app/plugins/panel/graph/graph.ts index b2252978d37..14e0d5c99c0 100755 --- a/public/app/plugins/panel/graph/graph.ts +++ b/public/app/plugins/panel/graph/graph.ts @@ -186,7 +186,7 @@ module.directive('grafanaGraph', function($rootScope, timeSrv) { // Series could have different timeSteps, // let's find the smallest one so that bars are correctly rendered. function getMinTimeStepOfSeries(data) { - var min = 100000000000; + var min = Number.MAX_VALUE; for (let i = 0; i < data.length; i++) { if (!data[i].stats.timeStep) { @@ -297,9 +297,7 @@ module.directive('grafanaGraph', function($rootScope, timeSrv) { break; } default: { - if (panel.bars) { - options.series.bars.barWidth = getMinTimeStepOfSeries(data) / 1.5; - } + options.series.bars.barWidth = getMinTimeStepOfSeries(data) / 1.5; addTimeAxis(options); break; } From 4f3c8c666323b95a22df87df1a2e85e7b4b9ce60 Mon Sep 17 00:00:00 2001 From: bergquist Date: Thu, 10 Nov 2016 10:41:00 +0100 Subject: [PATCH 09/28] fix(influxdb): add default operator ref #6523 --- pkg/tsdb/influxdb/query_builder.go | 36 ++++++++++++++++++------- pkg/tsdb/influxdb/query_builder_test.go | 28 +++++++++++++++---- 2 files changed, 49 insertions(+), 15 deletions(-) diff --git a/pkg/tsdb/influxdb/query_builder.go b/pkg/tsdb/influxdb/query_builder.go index a4b122cbbd2..b783ff5c603 100644 --- a/pkg/tsdb/influxdb/query_builder.go +++ b/pkg/tsdb/influxdb/query_builder.go @@ -5,9 +5,15 @@ import ( "strconv" "strings" + "regexp" + "github.com/grafana/grafana/pkg/tsdb" ) +var ( + regexpOperatorPattern *regexp.Regexp = regexp.MustCompile(`^\/.*\/$`) +) + type QueryBuilder struct{} func (qb *QueryBuilder) Build(query *Query, queryContext *tsdb.QueryContext) (string, error) { @@ -43,18 +49,28 @@ func (qb *QueryBuilder) renderTags(query *Query) []string { str += " " } - value := tag.Value - nValue, err := strconv.ParseFloat(tag.Value, 64) - - if tag.Operator == "=~" || tag.Operator == "!~" { - value = fmt.Sprintf("%s", value) - } else if err == nil { - value = fmt.Sprintf("%v", nValue) - } else { - value = fmt.Sprintf("'%s'", value) + //If the operator is missing we fall back to sensible defaults + if tag.Operator == "" { + if regexpOperatorPattern.Match([]byte(tag.Value)) { + tag.Operator = "=~" + } else { + tag.Operator = "=" + } } - res = append(res, fmt.Sprintf(`%s"%s" %s %s`, str, tag.Key, tag.Operator, value)) + textValue := "" + numericValue, err := strconv.ParseFloat(tag.Value, 64) + + // quote value unless regex or number + if tag.Operator == "=~" || tag.Operator == "!~" { + textValue = tag.Value + } else if err == nil { + textValue = fmt.Sprintf("%v", numericValue) + } else { + textValue = fmt.Sprintf("'%s'", tag.Value) + } + + res = append(res, fmt.Sprintf(`%s"%s" %s %s`, str, tag.Key, tag.Operator, textValue)) } return res diff --git a/pkg/tsdb/influxdb/query_builder_test.go b/pkg/tsdb/influxdb/query_builder_test.go index d13fe3a3c6f..408db18e549 100644 --- a/pkg/tsdb/influxdb/query_builder_test.go +++ b/pkg/tsdb/influxdb/query_builder_test.go @@ -86,16 +86,34 @@ func TestInfluxdbQueryBuilder(t *testing.T) { So(rawQuery, ShouldEqual, `Raw query`) }) - Convey("can render regex tags", func() { - query := &Query{Tags: []*Tag{&Tag{Operator: "=~", Value: "value", Key: "key"}}} + Convey("can render normal tags without operator", func() { + query := &Query{Tags: []*Tag{&Tag{Operator: "", Value: `value`, Key: "key"}}} - So(strings.Join(builder.renderTags(query), ""), ShouldEqual, `"key" =~ value`) + So(strings.Join(builder.renderTags(query), ""), ShouldEqual, `"key" = 'value'`) + }) + + Convey("can render regex tags without operator", func() { + query := &Query{Tags: []*Tag{&Tag{Operator: "", Value: `/value/`, Key: "key"}}} + + So(strings.Join(builder.renderTags(query), ""), ShouldEqual, `"key" =~ /value/`) + }) + + Convey("can render regex tags", func() { + query := &Query{Tags: []*Tag{&Tag{Operator: "=~", Value: `/value/`, Key: "key"}}} + + So(strings.Join(builder.renderTags(query), ""), ShouldEqual, `"key" =~ /value/`) }) Convey("can render number tags", func() { - query := &Query{Tags: []*Tag{&Tag{Operator: "=", Value: "1", Key: "key"}}} + query := &Query{Tags: []*Tag{&Tag{Operator: "=", Value: "10001", Key: "key"}}} - So(strings.Join(builder.renderTags(query), ""), ShouldEqual, `"key" = 1`) + So(strings.Join(builder.renderTags(query), ""), ShouldEqual, `"key" = 10001`) + }) + + Convey("can render number tags with decimals", func() { + query := &Query{Tags: []*Tag{&Tag{Operator: "=", Value: "10001.1", Key: "key"}}} + + So(strings.Join(builder.renderTags(query), ""), ShouldEqual, `"key" = 10001.1`) }) Convey("can render string tags", func() { From 70b9ba257357628efec51a81f09070f6f30a43b3 Mon Sep 17 00:00:00 2001 From: bergquist Date: Thu, 10 Nov 2016 11:30:14 +0100 Subject: [PATCH 10/28] tech(build): switch to golang 1.7.3 tls/cipher fixes https://github.com/golang/go/issues?q=milestone%3AGo1.7.3 --- circle.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/circle.yml b/circle.yml index 39dc626df04..2422715bda3 100644 --- a/circle.yml +++ b/circle.yml @@ -5,7 +5,7 @@ machine: GOPATH: "/home/ubuntu/.go_workspace" ORG_PATH: "github.com/grafana" REPO_PATH: "${ORG_PATH}/grafana" - GODIST: "go1.7.1.linux-amd64.tar.gz" + GODIST: "go1.7.3.linux-amd64.tar.gz" post: - mkdir -p download - test -e download/$GODIST || curl -o download/$GODIST https://storage.googleapis.com/golang/$GODIST From 316d754a212821d0e93fc821865232120c6fe111 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Torkel=20=C3=96degaard?= Date: Thu, 10 Nov 2016 11:31:14 +0100 Subject: [PATCH 11/28] fix(ui/browser): fixed border not showing in safari, fixes #6530 --- CHANGELOG.md | 1 + public/sass/mixins/_mixins.scss | 7 +++++-- 2 files changed, 6 insertions(+), 2 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 55298901618..bfc70e8449b 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -2,6 +2,7 @@ ### Bugfixes * **Graph Panel**: Bar width if bars was only used in series override, [#6528](https://github.com/grafana/grafana/issues/6528) +* **UI/Browser**: Fixed issue with page/view header gradient border not showing in Safari, [#6530](https://github.com/grafana/grafana/issues/6530) # 4.0-beta1 (2016-11-09) diff --git a/public/sass/mixins/_mixins.scss b/public/sass/mixins/_mixins.scss index d6f1cf2afc1..78f9d3fca08 100644 --- a/public/sass/mixins/_mixins.scss +++ b/public/sass/mixins/_mixins.scss @@ -335,21 +335,24 @@ } @mixin left-brand-border-gradient() { + border: none; border-image: linear-gradient(rgba(255,213,0,1) 0%, rgba(255,68,0,1) 99%, rgba(255,68,0,1) 100%); border-image-slice: 1; + border-style: solid; border-top: 0; border-right: 0; border-bottom: 0; - border-left: 2px solid transparent; + border-left-width: 2px; } @mixin brand-bottom-border() { border-image: $brand-gradient; border-image-slice: 1; + border-style: solid; border-top: 0; border-right: 0; border-left: 0; - border-bottom: 1px solid transparent; + border-bottom-width: 1px; } From 6767bdd76d9640defe8d92be5d4f99a7294e1483 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Torkel=20=C3=96degaard?= Date: Thu, 10 Nov 2016 12:04:55 +0100 Subject: [PATCH 12/28] fix(graph): fixed issue log base scale on right y axis, option was not applied at all, also issue with log scale and max value calculation fixed, fixes #6534 --- CHANGELOG.md | 1 + public/app/plugins/panel/graph/graph.ts | 4 +++- 2 files changed, 4 insertions(+), 1 deletion(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index bfc70e8449b..4dd0adba4c5 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,6 +1,7 @@ # 4.0-beta2 (unrelased) ### Bugfixes +* **Graph Panel**: Log base scale on right Y-axis had no effect, max value calc was not applied, [#6534](https://github.com/grafana/grafana/issues/6534) * **Graph Panel**: Bar width if bars was only used in series override, [#6528](https://github.com/grafana/grafana/issues/6528) * **UI/Browser**: Fixed issue with page/view header gradient border not showing in Safari, [#6530](https://github.com/grafana/grafana/issues/6530) diff --git a/public/app/plugins/panel/graph/graph.ts b/public/app/plugins/panel/graph/graph.ts index 14e0d5c99c0..73fe37d5cad 100755 --- a/public/app/plugins/panel/graph/graph.ts +++ b/public/app/plugins/panel/graph/graph.ts @@ -460,7 +460,7 @@ module.directive('grafanaGraph', function($rootScope, timeSrv) { show: panel.yaxes[0].show, index: 1, logBase: panel.yaxes[0].logBase || 1, - max: 100, // correct later + max: null }; options.yaxes.push(defaults); @@ -472,6 +472,8 @@ module.directive('grafanaGraph', function($rootScope, timeSrv) { secondY.logBase = panel.yaxes[1].logBase || 1; secondY.position = 'right'; options.yaxes.push(secondY); + + applyLogScale(options.yaxes[1], data); configureAxisMode(options.yaxes[1], panel.percentage && panel.stack ? "percent" : panel.yaxes[1].format); } From 9f7d0862e8c592c09f6406b2e0231323e2b0a61e Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Torkel=20=C3=96degaard?= Date: Thu, 10 Nov 2016 13:08:32 +0100 Subject: [PATCH 13/28] docs(): minor docs fix --- docs/sources/guides/whats-new-in-v3-1.md | 2 +- docs/sources/guides/whats-new-in-v4.md | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/docs/sources/guides/whats-new-in-v3-1.md b/docs/sources/guides/whats-new-in-v3-1.md index 5dd23dff156..236e77ea266 100644 --- a/docs/sources/guides/whats-new-in-v3-1.md +++ b/docs/sources/guides/whats-new-in-v3-1.md @@ -4,7 +4,7 @@ description = "Feature & improvement highlights for Grafana v3.1" keywords = ["grafana", "new", "documentation", "3.1"] type = "docs" [menu.docs] -name = "Version 3.1 (Latest)" +name = "Version 3.1" identifier = "v3.1" parent = "whatsnew" weight = 1 diff --git a/docs/sources/guides/whats-new-in-v4.md b/docs/sources/guides/whats-new-in-v4.md index 39b2437d054..71a5c0251da 100644 --- a/docs/sources/guides/whats-new-in-v4.md +++ b/docs/sources/guides/whats-new-in-v4.md @@ -4,7 +4,7 @@ description = "Feature & improvement highlights for Grafana v4.0" keywords = ["grafana", "new", "documentation", "4.0"] type = "docs" [menu.docs] -name = "Version 4.0" +name = "Version 4.0 (Latest)" identifier = "v4.0" parent = "whatsnew" weight = -1 From a948dfe5149a9845edde752d36f0e9b9d38a3be9 Mon Sep 17 00:00:00 2001 From: bergquist Date: Thu, 10 Nov 2016 14:16:18 +0100 Subject: [PATCH 14/28] fix(influxdb): fixes broken raw query usage --- pkg/tsdb/influxdb/model_parser.go | 2 ++ pkg/tsdb/influxdb/models.go | 1 + pkg/tsdb/influxdb/query_builder.go | 2 +- pkg/tsdb/influxdb/query_builder_test.go | 1 + 4 files changed, 5 insertions(+), 1 deletion(-) diff --git a/pkg/tsdb/influxdb/model_parser.go b/pkg/tsdb/influxdb/model_parser.go index 2db7a78ed75..410bf8f6e82 100644 --- a/pkg/tsdb/influxdb/model_parser.go +++ b/pkg/tsdb/influxdb/model_parser.go @@ -12,6 +12,7 @@ type InfluxdbQueryParser struct{} func (qp *InfluxdbQueryParser) Parse(model *simplejson.Json, dsInfo *tsdb.DataSourceInfo) (*Query, error) { policy := model.Get("policy").MustString("default") rawQuery := model.Get("query").MustString("") + useRawQuery := model.Get("rawQuery").MustBool(false) alias := model.Get("alias").MustString("") measurement := model.Get("measurement").MustString("") @@ -54,6 +55,7 @@ func (qp *InfluxdbQueryParser) Parse(model *simplejson.Json, dsInfo *tsdb.DataSo RawQuery: rawQuery, Interval: interval, Alias: alias, + UseRawQuery: useRawQuery, }, nil } diff --git a/pkg/tsdb/influxdb/models.go b/pkg/tsdb/influxdb/models.go index 0dcecd20773..44e05608290 100644 --- a/pkg/tsdb/influxdb/models.go +++ b/pkg/tsdb/influxdb/models.go @@ -8,6 +8,7 @@ type Query struct { GroupBy []*QueryPart Selects []*Select RawQuery string + UseRawQuery bool Alias string Interval string diff --git a/pkg/tsdb/influxdb/query_builder.go b/pkg/tsdb/influxdb/query_builder.go index b783ff5c603..8b3146d59fc 100644 --- a/pkg/tsdb/influxdb/query_builder.go +++ b/pkg/tsdb/influxdb/query_builder.go @@ -17,7 +17,7 @@ var ( type QueryBuilder struct{} func (qb *QueryBuilder) Build(query *Query, queryContext *tsdb.QueryContext) (string, error) { - if query.RawQuery != "" { + if query.UseRawQuery && query.RawQuery != "" { q := query.RawQuery q = strings.Replace(q, "$timeFilter", qb.renderTimeFilter(query, queryContext), 1) diff --git a/pkg/tsdb/influxdb/query_builder_test.go b/pkg/tsdb/influxdb/query_builder_test.go index 408db18e549..e97c7d8cd52 100644 --- a/pkg/tsdb/influxdb/query_builder_test.go +++ b/pkg/tsdb/influxdb/query_builder_test.go @@ -79,6 +79,7 @@ func TestInfluxdbQueryBuilder(t *testing.T) { GroupBy: []*QueryPart{groupBy1, groupBy3}, Interval: "10s", RawQuery: "Raw query", + UseRawQuery: true, } rawQuery, err := builder.Build(query, queryContext) From f924b241ae9109025311f4af3374f0fd0b92fb16 Mon Sep 17 00:00:00 2001 From: bergquist Date: Thu, 10 Nov 2016 14:38:06 +0100 Subject: [PATCH 15/28] tech(influxdb): refactor query builder trying to reduce the amounts of moving parts for influxdb --- pkg/tsdb/influxdb/influxdb.go | 4 +-- .../influxdb/{query_builder.go => query.go} | 30 +++++++++---------- .../{query_builder_test.go => query_test.go} | 24 +++++++-------- 3 files changed, 26 insertions(+), 32 deletions(-) rename pkg/tsdb/influxdb/{query_builder.go => query.go} (71%) rename pkg/tsdb/influxdb/{query_builder_test.go => query_test.go} (77%) diff --git a/pkg/tsdb/influxdb/influxdb.go b/pkg/tsdb/influxdb/influxdb.go index a64a6af2ee9..4efb495dd05 100644 --- a/pkg/tsdb/influxdb/influxdb.go +++ b/pkg/tsdb/influxdb/influxdb.go @@ -18,7 +18,6 @@ import ( type InfluxDBExecutor struct { *tsdb.DataSourceInfo QueryParser *InfluxdbQueryParser - QueryBuilder *QueryBuilder ResponseParser *ResponseParser } @@ -26,7 +25,6 @@ func NewInfluxDBExecutor(dsInfo *tsdb.DataSourceInfo) tsdb.Executor { return &InfluxDBExecutor{ DataSourceInfo: dsInfo, QueryParser: &InfluxdbQueryParser{}, - QueryBuilder: &QueryBuilder{}, ResponseParser: &ResponseParser{}, } } @@ -51,7 +49,7 @@ func (e *InfluxDBExecutor) Execute(ctx context.Context, queries tsdb.QuerySlice, return result.WithError(err) } - rawQuery, err := e.QueryBuilder.Build(query, context) + rawQuery, err := query.Build(context) if err != nil { return result.WithError(err) } diff --git a/pkg/tsdb/influxdb/query_builder.go b/pkg/tsdb/influxdb/query.go similarity index 71% rename from pkg/tsdb/influxdb/query_builder.go rename to pkg/tsdb/influxdb/query.go index 8b3146d59fc..d9208aaee3a 100644 --- a/pkg/tsdb/influxdb/query_builder.go +++ b/pkg/tsdb/influxdb/query.go @@ -14,28 +14,26 @@ var ( regexpOperatorPattern *regexp.Regexp = regexp.MustCompile(`^\/.*\/$`) ) -type QueryBuilder struct{} - -func (qb *QueryBuilder) Build(query *Query, queryContext *tsdb.QueryContext) (string, error) { +func (query *Query) Build(queryContext *tsdb.QueryContext) (string, error) { if query.UseRawQuery && query.RawQuery != "" { q := query.RawQuery - q = strings.Replace(q, "$timeFilter", qb.renderTimeFilter(query, queryContext), 1) + q = strings.Replace(q, "$timeFilter", query.renderTimeFilter(queryContext), 1) q = strings.Replace(q, "$interval", tsdb.CalculateInterval(queryContext.TimeRange), 1) return q, nil } - res := qb.renderSelectors(query, queryContext) - res += qb.renderMeasurement(query) - res += qb.renderWhereClause(query) - res += qb.renderTimeFilter(query, queryContext) - res += qb.renderGroupBy(query, queryContext) + res := query.renderSelectors(queryContext) + res += query.renderMeasurement() + res += query.renderWhereClause() + res += query.renderTimeFilter(queryContext) + res += query.renderGroupBy(queryContext) return res, nil } -func (qb *QueryBuilder) renderTags(query *Query) []string { +func (query *Query) renderTags() []string { var res []string for i, tag := range query.Tags { str := "" @@ -76,7 +74,7 @@ func (qb *QueryBuilder) renderTags(query *Query) []string { return res } -func (qb *QueryBuilder) renderTimeFilter(query *Query, queryContext *tsdb.QueryContext) string { +func (query *Query) renderTimeFilter(queryContext *tsdb.QueryContext) string { from := "now() - " + queryContext.TimeRange.From to := "" @@ -87,7 +85,7 @@ func (qb *QueryBuilder) renderTimeFilter(query *Query, queryContext *tsdb.QueryC return fmt.Sprintf("time > %s%s", from, to) } -func (qb *QueryBuilder) renderSelectors(query *Query, queryContext *tsdb.QueryContext) string { +func (query *Query) renderSelectors(queryContext *tsdb.QueryContext) string { res := "SELECT " var selectors []string @@ -103,7 +101,7 @@ func (qb *QueryBuilder) renderSelectors(query *Query, queryContext *tsdb.QueryCo return res + strings.Join(selectors, ", ") } -func (qb *QueryBuilder) renderMeasurement(query *Query) string { +func (query *Query) renderMeasurement() string { policy := "" if query.Policy == "" || query.Policy == "default" { policy = "" @@ -113,9 +111,9 @@ func (qb *QueryBuilder) renderMeasurement(query *Query) string { return fmt.Sprintf(` FROM %s"%s"`, policy, query.Measurement) } -func (qb *QueryBuilder) renderWhereClause(query *Query) string { +func (query *Query) renderWhereClause() string { res := " WHERE " - conditions := qb.renderTags(query) + conditions := query.renderTags() res += strings.Join(conditions, " ") if len(conditions) > 0 { res += " AND " @@ -124,7 +122,7 @@ func (qb *QueryBuilder) renderWhereClause(query *Query) string { return res } -func (qb *QueryBuilder) renderGroupBy(query *Query, queryContext *tsdb.QueryContext) string { +func (query *Query) renderGroupBy(queryContext *tsdb.QueryContext) string { groupBy := "" for i, group := range query.GroupBy { if i == 0 { diff --git a/pkg/tsdb/influxdb/query_builder_test.go b/pkg/tsdb/influxdb/query_test.go similarity index 77% rename from pkg/tsdb/influxdb/query_builder_test.go rename to pkg/tsdb/influxdb/query_test.go index e97c7d8cd52..e8c1312d673 100644 --- a/pkg/tsdb/influxdb/query_builder_test.go +++ b/pkg/tsdb/influxdb/query_test.go @@ -12,7 +12,6 @@ import ( func TestInfluxdbQueryBuilder(t *testing.T) { Convey("Influxdb query builder", t, func() { - builder := QueryBuilder{} qp1, _ := NewQueryPart("field", []string{"value"}) qp2, _ := NewQueryPart("mean", []string{}) @@ -37,7 +36,7 @@ func TestInfluxdbQueryBuilder(t *testing.T) { Interval: "10s", } - rawQuery, err := builder.Build(query, queryContext) + rawQuery, err := query.Build(queryContext) So(err, ShouldBeNil) So(rawQuery, ShouldEqual, `SELECT mean("value") FROM "policy"."cpu" WHERE time > now() - 5m GROUP BY time(10s) fill(null)`) }) @@ -51,23 +50,22 @@ func TestInfluxdbQueryBuilder(t *testing.T) { Interval: "5s", } - rawQuery, err := builder.Build(query, queryContext) + rawQuery, err := query.Build(queryContext) So(err, ShouldBeNil) So(rawQuery, ShouldEqual, `SELECT mean("value") FROM "cpu" WHERE "hostname" = 'server1' OR "hostname" = 'server2' AND time > now() - 5m GROUP BY time(5s), "datacenter" fill(null)`) }) Convey("can render time range", func() { query := Query{} - builder := &QueryBuilder{} Convey("render from: 2h to now-1h", func() { query := Query{} queryContext := &tsdb.QueryContext{TimeRange: tsdb.NewTimeRange("2h", "now-1h")} - So(builder.renderTimeFilter(&query, queryContext), ShouldEqual, "time > now() - 2h and time < now() - 1h") + So(query.renderTimeFilter(queryContext), ShouldEqual, "time > now() - 2h and time < now() - 1h") }) Convey("render from: 10m", func() { queryContext := &tsdb.QueryContext{TimeRange: tsdb.NewTimeRange("10m", "now")} - So(builder.renderTimeFilter(&query, queryContext), ShouldEqual, "time > now() - 10m") + So(query.renderTimeFilter(queryContext), ShouldEqual, "time > now() - 10m") }) }) @@ -82,7 +80,7 @@ func TestInfluxdbQueryBuilder(t *testing.T) { UseRawQuery: true, } - rawQuery, err := builder.Build(query, queryContext) + rawQuery, err := query.Build(queryContext) So(err, ShouldBeNil) So(rawQuery, ShouldEqual, `Raw query`) }) @@ -90,37 +88,37 @@ func TestInfluxdbQueryBuilder(t *testing.T) { Convey("can render normal tags without operator", func() { query := &Query{Tags: []*Tag{&Tag{Operator: "", Value: `value`, Key: "key"}}} - So(strings.Join(builder.renderTags(query), ""), ShouldEqual, `"key" = 'value'`) + So(strings.Join(query.renderTags(), ""), ShouldEqual, `"key" = 'value'`) }) Convey("can render regex tags without operator", func() { query := &Query{Tags: []*Tag{&Tag{Operator: "", Value: `/value/`, Key: "key"}}} - So(strings.Join(builder.renderTags(query), ""), ShouldEqual, `"key" =~ /value/`) + So(strings.Join(query.renderTags(), ""), ShouldEqual, `"key" =~ /value/`) }) Convey("can render regex tags", func() { query := &Query{Tags: []*Tag{&Tag{Operator: "=~", Value: `/value/`, Key: "key"}}} - So(strings.Join(builder.renderTags(query), ""), ShouldEqual, `"key" =~ /value/`) + So(strings.Join(query.renderTags(), ""), ShouldEqual, `"key" =~ /value/`) }) Convey("can render number tags", func() { query := &Query{Tags: []*Tag{&Tag{Operator: "=", Value: "10001", Key: "key"}}} - So(strings.Join(builder.renderTags(query), ""), ShouldEqual, `"key" = 10001`) + So(strings.Join(query.renderTags(), ""), ShouldEqual, `"key" = 10001`) }) Convey("can render number tags with decimals", func() { query := &Query{Tags: []*Tag{&Tag{Operator: "=", Value: "10001.1", Key: "key"}}} - So(strings.Join(builder.renderTags(query), ""), ShouldEqual, `"key" = 10001.1`) + So(strings.Join(query.renderTags(), ""), ShouldEqual, `"key" = 10001.1`) }) Convey("can render string tags", func() { query := &Query{Tags: []*Tag{&Tag{Operator: "=", Value: "value", Key: "key"}}} - So(strings.Join(builder.renderTags(query), ""), ShouldEqual, `"key" = 'value'`) + So(strings.Join(query.renderTags(), ""), ShouldEqual, `"key" = 'value'`) }) }) } From abb8f33d77c394ed257d10dea3351949943b23ba Mon Sep 17 00:00:00 2001 From: bergquist Date: Thu, 10 Nov 2016 16:07:13 +0100 Subject: [PATCH 16/28] feat(alertlist): make it possible to filter on alerts from current dashboard --- public/app/plugins/panel/alertlist/editor.html | 1 + public/app/plugins/panel/alertlist/module.ts | 13 +++++++++++-- 2 files changed, 12 insertions(+), 2 deletions(-) diff --git a/public/app/plugins/panel/alertlist/editor.html b/public/app/plugins/panel/alertlist/editor.html index b2038df34b8..344af7408fc 100644 --- a/public/app/plugins/panel/alertlist/editor.html +++ b/public/app/plugins/panel/alertlist/editor.html @@ -11,6 +11,7 @@ Max items
+
State filter
diff --git a/public/app/plugins/panel/alertlist/module.ts b/public/app/plugins/panel/alertlist/module.ts index 543c5f04833..d03fad49073 100644 --- a/public/app/plugins/panel/alertlist/module.ts +++ b/public/app/plugins/panel/alertlist/module.ts @@ -25,7 +25,8 @@ class AlertListPanel extends PanelCtrl { panelDefaults = { show: 'current', limit: 10, - stateFilter: [] + stateFilter: [], + onlyAlertsOnDashboard: false }; @@ -71,9 +72,13 @@ class AlertListPanel extends PanelCtrl { var params: any = { limit: this.panel.limit, type: 'alert', - newState: this.panel.stateFilter + newState: this.panel.stateFilter, }; + if (this.panel.onlyAlertsOnDashboard) { + params.dashboardId = this.dashboard.id; + } + params.from = dateMath.parse(this.dashboard.time.from).unix() * 1000; params.to = dateMath.parse(this.dashboard.time.to).unix() * 1000; @@ -93,6 +98,10 @@ class AlertListPanel extends PanelCtrl { state: this.panel.stateFilter }; + if (this.panel.onlyAlertsOnDashboard) { + params.dashboardId = this.dashboard.id; + } + this.backendSrv.get(`/api/alerts`, params) .then(res => { this.currentAlerts = _.map(res, al => { From a51de8e987311a0da3ac479dd37d12b10796e71d Mon Sep 17 00:00:00 2001 From: bergquist Date: Thu, 10 Nov 2016 16:15:48 +0100 Subject: [PATCH 17/28] tech(conf): remove dragoo :( --- conf/defaults.ini | 17 +---------------- conf/sample.ini | 15 --------------- 2 files changed, 1 insertion(+), 31 deletions(-) diff --git a/conf/defaults.ini b/conf/defaults.ini index b25f3ab6f28..7ead103d027 100644 --- a/conf/defaults.ini +++ b/conf/defaults.ini @@ -229,7 +229,7 @@ auth_url = https://accounts.google.com/o/oauth2/auth token_url = https://accounts.google.com/o/oauth2/token api_url = https://www.googleapis.com/oauth2/v1/userinfo allowed_domains = -hosted_domain = +hosted_domain = #################################### Grafana.net Auth #################### [auth.grafananet] @@ -390,21 +390,6 @@ global_api_key = -1 global_session = -1 #################################### Alerting ############################ -# docs about alerting can be found in /docs/sources/alerting/ -# __.-/| -# \`o_O' -# =( )= +----------------------------+ -# U| | Alerting is still in alpha | -# /\ /\ / | +----------------------------+ -# ) /^\) ^\/ _)\ | -# ) /^\/ _) \ | -# ) _ / / _) \___|_ -# /\ )/\/ || | )_)\___,|)) -# < > |(,,) )__) | -# || / \)___)\ -# | \____( )___) )____ -# \______(_______;;;)__;;;) - [alerting] # Makes it possible to turn off alert rule execution. execute_alerts = true diff --git a/conf/sample.ini b/conf/sample.ini index e1ed408210a..e8c9c990a63 100644 --- a/conf/sample.ini +++ b/conf/sample.ini @@ -339,21 +339,6 @@ ;path = /var/lib/grafana/dashboards #################################### Alerting ###################################### -# docs about alerting can be found in /docs/sources/alerting/ -# __.-/| -# \`o_O' -# =( )= +----------------------------+ -# U| | Alerting is still in alpha | -# /\ /\ / | +----------------------------+ -# ) /^\) ^\/ _)\ | -# ) /^\/ _) \ | -# ) _ / / _) \___|_ -# /\ )/\/ || | )_)\___,|)) -# < > |(,,) )__) | -# || / \)___)\ -# | \____( )___) )____ -# \______(_______;;;)__;;;) - [alerting] # Makes it possible to turn off alert rule execution. ;execute_alerts = true From e04d27c0b0d18cf2f92190e6f09ad564b46161c1 Mon Sep 17 00:00:00 2001 From: bergquist Date: Fri, 11 Nov 2016 08:46:52 +0100 Subject: [PATCH 18/28] fix(influxdb): return internal influxdb errors ref #6523 --- pkg/tsdb/influxdb/influxdb.go | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/pkg/tsdb/influxdb/influxdb.go b/pkg/tsdb/influxdb/influxdb.go index 4efb495dd05..5043a2deffc 100644 --- a/pkg/tsdb/influxdb/influxdb.go +++ b/pkg/tsdb/influxdb/influxdb.go @@ -82,6 +82,10 @@ func (e *InfluxDBExecutor) Execute(ctx context.Context, queries tsdb.QuerySlice, return result.WithError(err) } + if response.Err != nil { + return result.WithError(response.Err) + } + result.QueryResults = make(map[string]*tsdb.QueryResult) result.QueryResults["A"] = e.ResponseParser.Parse(&response, query) From b98f817d682a957da112df38a012c0bf5f7f9903 Mon Sep 17 00:00:00 2001 From: Andrew McDonald Date: Thu, 10 Nov 2016 23:54:44 -0800 Subject: [PATCH 19/28] Fix for cloudwatch datasource requesting too many datapoints (#6544) The range checking in _getPeriod appeared to be using a different variable than the period that had just been calculated for its bounds checks. --- public/app/plugins/datasource/cloudwatch/datasource.js | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/public/app/plugins/datasource/cloudwatch/datasource.js b/public/app/plugins/datasource/cloudwatch/datasource.js index fde9e8f09ea..105b08c2b17 100644 --- a/public/app/plugins/datasource/cloudwatch/datasource.js +++ b/public/app/plugins/datasource/cloudwatch/datasource.js @@ -78,10 +78,10 @@ function (angular, _, moment, dateMath, kbn, CloudWatchAnnotationQuery) { } else { period = kbn.interval_to_seconds(templateSrv.replace(target.period, options.scopedVars)); } - if (query.period < 60) { + if (period < 60) { period = 60; } - if (range / query.period >= 1440) { + if (range / period >= 1440) { period = Math.ceil(range / 1440 / 60) * 60; } From f5a804a5584cc06a24670d02cba0b8a94d9489b1 Mon Sep 17 00:00:00 2001 From: huydx Date: Fri, 11 Nov 2016 17:59:27 +0900 Subject: [PATCH 20/28] Fix typo (password strenght -> password strength) --- public/app/core/core.ts | 2 +- .../directives/{password_strenght.js => password_strength.js} | 0 2 files changed, 1 insertion(+), 1 deletion(-) rename public/app/core/directives/{password_strenght.js => password_strength.js} (100%) diff --git a/public/app/core/core.ts b/public/app/core/core.ts index 567df736a4e..8e9f64fd17f 100644 --- a/public/app/core/core.ts +++ b/public/app/core/core.ts @@ -10,7 +10,7 @@ import "./directives/grafana_version_check"; import "./directives/metric_segment"; import "./directives/misc"; import "./directives/ng_model_on_blur"; -import "./directives/password_strenght"; +import "./directives/password_strength"; import "./directives/spectrum_picker"; import "./directives/tags"; import "./directives/value_select_dropdown"; diff --git a/public/app/core/directives/password_strenght.js b/public/app/core/directives/password_strength.js similarity index 100% rename from public/app/core/directives/password_strenght.js rename to public/app/core/directives/password_strength.js From ad97db937c0d8de14a8a3779154df51b5b11a36a Mon Sep 17 00:00:00 2001 From: bergquist Date: Fri, 11 Nov 2016 13:57:11 +0100 Subject: [PATCH 21/28] feat(stats_usage): add stats about alerts --- pkg/metrics/publish.go | 1 + pkg/models/stats.go | 1 + pkg/services/sqlstore/stats.go | 6 +++++- 3 files changed, 7 insertions(+), 1 deletion(-) diff --git a/pkg/metrics/publish.go b/pkg/metrics/publish.go index 4255481b8d1..70db3fc0f86 100644 --- a/pkg/metrics/publish.go +++ b/pkg/metrics/publish.go @@ -101,6 +101,7 @@ func sendUsageStats() { metrics["stats.plugins.apps.count"] = len(plugins.Apps) metrics["stats.plugins.panels.count"] = len(plugins.Panels) metrics["stats.plugins.datasources.count"] = len(plugins.DataSources) + metrics["stats.alerts.count"] = statsQuery.Result.AlertCount dsStats := m.GetDataSourceStatsQuery{} if err := bus.Dispatch(&dsStats); err != nil { diff --git a/pkg/models/stats.go b/pkg/models/stats.go index 067dec763e5..9db645f76ac 100644 --- a/pkg/models/stats.go +++ b/pkg/models/stats.go @@ -5,6 +5,7 @@ type SystemStats struct { UserCount int64 OrgCount int64 PlaylistCount int64 + AlertCount int64 } type DataSourceStats struct { diff --git a/pkg/services/sqlstore/stats.go b/pkg/services/sqlstore/stats.go index 7580996ad57..eaf461c4d28 100644 --- a/pkg/services/sqlstore/stats.go +++ b/pkg/services/sqlstore/stats.go @@ -39,7 +39,11 @@ func GetSystemStats(query *m.GetSystemStatsQuery) error { ( SELECT COUNT(*) FROM ` + dialect.Quote("playlist") + ` - ) AS playlist_count + ) AS playlist_count, + ( + SELECT COUNT(*) + FROM ` + dialect.Quote("alert") + ` + ) AS alert_count ` var stats m.SystemStats From a87fd11f26ece20ed48d4cbae6a59025c44a5f0f Mon Sep 17 00:00:00 2001 From: bergquist Date: Fri, 11 Nov 2016 14:04:38 +0100 Subject: [PATCH 22/28] feat(stats): add alerts to global admin stats --- pkg/models/stats.go | 1 + pkg/services/sqlstore/stats.go | 6 +++++- public/app/features/admin/partials/stats.html | 4 ++++ 3 files changed, 10 insertions(+), 1 deletion(-) diff --git a/pkg/models/stats.go b/pkg/models/stats.go index 9db645f76ac..09c251b6cd7 100644 --- a/pkg/models/stats.go +++ b/pkg/models/stats.go @@ -30,6 +30,7 @@ type AdminStats struct { DataSourceCount int `json:"data_source_count"` PlaylistCount int `json:"playlist_count"` StarredDbCount int `json:"starred_db_count"` + AlertCount int `json:"alert_count"` } type GetAdminStatsQuery struct { diff --git a/pkg/services/sqlstore/stats.go b/pkg/services/sqlstore/stats.go index eaf461c4d28..dd1a8111332 100644 --- a/pkg/services/sqlstore/stats.go +++ b/pkg/services/sqlstore/stats.go @@ -89,7 +89,11 @@ func GetAdminStats(query *m.GetAdminStatsQuery) error { ( SELECT COUNT(DISTINCT ` + dialect.Quote("dashboard_id") + ` ) FROM ` + dialect.Quote("star") + ` - ) AS starred_db_count + ) AS starred_db_count, + ( + SELECT COUNT(*) + FROM ` + dialect.Quote("alert") + ` + ) AS alert_count ` var stats m.AdminStats diff --git a/public/app/features/admin/partials/stats.html b/public/app/features/admin/partials/stats.html index bcc246b9e82..92e32f4f947 100644 --- a/public/app/features/admin/partials/stats.html +++ b/public/app/features/admin/partials/stats.html @@ -46,6 +46,10 @@ Total starred dashboards {{ctrl.stats.starred_db_count}} + + Total alerts + {{ctrl.stats.alert_count}} +
From 4fdfee739a9fc3d5dd812658fd29bf5a9e2a890f Mon Sep 17 00:00:00 2001 From: bergquist Date: Mon, 14 Nov 2016 08:47:45 +0100 Subject: [PATCH 23/28] fix(influxdb): add support for regex measurments closes #6560 --- pkg/tsdb/influxdb/query.go | 12 ++++++++++-- pkg/tsdb/influxdb/query_test.go | 12 ++++++++++++ 2 files changed, 22 insertions(+), 2 deletions(-) diff --git a/pkg/tsdb/influxdb/query.go b/pkg/tsdb/influxdb/query.go index d9208aaee3a..e95b54b0a18 100644 --- a/pkg/tsdb/influxdb/query.go +++ b/pkg/tsdb/influxdb/query.go @@ -11,7 +11,8 @@ import ( ) var ( - regexpOperatorPattern *regexp.Regexp = regexp.MustCompile(`^\/.*\/$`) + regexpOperatorPattern *regexp.Regexp = regexp.MustCompile(`^\/.*\/$`) + regexpMeasurementPattern *regexp.Regexp = regexp.MustCompile(`^\/.*\/$`) ) func (query *Query) Build(queryContext *tsdb.QueryContext) (string, error) { @@ -108,7 +109,14 @@ func (query *Query) renderMeasurement() string { } else { policy = `"` + query.Policy + `".` } - return fmt.Sprintf(` FROM %s"%s"`, policy, query.Measurement) + + measurement := query.Measurement + + if !regexpMeasurementPattern.Match([]byte(measurement)) { + measurement = fmt.Sprintf(`"%s"`, measurement) + } + + return fmt.Sprintf(` FROM %s%s`, policy, measurement) } func (query *Query) renderWhereClause() string { diff --git a/pkg/tsdb/influxdb/query_test.go b/pkg/tsdb/influxdb/query_test.go index e8c1312d673..ee045444c9b 100644 --- a/pkg/tsdb/influxdb/query_test.go +++ b/pkg/tsdb/influxdb/query_test.go @@ -120,5 +120,17 @@ func TestInfluxdbQueryBuilder(t *testing.T) { So(strings.Join(query.renderTags(), ""), ShouldEqual, `"key" = 'value'`) }) + + Convey("can render regular measurement", func() { + query := &Query{Measurement: `apa`, Policy: "policy"} + + So(query.renderMeasurement(), ShouldEqual, ` FROM "policy"."apa"`) + }) + + Convey("can render regexp measurement", func() { + query := &Query{Measurement: `/apa/`, Policy: "policy"} + + So(query.renderMeasurement(), ShouldEqual, ` FROM "policy"./apa/`) + }) }) } From 62ff85e7748f46f63b74055b1ddbacbbb65cb45f Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Torkel=20=C3=96degaard?= Date: Mon, 14 Nov 2016 11:34:58 +0100 Subject: [PATCH 24/28] docs(): fixed ldap link --- docs/sources/installation/configuration.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/sources/installation/configuration.md b/docs/sources/installation/configuration.md index 2fc727d659f..f3b40b206bf 100644 --- a/docs/sources/installation/configuration.md +++ b/docs/sources/installation/configuration.md @@ -413,7 +413,7 @@ Set to `true` to enable LDAP integration (default: `false`) ### config_file Path to the LDAP specific configuration file (default: `/etc/grafana/ldap.toml`) -> For details on LDAP Configuration, go to the [LDAP Integration](ldap.md) page. +> For details on LDAP Configuration, go to the [LDAP Integration]({{< relref "ldap.md" >}}) page.
From 0cdf05ae5d22ba22409e5ffdf71d9b865a833ac2 Mon Sep 17 00:00:00 2001 From: Nic Roland Date: Mon, 14 Nov 2016 16:04:33 +0000 Subject: [PATCH 25/28] docs(datasources/influxdb): Fix broken image link Twas missing a leading `/`. --- docs/sources/datasources/influxdb.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/sources/datasources/influxdb.md b/docs/sources/datasources/influxdb.md index a5eab80af06..01b68d5b7dc 100644 --- a/docs/sources/datasources/influxdb.md +++ b/docs/sources/datasources/influxdb.md @@ -118,7 +118,7 @@ SHOW TAG VALUES WITH KEY = "hostname" WHERE region =~ /$region/ > Always you `regex values` or `regex wildcard` for All format or multi select format. -![](img/docs/influxdb/templating_simple_ex1.png) +![](/img/docs/influxdb/templating_simple_ex1.png) ## Annotations Annotations allows you to overlay rich event information on top of graphs. From b9d709ab27a9905ded1df5d7e670f2c92a12a9cb Mon Sep 17 00:00:00 2001 From: bergquist Date: Tue, 15 Nov 2016 11:47:44 +0100 Subject: [PATCH 26/28] feat(alerting): improve error logging for extracting alerts ref #6576 --- pkg/services/alerting/rule.go | 31 ++++++++++++++++++++++++++----- 1 file changed, 26 insertions(+), 5 deletions(-) diff --git a/pkg/services/alerting/rule.go b/pkg/services/alerting/rule.go index 809640ed4a7..bdf53798e34 100644 --- a/pkg/services/alerting/rule.go +++ b/pkg/services/alerting/rule.go @@ -26,11 +26,32 @@ type Rule struct { } type ValidationError struct { - Reason string + Reason string + Err error + Alertid int64 + DashboardId int64 + PanelId int64 } func (e ValidationError) Error() string { - return e.Reason + extraInfo := "" + if e.Alertid != 0 { + extraInfo = fmt.Sprintf("%s AlertId: %v", extraInfo, e.Alertid) + } + + if e.PanelId != 0 { + extraInfo = fmt.Sprintf("%s PanelId: %v ", extraInfo, e.PanelId) + } + + if e.DashboardId != 0 { + extraInfo = fmt.Sprintf("%s DashboardId: %v", extraInfo, e.DashboardId) + } + + if e.Err != nil { + return fmt.Sprintf("%s %s%s", e.Err.Error(), e.Reason, extraInfo) + } + + return fmt.Sprintf("Failed to extract alert.Reason: %s %s", e.Reason, extraInfo) } var ( @@ -83,7 +104,7 @@ func NewRuleFromDBAlert(ruleDef *m.Alert) (*Rule, error) { for _, v := range ruleDef.Settings.Get("notifications").MustArray() { jsonModel := simplejson.NewFromAny(v) if id, err := jsonModel.Get("id").Int64(); err != nil { - return nil, ValidationError{Reason: "Invalid notification schema"} + return nil, ValidationError{Reason: "Invalid notification schema", DashboardId: model.DashboardId, Alertid: model.Id, PanelId: model.PanelId} } else { model.Notifications = append(model.Notifications, id) } @@ -93,10 +114,10 @@ func NewRuleFromDBAlert(ruleDef *m.Alert) (*Rule, error) { conditionModel := simplejson.NewFromAny(condition) conditionType := conditionModel.Get("type").MustString() if factory, exist := conditionFactories[conditionType]; !exist { - return nil, ValidationError{Reason: "Unknown alert condition: " + conditionType} + return nil, ValidationError{Reason: "Unknown alert condition: " + conditionType, DashboardId: model.DashboardId, Alertid: model.Id, PanelId: model.PanelId} } else { if queryCondition, err := factory(conditionModel, index); err != nil { - return nil, err + return nil, ValidationError{Err: err, DashboardId: model.DashboardId, Alertid: model.Id, PanelId: model.PanelId} } else { model.Conditions = append(model.Conditions, queryCondition) } From a4de6da9a0c8a77d22b905d35ec01fc5e6f14ab6 Mon Sep 17 00:00:00 2001 From: Ben RUBSON Date: Tue, 15 Nov 2016 18:59:17 +0100 Subject: [PATCH 27/28] Correct series highlight (#6578) solves issue #6573 --- public/app/plugins/panel/graph/graph_tooltip.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/public/app/plugins/panel/graph/graph_tooltip.js b/public/app/plugins/panel/graph/graph_tooltip.js index 3aec815428f..40ed293ff4e 100644 --- a/public/app/plugins/panel/graph/graph_tooltip.js +++ b/public/app/plugins/panel/graph/graph_tooltip.js @@ -195,7 +195,7 @@ function ($) { } var highlightClass = ''; - if (item && i === item.seriesIndex) { + if (item && hoverInfo.index === item.seriesIndex) { highlightClass = 'graph-tooltip-list-item--highlight'; } From cbe8af967d0cbe9a81f437cea4fb92a5908742f9 Mon Sep 17 00:00:00 2001 From: bergquist Date: Wed, 16 Nov 2016 09:06:05 +0100 Subject: [PATCH 28/28] docs(api): add docs about creating new org close #6588 --- docs/sources/http_api/org.md | 28 ++++++++++++++++++++++++++++ 1 file changed, 28 insertions(+) diff --git a/docs/sources/http_api/org.md b/docs/sources/http_api/org.md index adb5d5cd31e..36188075124 100644 --- a/docs/sources/http_api/org.md +++ b/docs/sources/http_api/org.md @@ -85,6 +85,34 @@ page_keywords: grafana, admin, http, api, documentation, orgs, organisation } } +## Create Organisation + +`POST /api/org` + +**Example Request**: + + POST /api/org HTTP/1.1 + Accept: application/json + Content-Type: application/json + Authorization: Bearer eyJrIjoiT0tTcG1pUlY2RnVKZTFVaDFsNFZXdE9ZWmNrMkZYbk + + { + "name":"New Org." + } + + +**Example Response**: + + HTTP/1.1 200 + Content-Type: application/json + + { + "orgId":"1", + "message":"Organization created" + } + + + ## Update current Organisation `PUT /api/org`