From bfe28ee061ea42b27057c582f0b436cf12c46e88 Mon Sep 17 00:00:00 2001 From: Sven Klemm Date: Mon, 13 Aug 2018 12:08:14 +0200 Subject: [PATCH 01/14] Add $__unixEpochGroup macro to postgres datasource --- docs/sources/features/datasources/postgres.md | 2 ++ pkg/tsdb/postgres/macros.go | 21 +++++++++++++++++++ pkg/tsdb/postgres/macros_test.go | 12 +++++++++++ .../postgres/partials/query.editor.html | 2 ++ 4 files changed, 37 insertions(+) diff --git a/docs/sources/features/datasources/postgres.md b/docs/sources/features/datasources/postgres.md index 2be2db0837b..cf77643f06b 100644 --- a/docs/sources/features/datasources/postgres.md +++ b/docs/sources/features/datasources/postgres.md @@ -68,6 +68,8 @@ Macro example | Description *$__unixEpochFilter(dateColumn)* | Will be replaced by a time range filter using the specified column name with times represented as unix timestamp. For example, *dateColumn >= 1494410783 AND dateColumn <= 1494497183* *$__unixEpochFrom()* | Will be replaced by the start of the currently active time selection as unix timestamp. For example, *1494410783* *$__unixEpochTo()* | Will be replaced by the end of the currently active time selection as unix timestamp. For example, *1494497183* +*$__unixEpochGroup(dateColumn,'5m', [fillmode])* | Same as $__timeGroup but for times stored as unix timestamp (only available in Grafana 5.3+). +*$__unixEpochGroupAlias(dateColumn,'5m', [fillmode])* | Same as above but also adds a column alias (only available in Grafana 5.3+). We plan to add many more macros. If you have suggestions for what macros you would like to see, please [open an issue](https://github.com/grafana/grafana) in our GitHub repo. diff --git a/pkg/tsdb/postgres/macros.go b/pkg/tsdb/postgres/macros.go index a4b4aaa9d1e..d2a3d599441 100644 --- a/pkg/tsdb/postgres/macros.go +++ b/pkg/tsdb/postgres/macros.go @@ -134,6 +134,27 @@ func (m *postgresMacroEngine) evaluateMacro(name string, args []string) (string, return fmt.Sprintf("%d", m.timeRange.GetFromAsSecondsEpoch()), nil case "__unixEpochTo": return fmt.Sprintf("%d", m.timeRange.GetToAsSecondsEpoch()), nil + case "__unixEpochGroup": + if len(args) < 2 { + return "", fmt.Errorf("macro %v needs time column and interval and optional fill value", name) + } + interval, err := time.ParseDuration(strings.Trim(args[1], `'`)) + if err != nil { + return "", fmt.Errorf("error parsing interval %v", args[1]) + } + if len(args) == 3 { + err := tsdb.SetupFillmode(m.query, interval, args[2]) + if err != nil { + return "", err + } + } + return fmt.Sprintf("floor(%s/%v)*%v", args[0], interval.Seconds(), interval.Seconds()), nil + case "__unixEpochGroupAlias": + tg, err := m.evaluateMacro("__unixEpochGroup", args) + if err == nil { + return tg + " AS \"time\"", err + } + return "", err default: return "", fmt.Errorf("Unknown macro %v", name) } diff --git a/pkg/tsdb/postgres/macros_test.go b/pkg/tsdb/postgres/macros_test.go index beeea93893b..a029fc49ee0 100644 --- a/pkg/tsdb/postgres/macros_test.go +++ b/pkg/tsdb/postgres/macros_test.go @@ -110,6 +110,18 @@ func TestMacroEngine(t *testing.T) { So(sql, ShouldEqual, fmt.Sprintf("select %d", to.Unix())) }) + + Convey("interpolate __unixEpochGroup function", func() { + + sql, err := engine.Interpolate(query, timeRange, "SELECT $__unixEpochGroup(time_column,'5m')") + So(err, ShouldBeNil) + sql2, err := engine.Interpolate(query, timeRange, "SELECT $__unixEpochGroupAlias(time_column,'5m')") + So(err, ShouldBeNil) + + So(sql, ShouldEqual, "SELECT floor(time_column/300)*300") + So(sql2, ShouldEqual, sql+" AS \"time\"") + }) + }) Convey("Given a time range between 1960-02-01 07:00 and 1965-02-03 08:00", func() { diff --git a/public/app/plugins/datasource/postgres/partials/query.editor.html b/public/app/plugins/datasource/postgres/partials/query.editor.html index 20353b81ba2..763fd6a6e96 100644 --- a/public/app/plugins/datasource/postgres/partials/query.editor.html +++ b/public/app/plugins/datasource/postgres/partials/query.editor.html @@ -57,6 +57,8 @@ Macros: by setting fillvalue grafana will fill in missing values according to the interval fillvalue can be either a literal value, NULL or previous; previous will fill in the previous seen value or NULL if none has been seen yet - $__timeGroupAlias(column,'5m') -> (extract(epoch from column)/300)::bigint*300 AS "time" +- $__unixEpochGroup(column,'5m') -> floor(column/300)*300 +- $__unixEpochGroupAlias(column,'5m') -> floor(column/300)*300 AS "time" Example of group by and order by with $__timeGroup: SELECT From fbc67a1c64a0a94d169aea63aa00c0f1055dfc6d Mon Sep 17 00:00:00 2001 From: Sven Klemm Date: Mon, 13 Aug 2018 12:17:05 +0200 Subject: [PATCH 02/14] add $__unixEpochGroup to mysql datasource --- docs/sources/features/datasources/mysql.md | 2 ++ pkg/tsdb/mysql/macros.go | 21 +++++++++++++++++++ pkg/tsdb/mysql/macros_test.go | 12 +++++++++++ .../mysql/partials/query.editor.html | 2 ++ 4 files changed, 37 insertions(+) diff --git a/docs/sources/features/datasources/mysql.md b/docs/sources/features/datasources/mysql.md index cdb78deed35..afac746b050 100644 --- a/docs/sources/features/datasources/mysql.md +++ b/docs/sources/features/datasources/mysql.md @@ -71,6 +71,8 @@ Macro example | Description *$__unixEpochFilter(dateColumn)* | Will be replaced by a time range filter using the specified column name with times represented as unix timestamp. For example, *dateColumn > 1494410783 AND dateColumn < 1494497183* *$__unixEpochFrom()* | Will be replaced by the start of the currently active time selection as unix timestamp. For example, *1494410783* *$__unixEpochTo()* | Will be replaced by the end of the currently active time selection as unix timestamp. For example, *1494497183* +*$__unixEpochGroup(dateColumn,'5m', [fillmode])* | Same as $__timeGroup but for times stored as unix timestamp (only available in Grafana 5.3+). +*$__unixEpochGroupAlias(dateColumn,'5m', [fillmode])* | Same as above but also adds a column alias (only available in Grafana 5.3+). We plan to add many more macros. If you have suggestions for what macros you would like to see, please [open an issue](https://github.com/grafana/grafana) in our GitHub repo. diff --git a/pkg/tsdb/mysql/macros.go b/pkg/tsdb/mysql/macros.go index 48fa193edd5..0dabdd7c283 100644 --- a/pkg/tsdb/mysql/macros.go +++ b/pkg/tsdb/mysql/macros.go @@ -112,6 +112,27 @@ func (m *mySqlMacroEngine) evaluateMacro(name string, args []string) (string, er return fmt.Sprintf("%d", m.timeRange.GetFromAsSecondsEpoch()), nil case "__unixEpochTo": return fmt.Sprintf("%d", m.timeRange.GetToAsSecondsEpoch()), nil + case "__unixEpochGroup": + if len(args) < 2 { + return "", fmt.Errorf("macro %v needs time column and interval and optional fill value", name) + } + interval, err := time.ParseDuration(strings.Trim(args[1], `'`)) + if err != nil { + return "", fmt.Errorf("error parsing interval %v", args[1]) + } + if len(args) == 3 { + err := tsdb.SetupFillmode(m.query, interval, args[2]) + if err != nil { + return "", err + } + } + return fmt.Sprintf("%s DIV %v * %v", args[0], interval.Seconds(), interval.Seconds()), nil + case "__unixEpochGroupAlias": + tg, err := m.evaluateMacro("__unixEpochGroup", args) + if err == nil { + return tg + " AS \"time\"", err + } + return "", err default: return "", fmt.Errorf("Unknown macro %v", name) } diff --git a/pkg/tsdb/mysql/macros_test.go b/pkg/tsdb/mysql/macros_test.go index fd9d3f5688a..fe153ca3e2d 100644 --- a/pkg/tsdb/mysql/macros_test.go +++ b/pkg/tsdb/mysql/macros_test.go @@ -97,6 +97,18 @@ func TestMacroEngine(t *testing.T) { So(sql, ShouldEqual, fmt.Sprintf("select %d", to.Unix())) }) + + Convey("interpolate __unixEpochGroup function", func() { + + sql, err := engine.Interpolate(query, timeRange, "SELECT $__unixEpochGroup(time_column,'5m')") + So(err, ShouldBeNil) + sql2, err := engine.Interpolate(query, timeRange, "SELECT $__unixEpochGroupAlias(time_column,'5m')") + So(err, ShouldBeNil) + + So(sql, ShouldEqual, "SELECT time_column DIV 300 * 300") + So(sql2, ShouldEqual, sql+" AS \"time\"") + }) + }) Convey("Given a time range between 1960-02-01 07:00 and 1965-02-03 08:00", func() { diff --git a/public/app/plugins/datasource/mysql/partials/query.editor.html b/public/app/plugins/datasource/mysql/partials/query.editor.html index 7c799eec21b..1e829a1175d 100644 --- a/public/app/plugins/datasource/mysql/partials/query.editor.html +++ b/public/app/plugins/datasource/mysql/partials/query.editor.html @@ -57,6 +57,8 @@ Macros: by setting fillvalue grafana will fill in missing values according to the interval fillvalue can be either a literal value, NULL or previous; previous will fill in the previous seen value or NULL if none has been seen yet - $__timeGroupAlias(column,'5m') -> cast(cast(UNIX_TIMESTAMP(column)/(300) as signed)*300 as signed) AS "time" +- $__unixEpochGroup(column,'5m') -> column DIV 300 * 300 +- $__unixEpochGroupAlias(column,'5m') -> column DIV 300 * 300 AS "time" Example of group by and order by with $__timeGroup: SELECT From 8c4d59363e6aabd9cb772af41569f13e64951691 Mon Sep 17 00:00:00 2001 From: Sven Klemm Date: Mon, 13 Aug 2018 12:23:42 +0200 Subject: [PATCH 03/14] add $__unixEpochGroup to mssql datasource --- docs/sources/features/datasources/mssql.md | 2 ++ pkg/tsdb/mssql/macros.go | 21 +++++++++++++++++++ pkg/tsdb/mssql/macros_test.go | 12 +++++++++++ .../mssql/partials/query.editor.html | 2 ++ 4 files changed, 37 insertions(+) diff --git a/docs/sources/features/datasources/mssql.md b/docs/sources/features/datasources/mssql.md index caaf5a6b321..da0c9581e99 100644 --- a/docs/sources/features/datasources/mssql.md +++ b/docs/sources/features/datasources/mssql.md @@ -88,6 +88,8 @@ Macro example | Description *$__unixEpochFilter(dateColumn)* | Will be replaced by a time range filter using the specified column name with times represented as unix timestamp. For example, *dateColumn > 1494410783 AND dateColumn < 1494497183* *$__unixEpochFrom()* | Will be replaced by the start of the currently active time selection as unix timestamp. For example, *1494410783* *$__unixEpochTo()* | Will be replaced by the end of the currently active time selection as unix timestamp. For example, *1494497183* +*$__unixEpochGroup(dateColumn,'5m', [fillmode])* | Same as $__timeGroup but for times stored as unix timestamp (only available in Grafana 5.3+). +*$__unixEpochGroupAlias(dateColumn,'5m', [fillmode])* | Same as above but also adds a column alias (only available in Grafana 5.3+). We plan to add many more macros. If you have suggestions for what macros you would like to see, please [open an issue](https://github.com/grafana/grafana) in our GitHub repo. diff --git a/pkg/tsdb/mssql/macros.go b/pkg/tsdb/mssql/macros.go index 920e3781e0c..caba043e7b6 100644 --- a/pkg/tsdb/mssql/macros.go +++ b/pkg/tsdb/mssql/macros.go @@ -116,6 +116,27 @@ func (m *msSqlMacroEngine) evaluateMacro(name string, args []string) (string, er return fmt.Sprintf("%d", m.timeRange.GetFromAsSecondsEpoch()), nil case "__unixEpochTo": return fmt.Sprintf("%d", m.timeRange.GetToAsSecondsEpoch()), nil + case "__unixEpochGroup": + if len(args) < 2 { + return "", fmt.Errorf("macro %v needs time column and interval and optional fill value", name) + } + interval, err := time.ParseDuration(strings.Trim(args[1], `'`)) + if err != nil { + return "", fmt.Errorf("error parsing interval %v", args[1]) + } + if len(args) == 3 { + err := tsdb.SetupFillmode(m.query, interval, args[2]) + if err != nil { + return "", err + } + } + return fmt.Sprintf("FLOOR(%s/%v)*%v", args[0], interval.Seconds(), interval.Seconds()), nil + case "__unixEpochGroupAlias": + tg, err := m.evaluateMacro("__unixEpochGroup", args) + if err == nil { + return tg + " AS [time]", err + } + return "", err default: return "", fmt.Errorf("Unknown macro %v", name) } diff --git a/pkg/tsdb/mssql/macros_test.go b/pkg/tsdb/mssql/macros_test.go index 8362ae05aa6..8e0973b750c 100644 --- a/pkg/tsdb/mssql/macros_test.go +++ b/pkg/tsdb/mssql/macros_test.go @@ -145,6 +145,18 @@ func TestMacroEngine(t *testing.T) { So(sql, ShouldEqual, fmt.Sprintf("select %d", to.Unix())) }) + + Convey("interpolate __unixEpochGroup function", func() { + + sql, err := engine.Interpolate(query, timeRange, "SELECT $__unixEpochGroup(time_column,'5m')") + So(err, ShouldBeNil) + sql2, err := engine.Interpolate(query, timeRange, "SELECT $__unixEpochGroupAlias(time_column,'5m')") + So(err, ShouldBeNil) + + So(sql, ShouldEqual, "SELECT FLOOR(time_column/300)*300") + So(sql2, ShouldEqual, sql+" AS [time]") + }) + }) Convey("Given a time range between 1960-02-01 07:00 and 1965-02-03 08:00", func() { diff --git a/public/app/plugins/datasource/mssql/partials/query.editor.html b/public/app/plugins/datasource/mssql/partials/query.editor.html index 7888e36a24c..4b0a46b6412 100644 --- a/public/app/plugins/datasource/mssql/partials/query.editor.html +++ b/public/app/plugins/datasource/mssql/partials/query.editor.html @@ -57,6 +57,8 @@ Macros: by setting fillvalue grafana will fill in missing values according to the interval fillvalue can be either a literal value, NULL or previous; previous will fill in the previous seen value or NULL if none has been seen yet - $__timeGroupAlias(column, '5m'[, fillvalue]) -> CAST(ROUND(DATEDIFF(second, '1970-01-01', column)/300.0, 0) as bigint)*300 AS [time] +- $__unixEpochGroup(column,'5m') -> FLOOR(column/300)*300 +- $__unixEpochGroupAlias(column,'5m') -> FLOOR(column/300)*300 AS [time] Example of group by and order by with $__timeGroup: SELECT From aed89b49c01d677bf5b41fbdc893da59f9f2868d Mon Sep 17 00:00:00 2001 From: Marcus Efraimsson Date: Tue, 14 Aug 2018 14:48:14 +0200 Subject: [PATCH 04/14] should allow one default datasource per organisation using provisioning --- .../provisioning/datasources/config_reader.go | 6 ++--- .../datasources/config_reader_test.go | 14 ++++++++++ .../provisioning/datasources/datasources.go | 2 +- .../testdata/multiple-org-default/config.yaml | 27 +++++++++++++++++++ 4 files changed, 45 insertions(+), 4 deletions(-) create mode 100644 pkg/services/provisioning/datasources/testdata/multiple-org-default/config.yaml diff --git a/pkg/services/provisioning/datasources/config_reader.go b/pkg/services/provisioning/datasources/config_reader.go index 4b8931f0ed3..b2930c2b679 100644 --- a/pkg/services/provisioning/datasources/config_reader.go +++ b/pkg/services/provisioning/datasources/config_reader.go @@ -83,7 +83,7 @@ func (cr *configReader) parseDatasourceConfig(path string, file os.FileInfo) (*D } func validateDefaultUniqueness(datasources []*DatasourcesAsConfig) error { - defaultCount := 0 + defaultCount := map[int64]int{} for i := range datasources { if datasources[i].Datasources == nil { continue @@ -95,8 +95,8 @@ func validateDefaultUniqueness(datasources []*DatasourcesAsConfig) error { } if ds.IsDefault { - defaultCount++ - if defaultCount > 1 { + defaultCount[ds.OrgId] = defaultCount[ds.OrgId] + 1 + if defaultCount[ds.OrgId] > 1 { return ErrInvalidConfigToManyDefault } } diff --git a/pkg/services/provisioning/datasources/config_reader_test.go b/pkg/services/provisioning/datasources/config_reader_test.go index 2e407dbe4de..df99456b21b 100644 --- a/pkg/services/provisioning/datasources/config_reader_test.go +++ b/pkg/services/provisioning/datasources/config_reader_test.go @@ -19,6 +19,7 @@ var ( allProperties = "testdata/all-properties" versionZero = "testdata/version-0" brokenYaml = "testdata/broken-yaml" + multipleOrgsWithDefault = "testdata/multiple-org-default" fakeRepo *fakeRepository ) @@ -73,6 +74,19 @@ func TestDatasourceAsConfig(t *testing.T) { }) }) + Convey("Multiple datasources in different organizations with is_default in each organization", func() { + dc := newDatasourceProvisioner(logger) + err := dc.applyChanges(multipleOrgsWithDefault) + Convey("should not raise error", func() { + So(err, ShouldBeNil) + So(len(fakeRepo.inserted), ShouldEqual, 4) + So(fakeRepo.inserted[0].IsDefault, ShouldBeTrue) + So(fakeRepo.inserted[0].OrgId, ShouldEqual, 1) + So(fakeRepo.inserted[2].IsDefault, ShouldBeTrue) + So(fakeRepo.inserted[2].OrgId, ShouldEqual, 2) + }) + }) + Convey("Two configured datasource and purge others ", func() { Convey("two other datasources in database", func() { fakeRepo.loadAll = []*models.DataSource{ diff --git a/pkg/services/provisioning/datasources/datasources.go b/pkg/services/provisioning/datasources/datasources.go index 1fa0a3b3173..de6c876baad 100644 --- a/pkg/services/provisioning/datasources/datasources.go +++ b/pkg/services/provisioning/datasources/datasources.go @@ -11,7 +11,7 @@ import ( ) var ( - ErrInvalidConfigToManyDefault = errors.New("datasource.yaml config is invalid. Only one datasource can be marked as default") + ErrInvalidConfigToManyDefault = errors.New("datasource.yaml config is invalid. Only one datasource per organization can be marked as default") ) func Provision(configDirectory string) error { diff --git a/pkg/services/provisioning/datasources/testdata/multiple-org-default/config.yaml b/pkg/services/provisioning/datasources/testdata/multiple-org-default/config.yaml new file mode 100644 index 00000000000..447317a8c77 --- /dev/null +++ b/pkg/services/provisioning/datasources/testdata/multiple-org-default/config.yaml @@ -0,0 +1,27 @@ +apiVersion: 1 + +datasources: + - orgId: 1 + name: prometheus + type: prometheus + isDefault: True + access: proxy + url: http://prometheus.example.com:9090 + - name: Graphite + type: graphite + access: proxy + url: http://localhost:8080 + is_default: true + - orgId: 2 + name: prometheus + type: prometheus + isDefault: True + access: proxy + url: http://prometheus.example.com:9090 + - orgId: 2 + name: Graphite + type: graphite + access: proxy + url: http://localhost:8080 + is_default: true + From 04d50fb4054d7bb17a94cfa7b5cf34ef2a9c4a6b Mon Sep 17 00:00:00 2001 From: Pierre GIRAUD Date: Thu, 16 Aug 2018 15:19:07 +0200 Subject: [PATCH 05/14] Doc - fix broken link --- docs/sources/guides/basic_concepts.md | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/docs/sources/guides/basic_concepts.md b/docs/sources/guides/basic_concepts.md index b710a227a79..d3f8dd0ba63 100644 --- a/docs/sources/guides/basic_concepts.md +++ b/docs/sources/guides/basic_concepts.md @@ -54,7 +54,7 @@ We utilize a unit abstraction so that Grafana looks great on all screens both sm > Note: With MaxDataPoint functionality, Grafana can show you the perfect amount of datapoints no matter your resolution or time-range. -Utilize the [Repeating Row functionality](/reference/templating/#utilizing-template-variables-with-repeating-panels-and-repeating-rows) to dynamically create or remove entire Rows (that can be filled with Panels), based on the Template variables selected. +Utilize the [Repeating Rows functionality](/reference/templating/#repeating-rows) to dynamically create or remove entire Rows (that can be filled with Panels), based on the Template variables selected. Rows can be collapsed by clicking on the Row Title. If you save a Dashboard with a Row collapsed, it will save in that state and will not preload those graphs until the row is expanded. @@ -72,7 +72,7 @@ Panels like the [Graph](/reference/graph/) panel allow you to graph as many metr Panels can be made more dynamic by utilizing [Dashboard Templating](/reference/templating/) variable strings within the panel configuration (including queries to your Data Source configured via the Query Editor). -Utilize the [Repeating Panel](/reference/templating/#utilizing-template-variables-with-repeating-panels-and-repeating-rows) functionality to dynamically create or remove Panels based on the [Templating Variables](/reference/templating/#utilizing-template-variables-with-repeating-panels-and-repeating-rows) selected. +Utilize the [Repeating Panel](/reference/templating/#repeating-panels) functionality to dynamically create or remove Panels based on the [Templating Variables](/reference/templating/#repeating-panels) selected. The time range on Panels is normally what is set in the [Dashboard time picker](/reference/timerange/) but this can be overridden by utilizes [Panel specific time overrides](/reference/timerange/#panel-time-overrides-timeshift). From ea704fcf9bb03b45d84edd5013a74b8f7e988844 Mon Sep 17 00:00:00 2001 From: Pierre GIRAUD Date: Thu, 16 Aug 2018 16:49:04 +0200 Subject: [PATCH 06/14] Update doc about repeating panels There's no per-row contextual menu anymore Instead there's a option --- docs/sources/reference/templating.md | 26 ++++++++++++++++++++------ 1 file changed, 20 insertions(+), 6 deletions(-) diff --git a/docs/sources/reference/templating.md b/docs/sources/reference/templating.md index d04d56dc788..96109503e12 100644 --- a/docs/sources/reference/templating.md +++ b/docs/sources/reference/templating.md @@ -284,18 +284,32 @@ Currently only supported for Prometheus data sources. This variable represents t Template variables can be very useful to dynamically change your queries across a whole dashboard. If you want Grafana to dynamically create new panels or rows based on what values you have selected you can use the *Repeat* feature. -If you have a variable with `Multi-value` or `Include all value` options enabled you can choose one panel or one row and have Grafana repeat that row -for every selected value. You find this option under the General tab in panel edit mode. Select the variable to repeat by, and a `min span`. -The `min span` controls how small Grafana will make the panels (if you have many values selected). Grafana will automatically adjust the width of -each repeated panel so that the whole row is filled. Currently, you cannot mix other panels on a row with a repeated panel. +If you have a variable with `Multi-value` or `Include all value` options enabled you can choose one panel and have Grafana repeat that panel +for every selected value. You find the *Repeat* feature under the *General tab* in panel edit mode. + +The `direction` controls how the panels will be arranged. + +By choosing `horizontal` the panels will be arranged side-by-side. Grafana will automatically adjust the width +of each repeated panel so that the whole row is filled. Currently, you cannot mix other panels on a row with a repeated +panel. Each panel will never be smaller that the provided `Min width` if you have many selected values. + +By choosing `vertical` the panels will be arranged from top to bottom in a column. The `Min width` doesn't have any effect in this case. The width of the repeated panels will be the same as of the first panel (the original template) being repeated. Only make changes to the first panel (the original template). To have the changes take effect on all panels you need to trigger a dynamic dashboard re-build. You can do this by either changing the variable value (that is the basis for the repeat) or reload the dashboard. ## Repeating Rows -This option requires you to open the row options view. Hover over the row left side to trigger the row menu, in this menu click `Row Options`. This -opens the row options view. Here you find a *Repeat* dropdown where you can select the variable to repeat by. +As seen above with the *Panels* you can also repeat *Rows* if you have variables set with `Multi-value` or +`Include all value` selection option. + +To enable this feature you need to first add a new *Row* using the *Add Panel* menu. Then by hovering the row title and +clicking on the cog button, you will access the `Row Options` configuration panel. You can then select the variable +you want to repeat the row for. + +It may be a good idea to use a variable in the row title as well. + +Example: [Repeated Rows Dashboard](http://play.grafana.org/dashboard/db/repeated-rows) ### URL state From d06c8c0e6816bd255863ee3efe4952574d6a0292 Mon Sep 17 00:00:00 2001 From: Pierre GIRAUD Date: Fri, 17 Aug 2018 09:31:30 +0200 Subject: [PATCH 07/14] Doc - fix title level --- docs/sources/reference/templating.md | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/docs/sources/reference/templating.md b/docs/sources/reference/templating.md index 96109503e12..7f86465312c 100644 --- a/docs/sources/reference/templating.md +++ b/docs/sources/reference/templating.md @@ -311,11 +311,11 @@ It may be a good idea to use a variable in the row title as well. Example: [Repeated Rows Dashboard](http://play.grafana.org/dashboard/db/repeated-rows) -### URL state +## URL state Variable values are always synced to the URL using the syntax `var-=value`. -### Examples +## Examples - [Graphite Templated Dashboard](http://play.grafana.org/dashboard/db/graphite-templated-nested) - [Elasticsearch Templated Dashboard](http://play.grafana.org/dashboard/db/elasticsearch-templated) From 190432296d3e1b936d3ac3584bf084c8556930b5 Mon Sep 17 00:00:00 2001 From: Patrick O'Carroll Date: Fri, 17 Aug 2018 10:40:57 +0200 Subject: [PATCH 08/14] removed inverse btn styling and added bgColor to generic oauth and grafana.com login buttons, added styling so log in button uses dark theme inverse btn styling both for dark and light theme --- public/app/partials/login.html | 4 ++-- public/sass/_variables.scss | 4 ++-- public/sass/pages/_login.scss | 9 +++++++++ 3 files changed, 13 insertions(+), 4 deletions(-) diff --git a/public/app/partials/login.html b/public/app/partials/login.html index 87b3cada7b5..656103adce2 100644 --- a/public/app/partials/login.html +++ b/public/app/partials/login.html @@ -55,12 +55,12 @@ Sign in with GitLab - -