Compare commits
450 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
8db5f087eb | ||
|
|
8a1616305c | ||
|
|
7ca3d32a10 | ||
|
|
dba7383bb9 | ||
|
|
1ebea3775d | ||
|
|
5a80b0aede | ||
|
|
981f4f230c | ||
|
|
cac8b976d9 | ||
|
|
4366d2f281 | ||
|
|
c80995b067 | ||
|
|
ebaf8620ea | ||
|
|
64d8ae9dcb | ||
|
|
989bf2067c | ||
|
|
baeea98473 | ||
|
|
dce8522575 | ||
|
|
c47f670a58 | ||
|
|
fb50cec096 | ||
|
|
eb9bdb09b3 | ||
|
|
002ac79124 | ||
|
|
bd11b01aaa | ||
|
|
d7dd7e3c81 | ||
|
|
ea78d13b54 | ||
|
|
83c5853900 | ||
|
|
0f0be4e6e3 | ||
|
|
f308a25589 | ||
|
|
9a91a882b4 | ||
|
|
6ad6131aaf | ||
|
|
1f10928450 | ||
|
|
0cd0aa19d6 | ||
|
|
c2f2a43197 | ||
|
|
7fe1ac5fe7 | ||
|
|
aec448f7c6 | ||
|
|
2ce36c8670 | ||
|
|
fb35d839c1 | ||
|
|
c8f5d39d97 | ||
|
|
34bc19359d | ||
|
|
3dcae78126 | ||
|
|
054c7a154a | ||
|
|
6f3d61f4d2 | ||
|
|
305f8c10e9 | ||
|
|
fff4cfd11e | ||
|
|
d20434f828 | ||
|
|
1ef850fae0 | ||
|
|
2f7a59fb18 | ||
|
|
07be20eeb3 | ||
|
|
689b8d79df | ||
|
|
b70c538633 | ||
|
|
769bc5df21 | ||
|
|
4eb6d82254 | ||
|
|
7935739eb3 | ||
|
|
7403fa0fa7 | ||
|
|
5ebfd1e5ab | ||
|
|
a1a8c0fc07 | ||
|
|
7069fed31d | ||
|
|
9d916fdad6 | ||
|
|
ec14fa58b5 | ||
|
|
8e4a7060ca | ||
|
|
d0ace0bc13 | ||
|
|
2992fb4b0e | ||
|
|
3b14bee559 | ||
|
|
e7b718ed7a | ||
|
|
b5727949fd | ||
|
|
88f55b01d8 | ||
|
|
0848ba2e9c | ||
|
|
23c610015f | ||
|
|
2564dda2d0 | ||
|
|
1334a9e138 | ||
|
|
4bdc3a5829 | ||
|
|
a7068d835a | ||
|
|
c879cc7615 | ||
|
|
a90052dac3 | ||
|
|
7548734510 | ||
|
|
a45bd400f1 | ||
|
|
b63725033e | ||
|
|
ee5f69beb4 | ||
|
|
5895f9dc89 | ||
|
|
98f00f7e1f | ||
|
|
55d510a319 | ||
|
|
b38016e97f | ||
|
|
64d75db514 | ||
|
|
9d53653647 | ||
|
|
45704d4570 | ||
|
|
b81e01d1ec | ||
|
|
1f4df2b694 | ||
|
|
4ca3cc90dd | ||
|
|
79afd638bb | ||
|
|
b76de790f9 | ||
|
|
81a4df1372 | ||
|
|
c0d257a0ee | ||
|
|
04ea7efac9 | ||
|
|
19a57f7cb1 | ||
|
|
2f73208ca8 | ||
|
|
e1d61c9678 | ||
|
|
2bc57d856c | ||
|
|
71a839deaf | ||
|
|
4f3856adfb | ||
|
|
f6aa0e41e5 | ||
|
|
e23c678df9 | ||
|
|
5d312be419 | ||
|
|
ef52d956bf | ||
|
|
a286ffa5f2 | ||
|
|
43169e4302 | ||
|
|
4719a8c8dd | ||
|
|
592c46c8b5 | ||
|
|
7713009caa | ||
|
|
05fbed4a36 | ||
|
|
679b169291 | ||
|
|
d1c9760fa8 | ||
|
|
630e6f5da6 | ||
|
|
3184942aeb | ||
|
|
473c47cd1c | ||
|
|
002b4d3403 | ||
|
|
8aff343ce3 | ||
|
|
da47dc8947 | ||
|
|
a562dc7c2b | ||
|
|
138bee99ef | ||
|
|
d60339a9b1 | ||
|
|
68829a821e | ||
|
|
a174ad4c9a | ||
|
|
6ce990af57 | ||
|
|
8423260f58 | ||
|
|
5e4bbfb08e | ||
|
|
4814b05746 | ||
|
|
ec802dbc1f | ||
|
|
08dc08550c | ||
|
|
9e61cacd16 | ||
|
|
b228c23dbe | ||
|
|
3519d8d69b | ||
|
|
25aa9df270 | ||
|
|
83f1ae4e3e | ||
|
|
ccf093da81 | ||
|
|
f2f8ca52d9 | ||
|
|
16c5d0e4b7 | ||
|
|
43903d71ec | ||
|
|
5561a099b0 | ||
|
|
7af4daf23b | ||
|
|
213ba0377b | ||
|
|
e42c118b3d | ||
|
|
d6b512478e | ||
|
|
e951200404 | ||
|
|
81be4e2612 | ||
|
|
19b5d91b75 | ||
|
|
9b34346f6b | ||
|
|
22cfb614bf | ||
|
|
6f4038e885 | ||
|
|
bb52f00d2d | ||
|
|
7e432df511 | ||
|
|
4cda9d0128 | ||
|
|
eb4a568240 | ||
|
|
ab5bc1bf4a | ||
|
|
6a030b2e8c | ||
|
|
6979dbd921 | ||
|
|
1590729a24 | ||
|
|
8e4efeeece | ||
|
|
363ded6ec1 | ||
|
|
79feba0004 | ||
|
|
789473fd34 | ||
|
|
7479c82687 | ||
|
|
531586beba | ||
|
|
7a7e8c57d1 | ||
|
|
0841eb94a8 | ||
|
|
b6d09a3443 | ||
|
|
eeb2e2c6c9 | ||
|
|
15ece1da04 | ||
|
|
69b0e63502 | ||
|
|
32342926cf | ||
|
|
273c17f3f3 | ||
|
|
27ec5449d9 | ||
|
|
70f102dad4 | ||
|
|
6a20c28c8c | ||
|
|
5a3f32521e | ||
|
|
f8719efb67 | ||
|
|
e4541a7fd1 | ||
|
|
9cf7a2d2ed | ||
|
|
7284db0e54 | ||
|
|
02d426a2d4 | ||
|
|
2276869efc | ||
|
|
080c46f835 | ||
|
|
0c31c7b106 | ||
|
|
ba074062d6 | ||
|
|
3c1beb1bdd | ||
|
|
548bc6b7c4 | ||
|
|
be3c5d1355 | ||
|
|
2aae2556a5 | ||
|
|
7859d4ffca | ||
|
|
1af00f5209 | ||
|
|
0831238420 | ||
|
|
a1b543aa35 | ||
|
|
ef518f6ecd | ||
|
|
e1a86e63b8 | ||
|
|
dd83b67de5 | ||
|
|
50e2f7e366 | ||
|
|
3dac77468b | ||
|
|
4c4564b138 | ||
|
|
be0d471467 | ||
|
|
122e2b5c42 | ||
|
|
a365719d3f | ||
|
|
4cc878b56d | ||
|
|
fa79be1f2d | ||
|
|
a02cac2126 | ||
|
|
42a0e9ebdf | ||
|
|
d8f6c73aab | ||
|
|
d4e5a7d3f0 | ||
|
|
503336bcd5 | ||
|
|
03a88206a3 | ||
|
|
ae14655749 | ||
|
|
c338d8250b | ||
|
|
bd348e16bb | ||
|
|
010a29a42d | ||
|
|
38ae460f59 | ||
|
|
cb0ded069a | ||
|
|
f5e8a4069c | ||
|
|
1dea7dab2a | ||
|
|
a22dded6fa | ||
|
|
6bc6911293 | ||
|
|
fea741d7c2 | ||
|
|
0c86241c5b | ||
|
|
89ca4b247b | ||
|
|
00726e93eb | ||
|
|
f6cc741a6d | ||
|
|
8a7f87b752 | ||
|
|
46cc1464c4 | ||
|
|
cabb21317f | ||
|
|
a8006ada1e | ||
|
|
7d2b989459 | ||
|
|
68ed4d45fa | ||
|
|
992cc1b680 | ||
|
|
9e48d54465 | ||
|
|
dd56c4b94b | ||
|
|
5850ae51b1 | ||
|
|
8d6513a564 | ||
|
|
5e88177f28 | ||
|
|
468e8c13ee | ||
|
|
6c375ed2cb | ||
|
|
f3a2dc7c5f | ||
|
|
a21f53cf82 | ||
|
|
59cdd4d8d2 | ||
|
|
fe9fca381c | ||
|
|
4f5f38f41b | ||
|
|
e1fe15e094 | ||
|
|
bf5268c0b4 | ||
|
|
d98d8a404f | ||
|
|
c140d7aa06 | ||
|
|
8f3b060946 | ||
|
|
17445e0c98 | ||
|
|
8243ac39c2 | ||
|
|
fe1d395d79 | ||
|
|
4b34ff5b83 | ||
|
|
e4de6332de | ||
|
|
ea704306a0 | ||
|
|
1dcc51adce | ||
|
|
ec632bb9ed | ||
|
|
78e3556e95 | ||
|
|
8fba6dcb0d | ||
|
|
cf23734d7d | ||
|
|
e588b682fb | ||
|
|
36a537a3ce | ||
|
|
fa074249e4 | ||
|
|
f66e1c02a6 | ||
|
|
01d2aa8af0 | ||
|
|
f590db1b78 | ||
|
|
1d265e05c9 | ||
|
|
fe3d3bc384 | ||
|
|
feed90c0e2 | ||
|
|
0c95148486 | ||
|
|
62d84c1e14 | ||
|
|
110f157621 | ||
|
|
c6607f3fa7 | ||
|
|
d31f264576 | ||
|
|
83b79dd624 | ||
|
|
728e96e134 | ||
|
|
dcb5ea58ce | ||
|
|
39607d09d7 | ||
|
|
9c026a8e04 | ||
|
|
73295ab3ed | ||
|
|
8950f57050 | ||
|
|
9bfda72a55 | ||
|
|
7aa753a25f | ||
|
|
4dea94791d | ||
|
|
7f6924ff13 | ||
|
|
c94fa7015f | ||
|
|
e9e8ffc769 | ||
|
|
487c475a1e | ||
|
|
0a65100eaf | ||
|
|
0d5540ca09 | ||
|
|
9a4ae30227 | ||
|
|
2ec7bbb2bd | ||
|
|
2ae74bf13d | ||
|
|
9ee343f33d | ||
|
|
bb8849785a | ||
|
|
25a6ed8718 | ||
|
|
1543dc4234 | ||
|
|
5373a6027c | ||
|
|
8615912018 | ||
|
|
0e5e2f3fb9 | ||
|
|
eaefa3c1fa | ||
|
|
50d82155ed | ||
|
|
f25aeadf21 | ||
|
|
7366d7eae0 | ||
|
|
2ed753232f | ||
|
|
0778f31828 | ||
|
|
da3aea5e0a | ||
|
|
5b3e6e67ea | ||
|
|
7b42e13c79 | ||
|
|
ff108bd871 | ||
|
|
3641e85233 | ||
|
|
7eec60ec2e | ||
|
|
dd7f42ba03 | ||
|
|
9aa4f5c42a | ||
|
|
af79d046db | ||
|
|
3fac31e808 | ||
|
|
8175783f93 | ||
|
|
c927209447 | ||
|
|
3f1452fddb | ||
|
|
1258dc1cbb | ||
|
|
ca0085f429 | ||
|
|
a98124add7 | ||
|
|
ae249a623d | ||
|
|
63f78c0b59 | ||
|
|
632ae0986c | ||
|
|
31243e7132 | ||
|
|
367acc37c7 | ||
|
|
e695ce4aa5 | ||
|
|
dd0f4460cb | ||
|
|
f22d1af9e0 | ||
|
|
c0c86c1138 | ||
|
|
6aad2a5b2d | ||
|
|
f0e0e61809 | ||
|
|
ed661767f8 | ||
|
|
79b873e40e | ||
|
|
8fe21b43e9 | ||
|
|
53d862143b | ||
|
|
491fa6cad8 | ||
|
|
bf88242282 | ||
|
|
b8d9722b4f | ||
|
|
f6aefbd361 | ||
|
|
fe05a996b7 | ||
|
|
a45e2ec9c3 | ||
|
|
16b5b9f6be | ||
|
|
c0260fd913 | ||
|
|
7f9f388293 | ||
|
|
0229d28d64 | ||
|
|
55f1b36e31 | ||
|
|
adda84d124 | ||
|
|
837beda155 | ||
|
|
9500eb8aaa | ||
|
|
1887c4f279 | ||
|
|
01fc37571e | ||
|
|
de17dcf3b0 | ||
|
|
fed35909cc | ||
|
|
f1036a9382 | ||
|
|
0db678e18f | ||
|
|
416510773b | ||
|
|
ece6588c03 | ||
|
|
5e013f82b4 | ||
|
|
f8b8907cc4 | ||
|
|
84b1519480 | ||
|
|
d355862328 | ||
|
|
4326790bb4 | ||
|
|
4a6da233d9 | ||
|
|
fdec9a4daf | ||
|
|
bef4b9b3b0 | ||
|
|
3b565dccd8 | ||
|
|
88a8991722 | ||
|
|
13e8eb6d2e | ||
|
|
3a7ed2305d | ||
|
|
442f625a4e | ||
|
|
5f6de33487 | ||
|
|
353bb57a6f | ||
|
|
6c7454fc2b | ||
|
|
d6725d4fbe | ||
|
|
5749489e7e | ||
|
|
d0ddf76730 | ||
|
|
915412754c | ||
|
|
6d901fe145 | ||
|
|
00b18a8aea | ||
|
|
a20c419583 | ||
|
|
bcf784375b | ||
|
|
861843f4ca | ||
|
|
83ddd2b158 | ||
|
|
f211455643 | ||
|
|
f37a595f68 | ||
|
|
ec29b469e4 | ||
|
|
e3211f6e48 | ||
|
|
ee2e4c6567 | ||
|
|
98c84f9458 | ||
|
|
bc678899f7 | ||
|
|
d55b49b7b7 | ||
|
|
3c8133aa4b | ||
|
|
8c8fdadb78 | ||
|
|
e128310045 | ||
|
|
89c86c7a22 | ||
|
|
935dad653c | ||
|
|
e84ff24833 | ||
|
|
a234e894bb | ||
|
|
f160ad3bc8 | ||
|
|
2e350bbb8e | ||
|
|
2ac6e23fc5 | ||
|
|
89eda76f29 | ||
|
|
a687a6605d | ||
|
|
7a80a6627a | ||
|
|
a081f90403 | ||
|
|
d99c873221 | ||
|
|
c94f5b456a | ||
|
|
e978bfc368 | ||
|
|
67469e1809 | ||
|
|
8cdf12b694 | ||
|
|
114ba801b9 | ||
|
|
87308fd97f | ||
|
|
16b1c0e63e | ||
|
|
c7698a09ed | ||
|
|
d0ab028cfc | ||
|
|
7e3c9fcc1c | ||
|
|
fcdf282090 | ||
|
|
1188f8df73 | ||
|
|
72d7c4d620 | ||
|
|
93cff1a42a | ||
|
|
54f0650321 | ||
|
|
f534ea057c | ||
|
|
3aed6595c7 | ||
|
|
9fdecb320c | ||
|
|
7cac2e1817 | ||
|
|
c532c8640b | ||
|
|
0107f1eddb | ||
|
|
719151b9cb | ||
|
|
210df23430 | ||
|
|
a5c08a72fb | ||
|
|
cffbcb504d | ||
|
|
5bdd554671 | ||
|
|
6bf8144793 | ||
|
|
1a5e786467 | ||
|
|
56c0d91ee5 | ||
|
|
d530ccff1c | ||
|
|
40b74e6664 | ||
|
|
37962216ed | ||
|
|
7a7837f47e | ||
|
|
2a62374a61 | ||
|
|
f5a17e535b | ||
|
|
13eb0c1ece | ||
|
|
567e877b9e | ||
|
|
6f8110956d | ||
|
|
56cb16ff5b | ||
|
|
24a165662c | ||
|
|
185b0dcc05 | ||
|
|
84c6caabc5 | ||
|
|
81d3ab37c3 | ||
|
|
9666f45e9c | ||
|
|
c6f7d34c55 | ||
|
|
3e0b92d6fe | ||
|
|
665caa8e08 |
@@ -1,7 +1,7 @@
|
||||
[run]
|
||||
init_cmds = [
|
||||
["go", "build", "-o", "./bin/grafana-server", "./pkg/cmd/grafana-server"],
|
||||
["./bin/grafana-server"]
|
||||
["./bin/grafana-server", "cfg:app_mode=development"]
|
||||
]
|
||||
watch_all = true
|
||||
watch_dirs = [
|
||||
@@ -9,9 +9,9 @@ watch_dirs = [
|
||||
"$WORKDIR/public/views",
|
||||
"$WORKDIR/conf",
|
||||
]
|
||||
watch_exts = [".go", ".ini", ".toml", ".html"]
|
||||
watch_exts = [".go", ".ini", ".toml"]
|
||||
build_delay = 1500
|
||||
cmds = [
|
||||
["go", "build", "-o", "./bin/grafana-server", "./pkg/cmd/grafana-server"],
|
||||
["./bin/grafana-server"]
|
||||
["./bin/grafana-server", "cfg:app_mode=development"]
|
||||
]
|
||||
|
||||
@@ -1,13 +1,6 @@
|
||||
# http://editorconfig.org
|
||||
root = true
|
||||
|
||||
[*.go]
|
||||
indent_style = tab
|
||||
indent_size = 4
|
||||
charset = utf-8
|
||||
trim_trailing_whitespace = true
|
||||
insert_final_newline = true
|
||||
|
||||
[*]
|
||||
indent_style = space
|
||||
indent_size = 2
|
||||
@@ -15,5 +8,12 @@ charset = utf-8
|
||||
trim_trailing_whitespace = true
|
||||
insert_final_newline = true
|
||||
|
||||
[*.go]
|
||||
indent_style = tab
|
||||
indent_size = 4
|
||||
charset = utf-8
|
||||
trim_trailing_whitespace = true
|
||||
insert_final_newline = true
|
||||
|
||||
[*.md]
|
||||
trim_trailing_whitespace = false
|
||||
|
||||
11
.gitignore
vendored
11
.gitignore
vendored
@@ -4,6 +4,8 @@ coverage/
|
||||
.aws-config.json
|
||||
awsconfig
|
||||
/dist
|
||||
/public/build
|
||||
/public/views/index.html
|
||||
/emails/dist
|
||||
/public_gen
|
||||
/public/vendor/npm
|
||||
@@ -25,6 +27,7 @@ public/css/*.min.css
|
||||
.idea/
|
||||
*.iml
|
||||
*.tmp
|
||||
.DS_Store
|
||||
.vscode/
|
||||
|
||||
/data/*
|
||||
@@ -38,6 +41,14 @@ profile.cov
|
||||
.notouch
|
||||
/pkg/cmd/grafana-cli/grafana-cli
|
||||
/pkg/cmd/grafana-server/grafana-server
|
||||
/pkg/cmd/grafana-server/debug
|
||||
/examples/*/dist
|
||||
/packaging/**/*.rpm
|
||||
/packaging/**/*.deb
|
||||
|
||||
/vendor/**/*.py
|
||||
/vendor/**/*.xml
|
||||
/vendor/**/*.yml
|
||||
/vendor/**/*_test.go
|
||||
/vendor/**/.editorconfig
|
||||
/vendor/**/appengine*
|
||||
|
||||
21
.jsfmtrc
21
.jsfmtrc
@@ -1,21 +0,0 @@
|
||||
{
|
||||
"preset" : "default",
|
||||
|
||||
"lineBreak" : {
|
||||
"before" : {
|
||||
"VariableDeclarationWithoutInit" : 0,
|
||||
},
|
||||
|
||||
"after": {
|
||||
"AssignmentOperator": -1,
|
||||
"ArgumentListArrayExpression": ">=1"
|
||||
}
|
||||
},
|
||||
|
||||
"whiteSpace" : {
|
||||
"before" : {
|
||||
},
|
||||
"after" : {
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"browser": true,
|
||||
|
||||
"esversion": 6,
|
||||
"bitwise":false,
|
||||
"curly": true,
|
||||
"eqnull": true,
|
||||
|
||||
60
CHANGELOG.md
60
CHANGELOG.md
@@ -7,12 +7,69 @@
|
||||
- UX changes to nav & side menu
|
||||
- New dashboard grid layout system
|
||||
|
||||
# 4.6.0-beta2 (2017-10-17)
|
||||
|
||||
## Fixes
|
||||
* **ColorPicker**: Fix for color picker not showing [#9549](https://github.com/grafana/grafana/issues/9549)
|
||||
|
||||
# 4.6.0-beta1 (2017-10-13)
|
||||
|
||||
## New Features
|
||||
* **GCS**: Adds support for Google Cloud Storage [#8370](https://github.com/grafana/grafana/issues/8370) thx [@chuhlomin](https://github.com/chuhlomin)
|
||||
* **Prometheus**: Adds /metrics endpoint for exposing Grafana metrics. [#9187](https://github.com/grafana/grafana/pull/9187)
|
||||
* **Graph**: Add support for local formating in axis. [#1395](https://github.com/grafana/grafana/issues/1395), thx [@m0nhawk](https://github.com/m0nhawk)
|
||||
* **Jaeger**: Add support for open tracing using jaeger in Grafana. [#9213](https://github.com/grafana/grafana/pull/9213)
|
||||
* **Unit types**: New date & time unit types added, useful in singlestat to show dates & times. [#3678](https://github.com/grafana/grafana/issues/3678), [#6710](https://github.com/grafana/grafana/issues/6710), [#2764](https://github.com/grafana/grafana/issues/2764)
|
||||
* **CLI**: Make it possible to install plugins from any url [#5873](https://github.com/grafana/grafana/issues/5873)
|
||||
* **Prometheus**: Add support for instant queries [#5765](https://github.com/grafana/grafana/issues/5765), thx [@mtanda](https://github.com/mtanda)
|
||||
* **Cloudwatch**: Add support for alerting using the cloudwatch datasource [#8050](https://github.com/grafana/grafana/pull/8050), thx [@mtanda](https://github.com/mtanda)
|
||||
* **Pagerduty**: Include triggering series in pagerduty notification [#8479](https://github.com/grafana/grafana/issues/8479), thx [@rickymoorhouse](https://github.com/rickymoorhouse)
|
||||
* **Timezone**: Time ranges like Today & Yesterday now work correctly when timezone setting is set to UTC [#8916](https://github.com/grafana/grafana/issues/8916), thx [@ctide](https://github.com/ctide)
|
||||
* **Prometheus**: Align $__interval with the step parameters. [#9226](https://github.com/grafana/grafana/pull/9226), thx [@alin-amana](https://github.com/alin-amana)
|
||||
* **Prometheus**: Autocomplete for label name and label value [#9208](https://github.com/grafana/grafana/pull/9208), thx [@mtanda](https://github.com/mtanda)
|
||||
* **Postgres**: New Postgres data source [#9209](https://github.com/grafana/grafana/pull/9209), thx [@svenklemm](https://github.com/svenklemm)
|
||||
* **Datasources**: Make datasource HTTP requests verify TLS by default. closes [#9371](https://github.com/grafana/grafana/issues/9371), [#5334](https://github.com/grafana/grafana/issues/5334), [#8812](https://github.com/grafana/grafana/issues/8812), thx [@mattbostock](https://github.com/mattbostock)
|
||||
* **OAuth**: Verify TLS during OAuth callback [#9373](https://github.com/grafana/grafana/issues/9373), thx [@mattbostock](https://github.com/mattbostock)
|
||||
|
||||
## Minor
|
||||
* **SMTP**: Make it possible to set specific EHLO for smtp client. [#9319](https://github.com/grafana/grafana/issues/9319)
|
||||
* **Dataproxy**: Allow grafan to renegotiate tls connection [#9250](https://github.com/grafana/grafana/issues/9250)
|
||||
* **HTTP**: set net.Dialer.DualStack to true for all http clients [#9367](https://github.com/grafana/grafana/pull/9367)
|
||||
* **Alerting**: Add diff and percent diff as series reducers [#9386](https://github.com/grafana/grafana/pull/9386), thx [@shanhuhai5739](https://github.com/shanhuhai5739)
|
||||
* **Slack**: Allow images to be uploaded to slack when Token is precent [#7175](https://github.com/grafana/grafana/issues/7175), thx [@xginn8](https://github.com/xginn8)
|
||||
* **Opsgenie**: Use their latest API instead of old version [#9399](https://github.com/grafana/grafana/pull/9399), thx [@cglrkn](https://github.com/cglrkn)
|
||||
* **Table**: Add support for displaying the timestamp with milliseconds [#9429](https://github.com/grafana/grafana/pull/9429), thx [@s1061123](https://github.com/s1061123)
|
||||
* **Hipchat**: Add metrics, message and image to hipchat notifications [#9110](https://github.com/grafana/grafana/issues/9110), thx [@eloo](https://github.com/eloo)
|
||||
* **Kafka**: Add support for sending alert notifications to kafka [#7104](https://github.com/grafana/grafana/issues/7104), thx [@utkarshcmu](https://github.com/utkarshcmu)
|
||||
* **Alerting**: add count_non_null as series reducer [#9516](https://github.com/grafana/grafana/issues/9516)
|
||||
|
||||
## Tech
|
||||
* **Go**: Grafana is now built using golang 1.9
|
||||
* **Webpack**: Changed from systemjs to webpack (see readme or building from source guide for new build instructions). Systemjs is still used to load plugins but now plugins can only import a limited set of dependencies. See [PLUGIN_DEV.md](https://github.com/grafana/grafana/blob/master/PLUGIN_DEV.md) for more details on how this can effect some plugins.
|
||||
|
||||
# 4.5.2 (2017-09-22)
|
||||
|
||||
## Fixes
|
||||
* **Graphite**: Fix for issues with jsonData & graphiteVersion null errors [#9258](https://github.com/grafana/grafana/issues/9258)
|
||||
* **Graphite**: Fix for Grafana internal metrics to Graphite sending NaN values [#9279](https://github.com/grafana/grafana/issues/9279)
|
||||
* **HTTP API**: Fix for HEAD method requests [#9307](https://github.com/grafana/grafana/issues/9307)
|
||||
* **Templating**: Fix for duplicate template variable queries when refresh is set to time range change [#9185](https://github.com/grafana/grafana/issues/9185)
|
||||
* **Metrics**: dont write NaN values to graphite [#9279](https://github.com/grafana/grafana/issues/9279)
|
||||
|
||||
# 4.5.1 (2017-09-15)
|
||||
|
||||
## Fixes
|
||||
* **MySQL**: Fixed issue with query editor not showing [#9247](https://github.com/grafana/grafana/issues/9247)
|
||||
|
||||
## Breaking changes
|
||||
* **Metrics**: The metric structure for internal metrics about Grafana published to graphite has changed. This might break dashboards for internal metrics.
|
||||
|
||||
# 4.5.0 (2017-09-14)
|
||||
|
||||
## Fixes & Enhancements since beta1
|
||||
* **Security**: Security fix for api vulnerability (in multiple org setups).
|
||||
* **Shortcuts**: Adds shortcut for creating new dashboard [#8876](https://github.com/grafana/grafana/pull/8876) thx [@mtanda](https://github.com/mtanda)
|
||||
* **Graph**: Right Y-Axis label position fixed [#9172](https://github.com/grafana/grafana/pull/9172)
|
||||
* **Graph**: Right Y-Axis label position fixed [#9172](https://github.com/grafana/grafana/pull/9172)
|
||||
* **General**: Improve rounding of time intervals [#9197](https://github.com/grafana/grafana/pull/9197), thx [@alin-amana](https://github.com/alin-amana)
|
||||
|
||||
# 4.5.0-beta1 (2017-09-05)
|
||||
@@ -34,6 +91,7 @@
|
||||
### Breaking change
|
||||
|
||||
* **InfluxDB/Elasticsearch**: The panel & data source option named "Group by time interval" is now named "Min time interval" and does now always define a lower limit for the auto group by time. Without having to use `>` prefix (that prefix still works). This should in theory have close to zero actual impact on existing dashboards. It does mean that if you used this setting to define a hard group by time interval of, say "1d", if you zoomed to a time range wide enough the time range could increase above the "1d" range as the setting is now always considered a lower limit.
|
||||
* **Elasticsearch**: Elasticsearch metric queries without date histogram now return table formated data making table panel much easier to use for this use case. Should not break/change existing dashboards with stock panels but external panel plugins can be affected.
|
||||
|
||||
## Changes
|
||||
|
||||
|
||||
@@ -31,7 +31,7 @@ module.exports = function (grunt) {
|
||||
require('load-grunt-tasks')(grunt);
|
||||
|
||||
// load task definitions
|
||||
grunt.loadTasks('tasks');
|
||||
grunt.loadTasks('./scripts/grunt');
|
||||
|
||||
// Utility function to load plugin settings into config
|
||||
function loadConfig(config,path) {
|
||||
@@ -46,7 +46,7 @@ module.exports = function (grunt) {
|
||||
}
|
||||
|
||||
// Merge that object with what with whatever we have here
|
||||
loadConfig(config,'./tasks/options/');
|
||||
loadConfig(config,'./scripts/grunt/options/');
|
||||
// pass the config to grunt
|
||||
grunt.initConfig(config);
|
||||
};
|
||||
|
||||
28
PLUGIN_DEV.md
Normal file
28
PLUGIN_DEV.md
Normal file
@@ -0,0 +1,28 @@
|
||||
# Plugin Development
|
||||
|
||||
This document is not meant as complete guide for developing plugins but more as a changelog for changes in
|
||||
Grafana that can impact plugin development. When ever you as plugin author encounter an issue with your plugin after
|
||||
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)
|
||||
- [Plugin development guide](http://docs.grafana.org/plugins/developing/development/)
|
||||
|
||||
## Changes in v4.6
|
||||
|
||||
This version of Grafana has big changes that will impact a limited set of plugins. We moved from systemjs to webpack
|
||||
for built-in plugins & everything internal. External plugins still use systemjs but now with a limited
|
||||
set of Grafana components they can import. Plugins can depend on libs like lodash & moment and internal components
|
||||
like before using the same import paths. However since everything in Grafana is no longer accessible, a few plugins could encounter issues when importing a Grafana dependency.
|
||||
|
||||
[List of exposed components plugins can import/require](https://github.com/grafana/grafana/blob/master/public/app/features/plugins/plugin_loader.ts#L48)
|
||||
|
||||
If you think we missed exposing a crucial lib or Grafana component let us know by opening an issue.
|
||||
|
||||
### Deprecated components
|
||||
|
||||
The angular directive `<spectrum-picker>` is no deprecated (will still work for a version more) but we recommend plugin authors
|
||||
to upgrade to new `<color-picker color="ctrl.color" onChange="ctrl.onSparklineColorChange"></color-picker>`
|
||||
|
||||
31
README.md
31
README.md
@@ -24,7 +24,7 @@ the latest master builds [here](https://grafana.com/grafana/download)
|
||||
|
||||
### Dependencies
|
||||
|
||||
- Go 1.8.1
|
||||
- Go 1.9
|
||||
- NodeJS LTS
|
||||
|
||||
### Building the backend
|
||||
@@ -37,8 +37,7 @@ go run build.go build
|
||||
|
||||
### Building frontend assets
|
||||
|
||||
To build less to css for the frontend you will need a recent version of **node (v6+)**,
|
||||
npm (v2.5.0) and grunt (v0.4.5). Run the following:
|
||||
For this you need nodejs (v.6+).
|
||||
|
||||
```bash
|
||||
npm install -g yarn
|
||||
@@ -46,13 +45,24 @@ yarn install --pure-lockfile
|
||||
npm run build
|
||||
```
|
||||
|
||||
To build the frontend assets only on changes:
|
||||
To rebuild frontend assets (typescript, sass etc) as you change them start the watcher via.
|
||||
|
||||
```bash
|
||||
npm run dev
|
||||
npm run watch
|
||||
```
|
||||
|
||||
Run tests
|
||||
```bash
|
||||
npm run test
|
||||
```
|
||||
|
||||
Run tests in watch mode
|
||||
```bash
|
||||
npm run watch-test
|
||||
```
|
||||
|
||||
### Recompile backend on source change
|
||||
|
||||
To rebuild on source change.
|
||||
```bash
|
||||
go get github.com/Unknwon/bra
|
||||
@@ -69,11 +79,20 @@ You only need to add the options you want to override. Config files are applied
|
||||
1. grafana.ini
|
||||
1. custom.ini
|
||||
|
||||
In your custom.ini uncomment (remove the leading `;`) sign. And set `app_mode = development`.
|
||||
|
||||
## Contribute
|
||||
|
||||
If you have any idea for an improvement or found a bug do not hesitate to open an issue.
|
||||
And if you have time clone this repo and submit a pull request and help me make Grafana
|
||||
the kickass metrics & devops dashboard we all dream about!
|
||||
|
||||
## Plugin development
|
||||
|
||||
Checkout the [Plugin Development Guide](http://docs.grafana.org/plugins/developing/development/) and checkout the [PLUGIN_DEV.md](https://github.com/grafana/grafana/blob/master/PLUGIN_DEV.md) file for changes in Grafana that relate to
|
||||
plugin development.
|
||||
|
||||
## License
|
||||
|
||||
Grafana is distributed under Apache 2.0 License.
|
||||
Work in progress Grafana 2.0 (with included Grafana backend)
|
||||
|
||||
|
||||
@@ -17,7 +17,7 @@ But it will give you an idea of our current vision and plan.
|
||||
### Long term
|
||||
|
||||
- Backend plugins to support more Auth options, Alerting data sources & notifications
|
||||
- Universial time series transformations for any data source (meta queries)
|
||||
- Universal time series transformations for any data source (meta queries)
|
||||
- Reporting
|
||||
- Web socket & live data streams
|
||||
- Migrate to Angular2 or react
|
||||
|
||||
@@ -7,7 +7,7 @@ clone_folder: c:\gopath\src\github.com\grafana\grafana
|
||||
environment:
|
||||
nodejs_version: "6"
|
||||
GOPATH: c:\gopath
|
||||
GOVERSION: 1.8
|
||||
GOVERSION: 1.9.2
|
||||
|
||||
install:
|
||||
- rmdir c:\go /s /q
|
||||
|
||||
26
bower.json
26
bower.json
@@ -1,26 +0,0 @@
|
||||
{
|
||||
"name": "grafana",
|
||||
"version": "2.0.2",
|
||||
"homepage": "https://github.com/grafana/grafana",
|
||||
"authors": [],
|
||||
"license": "Apache 2.0",
|
||||
"ignore": [
|
||||
"**/.*",
|
||||
"node_modules",
|
||||
"bower_components",
|
||||
"public/vendor/",
|
||||
"test",
|
||||
"tests"
|
||||
],
|
||||
"dependencies": {
|
||||
"jquery": "3.1.0",
|
||||
"lodash": "4.15.0",
|
||||
"angular": "1.6.1",
|
||||
"angular-route": "1.6.1",
|
||||
"angular-mocks": "1.6.1",
|
||||
"angular-sanitize": "1.6.1",
|
||||
"angular-native-dragdrop": "1.2.2",
|
||||
"angular-bindonce": "0.3.3",
|
||||
"clipboard": "^1.5.16"
|
||||
}
|
||||
}
|
||||
@@ -9,7 +9,7 @@ machine:
|
||||
GOPATH: "/home/ubuntu/.go_workspace"
|
||||
ORG_PATH: "github.com/grafana"
|
||||
REPO_PATH: "${ORG_PATH}/grafana"
|
||||
GODIST: "go1.8.linux-amd64.tar.gz"
|
||||
GODIST: "go1.9.2.linux-amd64.tar.gz"
|
||||
post:
|
||||
- mkdir -p ~/download
|
||||
- mkdir -p ~/docker
|
||||
|
||||
@@ -318,6 +318,7 @@ key_file =
|
||||
skip_verify = false
|
||||
from_address = admin@grafana.localhost
|
||||
from_name = Grafana
|
||||
ehlo_identity =
|
||||
|
||||
[emails]
|
||||
welcome_email_on_sign_up = false
|
||||
@@ -452,13 +453,33 @@ url = https://grafana.com
|
||||
[grafana_com]
|
||||
url = https://grafana.com
|
||||
|
||||
#################################### Distributed tracing ############
|
||||
[tracing.jaeger]
|
||||
# jaeger destination (ex localhost:6831)
|
||||
address =
|
||||
# tag that will always be included in when creating new spans. ex (tag1:value1,tag2:value2)
|
||||
always_included_tag =
|
||||
# Type specifies the type of the sampler: const, probabilistic, rateLimiting, or remote
|
||||
sampler_type = const
|
||||
# jaeger samplerconfig param
|
||||
# for "const" sampler, 0 or 1 for always false/true respectively
|
||||
# for "probabilistic" sampler, a probability between 0 and 1
|
||||
# for "rateLimiting" sampler, the number of spans per second
|
||||
# for "remote" sampler, param is the same as for "probabilistic"
|
||||
# and indicates the initial sampling rate before the actual one
|
||||
# is received from the mothership
|
||||
sampler_param = 1
|
||||
|
||||
#################################### External Image Storage ##############
|
||||
[external_image_storage]
|
||||
# You can choose between (s3, webdav)
|
||||
# You can choose between (s3, webdav, gcs)
|
||||
provider =
|
||||
|
||||
[external_image_storage.s3]
|
||||
bucket_url =
|
||||
bucket =
|
||||
region =
|
||||
path =
|
||||
access_key =
|
||||
secret_key =
|
||||
|
||||
@@ -467,3 +488,7 @@ url =
|
||||
username =
|
||||
password =
|
||||
public_url =
|
||||
|
||||
[external_image_storage.gcs]
|
||||
key_file =
|
||||
bucket =
|
||||
|
||||
@@ -295,6 +295,8 @@
|
||||
;skip_verify = false
|
||||
;from_address = admin@grafana.localhost
|
||||
;from_name = Grafana
|
||||
# EHLO identity in SMTP dialog (defaults to instance_name)
|
||||
;ehlo_identity = dashboard.example.com
|
||||
|
||||
[emails]
|
||||
;welcome_email_on_sign_up = false
|
||||
@@ -391,6 +393,23 @@
|
||||
;address =
|
||||
;prefix = prod.grafana.%(instance_name)s.
|
||||
|
||||
#################################### Distributed tracing ############
|
||||
[tracing.jaeger]
|
||||
# Enable by setting the address sending traces to jaeger (ex localhost:6831)
|
||||
;address = localhost:6831
|
||||
# Tag that will always be included in when creating new spans. ex (tag1:value1,tag2:value2)
|
||||
;always_included_tag = tag1:value1
|
||||
# Type specifies the type of the sampler: const, probabilistic, rateLimiting, or remote
|
||||
;sampler_type = const
|
||||
# jaeger samplerconfig param
|
||||
# for "const" sampler, 0 or 1 for always false/true respectively
|
||||
# for "probabilistic" sampler, a probability between 0 and 1
|
||||
# for "rateLimiting" sampler, the number of spans per second
|
||||
# for "remote" sampler, param is the same as for "probabilistic"
|
||||
# and indicates the initial sampling rate before the actual one
|
||||
# is received from the mothership
|
||||
;sampler_param = 1
|
||||
|
||||
#################################### Grafana.com integration ##########################
|
||||
# Url used to to import dashboards directly from Grafana.com
|
||||
[grafana_com]
|
||||
@@ -399,11 +418,13 @@
|
||||
#################################### External image storage ##########################
|
||||
[external_image_storage]
|
||||
# Used for uploading images to public servers so they can be included in slack/email messages.
|
||||
# you can choose between (s3, webdav)
|
||||
# you can choose between (s3, webdav, gcs)
|
||||
;provider =
|
||||
|
||||
[external_image_storage.s3]
|
||||
;bucket_url =
|
||||
;bucket =
|
||||
;region =
|
||||
;path =
|
||||
;access_key =
|
||||
;secret_key =
|
||||
|
||||
@@ -412,3 +433,7 @@
|
||||
;public_url =
|
||||
;username =
|
||||
;password =
|
||||
|
||||
[external_image_storage.gcs]
|
||||
;key_file =
|
||||
;bucket =
|
||||
|
||||
6
docker/blocks/jaeger/fig
Normal file
6
docker/blocks/jaeger/fig
Normal file
@@ -0,0 +1,6 @@
|
||||
jaeger:
|
||||
image: jaegertracing/all-in-one:latest
|
||||
ports:
|
||||
- "localhost:6831:6831/udp"
|
||||
- "16686:16686"
|
||||
|
||||
@@ -1,2 +1,3 @@
|
||||
FROM prom/prometheus
|
||||
ADD prometheus.yml /etc/prometheus/
|
||||
ADD alert.rules /etc/prometheus/
|
||||
|
||||
10
docker/blocks/prometheus/alert.rules
Normal file
10
docker/blocks/prometheus/alert.rules
Normal file
@@ -0,0 +1,10 @@
|
||||
# Alert Rules
|
||||
|
||||
ALERT AppCrash
|
||||
IF process_open_fds > 0
|
||||
FOR 15s
|
||||
LABELS { severity="critical" }
|
||||
ANNOTATIONS {
|
||||
summary = "Number of open fds > 0",
|
||||
description = "Just testing"
|
||||
}
|
||||
@@ -18,3 +18,8 @@ fake-prometheus-data:
|
||||
environment:
|
||||
FD_DATASOURCE: prom
|
||||
|
||||
alertmanager:
|
||||
image: quay.io/prometheus/alertmanager
|
||||
net: host
|
||||
ports:
|
||||
- "9093:9093"
|
||||
|
||||
@@ -6,22 +6,30 @@ global:
|
||||
|
||||
# Load and evaluate rules in this file every 'evaluation_interval' seconds.
|
||||
rule_files:
|
||||
- "alert.rules"
|
||||
# - "first.rules"
|
||||
# - "second.rules"
|
||||
|
||||
# A scrape configuration containing exactly one endpoint to scrape:
|
||||
# Here it's Prometheus itself.
|
||||
scrape_configs:
|
||||
# The job name is added as a label `job=<job_name>` to any timeseries scraped from this config.
|
||||
- job_name: 'prometheus'
|
||||
|
||||
# Override the global default and scrape targets from this job every 5 seconds.
|
||||
scrape_interval: 10s
|
||||
scrape_timeout: 10s
|
||||
|
||||
# metrics_path defaults to '/metrics'
|
||||
# scheme defaults to 'http'.
|
||||
|
||||
alerting:
|
||||
alertmanagers:
|
||||
- scheme: http
|
||||
static_configs:
|
||||
#- targets: ['localhost:9090', '172.17.0.1:9091', '172.17.0.1:9100', '172.17.0.1:9150']
|
||||
- targets: ['localhost:9090', '127.0.0.1:9091', '127.0.0.1:9100', '127.0.0.1:9150']
|
||||
- targets:
|
||||
- "127.0.0.1:9093"
|
||||
|
||||
scrape_configs:
|
||||
- job_name: 'prometheus'
|
||||
static_configs:
|
||||
- targets: ['localhost:9090']
|
||||
|
||||
- job_name: 'node_exporter'
|
||||
static_configs:
|
||||
- targets: ['127.0.0.1:9100']
|
||||
|
||||
- job_name: 'fake-data-gen'
|
||||
static_configs:
|
||||
- targets: ['127.0.0.1:9091']
|
||||
|
||||
- job_name: 'grafana'
|
||||
static_configs:
|
||||
- targets: ['127.0.0.1:3000']
|
||||
|
||||
@@ -41,7 +41,7 @@ then there are two flags that can be used to set homepath and the config file pa
|
||||
|
||||
If you have not lost the admin password then it is better to set in the Grafana UI. If you need to set the password in a script then the [Grafana API](http://docs.grafana.org/http_api/user/#change-password) can be used. Here is an example with curl using basic auth:
|
||||
|
||||
```
|
||||
```bash
|
||||
curl -X PUT -H "Content-Type: application/json" -d '{
|
||||
"oldPassword": "admin",
|
||||
"newPassword": "newpass",
|
||||
|
||||
@@ -48,12 +48,15 @@ external image destination if available or fallback to attaching the image in th
|
||||
|
||||
To set up slack you need to configure an incoming webhook url at slack. You can follow their guide for how
|
||||
to do that https://api.slack.com/incoming-webhooks If you want to include screenshots of the firing alerts
|
||||
in the slack messages you have to configure the [external image destination](#external-image-store) in Grafana.
|
||||
in the slack messages you have to configure either the [external image destination](#external-image-store) in Grafana,
|
||||
or a bot integration via Slack Apps. Follow Slack's guide to set up a bot integration and use the token provided
|
||||
https://api.slack.com/bot-users, which starts with "xoxb".
|
||||
|
||||
Setting | Description
|
||||
---------- | -----------
|
||||
Recipient | allows you to override the slack recipient.
|
||||
Mention | make it possible to include a mention in the slack notification sent by Grafana. Ex @here or @channel
|
||||
Token | If provided, Grafana will upload the generated image via Slack's file.upload API method, not the external image destination.
|
||||
|
||||
### PagerDuty
|
||||
|
||||
@@ -112,6 +115,17 @@ In DingTalk PC Client:
|
||||
|
||||
Dingtalk supports the following "message type": `text`, `link` and `markdown`. Only the `text` message type is supported.
|
||||
|
||||
### Kafka
|
||||
|
||||
Notifications can be sent to a Kafka topic from Grafana using [Kafka REST Proxy](https://docs.confluent.io/1.0/kafka-rest/docs/index.html).
|
||||
There are couple of configurations options which need to be set in Grafana UI under Kafka Settings:
|
||||
|
||||
1. Kafka REST Proxy endpoint.
|
||||
|
||||
2. Kafka Topic.
|
||||
|
||||
Once these two properties are set, you can send the alerts to Kafka for further processing or throttling them.
|
||||
|
||||
### Other Supported Notification Channels
|
||||
|
||||
Grafana also supports the following Notification Channels:
|
||||
|
||||
@@ -50,11 +50,12 @@ Create a file at `~/.aws/credentials`. That is the `HOME` path for user running
|
||||
|
||||
Example content:
|
||||
|
||||
[default]
|
||||
aws_access_key_id = asdsadasdasdasd
|
||||
aws_secret_access_key = dasdasdsadasdasdasdsa
|
||||
region = us-west-2
|
||||
|
||||
```bash
|
||||
[default]
|
||||
aws_access_key_id = asdsadasdasdasd
|
||||
aws_secret_access_key = dasdasdsadasdasdasdsa
|
||||
region = us-west-2
|
||||
```
|
||||
|
||||
## Metric Query Editor
|
||||
|
||||
@@ -117,7 +118,9 @@ Filters syntax:
|
||||
|
||||
Example `ec2_instance_attribute()` query
|
||||
|
||||
ec2_instance_attribute(us-east-1, InstanceId, { "tag:Environment": [ "production" ] })
|
||||
```javascript
|
||||
ec2_instance_attribute(us-east-1, InstanceId, { "tag:Environment": [ "production" ] })
|
||||
```
|
||||
|
||||
### Selecting Attributes
|
||||
|
||||
@@ -156,7 +159,9 @@ Tags can be selected by prepending the tag name with `Tags.`
|
||||
|
||||
Example `ec2_instance_attribute()` query
|
||||
|
||||
ec2_instance_attribute(us-east-1, Tags.Name, { "tag:Team": [ "sysops" ] })
|
||||
```javascript
|
||||
ec2_instance_attribute(us-east-1, Tags.Name, { "tag:Team": [ "sysops" ] })
|
||||
```
|
||||
|
||||
## Cost
|
||||
|
||||
|
||||
@@ -38,8 +38,10 @@ Proxy access means that the Grafana backend will proxy all requests from the bro
|
||||
If you select direct access you must update your Elasticsearch configuration to allow other domains to access
|
||||
Elasticsearch from the browser. You do this by specifying these to options in your **elasticsearch.yml** config file.
|
||||
|
||||
http.cors.enabled: true
|
||||
http.cors.allow-origin: "*"
|
||||
```bash
|
||||
http.cors.enabled: true
|
||||
http.cors.allow-origin: "*"
|
||||
```
|
||||
|
||||
### Index settings
|
||||
|
||||
@@ -133,6 +135,5 @@ Name | Description
|
||||
------------ | -------------
|
||||
Query | You can leave the search query blank or specify a lucene query
|
||||
Time | The name of the time field, needs to be date field.
|
||||
Title | The name of the field to use for the event title.
|
||||
Text | Event description field.
|
||||
Tags | Optional field name to use for event tags (can be an array or a CSV string).
|
||||
Text | Optional field name to use event text body.
|
||||
|
||||
186
docs/sources/features/datasources/postgres.md
Normal file
186
docs/sources/features/datasources/postgres.md
Normal file
@@ -0,0 +1,186 @@
|
||||
+++
|
||||
title = "Using PostgreSQL in Grafana"
|
||||
description = "Guide for using PostgreSQL in Grafana"
|
||||
keywords = ["grafana", "postgresql", "guide"]
|
||||
type = "docs"
|
||||
[menu.docs]
|
||||
name = "PostgreSQL"
|
||||
parent = "datasources"
|
||||
weight = 7
|
||||
+++
|
||||
|
||||
# Using PostgreSQL in Grafana
|
||||
|
||||
Grafana ships with a built-in PostgreSQL data source plugin that allows you to query and visualize data from a PostgreSQL compatible database.
|
||||
|
||||
## Adding the data source
|
||||
|
||||
1. Open the side menu by clicking the Grafana icon in the top header.
|
||||
2. In the side menu under the `Dashboards` link you should find a link named `Data Sources`.
|
||||
3. Click the `+ Add data source` button in the top header.
|
||||
4. Select *PostgreSQL* from the *Type* dropdown.
|
||||
|
||||
### Database User Permissions (Important!)
|
||||
|
||||
The database user you specify when you add the data source should only be granted SELECT permissions on
|
||||
the specified database & tables you want to query. Grafana does not validate that the query is safe. The query
|
||||
could include any SQL statement. For example, statements like `DELETE FROM user;` and `DROP TABLE user;` would be
|
||||
executed. To protect against this we **Highly** recommmend you create a specific postgresql user with restricted permissions.
|
||||
|
||||
Example:
|
||||
|
||||
```sql
|
||||
CREATE USER grafanareader WITH PASSWORD 'password';
|
||||
GRANT USAGE ON SCHEMA schema TO grafanareader;
|
||||
GRANT SELECT ON schema.table TO grafanareader;
|
||||
```
|
||||
|
||||
Make sure the user does not get any unwanted privileges from the public role.
|
||||
|
||||
## Macros
|
||||
|
||||
To simplify syntax and to allow for dynamic parts, like date range filters, the query can contain macros.
|
||||
|
||||
Macro example | Description
|
||||
------------ | -------------
|
||||
*$__time(dateColumn)* | Will be replaced by an expression to rename the column to `time`. For example, *dateColumn as time*
|
||||
*$__timeSec(dateColumn)* | Will be replaced by an expression to rename the column to `time` and converting the value to unix timestamp. For example, *extract(epoch from dateColumn) as time*
|
||||
*$__timeFilter(dateColumn)* | Will be replaced by a time range filter using the specified column name. For example, *dateColumn > to_timestamp(1494410783) AND dateColumn < to_timestamp(1494497183)*
|
||||
*$__timeFrom()* | Will be replaced by the start of the currently active time selection. For example, *to_timestamp(1494410783)*
|
||||
*$__timeTo()* | Will be replaced by the end of the currently active time selection. For example, *to_timestamp(1494497183)*
|
||||
*$__timeGroup(dateColumn,'5m')* | Will be replaced by an expression usable in GROUP BY clause. For example, *(extract(epoch from "dateColumn")/extract(epoch from '5m'::interval))::int*extract(epoch from '5m'::interval)*
|
||||
*$__unixEpochFilter(dateColumn)* | Will be replaced by a time range filter using the specified column name with times represented as unix timestamp. For example, *dateColumn > 1494410783 AND dateColumn < 1494497183*
|
||||
*$__unixEpochFrom()* | Will be replaced by the start of the currently active time selection as unix timestamp. For example, *1494410783*
|
||||
*$__unixEpochTo()* | Will be replaced by the end of the currently active time selection as unix timestamp. For example, *1494497183*
|
||||
|
||||
We plan to add many more macros. If you have suggestions for what macros you would like to see, please [open an issue](https://github.com/grafana/grafana) in our GitHub repo.
|
||||
|
||||
The query editor has a link named `Generated SQL` that shows up after a query as been executed, while in panel edit mode. Click on it and it will expand and show the raw interpolated SQL string that was executed.
|
||||
|
||||
## Table queries
|
||||
|
||||
If the `Format as` query option is set to `Table` then you can basically do any type of SQL query. The table panel will automatically show the results of whatever columns & rows your query returns.
|
||||
|
||||
Query editor with example query:
|
||||
|
||||

|
||||
|
||||
|
||||
The query:
|
||||
|
||||
```sql
|
||||
SELECT
|
||||
title as "Title",
|
||||
"user".login as "Created By",
|
||||
dashboard.created as "Created On"
|
||||
FROM dashboard
|
||||
INNER JOIN "user" on "user".id = dashboard.created_by
|
||||
WHERE $__timeFilter(dashboard.created)
|
||||
```
|
||||
|
||||
You can control the name of the Table panel columns by using regular `as ` SQL column selection syntax.
|
||||
|
||||
The resulting table panel:
|
||||
|
||||

|
||||
|
||||
### Time series queries
|
||||
|
||||
If you set `Format as` to `Time series`, for use in Graph panel for example, then the query must return a column named `time` that returns either a sql datetime or any numeric datatype representing unix epoch in seconds.
|
||||
Any column except `time` and `metric` is treated as a value column.
|
||||
You may return a column named `metric` that is used as metric name for the value column.
|
||||
|
||||
Example with `metric` column
|
||||
|
||||
```sql
|
||||
SELECT
|
||||
$__timeGroup(time_date_time,'5m') as time,
|
||||
min(value_double),
|
||||
'min' as metric
|
||||
FROM test_data
|
||||
WHERE $__timeFilter(time_date_time)
|
||||
GROUP BY time
|
||||
ORDER BY time
|
||||
```
|
||||
|
||||
Example with multiple columns:
|
||||
|
||||
```sql
|
||||
SELECT
|
||||
$__timeGroup(time_date_time,'5m') as time,
|
||||
min(value_double) as min_value,
|
||||
max(value_double) as max_value
|
||||
FROM test_data
|
||||
WHERE $__timeFilter(time_date_time)
|
||||
GROUP BY time
|
||||
ORDER BY time
|
||||
```
|
||||
|
||||
## Templating
|
||||
|
||||
Instead of hard-coding things like server, application and sensor name in you metric queries you can use variables in their place. Variables are shown as dropdown select boxes at the top of the dashboard. These dropdowns makes it easy to change the data being displayed in your dashboard.
|
||||
|
||||
Checkout the [Templating]({{< relref "reference/templating.md" >}}) documentation for an introduction to the templating feature and the different types of template variables.
|
||||
|
||||
### Query Variable
|
||||
|
||||
If you add a template variable of the type `Query`, you can write a PostgreSQL query that can
|
||||
return things like measurement names, key names or key values that are shown as a dropdown select box.
|
||||
|
||||
For example, you can have a variable that contains all values for the `hostname` column in a table if you specify a query like this in the templating variable *Query* setting.
|
||||
|
||||
```sql
|
||||
SELECT hostname FROM host
|
||||
```
|
||||
|
||||
A query can return multiple columns and Grafana will automatically create a list from them. For example, the query below will return a list with values from `hostname` and `hostname2`.
|
||||
|
||||
```sql
|
||||
SELECT host.hostname, other_host.hostname2 FROM host JOIN other_host ON host.city = other_host.city
|
||||
```
|
||||
|
||||
Another option is a query that can create a key/value variable. The query should return two columns that are named `__text` and `__value`. The `__text` column value should be unique (if it is not unique then the first value is used). The options in the dropdown will have a text and value that allows you to have a friendly name as text and an id as the value. An example query with `hostname` as the text and `id` as the value:
|
||||
|
||||
```sql
|
||||
SELECT hostname AS __text, id AS __value FROM host
|
||||
```
|
||||
|
||||
You can also create nested variables. For example if you had another variable named `region`. Then you could have
|
||||
the hosts variable only show hosts from the current selected region with a query like this (if `region` is a multi-value variable then use the `IN` comparison operator rather than `=` to match against multiple values):
|
||||
|
||||
```sql
|
||||
SELECT hostname FROM host WHERE region IN($region)
|
||||
```
|
||||
|
||||
### Using Variables in Queries
|
||||
|
||||
Template variables are quoted automatically so if it is a string value do not wrap them in quotes in where clauses. If the variable is a multi-value variable then use the `IN` comparison operator rather than `=` to match against multiple values.
|
||||
|
||||
There are two syntaxes:
|
||||
|
||||
`$<varname>` Example with a template variable named `hostname`:
|
||||
|
||||
```sql
|
||||
SELECT
|
||||
atimestamp as time,
|
||||
aint as value
|
||||
FROM table
|
||||
WHERE $__timeFilter(atimestamp) and hostname in($hostname)
|
||||
ORDER BY atimestamp ASC
|
||||
```
|
||||
|
||||
`[[varname]]` Example with a template variable named `hostname`:
|
||||
|
||||
```sql
|
||||
SELECT
|
||||
atimestamp as time,
|
||||
aint as value
|
||||
FROM table
|
||||
WHERE $__timeFilter(atimestamp) and hostname in([[hostname]])
|
||||
ORDER BY atimestamp ASC
|
||||
```
|
||||
|
||||
## Alerting
|
||||
|
||||
Time series queries should work in alerting conditions. Table formatted queries is not yet supported in alert rule
|
||||
conditions.
|
||||
@@ -34,6 +34,7 @@ Name | Description
|
||||
*Basic Auth* | Enable basic authentication to the Prometheus data source.
|
||||
*User* | Name of your Prometheus user
|
||||
*Password* | Database user's password
|
||||
*Scrape interval* | This will be used as a lower limit for the Prometheus step query parameter. Default value is 15s.
|
||||
|
||||
## Query editor
|
||||
|
||||
|
||||
27
docs/sources/features/panels/alertlist.md
Normal file
27
docs/sources/features/panels/alertlist.md
Normal file
@@ -0,0 +1,27 @@
|
||||
+++
|
||||
title = "Alert List"
|
||||
keywords = ["grafana", "alert list", "documentation", "panel", "alertlist"]
|
||||
type = "docs"
|
||||
aliases = ["/reference/alertlist/"]
|
||||
[menu.docs]
|
||||
name = "Alert list"
|
||||
parent = "panels"
|
||||
weight = 4
|
||||
+++
|
||||
|
||||
|
||||
# Alert List Panel
|
||||
|
||||
{{< docs-imagebox img="/img/docs/v45/alert-list-panel.png" max-width="850px" >}}
|
||||
|
||||
The alert list panel allows you to display your dashbords alerts. The list can be configured to show current state or recent state changes. You can read more about alerts [here](http://docs.grafana.org/alerting/rules).
|
||||
|
||||
## Alert List Options
|
||||
|
||||
{{< docs-imagebox img="/img/docs/v45/alert-list-options.png" max-width="600px" class="docs-image--no-shadow docs-image--right" >}}
|
||||
|
||||
1. **Show**: Lets you choose between current state or recent state changes.
|
||||
2. **Max Items**: Max items set the maximum of items in a list.
|
||||
3. **Sort Order**: Lets you sort your list alphabeticaly(asc/desc) or by importance.
|
||||
4. **Alerts From** This Dashboard`: Shows alerts only from the dashboard the alert list is in.
|
||||
5. **State Filter**: Here you can filter your list by one or more parameters.
|
||||
@@ -12,42 +12,25 @@ weight = 4
|
||||
|
||||
# Dashboard List Panel
|
||||
|
||||
The dashboard list panel allows you to display dynamic links to other dashboards. The list can be configured to use starred dashboards, a search query and/or dashboard tags.
|
||||
{{< docs-imagebox img="/img/docs/v45/dashboard-list-panels.png" max-width="850px">}}
|
||||
|
||||
<img class="no-shadow" src="/img/docs/v2/dashboard_list_panels.png">
|
||||
The dashboard list panel allows you to display dynamic links to other dashboards. The list can be configured to use starred dashboards, recently viewed dashboards, a search query and/or dashboard tags.
|
||||
|
||||
> On each dashboard load, the dashlist panel will re-query the dashboard list, always providing the most up to date results.
|
||||
|
||||
## Mode: Starred Dashboards
|
||||
## Dashboard List Options
|
||||
|
||||
The `starred` dashboard selection displays starred dashboards, up to the number specified in the `Limit Number to` field, in alphabetical order. On dashboard load, the dashlist panel will re-query the favorites to appear in dashboard list panel, always providing the most up to date results.
|
||||
{{< docs-imagebox img="/img/docs/v45/dashboard-list-options.png" class="docs-image--no-shadow docs-image--right">}}
|
||||
|
||||
<img class="no-shadow" src="/img/docs/v2/dashboard_list_config_starred.png">
|
||||
1. **Starred**: The starred dashboard selection displays starred dashboards in alphabetical order.
|
||||
2. **Recently Viewed**: The recently viewed dashboard selection displays recently viewed dashboards in alphabetical order.
|
||||
3. **Search**: The search dashboard selection displays dashboards by search query or tag(s).
|
||||
4. **Show Headings**: When show headings is ticked the choosen list selection(Starred, Recently Viewed, Search) is shown as a heading.
|
||||
5. **Max Items**: Max items set the maximum of items in a list.
|
||||
6. **Query**: Here is where you enter your query you want to search by. Queries are case-insensitive, and partial values are accepted.
|
||||
7. **Tags**: Here is where you enter your tag(s) you want to search by. Note that existing tags will not appear as you type, and *are* case sensitive. To see a list of existing tags, you can always return to the dashboard, open the Dashboard Picker at the top and click `tags` link in the search bar.
|
||||
|
||||
|
||||
## Mode: Search Dashboards
|
||||
|
||||
The panel may be configured to search by either string query or tag(s). On dashboard load, the dashlist panel will re-query the dashboard list, always providing the most up to date results.
|
||||
|
||||
To configure dashboard list in this manner, select `search` from the Mode select box. When selected, the Search Options section will appear.
|
||||
|
||||
|
||||
Name | Description
|
||||
------------ | -------------
|
||||
Mode | Set search or starred mode
|
||||
Query | If in search mode specify the search query
|
||||
Tags | if in search mode specify dashboard tags to search for
|
||||
Limit number to | Specify the maximum number of dashboards
|
||||
|
||||
|
||||
### Search by string
|
||||
|
||||
To search by a string, enter a search query in the `Search Options: Query` field. Queries are case-insensitive, and partial values are accepted.
|
||||
<img class="no-shadow" src="/img/docs/v2/dashboard_list_config_string.png">
|
||||
|
||||
### Search by tag
|
||||
To search by one or more tags, enter your selection in the `Search Options: Tags:` field. Note that existing tags will not appear as you type, and *are* case sensitive. To see a list of existing tags, you can always return to the dashboard, open the Dashboard Picker at the top and click `tags` link in the search bar.
|
||||
<img class="no-shadow" src="/img/docs/v2/dashboard_list_config_tags.png">
|
||||
<div class="clearfix"></div>
|
||||
|
||||
> When multiple tags and strings appear, the dashboard list will display those matching ALL conditions.
|
||||
|
||||
|
||||
@@ -11,24 +11,26 @@ weight = 1
|
||||
|
||||
# Graph Panel
|
||||
|
||||
{{< docs-imagebox img="/img/docs/v45/graph_overview.png" class="docs-image--no-shadow" max-width="850px" >}}
|
||||
|
||||
The main panel in Grafana is simply named Graph. It provides a very rich set of graphing options.
|
||||
|
||||
<img src="/img/docs/v1/graph_overview.png" class="no-shadow">
|
||||
|
||||
Clicking the title for a panel exposes a menu. The `edit` option opens additional configuration
|
||||
1. Clicking the title for a panel exposes a menu. The `edit` option opens additional configuration
|
||||
options for the panel.
|
||||
2. Click to open color & axis selection.
|
||||
3. Click to only show this series. Shift/Ctrl + click to hide series.
|
||||
|
||||
## General
|
||||
|
||||

|
||||
{{< docs-imagebox img="/img/docs/v43/graph_general.png" max-width= "900px" >}}
|
||||
|
||||
The general tab allows customization of a panel's appearance and menu options.
|
||||
|
||||
### General Options
|
||||
|
||||
- ``Title`` - The panel title on the dashboard
|
||||
- ``Span`` - The panel width in columns
|
||||
- ``Height`` - The panel contents height in pixels
|
||||
- **Title** - The panel title on the dashboard
|
||||
- **Span** - The panel width in columns
|
||||
- **Height** - The panel contents height in pixels
|
||||
|
||||
### Drilldown / detail link
|
||||
|
||||
@@ -52,46 +54,48 @@ options.
|
||||
|
||||
## Axes
|
||||
|
||||

|
||||
{{< docs-imagebox img="/img/docs/v43/graph_axes_grid_options.png" max-width= "900px" >}}
|
||||
|
||||
The Axes tab controls the display of axes, grids and legend. The ``Left Y`` and ``Right Y`` can be customized using:
|
||||
The Axes tab controls the display of axes, grids and legend. The **Left Y** and **Right Y** can be customized using:
|
||||
|
||||
- ``Unit`` - The display unit for the Y value
|
||||
- ``Grid Max`` - The maximum Y value. (default auto)
|
||||
- ``Grid Min`` - The minimum Y value. (default auto)
|
||||
- ``Label`` - The Y axis label (default "")
|
||||
- **Unit** - The display unit for the Y value
|
||||
- **Scale** -
|
||||
- **Y-Min** - The minimum Y value. (default auto)
|
||||
- **Y-Max** - The maximum Y value. (default auto)
|
||||
- **Label** - The Y axis label (default "")
|
||||
|
||||
Axes can also be hidden by unchecking the appropriate box from `Show Axis`.
|
||||
Axes can also be hidden by unchecking the appropriate box from **Show**.
|
||||
|
||||
### X-Axis Mode
|
||||
|
||||
There are three options:
|
||||
|
||||
- The default option is `Time` and means the x-axis represents time and that the data is grouped by time (for example, by hour or by minute).
|
||||
- The default option is **Time** and means the x-axis represents time and that the data is grouped by time (for example, by hour or by minute).
|
||||
|
||||
- The `Series` option means that the data is grouped by series and not by time. The y-axis still represents the value.
|
||||
- The **Series** option means that the data is grouped by series and not by time. The y-axis still represents the value.
|
||||
|
||||
<img src="/img/docs/v4/x_axis_mode_series.png" class="no-shadow">
|
||||
{{< docs-imagebox img="/img/docs/v45/graph-x-axis-mode-series.png" max-width="700px">}}
|
||||
|
||||
- The `Histogram` option converts the graph into a histogram. A Histogram is a kind of bar chart that groups numbers into ranges, often called buckets or bins. Taller bars show that more data falls in that range. Histograms and buckets are described in more detail [here](http://docs.grafana.org/features/panels/heatmap/#histograms-and-buckets).
|
||||
- The **Histogram** option converts the graph into a histogram. A Histogram is a kind of bar chart that groups numbers into ranges, often called buckets or bins. Taller bars show that more data falls in that range. Histograms and buckets are described in more detail [here](http://docs.grafana.org/features/panels/heatmap/#histograms-and-buckets).
|
||||
|
||||
<img src="/img/docs/v43/heatmap_histogram.png" class="no-shadow">
|
||||
|
||||
### Legend
|
||||
|
||||
The legend hand be hidden by checking the ``Show`` checkbox. If it's shown, it can be
|
||||
displayed as a table of values by checking the ``Table`` checkbox. Series with no
|
||||
values can be hidden from the legend using the ``Hide empty`` checkbox.
|
||||
The legend hand be hidden by checking the **Show** checkbox. If it's shown, it can be
|
||||
displayed as a table of values by checking the **Table** checkbox. Series with no
|
||||
values can be hidden from the legend using the **Hide empty** checkbox.
|
||||
|
||||
### Legend Values
|
||||
|
||||
Additional values can be shown along-side the legend names:
|
||||
- ``Total`` - Sum of all values returned from metric query
|
||||
- ``Current`` - Last value returned from the metric query
|
||||
- ``Min`` - Minimum of all values returned from metric query
|
||||
- ``Max`` - Maximum of all values returned from the metric query
|
||||
- ``Avg`` - Average of all values returned from metric query
|
||||
- ``Decimals`` - Controls how many decimals are displayed for legend values (and graph hover tooltips)
|
||||
|
||||
- **Total** - Sum of all values returned from metric query
|
||||
- **Current** - Last value returned from the metric query
|
||||
- **Min** - Minimum of all values returned from metric query
|
||||
- **Max** - Maximum of all values returned from the metric query
|
||||
- **Avg** - Average of all values returned from metric query
|
||||
- **Decimals** - Controls how many decimals are displayed for legend values (and graph hover tooltips)
|
||||
|
||||
The legend values are calculated client side by Grafana and depend on what type of
|
||||
aggregation or point consolidation your metric query is using. All the above legend values cannot
|
||||
@@ -101,7 +105,7 @@ It is just the sum of all data points received by Grafana.
|
||||
|
||||
## Display styles
|
||||
|
||||

|
||||
{{< docs-imagebox img="/img/docs/v43/graph_display_styles.png" max-width= "900px" >}}
|
||||
|
||||
Display styles control visual properties of the graph.
|
||||
|
||||
@@ -113,23 +117,23 @@ the graph crosses a particular threshold.
|
||||
|
||||
### Chart Options
|
||||
|
||||
- ``Bar`` - Display values as a bar chart
|
||||
- ``Lines`` - Display values as a line graph
|
||||
- ``Points`` - Display points for values
|
||||
- **Bar** - Display values as a bar chart
|
||||
- **Lines** - Display values as a line graph
|
||||
- **Points** - Display points for values
|
||||
|
||||
### Line Options
|
||||
|
||||
- ``Line Fill`` - Amount of color fill for a series. 0 is none.
|
||||
- ``Line Width`` - The width of the line for a series.
|
||||
- ``Null point mode`` - How null values are displayed
|
||||
- ``Staircase line`` - Draws adjacent points as staircase
|
||||
- **Line Fill** - Amount of color fill for a series. 0 is none.
|
||||
- **Line Width** - The width of the line for a series.
|
||||
- **Null point mode** - How null values are displayed
|
||||
- **Staircase line** - Draws adjacent points as staircase
|
||||
|
||||
### Multiple Series
|
||||
|
||||
If there are multiple series, they can be displayed as a group.
|
||||
|
||||
- ``Stack`` - Each series is stacked on top of another
|
||||
- ``Percent`` - Each series is drawn as a percentage of the total of all series
|
||||
- **Stack** - Each series is stacked on top of another
|
||||
- **Percent** - Each series is drawn as a percentage of the total of all series
|
||||
|
||||
If you have stack enabled, you can select what the mouse hover feature should show.
|
||||
|
||||
@@ -138,12 +142,12 @@ If you have stack enabled, you can select what the mouse hover feature should sh
|
||||
|
||||
### Rendering
|
||||
|
||||
- ``Flot`` - Render the graphs in the browser using Flot (default)
|
||||
- ``Graphite PNG`` - Render the graph on the server using graphite's render API.
|
||||
- **Flot** - Render the graphs in the browser using Flot (default)
|
||||
- **Graphite PNG** - Render the graph on the server using graphite's render API.
|
||||
|
||||
### Tooltip
|
||||
|
||||
- ``All series`` - Show all series on the same tooltip and a x crosshairs to help follow all series
|
||||
- **All series** - Show all series on the same tooltip and a x crosshairs to help follow all series
|
||||
|
||||
### Series Specific Overrides
|
||||
|
||||
@@ -156,4 +160,6 @@ There is an option under Series overrides to draw lines as dashes. Set Dashes to
|
||||
|
||||
## Time Range
|
||||
|
||||

|
||||
The time range tab allows you to override the dashboard time range and specify a panel specific time. Either through a relative from now time option or through a timeshift.
|
||||
|
||||
{{< docs-imagebox img="/img/docs/v45/graph-time-range.png" max-width= "900px" >}}
|
||||
|
||||
@@ -12,69 +12,84 @@ weight = 2
|
||||
|
||||
# Singlestat Panel
|
||||
|
||||

|
||||
{{< docs-imagebox img="/img/docs/v45/singlestat-panel.png" class="docs-image--no-shadow" max-width="900px" >}}
|
||||
|
||||
The Singlestat Panel allows you to show the one main summary stat of a SINGLE series. It reduces the series into a single number (by looking at the max, min, average, or sum of values in the series). Singlestat also provides thresholds to color the stat or the Panel background. It can also translate the single number into a text value, and show a sparkline summary of the series.
|
||||
|
||||
### Singlestat Panel Configuration
|
||||
|
||||
The singlestat panel has a normal query editor to allow you define your exact metric queries like many other Panels. Through the Options tab, you can access the Singlestat-specific functionality.
|
||||
The singlestat panel has a normal query editor to allow you define your exact metric queries like many other Panels. In the Options tab, you can access the Singlestat-specific functionality.
|
||||
|
||||
<img class="no-shadow" src="/img/docs/v1/Singlestat-BaseSettings.png">
|
||||
{{< docs-imagebox img="/img/docs/v45/singlestat-value-options.png" class="docs-image--no-shadow" max-width="900px" >}}
|
||||
|
||||
1. `Big Value`: Big Value refers to how we display the main stat for the Singlestat Panel. This is always a single value that is displayed in the Panel in between two strings, `Prefix` and `Suffix`. The single number is calculated by choosing a function (min,max,average,current,total) of your metric query. This functions reduces your query into a single numeric value.
|
||||
2. `Font Size`: You can use this section to select the font size of the different texts in the Singlestat Panel, i.e. prefix, value and postfix.
|
||||
3. `Values`: The Value fields let you set the function (min, max, average, current, total, first, delta, range) that your entire query is reduced into a single value with. You can also set the font size of the Value field and font-size (as a %) of the metric query that the Panel is configured with. This reduces the entire query into a single summary value that is displayed.
|
||||
* `min` - The smallest value in the series
|
||||
* `max` - The largest value in the series
|
||||
* `avg` - The average of all the non-null values in the series
|
||||
* `current` - The last value in the series. If the series ends on null the previous value will be used.
|
||||
* `total` - The sum of all the non-null values in the series
|
||||
* `first` - The first value in the series
|
||||
* `delta` - The total incremental increase (of a counter) in the series. An attempt is made to account for counter resets, but this will only be accurate for single instance metrics. Used to show total counter increase in time series.
|
||||
* `diff` - The difference betwen 'current' (last value) and 'first'.
|
||||
* `range` - The difference between 'min' and 'max'. Useful the show the range of change for a gauge.
|
||||
4. `Prefix/Postfix`: The Prefix/Postfix fields let you define a custom label and font-size (as a %) to appear *before/after* the value. The `$__name` variable can be used here to use the series name or alias from the metric query.
|
||||
5. `Units`: Units are appended to the the Singlestat within the panel, and will respect the color and threshold settings for the value.
|
||||
6. `Decimals`: The Decimal field allows you to override the automatic decimal precision, and set it explicitly.
|
||||
1. **Stats**: The Stats field let you set the function (min, max, average, current, total, first, delta, range) that your entire query is reduced into a single value with. This reduces the entire query into a single summary value that is displayed.
|
||||
* **min** - The smallest value in the series
|
||||
* **max** - The largest value in the series
|
||||
* **avg** - The average of all the non-null values in the series
|
||||
* **current** - The last value in the series. If the series ends on null the previous value will be used.
|
||||
* **total** - The sum of all the non-null values in the series
|
||||
* **first** - The first value in the series
|
||||
* **delta** - The total incremental increase (of a counter) in the series. An attempt is made to account for counter resets, but this will only be accurate for single instance metrics. Used to show total counter increase in time series.
|
||||
* **diff** - The difference betwen 'current' (last value) and 'first'.
|
||||
* **range** - The difference between 'min' and 'max'. Useful the show the range of change for a gauge.
|
||||
2. **Prefix/Postfix**: The Prefix/Postfix fields let you define a custom label to appear *before/after* the value. The `$__name` variable can be used here to use the series name or alias from the metric query.
|
||||
3. **Units**: Units are appended to the the Singlestat within the panel, and will respect the color and threshold settings for the value.
|
||||
4. **Decimals**: The Decimal field allows you to override the automatic decimal precision, and set it explicitly.
|
||||
5. **Font Size**: You can use this section to select the font size of the different texts in the Singlestat Panel, i.e. prefix, value and postfix.
|
||||
|
||||
### Coloring
|
||||
|
||||
The coloring options of the Singlestat Panel config allow you to dynamically change the colors based on the Singlestat value.
|
||||
|
||||
<img class="no-shadow" src="/img/docs/v1/Singlestat-Coloring.png">
|
||||
{{< docs-imagebox img="/img/docs/v45/singlestat-color-options.png" max-width="500px" class="docs-image--right docs-image--no-shadow">}}
|
||||
|
||||
1. `Background`: This checkbox applies the configured thresholds and colors to the entirety of the Singlestat Panel background.
|
||||
2. `Value`: This checkbox applies the configured thresholds and colors to the summary stat.
|
||||
3. `Thresholds`: Change the background and value colors dynamically within the panel, depending on the Singlestat value. The threshold field accepts **2 comma-separated** values which represent 3 ranges that correspond to the three colors directly to the right. For example: if the thresholds are 70, 90 then the first color represents < 70, the second color represents between 70 and 90 and the third color represents > 90.
|
||||
4. `Colors`: Select a color and opacity
|
||||
5. `Invert order`: This link toggles the threshold color order.</br>For example: Green, Orange, Red (<img class="no-shadow" src="/img/docs(v1/gyr.png">) will become Red, Orange, Green (<img class="no-shadow" src="/img/docs/v1/ryg.png">).
|
||||
1. **Background**: This checkbox applies the configured thresholds and colors to the entirety of the Singlestat Panel background.
|
||||
2. **Thresholds**: Change the background and value colors dynamically within the panel, depending on the Singlestat value. The threshold field accepts **2 comma-separated** values which represent 3 ranges that correspond to the three colors directly to the right. For example: if the thresholds are 70, 90 then the first color represents < 70, the second color represents between 70 and 90 and the third color represents > 90.
|
||||
3. **Colors**: Select a color and opacity
|
||||
4. **Value**: This checkbox applies the configured thresholds and colors to the summary stat.
|
||||
5. **Invert order**: This link toggles the threshold color order.</br>For example: Green, Orange, Red (<img class="no-shadow" src="/img/docs(v1/gyr.png">) will become Red, Orange, Green (<img class="no-shadow" src="/img/docs/v1/ryg.png">).
|
||||
|
||||
### Spark Lines
|
||||
|
||||
Sparklines are a great way of seeing the historical data related to the summary stat, providing valuable context at a glance. Sparklines act differently than traditional Graph Panels and do not include x or y axis, coordinates, a legend, or ability to interact with the graph.
|
||||
|
||||
<img class="no-shadow" src="/img/docs/v1/Singlestat-Sparklines.png">
|
||||
{{< docs-imagebox img="/img/docs/v45/singlestat-spark-options.png" max-width="500px" class="docs-image--right docs-image--no-shadow">}}
|
||||
|
||||
1. `Show`: The show checkbox will toggle whether the spark line is shown in the Panel. When unselected, only the Singlestat value will appear.
|
||||
2. `Background`: Check if you want the sparklines to take up the full panel width, or uncheck if they should be below the main Singlestat value.
|
||||
3. `Line Color`: This color selection applies to the color of the sparkline itself.
|
||||
4. `Fill Color`: This color selection applies to the area below the sparkline.
|
||||
1. **Show**: The show checkbox will toggle whether the spark line is shown in the Panel. When unselected, only the Singlestat value will appear.
|
||||
2. **Full Height**: Check if you want the sparklines to take up the full panel height, or uncheck if they should be below the main Singlestat value.
|
||||
3. **Line Color**: This color selection applies to the color of the sparkline itself.
|
||||
4. **Fill Color**: This color selection applies to the area below the sparkline.
|
||||
|
||||
<div class="clearfix"></div>
|
||||
|
||||
> ***Pro-tip:*** Reduce the opacity on fill colors for nice looking panels.
|
||||
|
||||
### Gauge
|
||||
|
||||
Gauges gives a clear picture of how high a value is in it's context. It's a great way to see if a value is close to the thresholds. The gauge uses the colors set in the color options.
|
||||
|
||||
{{< docs-imagebox img="/img/docs/v45/singlestat-gauge-options.png" max-width="500px" class="docs-image--right docs-image--no-shadow">}}
|
||||
|
||||
1. **Show**: The show checkbox will toggle wether the gauge is shown in the panel. When unselected, only the Singlestat value will appear.
|
||||
2. **Min/Max**: This sets the start and end point for the gauge.
|
||||
3. **Threshold Labels**: Check if you want to show the threshold labels. Thresholds are set in the color options.
|
||||
4. **Threshold Markers**: Check if you want to have a second meter showing the thresholds.
|
||||
|
||||
<div class="clearfix"></div>
|
||||
|
||||
### Value to text mapping
|
||||
|
||||
{{< docs-imagebox img="/img/docs/v45/singlestat-value-mapping.png" class="docs-image--right docs-image--no-shadow">}}
|
||||
|
||||
Value to text mapping allows you to translate the value of the summary stat into explicit text. The text will respect all styling, thresholds and customization defined for the value. This can be useful to translate the number of the main Singlestat value into a context-specific human-readable word or message.
|
||||
|
||||
<img class="no-shadow" src="/img/docs/v1/Singlestat-ValueMapping.png">
|
||||
<div class="clearfix"></div>
|
||||
|
||||
## Troubleshooting
|
||||
|
||||
### Multiple Series Error
|
||||
|
||||
<img class="no-shadow" src="/img/docs/v2/Singlestat-MultiSeriesError.png">
|
||||
|
||||
{{< docs-imagebox img="/img/docs/v45/singelstat-multiple-series-error.png" class="docs-image--right docs-image--no-shadow">}}
|
||||
|
||||
Grafana 2.5 introduced stricter checking for multiple-series on singlestat panels. In previous versions, the panel logic did not verify that only a single series was used, and instead, displayed the first series encountered. Depending on your data source, this could have lead to inconsistent data being shown and/or a general confusion about which metric was being displayed.
|
||||
|
||||
|
||||
@@ -25,8 +25,8 @@ The table panel has many ways to manipulate your data for optimal presentation.
|
||||
{{< docs-imagebox img="/img/docs/v45/table_options.png" class="docs-image--no-shadow" max-width= "500px" >}}
|
||||
|
||||
|
||||
1. `Data`: Control how your query is transformed into a table.
|
||||
2. `Paging`: Table display options.
|
||||
1. **Data**: Control how your query is transformed into a table.
|
||||
2. **Paging**: Table display options.
|
||||
|
||||
|
||||
## Data to Table
|
||||
@@ -43,20 +43,20 @@ you want in the table. Only applicable for some transforms.
|
||||
|
||||
{{< docs-imagebox img="/img/docs/v45/table_ts_to_rows.png" >}}
|
||||
|
||||
In the most simple mode you can turn time series to rows. This means you get a `Time`, `Metric` and a `Value` column. Where `Metric` is the name of the time series.
|
||||
In the most simple mode you can turn time series to rows. This means you get a **Time**, **Metric** and a **Value** column. Where **Metric** is the name of the time series.
|
||||
|
||||
### Time series to columns
|
||||
|
||||
{{< docs-imagebox img="/img/docs/v45/table_ts_to_columns.png" >}}
|
||||
|
||||
|
||||
This transform allows you to take multiple time series and group them by time. Which will result in the primary column being `Time` and a column for each time series.
|
||||
This transform allows you to take multiple time series and group them by time. Which will result in the primary column being **Time** and a column for each time series.
|
||||
|
||||
### Time series aggregations
|
||||
|
||||
{{< docs-imagebox img="/img/docs/v45/table_ts_to_aggregations.png" >}}
|
||||
|
||||
This table transformation will lay out your table into rows by metric, allowing columns of `Avg`, `Min`, `Max`, `Total`, `Current` and `Count`. More than one column can be added.
|
||||
This table transformation will lay out your table into rows by metric, allowing columns of **Avg**, **Min**, **Max**, **Total**, **Current** and **Count**. More than one column can be added.
|
||||
|
||||
### Annotations
|
||||
|
||||
@@ -70,7 +70,7 @@ mode then any queries you have in the metrics tab will be ignored.
|
||||
|
||||
{{< docs-imagebox img="/img/docs/v45/table_json_data.png" max-width="500px" >}}
|
||||
|
||||
If you have an Elasticsearch **Raw Document** query or an Elasticsearch query without a `date histogram` use this
|
||||
If you have an Elasticsearch **Raw Document** query or an Elasticsearch query without a **date histogram** use this
|
||||
transform mode and pick the columns using the **Columns** section.
|
||||
|
||||
|
||||
@@ -80,9 +80,9 @@ transform mode and pick the columns using the **Columns** section.
|
||||
|
||||
{{< docs-imagebox img="/img/docs/v45/table_paging.png" class="docs-image--no-shadow docs-image--right" max-width="350px" >}}
|
||||
|
||||
1. `Pagination (Page Size)`: The table display fields allow you to control The `Pagination` (page size) is the threshold at which the table rows will be broken into pages. For example, if your table had 95 records with a pagination value of 10, your table would be split across 9 pages.
|
||||
2. `Scroll`: The `scroll bar` checkbox toggles the ability to scroll within the panel, when unchecked, the panel height will grow to display all rows.
|
||||
3. `Font Size`: The `font size` field allows you to increase or decrease the size for the panel, relative to the default font size.
|
||||
1. **Rows Per Page**: The table display fields allow you to control how many rows per page there should be. For example, if your table had 95 records with a rows per page value of 10, your table would be split across 10 pages.
|
||||
2. **Scroll**: The scroll bar checkbox toggles the ability to scroll within the panel, when unchecked, the panel height will grow to display all rows.
|
||||
3. **Font Size**: The font size field allows you to increase or decrease the size for the panel, relative to the default font size.
|
||||
|
||||
|
||||
## Column Styles
|
||||
@@ -91,9 +91,9 @@ The column styles allow you control how dates and numbers are formatted.
|
||||
|
||||
{{< docs-imagebox img="/img/docs/v45/table_column_styles.png" class="docs-image--no-shadow" >}}
|
||||
|
||||
1. `Name or regex`: The Name or Regex field controls what columns the rule should be applied to. The regex or name filter will be matched against the column name not against column values.
|
||||
2. `Column Header`: Title for the column, when using a Regex the title can include replacement strings like `$1`.
|
||||
3. `Add column style rule`: Add new column rule.
|
||||
4. `Thresholds` and `Coloring`: Specify color mode and thresholds limits.
|
||||
5. `Type`: The three supported types of types are `Number`, `String` and `Date`. `Unit` and `Decimals`: Specify unit and decimal precision for numbers.`Format`: Specify date format for dates.
|
||||
1. **Name or regex**: The Name or Regex field controls what columns the rule should be applied to. The regex or name filter will be matched against the column name not against column values.
|
||||
2. **Column Header**: Title for the column, when using a Regex the title can include replacement strings like `$1`.
|
||||
3. **Add column style rule**: Add new column rule.
|
||||
4. **Thresholds and Coloring**: Specify color mode and thresholds limits.
|
||||
5. **Type**: The three supported types of types are **Number**, **String** and **Date**. **Unit** and **Decimals**: Specify unit and decimal precision for numbers. **Format**: Specify date format for dates.
|
||||
|
||||
|
||||
23
docs/sources/features/panels/text.md
Normal file
23
docs/sources/features/panels/text.md
Normal file
@@ -0,0 +1,23 @@
|
||||
+++
|
||||
title = "Text"
|
||||
keywords = ["grafana", "text", "documentation", "panel"]
|
||||
type = "docs"
|
||||
aliases = ["/reference/alertlist/"]
|
||||
[menu.docs]
|
||||
name = "Text"
|
||||
parent = "panels"
|
||||
weight = 4
|
||||
+++
|
||||
|
||||
|
||||
# Text Panel
|
||||
|
||||
The text panel lets you make information and description panels etc. for your dashboards. There are three modes you can write in: markdown, HTML or text.
|
||||
|
||||
## Text Options
|
||||
|
||||
{{< docs-imagebox img="/img/docs/v45/text-options.png" max-width="600px" class="docs-image--no-shadow">}}
|
||||
|
||||
1. **Mode**: Here you can choose between markdown, HTML or text.
|
||||
2. **Content**: Here you write your content.
|
||||
|
||||
74
docs/sources/guides/whats-new-in-v4-6.md
Normal file
74
docs/sources/guides/whats-new-in-v4-6.md
Normal file
@@ -0,0 +1,74 @@
|
||||
+++
|
||||
title = "What's New in Grafana v4.6"
|
||||
description = "Feature & improvement highlights for Grafana v4.6"
|
||||
keywords = ["grafana", "new", "documentation", "4.6"]
|
||||
type = "docs"
|
||||
[menu.docs]
|
||||
name = "Version 4.6"
|
||||
identifier = "v4.6"
|
||||
parent = "whatsnew"
|
||||
weight = -5
|
||||
+++
|
||||
|
||||
# What's New in Grafana v4.6
|
||||
|
||||
Grafana v4.6 brings many enhancements to Annotations, Cloudwatch & Prometheus. It also adds support for Postgres as metric & table data source!
|
||||
|
||||
### Annotations
|
||||
|
||||
{{< docs-imagebox img="/img/docs/v46/add_annotation_region.png" max-width= "800px" >}}
|
||||
|
||||
You can now add annotation events and regions right from the graph panel! Just hold CTRL/CMD + click or drag region to open the **Add Annotation** view. The
|
||||
[Annotations]({{< relref "reference/annotations.md" >}}) documentation is updated to include details on this new exciting feature.
|
||||
|
||||
### Cloudwatch
|
||||
|
||||
Cloudwatch now supports alerting. Setup alert rules for any Cloudwatch metric!
|
||||
|
||||
{{< docs-imagebox img="/img/docs/v46/cloudwatch_alerting.png" max-width= "800px" >}}
|
||||
|
||||
### Postgres
|
||||
|
||||
Grafana v4.6 now ships with a built-in datasource plugin for Postgres. Have logs or metric data in Postgres? You can now visualize that data and
|
||||
define alert rules on it like any of our other data sources.
|
||||
|
||||
{{< docs-imagebox img="/img/docs/v46/postgres_table_query.png" max-width= "800px" >}}
|
||||
|
||||
### Prometheus
|
||||
|
||||
New enhancements include support for **instant queries** and improvements to query editor in the form of autocomplete for label names and label values.
|
||||
This makes exploring and filtering Prometheus data much easier.
|
||||
|
||||
## Changelog
|
||||
|
||||
### New Features
|
||||
|
||||
* **GCS**: Adds support for Google Cloud Storage [#8370](https://github.com/grafana/grafana/issues/8370) thx [@chuhlomin](https://github.com/chuhlomin)
|
||||
* **Prometheus**: Adds /metrics endpoint for exposing Grafana metrics. [#9187](https://github.com/grafana/grafana/pull/9187)
|
||||
* **Graph**: Add support for local formating in axis. [#1395](https://github.com/grafana/grafana/issues/1395), thx [@m0nhawk](https://github.com/m0nhawk)
|
||||
* **Jaeger**: Add support for open tracing using jaeger in Grafana. [#9213](https://github.com/grafana/grafana/pull/9213)
|
||||
* **Unit types**: New date & time unit types added, useful in singlestat to show dates & times. [#3678](https://github.com/grafana/grafana/issues/3678), [#6710](https://github.com/grafana/grafana/issues/6710), [#2764](https://github.com/grafana/grafana/issues/2764)
|
||||
* **CLI**: Make it possible to install plugins from any url [#5873](https://github.com/grafana/grafana/issues/5873)
|
||||
* **Prometheus**: Add support for instant queries [#5765](https://github.com/grafana/grafana/issues/5765), thx [@mtanda](https://github.com/mtanda)
|
||||
* **Cloudwatch**: Add support for alerting using the cloudwatch datasource [#8050](https://github.com/grafana/grafana/pull/8050), thx [@mtanda](https://github.com/mtanda)
|
||||
* **Pagerduty**: Include triggering series in pagerduty notification [#8479](https://github.com/grafana/grafana/issues/8479), thx [@rickymoorhouse](https://github.com/rickymoorhouse)
|
||||
* **Timezone**: Time ranges like Today & Yesterday now work correctly when timezone setting is set to UTC [#8916](https://github.com/grafana/grafana/issues/8916), thx [@ctide](https://github.com/ctide)
|
||||
* **Prometheus**: Align $__interval with the step parameters. [#9226](https://github.com/grafana/grafana/pull/9226), thx [@alin-amana](https://github.com/alin-amana)
|
||||
* **Prometheus**: Autocomplete for label name and label value [#9208](https://github.com/grafana/grafana/pull/9208), thx [@mtanda](https://github.com/mtanda)
|
||||
* **Postgres**: New Postgres data source [#9209](https://github.com/grafana/grafana/pull/9209), thx [@svenklemm](https://github.com/svenklemm)
|
||||
* **Datasources**: closes [#9371](https://github.com/grafana/grafana/issues/9371), [#5334](https://github.com/grafana/grafana/issues/5334), [#8812](https://github.com/grafana/grafana/issues/8812), thx [@mattbostock](https://github.com/mattbostock)
|
||||
|
||||
### Minor Changes
|
||||
|
||||
* **SMTP**: Make it possible to set specific EHLO for smtp client. [#9319](https://github.com/grafana/grafana/issues/9319)
|
||||
* **Dataproxy**: Allow grafan to renegotiate tls connection [#9250](https://github.com/grafana/grafana/issues/9250)
|
||||
* **HTTP**: set net.Dialer.DualStack to true for all http clients [#9367](https://github.com/grafana/grafana/pull/9367)
|
||||
* **Alerting**: Add diff and percent diff as series reducers [#9386](https://github.com/grafana/grafana/pull/9386), thx [@shanhuhai5739](https://github.com/shanhuhai5739)
|
||||
* **Slack**: Allow images to be uploaded to slack when Token is precent [#7175](https://github.com/grafana/grafana/issues/7175), thx [@xginn8](https://github.com/xginn8)
|
||||
* **Opsgenie**: Use their latest API instead of old version [#9399](https://github.com/grafana/grafana/pull/9399), thx [@cglrkn](https://github.com/cglrkn)
|
||||
* **Table**: Add support for displaying the timestamp with milliseconds [#9429](https://github.com/grafana/grafana/pull/9429), thx [@s1061123](https://github.com/s1061123)
|
||||
* **Hipchat**: Add metrics, message and image to hipchat notifications [#9110](https://github.com/grafana/grafana/issues/9110), thx [@eloo](https://github.com/eloo)
|
||||
|
||||
### Tech
|
||||
* **Go**: Grafana is now built using golang 1.9
|
||||
|
||||
@@ -23,157 +23,162 @@ Only works with Basic Authentication (username and password). See [introduction]
|
||||
|
||||
**Example Request**:
|
||||
|
||||
GET /api/admin/settings
|
||||
Accept: application/json
|
||||
Content-Type: application/json
|
||||
```bash
|
||||
GET /api/admin/settings
|
||||
Accept: application/json
|
||||
Content-Type: application/json
|
||||
```
|
||||
|
||||
**Example Response**:
|
||||
|
||||
HTTP/1.1 200
|
||||
Content-Type: application/json
|
||||
|
||||
{
|
||||
"DEFAULT":
|
||||
{
|
||||
"app_mode":"production"},
|
||||
"analytics":
|
||||
{
|
||||
"google_analytics_ua_id":"",
|
||||
"reporting_enabled":"false"
|
||||
},
|
||||
"auth.anonymous":{
|
||||
"enabled":"true",
|
||||
"org_name":"Main Org.",
|
||||
"org_role":"Viewer"
|
||||
},
|
||||
"auth.basic":{
|
||||
"enabled":"false"
|
||||
},
|
||||
"auth.github":{
|
||||
"allow_sign_up":"false",
|
||||
"allowed_domains":"",
|
||||
"allowed_organizations":"",
|
||||
"api_url":"https://api.github.com/user",
|
||||
"auth_url":"https://github.com/login/oauth/authorize",
|
||||
"client_id":"some_id",
|
||||
"client_secret":"************",
|
||||
"enabled":"false",
|
||||
"scopes":"user:email",
|
||||
"team_ids":"",
|
||||
"token_url":"https://github.com/login/oauth/access_token"
|
||||
},
|
||||
"auth.google":{
|
||||
"allow_sign_up":"false","allowed_domains":"",
|
||||
"api_url":"https://www.googleapis.com/oauth2/v1/userinfo",
|
||||
"auth_url":"https://accounts.google.com/o/oauth2/auth",
|
||||
"client_id":"some_client_id",
|
||||
"client_secret":"************",
|
||||
"enabled":"false",
|
||||
"scopes":"https://www.googleapis.com/auth/userinfo.profile https://www.googleapis.com/auth/userinfo.email",
|
||||
"token_url":"https://accounts.google.com/o/oauth2/token"
|
||||
},
|
||||
"auth.ldap":{
|
||||
"config_file":"/etc/grafana/ldap.toml",
|
||||
"enabled":"false"
|
||||
},
|
||||
"auth.proxy":{
|
||||
"auto_sign_up":"true",
|
||||
"enabled":"false",
|
||||
"header_name":"X-WEBAUTH-USER",
|
||||
"header_property":"username"
|
||||
},
|
||||
"dashboards.json":{
|
||||
"enabled":"false",
|
||||
"path":"/var/lib/grafana/dashboards"
|
||||
},
|
||||
"database":{
|
||||
"host":"127.0.0.1:0000",
|
||||
"name":"grafana",
|
||||
"password":"************",
|
||||
"path":"grafana.db",
|
||||
"ssl_mode":"disable",
|
||||
"type":"sqlite3",
|
||||
"user":"root"
|
||||
},
|
||||
"emails":{
|
||||
"templates_pattern":"emails/*.html",
|
||||
"welcome_email_on_sign_up":"false"
|
||||
},
|
||||
"event_publisher":{
|
||||
"enabled":"false",
|
||||
"exchange":"grafana_events",
|
||||
"rabbitmq_url":"amqp://localhost/"
|
||||
},
|
||||
"log":{
|
||||
"buffer_len":"10000",
|
||||
"level":"Info",
|
||||
"mode":"file"
|
||||
},
|
||||
"log.console":{
|
||||
"level":""
|
||||
},
|
||||
"log.file":{
|
||||
"daily_rotate":"true",
|
||||
"file_name":"",
|
||||
"level":"",
|
||||
"log_rotate":"true",
|
||||
"max_days":"7",
|
||||
"max_lines":"1000000",
|
||||
"max_lines_shift":"28",
|
||||
"max_size_shift":""
|
||||
},
|
||||
"paths":{
|
||||
"data":"/tsdb/grafana",
|
||||
"logs":"/logs/apps/grafana"},
|
||||
"security":{
|
||||
"admin_password":"************",
|
||||
"admin_user":"admin",
|
||||
"cookie_remember_name":"grafana_remember",
|
||||
"cookie_username":"grafana_user",
|
||||
"disable_gravatar":"false",
|
||||
"login_remember_days":"7",
|
||||
"secret_key":"************"
|
||||
},
|
||||
"server":{
|
||||
"cert_file":"",
|
||||
"cert_key":"",
|
||||
"domain":"mygraf.com",
|
||||
"enable_gzip":"false",
|
||||
"enforce_domain":"false",
|
||||
"http_addr":"127.0.0.1",
|
||||
"http_port":"0000",
|
||||
"protocol":"http",
|
||||
"root_url":"%(protocol)s://%(domain)s:%(http_port)s/",
|
||||
"router_logging":"true",
|
||||
"data_proxy_logging":"true",
|
||||
"static_root_path":"public"
|
||||
},
|
||||
"session":{
|
||||
"cookie_name":"grafana_sess",
|
||||
"cookie_secure":"false",
|
||||
"gc_interval_time":"",
|
||||
"provider":"file",
|
||||
"provider_config":"sessions",
|
||||
"session_life_time":"86400"
|
||||
},
|
||||
"smtp":{
|
||||
"cert_file":"",
|
||||
"enabled":"false",
|
||||
"from_address":"admin@grafana.localhost",
|
||||
"from_name":"Grafana",
|
||||
"host":"localhost:25",
|
||||
"key_file":"",
|
||||
"password":"************",
|
||||
"skip_verify":"false",
|
||||
"user":""},
|
||||
"users":{
|
||||
"allow_org_create":"true",
|
||||
"allow_sign_up":"false",
|
||||
"auto_assign_org":"true",
|
||||
"auto_assign_org_role":"Viewer"
|
||||
}
|
||||
}
|
||||
```bash
|
||||
HTTP/1.1 200
|
||||
Content-Type: application/json
|
||||
|
||||
{
|
||||
"DEFAULT":
|
||||
{
|
||||
"app_mode":"production"},
|
||||
"analytics":
|
||||
{
|
||||
"google_analytics_ua_id":"",
|
||||
"reporting_enabled":"false"
|
||||
},
|
||||
"auth.anonymous":{
|
||||
"enabled":"true",
|
||||
"org_name":"Main Org.",
|
||||
"org_role":"Viewer"
|
||||
},
|
||||
"auth.basic":{
|
||||
"enabled":"false"
|
||||
},
|
||||
"auth.github":{
|
||||
"allow_sign_up":"false",
|
||||
"allowed_domains":"",
|
||||
"allowed_organizations":"",
|
||||
"api_url":"https://api.github.com/user",
|
||||
"auth_url":"https://github.com/login/oauth/authorize",
|
||||
"client_id":"some_id",
|
||||
"client_secret":"************",
|
||||
"enabled":"false",
|
||||
"scopes":"user:email",
|
||||
"team_ids":"",
|
||||
"token_url":"https://github.com/login/oauth/access_token"
|
||||
},
|
||||
"auth.google":{
|
||||
"allow_sign_up":"false","allowed_domains":"",
|
||||
"api_url":"https://www.googleapis.com/oauth2/v1/userinfo",
|
||||
"auth_url":"https://accounts.google.com/o/oauth2/auth",
|
||||
"client_id":"some_client_id",
|
||||
"client_secret":"************",
|
||||
"enabled":"false",
|
||||
"scopes":"https://www.googleapis.com/auth/userinfo.profile https://www.googleapis.com/auth/userinfo.email",
|
||||
"token_url":"https://accounts.google.com/o/oauth2/token"
|
||||
},
|
||||
"auth.ldap":{
|
||||
"config_file":"/etc/grafana/ldap.toml",
|
||||
"enabled":"false"
|
||||
},
|
||||
"auth.proxy":{
|
||||
"auto_sign_up":"true",
|
||||
"enabled":"false",
|
||||
"header_name":"X-WEBAUTH-USER",
|
||||
"header_property":"username"
|
||||
},
|
||||
"dashboards.json":{
|
||||
"enabled":"false",
|
||||
"path":"/var/lib/grafana/dashboards"
|
||||
},
|
||||
"database":{
|
||||
"host":"127.0.0.1:0000",
|
||||
"name":"grafana",
|
||||
"password":"************",
|
||||
"path":"grafana.db",
|
||||
"ssl_mode":"disable",
|
||||
"type":"sqlite3",
|
||||
"user":"root"
|
||||
},
|
||||
"emails":{
|
||||
"templates_pattern":"emails/*.html",
|
||||
"welcome_email_on_sign_up":"false"
|
||||
},
|
||||
"event_publisher":{
|
||||
"enabled":"false",
|
||||
"exchange":"grafana_events",
|
||||
"rabbitmq_url":"amqp://localhost/"
|
||||
},
|
||||
"log":{
|
||||
"buffer_len":"10000",
|
||||
"level":"Info",
|
||||
"mode":"file"
|
||||
},
|
||||
"log.console":{
|
||||
"level":""
|
||||
},
|
||||
"log.file":{
|
||||
"daily_rotate":"true",
|
||||
"file_name":"",
|
||||
"level":"",
|
||||
"log_rotate":"true",
|
||||
"max_days":"7",
|
||||
"max_lines":"1000000",
|
||||
"max_lines_shift":"28",
|
||||
"max_size_shift":""
|
||||
},
|
||||
"paths":{
|
||||
"data":"/tsdb/grafana",
|
||||
"logs":"/logs/apps/grafana"},
|
||||
"security":{
|
||||
"admin_password":"************",
|
||||
"admin_user":"admin",
|
||||
"cookie_remember_name":"grafana_remember",
|
||||
"cookie_username":"grafana_user",
|
||||
"disable_gravatar":"false",
|
||||
"login_remember_days":"7",
|
||||
"secret_key":"************"
|
||||
},
|
||||
"server":{
|
||||
"cert_file":"",
|
||||
"cert_key":"",
|
||||
"domain":"mygraf.com",
|
||||
"enable_gzip":"false",
|
||||
"enforce_domain":"false",
|
||||
"http_addr":"127.0.0.1",
|
||||
"http_port":"0000",
|
||||
"protocol":"http",
|
||||
"root_url":"%(protocol)s://%(domain)s:%(http_port)s/",
|
||||
"router_logging":"true",
|
||||
"data_proxy_logging":"true",
|
||||
"static_root_path":"public"
|
||||
},
|
||||
"session":{
|
||||
"cookie_name":"grafana_sess",
|
||||
"cookie_secure":"false",
|
||||
"gc_interval_time":"",
|
||||
"provider":"file",
|
||||
"provider_config":"sessions",
|
||||
"session_life_time":"86400"
|
||||
},
|
||||
"smtp":{
|
||||
"cert_file":"",
|
||||
"enabled":"false",
|
||||
"from_address":"admin@grafana.localhost",
|
||||
"from_name":"Grafana",
|
||||
"ehlo_identity":"dashboard.example.com",
|
||||
"host":"localhost:25",
|
||||
"key_file":"",
|
||||
"password":"************",
|
||||
"skip_verify":"false",
|
||||
"user":""
|
||||
},
|
||||
"users":{
|
||||
"allow_org_create":"true",
|
||||
"allow_sign_up":"false",
|
||||
"auto_assign_org":"true",
|
||||
"auto_assign_org_role":"Viewer"
|
||||
}
|
||||
}
|
||||
```
|
||||
## Grafana Stats
|
||||
|
||||
`GET /api/admin/stats`
|
||||
@@ -182,26 +187,30 @@ Only works with Basic Authentication (username and password). See [introduction]
|
||||
|
||||
**Example Request**:
|
||||
|
||||
GET /api/admin/stats
|
||||
Accept: application/json
|
||||
Content-Type: application/json
|
||||
```bash
|
||||
GET /api/admin/stats
|
||||
Accept: application/json
|
||||
Content-Type: application/json
|
||||
```
|
||||
|
||||
**Example Response**:
|
||||
|
||||
HTTP/1.1 200
|
||||
Content-Type: application/json
|
||||
```json
|
||||
HTTP/1.1 200
|
||||
Content-Type: application/json
|
||||
|
||||
{
|
||||
"user_count":2,
|
||||
"org_count":1,
|
||||
"dashboard_count":4,
|
||||
"db_snapshot_count":2,
|
||||
"db_tag_count":6,
|
||||
"data_source_count":1,
|
||||
"playlist_count":1,
|
||||
"starred_db_count":2,
|
||||
"grafana_admin_count":2
|
||||
}
|
||||
{
|
||||
"user_count":2,
|
||||
"org_count":1,
|
||||
"dashboard_count":4,
|
||||
"db_snapshot_count":2,
|
||||
"db_tag_count":6,
|
||||
"data_source_count":1,
|
||||
"playlist_count":1,
|
||||
"starred_db_count":2,
|
||||
"grafana_admin_count":2
|
||||
}
|
||||
```
|
||||
|
||||
## Global Users
|
||||
|
||||
@@ -210,24 +219,28 @@ Only works with Basic Authentication (username and password). See [introduction]
|
||||
Create new user. Only works with Basic Authentication (username and password). See [introduction](http://docs.grafana.org/http_api/admin/#admin-api) for an explanation.
|
||||
|
||||
**Example Request**:
|
||||
```json
|
||||
|
||||
POST /api/admin/users HTTP/1.1
|
||||
Accept: application/json
|
||||
Content-Type: application/json
|
||||
POST /api/admin/users HTTP/1.1
|
||||
Accept: application/json
|
||||
Content-Type: application/json
|
||||
|
||||
{
|
||||
"name":"User",
|
||||
"email":"user@graf.com",
|
||||
"login":"user",
|
||||
"password":"userpassword"
|
||||
}
|
||||
{
|
||||
"name":"User",
|
||||
"email":"user@graf.com",
|
||||
"login":"user",
|
||||
"password":"userpassword"
|
||||
}
|
||||
```
|
||||
|
||||
**Example Response**:
|
||||
|
||||
HTTP/1.1 200
|
||||
Content-Type: application/json
|
||||
```json
|
||||
HTTP/1.1 200
|
||||
Content-Type: application/json
|
||||
|
||||
{"id":5,"message":"User created"}
|
||||
{"id":5,"message":"User created"}
|
||||
```
|
||||
|
||||
## Password for User
|
||||
|
||||
@@ -238,18 +251,22 @@ Change password for a specific user.
|
||||
|
||||
**Example Request**:
|
||||
|
||||
PUT /api/admin/users/2/password HTTP/1.1
|
||||
Accept: application/json
|
||||
Content-Type: application/json
|
||||
```json
|
||||
PUT /api/admin/users/2/password HTTP/1.1
|
||||
Accept: application/json
|
||||
Content-Type: application/json
|
||||
|
||||
{"password":"userpassword"}
|
||||
{"password":"userpassword"}
|
||||
```
|
||||
|
||||
**Example Response**:
|
||||
|
||||
HTTP/1.1 200
|
||||
Content-Type: application/json
|
||||
```json
|
||||
HTTP/1.1 200
|
||||
Content-Type: application/json
|
||||
|
||||
{"message": "User password updated"}
|
||||
{"message": "User password updated"}
|
||||
```
|
||||
|
||||
## Permissions
|
||||
|
||||
@@ -259,18 +276,22 @@ Only works with Basic Authentication (username and password). See [introduction]
|
||||
|
||||
**Example Request**:
|
||||
|
||||
PUT /api/admin/users/2/permissions HTTP/1.1
|
||||
Accept: application/json
|
||||
Content-Type: application/json
|
||||
```json
|
||||
PUT /api/admin/users/2/permissions HTTP/1.1
|
||||
Accept: application/json
|
||||
Content-Type: application/json
|
||||
|
||||
{"isGrafanaAdmin": true}
|
||||
{"isGrafanaAdmin": true}
|
||||
```
|
||||
|
||||
**Example Response**:
|
||||
|
||||
HTTP/1.1 200
|
||||
Content-Type: application/json
|
||||
```json
|
||||
HTTP/1.1 200
|
||||
Content-Type: application/json
|
||||
|
||||
{message: "User permissions updated"}
|
||||
{message: "User permissions updated"}
|
||||
```
|
||||
|
||||
## Delete global User
|
||||
|
||||
@@ -280,16 +301,20 @@ Only works with Basic Authentication (username and password). See [introduction]
|
||||
|
||||
**Example Request**:
|
||||
|
||||
DELETE /api/admin/users/2 HTTP/1.1
|
||||
Accept: application/json
|
||||
Content-Type: application/json
|
||||
```json
|
||||
DELETE /api/admin/users/2 HTTP/1.1
|
||||
Accept: application/json
|
||||
Content-Type: application/json
|
||||
```
|
||||
|
||||
**Example Response**:
|
||||
|
||||
HTTP/1.1 200
|
||||
Content-Type: application/json
|
||||
```json
|
||||
HTTP/1.1 200
|
||||
Content-Type: application/json
|
||||
|
||||
{message: "User deleted"}
|
||||
{message: "User deleted"}
|
||||
```
|
||||
|
||||
## Pause all alerts
|
||||
|
||||
@@ -299,13 +324,15 @@ Only works with Basic Authentication (username and password). See [introduction]
|
||||
|
||||
**Example Request**:
|
||||
|
||||
POST /api/admin/pause-all-alerts HTTP/1.1
|
||||
Accept: application/json
|
||||
Content-Type: application/json
|
||||
```json
|
||||
POST /api/admin/pause-all-alerts HTTP/1.1
|
||||
Accept: application/json
|
||||
Content-Type: application/json
|
||||
|
||||
{
|
||||
"paused": true
|
||||
}
|
||||
{
|
||||
"paused": true
|
||||
}
|
||||
```
|
||||
|
||||
JSON Body schema:
|
||||
|
||||
@@ -313,7 +340,9 @@ JSON Body schema:
|
||||
|
||||
**Example Response**:
|
||||
|
||||
HTTP/1.1 200
|
||||
Content-Type: application/json
|
||||
```json
|
||||
HTTP/1.1 200
|
||||
Content-Type: application/json
|
||||
|
||||
{state: "new state", message: "alerts pause/un paused", "alertsAffected": 100}
|
||||
{state: "new state", message: "alerts pause/un paused", "alertsAffected": 100}
|
||||
```
|
||||
@@ -23,11 +23,12 @@ This API can also be used to create, update and delete alert notifications.
|
||||
|
||||
**Example Request**:
|
||||
|
||||
GET /api/alerts HTTP/1.1
|
||||
Accept: application/json
|
||||
Content-Type: application/json
|
||||
Authorization: Bearer eyJrIjoiT0tTcG1pUlY2RnVKZTFVaDFsNFZXdE9ZWmNrMkZYbk
|
||||
|
||||
```http
|
||||
GET /api/alerts HTTP/1.1
|
||||
Accept: application/json
|
||||
Content-Type: application/json
|
||||
Authorization: Bearer eyJrIjoiT0tTcG1pUlY2RnVKZTFVaDFsNFZXdE9ZWmNrMkZYbk
|
||||
```
|
||||
Querystring Parameters:
|
||||
|
||||
These parameters are used as querystring parameters. For example:
|
||||
@@ -41,28 +42,30 @@ This API can also be used to create, update and delete alert notifications.
|
||||
|
||||
**Example Response**:
|
||||
|
||||
HTTP/1.1 200
|
||||
Content-Type: application/json
|
||||
[
|
||||
```http
|
||||
HTTP/1.1 200
|
||||
Content-Type: application/json
|
||||
[
|
||||
```
|
||||
|
||||
## Get one alert
|
||||
|
||||
`GET /api/alerts/:id`
|
||||
|
||||
**Example Request**:
|
||||
|
||||
```http
|
||||
GET /api/alerts/1 HTTP/1.1
|
||||
"id": 1,
|
||||
"dashboardId": 1,
|
||||
"panelId": 1,
|
||||
"name": "fire place sensor",
|
||||
"message": "Someone is trying to break in through the fire place",
|
||||
"state": "alerting",
|
||||
"evalDate": "0001-01-01T00:00:00Z",
|
||||
"evalData": [
|
||||
{
|
||||
"metric": "fire",
|
||||
"tags": null,
|
||||
"value": 5.349999999999999
|
||||
}
|
||||
"newStateDate": "2016-12-25",
|
||||
"executionError": "",
|
||||
"dashboardUri": "http://grafana.com/dashboard/db/sensors"
|
||||
Accept: application/json
|
||||
Content-Type: application/json
|
||||
Authorization: Bearer eyJrIjoiT0tTcG1pUlY2RnVKZTFVaDFsNFZXdE9ZWmNrMkZYbk
|
||||
```
|
||||
]
|
||||
|
||||
**Example Response**:
|
||||
|
||||
```http
|
||||
HTTP/1.1 200
|
||||
Content-Type: application/json
|
||||
{
|
||||
```
|
||||
|
||||
@@ -70,26 +73,30 @@ This API can also be used to create, update and delete alert notifications.
|
||||
|
||||
`POST /api/alerts/:id/pause`
|
||||
|
||||
GET /api/alerts/1 HTTP/1.1
|
||||
Accept: application/json
|
||||
Content-Type: application/json
|
||||
Authorization: Bearer eyJrIjoiT0tTcG1pUlY2RnVKZTFVaDFsNFZXdE9ZWmNrMkZYbk
|
||||
**Example Request**:
|
||||
|
||||
```http
|
||||
POST /api/alerts/1/pause HTTP/1.1
|
||||
Accept: application/json
|
||||
Content-Type: application/json
|
||||
Authorization: Bearer eyJrIjoiT0tTcG1pUlY2RnVKZTFVaDFsNFZXdE9ZWmNrMkZYbk
|
||||
|
||||
```
|
||||
HTTP/1.1 200
|
||||
Content-Type: application/json
|
||||
{
|
||||
"id": 1,
|
||||
"dashboardId": 1,
|
||||
"panelId": 1,
|
||||
"name": "fire place sensor",
|
||||
"message": "Someone is trying to break in through the fire place",
|
||||
"state": "alerting",
|
||||
"newStateDate": "2016-12-25",
|
||||
"executionError": "",
|
||||
"dashboardUri": "http://grafana.com/dashboard/db/sensors"
|
||||
}
|
||||
|
||||
The :id query parameter is the id of the alert to be paused or unpaused.
|
||||
|
||||
JSON Body Schema:
|
||||
|
||||
- **paused** – Can be `true` or `false`. True to pause an alert. False to unpause an alert.
|
||||
|
||||
**Example Response**:
|
||||
|
||||
```http
|
||||
HTTP/1.1 200
|
||||
Content-Type: application/json
|
||||
{
|
||||
```
|
||||
|
||||
## Get alert notifications
|
||||
|
||||
`GET /api/alert-notifications`
|
||||
@@ -97,14 +104,16 @@ This API can also be used to create, update and delete alert notifications.
|
||||
**Example Request**:
|
||||
|
||||
```http
|
||||
POST /api/alerts/1/pause HTTP/1.1
|
||||
Accept: application/json
|
||||
Content-Type: application/json
|
||||
Authorization: Bearer eyJrIjoiT0tTcG1pUlY2RnVKZTFVaDFsNFZXdE9ZWmNrMkZYbk
|
||||
GET /api/alert-notifications HTTP/1.1
|
||||
Accept: application/json
|
||||
Content-Type: application/json
|
||||
Authorization: Bearer eyJrIjoiT0tTcG1pUlY2RnVKZTFVaDFsNFZXdE9ZWmNrMkZYbk
|
||||
```
|
||||
|
||||
{
|
||||
"paused": true
|
||||
}
|
||||
|
||||
**Example Response**:
|
||||
|
||||
```http
|
||||
HTTP/1.1 200
|
||||
Content-Type: application/json
|
||||
|
||||
@@ -114,13 +123,15 @@ JSON Body Schema:
|
||||
|
||||
`POST /api/alert-notifications`
|
||||
|
||||
HTTP/1.1 200
|
||||
Content-Type: application/json
|
||||
{
|
||||
"alertId": 1,
|
||||
"state": "Paused",
|
||||
"message": "alert paused"
|
||||
}
|
||||
**Example Request**:
|
||||
|
||||
```http
|
||||
POST /api/alert-notifications HTTP/1.1
|
||||
Accept: application/json
|
||||
Content-Type: application/json
|
||||
Authorization: Bearer eyJrIjoiT0tTcG1pUlY2RnVKZTFVaDFsNFZXdE9ZWmNrMkZYbk
|
||||
|
||||
```
|
||||
|
||||
**Example Response**:
|
||||
|
||||
@@ -128,26 +139,29 @@ JSON Body Schema:
|
||||
HTTP/1.1 200
|
||||
Content-Type: application/json
|
||||
{
|
||||
GET /api/alert-notifications HTTP/1.1
|
||||
Accept: application/json
|
||||
Content-Type: application/json
|
||||
Authorization: Bearer eyJrIjoiT0tTcG1pUlY2RnVKZTFVaDFsNFZXdE9ZWmNrMkZYbk
|
||||
|
||||
```
|
||||
|
||||
## Update alert notification
|
||||
|
||||
`PUT /api/alert-notifications/1`
|
||||
|
||||
**Example Request**:
|
||||
|
||||
```http
|
||||
PUT /api/alert-notifications/1 HTTP/1.1
|
||||
HTTP/1.1 200
|
||||
Content-Type: application/json
|
||||
Accept: application/json
|
||||
Content-Type: application/json
|
||||
Authorization: Bearer eyJrIjoiT0tTcG1pUlY2RnVKZTFVaDFsNFZXdE9ZWmNrMkZYbk
|
||||
|
||||
{
|
||||
"id": 1,
|
||||
"name": "Team A",
|
||||
"type": "email",
|
||||
"isDefault": true,
|
||||
"created": "2017-01-01 12:45",
|
||||
"updated": "2017-01-01 12:45"
|
||||
}
|
||||
```
|
||||
|
||||
**Example Response**:
|
||||
|
||||
```http
|
||||
HTTP/1.1 200
|
||||
Content-Type: application/json
|
||||
{
|
||||
```
|
||||
|
||||
## Delete alert notification
|
||||
|
||||
@@ -155,34 +169,37 @@ JSON Body Schema:
|
||||
|
||||
**Example Request**:
|
||||
|
||||
POST /api/alert-notifications HTTP/1.1
|
||||
Accept: application/json
|
||||
Content-Type: application/json
|
||||
Authorization: Bearer eyJrIjoiT0tTcG1pUlY2RnVKZTFVaDFsNFZXdE9ZWmNrMkZYbk
|
||||
|
||||
{
|
||||
"name": "new alert notification", //Required
|
||||
"type": "email", //Required
|
||||
"isDefault": false,
|
||||
"settings": {
|
||||
"addresses": "carl@grafana.com;dev@grafana.com"
|
||||
}
|
||||
}
|
||||
```http
|
||||
DELETE /api/alert-notifications/1 HTTP/1.1
|
||||
Accept: application/json
|
||||
Content-Type: application/json
|
||||
Authorization: Bearer eyJrIjoiT0tTcG1pUlY2RnVKZTFVaDFsNFZXdE9ZWmNrMkZYbk
|
||||
```
|
||||
|
||||
**Example Response**:
|
||||
|
||||
```http
|
||||
HTTP/1.1 200
|
||||
Content-Type: application/json
|
||||
{
|
||||
```
|
||||
```
|
||||
|
||||
**Example Response**:
|
||||
|
||||
HTTP/1.1 200
|
||||
Content-Type: application/json
|
||||
{
|
||||
"id": 1,
|
||||
"name": "new alert notification",
|
||||
"type": "email",
|
||||
"isDefault": false,
|
||||
"settings": { addresses: "carl@grafana.com;dev@grafana.com"} }
|
||||
"created": "2017-01-01 12:34",
|
||||
"updated": "2017-01-01 12:34"
|
||||
}
|
||||
```http
|
||||
HTTP/1.1 200
|
||||
Content-Type: application/json
|
||||
{
|
||||
"id": 1,
|
||||
"name": "new alert notification",
|
||||
"type": "email",
|
||||
"isDefault": false,
|
||||
"settings": { addresses: "carl@grafana.com;dev@grafana.com"} }
|
||||
"created": "2017-01-01 12:34",
|
||||
"updated": "2017-01-01 12:34"
|
||||
}
|
||||
```
|
||||
|
||||
## Update alert notification
|
||||
|
||||
@@ -190,35 +207,38 @@ JSON Body Schema:
|
||||
|
||||
**Example Request**:
|
||||
|
||||
PUT /api/alert-notifications/1 HTTP/1.1
|
||||
Accept: application/json
|
||||
Content-Type: application/json
|
||||
Authorization: Bearer eyJrIjoiT0tTcG1pUlY2RnVKZTFVaDFsNFZXdE9ZWmNrMkZYbk
|
||||
|
||||
{
|
||||
"id": 1,
|
||||
"name": "new alert notification", //Required
|
||||
"type": "email", //Required
|
||||
"isDefault": false,
|
||||
"settings": {
|
||||
"addresses: "carl@grafana.com;dev@grafana.com"
|
||||
}
|
||||
}
|
||||
```http
|
||||
PUT /api/alert-notifications/1 HTTP/1.1
|
||||
Accept: application/json
|
||||
Content-Type: application/json
|
||||
Authorization: Bearer eyJrIjoiT0tTcG1pUlY2RnVKZTFVaDFsNFZXdE9ZWmNrMkZYbk
|
||||
|
||||
{
|
||||
"id": 1,
|
||||
"name": "new alert notification", //Required
|
||||
"type": "email", //Required
|
||||
"isDefault": false,
|
||||
"settings": {
|
||||
"addresses: "carl@grafana.com;dev@grafana.com"
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
**Example Response**:
|
||||
|
||||
HTTP/1.1 200
|
||||
Content-Type: application/json
|
||||
{
|
||||
"id": 1,
|
||||
"name": "new alert notification",
|
||||
"type": "email",
|
||||
"isDefault": false,
|
||||
"settings": { addresses: "carl@grafana.com;dev@grafana.com"} }
|
||||
"created": "2017-01-01 12:34",
|
||||
"updated": "2017-01-01 12:34"
|
||||
}
|
||||
```http
|
||||
HTTP/1.1 200
|
||||
Content-Type: application/json
|
||||
{
|
||||
"id": 1,
|
||||
"name": "new alert notification",
|
||||
"type": "email",
|
||||
"isDefault": false,
|
||||
"settings": { addresses: "carl@grafana.com;dev@grafana.com"} }
|
||||
"created": "2017-01-01 12:34",
|
||||
"updated": "2017-01-01 12:34"
|
||||
}
|
||||
```
|
||||
|
||||
## Delete alert notification
|
||||
|
||||
@@ -226,15 +246,19 @@ JSON Body Schema:
|
||||
|
||||
**Example Request**:
|
||||
|
||||
DELETE /api/alert-notifications/1 HTTP/1.1
|
||||
Accept: application/json
|
||||
Content-Type: application/json
|
||||
Authorization: Bearer eyJrIjoiT0tTcG1pUlY2RnVKZTFVaDFsNFZXdE9ZWmNrMkZYbk
|
||||
```http
|
||||
DELETE /api/alert-notifications/1 HTTP/1.1
|
||||
Accept: application/json
|
||||
Content-Type: application/json
|
||||
Authorization: Bearer eyJrIjoiT0tTcG1pUlY2RnVKZTFVaDFsNFZXdE9ZWmNrMkZYbk
|
||||
```
|
||||
|
||||
**Example Response**:
|
||||
|
||||
HTTP/1.1 200
|
||||
Content-Type: application/json
|
||||
{
|
||||
"message": "Notification deleted"
|
||||
}
|
||||
```http
|
||||
HTTP/1.1 200
|
||||
Content-Type: application/json
|
||||
{
|
||||
"message": "Notification deleted"
|
||||
}
|
||||
```
|
||||
220
docs/sources/http_api/annotations.md
Normal file
220
docs/sources/http_api/annotations.md
Normal file
@@ -0,0 +1,220 @@
|
||||
+++
|
||||
title = "Annotations HTTP API "
|
||||
description = "Grafana Annotations HTTP API"
|
||||
keywords = ["grafana", "http", "documentation", "api", "annotation", "annotations", "comment"]
|
||||
aliases = ["/http_api/annotations/"]
|
||||
type = "docs"
|
||||
[menu.docs]
|
||||
name = "Annotations"
|
||||
identifier = "annotationshttp"
|
||||
parent = "http_api"
|
||||
+++
|
||||
|
||||
# Annotations resources / actions
|
||||
|
||||
This is the API documentation for the new Grafana Annotations feature released in Grafana 4.6. Annotations are saved in the Grafana database (sqlite, mysql or postgres). Annotations can be global annotations that can be shown on any dashboard by configuring an annotation data source - they are filtered by tags. Or they can be tied to a panel on a dashboard and are then only shown on that panel.
|
||||
|
||||
## Find Annotations
|
||||
|
||||
`GET /api/annotations?from=1506676478816&to=1507281278816&tags=tag1&tags=tag2&limit=100`
|
||||
|
||||
**Example Request**:
|
||||
|
||||
```http
|
||||
GET /api/annotations?from=1506676478816&to=1507281278816&tags=tag1&tags=tag2&limit=100 HTTP/1.1
|
||||
Accept: application/json
|
||||
Content-Type: application/json
|
||||
Authorization: Basic YWRtaW46YWRtaW4=
|
||||
```
|
||||
|
||||
|
||||
Query Parameters:
|
||||
|
||||
- `from`: epoch datetime in milliseconds. Optional.
|
||||
- `to`: epoch datetime in milliseconds. Optional.
|
||||
- `limit`: number. Optional - default is 10. Max limit for results returned.
|
||||
- `alertId`: number. Optional. Find annotations for a specified alert.
|
||||
- `dashboardId`: number. Optional. Find annotations that are scoped to a specific dashboard
|
||||
- `panelId`: number. Optional. Find annotations that are scoped to a specific panel
|
||||
- `tags`: string. Optional. Use this to filter global annotations. Global annotations are annotations from an annotation data source that are not connected specifically to a dashboard or panel. To do an "AND" filtering with multiple tags, specify the tags parameter multiple times e.g. `tags=tag1&tags=tag2`.
|
||||
|
||||
**Example Response**:
|
||||
|
||||
```http
|
||||
HTTP/1.1 200
|
||||
Content-Type: application/json
|
||||
[
|
||||
```
|
||||
|
||||
## Create Annotation
|
||||
|
||||
Creates an annotation in the Grafana database. The `dashboardId` and `panelId` fields are optional. If they are not specified then a global annotation is created and can be queried in any dashboard that adds the Grafana annotations data source.
|
||||
|
||||
`POST /api/annotations`
|
||||
|
||||
**Example Request**:
|
||||
|
||||
```json
|
||||
POST /api/annotations HTTP/1.1
|
||||
Accept: application/json
|
||||
Content-Type: application/json
|
||||
|
||||
{
|
||||
"dashboardId":468,
|
||||
"panelId":1,
|
||||
"time":1507037197339,
|
||||
"isRegion":true,
|
||||
"timeEnd":1507180805056,
|
||||
"tags":["tag1","tag2"],
|
||||
"text":"Annotation Description"
|
||||
}
|
||||
```
|
||||
|
||||
**Example Response**:
|
||||
|
||||
```json
|
||||
HTTP/1.1 200
|
||||
Content-Type: application/json
|
||||
|
||||
{"message":"Annotation added"}
|
||||
```
|
||||
|
||||
## Create Annotation in Graphite format
|
||||
|
||||
Creates an annotation by using Graphite-compatible event format. The `when` and `data` fields are optional. If `when` is not specified then the current time will be used as annotation's timestamp. The `tags` field can also be in prior to Graphite `0.10.0`
|
||||
format (string with multiple tags being separated by a space).
|
||||
|
||||
`POST /api/annotations/graphite`
|
||||
|
||||
**Example Request**:
|
||||
|
||||
```json
|
||||
POST /api/annotations/graphite HTTP/1.1
|
||||
Accept: application/json
|
||||
Content-Type: application/json
|
||||
|
||||
{
|
||||
"what": "Event - deploy",
|
||||
"tags": ["deploy", "production"],
|
||||
"when": 1467844481,
|
||||
"data": "deploy of master branch happened at Wed Jul 6 22:34:41 UTC 2016"
|
||||
}
|
||||
```
|
||||
|
||||
**Example Response**:
|
||||
|
||||
```json
|
||||
HTTP/1.1 200
|
||||
Content-Type: application/json
|
||||
|
||||
{"message":"Graphite annotation added"}
|
||||
```
|
||||
|
||||
## Update Annotation
|
||||
|
||||
`PUT /api/annotations/:id`
|
||||
|
||||
**Example Request**:
|
||||
|
||||
```json
|
||||
PUT /api/annotations/1141 HTTP/1.1
|
||||
Accept: application/json
|
||||
Content-Type: application/json
|
||||
|
||||
{
|
||||
"time":1507037197339,
|
||||
"isRegion":true,
|
||||
"timeEnd":1507180805056,
|
||||
"text":"Annotation Description",
|
||||
"tags":["tag3","tag4","tag5"]
|
||||
}
|
||||
```
|
||||
|
||||
## Delete Annotation By Id
|
||||
|
||||
`DELETE /api/annotation/:id`
|
||||
|
||||
Deletes the annotation that matches the specified id.
|
||||
|
||||
**Example Request**:
|
||||
|
||||
```http
|
||||
DELETE /api/annotation/1 HTTP/1.1
|
||||
Accept: application/json
|
||||
Content-Type: application/json
|
||||
Authorization: Bearer eyJrIjoiT0tTcG1pUlY2RnVKZTFVaDFsNFZXdE9ZWmNrMkZYbk
|
||||
```
|
||||
|
||||
**Example Response**:
|
||||
|
||||
```http
|
||||
HTTP/1.1 200
|
||||
Content-Type: application/json
|
||||
|
||||
```
|
||||
|
||||
## Delete Annotation By RegionId
|
||||
|
||||
`DELETE /api/annotation/region/:id`
|
||||
|
||||
Deletes the annotation that matches the specified region id. A region is an annotation that covers a timerange and has a start and end time. In the Grafana database, this is a stored as two annotations connected by a region id.
|
||||
|
||||
**Example Request**:
|
||||
|
||||
```http
|
||||
DELETE /api/annotation/region/1 HTTP/1.1
|
||||
Accept: application/json
|
||||
Content-Type: application/json
|
||||
Authorization: Bearer eyJrIjoiT0tTcG1pUlY2RnVKZTFVaDFsNFZXdE9ZWmNrMkZYbk
|
||||
```
|
||||
|
||||
**Example Response**:
|
||||
|
||||
```http
|
||||
HTTP/1.1 200
|
||||
Content-Type: application/json
|
||||
|
||||
```
|
||||
Deletes the annotation that matches the specified id.
|
||||
|
||||
**Example Request**:
|
||||
|
||||
```http
|
||||
DELETE /api/annotation/1 HTTP/1.1
|
||||
Accept: application/json
|
||||
Content-Type: application/json
|
||||
Authorization: Bearer eyJrIjoiT0tTcG1pUlY2RnVKZTFVaDFsNFZXdE9ZWmNrMkZYbk
|
||||
```
|
||||
|
||||
**Example Response**:
|
||||
|
||||
```http
|
||||
HTTP/1.1 200
|
||||
Content-Type: application/json
|
||||
|
||||
{"message":"Annotation deleted"}
|
||||
```
|
||||
|
||||
## Delete Annotation By RegionId
|
||||
|
||||
`DELETE /api/annotation/region/:id`
|
||||
|
||||
Deletes the annotation that matches the specified region id. A region is an annotation that covers a timerange and has a start and end time. In the Grafana database, this is a stored as two annotations connected by a region id.
|
||||
|
||||
**Example Request**:
|
||||
|
||||
```http
|
||||
DELETE /api/annotation/region/1 HTTP/1.1
|
||||
Accept: application/json
|
||||
Content-Type: application/json
|
||||
Authorization: Bearer eyJrIjoiT0tTcG1pUlY2RnVKZTFVaDFsNFZXdE9ZWmNrMkZYbk
|
||||
```
|
||||
|
||||
**Example Response**:
|
||||
|
||||
```http
|
||||
HTTP/1.1 200
|
||||
Content-Type: application/json
|
||||
|
||||
{"message":"Annotation region deleted"}
|
||||
```
|
||||
@@ -21,7 +21,7 @@ If basic auth is enabled (it is enabled by default) you can authenticate your HT
|
||||
standard basic auth. Basic auth will also authenticate LDAP users.
|
||||
|
||||
curl example:
|
||||
```
|
||||
```bash
|
||||
?curl http://admin:admin@localhost:3000/api/org
|
||||
{"id":1,"name":"Main Org."}
|
||||
```
|
||||
@@ -36,9 +36,11 @@ You use the token in all requests in the `Authorization` header, like this:
|
||||
|
||||
**Example**:
|
||||
|
||||
GET http://your.grafana.com/api/dashboards/db/mydash HTTP/1.1
|
||||
Accept: application/json
|
||||
Authorization: Bearer eyJrIjoiT0tTcG1pUlY2RnVKZTFVaDFsNFZXdE9ZWmNrMkZYbk
|
||||
```http
|
||||
GET http://your.grafana.com/api/dashboards/db/mydash HTTP/1.1
|
||||
Accept: application/json
|
||||
Authorization: Bearer eyJrIjoiT0tTcG1pUlY2RnVKZTFVaDFsNFZXdE9ZWmNrMkZYbk
|
||||
```
|
||||
|
||||
The `Authorization` header value should be `Bearer <your api key>`.
|
||||
|
||||
@@ -50,28 +52,32 @@ The `Authorization` header value should be `Bearer <your api key>`.
|
||||
|
||||
**Example Request**:
|
||||
|
||||
GET /api/auth/keys HTTP/1.1
|
||||
Accept: application/json
|
||||
Content-Type: application/json
|
||||
Authorization: Bearer eyJrIjoiT0tTcG1pUlY2RnVKZTFVaDFsNFZXdE9ZWmNrMkZYbk
|
||||
```http
|
||||
GET /api/auth/keys HTTP/1.1
|
||||
Accept: application/json
|
||||
Content-Type: application/json
|
||||
Authorization: Bearer eyJrIjoiT0tTcG1pUlY2RnVKZTFVaDFsNFZXdE9ZWmNrMkZYbk
|
||||
```
|
||||
|
||||
**Example Response**:
|
||||
|
||||
HTTP/1.1 200
|
||||
Content-Type: application/json
|
||||
```http
|
||||
HTTP/1.1 200
|
||||
Content-Type: application/json
|
||||
|
||||
[
|
||||
{
|
||||
"id": 3,
|
||||
"name": "API",
|
||||
"role": "Admin"
|
||||
},
|
||||
{
|
||||
"id": 1,
|
||||
"name": "TestAdmin",
|
||||
"role": "Admin"
|
||||
}
|
||||
]
|
||||
```
|
||||
|
||||
## Create API Key
|
||||
|
||||
`POST /api/auth/keys`
|
||||
|
||||
**Example Request**:
|
||||
|
||||
```http
|
||||
POST /api/auth/keys HTTP/1.1
|
||||
Accept: application/json
|
||||
Content-Type: application/json
|
||||
Authorization: Bearer eyJrIjoiT0tTcG1pUlY2RnVKZTFVaDFsNFZXdE9ZWmNrMkZYbk
|
||||
|
||||
```
|
||||
|
||||
@@ -79,15 +85,17 @@ The `Authorization` header value should be `Bearer <your api key>`.
|
||||
|
||||
- **name** – The key name
|
||||
- **role** – Sets the access level/Grafana Role for the key. Can be one of the following values: `Viewer`, `Editor`, `Read Only Editor` or `Admin`.
|
||||
POST /api/auth/keys HTTP/1.1
|
||||
Accept: application/json
|
||||
Content-Type: application/json
|
||||
Authorization: Bearer eyJrIjoiT0tTcG1pUlY2RnVKZTFVaDFsNFZXdE9ZWmNrMkZYbk
|
||||
|
||||
**Example Response**:
|
||||
|
||||
```http
|
||||
HTTP/1.1 200
|
||||
Content-Type: application/json
|
||||
{
|
||||
"name": "mykey",
|
||||
"role": "Admin"
|
||||
}
|
||||
|
||||
```
|
||||
|
||||
## Delete API Key
|
||||
|
||||
`DELETE /api/auth/keys/:id`
|
||||
|
||||
**Example Request**:
|
||||
@@ -96,10 +104,12 @@ JSON Body schema:
|
||||
DELETE /api/auth/keys/3 HTTP/1.1
|
||||
Accept: application/json
|
||||
Content-Type: application/json
|
||||
HTTP/1.1 200
|
||||
Content-Type: application/json
|
||||
Authorization: Bearer eyJrIjoiT0tTcG1pUlY2RnVKZTFVaDFsNFZXdE9ZWmNrMkZYbk
|
||||
```
|
||||
**Example Response**:
|
||||
|
||||
{"name":"mykey","key":"eyJrIjoiWHZiSWd3NzdCYUZnNUtibE9obUpESmE3bzJYNDRIc0UiLCJuIjoibXlrZXkiLCJpZCI6MX1="}
|
||||
```http
|
||||
HTTP/1.1 200
|
||||
Content-Type: application/json
|
||||
|
||||
```
|
||||
@@ -107,14 +117,17 @@ JSON Body schema:
|
||||
|
||||
**Example Request**:
|
||||
|
||||
DELETE /api/auth/keys/3 HTTP/1.1
|
||||
Accept: application/json
|
||||
Content-Type: application/json
|
||||
Authorization: Bearer eyJrIjoiT0tTcG1pUlY2RnVKZTFVaDFsNFZXdE9ZWmNrMkZYbk
|
||||
|
||||
```http
|
||||
DELETE /api/auth/keys/3 HTTP/1.1
|
||||
Accept: application/json
|
||||
Content-Type: application/json
|
||||
Authorization: Bearer eyJrIjoiT0tTcG1pUlY2RnVKZTFVaDFsNFZXdE9ZWmNrMkZYbk
|
||||
```
|
||||
**Example Response**:
|
||||
|
||||
HTTP/1.1 200
|
||||
Content-Type: application/json
|
||||
```http
|
||||
HTTP/1.1 200
|
||||
Content-Type: application/json
|
||||
|
||||
{"message":"API key deleted"}
|
||||
{"message":"API key deleted"}
|
||||
```
|
||||
@@ -158,53 +158,57 @@ Will return the home dashboard.
|
||||
|
||||
`GET /api/search/`
|
||||
|
||||
"title":"Home",
|
||||
"version":5
|
||||
}
|
||||
}
|
||||
Query parameters:
|
||||
|
||||
- **query** – Search Query
|
||||
- **tag** – Tag to use
|
||||
- **starred** – Flag indicating if only starred Dashboards should be returned
|
||||
- **tagcloud** - Flag indicating if a tagcloud should be returned
|
||||
|
||||
**Example Request**:
|
||||
|
||||
|
||||
`GET /api/dashboards/tags`
|
||||
```http
|
||||
GET /api/search?query=MyDashboard&starred=true&tag=prod HTTP/1.1
|
||||
Accept: application/json
|
||||
Content-Type: application/json
|
||||
Authorization: Bearer eyJrIjoiT0tTcG1pUlY2RnVKZTFVaDFsNFZXdE9ZWmNrMkZYbk
|
||||
```
|
||||
|
||||
**Example Response**:
|
||||
|
||||
```http
|
||||
HTTP/1.1 200
|
||||
Content-Type: application/json
|
||||
|
||||
```
|
||||
"dashboard": {
|
||||
"editable":false,
|
||||
"hideControls":true,
|
||||
"nav":[
|
||||
{
|
||||
|
||||
**Example Request**:
|
||||
|
||||
GET /api/dashboards/tags HTTP/1.1
|
||||
Accept: application/json
|
||||
Content-Type: application/json
|
||||
Authorization: Bearer eyJrIjoiT0tTcG1pUlY2RnVKZTFVaDFsNFZXdE9ZWmNrMkZYbk
|
||||
|
||||
**Example Response**:
|
||||
|
||||
HTTP/1.1 200
|
||||
Content-Type: application/json
|
||||
|
||||
[
|
||||
{
|
||||
"term":"tag1",
|
||||
"count":1
|
||||
},
|
||||
{
|
||||
"term":"tag2",
|
||||
"count":4
|
||||
}
|
||||
]
|
||||
|
||||
## Search Dashboards
|
||||
|
||||
`GET /api/search/`
|
||||
|
||||
Query parameters:
|
||||
|
||||
- **query** – Search Query
|
||||
- **tag** – Tag to use
|
||||
- **starred** – Flag indicating if only starred Dashboards should be returned
|
||||
- **tagcloud** - Flag indicating if a tagcloud should be returned
|
||||
|
||||
"enable":false,
|
||||
"type":"timepicker"
|
||||
}
|
||||
],
|
||||
"rows": [
|
||||
{
|
||||
|
||||
}
|
||||
],
|
||||
"style":"dark",
|
||||
"tags":[],
|
||||
"templating":{
|
||||
"list":[
|
||||
]
|
||||
},
|
||||
"time":{
|
||||
},
|
||||
"timezone":"browser",
|
||||
"title":"Home",
|
||||
"version":5
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
## Tags for Dashboard
|
||||
|
||||
@@ -215,26 +219,30 @@ Get all tags of dashboards
|
||||
|
||||
**Example Request**:
|
||||
|
||||
|
||||
[
|
||||
{
|
||||
"id":1,
|
||||
```http
|
||||
GET /api/dashboards/tags HTTP/1.1
|
||||
Accept: application/json
|
||||
Content-Type: application/json
|
||||
Authorization: Bearer eyJrIjoiT0tTcG1pUlY2RnVKZTFVaDFsNFZXdE9ZWmNrMkZYbk
|
||||
```
|
||||
|
||||
**Example Response**:
|
||||
|
||||
"tags":[],
|
||||
"isStarred":false
|
||||
```http
|
||||
HTTP/1.1 200
|
||||
Content-Type: application/json
|
||||
|
||||
]
|
||||
{
|
||||
"term":"tag1",
|
||||
"count":1
|
||||
},
|
||||
{
|
||||
"term":"tag2",
|
||||
"count":4
|
||||
}
|
||||
]
|
||||
[
|
||||
{
|
||||
"term":"tag1",
|
||||
"count":1
|
||||
},
|
||||
{
|
||||
"term":"tag2",
|
||||
"count":4
|
||||
}
|
||||
]
|
||||
```
|
||||
|
||||
## Search Dashboards
|
||||
|
||||
@@ -249,23 +257,27 @@ Query parameters:
|
||||
|
||||
**Example Request**:
|
||||
|
||||
GET /api/search?query=MyDashboard&starred=true&tag=prod HTTP/1.1
|
||||
Accept: application/json
|
||||
Content-Type: application/json
|
||||
Authorization: Bearer eyJrIjoiT0tTcG1pUlY2RnVKZTFVaDFsNFZXdE9ZWmNrMkZYbk
|
||||
```http
|
||||
GET /api/search?query=MyDashboard&starred=true&tag=prod HTTP/1.1
|
||||
Accept: application/json
|
||||
Content-Type: application/json
|
||||
Authorization: Bearer eyJrIjoiT0tTcG1pUlY2RnVKZTFVaDFsNFZXdE9ZWmNrMkZYbk
|
||||
```
|
||||
|
||||
**Example Response**:
|
||||
|
||||
HTTP/1.1 200
|
||||
Content-Type: application/json
|
||||
```http
|
||||
HTTP/1.1 200
|
||||
Content-Type: application/json
|
||||
|
||||
[
|
||||
{
|
||||
"id":1,
|
||||
"title":"Production Overview",
|
||||
"uri":"db/production-overview",
|
||||
"type":"dash-db",
|
||||
"tags":[],
|
||||
"isStarred":false
|
||||
}
|
||||
]
|
||||
[
|
||||
{
|
||||
"id":1,
|
||||
"title":"Production Overview",
|
||||
"uri":"db/production-overview",
|
||||
"type":"dash-db",
|
||||
"tags":[],
|
||||
"isStarred":false
|
||||
}
|
||||
]
|
||||
```
|
||||
@@ -18,34 +18,38 @@ parent = "http_api"
|
||||
|
||||
**Example Request**:
|
||||
|
||||
GET /api/datasources HTTP/1.1
|
||||
Accept: application/json
|
||||
Content-Type: application/json
|
||||
Authorization: Bearer eyJrIjoiT0tTcG1pUlY2RnVKZTFVaDFsNFZXdE9ZWmNrMkZYbk
|
||||
```http
|
||||
GET /api/datasources HTTP/1.1
|
||||
Accept: application/json
|
||||
Content-Type: application/json
|
||||
Authorization: Bearer eyJrIjoiT0tTcG1pUlY2RnVKZTFVaDFsNFZXdE9ZWmNrMkZYbk
|
||||
```
|
||||
|
||||
**Example Response**:
|
||||
|
||||
HTTP/1.1 200
|
||||
Content-Type: application/json
|
||||
```http
|
||||
HTTP/1.1 200
|
||||
Content-Type: application/json
|
||||
|
||||
[
|
||||
{
|
||||
"id":1,
|
||||
"orgId":1,
|
||||
"name":"datasource_elastic",
|
||||
"type":"elasticsearch",
|
||||
"access":"proxy",
|
||||
"url":"http://mydatasource.com",
|
||||
"password":"",
|
||||
"user":"",
|
||||
"database":"grafana-dash",
|
||||
"basicAuth":false,
|
||||
"basicAuthUser":"",
|
||||
"basicAuthPassword":"",
|
||||
"isDefault":false,
|
||||
"jsonData":null
|
||||
}
|
||||
]
|
||||
```
|
||||
|
||||
## Get a single data sources by Id
|
||||
|
||||
`GET /api/datasources/:datasourceId`
|
||||
|
||||
**Example Request**:
|
||||
|
||||
```http
|
||||
GET /api/datasources/1 HTTP/1.1
|
||||
Accept: application/json
|
||||
Content-Type: application/json
|
||||
Authorization: Bearer eyJrIjoiT0tTcG1pUlY2RnVKZTFVaDFsNFZXdE9ZWmNrMkZYbk
|
||||
```
|
||||
|
||||
**Example Response**:
|
||||
|
||||
```http
|
||||
HTTP/1.1 200
|
||||
Content-Type: application/json
|
||||
|
||||
```
|
||||
@@ -53,32 +57,36 @@ parent = "http_api"
|
||||
## Get a single data source by Name
|
||||
|
||||
`GET /api/datasources/name/:name`
|
||||
GET /api/datasources/1 HTTP/1.1
|
||||
Accept: application/json
|
||||
Content-Type: application/json
|
||||
Authorization: Bearer eyJrIjoiT0tTcG1pUlY2RnVKZTFVaDFsNFZXdE9ZWmNrMkZYbk
|
||||
|
||||
**Example Request**:
|
||||
|
||||
```http
|
||||
GET /api/datasources/name/test_datasource HTTP/1.1
|
||||
Accept: application/json
|
||||
Content-Type: application/json
|
||||
Authorization: Bearer eyJrIjoiT0tTcG1pUlY2RnVKZTFVaDFsNFZXdE9ZWmNrMkZYbk
|
||||
```
|
||||
HTTP/1.1 200
|
||||
Content-Type: application/json
|
||||
|
||||
**Example Response**:
|
||||
|
||||
```http
|
||||
{
|
||||
"id":1,
|
||||
"orgId":1,
|
||||
"name":"test_datasource",
|
||||
"type":"graphite",
|
||||
"access":"proxy",
|
||||
"url":"http://mydatasource.com",
|
||||
"password":"",
|
||||
"user":"",
|
||||
"database":"",
|
||||
"basicAuth":false,
|
||||
"basicAuthUser":"",
|
||||
"basicAuthPassword":"",
|
||||
"isDefault":false,
|
||||
"jsonData":null
|
||||
}
|
||||
HTTP/1.1 200
|
||||
Content-Type: application/json
|
||||
|
||||
```
|
||||
|
||||
## Get data source Id by Name
|
||||
|
||||
`GET /api/datasources/id/:name`
|
||||
|
||||
**Example Request**:
|
||||
|
||||
```http
|
||||
GET /api/datasources/id/test_datasource HTTP/1.1
|
||||
Accept: application/json
|
||||
Content-Type: application/json
|
||||
Authorization: Bearer eyJrIjoiT0tTcG1pUlY2RnVKZTFVaDFsNFZXdE9ZWmNrMkZYbk
|
||||
```
|
||||
|
||||
**Example Response**:
|
||||
|
||||
@@ -86,32 +94,36 @@ parent = "http_api"
|
||||
HTTP/1.1 200
|
||||
Content-Type: application/json
|
||||
|
||||
GET /api/datasources/name/test_datasource HTTP/1.1
|
||||
Accept: application/json
|
||||
Content-Type: application/json
|
||||
Authorization: Bearer eyJrIjoiT0tTcG1pUlY2RnVKZTFVaDFsNFZXdE9ZWmNrMkZYbk
|
||||
```
|
||||
|
||||
## Create data source
|
||||
|
||||
`POST /api/datasources`
|
||||
|
||||
**Example Graphite Request**:
|
||||
|
||||
```http
|
||||
HTTP/1.1 200
|
||||
Content-Type: application/json
|
||||
POST /api/datasources HTTP/1.1
|
||||
Accept: application/json
|
||||
Content-Type: application/json
|
||||
Authorization: Bearer eyJrIjoiT0tTcG1pUlY2RnVKZTFVaDFsNFZXdE9ZWmNrMkZYbk
|
||||
{
|
||||
"id":1,
|
||||
"orgId":1,
|
||||
"name":"test_datasource",
|
||||
"type":"graphite",
|
||||
"access":"proxy",
|
||||
"url":"http://mydatasource.com",
|
||||
"password":"",
|
||||
"user":"",
|
||||
"database":"",
|
||||
"basicAuth":false,
|
||||
"basicAuthUser":"",
|
||||
"basicAuthPassword":"",
|
||||
"isDefault":false,
|
||||
"jsonData":null
|
||||
}
|
||||
|
||||
```
|
||||
|
||||
**Example CloudWatch Request**:
|
||||
|
||||
```http
|
||||
POST /api/datasources HTTP/1.1
|
||||
Accept: application/json
|
||||
Content-Type: application/json
|
||||
Authorization: Bearer eyJrIjoiT0tTcG1pUlY2RnVKZTFVaDFsNFZXdE9ZWmNrMkZYbk
|
||||
|
||||
```
|
||||
|
||||
**Example Response**:
|
||||
|
||||
```http
|
||||
HTTP/1.1 200
|
||||
Content-Type: application/json
|
||||
|
||||
```
|
||||
@@ -119,19 +131,23 @@ parent = "http_api"
|
||||
## Update an existing data source
|
||||
|
||||
`PUT /api/datasources/:datasourceId`
|
||||
GET /api/datasources/id/test_datasource HTTP/1.1
|
||||
Accept: application/json
|
||||
Content-Type: application/json
|
||||
Authorization: Bearer eyJrIjoiT0tTcG1pUlY2RnVKZTFVaDFsNFZXdE9ZWmNrMkZYbk
|
||||
|
||||
**Example Request**:
|
||||
|
||||
```http
|
||||
PUT /api/datasources/1 HTTP/1.1
|
||||
Accept: application/json
|
||||
Content-Type: application/json
|
||||
Authorization: Bearer eyJrIjoiT0tTcG1pUlY2RnVKZTFVaDFsNFZXdE9ZWmNrMkZYbk
|
||||
|
||||
HTTP/1.1 200
|
||||
Content-Type: application/json
|
||||
```
|
||||
|
||||
**Example Response**:
|
||||
|
||||
{
|
||||
"id":1
|
||||
}
|
||||
```http
|
||||
HTTP/1.1 200
|
||||
Content-Type: application/json
|
||||
|
||||
```
|
||||
|
||||
## Delete an existing data source by id
|
||||
@@ -139,48 +155,53 @@ parent = "http_api"
|
||||
`DELETE /api/datasources/:datasourceId`
|
||||
|
||||
**Example Request**:
|
||||
POST /api/datasources HTTP/1.1
|
||||
Accept: application/json
|
||||
Content-Type: application/json
|
||||
Authorization: Bearer eyJrIjoiT0tTcG1pUlY2RnVKZTFVaDFsNFZXdE9ZWmNrMkZYbk
|
||||
|
||||
```http
|
||||
DELETE /api/datasources/1 HTTP/1.1
|
||||
Accept: application/json
|
||||
Content-Type: application/json
|
||||
Authorization: Bearer eyJrIjoiT0tTcG1pUlY2RnVKZTFVaDFsNFZXdE9ZWmNrMkZYbk
|
||||
{
|
||||
"name":"test_datasource",
|
||||
"type":"graphite",
|
||||
"url":"http://mydatasource.com",
|
||||
"access":"proxy",
|
||||
"basicAuth":false
|
||||
}
|
||||
```
|
||||
|
||||
**Example Response**:
|
||||
|
||||
```http
|
||||
HTTP/1.1 200
|
||||
Content-Type: application/json
|
||||
|
||||
```
|
||||
|
||||
```
|
||||
POST /api/datasources HTTP/1.1
|
||||
Accept: application/json
|
||||
Content-Type: application/json
|
||||
Authorization: Bearer eyJrIjoiT0tTcG1pUlY2RnVKZTFVaDFsNFZXdE9ZWmNrMkZYbk
|
||||
|
||||
{
|
||||
"name": "test_datasource",
|
||||
"type": "cloudwatch",
|
||||
"url": "http://monitoring.us-west-1.amazonaws.com",
|
||||
"access": "proxy",
|
||||
"jsonData": {
|
||||
"authType": "keys",
|
||||
"defaultRegion": "us-west-1"
|
||||
},
|
||||
"secureJsonData": {
|
||||
"accessKey": "Ol4pIDpeKSA6XikgOl4p",
|
||||
"secretKey": "dGVzdCBrZXkgYmxlYXNlIGRvbid0IHN0ZWFs"
|
||||
}
|
||||
## Delete an existing data source by name
|
||||
|
||||
`DELETE /api/datasources/name/:datasourceName`
|
||||
|
||||
**Example Request**:
|
||||
|
||||
```http
|
||||
DELETE /api/datasources/name/test_datasource HTTP/1.1
|
||||
Accept: application/json
|
||||
Content-Type: application/json
|
||||
Authorization: Bearer eyJrIjoiT0tTcG1pUlY2RnVKZTFVaDFsNFZXdE9ZWmNrMkZYbk
|
||||
```
|
||||
|
||||
**Example Response**:
|
||||
|
||||
```http
|
||||
HTTP/1.1 200
|
||||
Content-Type: application/json
|
||||
|
||||
```
|
||||
```
|
||||
|
||||
## Data source proxy calls
|
||||
|
||||
`GET /api/datasources/proxy/:datasourceId/*`
|
||||
|
||||
HTTP/1.1 200
|
||||
Content-Type: application/json
|
||||
Proxies all calls to the actual datasource.
|
||||
HTTP/1.1 200
|
||||
Content-Type: application/json
|
||||
|
||||
{"id":1,"message":"Datasource added", "name": "test_datasource"}
|
||||
{"id":1,"message":"Datasource added", "name": "test_datasource"}
|
||||
```
|
||||
|
||||
## Update an existing data source
|
||||
|
||||
@@ -188,34 +209,38 @@ parent = "http_api"
|
||||
|
||||
**Example Request**:
|
||||
|
||||
PUT /api/datasources/1 HTTP/1.1
|
||||
Accept: application/json
|
||||
Content-Type: application/json
|
||||
Authorization: Bearer eyJrIjoiT0tTcG1pUlY2RnVKZTFVaDFsNFZXdE9ZWmNrMkZYbk
|
||||
```http
|
||||
PUT /api/datasources/1 HTTP/1.1
|
||||
Accept: application/json
|
||||
Content-Type: application/json
|
||||
Authorization: Bearer eyJrIjoiT0tTcG1pUlY2RnVKZTFVaDFsNFZXdE9ZWmNrMkZYbk
|
||||
|
||||
{
|
||||
"id":1,
|
||||
"orgId":1,
|
||||
"name":"test_datasource",
|
||||
"type":"graphite",
|
||||
"access":"proxy",
|
||||
"url":"http://mydatasource.com",
|
||||
"password":"",
|
||||
"user":"",
|
||||
"database":"",
|
||||
"basicAuth":true,
|
||||
"basicAuthUser":"basicuser",
|
||||
"basicAuthPassword":"basicuser",
|
||||
"isDefault":false,
|
||||
"jsonData":null
|
||||
}
|
||||
{
|
||||
"id":1,
|
||||
"orgId":1,
|
||||
"name":"test_datasource",
|
||||
"type":"graphite",
|
||||
"access":"proxy",
|
||||
"url":"http://mydatasource.com",
|
||||
"password":"",
|
||||
"user":"",
|
||||
"database":"",
|
||||
"basicAuth":true,
|
||||
"basicAuthUser":"basicuser",
|
||||
"basicAuthPassword":"basicuser",
|
||||
"isDefault":false,
|
||||
"jsonData":null
|
||||
}
|
||||
```
|
||||
|
||||
**Example Response**:
|
||||
|
||||
HTTP/1.1 200
|
||||
Content-Type: application/json
|
||||
```http
|
||||
HTTP/1.1 200
|
||||
Content-Type: application/json
|
||||
|
||||
{"message":"Datasource updated", "id": 1, "name": "test_datasource"}
|
||||
{"message":"Datasource updated", "id": 1, "name": "test_datasource"}
|
||||
```
|
||||
|
||||
## Delete an existing data source by id
|
||||
|
||||
@@ -223,17 +248,21 @@ parent = "http_api"
|
||||
|
||||
**Example Request**:
|
||||
|
||||
DELETE /api/datasources/1 HTTP/1.1
|
||||
Accept: application/json
|
||||
Content-Type: application/json
|
||||
Authorization: Bearer eyJrIjoiT0tTcG1pUlY2RnVKZTFVaDFsNFZXdE9ZWmNrMkZYbk
|
||||
```http
|
||||
DELETE /api/datasources/1 HTTP/1.1
|
||||
Accept: application/json
|
||||
Content-Type: application/json
|
||||
Authorization: Bearer eyJrIjoiT0tTcG1pUlY2RnVKZTFVaDFsNFZXdE9ZWmNrMkZYbk
|
||||
```
|
||||
|
||||
**Example Response**:
|
||||
|
||||
HTTP/1.1 200
|
||||
Content-Type: application/json
|
||||
```http
|
||||
HTTP/1.1 200
|
||||
Content-Type: application/json
|
||||
|
||||
{"message":"Data source deleted"}
|
||||
{"message":"Data source deleted"}
|
||||
```
|
||||
|
||||
## Delete an existing data source by name
|
||||
|
||||
@@ -241,17 +270,21 @@ parent = "http_api"
|
||||
|
||||
**Example Request**:
|
||||
|
||||
DELETE /api/datasources/name/test_datasource HTTP/1.1
|
||||
Accept: application/json
|
||||
Content-Type: application/json
|
||||
Authorization: Bearer eyJrIjoiT0tTcG1pUlY2RnVKZTFVaDFsNFZXdE9ZWmNrMkZYbk
|
||||
```http
|
||||
DELETE /api/datasources/name/test_datasource HTTP/1.1
|
||||
Accept: application/json
|
||||
Content-Type: application/json
|
||||
Authorization: Bearer eyJrIjoiT0tTcG1pUlY2RnVKZTFVaDFsNFZXdE9ZWmNrMkZYbk
|
||||
```
|
||||
|
||||
**Example Response**:
|
||||
|
||||
HTTP/1.1 200
|
||||
Content-Type: application/json
|
||||
```http
|
||||
HTTP/1.1 200
|
||||
Content-Type: application/json
|
||||
|
||||
{"message":"Data source deleted"}
|
||||
{"message":"Data source deleted"}
|
||||
```
|
||||
|
||||
## Data source proxy calls
|
||||
|
||||
|
||||
@@ -18,20 +18,24 @@ parent = "http_api"
|
||||
|
||||
**Example Request**:
|
||||
|
||||
GET /api/org HTTP/1.1
|
||||
Accept: application/json
|
||||
Content-Type: application/json
|
||||
Authorization: Bearer eyJrIjoiT0tTcG1pUlY2RnVKZTFVaDFsNFZXdE9ZWmNrMkZYbk
|
||||
```http
|
||||
GET /api/org HTTP/1.1
|
||||
Accept: application/json
|
||||
Content-Type: application/json
|
||||
Authorization: Bearer eyJrIjoiT0tTcG1pUlY2RnVKZTFVaDFsNFZXdE9ZWmNrMkZYbk
|
||||
```
|
||||
|
||||
**Example Response**:
|
||||
|
||||
HTTP/1.1 200
|
||||
Content-Type: application/json
|
||||
```http
|
||||
HTTP/1.1 200
|
||||
Content-Type: application/json
|
||||
|
||||
{
|
||||
"id":1,
|
||||
"name":"Main Org."
|
||||
}
|
||||
```
|
||||
|
||||
## Get Organisation by Id
|
||||
|
||||
`GET /api/orgs/:orgId`
|
||||
|
||||
**Example Request**:
|
||||
|
||||
@@ -39,57 +43,64 @@ parent = "http_api"
|
||||
GET /api/orgs/1 HTTP/1.1
|
||||
Accept: application/json
|
||||
Content-Type: application/json
|
||||
GET /api/orgs/1 HTTP/1.1
|
||||
Accept: application/json
|
||||
Content-Type: application/json
|
||||
Authorization: Bearer eyJrIjoiT0tTcG1pUlY2RnVKZTFVaDFsNFZXdE9ZWmNrMkZYbk
|
||||
Authorization: Bearer eyJrIjoiT0tTcG1pUlY2RnVKZTFVaDFsNFZXdE9ZWmNrMkZYbk
|
||||
```
|
||||
|
||||
**Example Response**:
|
||||
|
||||
```http
|
||||
HTTP/1.1 200
|
||||
Content-Type: application/json
|
||||
|
||||
HTTP/1.1 200
|
||||
Content-Type: application/json
|
||||
|
||||
{
|
||||
"id":1,
|
||||
"name":"Main Org.",
|
||||
"address":{
|
||||
"address1":"",
|
||||
"address2":"",
|
||||
"city":"",
|
||||
"zipCode":"",
|
||||
"state":"",
|
||||
"country":""
|
||||
}
|
||||
}
|
||||
```
|
||||
## Get Organisation by Name
|
||||
|
||||
`GET /api/orgs/name/:orgName`
|
||||
|
||||
**Example Request**:
|
||||
|
||||
```http
|
||||
GET /api/orgs/name/Main%20Org%2E HTTP/1.1
|
||||
Accept: application/json
|
||||
Content-Type: application/json
|
||||
Authorization: Bearer eyJrIjoiT0tTcG1pUlY2RnVKZTFVaDFsNFZXdE9ZWmNrMkZYbk
|
||||
```
|
||||
|
||||
**Example Response**:
|
||||
|
||||
```http
|
||||
HTTP/1.1 200
|
||||
Content-Type: application/json
|
||||
|
||||
```
|
||||
|
||||
## Create Organisation
|
||||
GET /api/orgs/name/Main%20Org%2E HTTP/1.1
|
||||
Accept: application/json
|
||||
Content-Type: application/json
|
||||
Authorization: Bearer eyJrIjoiT0tTcG1pUlY2RnVKZTFVaDFsNFZXdE9ZWmNrMkZYbk
|
||||
|
||||
`POST /api/orgs`
|
||||
|
||||
**Example Request**:
|
||||
|
||||
```http
|
||||
POST /api/orgs HTTP/1.1
|
||||
Accept: application/json
|
||||
Content-Type: application/json
|
||||
HTTP/1.1 200
|
||||
Content-Type: application/json
|
||||
Authorization: Bearer eyJrIjoiT0tTcG1pUlY2RnVKZTFVaDFsNFZXdE9ZWmNrMkZYbk
|
||||
|
||||
```
|
||||
|
||||
{
|
||||
"id":1,
|
||||
"name":"Main Org.",
|
||||
"address":{
|
||||
"address1":"",
|
||||
"address2":"",
|
||||
"city":"",
|
||||
"zipCode":"",
|
||||
"state":"",
|
||||
"country":""
|
||||
}
|
||||
}
|
||||
**Example Response**:
|
||||
|
||||
```http
|
||||
HTTP/1.1 200
|
||||
Content-Type: application/json
|
||||
|
||||
```
|
||||
|
||||
|
||||
## Update current Organisation
|
||||
|
||||
`PUT /api/org`
|
||||
|
||||
**Example Request**:
|
||||
|
||||
```http
|
||||
@@ -97,26 +108,28 @@ parent = "http_api"
|
||||
Accept: application/json
|
||||
Content-Type: application/json
|
||||
Authorization: Bearer eyJrIjoiT0tTcG1pUlY2RnVKZTFVaDFsNFZXdE9ZWmNrMkZYbk
|
||||
POST /api/orgs HTTP/1.1
|
||||
Accept: application/json
|
||||
Content-Type: application/json
|
||||
Authorization: Bearer eyJrIjoiT0tTcG1pUlY2RnVKZTFVaDFsNFZXdE9ZWmNrMkZYbk
|
||||
|
||||
{
|
||||
"name":"New Org."
|
||||
}
|
||||
|
||||
```
|
||||
|
||||
**Example Response**:
|
||||
|
||||
```http
|
||||
HTTP/1.1 200
|
||||
Content-Type: application/json
|
||||
|
||||
```
|
||||
|
||||
## Get all users within the actual organisation
|
||||
|
||||
HTTP/1.1 200
|
||||
Content-Type: application/json
|
||||
|
||||
{
|
||||
"orgId":"1",
|
||||
"message":"Organization created"
|
||||
}
|
||||
`GET /api/org/users`
|
||||
|
||||
**Example Request**:
|
||||
|
||||
```http
|
||||
GET /api/org/users HTTP/1.1
|
||||
Accept: application/json
|
||||
Content-Type: application/json
|
||||
Authorization: Bearer eyJrIjoiT0tTcG1pUlY2RnVKZTFVaDFsNFZXdE9ZWmNrMkZYbk
|
||||
```
|
||||
|
||||
**Example Response**:
|
||||
@@ -125,23 +138,25 @@ parent = "http_api"
|
||||
HTTP/1.1 200
|
||||
Content-Type: application/json
|
||||
|
||||
PUT /api/org HTTP/1.1
|
||||
Accept: application/json
|
||||
Content-Type: application/json
|
||||
Authorization: Bearer eyJrIjoiT0tTcG1pUlY2RnVKZTFVaDFsNFZXdE9ZWmNrMkZYbk
|
||||
|
||||
{
|
||||
"name":"Main Org."
|
||||
}
|
||||
```
|
||||
|
||||
## Add a new user to the actual organisation
|
||||
|
||||
`POST /api/org/users`
|
||||
|
||||
Adds a global user to the actual organisation.
|
||||
|
||||
**Example Request**:
|
||||
|
||||
```http
|
||||
POST /api/org/users HTTP/1.1
|
||||
Accept: application/json
|
||||
HTTP/1.1 200
|
||||
Content-Type: application/json
|
||||
|
||||
{"message":"Organization updated"}
|
||||
Content-Type: application/json
|
||||
Authorization: Bearer eyJrIjoiT0tTcG1pUlY2RnVKZTFVaDFsNFZXdE9ZWmNrMkZYbk
|
||||
|
||||
```
|
||||
|
||||
**Example Response**:
|
||||
|
||||
```http
|
||||
HTTP/1.1 200
|
||||
@@ -149,25 +164,29 @@ parent = "http_api"
|
||||
|
||||
```
|
||||
|
||||
GET /api/org/users HTTP/1.1
|
||||
Accept: application/json
|
||||
Content-Type: application/json
|
||||
Authorization: Bearer eyJrIjoiT0tTcG1pUlY2RnVKZTFVaDFsNFZXdE9ZWmNrMkZYbk
|
||||
## Updates the given user
|
||||
|
||||
`PATCH /api/org/users/:userId`
|
||||
|
||||
**Example Request**:
|
||||
|
||||
```http
|
||||
PATCH /api/org/users/1 HTTP/1.1
|
||||
Accept: application/json
|
||||
HTTP/1.1 200
|
||||
Content-Type: application/json
|
||||
Content-Type: application/json
|
||||
Authorization: Bearer eyJrIjoiT0tTcG1pUlY2RnVKZTFVaDFsNFZXdE9ZWmNrMkZYbk
|
||||
|
||||
```
|
||||
[
|
||||
{
|
||||
"orgId":1,
|
||||
"userId":1,
|
||||
"email":"admin@mygraf.com",
|
||||
"login":"admin",
|
||||
"role":"Admin"
|
||||
}
|
||||
]
|
||||
|
||||
**Example Response**:
|
||||
|
||||
```http
|
||||
HTTP/1.1 200
|
||||
Content-Type: application/json
|
||||
|
||||
```
|
||||
|
||||
## Delete user in actual organisation
|
||||
|
||||
`DELETE /api/org/users/:userId`
|
||||
|
||||
@@ -177,23 +196,26 @@ Adds a global user to the actual organisation.
|
||||
DELETE /api/org/users/1 HTTP/1.1
|
||||
Accept: application/json
|
||||
Content-Type: application/json
|
||||
POST /api/org/users HTTP/1.1
|
||||
Accept: application/json
|
||||
Content-Type: application/json
|
||||
Authorization: Bearer eyJrIjoiT0tTcG1pUlY2RnVKZTFVaDFsNFZXdE9ZWmNrMkZYbk
|
||||
|
||||
{
|
||||
"role": "Admin",
|
||||
"loginOrEmail": "admin"
|
||||
}
|
||||
Authorization: Bearer eyJrIjoiT0tTcG1pUlY2RnVKZTFVaDFsNFZXdE9ZWmNrMkZYbk
|
||||
```
|
||||
|
||||
**Example Response**:
|
||||
|
||||
```http
|
||||
HTTP/1.1 200
|
||||
Content-Type: application/json
|
||||
|
||||
```
|
||||
|
||||
# Organisations
|
||||
|
||||
## Search all Organisations
|
||||
HTTP/1.1 200
|
||||
Content-Type: application/json
|
||||
|
||||
`GET /api/orgs`
|
||||
|
||||
**Example Request**:
|
||||
{"message":"User added to organization"}
|
||||
|
||||
```http
|
||||
GET /api/orgs HTTP/1.1
|
||||
Accept: application/json
|
||||
Content-Type: application/json
|
||||
@@ -201,23 +223,25 @@ Adds a global user to the actual organisation.
|
||||
```
|
||||
|
||||
**Example Response**:
|
||||
PATCH /api/org/users/1 HTTP/1.1
|
||||
Accept: application/json
|
||||
Content-Type: application/json
|
||||
Authorization: Bearer eyJrIjoiT0tTcG1pUlY2RnVKZTFVaDFsNFZXdE9ZWmNrMkZYbk
|
||||
|
||||
{
|
||||
"role": "Viewer",
|
||||
}
|
||||
|
||||
```http
|
||||
HTTP/1.1 200
|
||||
Content-Type: application/json
|
||||
|
||||
```
|
||||
|
||||
## Update Organisation
|
||||
|
||||
`PUT /api/orgs/:orgId`
|
||||
|
||||
Update Organisation, fields *Adress 1*, *Adress 2*, *City* are not implemented yet.
|
||||
|
||||
HTTP/1.1 200
|
||||
Content-Type: application/json
|
||||
|
||||
{"message":"Organization user updated"}
|
||||
**Example Request**:
|
||||
|
||||
```http
|
||||
PUT /api/orgs/1 HTTP/1.1
|
||||
Accept: application/json
|
||||
Content-Type: application/json
|
||||
Authorization: Bearer eyJrIjoiT0tTcG1pUlY2RnVKZTFVaDFsNFZXdE9ZWmNrMkZYbk
|
||||
|
||||
```
|
||||
@@ -225,18 +249,21 @@ Adds a global user to the actual organisation.
|
||||
**Example Response**:
|
||||
|
||||
```http
|
||||
DELETE /api/org/users/1 HTTP/1.1
|
||||
Accept: application/json
|
||||
Content-Type: application/json
|
||||
Authorization: Bearer eyJrIjoiT0tTcG1pUlY2RnVKZTFVaDFsNFZXdE9ZWmNrMkZYbk
|
||||
HTTP/1.1 200
|
||||
Content-Type: application/json
|
||||
|
||||
```
|
||||
|
||||
## Get Users in Organisation
|
||||
|
||||
`GET /api/orgs/:orgId/users`
|
||||
|
||||
HTTP/1.1 200
|
||||
Content-Type: application/json
|
||||
|
||||
{"message":"User removed from organization"}
|
||||
**Example Request**:
|
||||
|
||||
```http
|
||||
GET /api/orgs/1/users HTTP/1.1
|
||||
Accept: application/json
|
||||
Content-Type: application/json
|
||||
Authorization: Bearer eyJrIjoiT0tTcG1pUlY2RnVKZTFVaDFsNFZXdE9ZWmNrMkZYbk
|
||||
```
|
||||
|
||||
@@ -246,22 +273,26 @@ Adds a global user to the actual organisation.
|
||||
HTTP/1.1 200
|
||||
Content-Type: application/json
|
||||
[
|
||||
GET /api/orgs HTTP/1.1
|
||||
Accept: application/json
|
||||
Content-Type: application/json
|
||||
Authorization: Bearer eyJrIjoiT0tTcG1pUlY2RnVKZTFVaDFsNFZXdE9ZWmNrMkZYbk
|
||||
```
|
||||
|
||||
## Add User in Organisation
|
||||
|
||||
`POST /api/orgs/:orgId/users`
|
||||
|
||||
**Example Request**:
|
||||
|
||||
```http
|
||||
HTTP/1.1 200
|
||||
Content-Type: application/json
|
||||
POST /api/orgs/1/users HTTP/1.1
|
||||
Accept: application/json
|
||||
Content-Type: application/json
|
||||
Authorization: Bearer eyJrIjoiT0tTcG1pUlY2RnVKZTFVaDFsNFZXdE9ZWmNrMkZYbk
|
||||
[
|
||||
{
|
||||
"id":1,
|
||||
"name":"Main Org."
|
||||
}
|
||||
]
|
||||
|
||||
```
|
||||
|
||||
**Example Response**:
|
||||
|
||||
```http
|
||||
HTTP/1.1 200
|
||||
Content-Type: application/json
|
||||
|
||||
```
|
||||
@@ -271,22 +302,25 @@ Update Organisation, fields *Adress 1*, *Adress 2*, *City* are not implemented y
|
||||
`PATCH /api/orgs/:orgId/users/:userId`
|
||||
|
||||
**Example Request**:
|
||||
PUT /api/orgs/1 HTTP/1.1
|
||||
Accept: application/json
|
||||
Content-Type: application/json
|
||||
Authorization: Bearer eyJrIjoiT0tTcG1pUlY2RnVKZTFVaDFsNFZXdE9ZWmNrMkZYbk
|
||||
|
||||
{
|
||||
"name":"Main Org 2."
|
||||
}
|
||||
|
||||
```http
|
||||
PATCH /api/orgs/1/users/2 HTTP/1.1
|
||||
Accept: application/json
|
||||
Content-Type: application/json
|
||||
Authorization: Bearer eyJrIjoiT0tTcG1pUlY2RnVKZTFVaDFsNFZXdE9ZWmNrMkZYbk
|
||||
|
||||
```
|
||||
|
||||
**Example Response**:
|
||||
|
||||
```http
|
||||
HTTP/1.1 200
|
||||
HTTP/1.1 200
|
||||
Content-Type: application/json
|
||||
Content-Type: application/json
|
||||
|
||||
```
|
||||
|
||||
{"message":"Organization updated"}
|
||||
## Delete User in Organisation
|
||||
|
||||
`DELETE /api/orgs/:orgId/users/:userId`
|
||||
|
||||
**Example Request**:
|
||||
@@ -294,24 +328,28 @@ Update Organisation, fields *Adress 1*, *Adress 2*, *City* are not implemented y
|
||||
```http
|
||||
DELETE /api/orgs/1/users/2 HTTP/1.1
|
||||
Accept: application/json
|
||||
GET /api/orgs/1/users HTTP/1.1
|
||||
Accept: application/json
|
||||
Content-Type: application/json
|
||||
Authorization: Bearer eyJrIjoiT0tTcG1pUlY2RnVKZTFVaDFsNFZXdE9ZWmNrMkZYbk
|
||||
Content-Type: application/json
|
||||
Authorization: Bearer eyJrIjoiT0tTcG1pUlY2RnVKZTFVaDFsNFZXdE9ZWmNrMkZYbk
|
||||
```
|
||||
|
||||
**Example Response**:
|
||||
|
||||
```http
|
||||
HTTP/1.1 200
|
||||
Content-Type: application/json
|
||||
HTTP/1.1 200
|
||||
Content-Type: application/json
|
||||
[
|
||||
{
|
||||
"orgId":1,
|
||||
"userId":1,
|
||||
"email":"admin@mygraf.com",
|
||||
"login":"admin",
|
||||
"role":"Admin"
|
||||
}
|
||||
]
|
||||
|
||||
```
|
||||
Content-Type: application/json
|
||||
[
|
||||
{
|
||||
"orgId":1,
|
||||
"userId":1,
|
||||
"email":"admin@mygraf.com",
|
||||
"login":"admin",
|
||||
"role":"Admin"
|
||||
}
|
||||
]
|
||||
```
|
||||
|
||||
## Add User in Organisation
|
||||
|
||||
@@ -319,22 +357,26 @@ Update Organisation, fields *Adress 1*, *Adress 2*, *City* are not implemented y
|
||||
|
||||
**Example Request**:
|
||||
|
||||
POST /api/orgs/1/users HTTP/1.1
|
||||
Accept: application/json
|
||||
Content-Type: application/json
|
||||
Authorization: Bearer eyJrIjoiT0tTcG1pUlY2RnVKZTFVaDFsNFZXdE9ZWmNrMkZYbk
|
||||
```http
|
||||
POST /api/orgs/1/users HTTP/1.1
|
||||
Accept: application/json
|
||||
Content-Type: application/json
|
||||
Authorization: Bearer eyJrIjoiT0tTcG1pUlY2RnVKZTFVaDFsNFZXdE9ZWmNrMkZYbk
|
||||
|
||||
{
|
||||
"loginOrEmail":"user",
|
||||
"role":"Viewer"
|
||||
}
|
||||
{
|
||||
"loginOrEmail":"user",
|
||||
"role":"Viewer"
|
||||
}
|
||||
```
|
||||
|
||||
**Example Response**:
|
||||
|
||||
HTTP/1.1 200
|
||||
Content-Type: application/json
|
||||
```http
|
||||
HTTP/1.1 200
|
||||
Content-Type: application/json
|
||||
|
||||
{"message":"User added to organization"}
|
||||
{"message":"User added to organization"}
|
||||
```
|
||||
|
||||
## Update Users in Organisation
|
||||
|
||||
@@ -342,21 +384,25 @@ Update Organisation, fields *Adress 1*, *Adress 2*, *City* are not implemented y
|
||||
|
||||
**Example Request**:
|
||||
|
||||
PATCH /api/orgs/1/users/2 HTTP/1.1
|
||||
Accept: application/json
|
||||
Content-Type: application/json
|
||||
Authorization: Bearer eyJrIjoiT0tTcG1pUlY2RnVKZTFVaDFsNFZXdE9ZWmNrMkZYbk
|
||||
```http
|
||||
PATCH /api/orgs/1/users/2 HTTP/1.1
|
||||
Accept: application/json
|
||||
Content-Type: application/json
|
||||
Authorization: Bearer eyJrIjoiT0tTcG1pUlY2RnVKZTFVaDFsNFZXdE9ZWmNrMkZYbk
|
||||
|
||||
{
|
||||
"role":"Admin"
|
||||
}
|
||||
{
|
||||
"role":"Admin"
|
||||
}
|
||||
```
|
||||
|
||||
**Example Response**:
|
||||
|
||||
HTTP/1.1 200
|
||||
Content-Type: application/json
|
||||
```http
|
||||
HTTP/1.1 200
|
||||
Content-Type: application/json
|
||||
|
||||
{"message":"Organization user updated"}
|
||||
{"message":"Organization user updated"}
|
||||
```
|
||||
|
||||
## Delete User in Organisation
|
||||
|
||||
@@ -364,14 +410,18 @@ Update Organisation, fields *Adress 1*, *Adress 2*, *City* are not implemented y
|
||||
|
||||
**Example Request**:
|
||||
|
||||
DELETE /api/orgs/1/users/2 HTTP/1.1
|
||||
Accept: application/json
|
||||
Content-Type: application/json
|
||||
Authorization: Bearer eyJrIjoiT0tTcG1pUlY2RnVKZTFVaDFsNFZXdE9ZWmNrMkZYbk
|
||||
```http
|
||||
DELETE /api/orgs/1/users/2 HTTP/1.1
|
||||
Accept: application/json
|
||||
Content-Type: application/json
|
||||
Authorization: Bearer eyJrIjoiT0tTcG1pUlY2RnVKZTFVaDFsNFZXdE9ZWmNrMkZYbk
|
||||
```
|
||||
|
||||
**Example Response**:
|
||||
|
||||
HTTP/1.1 200
|
||||
Content-Type: application/json
|
||||
```http
|
||||
HTTP/1.1 200
|
||||
Content-Type: application/json
|
||||
|
||||
{"message":"User removed from organization"}
|
||||
{"message":"User removed from organization"}
|
||||
```
|
||||
@@ -18,43 +18,47 @@ parent = "http_api"
|
||||
|
||||
**Example Request**:
|
||||
|
||||
GET /api/frontend/settings HTTP/1.1
|
||||
Accept: application/json
|
||||
Content-Type: application/json
|
||||
Authorization: Bearer eyJrIjoiT0tTcG1pUlY2RnVKZTFVaDFsNFZXdE9ZWmNrMkZYbk
|
||||
```http
|
||||
GET /api/frontend/settings HTTP/1.1
|
||||
Accept: application/json
|
||||
Content-Type: application/json
|
||||
Authorization: Bearer eyJrIjoiT0tTcG1pUlY2RnVKZTFVaDFsNFZXdE9ZWmNrMkZYbk
|
||||
```
|
||||
|
||||
**Example Response**:
|
||||
|
||||
HTTP/1.1 200
|
||||
Content-Type: application/json
|
||||
```http
|
||||
HTTP/1.1 200
|
||||
Content-Type: application/json
|
||||
|
||||
{
|
||||
"allowOrgCreate":true,
|
||||
"appSubUrl":"",
|
||||
"buildInfo":{
|
||||
"buildstamp":xxxxxx,
|
||||
"commit":"vyyyy",
|
||||
"version":"zzzzz"
|
||||
},
|
||||
"datasources":{
|
||||
"datasourcename":{
|
||||
"index":"grafana-dash",
|
||||
"meta":{
|
||||
"annotations":true,
|
||||
"module":"plugins/datasource/grafana/datasource",
|
||||
"name":"Grafana",
|
||||
"partials":{
|
||||
"annotations":"app/plugins/datasource/grafana/partials/annotations.editor.html",
|
||||
"config":"app/plugins/datasource/grafana/partials/config.html"
|
||||
},
|
||||
"pluginType":"datasource",
|
||||
"serviceName":"Grafana",
|
||||
"type":"grafanasearch"
|
||||
}
|
||||
}
|
||||
},
|
||||
"defaultDatasource": "Grafana"
|
||||
```
|
||||
|
||||
# Login API
|
||||
|
||||
## Renew session based on remember cookie
|
||||
|
||||
`GET /api/login/ping`
|
||||
|
||||
**Example Request**:
|
||||
|
||||
```http
|
||||
GET /api/login/ping HTTP/1.1
|
||||
Accept: application/json
|
||||
Content-Type: application/json
|
||||
Authorization: Bearer eyJrIjoiT0tTcG1pUlY2RnVKZTFVaDFsNFZXdE9ZWmNrMkZYbk
|
||||
```
|
||||
|
||||
**Example Response**:
|
||||
|
||||
```http
|
||||
HTTP/1.1 200
|
||||
Content-Type: application/json
|
||||
|
||||
```
|
||||
},
|
||||
"defaultDatasource": "Grafana"
|
||||
}
|
||||
```
|
||||
|
||||
# Login API
|
||||
|
||||
@@ -64,14 +68,18 @@ parent = "http_api"
|
||||
|
||||
**Example Request**:
|
||||
|
||||
GET /api/login/ping HTTP/1.1
|
||||
Accept: application/json
|
||||
Content-Type: application/json
|
||||
Authorization: Bearer eyJrIjoiT0tTcG1pUlY2RnVKZTFVaDFsNFZXdE9ZWmNrMkZYbk
|
||||
```http
|
||||
GET /api/login/ping HTTP/1.1
|
||||
Accept: application/json
|
||||
Content-Type: application/json
|
||||
Authorization: Bearer eyJrIjoiT0tTcG1pUlY2RnVKZTFVaDFsNFZXdE9ZWmNrMkZYbk
|
||||
```
|
||||
|
||||
**Example Response**:
|
||||
|
||||
HTTP/1.1 200
|
||||
Content-Type: application/json
|
||||
```http
|
||||
HTTP/1.1 200
|
||||
Content-Type: application/json
|
||||
|
||||
{"message": "Logged in"}
|
||||
{"message": "Logged in"}
|
||||
```
|
||||
@@ -26,17 +26,21 @@ system default value.
|
||||
|
||||
**Example Request**:
|
||||
|
||||
GET /api/user/preferences HTTP/1.1
|
||||
Accept: application/json
|
||||
Content-Type: application/json
|
||||
Authorization: Bearer eyJrIjoiT0tTcG1pUlY2RnVKZTFVaDFsNFZXdE9ZWmNrMkZYbk
|
||||
```http
|
||||
GET /api/user/preferences HTTP/1.1
|
||||
Accept: application/json
|
||||
Content-Type: application/json
|
||||
Authorization: Bearer eyJrIjoiT0tTcG1pUlY2RnVKZTFVaDFsNFZXdE9ZWmNrMkZYbk
|
||||
```
|
||||
|
||||
**Example Response**:
|
||||
|
||||
HTTP/1.1 200
|
||||
Content-Type: application/json
|
||||
```http
|
||||
HTTP/1.1 200
|
||||
Content-Type: application/json
|
||||
|
||||
{"theme":"","homeDashboardId":0,"timezone":""}
|
||||
```
|
||||
|
||||
## Update Current User Prefs
|
||||
|
||||
`PUT /api/user/preferences`
|
||||
@@ -44,23 +48,27 @@ system default value.
|
||||
**Example Request**:
|
||||
|
||||
```http
|
||||
PUT /api/user/preferences HTTP/1.1
|
||||
Accept: application/json
|
||||
Content-Type: application/json
|
||||
Authorization: Bearer eyJrIjoiT0tTcG1pUlY2RnVKZTFVaDFsNFZXdE9ZWmNrMkZYbk
|
||||
PUT /api/user/preferences HTTP/1.1
|
||||
Accept: application/json
|
||||
Content-Type: application/json
|
||||
Authorization: Bearer eyJrIjoiT0tTcG1pUlY2RnVKZTFVaDFsNFZXdE9ZWmNrMkZYbk
|
||||
|
||||
```
|
||||
{
|
||||
"theme": "",
|
||||
"homeDashboardId":0,
|
||||
"timezone":"utc"
|
||||
}
|
||||
|
||||
**Example Response**:
|
||||
|
||||
```http
|
||||
HTTP/1.1 200
|
||||
Content-Type: text/plain; charset=utf-8
|
||||
|
||||
```
|
||||
|
||||
HTTP/1.1 200
|
||||
Content-Type: text/plain; charset=utf-8
|
||||
## Get Current Org Prefs
|
||||
|
||||
`GET /api/org/preferences`
|
||||
|
||||
{"message":"Preferences updated"}
|
||||
**Example Request**:
|
||||
|
||||
```http
|
||||
GET /api/org/preferences HTTP/1.1
|
||||
Accept: application/json
|
||||
@@ -68,17 +76,21 @@ system default value.
|
||||
Authorization: Bearer eyJrIjoiT0tTcG1pUlY2RnVKZTFVaDFsNFZXdE9ZWmNrMkZYbk
|
||||
```
|
||||
|
||||
GET /api/org/preferences HTTP/1.1
|
||||
Accept: application/json
|
||||
Content-Type: application/json
|
||||
Authorization: Bearer eyJrIjoiT0tTcG1pUlY2RnVKZTFVaDFsNFZXdE9ZWmNrMkZYbk
|
||||
**Example Response**:
|
||||
|
||||
```http
|
||||
HTTP/1.1 200
|
||||
Content-Type: application/json
|
||||
|
||||
```
|
||||
|
||||
## Update Current Org Prefs
|
||||
HTTP/1.1 200
|
||||
Content-Type: application/json
|
||||
|
||||
`PUT /api/org/preferences`
|
||||
|
||||
**Example Request**:
|
||||
{"theme":"","homeDashboardId":0,"timezone":""}
|
||||
|
||||
```http
|
||||
PUT /api/org/preferences HTTP/1.1
|
||||
Accept: application/json
|
||||
Content-Type: application/json
|
||||
@@ -86,20 +98,24 @@ system default value.
|
||||
|
||||
```
|
||||
|
||||
PUT /api/org/preferences HTTP/1.1
|
||||
Accept: application/json
|
||||
Content-Type: application/json
|
||||
Authorization: Bearer eyJrIjoiT0tTcG1pUlY2RnVKZTFVaDFsNFZXdE9ZWmNrMkZYbk
|
||||
**Example Response**:
|
||||
|
||||
```http
|
||||
HTTP/1.1 200
|
||||
Content-Type: text/plain; charset=utf-8
|
||||
|
||||
{
|
||||
"theme": "",
|
||||
"homeDashboardId":0,
|
||||
"timezone":"utc"
|
||||
}
|
||||
```
|
||||
"theme": "",
|
||||
"homeDashboardId":0,
|
||||
"timezone":"utc"
|
||||
}
|
||||
```
|
||||
|
||||
**Example Response**:
|
||||
|
||||
HTTP/1.1 200
|
||||
Content-Type: text/plain; charset=utf-8
|
||||
```http
|
||||
HTTP/1.1 200
|
||||
Content-Type: text/plain; charset=utf-8
|
||||
|
||||
{"message":"Preferences updated"}
|
||||
{"message":"Preferences updated"}
|
||||
```
|
||||
@@ -17,6 +17,7 @@ parent = "http_api"
|
||||
|
||||
**Example Request**:
|
||||
|
||||
```http
|
||||
POST /api/snapshots HTTP/1.1
|
||||
Accept: application/json
|
||||
Content-Type: application/json
|
||||
@@ -51,18 +52,20 @@ parent = "http_api"
|
||||
},
|
||||
"expires": 3600
|
||||
}
|
||||
```
|
||||
|
||||
JSON Body schema:
|
||||
|
||||
- **dashboard** – Required. The complete dashboard model.
|
||||
- **name** – Optional. snapshot name
|
||||
- **expires** - Optional. When the snapshot should expire in seconds. 3600 is 1 hour, 86400 is 1 day. Default is never to expire.
|
||||
- **expires** - Optional. When the snapshot should expire in seconds. 3600 is 1 hour, 86400 is 1 day. Default is never to expire.
|
||||
- **external** - Optional. Save the snapshot on an external server rather than locally. Default is `false`.
|
||||
- **key** - Optional. Define the unique key. Required if **external** is `true`.
|
||||
- **deleteKey** - Optional. Unique key used to delete the snapshot. It is different from the **key** so that only the creator can delete the snapshot. Required if **external** is `true`.
|
||||
|
||||
**Example Response**:
|
||||
|
||||
```http
|
||||
HTTP/1.1 200
|
||||
Content-Type: application/json
|
||||
{
|
||||
@@ -71,6 +74,7 @@ JSON Body schema:
|
||||
"key":"YYYYYYY",
|
||||
"url":"myurl/dashboard/snapshot/YYYYYYY"
|
||||
}
|
||||
```
|
||||
|
||||
Keys:
|
||||
|
||||
@@ -83,54 +87,58 @@ Keys:
|
||||
|
||||
**Example Request**:
|
||||
|
||||
GET /api/snapshots/YYYYYYY HTTP/1.1
|
||||
Accept: application/json
|
||||
Content-Type: application/json
|
||||
Authorization: Bearer eyJrIjoiT0tTcG1pUlY2RnVKZTFVaDFsNFZXdE9ZWmNrMkZYbk
|
||||
```http
|
||||
GET /api/snapshots/YYYYYYY HTTP/1.1
|
||||
Accept: application/json
|
||||
Content-Type: application/json
|
||||
Authorization: Bearer eyJrIjoiT0tTcG1pUlY2RnVKZTFVaDFsNFZXdE9ZWmNrMkZYbk
|
||||
```
|
||||
|
||||
**Example Response**:
|
||||
|
||||
HTTP/1.1 200
|
||||
Content-Type: application/json
|
||||
```http
|
||||
HTTP/1.1 200
|
||||
Content-Type: application/json
|
||||
|
||||
{
|
||||
"meta":{
|
||||
"isSnapshot":true,
|
||||
"type":"snapshot",
|
||||
"canSave":false,
|
||||
"canEdit":false,
|
||||
"canStar":false,
|
||||
"slug":"",
|
||||
"expires":"2200-13-32T25:23:23+02:00",
|
||||
"created":"2200-13-32T28:24:23+02:00"
|
||||
},
|
||||
"dashboard": {
|
||||
"editable":false,
|
||||
"hideControls":true,
|
||||
"nav":[
|
||||
{
|
||||
"enable":false,
|
||||
"type":"timepicker"
|
||||
}
|
||||
],
|
||||
"rows": [
|
||||
{
|
||||
```
|
||||
|
||||
## Delete Snapshot by Id
|
||||
|
||||
`GET /api/snapshots-delete/:key`
|
||||
|
||||
**Example Request**:
|
||||
|
||||
```http
|
||||
GET /api/snapshots/YYYYYYY HTTP/1.1
|
||||
Accept: application/json
|
||||
Content-Type: application/json
|
||||
Authorization: Bearer eyJrIjoiT0tTcG1pUlY2RnVKZTFVaDFsNFZXdE9ZWmNrMkZYbk
|
||||
```
|
||||
|
||||
**Example Response**:
|
||||
|
||||
```http
|
||||
HTTP/1.1 200
|
||||
Content-Type: application/json
|
||||
|
||||
```
|
||||
|
||||
}
|
||||
],
|
||||
"style":"dark",
|
||||
"tags":[],
|
||||
"templating":{
|
||||
"list":[
|
||||
]
|
||||
},
|
||||
"time":{
|
||||
},
|
||||
"timezone":"browser",
|
||||
"title":"Home",
|
||||
"version":5
|
||||
}
|
||||
}
|
||||
}
|
||||
],
|
||||
"style":"dark",
|
||||
"tags":[],
|
||||
"templating":{
|
||||
"list":[
|
||||
]
|
||||
},
|
||||
"time":{
|
||||
},
|
||||
"timezone":"browser",
|
||||
"title":"Home",
|
||||
"version":5
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
## Delete Snapshot by Id
|
||||
|
||||
@@ -138,14 +146,18 @@ Keys:
|
||||
|
||||
**Example Request**:
|
||||
|
||||
GET /api/snapshots/YYYYYYY HTTP/1.1
|
||||
Accept: application/json
|
||||
Content-Type: application/json
|
||||
Authorization: Bearer eyJrIjoiT0tTcG1pUlY2RnVKZTFVaDFsNFZXdE9ZWmNrMkZYbk
|
||||
```http
|
||||
GET /api/snapshots/YYYYYYY HTTP/1.1
|
||||
Accept: application/json
|
||||
Content-Type: application/json
|
||||
Authorization: Bearer eyJrIjoiT0tTcG1pUlY2RnVKZTFVaDFsNFZXdE9ZWmNrMkZYbk
|
||||
```
|
||||
|
||||
**Example Response**:
|
||||
|
||||
HTTP/1.1 200
|
||||
Content-Type: application/json
|
||||
```http
|
||||
HTTP/1.1 200
|
||||
Content-Type: application/json
|
||||
|
||||
{"message":"Snapshot deleted. It might take an hour before it's cleared from a CDN cache."}
|
||||
{"message":"Snapshot deleted. It might take an hour before it's cleared from a CDN cache."}
|
||||
```
|
||||
@@ -17,34 +17,38 @@ parent = "http_api"
|
||||
|
||||
**Example Request**:
|
||||
|
||||
GET /api/users HTTP/1.1
|
||||
Accept: application/json
|
||||
Content-Type: application/json
|
||||
Authorization: Basic YWRtaW46YWRtaW4=
|
||||
```http
|
||||
GET /api/users HTTP/1.1
|
||||
Accept: application/json
|
||||
Content-Type: application/json
|
||||
Authorization: Basic YWRtaW46YWRtaW4=
|
||||
```
|
||||
|
||||
Default value for the `perpage` parameter is `1000` and for the `page` parameter is `1`. Requires basic authentication and that the authenticated user is a Grafana Admin.
|
||||
|
||||
**Example Response**:
|
||||
|
||||
HTTP/1.1 200
|
||||
Content-Type: application/json
|
||||
```http
|
||||
HTTP/1.1 200
|
||||
Content-Type: application/json
|
||||
|
||||
[
|
||||
{
|
||||
"id": 1,
|
||||
"name": "Admin",
|
||||
"login": "admin",
|
||||
"email": "admin@mygraf.com",
|
||||
"isAdmin": true
|
||||
},
|
||||
{
|
||||
"id": 2,
|
||||
"name": "User",
|
||||
"login": "user",
|
||||
"email": "user@mygraf.com",
|
||||
"isAdmin": false
|
||||
}
|
||||
]
|
||||
```
|
||||
|
||||
## Search Users with Paging
|
||||
|
||||
`GET /api/users/search?perpage=10&page=1&query=mygraf`
|
||||
|
||||
**Example Request**:
|
||||
|
||||
```http
|
||||
GET /api/users/search?perpage=10&page=1&query=mygraf HTTP/1.1
|
||||
Accept: application/json
|
||||
Content-Type: application/json
|
||||
Authorization: Basic YWRtaW46YWRtaW4=
|
||||
```
|
||||
|
||||
Default value for the `perpage` parameter is `1000` and for the `page` parameter is `1`. The `totalCount` field in the response can be used for pagination of the user list E.g. if `totalCount` is equal to 100 users and the `perpage` parameter is set to 10 then there are 10 pages of users. The `query` parameter is optional and it will return results where the query value is contained in one of the `name`, `login` or `email` fields. Query values with spaces need to be url encoded e.g. `query=Jane%20Doe`.
|
||||
|
||||
Requires basic authentication and that the authenticated user is a Grafana Admin.
|
||||
|
||||
**Example Response**:
|
||||
@@ -52,10 +56,12 @@ Default value for the `perpage` parameter is `1000` and for the `page` parameter
|
||||
```http
|
||||
HTTP/1.1 200
|
||||
Content-Type: application/json
|
||||
GET /api/users/search?perpage=10&page=1&query=mygraf HTTP/1.1
|
||||
Accept: application/json
|
||||
Content-Type: application/json
|
||||
Authorization: Basic YWRtaW46YWRtaW4=
|
||||
{
|
||||
```
|
||||
|
||||
## Get single user by Id
|
||||
|
||||
`GET /api/users/:id`
|
||||
|
||||
**Example Request**:
|
||||
|
||||
@@ -63,29 +69,31 @@ Requires basic authentication and that the authenticated user is a Grafana Admin
|
||||
GET /api/users/1 HTTP/1.1
|
||||
Accept: application/json
|
||||
Content-Type: application/json
|
||||
HTTP/1.1 200
|
||||
Content-Type: application/json
|
||||
Authorization: Basic YWRtaW46YWRtaW4=
|
||||
```
|
||||
Requires basic authentication and that the authenticated user is a Grafana Admin.
|
||||
|
||||
**Example Response**:
|
||||
|
||||
```http
|
||||
"totalCount": 2,
|
||||
"users": [
|
||||
{
|
||||
"id": 1,
|
||||
"name": "Admin",
|
||||
"login": "admin",
|
||||
"email": "admin@mygraf.com",
|
||||
"isAdmin": true
|
||||
},
|
||||
{
|
||||
"id": 2,
|
||||
"name": "User",
|
||||
"login": "user",
|
||||
"email": "user@mygraf.com",
|
||||
"isAdmin": false
|
||||
}
|
||||
],
|
||||
"page": 1,
|
||||
"perPage": 10
|
||||
HTTP/1.1 200
|
||||
Content-Type: application/json
|
||||
|
||||
```
|
||||
|
||||
## Get single user by Username(login) or Email
|
||||
|
||||
`GET /api/users/lookup?loginOrEmail=user@mygraf.com`
|
||||
|
||||
**Example Request using the email as option**:
|
||||
|
||||
```http
|
||||
GET /api/users/lookup?loginOrEmail=user@mygraf.com HTTP/1.1
|
||||
Accept: application/json
|
||||
Content-Type: application/json
|
||||
Authorization: Bearer eyJrIjoiT0tTcG1pUlY2RnVKZTFVaDFsNFZXdE9ZWmNrMkZYbk
|
||||
```
|
||||
|
||||
**Example Request using the username as option**:
|
||||
|
||||
```http
|
||||
@@ -93,26 +101,29 @@ Requires basic authentication and that the authenticated user is a Grafana Admin
|
||||
Accept: application/json
|
||||
Content-Type: application/json
|
||||
Authorization: Basic YWRtaW46YWRtaW4=
|
||||
GET /api/users/1 HTTP/1.1
|
||||
Accept: application/json
|
||||
Content-Type: application/json
|
||||
Authorization: Basic YWRtaW46YWRtaW4=
|
||||
|
||||
```
|
||||
|
||||
Requires basic authentication and that the authenticated user is a Grafana Admin.
|
||||
|
||||
**Example Response**:
|
||||
|
||||
```http
|
||||
HTTP/1.1 200
|
||||
Content-Type: application/json
|
||||
|
||||
HTTP/1.1 200
|
||||
Content-Type: application/json
|
||||
```
|
||||
|
||||
## User Update
|
||||
|
||||
{
|
||||
"email": "user@mygraf.com"
|
||||
"name": "admin",
|
||||
"login": "admin",
|
||||
"theme": "light",
|
||||
"orgId": 1,
|
||||
"isGrafanaAdmin": true
|
||||
}
|
||||
`PUT /api/users/:id`
|
||||
|
||||
**Example Request**:
|
||||
|
||||
```http
|
||||
PUT /api/users/2 HTTP/1.1
|
||||
Accept: application/json
|
||||
Content-Type: application/json
|
||||
Authorization: Basic YWRtaW46YWRtaW4=
|
||||
|
||||
```
|
||||
|
||||
@@ -120,34 +131,39 @@ Requires basic authentication and that the authenticated user is a Grafana Admin
|
||||
|
||||
**Example Response**:
|
||||
|
||||
GET /api/users/lookup?loginOrEmail=user@mygraf.com HTTP/1.1
|
||||
Accept: application/json
|
||||
Content-Type: application/json
|
||||
Authorization: Bearer eyJrIjoiT0tTcG1pUlY2RnVKZTFVaDFsNFZXdE9ZWmNrMkZYbk
|
||||
```http
|
||||
HTTP/1.1 200
|
||||
Content-Type: application/json
|
||||
|
||||
```
|
||||
|
||||
## Get Organisations for user
|
||||
|
||||
|
||||
GET /api/users/lookup?loginOrEmail=admin HTTP/1.1
|
||||
Accept: application/json
|
||||
Content-Type: application/json
|
||||
Authorization: Basic YWRtaW46YWRtaW4=
|
||||
`GET /api/users/:id/orgs`
|
||||
|
||||
**Example Request**:
|
||||
|
||||
```http
|
||||
GET /api/users/1/orgs HTTP/1.1
|
||||
Accept: application/json
|
||||
Content-Type: application/json
|
||||
Authorization: Basic YWRtaW46YWRtaW4=
|
||||
```
|
||||
|
||||
Requires basic authentication and that the authenticated user is a Grafana Admin.
|
||||
HTTP/1.1 200
|
||||
Content-Type: application/json
|
||||
|
||||
{
|
||||
"email": "user@mygraf.com"
|
||||
"name": "admin",
|
||||
"login": "admin",
|
||||
"theme": "light",
|
||||
"orgId": 1,
|
||||
"isGrafanaAdmin": true
|
||||
}
|
||||
|
||||
**Example Response**:
|
||||
|
||||
```http
|
||||
HTTP/1.1 200
|
||||
Content-Type: application/json
|
||||
|
||||
```
|
||||
|
||||
## User
|
||||
|
||||
## Actual User
|
||||
|
||||
`GET /api/user`
|
||||
|
||||
**Example Request**:
|
||||
@@ -155,27 +171,30 @@ Requires basic authentication and that the authenticated user is a Grafana Admin
|
||||
```http
|
||||
GET /api/user HTTP/1.1
|
||||
Accept: application/json
|
||||
PUT /api/users/2 HTTP/1.1
|
||||
Accept: application/json
|
||||
Content-Type: application/json
|
||||
Authorization: Basic YWRtaW46YWRtaW4=
|
||||
Content-Type: application/json
|
||||
Authorization: Bearer eyJrIjoiT0tTcG1pUlY2RnVKZTFVaDFsNFZXdE9ZWmNrMkZYbk
|
||||
```
|
||||
|
||||
**Example Response**:
|
||||
|
||||
{
|
||||
"email":"user@mygraf.com",
|
||||
"name":"User2",
|
||||
"login":"user",
|
||||
"theme":"light"
|
||||
}
|
||||
```http
|
||||
HTTP/1.1 200
|
||||
Content-Type: application/json
|
||||
|
||||
```
|
||||
|
||||
## Change Password
|
||||
|
||||
`PUT /api/user/password`
|
||||
|
||||
Changes the password for the user
|
||||
|
||||
HTTP/1.1 200
|
||||
Content-Type: application/json
|
||||
|
||||
{"message":"User updated"}
|
||||
**Example Request**:
|
||||
|
||||
```http
|
||||
PUT /api/user/password HTTP/1.1
|
||||
Accept: application/json
|
||||
Content-Type: application/json
|
||||
Authorization: Bearer eyJrIjoiT0tTcG1pUlY2RnVKZTFVaDFsNFZXdE9ZWmNrMkZYbk
|
||||
|
||||
```
|
||||
@@ -183,25 +202,29 @@ Requires basic authentication and that the authenticated user is a Grafana Admin
|
||||
**Example Response**:
|
||||
|
||||
```http
|
||||
GET /api/users/1/orgs HTTP/1.1
|
||||
Accept: application/json
|
||||
Content-Type: application/json
|
||||
Authorization: Basic YWRtaW46YWRtaW4=
|
||||
HTTP/1.1 200
|
||||
Content-Type: application/json
|
||||
|
||||
```
|
||||
|
||||
## Switch user context for a specified user
|
||||
|
||||
`POST /api/users/:userId/using/:organizationId`
|
||||
|
||||
Switch user context to the given organization. Requires basic authentication and that the authenticated user is a Grafana Admin.
|
||||
|
||||
HTTP/1.1 200
|
||||
Content-Type: application/json
|
||||
**Example Request**:
|
||||
|
||||
```http
|
||||
POST /api/users/7/using/2 HTTP/1.1
|
||||
[
|
||||
{
|
||||
"orgId":1,
|
||||
"name":"Main Org.",
|
||||
"role":"Admin"
|
||||
}
|
||||
]
|
||||
Authorization: Basic YWRtaW46YWRtaW4=
|
||||
```
|
||||
|
||||
**Example Response**:
|
||||
|
||||
```http
|
||||
HTTP/1.1 200
|
||||
Content-Type: application/json
|
||||
|
||||
```
|
||||
|
||||
@@ -211,24 +234,28 @@ Requires basic authentication and that the authenticated user is a Grafana Admin
|
||||
|
||||
Switch user context to the given organization.
|
||||
|
||||
GET /api/user HTTP/1.1
|
||||
Accept: application/json
|
||||
Content-Type: application/json
|
||||
Authorization: Bearer eyJrIjoiT0tTcG1pUlY2RnVKZTFVaDFsNFZXdE9ZWmNrMkZYbk
|
||||
**Example Request**:
|
||||
|
||||
```http
|
||||
POST /api/user/using/2 HTTP/1.1
|
||||
Accept: application/json
|
||||
Content-Type: application/json
|
||||
Authorization: Bearer eyJrIjoiT0tTcG1pUlY2RnVKZTFVaDFsNFZXdE9ZWmNrMkZYbk
|
||||
```
|
||||
|
||||
HTTP/1.1 200
|
||||
Content-Type: application/json
|
||||
**Example Response**:
|
||||
|
||||
```http
|
||||
HTTP/1.1 200
|
||||
{
|
||||
"email":"admin@mygraf.com",
|
||||
"name":"Admin",
|
||||
"login":"admin",
|
||||
"theme":"light",
|
||||
"orgId":1,
|
||||
"isGrafanaAdmin":true
|
||||
}
|
||||
Content-Type: application/json
|
||||
|
||||
```
|
||||
|
||||
## Organisations of the actual User
|
||||
|
||||
`GET /api/user/orgs`
|
||||
|
||||
Return a list of all organisations of the current user.
|
||||
|
||||
**Example Request**:
|
||||
|
||||
@@ -238,23 +265,27 @@ Changes the password for the user
|
||||
Content-Type: application/json
|
||||
Authorization: Bearer eyJrIjoiT0tTcG1pUlY2RnVKZTFVaDFsNFZXdE9ZWmNrMkZYbk
|
||||
```
|
||||
PUT /api/user/password HTTP/1.1
|
||||
Accept: application/json
|
||||
Content-Type: application/json
|
||||
Authorization: Bearer eyJrIjoiT0tTcG1pUlY2RnVKZTFVaDFsNFZXdE9ZWmNrMkZYbk
|
||||
|
||||
**Example Response**:
|
||||
|
||||
```http
|
||||
HTTP/1.1 200
|
||||
Content-Type: application/json
|
||||
{
|
||||
"oldPassword": "old_password",
|
||||
"newPassword": "new_password",
|
||||
"confirmNew": "confirm_new_password"
|
||||
}
|
||||
|
||||
```
|
||||
|
||||
## Star a dashboard
|
||||
|
||||
`POST /api/user/stars/dashboard/:dashboardId`
|
||||
|
||||
Stars the given Dashboard for the actual user.
|
||||
|
||||
HTTP/1.1 200
|
||||
Content-Type: application/json
|
||||
**Example Request**:
|
||||
|
||||
```http
|
||||
POST /api/user/stars/dashboard/1 HTTP/1.1
|
||||
{"message":"User password changed"}
|
||||
Accept: application/json
|
||||
Content-Type: application/json
|
||||
Authorization: Bearer eyJrIjoiT0tTcG1pUlY2RnVKZTFVaDFsNFZXdE9ZWmNrMkZYbk
|
||||
```
|
||||
|
||||
@@ -264,15 +295,19 @@ Switch user context to the given organization. Requires basic authentication and
|
||||
HTTP/1.1 200
|
||||
Content-Type: application/json
|
||||
|
||||
POST /api/users/7/using/2 HTTP/1.1
|
||||
Authorization: Basic YWRtaW46YWRtaW4=
|
||||
```
|
||||
|
||||
## Unstar a dashboard
|
||||
|
||||
`DELETE /api/user/stars/dashboard/:dashboardId`
|
||||
|
||||
Deletes the starring of the given Dashboard for the actual user.
|
||||
HTTP/1.1 200
|
||||
Content-Type: application/json
|
||||
|
||||
**Example Request**:
|
||||
|
||||
```http
|
||||
{"message":"Active organization changed"}
|
||||
DELETE /api/user/stars/dashboard/1 HTTP/1.1
|
||||
Accept: application/json
|
||||
Content-Type: application/json
|
||||
Authorization: Bearer eyJrIjoiT0tTcG1pUlY2RnVKZTFVaDFsNFZXdE9ZWmNrMkZYbk
|
||||
```
|
||||
@@ -282,17 +317,21 @@ Switch user context to the given organization.
|
||||
```http
|
||||
HTTP/1.1 200
|
||||
Content-Type: application/json
|
||||
POST /api/user/using/2 HTTP/1.1
|
||||
Accept: application/json
|
||||
Content-Type: application/json
|
||||
Authorization: Bearer eyJrIjoiT0tTcG1pUlY2RnVKZTFVaDFsNFZXdE9ZWmNrMkZYbk
|
||||
|
||||
```
|
||||
Accept: application/json
|
||||
Content-Type: application/json
|
||||
Authorization: Bearer eyJrIjoiT0tTcG1pUlY2RnVKZTFVaDFsNFZXdE9ZWmNrMkZYbk
|
||||
```
|
||||
|
||||
**Example Response**:
|
||||
|
||||
HTTP/1.1 200
|
||||
Content-Type: application/json
|
||||
```http
|
||||
HTTP/1.1 200
|
||||
Content-Type: application/json
|
||||
|
||||
{"message":"Active organization changed"}
|
||||
{"message":"Active organization changed"}
|
||||
```
|
||||
|
||||
## Organisations of the actual User
|
||||
|
||||
@@ -302,23 +341,27 @@ Return a list of all organisations of the current user.
|
||||
|
||||
**Example Request**:
|
||||
|
||||
GET /api/user/orgs HTTP/1.1
|
||||
Accept: application/json
|
||||
Content-Type: application/json
|
||||
Authorization: Bearer eyJrIjoiT0tTcG1pUlY2RnVKZTFVaDFsNFZXdE9ZWmNrMkZYbk
|
||||
```http
|
||||
GET /api/user/orgs HTTP/1.1
|
||||
Accept: application/json
|
||||
Content-Type: application/json
|
||||
Authorization: Bearer eyJrIjoiT0tTcG1pUlY2RnVKZTFVaDFsNFZXdE9ZWmNrMkZYbk
|
||||
```
|
||||
|
||||
**Example Response**:
|
||||
|
||||
HTTP/1.1 200
|
||||
Content-Type: application/json
|
||||
```http
|
||||
HTTP/1.1 200
|
||||
Content-Type: application/json
|
||||
|
||||
[
|
||||
{
|
||||
"orgId":1,
|
||||
"name":"Main Org.",
|
||||
"role":"Admin"
|
||||
}
|
||||
]
|
||||
[
|
||||
{
|
||||
"orgId":1,
|
||||
"name":"Main Org.",
|
||||
"role":"Admin"
|
||||
}
|
||||
]
|
||||
```
|
||||
|
||||
## Star a dashboard
|
||||
|
||||
@@ -328,17 +371,21 @@ Stars the given Dashboard for the actual user.
|
||||
|
||||
**Example Request**:
|
||||
|
||||
POST /api/user/stars/dashboard/1 HTTP/1.1
|
||||
Accept: application/json
|
||||
Content-Type: application/json
|
||||
Authorization: Bearer eyJrIjoiT0tTcG1pUlY2RnVKZTFVaDFsNFZXdE9ZWmNrMkZYbk
|
||||
```http
|
||||
POST /api/user/stars/dashboard/1 HTTP/1.1
|
||||
Accept: application/json
|
||||
Content-Type: application/json
|
||||
Authorization: Bearer eyJrIjoiT0tTcG1pUlY2RnVKZTFVaDFsNFZXdE9ZWmNrMkZYbk
|
||||
```
|
||||
|
||||
**Example Response**:
|
||||
|
||||
HTTP/1.1 200
|
||||
Content-Type: application/json
|
||||
```http
|
||||
HTTP/1.1 200
|
||||
Content-Type: application/json
|
||||
|
||||
{"message":"Dashboard starred!"}
|
||||
{"message":"Dashboard starred!"}
|
||||
```
|
||||
|
||||
## Unstar a dashboard
|
||||
|
||||
@@ -348,14 +395,18 @@ Deletes the starring of the given Dashboard for the actual user.
|
||||
|
||||
**Example Request**:
|
||||
|
||||
DELETE /api/user/stars/dashboard/1 HTTP/1.1
|
||||
Accept: application/json
|
||||
Content-Type: application/json
|
||||
Authorization: Bearer eyJrIjoiT0tTcG1pUlY2RnVKZTFVaDFsNFZXdE9ZWmNrMkZYbk
|
||||
```http
|
||||
DELETE /api/user/stars/dashboard/1 HTTP/1.1
|
||||
Accept: application/json
|
||||
Content-Type: application/json
|
||||
Authorization: Bearer eyJrIjoiT0tTcG1pUlY2RnVKZTFVaDFsNFZXdE9ZWmNrMkZYbk
|
||||
```
|
||||
|
||||
**Example Response**:
|
||||
|
||||
HTTP/1.1 200
|
||||
Content-Type: application/json
|
||||
```http
|
||||
HTTP/1.1 200
|
||||
Content-Type: application/json
|
||||
|
||||
{"message":"Dashboard unstarred"}
|
||||
{"message":"Dashboard unstarred"}
|
||||
```
|
||||
@@ -15,7 +15,7 @@ weight = 1
|
||||
It should be straight forward to get Grafana up and running behind a reverse proxy. But here are some things that you might run into.
|
||||
|
||||
Links and redirects will not be rendered correctly unless you set the server.domain setting.
|
||||
```
|
||||
```bash
|
||||
[server]
|
||||
domain = foo.bar
|
||||
```
|
||||
@@ -28,14 +28,14 @@ Here are some example configurations for running Grafana behind a reverse proxy.
|
||||
|
||||
### Grafana configuration (ex http://foo.bar.com)
|
||||
|
||||
```
|
||||
```bash
|
||||
[server]
|
||||
domain = foo.bar
|
||||
```
|
||||
|
||||
### Nginx configuration
|
||||
|
||||
```
|
||||
```bash
|
||||
server {
|
||||
listen 80;
|
||||
root /usr/share/nginx/www;
|
||||
@@ -50,14 +50,14 @@ server {
|
||||
### Examples with **sub path** (ex http://foo.bar.com/grafana)
|
||||
|
||||
#### Grafana configuration with sub path
|
||||
```
|
||||
```bash
|
||||
[server]
|
||||
domain = foo.bar
|
||||
root_url = %(protocol)s://%(domain)s:/grafana
|
||||
```
|
||||
|
||||
#### Nginx configuration with sub path
|
||||
```
|
||||
```bash
|
||||
server {
|
||||
listen 80;
|
||||
root /usr/share/nginx/www;
|
||||
|
||||
@@ -37,26 +37,31 @@ A common problem is forgetting to uncomment a line in the `custom.ini` (or `graf
|
||||
All options in the configuration file (listed below) can be overridden
|
||||
using environment variables using the syntax:
|
||||
|
||||
GF_<SectionName>_<KeyName>
|
||||
```bash
|
||||
GF_<SectionName>_<KeyName>
|
||||
```
|
||||
|
||||
Where the section name is the text within the brackets. Everything
|
||||
should be upper case, `.` should be replaced by `_`. For example, given these configuration settings:
|
||||
|
||||
# default section
|
||||
instance_name = ${HOSTNAME}
|
||||
```bash
|
||||
# default section
|
||||
instance_name = ${HOSTNAME}
|
||||
|
||||
[security]
|
||||
admin_user = admin
|
||||
|
||||
[auth.google]
|
||||
client_secret = 0ldS3cretKey
|
||||
[security]
|
||||
admin_user = admin
|
||||
|
||||
[auth.google]
|
||||
client_secret = 0ldS3cretKey
|
||||
```
|
||||
|
||||
Then you can override them using:
|
||||
|
||||
export GF_DEFAULT_INSTANCE_NAME=my-instance
|
||||
export GF_SECURITY_ADMIN_USER=true
|
||||
export GF_AUTH_GOOGLE_CLIENT_SECRET=newS3cretKey
|
||||
```bash
|
||||
export GF_DEFAULT_INSTANCE_NAME=my-instance
|
||||
export GF_SECURITY_ADMIN_USER=true
|
||||
export GF_AUTH_GOOGLE_CLIENT_SECRET=newS3cretKey
|
||||
```
|
||||
|
||||
<hr />
|
||||
|
||||
@@ -93,11 +98,15 @@ The IP address to bind to. If empty will bind to all interfaces
|
||||
The port to bind to, defaults to `3000`. To use port 80 you need to
|
||||
either give the Grafana binary permission for example:
|
||||
|
||||
$ sudo setcap 'cap_net_bind_service=+ep' /usr/sbin/grafana-server
|
||||
```bash
|
||||
$ sudo setcap 'cap_net_bind_service=+ep' /usr/sbin/grafana-server
|
||||
```
|
||||
|
||||
Or redirect port 80 to the Grafana port using:
|
||||
|
||||
$ sudo iptables -t nat -A PREROUTING -p tcp --dport 80 -j REDIRECT --to-port 3000
|
||||
```bash
|
||||
$ sudo iptables -t nat -A PREROUTING -p tcp --dport 80 -j REDIRECT --to-port 3000
|
||||
```
|
||||
|
||||
Another way is put a webserver like Nginx or Apache in front of Grafana and have them proxy requests to Grafana.
|
||||
|
||||
@@ -308,29 +317,33 @@ options are `Editor` and `Admin`.
|
||||
|
||||
## [auth.github]
|
||||
|
||||
You need to create a GitHub application (you find this under the GitHub
|
||||
profile page). When you create the application you will need to specify
|
||||
You need to create a GitHub OAuth application (you find this under the GitHub
|
||||
settings page). When you create the application you will need to specify
|
||||
a callback URL. Specify this as callback:
|
||||
|
||||
http://<my_grafana_server_name_or_ip>:<grafana_server_port>/login/github
|
||||
```bash
|
||||
http://<my_grafana_server_name_or_ip>:<grafana_server_port>/login/github
|
||||
```
|
||||
|
||||
This callback URL must match the full HTTP address that you use in your
|
||||
browser to access Grafana, but with the prefix path of `/login/github`.
|
||||
When the GitHub application is created you will get a Client ID and a
|
||||
When the GitHub OAuth application is created you will get a Client ID and a
|
||||
Client Secret. Specify these in the Grafana configuration file. For
|
||||
example:
|
||||
|
||||
[auth.github]
|
||||
enabled = true
|
||||
allow_sign_up = true
|
||||
client_id = YOUR_GITHUB_APP_CLIENT_ID
|
||||
client_secret = YOUR_GITHUB_APP_CLIENT_SECRET
|
||||
scopes = user:email
|
||||
auth_url = https://github.com/login/oauth/authorize
|
||||
token_url = https://github.com/login/oauth/access_token
|
||||
api_url = https://api.github.com/user
|
||||
team_ids =
|
||||
allowed_organizations =
|
||||
```bash
|
||||
[auth.github]
|
||||
enabled = true
|
||||
allow_sign_up = true
|
||||
client_id = YOUR_GITHUB_APP_CLIENT_ID
|
||||
client_secret = YOUR_GITHUB_APP_CLIENT_SECRET
|
||||
scopes = user:email
|
||||
auth_url = https://github.com/login/oauth/authorize
|
||||
token_url = https://github.com/login/oauth/access_token
|
||||
api_url = https://api.github.com/user
|
||||
team_ids =
|
||||
allowed_organizations =
|
||||
```
|
||||
|
||||
Restart the Grafana back-end. You should now see a GitHub login button
|
||||
on the login page. You can now login or sign up with your GitHub
|
||||
@@ -348,15 +361,17 @@ GitHub. If the authenticated user isn't a member of at least one of the
|
||||
teams they will not be able to register or authenticate with your
|
||||
Grafana instance. For example:
|
||||
|
||||
[auth.github]
|
||||
enabled = true
|
||||
client_id = YOUR_GITHUB_APP_CLIENT_ID
|
||||
client_secret = YOUR_GITHUB_APP_CLIENT_SECRET
|
||||
scopes = user:email,read:org
|
||||
team_ids = 150,300
|
||||
auth_url = https://github.com/login/oauth/authorize
|
||||
token_url = https://github.com/login/oauth/access_token
|
||||
allow_sign_up = true
|
||||
```bash
|
||||
[auth.github]
|
||||
enabled = true
|
||||
client_id = YOUR_GITHUB_APP_CLIENT_ID
|
||||
client_secret = YOUR_GITHUB_APP_CLIENT_SECRET
|
||||
scopes = user:email,read:org
|
||||
team_ids = 150,300
|
||||
auth_url = https://github.com/login/oauth/authorize
|
||||
token_url = https://github.com/login/oauth/access_token
|
||||
allow_sign_up = true
|
||||
```
|
||||
|
||||
### allowed_organizations
|
||||
|
||||
@@ -365,16 +380,18 @@ organizations on GitHub. If the authenticated user isn't a member of at least
|
||||
one of the organizations they will not be able to register or authenticate with
|
||||
your Grafana instance. For example
|
||||
|
||||
[auth.github]
|
||||
enabled = true
|
||||
client_id = YOUR_GITHUB_APP_CLIENT_ID
|
||||
client_secret = YOUR_GITHUB_APP_CLIENT_SECRET
|
||||
scopes = user:email,read:org
|
||||
auth_url = https://github.com/login/oauth/authorize
|
||||
token_url = https://github.com/login/oauth/access_token
|
||||
allow_sign_up = true
|
||||
# space-delimited organization names
|
||||
allowed_organizations = github google
|
||||
```bash
|
||||
[auth.github]
|
||||
enabled = true
|
||||
client_id = YOUR_GITHUB_APP_CLIENT_ID
|
||||
client_secret = YOUR_GITHUB_APP_CLIENT_SECRET
|
||||
scopes = user:email,read:org
|
||||
auth_url = https://github.com/login/oauth/authorize
|
||||
token_url = https://github.com/login/oauth/access_token
|
||||
allow_sign_up = true
|
||||
# space-delimited organization names
|
||||
allowed_organizations = github google
|
||||
```
|
||||
|
||||
<hr>
|
||||
|
||||
@@ -385,22 +402,26 @@ Developer Console](https://console.developers.google.com/project). When
|
||||
you create the project you will need to specify a callback URL. Specify
|
||||
this as callback:
|
||||
|
||||
http://<my_grafana_server_name_or_ip>:<grafana_server_port>/login/google
|
||||
```bash
|
||||
http://<my_grafana_server_name_or_ip>:<grafana_server_port>/login/google
|
||||
```
|
||||
|
||||
This callback URL must match the full HTTP address that you use in your
|
||||
browser to access Grafana, but with the prefix path of `/login/google`.
|
||||
When the Google project is created you will get a Client ID and a Client
|
||||
Secret. Specify these in the Grafana configuration file. For example:
|
||||
|
||||
[auth.google]
|
||||
enabled = true
|
||||
client_id = YOUR_GOOGLE_APP_CLIENT_ID
|
||||
client_secret = YOUR_GOOGLE_APP_CLIENT_SECRET
|
||||
scopes = https://www.googleapis.com/auth/userinfo.profile https://www.googleapis.com/auth/userinfo.email
|
||||
auth_url = https://accounts.google.com/o/oauth2/auth
|
||||
token_url = https://accounts.google.com/o/oauth2/token
|
||||
allowed_domains = mycompany.com mycompany.org
|
||||
allow_sign_up = true
|
||||
```bash
|
||||
[auth.google]
|
||||
enabled = true
|
||||
client_id = YOUR_GOOGLE_APP_CLIENT_ID
|
||||
client_secret = YOUR_GOOGLE_APP_CLIENT_SECRET
|
||||
scopes = https://www.googleapis.com/auth/userinfo.profile https://www.googleapis.com/auth/userinfo.email
|
||||
auth_url = https://accounts.google.com/o/oauth2/auth
|
||||
token_url = https://accounts.google.com/o/oauth2/token
|
||||
allowed_domains = mycompany.com mycompany.org
|
||||
allow_sign_up = true
|
||||
```
|
||||
|
||||
Restart the Grafana back-end. You should now see a Google login button
|
||||
on the login page. You can now login or sign up with your Google
|
||||
@@ -418,19 +439,55 @@ This option could be used if have your own oauth service.
|
||||
This callback URL must match the full HTTP address that you use in your
|
||||
browser to access Grafana, but with the prefix path of `/login/generic_oauth`.
|
||||
|
||||
[auth.generic_oauth]
|
||||
enabled = true
|
||||
client_id = YOUR_APP_CLIENT_ID
|
||||
client_secret = YOUR_APP_CLIENT_SECRET
|
||||
scopes =
|
||||
auth_url =
|
||||
token_url =
|
||||
api_url =
|
||||
allowed_domains = mycompany.com mycompany.org
|
||||
allow_sign_up = true
|
||||
```bash
|
||||
[auth.generic_oauth]
|
||||
enabled = true
|
||||
client_id = YOUR_APP_CLIENT_ID
|
||||
client_secret = YOUR_APP_CLIENT_SECRET
|
||||
scopes =
|
||||
auth_url =
|
||||
token_url =
|
||||
api_url =
|
||||
allowed_domains = mycompany.com mycompany.org
|
||||
allow_sign_up = true
|
||||
```
|
||||
|
||||
Set api_url to the resource that returns [OpenID UserInfo](https://connect2id.com/products/server/docs/api/userinfo) compatible information.
|
||||
|
||||
### Set up oauth2 with Okta
|
||||
|
||||
First set up Grafana as an OpenId client "webapplication" in Okta. Then set the Base URIs to `https://<grafana domain>/` and set the Login redirect URIs to `https://<grafana domain>/login/generic_oauth`.
|
||||
|
||||
Finaly set up the generic oauth module like this:
|
||||
```bash
|
||||
[auth.generic_oauth]
|
||||
name = Okta
|
||||
enabled = true
|
||||
scopes = openid profile email
|
||||
client_id = <okta application Client ID>
|
||||
client_secret = <okta application Client Secret>
|
||||
auth_url = https://<okta domain>/oauth2/v1/authorize
|
||||
token_url = https://<okta domain>/oauth2/v1/token
|
||||
api_url = https://<okta domain>/oauth2/v1/userinfo
|
||||
```
|
||||
|
||||
### Set up oauth2 with Bitbucket
|
||||
|
||||
```bash
|
||||
[auth.generic_oauth]
|
||||
name = BitBucket
|
||||
enabled = true
|
||||
allow_sign_up = true
|
||||
client_id = <client id>
|
||||
client_secret = <secret>
|
||||
scopes = account email
|
||||
auth_url = https://bitbucket.org/site/oauth2/authorize
|
||||
token_url = https://bitbucket.org/site/oauth2/access_token
|
||||
api_url = https://api.bitbucket.org/2.0/user
|
||||
team_ids =
|
||||
allowed_organizations =
|
||||
```
|
||||
|
||||
<hr>
|
||||
|
||||
## [auth.basic]
|
||||
@@ -503,21 +560,25 @@ session table manually.
|
||||
|
||||
Mysql Example:
|
||||
|
||||
CREATE TABLE `session` (
|
||||
`key` CHAR(16) NOT NULL,
|
||||
`data` BLOB,
|
||||
`expiry` INT(11) UNSIGNED NOT NULL,
|
||||
PRIMARY KEY (`key`)
|
||||
) ENGINE=MyISAM DEFAULT CHARSET=utf8;
|
||||
```bash
|
||||
CREATE TABLE `session` (
|
||||
`key` CHAR(16) NOT NULL,
|
||||
`data` BLOB,
|
||||
`expiry` INT(11) UNSIGNED NOT NULL,
|
||||
PRIMARY KEY (`key`)
|
||||
) ENGINE=MyISAM DEFAULT CHARSET=utf8;
|
||||
```
|
||||
|
||||
Postgres Example:
|
||||
|
||||
CREATE TABLE session (
|
||||
key CHAR(16) NOT NULL,
|
||||
data BYTEA,
|
||||
expiry INTEGER NOT NULL,
|
||||
PRIMARY KEY (key)
|
||||
);
|
||||
```bash
|
||||
CREATE TABLE session (
|
||||
key CHAR(16) NOT NULL,
|
||||
data BYTEA,
|
||||
expiry INTEGER NOT NULL,
|
||||
PRIMARY KEY (key)
|
||||
);
|
||||
```
|
||||
|
||||
Postgres valid `sslmode` are `disable`, `require` (default), `verify-ca`, and `verify-full`.
|
||||
|
||||
@@ -593,6 +654,9 @@ Address used when sending out emails, defaults to `admin@grafana.localhost`
|
||||
### from_name
|
||||
Name to be used when sending out emails, defaults to `Grafana`
|
||||
|
||||
### ehlo_identity
|
||||
Name to be used as client identity for EHLO in SMTP dialog, defaults to instance_name.
|
||||
|
||||
## [log]
|
||||
|
||||
### mode
|
||||
@@ -645,15 +709,24 @@ Time to live for snapshots.
|
||||
These options control how images should be made public so they can be shared on services like slack.
|
||||
|
||||
### provider
|
||||
You can choose between (s3, webdav). If left empty Grafana will ignore the upload action.
|
||||
You can choose between (s3, webdav, gcs). If left empty Grafana will ignore the upload action.
|
||||
|
||||
## [external_image_storage.s3]
|
||||
|
||||
### bucket
|
||||
Bucket name for S3. e.g. grafana.snapshot
|
||||
|
||||
### region
|
||||
Region name for S3. e.g. 'us-east-1', 'cn-north-1', etc
|
||||
|
||||
### path
|
||||
Optional extra path inside bucket, useful to apply expiration policies
|
||||
|
||||
### bucket_url
|
||||
(for backward compatibility, only works when no bucket or region are configured)
|
||||
Bucket URL for S3. AWS region can be specified within URL or defaults to 'us-east-1', e.g.
|
||||
- http://grafana.s3.amazonaws.com/
|
||||
- https://grafana.s3-ap-southeast-2.amazonaws.com/
|
||||
- https://grafana.s3-cn-north-1.amazonaws.com.cn
|
||||
|
||||
### access_key
|
||||
Access key. e.g. AAAAAAAAAAAAAAAAAAAA
|
||||
@@ -677,6 +750,17 @@ basic auth username
|
||||
### password
|
||||
basic auth password
|
||||
|
||||
## [external_image_storage.gcs]
|
||||
|
||||
### key_file
|
||||
Path to JSON key file associated with a Google service account to authenticate and authorize.
|
||||
Service Account keys can be created and downloaded from https://console.developers.google.com/permissions/serviceaccounts.
|
||||
|
||||
Service Account should have "Storage Object Writer" role.
|
||||
|
||||
### bucket name
|
||||
Bucket Name on Google Cloud Storage.
|
||||
|
||||
## [alerting]
|
||||
|
||||
### enabled
|
||||
|
||||
@@ -15,39 +15,47 @@ weight = 1
|
||||
|
||||
Description | Download
|
||||
------------ | -------------
|
||||
Stable for Debian-based Linux | [grafana_4.4.3_amd64.deb](https://s3-us-west-2.amazonaws.com/grafana-releases/release/grafana_4.4.3_amd64.deb)
|
||||
Beta for Debian-based Linux | [grafana_4.5.0-beta1_amd64.deb](https://s3-us-west-2.amazonaws.com/grafana-releases/release/grafana_4.5.0-beta1_amd64.deb)
|
||||
Stable for Debian-based Linux | [grafana_4.5.2_amd64.deb](https://s3-us-west-2.amazonaws.com/grafana-releases/release/grafana_4.5.2_amd64.deb)
|
||||
|
||||
<!-- Beta for Debian-based Linux | [grafana_4.5.0-beta1_amd64.deb](https://s3-us-west-2.amazonaws.com/grafana-releases/release/grafana_4.5.0-beta1_amd64.deb) -->
|
||||
|
||||
Read [Upgrading Grafana]({{< relref "installation/upgrading.md" >}}) for tips and guidance on updating an existing
|
||||
installation.
|
||||
|
||||
## Install Stable
|
||||
|
||||
|
||||
```bash
|
||||
wget https://s3-us-west-2.amazonaws.com/grafana-releases/release/grafana_4.4.3_amd64.deb
|
||||
wget https://s3-us-west-2.amazonaws.com/grafana-releases/release/grafana_4.5.2_amd64.deb
|
||||
sudo apt-get install -y adduser libfontconfig
|
||||
sudo dpkg -i grafana_4.4.3_amd64.deb
|
||||
sudo dpkg -i grafana_4.5.2_amd64.deb
|
||||
```
|
||||
|
||||
<!--
|
||||
## Install Latest Beta
|
||||
|
||||
```bash
|
||||
wget https://s3-us-west-2.amazonaws.com/grafana-releases/release/grafana_4.5.0-beta1_amd64.deb
|
||||
wget https://s3-us-west-2.amazonaws.com/grafana-releases/release/grafana_4.5.2-beta1_amd64.deb
|
||||
sudo apt-get install -y adduser libfontconfig
|
||||
sudo dpkg -i grafana_4.5.0-beta1_amd64.deb
|
||||
sudo dpkg -i grafana_4.5.2-beta1_amd64.deb
|
||||
```
|
||||
-->
|
||||
|
||||
## APT Repository
|
||||
|
||||
Add the following line to your `/etc/apt/sources.list` file.
|
||||
|
||||
deb https://packagecloud.io/grafana/stable/debian/ jessie main
|
||||
```bash
|
||||
deb https://packagecloud.io/grafana/stable/debian/ jessie main
|
||||
```
|
||||
|
||||
Use the above line even if you are on Ubuntu or another Debian version.
|
||||
There is also a testing repository if you want beta or release
|
||||
candidates.
|
||||
|
||||
deb https://packagecloud.io/grafana/testing/debian/ jessie main
|
||||
```bash
|
||||
deb https://packagecloud.io/grafana/testing/debian/ jessie main
|
||||
```
|
||||
|
||||
Then add the [Package Cloud](https://packagecloud.io/grafana) key. This
|
||||
allows you to install signed packages.
|
||||
|
||||
@@ -14,7 +14,9 @@ weight = 4
|
||||
|
||||
Grafana is very easy to install and run using the offical docker container.
|
||||
|
||||
$ docker run -d -p 3000:3000 grafana/grafana
|
||||
```bash
|
||||
$ docker run -d -p 3000:3000 grafana/grafana
|
||||
```
|
||||
|
||||
All Grafana configuration settings can be defined using environment
|
||||
variables, this is especially useful when using the above container.
|
||||
@@ -26,10 +28,12 @@ folder `/var/lib/grafana` and configuration files is in `/etc/grafana/`
|
||||
folder. You can map these volumes to host folders when you start the
|
||||
container:
|
||||
|
||||
$ docker run -d -p 3000:3000 \
|
||||
-v /var/lib/grafana:/var/lib/grafana \
|
||||
-e "GF_SECURITY_ADMIN_PASSWORD=secret" \
|
||||
grafana/grafana
|
||||
```bash
|
||||
$ docker run -d -p 3000:3000 \
|
||||
-v /var/lib/grafana:/var/lib/grafana \
|
||||
-e "GF_SECURITY_ADMIN_PASSWORD=secret" \
|
||||
grafana/grafana
|
||||
```
|
||||
|
||||
In the above example I map the data folder and sets a configuration option via
|
||||
an `ENV` instruction.
|
||||
|
||||
@@ -92,7 +92,7 @@ org_role = "Viewer"
|
||||
By default the configuration expects you to specify a bind DN and bind password. This should be a read only user that can perform LDAP searches.
|
||||
When the user DN is found a second bind is performed with the user provided username & password (in the normal Grafana login form).
|
||||
|
||||
```
|
||||
```bash
|
||||
bind_dn = "cn=admin,dc=grafana,dc=org"
|
||||
bind_password = "grafana"
|
||||
```
|
||||
@@ -102,7 +102,7 @@ bind_password = "grafana"
|
||||
If you can provide a single bind expression that matches all possible users, you can skip the second bind and bind against the user DN directly.
|
||||
This allows you to not specify a bind_password in the configuration file.
|
||||
|
||||
```
|
||||
```bash
|
||||
bind_dn = "cn=%s,o=users,dc=grafana,dc=org"
|
||||
```
|
||||
|
||||
|
||||
@@ -15,7 +15,7 @@ Installation can be done using [homebrew](http://brew.sh/)
|
||||
|
||||
Install latest stable:
|
||||
|
||||
```
|
||||
```bash
|
||||
brew update
|
||||
brew install grafana
|
||||
```
|
||||
@@ -24,7 +24,7 @@ To start grafana look at the command printed after the homebrew install complete
|
||||
|
||||
To upgrade use the reinstall command
|
||||
|
||||
```
|
||||
```bash
|
||||
brew update
|
||||
brew reinstall grafana
|
||||
```
|
||||
@@ -34,12 +34,44 @@ brew reinstall grafana
|
||||
You can also install the latest unstable grafana from git:
|
||||
|
||||
|
||||
```
|
||||
```bash
|
||||
brew install --HEAD grafana/grafana/grafana
|
||||
```
|
||||
|
||||
To upgrade grafana if you've installed from HEAD:
|
||||
|
||||
```
|
||||
```bash
|
||||
brew reinstall --HEAD grafana/grafana/grafana
|
||||
```
|
||||
|
||||
### Starting Grafana
|
||||
|
||||
To start Grafana using homebrew services first make sure homebrew/services is installed.
|
||||
|
||||
```bash
|
||||
brew tap homebrew/services
|
||||
```
|
||||
|
||||
Then start Grafana using:
|
||||
|
||||
```bash
|
||||
brew services start grafana
|
||||
```
|
||||
|
||||
|
||||
### Configuration
|
||||
|
||||
The Configuration file should be located at `/usr/local/etc/grafana/grafana.ini`.
|
||||
|
||||
### Logs
|
||||
|
||||
The log file should be located at `/usr/local/var/log/grafana/grafana.log`.
|
||||
|
||||
### Plugins
|
||||
|
||||
If you want to manually install a plugin place it here: `/usr/local/var/lib/grafana/plugins`.
|
||||
|
||||
### Database
|
||||
|
||||
The default sqlite database is located at `/usr/local/var/lib/grafana`
|
||||
|
||||
|
||||
@@ -15,8 +15,9 @@ weight = 2
|
||||
|
||||
Description | Download
|
||||
------------ | -------------
|
||||
Stable for CentOS / Fedora / OpenSuse / Redhat Linux | [4.4.3 (x86-64 rpm)](https://s3-us-west-2.amazonaws.com/grafana-releases/release/grafana-4.4.3-1.x86_64.rpm)
|
||||
Latest Beta for CentOS / Fedora / OpenSuse / Redhat Linux | [4.5.0-beta1 (x86-64 rpm)](https://s3-us-west-2.amazonaws.com/grafana-releases/release/grafana-4.5.0-beta1.x86_64.rpm)
|
||||
Stable for CentOS / Fedora / OpenSuse / Redhat Linux | [4.5.2 (x86-64 rpm)](https://s3-us-west-2.amazonaws.com/grafana-releases/release/grafana-4.5.2-1.x86_64.rpm)
|
||||
|
||||
<!-- Latest Beta for CentOS / Fedora / OpenSuse / Redhat Linux | [4.5.0-beta1 (x86-64 rpm)](https://s3-us-west-2.amazonaws.com/grafana-releases/release/grafana-4.5.0-beta1.x86_64.rpm) -->
|
||||
|
||||
Read [Upgrading Grafana]({{< relref "installation/upgrading.md" >}}) for tips and guidance on updating an existing
|
||||
installation.
|
||||
@@ -25,42 +26,53 @@ installation.
|
||||
|
||||
You can install Grafana using Yum directly.
|
||||
|
||||
$ sudo yum install https://s3-us-west-2.amazonaws.com/grafana-releases/release/grafana-4.4.3-1.x86_64.rpm
|
||||
```bash
|
||||
$ sudo yum install https://s3-us-west-2.amazonaws.com/grafana-releases/release/grafana-4.5.2-1.x86_64.rpm
|
||||
```
|
||||
|
||||
Or install manually using `rpm`.
|
||||
|
||||
#### On CentOS / Fedora / Redhat:
|
||||
|
||||
$ wget https://s3-us-west-2.amazonaws.com/grafana-releases/release/grafana-4.4.3-1.x86_64.rpm
|
||||
$ sudo yum install initscripts fontconfig
|
||||
$ sudo rpm -Uvh grafana-4.4.3-1.x86_64.rpm
|
||||
```bash
|
||||
$ wget https://s3-us-west-2.amazonaws.com/grafana-releases/release/grafana-4.5.2-1.x86_64.rpm
|
||||
$ sudo yum install initscripts fontconfig
|
||||
$ sudo rpm -Uvh grafana-4.5.2-1.x86_64.rpm
|
||||
```
|
||||
|
||||
#### On OpenSuse:
|
||||
|
||||
$ sudo rpm -i --nodeps grafana-4.4.3-1.x86_64.rpm
|
||||
```bash
|
||||
$ sudo rpm -i --nodeps grafana-4.5.2-1.x86_64.rpm
|
||||
```
|
||||
|
||||
## Install via YUM Repository
|
||||
|
||||
Add the following to a new file at `/etc/yum.repos.d/grafana.repo`
|
||||
|
||||
[grafana]
|
||||
name=grafana
|
||||
baseurl=https://packagecloud.io/grafana/stable/el/6/$basearch
|
||||
repo_gpgcheck=1
|
||||
enabled=1
|
||||
gpgcheck=1
|
||||
gpgkey=https://packagecloud.io/gpg.key https://grafanarel.s3.amazonaws.com/RPM-GPG-KEY-grafana
|
||||
sslverify=1
|
||||
sslcacert=/etc/pki/tls/certs/ca-bundle.crt
|
||||
```bash
|
||||
[grafana]
|
||||
name=grafana
|
||||
baseurl=https://packagecloud.io/grafana/stable/el/6/$basearch
|
||||
repo_gpgcheck=1
|
||||
enabled=1
|
||||
gpgcheck=1
|
||||
gpgkey=https://packagecloud.io/gpg.key https://grafanarel.s3.amazonaws.com/RPM-GPG-KEY-grafana
|
||||
sslverify=1
|
||||
sslcacert=/etc/pki/tls/certs/ca-bundle.crt
|
||||
```
|
||||
|
||||
There is also a testing repository if you want beta or release
|
||||
candidates.
|
||||
There is also a testing repository if you want beta or release candidates.
|
||||
|
||||
baseurl=https://packagecloud.io/grafana/testing/el/6/$basearch
|
||||
```bash
|
||||
baseurl=https://packagecloud.io/grafana/testing/el/6/$basearch
|
||||
```
|
||||
|
||||
Then install Grafana via the `yum` command.
|
||||
|
||||
$ sudo yum install grafana
|
||||
```bash
|
||||
$ sudo yum install grafana
|
||||
```
|
||||
|
||||
### RPM GPG Key
|
||||
|
||||
@@ -81,7 +93,9 @@ key](https://grafanarel.s3.amazonaws.com/RPM-GPG-KEY-grafana).
|
||||
|
||||
You can start Grafana by running:
|
||||
|
||||
$ sudo service grafana-server start
|
||||
```bash
|
||||
$ sudo service grafana-server start
|
||||
```
|
||||
|
||||
This will start the `grafana-server` process as the `grafana` user,
|
||||
which is created during package installation. The default HTTP port is
|
||||
@@ -89,17 +103,23 @@ which is created during package installation. The default HTTP port is
|
||||
|
||||
To configure the Grafana server to start at boot time:
|
||||
|
||||
$ sudo /sbin/chkconfig --add grafana-server
|
||||
```bash
|
||||
$ sudo /sbin/chkconfig --add grafana-server
|
||||
```
|
||||
|
||||
## Start the server (via systemd)
|
||||
|
||||
$ systemctl daemon-reload
|
||||
$ systemctl start grafana-server
|
||||
$ systemctl status grafana-server
|
||||
```bash
|
||||
$ systemctl daemon-reload
|
||||
$ systemctl start grafana-server
|
||||
$ systemctl status grafana-server
|
||||
```
|
||||
|
||||
### Enable the systemd service to start at boot
|
||||
|
||||
sudo systemctl enable grafana-server.service
|
||||
```bash
|
||||
sudo systemctl enable grafana-server.service
|
||||
```
|
||||
|
||||
## Environment file
|
||||
|
||||
@@ -138,7 +158,7 @@ for example in alert notifications.
|
||||
|
||||
If the image is missing text make sure you have font packages installed.
|
||||
|
||||
```
|
||||
```bash
|
||||
yum install fontconfig
|
||||
yum install freetype*
|
||||
yum install urw-fonts
|
||||
|
||||
@@ -29,7 +29,7 @@ installed grafana to custom location using a binary tar/zip it is usally in `<gr
|
||||
|
||||
#### mysql
|
||||
|
||||
```
|
||||
```bash
|
||||
backup:
|
||||
> mysqldump -u root -p[root_password] [grafana] > grafana_backup.sql
|
||||
|
||||
@@ -39,7 +39,7 @@ restore:
|
||||
|
||||
#### postgres
|
||||
|
||||
```
|
||||
```bash
|
||||
backup:
|
||||
> pg_dump grafana > grafana_backup
|
||||
|
||||
@@ -54,7 +54,7 @@ and execute the same `dpkg -i` command but with the new package. It will upgrade
|
||||
|
||||
If you used our APT repository:
|
||||
|
||||
```
|
||||
```bash
|
||||
sudo apt-get update
|
||||
sudo apt-get install grafana
|
||||
```
|
||||
@@ -73,14 +73,14 @@ and execute the same `yum install` or `rpm -i` command but with the new package.
|
||||
|
||||
If you used our YUM repository:
|
||||
|
||||
```
|
||||
```bash
|
||||
sudo yum update grafana
|
||||
```
|
||||
|
||||
### Docker
|
||||
|
||||
This just an example, details depend on how you configured your grafana container.
|
||||
```
|
||||
```bash
|
||||
docker pull grafana
|
||||
docker stop my-grafana-container
|
||||
docker rm my-grafana-container
|
||||
@@ -94,10 +94,10 @@ to the same location (and overwrite the existing files). This might overwrite yo
|
||||
recommend you place your config changes in a file named `<grafana_install_dir>/conf/custom.ini`
|
||||
as this will make upgrades easier without risking losing your config changes.
|
||||
|
||||
## Upgrading form 1.x
|
||||
## Upgrading from 1.x
|
||||
|
||||
[Migrating from 1.x to 2.x]({{< relref "installation/migrating_to2.md" >}})
|
||||
|
||||
## Upgrading form 2.x
|
||||
## Upgrading from 2.x
|
||||
|
||||
We are not aware of any issues upgrading directly from 2.x to 4.x but to on the safe side go via 3.x.
|
||||
We are not aware of any issues upgrading directly from 2.x to 4.x but to be on the safe side go via 3.x => 4.x.
|
||||
|
||||
@@ -13,7 +13,7 @@ weight = 3
|
||||
|
||||
Description | Download
|
||||
------------ | -------------
|
||||
Latest stable package for Windows | [grafana.4.4.3.windows-x64.zip](https://s3-us-west-2.amazonaws.com/grafana-releases/release/grafana-4.4.3.windows-x64.zip)
|
||||
Latest stable package for Windows | [grafana.4.5.2.windows-x64.zip](https://s3-us-west-2.amazonaws.com/grafana-releases/release/grafana-4.5.2.windows-x64.zip)
|
||||
|
||||
Read [Upgrading Grafana]({{< relref "installation/upgrading.md" >}}) for tips and guidance on updating an existing
|
||||
installation.
|
||||
|
||||
@@ -23,7 +23,7 @@ The most important fields are the first three, especially the id. The convention
|
||||
|
||||
Examples:
|
||||
|
||||
```
|
||||
```bash
|
||||
raintank-worldping-app
|
||||
grafana-simple-json-datasource
|
||||
grafana-piechart-panel
|
||||
@@ -66,7 +66,7 @@ The README.md file is rendered both on Grafana.net and in the plugins section in
|
||||
|
||||
Here is a typical directory structure for a plugin.
|
||||
|
||||
```
|
||||
```bash
|
||||
johnnyb-awesome-datasource
|
||||
|-- dist
|
||||
|-- spec
|
||||
|
||||
@@ -45,7 +45,7 @@ The javascript object that communicates with the database and transforms data to
|
||||
|
||||
The Datasource should contain the following functions:
|
||||
|
||||
```
|
||||
```javascript
|
||||
query(options) //used by panels to get data
|
||||
testDatasource() //used by datasource configuration page to make sure the connection is working
|
||||
annotationQuery(options) // used by dashboards to get annotations
|
||||
|
||||
@@ -30,37 +30,37 @@ On Linux systems the grafana-cli will assume that the grafana plugin directory i
|
||||
### Grafana-cli Commands
|
||||
|
||||
List available plugins
|
||||
```
|
||||
```bash
|
||||
grafana-cli plugins list-remote
|
||||
```
|
||||
|
||||
Install the latest version of a plugin
|
||||
```
|
||||
```bash
|
||||
grafana-cli plugins install <plugin-id>
|
||||
```
|
||||
|
||||
Install a specific version of a plugin
|
||||
```
|
||||
```bash
|
||||
grafana-cli plugins install <plugin-id> <version>
|
||||
```
|
||||
|
||||
List installed plugins
|
||||
```
|
||||
```bash
|
||||
grafana-cli plugins ls
|
||||
```
|
||||
|
||||
Update all installed plugins
|
||||
```
|
||||
```bash
|
||||
grafana-cli plugins update-all
|
||||
```
|
||||
|
||||
Update one plugin
|
||||
```
|
||||
```bash
|
||||
grafana-cli plugins update <plugin-id>
|
||||
```
|
||||
|
||||
Remove one plugin
|
||||
```
|
||||
```bash
|
||||
grafana-cli plugins remove <plugin-id>
|
||||
```
|
||||
|
||||
@@ -72,6 +72,11 @@ The Download URL from Grafana.com API is in this form:
|
||||
|
||||
`https://grafana.com/api/plugins/<plugin id>/versions/<version number>/download`
|
||||
|
||||
You can specify a local URL by using the `--pluginUrl` option.
|
||||
```bash
|
||||
grafana-cli --pluginUrl https://nexus.company.com/grafana/plugins/<plugin-id>-<plugin-version>.zip plugins install <plugin-id>
|
||||
```
|
||||
|
||||
To manually install a Plugin via the Grafana.com API:
|
||||
|
||||
1. Find the plugin you want to download, the plugin id can be found on the Installation Tab on the plugin's page on Grafana.com. In this example, the plugin id is `jdbranham-diagram-panel`:
|
||||
@@ -79,7 +84,7 @@ To manually install a Plugin via the Grafana.com API:
|
||||
{{< imgbox img="/img/docs/installation-tab.png" caption="Installation Tab" >}}
|
||||
|
||||
2. Use the Grafana API to find the plugin using this url `https://grafana.com/api/plugins/<plugin id from step 1>`. For example: https://grafana.com/api/plugins/jdbranham-diagram-panel should return:
|
||||
```
|
||||
```bash
|
||||
{
|
||||
"id": 145,
|
||||
"typeId": 3,
|
||||
@@ -92,7 +97,7 @@ To manually install a Plugin via the Grafana.com API:
|
||||
```
|
||||
|
||||
3. Find the download link:
|
||||
```
|
||||
```bash
|
||||
{
|
||||
"rel": "download",
|
||||
"href": "/plugins/jdbranham-diagram-panel/versions/1.4.0/download"
|
||||
|
||||
@@ -13,27 +13,27 @@ dev environment. Grafana ships with its own required backend server; also comple
|
||||
|
||||
## Dependencies
|
||||
|
||||
- [Go 1.8.1](https://golang.org/dl/)
|
||||
- [Go 1.9.2](https://golang.org/dl/)
|
||||
- [NodeJS LTS](https://nodejs.org/download/)
|
||||
- [Git](https://git-scm.com/downloads)
|
||||
|
||||
## Get Code
|
||||
Create a directory for the project and set your path accordingly (or use the [default Go workspace directory](https://golang.org/doc/code.html#GOPATH)). Then download and install Grafana into your $GOPATH directory:
|
||||
|
||||
```
|
||||
```bash
|
||||
export GOPATH=`pwd`
|
||||
go get github.com/grafana/grafana
|
||||
```
|
||||
|
||||
On Windows use setx instead of export and then restart your command prompt:
|
||||
```
|
||||
setx GOPATH %cd%
|
||||
```bash
|
||||
setx GOPATH %cd%
|
||||
```
|
||||
|
||||
You may see an error such as: `package github.com/grafana/grafana: no buildable Go source files`. This is just a warning, and you can proceed with the directions.
|
||||
|
||||
## Building the backend
|
||||
```
|
||||
```bash
|
||||
cd $GOPATH/src/github.com/grafana/grafana
|
||||
go run build.go setup
|
||||
go run build.go build # (or 'go build ./pkg/cmd/grafana-server')
|
||||
@@ -43,36 +43,26 @@ go run build.go build # (or 'go build ./pkg/cmd/grafana-server')
|
||||
The Grafana backend includes Sqlite3 which requires GCC to compile. So in order to compile Grafana on windows you need
|
||||
to install GCC. We recommend [TDM-GCC](http://tdm-gcc.tdragon.net/download).
|
||||
|
||||
[node-gyp](https://github.com/nodejs/node-gyp#installation) is the Node.js native addon build tool and it requires extra dependencies to be installed on Windows. In a command prompt which is run as administrator, run:
|
||||
[node-gyp](https://github.com/nodejs/node-gyp#installation) is the Node.js native addon build tool and it requires extra dependencies to be installed on Windows. In a command prompt which is run as administrator, run:
|
||||
|
||||
```
|
||||
```bash
|
||||
npm --add-python-to-path='true' --debug install --global windows-build-tools
|
||||
```
|
||||
|
||||
## Build the Front-end Assets
|
||||
## Build the Frontend Assets
|
||||
|
||||
To build less to css for the frontend you will need a recent version of node (v0.12.0),
|
||||
npm (v2.5.0) and grunt (v0.4.5). Run the following:
|
||||
For this you need nodejs (v.6+).
|
||||
|
||||
```
|
||||
```bash
|
||||
npm install -g yarn
|
||||
yarn install --pure-lockfile
|
||||
npm install -g grunt-cli
|
||||
grunt
|
||||
npm run build
|
||||
```
|
||||
|
||||
## Recompile backend on source change
|
||||
To rebuild on source change
|
||||
```
|
||||
go get github.com/Unknwon/bra
|
||||
bra run
|
||||
```
|
||||
|
||||
If the `bra run` command does not work, make sure that the bin directory in your Go workspace directory is in the path. $GOPATH/bin (or %GOPATH%\bin in Windows) is in your path.
|
||||
|
||||
## Running Grafana Locally
|
||||
You can run a local instance of Grafana by running:
|
||||
```
|
||||
|
||||
```bash
|
||||
./bin/grafana-server
|
||||
```
|
||||
If you built the binary with `go run build.go build`, run `./bin/grafana-server`
|
||||
@@ -81,19 +71,24 @@ If you built it with `go build .`, run `./grafana`
|
||||
|
||||
Open grafana in your browser (default [http://localhost:3000](http://localhost:3000)) and login with admin user (default user/pass = admin/admin).
|
||||
|
||||
## Developing for Grafana
|
||||
To add features, customize your config, etc, you'll need to rebuild on source change.
|
||||
```
|
||||
## Developing Grafana
|
||||
|
||||
To add features, customize your config, etc, you'll need to rebuild the backend when you change the source code. We use a tool named `bra` that
|
||||
does this.
|
||||
|
||||
```bash
|
||||
go get github.com/Unknwon/bra
|
||||
|
||||
bra run
|
||||
```
|
||||
You'll also need to run `grunt watch` to watch for changes to the front-end.
|
||||
|
||||
You'll also need to run `npm run watch` to watch for changes to the front-end (typescript, html, sass)
|
||||
|
||||
## Creating optimized release packages
|
||||
|
||||
This step builds linux packages and requires that fpm is installed. Install fpm via `gem install fpm`.
|
||||
|
||||
```
|
||||
```bash
|
||||
go run build.go build package
|
||||
```
|
||||
|
||||
@@ -105,6 +100,10 @@ You only need to add the options you want to override. Config files are applied
|
||||
1. grafana.ini
|
||||
2. custom.ini
|
||||
|
||||
### Set app_mode to development
|
||||
|
||||
In your custom.ini uncomment (remove the leading `;`) sign. And set `app_mode = development`.
|
||||
|
||||
Learn more about Grafana config options in the [Configuration section](/installation/configuration/)
|
||||
|
||||
## Create a pull requests
|
||||
@@ -119,7 +118,7 @@ Please contribute to the Grafana project and submit a pull request! Build new fe
|
||||
|
||||
**Problem**: When running `bra run` for the first time you get an error that it is not a recognized command.
|
||||
|
||||
**Solution**: Add the bin directory in your Go workspace directory to the path. Per default this is `$HOME/go/bin` on Linux and `%USERPROFILE%\go\bin` on Windows or `$GOPATH/bin` (`%GOPATH%\bin` on Windows) if you have set your own workspace directory.
|
||||
**Solution**: Add the bin directory in your Go workspace directory to the path. Per default this is `$HOME/go/bin` on Linux and `%USERPROFILE%\go\bin` on Windows or `$GOPATH/bin` (`%GOPATH%\bin` on Windows) if you have set your own workspace directory.
|
||||
<br><br>
|
||||
|
||||
**Problem**: When executing a `go get` command on Windows and you get an error about the git repository not existing.
|
||||
|
||||
@@ -10,12 +10,45 @@ weight = 2
|
||||
|
||||
# Annotations
|
||||
|
||||
{{< docs-imagebox img="/img/docs/v46/annotations.png" max-width="800px" >}}
|
||||
|
||||
Annotations provide a way to mark points on the graph with rich events. When you hover over an annotation
|
||||
you can get title, tags, and text information for the event.
|
||||
you can get event description and event tags. The text field can include links to other systems with more detail.
|
||||
|
||||

|
||||
## Native annotations
|
||||
|
||||
## Queries
|
||||
Grafana v4.6+ comes with a native annotation store and the ability to add annotation events directly from the graph panel or via the [HTTP API]({{< relref "http_api/annotations.md" >}}).
|
||||
|
||||
## Adding annotations
|
||||
|
||||
By holding down CTRL/CMD + mouse click. Add tags to the annotation will make it searchable from other dashboards.
|
||||
|
||||
{{< docs-imagebox img="/img/docs/annotations/annotation-still.png"
|
||||
max-width="600px" animated-gif="/img/docs/annotations/annotation.gif" >}}
|
||||
|
||||
### Adding regions events
|
||||
|
||||
You can also hold down CTRL/CMD and select region to create a region annotation.
|
||||
|
||||
{{< docs-imagebox img="/img/docs/annotations/region-annotation-still.png"
|
||||
max-width="600px" animated-gif="/img/docs/annotations/region-annotation.gif" >}}
|
||||
|
||||
### Built in query
|
||||
|
||||
After you added an annotation they will still be visible. This is due to the built in annotation query that exists on all dashboards. This annotation query will
|
||||
fetch all annotation events that originate from the current dashboard and show them on the panel where they where created. This includes alert state history annotations. You can
|
||||
stop annotations from being fetched & drawn by opening the **Annotations** settings (via Dashboard cogs menu) and modifying the query named `Annotations & Alerts (Built-in)`.
|
||||
|
||||
When you copy a dashboard using the **Save As** feature it will get a new dashboard id so annotations created on source dashboard will no longer be visible on the copy. You
|
||||
can still show them if you add a new **Annotation Query** and filter by tags. But this only works if the annotations on the source dashboard had tags to filter by.
|
||||
|
||||
### Query by tag
|
||||
|
||||
You can create new annotation queries that fetch annotations from the native annotation store via the `-- Grafana --` data source and by setting *Filter by* to `Tags`. Specify at least
|
||||
one tag. For example create an annotation query name `outages` and specify a tag named `outage`. This query will show all annotations you create (from any dashboard or via API) that
|
||||
have the `outage` tag.
|
||||
|
||||
## Querying other data sources
|
||||
|
||||
Annotation events are fetched via annotation queries. To add a new annotation query to a dashboard
|
||||
open the dashboard settings menu, then select `Annotations`. This will open the dashboard annotations
|
||||
|
||||
@@ -24,7 +24,7 @@ When a user creates a new dashboard, a new dashboard JSON object is initialized
|
||||
|
||||
> Note: In the following JSON, id is shown as null which is the default value assigned to it until a dashboard is saved. Once a dashboard is saved, an integer value is assigned to the `id` field.
|
||||
|
||||
```
|
||||
```json
|
||||
{
|
||||
"id": null,
|
||||
"title": "New dashboard",
|
||||
|
||||
@@ -1,34 +0,0 @@
|
||||
+++
|
||||
title = "Keyboard shortcuts"
|
||||
keywords = ["grafana", "dashboard", "documentation", "shortcuts"]
|
||||
type = "docs"
|
||||
[menu.docs]
|
||||
parent = "dashboard_features"
|
||||
weight = 8
|
||||
+++
|
||||
|
||||
# Keyboard Shortcuts
|
||||
|
||||
No mouse? No problem. Grafana has extensive keyboard shortcuts to allow you to navigate throughout the interface. This comes in especially handy when dealing with dealing with single-purpose machines powering on-wall displays that may not have a mouse available.
|
||||
|
||||
## Dashboard Keyboard Shortcuts
|
||||
|
||||
Press `Shift`+`?` to open the keyboard shortcut dialog from anywhere within the dashboard views.
|
||||
|
||||
<img class="no-shadow" src="/img/docs/v2/Grafana-Keyboard-Shortcuts.gif" style="width:80%;">
|
||||
|
||||
|
||||
|Shortcut|Action|
|
||||
|---|---|
|
||||
|`Esc`|Exit fullscreen edit/view mode, close search or any editor view|
|
||||
|`F`|Open dashboard search view (also contains import/playlist controls)|
|
||||
|`R`|Refresh (Fetches new data and rerenders panels)|
|
||||
|`CTRL`+`S`|Save dashboard|
|
||||
|`CTRL`+`H`|Hide row controls|
|
||||
|`CTRL`+`Z`|Zoom out|
|
||||
|`CTRL`+`O`|Enable/Disable shared graph crosshair|
|
||||
|
||||
|
||||
**Note**: Grafana keyboard shortcuts are the same across operating system.
|
||||
|
||||
Have a suggestion for a new keyboard shortcut? Let us know.
|
||||
@@ -43,7 +43,7 @@ You also get a link to service side rendered PNG of the panel. Useful if you wan
|
||||
|
||||
Example of a link to a server-side rendered PNG:
|
||||
|
||||
```
|
||||
```bash
|
||||
http://play.grafana.org/render/dashboard-solo/db/grafana-play-home?orgId=1&panelId=4&from=1499272191563&to=1499279391563&width=1000&height=500&tz=UTC%2B02%3A00&timeout=5000
|
||||
```
|
||||
|
||||
|
||||
@@ -22,24 +22,24 @@ Some parts of the API are only available through basic authentication and these
|
||||
The task is to create a new organization and then add a Token that can be used by other users. In the examples below which use basic auth, the user is `admin` and the password is `admin`.
|
||||
|
||||
1. [Create the org](http://docs.grafana.org/http_api/org/#create-organisation). Here is an example using curl:
|
||||
```
|
||||
```bash
|
||||
curl -X POST -H "Content-Type: application/json" -d '{"name":"apiorg"}' http://admin:admin@localhost:3000/api/orgs
|
||||
```
|
||||
|
||||
This should return a response: `{"message":"Organization created","orgId":6}`. Use the orgId for the next steps.
|
||||
|
||||
2. Optional step. If the org was created previously and/or step 3 fails then first [add your Admin user to the org](http://docs.grafana.org/http_api/org/#add-user-in-organisation):
|
||||
```
|
||||
```bash
|
||||
curl -X POST -H "Content-Type: application/json" -d '{"loginOrEmail":"admin", "role": "Admin"}' http://admin:admin@localhost:3000/api/orgs/<org id of new org>/users
|
||||
```
|
||||
|
||||
3. [Switch the org context for the Admin user to the new org](http://docs.grafana.org/http_api/user/#switch-user-context):
|
||||
```
|
||||
```bash
|
||||
curl -X POST http://admin:admin@localhost:3000/api/user/using/<id of new org>
|
||||
```
|
||||
|
||||
4. [Create the API token](http://docs.grafana.org/http_api/auth/#create-api-key):
|
||||
```
|
||||
```bash
|
||||
curl -X POST -H "Content-Type: application/json" -d '{"name":"apikeycurl", "role": "Admin"}' http://admin:admin@localhost:3000/api/auth/keys
|
||||
```
|
||||
|
||||
@@ -49,11 +49,11 @@ The task is to create a new organization and then add a Token that can be used b
|
||||
|
||||
## How To Add A Dashboard
|
||||
|
||||
Using the Token that was created in the previous step, you can create a dashboard or carry out other actions without having to switch organizations.
|
||||
Using the Token that was created in the previous step, you can create a dashboard or carry out other actions without having to switch organizations.
|
||||
|
||||
1. [Add a dashboard](http://docs.grafana.org/http_api/dashboard/#create-update-dashboard) using the key (or bearer token as it is also called):
|
||||
|
||||
```
|
||||
```bash
|
||||
curl -X POST --insecure -H "Authorization: Bearer eyJrIjoiR0ZXZmt1UFc0OEpIOGN5RWdUalBJTllUTk83VlhtVGwiLCJuIjoiYXBpa2V5Y3VybCIsImlkIjo2fQ==" -H "Content-Type: application/json" -d '{
|
||||
"dashboard": {
|
||||
"id": null,
|
||||
|
||||
243
docs/sources/tutorials/authproxy.md
Normal file
243
docs/sources/tutorials/authproxy.md
Normal file
@@ -0,0 +1,243 @@
|
||||
+++
|
||||
title = "Grafana Authproxy"
|
||||
type = "docs"
|
||||
keywords = ["grafana", "tutorials", "authproxy"]
|
||||
[menu.docs]
|
||||
parent = "tutorials"
|
||||
weight = 10
|
||||
+++
|
||||
|
||||
# Grafana Authproxy
|
||||
|
||||
AuthProxy allows you to offload the authentication of users to a web server (there are many reasons why you’d want to run a web server in front of a production version of Grafana, especially if it’s exposed to the Internet).
|
||||
|
||||
Popular web servers have a very extensive list of pluggable authentication modules, and any of them can be used with the AuthProxy feature.
|
||||
|
||||
The Grafana AuthProxy feature is very simple in design, but it is this simplicity that makes it so powerful.
|
||||
|
||||
## Interacting with Grafana’s AuthProxy via curl
|
||||
|
||||
The AuthProxy feature can be configured through the Grafana configuration file with the following options:
|
||||
|
||||
```js
|
||||
[auth.proxy]
|
||||
enabled = true
|
||||
header_name = X-WEBAUTH-USER
|
||||
header_property = username
|
||||
auto_sign_up = true
|
||||
```
|
||||
|
||||
* **enabled**: this is to toggle the feature on or off
|
||||
* **header_name**: this is the HTTP header name that passes the username or email address of the authenticated user to Grafana. Grafana will trust what ever username is contained in this header and automatically log the user in.
|
||||
* **header_property**: this tells Grafana whether the value in the header_name is a username or an email address. (In Grafana you can log in using your account username or account email)
|
||||
* **auto_sign_up**: If set to true, Grafana will automatically create user accounts in the Grafana DB if one does not exist. If set to false, users who do not exist in the GrafanaDB won’t be able to log in, even though their username and password are valid.
|
||||
|
||||
With a fresh install of Grafana, using the above configuration for the authProxy feature, we can send a simple API call to list all users. The only user that will be present is the default “Admin” user that is added the first time Grafana starts up. As you can see all we need to do to authenticate the request is to provide the “X-WEBAUTH-USER” header.
|
||||
|
||||
```bash
|
||||
curl -H "X-WEBAUTH-USER: admin" http://localhost:3000/api/users
|
||||
[
|
||||
{
|
||||
"id":1,
|
||||
"name":"",
|
||||
"login":"admin",
|
||||
"email":"admin@localhost",
|
||||
"isAdmin":true
|
||||
}
|
||||
]
|
||||
```
|
||||
|
||||
We can then send a second request to the `/api/user` method which will return the details of the logged in user. We will use this request to show how Grafana automatically adds the new user we specify to the system. Here we create a new user called “anthony”.
|
||||
|
||||
```bash
|
||||
curl -H "X-WEBAUTH-USER: anthony" http://localhost:3000/api/user
|
||||
{
|
||||
"email":"anthony",
|
||||
"name":"",
|
||||
"login":"anthony",
|
||||
"theme":"",
|
||||
"orgId":1,
|
||||
"isGrafanaAdmin":false
|
||||
}
|
||||
```
|
||||
|
||||
## Making Apache’s auth work together with Grafana’s AuthProxy
|
||||
|
||||
I’ll demonstrate how to use Apache for authenticating users. In this example we use BasicAuth with Apache’s text file based authentication handler, i.e. htpasswd files. However, any available Apache authentication capabilities could be used.
|
||||
|
||||
### Apache BasicAuth
|
||||
|
||||
In this example we use Apache as a reverseProxy in front of Grafana. Apache handles the Authentication of users before forwarding requests to the Grafana backend service.
|
||||
|
||||
#### Apache configuration
|
||||
|
||||
```bash
|
||||
<VirtualHost *:80>
|
||||
ServerAdmin webmaster@authproxy
|
||||
ServerName authproxy
|
||||
ErrorLog "logs/authproxy-error_log"
|
||||
CustomLog "logs/authproxy-access_log" common
|
||||
|
||||
<Proxy *>
|
||||
AuthType Basic
|
||||
AuthName GrafanaAuthProxy
|
||||
AuthBasicProvider file
|
||||
AuthUserFile /etc/apache2/grafana_htpasswd
|
||||
Require valid-user
|
||||
|
||||
RewriteEngine On
|
||||
RewriteRule .* - [E=PROXY_USER:%{LA-U:REMOTE_USER},NS]
|
||||
RequestHeader set X-WEBAUTH-USER "%{PROXY_USER}e"
|
||||
</Proxy>
|
||||
|
||||
RequestHeader unset Authorization
|
||||
|
||||
ProxyRequests Off
|
||||
ProxyPass / http://localhost:3000/
|
||||
ProxyPassReverse / http://localhost:3000/
|
||||
</VirtualHost>
|
||||
```
|
||||
|
||||
* The first 4 lines of the virtualhost configuration are standard, so we won’t go into detail on what they do.
|
||||
|
||||
* We use a **\<proxy>** configuration block for applying our authentication rules to every proxied request. These rules include requiring basic authentication where user:password credentials are stored in the **/etc/apache2/grafana_htpasswd** file. This file can be created with the `htpasswd` command.
|
||||
|
||||
* The next part of the configuration is the tricky part. We use Apache’s rewrite engine to create our **X-WEBAUTH-USER header**, populated with the authenticated user.
|
||||
|
||||
* **RewriteRule .* - [E=PROXY_USER:%{LA-U:REMOTE_USER}, NS]**: This line is a little bit of magic. What it does, is for every request use the rewriteEngines look-ahead (LA-U) feature to determine what the REMOTE_USER variable would be set to after processing the request. Then assign the result to the variable PROXY_USER. This is neccessary as the REMOTE_USER variable is not available to the RequestHeader function.
|
||||
|
||||
* **RequestHeader set X-WEBAUTH-USER “%{PROXY_USER}e”**: With the authenticated username now stored in the PROXY_USER variable, we create a new HTTP request header that will be sent to our backend Grafana containing the username.
|
||||
|
||||
* The **RequestHeader unset Authorization** removes the Authorization header from the HTTP request before it is forwarded to Grafana. This ensures that Grafana does not try to authenticate the user using these credentials (BasicAuth is a supported authentication handler in Grafana).
|
||||
|
||||
* The last 3 lines are then just standard reverse proxy configuration to direct all authenticated requests to our Grafana server running on port 3000.
|
||||
|
||||
#### Grafana configuration
|
||||
|
||||
```bash
|
||||
############# Users ################
|
||||
[users]
|
||||
# disable user signup / registration
|
||||
allow_sign_up = false
|
||||
|
||||
# Set to true to automatically assign new users to the default organization (id 1)
|
||||
auto_assign_org = true
|
||||
|
||||
# Default role new users will be automatically assigned (if auto_assign_org above is set to true)
|
||||
auto_assign_org_role = Editor
|
||||
|
||||
|
||||
############ Auth Proxy ########
|
||||
[auth.proxy]
|
||||
enabled = true
|
||||
|
||||
# the Header name that contains the authenticated user.
|
||||
header_name = X-WEBAUTH-USER
|
||||
|
||||
# does the user authenticate against the proxy using a 'username' or an 'email'
|
||||
header_property = username
|
||||
|
||||
# automatically add the user to the system if they don't already exist.
|
||||
auto_sign_up = true
|
||||
```
|
||||
|
||||
#### Full walk through using Docker.
|
||||
|
||||
##### Grafana Container
|
||||
|
||||
For this example, we use the offical Grafana docker image available at [Docker Hub](https://hub.docker.com/r/grafana/grafana/)
|
||||
|
||||
* Create a file `grafana.ini` with the following contents
|
||||
|
||||
```bash
|
||||
[users]
|
||||
allow_sign_up = false
|
||||
auto_assign_org = true
|
||||
auto_assign_org_role = Editor
|
||||
|
||||
[auth.proxy]
|
||||
enabled = true
|
||||
header_name = X-WEBAUTH-USER
|
||||
header_property = username
|
||||
auto_sign_up = true
|
||||
```
|
||||
|
||||
* Launch the Grafana container, using our custom grafana.ini to replace `/etc/grafana/grafana.ini`. We dont expose any ports for this container as it will only be connected to by our Apache container.
|
||||
|
||||
```bash
|
||||
docker run -i -v $(pwd)/grafana.ini:/etc/grafana/grafana.ini --name grafana grafana/grafana
|
||||
```
|
||||
|
||||
### Apache Container
|
||||
|
||||
For this example we use the offical Apache docker image available at [Docker Hub](https://hub.docker.com/_/httpd/)
|
||||
|
||||
* Create a file `httpd.conf` with the following contents
|
||||
|
||||
```bash
|
||||
ServerRoot "/usr/local/apache2"
|
||||
Listen 80
|
||||
LoadModule authn_file_module modules/mod_authn_file.so
|
||||
LoadModule authn_core_module modules/mod_authn_core.so
|
||||
LoadModule authz_host_module modules/mod_authz_host.so
|
||||
LoadModule authz_user_module modules/mod_authz_user.so
|
||||
LoadModule authz_core_module modules/mod_authz_core.so
|
||||
LoadModule auth_basic_module modules/mod_auth_basic.so
|
||||
LoadModule log_config_module modules/mod_log_config.so
|
||||
LoadModule env_module modules/mod_env.so
|
||||
LoadModule headers_module modules/mod_headers.so
|
||||
LoadModule unixd_module modules/mod_unixd.so
|
||||
LoadModule rewrite_module modules/mod_rewrite.so
|
||||
LoadModule proxy_module modules/mod_proxy.so
|
||||
LoadModule proxy_http_module modules/mod_proxy_http.so
|
||||
<IfModule unixd_module>
|
||||
User daemon
|
||||
Group daemon
|
||||
</IfModule>
|
||||
ServerAdmin you@example.com
|
||||
<Directory />
|
||||
AllowOverride none
|
||||
Require all denied
|
||||
</Directory>
|
||||
DocumentRoot "/usr/local/apache2/htdocs"
|
||||
ErrorLog /proc/self/fd/2
|
||||
LogLevel error
|
||||
<IfModule log_config_module>
|
||||
LogFormat "%h %l %u %t \"%r\" %>s %b \"%{Referer}i\" \"%{User-Agent}i\"" combined
|
||||
LogFormat "%h %l %u %t \"%r\" %>s %b" common
|
||||
<IfModule logio_module>
|
||||
LogFormat "%h %l %u %t \"%r\" %>s %b \"%{Referer}i\" \"%{User-Agent}i\" %I %O" combinedio
|
||||
</IfModule>
|
||||
CustomLog /proc/self/fd/1 common
|
||||
</IfModule>
|
||||
<Proxy *>
|
||||
AuthType Basic
|
||||
AuthName GrafanaAuthProxy
|
||||
AuthBasicProvider file
|
||||
AuthUserFile /tmp/htpasswd
|
||||
Require valid-user
|
||||
RewriteEngine On
|
||||
RewriteRule .* - [E=PROXY_USER:%{LA-U:REMOTE_USER},NS]
|
||||
RequestHeader set X-WEBAUTH-USER "%{PROXY_USER}e"
|
||||
</Proxy>
|
||||
RequestHeader unset Authorization
|
||||
ProxyRequests Off
|
||||
ProxyPass / http://grafana:3000/
|
||||
ProxyPassReverse / http://grafana:3000/
|
||||
```
|
||||
|
||||
* Create a htpasswd file. We create a new user **anthony** with the password **password**
|
||||
|
||||
```bash
|
||||
htpasswd -bc htpasswd anthony password
|
||||
```
|
||||
|
||||
* Launch the httpd container using our custom httpd.conf and our htpasswd file. The container will listen on port 80, and we create a link to the **grafana** container so that this container can resolve the hostname **grafana** to the grafana container’s ip address.
|
||||
|
||||
```bash
|
||||
docker run -i -p 80:80 --link grafana:grafana -v $(pwd)/httpd.conf:/usr/local/apache2/conf/httpd.conf -v $(pwd)/htpasswd:/tmp/htpasswd httpd:2.4
|
||||
```
|
||||
|
||||
### Use grafana.
|
||||
|
||||
With our Grafana and Apache containers running, you can now connect to http://localhost/ and log in using the username/password we created in the htpasswd file.
|
||||
@@ -39,9 +39,9 @@ read the official [Getting Started With Hubot](https://hubot.github.com/docs/) g
|
||||
## Install Hubot-Grafana script
|
||||
|
||||
In your Hubot project repo install the Grafana plugin using `npm`:
|
||||
|
||||
npm install hubot-grafana --save
|
||||
|
||||
```bash
|
||||
npm install hubot-grafana --save
|
||||
```
|
||||
Edit the file external-scripts.json, and add hubot-grafana to the list of plugins.
|
||||
|
||||
```json
|
||||
@@ -56,13 +56,15 @@ Edit the file external-scripts.json, and add hubot-grafana to the list of plugin
|
||||
|
||||
The `hubot-grafana` plugin requires a number of environment variables to be set in order to work properly.
|
||||
|
||||
export HUBOT_GRAFANA_HOST=http://play.grafana.org
|
||||
export HUBOT_GRAFANA_API_KEY=abcd01234deadbeef01234
|
||||
export HUBOT_GRAFANA_S3_BUCKET=mybucket
|
||||
export HUBOT_GRAFANA_S3_ACCESS_KEY_ID=ABCDEF123456XYZ
|
||||
export HUBOT_GRAFANA_S3_SECRET_ACCESS_KEY=aBcD01234dEaDbEef01234
|
||||
export HUBOT_GRAFANA_S3_PREFIX=graphs
|
||||
export HUBOT_GRAFANA_S3_REGION=us-standard
|
||||
```bash
|
||||
export HUBOT_GRAFANA_HOST=http://play.grafana.org
|
||||
export HUBOT_GRAFANA_API_KEY=abcd01234deadbeef01234
|
||||
export HUBOT_GRAFANA_S3_BUCKET=mybucket
|
||||
export HUBOT_GRAFANA_S3_ACCESS_KEY_ID=ABCDEF123456XYZ
|
||||
export HUBOT_GRAFANA_S3_SECRET_ACCESS_KEY=aBcD01234dEaDbEef01234
|
||||
export HUBOT_GRAFANA_S3_PREFIX=graphs
|
||||
export HUBOT_GRAFANA_S3_REGION=us-standard
|
||||
```
|
||||
|
||||
### Grafana server side rendering
|
||||
|
||||
@@ -74,7 +76,6 @@ If you do not get an image when opening this link verify that the required font
|
||||
|
||||
### Grafana API Key
|
||||
|
||||
<!--<img src="/img/docs/v2/orgdropdown_api_keys.png" style="width: 150px" class="right"></img>-->
|
||||
{{< docs-imagebox img="/img/docs/v2/orgdropdown_api_keys.png" max-width="150px" class="docs-image--right">}}
|
||||
|
||||
You need to set the environment variable `HUBOT_GRAFANA_API_KEY` to a Grafana API Key.
|
||||
@@ -113,7 +114,9 @@ can create hubot command aliases with the hubot script `hubot-alias`.
|
||||
|
||||
Install it:
|
||||
|
||||
npm i --save hubot-alias
|
||||
```bash
|
||||
npm i --save hubot-alias
|
||||
```
|
||||
|
||||
Now add `hubot-alias` to the list of plugins in `external-scripts.json` and restart hubot.
|
||||
|
||||
|
||||
@@ -1,23 +1,30 @@
|
||||
var webpack = require('webpack');
|
||||
var path = require('path');
|
||||
var webpackTestConfig = require('./scripts/webpack/webpack.test.js');
|
||||
|
||||
module.exports = function(config) {
|
||||
|
||||
'use strict';
|
||||
|
||||
config.set({
|
||||
basePath: __dirname + '/public_gen',
|
||||
|
||||
frameworks: ['mocha', 'expect', 'sinon'],
|
||||
|
||||
// list of files / patterns to load in the browser
|
||||
files: [
|
||||
'vendor/npm/es6-shim/es6-shim.js',
|
||||
'vendor/npm/systemjs/dist/system.src.js',
|
||||
'test/test-main.js',
|
||||
|
||||
{pattern: '**/*.js', included: false},
|
||||
{ pattern: 'public/test/index.ts', watched: false }
|
||||
],
|
||||
|
||||
preprocessors: {
|
||||
'public/test/index.ts': ['webpack', 'sourcemap'],
|
||||
},
|
||||
|
||||
webpack: webpackTestConfig,
|
||||
webpackServer: {
|
||||
noInfo: true, // please don't spam the console when running in karma!
|
||||
},
|
||||
|
||||
// list of files to exclude
|
||||
exclude: [],
|
||||
|
||||
reporters: ['dots'],
|
||||
port: 9876,
|
||||
colors: true,
|
||||
@@ -26,9 +33,8 @@ module.exports = function(config) {
|
||||
browsers: ['PhantomJS'],
|
||||
captureTimeout: 20000,
|
||||
singleRun: true,
|
||||
autoWatchBatchDelay: 1000,
|
||||
browserNoActivityTimeout: 60000,
|
||||
|
||||
// autoWatchBatchDelay: 1000,
|
||||
// browserNoActivityTimeout: 60000,
|
||||
});
|
||||
|
||||
};
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
{
|
||||
"stable": "4.4.1",
|
||||
"testing": "4.4.1"
|
||||
"stable": "4.5.2",
|
||||
"testing": "4.5.2"
|
||||
}
|
||||
|
||||
116
package.json
116
package.json
@@ -4,18 +4,35 @@
|
||||
"company": "Grafana Labs"
|
||||
},
|
||||
"name": "grafana",
|
||||
"version": "4.5.1",
|
||||
"version": "4.6.2",
|
||||
"repository": {
|
||||
"type": "git",
|
||||
"url": "http://github.com/grafana/grafana.git"
|
||||
},
|
||||
"devDependencies": {
|
||||
"@types/d3": "^4.10.1",
|
||||
"@types/enzyme": "^2.8.9",
|
||||
"@types/node": "^8.0.31",
|
||||
"@types/react": "^16.0.5",
|
||||
"@types/react-dom": "^15.5.4",
|
||||
"angular-mocks": "^1.6.6",
|
||||
"autoprefixer": "^6.4.0",
|
||||
"awesome-typescript-loader": "^3.2.3",
|
||||
"babel-core": "^6.26.0",
|
||||
"babel-loader": "^7.1.2",
|
||||
"babel-preset-es2015": "^6.24.1",
|
||||
"css-loader": "^0.28.7",
|
||||
"enzyme": "^3.0.0",
|
||||
"enzyme-adapter-react-16": "^1.0.0",
|
||||
"es6-promise": "^3.0.2",
|
||||
"es6-shim": "^0.35.1",
|
||||
"es6-shim": "^0.35.3",
|
||||
"expect.js": "~0.2.0",
|
||||
"expose-loader": "^0.7.3",
|
||||
"extract-text-webpack-plugin": "^3.0.0",
|
||||
"file-loader": "^0.11.2",
|
||||
"gaze": "^1.1.2",
|
||||
"glob": "~7.0.0",
|
||||
"grunt": "^0.4.5",
|
||||
"grunt": "1.0.1",
|
||||
"grunt-angular-templates": "^1.1.0",
|
||||
"grunt-cli": "~1.2.0",
|
||||
"grunt-contrib-clean": "~1.0.0",
|
||||
@@ -23,59 +40,90 @@
|
||||
"grunt-contrib-concat": "^1.0.1",
|
||||
"grunt-contrib-copy": "~1.0.0",
|
||||
"grunt-contrib-cssmin": "~1.0.2",
|
||||
"grunt-contrib-htmlmin": "~2.0.0",
|
||||
"grunt-contrib-jshint": "~1.1.0",
|
||||
"grunt-contrib-uglify": "~2.0.0",
|
||||
"grunt-contrib-watch": "^1.0.0",
|
||||
"grunt-exec": "^1.0.1",
|
||||
"grunt-filerev": "^2.3.1",
|
||||
"grunt-jscs": "3.0.1",
|
||||
"grunt-karma": "~2.0.0",
|
||||
"grunt-ng-annotate": "^3.0.0",
|
||||
"grunt-notify": "^0.4.5",
|
||||
"grunt-postcss": "^0.8.0",
|
||||
"grunt-sass": "^2.0.0",
|
||||
"grunt-string-replace": "~1.3.1",
|
||||
"grunt-systemjs-builder": "^0.2.7",
|
||||
"grunt-sass-lint": "^0.2.2",
|
||||
"grunt-usemin": "3.1.1",
|
||||
"grunt-webpack": "^3.0.2",
|
||||
"html-loader": "^0.5.1",
|
||||
"html-webpack-plugin": "^2.30.1",
|
||||
"husky": "^0.14.3",
|
||||
"jshint-stylish": "~2.2.1",
|
||||
"karma": "1.3.0",
|
||||
"karma-chrome-launcher": "~2.0.0",
|
||||
"json-loader": "^0.5.7",
|
||||
"karma": "1.7.0",
|
||||
"karma-chrome-launcher": "~2.2.0",
|
||||
"karma-coverage": "1.1.1",
|
||||
"karma-expect": "~1.1.3",
|
||||
"karma-mocha": "~1.3.0",
|
||||
"karma-phantomjs-launcher": "1.0.2",
|
||||
"karma-phantomjs-launcher": "1.0.4",
|
||||
"karma-sinon": "^1.0.5",
|
||||
"karma-sourcemap-loader": "^0.3.7",
|
||||
"karma-webpack": "^2.0.4",
|
||||
"lint-staged": "^4.2.3",
|
||||
"load-grunt-tasks": "3.5.2",
|
||||
"mocha": "3.2.0",
|
||||
"phantomjs-prebuilt": "^2.1.14",
|
||||
"reflect-metadata": "0.1.8",
|
||||
"rxjs": "^5.0.0-rc.5",
|
||||
"mocha": "^4.0.1",
|
||||
"ng-annotate-loader": "^0.6.1",
|
||||
"ng-annotate-webpack-plugin": "^0.2.1-pre",
|
||||
"ngtemplate-loader": "^2.0.1",
|
||||
"npm": "^5.4.2",
|
||||
"phantomjs-prebuilt": "^2.1.15",
|
||||
"postcss-browser-reporter": "^0.5.0",
|
||||
"postcss-loader": "^2.0.6",
|
||||
"postcss-reporter": "^5.0.0",
|
||||
"prettier": "1.7.3",
|
||||
"react-test-renderer": "^16.0.0",
|
||||
"sass-lint": "^1.10.2",
|
||||
"systemjs": "0.19.41",
|
||||
"sass-loader": "^6.0.6",
|
||||
"sinon": "1.17.6",
|
||||
"systemjs": "0.20.19",
|
||||
"systemjs-plugin-css": "^0.1.36",
|
||||
"ts-loader": "^2.3.7",
|
||||
"tslint": "^5.7.0",
|
||||
"tslint-loader": "^3.5.3",
|
||||
"typescript": "^2.5.2",
|
||||
"webpack": "^3.6.0",
|
||||
"webpack-bundle-analyzer": "^2.9.0",
|
||||
"webpack-cleanup-plugin": "^0.5.1",
|
||||
"webpack-merge": "^4.1.0",
|
||||
"zone.js": "^0.7.2"
|
||||
},
|
||||
"scripts": {
|
||||
"build": "./node_modules/grunt-cli/bin/grunt",
|
||||
"test": "./node_modules/grunt-cli/bin/grunt test",
|
||||
"dev": "./node_modules/grunt-cli/bin/grunt && ./node_modules/grunt-cli/bin/grunt watch"
|
||||
"dev": "./node_modules/.bin/webpack --progress --colors --config scripts/webpack/webpack.dev.js",
|
||||
"watch": "./node_modules/.bin/webpack --progress --colors --watch --config scripts/webpack/webpack.dev.js",
|
||||
"build": "./node_modules/.bin/grunt build",
|
||||
"test": "./node_modules/.bin/grunt test",
|
||||
"lint": "./node_modules/.bin/tslint -c tslint.json --project tsconfig.json --type-check",
|
||||
"watch-test": "./node_modules/grunt-cli/bin/grunt karma:dev"
|
||||
},
|
||||
"license": "Apache-2.0",
|
||||
"dependencies": {
|
||||
"ace-builds": "^1.2.8",
|
||||
"eventemitter3": "^2.0.2",
|
||||
"gaze": "^1.1.2",
|
||||
"grunt-jscs": "3.0.1",
|
||||
"grunt-sass-lint": "^0.2.2",
|
||||
"grunt-sync": "^0.6.2",
|
||||
"karma-sinon": "^1.0.5",
|
||||
"lodash": "^4.17.2",
|
||||
"angular": "^1.6.6",
|
||||
"angular-bindonce": "^0.3.1",
|
||||
"angular-mocks": "^1.6.6",
|
||||
"angular-native-dragdrop": "^1.2.2",
|
||||
"angular-route": "^1.6.6",
|
||||
"angular-sanitize": "^1.6.6",
|
||||
"babel-polyfill": "^6.26.0",
|
||||
"brace": "^0.10.0",
|
||||
"clipboard": "^1.7.1",
|
||||
"eventemitter3": "^2.0.3",
|
||||
"file-saver": "^1.3.3",
|
||||
"jquery": "^3.2.1",
|
||||
"lodash": "^4.17.4",
|
||||
"moment": "^2.18.1",
|
||||
"mousetrap": "^1.6.0",
|
||||
"ngreact": "^0.4.1",
|
||||
"react": "^16.0.0",
|
||||
"react-dom": "^16.0.0",
|
||||
"remarkable": "^1.7.1",
|
||||
"sinon": "1.17.6",
|
||||
"systemjs-builder": "^0.15.34",
|
||||
"rxjs": "^5.4.3",
|
||||
"tether": "^1.4.0",
|
||||
"tether-drop": "https://github.com/torkelo/drop",
|
||||
"tslint": "^5.1.0",
|
||||
"typescript": "^2.2.2",
|
||||
"virtual-scroll": "^1.1.1"
|
||||
"tinycolor2": "^1.4.1"
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
#! /usr/bin/env bash
|
||||
version=4.5.1
|
||||
version=4.5.2
|
||||
|
||||
wget https://s3-us-west-2.amazonaws.com/grafana-releases/release/grafana_${version}_amd64.deb
|
||||
|
||||
@@ -8,15 +8,15 @@ package_cloud push grafana/stable/debian/wheezy grafana_${version}_amd64.deb
|
||||
package_cloud push grafana/stable/debian/stretch grafana_${version}_amd64.deb
|
||||
|
||||
package_cloud push grafana/testing/debian/jessie grafana_${version}_amd64.deb
|
||||
package_cloud push grafana/testing/debian/wheezy grafana_${version}_amd64.deb
|
||||
package_cloud push grafana/testing/debian/stretch grafana_${version}_amd64.deb
|
||||
package_cloud push grafana/testing/debian/wheezy grafana_${version}_amd64.deb --verbose
|
||||
package_cloud push grafana/testing/debian/stretch grafana_${version}_amd64.deb --verbose
|
||||
|
||||
wget https://s3-us-west-2.amazonaws.com/grafana-releases/release/grafana-${version}-1.x86_64.rpm
|
||||
|
||||
package_cloud push grafana/testing/el/6 grafana-${version}-1.x86_64.rpm
|
||||
package_cloud push grafana/testing/el/7 grafana-${version}-1.x86_64.rpm
|
||||
package_cloud push grafana/testing/el/6 grafana-${version}-1.x86_64.rpm --verbose
|
||||
package_cloud push grafana/testing/el/7 grafana-${version}-1.x86_64.rpm --verbose
|
||||
|
||||
package_cloud push grafana/stable/el/7 grafana-${version}-1.x86_64.rpm
|
||||
package_cloud push grafana/stable/el/6 grafana-${version}-1.x86_64.rpm
|
||||
package_cloud push grafana/stable/el/7 grafana-${version}-1.x86_64.rpm --verbose
|
||||
package_cloud push grafana/stable/el/6 grafana-${version}-1.x86_64.rpm --verbose
|
||||
|
||||
rm grafana*.{deb,rpm}
|
||||
|
||||
@@ -1,10 +1,10 @@
|
||||
#! /usr/bin/env bash
|
||||
deb_ver=4.5.0-beta1
|
||||
rpm_ver=4.5.0-beta1
|
||||
deb_ver=4.6.0-beta3
|
||||
rpm_ver=4.6.0-beta3
|
||||
|
||||
# wget https://s3-us-west-2.amazonaws.com/grafana-releases/release/grafana_${deb_ver}_amd64.deb
|
||||
wget https://s3-us-west-2.amazonaws.com/grafana-releases/release/grafana_${deb_ver}_amd64.deb
|
||||
|
||||
# package_cloud push grafana/testing/debian/jessie grafana_${deb_ver}_amd64.deb
|
||||
package_cloud push grafana/testing/debian/jessie grafana_${deb_ver}_amd64.deb
|
||||
package_cloud push grafana/testing/debian/wheezy grafana_${deb_ver}_amd64.deb
|
||||
package_cloud push grafana/testing/debian/stretch grafana_${deb_ver}_amd64.deb
|
||||
|
||||
|
||||
@@ -1,7 +1,11 @@
|
||||
package api
|
||||
|
||||
import (
|
||||
"strings"
|
||||
"time"
|
||||
|
||||
"github.com/grafana/grafana/pkg/api/dtos"
|
||||
"github.com/grafana/grafana/pkg/components/simplejson"
|
||||
"github.com/grafana/grafana/pkg/middleware"
|
||||
"github.com/grafana/grafana/pkg/services/annotations"
|
||||
)
|
||||
@@ -11,13 +15,12 @@ func GetAnnotations(c *middleware.Context) Response {
|
||||
query := &annotations.ItemQuery{
|
||||
From: c.QueryInt64("from") / 1000,
|
||||
To: c.QueryInt64("to") / 1000,
|
||||
Type: annotations.ItemType(c.Query("type")),
|
||||
OrgId: c.OrgId,
|
||||
AlertId: c.QueryInt64("alertId"),
|
||||
DashboardId: c.QueryInt64("dashboardId"),
|
||||
PanelId: c.QueryInt64("panelId"),
|
||||
Limit: c.QueryInt64("limit"),
|
||||
NewState: c.QueryStrings("newState"),
|
||||
Tags: c.QueryStrings("tags"),
|
||||
}
|
||||
|
||||
repo := annotations.GetRepository()
|
||||
@@ -27,40 +30,45 @@ func GetAnnotations(c *middleware.Context) Response {
|
||||
return ApiError(500, "Failed to get annotations", err)
|
||||
}
|
||||
|
||||
result := make([]dtos.Annotation, 0)
|
||||
|
||||
for _, item := range items {
|
||||
result = append(result, dtos.Annotation{
|
||||
AlertId: item.AlertId,
|
||||
Time: item.Epoch * 1000,
|
||||
Data: item.Data,
|
||||
NewState: item.NewState,
|
||||
PrevState: item.PrevState,
|
||||
Text: item.Text,
|
||||
Metric: item.Metric,
|
||||
Title: item.Title,
|
||||
PanelId: item.PanelId,
|
||||
RegionId: item.RegionId,
|
||||
Type: string(item.Type),
|
||||
})
|
||||
if item.Email != "" {
|
||||
item.AvatarUrl = dtos.GetGravatarUrl(item.Email)
|
||||
}
|
||||
item.Time = item.Time * 1000
|
||||
}
|
||||
|
||||
return Json(200, result)
|
||||
return Json(200, items)
|
||||
}
|
||||
|
||||
type CreateAnnotationError struct {
|
||||
message string
|
||||
}
|
||||
|
||||
func (e *CreateAnnotationError) Error() string {
|
||||
return e.message
|
||||
}
|
||||
|
||||
func PostAnnotation(c *middleware.Context, cmd dtos.PostAnnotationsCmd) Response {
|
||||
repo := annotations.GetRepository()
|
||||
|
||||
if cmd.Text == "" {
|
||||
err := &CreateAnnotationError{"text field should not be empty"}
|
||||
return ApiError(500, "Failed to save annotation", err)
|
||||
}
|
||||
|
||||
item := annotations.Item{
|
||||
OrgId: c.OrgId,
|
||||
UserId: c.UserId,
|
||||
DashboardId: cmd.DashboardId,
|
||||
PanelId: cmd.PanelId,
|
||||
Epoch: cmd.Time / 1000,
|
||||
Title: cmd.Title,
|
||||
Text: cmd.Text,
|
||||
CategoryId: cmd.CategoryId,
|
||||
NewState: cmd.FillColor,
|
||||
Type: annotations.EventType,
|
||||
Data: cmd.Data,
|
||||
Tags: cmd.Tags,
|
||||
}
|
||||
|
||||
if item.Epoch == 0 {
|
||||
item.Epoch = time.Now().Unix()
|
||||
}
|
||||
|
||||
if err := repo.Save(&item); err != nil {
|
||||
@@ -71,12 +79,16 @@ func PostAnnotation(c *middleware.Context, cmd dtos.PostAnnotationsCmd) Response
|
||||
if cmd.IsRegion {
|
||||
item.RegionId = item.Id
|
||||
|
||||
if item.Data == nil {
|
||||
item.Data = simplejson.New()
|
||||
}
|
||||
|
||||
if err := repo.Update(&item); err != nil {
|
||||
return ApiError(500, "Failed set regionId on annotation", err)
|
||||
}
|
||||
|
||||
item.Id = 0
|
||||
item.Epoch = cmd.TimeEnd
|
||||
item.Epoch = cmd.TimeEnd / 1000
|
||||
|
||||
if err := repo.Save(&item); err != nil {
|
||||
return ApiError(500, "Failed save annotation for region end time", err)
|
||||
@@ -86,6 +98,100 @@ func PostAnnotation(c *middleware.Context, cmd dtos.PostAnnotationsCmd) Response
|
||||
return ApiSuccess("Annotation added")
|
||||
}
|
||||
|
||||
func formatGraphiteAnnotation(what string, data string) string {
|
||||
text := what
|
||||
if data != "" {
|
||||
text = text + "\n" + data
|
||||
}
|
||||
return text
|
||||
}
|
||||
|
||||
func PostGraphiteAnnotation(c *middleware.Context, cmd dtos.PostGraphiteAnnotationsCmd) Response {
|
||||
repo := annotations.GetRepository()
|
||||
|
||||
if cmd.What == "" {
|
||||
err := &CreateAnnotationError{"what field should not be empty"}
|
||||
return ApiError(500, "Failed to save Graphite annotation", err)
|
||||
}
|
||||
|
||||
if cmd.When == 0 {
|
||||
cmd.When = time.Now().Unix()
|
||||
}
|
||||
text := formatGraphiteAnnotation(cmd.What, cmd.Data)
|
||||
|
||||
// Support tags in prior to Graphite 0.10.0 format (string of tags separated by space)
|
||||
var tagsArray []string
|
||||
switch tags := cmd.Tags.(type) {
|
||||
case string:
|
||||
if tags != "" {
|
||||
tagsArray = strings.Split(tags, " ")
|
||||
} else {
|
||||
tagsArray = []string{}
|
||||
}
|
||||
case []interface{}:
|
||||
for _, t := range tags {
|
||||
if tagStr, ok := t.(string); ok {
|
||||
tagsArray = append(tagsArray, tagStr)
|
||||
} else {
|
||||
err := &CreateAnnotationError{"tag should be a string"}
|
||||
return ApiError(500, "Failed to save Graphite annotation", err)
|
||||
}
|
||||
}
|
||||
default:
|
||||
err := &CreateAnnotationError{"unsupported tags format"}
|
||||
return ApiError(500, "Failed to save Graphite annotation", err)
|
||||
}
|
||||
|
||||
item := annotations.Item{
|
||||
OrgId: c.OrgId,
|
||||
UserId: c.UserId,
|
||||
Epoch: cmd.When,
|
||||
Text: text,
|
||||
Tags: tagsArray,
|
||||
}
|
||||
|
||||
if err := repo.Save(&item); err != nil {
|
||||
return ApiError(500, "Failed to save Graphite annotation", err)
|
||||
}
|
||||
|
||||
return ApiSuccess("Graphite annotation added")
|
||||
}
|
||||
|
||||
func UpdateAnnotation(c *middleware.Context, cmd dtos.UpdateAnnotationsCmd) Response {
|
||||
annotationId := c.ParamsInt64(":annotationId")
|
||||
|
||||
repo := annotations.GetRepository()
|
||||
|
||||
item := annotations.Item{
|
||||
OrgId: c.OrgId,
|
||||
UserId: c.UserId,
|
||||
Id: annotationId,
|
||||
Epoch: cmd.Time / 1000,
|
||||
Text: cmd.Text,
|
||||
Tags: cmd.Tags,
|
||||
}
|
||||
|
||||
if err := repo.Update(&item); err != nil {
|
||||
return ApiError(500, "Failed to update annotation", err)
|
||||
}
|
||||
|
||||
if cmd.IsRegion {
|
||||
itemRight := item
|
||||
itemRight.RegionId = item.Id
|
||||
itemRight.Epoch = cmd.TimeEnd / 1000
|
||||
|
||||
// We don't know id of region right event, so set it to 0 and find then using query like
|
||||
// ... WHERE region_id = <item.RegionId> AND id != <item.RegionId> ...
|
||||
itemRight.Id = 0
|
||||
|
||||
if err := repo.Update(&itemRight); err != nil {
|
||||
return ApiError(500, "Failed to update annotation for region end time", err)
|
||||
}
|
||||
}
|
||||
|
||||
return ApiSuccess("Annotation updated")
|
||||
}
|
||||
|
||||
func DeleteAnnotations(c *middleware.Context, cmd dtos.DeleteAnnotationsCmd) Response {
|
||||
repo := annotations.GetRepository()
|
||||
|
||||
@@ -101,3 +207,33 @@ func DeleteAnnotations(c *middleware.Context, cmd dtos.DeleteAnnotationsCmd) Res
|
||||
|
||||
return ApiSuccess("Annotations deleted")
|
||||
}
|
||||
|
||||
func DeleteAnnotationById(c *middleware.Context) Response {
|
||||
repo := annotations.GetRepository()
|
||||
annotationId := c.ParamsInt64(":annotationId")
|
||||
|
||||
err := repo.Delete(&annotations.DeleteParams{
|
||||
Id: annotationId,
|
||||
})
|
||||
|
||||
if err != nil {
|
||||
return ApiError(500, "Failed to delete annotation", err)
|
||||
}
|
||||
|
||||
return ApiSuccess("Annotation deleted")
|
||||
}
|
||||
|
||||
func DeleteAnnotationRegion(c *middleware.Context) Response {
|
||||
repo := annotations.GetRepository()
|
||||
regionId := c.ParamsInt64(":regionId")
|
||||
|
||||
err := repo.Delete(&annotations.DeleteParams{
|
||||
RegionId: regionId,
|
||||
})
|
||||
|
||||
if err != nil {
|
||||
return ApiError(500, "Failed to delete annotation region", err)
|
||||
}
|
||||
|
||||
return ApiSuccess("Annotation region deleted")
|
||||
}
|
||||
|
||||
@@ -21,7 +21,7 @@ func (hs *HttpServer) registerRoutes() {
|
||||
// automatically set HEAD for every GET
|
||||
macaronR.SetAutoHead(true)
|
||||
|
||||
r := newRouteRegister(middleware.RequestMetrics)
|
||||
r := newRouteRegister(middleware.RequestMetrics, middleware.RequestTracing)
|
||||
|
||||
// not logged in views
|
||||
r.Get("/", reqSignedIn, Index)
|
||||
@@ -267,7 +267,7 @@ func (hs *HttpServer) registerRoutes() {
|
||||
|
||||
apiRoute.Group("/alerts", func(alertsRoute RouteRegister) {
|
||||
alertsRoute.Post("/test", bind(dtos.AlertTestCommand{}), wrap(AlertTest))
|
||||
alertsRoute.Post("/:alertId/pause", bind(dtos.PauseAlertCommand{}), wrap(PauseAlert), reqEditorRole)
|
||||
alertsRoute.Post("/:alertId/pause", reqEditorRole, bind(dtos.PauseAlertCommand{}), wrap(PauseAlert))
|
||||
alertsRoute.Get("/:alertId", ValidateOrgAlert, wrap(GetAlert))
|
||||
alertsRoute.Get("/", wrap(GetAlerts))
|
||||
alertsRoute.Get("/states-for-dashboard", wrap(GetAlertStatesForDashboard))
|
||||
@@ -289,6 +289,10 @@ func (hs *HttpServer) registerRoutes() {
|
||||
|
||||
apiRoute.Group("/annotations", func(annotationsRoute RouteRegister) {
|
||||
annotationsRoute.Post("/", bind(dtos.PostAnnotationsCmd{}), wrap(PostAnnotation))
|
||||
annotationsRoute.Delete("/:annotationId", wrap(DeleteAnnotationById))
|
||||
annotationsRoute.Put("/:annotationId", bind(dtos.UpdateAnnotationsCmd{}), wrap(UpdateAnnotation))
|
||||
annotationsRoute.Delete("/region/:regionId", wrap(DeleteAnnotationRegion))
|
||||
annotationsRoute.Post("/graphite", bind(dtos.PostGraphiteAnnotationsCmd{}), wrap(PostGraphiteAnnotation))
|
||||
}, reqEditorRole)
|
||||
|
||||
// error test
|
||||
|
||||
@@ -6,27 +6,33 @@ import (
|
||||
"net/http"
|
||||
"time"
|
||||
|
||||
"gopkg.in/macaron.v1"
|
||||
|
||||
"github.com/grafana/grafana/pkg/api/pluginproxy"
|
||||
"github.com/grafana/grafana/pkg/log"
|
||||
"github.com/grafana/grafana/pkg/middleware"
|
||||
m "github.com/grafana/grafana/pkg/models"
|
||||
"github.com/grafana/grafana/pkg/plugins"
|
||||
"github.com/grafana/grafana/pkg/setting"
|
||||
"github.com/grafana/grafana/pkg/util"
|
||||
macaron "gopkg.in/macaron.v1"
|
||||
)
|
||||
|
||||
var pluginProxyTransport = &http.Transport{
|
||||
TLSClientConfig: &tls.Config{InsecureSkipVerify: true},
|
||||
Proxy: http.ProxyFromEnvironment,
|
||||
Dial: (&net.Dialer{
|
||||
Timeout: 30 * time.Second,
|
||||
KeepAlive: 30 * time.Second,
|
||||
}).Dial,
|
||||
TLSHandshakeTimeout: 10 * time.Second,
|
||||
}
|
||||
var pluginProxyTransport *http.Transport
|
||||
|
||||
func InitAppPluginRoutes(r *macaron.Macaron) {
|
||||
pluginProxyTransport = &http.Transport{
|
||||
TLSClientConfig: &tls.Config{
|
||||
InsecureSkipVerify: setting.PluginAppsSkipVerifyTLS,
|
||||
Renegotiation: tls.RenegotiateFreelyAsClient,
|
||||
},
|
||||
Proxy: http.ProxyFromEnvironment,
|
||||
Dial: (&net.Dialer{
|
||||
Timeout: 30 * time.Second,
|
||||
KeepAlive: 30 * time.Second,
|
||||
DualStack: true,
|
||||
}).Dial,
|
||||
TLSHandshakeTimeout: 10 * time.Second,
|
||||
}
|
||||
|
||||
for _, plugin := range plugins.Apps {
|
||||
for _, route := range plugin.Routes {
|
||||
url := util.JoinUrlFragments("/api/plugin-proxy/"+plugin.Id, route.Path)
|
||||
|
||||
@@ -65,7 +65,7 @@ func New(hash string) *Avatar {
|
||||
return &Avatar{
|
||||
hash: hash,
|
||||
reqParams: url.Values{
|
||||
"d": {"404"},
|
||||
"d": {"retro"},
|
||||
"size": {"200"},
|
||||
"r": {"pg"}}.Encode(),
|
||||
}
|
||||
@@ -146,7 +146,7 @@ func CacheServer() http.Handler {
|
||||
}
|
||||
|
||||
func newNotFound() *Avatar {
|
||||
avatar := &Avatar{}
|
||||
avatar := &Avatar{notFound: true}
|
||||
|
||||
// load transparent png into buffer
|
||||
path := filepath.Join(setting.StaticRootPath, "img", "transparent.png")
|
||||
|
||||
@@ -1,516 +0,0 @@
|
||||
package cloudwatch
|
||||
|
||||
import (
|
||||
"encoding/json"
|
||||
"errors"
|
||||
"fmt"
|
||||
"io/ioutil"
|
||||
"os"
|
||||
"strings"
|
||||
"sync"
|
||||
"time"
|
||||
|
||||
"github.com/aws/aws-sdk-go/aws"
|
||||
"github.com/aws/aws-sdk-go/aws/awsutil"
|
||||
"github.com/aws/aws-sdk-go/aws/credentials"
|
||||
"github.com/aws/aws-sdk-go/aws/credentials/ec2rolecreds"
|
||||
"github.com/aws/aws-sdk-go/aws/credentials/endpointcreds"
|
||||
"github.com/aws/aws-sdk-go/aws/ec2metadata"
|
||||
"github.com/aws/aws-sdk-go/aws/session"
|
||||
"github.com/aws/aws-sdk-go/service/cloudwatch"
|
||||
"github.com/aws/aws-sdk-go/service/ec2"
|
||||
"github.com/aws/aws-sdk-go/service/sts"
|
||||
"github.com/grafana/grafana/pkg/metrics"
|
||||
"github.com/grafana/grafana/pkg/middleware"
|
||||
m "github.com/grafana/grafana/pkg/models"
|
||||
)
|
||||
|
||||
type actionHandler func(*cwRequest, *middleware.Context)
|
||||
|
||||
var actionHandlers map[string]actionHandler
|
||||
|
||||
type cwRequest struct {
|
||||
Region string `json:"region"`
|
||||
Action string `json:"action"`
|
||||
Body []byte `json:"-"`
|
||||
DataSource *m.DataSource
|
||||
}
|
||||
|
||||
type datasourceInfo struct {
|
||||
Profile string
|
||||
Region string
|
||||
AuthType string
|
||||
AssumeRoleArn string
|
||||
Namespace string
|
||||
|
||||
AccessKey string
|
||||
SecretKey string
|
||||
}
|
||||
|
||||
func (req *cwRequest) GetDatasourceInfo() *datasourceInfo {
|
||||
authType := req.DataSource.JsonData.Get("authType").MustString()
|
||||
assumeRoleArn := req.DataSource.JsonData.Get("assumeRoleArn").MustString()
|
||||
accessKey := ""
|
||||
secretKey := ""
|
||||
|
||||
for key, value := range req.DataSource.SecureJsonData.Decrypt() {
|
||||
if key == "accessKey" {
|
||||
accessKey = value
|
||||
}
|
||||
if key == "secretKey" {
|
||||
secretKey = value
|
||||
}
|
||||
}
|
||||
|
||||
return &datasourceInfo{
|
||||
AuthType: authType,
|
||||
AssumeRoleArn: assumeRoleArn,
|
||||
Region: req.Region,
|
||||
Profile: req.DataSource.Database,
|
||||
AccessKey: accessKey,
|
||||
SecretKey: secretKey,
|
||||
}
|
||||
}
|
||||
|
||||
func init() {
|
||||
actionHandlers = map[string]actionHandler{
|
||||
"GetMetricStatistics": handleGetMetricStatistics,
|
||||
"ListMetrics": handleListMetrics,
|
||||
"DescribeAlarms": handleDescribeAlarms,
|
||||
"DescribeAlarmsForMetric": handleDescribeAlarmsForMetric,
|
||||
"DescribeAlarmHistory": handleDescribeAlarmHistory,
|
||||
"DescribeInstances": handleDescribeInstances,
|
||||
"__GetRegions": handleGetRegions,
|
||||
"__GetNamespaces": handleGetNamespaces,
|
||||
"__GetMetrics": handleGetMetrics,
|
||||
"__GetDimensions": handleGetDimensions,
|
||||
}
|
||||
}
|
||||
|
||||
type cache struct {
|
||||
credential *credentials.Credentials
|
||||
expiration *time.Time
|
||||
}
|
||||
|
||||
var awsCredentialCache map[string]cache = make(map[string]cache)
|
||||
var credentialCacheLock sync.RWMutex
|
||||
|
||||
func getCredentials(dsInfo *datasourceInfo) (*credentials.Credentials, error) {
|
||||
cacheKey := dsInfo.Profile + ":" + dsInfo.AssumeRoleArn
|
||||
credentialCacheLock.RLock()
|
||||
if _, ok := awsCredentialCache[cacheKey]; ok {
|
||||
if awsCredentialCache[cacheKey].expiration != nil &&
|
||||
(*awsCredentialCache[cacheKey].expiration).After(time.Now().UTC()) {
|
||||
result := awsCredentialCache[cacheKey].credential
|
||||
credentialCacheLock.RUnlock()
|
||||
return result, nil
|
||||
}
|
||||
}
|
||||
credentialCacheLock.RUnlock()
|
||||
|
||||
accessKeyId := ""
|
||||
secretAccessKey := ""
|
||||
sessionToken := ""
|
||||
var expiration *time.Time
|
||||
expiration = nil
|
||||
if dsInfo.AuthType == "arn" && strings.Index(dsInfo.AssumeRoleArn, "arn:aws:iam:") == 0 {
|
||||
params := &sts.AssumeRoleInput{
|
||||
RoleArn: aws.String(dsInfo.AssumeRoleArn),
|
||||
RoleSessionName: aws.String("GrafanaSession"),
|
||||
DurationSeconds: aws.Int64(900),
|
||||
}
|
||||
|
||||
stsSess, err := session.NewSession()
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
stsCreds := credentials.NewChainCredentials(
|
||||
[]credentials.Provider{
|
||||
&credentials.EnvProvider{},
|
||||
&credentials.SharedCredentialsProvider{Filename: "", Profile: dsInfo.Profile},
|
||||
remoteCredProvider(stsSess),
|
||||
})
|
||||
stsConfig := &aws.Config{
|
||||
Region: aws.String(dsInfo.Region),
|
||||
Credentials: stsCreds,
|
||||
}
|
||||
|
||||
sess, err := session.NewSession(stsConfig)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
svc := sts.New(sess, stsConfig)
|
||||
resp, err := svc.AssumeRole(params)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
if resp.Credentials != nil {
|
||||
accessKeyId = *resp.Credentials.AccessKeyId
|
||||
secretAccessKey = *resp.Credentials.SecretAccessKey
|
||||
sessionToken = *resp.Credentials.SessionToken
|
||||
expiration = resp.Credentials.Expiration
|
||||
}
|
||||
}
|
||||
|
||||
sess, err := session.NewSession()
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
creds := credentials.NewChainCredentials(
|
||||
[]credentials.Provider{
|
||||
&credentials.StaticProvider{Value: credentials.Value{
|
||||
AccessKeyID: accessKeyId,
|
||||
SecretAccessKey: secretAccessKey,
|
||||
SessionToken: sessionToken,
|
||||
}},
|
||||
&credentials.EnvProvider{},
|
||||
&credentials.StaticProvider{Value: credentials.Value{
|
||||
AccessKeyID: dsInfo.AccessKey,
|
||||
SecretAccessKey: dsInfo.SecretKey,
|
||||
}},
|
||||
&credentials.SharedCredentialsProvider{Filename: "", Profile: dsInfo.Profile},
|
||||
remoteCredProvider(sess),
|
||||
})
|
||||
|
||||
credentialCacheLock.Lock()
|
||||
awsCredentialCache[cacheKey] = cache{
|
||||
credential: creds,
|
||||
expiration: expiration,
|
||||
}
|
||||
credentialCacheLock.Unlock()
|
||||
|
||||
return creds, nil
|
||||
}
|
||||
|
||||
func remoteCredProvider(sess *session.Session) credentials.Provider {
|
||||
ecsCredURI := os.Getenv("AWS_CONTAINER_CREDENTIALS_RELATIVE_URI")
|
||||
|
||||
if len(ecsCredURI) > 0 {
|
||||
return ecsCredProvider(sess, ecsCredURI)
|
||||
}
|
||||
return ec2RoleProvider(sess)
|
||||
}
|
||||
|
||||
func ecsCredProvider(sess *session.Session, uri string) credentials.Provider {
|
||||
const host = `169.254.170.2`
|
||||
|
||||
c := ec2metadata.New(sess)
|
||||
return endpointcreds.NewProviderClient(
|
||||
c.Client.Config,
|
||||
c.Client.Handlers,
|
||||
fmt.Sprintf("http://%s%s", host, uri),
|
||||
func(p *endpointcreds.Provider) { p.ExpiryWindow = 5 * time.Minute })
|
||||
}
|
||||
|
||||
func ec2RoleProvider(sess *session.Session) credentials.Provider {
|
||||
return &ec2rolecreds.EC2RoleProvider{Client: ec2metadata.New(sess), ExpiryWindow: 5 * time.Minute}
|
||||
}
|
||||
|
||||
func getAwsConfig(req *cwRequest) (*aws.Config, error) {
|
||||
creds, err := getCredentials(req.GetDatasourceInfo())
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
cfg := &aws.Config{
|
||||
Region: aws.String(req.Region),
|
||||
Credentials: creds,
|
||||
}
|
||||
return cfg, nil
|
||||
}
|
||||
|
||||
func handleGetMetricStatistics(req *cwRequest, c *middleware.Context) {
|
||||
cfg, err := getAwsConfig(req)
|
||||
if err != nil {
|
||||
c.JsonApiErr(500, "Unable to call AWS API", err)
|
||||
return
|
||||
}
|
||||
sess, err := session.NewSession(cfg)
|
||||
if err != nil {
|
||||
c.JsonApiErr(500, "Unable to call AWS API", err)
|
||||
return
|
||||
}
|
||||
svc := cloudwatch.New(sess, cfg)
|
||||
|
||||
reqParam := &struct {
|
||||
Parameters struct {
|
||||
Namespace string `json:"namespace"`
|
||||
MetricName string `json:"metricName"`
|
||||
Dimensions []*cloudwatch.Dimension `json:"dimensions"`
|
||||
Statistics []*string `json:"statistics"`
|
||||
ExtendedStatistics []*string `json:"extendedStatistics"`
|
||||
StartTime int64 `json:"startTime"`
|
||||
EndTime int64 `json:"endTime"`
|
||||
Period int64 `json:"period"`
|
||||
} `json:"parameters"`
|
||||
}{}
|
||||
json.Unmarshal(req.Body, reqParam)
|
||||
|
||||
params := &cloudwatch.GetMetricStatisticsInput{
|
||||
Namespace: aws.String(reqParam.Parameters.Namespace),
|
||||
MetricName: aws.String(reqParam.Parameters.MetricName),
|
||||
Dimensions: reqParam.Parameters.Dimensions,
|
||||
StartTime: aws.Time(time.Unix(reqParam.Parameters.StartTime, 0)),
|
||||
EndTime: aws.Time(time.Unix(reqParam.Parameters.EndTime, 0)),
|
||||
Period: aws.Int64(reqParam.Parameters.Period),
|
||||
}
|
||||
if len(reqParam.Parameters.Statistics) != 0 {
|
||||
params.Statistics = reqParam.Parameters.Statistics
|
||||
}
|
||||
if len(reqParam.Parameters.ExtendedStatistics) != 0 {
|
||||
params.ExtendedStatistics = reqParam.Parameters.ExtendedStatistics
|
||||
}
|
||||
|
||||
resp, err := svc.GetMetricStatistics(params)
|
||||
if err != nil {
|
||||
c.JsonApiErr(500, "Unable to call AWS API", err)
|
||||
return
|
||||
}
|
||||
metrics.M_Aws_CloudWatch_GetMetricStatistics.Inc()
|
||||
|
||||
c.JSON(200, resp)
|
||||
}
|
||||
|
||||
func handleListMetrics(req *cwRequest, c *middleware.Context) {
|
||||
cfg, err := getAwsConfig(req)
|
||||
if err != nil {
|
||||
c.JsonApiErr(500, "Unable to call AWS API", err)
|
||||
return
|
||||
}
|
||||
sess, err := session.NewSession(cfg)
|
||||
if err != nil {
|
||||
c.JsonApiErr(500, "Unable to call AWS API", err)
|
||||
return
|
||||
}
|
||||
svc := cloudwatch.New(sess, cfg)
|
||||
|
||||
reqParam := &struct {
|
||||
Parameters struct {
|
||||
Namespace string `json:"namespace"`
|
||||
MetricName string `json:"metricName"`
|
||||
Dimensions []*cloudwatch.DimensionFilter `json:"dimensions"`
|
||||
} `json:"parameters"`
|
||||
}{}
|
||||
json.Unmarshal(req.Body, reqParam)
|
||||
|
||||
params := &cloudwatch.ListMetricsInput{
|
||||
Namespace: aws.String(reqParam.Parameters.Namespace),
|
||||
MetricName: aws.String(reqParam.Parameters.MetricName),
|
||||
Dimensions: reqParam.Parameters.Dimensions,
|
||||
}
|
||||
|
||||
var resp cloudwatch.ListMetricsOutput
|
||||
err = svc.ListMetricsPages(params,
|
||||
func(page *cloudwatch.ListMetricsOutput, lastPage bool) bool {
|
||||
metrics.M_Aws_CloudWatch_ListMetrics.Inc()
|
||||
metrics, _ := awsutil.ValuesAtPath(page, "Metrics")
|
||||
for _, metric := range metrics {
|
||||
resp.Metrics = append(resp.Metrics, metric.(*cloudwatch.Metric))
|
||||
}
|
||||
return !lastPage
|
||||
})
|
||||
if err != nil {
|
||||
c.JsonApiErr(500, "Unable to call AWS API", err)
|
||||
return
|
||||
}
|
||||
|
||||
c.JSON(200, resp)
|
||||
}
|
||||
|
||||
func handleDescribeAlarms(req *cwRequest, c *middleware.Context) {
|
||||
cfg, err := getAwsConfig(req)
|
||||
if err != nil {
|
||||
c.JsonApiErr(500, "Unable to call AWS API", err)
|
||||
return
|
||||
}
|
||||
sess, err := session.NewSession(cfg)
|
||||
if err != nil {
|
||||
c.JsonApiErr(500, "Unable to call AWS API", err)
|
||||
return
|
||||
}
|
||||
svc := cloudwatch.New(sess, cfg)
|
||||
|
||||
reqParam := &struct {
|
||||
Parameters struct {
|
||||
ActionPrefix string `json:"actionPrefix"`
|
||||
AlarmNamePrefix string `json:"alarmNamePrefix"`
|
||||
AlarmNames []*string `json:"alarmNames"`
|
||||
StateValue string `json:"stateValue"`
|
||||
} `json:"parameters"`
|
||||
}{}
|
||||
json.Unmarshal(req.Body, reqParam)
|
||||
|
||||
params := &cloudwatch.DescribeAlarmsInput{
|
||||
MaxRecords: aws.Int64(100),
|
||||
}
|
||||
if reqParam.Parameters.ActionPrefix != "" {
|
||||
params.ActionPrefix = aws.String(reqParam.Parameters.ActionPrefix)
|
||||
}
|
||||
if reqParam.Parameters.AlarmNamePrefix != "" {
|
||||
params.AlarmNamePrefix = aws.String(reqParam.Parameters.AlarmNamePrefix)
|
||||
}
|
||||
if len(reqParam.Parameters.AlarmNames) != 0 {
|
||||
params.AlarmNames = reqParam.Parameters.AlarmNames
|
||||
}
|
||||
if reqParam.Parameters.StateValue != "" {
|
||||
params.StateValue = aws.String(reqParam.Parameters.StateValue)
|
||||
}
|
||||
|
||||
resp, err := svc.DescribeAlarms(params)
|
||||
if err != nil {
|
||||
c.JsonApiErr(500, "Unable to call AWS API", err)
|
||||
return
|
||||
}
|
||||
|
||||
c.JSON(200, resp)
|
||||
}
|
||||
|
||||
func handleDescribeAlarmsForMetric(req *cwRequest, c *middleware.Context) {
|
||||
cfg, err := getAwsConfig(req)
|
||||
if err != nil {
|
||||
c.JsonApiErr(500, "Unable to call AWS API", err)
|
||||
return
|
||||
}
|
||||
sess, err := session.NewSession(cfg)
|
||||
if err != nil {
|
||||
c.JsonApiErr(500, "Unable to call AWS API", err)
|
||||
return
|
||||
}
|
||||
svc := cloudwatch.New(sess, cfg)
|
||||
|
||||
reqParam := &struct {
|
||||
Parameters struct {
|
||||
Namespace string `json:"namespace"`
|
||||
MetricName string `json:"metricName"`
|
||||
Dimensions []*cloudwatch.Dimension `json:"dimensions"`
|
||||
Statistic string `json:"statistic"`
|
||||
ExtendedStatistic string `json:"extendedStatistic"`
|
||||
Period int64 `json:"period"`
|
||||
} `json:"parameters"`
|
||||
}{}
|
||||
json.Unmarshal(req.Body, reqParam)
|
||||
|
||||
params := &cloudwatch.DescribeAlarmsForMetricInput{
|
||||
Namespace: aws.String(reqParam.Parameters.Namespace),
|
||||
MetricName: aws.String(reqParam.Parameters.MetricName),
|
||||
Period: aws.Int64(reqParam.Parameters.Period),
|
||||
}
|
||||
if len(reqParam.Parameters.Dimensions) != 0 {
|
||||
params.Dimensions = reqParam.Parameters.Dimensions
|
||||
}
|
||||
if reqParam.Parameters.Statistic != "" {
|
||||
params.Statistic = aws.String(reqParam.Parameters.Statistic)
|
||||
}
|
||||
if reqParam.Parameters.ExtendedStatistic != "" {
|
||||
params.ExtendedStatistic = aws.String(reqParam.Parameters.ExtendedStatistic)
|
||||
}
|
||||
|
||||
resp, err := svc.DescribeAlarmsForMetric(params)
|
||||
if err != nil {
|
||||
c.JsonApiErr(500, "Unable to call AWS API", err)
|
||||
return
|
||||
}
|
||||
|
||||
c.JSON(200, resp)
|
||||
}
|
||||
|
||||
func handleDescribeAlarmHistory(req *cwRequest, c *middleware.Context) {
|
||||
cfg, err := getAwsConfig(req)
|
||||
if err != nil {
|
||||
c.JsonApiErr(500, "Unable to call AWS API", err)
|
||||
return
|
||||
}
|
||||
sess, err := session.NewSession(cfg)
|
||||
if err != nil {
|
||||
c.JsonApiErr(500, "Unable to call AWS API", err)
|
||||
return
|
||||
}
|
||||
svc := cloudwatch.New(sess, cfg)
|
||||
|
||||
reqParam := &struct {
|
||||
Parameters struct {
|
||||
AlarmName string `json:"alarmName"`
|
||||
HistoryItemType string `json:"historyItemType"`
|
||||
StartDate int64 `json:"startDate"`
|
||||
EndDate int64 `json:"endDate"`
|
||||
} `json:"parameters"`
|
||||
}{}
|
||||
json.Unmarshal(req.Body, reqParam)
|
||||
|
||||
params := &cloudwatch.DescribeAlarmHistoryInput{
|
||||
AlarmName: aws.String(reqParam.Parameters.AlarmName),
|
||||
StartDate: aws.Time(time.Unix(reqParam.Parameters.StartDate, 0)),
|
||||
EndDate: aws.Time(time.Unix(reqParam.Parameters.EndDate, 0)),
|
||||
}
|
||||
if reqParam.Parameters.HistoryItemType != "" {
|
||||
params.HistoryItemType = aws.String(reqParam.Parameters.HistoryItemType)
|
||||
}
|
||||
|
||||
resp, err := svc.DescribeAlarmHistory(params)
|
||||
if err != nil {
|
||||
c.JsonApiErr(500, "Unable to call AWS API", err)
|
||||
return
|
||||
}
|
||||
|
||||
c.JSON(200, resp)
|
||||
}
|
||||
|
||||
func handleDescribeInstances(req *cwRequest, c *middleware.Context) {
|
||||
cfg, err := getAwsConfig(req)
|
||||
if err != nil {
|
||||
c.JsonApiErr(500, "Unable to call AWS API", err)
|
||||
return
|
||||
}
|
||||
sess, err := session.NewSession(cfg)
|
||||
if err != nil {
|
||||
c.JsonApiErr(500, "Unable to call AWS API", err)
|
||||
return
|
||||
}
|
||||
svc := ec2.New(sess, cfg)
|
||||
|
||||
reqParam := &struct {
|
||||
Parameters struct {
|
||||
Filters []*ec2.Filter `json:"filters"`
|
||||
InstanceIds []*string `json:"instanceIds"`
|
||||
} `json:"parameters"`
|
||||
}{}
|
||||
json.Unmarshal(req.Body, reqParam)
|
||||
|
||||
params := &ec2.DescribeInstancesInput{}
|
||||
if len(reqParam.Parameters.Filters) > 0 {
|
||||
params.Filters = reqParam.Parameters.Filters
|
||||
}
|
||||
if len(reqParam.Parameters.InstanceIds) > 0 {
|
||||
params.InstanceIds = reqParam.Parameters.InstanceIds
|
||||
}
|
||||
|
||||
var resp ec2.DescribeInstancesOutput
|
||||
err = svc.DescribeInstancesPages(params,
|
||||
func(page *ec2.DescribeInstancesOutput, lastPage bool) bool {
|
||||
reservations, _ := awsutil.ValuesAtPath(page, "Reservations")
|
||||
for _, reservation := range reservations {
|
||||
resp.Reservations = append(resp.Reservations, reservation.(*ec2.Reservation))
|
||||
}
|
||||
return !lastPage
|
||||
})
|
||||
if err != nil {
|
||||
c.JsonApiErr(500, "Unable to call AWS API", err)
|
||||
return
|
||||
}
|
||||
|
||||
c.JSON(200, resp)
|
||||
}
|
||||
|
||||
func HandleRequest(c *middleware.Context, ds *m.DataSource) {
|
||||
var req cwRequest
|
||||
req.Body, _ = ioutil.ReadAll(c.Req.Request.Body)
|
||||
req.DataSource = ds
|
||||
json.Unmarshal(req.Body, &req)
|
||||
|
||||
if handler, found := actionHandlers[req.Action]; !found {
|
||||
c.JsonApiErr(500, "Unexpected AWS Action", errors.New(req.Action))
|
||||
return
|
||||
} else {
|
||||
handler(&req, c)
|
||||
}
|
||||
}
|
||||
@@ -1,69 +0,0 @@
|
||||
package cloudwatch
|
||||
|
||||
import (
|
||||
"testing"
|
||||
|
||||
"github.com/aws/aws-sdk-go/aws"
|
||||
"github.com/aws/aws-sdk-go/service/cloudwatch"
|
||||
. "github.com/smartystreets/goconvey/convey"
|
||||
)
|
||||
|
||||
func TestCloudWatchMetrics(t *testing.T) {
|
||||
|
||||
Convey("When calling getMetricsForCustomMetrics", t, func() {
|
||||
dsInfo := &datasourceInfo{
|
||||
Region: "us-east-1",
|
||||
Namespace: "Foo",
|
||||
Profile: "default",
|
||||
AssumeRoleArn: "",
|
||||
}
|
||||
f := func(dsInfo *datasourceInfo) (cloudwatch.ListMetricsOutput, error) {
|
||||
return cloudwatch.ListMetricsOutput{
|
||||
Metrics: []*cloudwatch.Metric{
|
||||
{
|
||||
MetricName: aws.String("Test_MetricName"),
|
||||
Dimensions: []*cloudwatch.Dimension{
|
||||
{
|
||||
Name: aws.String("Test_DimensionName"),
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
}, nil
|
||||
}
|
||||
metrics, _ := getMetricsForCustomMetrics(dsInfo, f)
|
||||
|
||||
Convey("Should contain Test_MetricName", func() {
|
||||
So(metrics, ShouldContain, "Test_MetricName")
|
||||
})
|
||||
})
|
||||
|
||||
Convey("When calling getDimensionsForCustomMetrics", t, func() {
|
||||
dsInfo := &datasourceInfo{
|
||||
Region: "us-east-1",
|
||||
Namespace: "Foo",
|
||||
Profile: "default",
|
||||
AssumeRoleArn: "",
|
||||
}
|
||||
f := func(dsInfo *datasourceInfo) (cloudwatch.ListMetricsOutput, error) {
|
||||
return cloudwatch.ListMetricsOutput{
|
||||
Metrics: []*cloudwatch.Metric{
|
||||
{
|
||||
MetricName: aws.String("Test_MetricName"),
|
||||
Dimensions: []*cloudwatch.Dimension{
|
||||
{
|
||||
Name: aws.String("Test_DimensionName"),
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
}, nil
|
||||
}
|
||||
dimensionKeys, _ := getDimensionsForCustomMetrics(dsInfo, f)
|
||||
|
||||
Convey("Should contain Test_DimensionName", func() {
|
||||
So(dimensionKeys, ShouldContain, "Test_DimensionName")
|
||||
})
|
||||
})
|
||||
|
||||
}
|
||||
@@ -2,37 +2,37 @@ package dtos
|
||||
|
||||
import "github.com/grafana/grafana/pkg/components/simplejson"
|
||||
|
||||
type Annotation struct {
|
||||
AlertId int64 `json:"alertId"`
|
||||
DashboardId int64 `json:"dashboardId"`
|
||||
PanelId int64 `json:"panelId"`
|
||||
NewState string `json:"newState"`
|
||||
PrevState string `json:"prevState"`
|
||||
Time int64 `json:"time"`
|
||||
Title string `json:"title"`
|
||||
Text string `json:"text"`
|
||||
Metric string `json:"metric"`
|
||||
RegionId int64 `json:"regionId"`
|
||||
Type string `json:"type"`
|
||||
|
||||
Data *simplejson.Json `json:"data"`
|
||||
type PostAnnotationsCmd struct {
|
||||
DashboardId int64 `json:"dashboardId"`
|
||||
PanelId int64 `json:"panelId"`
|
||||
Time int64 `json:"time"`
|
||||
Text string `json:"text"`
|
||||
Tags []string `json:"tags"`
|
||||
Data *simplejson.Json `json:"data"`
|
||||
IsRegion bool `json:"isRegion"`
|
||||
TimeEnd int64 `json:"timeEnd"`
|
||||
}
|
||||
|
||||
type PostAnnotationsCmd struct {
|
||||
DashboardId int64 `json:"dashboardId"`
|
||||
PanelId int64 `json:"panelId"`
|
||||
CategoryId int64 `json:"categoryId"`
|
||||
Time int64 `json:"time"`
|
||||
Title string `json:"title"`
|
||||
Text string `json:"text"`
|
||||
|
||||
FillColor string `json:"fillColor"`
|
||||
IsRegion bool `json:"isRegion"`
|
||||
TimeEnd int64 `json:"timeEnd"`
|
||||
type UpdateAnnotationsCmd struct {
|
||||
Id int64 `json:"id"`
|
||||
Time int64 `json:"time"`
|
||||
Text string `json:"text"`
|
||||
Tags []string `json:"tags"`
|
||||
IsRegion bool `json:"isRegion"`
|
||||
TimeEnd int64 `json:"timeEnd"`
|
||||
}
|
||||
|
||||
type DeleteAnnotationsCmd struct {
|
||||
AlertId int64 `json:"alertId"`
|
||||
DashboardId int64 `json:"dashboardId"`
|
||||
PanelId int64 `json:"panelId"`
|
||||
AlertId int64 `json:"alertId"`
|
||||
DashboardId int64 `json:"dashboardId"`
|
||||
PanelId int64 `json:"panelId"`
|
||||
AnnotationId int64 `json:"annotationId"`
|
||||
RegionId int64 `json:"regionId"`
|
||||
}
|
||||
|
||||
type PostGraphiteAnnotationsCmd struct {
|
||||
When int64 `json:"when"`
|
||||
What string `json:"what"`
|
||||
Data string `json:"data"`
|
||||
Tags interface{} `json:"tags"`
|
||||
}
|
||||
|
||||
@@ -1,7 +1,6 @@
|
||||
package api
|
||||
|
||||
import (
|
||||
"crypto/tls"
|
||||
"net"
|
||||
"net/http"
|
||||
"net/http/httputil"
|
||||
@@ -14,11 +13,11 @@ import (
|
||||
)
|
||||
|
||||
var grafanaComProxyTransport = &http.Transport{
|
||||
TLSClientConfig: &tls.Config{InsecureSkipVerify: false},
|
||||
Proxy: http.ProxyFromEnvironment,
|
||||
Proxy: http.ProxyFromEnvironment,
|
||||
Dial: (&net.Dialer{
|
||||
Timeout: 30 * time.Second,
|
||||
KeepAlive: 30 * time.Second,
|
||||
DualStack: true,
|
||||
}).Dial,
|
||||
TLSHandshakeTimeout: 10 * time.Second,
|
||||
}
|
||||
|
||||
@@ -11,6 +11,8 @@ import (
|
||||
"path"
|
||||
"time"
|
||||
|
||||
"github.com/prometheus/client_golang/prometheus"
|
||||
|
||||
"github.com/prometheus/client_golang/prometheus/promhttp"
|
||||
|
||||
gocache "github.com/patrickmn/go-cache"
|
||||
@@ -19,7 +21,6 @@ import (
|
||||
"github.com/grafana/grafana/pkg/api/live"
|
||||
httpstatic "github.com/grafana/grafana/pkg/api/static"
|
||||
"github.com/grafana/grafana/pkg/bus"
|
||||
"github.com/grafana/grafana/pkg/cmd/grafana-cli/logger"
|
||||
"github.com/grafana/grafana/pkg/components/simplejson"
|
||||
"github.com/grafana/grafana/pkg/log"
|
||||
"github.com/grafana/grafana/pkg/middleware"
|
||||
@@ -153,7 +154,7 @@ func (hs *HttpServer) newMacaron() *macaron.Macaron {
|
||||
|
||||
for _, route := range plugins.StaticRoutes {
|
||||
pluginRoute := path.Join("/public/plugins/", route.PluginId)
|
||||
logger.Debug("Plugins: Adding route", "route", pluginRoute, "dir", route.Directory)
|
||||
hs.log.Debug("Plugins: Adding route", "route", pluginRoute, "dir", route.Directory)
|
||||
hs.mapStatic(m, route.Directory, "", pluginRoute)
|
||||
}
|
||||
|
||||
@@ -187,7 +188,8 @@ func (hs *HttpServer) metricsEndpoint(ctx *macaron.Context) {
|
||||
return
|
||||
}
|
||||
|
||||
promhttp.Handler().ServeHTTP(ctx.Resp, ctx.Req.Request)
|
||||
promhttp.HandlerFor(prometheus.DefaultGatherer, promhttp.HandlerOpts{}).
|
||||
ServeHTTP(ctx.Resp, ctx.Req.Request)
|
||||
}
|
||||
|
||||
func (hs *HttpServer) healthHandler(ctx *macaron.Context) {
|
||||
|
||||
@@ -8,7 +8,6 @@ import (
|
||||
"errors"
|
||||
"fmt"
|
||||
"io/ioutil"
|
||||
"log"
|
||||
"net/http"
|
||||
"net/url"
|
||||
|
||||
@@ -16,6 +15,7 @@ import (
|
||||
"golang.org/x/oauth2"
|
||||
|
||||
"github.com/grafana/grafana/pkg/bus"
|
||||
"github.com/grafana/grafana/pkg/log"
|
||||
"github.com/grafana/grafana/pkg/metrics"
|
||||
"github.com/grafana/grafana/pkg/middleware"
|
||||
m "github.com/grafana/grafana/pkg/models"
|
||||
@@ -29,6 +29,7 @@ var (
|
||||
ErrSignUpNotAllowed = errors.New("Signup is not allowed for this adapter")
|
||||
ErrUsersQuotaReached = errors.New("Users quota reached")
|
||||
ErrNoEmail = errors.New("Login provider didn't return an email address")
|
||||
oauthLogger = log.New("oauth.login")
|
||||
)
|
||||
|
||||
func GenStateString() string {
|
||||
@@ -50,10 +51,11 @@ func OAuthLogin(ctx *middleware.Context) {
|
||||
return
|
||||
}
|
||||
|
||||
error := ctx.Query("error")
|
||||
if error != "" {
|
||||
errorParam := ctx.Query("error")
|
||||
if errorParam != "" {
|
||||
errorDesc := ctx.Query("error_description")
|
||||
redirectWithError(ctx, ErrProviderDeniedRequest, "error", error, "errorDesc", errorDesc)
|
||||
oauthLogger.Error("failed to login ", "error", errorParam, "errorDesc", errorDesc)
|
||||
redirectWithError(ctx, ErrProviderDeniedRequest, "error", errorParam, "errorDesc", errorDesc)
|
||||
return
|
||||
}
|
||||
|
||||
@@ -69,8 +71,12 @@ func OAuthLogin(ctx *middleware.Context) {
|
||||
return
|
||||
}
|
||||
|
||||
// verify state string
|
||||
savedState := ctx.Session.Get(middleware.SESS_KEY_OAUTH_STATE).(string)
|
||||
savedState, ok := ctx.Session.Get(middleware.SESS_KEY_OAUTH_STATE).(string)
|
||||
if !ok {
|
||||
ctx.Handle(500, "login.OAuthLogin(missing saved state)", nil)
|
||||
return
|
||||
}
|
||||
|
||||
queryState := ctx.Query("state")
|
||||
if savedState != queryState {
|
||||
ctx.Handle(500, "login.OAuthLogin(state mismatch)", nil)
|
||||
@@ -78,36 +84,37 @@ func OAuthLogin(ctx *middleware.Context) {
|
||||
}
|
||||
|
||||
// handle call back
|
||||
tr := &http.Transport{
|
||||
TLSClientConfig: &tls.Config{
|
||||
InsecureSkipVerify: setting.OAuthService.OAuthInfos[name].TlsSkipVerify,
|
||||
},
|
||||
}
|
||||
oauthClient := &http.Client{
|
||||
Transport: tr,
|
||||
}
|
||||
|
||||
// initialize oauth2 context
|
||||
oauthCtx := oauth2.NoContext
|
||||
if setting.OAuthService.OAuthInfos[name].TlsClientCert != "" {
|
||||
if setting.OAuthService.OAuthInfos[name].TlsClientCert != "" || setting.OAuthService.OAuthInfos[name].TlsClientKey != "" {
|
||||
cert, err := tls.LoadX509KeyPair(setting.OAuthService.OAuthInfos[name].TlsClientCert, setting.OAuthService.OAuthInfos[name].TlsClientKey)
|
||||
if err != nil {
|
||||
log.Fatal(err)
|
||||
log.Fatal(1, "Failed to setup TlsClientCert", "oauth provider", name, "error", err)
|
||||
}
|
||||
|
||||
// Load CA cert
|
||||
tr.TLSClientConfig.Certificates = append(tr.TLSClientConfig.Certificates, cert)
|
||||
}
|
||||
|
||||
if setting.OAuthService.OAuthInfos[name].TlsClientCa != "" {
|
||||
caCert, err := ioutil.ReadFile(setting.OAuthService.OAuthInfos[name].TlsClientCa)
|
||||
if err != nil {
|
||||
log.Fatal(err)
|
||||
log.Fatal(1, "Failed to setup TlsClientCa", "oauth provider", name, "error", err)
|
||||
}
|
||||
caCertPool := x509.NewCertPool()
|
||||
caCertPool.AppendCertsFromPEM(caCert)
|
||||
|
||||
tr := &http.Transport{
|
||||
TLSClientConfig: &tls.Config{
|
||||
InsecureSkipVerify: true,
|
||||
Certificates: []tls.Certificate{cert},
|
||||
RootCAs: caCertPool,
|
||||
},
|
||||
}
|
||||
sslcli := &http.Client{Transport: tr}
|
||||
|
||||
oauthCtx = context.Background()
|
||||
oauthCtx = context.WithValue(oauthCtx, oauth2.HTTPClient, sslcli)
|
||||
tr.TLSClientConfig.RootCAs = caCertPool
|
||||
}
|
||||
|
||||
oauthCtx := context.WithValue(context.Background(), oauth2.HTTPClient, oauthClient)
|
||||
|
||||
// get token from provider
|
||||
token, err := connect.Exchange(oauthCtx, code)
|
||||
if err != nil {
|
||||
|
||||
@@ -31,7 +31,7 @@ func QueryMetrics(c *middleware.Context, reqDto dtos.MetricRequest) Response {
|
||||
return ApiError(500, "failed to fetch data source", err)
|
||||
}
|
||||
|
||||
request := &tsdb.Request{TimeRange: timeRange}
|
||||
request := &tsdb.TsdbQuery{TimeRange: timeRange}
|
||||
|
||||
for _, query := range reqDto.Queries {
|
||||
request.Queries = append(request.Queries, &tsdb.Query{
|
||||
@@ -43,7 +43,7 @@ func QueryMetrics(c *middleware.Context, reqDto dtos.MetricRequest) Response {
|
||||
})
|
||||
}
|
||||
|
||||
resp, err := tsdb.HandleRequest(context.Background(), request)
|
||||
resp, err := tsdb.HandleRequest(context.Background(), dsQuery.Result, request)
|
||||
if err != nil {
|
||||
return ApiError(500, "Metric request error", err)
|
||||
}
|
||||
@@ -98,18 +98,19 @@ func GetTestDataRandomWalk(c *middleware.Context) Response {
|
||||
intervalMs := c.QueryInt64("intervalMs")
|
||||
|
||||
timeRange := tsdb.NewTimeRange(from, to)
|
||||
request := &tsdb.Request{TimeRange: timeRange}
|
||||
request := &tsdb.TsdbQuery{TimeRange: timeRange}
|
||||
|
||||
dsInfo := &models.DataSource{Type: "grafana-testdata-datasource"}
|
||||
request.Queries = append(request.Queries, &tsdb.Query{
|
||||
RefId: "A",
|
||||
IntervalMs: intervalMs,
|
||||
Model: simplejson.NewFromAny(&util.DynMap{
|
||||
"scenario": "random_walk",
|
||||
}),
|
||||
DataSource: &models.DataSource{Type: "grafana-testdata-datasource"},
|
||||
DataSource: dsInfo,
|
||||
})
|
||||
|
||||
resp, err := tsdb.HandleRequest(context.Background(), request)
|
||||
resp, err := tsdb.HandleRequest(context.Background(), dsInfo, request)
|
||||
if err != nil {
|
||||
return ApiError(500, "Metric request error", err)
|
||||
}
|
||||
|
||||
@@ -15,7 +15,8 @@ import (
|
||||
"text/template"
|
||||
"time"
|
||||
|
||||
"github.com/grafana/grafana/pkg/api/cloudwatch"
|
||||
"github.com/opentracing/opentracing-go"
|
||||
|
||||
"github.com/grafana/grafana/pkg/log"
|
||||
"github.com/grafana/grafana/pkg/middleware"
|
||||
m "github.com/grafana/grafana/pkg/models"
|
||||
@@ -61,11 +62,6 @@ func NewDataSourceProxy(ds *m.DataSource, plugin *plugins.DataSourcePlugin, ctx
|
||||
}
|
||||
|
||||
func (proxy *DataSourceProxy) HandleRequest() {
|
||||
if proxy.ds.Type == m.DS_CLOUDWATCH {
|
||||
cloudwatch.HandleRequest(proxy.ctx, proxy.ds)
|
||||
return
|
||||
}
|
||||
|
||||
if err := proxy.validateRequest(); err != nil {
|
||||
proxy.ctx.JsonApiErr(403, err.Error(), nil)
|
||||
return
|
||||
@@ -85,6 +81,20 @@ func (proxy *DataSourceProxy) HandleRequest() {
|
||||
|
||||
proxy.logRequest()
|
||||
|
||||
span, ctx := opentracing.StartSpanFromContext(proxy.ctx.Req.Context(), "datasource reverse proxy")
|
||||
proxy.ctx.Req.Request = proxy.ctx.Req.WithContext(ctx)
|
||||
|
||||
defer span.Finish()
|
||||
span.SetTag("datasource_id", proxy.ds.Id)
|
||||
span.SetTag("datasource_type", proxy.ds.Type)
|
||||
span.SetTag("user_id", proxy.ctx.SignedInUser.UserId)
|
||||
span.SetTag("org_id", proxy.ctx.SignedInUser.OrgId)
|
||||
|
||||
opentracing.GlobalTracer().Inject(
|
||||
span.Context(),
|
||||
opentracing.HTTPHeaders,
|
||||
opentracing.HTTPHeadersCarrier(proxy.ctx.Req.Request.Header))
|
||||
|
||||
reverseProxy.ServeHTTP(proxy.ctx.Resp, proxy.ctx.Req.Request)
|
||||
proxy.ctx.Resp.Header().Del("Set-Cookie")
|
||||
}
|
||||
|
||||
@@ -158,7 +158,9 @@ func GetPluginMarkdown(c *middleware.Context) Response {
|
||||
|
||||
return ApiError(500, "Could not get markdown file", err)
|
||||
} else {
|
||||
return Respond(200, content)
|
||||
resp := Respond(200, content)
|
||||
resp.Header("Content-Type", "text/plain; charset=utf-8")
|
||||
return resp
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -8,6 +8,7 @@ import (
|
||||
|
||||
type Router interface {
|
||||
Handle(method, pattern string, handlers []macaron.Handler) *macaron.Route
|
||||
Get(pattern string, handlers ...macaron.Handler) *macaron.Route
|
||||
}
|
||||
|
||||
type RouteRegister interface {
|
||||
@@ -62,7 +63,14 @@ func (rr *routeRegister) Group(pattern string, fn func(rr RouteRegister), handle
|
||||
|
||||
func (rr *routeRegister) Register(router Router) *macaron.Router {
|
||||
for _, r := range rr.routes {
|
||||
router.Handle(r.method, r.pattern, r.handlers)
|
||||
// GET requests have to be added to macaron routing using Get()
|
||||
// Otherwise HEAD requests will not be allowed.
|
||||
// https://github.com/go-macaron/macaron/blob/a325110f8b392bce3e5cdeb8c44bf98078ada3be/router.go#L198
|
||||
if r.method == http.MethodGet {
|
||||
router.Get(r.pattern, r.handlers...)
|
||||
} else {
|
||||
router.Handle(r.method, r.pattern, r.handlers)
|
||||
}
|
||||
}
|
||||
|
||||
for _, g := range rr.groups {
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
package api
|
||||
|
||||
import (
|
||||
"net/http"
|
||||
"strconv"
|
||||
"testing"
|
||||
|
||||
@@ -21,6 +22,16 @@ func (fr *fakeRouter) Handle(method, pattern string, handlers []macaron.Handler)
|
||||
return &macaron.Route{}
|
||||
}
|
||||
|
||||
func (fr *fakeRouter) Get(pattern string, handlers ...macaron.Handler) *macaron.Route {
|
||||
fr.route = append(fr.route, route{
|
||||
pattern: pattern,
|
||||
method: http.MethodGet,
|
||||
handlers: handlers,
|
||||
})
|
||||
|
||||
return &macaron.Route{}
|
||||
}
|
||||
|
||||
func emptyHandlers(n int) []macaron.Handler {
|
||||
res := []macaron.Handler{}
|
||||
for i := 1; n >= i; i++ {
|
||||
|
||||
@@ -19,6 +19,7 @@ type CommandLine interface {
|
||||
|
||||
PluginDirectory() string
|
||||
RepoDirectory() string
|
||||
PluginURL() string
|
||||
}
|
||||
|
||||
type contextCommandLine struct {
|
||||
@@ -44,3 +45,7 @@ func (c *contextCommandLine) PluginDirectory() string {
|
||||
func (c *contextCommandLine) RepoDirectory() string {
|
||||
return c.GlobalString("repo")
|
||||
}
|
||||
|
||||
func (c *contextCommandLine) PluginURL() string {
|
||||
return c.GlobalString("pluginUrl")
|
||||
}
|
||||
|
||||
@@ -101,3 +101,7 @@ func (fcli *FakeCommandLine) RepoDirectory() string {
|
||||
func (fcli *FakeCommandLine) PluginDirectory() string {
|
||||
return fcli.GlobalString("pluginsDir")
|
||||
}
|
||||
|
||||
func (fcli *FakeCommandLine) PluginURL() string {
|
||||
return fcli.GlobalString("pluginUrl")
|
||||
}
|
||||
|
||||
@@ -58,37 +58,39 @@ func installCommand(c CommandLine) error {
|
||||
}
|
||||
|
||||
func InstallPlugin(pluginName, version string, c CommandLine) error {
|
||||
plugin, err := s.GetPlugin(pluginName, c.RepoDirectory())
|
||||
pluginFolder := c.PluginDirectory()
|
||||
if err != nil {
|
||||
return err
|
||||
downloadURL := c.PluginURL()
|
||||
if downloadURL == "" {
|
||||
plugin, err := s.GetPlugin(pluginName, c.RepoDirectory())
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
v, err := SelectVersion(plugin, version)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
if version == "" {
|
||||
version = v.Version
|
||||
}
|
||||
downloadURL = fmt.Sprintf("%s/%s/versions/%s/download",
|
||||
c.GlobalString("repo"),
|
||||
pluginName,
|
||||
version)
|
||||
}
|
||||
|
||||
v, err := SelectVersion(plugin, version)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
if version == "" {
|
||||
version = v.Version
|
||||
}
|
||||
|
||||
downloadURL := fmt.Sprintf("%s/%s/versions/%s/download",
|
||||
c.GlobalString("repo"),
|
||||
pluginName,
|
||||
version)
|
||||
|
||||
logger.Infof("installing %v @ %v\n", plugin.Id, version)
|
||||
logger.Infof("installing %v @ %v\n", pluginName, version)
|
||||
logger.Infof("from url: %v\n", downloadURL)
|
||||
logger.Infof("into: %v\n", pluginFolder)
|
||||
logger.Info("\n")
|
||||
|
||||
err = downloadFile(plugin.Id, pluginFolder, downloadURL)
|
||||
err := downloadFile(pluginName, pluginFolder, downloadURL)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
logger.Infof("%s Installed %s successfully \n", color.GreenString("✔"), plugin.Id)
|
||||
logger.Infof("%s Installed %s successfully \n", color.GreenString("✔"), pluginName)
|
||||
|
||||
res, _ := s.ReadPlugin(pluginFolder, pluginName)
|
||||
for _, v := range res.Dependencies.Plugins {
|
||||
|
||||
@@ -17,8 +17,6 @@ var version = "master"
|
||||
func main() {
|
||||
setupLogging()
|
||||
|
||||
services.Init(version)
|
||||
|
||||
app := cli.NewApp()
|
||||
app.Name = "Grafana cli"
|
||||
app.Usage = ""
|
||||
@@ -38,12 +36,26 @@ func main() {
|
||||
Value: "https://grafana.com/api/plugins",
|
||||
EnvVar: "GF_PLUGIN_REPO",
|
||||
},
|
||||
cli.StringFlag{
|
||||
Name: "pluginUrl",
|
||||
Usage: "Full url to the plugin zip file instead of downloading the plugin from grafana.com/api",
|
||||
Value: "",
|
||||
EnvVar: "GF_PLUGIN_URL",
|
||||
},
|
||||
cli.BoolFlag{
|
||||
Name: "insecure",
|
||||
Usage: "Skip TLS verification (insecure)",
|
||||
},
|
||||
cli.BoolFlag{
|
||||
Name: "debug, d",
|
||||
Usage: "enable debug logging",
|
||||
},
|
||||
}
|
||||
|
||||
app.Before = func(c *cli.Context) error {
|
||||
services.Init(version, c.GlobalBool("insecure"))
|
||||
return nil
|
||||
}
|
||||
app.Commands = commands.Commands
|
||||
app.CommandNotFound = cmdNotFound
|
||||
|
||||
|
||||
@@ -22,7 +22,7 @@ var (
|
||||
grafanaVersion string
|
||||
)
|
||||
|
||||
func Init(version string) {
|
||||
func Init(version string, skipTLSVerify bool) {
|
||||
grafanaVersion = version
|
||||
|
||||
tr := &http.Transport{
|
||||
@@ -30,13 +30,15 @@ func Init(version string) {
|
||||
DialContext: (&net.Dialer{
|
||||
Timeout: 30 * time.Second,
|
||||
KeepAlive: 30 * time.Second,
|
||||
DualStack: true,
|
||||
}).DialContext,
|
||||
MaxIdleConns: 100,
|
||||
IdleConnTimeout: 90 * time.Second,
|
||||
TLSHandshakeTimeout: 10 * time.Second,
|
||||
ExpectContinueTimeout: 1 * time.Second,
|
||||
|
||||
TLSClientConfig: &tls.Config{InsecureSkipVerify: false},
|
||||
TLSClientConfig: &tls.Config{
|
||||
InsecureSkipVerify: skipTLSVerify,
|
||||
},
|
||||
}
|
||||
|
||||
HttpClient = http.Client{
|
||||
|
||||
@@ -14,22 +14,24 @@ import (
|
||||
"net/http"
|
||||
_ "net/http/pprof"
|
||||
|
||||
"github.com/grafana/grafana/pkg/metrics"
|
||||
"github.com/grafana/grafana/pkg/models"
|
||||
"github.com/grafana/grafana/pkg/services/sqlstore"
|
||||
"github.com/grafana/grafana/pkg/setting"
|
||||
|
||||
_ "github.com/grafana/grafana/pkg/services/alerting/conditions"
|
||||
_ "github.com/grafana/grafana/pkg/services/alerting/notifiers"
|
||||
_ "github.com/grafana/grafana/pkg/tsdb/cloudwatch"
|
||||
_ "github.com/grafana/grafana/pkg/tsdb/graphite"
|
||||
_ "github.com/grafana/grafana/pkg/tsdb/influxdb"
|
||||
_ "github.com/grafana/grafana/pkg/tsdb/mqe"
|
||||
_ "github.com/grafana/grafana/pkg/tsdb/mysql"
|
||||
_ "github.com/grafana/grafana/pkg/tsdb/opentsdb"
|
||||
_ "github.com/grafana/grafana/pkg/tsdb/postgres"
|
||||
_ "github.com/grafana/grafana/pkg/tsdb/prometheus"
|
||||
_ "github.com/grafana/grafana/pkg/tsdb/testdata"
|
||||
)
|
||||
|
||||
var version = "4.1.0"
|
||||
var version = "4.6.0"
|
||||
var commit = "NA"
|
||||
var buildstamp string
|
||||
var build_date string
|
||||
@@ -80,6 +82,8 @@ func main() {
|
||||
setting.BuildCommit = commit
|
||||
setting.BuildStamp = buildstampInt64
|
||||
|
||||
metrics.M_Grafana_Version.WithLabelValues(version).Set(1)
|
||||
|
||||
server := NewGrafanaServer()
|
||||
server.Start()
|
||||
}
|
||||
|
||||
@@ -24,6 +24,7 @@ import (
|
||||
"github.com/grafana/grafana/pkg/services/search"
|
||||
"github.com/grafana/grafana/pkg/setting"
|
||||
"github.com/grafana/grafana/pkg/social"
|
||||
"github.com/grafana/grafana/pkg/tracing"
|
||||
)
|
||||
|
||||
func NewGrafanaServer() models.GrafanaServer {
|
||||
@@ -61,6 +62,14 @@ func (g *GrafanaServerImpl) Start() {
|
||||
eventpublisher.Init()
|
||||
plugins.Init()
|
||||
|
||||
closer, err := tracing.Init(setting.Cfg)
|
||||
if err != nil {
|
||||
g.log.Error("Tracing settings is not valid", "error", err)
|
||||
g.Shutdown(1, "Startup failed")
|
||||
return
|
||||
}
|
||||
defer closer.Close()
|
||||
|
||||
// init alerting
|
||||
if setting.AlertingEnabled && setting.ExecuteAlerts {
|
||||
engine := alerting.NewEngine()
|
||||
@@ -71,8 +80,8 @@ func (g *GrafanaServerImpl) Start() {
|
||||
cleanUpService := cleanup.NewCleanUpService()
|
||||
g.childRoutines.Go(func() error { return cleanUpService.Run(g.context) })
|
||||
|
||||
if err := notifications.Init(); err != nil {
|
||||
g.log.Error("Notification service failed to initialize", "erro", err)
|
||||
if err = notifications.Init(); err != nil {
|
||||
g.log.Error("Notification service failed to initialize", "error", err)
|
||||
g.Shutdown(1, "Startup failed")
|
||||
return
|
||||
}
|
||||
|
||||
88
pkg/components/imguploader/gcsuploader.go
Normal file
88
pkg/components/imguploader/gcsuploader.go
Normal file
@@ -0,0 +1,88 @@
|
||||
package imguploader
|
||||
|
||||
import (
|
||||
"context"
|
||||
"fmt"
|
||||
"io/ioutil"
|
||||
"net/http"
|
||||
"os"
|
||||
|
||||
"github.com/grafana/grafana/pkg/log"
|
||||
"github.com/grafana/grafana/pkg/util"
|
||||
"golang.org/x/oauth2/google"
|
||||
)
|
||||
|
||||
const (
|
||||
tokenUrl string = "https://www.googleapis.com/auth/devstorage.read_write"
|
||||
uploadUrl string = "https://www.googleapis.com/upload/storage/v1/b/%s/o?uploadType=media&name=%s&predefinedAcl=publicRead"
|
||||
)
|
||||
|
||||
type GCSUploader struct {
|
||||
keyFile string
|
||||
bucket string
|
||||
log log.Logger
|
||||
}
|
||||
|
||||
func NewGCSUploader(keyFile, bucket string) *GCSUploader {
|
||||
return &GCSUploader{
|
||||
keyFile: keyFile,
|
||||
bucket: bucket,
|
||||
log: log.New("gcsuploader"),
|
||||
}
|
||||
}
|
||||
|
||||
func (u *GCSUploader) Upload(ctx context.Context, imageDiskPath string) (string, error) {
|
||||
key := util.GetRandomString(20) + ".png"
|
||||
|
||||
u.log.Debug("Opening key file ", u.keyFile)
|
||||
data, err := ioutil.ReadFile(u.keyFile)
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
|
||||
u.log.Debug("Creating JWT conf")
|
||||
conf, err := google.JWTConfigFromJSON(data, tokenUrl)
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
|
||||
u.log.Debug("Creating HTTP client")
|
||||
client := conf.Client(ctx)
|
||||
err = u.uploadFile(client, imageDiskPath, key)
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
|
||||
return fmt.Sprintf("https://storage.googleapis.com/%s/%s", u.bucket, key), nil
|
||||
}
|
||||
|
||||
func (u *GCSUploader) uploadFile(client *http.Client, imageDiskPath, key string) error {
|
||||
u.log.Debug("Opening image file ", imageDiskPath)
|
||||
|
||||
fileReader, err := os.Open(imageDiskPath)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
reqUrl := fmt.Sprintf(uploadUrl, u.bucket, key)
|
||||
u.log.Debug("Request URL: ", reqUrl)
|
||||
|
||||
req, err := http.NewRequest("POST", reqUrl, fileReader)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
req.Header.Add("Content-Type", "image/png")
|
||||
u.log.Debug("Sending POST request to GCS")
|
||||
|
||||
resp, err := client.Do(req)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
if resp.StatusCode != 200 {
|
||||
return fmt.Errorf("GCS response status code %d", resp.StatusCode)
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
24
pkg/components/imguploader/gcsuploader_test.go
Normal file
24
pkg/components/imguploader/gcsuploader_test.go
Normal file
@@ -0,0 +1,24 @@
|
||||
package imguploader
|
||||
|
||||
import (
|
||||
"context"
|
||||
"testing"
|
||||
|
||||
"github.com/grafana/grafana/pkg/setting"
|
||||
. "github.com/smartystreets/goconvey/convey"
|
||||
)
|
||||
|
||||
func TestUploadToGCS(t *testing.T) {
|
||||
SkipConvey("[Integration test] for external_image_store.gcs", t, func() {
|
||||
setting.NewConfigContext(&setting.CommandLineArgs{
|
||||
HomePath: "../../../",
|
||||
})
|
||||
|
||||
gcsUploader, _ := NewImageUploader()
|
||||
|
||||
path, err := gcsUploader.Upload(context.Background(), "../../../public/img/logo_transparent_400x.png")
|
||||
|
||||
So(err, ShouldBeNil)
|
||||
So(path, ShouldNotEqual, "")
|
||||
})
|
||||
}
|
||||
@@ -1,6 +1,7 @@
|
||||
package imguploader
|
||||
|
||||
import (
|
||||
"context"
|
||||
"fmt"
|
||||
"regexp"
|
||||
|
||||
@@ -8,13 +9,13 @@ import (
|
||||
)
|
||||
|
||||
type ImageUploader interface {
|
||||
Upload(path string) (string, error)
|
||||
Upload(ctx context.Context, path string) (string, error)
|
||||
}
|
||||
|
||||
type NopImageUploader struct {
|
||||
}
|
||||
|
||||
func (NopImageUploader) Upload(path string) (string, error) {
|
||||
func (NopImageUploader) Upload(ctx context.Context, path string) (string, error) {
|
||||
return "", nil
|
||||
}
|
||||
|
||||
@@ -27,15 +28,27 @@ func NewImageUploader() (ImageUploader, error) {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
bucket := s3sec.Key("bucket").MustString("")
|
||||
region := s3sec.Key("region").MustString("")
|
||||
path := s3sec.Key("path").MustString("")
|
||||
bucketUrl := s3sec.Key("bucket_url").MustString("")
|
||||
accessKey := s3sec.Key("access_key").MustString("")
|
||||
secretKey := s3sec.Key("secret_key").MustString("")
|
||||
info, err := getRegionAndBucketFromUrl(bucketUrl)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
|
||||
if path != "" && path[len(path)-1:] != "/" {
|
||||
path += "/"
|
||||
}
|
||||
|
||||
return NewS3Uploader(info.region, info.bucket, "public-read", accessKey, secretKey), nil
|
||||
if bucket == "" || region == "" {
|
||||
info, err := getRegionAndBucketFromUrl(bucketUrl)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
bucket = info.bucket
|
||||
region = info.region
|
||||
}
|
||||
|
||||
return NewS3Uploader(region, bucket, path, "public-read", accessKey, secretKey), nil
|
||||
case "webdav":
|
||||
webdavSec, err := setting.Cfg.GetSection("external_image_storage.webdav")
|
||||
if err != nil {
|
||||
@@ -52,6 +65,16 @@ func NewImageUploader() (ImageUploader, error) {
|
||||
password := webdavSec.Key("password").String()
|
||||
|
||||
return NewWebdavImageUploader(url, username, password, public_url)
|
||||
case "gcs":
|
||||
gcssec, err := setting.Cfg.GetSection("external_image_storage.gcs")
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
keyFile := gcssec.Key("key_file").MustString("")
|
||||
bucketName := gcssec.Key("bucket").MustString("")
|
||||
|
||||
return NewGCSUploader(keyFile, bucketName), nil
|
||||
}
|
||||
|
||||
return NopImageUploader{}, nil
|
||||
|
||||
@@ -96,5 +96,28 @@ func TestImageUploaderFactory(t *testing.T) {
|
||||
So(original.username, ShouldEqual, "username")
|
||||
So(original.password, ShouldEqual, "password")
|
||||
})
|
||||
|
||||
Convey("GCS uploader", func() {
|
||||
var err error
|
||||
|
||||
setting.NewConfigContext(&setting.CommandLineArgs{
|
||||
HomePath: "../../../",
|
||||
})
|
||||
|
||||
setting.ImageUploadProvider = "gcs"
|
||||
|
||||
gcpSec, err := setting.Cfg.GetSection("external_image_storage.gcs")
|
||||
gcpSec.NewKey("key_file", "/etc/secrets/project-79a52befa3f6.json")
|
||||
gcpSec.NewKey("bucket", "project-grafana-east")
|
||||
|
||||
uploader, err := NewImageUploader()
|
||||
|
||||
So(err, ShouldBeNil)
|
||||
original, ok := uploader.(*GCSUploader)
|
||||
|
||||
So(ok, ShouldBeTrue)
|
||||
So(original.keyFile, ShouldEqual, "/etc/secrets/project-79a52befa3f6.json")
|
||||
So(original.bucket, ShouldEqual, "project-grafana-east")
|
||||
})
|
||||
})
|
||||
}
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
package imguploader
|
||||
|
||||
import (
|
||||
"context"
|
||||
"os"
|
||||
"time"
|
||||
|
||||
@@ -8,6 +9,7 @@ import (
|
||||
"github.com/aws/aws-sdk-go/aws/credentials"
|
||||
"github.com/aws/aws-sdk-go/aws/credentials/ec2rolecreds"
|
||||
"github.com/aws/aws-sdk-go/aws/ec2metadata"
|
||||
"github.com/aws/aws-sdk-go/aws/endpoints"
|
||||
"github.com/aws/aws-sdk-go/aws/session"
|
||||
"github.com/aws/aws-sdk-go/service/s3"
|
||||
"github.com/grafana/grafana/pkg/log"
|
||||
@@ -17,16 +19,18 @@ import (
|
||||
type S3Uploader struct {
|
||||
region string
|
||||
bucket string
|
||||
path string
|
||||
acl string
|
||||
secretKey string
|
||||
accessKey string
|
||||
log log.Logger
|
||||
}
|
||||
|
||||
func NewS3Uploader(region, bucket, acl, accessKey, secretKey string) *S3Uploader {
|
||||
func NewS3Uploader(region, bucket, path, acl, accessKey, secretKey string) *S3Uploader {
|
||||
return &S3Uploader{
|
||||
region: region,
|
||||
bucket: bucket,
|
||||
path: path,
|
||||
acl: acl,
|
||||
accessKey: accessKey,
|
||||
secretKey: secretKey,
|
||||
@@ -34,7 +38,7 @@ func NewS3Uploader(region, bucket, acl, accessKey, secretKey string) *S3Uploader
|
||||
}
|
||||
}
|
||||
|
||||
func (u *S3Uploader) Upload(imageDiskPath string) (string, error) {
|
||||
func (u *S3Uploader) Upload(ctx context.Context, imageDiskPath string) (string, error) {
|
||||
sess, err := session.NewSession()
|
||||
if err != nil {
|
||||
return "", err
|
||||
@@ -53,8 +57,10 @@ func (u *S3Uploader) Upload(imageDiskPath string) (string, error) {
|
||||
Credentials: creds,
|
||||
}
|
||||
|
||||
key := util.GetRandomString(20) + ".png"
|
||||
log.Debug("Uploading image to s3", "bucket = ", u.bucket, ", key = ", key)
|
||||
s3_endpoint, _ := endpoints.DefaultResolver().EndpointFor("s3", u.region)
|
||||
key := u.path + util.GetRandomString(20) + ".png"
|
||||
image_url := s3_endpoint.URL + "/" + u.bucket + "/" + key
|
||||
log.Debug("Uploading image to s3", "url = ", image_url)
|
||||
|
||||
file, err := os.Open(imageDiskPath)
|
||||
if err != nil {
|
||||
@@ -77,10 +83,5 @@ func (u *S3Uploader) Upload(imageDiskPath string) (string, error) {
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
|
||||
if u.region == "us-east-1" {
|
||||
return "https://" + u.bucket + ".s3.amazonaws.com/" + key, nil
|
||||
} else {
|
||||
return "https://" + u.bucket + ".s3-" + u.region + ".amazonaws.com/" + key, nil
|
||||
}
|
||||
return image_url, nil
|
||||
}
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
package imguploader
|
||||
|
||||
import (
|
||||
"context"
|
||||
"testing"
|
||||
|
||||
"github.com/grafana/grafana/pkg/setting"
|
||||
@@ -15,7 +16,7 @@ func TestUploadToS3(t *testing.T) {
|
||||
|
||||
s3Uploader, _ := NewImageUploader()
|
||||
|
||||
path, err := s3Uploader.Upload("../../../public/img/logo_transparent_400x.png")
|
||||
path, err := s3Uploader.Upload(context.Background(), "../../../public/img/logo_transparent_400x.png")
|
||||
|
||||
So(err, ShouldBeNil)
|
||||
So(path, ShouldNotEqual, "")
|
||||
|
||||
@@ -2,6 +2,7 @@ package imguploader
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"context"
|
||||
"fmt"
|
||||
"io/ioutil"
|
||||
"net"
|
||||
@@ -23,7 +24,8 @@ type WebdavUploader struct {
|
||||
var netTransport = &http.Transport{
|
||||
Proxy: http.ProxyFromEnvironment,
|
||||
Dial: (&net.Dialer{
|
||||
Timeout: 60 * time.Second,
|
||||
Timeout: 60 * time.Second,
|
||||
DualStack: true,
|
||||
}).Dial,
|
||||
TLSHandshakeTimeout: 5 * time.Second,
|
||||
}
|
||||
@@ -33,7 +35,7 @@ var netClient = &http.Client{
|
||||
Transport: netTransport,
|
||||
}
|
||||
|
||||
func (u *WebdavUploader) Upload(pa string) (string, error) {
|
||||
func (u *WebdavUploader) Upload(ctx context.Context, pa string) (string, error) {
|
||||
url, _ := url.Parse(u.url)
|
||||
filename := util.GetRandomString(20) + ".png"
|
||||
url.Path = path.Join(url.Path, filename)
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
package imguploader
|
||||
|
||||
import (
|
||||
"context"
|
||||
"testing"
|
||||
|
||||
. "github.com/smartystreets/goconvey/convey"
|
||||
@@ -11,7 +12,7 @@ func TestUploadToWebdav(t *testing.T) {
|
||||
// Can be tested with this docker container: https://hub.docker.com/r/morrisjobke/webdav/
|
||||
SkipConvey("[Integration test] for external_image_store.webdav", t, func() {
|
||||
webdavUploader, _ := NewWebdavImageUploader("http://localhost:8888/webdav/", "test", "test", "")
|
||||
path, err := webdavUploader.Upload("../../../public/img/logo_transparent_400x.png")
|
||||
path, err := webdavUploader.Upload(context.Background(), "../../../public/img/logo_transparent_400x.png")
|
||||
|
||||
So(err, ShouldBeNil)
|
||||
So(path, ShouldStartWith, "http://localhost:8888/webdav/")
|
||||
@@ -19,7 +20,7 @@ func TestUploadToWebdav(t *testing.T) {
|
||||
|
||||
SkipConvey("[Integration test] for external_image_store.webdav with public url", t, func() {
|
||||
webdavUploader, _ := NewWebdavImageUploader("http://localhost:8888/webdav/", "test", "test", "http://publicurl:8888/webdav")
|
||||
path, err := webdavUploader.Upload("../../../public/img/logo_transparent_400x.png")
|
||||
path, err := webdavUploader.Upload(context.Background(), "../../../public/img/logo_transparent_400x.png")
|
||||
|
||||
So(err, ShouldBeNil)
|
||||
So(path, ShouldStartWith, "http://publicurl:8888/webdav/")
|
||||
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user