diff --git a/.bra.toml b/.bra.toml
index 15961e1e3fd..aa7a1680adc 100644
--- a/.bra.toml
+++ b/.bra.toml
@@ -4,6 +4,7 @@ init_cmds = [
["./bin/grafana-server", "cfg:app_mode=development"]
]
watch_all = true
+follow_symlinks = true
watch_dirs = [
"$WORKDIR/pkg",
"$WORKDIR/public/views",
diff --git a/.circleci/config.yml b/.circleci/config.yml
index 1e046aec34d..a5497e6c7e8 100644
--- a/.circleci/config.yml
+++ b/.circleci/config.yml
@@ -19,7 +19,7 @@ version: 2
jobs:
mysql-integration-test:
docker:
- - image: circleci/golang:1.10
+ - image: circleci/golang:1.11
- image: circleci/mysql:5.6-ram
environment:
MYSQL_ROOT_PASSWORD: rootpass
@@ -32,14 +32,14 @@ jobs:
- run: sudo apt update
- run: sudo apt install -y mysql-client
- run: dockerize -wait tcp://127.0.0.1:3306 -timeout 120s
- - run: cat docker/blocks/mysql_tests/setup.sql | mysql -h 127.0.0.1 -P 3306 -u root -prootpass
+ - run: cat devenv/docker/blocks/mysql_tests/setup.sql | mysql -h 127.0.0.1 -P 3306 -u root -prootpass
- run:
name: mysql integration tests
command: 'GRAFANA_TEST_DB=mysql go test ./pkg/services/sqlstore/... ./pkg/tsdb/mysql/... '
postgres-integration-test:
docker:
- - image: circleci/golang:1.10
+ - image: circleci/golang:1.11
- image: circleci/postgres:9.3-ram
environment:
POSTGRES_USER: grafanatest
@@ -51,7 +51,7 @@ jobs:
- run: sudo apt update
- run: sudo apt install -y postgresql-client
- run: dockerize -wait tcp://127.0.0.1:5432 -timeout 120s
- - run: 'PGPASSWORD=grafanatest psql -p 5432 -h 127.0.0.1 -U grafanatest -d grafanatest -f docker/blocks/postgres_tests/setup.sql'
+ - run: 'PGPASSWORD=grafanatest psql -p 5432 -h 127.0.0.1 -U grafanatest -d grafanatest -f devenv/docker/blocks/postgres_tests/setup.sql'
- run:
name: postgres integration tests
command: 'GRAFANA_TEST_DB=postgres go test ./pkg/services/sqlstore/... ./pkg/tsdb/postgres/...'
@@ -74,22 +74,23 @@ jobs:
gometalinter:
docker:
- - image: circleci/golang:1.10
+ - image: circleci/golang:1.11
environment:
# we need CGO because of go-sqlite3
CGO_ENABLED: 1
working_directory: /go/src/github.com/grafana/grafana
steps:
- checkout
- - run: 'go get -u gopkg.in/alecthomas/gometalinter.v2'
+ - run: 'go get -u github.com/alecthomas/gometalinter'
- run: 'go get -u github.com/tsenart/deadcode'
+ - run: 'go get -u github.com/jgautheron/goconst/cmd/goconst'
- run: 'go get -u github.com/gordonklaus/ineffassign'
- run: 'go get -u github.com/opennota/check/cmd/structcheck'
- run: 'go get -u github.com/mdempsky/unconvert'
- run: 'go get -u github.com/opennota/check/cmd/varcheck'
- run:
name: run linters
- command: 'gometalinter.v2 --enable-gc --vendor --deadline 10m --disable-all --enable=deadcode --enable=ineffassign --enable=structcheck --enable=unconvert --enable=varcheck ./...'
+ command: 'gometalinter --enable-gc --vendor --deadline 10m --disable-all --enable=deadcode --enable=goconst --enable=ineffassign --enable=structcheck --enable=unconvert --enable=varcheck ./...'
- run:
name: run go vet
command: 'go vet ./pkg/...'
@@ -115,7 +116,7 @@ jobs:
test-backend:
docker:
- - image: circleci/golang:1.10
+ - image: circleci/golang:1.11
working_directory: /go/src/github.com/grafana/grafana
steps:
- checkout
@@ -125,7 +126,7 @@ jobs:
build-all:
docker:
- - image: grafana/build-container:1.0.0
+ - image: grafana/build-container:1.2.0
working_directory: /go/src/github.com/grafana/grafana
steps:
- checkout
@@ -157,18 +158,23 @@ jobs:
name: sha-sum packages
command: 'go run build.go sha-dist'
- run:
- name: Build Grafana.com publisher
+ name: Build Grafana.com master publisher
command: 'go build -o scripts/publish scripts/build/publish.go'
+ - run:
+ name: Build Grafana.com release publisher
+ command: 'cd scripts/build/release_publisher && go build -o release_publisher .'
- persist_to_workspace:
root: .
paths:
- dist/grafana*
- scripts/*.sh
- scripts/publish
+ - scripts/build/release_publisher/release_publisher
+ - scripts/build/publish.sh
build:
docker:
- - image: grafana/build-container:1.0.0
+ - image: grafana/build-container:1.2.0
working_directory: /go/src/github.com/grafana/grafana
steps:
- checkout
@@ -227,7 +233,7 @@ jobs:
build-enterprise:
docker:
- - image: grafana/build-container:v0.1
+ - image: grafana/build-container:1.2.0
working_directory: /go/src/github.com/grafana/grafana
steps:
- checkout
@@ -298,8 +304,8 @@ jobs:
name: deploy to s3
command: 'aws s3 sync ./dist s3://$BUCKET_NAME/release'
- run:
- name: Trigger Windows build
- command: './scripts/trigger_windows_build.sh ${APPVEYOR_TOKEN} ${CIRCLE_SHA1} release'
+ name: Deploy to Grafana.com
+ command: './scripts/build/publish.sh'
workflows:
version: 2
diff --git a/.github/CONTRIBUTING.md b/.github/CONTRIBUTING.md
deleted file mode 100644
index 769ba2a519b..00000000000
--- a/.github/CONTRIBUTING.md
+++ /dev/null
@@ -1,22 +0,0 @@
-Follow the setup guide in README.md
-
-### Rebuild frontend assets on source change
-```
-yarn watch
-```
-
-### Rerun tests on source change
-```
-yarn jest
-```
-
-### Run tests for backend assets before commit
-```
-test -z "$(gofmt -s -l . | grep -v -E 'vendor/(github.com|golang.org|gopkg.in)' | tee /dev/stderr)"
-```
-
-### Run tests for frontend assets before commit
-```
-yarn test
-go test -v ./pkg/...
-```
diff --git a/.gitignore b/.gitignore
index bf97948d178..21083741e14 100644
--- a/.gitignore
+++ b/.gitignore
@@ -40,8 +40,8 @@ public/css/*.min.css
conf/custom.ini
fig.yml
-docker-compose.yml
-docker-compose.yaml
+devenv/docker-compose.yml
+devenv/docker-compose.yaml
/conf/provisioning/**/custom.yaml
/conf/provisioning/**/dev.yaml
/conf/ldap_dev.toml
@@ -54,6 +54,7 @@ profile.cov
/pkg/cmd/grafana-server/grafana-server
/pkg/cmd/grafana-server/debug
/pkg/extensions
+/public/app/extensions
debug.test
/examples/*/dist
/packaging/**/*.rpm
@@ -68,7 +69,9 @@ debug.test
/vendor/**/*.yml
/vendor/**/*_test.go
/vendor/**/.editorconfig
-/vendor/**/appengine*
*.orig
/devenv/bulk-dashboards/*.json
+/devenv/bulk_alerting_dashboards/*.json
+
+/scripts/build/release_publisher/release_publisher
diff --git a/CHANGELOG.md b/CHANGELOG.md
index aed25afb02e..c80f2852f2c 100644
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -1,77 +1,168 @@
-# 5.3.0 (unreleased)
+# 5.4.0 (unreleased)
+### New Features
+
+* **Alerting**: Option to disable OK alert notifications [#12330](https://github.com/grafana/grafana/issues/12330) & [#6696](https://github.com/grafana/grafana/issues/6696), thx [@davewat](https://github.com/davewat)
+* **Postgres/MySQL/MSSQL**: Adds support for configuration of max open/idle connections and connection max lifetime. Also, panels with multiple SQL queries will now be executed concurrently [#11711](https://github.com/grafana/grafana/issues/11711), thx [@connection-reset](https://github.com/connection-reset)
+* **MSSQL**: Add encrypt setting to allow configuration of how data sent between client and server are encrypted [#13629](https://github.com/grafana/grafana/issues/13629), thx [@ramiro](https://github.com/ramiro)
+* **MySQL**: Support connecting thru Unix socket for MySQL datasource [#12342](https://github.com/grafana/grafana/issues/12342), thx [@Yukinoshita-Yukino](https://github.com/Yukinoshita-Yukino)
+
+### Minor
+
+* **Cloudwatch**: Show all available CloudWatch regions [#12308](https://github.com/grafana/grafana/issues/12308), thx [@mtanda](https://github.com/mtanda)
+* **Units**: New clock time format, to format ms or second values as for example `01h:59m`, [#13635](https://github.com/grafana/grafana/issues/13635), thx [@franciscocpg](https://github.com/franciscocpg)
+* **Datasource Proxy**: Keep trailing slash for datasource proxy requests [#13326](https://github.com/grafana/grafana/pull/13326), thx [@ryantxu](https://github.com/ryantxu)
+
+### Breaking changes
+
+* Postgres/MySQL/MSSQL datasources now per default uses `max open connections` = `unlimited` (earlier 10), `max idle connections` = `2` (earlier 10) and `connection max lifetime` = `4` hours (earlier unlimited)
+
+# 5.3.2 (unreleased)
+
+* **Postgres**: Fix template variables error [#13692](https://github.com/grafana/grafana/issues/13692), thx [@svenklemm](https://github.com/svenklemm)
+* **Cloudwatch**: Fix service panic because of race conditions [#13674](https://github.com/grafana/grafana/issues/13674), thx [@mtanda](https://github.com/mtanda)
+* **LDAP**: Fix super admins can also be admins of orgs [#13710](https://github.com/grafana/grafana/issues/13710), thx [@adrien-f](https://github.com/adrien-f)
+
+# 5.3.1 (2018-10-16)
+
+* **Render**: Fix PhantomJS render of graph panel when legend displayed as table to the right [#13616](https://github.com/grafana/grafana/issues/13616)
+* **Stackdriver**: Filter option disappears after removing initial filter [#13607](https://github.com/grafana/grafana/issues/13607)
+* **Elasticsearch**: Fix no limit size in terms aggregation for alerting queries [#13172](https://github.com/grafana/grafana/issues/13172), thx [@Yukinoshita-Yukino](https://github.com/Yukinoshita-Yukino)
+* **InfluxDB**: Fix for annotation issue that caused text to be shown twice [#13553](https://github.com/grafana/grafana/issues/13553)
+* **Variables**: Fix nesting variables leads to exception and missing refresh [#13628](https://github.com/grafana/grafana/issues/13628)
+* **Variables**: Prometheus: Single letter labels are not supported [#13641](https://github.com/grafana/grafana/issues/13641), thx [@olshansky](https://github.com/olshansky)
+* **Graph**: Fix graph time formatting for Last 24h ranges [#13650](https://github.com/grafana/grafana/issues/13650)
+* **Playlist**: Fix cannot add dashboards with long names to playlist [#13464](https://github.com/grafana/grafana/issues/13464), thx [@neufeldtech](https://github.com/neufeldtech)
+* **HTTP API**: Fix /api/org/users so that query and limit querystrings works
+
+# 5.3.0 (2018-10-10)
+
+* **Stackdriver**: Filter wildcards and regex matching are not yet supported [#13495](https://github.com/grafana/grafana/issues/13495)
+* **Stackdriver**: Support the distribution metric type for heatmaps [#13559](https://github.com/grafana/grafana/issues/13559)
+* **Cloudwatch**: Automatically set graph yaxis unit [#13575](https://github.com/grafana/grafana/issues/13575), thx [@mtanda](https://github.com/mtanda)
+
+# 5.3.0-beta3 (2018-10-03)
+
+* **Stackdriver**: Fix for missing ngInject [#13511](https://github.com/grafana/grafana/pull/13511)
+* **Permissions**: Fix for broken permissions selector [#13507](https://github.com/grafana/grafana/issues/13507)
+* **Alerting**: Alert reminders deduping not working as expected when running multiple Grafana instances [#13492](https://github.com/grafana/grafana/issues/13492)
+
+# 5.3.0-beta2 (2018-10-01)
+
+### New Features
+
+* **Annotations**: Enable template variables in tagged annotations queries [#9735](https://github.com/grafana/grafana/issues/9735)
+* **Stackdriver**: Support for Google Stackdriver Datasource [#13289](https://github.com/grafana/grafana/pull/13289)
+
+### Minor
+
+* **Provisioning**: Dashboard Provisioning now support symlinks that changes target [#12534](https://github.com/grafana/grafana/issues/12534), thx [@auhlig](https://github.com/auhlig)
+* **OAuth**: Allow oauth email attribute name to be configurable [#12986](https://github.com/grafana/grafana/issues/12986), thx [@bobmshannon](https://github.com/bobmshannon)
+* **Tags**: Default sort order for GetDashboardTags [#11681](https://github.com/grafana/grafana/pull/11681), thx [@Jonnymcc](https://github.com/Jonnymcc)
+* **Prometheus**: Label completion queries respect dashboard time range [#12251](https://github.com/grafana/grafana/pull/12251), thx [@mtanda](https://github.com/mtanda)
+* **Prometheus**: Allow to display annotations based on Prometheus series value [#10159](https://github.com/grafana/grafana/issues/10159), thx [@mtanda](https://github.com/mtanda)
+* **Prometheus**: Adhoc-filtering for Prometheus dashboards [#13212](https://github.com/grafana/grafana/issues/13212)
+* **Singlestat**: Fix gauge display accuracy for percents [#13270](https://github.com/grafana/grafana/issues/13270), thx [@tianon](https://github.com/tianon)
+* **Dashboard**: Prevent auto refresh from starting when loading dashboard with absolute time range [#12030](https://github.com/grafana/grafana/issues/12030)
+* **Templating**: New templating variable type `Text box` that allows free text input [#3173](https://github.com/grafana/grafana/issues/3173)
+* **Alerting**: Link to view full size image in Microsoft Teams alert notifier [#13121](https://github.com/grafana/grafana/issues/13121), thx [@holiiveira](https://github.com/holiiveira)
+* **Alerting**: Fixes a bug where all alerts would send reminders after upgrade & restart [#13402](https://github.com/grafana/grafana/pull/13402)
+* **Alerting**: Concurrent render limit for graphs used in notifications [#13401](https://github.com/grafana/grafana/pull/13401)
+* **Postgres/MySQL/MSSQL**: Add support for replacing $__interval and $__interval_ms in alert queries [#11555](https://github.com/grafana/grafana/issues/11555), thx [@svenklemm](https://github.com/svenklemm)
+
+# 5.3.0-beta1 (2018-09-06)
+
+### New Major Features
+
+* **Alerting**: Notification reminders [#7330](https://github.com/grafana/grafana/issues/7330), thx [@jbaublitz](https://github.com/jbaublitz)
+* **Dashboard**: TV & Kiosk mode changes, new cycle view mode button in dashboard toolbar [#13025](https://github.com/grafana/grafana/pull/13025)
* **OAuth**: Gitlab OAuth with support for filter by groups [#5623](https://github.com/grafana/grafana/issues/5623), thx [@BenoitKnecht](https://github.com/BenoitKnecht)
-* **Dataproxy**: Pass configured/auth headers to a Datasource [#10971](https://github.com/grafana/grafana/issues/10971), thx [@mrsiano](https://github.com/mrsiano)
-* **Cleanup**: Make temp file time to live configurable [#11607](https://github.com/grafana/grafana/issues/11607), thx [@xapon](https://github.com/xapon)
+* **Postgres**: Graphical query builder [#10095](https://github.com/grafana/grafana/issues/10095), thx [svenklemm](https://github.com/svenklemm)
+
+### New Features
+
* **LDAP**: Define Grafana Admin permission in ldap group mappings [#2469](https://github.com/grafana/grafana/issues/2496), PR [#12622](https://github.com/grafana/grafana/issues/12622)
-* **Cloudwatch**: CloudWatch GetMetricData support [#11487](https://github.com/grafana/grafana/issues/11487), thx [@mtanda](https://github.com/mtanda)
-* **Configuration**: Allow auto-assigning users to specific organization (other than Main. Org) [#1823](https://github.com/grafana/grafana/issues/1823) [#12801](https://github.com/grafana/grafana/issues/12801), thx [@gzzo](https://github.com/gzzo) and [@ofosos](https://github.com/ofosos)
-* **Profile**: List teams that the user is member of in current/active organization [#12476](https://github.com/grafana/grafana/issues/12476)
* **LDAP**: Client certificates support [#12805](https://github.com/grafana/grafana/issues/12805), thx [@nyxi](https://github.com/nyxi)
+* **Profile**: List teams that the user is member of in current/active organization [#12476](https://github.com/grafana/grafana/issues/12476)
+* **Configuration**: Allow auto-assigning users to specific organization (other than Main. Org) [#1823](https://github.com/grafana/grafana/issues/1823) [#12801](https://github.com/grafana/grafana/issues/12801), thx [@gzzo](https://github.com/gzzo) and [@ofosos](https://github.com/ofosos)
+* **Dataproxy**: Pass configured/auth headers to a Datasource [#10971](https://github.com/grafana/grafana/issues/10971), thx [@mrsiano](https://github.com/mrsiano)
+* **CloudWatch**: GetMetricData support [#11487](https://github.com/grafana/grafana/issues/11487), thx [@mtanda](https://github.com/mtanda)
* **Postgres**: TimescaleDB support, e.g. use `time_bucket` for grouping by time when option enabled [#12680](https://github.com/grafana/grafana/pull/12680), thx [svenklemm](https://github.com/svenklemm)
+* **Cleanup**: Make temp file time to live configurable [#11607](https://github.com/grafana/grafana/issues/11607), thx [@xapon](https://github.com/xapon)
### Minor
-* **Api**: Delete nonexistent datasource should return 404 [#12313](https://github.com/grafana/grafana/issues/12313), thx [@AustinWinstanley](https://github.com/AustinWinstanley)
-* **Dashboard**: Fix selecting current dashboard from search should not reload dashboard [#12248](https://github.com/grafana/grafana/issues/12248)
-* **Dashboard**: Use uid when linking to dashboards internally in a dashboard [#10705](https://github.com/grafana/grafana/issues/10705)
-* **Singlestat**: Make colorization of prefix and postfix optional in singlestat [#11892](https://github.com/grafana/grafana/pull/11892), thx [@ApsOps](https://github.com/ApsOps)
+* **Alerting**: Its now possible to configure the default value for how to handle errors and no data in alerting. [#10424](https://github.com/grafana/grafana/issues/10424)
+* **Alerting**: Fix diff and percent_diff reducers [#11563](https://github.com/grafana/grafana/issues/11563), thx [@jessetane](https://github.com/jessetane)
+* **Alerting**: Fix rendering timeout which could cause notifications to not be sent due to rendering timing out [#12151](https://github.com/grafana/grafana/issues/12151)
+* **Docker**: Make it possible to set a specific plugin url [#12861](https://github.com/grafana/grafana/pull/12861), thx [ClementGautier](https://github.com/ClementGautier)
+* **GrafanaCli**: Fixed issue with grafana-cli install plugin resulting in corrupt http response from source error. Fixes [#13079](https://github.com/grafana/grafana/issues/13079)
+* **Provisioning**: Should allow one default datasource per organisation [#12229](https://github.com/grafana/grafana/issues/12229)
+* **Github OAuth**: Allow changes of user info at Github to be synched to Grafana when signing in [#11818](https://github.com/grafana/grafana/issues/11818), thx [@rwaweber](https://github.com/rwaweber)
+* **OAuth**: Fix overriding tls_skip_verify_insecure using environment variable [#12747](https://github.com/grafana/grafana/issues/12747), thx [@jangaraj](https://github.com/jangaraj)
* **Prometheus**: Fix graph panel bar width issue in aligned prometheus queries [#12379](https://github.com/grafana/grafana/issues/12379)
* **Prometheus**: Heatmap - fix unhandled error when some points are missing [#12484](https://github.com/grafana/grafana/issues/12484)
* **Prometheus**: Add $__interval, $__interval_ms, $__range, $__range_s & $__range_ms support for dashboard and template queries [#12597](https://github.com/grafana/grafana/issues/12597) [#12882](https://github.com/grafana/grafana/issues/12882), thx [@roidelapluie](https://github.com/roidelapluie)
-* **Variables**: Skip unneeded extra query request when de-selecting variable values used for repeated panels [#8186](https://github.com/grafana/grafana/issues/8186), thx [@mtanda](https://github.com/mtanda)
-* **Variables**: Limit amount of queries executed when updating variable that other variable(s) are dependent on [#11890](https://github.com/grafana/grafana/issues/11890)
-* **Variables**: Support query variable refresh when another variable referenced in `Regex` field change its value [#12952](https://github.com/grafana/grafana/issues/12952), thx [@franciscocpg](https://github.com/franciscocpg)
-* **Variables**: Support variables in query variable `Custom all value` field [#12965](https://github.com/grafana/grafana/issues/12965), thx [@franciscocpg](https://github.com/franciscocpg)
+* **Elasticsearch**: For alerting/backend, support having index name to the right of pattern in index pattern [#12731](https://github.com/grafana/grafana/issues/12731)
+* **Graphite**: Fix for quoting of int function parameters (when using variables) [#11927](https://github.com/grafana/grafana/pull/11927)
+* **InfluxDB**: Support timeFilter in query templating for InfluxDB [#12598](https://github.com/grafana/grafana/pull/12598), thx [kichristensen](https://github.com/kichristensen)
* **Postgres/MySQL/MSSQL**: New $__unixEpochGroup and $__unixEpochGroupAlias macros [#12892](https://github.com/grafana/grafana/issues/12892), thx [@svenklemm](https://github.com/svenklemm)
* **Postgres/MySQL/MSSQL**: Add previous fill mode to $__timeGroup macro which will fill in previously seen value when point is missing [#12756](https://github.com/grafana/grafana/issues/12756), thx [@svenklemm](https://github.com/svenklemm)
* **Postgres/MySQL/MSSQL**: Use floor rounding in $__timeGroup macro function [#12460](https://github.com/grafana/grafana/issues/12460), thx [@svenklemm](https://github.com/svenklemm)
* **Postgres/MySQL/MSSQL**: Use metric column as prefix when returning multiple value columns [#12727](https://github.com/grafana/grafana/issues/12727), thx [@svenklemm](https://github.com/svenklemm)
* **Postgres/MySQL/MSSQL**: New $__timeGroupAlias macro. Postgres $__timeGroup no longer automatically adds time column alias [#12749](https://github.com/grafana/grafana/issues/12749), thx [@svenklemm](https://github.com/svenklemm)
* **Postgres/MySQL/MSSQL**: Escape single quotes in variables [#12785](https://github.com/grafana/grafana/issues/12785), thx [@eMerzh](https://github.com/eMerzh)
+* **Postgres/MySQL/MSSQL**: Min time interval support [#13157](https://github.com/grafana/grafana/issues/13157), thx [@svenklemm](https://github.com/svenklemm)
* **MySQL/MSSQL**: Use datetime format instead of epoch for $__timeFilter, $__timeFrom and $__timeTo macros [#11618](https://github.com/grafana/grafana/issues/11618) [#11619](https://github.com/grafana/grafana/issues/11619), thx [@AustinWinstanley](https://github.com/AustinWinstanley)
* **Postgres**: Escape ssl mode parameter in connectionstring [#12644](https://github.com/grafana/grafana/issues/12644), thx [@yogyrahmawan](https://github.com/yogyrahmawan)
-* **Github OAuth**: Allow changes of user info at Github to be synched to Grafana when signing in [#11818](https://github.com/grafana/grafana/issues/11818), thx [@rwaweber](https://github.com/rwaweber)
-* **Alerting**: Fix diff and percent_diff reducers [#11563](https://github.com/grafana/grafana/issues/11563), thx [@jessetane](https://github.com/jessetane)
-* **Alerting**: Fix rendering timeout which could cause notifications to not be sent due to rendering timing out [#12151](https://github.com/grafana/grafana/issues/12151)
* **Cloudwatch**: Improved error handling [#12489](https://github.com/grafana/grafana/issues/12489), thx [@mtanda](https://github.com/mtanda)
* **Cloudwatch**: AppSync metrics and dimensions [#12300](https://github.com/grafana/grafana/issues/12300), thx [@franciscocpg](https://github.com/franciscocpg)
* **Cloudwatch**: Direct Connect metrics and dimensions [#12762](https://github.com/grafana/grafana/pulls/12762), thx [@mindriot88](https://github.com/mindriot88)
* **Cloudwatch**: Added BurstBalance metric to list of AWS RDS metrics [#12561](https://github.com/grafana/grafana/pulls/12561), thx [@activeshadow](https://github.com/activeshadow)
* **Cloudwatch**: Add new Redshift metrics and dimensions [#12063](https://github.com/grafana/grafana/pulls/12063), thx [@A21z](https://github.com/A21z)
+* **Dashboard**: Fix selecting current dashboard from search should not reload dashboard [#12248](https://github.com/grafana/grafana/issues/12248)
+* **Dashboard**: Use uid when linking to dashboards internally in a dashboard [#10705](https://github.com/grafana/grafana/issues/10705)
+* **Graph**: Option to hide series from tooltip [#3341](https://github.com/grafana/grafana/issues/3341), thx [@mtanda](https://github.com/mtanda)
+* **Singlestat**: Make colorization of prefix and postfix optional in singlestat [#11892](https://github.com/grafana/grafana/pull/11892), thx [@ApsOps](https://github.com/ApsOps)
* **Table**: Adjust header contrast for the light theme [#12668](https://github.com/grafana/grafana/issues/12668)
* **Table**: Fix link color when using light theme and thresholds in use [#12766](https://github.com/grafana/grafana/issues/12766)
-om/grafana/grafana/issues/12668)
* **Table**: Fix for useless horizontal scrollbar for table panel [#9964](https://github.com/grafana/grafana/issues/9964)
* **Table**: Make table sorting stable when null values exist [#12362](https://github.com/grafana/grafana/pull/12362), thx [@bz2](https://github.com/bz2)
-* **Elasticsearch**: For alerting/backend, support having index name to the right of pattern in index pattern [#12731](https://github.com/grafana/grafana/issues/12731)
-* **OAuth**: Fix overriding tls_skip_verify_insecure using environment variable [#12747](https://github.com/grafana/grafana/issues/12747), thx [@jangaraj](https://github.com/jangaraj)
+* **Heatmap**: Fix broken tooltip and crosshair on Firefox [#12486](https://github.com/grafana/grafana/issues/12486)
+* **Datasource**: Fix UI issue with secret fields after updating datasource [#11270](https://github.com/grafana/grafana/issues/11270)
+* **Variables**: Skip unneeded extra query request when de-selecting variable values used for repeated panels [#8186](https://github.com/grafana/grafana/issues/8186), thx [@mtanda](https://github.com/mtanda)
+* **Variables**: Limit amount of queries executed when updating variable that other variable(s) are dependent on [#11890](https://github.com/grafana/grafana/issues/11890)
+* **Variables**: Support query variable refresh when another variable referenced in `Regex` field change its value [#12952](https://github.com/grafana/grafana/issues/12952), thx [@franciscocpg](https://github.com/franciscocpg)
+* **Variables**: Support variables in query variable `Custom all value` field [#12965](https://github.com/grafana/grafana/issues/12965), thx [@franciscocpg](https://github.com/franciscocpg)
* **Units**: Change units to include characters for power of 2 and 3 [#12744](https://github.com/grafana/grafana/pull/12744), thx [@Worty](https://github.com/Worty)
* **Units**: Polish złoty currency [#12691](https://github.com/grafana/grafana/pull/12691), thx [@mwegrzynek](https://github.com/mwegrzynek)
-* **Graph**: Option to hide series from tooltip [#3341](https://github.com/grafana/grafana/issues/3341), thx [@mtanda](https://github.com/mtanda)
+* **Units**: Adds bitcoin axes unit. [#13125](https://github.com/grafana/grafana/pull/13125)
+* **Api**: Delete nonexistent datasource should return 404 [#12313](https://github.com/grafana/grafana/issues/12313), thx [@AustinWinstanley](https://github.com/AustinWinstanley)
+* **Logging**: Reopen log files after receiving a SIGHUP signal [#13112](https://github.com/grafana/grafana/pull/13112), thx [@filewalkwithme](https://github.com/filewalkwithme)
+* **Login**: Show loading animation while waiting for authentication response on login [#12865](https://github.com/grafana/grafana/issues/12865)
* **UI**: Fix iOS home screen "app" icon and Windows 10 app experience [#12752](https://github.com/grafana/grafana/issues/12752), thx [@andig](https://github.com/andig)
-* **Datasource**: Fix UI issue with secret fields after updating datasource [#11270](https://github.com/grafana/grafana/issues/11270)
* **Plugins**: Convert URL-like text to links in plugins readme [#12843](https://github.com/grafana/grafana/pull/12843), thx [pgiraud](https://github.com/pgiraud)
-* **Docker**: Make it possible to set a specific plugin url [#12861](https://github.com/grafana/grafana/pull/12861), thx [ClementGautier](https://github.com/ClementGautier)
-* **Graphite**: Fix for quoting of int function parameters (when using variables) [#11927](https://github.com/grafana/grafana/pull/11927)
-* **InfluxDB**: Support timeFilter in query templating for InfluxDB [#12598](https://github.com/grafana/grafana/pull/12598), thx [kichristensen](https://github.com/kichristensen)
-* **Provisioning**: Should allow one default datasource per organisation [#12229](https://github.com/grafana/grafana/issues/12229)
-* **Heatmap**: Fix broken tooltip and crosshair on Firefox [#12486](https://github.com/grafana/grafana/issues/12486)
-* **Login**: Show loading animation while waiting for authentication response on login [#12865](https://github.com/grafana/grafana/issues/12865)
### Breaking changes
* Postgres datasource no longer automatically adds time column alias when using the $__timeGroup alias. However, there's code in place which should make this change backward compatible and shouldn't create any issues.
+* Kiosk mode now also hides submenu (variables)
+* ?inactive url parameter no longer supported, replaced with kiosk=tv url parameter
### New experimental features
-These are new features that's still being worked on and are in an experimental phase. We incourage users to try these out and provide any feedback in related issue.
+These are new features that's still being worked on and are in an experimental phase. We encourage users to try these out and provide any feedback in related issue.
* **Dashboard**: Auto fit dashboard panels to optimize space used for current TV / Monitor [#12768](https://github.com/grafana/grafana/issues/12768)
### Tech
* **Frontend**: Convert all Frontend Karma tests to Jest tests [#12224](https://github.com/grafana/grafana/issues/12224)
+* **Backend**: Upgrade to golang 1.11 [#13030](https://github.com/grafana/grafana/issues/13030)
+
+# 5.2.4 (2018-09-07)
+
+* **GrafanaCli**: Fixed issue with grafana-cli install plugin resulting in corrupt http response from source error. Fixes [#13079](https://github.com/grafana/grafana/issues/13079)
# 5.2.3 (2018-08-29)
@@ -277,7 +368,7 @@ See [security announcement](https://community.grafana.com/t/grafana-5-2-3-and-4-
* **Dashboard**: Sizing and positioning of settings menu icons [#11572](https://github.com/grafana/grafana/pull/11572)
* **Dashboard**: Add search filter/tabs to new panel control [#10427](https://github.com/grafana/grafana/issues/10427)
* **Folders**: User with org viewer role should not be able to save/move dashboards in/to general folder [#11553](https://github.com/grafana/grafana/issues/11553)
-* **Influxdb**: Dont assume the first column in table response is time. [#11476](https://github.com/grafana/grafana/issues/11476), thx [@hahnjo](https://github.com/hahnjo)
+* **Influxdb**: Don't assume the first column in table response is time. [#11476](https://github.com/grafana/grafana/issues/11476), thx [@hahnjo](https://github.com/hahnjo)
### Tech
* Backend code simplification [#11613](https://github.com/grafana/grafana/pull/11613), thx [@knweiss](https://github.com/knweiss)
@@ -464,7 +555,7 @@ See [security announcement](https://community.grafana.com/t/grafana-5-2-3-and-4-
# 4.6.2 (2017-11-16)
## Important
-* **Prometheus**: Fixes bug with new prometheus alerts in Grafana. Make sure to download this version if your using Prometheus for alerting. More details in the issue. [#9777](https://github.com/grafana/grafana/issues/9777)
+* **Prometheus**: Fixes bug with new prometheus alerts in Grafana. Make sure to download this version if you're using Prometheus for alerting. More details in the issue. [#9777](https://github.com/grafana/grafana/issues/9777)
## Fixes
* **Color picker**: Bug after using textbox input field to change/paste color string [#9769](https://github.com/grafana/grafana/issues/9769)
@@ -1423,7 +1514,7 @@ Grafana 2.x is fundamentally different from 1.x; it now ships with an integrated
**New features**
- [Issue #1623](https://github.com/grafana/grafana/issues/1623). Share Dashboard: Dashboard snapshot sharing (dash and data snapshot), save to local or save to public snapshot dashboard snapshots.raintank.io site
-- [Issue #1622](https://github.com/grafana/grafana/issues/1622). Share Panel: The share modal now has an embed option, gives you an iframe that you can use to embedd a single graph on another web site
+- [Issue #1622](https://github.com/grafana/grafana/issues/1622). Share Panel: The share modal now has an embed option, gives you an iframe that you can use to embed a single graph on another web site
- [Issue #718](https://github.com/grafana/grafana/issues/718). Dashboard: When saving a dashboard and another user has made changes in between the user is prompted with a warning if he really wants to overwrite the other's changes
- [Issue #1331](https://github.com/grafana/grafana/issues/1331). Graph & Singlestat: New axis/unit format selector and more units (kbytes, Joule, Watt, eV), and new design for graph axis & grid tab and single stat options tab views
- [Issue #1241](https://github.com/grafana/grafana/issues/1242). Timepicker: New option in timepicker (under dashboard settings), to change ``now`` to be for example ``now-1m``, useful when you want to ignore last minute because it contains incomplete data
diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md
new file mode 100644
index 00000000000..8b2ba090fe1
--- /dev/null
+++ b/CONTRIBUTING.md
@@ -0,0 +1,56 @@
+
+# Contributing
+
+Grafana uses GitHub to manage contributions.
+Contributions take the form of pull requests that will be reviewed by the core team.
+
+* If you are a new contributor see: [Steps to Contribute](#steps-to-contribute)
+
+* If you have a trivial fix or improvement, go ahead and create a pull request.
+
+* If you plan to do something more involved, discuss your idea on the respective [issue](https://github.com/grafana/grafana/issues) or create a [new issue](https://github.com/grafana/grafana/issues/new) if it does not exist. This will avoid unnecessary work and surely give you and us a good deal of inspiration.
+
+
+## Steps to Contribute
+
+Should you wish to work on a GitHub issue, check first if it is not already assigned to someone. If it is free, you claim it by commenting on the issue that you want to work on it. This is to prevent duplicated efforts from contributors on the same issue.
+
+Please check the [`beginner friendly`](https://github.com/grafana/grafana/issues?q=is%3Aopen+is%3Aissue+label%3A%22beginner+friendly%22) label to find issues that are good for getting started. If you have questions about one of the issues, with or without the tag, please comment on them and one of the core team or the original poster will clarify it.
+
+
+
+## Setup
+
+Follow the setup guide in README.md
+
+### Rebuild frontend assets on source change
+```
+yarn watch
+```
+
+### Rerun tests on source change
+```
+yarn jest
+```
+
+### Run tests for backend assets before commit
+```
+test -z "$(gofmt -s -l . | grep -v -E 'vendor/(github.com|golang.org|gopkg.in)' | tee /dev/stderr)"
+```
+
+### Run tests for frontend assets before commit
+```
+yarn test
+go test -v ./pkg/...
+```
+
+
+## Pull Request Checklist
+
+* Branch from the master branch and, if needed, rebase to the current master branch before submitting your pull request. If it doesn't merge cleanly with master you may be asked to rebase your changes.
+
+* Commits should be as small as possible, while ensuring that each commit is correct independently (i.e., each commit should compile and pass tests).
+
+* If your patch is not getting reviewed or you need a specific person to review it, you can @-reply a reviewer asking for a review in the pull request or a comment.
+
+* Add tests relevant to the fixed bug or new feature.
diff --git a/Dockerfile b/Dockerfile
index f7e45893c38..28dd71952af 100644
--- a/Dockerfile
+++ b/Dockerfile
@@ -1,5 +1,5 @@
# Golang build container
-FROM golang:1.10
+FROM golang:1.11
WORKDIR $GOPATH/src/github.com/grafana/grafana
diff --git a/Gopkg.lock b/Gopkg.lock
index 6f08e208ecd..4286add847d 100644
--- a/Gopkg.lock
+++ b/Gopkg.lock
@@ -19,6 +19,12 @@
packages = ["."]
revision = "7677a1d7c1137cd3dd5ba7a076d0c898a1ef4520"
+[[projects]]
+ branch = "master"
+ name = "github.com/VividCortex/mysqlerr"
+ packages = ["."]
+ revision = "6c6b55f8796f578c870b7e19bafb16103bc40095"
+
[[projects]]
name = "github.com/aws/aws-sdk-go"
packages = [
@@ -258,7 +264,7 @@
branch = "master"
name = "github.com/hashicorp/yamux"
packages = ["."]
- revision = "2658be15c5f05e76244154714161f17e3e77de2e"
+ revision = "7221087c3d281fda5f794e28c2ea4c6e4d5c4558"
[[projects]]
name = "github.com/inconshreveable/log15"
@@ -427,12 +433,6 @@
revision = "1744e2970ca51c86172c8190fadad617561ed6e7"
version = "v1.0.0"
-[[projects]]
- branch = "master"
- name = "github.com/shurcooL/sanitized_anchor_name"
- packages = ["."]
- revision = "86672fcb3f950f35f2e675df2240550f2a50762f"
-
[[projects]]
name = "github.com/smartystreets/assertions"
packages = [
@@ -507,6 +507,8 @@
branch = "master"
name = "golang.org/x/crypto"
packages = [
+ "ed25519",
+ "ed25519/internal/edwards25519",
"md4",
"pbkdf2"
]
@@ -670,6 +672,16 @@
revision = "e6179049628164864e6e84e973cfb56335748dea"
version = "v2.3.2"
+[[projects]]
+ name = "gopkg.in/square/go-jose.v2"
+ packages = [
+ ".",
+ "cipher",
+ "json"
+ ]
+ revision = "ef984e69dd356202fd4e4910d4d9c24468bdf0b8"
+ version = "v2.1.9"
+
[[projects]]
name = "gopkg.in/yaml.v2"
packages = ["."]
@@ -679,6 +691,6 @@
[solve-meta]
analyzer-name = "dep"
analyzer-version = 1
- inputs-digest = "cb8e7fd81f23ec987fc4d5dd9d31ae0f1164bc2f30cbea2fe86e0d97dd945beb"
+ inputs-digest = "6f7f271afd27f78b7d8ebe27436fee72c9925fb82a978bdc57fde44e01f3ca51"
solver-name = "gps-cdcl"
solver-version = 1
diff --git a/Gopkg.toml b/Gopkg.toml
index 6c91ec37221..e3cbdeabb5d 100644
--- a/Gopkg.toml
+++ b/Gopkg.toml
@@ -203,3 +203,11 @@ ignored = [
[[constraint]]
name = "github.com/denisenkom/go-mssqldb"
revision = "270bc3860bb94dd3a3ffd047377d746c5e276726"
+
+[[constraint]]
+ name = "github.com/VividCortex/mysqlerr"
+ branch = "master"
+
+[[constraint]]
+ name = "gopkg.in/square/go-jose.v2"
+ version = "2.1.9"
diff --git a/Gruntfile.js b/Gruntfile.js
index 8a71fb44148..2d5990b5f58 100644
--- a/Gruntfile.js
+++ b/Gruntfile.js
@@ -25,7 +25,6 @@ module.exports = function (grunt) {
}
}
- config.coverage = grunt.option('coverage');
config.phjs = grunt.option('phjsToRelease');
config.pkg.version = grunt.option('pkgVer') || config.pkg.version;
diff --git a/Makefile b/Makefile
index c6915409ed7..c9e51d897f3 100644
--- a/Makefile
+++ b/Makefile
@@ -43,6 +43,3 @@ test: test-go test-js
run:
./bin/grafana-server
-
-protoc:
- protoc -I pkg/tsdb/models pkg/tsdb/models/*.proto --go_out=plugins=grpc:pkg/tsdb/models/.
diff --git a/PLUGIN_DEV.md b/PLUGIN_DEV.md
index 4e2e080ebe6..168b21dbd88 100644
--- a/PLUGIN_DEV.md
+++ b/PLUGIN_DEV.md
@@ -6,8 +6,8 @@ upgrading Grafana please check here before creating an issue.
## Links
-- [Datasource plugin written in typescript](https://github.com/grafana/typescript-template-datasource)
-- [Simple json dataource plugin](https://github.com/grafana/simple-json-datasource)
+- [Datasource plugin written in TypeScript](https://github.com/grafana/typescript-template-datasource)
+- [Simple JSON datasource plugin](https://github.com/grafana/simple-json-datasource)
- [Plugin development guide](http://docs.grafana.org/plugins/developing/development/)
- [Webpack Grafana plugin template project](https://github.com/CorpGlory/grafana-plugin-template-webpack)
diff --git a/README.md b/README.md
index 74fb10c8066..5882ea8a6a3 100644
--- a/README.md
+++ b/README.md
@@ -24,7 +24,7 @@ the latest master builds [here](https://grafana.com/grafana/download)
### Dependencies
-- Go 1.10
+- Go (Latest Stable)
- NodeJS LTS
### Building the backend
@@ -69,15 +69,27 @@ bra run
Open grafana in your browser (default: `http://localhost:3000`) and login with admin user (default: `user/pass = admin/admin`).
-### Building a docker image (on linux/amd64)
+### Building a Docker image
-This builds a docker image from your local sources:
+There are two different ways to build a Grafana docker image. If you're machine is setup for Grafana development and you run linux/amd64 you can build just the image. Otherwise, there is the option to build Grafana completely within Docker.
+
+Run the image you have built using: `docker run --rm -p 3000:3000 grafana/grafana:dev`
+
+#### Building on linux/amd64 (fast)
1. Build the frontend `go run build.go build-frontend`
2. Build the docker image `make build-docker-dev`
The resulting image will be tagged as `grafana/grafana:dev`
+#### Building anywhere (slower)
+
+Choose this option to build on platforms other than linux/amd64 and/or not have to setup the Grafana development environment.
+
+1. `make build-docker-full` or `docker build -t grafana/grafana:dev .`
+
+The resulting image will be tagged as `grafana/grafana:dev`
+
### Dev config
Create a custom.ini in the conf directory to override default configuration options.
@@ -113,18 +125,6 @@ GRAFANA_TEST_DB=mysql go test ./pkg/...
GRAFANA_TEST_DB=postgres go test ./pkg/...
```
-## Building custom docker image
-
-You can build a custom image using Docker, which doesn't require installing any dependencies besides docker itself.
-```bash
-git clone https://github.com/grafana/grafana
-cd grafana
-docker build -t grafana:dev .
-docker run -d --name=grafana -p 3000:3000 grafana:dev
-```
-
-Open grafana in your browser (default: `http://localhost:3000`) and login with admin user (default: `user/pass = admin/admin`).
-
## Contribute
If you have any idea for an improvement or found a bug, do not hesitate to open an issue.
@@ -138,5 +138,5 @@ plugin development.
## License
-Grafana is distributed under Apache 2.0 License.
+Grafana is distributed under [Apache 2.0 License](https://github.com/grafana/grafana/blob/master/LICENSE.md).
diff --git a/UPGRADING_DEPENDENCIES.md b/UPGRADING_DEPENDENCIES.md
new file mode 100644
index 00000000000..f3d2adbd71a
--- /dev/null
+++ b/UPGRADING_DEPENDENCIES.md
@@ -0,0 +1,89 @@
+# Guide to Upgrading Dependencies
+
+Upgrading Go or Node.js requires making changes in many different files. See below for a list and explanation for each.
+
+## Go
+
+- CircleCi
+- `grafana/build-container`
+- Appveyor
+- Dockerfile
+
+## Node.js
+
+- CircleCI
+- `grafana/build-container`
+- Appveyor
+- Dockerfile
+
+## Go Dependencies
+
+Updated using `dep`.
+
+- `Gopkg.toml`
+- `Gopkg.lock`
+
+## Node.js Dependencies
+
+Updated using `yarn`.
+
+- `package.json`
+
+## Where to make changes
+
+### CircleCI
+
+Our builds run on CircleCI through our build script.
+
+#### Files
+
+- `.circleci/config.yml`.
+
+#### Dependencies
+
+- nodejs
+- golang
+- grafana/build-container (our custom docker build container)
+
+### grafana/build-container
+
+The main build step (in CircleCI) is built using a custom build container that comes pre-baked with some of the neccesary dependencies.
+
+Link: [grafana-build-container](https://github.com/grafana/grafana-build-container)
+
+#### Dependencies
+
+- fpm
+- nodejs
+- golang
+- crosscompiling (several compilers)
+
+### Appveyor
+
+Master and release builds trigger test runs on Appveyors build environment so that tests will run on Windows.
+
+#### Files:
+
+- `appveyor.yml`
+
+#### Dependencies
+
+- nodejs
+- golang
+
+### Dockerfile
+
+There is a Docker build for Grafana in the root of the project that allows anyone to build Grafana just using Docker.
+
+#### Files
+
+- `Dockerfile`
+
+#### Dependencies
+
+- nodejs
+- golang
+
+### Local developer environments
+
+Please send out a notice in the grafana-dev slack channel when updating Go or Node.js to make it easier for everyone to update their local developer environments.
\ No newline at end of file
diff --git a/appveyor.yml b/appveyor.yml
index 5cdec1b8bf5..4bbd3668e19 100644
--- a/appveyor.yml
+++ b/appveyor.yml
@@ -5,9 +5,9 @@ os: Windows Server 2012 R2
clone_folder: c:\gopath\src\github.com\grafana\grafana
environment:
- nodejs_version: "6"
+ nodejs_version: "8"
GOPATH: C:\gopath
- GOVERSION: 1.10
+ GOVERSION: 1.11
install:
- rmdir c:\go /s /q
diff --git a/build.go b/build.go
index 561dd70df0e..69fbf3bada8 100644
--- a/build.go
+++ b/build.go
@@ -22,6 +22,11 @@ import (
"time"
)
+const (
+ windows = "windows"
+ linux = "linux"
+)
+
var (
//versionRe = regexp.MustCompile(`-[0-9]{1,3}-g[0-9a-f]{5,10}`)
goarch string
@@ -110,17 +115,16 @@ func main() {
case "package":
grunt(gruntBuildArg("build")...)
grunt(gruntBuildArg("package")...)
- if goos == "linux" {
+ if goos == linux {
createLinuxPackages()
}
case "package-only":
grunt(gruntBuildArg("package")...)
- if goos == "linux" {
+ if goos == linux {
createLinuxPackages()
}
-
case "pkg-rpm":
grunt(gruntBuildArg("release")...)
createRpmPackages()
@@ -379,7 +383,7 @@ func ensureGoPath() {
}
func grunt(params ...string) {
- if runtime.GOOS == "windows" {
+ if runtime.GOOS == windows {
runPrint(`.\node_modules\.bin\grunt`, params...)
} else {
runPrint("./node_modules/.bin/grunt", params...)
@@ -417,11 +421,11 @@ func test(pkg string) {
func build(binaryName, pkg string, tags []string) {
binary := fmt.Sprintf("./bin/%s-%s/%s", goos, goarch, binaryName)
if isDev {
- //dont include os and arch in output path in dev environment
+ //don't include os and arch in output path in dev environment
binary = fmt.Sprintf("./bin/%s", binaryName)
}
- if goos == "windows" {
+ if goos == windows {
binary += ".exe"
}
@@ -485,11 +489,11 @@ func clean() {
func setBuildEnv() {
os.Setenv("GOOS", goos)
- if goos == "windows" {
+ if goos == windows {
// require windows >=7
os.Setenv("CGO_CFLAGS", "-D_WIN32_WINNT=0x0601")
}
- if goarch != "amd64" || goos != "linux" {
+ if goarch != "amd64" || goos != linux {
// needed for all other archs
cgo = true
}
diff --git a/codecov.yml b/codecov.yml
deleted file mode 100644
index b2a839365ac..00000000000
--- a/codecov.yml
+++ /dev/null
@@ -1,11 +0,0 @@
-coverage:
- precision: 2
- round: down
- range: "50...100"
-
- status:
- project: yes
- patch: yes
- changes: no
-
-comment: off
diff --git a/conf/defaults.ini b/conf/defaults.ini
index 90fc144c6e0..eb8debc0094 100644
--- a/conf/defaults.ini
+++ b/conf/defaults.ini
@@ -321,6 +321,7 @@ allow_sign_up = true
client_id = some_id
client_secret = some_secret
scopes = user:email
+email_attribute_name = email:primary
auth_url =
token_url =
api_url =
@@ -467,6 +468,16 @@ enabled = true
# Makes it possible to turn off alert rule execution but alerting UI is visible
execute_alerts = true
+# Default setting for new alert rules. Defaults to categorize error and timeouts as alerting. (alerting, keep_state)
+error_or_timeout = alerting
+
+# Default setting for how Grafana handles nodata or null values in alerting. (alerting, no_data, keep_state, ok)
+nodata_or_nullvalues = no_data
+
+# Alert notifications can include images, but rendering many images at the same time can overload the server
+# This limit will protect the server from render overloading and make sure notifications are sent out quickly
+concurrent_render_limit = 5
+
#################################### Explore #############################
[explore]
# Enable the Explore section
@@ -538,3 +549,8 @@ container_name =
[external_image_storage.local]
# does not require any configuration
+
+[rendering]
+# Options to configure external image rendering server like https://github.com/grafana/grafana-image-renderer
+server_url =
+callback_url =
diff --git a/conf/ldap.toml b/conf/ldap.toml
index 9a7088ed823..b684f2556d5 100644
--- a/conf/ldap.toml
+++ b/conf/ldap.toml
@@ -31,37 +31,11 @@ search_filter = "(cn=%s)"
# An array of base dns to search through
search_base_dns = ["dc=grafana,dc=org"]
-# In POSIX LDAP schemas, without memberOf attribute a secondary query must be made for groups.
-# This is done by enabling group_search_filter below. You must also set member_of= "cn"
-# in [servers.attributes] below.
-
-# Users with nested/recursive group membership and an LDAP server that supports LDAP_MATCHING_RULE_IN_CHAIN
-# can set group_search_filter, group_search_filter_user_attribute, group_search_base_dns and member_of
-# below in such a way that the user's recursive group membership is considered.
-#
-# Nested Groups + Active Directory (AD) Example:
-#
-# AD groups store the Distinguished Names (DNs) of members, so your filter must
-# recursively search your groups for the authenticating user's DN. For example:
-#
-# group_search_filter = "(member:1.2.840.113556.1.4.1941:=%s)"
-# group_search_filter_user_attribute = "distinguishedName"
-# group_search_base_dns = ["ou=groups,dc=grafana,dc=org"]
-#
-# [servers.attributes]
-# ...
-# member_of = "distinguishedName"
-
-## Group search filter, to retrieve the groups of which the user is a member (only set if memberOf attribute is not available)
+## For Posix or LDAP setups that does not support member_of attribute you can define the below settings
+## Please check grafana LDAP docs for examples
# group_search_filter = "(&(objectClass=posixGroup)(memberUid=%s))"
-## Group search filter user attribute defines what user attribute gets substituted for %s in group_search_filter.
-## Defaults to the value of username in [server.attributes]
-## Valid options are any of your values in [servers.attributes]
-## If you are using nested groups you probably want to set this and member_of in
-## [servers.attributes] to "distinguishedName"
-# group_search_filter_user_attribute = "distinguishedName"
-## An array of the base DNs to search through for groups. Typically uses ou=groups
# group_search_base_dns = ["ou=groups,dc=grafana,dc=org"]
+# group_search_filter_user_attribute = "uid"
# Specify names of the ldap attributes your ldap uses
[servers.attributes]
diff --git a/conf/sample.ini b/conf/sample.ini
index 4291071e026..e6a03718d19 100644
--- a/conf/sample.ini
+++ b/conf/sample.ini
@@ -387,6 +387,16 @@ log_queries =
# Makes it possible to turn off alert rule execution but alerting UI is visible
;execute_alerts = true
+# Default setting for new alert rules. Defaults to categorize error and timeouts as alerting. (alerting, keep_state)
+;error_or_timeout = alerting
+
+# Default setting for how Grafana handles nodata or null values in alerting. (alerting, no_data, keep_state, ok)
+;nodata_or_nullvalues = no_data
+
+# Alert notifications can include images, but rendering many images at the same time can overload the server
+# This limit will protect the server from render overloading and make sure notifications are sent out quickly
+;concurrent_render_limit = 5
+
#################################### Explore #############################
[explore]
# Enable the Explore section
@@ -425,7 +435,7 @@ log_queries =
;sampler_param = 1
#################################### Grafana.com integration ##########################
-# Url used to to import dashboards directly from Grafana.com
+# Url used to import dashboards directly from Grafana.com
[grafana_com]
;url = https://grafana.com
@@ -460,3 +470,8 @@ log_queries =
[external_image_storage.local]
# does not require any configuration
+
+[rendering]
+# Options to configure external image rendering server like https://github.com/grafana/grafana-image-renderer
+;server_url =
+;callback_url =
diff --git a/scripts/benchmarks/ab/ab_test.sh b/devenv/benchmarks/ab/ab_test.sh
similarity index 100%
rename from scripts/benchmarks/ab/ab_test.sh
rename to devenv/benchmarks/ab/ab_test.sh
diff --git a/devenv/bulk_alerting_dashboards/bulk_alerting_dashboards.yaml b/devenv/bulk_alerting_dashboards/bulk_alerting_dashboards.yaml
new file mode 100644
index 00000000000..1ede5dcd30a
--- /dev/null
+++ b/devenv/bulk_alerting_dashboards/bulk_alerting_dashboards.yaml
@@ -0,0 +1,9 @@
+apiVersion: 1
+
+providers:
+ - name: 'Bulk alerting dashboards'
+ folder: 'Bulk alerting dashboards'
+ type: file
+ options:
+ path: devenv/bulk_alerting_dashboards
+
diff --git a/devenv/bulk_alerting_dashboards/bulkdash_alerting.jsonnet b/devenv/bulk_alerting_dashboards/bulkdash_alerting.jsonnet
new file mode 100644
index 00000000000..a7acd57745d
--- /dev/null
+++ b/devenv/bulk_alerting_dashboards/bulkdash_alerting.jsonnet
@@ -0,0 +1,168 @@
+{
+ "editable": true,
+ "gnetId": null,
+ "graphTooltip": 0,
+ "id": null,
+ "links": [],
+ "panels": [
+ {
+ "alert": {
+ "conditions": [
+ {
+ "evaluator": {
+ "params": [
+ 65
+ ],
+ "type": "gt"
+ },
+ "operator": {
+ "type": "and"
+ },
+ "query": {
+ "params": [
+ "A",
+ "5m",
+ "now"
+ ]
+ },
+ "reducer": {
+ "params": [],
+ "type": "avg"
+ },
+ "type": "query"
+ }
+ ],
+ "executionErrorState": "alerting",
+ "frequency": "10s",
+ "handler": 1,
+ "name": "bulk alerting",
+ "noDataState": "no_data",
+ "notifications": []
+ },
+ "aliasColors": {},
+ "bars": false,
+ "dashLength": 10,
+ "dashes": false,
+ "datasource": "gdev-prometheus",
+ "fill": 1,
+ "gridPos": {
+ "h": 9,
+ "w": 12,
+ "x": 0,
+ "y": 0
+ },
+ "id": 2,
+ "legend": {
+ "avg": false,
+ "current": false,
+ "max": false,
+ "min": false,
+ "show": true,
+ "total": false,
+ "values": false
+ },
+ "lines": true,
+ "linewidth": 1,
+ "nullPointMode": "null",
+ "percentage": false,
+ "pointradius": 5,
+ "points": false,
+ "renderer": "flot",
+ "seriesOverrides": [],
+ "spaceLength": 10,
+ "stack": false,
+ "steppedLine": false,
+ "targets": [
+ {
+ "$$hashKey": "object:117",
+ "expr": "go_goroutines",
+ "format": "time_series",
+ "intervalFactor": 1,
+ "refId": "A"
+ }
+ ],
+ "thresholds": [
+ {
+ "colorMode": "critical",
+ "fill": true,
+ "line": true,
+ "op": "gt",
+ "value": 50
+ }
+ ],
+ "timeFrom": null,
+ "timeShift": null,
+ "title": "Panel Title",
+ "tooltip": {
+ "shared": true,
+ "sort": 0,
+ "value_type": "individual"
+ },
+ "type": "graph",
+ "xaxis": {
+ "buckets": null,
+ "mode": "time",
+ "name": null,
+ "show": true,
+ "values": []
+ },
+ "yaxes": [
+ {
+ "format": "short",
+ "label": null,
+ "logBase": 1,
+ "max": null,
+ "min": null,
+ "show": true
+ },
+ {
+ "format": "short",
+ "label": null,
+ "logBase": 1,
+ "max": null,
+ "min": null,
+ "show": true
+ }
+ ]
+ }
+ ],
+ "schemaVersion": 16,
+ "style": "dark",
+ "tags": [],
+ "templating": {
+ "list": []
+ },
+ "time": {
+ "from": "now-6h",
+ "to": "now"
+ },
+ "timepicker": {
+ "refresh_intervals": [
+ "5s",
+ "10s",
+ "30s",
+ "1m",
+ "5m",
+ "15m",
+ "30m",
+ "1h",
+ "2h",
+ "1d"
+ ],
+ "time_options": [
+ "5m",
+ "15m",
+ "1h",
+ "6h",
+ "12h",
+ "24h",
+ "2d",
+ "7d",
+ "30d"
+ ]
+ },
+ "timezone": "",
+ "title": "New dashboard",
+ "uid": null,
+ "version": 0
+}
\ No newline at end of file
diff --git a/docker/create_docker_compose.sh b/devenv/create_docker_compose.sh
similarity index 94%
rename from docker/create_docker_compose.sh
rename to devenv/create_docker_compose.sh
index 9d28ede8e7e..5da9e8f5c8f 100755
--- a/docker/create_docker_compose.sh
+++ b/devenv/create_docker_compose.sh
@@ -1,13 +1,13 @@
#!/bin/bash
-blocks_dir=blocks
+blocks_dir=docker/blocks
docker_dir=docker
template_dir=templates
grafana_config_file=conf.tmp
grafana_config=config
-compose_header_file=compose_header.yml
+compose_header_file=docker/compose_header.yml
fig_file=docker-compose.yaml
fig_config=docker-compose.yaml
diff --git a/devenv/dev-dashboards/panel_tests_polystat.json b/devenv/dev-dashboards/panel_tests_polystat.json
new file mode 100644
index 00000000000..51d3085c438
--- /dev/null
+++ b/devenv/dev-dashboards/panel_tests_polystat.json
@@ -0,0 +1,3343 @@
+{
+ "annotations": {
+ "list": [
+ {
+ "builtIn": 1,
+ "datasource": "-- Grafana --",
+ "enable": true,
+ "hide": true,
+ "iconColor": "rgba(0, 211, 255, 1)",
+ "name": "Annotations & Alerts",
+ "type": "dashboard"
+ }
+ ]
+ },
+ "editable": true,
+ "gnetId": null,
+ "graphTooltip": 0,
+ "links": [],
+ "panels": [
+ {
+ "animationModes": [
+ {
+ "text": "Show All",
+ "value": "all"
+ },
+ {
+ "text": "Show Triggered",
+ "value": "triggered"
+ }
+ ],
+ "colors": [
+ "#299c46",
+ "rgba(237, 129, 40, 0.89)",
+ "#d44a3a"
+ ],
+ "d3DivId": "d3_svg_4",
+ "datasource": "gdev-testdata",
+ "decimals": 2,
+ "displayModes": [
+ {
+ "text": "Show All",
+ "value": "all"
+ },
+ {
+ "text": "Show Triggered",
+ "value": "triggered"
+ }
+ ],
+ "fontSizes": [
+ 4,
+ 5,
+ 6,
+ 7,
+ 8,
+ 9,
+ 10,
+ 11,
+ 12,
+ 13,
+ 14,
+ 15,
+ 16,
+ 17,
+ 18,
+ 19,
+ 20,
+ 22,
+ 24,
+ 26,
+ 28,
+ 30,
+ 32,
+ 34,
+ 36,
+ 38,
+ 40,
+ 42,
+ 44,
+ 46,
+ 48,
+ 50,
+ 52,
+ 54,
+ 56,
+ 58,
+ 60,
+ 62,
+ 64,
+ 66,
+ 68,
+ 70
+ ],
+ "fontTypes": [
+ "Open Sans",
+ "Arial",
+ "Avant Garde",
+ "Bookman",
+ "Consolas",
+ "Courier",
+ "Courier New",
+ "Futura",
+ "Garamond",
+ "Helvetica",
+ "Palatino",
+ "Times",
+ "Times New Roman",
+ "Verdana"
+ ],
+ "format": "none",
+ "gridPos": {
+ "h": 9,
+ "w": 12,
+ "x": 0,
+ "y": 0
+ },
+ "id": 4,
+ "links": [],
+ "notcolors": [
+ "rgba(245, 54, 54, 0.9)",
+ "rgba(237, 129, 40, 0.89)",
+ "rgba(50, 172, 45, 0.97)"
+ ],
+ "operatorName": "avg",
+ "operatorOptions": [
+ {
+ "text": "Average",
+ "value": "avg"
+ },
+ {
+ "text": "Count",
+ "value": "count"
+ },
+ {
+ "text": "Current",
+ "value": "current"
+ },
+ {
+ "text": "Delta",
+ "value": "delta"
+ },
+ {
+ "text": "Difference",
+ "value": "diff"
+ },
+ {
+ "text": "First",
+ "value": "first"
+ },
+ {
+ "text": "Log Min",
+ "value": "logmin"
+ },
+ {
+ "text": "Max",
+ "value": "max"
+ },
+ {
+ "text": "Min",
+ "value": "min"
+ },
+ {
+ "text": "Name",
+ "value": "name"
+ },
+ {
+ "text": "Time of Last Point",
+ "value": "last_time"
+ },
+ {
+ "text": "Time Step",
+ "value": "time_step"
+ },
+ {
+ "text": "Total",
+ "value": "total"
+ }
+ ],
+ "polystat": {
+ "animationSpeed": 2500,
+ "columnAutoSize": true,
+ "columns": "",
+ "defaultClickThrough": "",
+ "defaultClickThroughSanitize": true,
+ "displayLimit": 100,
+ "fontAutoScale": true,
+ "fontSize": 12,
+ "globalDisplayMode": "all",
+ "globalOperatorName": "avg",
+ "gradientEnabled": true,
+ "hexagonSortByDirection": "asc",
+ "hexagonSortByField": "name",
+ "maxMetrics": 0,
+ "polygonBorderColor": "black",
+ "polygonBorderSize": 2,
+ "radius": "",
+ "radiusAutoSize": true,
+ "rowAutoSize": true,
+ "rows": "",
+ "shape": "hexagon_pointed_top",
+ "tooltipDisplayMode": "all",
+ "tooltipDisplayTextTriggeredEmpty": "OK",
+ "tooltipFontSize": 12,
+ "tooltipFontType": "Open Sans",
+ "tooltipPrimarySortDirection": "desc",
+ "tooltipPrimarySortField": "thresholdLevel",
+ "tooltipSecondarySortDirection": "desc",
+ "tooltipSecondarySortField": "value",
+ "tooltipTimestampEnabled": true
+ },
+ "savedComposites": [],
+ "savedOverrides": [],
+ "shapes": [
+ {
+ "text": "Hexagon Pointed Top",
+ "value": "hexagon_pointed_top"
+ },
+ {
+ "text": "Hexagon Flat Top",
+ "value": "hexagon_flat_top"
+ },
+ {
+ "text": "Circle",
+ "value": "circle"
+ },
+ {
+ "text": "Cross",
+ "value": "cross"
+ },
+ {
+ "text": "Diamond",
+ "value": "diamond"
+ },
+ {
+ "text": "Square",
+ "value": "square"
+ },
+ {
+ "text": "Star",
+ "value": "star"
+ },
+ {
+ "text": "Triangle",
+ "value": "triangle"
+ },
+ {
+ "text": "Wye",
+ "value": "wye"
+ }
+ ],
+ "sortDirections": [
+ {
+ "text": "Ascending",
+ "value": "asc"
+ },
+ {
+ "text": "Descending",
+ "value": "desc"
+ }
+ ],
+ "sortFields": [
+ {
+ "text": "Name",
+ "value": "name"
+ },
+ {
+ "text": "Threshold Level",
+ "value": "thresholdLevel"
+ },
+ {
+ "text": "Value",
+ "value": "value"
+ }
+ ],
+ "svgContainer": {},
+ "targets": [
+ {
+ "expr": "",
+ "format": "time_series",
+ "intervalFactor": 1,
+ "refId": "A",
+ "scenarioId": "random_walk"
+ },
+ {
+ "expr": "",
+ "format": "time_series",
+ "intervalFactor": 1,
+ "refId": "B",
+ "scenarioId": "random_walk"
+ },
+ {
+ "expr": "",
+ "format": "time_series",
+ "intervalFactor": 1,
+ "refId": "C",
+ "scenarioId": "random_walk"
+ },
+ {
+ "expr": "",
+ "format": "time_series",
+ "intervalFactor": 1,
+ "refId": "D",
+ "scenarioId": "random_walk"
+ },
+ {
+ "expr": "",
+ "format": "time_series",
+ "intervalFactor": 1,
+ "refId": "E",
+ "scenarioId": "random_walk"
+ }
+ ],
+ "thresholdStates": [
+ {
+ "text": "ok",
+ "value": 0
+ },
+ {
+ "text": "warning",
+ "value": 1
+ },
+ {
+ "text": "critical",
+ "value": 2
+ },
+ {
+ "text": "custom",
+ "value": 3
+ }
+ ],
+ "title": "Poor use of space",
+ "type": "grafana-polystat-panel",
+ "unitFormats": [
+ {
+ "submenu": [
+ {
+ "text": "none",
+ "value": "none"
+ },
+ {
+ "text": "short",
+ "value": "short"
+ },
+ {
+ "text": "percent (0-100)",
+ "value": "percent"
+ },
+ {
+ "text": "percent (0.0-1.0)",
+ "value": "percentunit"
+ },
+ {
+ "text": "Humidity (%H)",
+ "value": "humidity"
+ },
+ {
+ "text": "decibel",
+ "value": "dB"
+ },
+ {
+ "text": "hexadecimal (0x)",
+ "value": "hex0x"
+ },
+ {
+ "text": "hexadecimal",
+ "value": "hex"
+ },
+ {
+ "text": "scientific notation",
+ "value": "sci"
+ },
+ {
+ "text": "locale format",
+ "value": "locale"
+ }
+ ],
+ "text": "none"
+ },
+ {
+ "submenu": [
+ {
+ "text": "Dollars ($)",
+ "value": "currencyUSD"
+ },
+ {
+ "text": "Pounds (£)",
+ "value": "currencyGBP"
+ },
+ {
+ "text": "Euro (€)",
+ "value": "currencyEUR"
+ },
+ {
+ "text": "Yen (¥)",
+ "value": "currencyJPY"
+ },
+ {
+ "text": "Rubles (₽)",
+ "value": "currencyRUB"
+ },
+ {
+ "text": "Hryvnias (₴)",
+ "value": "currencyUAH"
+ },
+ {
+ "text": "Real (R$)",
+ "value": "currencyBRL"
+ },
+ {
+ "text": "Danish Krone (kr)",
+ "value": "currencyDKK"
+ },
+ {
+ "text": "Icelandic Króna (kr)",
+ "value": "currencyISK"
+ },
+ {
+ "text": "Norwegian Krone (kr)",
+ "value": "currencyNOK"
+ },
+ {
+ "text": "Swedish Krona (kr)",
+ "value": "currencySEK"
+ },
+ {
+ "text": "Czech koruna (czk)",
+ "value": "currencyCZK"
+ },
+ {
+ "text": "Swiss franc (CHF)",
+ "value": "currencyCHF"
+ },
+ {
+ "text": "Polish Złoty (PLN)",
+ "value": "currencyPLN"
+ },
+ {
+ "text": "Bitcoin (฿)",
+ "value": "currencyBTC"
+ }
+ ],
+ "text": "currency"
+ },
+ {
+ "submenu": [
+ {
+ "text": "Hertz (1/s)",
+ "value": "hertz"
+ },
+ {
+ "text": "nanoseconds (ns)",
+ "value": "ns"
+ },
+ {
+ "text": "microseconds (µs)",
+ "value": "µs"
+ },
+ {
+ "text": "milliseconds (ms)",
+ "value": "ms"
+ },
+ {
+ "text": "seconds (s)",
+ "value": "s"
+ },
+ {
+ "text": "minutes (m)",
+ "value": "m"
+ },
+ {
+ "text": "hours (h)",
+ "value": "h"
+ },
+ {
+ "text": "days (d)",
+ "value": "d"
+ },
+ {
+ "text": "duration (ms)",
+ "value": "dtdurationms"
+ },
+ {
+ "text": "duration (s)",
+ "value": "dtdurations"
+ },
+ {
+ "text": "duration (hh:mm:ss)",
+ "value": "dthms"
+ },
+ {
+ "text": "Timeticks (s/100)",
+ "value": "timeticks"
+ }
+ ],
+ "text": "time"
+ },
+ {
+ "submenu": [
+ {
+ "text": "YYYY-MM-DD HH:mm:ss",
+ "value": "dateTimeAsIso"
+ },
+ {
+ "text": "DD/MM/YYYY h:mm:ss a",
+ "value": "dateTimeAsUS"
+ },
+ {
+ "text": "From Now",
+ "value": "dateTimeFromNow"
+ }
+ ],
+ "text": "date & time"
+ },
+ {
+ "submenu": [
+ {
+ "text": "bits",
+ "value": "bits"
+ },
+ {
+ "text": "bytes",
+ "value": "bytes"
+ },
+ {
+ "text": "kibibytes",
+ "value": "kbytes"
+ },
+ {
+ "text": "mebibytes",
+ "value": "mbytes"
+ },
+ {
+ "text": "gibibytes",
+ "value": "gbytes"
+ }
+ ],
+ "text": "data (IEC)"
+ },
+ {
+ "submenu": [
+ {
+ "text": "bits",
+ "value": "decbits"
+ },
+ {
+ "text": "bytes",
+ "value": "decbytes"
+ },
+ {
+ "text": "kilobytes",
+ "value": "deckbytes"
+ },
+ {
+ "text": "megabytes",
+ "value": "decmbytes"
+ },
+ {
+ "text": "gigabytes",
+ "value": "decgbytes"
+ }
+ ],
+ "text": "data (Metric)"
+ },
+ {
+ "submenu": [
+ {
+ "text": "packets/sec",
+ "value": "pps"
+ },
+ {
+ "text": "bits/sec",
+ "value": "bps"
+ },
+ {
+ "text": "bytes/sec",
+ "value": "Bps"
+ },
+ {
+ "text": "kilobits/sec",
+ "value": "Kbits"
+ },
+ {
+ "text": "kilobytes/sec",
+ "value": "KBs"
+ },
+ {
+ "text": "megabits/sec",
+ "value": "Mbits"
+ },
+ {
+ "text": "megabytes/sec",
+ "value": "MBs"
+ },
+ {
+ "text": "gigabytes/sec",
+ "value": "GBs"
+ },
+ {
+ "text": "gigabits/sec",
+ "value": "Gbits"
+ }
+ ],
+ "text": "data rate"
+ },
+ {
+ "submenu": [
+ {
+ "text": "hashes/sec",
+ "value": "Hs"
+ },
+ {
+ "text": "kilohashes/sec",
+ "value": "KHs"
+ },
+ {
+ "text": "megahashes/sec",
+ "value": "MHs"
+ },
+ {
+ "text": "gigahashes/sec",
+ "value": "GHs"
+ },
+ {
+ "text": "terahashes/sec",
+ "value": "THs"
+ },
+ {
+ "text": "petahashes/sec",
+ "value": "PHs"
+ },
+ {
+ "text": "exahashes/sec",
+ "value": "EHs"
+ }
+ ],
+ "text": "hash rate"
+ },
+ {
+ "submenu": [
+ {
+ "text": "ops/sec (ops)",
+ "value": "ops"
+ },
+ {
+ "text": "requests/sec (rps)",
+ "value": "reqps"
+ },
+ {
+ "text": "reads/sec (rps)",
+ "value": "rps"
+ },
+ {
+ "text": "writes/sec (wps)",
+ "value": "wps"
+ },
+ {
+ "text": "I/O ops/sec (iops)",
+ "value": "iops"
+ },
+ {
+ "text": "ops/min (opm)",
+ "value": "opm"
+ },
+ {
+ "text": "reads/min (rpm)",
+ "value": "rpm"
+ },
+ {
+ "text": "writes/min (wpm)",
+ "value": "wpm"
+ }
+ ],
+ "text": "throughput"
+ },
+ {
+ "submenu": [
+ {
+ "text": "millimetre (mm)",
+ "value": "lengthmm"
+ },
+ {
+ "text": "meter (m)",
+ "value": "lengthm"
+ },
+ {
+ "text": "feet (ft)",
+ "value": "lengthft"
+ },
+ {
+ "text": "kilometer (km)",
+ "value": "lengthkm"
+ },
+ {
+ "text": "mile (mi)",
+ "value": "lengthmi"
+ }
+ ],
+ "text": "length"
+ },
+ {
+ "submenu": [
+ {
+ "text": "Square Meters (m²)",
+ "value": "areaM2"
+ },
+ {
+ "text": "Square Feet (ft²)",
+ "value": "areaF2"
+ },
+ {
+ "text": "Square Miles (mi²)",
+ "value": "areaMI2"
+ }
+ ],
+ "text": "area"
+ },
+ {
+ "submenu": [
+ {
+ "text": "milligram (mg)",
+ "value": "massmg"
+ },
+ {
+ "text": "gram (g)",
+ "value": "massg"
+ },
+ {
+ "text": "kilogram (kg)",
+ "value": "masskg"
+ },
+ {
+ "text": "metric ton (t)",
+ "value": "masst"
+ }
+ ],
+ "text": "mass"
+ },
+ {
+ "submenu": [
+ {
+ "text": "metres/second (m/s)",
+ "value": "velocityms"
+ },
+ {
+ "text": "kilometers/hour (km/h)",
+ "value": "velocitykmh"
+ },
+ {
+ "text": "miles/hour (mph)",
+ "value": "velocitymph"
+ },
+ {
+ "text": "knot (kn)",
+ "value": "velocityknot"
+ }
+ ],
+ "text": "velocity"
+ },
+ {
+ "submenu": [
+ {
+ "text": "millilitre (mL)",
+ "value": "mlitre"
+ },
+ {
+ "text": "litre (L)",
+ "value": "litre"
+ },
+ {
+ "text": "cubic metre",
+ "value": "m3"
+ },
+ {
+ "text": "Normal cubic metre",
+ "value": "Nm3"
+ },
+ {
+ "text": "cubic decimetre",
+ "value": "dm3"
+ },
+ {
+ "text": "gallons",
+ "value": "gallons"
+ }
+ ],
+ "text": "volume"
+ },
+ {
+ "submenu": [
+ {
+ "text": "Watt (W)",
+ "value": "watt"
+ },
+ {
+ "text": "Kilowatt (kW)",
+ "value": "kwatt"
+ },
+ {
+ "text": "Milliwatt (mW)",
+ "value": "mwatt"
+ },
+ {
+ "text": "Watt per square metre (W/m²)",
+ "value": "Wm2"
+ },
+ {
+ "text": "Volt-ampere (VA)",
+ "value": "voltamp"
+ },
+ {
+ "text": "Kilovolt-ampere (kVA)",
+ "value": "kvoltamp"
+ },
+ {
+ "text": "Volt-ampere reactive (var)",
+ "value": "voltampreact"
+ },
+ {
+ "text": "Kilovolt-ampere reactive (kvar)",
+ "value": "kvoltampreact"
+ },
+ {
+ "text": "Watt-hour (Wh)",
+ "value": "watth"
+ },
+ {
+ "text": "Kilowatt-hour (kWh)",
+ "value": "kwatth"
+ },
+ {
+ "text": "Kilowatt-min (kWm)",
+ "value": "kwattm"
+ },
+ {
+ "text": "Joule (J)",
+ "value": "joule"
+ },
+ {
+ "text": "Electron volt (eV)",
+ "value": "ev"
+ },
+ {
+ "text": "Ampere (A)",
+ "value": "amp"
+ },
+ {
+ "text": "Kiloampere (kA)",
+ "value": "kamp"
+ },
+ {
+ "text": "Milliampere (mA)",
+ "value": "mamp"
+ },
+ {
+ "text": "Volt (V)",
+ "value": "volt"
+ },
+ {
+ "text": "Kilovolt (kV)",
+ "value": "kvolt"
+ },
+ {
+ "text": "Millivolt (mV)",
+ "value": "mvolt"
+ },
+ {
+ "text": "Decibel-milliwatt (dBm)",
+ "value": "dBm"
+ },
+ {
+ "text": "Ohm (Ω)",
+ "value": "ohm"
+ },
+ {
+ "text": "Lumens (Lm)",
+ "value": "lumens"
+ }
+ ],
+ "text": "energy"
+ },
+ {
+ "submenu": [
+ {
+ "text": "Celsius (°C)",
+ "value": "celsius"
+ },
+ {
+ "text": "Farenheit (°F)",
+ "value": "farenheit"
+ },
+ {
+ "text": "Kelvin (K)",
+ "value": "kelvin"
+ }
+ ],
+ "text": "temperature"
+ },
+ {
+ "submenu": [
+ {
+ "text": "Millibars",
+ "value": "pressurembar"
+ },
+ {
+ "text": "Bars",
+ "value": "pressurebar"
+ },
+ {
+ "text": "Kilobars",
+ "value": "pressurekbar"
+ },
+ {
+ "text": "Hectopascals",
+ "value": "pressurehpa"
+ },
+ {
+ "text": "Kilopascals",
+ "value": "pressurekpa"
+ },
+ {
+ "text": "Inches of mercury",
+ "value": "pressurehg"
+ },
+ {
+ "text": "PSI",
+ "value": "pressurepsi"
+ }
+ ],
+ "text": "pressure"
+ },
+ {
+ "submenu": [
+ {
+ "text": "Newton-meters (Nm)",
+ "value": "forceNm"
+ },
+ {
+ "text": "Kilonewton-meters (kNm)",
+ "value": "forcekNm"
+ },
+ {
+ "text": "Newtons (N)",
+ "value": "forceN"
+ },
+ {
+ "text": "Kilonewtons (kN)",
+ "value": "forcekN"
+ }
+ ],
+ "text": "force"
+ },
+ {
+ "submenu": [
+ {
+ "text": "Gallons/min (gpm)",
+ "value": "flowgpm"
+ },
+ {
+ "text": "Cubic meters/sec (cms)",
+ "value": "flowcms"
+ },
+ {
+ "text": "Cubic feet/sec (cfs)",
+ "value": "flowcfs"
+ },
+ {
+ "text": "Cubic feet/min (cfm)",
+ "value": "flowcfm"
+ },
+ {
+ "text": "Litre/hour",
+ "value": "litreh"
+ },
+ {
+ "text": "Litre/min (l/min)",
+ "value": "flowlpm"
+ },
+ {
+ "text": "milliLitre/min (mL/min)",
+ "value": "flowmlpm"
+ }
+ ],
+ "text": "flow"
+ },
+ {
+ "submenu": [
+ {
+ "text": "Degrees (°)",
+ "value": "degree"
+ },
+ {
+ "text": "Radians",
+ "value": "radian"
+ },
+ {
+ "text": "Gradian",
+ "value": "grad"
+ }
+ ],
+ "text": "angle"
+ },
+ {
+ "submenu": [
+ {
+ "text": "Meters/sec²",
+ "value": "accMS2"
+ },
+ {
+ "text": "Feet/sec²",
+ "value": "accFS2"
+ },
+ {
+ "text": "G unit",
+ "value": "accG"
+ }
+ ],
+ "text": "acceleration"
+ },
+ {
+ "submenu": [
+ {
+ "text": "Becquerel (Bq)",
+ "value": "radbq"
+ },
+ {
+ "text": "curie (Ci)",
+ "value": "radci"
+ },
+ {
+ "text": "Gray (Gy)",
+ "value": "radgy"
+ },
+ {
+ "text": "rad",
+ "value": "radrad"
+ },
+ {
+ "text": "Sievert (Sv)",
+ "value": "radsv"
+ },
+ {
+ "text": "rem",
+ "value": "radrem"
+ },
+ {
+ "text": "Exposure (C/kg)",
+ "value": "radexpckg"
+ },
+ {
+ "text": "roentgen (R)",
+ "value": "radr"
+ },
+ {
+ "text": "Sievert/hour (Sv/h)",
+ "value": "radsvh"
+ }
+ ],
+ "text": "radiation"
+ },
+ {
+ "submenu": [
+ {
+ "text": "parts-per-million (ppm)",
+ "value": "ppm"
+ },
+ {
+ "text": "parts-per-billion (ppb)",
+ "value": "conppb"
+ },
+ {
+ "text": "nanogram per cubic metre (ng/m³)",
+ "value": "conngm3"
+ },
+ {
+ "text": "nanogram per normal cubic metre (ng/Nm³)",
+ "value": "conngNm3"
+ },
+ {
+ "text": "microgram per cubic metre (μg/m³)",
+ "value": "conμgm3"
+ },
+ {
+ "text": "microgram per normal cubic metre (μg/Nm³)",
+ "value": "conμgNm3"
+ },
+ {
+ "text": "milligram per cubic metre (mg/m³)",
+ "value": "conmgm3"
+ },
+ {
+ "text": "milligram per normal cubic metre (mg/Nm³)",
+ "value": "conmgNm3"
+ },
+ {
+ "text": "gram per cubic metre (g/m³)",
+ "value": "congm3"
+ },
+ {
+ "text": "gram per normal cubic metre (g/Nm³)",
+ "value": "congNm3"
+ }
+ ],
+ "text": "concentration"
+ }
+ ]
+ },
+ {
+ "animationModes": [
+ {
+ "text": "Show All",
+ "value": "all"
+ },
+ {
+ "text": "Show Triggered",
+ "value": "triggered"
+ }
+ ],
+ "colors": [
+ "#299c46",
+ "rgba(237, 129, 40, 0.89)",
+ "#d44a3a"
+ ],
+ "d3DivId": "d3_svg_5",
+ "datasource": "gdev-testdata",
+ "decimals": 2,
+ "displayModes": [
+ {
+ "text": "Show All",
+ "value": "all"
+ },
+ {
+ "text": "Show Triggered",
+ "value": "triggered"
+ }
+ ],
+ "fontSizes": [
+ 4,
+ 5,
+ 6,
+ 7,
+ 8,
+ 9,
+ 10,
+ 11,
+ 12,
+ 13,
+ 14,
+ 15,
+ 16,
+ 17,
+ 18,
+ 19,
+ 20,
+ 22,
+ 24,
+ 26,
+ 28,
+ 30,
+ 32,
+ 34,
+ 36,
+ 38,
+ 40,
+ 42,
+ 44,
+ 46,
+ 48,
+ 50,
+ 52,
+ 54,
+ 56,
+ 58,
+ 60,
+ 62,
+ 64,
+ 66,
+ 68,
+ 70
+ ],
+ "fontTypes": [
+ "Open Sans",
+ "Arial",
+ "Avant Garde",
+ "Bookman",
+ "Consolas",
+ "Courier",
+ "Courier New",
+ "Futura",
+ "Garamond",
+ "Helvetica",
+ "Palatino",
+ "Times",
+ "Times New Roman",
+ "Verdana"
+ ],
+ "format": "none",
+ "gridPos": {
+ "h": 9,
+ "w": 12,
+ "x": 12,
+ "y": 0
+ },
+ "id": 5,
+ "links": [],
+ "notcolors": [
+ "rgba(245, 54, 54, 0.9)",
+ "rgba(237, 129, 40, 0.89)",
+ "rgba(50, 172, 45, 0.97)"
+ ],
+ "operatorName": "avg",
+ "operatorOptions": [
+ {
+ "text": "Average",
+ "value": "avg"
+ },
+ {
+ "text": "Count",
+ "value": "count"
+ },
+ {
+ "text": "Current",
+ "value": "current"
+ },
+ {
+ "text": "Delta",
+ "value": "delta"
+ },
+ {
+ "text": "Difference",
+ "value": "diff"
+ },
+ {
+ "text": "First",
+ "value": "first"
+ },
+ {
+ "text": "Log Min",
+ "value": "logmin"
+ },
+ {
+ "text": "Max",
+ "value": "max"
+ },
+ {
+ "text": "Min",
+ "value": "min"
+ },
+ {
+ "text": "Name",
+ "value": "name"
+ },
+ {
+ "text": "Time of Last Point",
+ "value": "last_time"
+ },
+ {
+ "text": "Time Step",
+ "value": "time_step"
+ },
+ {
+ "text": "Total",
+ "value": "total"
+ }
+ ],
+ "polystat": {
+ "animationSpeed": 2500,
+ "columnAutoSize": true,
+ "columns": "",
+ "defaultClickThrough": "",
+ "defaultClickThroughSanitize": true,
+ "displayLimit": 100,
+ "fontAutoScale": true,
+ "fontSize": 12,
+ "globalDisplayMode": "all",
+ "globalOperatorName": "avg",
+ "gradientEnabled": true,
+ "hexagonSortByDirection": "asc",
+ "hexagonSortByField": "name",
+ "maxMetrics": 0,
+ "polygonBorderColor": "black",
+ "polygonBorderSize": 2,
+ "radius": "",
+ "radiusAutoSize": true,
+ "rowAutoSize": true,
+ "rows": "",
+ "shape": "hexagon_pointed_top",
+ "tooltipDisplayMode": "all",
+ "tooltipDisplayTextTriggeredEmpty": "OK",
+ "tooltipFontSize": 12,
+ "tooltipFontType": "Open Sans",
+ "tooltipPrimarySortDirection": "desc",
+ "tooltipPrimarySortField": "thresholdLevel",
+ "tooltipSecondarySortDirection": "desc",
+ "tooltipSecondarySortField": "value",
+ "tooltipTimestampEnabled": true
+ },
+ "savedComposites": [
+ {
+ "compositeName": "comp",
+ "members": [
+ {
+ "seriesName": "A-series"
+ },
+ {
+ "seriesName": "B-series"
+ }
+ ],
+ "enabled": true,
+ "clickThrough": "",
+ "hideMembers": true,
+ "showName": true,
+ "showValue": true,
+ "animateMode": "all",
+ "thresholdLevel": 0,
+ "sanitizeURLEnabled": true,
+ "sanitizedURL": ""
+ }
+ ],
+ "savedOverrides": [],
+ "shapes": [
+ {
+ "text": "Hexagon Pointed Top",
+ "value": "hexagon_pointed_top"
+ },
+ {
+ "text": "Hexagon Flat Top",
+ "value": "hexagon_flat_top"
+ },
+ {
+ "text": "Circle",
+ "value": "circle"
+ },
+ {
+ "text": "Cross",
+ "value": "cross"
+ },
+ {
+ "text": "Diamond",
+ "value": "diamond"
+ },
+ {
+ "text": "Square",
+ "value": "square"
+ },
+ {
+ "text": "Star",
+ "value": "star"
+ },
+ {
+ "text": "Triangle",
+ "value": "triangle"
+ },
+ {
+ "text": "Wye",
+ "value": "wye"
+ }
+ ],
+ "sortDirections": [
+ {
+ "text": "Ascending",
+ "value": "asc"
+ },
+ {
+ "text": "Descending",
+ "value": "desc"
+ }
+ ],
+ "sortFields": [
+ {
+ "text": "Name",
+ "value": "name"
+ },
+ {
+ "text": "Threshold Level",
+ "value": "thresholdLevel"
+ },
+ {
+ "text": "Value",
+ "value": "value"
+ }
+ ],
+ "svgContainer": {},
+ "targets": [
+ {
+ "expr": "",
+ "format": "time_series",
+ "intervalFactor": 1,
+ "refId": "A",
+ "scenarioId": "random_walk"
+ },
+ {
+ "expr": "",
+ "format": "time_series",
+ "intervalFactor": 1,
+ "refId": "B",
+ "scenarioId": "random_walk"
+ },
+ {
+ "expr": "",
+ "format": "time_series",
+ "intervalFactor": 1,
+ "refId": "C",
+ "scenarioId": "random_walk"
+ },
+ {
+ "expr": "",
+ "format": "time_series",
+ "intervalFactor": 1,
+ "refId": "D",
+ "scenarioId": "random_walk"
+ },
+ {
+ "expr": "",
+ "format": "time_series",
+ "intervalFactor": 1,
+ "refId": "E",
+ "scenarioId": "random_walk"
+ }
+ ],
+ "thresholdStates": [
+ {
+ "text": "ok",
+ "value": 0
+ },
+ {
+ "text": "warning",
+ "value": 1
+ },
+ {
+ "text": "critical",
+ "value": 2
+ },
+ {
+ "text": "custom",
+ "value": 3
+ }
+ ],
+ "title": "Composite crash",
+ "type": "grafana-polystat-panel",
+ "unitFormats": [
+ {
+ "submenu": [
+ {
+ "text": "none",
+ "value": "none"
+ },
+ {
+ "text": "short",
+ "value": "short"
+ },
+ {
+ "text": "percent (0-100)",
+ "value": "percent"
+ },
+ {
+ "text": "percent (0.0-1.0)",
+ "value": "percentunit"
+ },
+ {
+ "text": "Humidity (%H)",
+ "value": "humidity"
+ },
+ {
+ "text": "decibel",
+ "value": "dB"
+ },
+ {
+ "text": "hexadecimal (0x)",
+ "value": "hex0x"
+ },
+ {
+ "text": "hexadecimal",
+ "value": "hex"
+ },
+ {
+ "text": "scientific notation",
+ "value": "sci"
+ },
+ {
+ "text": "locale format",
+ "value": "locale"
+ }
+ ],
+ "text": "none"
+ },
+ {
+ "submenu": [
+ {
+ "text": "Dollars ($)",
+ "value": "currencyUSD"
+ },
+ {
+ "text": "Pounds (£)",
+ "value": "currencyGBP"
+ },
+ {
+ "text": "Euro (€)",
+ "value": "currencyEUR"
+ },
+ {
+ "text": "Yen (¥)",
+ "value": "currencyJPY"
+ },
+ {
+ "text": "Rubles (₽)",
+ "value": "currencyRUB"
+ },
+ {
+ "text": "Hryvnias (₴)",
+ "value": "currencyUAH"
+ },
+ {
+ "text": "Real (R$)",
+ "value": "currencyBRL"
+ },
+ {
+ "text": "Danish Krone (kr)",
+ "value": "currencyDKK"
+ },
+ {
+ "text": "Icelandic Króna (kr)",
+ "value": "currencyISK"
+ },
+ {
+ "text": "Norwegian Krone (kr)",
+ "value": "currencyNOK"
+ },
+ {
+ "text": "Swedish Krona (kr)",
+ "value": "currencySEK"
+ },
+ {
+ "text": "Czech koruna (czk)",
+ "value": "currencyCZK"
+ },
+ {
+ "text": "Swiss franc (CHF)",
+ "value": "currencyCHF"
+ },
+ {
+ "text": "Polish Złoty (PLN)",
+ "value": "currencyPLN"
+ },
+ {
+ "text": "Bitcoin (฿)",
+ "value": "currencyBTC"
+ }
+ ],
+ "text": "currency"
+ },
+ {
+ "submenu": [
+ {
+ "text": "Hertz (1/s)",
+ "value": "hertz"
+ },
+ {
+ "text": "nanoseconds (ns)",
+ "value": "ns"
+ },
+ {
+ "text": "microseconds (µs)",
+ "value": "µs"
+ },
+ {
+ "text": "milliseconds (ms)",
+ "value": "ms"
+ },
+ {
+ "text": "seconds (s)",
+ "value": "s"
+ },
+ {
+ "text": "minutes (m)",
+ "value": "m"
+ },
+ {
+ "text": "hours (h)",
+ "value": "h"
+ },
+ {
+ "text": "days (d)",
+ "value": "d"
+ },
+ {
+ "text": "duration (ms)",
+ "value": "dtdurationms"
+ },
+ {
+ "text": "duration (s)",
+ "value": "dtdurations"
+ },
+ {
+ "text": "duration (hh:mm:ss)",
+ "value": "dthms"
+ },
+ {
+ "text": "Timeticks (s/100)",
+ "value": "timeticks"
+ }
+ ],
+ "text": "time"
+ },
+ {
+ "submenu": [
+ {
+ "text": "YYYY-MM-DD HH:mm:ss",
+ "value": "dateTimeAsIso"
+ },
+ {
+ "text": "DD/MM/YYYY h:mm:ss a",
+ "value": "dateTimeAsUS"
+ },
+ {
+ "text": "From Now",
+ "value": "dateTimeFromNow"
+ }
+ ],
+ "text": "date & time"
+ },
+ {
+ "submenu": [
+ {
+ "text": "bits",
+ "value": "bits"
+ },
+ {
+ "text": "bytes",
+ "value": "bytes"
+ },
+ {
+ "text": "kibibytes",
+ "value": "kbytes"
+ },
+ {
+ "text": "mebibytes",
+ "value": "mbytes"
+ },
+ {
+ "text": "gibibytes",
+ "value": "gbytes"
+ }
+ ],
+ "text": "data (IEC)"
+ },
+ {
+ "submenu": [
+ {
+ "text": "bits",
+ "value": "decbits"
+ },
+ {
+ "text": "bytes",
+ "value": "decbytes"
+ },
+ {
+ "text": "kilobytes",
+ "value": "deckbytes"
+ },
+ {
+ "text": "megabytes",
+ "value": "decmbytes"
+ },
+ {
+ "text": "gigabytes",
+ "value": "decgbytes"
+ }
+ ],
+ "text": "data (Metric)"
+ },
+ {
+ "submenu": [
+ {
+ "text": "packets/sec",
+ "value": "pps"
+ },
+ {
+ "text": "bits/sec",
+ "value": "bps"
+ },
+ {
+ "text": "bytes/sec",
+ "value": "Bps"
+ },
+ {
+ "text": "kilobits/sec",
+ "value": "Kbits"
+ },
+ {
+ "text": "kilobytes/sec",
+ "value": "KBs"
+ },
+ {
+ "text": "megabits/sec",
+ "value": "Mbits"
+ },
+ {
+ "text": "megabytes/sec",
+ "value": "MBs"
+ },
+ {
+ "text": "gigabytes/sec",
+ "value": "GBs"
+ },
+ {
+ "text": "gigabits/sec",
+ "value": "Gbits"
+ }
+ ],
+ "text": "data rate"
+ },
+ {
+ "submenu": [
+ {
+ "text": "hashes/sec",
+ "value": "Hs"
+ },
+ {
+ "text": "kilohashes/sec",
+ "value": "KHs"
+ },
+ {
+ "text": "megahashes/sec",
+ "value": "MHs"
+ },
+ {
+ "text": "gigahashes/sec",
+ "value": "GHs"
+ },
+ {
+ "text": "terahashes/sec",
+ "value": "THs"
+ },
+ {
+ "text": "petahashes/sec",
+ "value": "PHs"
+ },
+ {
+ "text": "exahashes/sec",
+ "value": "EHs"
+ }
+ ],
+ "text": "hash rate"
+ },
+ {
+ "submenu": [
+ {
+ "text": "ops/sec (ops)",
+ "value": "ops"
+ },
+ {
+ "text": "requests/sec (rps)",
+ "value": "reqps"
+ },
+ {
+ "text": "reads/sec (rps)",
+ "value": "rps"
+ },
+ {
+ "text": "writes/sec (wps)",
+ "value": "wps"
+ },
+ {
+ "text": "I/O ops/sec (iops)",
+ "value": "iops"
+ },
+ {
+ "text": "ops/min (opm)",
+ "value": "opm"
+ },
+ {
+ "text": "reads/min (rpm)",
+ "value": "rpm"
+ },
+ {
+ "text": "writes/min (wpm)",
+ "value": "wpm"
+ }
+ ],
+ "text": "throughput"
+ },
+ {
+ "submenu": [
+ {
+ "text": "millimetre (mm)",
+ "value": "lengthmm"
+ },
+ {
+ "text": "meter (m)",
+ "value": "lengthm"
+ },
+ {
+ "text": "feet (ft)",
+ "value": "lengthft"
+ },
+ {
+ "text": "kilometer (km)",
+ "value": "lengthkm"
+ },
+ {
+ "text": "mile (mi)",
+ "value": "lengthmi"
+ }
+ ],
+ "text": "length"
+ },
+ {
+ "submenu": [
+ {
+ "text": "Square Meters (m²)",
+ "value": "areaM2"
+ },
+ {
+ "text": "Square Feet (ft²)",
+ "value": "areaF2"
+ },
+ {
+ "text": "Square Miles (mi²)",
+ "value": "areaMI2"
+ }
+ ],
+ "text": "area"
+ },
+ {
+ "submenu": [
+ {
+ "text": "milligram (mg)",
+ "value": "massmg"
+ },
+ {
+ "text": "gram (g)",
+ "value": "massg"
+ },
+ {
+ "text": "kilogram (kg)",
+ "value": "masskg"
+ },
+ {
+ "text": "metric ton (t)",
+ "value": "masst"
+ }
+ ],
+ "text": "mass"
+ },
+ {
+ "submenu": [
+ {
+ "text": "metres/second (m/s)",
+ "value": "velocityms"
+ },
+ {
+ "text": "kilometers/hour (km/h)",
+ "value": "velocitykmh"
+ },
+ {
+ "text": "miles/hour (mph)",
+ "value": "velocitymph"
+ },
+ {
+ "text": "knot (kn)",
+ "value": "velocityknot"
+ }
+ ],
+ "text": "velocity"
+ },
+ {
+ "submenu": [
+ {
+ "text": "millilitre (mL)",
+ "value": "mlitre"
+ },
+ {
+ "text": "litre (L)",
+ "value": "litre"
+ },
+ {
+ "text": "cubic metre",
+ "value": "m3"
+ },
+ {
+ "text": "Normal cubic metre",
+ "value": "Nm3"
+ },
+ {
+ "text": "cubic decimetre",
+ "value": "dm3"
+ },
+ {
+ "text": "gallons",
+ "value": "gallons"
+ }
+ ],
+ "text": "volume"
+ },
+ {
+ "submenu": [
+ {
+ "text": "Watt (W)",
+ "value": "watt"
+ },
+ {
+ "text": "Kilowatt (kW)",
+ "value": "kwatt"
+ },
+ {
+ "text": "Milliwatt (mW)",
+ "value": "mwatt"
+ },
+ {
+ "text": "Watt per square metre (W/m²)",
+ "value": "Wm2"
+ },
+ {
+ "text": "Volt-ampere (VA)",
+ "value": "voltamp"
+ },
+ {
+ "text": "Kilovolt-ampere (kVA)",
+ "value": "kvoltamp"
+ },
+ {
+ "text": "Volt-ampere reactive (var)",
+ "value": "voltampreact"
+ },
+ {
+ "text": "Kilovolt-ampere reactive (kvar)",
+ "value": "kvoltampreact"
+ },
+ {
+ "text": "Watt-hour (Wh)",
+ "value": "watth"
+ },
+ {
+ "text": "Kilowatt-hour (kWh)",
+ "value": "kwatth"
+ },
+ {
+ "text": "Kilowatt-min (kWm)",
+ "value": "kwattm"
+ },
+ {
+ "text": "Joule (J)",
+ "value": "joule"
+ },
+ {
+ "text": "Electron volt (eV)",
+ "value": "ev"
+ },
+ {
+ "text": "Ampere (A)",
+ "value": "amp"
+ },
+ {
+ "text": "Kiloampere (kA)",
+ "value": "kamp"
+ },
+ {
+ "text": "Milliampere (mA)",
+ "value": "mamp"
+ },
+ {
+ "text": "Volt (V)",
+ "value": "volt"
+ },
+ {
+ "text": "Kilovolt (kV)",
+ "value": "kvolt"
+ },
+ {
+ "text": "Millivolt (mV)",
+ "value": "mvolt"
+ },
+ {
+ "text": "Decibel-milliwatt (dBm)",
+ "value": "dBm"
+ },
+ {
+ "text": "Ohm (Ω)",
+ "value": "ohm"
+ },
+ {
+ "text": "Lumens (Lm)",
+ "value": "lumens"
+ }
+ ],
+ "text": "energy"
+ },
+ {
+ "submenu": [
+ {
+ "text": "Celsius (°C)",
+ "value": "celsius"
+ },
+ {
+ "text": "Farenheit (°F)",
+ "value": "farenheit"
+ },
+ {
+ "text": "Kelvin (K)",
+ "value": "kelvin"
+ }
+ ],
+ "text": "temperature"
+ },
+ {
+ "submenu": [
+ {
+ "text": "Millibars",
+ "value": "pressurembar"
+ },
+ {
+ "text": "Bars",
+ "value": "pressurebar"
+ },
+ {
+ "text": "Kilobars",
+ "value": "pressurekbar"
+ },
+ {
+ "text": "Hectopascals",
+ "value": "pressurehpa"
+ },
+ {
+ "text": "Kilopascals",
+ "value": "pressurekpa"
+ },
+ {
+ "text": "Inches of mercury",
+ "value": "pressurehg"
+ },
+ {
+ "text": "PSI",
+ "value": "pressurepsi"
+ }
+ ],
+ "text": "pressure"
+ },
+ {
+ "submenu": [
+ {
+ "text": "Newton-meters (Nm)",
+ "value": "forceNm"
+ },
+ {
+ "text": "Kilonewton-meters (kNm)",
+ "value": "forcekNm"
+ },
+ {
+ "text": "Newtons (N)",
+ "value": "forceN"
+ },
+ {
+ "text": "Kilonewtons (kN)",
+ "value": "forcekN"
+ }
+ ],
+ "text": "force"
+ },
+ {
+ "submenu": [
+ {
+ "text": "Gallons/min (gpm)",
+ "value": "flowgpm"
+ },
+ {
+ "text": "Cubic meters/sec (cms)",
+ "value": "flowcms"
+ },
+ {
+ "text": "Cubic feet/sec (cfs)",
+ "value": "flowcfs"
+ },
+ {
+ "text": "Cubic feet/min (cfm)",
+ "value": "flowcfm"
+ },
+ {
+ "text": "Litre/hour",
+ "value": "litreh"
+ },
+ {
+ "text": "Litre/min (l/min)",
+ "value": "flowlpm"
+ },
+ {
+ "text": "milliLitre/min (mL/min)",
+ "value": "flowmlpm"
+ }
+ ],
+ "text": "flow"
+ },
+ {
+ "submenu": [
+ {
+ "text": "Degrees (°)",
+ "value": "degree"
+ },
+ {
+ "text": "Radians",
+ "value": "radian"
+ },
+ {
+ "text": "Gradian",
+ "value": "grad"
+ }
+ ],
+ "text": "angle"
+ },
+ {
+ "submenu": [
+ {
+ "text": "Meters/sec²",
+ "value": "accMS2"
+ },
+ {
+ "text": "Feet/sec²",
+ "value": "accFS2"
+ },
+ {
+ "text": "G unit",
+ "value": "accG"
+ }
+ ],
+ "text": "acceleration"
+ },
+ {
+ "submenu": [
+ {
+ "text": "Becquerel (Bq)",
+ "value": "radbq"
+ },
+ {
+ "text": "curie (Ci)",
+ "value": "radci"
+ },
+ {
+ "text": "Gray (Gy)",
+ "value": "radgy"
+ },
+ {
+ "text": "rad",
+ "value": "radrad"
+ },
+ {
+ "text": "Sievert (Sv)",
+ "value": "radsv"
+ },
+ {
+ "text": "rem",
+ "value": "radrem"
+ },
+ {
+ "text": "Exposure (C/kg)",
+ "value": "radexpckg"
+ },
+ {
+ "text": "roentgen (R)",
+ "value": "radr"
+ },
+ {
+ "text": "Sievert/hour (Sv/h)",
+ "value": "radsvh"
+ }
+ ],
+ "text": "radiation"
+ },
+ {
+ "submenu": [
+ {
+ "text": "parts-per-million (ppm)",
+ "value": "ppm"
+ },
+ {
+ "text": "parts-per-billion (ppb)",
+ "value": "conppb"
+ },
+ {
+ "text": "nanogram per cubic metre (ng/m³)",
+ "value": "conngm3"
+ },
+ {
+ "text": "nanogram per normal cubic metre (ng/Nm³)",
+ "value": "conngNm3"
+ },
+ {
+ "text": "microgram per cubic metre (μg/m³)",
+ "value": "conμgm3"
+ },
+ {
+ "text": "microgram per normal cubic metre (μg/Nm³)",
+ "value": "conμgNm3"
+ },
+ {
+ "text": "milligram per cubic metre (mg/m³)",
+ "value": "conmgm3"
+ },
+ {
+ "text": "milligram per normal cubic metre (mg/Nm³)",
+ "value": "conmgNm3"
+ },
+ {
+ "text": "gram per cubic metre (g/m³)",
+ "value": "congm3"
+ },
+ {
+ "text": "gram per normal cubic metre (g/Nm³)",
+ "value": "congNm3"
+ }
+ ],
+ "text": "concentration"
+ }
+ ]
+ },
+ {
+ "animationModes": [
+ {
+ "text": "Show All",
+ "value": "all"
+ },
+ {
+ "text": "Show Triggered",
+ "value": "triggered"
+ }
+ ],
+ "colors": [
+ "#299c46",
+ "rgba(237, 129, 40, 0.89)",
+ "#d44a3a"
+ ],
+ "d3DivId": "d3_svg_2",
+ "datasource": "gdev-testdata",
+ "decimals": 2,
+ "displayModes": [
+ {
+ "text": "Show All",
+ "value": "all"
+ },
+ {
+ "text": "Show Triggered",
+ "value": "triggered"
+ }
+ ],
+ "fontSizes": [
+ 4,
+ 5,
+ 6,
+ 7,
+ 8,
+ 9,
+ 10,
+ 11,
+ 12,
+ 13,
+ 14,
+ 15,
+ 16,
+ 17,
+ 18,
+ 19,
+ 20,
+ 22,
+ 24,
+ 26,
+ 28,
+ 30,
+ 32,
+ 34,
+ 36,
+ 38,
+ 40,
+ 42,
+ 44,
+ 46,
+ 48,
+ 50,
+ 52,
+ 54,
+ 56,
+ 58,
+ 60,
+ 62,
+ 64,
+ 66,
+ 68,
+ 70
+ ],
+ "fontTypes": [
+ "Open Sans",
+ "Arial",
+ "Avant Garde",
+ "Bookman",
+ "Consolas",
+ "Courier",
+ "Courier New",
+ "Futura",
+ "Garamond",
+ "Helvetica",
+ "Palatino",
+ "Times",
+ "Times New Roman",
+ "Verdana"
+ ],
+ "format": "none",
+ "gridPos": {
+ "h": 10,
+ "w": 12,
+ "x": 0,
+ "y": 9
+ },
+ "id": 2,
+ "links": [],
+ "notcolors": [
+ "rgba(245, 54, 54, 0.9)",
+ "rgba(237, 129, 40, 0.89)",
+ "rgba(50, 172, 45, 0.97)"
+ ],
+ "operatorName": "avg",
+ "operatorOptions": [
+ {
+ "text": "Average",
+ "value": "avg"
+ },
+ {
+ "text": "Count",
+ "value": "count"
+ },
+ {
+ "text": "Current",
+ "value": "current"
+ },
+ {
+ "text": "Delta",
+ "value": "delta"
+ },
+ {
+ "text": "Difference",
+ "value": "diff"
+ },
+ {
+ "text": "First",
+ "value": "first"
+ },
+ {
+ "text": "Log Min",
+ "value": "logmin"
+ },
+ {
+ "text": "Max",
+ "value": "max"
+ },
+ {
+ "text": "Min",
+ "value": "min"
+ },
+ {
+ "text": "Name",
+ "value": "name"
+ },
+ {
+ "text": "Time of Last Point",
+ "value": "last_time"
+ },
+ {
+ "text": "Time Step",
+ "value": "time_step"
+ },
+ {
+ "text": "Total",
+ "value": "total"
+ }
+ ],
+ "polystat": {
+ "animationSpeed": 2500,
+ "columnAutoSize": true,
+ "columns": 1,
+ "defaultClickThrough": "",
+ "defaultClickThroughSanitize": true,
+ "displayLimit": 100,
+ "fontAutoScale": true,
+ "fontSize": 12,
+ "globalDisplayMode": "all",
+ "globalOperatorName": "avg",
+ "gradientEnabled": true,
+ "hexagonSortByDirection": "asc",
+ "hexagonSortByField": "name",
+ "maxMetrics": 0,
+ "polygonBorderColor": "black",
+ "polygonBorderSize": 2,
+ "radius": "",
+ "radiusAutoSize": true,
+ "rowAutoSize": true,
+ "rows": 1,
+ "shape": "hexagon_pointed_top",
+ "tooltipDisplayMode": "all",
+ "tooltipDisplayTextTriggeredEmpty": "OK",
+ "tooltipFontSize": 12,
+ "tooltipFontType": "Open Sans",
+ "tooltipPrimarySortDirection": "desc",
+ "tooltipPrimarySortField": "thresholdLevel",
+ "tooltipSecondarySortDirection": "desc",
+ "tooltipSecondarySortField": "value",
+ "tooltipTimestampEnabled": true
+ },
+ "savedComposites": [],
+ "savedOverrides": [],
+ "shapes": [
+ {
+ "text": "Hexagon Pointed Top",
+ "value": "hexagon_pointed_top"
+ },
+ {
+ "text": "Hexagon Flat Top",
+ "value": "hexagon_flat_top"
+ },
+ {
+ "text": "Circle",
+ "value": "circle"
+ },
+ {
+ "text": "Cross",
+ "value": "cross"
+ },
+ {
+ "text": "Diamond",
+ "value": "diamond"
+ },
+ {
+ "text": "Square",
+ "value": "square"
+ },
+ {
+ "text": "Star",
+ "value": "star"
+ },
+ {
+ "text": "Triangle",
+ "value": "triangle"
+ },
+ {
+ "text": "Wye",
+ "value": "wye"
+ }
+ ],
+ "sortDirections": [
+ {
+ "text": "Ascending",
+ "value": "asc"
+ },
+ {
+ "text": "Descending",
+ "value": "desc"
+ }
+ ],
+ "sortFields": [
+ {
+ "text": "Name",
+ "value": "name"
+ },
+ {
+ "text": "Threshold Level",
+ "value": "thresholdLevel"
+ },
+ {
+ "text": "Value",
+ "value": "value"
+ }
+ ],
+ "svgContainer": {},
+ "targets": [
+ {
+ "alias": "Sensor-A",
+ "expr": "",
+ "format": "time_series",
+ "intervalFactor": 1,
+ "refId": "A",
+ "scenarioId": "csv_metric_values",
+ "stringInput": "1,20,90,30,5,0"
+ },
+ {
+ "alias": "Sensor-B",
+ "expr": "",
+ "format": "time_series",
+ "intervalFactor": 1,
+ "refId": "B",
+ "scenarioId": "csv_metric_values",
+ "stringInput": "3433,23432,55"
+ },
+ {
+ "alias": "Sensor-C",
+ "expr": "",
+ "format": "time_series",
+ "intervalFactor": 1,
+ "refId": "C",
+ "scenarioId": "csv_metric_values",
+ "stringInput": "1,2,3,4,5,6"
+ },
+ {
+ "alias": "Sensor-E",
+ "expr": "",
+ "format": "time_series",
+ "intervalFactor": 1,
+ "refId": "D",
+ "scenarioId": "csv_metric_values",
+ "stringInput": "1,20,90,30,5,0"
+ }
+ ],
+ "thresholdStates": [
+ {
+ "text": "ok",
+ "value": 0
+ },
+ {
+ "text": "warning",
+ "value": 1
+ },
+ {
+ "text": "critical",
+ "value": 2
+ },
+ {
+ "text": "custom",
+ "value": 3
+ }
+ ],
+ "title": "No Value in Sensor-C Bug",
+ "type": "grafana-polystat-panel",
+ "unitFormats": [
+ {
+ "submenu": [
+ {
+ "text": "none",
+ "value": "none"
+ },
+ {
+ "text": "short",
+ "value": "short"
+ },
+ {
+ "text": "percent (0-100)",
+ "value": "percent"
+ },
+ {
+ "text": "percent (0.0-1.0)",
+ "value": "percentunit"
+ },
+ {
+ "text": "Humidity (%H)",
+ "value": "humidity"
+ },
+ {
+ "text": "decibel",
+ "value": "dB"
+ },
+ {
+ "text": "hexadecimal (0x)",
+ "value": "hex0x"
+ },
+ {
+ "text": "hexadecimal",
+ "value": "hex"
+ },
+ {
+ "text": "scientific notation",
+ "value": "sci"
+ },
+ {
+ "text": "locale format",
+ "value": "locale"
+ }
+ ],
+ "text": "none"
+ },
+ {
+ "submenu": [
+ {
+ "text": "Dollars ($)",
+ "value": "currencyUSD"
+ },
+ {
+ "text": "Pounds (£)",
+ "value": "currencyGBP"
+ },
+ {
+ "text": "Euro (€)",
+ "value": "currencyEUR"
+ },
+ {
+ "text": "Yen (¥)",
+ "value": "currencyJPY"
+ },
+ {
+ "text": "Rubles (₽)",
+ "value": "currencyRUB"
+ },
+ {
+ "text": "Hryvnias (₴)",
+ "value": "currencyUAH"
+ },
+ {
+ "text": "Real (R$)",
+ "value": "currencyBRL"
+ },
+ {
+ "text": "Danish Krone (kr)",
+ "value": "currencyDKK"
+ },
+ {
+ "text": "Icelandic Króna (kr)",
+ "value": "currencyISK"
+ },
+ {
+ "text": "Norwegian Krone (kr)",
+ "value": "currencyNOK"
+ },
+ {
+ "text": "Swedish Krona (kr)",
+ "value": "currencySEK"
+ },
+ {
+ "text": "Czech koruna (czk)",
+ "value": "currencyCZK"
+ },
+ {
+ "text": "Swiss franc (CHF)",
+ "value": "currencyCHF"
+ },
+ {
+ "text": "Polish Złoty (PLN)",
+ "value": "currencyPLN"
+ },
+ {
+ "text": "Bitcoin (฿)",
+ "value": "currencyBTC"
+ }
+ ],
+ "text": "currency"
+ },
+ {
+ "submenu": [
+ {
+ "text": "Hertz (1/s)",
+ "value": "hertz"
+ },
+ {
+ "text": "nanoseconds (ns)",
+ "value": "ns"
+ },
+ {
+ "text": "microseconds (µs)",
+ "value": "µs"
+ },
+ {
+ "text": "milliseconds (ms)",
+ "value": "ms"
+ },
+ {
+ "text": "seconds (s)",
+ "value": "s"
+ },
+ {
+ "text": "minutes (m)",
+ "value": "m"
+ },
+ {
+ "text": "hours (h)",
+ "value": "h"
+ },
+ {
+ "text": "days (d)",
+ "value": "d"
+ },
+ {
+ "text": "duration (ms)",
+ "value": "dtdurationms"
+ },
+ {
+ "text": "duration (s)",
+ "value": "dtdurations"
+ },
+ {
+ "text": "duration (hh:mm:ss)",
+ "value": "dthms"
+ },
+ {
+ "text": "Timeticks (s/100)",
+ "value": "timeticks"
+ }
+ ],
+ "text": "time"
+ },
+ {
+ "submenu": [
+ {
+ "text": "YYYY-MM-DD HH:mm:ss",
+ "value": "dateTimeAsIso"
+ },
+ {
+ "text": "DD/MM/YYYY h:mm:ss a",
+ "value": "dateTimeAsUS"
+ },
+ {
+ "text": "From Now",
+ "value": "dateTimeFromNow"
+ }
+ ],
+ "text": "date & time"
+ },
+ {
+ "submenu": [
+ {
+ "text": "bits",
+ "value": "bits"
+ },
+ {
+ "text": "bytes",
+ "value": "bytes"
+ },
+ {
+ "text": "kibibytes",
+ "value": "kbytes"
+ },
+ {
+ "text": "mebibytes",
+ "value": "mbytes"
+ },
+ {
+ "text": "gibibytes",
+ "value": "gbytes"
+ }
+ ],
+ "text": "data (IEC)"
+ },
+ {
+ "submenu": [
+ {
+ "text": "bits",
+ "value": "decbits"
+ },
+ {
+ "text": "bytes",
+ "value": "decbytes"
+ },
+ {
+ "text": "kilobytes",
+ "value": "deckbytes"
+ },
+ {
+ "text": "megabytes",
+ "value": "decmbytes"
+ },
+ {
+ "text": "gigabytes",
+ "value": "decgbytes"
+ }
+ ],
+ "text": "data (Metric)"
+ },
+ {
+ "submenu": [
+ {
+ "text": "packets/sec",
+ "value": "pps"
+ },
+ {
+ "text": "bits/sec",
+ "value": "bps"
+ },
+ {
+ "text": "bytes/sec",
+ "value": "Bps"
+ },
+ {
+ "text": "kilobits/sec",
+ "value": "Kbits"
+ },
+ {
+ "text": "kilobytes/sec",
+ "value": "KBs"
+ },
+ {
+ "text": "megabits/sec",
+ "value": "Mbits"
+ },
+ {
+ "text": "megabytes/sec",
+ "value": "MBs"
+ },
+ {
+ "text": "gigabytes/sec",
+ "value": "GBs"
+ },
+ {
+ "text": "gigabits/sec",
+ "value": "Gbits"
+ }
+ ],
+ "text": "data rate"
+ },
+ {
+ "submenu": [
+ {
+ "text": "hashes/sec",
+ "value": "Hs"
+ },
+ {
+ "text": "kilohashes/sec",
+ "value": "KHs"
+ },
+ {
+ "text": "megahashes/sec",
+ "value": "MHs"
+ },
+ {
+ "text": "gigahashes/sec",
+ "value": "GHs"
+ },
+ {
+ "text": "terahashes/sec",
+ "value": "THs"
+ },
+ {
+ "text": "petahashes/sec",
+ "value": "PHs"
+ },
+ {
+ "text": "exahashes/sec",
+ "value": "EHs"
+ }
+ ],
+ "text": "hash rate"
+ },
+ {
+ "submenu": [
+ {
+ "text": "ops/sec (ops)",
+ "value": "ops"
+ },
+ {
+ "text": "requests/sec (rps)",
+ "value": "reqps"
+ },
+ {
+ "text": "reads/sec (rps)",
+ "value": "rps"
+ },
+ {
+ "text": "writes/sec (wps)",
+ "value": "wps"
+ },
+ {
+ "text": "I/O ops/sec (iops)",
+ "value": "iops"
+ },
+ {
+ "text": "ops/min (opm)",
+ "value": "opm"
+ },
+ {
+ "text": "reads/min (rpm)",
+ "value": "rpm"
+ },
+ {
+ "text": "writes/min (wpm)",
+ "value": "wpm"
+ }
+ ],
+ "text": "throughput"
+ },
+ {
+ "submenu": [
+ {
+ "text": "millimetre (mm)",
+ "value": "lengthmm"
+ },
+ {
+ "text": "meter (m)",
+ "value": "lengthm"
+ },
+ {
+ "text": "feet (ft)",
+ "value": "lengthft"
+ },
+ {
+ "text": "kilometer (km)",
+ "value": "lengthkm"
+ },
+ {
+ "text": "mile (mi)",
+ "value": "lengthmi"
+ }
+ ],
+ "text": "length"
+ },
+ {
+ "submenu": [
+ {
+ "text": "Square Meters (m²)",
+ "value": "areaM2"
+ },
+ {
+ "text": "Square Feet (ft²)",
+ "value": "areaF2"
+ },
+ {
+ "text": "Square Miles (mi²)",
+ "value": "areaMI2"
+ }
+ ],
+ "text": "area"
+ },
+ {
+ "submenu": [
+ {
+ "text": "milligram (mg)",
+ "value": "massmg"
+ },
+ {
+ "text": "gram (g)",
+ "value": "massg"
+ },
+ {
+ "text": "kilogram (kg)",
+ "value": "masskg"
+ },
+ {
+ "text": "metric ton (t)",
+ "value": "masst"
+ }
+ ],
+ "text": "mass"
+ },
+ {
+ "submenu": [
+ {
+ "text": "metres/second (m/s)",
+ "value": "velocityms"
+ },
+ {
+ "text": "kilometers/hour (km/h)",
+ "value": "velocitykmh"
+ },
+ {
+ "text": "miles/hour (mph)",
+ "value": "velocitymph"
+ },
+ {
+ "text": "knot (kn)",
+ "value": "velocityknot"
+ }
+ ],
+ "text": "velocity"
+ },
+ {
+ "submenu": [
+ {
+ "text": "millilitre (mL)",
+ "value": "mlitre"
+ },
+ {
+ "text": "litre (L)",
+ "value": "litre"
+ },
+ {
+ "text": "cubic metre",
+ "value": "m3"
+ },
+ {
+ "text": "Normal cubic metre",
+ "value": "Nm3"
+ },
+ {
+ "text": "cubic decimetre",
+ "value": "dm3"
+ },
+ {
+ "text": "gallons",
+ "value": "gallons"
+ }
+ ],
+ "text": "volume"
+ },
+ {
+ "submenu": [
+ {
+ "text": "Watt (W)",
+ "value": "watt"
+ },
+ {
+ "text": "Kilowatt (kW)",
+ "value": "kwatt"
+ },
+ {
+ "text": "Milliwatt (mW)",
+ "value": "mwatt"
+ },
+ {
+ "text": "Watt per square metre (W/m²)",
+ "value": "Wm2"
+ },
+ {
+ "text": "Volt-ampere (VA)",
+ "value": "voltamp"
+ },
+ {
+ "text": "Kilovolt-ampere (kVA)",
+ "value": "kvoltamp"
+ },
+ {
+ "text": "Volt-ampere reactive (var)",
+ "value": "voltampreact"
+ },
+ {
+ "text": "Kilovolt-ampere reactive (kvar)",
+ "value": "kvoltampreact"
+ },
+ {
+ "text": "Watt-hour (Wh)",
+ "value": "watth"
+ },
+ {
+ "text": "Kilowatt-hour (kWh)",
+ "value": "kwatth"
+ },
+ {
+ "text": "Kilowatt-min (kWm)",
+ "value": "kwattm"
+ },
+ {
+ "text": "Joule (J)",
+ "value": "joule"
+ },
+ {
+ "text": "Electron volt (eV)",
+ "value": "ev"
+ },
+ {
+ "text": "Ampere (A)",
+ "value": "amp"
+ },
+ {
+ "text": "Kiloampere (kA)",
+ "value": "kamp"
+ },
+ {
+ "text": "Milliampere (mA)",
+ "value": "mamp"
+ },
+ {
+ "text": "Volt (V)",
+ "value": "volt"
+ },
+ {
+ "text": "Kilovolt (kV)",
+ "value": "kvolt"
+ },
+ {
+ "text": "Millivolt (mV)",
+ "value": "mvolt"
+ },
+ {
+ "text": "Decibel-milliwatt (dBm)",
+ "value": "dBm"
+ },
+ {
+ "text": "Ohm (Ω)",
+ "value": "ohm"
+ },
+ {
+ "text": "Lumens (Lm)",
+ "value": "lumens"
+ }
+ ],
+ "text": "energy"
+ },
+ {
+ "submenu": [
+ {
+ "text": "Celsius (°C)",
+ "value": "celsius"
+ },
+ {
+ "text": "Farenheit (°F)",
+ "value": "farenheit"
+ },
+ {
+ "text": "Kelvin (K)",
+ "value": "kelvin"
+ }
+ ],
+ "text": "temperature"
+ },
+ {
+ "submenu": [
+ {
+ "text": "Millibars",
+ "value": "pressurembar"
+ },
+ {
+ "text": "Bars",
+ "value": "pressurebar"
+ },
+ {
+ "text": "Kilobars",
+ "value": "pressurekbar"
+ },
+ {
+ "text": "Hectopascals",
+ "value": "pressurehpa"
+ },
+ {
+ "text": "Kilopascals",
+ "value": "pressurekpa"
+ },
+ {
+ "text": "Inches of mercury",
+ "value": "pressurehg"
+ },
+ {
+ "text": "PSI",
+ "value": "pressurepsi"
+ }
+ ],
+ "text": "pressure"
+ },
+ {
+ "submenu": [
+ {
+ "text": "Newton-meters (Nm)",
+ "value": "forceNm"
+ },
+ {
+ "text": "Kilonewton-meters (kNm)",
+ "value": "forcekNm"
+ },
+ {
+ "text": "Newtons (N)",
+ "value": "forceN"
+ },
+ {
+ "text": "Kilonewtons (kN)",
+ "value": "forcekN"
+ }
+ ],
+ "text": "force"
+ },
+ {
+ "submenu": [
+ {
+ "text": "Gallons/min (gpm)",
+ "value": "flowgpm"
+ },
+ {
+ "text": "Cubic meters/sec (cms)",
+ "value": "flowcms"
+ },
+ {
+ "text": "Cubic feet/sec (cfs)",
+ "value": "flowcfs"
+ },
+ {
+ "text": "Cubic feet/min (cfm)",
+ "value": "flowcfm"
+ },
+ {
+ "text": "Litre/hour",
+ "value": "litreh"
+ },
+ {
+ "text": "Litre/min (l/min)",
+ "value": "flowlpm"
+ },
+ {
+ "text": "milliLitre/min (mL/min)",
+ "value": "flowmlpm"
+ }
+ ],
+ "text": "flow"
+ },
+ {
+ "submenu": [
+ {
+ "text": "Degrees (°)",
+ "value": "degree"
+ },
+ {
+ "text": "Radians",
+ "value": "radian"
+ },
+ {
+ "text": "Gradian",
+ "value": "grad"
+ }
+ ],
+ "text": "angle"
+ },
+ {
+ "submenu": [
+ {
+ "text": "Meters/sec²",
+ "value": "accMS2"
+ },
+ {
+ "text": "Feet/sec²",
+ "value": "accFS2"
+ },
+ {
+ "text": "G unit",
+ "value": "accG"
+ }
+ ],
+ "text": "acceleration"
+ },
+ {
+ "submenu": [
+ {
+ "text": "Becquerel (Bq)",
+ "value": "radbq"
+ },
+ {
+ "text": "curie (Ci)",
+ "value": "radci"
+ },
+ {
+ "text": "Gray (Gy)",
+ "value": "radgy"
+ },
+ {
+ "text": "rad",
+ "value": "radrad"
+ },
+ {
+ "text": "Sievert (Sv)",
+ "value": "radsv"
+ },
+ {
+ "text": "rem",
+ "value": "radrem"
+ },
+ {
+ "text": "Exposure (C/kg)",
+ "value": "radexpckg"
+ },
+ {
+ "text": "roentgen (R)",
+ "value": "radr"
+ },
+ {
+ "text": "Sievert/hour (Sv/h)",
+ "value": "radsvh"
+ }
+ ],
+ "text": "radiation"
+ },
+ {
+ "submenu": [
+ {
+ "text": "parts-per-million (ppm)",
+ "value": "ppm"
+ },
+ {
+ "text": "parts-per-billion (ppb)",
+ "value": "conppb"
+ },
+ {
+ "text": "nanogram per cubic metre (ng/m³)",
+ "value": "conngm3"
+ },
+ {
+ "text": "nanogram per normal cubic metre (ng/Nm³)",
+ "value": "conngNm3"
+ },
+ {
+ "text": "microgram per cubic metre (μg/m³)",
+ "value": "conμgm3"
+ },
+ {
+ "text": "microgram per normal cubic metre (μg/Nm³)",
+ "value": "conμgNm3"
+ },
+ {
+ "text": "milligram per cubic metre (mg/m³)",
+ "value": "conmgm3"
+ },
+ {
+ "text": "milligram per normal cubic metre (mg/Nm³)",
+ "value": "conmgNm3"
+ },
+ {
+ "text": "gram per cubic metre (g/m³)",
+ "value": "congm3"
+ },
+ {
+ "text": "gram per normal cubic metre (g/Nm³)",
+ "value": "congNm3"
+ }
+ ],
+ "text": "concentration"
+ }
+ ]
+ }
+ ],
+ "schemaVersion": 16,
+ "style": "dark",
+ "tags": [
+ "panel-test",
+ "gdev"
+ ],
+ "templating": {
+ "list": []
+ },
+ "time": {
+ "from": "now-6h",
+ "to": "now"
+ },
+ "timepicker": {
+ "refresh_intervals": [
+ "5s",
+ "10s",
+ "30s",
+ "1m",
+ "5m",
+ "15m",
+ "30m",
+ "1h",
+ "2h",
+ "1d"
+ ],
+ "time_options": [
+ "5m",
+ "15m",
+ "1h",
+ "6h",
+ "12h",
+ "24h",
+ "2d",
+ "7d",
+ "30d"
+ ]
+ },
+ "timezone": "",
+ "title": "Panel Tests - Polystat",
+ "uid": "Kp9Z0hTik",
+ "version": 5
+}
diff --git a/devenv/dev-dashboards/panel_tests_slow_queries_and_annotations.json b/devenv/dev-dashboards/panel_tests_slow_queries_and_annotations.json
new file mode 100644
index 00000000000..08bf6dce9d0
--- /dev/null
+++ b/devenv/dev-dashboards/panel_tests_slow_queries_and_annotations.json
@@ -0,0 +1,1166 @@
+{
+ "annotations": {
+ "list": [
+ {
+ "builtIn": 1,
+ "datasource": "-- Grafana --",
+ "enable": true,
+ "hide": true,
+ "iconColor": "rgba(0, 211, 255, 1)",
+ "name": "Annotations & Alerts",
+ "type": "dashboard"
+ },
+ {
+ "datasource": "-- Grafana --",
+ "enable": true,
+ "hide": false,
+ "iconColor": "rgba(255, 96, 96, 1)",
+ "limit": 100,
+ "matchAny": false,
+ "name": "annotations",
+ "showIn": 0,
+ "tags": [
+ "asd"
+ ],
+ "type": "tags"
+ }
+ ]
+ },
+ "editable": true,
+ "gnetId": null,
+ "graphTooltip": 0,
+ "links": [],
+ "panels": [
+ {
+ "aliasColors": {},
+ "bars": false,
+ "dashLength": 10,
+ "dashes": false,
+ "datasource": "gdev-testdata",
+ "fill": 1,
+ "gridPos": {
+ "h": 7,
+ "w": 13,
+ "x": 0,
+ "y": 0
+ },
+ "id": 6,
+ "legend": {
+ "avg": false,
+ "current": false,
+ "max": false,
+ "min": false,
+ "show": true,
+ "total": false,
+ "values": false
+ },
+ "lines": true,
+ "linewidth": 1,
+ "nullPointMode": "null",
+ "percentage": false,
+ "pointradius": 5,
+ "points": false,
+ "renderer": "flot",
+ "seriesOverrides": [],
+ "spaceLength": 10,
+ "stack": false,
+ "steppedLine": false,
+ "targets": [
+ {
+ "expr": "",
+ "format": "time_series",
+ "intervalFactor": 1,
+ "refId": "A",
+ "scenarioId": "slow_query",
+ "stringInput": "30s"
+ }
+ ],
+ "thresholds": [],
+ "timeFrom": null,
+ "timeShift": null,
+ "title": "Panel Title",
+ "tooltip": {
+ "shared": true,
+ "sort": 0,
+ "value_type": "individual"
+ },
+ "type": "graph",
+ "xaxis": {
+ "buckets": null,
+ "mode": "time",
+ "name": null,
+ "show": true,
+ "values": []
+ },
+ "yaxes": [
+ {
+ "format": "short",
+ "label": null,
+ "logBase": 1,
+ "max": null,
+ "min": null,
+ "show": true
+ },
+ {
+ "format": "short",
+ "label": null,
+ "logBase": 1,
+ "max": null,
+ "min": null,
+ "show": true
+ }
+ ],
+ "yaxis": {
+ "align": false,
+ "alignLevel": null
+ }
+ },
+ {
+ "aliasColors": {},
+ "bars": false,
+ "dashLength": 10,
+ "dashes": false,
+ "datasource": "gdev-testdata",
+ "fill": 1,
+ "gridPos": {
+ "h": 7,
+ "w": 11,
+ "x": 13,
+ "y": 0
+ },
+ "id": 7,
+ "legend": {
+ "avg": false,
+ "current": false,
+ "max": false,
+ "min": false,
+ "show": true,
+ "total": false,
+ "values": false
+ },
+ "lines": true,
+ "linewidth": 1,
+ "nullPointMode": "null",
+ "percentage": false,
+ "pointradius": 5,
+ "points": false,
+ "renderer": "flot",
+ "seriesOverrides": [],
+ "spaceLength": 10,
+ "stack": false,
+ "steppedLine": false,
+ "targets": [
+ {
+ "expr": "",
+ "format": "time_series",
+ "intervalFactor": 1,
+ "refId": "A",
+ "scenarioId": "slow_query",
+ "stringInput": "30s"
+ }
+ ],
+ "thresholds": [],
+ "timeFrom": null,
+ "timeShift": null,
+ "title": "Panel Title",
+ "tooltip": {
+ "shared": true,
+ "sort": 0,
+ "value_type": "individual"
+ },
+ "type": "graph",
+ "xaxis": {
+ "buckets": null,
+ "mode": "time",
+ "name": null,
+ "show": true,
+ "values": []
+ },
+ "yaxes": [
+ {
+ "format": "short",
+ "label": null,
+ "logBase": 1,
+ "max": null,
+ "min": null,
+ "show": true
+ },
+ {
+ "format": "short",
+ "label": null,
+ "logBase": 1,
+ "max": null,
+ "min": null,
+ "show": true
+ }
+ ],
+ "yaxis": {
+ "align": false,
+ "alignLevel": null
+ }
+ },
+ {
+ "aliasColors": {},
+ "bars": false,
+ "dashLength": 10,
+ "dashes": false,
+ "datasource": "gdev-testdata",
+ "fill": 1,
+ "gridPos": {
+ "h": 7,
+ "w": 8,
+ "x": 0,
+ "y": 7
+ },
+ "id": 8,
+ "legend": {
+ "avg": false,
+ "current": false,
+ "max": false,
+ "min": false,
+ "show": true,
+ "total": false,
+ "values": false
+ },
+ "lines": true,
+ "linewidth": 1,
+ "nullPointMode": "null",
+ "percentage": false,
+ "pointradius": 5,
+ "points": false,
+ "renderer": "flot",
+ "seriesOverrides": [],
+ "spaceLength": 10,
+ "stack": false,
+ "steppedLine": false,
+ "targets": [
+ {
+ "expr": "",
+ "format": "time_series",
+ "intervalFactor": 1,
+ "refId": "A",
+ "scenarioId": "slow_query",
+ "stringInput": "30s"
+ }
+ ],
+ "thresholds": [],
+ "timeFrom": null,
+ "timeShift": null,
+ "title": "Panel Title",
+ "tooltip": {
+ "shared": true,
+ "sort": 0,
+ "value_type": "individual"
+ },
+ "type": "graph",
+ "xaxis": {
+ "buckets": null,
+ "mode": "time",
+ "name": null,
+ "show": true,
+ "values": []
+ },
+ "yaxes": [
+ {
+ "format": "short",
+ "label": null,
+ "logBase": 1,
+ "max": null,
+ "min": null,
+ "show": true
+ },
+ {
+ "format": "short",
+ "label": null,
+ "logBase": 1,
+ "max": null,
+ "min": null,
+ "show": true
+ }
+ ],
+ "yaxis": {
+ "align": false,
+ "alignLevel": null
+ }
+ },
+ {
+ "aliasColors": {},
+ "bars": false,
+ "dashLength": 10,
+ "dashes": false,
+ "datasource": "gdev-testdata",
+ "fill": 1,
+ "gridPos": {
+ "h": 7,
+ "w": 8,
+ "x": 8,
+ "y": 7
+ },
+ "id": 18,
+ "legend": {
+ "avg": false,
+ "current": false,
+ "max": false,
+ "min": false,
+ "show": true,
+ "total": false,
+ "values": false
+ },
+ "lines": true,
+ "linewidth": 1,
+ "nullPointMode": "null",
+ "percentage": false,
+ "pointradius": 5,
+ "points": false,
+ "renderer": "flot",
+ "seriesOverrides": [],
+ "spaceLength": 10,
+ "stack": false,
+ "steppedLine": false,
+ "targets": [
+ {
+ "expr": "",
+ "format": "time_series",
+ "intervalFactor": 1,
+ "refId": "A",
+ "scenarioId": "slow_query",
+ "stringInput": "30s"
+ }
+ ],
+ "thresholds": [],
+ "timeFrom": null,
+ "timeShift": null,
+ "title": "Panel Title",
+ "tooltip": {
+ "shared": true,
+ "sort": 0,
+ "value_type": "individual"
+ },
+ "type": "graph",
+ "xaxis": {
+ "buckets": null,
+ "mode": "time",
+ "name": null,
+ "show": true,
+ "values": []
+ },
+ "yaxes": [
+ {
+ "format": "short",
+ "label": null,
+ "logBase": 1,
+ "max": null,
+ "min": null,
+ "show": true
+ },
+ {
+ "format": "short",
+ "label": null,
+ "logBase": 1,
+ "max": null,
+ "min": null,
+ "show": true
+ }
+ ],
+ "yaxis": {
+ "align": false,
+ "alignLevel": null
+ }
+ },
+ {
+ "aliasColors": {},
+ "bars": false,
+ "dashLength": 10,
+ "dashes": false,
+ "datasource": "gdev-testdata",
+ "fill": 1,
+ "gridPos": {
+ "h": 7,
+ "w": 8,
+ "x": 16,
+ "y": 7
+ },
+ "id": 17,
+ "legend": {
+ "avg": false,
+ "current": false,
+ "max": false,
+ "min": false,
+ "show": true,
+ "total": false,
+ "values": false
+ },
+ "lines": true,
+ "linewidth": 1,
+ "nullPointMode": "null",
+ "percentage": false,
+ "pointradius": 5,
+ "points": false,
+ "renderer": "flot",
+ "seriesOverrides": [],
+ "spaceLength": 10,
+ "stack": false,
+ "steppedLine": false,
+ "targets": [
+ {
+ "expr": "",
+ "format": "time_series",
+ "intervalFactor": 1,
+ "refId": "A",
+ "scenarioId": "slow_query",
+ "stringInput": "30s"
+ }
+ ],
+ "thresholds": [],
+ "timeFrom": null,
+ "timeShift": null,
+ "title": "Panel Title",
+ "tooltip": {
+ "shared": true,
+ "sort": 0,
+ "value_type": "individual"
+ },
+ "type": "graph",
+ "xaxis": {
+ "buckets": null,
+ "mode": "time",
+ "name": null,
+ "show": true,
+ "values": []
+ },
+ "yaxes": [
+ {
+ "format": "short",
+ "label": null,
+ "logBase": 1,
+ "max": null,
+ "min": null,
+ "show": true
+ },
+ {
+ "format": "short",
+ "label": null,
+ "logBase": 1,
+ "max": null,
+ "min": null,
+ "show": true
+ }
+ ],
+ "yaxis": {
+ "align": false,
+ "alignLevel": null
+ }
+ },
+ {
+ "aliasColors": {},
+ "bars": false,
+ "dashLength": 10,
+ "dashes": false,
+ "datasource": "gdev-testdata",
+ "fill": 1,
+ "gridPos": {
+ "h": 5,
+ "w": 8,
+ "x": 0,
+ "y": 14
+ },
+ "id": 10,
+ "legend": {
+ "avg": false,
+ "current": false,
+ "max": false,
+ "min": false,
+ "show": true,
+ "total": false,
+ "values": false
+ },
+ "lines": true,
+ "linewidth": 1,
+ "nullPointMode": "null",
+ "percentage": false,
+ "pointradius": 5,
+ "points": false,
+ "renderer": "flot",
+ "seriesOverrides": [],
+ "spaceLength": 10,
+ "stack": false,
+ "steppedLine": false,
+ "targets": [
+ {
+ "expr": "",
+ "format": "time_series",
+ "intervalFactor": 1,
+ "refId": "A",
+ "scenarioId": "slow_query",
+ "stringInput": "5s"
+ }
+ ],
+ "thresholds": [],
+ "timeFrom": null,
+ "timeShift": null,
+ "title": "Panel Title",
+ "tooltip": {
+ "shared": true,
+ "sort": 0,
+ "value_type": "individual"
+ },
+ "type": "graph",
+ "xaxis": {
+ "buckets": null,
+ "mode": "time",
+ "name": null,
+ "show": true,
+ "values": []
+ },
+ "yaxes": [
+ {
+ "format": "short",
+ "label": null,
+ "logBase": 1,
+ "max": null,
+ "min": null,
+ "show": true
+ },
+ {
+ "format": "short",
+ "label": null,
+ "logBase": 1,
+ "max": null,
+ "min": null,
+ "show": true
+ }
+ ],
+ "yaxis": {
+ "align": false,
+ "alignLevel": null
+ }
+ },
+ {
+ "aliasColors": {},
+ "bars": false,
+ "dashLength": 10,
+ "dashes": false,
+ "datasource": "gdev-testdata",
+ "fill": 1,
+ "gridPos": {
+ "h": 5,
+ "w": 8,
+ "x": 8,
+ "y": 14
+ },
+ "id": 9,
+ "legend": {
+ "avg": false,
+ "current": false,
+ "max": false,
+ "min": false,
+ "show": true,
+ "total": false,
+ "values": false
+ },
+ "lines": true,
+ "linewidth": 1,
+ "nullPointMode": "null",
+ "percentage": false,
+ "pointradius": 5,
+ "points": false,
+ "renderer": "flot",
+ "seriesOverrides": [],
+ "spaceLength": 10,
+ "stack": false,
+ "steppedLine": false,
+ "targets": [
+ {
+ "expr": "",
+ "format": "time_series",
+ "intervalFactor": 1,
+ "refId": "A",
+ "scenarioId": "slow_query",
+ "stringInput": "5s"
+ }
+ ],
+ "thresholds": [],
+ "timeFrom": null,
+ "timeShift": null,
+ "title": "Panel Title",
+ "tooltip": {
+ "shared": true,
+ "sort": 0,
+ "value_type": "individual"
+ },
+ "type": "graph",
+ "xaxis": {
+ "buckets": null,
+ "mode": "time",
+ "name": null,
+ "show": true,
+ "values": []
+ },
+ "yaxes": [
+ {
+ "format": "short",
+ "label": null,
+ "logBase": 1,
+ "max": null,
+ "min": null,
+ "show": true
+ },
+ {
+ "format": "short",
+ "label": null,
+ "logBase": 1,
+ "max": null,
+ "min": null,
+ "show": true
+ }
+ ],
+ "yaxis": {
+ "align": false,
+ "alignLevel": null
+ }
+ },
+ {
+ "aliasColors": {},
+ "bars": false,
+ "dashLength": 10,
+ "dashes": false,
+ "datasource": "gdev-testdata",
+ "fill": 1,
+ "gridPos": {
+ "h": 5,
+ "w": 8,
+ "x": 16,
+ "y": 14
+ },
+ "id": 11,
+ "legend": {
+ "avg": false,
+ "current": false,
+ "max": false,
+ "min": false,
+ "show": true,
+ "total": false,
+ "values": false
+ },
+ "lines": true,
+ "linewidth": 1,
+ "nullPointMode": "null",
+ "percentage": false,
+ "pointradius": 5,
+ "points": false,
+ "renderer": "flot",
+ "seriesOverrides": [],
+ "spaceLength": 10,
+ "stack": false,
+ "steppedLine": false,
+ "targets": [
+ {
+ "expr": "",
+ "format": "time_series",
+ "intervalFactor": 1,
+ "refId": "A",
+ "scenarioId": "slow_query",
+ "stringInput": "5s"
+ }
+ ],
+ "thresholds": [],
+ "timeFrom": null,
+ "timeShift": null,
+ "title": "Panel Title",
+ "tooltip": {
+ "shared": true,
+ "sort": 0,
+ "value_type": "individual"
+ },
+ "type": "graph",
+ "xaxis": {
+ "buckets": null,
+ "mode": "time",
+ "name": null,
+ "show": true,
+ "values": []
+ },
+ "yaxes": [
+ {
+ "format": "short",
+ "label": null,
+ "logBase": 1,
+ "max": null,
+ "min": null,
+ "show": true
+ },
+ {
+ "format": "short",
+ "label": null,
+ "logBase": 1,
+ "max": null,
+ "min": null,
+ "show": true
+ }
+ ],
+ "yaxis": {
+ "align": false,
+ "alignLevel": null
+ }
+ },
+ {
+ "aliasColors": {},
+ "bars": false,
+ "dashLength": 10,
+ "dashes": false,
+ "datasource": "gdev-testdata",
+ "fill": 1,
+ "gridPos": {
+ "h": 5,
+ "w": 8,
+ "x": 0,
+ "y": 19
+ },
+ "id": 14,
+ "legend": {
+ "avg": false,
+ "current": false,
+ "max": false,
+ "min": false,
+ "show": true,
+ "total": false,
+ "values": false
+ },
+ "lines": true,
+ "linewidth": 1,
+ "nullPointMode": "null",
+ "percentage": false,
+ "pointradius": 5,
+ "points": false,
+ "renderer": "flot",
+ "seriesOverrides": [],
+ "spaceLength": 10,
+ "stack": false,
+ "steppedLine": false,
+ "targets": [
+ {
+ "expr": "",
+ "format": "time_series",
+ "intervalFactor": 1,
+ "refId": "A",
+ "scenarioId": "slow_query",
+ "stringInput": "5s"
+ }
+ ],
+ "thresholds": [],
+ "timeFrom": null,
+ "timeShift": null,
+ "title": "Panel Title",
+ "tooltip": {
+ "shared": true,
+ "sort": 0,
+ "value_type": "individual"
+ },
+ "type": "graph",
+ "xaxis": {
+ "buckets": null,
+ "mode": "time",
+ "name": null,
+ "show": true,
+ "values": []
+ },
+ "yaxes": [
+ {
+ "format": "short",
+ "label": null,
+ "logBase": 1,
+ "max": null,
+ "min": null,
+ "show": true
+ },
+ {
+ "format": "short",
+ "label": null,
+ "logBase": 1,
+ "max": null,
+ "min": null,
+ "show": true
+ }
+ ],
+ "yaxis": {
+ "align": false,
+ "alignLevel": null
+ }
+ },
+ {
+ "aliasColors": {},
+ "bars": false,
+ "dashLength": 10,
+ "dashes": false,
+ "datasource": "gdev-testdata",
+ "fill": 1,
+ "gridPos": {
+ "h": 5,
+ "w": 8,
+ "x": 8,
+ "y": 19
+ },
+ "id": 15,
+ "legend": {
+ "avg": false,
+ "current": false,
+ "max": false,
+ "min": false,
+ "show": true,
+ "total": false,
+ "values": false
+ },
+ "lines": true,
+ "linewidth": 1,
+ "nullPointMode": "null",
+ "percentage": false,
+ "pointradius": 5,
+ "points": false,
+ "renderer": "flot",
+ "seriesOverrides": [],
+ "spaceLength": 10,
+ "stack": false,
+ "steppedLine": false,
+ "targets": [
+ {
+ "expr": "",
+ "format": "time_series",
+ "intervalFactor": 1,
+ "refId": "A",
+ "scenarioId": "slow_query",
+ "stringInput": "5s"
+ }
+ ],
+ "thresholds": [],
+ "timeFrom": null,
+ "timeShift": null,
+ "title": "Panel Title",
+ "tooltip": {
+ "shared": true,
+ "sort": 0,
+ "value_type": "individual"
+ },
+ "type": "graph",
+ "xaxis": {
+ "buckets": null,
+ "mode": "time",
+ "name": null,
+ "show": true,
+ "values": []
+ },
+ "yaxes": [
+ {
+ "format": "short",
+ "label": null,
+ "logBase": 1,
+ "max": null,
+ "min": null,
+ "show": true
+ },
+ {
+ "format": "short",
+ "label": null,
+ "logBase": 1,
+ "max": null,
+ "min": null,
+ "show": true
+ }
+ ],
+ "yaxis": {
+ "align": false,
+ "alignLevel": null
+ }
+ },
+ {
+ "aliasColors": {},
+ "bars": false,
+ "dashLength": 10,
+ "dashes": false,
+ "datasource": "gdev-testdata",
+ "fill": 1,
+ "gridPos": {
+ "h": 5,
+ "w": 8,
+ "x": 16,
+ "y": 19
+ },
+ "id": 12,
+ "legend": {
+ "avg": false,
+ "current": false,
+ "max": false,
+ "min": false,
+ "show": true,
+ "total": false,
+ "values": false
+ },
+ "lines": true,
+ "linewidth": 1,
+ "nullPointMode": "null",
+ "percentage": false,
+ "pointradius": 5,
+ "points": false,
+ "renderer": "flot",
+ "seriesOverrides": [],
+ "spaceLength": 10,
+ "stack": false,
+ "steppedLine": false,
+ "targets": [
+ {
+ "expr": "",
+ "format": "time_series",
+ "intervalFactor": 1,
+ "refId": "A",
+ "scenarioId": "slow_query",
+ "stringInput": "5s"
+ }
+ ],
+ "thresholds": [],
+ "timeFrom": null,
+ "timeShift": null,
+ "title": "Panel Title",
+ "tooltip": {
+ "shared": true,
+ "sort": 0,
+ "value_type": "individual"
+ },
+ "type": "graph",
+ "xaxis": {
+ "buckets": null,
+ "mode": "time",
+ "name": null,
+ "show": true,
+ "values": []
+ },
+ "yaxes": [
+ {
+ "format": "short",
+ "label": null,
+ "logBase": 1,
+ "max": null,
+ "min": null,
+ "show": true
+ },
+ {
+ "format": "short",
+ "label": null,
+ "logBase": 1,
+ "max": null,
+ "min": null,
+ "show": true
+ }
+ ],
+ "yaxis": {
+ "align": false,
+ "alignLevel": null
+ }
+ },
+ {
+ "aliasColors": {},
+ "bars": false,
+ "dashLength": 10,
+ "dashes": false,
+ "datasource": "gdev-testdata",
+ "fill": 1,
+ "gridPos": {
+ "h": 6,
+ "w": 16,
+ "x": 0,
+ "y": 24
+ },
+ "id": 13,
+ "legend": {
+ "avg": false,
+ "current": false,
+ "max": false,
+ "min": false,
+ "show": true,
+ "total": false,
+ "values": false
+ },
+ "lines": true,
+ "linewidth": 1,
+ "nullPointMode": "null",
+ "percentage": false,
+ "pointradius": 5,
+ "points": false,
+ "renderer": "flot",
+ "seriesOverrides": [],
+ "spaceLength": 10,
+ "stack": false,
+ "steppedLine": false,
+ "targets": [
+ {
+ "expr": "",
+ "format": "time_series",
+ "intervalFactor": 1,
+ "refId": "A",
+ "scenarioId": "slow_query",
+ "stringInput": "5s"
+ }
+ ],
+ "thresholds": [],
+ "timeFrom": null,
+ "timeShift": null,
+ "title": "Panel Title",
+ "tooltip": {
+ "shared": true,
+ "sort": 0,
+ "value_type": "individual"
+ },
+ "type": "graph",
+ "xaxis": {
+ "buckets": null,
+ "mode": "time",
+ "name": null,
+ "show": true,
+ "values": []
+ },
+ "yaxes": [
+ {
+ "format": "short",
+ "label": null,
+ "logBase": 1,
+ "max": null,
+ "min": null,
+ "show": true
+ },
+ {
+ "format": "short",
+ "label": null,
+ "logBase": 1,
+ "max": null,
+ "min": null,
+ "show": true
+ }
+ ],
+ "yaxis": {
+ "align": false,
+ "alignLevel": null
+ }
+ },
+ {
+ "aliasColors": {},
+ "bars": false,
+ "dashLength": 10,
+ "dashes": false,
+ "datasource": "gdev-testdata",
+ "fill": 1,
+ "gridPos": {
+ "h": 6,
+ "w": 8,
+ "x": 16,
+ "y": 24
+ },
+ "id": 16,
+ "legend": {
+ "avg": false,
+ "current": false,
+ "max": false,
+ "min": false,
+ "show": true,
+ "total": false,
+ "values": false
+ },
+ "lines": true,
+ "linewidth": 1,
+ "nullPointMode": "null",
+ "percentage": false,
+ "pointradius": 5,
+ "points": false,
+ "renderer": "flot",
+ "seriesOverrides": [],
+ "spaceLength": 10,
+ "stack": false,
+ "steppedLine": false,
+ "targets": [
+ {
+ "expr": "",
+ "format": "time_series",
+ "intervalFactor": 1,
+ "refId": "A",
+ "scenarioId": "slow_query",
+ "stringInput": "5s"
+ }
+ ],
+ "thresholds": [],
+ "timeFrom": null,
+ "timeShift": null,
+ "title": "Panel Title",
+ "tooltip": {
+ "shared": true,
+ "sort": 0,
+ "value_type": "individual"
+ },
+ "type": "graph",
+ "xaxis": {
+ "buckets": null,
+ "mode": "time",
+ "name": null,
+ "show": true,
+ "values": []
+ },
+ "yaxes": [
+ {
+ "format": "short",
+ "label": null,
+ "logBase": 1,
+ "max": null,
+ "min": null,
+ "show": true
+ },
+ {
+ "format": "short",
+ "label": null,
+ "logBase": 1,
+ "max": null,
+ "min": null,
+ "show": true
+ }
+ ],
+ "yaxis": {
+ "align": false,
+ "alignLevel": null
+ }
+ }
+ ],
+ "schemaVersion": 16,
+ "style": "dark",
+ "tags": [],
+ "templating": {
+ "list": []
+ },
+ "time": {
+ "from": "now-6h",
+ "to": "now"
+ },
+ "timepicker": {
+ "refresh_intervals": [
+ "5s",
+ "10s",
+ "30s",
+ "1m",
+ "5m",
+ "15m",
+ "30m",
+ "1h",
+ "2h",
+ "1d"
+ ],
+ "time_options": [
+ "5m",
+ "15m",
+ "1h",
+ "6h",
+ "12h",
+ "24h",
+ "2d",
+ "7d",
+ "30d"
+ ]
+ },
+ "timezone": "",
+ "title": "Panel tests - Slow Queries & Annotations",
+ "uid": "xtY_uCAiz",
+ "version": 11
+}
diff --git a/docker/blocks/apache_proxy/Dockerfile b/devenv/docker/blocks/apache_proxy/Dockerfile
similarity index 100%
rename from docker/blocks/apache_proxy/Dockerfile
rename to devenv/docker/blocks/apache_proxy/Dockerfile
diff --git a/docker/blocks/apache_proxy/docker-compose.yaml b/devenv/docker/blocks/apache_proxy/docker-compose.yaml
similarity index 88%
rename from docker/blocks/apache_proxy/docker-compose.yaml
rename to devenv/docker/blocks/apache_proxy/docker-compose.yaml
index 86d4befadd6..3791213f05a 100644
--- a/docker/blocks/apache_proxy/docker-compose.yaml
+++ b/devenv/docker/blocks/apache_proxy/docker-compose.yaml
@@ -5,5 +5,5 @@
# root_url = %(protocol)s://%(domain)s:10081/grafana/
apacheproxy:
- build: blocks/apache_proxy
+ build: docker/blocks/apache_proxy
network_mode: host
diff --git a/docker/blocks/apache_proxy/ports.conf b/devenv/docker/blocks/apache_proxy/ports.conf
similarity index 100%
rename from docker/blocks/apache_proxy/ports.conf
rename to devenv/docker/blocks/apache_proxy/ports.conf
diff --git a/docker/blocks/apache_proxy/proxy.conf b/devenv/docker/blocks/apache_proxy/proxy.conf
similarity index 100%
rename from docker/blocks/apache_proxy/proxy.conf
rename to devenv/docker/blocks/apache_proxy/proxy.conf
diff --git a/docker/blocks/collectd/Dockerfile b/devenv/docker/blocks/collectd/Dockerfile
similarity index 100%
rename from docker/blocks/collectd/Dockerfile
rename to devenv/docker/blocks/collectd/Dockerfile
diff --git a/docker/blocks/collectd/README.md b/devenv/docker/blocks/collectd/README.md
similarity index 100%
rename from docker/blocks/collectd/README.md
rename to devenv/docker/blocks/collectd/README.md
diff --git a/docker/blocks/collectd/collectd.conf.tpl b/devenv/docker/blocks/collectd/collectd.conf.tpl
similarity index 100%
rename from docker/blocks/collectd/collectd.conf.tpl
rename to devenv/docker/blocks/collectd/collectd.conf.tpl
diff --git a/docker/blocks/collectd/docker-compose.yaml b/devenv/docker/blocks/collectd/docker-compose.yaml
similarity index 87%
rename from docker/blocks/collectd/docker-compose.yaml
rename to devenv/docker/blocks/collectd/docker-compose.yaml
index c95827f7928..c5e189b58d8 100644
--- a/docker/blocks/collectd/docker-compose.yaml
+++ b/devenv/docker/blocks/collectd/docker-compose.yaml
@@ -1,5 +1,5 @@
collectd:
- build: blocks/collectd
+ build: docker/blocks/collectd
environment:
HOST_NAME: myserver
GRAPHITE_HOST: graphite
diff --git a/docker/blocks/collectd/etc_mtab b/devenv/docker/blocks/collectd/etc_mtab
similarity index 100%
rename from docker/blocks/collectd/etc_mtab
rename to devenv/docker/blocks/collectd/etc_mtab
diff --git a/docker/blocks/collectd/start_container b/devenv/docker/blocks/collectd/start_container
similarity index 100%
rename from docker/blocks/collectd/start_container
rename to devenv/docker/blocks/collectd/start_container
diff --git a/docker/blocks/elastic/docker-compose.yaml b/devenv/docker/blocks/elastic/docker-compose.yaml
similarity index 100%
rename from docker/blocks/elastic/docker-compose.yaml
rename to devenv/docker/blocks/elastic/docker-compose.yaml
diff --git a/docker/blocks/elastic/elasticsearch.yml b/devenv/docker/blocks/elastic/elasticsearch.yml
similarity index 100%
rename from docker/blocks/elastic/elasticsearch.yml
rename to devenv/docker/blocks/elastic/elasticsearch.yml
diff --git a/docker/blocks/elastic1/docker-compose.yaml b/devenv/docker/blocks/elastic1/docker-compose.yaml
similarity index 100%
rename from docker/blocks/elastic1/docker-compose.yaml
rename to devenv/docker/blocks/elastic1/docker-compose.yaml
diff --git a/docker/blocks/elastic1/elasticsearch.yml b/devenv/docker/blocks/elastic1/elasticsearch.yml
similarity index 100%
rename from docker/blocks/elastic1/elasticsearch.yml
rename to devenv/docker/blocks/elastic1/elasticsearch.yml
diff --git a/docker/blocks/elastic5/docker-compose.yaml b/devenv/docker/blocks/elastic5/docker-compose.yaml
similarity index 100%
rename from docker/blocks/elastic5/docker-compose.yaml
rename to devenv/docker/blocks/elastic5/docker-compose.yaml
diff --git a/docker/blocks/elastic5/elasticsearch.yml b/devenv/docker/blocks/elastic5/elasticsearch.yml
similarity index 100%
rename from docker/blocks/elastic5/elasticsearch.yml
rename to devenv/docker/blocks/elastic5/elasticsearch.yml
diff --git a/docker/blocks/elastic6/docker-compose.yaml b/devenv/docker/blocks/elastic6/docker-compose.yaml
similarity index 100%
rename from docker/blocks/elastic6/docker-compose.yaml
rename to devenv/docker/blocks/elastic6/docker-compose.yaml
diff --git a/docker/blocks/elastic6/elasticsearch.yml b/devenv/docker/blocks/elastic6/elasticsearch.yml
similarity index 100%
rename from docker/blocks/elastic6/elasticsearch.yml
rename to devenv/docker/blocks/elastic6/elasticsearch.yml
diff --git a/docker/blocks/graphite/Dockerfile b/devenv/docker/blocks/graphite/Dockerfile
similarity index 100%
rename from docker/blocks/graphite/Dockerfile
rename to devenv/docker/blocks/graphite/Dockerfile
diff --git a/docker/blocks/graphite/docker-compose.yaml b/devenv/docker/blocks/graphite/docker-compose.yaml
similarity index 89%
rename from docker/blocks/graphite/docker-compose.yaml
rename to devenv/docker/blocks/graphite/docker-compose.yaml
index 606e28638f7..acebd2bd9c0 100644
--- a/docker/blocks/graphite/docker-compose.yaml
+++ b/devenv/docker/blocks/graphite/docker-compose.yaml
@@ -1,5 +1,5 @@
graphite09:
- build: blocks/graphite
+ build: docker/blocks/graphite
ports:
- "8080:80"
- "2003:2003"
diff --git a/docker/blocks/graphite/files/carbon.conf b/devenv/docker/blocks/graphite/files/carbon.conf
similarity index 100%
rename from docker/blocks/graphite/files/carbon.conf
rename to devenv/docker/blocks/graphite/files/carbon.conf
diff --git a/docker/blocks/graphite/files/events_views.py b/devenv/docker/blocks/graphite/files/events_views.py
similarity index 100%
rename from docker/blocks/graphite/files/events_views.py
rename to devenv/docker/blocks/graphite/files/events_views.py
diff --git a/docker/blocks/graphite/files/initial_data.json b/devenv/docker/blocks/graphite/files/initial_data.json
similarity index 100%
rename from docker/blocks/graphite/files/initial_data.json
rename to devenv/docker/blocks/graphite/files/initial_data.json
diff --git a/docker/blocks/graphite/files/local_settings.py b/devenv/docker/blocks/graphite/files/local_settings.py
similarity index 100%
rename from docker/blocks/graphite/files/local_settings.py
rename to devenv/docker/blocks/graphite/files/local_settings.py
diff --git a/docker/blocks/graphite/files/my_htpasswd b/devenv/docker/blocks/graphite/files/my_htpasswd
similarity index 100%
rename from docker/blocks/graphite/files/my_htpasswd
rename to devenv/docker/blocks/graphite/files/my_htpasswd
diff --git a/docker/blocks/graphite/files/nginx.conf b/devenv/docker/blocks/graphite/files/nginx.conf
similarity index 100%
rename from docker/blocks/graphite/files/nginx.conf
rename to devenv/docker/blocks/graphite/files/nginx.conf
diff --git a/docker/blocks/graphite/files/statsd_config.js b/devenv/docker/blocks/graphite/files/statsd_config.js
similarity index 100%
rename from docker/blocks/graphite/files/statsd_config.js
rename to devenv/docker/blocks/graphite/files/statsd_config.js
diff --git a/docker/blocks/graphite/files/storage-aggregation.conf b/devenv/docker/blocks/graphite/files/storage-aggregation.conf
similarity index 100%
rename from docker/blocks/graphite/files/storage-aggregation.conf
rename to devenv/docker/blocks/graphite/files/storage-aggregation.conf
diff --git a/docker/blocks/graphite/files/storage-schemas.conf b/devenv/docker/blocks/graphite/files/storage-schemas.conf
similarity index 100%
rename from docker/blocks/graphite/files/storage-schemas.conf
rename to devenv/docker/blocks/graphite/files/storage-schemas.conf
diff --git a/docker/blocks/graphite/files/supervisord.conf b/devenv/docker/blocks/graphite/files/supervisord.conf
similarity index 100%
rename from docker/blocks/graphite/files/supervisord.conf
rename to devenv/docker/blocks/graphite/files/supervisord.conf
diff --git a/docker/blocks/graphite1/Dockerfile b/devenv/docker/blocks/graphite1/Dockerfile
similarity index 100%
rename from docker/blocks/graphite1/Dockerfile
rename to devenv/docker/blocks/graphite1/Dockerfile
diff --git a/docker/blocks/graphite1/big-dashboard.json b/devenv/docker/blocks/graphite1/big-dashboard.json
similarity index 100%
rename from docker/blocks/graphite1/big-dashboard.json
rename to devenv/docker/blocks/graphite1/big-dashboard.json
diff --git a/docker/blocks/graphite1/conf/etc/logrotate.d/graphite-statsd b/devenv/docker/blocks/graphite1/conf/etc/logrotate.d/graphite-statsd
similarity index 100%
rename from docker/blocks/graphite1/conf/etc/logrotate.d/graphite-statsd
rename to devenv/docker/blocks/graphite1/conf/etc/logrotate.d/graphite-statsd
diff --git a/docker/blocks/graphite1/conf/etc/my_init.d/01_conf_init.sh b/devenv/docker/blocks/graphite1/conf/etc/my_init.d/01_conf_init.sh
similarity index 100%
rename from docker/blocks/graphite1/conf/etc/my_init.d/01_conf_init.sh
rename to devenv/docker/blocks/graphite1/conf/etc/my_init.d/01_conf_init.sh
diff --git a/docker/blocks/graphite1/conf/etc/nginx/nginx.conf b/devenv/docker/blocks/graphite1/conf/etc/nginx/nginx.conf
similarity index 100%
rename from docker/blocks/graphite1/conf/etc/nginx/nginx.conf
rename to devenv/docker/blocks/graphite1/conf/etc/nginx/nginx.conf
diff --git a/docker/blocks/graphite1/conf/etc/nginx/sites-enabled/graphite-statsd.conf b/devenv/docker/blocks/graphite1/conf/etc/nginx/sites-enabled/graphite-statsd.conf
similarity index 100%
rename from docker/blocks/graphite1/conf/etc/nginx/sites-enabled/graphite-statsd.conf
rename to devenv/docker/blocks/graphite1/conf/etc/nginx/sites-enabled/graphite-statsd.conf
diff --git a/docker/blocks/graphite1/conf/etc/service/carbon-aggregator/run b/devenv/docker/blocks/graphite1/conf/etc/service/carbon-aggregator/run
similarity index 100%
rename from docker/blocks/graphite1/conf/etc/service/carbon-aggregator/run
rename to devenv/docker/blocks/graphite1/conf/etc/service/carbon-aggregator/run
diff --git a/docker/blocks/graphite1/conf/etc/service/carbon/run b/devenv/docker/blocks/graphite1/conf/etc/service/carbon/run
similarity index 100%
rename from docker/blocks/graphite1/conf/etc/service/carbon/run
rename to devenv/docker/blocks/graphite1/conf/etc/service/carbon/run
diff --git a/docker/blocks/graphite1/conf/etc/service/graphite/run b/devenv/docker/blocks/graphite1/conf/etc/service/graphite/run
similarity index 100%
rename from docker/blocks/graphite1/conf/etc/service/graphite/run
rename to devenv/docker/blocks/graphite1/conf/etc/service/graphite/run
diff --git a/docker/blocks/graphite1/conf/etc/service/nginx/run b/devenv/docker/blocks/graphite1/conf/etc/service/nginx/run
similarity index 100%
rename from docker/blocks/graphite1/conf/etc/service/nginx/run
rename to devenv/docker/blocks/graphite1/conf/etc/service/nginx/run
diff --git a/docker/blocks/graphite1/conf/etc/service/statsd/run b/devenv/docker/blocks/graphite1/conf/etc/service/statsd/run
similarity index 100%
rename from docker/blocks/graphite1/conf/etc/service/statsd/run
rename to devenv/docker/blocks/graphite1/conf/etc/service/statsd/run
diff --git a/docker/blocks/graphite1/conf/opt/graphite/conf/aggregation-rules.conf b/devenv/docker/blocks/graphite1/conf/opt/graphite/conf/aggregation-rules.conf
similarity index 96%
rename from docker/blocks/graphite1/conf/opt/graphite/conf/aggregation-rules.conf
rename to devenv/docker/blocks/graphite1/conf/opt/graphite/conf/aggregation-rules.conf
index c9520124a2a..792bbfd6857 100644
--- a/docker/blocks/graphite1/conf/opt/graphite/conf/aggregation-rules.conf
+++ b/devenv/docker/blocks/graphite1/conf/opt/graphite/conf/aggregation-rules.conf
@@ -8,7 +8,7 @@
# 'avg'. The name of the aggregate metric will be derived from
# 'output_template' filling in any captured fields from 'input_pattern'.
#
-# For example, if you're metric naming scheme is:
+# For example, if your metric naming scheme is:
#
# .applications...
#
diff --git a/docker/blocks/graphite1/conf/opt/graphite/conf/blacklist.conf b/devenv/docker/blocks/graphite1/conf/opt/graphite/conf/blacklist.conf
similarity index 100%
rename from docker/blocks/graphite1/conf/opt/graphite/conf/blacklist.conf
rename to devenv/docker/blocks/graphite1/conf/opt/graphite/conf/blacklist.conf
diff --git a/docker/blocks/graphite1/conf/opt/graphite/conf/carbon.amqp.conf b/devenv/docker/blocks/graphite1/conf/opt/graphite/conf/carbon.amqp.conf
similarity index 100%
rename from docker/blocks/graphite1/conf/opt/graphite/conf/carbon.amqp.conf
rename to devenv/docker/blocks/graphite1/conf/opt/graphite/conf/carbon.amqp.conf
diff --git a/docker/blocks/graphite1/conf/opt/graphite/conf/carbon.conf b/devenv/docker/blocks/graphite1/conf/opt/graphite/conf/carbon.conf
similarity index 100%
rename from docker/blocks/graphite1/conf/opt/graphite/conf/carbon.conf
rename to devenv/docker/blocks/graphite1/conf/opt/graphite/conf/carbon.conf
diff --git a/docker/blocks/graphite1/conf/opt/graphite/conf/dashboard.conf b/devenv/docker/blocks/graphite1/conf/opt/graphite/conf/dashboard.conf
similarity index 100%
rename from docker/blocks/graphite1/conf/opt/graphite/conf/dashboard.conf
rename to devenv/docker/blocks/graphite1/conf/opt/graphite/conf/dashboard.conf
diff --git a/docker/blocks/graphite1/conf/opt/graphite/conf/graphTemplates.conf b/devenv/docker/blocks/graphite1/conf/opt/graphite/conf/graphTemplates.conf
similarity index 100%
rename from docker/blocks/graphite1/conf/opt/graphite/conf/graphTemplates.conf
rename to devenv/docker/blocks/graphite1/conf/opt/graphite/conf/graphTemplates.conf
diff --git a/docker/blocks/graphite1/conf/opt/graphite/conf/relay-rules.conf b/devenv/docker/blocks/graphite1/conf/opt/graphite/conf/relay-rules.conf
similarity index 100%
rename from docker/blocks/graphite1/conf/opt/graphite/conf/relay-rules.conf
rename to devenv/docker/blocks/graphite1/conf/opt/graphite/conf/relay-rules.conf
diff --git a/docker/blocks/graphite1/conf/opt/graphite/conf/rewrite-rules.conf b/devenv/docker/blocks/graphite1/conf/opt/graphite/conf/rewrite-rules.conf
similarity index 100%
rename from docker/blocks/graphite1/conf/opt/graphite/conf/rewrite-rules.conf
rename to devenv/docker/blocks/graphite1/conf/opt/graphite/conf/rewrite-rules.conf
diff --git a/docker/blocks/graphite1/conf/opt/graphite/conf/storage-aggregation.conf b/devenv/docker/blocks/graphite1/conf/opt/graphite/conf/storage-aggregation.conf
similarity index 100%
rename from docker/blocks/graphite1/conf/opt/graphite/conf/storage-aggregation.conf
rename to devenv/docker/blocks/graphite1/conf/opt/graphite/conf/storage-aggregation.conf
diff --git a/docker/blocks/graphite1/conf/opt/graphite/conf/storage-schemas.conf b/devenv/docker/blocks/graphite1/conf/opt/graphite/conf/storage-schemas.conf
similarity index 100%
rename from docker/blocks/graphite1/conf/opt/graphite/conf/storage-schemas.conf
rename to devenv/docker/blocks/graphite1/conf/opt/graphite/conf/storage-schemas.conf
diff --git a/docker/blocks/graphite1/conf/opt/graphite/conf/whitelist.conf b/devenv/docker/blocks/graphite1/conf/opt/graphite/conf/whitelist.conf
similarity index 100%
rename from docker/blocks/graphite1/conf/opt/graphite/conf/whitelist.conf
rename to devenv/docker/blocks/graphite1/conf/opt/graphite/conf/whitelist.conf
diff --git a/docker/blocks/graphite1/conf/opt/graphite/webapp/graphite/app_settings.py b/devenv/docker/blocks/graphite1/conf/opt/graphite/webapp/graphite/app_settings.py
similarity index 100%
rename from docker/blocks/graphite1/conf/opt/graphite/webapp/graphite/app_settings.py
rename to devenv/docker/blocks/graphite1/conf/opt/graphite/webapp/graphite/app_settings.py
diff --git a/docker/blocks/graphite1/conf/opt/graphite/webapp/graphite/local_settings.py b/devenv/docker/blocks/graphite1/conf/opt/graphite/webapp/graphite/local_settings.py
similarity index 100%
rename from docker/blocks/graphite1/conf/opt/graphite/webapp/graphite/local_settings.py
rename to devenv/docker/blocks/graphite1/conf/opt/graphite/webapp/graphite/local_settings.py
diff --git a/docker/blocks/graphite1/conf/opt/statsd/config.js b/devenv/docker/blocks/graphite1/conf/opt/statsd/config.js
similarity index 100%
rename from docker/blocks/graphite1/conf/opt/statsd/config.js
rename to devenv/docker/blocks/graphite1/conf/opt/statsd/config.js
diff --git a/docker/blocks/graphite1/conf/usr/local/bin/django_admin_init.exp b/devenv/docker/blocks/graphite1/conf/usr/local/bin/django_admin_init.exp
similarity index 100%
rename from docker/blocks/graphite1/conf/usr/local/bin/django_admin_init.exp
rename to devenv/docker/blocks/graphite1/conf/usr/local/bin/django_admin_init.exp
diff --git a/docker/blocks/graphite1/conf/usr/local/bin/manage.sh b/devenv/docker/blocks/graphite1/conf/usr/local/bin/manage.sh
similarity index 100%
rename from docker/blocks/graphite1/conf/usr/local/bin/manage.sh
rename to devenv/docker/blocks/graphite1/conf/usr/local/bin/manage.sh
diff --git a/docker/blocks/graphite1/docker-compose.yaml b/devenv/docker/blocks/graphite1/docker-compose.yaml
similarity index 90%
rename from docker/blocks/graphite1/docker-compose.yaml
rename to devenv/docker/blocks/graphite1/docker-compose.yaml
index cd10593f423..1fa3e738ba8 100644
--- a/docker/blocks/graphite1/docker-compose.yaml
+++ b/devenv/docker/blocks/graphite1/docker-compose.yaml
@@ -1,6 +1,6 @@
graphite:
build:
- context: blocks/graphite1
+ context: docker/blocks/graphite1
args:
version: master
ports:
diff --git a/docker/blocks/graphite11/big-dashboard.json b/devenv/docker/blocks/graphite11/big-dashboard.json
similarity index 100%
rename from docker/blocks/graphite11/big-dashboard.json
rename to devenv/docker/blocks/graphite11/big-dashboard.json
diff --git a/docker/blocks/graphite11/docker-compose.yaml b/devenv/docker/blocks/graphite11/docker-compose.yaml
similarity index 100%
rename from docker/blocks/graphite11/docker-compose.yaml
rename to devenv/docker/blocks/graphite11/docker-compose.yaml
diff --git a/docker/blocks/influxdb/docker-compose.yaml b/devenv/docker/blocks/influxdb/docker-compose.yaml
similarity index 80%
rename from docker/blocks/influxdb/docker-compose.yaml
rename to devenv/docker/blocks/influxdb/docker-compose.yaml
index 3434f5d09b9..e1727807d41 100644
--- a/docker/blocks/influxdb/docker-compose.yaml
+++ b/devenv/docker/blocks/influxdb/docker-compose.yaml
@@ -6,7 +6,7 @@
- "8083:8083"
- "8086:8086"
volumes:
- - ./blocks/influxdb/influxdb.conf:/etc/influxdb/influxdb.conf
+ - ./docker/blocks/influxdb/influxdb.conf:/etc/influxdb/influxdb.conf
fake-influxdb-data:
image: grafana/fake-data-gen
diff --git a/docker/blocks/influxdb/influxdb.conf b/devenv/docker/blocks/influxdb/influxdb.conf
similarity index 100%
rename from docker/blocks/influxdb/influxdb.conf
rename to devenv/docker/blocks/influxdb/influxdb.conf
diff --git a/docker/blocks/jaeger/docker-compose.yaml b/devenv/docker/blocks/jaeger/docker-compose.yaml
similarity index 100%
rename from docker/blocks/jaeger/docker-compose.yaml
rename to devenv/docker/blocks/jaeger/docker-compose.yaml
diff --git a/docker/blocks/memcached/docker-compose.yaml b/devenv/docker/blocks/memcached/docker-compose.yaml
similarity index 100%
rename from docker/blocks/memcached/docker-compose.yaml
rename to devenv/docker/blocks/memcached/docker-compose.yaml
diff --git a/docker/blocks/mssql/build/Dockerfile b/devenv/docker/blocks/mssql/build/Dockerfile
similarity index 100%
rename from docker/blocks/mssql/build/Dockerfile
rename to devenv/docker/blocks/mssql/build/Dockerfile
diff --git a/docker/blocks/mssql/build/entrypoint.sh b/devenv/docker/blocks/mssql/build/entrypoint.sh
similarity index 100%
rename from docker/blocks/mssql/build/entrypoint.sh
rename to devenv/docker/blocks/mssql/build/entrypoint.sh
diff --git a/docker/blocks/mssql/build/setup.sh b/devenv/docker/blocks/mssql/build/setup.sh
similarity index 100%
rename from docker/blocks/mssql/build/setup.sh
rename to devenv/docker/blocks/mssql/build/setup.sh
diff --git a/docker/blocks/mssql/build/setup.sql.template b/devenv/docker/blocks/mssql/build/setup.sql.template
similarity index 100%
rename from docker/blocks/mssql/build/setup.sql.template
rename to devenv/docker/blocks/mssql/build/setup.sql.template
diff --git a/docker/blocks/mssql/docker-compose.yaml b/devenv/docker/blocks/mssql/docker-compose.yaml
similarity index 85%
rename from docker/blocks/mssql/docker-compose.yaml
rename to devenv/docker/blocks/mssql/docker-compose.yaml
index a346fb791f7..05a93629e73 100644
--- a/docker/blocks/mssql/docker-compose.yaml
+++ b/devenv/docker/blocks/mssql/docker-compose.yaml
@@ -1,6 +1,6 @@
mssql:
build:
- context: blocks/mssql/build
+ context: docker/blocks/mssql/build
environment:
ACCEPT_EULA: Y
MSSQL_SA_PASSWORD: Password!
diff --git a/docker/blocks/mssql_tests/docker-compose.yaml b/devenv/docker/blocks/mssql_tests/docker-compose.yaml
similarity index 79%
rename from docker/blocks/mssql_tests/docker-compose.yaml
rename to devenv/docker/blocks/mssql_tests/docker-compose.yaml
index 5da6aad82af..eea4d1e3561 100644
--- a/docker/blocks/mssql_tests/docker-compose.yaml
+++ b/devenv/docker/blocks/mssql_tests/docker-compose.yaml
@@ -1,6 +1,6 @@
mssqltests:
build:
- context: blocks/mssql/build
+ context: docker/blocks/mssql/build
environment:
ACCEPT_EULA: Y
MSSQL_SA_PASSWORD: Password!
diff --git a/docker/blocks/mysql/config b/devenv/docker/blocks/mysql/config
similarity index 100%
rename from docker/blocks/mysql/config
rename to devenv/docker/blocks/mysql/config
diff --git a/docker/blocks/mysql/docker-compose.yaml b/devenv/docker/blocks/mysql/docker-compose.yaml
similarity index 100%
rename from docker/blocks/mysql/docker-compose.yaml
rename to devenv/docker/blocks/mysql/docker-compose.yaml
diff --git a/docker/blocks/mysql_opendata/Dockerfile b/devenv/docker/blocks/mysql_opendata/Dockerfile
similarity index 100%
rename from docker/blocks/mysql_opendata/Dockerfile
rename to devenv/docker/blocks/mysql_opendata/Dockerfile
diff --git a/docker/blocks/mysql_opendata/docker-compose.yaml b/devenv/docker/blocks/mysql_opendata/docker-compose.yaml
similarity index 82%
rename from docker/blocks/mysql_opendata/docker-compose.yaml
rename to devenv/docker/blocks/mysql_opendata/docker-compose.yaml
index 594eeed284a..4d478ee0860 100644
--- a/docker/blocks/mysql_opendata/docker-compose.yaml
+++ b/devenv/docker/blocks/mysql_opendata/docker-compose.yaml
@@ -1,5 +1,5 @@
mysql_opendata:
- build: blocks/mysql_opendata
+ build: docker/blocks/mysql_opendata
environment:
MYSQL_ROOT_PASSWORD: rootpass
MYSQL_DATABASE: testdata
diff --git a/docker/blocks/mysql_opendata/import_csv.sql b/devenv/docker/blocks/mysql_opendata/import_csv.sql
similarity index 100%
rename from docker/blocks/mysql_opendata/import_csv.sql
rename to devenv/docker/blocks/mysql_opendata/import_csv.sql
diff --git a/docker/blocks/mysql_tests/Dockerfile b/devenv/docker/blocks/mysql_tests/Dockerfile
similarity index 100%
rename from docker/blocks/mysql_tests/Dockerfile
rename to devenv/docker/blocks/mysql_tests/Dockerfile
diff --git a/docker/blocks/mysql_tests/docker-compose.yaml b/devenv/docker/blocks/mysql_tests/docker-compose.yaml
similarity index 84%
rename from docker/blocks/mysql_tests/docker-compose.yaml
rename to devenv/docker/blocks/mysql_tests/docker-compose.yaml
index 035a6167017..a7509d47880 100644
--- a/docker/blocks/mysql_tests/docker-compose.yaml
+++ b/devenv/docker/blocks/mysql_tests/docker-compose.yaml
@@ -1,6 +1,6 @@
mysqltests:
build:
- context: blocks/mysql_tests
+ context: docker/blocks/mysql_tests
environment:
MYSQL_ROOT_PASSWORD: rootpass
MYSQL_DATABASE: grafana_tests
diff --git a/docker/blocks/mysql_tests/setup.sql b/devenv/docker/blocks/mysql_tests/setup.sql
similarity index 100%
rename from docker/blocks/mysql_tests/setup.sql
rename to devenv/docker/blocks/mysql_tests/setup.sql
diff --git a/docker/blocks/nginx_proxy/Dockerfile b/devenv/docker/blocks/nginx_proxy/Dockerfile
similarity index 100%
rename from docker/blocks/nginx_proxy/Dockerfile
rename to devenv/docker/blocks/nginx_proxy/Dockerfile
diff --git a/docker/blocks/nginx_proxy/docker-compose.yaml b/devenv/docker/blocks/nginx_proxy/docker-compose.yaml
similarity index 88%
rename from docker/blocks/nginx_proxy/docker-compose.yaml
rename to devenv/docker/blocks/nginx_proxy/docker-compose.yaml
index a0ceceb83ac..aefd7226f36 100644
--- a/docker/blocks/nginx_proxy/docker-compose.yaml
+++ b/devenv/docker/blocks/nginx_proxy/docker-compose.yaml
@@ -5,5 +5,5 @@
# root_url = %(protocol)s://%(domain)s:10080/grafana/
nginxproxy:
- build: blocks/nginx_proxy
+ build: docker/blocks/nginx_proxy
network_mode: host
diff --git a/docker/blocks/nginx_proxy/htpasswd b/devenv/docker/blocks/nginx_proxy/htpasswd
similarity index 100%
rename from docker/blocks/nginx_proxy/htpasswd
rename to devenv/docker/blocks/nginx_proxy/htpasswd
diff --git a/docker/blocks/nginx_proxy/nginx.conf b/devenv/docker/blocks/nginx_proxy/nginx.conf
similarity index 100%
rename from docker/blocks/nginx_proxy/nginx.conf
rename to devenv/docker/blocks/nginx_proxy/nginx.conf
diff --git a/docker/blocks/openldap/Dockerfile b/devenv/docker/blocks/openldap/Dockerfile
similarity index 100%
rename from docker/blocks/openldap/Dockerfile
rename to devenv/docker/blocks/openldap/Dockerfile
diff --git a/docker/blocks/openldap/docker-compose.yaml b/devenv/docker/blocks/openldap/docker-compose.yaml
similarity index 82%
rename from docker/blocks/openldap/docker-compose.yaml
rename to devenv/docker/blocks/openldap/docker-compose.yaml
index be06524a57d..d11858ccfb9 100644
--- a/docker/blocks/openldap/docker-compose.yaml
+++ b/devenv/docker/blocks/openldap/docker-compose.yaml
@@ -1,5 +1,5 @@
openldap:
- build: blocks/openldap
+ build: docker/blocks/openldap
environment:
SLAPD_PASSWORD: grafana
SLAPD_DOMAIN: grafana.org
diff --git a/docker/blocks/openldap/entrypoint.sh b/devenv/docker/blocks/openldap/entrypoint.sh
similarity index 100%
rename from docker/blocks/openldap/entrypoint.sh
rename to devenv/docker/blocks/openldap/entrypoint.sh
diff --git a/docker/blocks/openldap/ldap_dev.toml b/devenv/docker/blocks/openldap/ldap_dev.toml
similarity index 99%
rename from docker/blocks/openldap/ldap_dev.toml
rename to devenv/docker/blocks/openldap/ldap_dev.toml
index e79771b57de..8767ff3c64a 100644
--- a/docker/blocks/openldap/ldap_dev.toml
+++ b/devenv/docker/blocks/openldap/ldap_dev.toml
@@ -72,6 +72,7 @@ email = "email"
[[servers.group_mappings]]
group_dn = "cn=admins,ou=groups,dc=grafana,dc=org"
org_role = "Admin"
+grafana_admin = true
# The Grafana organization database id, optional, if left out the default org (id 1) will be used
# org_id = 1
diff --git a/docker/blocks/openldap/modules/memberof.ldif b/devenv/docker/blocks/openldap/modules/memberof.ldif
similarity index 100%
rename from docker/blocks/openldap/modules/memberof.ldif
rename to devenv/docker/blocks/openldap/modules/memberof.ldif
diff --git a/docker/blocks/openldap/notes.md b/devenv/docker/blocks/openldap/notes.md
similarity index 100%
rename from docker/blocks/openldap/notes.md
rename to devenv/docker/blocks/openldap/notes.md
diff --git a/docker/blocks/openldap/prepopulate.sh b/devenv/docker/blocks/openldap/prepopulate.sh
similarity index 100%
rename from docker/blocks/openldap/prepopulate.sh
rename to devenv/docker/blocks/openldap/prepopulate.sh
diff --git a/docker/blocks/openldap/prepopulate/1_units.ldif b/devenv/docker/blocks/openldap/prepopulate/1_units.ldif
similarity index 100%
rename from docker/blocks/openldap/prepopulate/1_units.ldif
rename to devenv/docker/blocks/openldap/prepopulate/1_units.ldif
diff --git a/docker/blocks/openldap/prepopulate/2_users.ldif b/devenv/docker/blocks/openldap/prepopulate/2_users.ldif
similarity index 100%
rename from docker/blocks/openldap/prepopulate/2_users.ldif
rename to devenv/docker/blocks/openldap/prepopulate/2_users.ldif
diff --git a/docker/blocks/openldap/prepopulate/3_groups.ldif b/devenv/docker/blocks/openldap/prepopulate/3_groups.ldif
similarity index 100%
rename from docker/blocks/openldap/prepopulate/3_groups.ldif
rename to devenv/docker/blocks/openldap/prepopulate/3_groups.ldif
diff --git a/docker/blocks/opentsdb/docker-compose.yaml b/devenv/docker/blocks/opentsdb/docker-compose.yaml
similarity index 100%
rename from docker/blocks/opentsdb/docker-compose.yaml
rename to devenv/docker/blocks/opentsdb/docker-compose.yaml
diff --git a/docker/blocks/postgres/docker-compose.yaml b/devenv/docker/blocks/postgres/docker-compose.yaml
similarity index 100%
rename from docker/blocks/postgres/docker-compose.yaml
rename to devenv/docker/blocks/postgres/docker-compose.yaml
diff --git a/docker/blocks/postgres_tests/Dockerfile b/devenv/docker/blocks/postgres_tests/Dockerfile
similarity index 100%
rename from docker/blocks/postgres_tests/Dockerfile
rename to devenv/docker/blocks/postgres_tests/Dockerfile
diff --git a/docker/blocks/postgres_tests/docker-compose.yaml b/devenv/docker/blocks/postgres_tests/docker-compose.yaml
similarity index 63%
rename from docker/blocks/postgres_tests/docker-compose.yaml
rename to devenv/docker/blocks/postgres_tests/docker-compose.yaml
index f5ce0a5a3d3..7e6da7d8517 100644
--- a/docker/blocks/postgres_tests/docker-compose.yaml
+++ b/devenv/docker/blocks/postgres_tests/docker-compose.yaml
@@ -1,6 +1,6 @@
postgrestest:
build:
- context: blocks/postgres_tests
+ context: docker/blocks/postgres_tests
environment:
POSTGRES_USER: grafanatest
POSTGRES_PASSWORD: grafanatest
diff --git a/docker/blocks/postgres_tests/setup.sql b/devenv/docker/blocks/postgres_tests/setup.sql
similarity index 100%
rename from docker/blocks/postgres_tests/setup.sql
rename to devenv/docker/blocks/postgres_tests/setup.sql
diff --git a/docker/blocks/prometheus/Dockerfile b/devenv/docker/blocks/prometheus/Dockerfile
similarity index 100%
rename from docker/blocks/prometheus/Dockerfile
rename to devenv/docker/blocks/prometheus/Dockerfile
diff --git a/docker/blocks/prometheus/alert.rules b/devenv/docker/blocks/prometheus/alert.rules
similarity index 100%
rename from docker/blocks/prometheus/alert.rules
rename to devenv/docker/blocks/prometheus/alert.rules
diff --git a/docker/blocks/prometheus2/docker-compose.yaml b/devenv/docker/blocks/prometheus/docker-compose.yaml
similarity index 86%
rename from docker/blocks/prometheus2/docker-compose.yaml
rename to devenv/docker/blocks/prometheus/docker-compose.yaml
index 589df868084..db778060dde 100644
--- a/docker/blocks/prometheus2/docker-compose.yaml
+++ b/devenv/docker/blocks/prometheus/docker-compose.yaml
@@ -1,5 +1,5 @@
prometheus:
- build: blocks/prometheus2
+ build: docker/blocks/prometheus
network_mode: host
ports:
- "9090:9090"
@@ -25,7 +25,7 @@
- "9093:9093"
prometheus-random-data:
- build: blocks/prometheus_random_data
+ build: docker/blocks/prometheus_random_data
network_mode: host
ports:
- "8081:8080"
diff --git a/docker/blocks/prometheus/prometheus.yml b/devenv/docker/blocks/prometheus/prometheus.yml
similarity index 100%
rename from docker/blocks/prometheus/prometheus.yml
rename to devenv/docker/blocks/prometheus/prometheus.yml
diff --git a/docker/blocks/prometheus2/Dockerfile b/devenv/docker/blocks/prometheus2/Dockerfile
similarity index 100%
rename from docker/blocks/prometheus2/Dockerfile
rename to devenv/docker/blocks/prometheus2/Dockerfile
diff --git a/docker/blocks/prometheus2/alert.rules b/devenv/docker/blocks/prometheus2/alert.rules
similarity index 100%
rename from docker/blocks/prometheus2/alert.rules
rename to devenv/docker/blocks/prometheus2/alert.rules
diff --git a/docker/blocks/prometheus/docker-compose.yaml b/devenv/docker/blocks/prometheus2/docker-compose.yaml
similarity index 85%
rename from docker/blocks/prometheus/docker-compose.yaml
rename to devenv/docker/blocks/prometheus2/docker-compose.yaml
index 3c304cc74ad..d586b4b5742 100644
--- a/docker/blocks/prometheus/docker-compose.yaml
+++ b/devenv/docker/blocks/prometheus2/docker-compose.yaml
@@ -1,5 +1,5 @@
prometheus:
- build: blocks/prometheus
+ build: docker/blocks/prometheus2
network_mode: host
ports:
- "9090:9090"
@@ -25,7 +25,7 @@
- "9093:9093"
prometheus-random-data:
- build: blocks/prometheus_random_data
+ build: docker/blocks/prometheus_random_data
network_mode: host
ports:
- "8081:8080"
diff --git a/docker/blocks/prometheus2/prometheus.yml b/devenv/docker/blocks/prometheus2/prometheus.yml
similarity index 100%
rename from docker/blocks/prometheus2/prometheus.yml
rename to devenv/docker/blocks/prometheus2/prometheus.yml
diff --git a/docker/blocks/prometheus_mac/Dockerfile b/devenv/docker/blocks/prometheus_mac/Dockerfile
similarity index 100%
rename from docker/blocks/prometheus_mac/Dockerfile
rename to devenv/docker/blocks/prometheus_mac/Dockerfile
diff --git a/docker/blocks/prometheus_mac/alert.rules b/devenv/docker/blocks/prometheus_mac/alert.rules
similarity index 100%
rename from docker/blocks/prometheus_mac/alert.rules
rename to devenv/docker/blocks/prometheus_mac/alert.rules
diff --git a/docker/blocks/prometheus_mac/docker-compose.yaml b/devenv/docker/blocks/prometheus_mac/docker-compose.yaml
similarity index 82%
rename from docker/blocks/prometheus_mac/docker-compose.yaml
rename to devenv/docker/blocks/prometheus_mac/docker-compose.yaml
index ef53b07418a..b73d278fae2 100644
--- a/docker/blocks/prometheus_mac/docker-compose.yaml
+++ b/devenv/docker/blocks/prometheus_mac/docker-compose.yaml
@@ -1,5 +1,5 @@
prometheus:
- build: blocks/prometheus_mac
+ build: docker/blocks/prometheus_mac
ports:
- "9090:9090"
@@ -21,6 +21,6 @@
- "9093:9093"
prometheus-random-data:
- build: blocks/prometheus_random_data
+ build: docker/blocks/prometheus_random_data
ports:
- "8081:8080"
diff --git a/docker/blocks/prometheus_mac/prometheus.yml b/devenv/docker/blocks/prometheus_mac/prometheus.yml
similarity index 100%
rename from docker/blocks/prometheus_mac/prometheus.yml
rename to devenv/docker/blocks/prometheus_mac/prometheus.yml
diff --git a/docker/blocks/prometheus_random_data/Dockerfile b/devenv/docker/blocks/prometheus_random_data/Dockerfile
similarity index 100%
rename from docker/blocks/prometheus_random_data/Dockerfile
rename to devenv/docker/blocks/prometheus_random_data/Dockerfile
diff --git a/docker/blocks/smtp/Dockerfile b/devenv/docker/blocks/smtp/Dockerfile
similarity index 100%
rename from docker/blocks/smtp/Dockerfile
rename to devenv/docker/blocks/smtp/Dockerfile
diff --git a/docker/blocks/smtp/bootstrap.sh b/devenv/docker/blocks/smtp/bootstrap.sh
similarity index 100%
rename from docker/blocks/smtp/bootstrap.sh
rename to devenv/docker/blocks/smtp/bootstrap.sh
diff --git a/docker/blocks/smtp/docker-compose.yaml b/devenv/docker/blocks/smtp/docker-compose.yaml
similarity index 100%
rename from docker/blocks/smtp/docker-compose.yaml
rename to devenv/docker/blocks/smtp/docker-compose.yaml
diff --git a/docker/buildcontainer/Dockerfile b/devenv/docker/buildcontainer/Dockerfile
similarity index 100%
rename from docker/buildcontainer/Dockerfile
rename to devenv/docker/buildcontainer/Dockerfile
diff --git a/docker/buildcontainer/build.sh b/devenv/docker/buildcontainer/build.sh
similarity index 100%
rename from docker/buildcontainer/build.sh
rename to devenv/docker/buildcontainer/build.sh
diff --git a/docker/buildcontainer/build_circle.sh b/devenv/docker/buildcontainer/build_circle.sh
similarity index 100%
rename from docker/buildcontainer/build_circle.sh
rename to devenv/docker/buildcontainer/build_circle.sh
diff --git a/docker/buildcontainer/run_circle.sh b/devenv/docker/buildcontainer/run_circle.sh
similarity index 100%
rename from docker/buildcontainer/run_circle.sh
rename to devenv/docker/buildcontainer/run_circle.sh
diff --git a/docker/compose_header.yml b/devenv/docker/compose_header.yml
similarity index 100%
rename from docker/compose_header.yml
rename to devenv/docker/compose_header.yml
diff --git a/docker/debtest/Dockerfile b/devenv/docker/debtest/Dockerfile
similarity index 100%
rename from docker/debtest/Dockerfile
rename to devenv/docker/debtest/Dockerfile
diff --git a/docker/debtest/build.sh b/devenv/docker/debtest/build.sh
similarity index 100%
rename from docker/debtest/build.sh
rename to devenv/docker/debtest/build.sh
diff --git a/devenv/docker/ha_test/.gitignore b/devenv/docker/ha_test/.gitignore
new file mode 100644
index 00000000000..0f4e139e204
--- /dev/null
+++ b/devenv/docker/ha_test/.gitignore
@@ -0,0 +1 @@
+grafana/provisioning/dashboards/alerts/alert-*
\ No newline at end of file
diff --git a/devenv/docker/ha_test/README.md b/devenv/docker/ha_test/README.md
new file mode 100644
index 00000000000..bc93727ceae
--- /dev/null
+++ b/devenv/docker/ha_test/README.md
@@ -0,0 +1,137 @@
+# Grafana High Availability (HA) test setup
+
+A set of docker compose services which together creates a Grafana HA test setup with capability of easily
+scaling up/down number of Grafana instances.
+
+Included services
+
+* Grafana
+* Mysql - Grafana configuration database and session storage
+* Prometheus - Monitoring of Grafana and used as datasource of provisioned alert rules
+* Nginx - Reverse proxy for Grafana and Prometheus. Enables browsing Grafana/Prometheus UI using a hostname
+
+## Prerequisites
+
+### Build grafana docker container
+
+Build a Grafana docker container from current branch and commit and tag it as grafana/grafana:dev.
+
+```bash
+$ cd
+$ make build-docker-full
+```
+
+### Virtual host names
+
+#### Alternative 1 - Use dnsmasq
+
+```bash
+$ sudo apt-get install dnsmasq
+$ echo 'address=/loc/127.0.0.1' | sudo tee /etc/dnsmasq.d/dnsmasq-loc.conf > /dev/null
+$ sudo /etc/init.d/dnsmasq restart
+$ ping whatever.loc
+PING whatever.loc (127.0.0.1) 56(84) bytes of data.
+64 bytes from localhost (127.0.0.1): icmp_seq=1 ttl=64 time=0.076 ms
+--- whatever.loc ping statistics ---
+1 packet transmitted, 1 received, 0% packet loss, time 1998ms
+```
+
+#### Alternative 2 - Manually update /etc/hosts
+
+Update your `/etc/hosts` to be able to access Grafana and/or Prometheus UI using a hostname.
+
+```bash
+$ cat /etc/hosts
+127.0.0.1 grafana.loc
+127.0.0.1 prometheus.loc
+```
+
+## Start services
+
+```bash
+$ docker-compose up -d
+```
+
+Browse
+* http://grafana.loc/
+* http://prometheus.loc/
+
+Check for any errors
+
+```bash
+$ docker-compose logs | grep error
+```
+
+### Scale Grafana instances up/down
+
+Scale number of Grafana instances to ``
+
+```bash
+$ docker-compose up --scale grafana= -d
+# for example 3 instances
+$ docker-compose up --scale grafana=3 -d
+```
+
+## Test alerting
+
+### Create notification channels
+
+Creates default notification channels, if not already exists
+
+```bash
+$ ./alerts.sh setup
+```
+
+### Slack notifications
+
+Disable
+
+```bash
+$ ./alerts.sh slack -d
+```
+
+Enable and configure url
+
+```bash
+$ ./alerts.sh slack -u https://hooks.slack.com/services/...
+```
+
+Enable, configure url and enable reminders
+
+```bash
+$ ./alerts.sh slack -u https://hooks.slack.com/services/... -r -e 10m
+```
+
+### Provision alert dashboards with alert rules
+
+Provision 1 dashboard/alert rule (default)
+
+```bash
+$ ./alerts.sh provision
+```
+
+Provision 10 dashboards/alert rules
+
+```bash
+$ ./alerts.sh provision -a 10
+```
+
+Provision 10 dashboards/alert rules and change condition to `gt > 100`
+
+```bash
+$ ./alerts.sh provision -a 10 -c 100
+```
+
+### Pause/unpause all alert rules
+
+Pause
+
+```bash
+$ ./alerts.sh pause
+```
+
+Unpause
+
+```bash
+$ ./alerts.sh unpause
+```
diff --git a/devenv/docker/ha_test/alerts.sh b/devenv/docker/ha_test/alerts.sh
new file mode 100755
index 00000000000..a05a4581739
--- /dev/null
+++ b/devenv/docker/ha_test/alerts.sh
@@ -0,0 +1,156 @@
+#!/bin/bash
+
+requiresJsonnet() {
+ if ! type "jsonnet" > /dev/null; then
+ echo "you need you install jsonnet to run this script"
+ echo "follow the instructions on https://github.com/google/jsonnet"
+ exit 1
+ fi
+}
+
+setup() {
+ STATUS=$(curl -s -o /dev/null -w '%{http_code}' http://admin:admin@grafana.loc/api/alert-notifications/1)
+ if [ $STATUS -eq 200 ]; then
+ echo "Email already exists, skipping..."
+ else
+ curl -H "Content-Type: application/json" \
+ -d '{
+ "name": "Email",
+ "type": "email",
+ "isDefault": false,
+ "sendReminder": false,
+ "uploadImage": true,
+ "settings": {
+ "addresses": "user@test.com"
+ }
+ }' \
+ http://admin:admin@grafana.loc/api/alert-notifications
+ fi
+
+ STATUS=$(curl -s -o /dev/null -w '%{http_code}' http://admin:admin@grafana.loc/api/alert-notifications/2)
+ if [ $STATUS -eq 200 ]; then
+ echo "Slack already exists, skipping..."
+ else
+ curl -H "Content-Type: application/json" \
+ -d '{
+ "name": "Slack",
+ "type": "slack",
+ "isDefault": false,
+ "sendReminder": false,
+ "uploadImage": true
+ }' \
+ http://admin:admin@grafana.loc/api/alert-notifications
+ fi
+}
+
+slack() {
+ enabled=true
+ url=''
+ remind=false
+ remindEvery='10m'
+
+ while getopts ":e:u:dr" o; do
+ case "${o}" in
+ e)
+ remindEvery=${OPTARG}
+ ;;
+ u)
+ url=${OPTARG}
+ ;;
+ d)
+ enabled=false
+ ;;
+ r)
+ remind=true
+ ;;
+ esac
+ done
+ shift $((OPTIND-1))
+
+ curl -X PUT \
+ -H "Content-Type: application/json" \
+ -d '{
+ "id": 2,
+ "name": "Slack",
+ "type": "slack",
+ "isDefault": '$enabled',
+ "sendReminder": '$remind',
+ "frequency": "'$remindEvery'",
+ "uploadImage": true,
+ "settings": {
+ "url": "'$url'"
+ }
+ }' \
+ http://admin:admin@grafana.loc/api/alert-notifications/2
+}
+
+provision() {
+ alerts=1
+ condition=65
+ while getopts ":a:c:" o; do
+ case "${o}" in
+ a)
+ alerts=${OPTARG}
+ ;;
+ c)
+ condition=${OPTARG}
+ ;;
+ esac
+ done
+ shift $((OPTIND-1))
+
+ requiresJsonnet
+
+ rm -rf grafana/provisioning/dashboards/alerts/alert-*.json
+ jsonnet -m grafana/provisioning/dashboards/alerts grafana/provisioning/alerts.jsonnet --ext-code alerts=$alerts --ext-code condition=$condition
+}
+
+pause() {
+ curl -H "Content-Type: application/json" \
+ -d '{"paused":true}' \
+ http://admin:admin@grafana.loc/api/admin/pause-all-alerts
+}
+
+unpause() {
+ curl -H "Content-Type: application/json" \
+ -d '{"paused":false}' \
+ http://admin:admin@grafana.loc/api/admin/pause-all-alerts
+}
+
+usage() {
+ echo -e "Usage: ./alerts.sh COMMAND [OPTIONS]\n"
+ echo -e "Commands"
+ echo -e " setup\t\t creates default alert notification channels"
+ echo -e " slack\t\t configure slack notification channel"
+ echo -e " [-d]\t\t\t disable notifier, default enabled"
+ echo -e " [-u]\t\t\t url"
+ echo -e " [-r]\t\t\t send reminders"
+ echo -e " [-e ]\t\t default 10m\n"
+ echo -e " provision\t provision alerts"
+ echo -e " [-a ]\t default 1"
+ echo -e " [-c ]\t default 65\n"
+ echo -e " pause\t\t pause all alerts"
+ echo -e " unpause\t unpause all alerts"
+}
+
+main() {
+ local cmd=$1
+
+ if [[ $cmd == "setup" ]]; then
+ setup
+ elif [[ $cmd == "slack" ]]; then
+ slack "${@:2}"
+ elif [[ $cmd == "provision" ]]; then
+ provision "${@:2}"
+ elif [[ $cmd == "pause" ]]; then
+ pause
+ elif [[ $cmd == "unpause" ]]; then
+ unpause
+ fi
+
+ if [[ -z "$cmd" ]]; then
+ usage
+ fi
+}
+
+main "$@"
diff --git a/devenv/docker/ha_test/docker-compose.yaml b/devenv/docker/ha_test/docker-compose.yaml
new file mode 100644
index 00000000000..ce8630d88a4
--- /dev/null
+++ b/devenv/docker/ha_test/docker-compose.yaml
@@ -0,0 +1,78 @@
+version: "2.1"
+
+services:
+ nginx-proxy:
+ image: jwilder/nginx-proxy
+ ports:
+ - "80:80"
+ volumes:
+ - /var/run/docker.sock:/tmp/docker.sock:ro
+
+ db:
+ image: mysql
+ environment:
+ MYSQL_ROOT_PASSWORD: rootpass
+ MYSQL_DATABASE: grafana
+ MYSQL_USER: grafana
+ MYSQL_PASSWORD: password
+ ports:
+ - 3306
+ healthcheck:
+ test: ["CMD", "mysqladmin" ,"ping", "-h", "localhost"]
+ timeout: 10s
+ retries: 10
+
+ # db:
+ # image: postgres:9.3
+ # environment:
+ # POSTGRES_DATABASE: grafana
+ # POSTGRES_USER: grafana
+ # POSTGRES_PASSWORD: password
+ # ports:
+ # - 5432
+ # healthcheck:
+ # test: ["CMD-SHELL", "pg_isready -d grafana -U grafana"]
+ # timeout: 10s
+ # retries: 10
+
+ grafana:
+ image: grafana/grafana:dev
+ volumes:
+ - ./grafana/provisioning/:/etc/grafana/provisioning/
+ environment:
+ - VIRTUAL_HOST=grafana.loc
+ - GF_SERVER_ROOT_URL=http://grafana.loc
+ - GF_DATABASE_NAME=grafana
+ - GF_DATABASE_USER=grafana
+ - GF_DATABASE_PASSWORD=password
+ - GF_DATABASE_TYPE=mysql
+ - GF_DATABASE_HOST=db:3306
+ - GF_SESSION_PROVIDER=mysql
+ - GF_SESSION_PROVIDER_CONFIG=grafana:password@tcp(db:3306)/grafana?allowNativePasswords=true
+ # - GF_DATABASE_TYPE=postgres
+ # - GF_DATABASE_HOST=db:5432
+ # - GF_DATABASE_SSL_MODE=disable
+ # - GF_SESSION_PROVIDER=postgres
+ # - GF_SESSION_PROVIDER_CONFIG=user=grafana password=password host=db port=5432 dbname=grafana sslmode=disable
+ - GF_LOG_FILTERS=alerting.notifier:debug,alerting.notifier.slack:debug
+ ports:
+ - 3000
+ depends_on:
+ db:
+ condition: service_healthy
+
+ prometheus:
+ image: prom/prometheus:v2.4.2
+ volumes:
+ - ./prometheus/:/etc/prometheus/
+ environment:
+ - VIRTUAL_HOST=prometheus.loc
+ ports:
+ - 9090
+
+ # mysqld-exporter:
+ # image: prom/mysqld-exporter
+ # environment:
+ # - DATA_SOURCE_NAME=grafana:password@(mysql:3306)/
+ # ports:
+ # - 9104
diff --git a/devenv/docker/ha_test/grafana/provisioning/alerts.jsonnet b/devenv/docker/ha_test/grafana/provisioning/alerts.jsonnet
new file mode 100644
index 00000000000..86ded7e79d6
--- /dev/null
+++ b/devenv/docker/ha_test/grafana/provisioning/alerts.jsonnet
@@ -0,0 +1,202 @@
+local numAlerts = std.extVar('alerts');
+local condition = std.extVar('condition');
+local arr = std.range(1, numAlerts);
+
+local alertDashboardTemplate = {
+ "editable": true,
+ "gnetId": null,
+ "graphTooltip": 0,
+ "id": null,
+ "links": [],
+ "panels": [
+ {
+ "alert": {
+ "conditions": [
+ {
+ "evaluator": {
+ "params": [
+ 65
+ ],
+ "type": "gt"
+ },
+ "operator": {
+ "type": "and"
+ },
+ "query": {
+ "params": [
+ "A",
+ "5m",
+ "now"
+ ]
+ },
+ "reducer": {
+ "params": [],
+ "type": "avg"
+ },
+ "type": "query"
+ }
+ ],
+ "executionErrorState": "alerting",
+ "frequency": "10s",
+ "handler": 1,
+ "name": "bulk alerting",
+ "noDataState": "no_data",
+ "notifications": [
+ {
+ "id": 2
+ }
+ ]
+ },
+ "aliasColors": {},
+ "bars": false,
+ "dashLength": 10,
+ "dashes": false,
+ "datasource": "Prometheus",
+ "fill": 1,
+ "gridPos": {
+ "h": 9,
+ "w": 12,
+ "x": 0,
+ "y": 0
+ },
+ "id": 2,
+ "legend": {
+ "avg": false,
+ "current": false,
+ "max": false,
+ "min": false,
+ "show": true,
+ "total": false,
+ "values": false
+ },
+ "lines": true,
+ "linewidth": 1,
+ "nullPointMode": "null",
+ "percentage": false,
+ "pointradius": 5,
+ "points": false,
+ "renderer": "flot",
+ "seriesOverrides": [],
+ "spaceLength": 10,
+ "stack": false,
+ "steppedLine": false,
+ "targets": [
+ {
+ "$$hashKey": "object:117",
+ "expr": "go_goroutines",
+ "format": "time_series",
+ "intervalFactor": 1,
+ "refId": "A"
+ }
+ ],
+ "thresholds": [
+ {
+ "colorMode": "critical",
+ "fill": true,
+ "line": true,
+ "op": "gt",
+ "value": 50
+ }
+ ],
+ "timeFrom": null,
+ "timeShift": null,
+ "title": "Panel Title",
+ "tooltip": {
+ "shared": true,
+ "sort": 0,
+ "value_type": "individual"
+ },
+ "type": "graph",
+ "xaxis": {
+ "buckets": null,
+ "mode": "time",
+ "name": null,
+ "show": true,
+ "values": []
+ },
+ "yaxes": [
+ {
+ "format": "short",
+ "label": null,
+ "logBase": 1,
+ "max": null,
+ "min": null,
+ "show": true
+ },
+ {
+ "format": "short",
+ "label": null,
+ "logBase": 1,
+ "max": null,
+ "min": null,
+ "show": true
+ }
+ ]
+ }
+ ],
+ "schemaVersion": 16,
+ "style": "dark",
+ "tags": [],
+ "templating": {
+ "list": []
+ },
+ "time": {
+ "from": "now-6h",
+ "to": "now"
+ },
+ "timepicker": {
+ "refresh_intervals": [
+ "5s",
+ "10s",
+ "30s",
+ "1m",
+ "5m",
+ "15m",
+ "30m",
+ "1h",
+ "2h",
+ "1d"
+ ],
+ "time_options": [
+ "5m",
+ "15m",
+ "1h",
+ "6h",
+ "12h",
+ "24h",
+ "2d",
+ "7d",
+ "30d"
+ ]
+ },
+ "timezone": "",
+ "title": "New dashboard",
+ "uid": null,
+ "version": 0
+};
+
+
+{
+ ['alert-' + std.toString(x) + '.json']:
+ alertDashboardTemplate + {
+ panels: [
+ alertDashboardTemplate.panels[0] +
+ {
+ alert+: {
+ name: 'Alert rule ' + x,
+ conditions: [
+ alertDashboardTemplate.panels[0].alert.conditions[0] +
+ {
+ evaluator+: {
+ params: [condition]
+ }
+ },
+ ],
+ },
+ },
+ ],
+ uid: 'alert-' + x,
+ title: 'Alert ' + x
+ },
+ for x in arr
+}
\ No newline at end of file
diff --git a/devenv/docker/ha_test/grafana/provisioning/dashboards/alerts.yaml b/devenv/docker/ha_test/grafana/provisioning/dashboards/alerts.yaml
new file mode 100644
index 00000000000..60b6cd4bb04
--- /dev/null
+++ b/devenv/docker/ha_test/grafana/provisioning/dashboards/alerts.yaml
@@ -0,0 +1,8 @@
+apiVersion: 1
+
+providers:
+ - name: 'Alerts'
+ folder: 'Alerts'
+ type: file
+ options:
+ path: /etc/grafana/provisioning/dashboards/alerts
diff --git a/devenv/docker/ha_test/grafana/provisioning/dashboards/alerts/overview.json b/devenv/docker/ha_test/grafana/provisioning/dashboards/alerts/overview.json
new file mode 100644
index 00000000000..53e33c37b1f
--- /dev/null
+++ b/devenv/docker/ha_test/grafana/provisioning/dashboards/alerts/overview.json
@@ -0,0 +1,172 @@
+{
+ "annotations": {
+ "list": [
+ {
+ "builtIn": 1,
+ "datasource": "-- Grafana --",
+ "enable": true,
+ "hide": true,
+ "iconColor": "rgba(0, 211, 255, 1)",
+ "name": "Annotations & Alerts",
+ "type": "dashboard"
+ }
+ ]
+ },
+ "editable": true,
+ "gnetId": null,
+ "graphTooltip": 0,
+ "links": [],
+ "panels": [
+ {
+ "aliasColors": {
+ "Active alerts": "#bf1b00"
+ },
+ "bars": false,
+ "dashLength": 10,
+ "dashes": false,
+ "datasource": "Prometheus",
+ "fill": 1,
+ "gridPos": {
+ "h": 12,
+ "w": 24,
+ "x": 0,
+ "y": 0
+ },
+ "id": 2,
+ "interval": "",
+ "legend": {
+ "alignAsTable": true,
+ "avg": false,
+ "current": true,
+ "max": false,
+ "min": false,
+ "rightSide": true,
+ "show": true,
+ "total": false,
+ "values": true
+ },
+ "lines": true,
+ "linewidth": 2,
+ "links": [],
+ "nullPointMode": "null",
+ "percentage": false,
+ "pointradius": 5,
+ "points": false,
+ "renderer": "flot",
+ "seriesOverrides": [
+ {
+ "alias": "Active grafana instances",
+ "dashes": true,
+ "fill": 0
+ }
+ ],
+ "spaceLength": 10,
+ "stack": false,
+ "steppedLine": false,
+ "targets": [
+ {
+ "expr": "sum(increase(grafana_alerting_notification_sent_total[1m])) by(job)",
+ "format": "time_series",
+ "instant": false,
+ "interval": "1m",
+ "intervalFactor": 1,
+ "legendFormat": "Notifications sent",
+ "refId": "A"
+ },
+ {
+ "expr": "min(grafana_alerting_active_alerts) without(instance)",
+ "format": "time_series",
+ "interval": "1m",
+ "intervalFactor": 1,
+ "legendFormat": "Active alerts",
+ "refId": "B"
+ },
+ {
+ "expr": "count(up{job=\"grafana\"})",
+ "format": "time_series",
+ "intervalFactor": 1,
+ "legendFormat": "Active grafana instances",
+ "refId": "C"
+ }
+ ],
+ "thresholds": [],
+ "timeFrom": null,
+ "timeShift": null,
+ "title": "Notifications sent vs active alerts",
+ "tooltip": {
+ "shared": true,
+ "sort": 0,
+ "value_type": "individual"
+ },
+ "type": "graph",
+ "xaxis": {
+ "buckets": null,
+ "mode": "time",
+ "name": null,
+ "show": true,
+ "values": []
+ },
+ "yaxes": [
+ {
+ "format": "short",
+ "label": null,
+ "logBase": 1,
+ "max": null,
+ "min": "0",
+ "show": true
+ },
+ {
+ "format": "short",
+ "label": null,
+ "logBase": 1,
+ "max": null,
+ "min": null,
+ "show": true
+ }
+ ],
+ "yaxis": {
+ "align": false,
+ "alignLevel": 3
+ }
+ }
+ ],
+ "schemaVersion": 16,
+ "style": "dark",
+ "tags": [],
+ "templating": {
+ "list": []
+ },
+ "time": {
+ "from": "now-1h",
+ "to": "now"
+ },
+ "timepicker": {
+ "refresh_intervals": [
+ "5s",
+ "10s",
+ "30s",
+ "1m",
+ "5m",
+ "15m",
+ "30m",
+ "1h",
+ "2h",
+ "1d"
+ ],
+ "time_options": [
+ "5m",
+ "15m",
+ "1h",
+ "6h",
+ "12h",
+ "24h",
+ "2d",
+ "7d",
+ "30d"
+ ]
+ },
+ "timezone": "",
+ "title": "Overview",
+ "uid": "xHy7-hAik",
+ "version": 6
+}
\ No newline at end of file
diff --git a/devenv/docker/ha_test/grafana/provisioning/datasources/datasources.yaml b/devenv/docker/ha_test/grafana/provisioning/datasources/datasources.yaml
new file mode 100644
index 00000000000..8d59793be16
--- /dev/null
+++ b/devenv/docker/ha_test/grafana/provisioning/datasources/datasources.yaml
@@ -0,0 +1,11 @@
+apiVersion: 1
+
+datasources:
+ - name: Prometheus
+ type: prometheus
+ access: proxy
+ url: http://prometheus:9090
+ jsonData:
+ timeInterval: 10s
+ queryTimeout: 30s
+ httpMethod: POST
\ No newline at end of file
diff --git a/devenv/docker/ha_test/prometheus/prometheus.yml b/devenv/docker/ha_test/prometheus/prometheus.yml
new file mode 100644
index 00000000000..ea97ba8ba05
--- /dev/null
+++ b/devenv/docker/ha_test/prometheus/prometheus.yml
@@ -0,0 +1,39 @@
+# my global config
+global:
+ scrape_interval: 10s # By default, scrape targets every 15 seconds.
+ evaluation_interval: 10s # By default, scrape targets every 15 seconds.
+ # scrape_timeout is set to the global default (10s).
+
+# Load and evaluate rules in this file every 'evaluation_interval' seconds.
+#rule_files:
+# - "alert.rules"
+# - "first.rules"
+# - "second.rules"
+
+# alerting:
+# alertmanagers:
+# - scheme: http
+# static_configs:
+# - targets:
+# - "127.0.0.1:9093"
+
+scrape_configs:
+ - job_name: 'prometheus'
+ static_configs:
+ - targets: ['localhost:9090']
+
+ - job_name: 'grafana'
+ dns_sd_configs:
+ - names:
+ - 'grafana'
+ type: 'A'
+ port: 3000
+ refresh_interval: 10s
+
+ # - job_name: 'mysql'
+ # dns_sd_configs:
+ # - names:
+ # - 'mysqld-exporter'
+ # type: 'A'
+ # port: 9104
+ # refresh_interval: 10s
\ No newline at end of file
diff --git a/docker/rpmtest/build.sh b/devenv/docker/rpmtest/build.sh
similarity index 100%
rename from docker/rpmtest/build.sh
rename to devenv/docker/rpmtest/build.sh
diff --git a/tests/api/clearState.test.ts b/devenv/e2e-api-tests/clearState.test.ts
similarity index 100%
rename from tests/api/clearState.test.ts
rename to devenv/e2e-api-tests/clearState.test.ts
diff --git a/tests/api/client.ts b/devenv/e2e-api-tests/client.ts
similarity index 100%
rename from tests/api/client.ts
rename to devenv/e2e-api-tests/client.ts
diff --git a/tests/api/dashboard.test.ts b/devenv/e2e-api-tests/dashboard.test.ts
similarity index 100%
rename from tests/api/dashboard.test.ts
rename to devenv/e2e-api-tests/dashboard.test.ts
diff --git a/tests/api/folder.test.ts b/devenv/e2e-api-tests/folder.test.ts
similarity index 100%
rename from tests/api/folder.test.ts
rename to devenv/e2e-api-tests/folder.test.ts
diff --git a/tests/api/jest.js b/devenv/e2e-api-tests/jest.js
similarity index 100%
rename from tests/api/jest.js
rename to devenv/e2e-api-tests/jest.js
diff --git a/tests/api/search.test.ts b/devenv/e2e-api-tests/search.test.ts
similarity index 100%
rename from tests/api/search.test.ts
rename to devenv/e2e-api-tests/search.test.ts
diff --git a/tests/api/setup.ts b/devenv/e2e-api-tests/setup.ts
similarity index 100%
rename from tests/api/setup.ts
rename to devenv/e2e-api-tests/setup.ts
diff --git a/tests/api/tsconfig.json b/devenv/e2e-api-tests/tsconfig.json
similarity index 100%
rename from tests/api/tsconfig.json
rename to devenv/e2e-api-tests/tsconfig.json
diff --git a/tests/api/user.test.ts b/devenv/e2e-api-tests/user.test.ts
similarity index 100%
rename from tests/api/user.test.ts
rename to devenv/e2e-api-tests/user.test.ts
diff --git a/devenv/setup.sh b/devenv/setup.sh
index cc71ecc71bf..c9cc0d47a6f 100755
--- a/devenv/setup.sh
+++ b/devenv/setup.sh
@@ -11,7 +11,21 @@ bulkDashboard() {
let COUNTER=COUNTER+1
done
- ln -s -f -r ./bulk-dashboards/bulk-dashboards.yaml ../conf/provisioning/dashboards/custom.yaml
+ ln -s -f ../../../devenv/bulk-dashboards/bulk-dashboards.yaml ../conf/provisioning/dashboards/custom.yaml
+}
+
+bulkAlertingDashboard() {
+
+ requiresJsonnet
+
+ COUNTER=0
+ MAX=100
+ while [ $COUNTER -lt $MAX ]; do
+ jsonnet -o "bulk_alerting_dashboards/alerting_dashboard${COUNTER}.json" -e "local bulkDash = import 'bulk_alerting_dashboards/bulkdash_alerting.jsonnet'; bulkDash + { uid: 'bd-${COUNTER}', title: 'alerting-title-${COUNTER}' }"
+ let COUNTER=COUNTER+1
+ done
+
+ ln -s -f ../../../devenv/bulk_alerting_dashboards/bulk_alerting_dashboards.yaml ../conf/provisioning/dashboards/custom.yaml
}
requiresJsonnet() {
@@ -36,8 +50,9 @@ devDatasources() {
usage() {
echo -e "\n"
echo "Usage:"
- echo " bulk-dashboards - create and provisioning 400 dashboards"
- echo " no args - provisiong core datasources and dev dashboards"
+ echo " bulk-dashboards - create and provisioning 400 dashboards"
+ echo " bulk-alerting-dashboards - create and provisioning 400 dashboards with alerts"
+ echo " no args - provisiong core datasources and dev dashboards"
}
main() {
@@ -48,7 +63,9 @@ main() {
local cmd=$1
- if [[ $cmd == "bulk-dashboards" ]]; then
+ if [[ $cmd == "bulk-alerting-dashboards" ]]; then
+ bulkAlertingDashboard
+ elif [[ $cmd == "bulk-dashboards" ]]; then
bulkDashboard
else
devDashboards
diff --git a/docs/README.md b/docs/README.md
index ff5ef6a4131..7310f184a60 100644
--- a/docs/README.md
+++ b/docs/README.md
@@ -65,7 +65,7 @@ make docs-build
This will rebuild the docs docker container.
-To be able to use the image your have to quit (CTRL-C) the `make watch` command (that you run in the same directory as this README). Then simply rerun `make watch`, it will restart the docs server but now with access to your image.
+To be able to use the image you have to quit (CTRL-C) the `make watch` command (that you run in the same directory as this README). Then simply rerun `make watch`, it will restart the docs server but now with access to your image.
### Editing content
diff --git a/docs/sources/administration/permissions.md b/docs/sources/administration/permissions.md
index e7b84a417c0..0d374f03647 100644
--- a/docs/sources/administration/permissions.md
+++ b/docs/sources/administration/permissions.md
@@ -52,12 +52,10 @@ This admin flag makes a user a `Super Admin`. This means they can access the `Se
### Dashboard & Folder Permissions
-> Introduced in Grafana v5.0
-
{{< docs-imagebox img="/img/docs/v50/folder_permissions.png" max-width="500px" class="docs-image--right" >}}
For dashboards and dashboard folders there is a **Permissions** page that make it possible to
-remove the default role based permssions for Editors and Viewers. It's here you can add and assign permissions to specific **Users** and **Teams**.
+remove the default role based permissions for Editors and Viewers. It's here you can add and assign permissions to specific **Users** and **Teams**.
You can assign & remove permissions for **Organization Roles**, **Users** and **Teams**.
@@ -104,7 +102,7 @@ Permissions for a dashboard:
Result: You cannot override to a lower permission. `user1` has Admin permission as the highest permission always wins.
-- **View**: Can only view existing dashboars/folders.
+- **View**: Can only view existing dashboards/folders.
- You cannot override permissions for users with **Org Admin Role**
- A more specific permission with lower permission level will not have any effect if a more general rule exists with higher permission level. For example if "Everyone with Editor Role Can Edit" exists in the ACL list then **John Doe** will still have Edit permission even after you have specifically added a permission for this user with the permission set to **View**. You need to remove or lower the permission level of the more general rule.
diff --git a/docs/sources/administration/provisioning.md b/docs/sources/administration/provisioning.md
index c57fb1683f0..8916b2bf6e3 100644
--- a/docs/sources/administration/provisioning.md
+++ b/docs/sources/administration/provisioning.md
@@ -71,6 +71,7 @@ Puppet | [https://forge.puppet.com/puppet/grafana](https://forge.puppet.com/pupp
Ansible | [https://github.com/cloudalchemy/ansible-grafana](https://github.com/cloudalchemy/ansible-grafana)
Chef | [https://github.com/JonathanTron/chef-grafana](https://github.com/JonathanTron/chef-grafana)
Saltstack | [https://github.com/salt-formulas/salt-formula-grafana](https://github.com/salt-formulas/salt-formula-grafana)
+Jsonnet | [https://github.com/grafana/grafonnet-lib/](https://github.com/grafana/grafonnet-lib/)
## Datasources
@@ -122,7 +123,7 @@ datasources:
withCredentials:
# mark as default datasource. Max one per org
isDefault:
- #
```
-The response is a textual respresentation of the diff, with the dashboard values being in JSON, similar to the diffs seen on sites like GitHub or GitLab.
+The response is a textual representation of the diff, with the dashboard values being in JSON, similar to the diffs seen on sites like GitHub or GitLab.
Status Codes:
diff --git a/docs/sources/index.md b/docs/sources/index.md
index da977b73e0c..e9a900d93f1 100644
--- a/docs/sources/index.md
+++ b/docs/sources/index.md
@@ -60,9 +60,9 @@ aliases = ["v1.1", "guides/reference/admin"]
Provisioning
A guide to help you automate your Grafana setup & configuration.
+ Send reminder every
+
+ Specify how often reminders should be sent, e.g. every 30s, 1m, 10m, 30m or 1h etc.
+
+
+
+
+
+
+
+ Alert reminders are sent after rules are evaluated. Therefore a reminder can never be sent more frequently than a configured alert rule evaluation interval.
+
+
+ You will only be able to view this key here once! It is not stored in this form. So be sure to copy it now.
+
+
+ You can authenticate request using the Authorization HTTP header, example:
+
+
+
+ You will only be able to view this key here once! It is not stored in this form. So be sure to copy it now.
+
+
+ You can authenticate request using the Authorization HTTP header, example:
+
+
+
+ curl -H "Authorization: Bearer
+ api key test
+ "
+ test/path
+ /api/dashboards/home
+
+
+
+
+`;
diff --git a/public/app/features/api-keys/__snapshots__/ApiKeysPage.test.tsx.snap b/public/app/features/api-keys/__snapshots__/ApiKeysPage.test.tsx.snap
new file mode 100644
index 00000000000..b1cac8469be
--- /dev/null
+++ b/public/app/features/api-keys/__snapshots__/ApiKeysPage.test.tsx.snap
@@ -0,0 +1,414 @@
+// Jest Snapshot v1, https://goo.gl/fbAQLP
+
+exports[`Render should render API keys table 1`] = `
+
- You will only be able to view this key here once! It is not stored in this form. So be sure to copy it now.
-
-
- You can authenticate request using the Authorization HTTP header, example:
-
-
-
A playlist rotates through a pre-selected list of Dashboards. A Playlist can be a great way to build situational awareness, or just show off your metrics to your team or visitors.
- Access mode controls how requests to the data source will be handled.
- Server should be the preferred way if nothing else stated.
-
-
Server access mode (Default):
-
- All requests will be made from the browser to Grafana backend/server which in turn will forward the requests to the data source
- and by that circumvent possible Cross-Origin Resource Sharing (CORS) requirements.
- The URL needs to be accessible from the grafana backend/server if you select this access mode.
-
-
Browser access mode:
-
- All requests will be made from the browser directly to the data source and may be subject to
- Cross-Origin Resource Sharing (CORS) requirements. The URL needs to be accessible from the browser if you select this
- access mode.
-
-
-
-
+
+
+ Access mode controls how requests to the data source will be handled.
+ Server should be the preferred way if nothing else stated.
+
+
Server access mode (Default):
+
+ All requests will be made from the browser to Grafana backend/server which in turn will forward the requests to the data source
+ and by that circumvent possible Cross-Origin Resource Sharing (CORS) requirements.
+ The URL needs to be accessible from the grafana backend/server if you select this access mode.
+
+
Browser access mode:
+
+ All requests will be made from the browser directly to the data source and may be subject to
+ Cross-Origin Resource Sharing (CORS) requirements. The URL needs to be accessible from the browser if you select this
+ access mode.
+
-
Auth
-
-
-
-
-
-
-
-
-
-
+
+
+ Whitelisted Cookies
+
+
+
+ Grafana Proxy deletes forwarded cookies by default. Specify cookies by name that should be forwarded to the data source.
+
+
+
+
-
-
-
-
+
Auth
+
+
+
+
+
+
+
+
+
+
+
+
+
-
-
Basic Auth Details
-
-
- User
-
-
-
+
+
Basic Auth Details
+
+ User
+
+
+
+ Password
+
+
+
-
-
- Password
-
-
-
-
+
+
+
TLS Auth Details
+ TLS Certs are encrypted and stored in the Grafana database.
+
+
+
+
+
+
+
+
+
-
-
-
TLS Auth Details
- TLS Certs are encrypted and stored in the Grafana database.
-
- Whitelisted Cookies
-
-
-
- Grafana Proxy deletes forwarded cookies by default. Specify cookies by name that should be forwarded to the data source.
-
-
-
+
Specify the region, such as for US West (Oregon) use ` us-west-2 ` as the region.
diff --git a/public/app/plugins/datasource/cloudwatch/partials/query.parameter.html b/public/app/plugins/datasource/cloudwatch/partials/query.parameter.html
index 7da6e7d2a83..2a951bc9257 100644
--- a/public/app/plugins/datasource/cloudwatch/partials/query.parameter.html
+++ b/public/app/plugins/datasource/cloudwatch/partials/query.parameter.html
@@ -37,8 +37,7 @@
Id
Id can include numbers, letters, and underscore, and must start with a lowercase letter.
-
+
diff --git a/public/app/plugins/datasource/influxdb/query_builder.ts b/public/app/plugins/datasource/influxdb/query_builder.ts
index f4ac4373ab6..a61216787d3 100644
--- a/public/app/plugins/datasource/influxdb/query_builder.ts
+++ b/public/app/plugins/datasource/influxdb/query_builder.ts
@@ -1,9 +1,9 @@
import _ from 'lodash';
function renderTagCondition(tag, index) {
- var str = '';
- var operator = tag.operator;
- var value = tag.value;
+ let str = '';
+ let operator = tag.operator;
+ let value = tag.value;
if (index > 0) {
str = (tag.condition || 'AND') + ' ';
}
@@ -28,9 +28,9 @@ export class InfluxQueryBuilder {
constructor(private target, private database?) {}
buildExploreQuery(type: string, withKey?: string, withMeasurementFilter?: string) {
- var query;
- var measurement;
- var policy;
+ let query;
+ let measurement;
+ let policy;
if (type === 'TAG_KEYS') {
query = 'SHOW TAG KEYS';
@@ -84,7 +84,7 @@ export class InfluxQueryBuilder {
if (this.target.tags && this.target.tags.length > 0) {
const whereConditions = _.reduce(
this.target.tags,
- function(memo, tag) {
+ (memo, tag) => {
// do not add a condition for the key we want to explore for
if (tag.key === withKey) {
return memo;
diff --git a/public/app/plugins/datasource/influxdb/query_ctrl.ts b/public/app/plugins/datasource/influxdb/query_ctrl.ts
index 5ef8f2c7d1d..f531fe6c4d9 100644
--- a/public/app/plugins/datasource/influxdb/query_ctrl.ts
+++ b/public/app/plugins/datasource/influxdb/query_ctrl.ts
@@ -19,7 +19,7 @@ export class InfluxQueryCtrl extends QueryCtrl {
measurementSegment: any;
removeTagFilterSegment: any;
- /** @ngInject **/
+ /** @ngInject */
constructor($scope, $injector, private templateSrv, private $q, private uiSegmentSrv) {
super($scope, $injector);
this.target = this.target;
@@ -70,7 +70,7 @@ export class InfluxQueryCtrl extends QueryCtrl {
const categories = queryPart.getCategories();
this.selectMenu = _.reduce(
categories,
- function(memo, cat, key) {
+ (memo, cat, key) => {
const menu = {
text: key,
submenu: cat.map(item => {
@@ -279,7 +279,7 @@ export class InfluxQueryCtrl extends QueryCtrl {
}
}
- var query, addTemplateVars;
+ let query, addTemplateVars;
if (segment.type === 'key' || segment.type === 'plus-button') {
query = this.queryBuilder.buildExploreQuery('TAG_KEYS');
addTemplateVars = false;
@@ -343,8 +343,8 @@ export class InfluxQueryCtrl extends QueryCtrl {
rebuildTargetTagConditions() {
const tags = [];
- var tagIndex = 0;
- var tagOperator = '';
+ let tagIndex = 0;
+ let tagOperator = '';
_.each(this.tagSegments, (segment2, index) => {
if (segment2.type === 'key') {
diff --git a/public/app/plugins/datasource/influxdb/query_part.ts b/public/app/plugins/datasource/influxdb/query_part.ts
index e03ff82d5f0..4bc92bcfe72 100644
--- a/public/app/plugins/datasource/influxdb/query_part.ts
+++ b/public/app/plugins/datasource/influxdb/query_part.ts
@@ -41,7 +41,7 @@ function fieldRenderer(part, innerExpr) {
function replaceAggregationAddStrategy(selectParts, partModel) {
// look for existing aggregation
- for (var i = 0; i < selectParts.length; i++) {
+ for (let i = 0; i < selectParts.length; i++) {
const part = selectParts[i];
if (part.def.category === categories.Aggregations) {
if (part.def.type === partModel.def.type) {
@@ -79,7 +79,7 @@ function replaceAggregationAddStrategy(selectParts, partModel) {
}
function addTransformationStrategy(selectParts, partModel) {
- var i;
+ let i;
// look for index to add transformation
for (i = 0; i < selectParts.length; i++) {
const part = selectParts[i];
@@ -126,7 +126,7 @@ function addAliasStrategy(selectParts, partModel) {
function addFieldStrategy(selectParts, partModel, query) {
// copy all parts
- const parts = _.map(selectParts, function(part: any) {
+ const parts = _.map(selectParts, (part: any) => {
return createPart({ type: part.def.type, params: _.clone(part.params) });
});
@@ -453,7 +453,7 @@ register({
export default {
create: createPart,
- getCategories: function() {
+ getCategories: () => {
return categories;
},
replaceAggregationAdd: replaceAggregationAddStrategy,
diff --git a/public/app/plugins/datasource/influxdb/specs/datasource.test.ts b/public/app/plugins/datasource/influxdb/specs/datasource.test.ts
index 60f49bd4905..62049535e3e 100644
--- a/public/app/plugins/datasource/influxdb/specs/datasource.test.ts
+++ b/public/app/plugins/datasource/influxdb/specs/datasource.test.ts
@@ -10,7 +10,7 @@ describe('InfluxDataSource', () => {
instanceSettings: { url: 'url', name: 'influxDb', jsonData: {} },
};
- beforeEach(function() {
+ beforeEach(() => {
ctx.instanceSettings.url = '/api/datasources/proxy/1';
ctx.ds = new InfluxDatasource(ctx.instanceSettings, ctx.$q, ctx.backendSrv, ctx.templateSrv);
});
@@ -26,7 +26,7 @@ describe('InfluxDataSource', () => {
let requestQuery;
beforeEach(async () => {
- ctx.backendSrv.datasourceRequest = function(req) {
+ ctx.backendSrv.datasourceRequest = req => {
requestQuery = req.params.q;
return ctx.$q.when({
results: [
@@ -43,7 +43,7 @@ describe('InfluxDataSource', () => {
});
};
- await ctx.ds.metricFindQuery(query, queryOptions).then(function(_) {});
+ await ctx.ds.metricFindQuery(query, queryOptions).then(_ => {});
});
it('should replace $timefilter', () => {
diff --git a/public/app/plugins/datasource/influxdb/specs/influx_query.test.ts b/public/app/plugins/datasource/influxdb/specs/influx_query.test.ts
index a62d5384ac6..f8e65c21f2d 100644
--- a/public/app/plugins/datasource/influxdb/specs/influx_query.test.ts
+++ b/public/app/plugins/datasource/influxdb/specs/influx_query.test.ts
@@ -1,10 +1,10 @@
import InfluxQuery from '../influx_query';
-describe('InfluxQuery', function() {
+describe('InfluxQuery', () => {
const templateSrv = { replace: val => val };
- describe('render series with mesurement only', function() {
- it('should generate correct query', function() {
+ describe('render series with mesurement only', () => {
+ it('should generate correct query', () => {
const query = new InfluxQuery(
{
measurement: 'cpu',
@@ -18,8 +18,8 @@ describe('InfluxQuery', function() {
});
});
- describe('render series with policy only', function() {
- it('should generate correct query', function() {
+ describe('render series with policy only', () => {
+ it('should generate correct query', () => {
const query = new InfluxQuery(
{
measurement: 'cpu',
@@ -36,8 +36,8 @@ describe('InfluxQuery', function() {
});
});
- describe('render series with math and alias', function() {
- it('should generate correct query', function() {
+ describe('render series with math and alias', () => {
+ it('should generate correct query', () => {
const query = new InfluxQuery(
{
measurement: 'cpu',
@@ -61,8 +61,8 @@ describe('InfluxQuery', function() {
});
});
- describe('series with single tag only', function() {
- it('should generate correct query', function() {
+ describe('series with single tag only', () => {
+ it('should generate correct query', () => {
const query = new InfluxQuery(
{
measurement: 'cpu',
@@ -81,7 +81,7 @@ describe('InfluxQuery', function() {
);
});
- it('should switch regex operator with tag value is regex', function() {
+ it('should switch regex operator with tag value is regex', () => {
const query = new InfluxQuery(
{
measurement: 'cpu',
@@ -99,8 +99,8 @@ describe('InfluxQuery', function() {
});
});
- describe('series with multiple tags only', function() {
- it('should generate correct query', function() {
+ describe('series with multiple tags only', () => {
+ it('should generate correct query', () => {
const query = new InfluxQuery(
{
measurement: 'cpu',
@@ -119,8 +119,8 @@ describe('InfluxQuery', function() {
});
});
- describe('series with tags OR condition', function() {
- it('should generate correct query', function() {
+ describe('series with tags OR condition', () => {
+ it('should generate correct query', () => {
const query = new InfluxQuery(
{
measurement: 'cpu',
@@ -139,8 +139,8 @@ describe('InfluxQuery', function() {
});
});
- describe('query with value condition', function() {
- it('should not quote value', function() {
+ describe('query with value condition', () => {
+ it('should not quote value', () => {
const query = new InfluxQuery(
{
measurement: 'cpu',
@@ -156,8 +156,8 @@ describe('InfluxQuery', function() {
});
});
- describe('series with groupByTag', function() {
- it('should generate correct query', function() {
+ describe('series with groupByTag', () => {
+ it('should generate correct query', () => {
const query = new InfluxQuery(
{
measurement: 'cpu',
@@ -173,8 +173,8 @@ describe('InfluxQuery', function() {
});
});
- describe('render series without group by', function() {
- it('should generate correct query', function() {
+ describe('render series without group by', () => {
+ it('should generate correct query', () => {
const query = new InfluxQuery(
{
measurement: 'cpu',
@@ -189,8 +189,8 @@ describe('InfluxQuery', function() {
});
});
- describe('render series without group by and fill', function() {
- it('should generate correct query', function() {
+ describe('render series without group by and fill', () => {
+ it('should generate correct query', () => {
const query = new InfluxQuery(
{
measurement: 'cpu',
@@ -205,8 +205,8 @@ describe('InfluxQuery', function() {
});
});
- describe('when adding group by part', function() {
- it('should add tag before fill', function() {
+ describe('when adding group by part', () => {
+ it('should add tag before fill', () => {
const query = new InfluxQuery(
{
measurement: 'cpu',
@@ -223,7 +223,7 @@ describe('InfluxQuery', function() {
expect(query.target.groupBy[2].type).toBe('fill');
});
- it('should add tag last if no fill', function() {
+ it('should add tag last if no fill', () => {
const query = new InfluxQuery(
{
measurement: 'cpu',
@@ -239,8 +239,8 @@ describe('InfluxQuery', function() {
});
});
- describe('when adding select part', function() {
- it('should add mean after after field', function() {
+ describe('when adding select part', () => {
+ it('should add mean after after field', () => {
const query = new InfluxQuery(
{
measurement: 'cpu',
@@ -255,7 +255,7 @@ describe('InfluxQuery', function() {
expect(query.target.select[0][1].type).toBe('mean');
});
- it('should replace sum by mean', function() {
+ it('should replace sum by mean', () => {
const query = new InfluxQuery(
{
measurement: 'cpu',
@@ -270,7 +270,7 @@ describe('InfluxQuery', function() {
expect(query.target.select[0][1].type).toBe('sum');
});
- it('should add math before alias', function() {
+ it('should add math before alias', () => {
const query = new InfluxQuery(
{
measurement: 'cpu',
@@ -285,7 +285,7 @@ describe('InfluxQuery', function() {
expect(query.target.select[0][2].type).toBe('math');
});
- it('should add math last', function() {
+ it('should add math last', () => {
const query = new InfluxQuery(
{
measurement: 'cpu',
@@ -300,7 +300,7 @@ describe('InfluxQuery', function() {
expect(query.target.select[0][2].type).toBe('math');
});
- it('should replace math', function() {
+ it('should replace math', () => {
const query = new InfluxQuery(
{
measurement: 'cpu',
@@ -315,7 +315,7 @@ describe('InfluxQuery', function() {
expect(query.target.select[0][2].type).toBe('math');
});
- it('should add math when one only query part', function() {
+ it('should add math when one only query part', () => {
const query = new InfluxQuery(
{
measurement: 'cpu',
@@ -330,8 +330,8 @@ describe('InfluxQuery', function() {
expect(query.target.select[0][1].type).toBe('math');
});
- describe('when render adhoc filters', function() {
- it('should generate correct query segment', function() {
+ describe('when render adhoc filters', () => {
+ it('should generate correct query segment', () => {
const query = new InfluxQuery({ measurement: 'cpu' }, templateSrv, {});
const queryText = query.renderAdhocFilters([
diff --git a/public/app/plugins/datasource/influxdb/specs/influx_series.test.ts b/public/app/plugins/datasource/influxdb/specs/influx_series.test.ts
index bb20db1ba76..44232173e27 100644
--- a/public/app/plugins/datasource/influxdb/specs/influx_series.test.ts
+++ b/public/app/plugins/datasource/influxdb/specs/influx_series.test.ts
@@ -1,7 +1,7 @@
import InfluxSeries from '../influx_series';
-describe('when generating timeseries from influxdb response', function() {
- describe('given multiple fields for series', function() {
+describe('when generating timeseries from influxdb response', () => {
+ describe('given multiple fields for series', () => {
const options = {
alias: '',
series: [
@@ -13,8 +13,8 @@ describe('when generating timeseries from influxdb response', function() {
},
],
};
- describe('and no alias', function() {
- it('should generate multiple datapoints for each column', function() {
+ describe('and no alias', () => {
+ it('should generate multiple datapoints for each column', () => {
const series = new InfluxSeries(options);
const result = series.getTimeSeries();
@@ -39,8 +39,8 @@ describe('when generating timeseries from influxdb response', function() {
});
});
- describe('and simple alias', function() {
- it('should use alias', function() {
+ describe('and simple alias', () => {
+ it('should use alias', () => {
options.alias = 'new series';
const series = new InfluxSeries(options);
const result = series.getTimeSeries();
@@ -51,8 +51,8 @@ describe('when generating timeseries from influxdb response', function() {
});
});
- describe('and alias patterns', function() {
- it('should replace patterns', function() {
+ describe('and alias patterns', () => {
+ it('should replace patterns', () => {
options.alias = 'alias: $m -> $tag_server ([[measurement]])';
const series = new InfluxSeries(options);
const result = series.getTimeSeries();
@@ -64,7 +64,7 @@ describe('when generating timeseries from influxdb response', function() {
});
});
- describe('given measurement with default fieldname', function() {
+ describe('given measurement with default fieldname', () => {
const options = {
series: [
{
@@ -82,8 +82,8 @@ describe('when generating timeseries from influxdb response', function() {
],
};
- describe('and no alias', function() {
- it('should generate label with no field', function() {
+ describe('and no alias', () => {
+ it('should generate label with no field', () => {
const series = new InfluxSeries(options);
const result = series.getTimeSeries();
@@ -93,7 +93,7 @@ describe('when generating timeseries from influxdb response', function() {
});
});
- describe('given two series', function() {
+ describe('given two series', () => {
const options = {
alias: '',
series: [
@@ -112,8 +112,8 @@ describe('when generating timeseries from influxdb response', function() {
],
};
- describe('and no alias', function() {
- it('should generate two time series', function() {
+ describe('and no alias', () => {
+ it('should generate two time series', () => {
const series = new InfluxSeries(options);
const result = series.getTimeSeries();
@@ -132,8 +132,8 @@ describe('when generating timeseries from influxdb response', function() {
});
});
- describe('and simple alias', function() {
- it('should use alias', function() {
+ describe('and simple alias', () => {
+ it('should use alias', () => {
options.alias = 'new series';
const series = new InfluxSeries(options);
const result = series.getTimeSeries();
@@ -142,8 +142,8 @@ describe('when generating timeseries from influxdb response', function() {
});
});
- describe('and alias patterns', function() {
- it('should replace patterns', function() {
+ describe('and alias patterns', () => {
+ it('should replace patterns', () => {
options.alias = 'alias: $m -> $tag_server ([[measurement]])';
const series = new InfluxSeries(options);
const result = series.getTimeSeries();
@@ -154,7 +154,7 @@ describe('when generating timeseries from influxdb response', function() {
});
});
- describe('given measurement with dots', function() {
+ describe('given measurement with dots', () => {
const options = {
alias: '',
series: [
@@ -167,7 +167,7 @@ describe('when generating timeseries from influxdb response', function() {
],
};
- it('should replace patterns', function() {
+ it('should replace patterns', () => {
options.alias = 'alias: $1 -> [[3]]';
const series = new InfluxSeries(options);
const result = series.getTimeSeries();
@@ -176,7 +176,7 @@ describe('when generating timeseries from influxdb response', function() {
});
});
- describe('given table response', function() {
+ describe('given table response', () => {
const options = {
alias: '',
series: [
@@ -189,7 +189,7 @@ describe('when generating timeseries from influxdb response', function() {
],
};
- it('should return table', function() {
+ it('should return table', () => {
const series = new InfluxSeries(options);
const table = series.getTable();
@@ -200,7 +200,7 @@ describe('when generating timeseries from influxdb response', function() {
});
});
- describe('given table response from SHOW CARDINALITY', function() {
+ describe('given table response from SHOW CARDINALITY', () => {
const options = {
alias: '',
series: [
@@ -212,7 +212,7 @@ describe('when generating timeseries from influxdb response', function() {
],
};
- it('should return table', function() {
+ it('should return table', () => {
const series = new InfluxSeries(options);
const table = series.getTable();
@@ -223,8 +223,8 @@ describe('when generating timeseries from influxdb response', function() {
});
});
- describe('given annotation response', function() {
- describe('with empty tagsColumn', function() {
+ describe('given annotation response', () => {
+ describe('with empty tagsColumn', () => {
const options = {
alias: '',
annotation: {},
@@ -238,7 +238,7 @@ describe('when generating timeseries from influxdb response', function() {
],
};
- it('should multiple tags', function() {
+ it('should multiple tags', () => {
const series = new InfluxSeries(options);
const annotations = series.getAnnotations();
@@ -246,7 +246,7 @@ describe('when generating timeseries from influxdb response', function() {
});
});
- describe('given annotation response', function() {
+ describe('given annotation response', () => {
const options = {
alias: '',
annotation: {
@@ -262,7 +262,7 @@ describe('when generating timeseries from influxdb response', function() {
],
};
- it('should multiple tags', function() {
+ it('should multiple tags', () => {
const series = new InfluxSeries(options);
const annotations = series.getAnnotations();
diff --git a/public/app/plugins/datasource/influxdb/specs/query_builder.test.ts b/public/app/plugins/datasource/influxdb/specs/query_builder.test.ts
index d8b27f8b1bf..e21b95ac374 100644
--- a/public/app/plugins/datasource/influxdb/specs/query_builder.test.ts
+++ b/public/app/plugins/datasource/influxdb/specs/query_builder.test.ts
@@ -1,14 +1,14 @@
import { InfluxQueryBuilder } from '../query_builder';
-describe('InfluxQueryBuilder', function() {
- describe('when building explore queries', function() {
- it('should only have measurement condition in tag keys query given query with measurement', function() {
+describe('InfluxQueryBuilder', () => {
+ describe('when building explore queries', () => {
+ it('should only have measurement condition in tag keys query given query with measurement', () => {
const builder = new InfluxQueryBuilder({ measurement: 'cpu', tags: [] });
const query = builder.buildExploreQuery('TAG_KEYS');
expect(query).toBe('SHOW TAG KEYS FROM "cpu"');
});
- it('should handle regex measurement in tag keys query', function() {
+ it('should handle regex measurement in tag keys query', () => {
const builder = new InfluxQueryBuilder({
measurement: '/.*/',
tags: [],
@@ -17,13 +17,13 @@ describe('InfluxQueryBuilder', function() {
expect(query).toBe('SHOW TAG KEYS FROM /.*/');
});
- it('should have no conditions in tags keys query given query with no measurement or tag', function() {
+ it('should have no conditions in tags keys query given query with no measurement or tag', () => {
const builder = new InfluxQueryBuilder({ measurement: '', tags: [] });
const query = builder.buildExploreQuery('TAG_KEYS');
expect(query).toBe('SHOW TAG KEYS');
});
- it('should have where condition in tag keys query with tags', function() {
+ it('should have where condition in tag keys query with tags', () => {
const builder = new InfluxQueryBuilder({
measurement: '',
tags: [{ key: 'host', value: 'se1' }],
@@ -32,25 +32,25 @@ describe('InfluxQueryBuilder', function() {
expect(query).toBe('SHOW TAG KEYS WHERE "host" = \'se1\'');
});
- it('should have no conditions in measurement query for query with no tags', function() {
+ it('should have no conditions in measurement query for query with no tags', () => {
const builder = new InfluxQueryBuilder({ measurement: '', tags: [] });
const query = builder.buildExploreQuery('MEASUREMENTS');
expect(query).toBe('SHOW MEASUREMENTS LIMIT 100');
});
- it('should have no conditions in measurement query for query with no tags and empty query', function() {
+ it('should have no conditions in measurement query for query with no tags and empty query', () => {
const builder = new InfluxQueryBuilder({ measurement: '', tags: [] });
const query = builder.buildExploreQuery('MEASUREMENTS', undefined, '');
expect(query).toBe('SHOW MEASUREMENTS LIMIT 100');
});
- it('should have WITH MEASUREMENT in measurement query for non-empty query with no tags', function() {
+ it('should have WITH MEASUREMENT in measurement query for non-empty query with no tags', () => {
const builder = new InfluxQueryBuilder({ measurement: '', tags: [] });
const query = builder.buildExploreQuery('MEASUREMENTS', undefined, 'something');
expect(query).toBe('SHOW MEASUREMENTS WITH MEASUREMENT =~ /something/ LIMIT 100');
});
- it('should have WITH MEASUREMENT WHERE in measurement query for non-empty query with tags', function() {
+ it('should have WITH MEASUREMENT WHERE in measurement query for non-empty query with tags', () => {
const builder = new InfluxQueryBuilder({
measurement: '',
tags: [{ key: 'app', value: 'email' }],
@@ -59,7 +59,7 @@ describe('InfluxQueryBuilder', function() {
expect(query).toBe('SHOW MEASUREMENTS WITH MEASUREMENT =~ /something/ WHERE "app" = \'email\' LIMIT 100');
});
- it('should have where condition in measurement query for query with tags', function() {
+ it('should have where condition in measurement query for query with tags', () => {
const builder = new InfluxQueryBuilder({
measurement: '',
tags: [{ key: 'app', value: 'email' }],
@@ -68,7 +68,7 @@ describe('InfluxQueryBuilder', function() {
expect(query).toBe('SHOW MEASUREMENTS WHERE "app" = \'email\' LIMIT 100');
});
- it('should have where tag name IN filter in tag values query for query with one tag', function() {
+ it('should have where tag name IN filter in tag values query for query with one tag', () => {
const builder = new InfluxQueryBuilder({
measurement: '',
tags: [{ key: 'app', value: 'asdsadsad' }],
@@ -77,7 +77,7 @@ describe('InfluxQueryBuilder', function() {
expect(query).toBe('SHOW TAG VALUES WITH KEY = "app"');
});
- it('should have measurement tag condition and tag name IN filter in tag values query', function() {
+ it('should have measurement tag condition and tag name IN filter in tag values query', () => {
const builder = new InfluxQueryBuilder({
measurement: 'cpu',
tags: [{ key: 'app', value: 'email' }, { key: 'host', value: 'server1' }],
@@ -86,7 +86,7 @@ describe('InfluxQueryBuilder', function() {
expect(query).toBe('SHOW TAG VALUES FROM "cpu" WITH KEY = "app" WHERE "host" = \'server1\'');
});
- it('should select from policy correctly if policy is specified', function() {
+ it('should select from policy correctly if policy is specified', () => {
const builder = new InfluxQueryBuilder({
measurement: 'cpu',
policy: 'one_week',
@@ -96,7 +96,7 @@ describe('InfluxQueryBuilder', function() {
expect(query).toBe('SHOW TAG VALUES FROM "one_week"."cpu" WITH KEY = "app" WHERE "host" = \'server1\'');
});
- it('should not include policy when policy is default', function() {
+ it('should not include policy when policy is default', () => {
const builder = new InfluxQueryBuilder({
measurement: 'cpu',
policy: 'default',
@@ -106,7 +106,7 @@ describe('InfluxQueryBuilder', function() {
expect(query).toBe('SHOW TAG VALUES FROM "cpu" WITH KEY = "app"');
});
- it('should switch to regex operator in tag condition', function() {
+ it('should switch to regex operator in tag condition', () => {
const builder = new InfluxQueryBuilder({
measurement: 'cpu',
tags: [{ key: 'host', value: '/server.*/' }],
@@ -115,7 +115,7 @@ describe('InfluxQueryBuilder', function() {
expect(query).toBe('SHOW TAG VALUES FROM "cpu" WITH KEY = "app" WHERE "host" =~ /server.*/');
});
- it('should build show field query', function() {
+ it('should build show field query', () => {
const builder = new InfluxQueryBuilder({
measurement: 'cpu',
tags: [{ key: 'app', value: 'email' }],
@@ -124,7 +124,7 @@ describe('InfluxQueryBuilder', function() {
expect(query).toBe('SHOW FIELD KEYS FROM "cpu"');
});
- it('should build show field query with regexp', function() {
+ it('should build show field query with regexp', () => {
const builder = new InfluxQueryBuilder({
measurement: '/$var/',
tags: [{ key: 'app', value: 'email' }],
@@ -133,7 +133,7 @@ describe('InfluxQueryBuilder', function() {
expect(query).toBe('SHOW FIELD KEYS FROM /$var/');
});
- it('should build show retention policies query', function() {
+ it('should build show retention policies query', () => {
const builder = new InfluxQueryBuilder({ measurement: 'cpu', tags: [] }, 'site');
const query = builder.buildExploreQuery('RETENTION POLICIES');
expect(query).toBe('SHOW RETENTION POLICIES on "site"');
diff --git a/public/app/plugins/datasource/influxdb/specs/query_ctrl.test.ts b/public/app/plugins/datasource/influxdb/specs/query_ctrl.test.ts
index 88d4fb143cd..b50c7d8cde6 100644
--- a/public/app/plugins/datasource/influxdb/specs/query_ctrl.test.ts
+++ b/public/app/plugins/datasource/influxdb/specs/query_ctrl.test.ts
@@ -3,7 +3,7 @@ import { uiSegmentSrv } from 'app/core/services/segment_srv';
import { InfluxQueryCtrl } from '../query_ctrl';
describe('InfluxDBQueryCtrl', () => {
- const ctx = {};
+ const ctx = {} as any;
beforeEach(() => {
InfluxQueryCtrl.prototype.datasource = {
diff --git a/public/app/plugins/datasource/logging/result_transformer.test.ts b/public/app/plugins/datasource/logging/result_transformer.test.ts
index 0d203f748ba..c1e6913a388 100644
--- a/public/app/plugins/datasource/logging/result_transformer.test.ts
+++ b/public/app/plugins/datasource/logging/result_transformer.test.ts
@@ -1,29 +1,6 @@
import { LogLevel } from 'app/core/logs_model';
-import { getLogLevel, getSearchMatches } from './result_transformer';
-
-describe('getSearchMatches()', () => {
- it('gets no matches for when search and or line are empty', () => {
- expect(getSearchMatches('', '')).toEqual([]);
- expect(getSearchMatches('foo', '')).toEqual([]);
- expect(getSearchMatches('', 'foo')).toEqual([]);
- });
-
- it('gets no matches for unmatched search string', () => {
- expect(getSearchMatches('foo', 'bar')).toEqual([]);
- });
-
- it('gets matches for matched search string', () => {
- expect(getSearchMatches('foo', 'foo')).toEqual([{ length: 3, start: 0, text: 'foo' }]);
- expect(getSearchMatches(' foo ', 'foo')).toEqual([{ length: 3, start: 1, text: 'foo' }]);
- });
-
- expect(getSearchMatches(' foo foo bar ', 'foo|bar')).toEqual([
- { length: 3, start: 1, text: 'foo' },
- { length: 3, start: 5, text: 'foo' },
- { length: 3, start: 9, text: 'bar' },
- ]);
-});
+import { getLogLevel } from './result_transformer';
describe('getLoglevel()', () => {
it('returns no log level on empty line', () => {
diff --git a/public/app/plugins/datasource/logging/result_transformer.ts b/public/app/plugins/datasource/logging/result_transformer.ts
index e238778614c..526a9c7da2c 100644
--- a/public/app/plugins/datasource/logging/result_transformer.ts
+++ b/public/app/plugins/datasource/logging/result_transformer.ts
@@ -19,24 +19,6 @@ export function getLogLevel(line: string): LogLevel {
return level;
}
-export function getSearchMatches(line: string, search: string) {
- // Empty search can send re.exec() into infinite loop, exit early
- if (!line || !search) {
- return [];
- }
- const regexp = new RegExp(`(?:${search})`, 'g');
- const matches = [];
- let match;
- while ((match = regexp.exec(line))) {
- matches.push({
- text: match[0],
- start: match.index,
- length: match[0].length,
- });
- }
- return matches;
-}
-
export function processEntry(entry: { line: string; timestamp: string }, stream): LogRow {
const { line, timestamp } = entry;
const { labels } = stream;
@@ -44,16 +26,15 @@ export function processEntry(entry: { line: string; timestamp: string }, stream)
const time = moment(timestamp);
const timeFromNow = time.fromNow();
const timeLocal = time.format('YYYY-MM-DD HH:mm:ss');
- const searchMatches = getSearchMatches(line, stream.search);
const logLevel = getLogLevel(line);
return {
key,
logLevel,
- searchMatches,
timeFromNow,
timeLocal,
entry: line,
+ searchWords: [stream.search],
timestamp: timestamp,
};
}
diff --git a/public/app/plugins/datasource/mixed/datasource.ts b/public/app/plugins/datasource/mixed/datasource.ts
index bfdfcd61c77..6018329093e 100644
--- a/public/app/plugins/datasource/mixed/datasource.ts
+++ b/public/app/plugins/datasource/mixed/datasource.ts
@@ -13,14 +13,14 @@ class MixedDatasource {
return this.$q([]);
}
- return this.datasourceSrv.get(dsName).then(function(ds) {
+ return this.datasourceSrv.get(dsName).then(ds => {
const opt = angular.copy(options);
opt.targets = targets;
return ds.query(opt);
});
});
- return this.$q.all(promises).then(function(results) {
+ return this.$q.all(promises).then(results => {
return { data: _.flatten(_.map(results, 'data')) };
});
}
diff --git a/public/app/plugins/datasource/mssql/config_ctrl.ts b/public/app/plugins/datasource/mssql/config_ctrl.ts
new file mode 100644
index 00000000000..c80d657a914
--- /dev/null
+++ b/public/app/plugins/datasource/mssql/config_ctrl.ts
@@ -0,0 +1,10 @@
+export class MssqlConfigCtrl {
+ static templateUrl = 'partials/config.html';
+
+ current: any;
+
+ /** @ngInject */
+ constructor($scope) {
+ this.current.jsonData.encrypt = this.current.jsonData.encrypt || 'false';
+ }
+}
diff --git a/public/app/plugins/datasource/mssql/datasource.ts b/public/app/plugins/datasource/mssql/datasource.ts
index f30ea4c97fe..23aa5504d3e 100644
--- a/public/app/plugins/datasource/mssql/datasource.ts
+++ b/public/app/plugins/datasource/mssql/datasource.ts
@@ -5,12 +5,14 @@ export class MssqlDatasource {
id: any;
name: any;
responseParser: ResponseParser;
+ interval: string;
- /** @ngInject **/
+ /** @ngInject */
constructor(instanceSettings, private backendSrv, private $q, private templateSrv) {
this.name = instanceSettings.name;
this.id = instanceSettings.id;
this.responseParser = new ResponseParser(this.$q);
+ this.interval = (instanceSettings.jsonData || {}).timeInterval;
}
interpolateVariable(value, variable) {
@@ -26,7 +28,7 @@ export class MssqlDatasource {
return value;
}
- const quotedValues = _.map(value, function(val) {
+ const quotedValues = _.map(value, val => {
if (typeof value === 'number') {
return value;
}
diff --git a/public/app/plugins/datasource/mssql/module.ts b/public/app/plugins/datasource/mssql/module.ts
index a2e1e923bc6..bf46b6d0947 100644
--- a/public/app/plugins/datasource/mssql/module.ts
+++ b/public/app/plugins/datasource/mssql/module.ts
@@ -1,9 +1,6 @@
import { MssqlDatasource } from './datasource';
import { MssqlQueryCtrl } from './query_ctrl';
-
-class MssqlConfigCtrl {
- static templateUrl = 'partials/config.html';
-}
+import { MssqlConfigCtrl } from './config_ctrl';
const defaultQuery = `SELECT
as time,
@@ -21,7 +18,7 @@ class MssqlAnnotationsQueryCtrl {
annotation: any;
- /** @ngInject **/
+ /** @ngInject */
constructor() {
this.annotation.rawQuery = this.annotation.rawQuery || defaultQuery;
}
diff --git a/public/app/plugins/datasource/mssql/partials/config.html b/public/app/plugins/datasource/mssql/partials/config.html
index 7f9dc03f286..db76f60e5e3 100644
--- a/public/app/plugins/datasource/mssql/partials/config.html
+++ b/public/app/plugins/datasource/mssql/partials/config.html
@@ -27,6 +27,68 @@
reset
+
+
+
+
+
+
+ Determines whether or to which extent a secure SSL TCP/IP connection will be negotiated with the server.
+
+
disable - Data sent between client and server is not encrypted.
+
false - Data sent between client and server is not encrypted beyond the login packet. (default)
+
true - Data sent between client and server is encrypted.
+
+ If you're using an older version of Microsoft SQL Server like 2008 and 2008R2 you may need to disable encryption to be able to connect.
+
+
+
+
+
+Connection limits
+
+
+
+ Max open
+
+
+ The maximum number of open connections to the database. If Max idle connections is greater than 0 and the
+ Max open connections is less than Max idle connections, then Max idle connections will be
+ reduced to match the Max open connections limit. If set to 0, there is no limit on the number of open
+ connections.
+
+
+
+ Max idle
+
+
+ The maximum number of connections in the idle connection pool. If Max open connections is greater than 0 but
+ less than the Max idle connections, then the Max idle connections will be reduced to match the
+ Max open connections limit. If set to 0, no idle connections are retained.
+
+
+
+ Max lifetime
+
+
+ The maximum amount of time in seconds a connection may be reused. If set to 0, connections are reused forever.
+
+
+
+
+
MSSQL details
+
+
+
+
+ Min time interval
+
+
+ A lower limit for the auto group by time interval. Recommended to be set to write frequency,
+ for example 1m if your data is written every minute.
+
+
+
diff --git a/public/app/plugins/datasource/mssql/partials/query.editor.html b/public/app/plugins/datasource/mssql/partials/query.editor.html
index 4b0a46b6412..ba3bb6a4b82 100644
--- a/public/app/plugins/datasource/mssql/partials/query.editor.html
+++ b/public/app/plugins/datasource/mssql/partials/query.editor.html
@@ -45,6 +45,8 @@ Optional:
- If multiple value columns are returned the metric column is used as prefix.
- If no column named metric is found the column name of the value column is used as series name
+Resultsets of time series queries need to be sorted by time.
+
Table:
- return any set of columns
diff --git a/public/app/plugins/datasource/mssql/plugin.json b/public/app/plugins/datasource/mssql/plugin.json
index ac5ea49ebe9..a3df148bc2b 100644
--- a/public/app/plugins/datasource/mssql/plugin.json
+++ b/public/app/plugins/datasource/mssql/plugin.json
@@ -17,5 +17,10 @@
"alerting": true,
"annotations": true,
- "metrics": true
+ "metrics": true,
+
+ "queryOptions": {
+ "minInterval": true
+ }
+
}
diff --git a/public/app/plugins/datasource/mssql/query_ctrl.ts b/public/app/plugins/datasource/mssql/query_ctrl.ts
index 1b64a571c6c..7598ca292fe 100644
--- a/public/app/plugins/datasource/mssql/query_ctrl.ts
+++ b/public/app/plugins/datasource/mssql/query_ctrl.ts
@@ -33,7 +33,7 @@ export class MssqlQueryCtrl extends QueryCtrl {
lastQueryError: string;
showHelp: boolean;
- /** @ngInject **/
+ /** @ngInject */
constructor($scope, $injector) {
super($scope, $injector);
diff --git a/public/app/plugins/datasource/mssql/specs/datasource.test.ts b/public/app/plugins/datasource/mssql/specs/datasource.test.ts
index 0308717775b..0dd496bfe59 100644
--- a/public/app/plugins/datasource/mssql/specs/datasource.test.ts
+++ b/public/app/plugins/datasource/mssql/specs/datasource.test.ts
@@ -4,20 +4,20 @@ import { TemplateSrvStub } from 'test/specs/helpers';
import { CustomVariable } from 'app/features/templating/custom_variable';
import q from 'q';
-describe('MSSQLDatasource', function() {
+describe('MSSQLDatasource', () => {
const ctx: any = {
backendSrv: {},
templateSrv: new TemplateSrvStub(),
};
- beforeEach(function() {
+ beforeEach(() => {
ctx.$q = q;
ctx.instanceSettings = { name: 'mssql' };
ctx.ds = new MssqlDatasource(ctx.instanceSettings, ctx.backendSrv, ctx.$q, ctx.templateSrv);
});
- describe('When performing annotationQuery', function() {
+ describe('When performing annotationQuery', () => {
let results;
const annotationName = 'MyAnno';
@@ -61,7 +61,7 @@ describe('MSSQLDatasource', function() {
});
});
- it('should return annotation list', function() {
+ it('should return annotation list', () => {
expect(results.length).toBe(3);
expect(results[0].text).toBe('some text');
@@ -75,7 +75,7 @@ describe('MSSQLDatasource', function() {
});
});
- describe('When performing metricFindQuery', function() {
+ describe('When performing metricFindQuery', () => {
let results;
const query = 'select * from atable';
const response = {
@@ -95,24 +95,24 @@ describe('MSSQLDatasource', function() {
},
};
- beforeEach(function() {
- ctx.backendSrv.datasourceRequest = function(options) {
+ beforeEach(() => {
+ ctx.backendSrv.datasourceRequest = options => {
return ctx.$q.when({ data: response, status: 200 });
};
- return ctx.ds.metricFindQuery(query).then(function(data) {
+ return ctx.ds.metricFindQuery(query).then(data => {
results = data;
});
});
- it('should return list of all column values', function() {
+ it('should return list of all column values', () => {
expect(results.length).toBe(6);
expect(results[0].text).toBe('aTitle');
expect(results[5].text).toBe('some text3');
});
});
- describe('When performing metricFindQuery with key, value columns', function() {
+ describe('When performing metricFindQuery with key, value columns', () => {
let results;
const query = 'select * from atable';
const response = {
@@ -132,17 +132,17 @@ describe('MSSQLDatasource', function() {
},
};
- beforeEach(function() {
- ctx.backendSrv.datasourceRequest = function(options) {
+ beforeEach(() => {
+ ctx.backendSrv.datasourceRequest = options => {
return ctx.$q.when({ data: response, status: 200 });
};
- return ctx.ds.metricFindQuery(query).then(function(data) {
+ return ctx.ds.metricFindQuery(query).then(data => {
results = data;
});
});
- it('should return list of as text, value', function() {
+ it('should return list of as text, value', () => {
expect(results.length).toBe(3);
expect(results[0].text).toBe('aTitle');
expect(results[0].value).toBe('value1');
@@ -151,7 +151,7 @@ describe('MSSQLDatasource', function() {
});
});
- describe('When performing metricFindQuery with key, value columns and with duplicate keys', function() {
+ describe('When performing metricFindQuery with key, value columns and with duplicate keys', () => {
let results;
const query = 'select * from atable';
const response = {
@@ -171,17 +171,17 @@ describe('MSSQLDatasource', function() {
},
};
- beforeEach(function() {
- ctx.backendSrv.datasourceRequest = function(options) {
+ beforeEach(() => {
+ ctx.backendSrv.datasourceRequest = options => {
return ctx.$q.when({ data: response, status: 200 });
};
- return ctx.ds.metricFindQuery(query).then(function(data) {
+ return ctx.ds.metricFindQuery(query).then(data => {
results = data;
});
});
- it('should return list of unique keys', function() {
+ it('should return list of unique keys', () => {
expect(results.length).toBe(1);
expect(results[0].text).toBe('aTitle');
expect(results[0].value).toBe('same');
@@ -189,7 +189,7 @@ describe('MSSQLDatasource', function() {
});
describe('When interpolating variables', () => {
- beforeEach(function() {
+ beforeEach(() => {
ctx.variable = new CustomVariable({}, {});
});
diff --git a/public/app/plugins/datasource/mysql/datasource.ts b/public/app/plugins/datasource/mysql/datasource.ts
index 7b112ef4336..23bee7dbb6e 100644
--- a/public/app/plugins/datasource/mysql/datasource.ts
+++ b/public/app/plugins/datasource/mysql/datasource.ts
@@ -7,6 +7,7 @@ export class MysqlDatasource {
name: any;
responseParser: ResponseParser;
queryModel: MysqlQuery;
+ interval: string;
/** @ngInject **/
constructor(instanceSettings, private backendSrv, private $q, private templateSrv, private timeSrv) {
@@ -14,6 +15,7 @@ export class MysqlDatasource {
this.id = instanceSettings.id;
this.responseParser = new ResponseParser(this.$q);
this.queryModel = new MysqlQuery({});
+ this.interval = (instanceSettings.jsonData || {}).timeInterval;
}
interpolateVariable(value, variable) {
diff --git a/public/app/plugins/datasource/mysql/module.ts b/public/app/plugins/datasource/mysql/module.ts
index bb5b5bd9056..2d8caf17af4 100644
--- a/public/app/plugins/datasource/mysql/module.ts
+++ b/public/app/plugins/datasource/mysql/module.ts
@@ -20,7 +20,7 @@ class MysqlAnnotationsQueryCtrl {
annotation: any;
- /** @ngInject **/
+ /** @ngInject */
constructor() {
this.annotation.rawQuery = this.annotation.rawQuery || defaultQuery;
}
diff --git a/public/app/plugins/datasource/mysql/partials/config.html b/public/app/plugins/datasource/mysql/partials/config.html
index 8cbeece71dd..a35633c626a 100644
--- a/public/app/plugins/datasource/mysql/partials/config.html
+++ b/public/app/plugins/datasource/mysql/partials/config.html
@@ -24,6 +24,53 @@
+Connection limits
+
+
+
+ Max open
+
+
+ The maximum number of open connections to the database. If Max idle connections is greater than 0 and the
+ Max open connections is less than Max idle connections, then Max idle connections will be
+ reduced to match the Max open connections limit. If set to 0, there is no limit on the number of open
+ connections.
+
+
+
+ Max idle
+
+
+ The maximum number of connections in the idle connection pool. If Max open connections is greater than 0 but
+ less than the Max idle connections, then the Max idle connections will be reduced to match the
+ Max open connections limit. If set to 0, no idle connections are retained.
+
+
+
+ Max lifetime
+
+
+ The maximum amount of time in seconds a connection may be reused. If set to 0, connections are reused forever.
+ This should always be lower than configured wait_timeout in MySQL.
+
+
+
+
+
MySQL details
+
+
+
+
+ Min time interval
+
+
+ A lower limit for the auto group by time interval. Recommended to be set to write frequency,
+ for example 1m if your data is written every minute.
+
+
+
+
+
User Permission
diff --git a/public/app/plugins/datasource/mysql/partials/query.editor.html b/public/app/plugins/datasource/mysql/partials/query.editor.html
index 0c630947657..d01b201b748 100644
--- a/public/app/plugins/datasource/mysql/partials/query.editor.html
+++ b/public/app/plugins/datasource/mysql/partials/query.editor.html
@@ -137,6 +137,8 @@ Optional:
- If multiple value columns are returned the metric column is used as prefix.
- If no column named metric is found the column name of the value column is used as series name
+Resultsets of time series queries need to be sorted by time.
+
Table:
- return any set of columns
diff --git a/public/app/plugins/datasource/mysql/plugin.json b/public/app/plugins/datasource/mysql/plugin.json
index 363b9364016..f3a8efe267e 100644
--- a/public/app/plugins/datasource/mysql/plugin.json
+++ b/public/app/plugins/datasource/mysql/plugin.json
@@ -18,5 +18,10 @@
"alerting": true,
"annotations": true,
- "metrics": true
+ "metrics": true,
+
+ "queryOptions": {
+ "minInterval": true
+ }
+
}
diff --git a/public/app/plugins/datasource/mysql/specs/datasource.test.ts b/public/app/plugins/datasource/mysql/specs/datasource.test.ts
index 163f3afe671..cc1e54ac496 100644
--- a/public/app/plugins/datasource/mysql/specs/datasource.test.ts
+++ b/public/app/plugins/datasource/mysql/specs/datasource.test.ts
@@ -2,7 +2,7 @@ import moment from 'moment';
import { MysqlDatasource } from '../datasource';
import { CustomVariable } from 'app/features/templating/custom_variable';
-describe('MySQLDatasource', function() {
+describe('MySQLDatasource', () => {
const instanceSettings = { name: 'mysql' };
const backendSrv = {};
const templateSrv = {
@@ -28,7 +28,7 @@ describe('MySQLDatasource', function() {
ctx.ds = new MysqlDatasource(instanceSettings, backendSrv, {}, templateSrv, ctx.timeSrvMock);
});
- describe('When performing annotationQuery', function() {
+ describe('When performing annotationQuery', () => {
let results;
const annotationName = 'MyAnno';
@@ -62,16 +62,16 @@ describe('MySQLDatasource', function() {
},
};
- beforeEach(function() {
+ beforeEach(() => {
ctx.backendSrv.datasourceRequest = jest.fn(options => {
return Promise.resolve({ data: response, status: 200 });
});
- ctx.ds.annotationQuery(options).then(function(data) {
+ ctx.ds.annotationQuery(options).then(data => {
results = data;
});
});
- it('should return annotation list', function() {
+ it('should return annotation list', () => {
expect(results.length).toBe(3);
expect(results[0].text).toBe('some text');
@@ -85,7 +85,7 @@ describe('MySQLDatasource', function() {
});
});
- describe('When performing metricFindQuery', function() {
+ describe('When performing metricFindQuery', () => {
let results;
const query = 'select * from atable';
const response = {
@@ -105,23 +105,23 @@ describe('MySQLDatasource', function() {
},
};
- beforeEach(function() {
+ beforeEach(() => {
ctx.backendSrv.datasourceRequest = jest.fn(options => {
return Promise.resolve({ data: response, status: 200 });
});
- ctx.ds.metricFindQuery(query).then(function(data) {
+ ctx.ds.metricFindQuery(query).then(data => {
results = data;
});
});
- it('should return list of all column values', function() {
+ it('should return list of all column values', () => {
expect(results.length).toBe(6);
expect(results[0].text).toBe('aTitle');
expect(results[5].text).toBe('some text3');
});
});
- describe('When performing metricFindQuery with key, value columns', function() {
+ describe('When performing metricFindQuery with key, value columns', () => {
let results;
const query = 'select * from atable';
const response = {
@@ -141,16 +141,16 @@ describe('MySQLDatasource', function() {
},
};
- beforeEach(function() {
+ beforeEach(() => {
ctx.backendSrv.datasourceRequest = jest.fn(options => {
return Promise.resolve({ data: response, status: 200 });
});
- ctx.ds.metricFindQuery(query).then(function(data) {
+ ctx.ds.metricFindQuery(query).then(data => {
results = data;
});
});
- it('should return list of as text, value', function() {
+ it('should return list of as text, value', () => {
expect(results.length).toBe(3);
expect(results[0].text).toBe('aTitle');
expect(results[0].value).toBe('value1');
@@ -159,7 +159,7 @@ describe('MySQLDatasource', function() {
});
});
- describe('When performing metricFindQuery with key, value columns and with duplicate keys', function() {
+ describe('When performing metricFindQuery with key, value columns and with duplicate keys', () => {
let results;
const query = 'select * from atable';
const response = {
@@ -179,16 +179,16 @@ describe('MySQLDatasource', function() {
},
};
- beforeEach(function() {
+ beforeEach(() => {
ctx.backendSrv.datasourceRequest = jest.fn(options => {
return Promise.resolve({ data: response, status: 200 });
});
- ctx.ds.metricFindQuery(query).then(function(data) {
+ ctx.ds.metricFindQuery(query).then(data => {
results = data;
});
});
- it('should return list of unique keys', function() {
+ it('should return list of unique keys', () => {
expect(results.length).toBe(1);
expect(results[0].text).toBe('aTitle');
expect(results[0].value).toBe('same');
@@ -196,7 +196,7 @@ describe('MySQLDatasource', function() {
});
describe('When interpolating variables', () => {
- beforeEach(function() {
+ beforeEach(() => {
ctx.variable = new CustomVariable({}, {});
});
diff --git a/public/app/plugins/datasource/opentsdb/datasource.ts b/public/app/plugins/datasource/opentsdb/datasource.ts
index 699c5f4dad3..772f2aa7ff9 100644
--- a/public/app/plugins/datasource/opentsdb/datasource.ts
+++ b/public/app/plugins/datasource/opentsdb/datasource.ts
@@ -10,7 +10,6 @@ export default class OpenTsDatasource {
basicAuth: any;
tsdbVersion: any;
tsdbResolution: any;
- supportMetrics: any;
tagKeys: any;
aggregatorsPromise: any;
@@ -26,7 +25,6 @@ export default class OpenTsDatasource {
instanceSettings.jsonData = instanceSettings.jsonData || {};
this.tsdbVersion = instanceSettings.jsonData.tsdbVersion || 1;
this.tsdbResolution = instanceSettings.jsonData.tsdbResolution || 1;
- this.supportMetrics = true;
this.tagKeys = {};
this.aggregatorsPromise = null;
@@ -39,15 +37,12 @@ export default class OpenTsDatasource {
const end = this.convertToTSDBTime(options.rangeRaw.to, true);
const qs = [];
- _.each(
- options.targets,
- function(target) {
- if (!target.metric) {
- return;
- }
- qs.push(this.convertTargetToQuery(target, options, this.tsdbVersion));
- }.bind(this)
- );
+ _.each(options.targets, target => {
+ if (!target.metric) {
+ return;
+ }
+ qs.push(this.convertTargetToQuery(target, options, this.tsdbVersion));
+ });
const queries = _.compact(qs);
@@ -59,46 +54,35 @@ export default class OpenTsDatasource {
}
const groupByTags = {};
- _.each(queries, function(query) {
+ _.each(queries, query => {
if (query.filters && query.filters.length > 0) {
- _.each(query.filters, function(val) {
+ _.each(query.filters, val => {
groupByTags[val.tagk] = true;
});
} else {
- _.each(query.tags, function(val, key) {
+ _.each(query.tags, (val, key) => {
groupByTags[key] = true;
});
}
});
- options.targets = _.filter(options.targets, function(query) {
+ options.targets = _.filter(options.targets, query => {
return query.hide !== true;
});
- return this.performTimeSeriesQuery(queries, start, end).then(
- function(response) {
- const metricToTargetMapping = this.mapMetricsToTargets(response.data, options, this.tsdbVersion);
- const result = _.map(
- response.data,
- function(metricData, index) {
- index = metricToTargetMapping[index];
- if (index === -1) {
- index = 0;
- }
- this._saveTagKeys(metricData);
-
- return this.transformMetricData(
- metricData,
- groupByTags,
- options.targets[index],
- options,
- this.tsdbResolution
- );
- }.bind(this)
- );
- return { data: result };
- }.bind(this)
- );
+ return this.performTimeSeriesQuery(queries, start, end).then(response => {
+ const metricToTargetMapping = this.mapMetricsToTargets(response.data, options, this.tsdbVersion);
+ const result = _.map(response.data, (metricData, index) => {
+ index = metricToTargetMapping[index];
+ if (index === -1) {
+ index = 0;
+ }
+ this._saveTagKeys(metricData);
+
+ return this.transformMetricData(metricData, groupByTags, options.targets[index], options, this.tsdbResolution);
+ });
+ return { data: result };
+ });
}
annotationQuery(options) {
@@ -111,33 +95,31 @@ export default class OpenTsDatasource {
const queries = _.compact(qs);
- return this.performTimeSeriesQuery(queries, start, end).then(
- function(results) {
- if (results.data[0]) {
- var annotationObject = results.data[0].annotations;
- if (options.annotation.isGlobal) {
- annotationObject = results.data[0].globalAnnotations;
- }
- if (annotationObject) {
- _.each(annotationObject, function(annotation) {
- const event = {
- text: annotation.description,
- time: Math.floor(annotation.startTime) * 1000,
- annotation: options.annotation,
- };
-
- eventList.push(event);
- });
- }
+ return this.performTimeSeriesQuery(queries, start, end).then(results => {
+ if (results.data[0]) {
+ let annotationObject = results.data[0].annotations;
+ if (options.annotation.isGlobal) {
+ annotationObject = results.data[0].globalAnnotations;
}
- return eventList;
- }.bind(this)
- );
+ if (annotationObject) {
+ _.each(annotationObject, annotation => {
+ const event = {
+ text: annotation.description,
+ time: Math.floor(annotation.startTime) * 1000,
+ annotation: options.annotation,
+ };
+
+ eventList.push(event);
+ });
+ }
+ }
+ return eventList;
+ });
}
targetContainsTemplate(target) {
if (target.filters && target.filters.length > 0) {
- for (var i = 0; i < target.filters.length; i++) {
+ for (let i = 0; i < target.filters.length; i++) {
if (this.templateSrv.variableExists(target.filters[i].filter)) {
return true;
}
@@ -156,7 +138,7 @@ export default class OpenTsDatasource {
}
performTimeSeriesQuery(queries, start, end) {
- var msResolution = false;
+ let msResolution = false;
if (this.tsdbResolution === 2) {
msResolution = true;
}
@@ -191,7 +173,7 @@ export default class OpenTsDatasource {
_saveTagKeys(metricData) {
const tagKeys = Object.keys(metricData.tags);
- _.each(metricData.aggregateTags, function(tag) {
+ _.each(metricData.aggregateTags, tag => {
tagKeys.push(tag);
});
@@ -199,7 +181,7 @@ export default class OpenTsDatasource {
}
_performSuggestQuery(query, type) {
- return this._get('/api/suggest', { type: type, q: query, max: 1000 }).then(function(result) {
+ return this._get('/api/suggest', { type: type, q: query, max: 1000 }).then(result => {
return result.data;
});
}
@@ -209,11 +191,11 @@ export default class OpenTsDatasource {
return this.$q.when([]);
}
- const keysArray = keys.split(',').map(function(key) {
+ const keysArray = keys.split(',').map(key => {
return key.trim();
});
const key = keysArray[0];
- var keysQuery = key + '=*';
+ let keysQuery = key + '=*';
if (keysArray.length > 1) {
keysQuery += ',' + keysArray.splice(1).join(',');
@@ -221,10 +203,10 @@ export default class OpenTsDatasource {
const m = metric + '{' + keysQuery + '}';
- return this._get('/api/search/lookup', { m: m, limit: 3000 }).then(function(result) {
+ return this._get('/api/search/lookup', { m: m, limit: 3000 }).then(result => {
result = result.data.results;
const tagvs = [];
- _.each(result, function(r) {
+ _.each(result, r => {
if (tagvs.indexOf(r.tags[key]) === -1) {
tagvs.push(r.tags[key]);
}
@@ -238,11 +220,11 @@ export default class OpenTsDatasource {
return this.$q.when([]);
}
- return this._get('/api/search/lookup', { m: metric, limit: 1000 }).then(function(result) {
+ return this._get('/api/search/lookup', { m: metric, limit: 1000 }).then(result => {
result = result.data.results;
const tagks = [];
- _.each(result, function(r) {
- _.each(r.tags, function(tagv, tagk) {
+ _.each(result, r => {
+ _.each(r.tags, (tagv, tagk) => {
if (tagks.indexOf(tagk) === -1) {
tagks.push(tagk);
}
@@ -278,55 +260,55 @@ export default class OpenTsDatasource {
return this.$q.when([]);
}
- var interpolated;
+ let interpolated;
try {
interpolated = this.templateSrv.replace(query, {}, 'distributed');
} catch (err) {
return this.$q.reject(err);
}
- const responseTransform = function(result) {
- return _.map(result, function(value) {
+ const responseTransform = result => {
+ return _.map(result, value => {
return { text: value };
});
};
- const metrics_regex = /metrics\((.*)\)/;
- const tag_names_regex = /tag_names\((.*)\)/;
- const tag_values_regex = /tag_values\((.*?),\s?(.*)\)/;
- const tag_names_suggest_regex = /suggest_tagk\((.*)\)/;
- const tag_values_suggest_regex = /suggest_tagv\((.*)\)/;
+ const metricsRegex = /metrics\((.*)\)/;
+ const tagNamesRegex = /tag_names\((.*)\)/;
+ const tagValuesRegex = /tag_values\((.*?),\s?(.*)\)/;
+ const tagNamesSuggestRegex = /suggest_tagk\((.*)\)/;
+ const tagValuesSuggestRegex = /suggest_tagv\((.*)\)/;
- const metrics_query = interpolated.match(metrics_regex);
- if (metrics_query) {
- return this._performSuggestQuery(metrics_query[1], 'metrics').then(responseTransform);
+ const metricsQuery = interpolated.match(metricsRegex);
+ if (metricsQuery) {
+ return this._performSuggestQuery(metricsQuery[1], 'metrics').then(responseTransform);
}
- const tag_names_query = interpolated.match(tag_names_regex);
- if (tag_names_query) {
- return this._performMetricKeyLookup(tag_names_query[1]).then(responseTransform);
+ const tagNamesQuery = interpolated.match(tagNamesRegex);
+ if (tagNamesQuery) {
+ return this._performMetricKeyLookup(tagNamesQuery[1]).then(responseTransform);
}
- const tag_values_query = interpolated.match(tag_values_regex);
- if (tag_values_query) {
- return this._performMetricKeyValueLookup(tag_values_query[1], tag_values_query[2]).then(responseTransform);
+ const tagValuesQuery = interpolated.match(tagValuesRegex);
+ if (tagValuesQuery) {
+ return this._performMetricKeyValueLookup(tagValuesQuery[1], tagValuesQuery[2]).then(responseTransform);
}
- const tag_names_suggest_query = interpolated.match(tag_names_suggest_regex);
- if (tag_names_suggest_query) {
- return this._performSuggestQuery(tag_names_suggest_query[1], 'tagk').then(responseTransform);
+ const tagNamesSuggestQuery = interpolated.match(tagNamesSuggestRegex);
+ if (tagNamesSuggestQuery) {
+ return this._performSuggestQuery(tagNamesSuggestQuery[1], 'tagk').then(responseTransform);
}
- const tag_values_suggest_query = interpolated.match(tag_values_suggest_regex);
- if (tag_values_suggest_query) {
- return this._performSuggestQuery(tag_values_suggest_query[1], 'tagv').then(responseTransform);
+ const tagValuesSuggestQuery = interpolated.match(tagValuesSuggestRegex);
+ if (tagValuesSuggestQuery) {
+ return this._performSuggestQuery(tagValuesSuggestQuery[1], 'tagv').then(responseTransform);
}
return this.$q.when([]);
}
testDatasource() {
- return this._performSuggestQuery('cpu', 'metrics').then(function() {
+ return this._performSuggestQuery('cpu', 'metrics').then(() => {
return { status: 'success', message: 'Data source is working' };
});
}
@@ -336,7 +318,7 @@ export default class OpenTsDatasource {
return this.aggregatorsPromise;
}
- this.aggregatorsPromise = this._get('/api/aggregators').then(function(result) {
+ this.aggregatorsPromise = this._get('/api/aggregators').then(result => {
if (result.data && _.isArray(result.data)) {
return result.data.sort();
}
@@ -350,7 +332,7 @@ export default class OpenTsDatasource {
return this.filterTypesPromise;
}
- this.filterTypesPromise = this._get('/api/config/filters').then(function(result) {
+ this.filterTypesPromise = this._get('/api/config/filters').then(result => {
if (result.data) {
return Object.keys(result.data).sort();
}
@@ -365,7 +347,7 @@ export default class OpenTsDatasource {
// TSDB returns datapoints has a hash of ts => value.
// Can't use _.pairs(invert()) because it stringifies keys/values
- _.each(md.dps, function(v, k) {
+ _.each(md.dps, (v, k) => {
if (tsdbResolution === 2) {
dps.push([v, k * 1]);
} else {
@@ -379,17 +361,17 @@ export default class OpenTsDatasource {
createMetricLabel(md, target, groupByTags, options) {
if (target.alias) {
const scopedVars = _.clone(options.scopedVars || {});
- _.each(md.tags, function(value, key) {
+ _.each(md.tags, (value, key) => {
scopedVars['tag_' + key] = { value: value };
});
return this.templateSrv.replace(target.alias, scopedVars);
}
- var label = md.metric;
+ let label = md.metric;
const tagData = [];
if (!_.isEmpty(md.tags)) {
- _.each(_.toPairs(md.tags), function(tag) {
+ _.each(_.toPairs(md.tags), tag => {
if (_.has(groupByTags, tag[0])) {
tagData.push(tag[0] + '=' + tag[1]);
}
@@ -424,11 +406,11 @@ export default class OpenTsDatasource {
};
if (target.counterMax && target.counterMax.length) {
- query.rateOptions.counterMax = parseInt(target.counterMax);
+ query.rateOptions.counterMax = parseInt(target.counterMax, 10);
}
if (target.counterResetValue && target.counterResetValue.length) {
- query.rateOptions.resetValue = parseInt(target.counterResetValue);
+ query.rateOptions.resetValue = parseInt(target.counterResetValue, 10);
}
if (tsdbVersion >= 2) {
@@ -438,7 +420,7 @@ export default class OpenTsDatasource {
}
if (!target.disableDownsampling) {
- var interval = this.templateSrv.replace(target.downsampleInterval || options.interval);
+ let interval = this.templateSrv.replace(target.downsampleInterval || options.interval);
if (interval.match(/\.[0-9]+s/)) {
interval = parseFloat(interval) * 1000 + 'ms';
@@ -454,9 +436,9 @@ export default class OpenTsDatasource {
if (target.filters && target.filters.length > 0) {
query.filters = angular.copy(target.filters);
if (query.filters) {
- for (const filter_key in query.filters) {
- query.filters[filter_key].filter = this.templateSrv.replace(
- query.filters[filter_key].filter,
+ for (const filterKey in query.filters) {
+ query.filters[filterKey].filter = this.templateSrv.replace(
+ query.filters[filterKey].filter,
options.scopedVars,
'pipe'
);
@@ -465,8 +447,8 @@ export default class OpenTsDatasource {
} else {
query.tags = angular.copy(target.tags);
if (query.tags) {
- for (const tag_key in query.tags) {
- query.tags[tag_key] = this.templateSrv.replace(query.tags[tag_key], options.scopedVars, 'pipe');
+ for (const tagKey in query.tags) {
+ query.tags[tagKey] = this.templateSrv.replace(query.tags[tagKey], options.scopedVars, 'pipe');
}
}
}
@@ -479,7 +461,7 @@ export default class OpenTsDatasource {
}
mapMetricsToTargets(metrics, options, tsdbVersion) {
- var interpolatedTagValue, arrTagV;
+ let interpolatedTagValue, arrTagV;
return _.map(metrics, metricData => {
if (tsdbVersion === 3) {
return metricData.query.index;
diff --git a/public/app/plugins/datasource/opentsdb/query_ctrl.ts b/public/app/plugins/datasource/opentsdb/query_ctrl.ts
index e00a01ebd9f..2e6f1d54302 100644
--- a/public/app/plugins/datasource/opentsdb/query_ctrl.ts
+++ b/public/app/plugins/datasource/opentsdb/query_ctrl.ts
@@ -19,7 +19,7 @@ export class OpenTsQueryCtrl extends QueryCtrl {
addTagMode: boolean;
addFilterMode: boolean;
- /** @ngInject **/
+ /** @ngInject */
constructor($scope, $injector) {
super($scope, $injector);
@@ -88,7 +88,7 @@ export class OpenTsQueryCtrl extends QueryCtrl {
}
getTextValues(metricFindResult) {
- return _.map(metricFindResult, function(value) {
+ return _.map(metricFindResult, value => {
return value.text;
});
}
diff --git a/public/app/plugins/datasource/opentsdb/specs/datasource.test.ts b/public/app/plugins/datasource/opentsdb/specs/datasource.test.ts
index e7e53c0dd5b..a31f4f1fe3b 100644
--- a/public/app/plugins/datasource/opentsdb/specs/datasource.test.ts
+++ b/public/app/plugins/datasource/opentsdb/specs/datasource.test.ts
@@ -2,13 +2,13 @@ import OpenTsDatasource from '../datasource';
import $q from 'q';
describe('opentsdb', () => {
- const ctx = {
+ const ctx = {
backendSrv: {},
ds: {},
templateSrv: {
replace: str => str,
},
- };
+ } as any;
const instanceSettings = { url: '', jsonData: { tsdbVersion: 1 } };
beforeEach(() => {
@@ -20,16 +20,16 @@ describe('opentsdb', () => {
let requestOptions;
beforeEach(async () => {
- ctx.backendSrv.datasourceRequest = await function(options) {
+ ctx.backendSrv.datasourceRequest = await (options => {
requestOptions = options;
return Promise.resolve({
data: [{ target: 'prod1.count', datapoints: [[10, 1], [12, 1]] }],
});
- };
+ });
});
it('metrics() should generate api suggest query', () => {
- ctx.ctrl.metricFindQuery('metrics(pew)').then(function(data) {
+ ctx.ctrl.metricFindQuery('metrics(pew)').then(data => {
results = data;
});
expect(requestOptions.url).toBe('/api/suggest');
@@ -39,7 +39,7 @@ describe('opentsdb', () => {
});
it('tag_names(cpu) should generate lookup query', () => {
- ctx.ctrl.metricFindQuery('tag_names(cpu)').then(function(data) {
+ ctx.ctrl.metricFindQuery('tag_names(cpu)').then(data => {
results = data;
});
expect(requestOptions.url).toBe('/api/search/lookup');
@@ -47,7 +47,7 @@ describe('opentsdb', () => {
});
it('tag_values(cpu, test) should generate lookup query', () => {
- ctx.ctrl.metricFindQuery('tag_values(cpu, hostname)').then(function(data) {
+ ctx.ctrl.metricFindQuery('tag_values(cpu, hostname)').then(data => {
results = data;
});
expect(requestOptions.url).toBe('/api/search/lookup');
@@ -55,7 +55,7 @@ describe('opentsdb', () => {
});
it('tag_values(cpu, test) should generate lookup query', () => {
- ctx.ctrl.metricFindQuery('tag_values(cpu, hostname, env=$env)').then(function(data) {
+ ctx.ctrl.metricFindQuery('tag_values(cpu, hostname, env=$env)').then(data => {
results = data;
});
expect(requestOptions.url).toBe('/api/search/lookup');
@@ -63,7 +63,7 @@ describe('opentsdb', () => {
});
it('tag_values(cpu, test) should generate lookup query', () => {
- ctx.ctrl.metricFindQuery('tag_values(cpu, hostname, env=$env, region=$region)').then(function(data) {
+ ctx.ctrl.metricFindQuery('tag_values(cpu, hostname, env=$env, region=$region)').then(data => {
results = data;
});
expect(requestOptions.url).toBe('/api/search/lookup');
@@ -71,7 +71,7 @@ describe('opentsdb', () => {
});
it('suggest_tagk() should generate api suggest query', () => {
- ctx.ctrl.metricFindQuery('suggest_tagk(foo)').then(function(data) {
+ ctx.ctrl.metricFindQuery('suggest_tagk(foo)').then(data => {
results = data;
});
expect(requestOptions.url).toBe('/api/suggest');
@@ -80,7 +80,7 @@ describe('opentsdb', () => {
});
it('suggest_tagv() should generate api suggest query', () => {
- ctx.ctrl.metricFindQuery('suggest_tagv(bar)').then(function(data) {
+ ctx.ctrl.metricFindQuery('suggest_tagv(bar)').then(data => {
results = data;
});
expect(requestOptions.url).toBe('/api/suggest');
diff --git a/public/app/plugins/datasource/opentsdb/specs/query_ctrl.test.ts b/public/app/plugins/datasource/opentsdb/specs/query_ctrl.test.ts
index 6fdcd29aecd..eaf20059900 100644
--- a/public/app/plugins/datasource/opentsdb/specs/query_ctrl.test.ts
+++ b/public/app/plugins/datasource/opentsdb/specs/query_ctrl.test.ts
@@ -1,14 +1,14 @@
import { OpenTsQueryCtrl } from '../query_ctrl';
describe('OpenTsQueryCtrl', () => {
- const ctx = {
+ const ctx = {
target: { target: '' },
datasource: {
tsdbVersion: '',
getAggregators: () => Promise.resolve([]),
getFilterTypes: () => Promise.resolve([]),
},
- };
+ } as any;
ctx.panelCtrl = {
panel: {
diff --git a/public/app/plugins/datasource/postgres/config_ctrl.ts b/public/app/plugins/datasource/postgres/config_ctrl.ts
index 7c7745aeaf1..a396b9f9aa4 100644
--- a/public/app/plugins/datasource/postgres/config_ctrl.ts
+++ b/public/app/plugins/datasource/postgres/config_ctrl.ts
@@ -7,7 +7,7 @@ export class PostgresConfigCtrl {
datasourceSrv: any;
showTimescaleDBHelp: boolean;
- /** @ngInject **/
+ /** @ngInject */
constructor($scope, datasourceSrv) {
this.datasourceSrv = datasourceSrv;
this.current.jsonData.sslmode = this.current.jsonData.sslmode || 'verify-full';
@@ -34,8 +34,8 @@ export class PostgresConfigCtrl {
});
}
- let major = Math.trunc(version / 100);
- let minor = version % 100;
+ const major = Math.trunc(version / 100);
+ const minor = version % 100;
let name = String(major);
if (version < 1000) {
name = String(major) + '.' + String(minor);
diff --git a/public/app/plugins/datasource/postgres/datasource.ts b/public/app/plugins/datasource/postgres/datasource.ts
index 918a4859203..13948c5d793 100644
--- a/public/app/plugins/datasource/postgres/datasource.ts
+++ b/public/app/plugins/datasource/postgres/datasource.ts
@@ -8,17 +8,19 @@ export class PostgresDatasource {
jsonData: any;
responseParser: ResponseParser;
queryModel: PostgresQuery;
+ interval: string;
- /** @ngInject **/
+ /** @ngInject */
constructor(instanceSettings, private backendSrv, private $q, private templateSrv, private timeSrv) {
this.name = instanceSettings.name;
this.id = instanceSettings.id;
this.jsonData = instanceSettings.jsonData;
this.responseParser = new ResponseParser(this.$q);
this.queryModel = new PostgresQuery({});
+ this.interval = (instanceSettings.jsonData || {}).timeInterval;
}
- interpolateVariable(value, variable) {
+ interpolateVariable = (value, variable) => {
if (typeof value === 'string') {
if (variable.multi || variable.includeAll) {
return this.queryModel.quoteLiteral(value);
@@ -35,13 +37,13 @@ export class PostgresDatasource {
return this.queryModel.quoteLiteral(v);
});
return quotedValues.join(',');
- }
+ };
query(options) {
const queries = _.filter(options.targets, target => {
return target.hide !== true;
}).map(target => {
- let queryModel = new PostgresQuery(target, this.templateSrv, options.scopedVars);
+ const queryModel = new PostgresQuery(target, this.templateSrv, options.scopedVars);
return {
refId: target.refId,
@@ -110,7 +112,7 @@ export class PostgresDatasource {
format: 'table',
};
- let range = this.timeSrv.timeRange();
+ const range = this.timeSrv.timeRange();
const data = {
queries: [interpolatedQuery],
from: range.from.valueOf().toString(),
diff --git a/public/app/plugins/datasource/postgres/meta_query.ts b/public/app/plugins/datasource/postgres/meta_query.ts
index cd251ca5e49..fd13f3b4482 100644
--- a/public/app/plugins/datasource/postgres/meta_query.ts
+++ b/public/app/plugins/datasource/postgres/meta_query.ts
@@ -47,11 +47,9 @@ SELECT
ORDER BY ordinal_position LIMIT 1
) AS value_column
FROM information_schema.tables t
-WHERE
- table_schema IN (
- SELECT CASE WHEN trim(unnest) = '"$user"' THEN user ELSE trim(unnest) END
- FROM unnest(string_to_array(current_setting('search_path'),','))
- ) AND
+WHERE `;
+ query += this.buildSchemaConstraint();
+ query += ` AND
EXISTS
( SELECT 1
FROM information_schema.columns c
@@ -74,10 +72,16 @@ LIMIT 1
}
buildSchemaConstraint() {
- let query = `
+ const query = `
table_schema IN (
- SELECT CASE WHEN trim(unnest) = \'"$user"\' THEN user ELSE trim(unnest) END
- FROM unnest(string_to_array(current_setting(\'search_path\'),\',\'))
+ SELECT
+ CASE WHEN trim(s[i]) = '"$user"' THEN user ELSE trim(s[i]) END
+ FROM
+ generate_series(
+ array_lower(string_to_array(current_setting('search_path'),','),1),
+ array_upper(string_to_array(current_setting('search_path'),','),1)
+ ) as i,
+ string_to_array(current_setting('search_path'),',') s
)`;
return query;
}
@@ -87,16 +91,12 @@ table_schema IN (
// check for schema qualified table
if (table.includes('.')) {
- let parts = table.split('.');
+ const parts = table.split('.');
query = 'table_schema = ' + this.quoteIdentAsLiteral(parts[0]);
query += ' AND table_name = ' + this.quoteIdentAsLiteral(parts[1]);
return query;
} else {
- query = `
-table_schema IN (
- SELECT CASE WHEN trim(unnest) = \'"$user"\' THEN user ELSE trim(unnest) END
- FROM unnest(string_to_array(current_setting(\'search_path\'),\',\'))
-)`;
+ query = this.buildSchemaConstraint();
query += ' AND table_name = ' + this.quoteIdentAsLiteral(table);
return query;
@@ -144,23 +144,14 @@ table_schema IN (
let query = 'SELECT DISTINCT quote_literal(' + column + ')';
query += ' FROM ' + this.target.table;
query += ' WHERE $__timeFilter(' + this.target.timeColumn + ')';
+ query += ' AND ' + column + ' IS NOT NULL';
query += ' ORDER BY 1 LIMIT 100';
return query;
}
buildDatatypeQuery(column: string) {
- let query = `
-SELECT udt_name
-FROM information_schema.columns
-WHERE
- table_schema IN (
- SELECT schema FROM (
- SELECT CASE WHEN trim(unnest) = \'"$user"\' THEN user ELSE trim(unnest) END as schema
- FROM unnest(string_to_array(current_setting(\'search_path\'),\',\'))
- ) s
- WHERE EXISTS (SELECT 1 FROM information_schema.schemata WHERE schema_name = s.schema)
- )
-`;
+ let query = 'SELECT udt_name FROM information_schema.columns WHERE ';
+ query += this.buildSchemaConstraint();
query += ' AND table_name = ' + this.quoteIdentAsLiteral(this.target.table);
query += ' AND column_name = ' + this.quoteIdentAsLiteral(column);
return query;
diff --git a/public/app/plugins/datasource/postgres/module.ts b/public/app/plugins/datasource/postgres/module.ts
index b5b052f12b9..f81cdb7c371 100644
--- a/public/app/plugins/datasource/postgres/module.ts
+++ b/public/app/plugins/datasource/postgres/module.ts
@@ -17,7 +17,7 @@ class PostgresAnnotationsQueryCtrl {
annotation: any;
- /** @ngInject **/
+ /** @ngInject */
constructor() {
this.annotation.rawQuery = this.annotation.rawQuery || defaultQuery;
}
diff --git a/public/app/plugins/datasource/postgres/partials/config.html b/public/app/plugins/datasource/postgres/partials/config.html
index a4df858db7e..13b74d6b20e 100644
--- a/public/app/plugins/datasource/postgres/partials/config.html
+++ b/public/app/plugins/datasource/postgres/partials/config.html
@@ -38,6 +38,37 @@
+Connection limits
+
+
+
+ Max open
+
+
+ The maximum number of open connections to the database. If Max idle connections is greater than 0 and the
+ Max open connections is less than Max idle connections, then Max idle connections will be
+ reduced to match the Max open connections limit. If set to 0, there is no limit on the number of open
+ connections.
+
+
+
+ Max idle
+
+
+ The maximum number of connections in the idle connection pool. If Max open connections is greater than 0 but
+ less than the Max idle connections, then the Max idle connections will be reduced to match the
+ Max open connections limit. If set to 0, no idle connections are retained.
+
+
+
+ Max lifetime
+
+
+ The maximum amount of time in seconds a connection may be reused. If set to 0, connections are reused forever.
+
+
+
+
PostgreSQL details
@@ -61,6 +92,16 @@
+
+
+ Min time interval
+
+
+ A lower limit for the auto group by time interval. Recommended to be set to write frequency,
+ for example 1m if your data is written every minute.
+
+
+
diff --git a/public/app/plugins/datasource/postgres/partials/query.editor.html b/public/app/plugins/datasource/postgres/partials/query.editor.html
index fd944f4266c..6c3bf02cb51 100644
--- a/public/app/plugins/datasource/postgres/partials/query.editor.html
+++ b/public/app/plugins/datasource/postgres/partials/query.editor.html
@@ -143,6 +143,8 @@ Optional:
- If multiple value columns are returned the metric column is used as prefix.
- If no column named metric is found the column name of the value column is used as series name
+Resultsets of time series queries need to be sorted by time.
+
Table:
- return any set of columns
diff --git a/public/app/plugins/datasource/postgres/plugin.json b/public/app/plugins/datasource/postgres/plugin.json
index 2c2e1690a65..f236aa01b06 100644
--- a/public/app/plugins/datasource/postgres/plugin.json
+++ b/public/app/plugins/datasource/postgres/plugin.json
@@ -18,6 +18,10 @@
"alerting": true,
"annotations": true,
- "metrics": true
+ "metrics": true,
+
+ "queryOptions": {
+ "minInterval": true
+ }
}
diff --git a/public/app/plugins/datasource/postgres/postgres_query.ts b/public/app/plugins/datasource/postgres/postgres_query.ts
index 3e055f42f03..89518bb15e0 100644
--- a/public/app/plugins/datasource/postgres/postgres_query.ts
+++ b/public/app/plugins/datasource/postgres/postgres_query.ts
@@ -44,15 +44,15 @@ export default class PostgresQuery {
}
quoteIdentifier(value) {
- return '"' + value.replace(/"/g, '""') + '"';
+ return '"' + String(value).replace(/"/g, '""') + '"';
}
quoteLiteral(value) {
- return "'" + value.replace(/'/g, "''") + "'";
+ return "'" + String(value).replace(/'/g, "''") + "'";
}
escapeLiteral(value) {
- return value.replace(/'/g, "''");
+ return String(value).replace(/'/g, "''");
}
hasTimeGroup() {
@@ -73,12 +73,12 @@ export default class PostgresQuery {
return this.quoteLiteral(value);
}
- let escapedValues = _.map(value, this.quoteLiteral);
+ const escapedValues = _.map(value, this.quoteLiteral);
return escapedValues.join(',');
}
render(interpolate?) {
- let target = this.target;
+ const target = this.target;
// new query with no table set yet
if (!this.target.rawQuery && !('table' in this.target)) {
@@ -101,7 +101,7 @@ export default class PostgresQuery {
}
buildTimeColumn(alias = true) {
- let timeGroup = this.hasTimeGroup();
+ const timeGroup = this.hasTimeGroup();
let query;
let macro = '$__timeGroup';
@@ -139,7 +139,7 @@ export default class PostgresQuery {
buildValueColumns() {
let query = '';
- for (let column of this.target.select) {
+ for (const column of this.target.select) {
query += ',\n ' + this.buildValueColumn(column);
}
@@ -149,14 +149,14 @@ export default class PostgresQuery {
buildValueColumn(column) {
let query = '';
- let columnName = _.find(column, (g: any) => g.type === 'column');
+ const columnName = _.find(column, (g: any) => g.type === 'column');
query = columnName.params[0];
- let aggregate = _.find(column, (g: any) => g.type === 'aggregate' || g.type === 'percentile');
- let windows = _.find(column, (g: any) => g.type === 'window' || g.type === 'moving_window');
+ const aggregate = _.find(column, (g: any) => g.type === 'aggregate' || g.type === 'percentile');
+ const windows = _.find(column, (g: any) => g.type === 'window' || g.type === 'moving_window');
if (aggregate) {
- let func = aggregate.params[0];
+ const func = aggregate.params[0];
switch (aggregate.type) {
case 'aggregate':
if (func === 'first' || func === 'last') {
@@ -172,13 +172,13 @@ export default class PostgresQuery {
}
if (windows) {
- let overParts = [];
+ const overParts = [];
if (this.hasMetricColumn()) {
overParts.push('PARTITION BY ' + this.target.metricColumn);
}
overParts.push('ORDER BY ' + this.buildTimeColumn(false));
- let over = overParts.join(' ');
+ const over = overParts.join(' ');
let curr: string;
let prev: string;
switch (windows.type) {
@@ -187,7 +187,8 @@ export default class PostgresQuery {
case 'increase':
curr = query;
prev = 'lag(' + curr + ') OVER (' + over + ')';
- query = '(CASE WHEN ' + curr + ' >= ' + prev + ' THEN ' + curr + ' - ' + prev + ' ELSE ' + curr + ' END)';
+ query = '(CASE WHEN ' + curr + ' >= ' + prev + ' THEN ' + curr + ' - ' + prev;
+ query += ' WHEN ' + prev + ' IS NULL THEN NULL ELSE ' + curr + ' END)';
break;
case 'rate':
let timeColumn = this.target.timeColumn;
@@ -197,7 +198,8 @@ export default class PostgresQuery {
curr = query;
prev = 'lag(' + curr + ') OVER (' + over + ')';
- query = '(CASE WHEN ' + curr + ' >= ' + prev + ' THEN ' + curr + ' - ' + prev + ' ELSE ' + curr + ' END)';
+ query = '(CASE WHEN ' + curr + ' >= ' + prev + ' THEN ' + curr + ' - ' + prev;
+ query += ' WHEN ' + prev + ' IS NULL THEN NULL ELSE ' + curr + ' END)';
query += '/extract(epoch from ' + timeColumn + ' - lag(' + timeColumn + ') OVER (' + over + '))';
break;
default:
@@ -211,7 +213,7 @@ export default class PostgresQuery {
}
}
- let alias = _.find(column, (g: any) => g.type === 'alias');
+ const alias = _.find(column, (g: any) => g.type === 'alias');
if (alias) {
query += ' AS ' + this.quoteIdentifier(alias.params[0]);
}
@@ -221,7 +223,7 @@ export default class PostgresQuery {
buildWhereClause() {
let query = '';
- let conditions = _.map(this.target.where, (tag, index) => {
+ const conditions = _.map(this.target.where, (tag, index) => {
switch (tag.type) {
case 'macro':
return tag.name + '(' + this.target.timeColumn + ')';
@@ -244,7 +246,7 @@ export default class PostgresQuery {
let groupSection = '';
for (let i = 0; i < this.target.group.length; i++) {
- let part = this.target.group[i];
+ const part = this.target.group[i];
if (i > 0) {
groupSection += ', ';
}
@@ -279,6 +281,9 @@ export default class PostgresQuery {
query += this.buildGroupClause();
query += '\nORDER BY 1';
+ if (this.hasMetricColumn()) {
+ query += ',2';
+ }
return query;
}
diff --git a/public/app/plugins/datasource/postgres/query_ctrl.ts b/public/app/plugins/datasource/postgres/query_ctrl.ts
index 6102b0cd562..9343a260a9e 100644
--- a/public/app/plugins/datasource/postgres/query_ctrl.ts
+++ b/public/app/plugins/datasource/postgres/query_ctrl.ts
@@ -39,7 +39,7 @@ export class PostgresQueryCtrl extends QueryCtrl {
whereParts: SqlPart[];
groupAdd: any;
- /** @ngInject **/
+ /** @ngInject */
constructor($scope, $injector, private templateSrv, private $q, private uiSegmentSrv) {
super($scope, $injector);
this.target = this.target;
@@ -96,7 +96,7 @@ export class PostgresQueryCtrl extends QueryCtrl {
}
updateProjection() {
- this.selectParts = _.map(this.target.select, function(parts: any) {
+ this.selectParts = _.map(this.target.select, (parts: any) => {
return _.map(parts, sqlPart.create).filter(n => n);
});
this.whereParts = _.map(this.target.where, sqlPart.create).filter(n => n);
@@ -104,22 +104,22 @@ export class PostgresQueryCtrl extends QueryCtrl {
}
updatePersistedParts() {
- this.target.select = _.map(this.selectParts, function(selectParts) {
- return _.map(selectParts, function(part: any) {
+ this.target.select = _.map(this.selectParts, selectParts => {
+ return _.map(selectParts, (part: any) => {
return { type: part.def.type, datatype: part.datatype, params: part.params };
});
});
- this.target.where = _.map(this.whereParts, function(part: any) {
+ this.target.where = _.map(this.whereParts, (part: any) => {
return { type: part.def.type, datatype: part.datatype, name: part.name, params: part.params };
});
- this.target.group = _.map(this.groupParts, function(part: any) {
+ this.target.group = _.map(this.groupParts, (part: any) => {
return { type: part.def.type, datatype: part.datatype, params: part.params };
});
}
buildSelectMenu() {
this.selectMenu = [];
- let aggregates = {
+ const aggregates = {
text: 'Aggregate Functions',
value: 'aggregate',
submenu: [
@@ -143,7 +143,7 @@ export class PostgresQueryCtrl extends QueryCtrl {
// ordered set aggregates require postgres 9.4+
if (this.datasource.jsonData.postgresVersion >= 904) {
- let aggregates2 = {
+ const aggregates2 = {
text: 'Ordered-Set Aggregate Functions',
value: 'percentile',
submenu: [
@@ -154,7 +154,7 @@ export class PostgresQueryCtrl extends QueryCtrl {
this.selectMenu.push(aggregates2);
}
- let windows = {
+ const windows = {
text: 'Window Functions',
value: 'window',
submenu: [
@@ -187,7 +187,7 @@ export class PostgresQueryCtrl extends QueryCtrl {
}
resetPlusButton(button) {
- let plusButton = this.uiSegmentSrv.newPlusButton();
+ const plusButton = this.uiSegmentSrv.newPlusButton();
button.html = plusButton.html;
button.value = plusButton.value;
}
@@ -205,21 +205,21 @@ export class PostgresQueryCtrl extends QueryCtrl {
this.target.group = [];
this.updateProjection();
- let segment = this.uiSegmentSrv.newSegment('none');
+ const segment = this.uiSegmentSrv.newSegment('none');
this.metricColumnSegment.html = segment.html;
this.metricColumnSegment.value = segment.value;
this.target.metricColumn = 'none';
- let task1 = this.datasource.metricFindQuery(this.metaBuilder.buildColumnQuery('time')).then(result => {
+ const task1 = this.datasource.metricFindQuery(this.metaBuilder.buildColumnQuery('time')).then(result => {
// check if time column is still valid
if (result.length > 0 && !_.find(result, (r: any) => r.text === this.target.timeColumn)) {
- let segment = this.uiSegmentSrv.newSegment(result[0].text);
+ const segment = this.uiSegmentSrv.newSegment(result[0].text);
this.timeColumnSegment.html = segment.html;
this.timeColumnSegment.value = segment.value;
}
return this.timeColumnChanged(false);
});
- let task2 = this.datasource.metricFindQuery(this.metaBuilder.buildColumnQuery('value')).then(result => {
+ const task2 = this.datasource.metricFindQuery(this.metaBuilder.buildColumnQuery('value')).then(result => {
if (result.length > 0) {
this.target.select = [[{ type: 'column', params: [result[0].text] }]];
this.updateProjection();
@@ -301,7 +301,7 @@ export class PostgresQueryCtrl extends QueryCtrl {
transformToSegments(config) {
return results => {
- let segments = _.map(results, segment => {
+ const segments = _.map(results, segment => {
return this.uiSegmentSrv.newSegment({
value: segment.text,
expandable: segment.expandable,
@@ -309,7 +309,7 @@ export class PostgresQueryCtrl extends QueryCtrl {
});
if (config.addTemplateVars) {
- for (let variable of this.templateSrv.variables) {
+ for (const variable of this.templateSrv.variables) {
let value;
value = '$' + variable.name;
if (config.templateQuoter && variable.multi === false) {
@@ -355,7 +355,7 @@ export class PostgresQueryCtrl extends QueryCtrl {
switch (partType) {
case 'column':
- let parts = _.map(selectParts, function(part: any) {
+ const parts = _.map(selectParts, (part: any) => {
return sqlPart.create({ type: part.def.type, params: _.clone(part.params) });
});
this.selectParts.push(parts);
@@ -366,7 +366,7 @@ export class PostgresQueryCtrl extends QueryCtrl {
if (this.target.group.length === 0) {
this.addGroup('time', '$__interval');
}
- let aggIndex = this.findAggregateIndex(selectParts);
+ const aggIndex = this.findAggregateIndex(selectParts);
if (aggIndex !== -1) {
// replace current aggregation
selectParts[aggIndex] = partModel;
@@ -379,12 +379,12 @@ export class PostgresQueryCtrl extends QueryCtrl {
break;
case 'moving_window':
case 'window':
- let windowIndex = this.findWindowIndex(selectParts);
+ const windowIndex = this.findWindowIndex(selectParts);
if (windowIndex !== -1) {
// replace current window function
selectParts[windowIndex] = partModel;
} else {
- let aggIndex = this.findAggregateIndex(selectParts);
+ const aggIndex = this.findAggregateIndex(selectParts);
if (aggIndex !== -1) {
selectParts.splice(aggIndex + 1, 0, partModel);
} else {
@@ -418,11 +418,11 @@ export class PostgresQueryCtrl extends QueryCtrl {
if (part.def.type === 'column') {
// remove all parts of column unless its last column
if (this.selectParts.length > 1) {
- let modelsIndex = _.indexOf(this.selectParts, selectParts);
+ const modelsIndex = _.indexOf(this.selectParts, selectParts);
this.selectParts.splice(modelsIndex, 1);
}
} else {
- let partIndex = _.indexOf(selectParts, part);
+ const partIndex = _.indexOf(selectParts, part);
selectParts.splice(partIndex, 1);
}
@@ -490,7 +490,7 @@ export class PostgresQueryCtrl extends QueryCtrl {
if (partType === 'time') {
params = ['$__interval', 'none'];
}
- let partModel = sqlPart.create({ type: partType, params: params });
+ const partModel = sqlPart.create({ type: partType, params: params });
if (partType === 'time') {
// put timeGroup at start
@@ -500,12 +500,12 @@ export class PostgresQueryCtrl extends QueryCtrl {
}
// add aggregates when adding group by
- for (let selectParts of this.selectParts) {
+ for (const selectParts of this.selectParts) {
if (!selectParts.some(part => part.def.type === 'aggregate')) {
- let aggregate = sqlPart.create({ type: 'aggregate', params: ['avg'] });
+ const aggregate = sqlPart.create({ type: 'aggregate', params: ['avg'] });
selectParts.splice(1, 0, aggregate);
if (!selectParts.some(part => part.def.type === 'alias')) {
- let alias = sqlPart.create({ type: 'alias', params: [selectParts[0].part.params[0]] });
+ const alias = sqlPart.create({ type: 'alias', params: [selectParts[0].part.params[0]] });
selectParts.push(alias);
}
}
@@ -587,7 +587,7 @@ export class PostgresQueryCtrl extends QueryCtrl {
}
getWhereOptions() {
- var options = [];
+ const options = [];
if (this.queryModel.hasUnixEpochTimecolumn()) {
options.push(this.uiSegmentSrv.newSegment({ type: 'macro', value: '$__unixEpochFilter' }));
} else {
@@ -600,7 +600,7 @@ export class PostgresQueryCtrl extends QueryCtrl {
addWhereAction(part, index) {
switch (this.whereAdd.type) {
case 'macro': {
- let partModel = sqlPart.create({ type: 'macro', name: this.whereAdd.value, params: [] });
+ const partModel = sqlPart.create({ type: 'macro', name: this.whereAdd.value, params: [] });
if (this.whereParts.length >= 1 && this.whereParts[0].def.type === 'macro') {
// replace current macro
this.whereParts[0] = partModel;
@@ -623,11 +623,11 @@ export class PostgresQueryCtrl extends QueryCtrl {
return this.datasource
.metricFindQuery(this.metaBuilder.buildColumnQuery('group'))
.then(tags => {
- var options = [];
+ const options = [];
if (!this.queryModel.hasTimeGroup()) {
options.push(this.uiSegmentSrv.newSegment({ type: 'time', value: 'time($__interval,none)' }));
}
- for (let tag of tags) {
+ for (const tag of tags) {
options.push(this.uiSegmentSrv.newSegment({ type: 'column', value: tag.text }));
}
return options;
diff --git a/public/app/plugins/datasource/postgres/specs/datasource.test.ts b/public/app/plugins/datasource/postgres/specs/datasource.test.ts
index cf5805bab05..49541028caf 100644
--- a/public/app/plugins/datasource/postgres/specs/datasource.test.ts
+++ b/public/app/plugins/datasource/postgres/specs/datasource.test.ts
@@ -2,7 +2,7 @@ import moment from 'moment';
import { PostgresDatasource } from '../datasource';
import { CustomVariable } from 'app/features/templating/custom_variable';
-describe('PostgreSQLDatasource', function() {
+describe('PostgreSQLDatasource', () => {
const instanceSettings = { name: 'postgresql' };
const backendSrv = {};
@@ -13,7 +13,7 @@ describe('PostgreSQLDatasource', function() {
from: moment.utc('2018-04-25 10:00'),
to: moment.utc('2018-04-25 11:00'),
};
- const ctx = {
+ const ctx = {
backendSrv,
timeSrvMock: {
timeRange: () => ({
@@ -22,13 +22,13 @@ describe('PostgreSQLDatasource', function() {
raw: raw,
}),
},
- };
+ } as any;
beforeEach(() => {
ctx.ds = new PostgresDatasource(instanceSettings, backendSrv, {}, templateSrv, ctx.timeSrvMock);
});
- describe('When performing annotationQuery', function() {
+ describe('When performing annotationQuery', () => {
let results;
const annotationName = 'MyAnno';
@@ -62,16 +62,16 @@ describe('PostgreSQLDatasource', function() {
},
};
- beforeEach(function() {
+ beforeEach(() => {
ctx.backendSrv.datasourceRequest = jest.fn(options => {
return Promise.resolve({ data: response, status: 200 });
});
- ctx.ds.annotationQuery(options).then(function(data) {
+ ctx.ds.annotationQuery(options).then(data => {
results = data;
});
});
- it('should return annotation list', function() {
+ it('should return annotation list', () => {
expect(results.length).toBe(3);
expect(results[0].text).toBe('some text');
@@ -85,7 +85,7 @@ describe('PostgreSQLDatasource', function() {
});
});
- describe('When performing metricFindQuery', function() {
+ describe('When performing metricFindQuery', () => {
let results;
const query = 'select * from atable';
const response = {
@@ -105,23 +105,23 @@ describe('PostgreSQLDatasource', function() {
},
};
- beforeEach(function() {
+ beforeEach(() => {
ctx.backendSrv.datasourceRequest = jest.fn(options => {
return Promise.resolve({ data: response, status: 200 });
});
- ctx.ds.metricFindQuery(query).then(function(data) {
+ ctx.ds.metricFindQuery(query).then(data => {
results = data;
});
});
- it('should return list of all column values', function() {
+ it('should return list of all column values', () => {
expect(results.length).toBe(6);
expect(results[0].text).toBe('aTitle');
expect(results[5].text).toBe('some text3');
});
});
- describe('When performing metricFindQuery with key, value columns', function() {
+ describe('When performing metricFindQuery with key, value columns', () => {
let results;
const query = 'select * from atable';
const response = {
@@ -141,16 +141,16 @@ describe('PostgreSQLDatasource', function() {
},
};
- beforeEach(function() {
+ beforeEach(() => {
ctx.backendSrv.datasourceRequest = jest.fn(options => {
return Promise.resolve({ data: response, status: 200 });
});
- ctx.ds.metricFindQuery(query).then(function(data) {
+ ctx.ds.metricFindQuery(query).then(data => {
results = data;
});
});
- it('should return list of as text, value', function() {
+ it('should return list of as text, value', () => {
expect(results.length).toBe(3);
expect(results[0].text).toBe('aTitle');
expect(results[0].value).toBe('value1');
@@ -159,7 +159,7 @@ describe('PostgreSQLDatasource', function() {
});
});
- describe('When performing metricFindQuery with key, value columns and with duplicate keys', function() {
+ describe('When performing metricFindQuery with key, value columns and with duplicate keys', () => {
let results;
const query = 'select * from atable';
const response = {
@@ -183,13 +183,13 @@ describe('PostgreSQLDatasource', function() {
ctx.backendSrv.datasourceRequest = jest.fn(options => {
return Promise.resolve({ data: response, status: 200 });
});
- ctx.ds.metricFindQuery(query).then(function(data) {
+ ctx.ds.metricFindQuery(query).then(data => {
results = data;
});
//ctx.$rootScope.$apply();
});
- it('should return list of unique keys', function() {
+ it('should return list of unique keys', () => {
expect(results.length).toBe(1);
expect(results[0].text).toBe('aTitle');
expect(results[0].value).toBe('same');
@@ -197,7 +197,7 @@ describe('PostgreSQLDatasource', function() {
});
describe('When interpolating variables', () => {
- beforeEach(function() {
+ beforeEach(() => {
ctx.variable = new CustomVariable({}, {});
});
diff --git a/public/app/plugins/datasource/postgres/specs/postgres_query.test.ts b/public/app/plugins/datasource/postgres/specs/postgres_query.test.ts
index 83ba6eb5c74..42b143c01c8 100644
--- a/public/app/plugins/datasource/postgres/specs/postgres_query.test.ts
+++ b/public/app/plugins/datasource/postgres/specs/postgres_query.test.ts
@@ -1,23 +1,23 @@
import PostgresQuery from '../postgres_query';
-describe('PostgresQuery', function() {
- let templateSrv = {
+describe('PostgresQuery', () => {
+ const templateSrv = {
replace: jest.fn(text => text),
};
- describe('When initializing', function() {
- it('should not be in SQL mode', function() {
- let query = new PostgresQuery({}, templateSrv);
+ describe('When initializing', () => {
+ it('should not be in SQL mode', () => {
+ const query = new PostgresQuery({}, templateSrv);
expect(query.target.rawQuery).toBe(false);
});
- it('should be in SQL mode for pre query builder queries', function() {
- let query = new PostgresQuery({ rawSql: 'SELECT 1' }, templateSrv);
+ it('should be in SQL mode for pre query builder queries', () => {
+ const query = new PostgresQuery({ rawSql: 'SELECT 1' }, templateSrv);
expect(query.target.rawQuery).toBe(true);
});
});
- describe('When generating time column SQL', function() {
- let query = new PostgresQuery({}, templateSrv);
+ describe('When generating time column SQL', () => {
+ const query = new PostgresQuery({}, templateSrv);
query.target.timeColumn = 'time';
expect(query.buildTimeColumn()).toBe('time AS "time"');
@@ -25,7 +25,7 @@ describe('PostgresQuery', function() {
expect(query.buildTimeColumn()).toBe('"time" AS "time"');
});
- describe('When generating time column SQL with group by time', function() {
+ describe('When generating time column SQL with group by time', () => {
let query = new PostgresQuery(
{ timeColumn: 'time', group: [{ type: 'time', params: ['5m', 'none'] }] },
templateSrv
@@ -44,8 +44,8 @@ describe('PostgresQuery', function() {
expect(query.buildTimeColumn(false)).toBe('$__unixEpochGroup(time,5m)');
});
- describe('When generating metric column SQL', function() {
- let query = new PostgresQuery({}, templateSrv);
+ describe('When generating metric column SQL', () => {
+ const query = new PostgresQuery({}, templateSrv);
query.target.metricColumn = 'host';
expect(query.buildMetricColumn()).toBe('host AS metric');
@@ -53,8 +53,8 @@ describe('PostgresQuery', function() {
expect(query.buildMetricColumn()).toBe('"host" AS metric');
});
- describe('When generating value column SQL', function() {
- let query = new PostgresQuery({}, templateSrv);
+ describe('When generating value column SQL', () => {
+ const query = new PostgresQuery({}, templateSrv);
let column = [{ type: 'column', params: ['value'] }];
expect(query.buildValueColumn(column)).toBe('value');
@@ -72,12 +72,14 @@ describe('PostgresQuery', function() {
{ type: 'window', params: ['increase'] },
];
expect(query.buildValueColumn(column)).toBe(
- '(CASE WHEN v >= lag(v) OVER (ORDER BY time) THEN v - lag(v) OVER (ORDER BY time) ELSE v END) AS "a"'
+ '(CASE WHEN v >= lag(v) OVER (ORDER BY time) ' +
+ 'THEN v - lag(v) OVER (ORDER BY time) ' +
+ 'WHEN lag(v) OVER (ORDER BY time) IS NULL THEN NULL ELSE v END) AS "a"'
);
});
- describe('When generating value column SQL with metric column', function() {
- let query = new PostgresQuery({}, templateSrv);
+ describe('When generating value column SQL with metric column', () => {
+ const query = new PostgresQuery({}, templateSrv);
query.target.metricColumn = 'host';
let column = [{ type: 'column', params: ['value'] }];
@@ -96,7 +98,9 @@ describe('PostgresQuery', function() {
{ type: 'window', params: ['increase'] },
];
expect(query.buildValueColumn(column)).toBe(
- '(CASE WHEN v >= lag(v) OVER (PARTITION BY host ORDER BY time) THEN v - lag(v) OVER (PARTITION BY host ORDER BY time) ELSE v END) AS "a"'
+ '(CASE WHEN v >= lag(v) OVER (PARTITION BY host ORDER BY time) ' +
+ 'THEN v - lag(v) OVER (PARTITION BY host ORDER BY time) ' +
+ 'WHEN lag(v) OVER (PARTITION BY host ORDER BY time) IS NULL THEN NULL ELSE v END) AS "a"'
);
column = [
{ type: 'column', params: ['v'] },
@@ -106,12 +110,13 @@ describe('PostgresQuery', function() {
];
expect(query.buildValueColumn(column)).toBe(
'(CASE WHEN max(v) >= lag(max(v)) OVER (PARTITION BY host ORDER BY time) ' +
- 'THEN max(v) - lag(max(v)) OVER (PARTITION BY host ORDER BY time) ELSE max(v) END) AS "a"'
+ 'THEN max(v) - lag(max(v)) OVER (PARTITION BY host ORDER BY time) ' +
+ 'WHEN lag(max(v)) OVER (PARTITION BY host ORDER BY time) IS NULL THEN NULL ELSE max(v) END) AS "a"'
);
});
- describe('When generating WHERE clause', function() {
- let query = new PostgresQuery({ where: [] }, templateSrv);
+ describe('When generating WHERE clause', () => {
+ const query = new PostgresQuery({ where: [] }, templateSrv);
expect(query.buildWhereClause()).toBe('');
@@ -126,8 +131,8 @@ describe('PostgresQuery', function() {
expect(query.buildWhereClause()).toBe('\nWHERE\n $__timeFilter(t) AND\n v = 1');
});
- describe('When generating GROUP BY clause', function() {
- let query = new PostgresQuery({ group: [], metricColumn: 'none' }, templateSrv);
+ describe('When generating GROUP BY clause', () => {
+ const query = new PostgresQuery({ group: [], metricColumn: 'none' }, templateSrv);
expect(query.buildGroupClause()).toBe('');
query.target.group = [{ type: 'time', params: ['5m'] }];
@@ -136,20 +141,20 @@ describe('PostgresQuery', function() {
expect(query.buildGroupClause()).toBe('\nGROUP BY 1,2');
});
- describe('When generating complete statement', function() {
- let target = {
+ describe('When generating complete statement', () => {
+ const target = {
timeColumn: 't',
table: 'table',
select: [[{ type: 'column', params: ['value'] }]],
where: [],
};
let result = 'SELECT\n t AS "time",\n value\nFROM table\nORDER BY 1';
- let query = new PostgresQuery(target, templateSrv);
+ const query = new PostgresQuery(target, templateSrv);
expect(query.buildQuery()).toBe(result);
query.target.metricColumn = 'm';
- result = 'SELECT\n t AS "time",\n m AS metric,\n value\nFROM table\nORDER BY 1';
+ result = 'SELECT\n t AS "time",\n m AS metric,\n value\nFROM table\nORDER BY 1,2';
expect(query.buildQuery()).toBe(result);
});
});
diff --git a/public/app/plugins/datasource/postgres/sql_part.ts b/public/app/plugins/datasource/postgres/sql_part.ts
index fb132930747..695060f6366 100644
--- a/public/app/plugins/datasource/postgres/sql_part.ts
+++ b/public/app/plugins/datasource/postgres/sql_part.ts
@@ -1,9 +1,9 @@
import { SqlPartDef, SqlPart } from 'app/core/components/sql_part/sql_part';
-let index = [];
+const index = [];
function createPart(part): any {
- let def = index[part.type];
+ const def = index[part.type];
if (!def) {
return null;
}
diff --git a/public/app/plugins/datasource/prometheus/add_label_to_query.ts b/public/app/plugins/datasource/prometheus/add_label_to_query.ts
new file mode 100644
index 00000000000..433bb3e78f9
--- /dev/null
+++ b/public/app/plugins/datasource/prometheus/add_label_to_query.ts
@@ -0,0 +1,94 @@
+import _ from 'lodash';
+
+const keywords = 'by|without|on|ignoring|group_left|group_right';
+
+// Duplicate from mode-prometheus.js, which can't be used in tests due to global ace not being loaded.
+const builtInWords = [
+ keywords,
+ 'count|count_values|min|max|avg|sum|stddev|stdvar|bottomk|topk|quantile',
+ 'true|false|null|__name__|job',
+ 'abs|absent|ceil|changes|clamp_max|clamp_min|count_scalar|day_of_month|day_of_week|days_in_month|delta|deriv',
+ 'drop_common_labels|exp|floor|histogram_quantile|holt_winters|hour|idelta|increase|irate|label_replace|ln|log2',
+ 'log10|minute|month|predict_linear|rate|resets|round|scalar|sort|sort_desc|sqrt|time|vector|year|avg_over_time',
+ 'min_over_time|max_over_time|sum_over_time|count_over_time|quantile_over_time|stddev_over_time|stdvar_over_time',
+]
+ .join('|')
+ .split('|');
+
+const metricNameRegexp = /([A-Za-z:][\w:]*)\b(?![\(\]{=!",])/g;
+const selectorRegexp = /{([^{]*)}/g;
+
+// addLabelToQuery('foo', 'bar', 'baz') => 'foo{bar="baz"}'
+export function addLabelToQuery(query: string, key: string, value: string, operator?: string): string {
+ if (!key || !value) {
+ throw new Error('Need label to add to query.');
+ }
+
+ // Add empty selectors to bare metric names
+ let previousWord;
+ query = query.replace(metricNameRegexp, (match, word, offset) => {
+ const insideSelector = isPositionInsideChars(query, offset, '{', '}');
+ // Handle "sum by (key) (metric)"
+ const previousWordIsKeyWord = previousWord && keywords.split('|').indexOf(previousWord) > -1;
+ previousWord = word;
+ if (!insideSelector && !previousWordIsKeyWord && builtInWords.indexOf(word) === -1) {
+ return `${word}{}`;
+ }
+ return word;
+ });
+
+ // Adding label to existing selectors
+ let match = selectorRegexp.exec(query);
+ const parts = [];
+ let lastIndex = 0;
+ let suffix = '';
+
+ while (match) {
+ const prefix = query.slice(lastIndex, match.index);
+ const selector = match[1];
+ const selectorWithLabel = addLabelToSelector(selector, key, value, operator);
+ lastIndex = match.index + match[1].length + 2;
+ suffix = query.slice(match.index + match[0].length);
+ parts.push(prefix, '{', selectorWithLabel, '}');
+ match = selectorRegexp.exec(query);
+ }
+
+ parts.push(suffix);
+ return parts.join('');
+}
+
+const labelRegexp = /(\w+)\s*(=|!=|=~|!~)\s*("[^"]*")/g;
+
+function addLabelToSelector(selector: string, labelKey: string, labelValue: string, labelOperator?: string) {
+ const parsedLabels = [];
+
+ // Split selector into labels
+ if (selector) {
+ let match = labelRegexp.exec(selector);
+ while (match) {
+ parsedLabels.push({ key: match[1], operator: match[2], value: match[3] });
+ match = labelRegexp.exec(selector);
+ }
+ }
+
+ // Add new label
+ const operatorForLabelKey = labelOperator || '=';
+ parsedLabels.push({ key: labelKey, operator: operatorForLabelKey, value: `"${labelValue}"` });
+
+ // Sort labels by key and put them together
+ return _.chain(parsedLabels)
+ .uniqWith(_.isEqual)
+ .compact()
+ .sortBy('key')
+ .map(({ key, operator, value }) => `${key}${operator}${value}`)
+ .value()
+ .join(',');
+}
+
+function isPositionInsideChars(text: string, position: number, openChar: string, closeChar: string) {
+ const nextSelectorStart = text.slice(position).indexOf(openChar);
+ const nextSelectorEnd = text.slice(position).indexOf(closeChar);
+ return nextSelectorEnd > -1 && (nextSelectorStart === -1 || nextSelectorStart > nextSelectorEnd);
+}
+
+export default addLabelToQuery;
diff --git a/public/app/plugins/datasource/prometheus/completer.ts b/public/app/plugins/datasource/prometheus/completer.ts
index 30a11a77961..ebfb6670f40 100644
--- a/public/app/plugins/datasource/prometheus/completer.ts
+++ b/public/app/plugins/datasource/prometheus/completer.ts
@@ -113,7 +113,7 @@ export class PromCompleter {
_.uniq(
_.flatten(
result.map(r => {
- return Object.keys(r.metric);
+ return Object.keys(r);
})
)
),
@@ -151,7 +151,7 @@ export class PromCompleter {
const labelValues = this.transformToCompletions(
_.uniq(
result.map(r => {
- return r.metric[labelName];
+ return r[labelName];
})
),
'label value'
@@ -191,7 +191,7 @@ export class PromCompleter {
_.uniq(
_.flatten(
result.map(r => {
- return Object.keys(r.metric);
+ return Object.keys(r);
})
)
),
@@ -233,7 +233,7 @@ export class PromCompleter {
_.uniq(
_.flatten(
result.map(r => {
- return Object.keys(r.metric);
+ return Object.keys(r);
})
)
),
@@ -249,7 +249,7 @@ export class PromCompleter {
_.uniq(
_.flatten(
result.map(r => {
- return Object.keys(r.metric);
+ return Object.keys(r);
})
)
),
@@ -264,7 +264,7 @@ export class PromCompleter {
return Promise.resolve([]);
}
- getLabelNameAndValueForExpression(expr, type) {
+ getLabelNameAndValueForExpression(expr: string, type: string): Promise {
if (this.labelQueryCache[expr]) {
return Promise.resolve(this.labelQueryCache[expr]);
}
@@ -276,9 +276,11 @@ export class PromCompleter {
}
query = '{__name__' + op + '"' + expr + '"}';
}
- return this.datasource.performInstantQuery({ expr: query }, new Date().getTime() / 1000).then(response => {
- this.labelQueryCache[expr] = response.data.data.result;
- return response.data.data.result;
+ const { start, end } = this.datasource.getTimeRange();
+ const url = '/api/v1/series?match[]=' + encodeURIComponent(query) + '&start=' + start + '&end=' + end;
+ return this.datasource.metadataRequest(url).then(response => {
+ this.labelQueryCache[expr] = response.data.data;
+ return response.data.data;
});
}
@@ -294,9 +296,9 @@ export class PromCompleter {
}
findMetricName(session, row, column) {
- var metricName = '';
+ let metricName = '';
- var tokens;
+ let tokens;
const nameLabelNameToken = this.findToken(
session,
row,
@@ -323,9 +325,9 @@ export class PromCompleter {
}
findToken(session, row, column, target, value, guard) {
- var tokens, idx;
+ let tokens, idx;
// find index and get column of previous token
- for (var r = row; r >= 0; r--) {
+ for (let r = row; r >= 0; r--) {
let c;
tokens = session.getTokens(r);
if (r === row) {
diff --git a/public/app/plugins/datasource/prometheus/datasource.ts b/public/app/plugins/datasource/prometheus/datasource.ts
index 8cd1abd9ccf..856ab035ea0 100644
--- a/public/app/plugins/datasource/prometheus/datasource.ts
+++ b/public/app/plugins/datasource/prometheus/datasource.ts
@@ -7,6 +7,9 @@ import PrometheusMetricFindQuery from './metric_find_query';
import { ResultTransformer } from './result_transformer';
import { BackendSrv } from 'app/core/services/backend_srv';
+import addLabelToQuery from './add_label_to_query';
+import { getQueryHints } from './query_hints';
+
export function alignRange(start, end, step) {
const alignedEnd = Math.ceil(end / step) * step;
const alignedStart = Math.floor(start / step) * step;
@@ -16,170 +19,6 @@ export function alignRange(start, end, step) {
};
}
-const keywords = 'by|without|on|ignoring|group_left|group_right';
-
-// Duplicate from mode-prometheus.js, which can't be used in tests due to global ace not being loaded.
-const builtInWords = [
- keywords,
- 'count|count_values|min|max|avg|sum|stddev|stdvar|bottomk|topk|quantile',
- 'true|false|null|__name__|job',
- 'abs|absent|ceil|changes|clamp_max|clamp_min|count_scalar|day_of_month|day_of_week|days_in_month|delta|deriv',
- 'drop_common_labels|exp|floor|histogram_quantile|holt_winters|hour|idelta|increase|irate|label_replace|ln|log2',
- 'log10|minute|month|predict_linear|rate|resets|round|scalar|sort|sort_desc|sqrt|time|vector|year|avg_over_time',
- 'min_over_time|max_over_time|sum_over_time|count_over_time|quantile_over_time|stddev_over_time|stdvar_over_time',
-]
- .join('|')
- .split('|');
-
-// addLabelToQuery('foo', 'bar', 'baz') => 'foo{bar="baz"}'
-export function addLabelToQuery(query: string, key: string, value: string): string {
- if (!key || !value) {
- throw new Error('Need label to add to query.');
- }
-
- // Add empty selector to bare metric name
- let previousWord;
- query = query.replace(/(\w+)\b(?![\(\]{=",])/g, (match, word, offset) => {
- // Check if inside a selector
- const nextSelectorStart = query.slice(offset).indexOf('{');
- const nextSelectorEnd = query.slice(offset).indexOf('}');
- const insideSelector = nextSelectorEnd > -1 && (nextSelectorStart === -1 || nextSelectorStart > nextSelectorEnd);
- // Handle "sum by (key) (metric)"
- const previousWordIsKeyWord = previousWord && keywords.split('|').indexOf(previousWord) > -1;
- previousWord = word;
- if (!insideSelector && !previousWordIsKeyWord && builtInWords.indexOf(word) === -1) {
- return `${word}{}`;
- }
- return word;
- });
-
- // Adding label to existing selectors
- const selectorRegexp = /{([^{]*)}/g;
- let match = null;
- const parts = [];
- let lastIndex = 0;
- let suffix = '';
- while ((match = selectorRegexp.exec(query))) {
- const prefix = query.slice(lastIndex, match.index);
- const selectorParts = match[1].split(',');
- const labels = selectorParts.reduce((acc, label) => {
- const labelParts = label.split('=');
- if (labelParts.length === 2) {
- acc[labelParts[0]] = labelParts[1];
- }
- return acc;
- }, {});
- labels[key] = `"${value}"`;
- const selector = Object.keys(labels)
- .sort()
- .map(key => `${key}=${labels[key]}`)
- .join(',');
- lastIndex = match.index + match[1].length + 2;
- suffix = query.slice(match.index + match[0].length);
- parts.push(prefix, '{', selector, '}');
- }
- parts.push(suffix);
- return parts.join('');
-}
-
-export function determineQueryHints(series: any[], datasource?: any): any[] {
- const hints = series.map((s, i) => {
- const query: string = s.query;
- const index: number = s.responseIndex;
- if (query === undefined || index === undefined) {
- return null;
- }
-
- // ..._bucket metric needs a histogram_quantile()
- const histogramMetric = query.trim().match(/^\w+_bucket$/);
- if (histogramMetric) {
- const label = 'Time series has buckets, you probably wanted a histogram.';
- return {
- index,
- label,
- fix: {
- label: 'Fix by adding histogram_quantile().',
- action: {
- type: 'ADD_HISTOGRAM_QUANTILE',
- query,
- index,
- },
- },
- };
- }
-
- // Check for monotony
- const datapoints: [number, number][] = s.datapoints;
- if (datapoints.length > 1) {
- let increasing = false;
- const monotonic = datapoints.filter(dp => dp[0] !== null).every((dp, index) => {
- if (index === 0) {
- return true;
- }
- increasing = increasing || dp[0] > datapoints[index - 1][0];
- // monotonic?
- return dp[0] >= datapoints[index - 1][0];
- });
- if (increasing && monotonic) {
- const simpleMetric = query.trim().match(/^\w+$/);
- let label = 'Time series is monotonously increasing.';
- let fix;
- if (simpleMetric) {
- fix = {
- label: 'Fix by adding rate().',
- action: {
- type: 'ADD_RATE',
- query,
- index,
- },
- };
- } else {
- label = `${label} Try applying a rate() function.`;
- }
- return {
- label,
- index,
- fix,
- };
- }
- }
-
- // Check for recording rules expansion
- if (datasource && datasource.ruleMappings) {
- const mapping = datasource.ruleMappings;
- const mappingForQuery = Object.keys(mapping).reduce((acc, ruleName) => {
- if (query.search(ruleName) > -1) {
- return {
- ...acc,
- [ruleName]: mapping[ruleName],
- };
- }
- return acc;
- }, {});
- if (_.size(mappingForQuery) > 0) {
- const label = 'Query contains recording rules.';
- return {
- label,
- index,
- fix: {
- label: 'Expand rules',
- action: {
- type: 'EXPAND_RULES',
- query,
- index,
- mapping: mappingForQuery,
- },
- },
- };
- }
- }
-
- // No hint found
- return null;
- });
- return hints;
-}
-
export function extractRuleMappingFromGroups(groups: any[]) {
return groups.reduce(
(mapping, group) =>
@@ -213,8 +52,6 @@ export class PrometheusDatasource {
editorSrc: string;
name: string;
ruleMappings: { [index: string]: string };
- supportsExplore: boolean;
- supportMetrics: boolean;
url: string;
directUrl: string;
basicAuth: any;
@@ -230,8 +67,6 @@ export class PrometheusDatasource {
this.type = 'prometheus';
this.editorSrc = 'app/features/prometheus/partials/query.editor.html';
this.name = instanceSettings.name;
- this.supportsExplore = true;
- this.supportMetrics = true;
this.url = instanceSettings.url;
this.directUrl = instanceSettings.directUrl;
this.basicAuth = instanceSettings.basicAuth;
@@ -368,7 +203,7 @@ export class PrometheusDatasource {
result = [...result, ...series];
if (queries[index].hinting) {
- const queryHints = determineQueryHints(series, this);
+ const queryHints = getQueryHints(series, this);
hints = [...hints, ...queryHints];
}
});
@@ -384,7 +219,7 @@ export class PrometheusDatasource {
};
const range = Math.ceil(end - start);
- var interval = kbn.interval_to_seconds(options.interval);
+ let interval = kbn.interval_to_seconds(options.interval);
// Minimum interval ("Min step"), if specified for the query. or same as interval otherwise
const minInterval = kbn.interval_to_seconds(
this.templateSrv.replace(target.interval, options.scopedVars) || options.interval
@@ -392,7 +227,7 @@ export class PrometheusDatasource {
const intervalFactor = target.intervalFactor || 1;
// Adjust the interval to take into account any specified minimum and interval factor plus Prometheus limits
const adjustedInterval = this.adjustInterval(interval, minInterval, range, intervalFactor);
- var scopedVars = { ...options.scopedVars, ...this.getRangeScopedVars() };
+ let scopedVars = { ...options.scopedVars, ...this.getRangeScopedVars() };
// If the interval was adjusted, make a shallow copy of scopedVars with updated interval vars
if (interval !== adjustedInterval) {
interval = adjustedInterval;
@@ -404,8 +239,21 @@ export class PrometheusDatasource {
}
query.step = interval;
+ let expr = target.expr;
+
+ // Apply adhoc filters
+ const adhocFilters = this.templateSrv.getAdhocFilters(this.name);
+ expr = adhocFilters.reduce((acc, filter) => {
+ const { key, operator } = filter;
+ let { value } = filter;
+ if (operator === '=~' || operator === '!~') {
+ value = prometheusSpecialRegexEscape(value);
+ }
+ return addLabelToQuery(acc, key, value, operator);
+ }, expr);
+
// Only replace vars in expression after having (possibly) updated interval vars
- query.expr = this.templateSrv.replace(target.expr, scopedVars, this.interpolateQueryExpr);
+ query.expr = this.templateSrv.replace(expr, scopedVars, this.interpolateQueryExpr);
query.requestId = options.panelId + target.refId;
// Align query interval with step
@@ -507,7 +355,7 @@ export class PrometheusDatasource {
annotationQuery(options) {
const annotation = options.annotation;
const expr = annotation.expr || '';
- var tagKeys = annotation.tagKeys || '';
+ let tagKeys = annotation.tagKeys || '';
const titleFormat = annotation.titleFormat || '';
const textFormat = annotation.textFormat || '';
@@ -526,27 +374,33 @@ export class PrometheusDatasource {
const query = this.createQuery({ expr, interval: step }, queryOptions, start, end);
const self = this;
- return this.performTimeSeriesQuery(query, query.start, query.end).then(function(results) {
+ return this.performTimeSeriesQuery(query, query.start, query.end).then(results => {
const eventList = [];
tagKeys = tagKeys.split(',');
- _.each(results.data.data.result, function(series) {
+ _.each(results.data.data.result, series => {
const tags = _.chain(series.metric)
- .filter(function(v, k) {
+ .filter((v, k) => {
return _.includes(tagKeys, k);
})
.value();
for (const value of series.values) {
- if (value[1] === '1') {
+ const valueIsTrue = value[1] === '1'; // e.g. ALERTS
+ if (valueIsTrue || annotation.useValueForTime) {
const event = {
annotation: annotation,
- time: Math.floor(parseFloat(value[0])) * 1000,
title: self.resultTransformer.renderTemplate(titleFormat, series.metric),
tags: tags,
text: self.resultTransformer.renderTemplate(textFormat, series.metric),
};
+ if (annotation.useValueForTime) {
+ event['time'] = Math.floor(parseFloat(value[1]));
+ } else {
+ event['time'] = Math.floor(parseFloat(value[0])) * 1000;
+ }
+
eventList.push(event);
}
}
@@ -567,10 +421,10 @@ export class PrometheusDatasource {
});
}
- getExploreState(panel) {
+ getExploreState(targets: any[]) {
let state = {};
- if (panel.targets) {
- const queries = panel.targets.map(t => ({
+ if (targets && targets.length > 0) {
+ const queries = targets.map(t => ({
query: this.templateSrv.replace(t.expr, {}, this.interpolateQueryExpr),
format: t.format,
}));
@@ -629,6 +483,14 @@ export class PrometheusDatasource {
return Math.ceil(date.valueOf() / 1000);
}
+ getTimeRange(): { start: number; end: number } {
+ const range = this.timeSrv.timeRange();
+ return {
+ start: this.getPrometheusTime(range.from, false),
+ end: this.getPrometheusTime(range.to, true),
+ };
+ }
+
getOriginalMetricName(labelData) {
return this.resultTransformer.getOriginalMetricName(labelData);
}
diff --git a/public/app/plugins/datasource/prometheus/metric_find_query.ts b/public/app/plugins/datasource/prometheus/metric_find_query.ts
index 7eccd62eb20..680f7a8fb98 100644
--- a/public/app/plugins/datasource/prometheus/metric_find_query.ts
+++ b/public/app/plugins/datasource/prometheus/metric_find_query.ts
@@ -12,27 +12,27 @@ export default class PrometheusMetricFindQuery {
}
process() {
- const label_values_regex = /^label_values\((?:(.+),\s*)?([a-zA-Z_][a-zA-Z0-9_]+)\)\s*$/;
- const metric_names_regex = /^metrics\((.+)\)\s*$/;
- const query_result_regex = /^query_result\((.+)\)\s*$/;
-
- const label_values_query = this.query.match(label_values_regex);
- if (label_values_query) {
- if (label_values_query[1]) {
- return this.labelValuesQuery(label_values_query[2], label_values_query[1]);
+ const labelValuesRegex = /^label_values\((?:(.+),\s*)?([a-zA-Z_][a-zA-Z0-9_]*)\)\s*$/;
+ const metricNamesRegex = /^metrics\((.+)\)\s*$/;
+ const queryResultRegex = /^query_result\((.+)\)\s*$/;
+
+ const labelValuesQuery = this.query.match(labelValuesRegex);
+ if (labelValuesQuery) {
+ if (labelValuesQuery[1]) {
+ return this.labelValuesQuery(labelValuesQuery[2], labelValuesQuery[1]);
} else {
- return this.labelValuesQuery(label_values_query[2], null);
+ return this.labelValuesQuery(labelValuesQuery[2], null);
}
}
- const metric_names_query = this.query.match(metric_names_regex);
- if (metric_names_query) {
- return this.metricNameQuery(metric_names_query[1]);
+ const metricNamesQuery = this.query.match(metricNamesRegex);
+ if (metricNamesQuery) {
+ return this.metricNameQuery(metricNamesQuery[1]);
}
- const query_result_query = this.query.match(query_result_regex);
- if (query_result_query) {
- return this.queryResultQuery(query_result_query[1]);
+ const queryResultQuery = this.query.match(queryResultRegex);
+ if (queryResultQuery) {
+ return this.queryResultQuery(queryResultQuery[1]);
}
// if query contains full metric name, return metric name and label list
@@ -40,14 +40,14 @@ export default class PrometheusMetricFindQuery {
}
labelValuesQuery(label, metric) {
- var url;
+ let url;
if (!metric) {
// return label values globally
url = '/api/v1/label/' + label + '/values';
- return this.datasource.metadataRequest(url).then(function(result) {
- return _.map(result.data.data, function(value) {
+ return this.datasource.metadataRequest(url).then(result => {
+ return _.map(result.data.data, value => {
return { text: value };
});
});
@@ -56,14 +56,14 @@ export default class PrometheusMetricFindQuery {
const end = this.datasource.getPrometheusTime(this.range.to, true);
url = '/api/v1/series?match[]=' + encodeURIComponent(metric) + '&start=' + start + '&end=' + end;
- return this.datasource.metadataRequest(url).then(function(result) {
- const _labels = _.map(result.data.data, function(metric) {
+ return this.datasource.metadataRequest(url).then(result => {
+ const _labels = _.map(result.data.data, metric => {
return metric[label] || '';
- }).filter(function(label) {
+ }).filter(label => {
return label !== '';
});
- return _.uniq(_labels).map(function(metric) {
+ return _.uniq(_labels).map(metric => {
return {
text: metric,
expandable: true,
@@ -76,13 +76,13 @@ export default class PrometheusMetricFindQuery {
metricNameQuery(metricFilterPattern) {
const url = '/api/v1/label/__name__/values';
- return this.datasource.metadataRequest(url).then(function(result) {
+ return this.datasource.metadataRequest(url).then(result => {
return _.chain(result.data.data)
- .filter(function(metricName) {
+ .filter(metricName => {
const r = new RegExp(metricFilterPattern);
return r.test(metricName);
})
- .map(function(matchedMetricName) {
+ .map(matchedMetricName => {
return {
text: matchedMetricName,
expandable: true,
@@ -94,13 +94,13 @@ export default class PrometheusMetricFindQuery {
queryResultQuery(query) {
const end = this.datasource.getPrometheusTime(this.range.to, true);
- return this.datasource.performInstantQuery({ expr: query }, end).then(function(result) {
- return _.map(result.data.data.result, function(metricData) {
- var text = metricData.metric.__name__ || '';
+ return this.datasource.performInstantQuery({ expr: query }, end).then(result => {
+ return _.map(result.data.data.result, metricData => {
+ let text = metricData.metric.__name__ || '';
delete metricData.metric.__name__;
text +=
'{' +
- _.map(metricData.metric, function(v, k) {
+ _.map(metricData.metric, (v, k) => {
return k + '="' + v + '"';
}).join(',') +
'}';
@@ -120,7 +120,7 @@ export default class PrometheusMetricFindQuery {
const url = '/api/v1/series?match[]=' + encodeURIComponent(query) + '&start=' + start + '&end=' + end;
const self = this;
- return this.datasource.metadataRequest(url).then(function(result) {
+ return this.datasource.metadataRequest(url).then(result => {
return _.map(result.data.data, metric => {
return {
text: self.datasource.getOriginalMetricName(metric),
diff --git a/public/app/plugins/datasource/prometheus/partials/annotations.editor.html b/public/app/plugins/datasource/prometheus/partials/annotations.editor.html
index 09ee52bda45..6e5982123fd 100644
--- a/public/app/plugins/datasource/prometheus/partials/annotations.editor.html
+++ b/public/app/plugins/datasource/prometheus/partials/annotations.editor.html
@@ -10,7 +10,7 @@
+An annotation is an event that is overlaid on top of graphs. Annotation rendering is expensive so it is important to limit the number of rows returned.
+
+The Title and Text fields support templating and can use data returned from the query. For example, the Title field could have the following text:
+
+{{metric.type}} has value: {{metric.value}}
+
+Example Result: monitoring.googleapis.com/uptime_check/http_status has this value: 502
+
+
+{{metric.value}} = value of the metric/point
+{{metric.type}} = metric type e.g. compute.googleapis.com/instance/cpu/usage_time
+{{metric.name}} = name part of metric e.g. instance/cpu/usage_time
+{{metric.service}} = service part of metric e.g. compute
+
+{{metric.label.label_name}} = Metric label metadata e.g. metric.label.instance_name
+{{resource.label.label_name}} = Resource label metadata e.g. resource.label.zone
+
+
diff --git a/public/app/plugins/datasource/stackdriver/partials/config.html b/public/app/plugins/datasource/stackdriver/partials/config.html
new file mode 100644
index 00000000000..46b79d8bb0d
--- /dev/null
+++ b/public/app/plugins/datasource/stackdriver/partials/config.html
@@ -0,0 +1,86 @@
+
+
+
GCP Service Account
+
+ To authenticate with the Stackdriver API, you need to create a Google Cloud Platform (GCP) Service Account for
+ the Project you want to show data for. A Grafana datasource integrates with one GCP Project. If you want to
+ visualize data from multiple GCP Projects then you need to create one datasource per GCP Project.
+
+
+ The Monitoring Viewer role provides all the permissions that Grafana needs.
+
+
+ The following APIs need to be enabled on GCP for the datasource to work:
+
Detailed instructions on how to create a Service Account can be found in
+ the documentation.
+
+
+
+
+
+
Service Account Authentication
+ Upload your Service Account key file or paste in the contents of the file. The file
+ contents will be encrypted and saved in the Grafana database.
+
+ Do not forget to save your changes after uploading a file.
+
diff --git a/public/app/plugins/datasource/stackdriver/partials/query.aggregation.html b/public/app/plugins/datasource/stackdriver/partials/query.aggregation.html
new file mode 100755
index 00000000000..379b9a36dc3
--- /dev/null
+++ b/public/app/plugins/datasource/stackdriver/partials/query.aggregation.html
@@ -0,0 +1,46 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/public/app/plugins/datasource/stackdriver/partials/query.editor.html b/public/app/plugins/datasource/stackdriver/partials/query.editor.html
new file mode 100755
index 00000000000..7cc93e9d1a8
--- /dev/null
+++ b/public/app/plugins/datasource/stackdriver/partials/query.editor.html
@@ -0,0 +1,74 @@
+
+
+
+
+
+ Alias By
+
+
+
+
+
+
+
+
+ Project
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
{{ctrl.lastQueryMeta.rawQueryString}}
+
+
+
Alias Patterns
+
+ Format the legend keys any way you want by using alias patterns.