diff --git a/circle.yml b/circle.yml
index bb22fcb0a6e..22dab8ab893 100644
--- a/circle.yml
+++ b/circle.yml
@@ -12,6 +12,8 @@ dependencies:
- mkdir -p ${GOPATH}/src/${ORG_PATH}
- ln -s ~/grafana ${GOPATH}/src/${ORG_PATH}
- go get github.com/tools/godep
+ - rm -rf node_modules
+ - npm install -g npm
- npm install
test:
@@ -25,3 +27,10 @@ test:
# js tests
- ./node_modules/grunt-cli/bin/grunt test
- npm run coveralls
+
+deployment:
+ master:
+ branch: master
+ owner: grafana
+ commands:
+ - ./trigger_grafana_packer.sh ${TRIGGER_GRAFANA_PACKER_CIRCLECI_TOKEN}
diff --git a/docker/blocks/elastic/elasticsearch/config/.placeholder b/docker/blocks/elastic/elasticsearch/config/.placeholder
new file mode 100644
index 00000000000..9ad266259c2
--- /dev/null
+++ b/docker/blocks/elastic/elasticsearch/config/.placeholder
@@ -0,0 +1 @@
+Ensure the existence of the parent folder.
diff --git a/docker/blocks/elastic/fig b/docker/blocks/elastic/fig
new file mode 100644
index 00000000000..498402ac7b0
--- /dev/null
+++ b/docker/blocks/elastic/fig
@@ -0,0 +1,6 @@
+elasticsearch:
+ image: elasticsearch:latest
+ command: elasticsearch -Des.network.host=0.0.0.0
+ ports:
+ - "9200:9200"
+ - "9300:9300"
diff --git a/docker/blocks/influxdb/Dockerfile b/docker/blocks/influxdb/Dockerfile
deleted file mode 100644
index 69d10992464..00000000000
--- a/docker/blocks/influxdb/Dockerfile
+++ /dev/null
@@ -1,16 +0,0 @@
-# influxdb
-
-FROM ubuntu
-
-RUN mkdir -p /opt/influxdb/shared/data
-
-ADD http://s3.amazonaws.com/influxdb/influxdb_0.8.8_amd64.deb /influx88.deb
-RUN dpkg -i /influx88.deb
-RUN rm -rf /opt/influxdb/shared/data
-
-ADD config.toml /opt/influxdb/shared/config.toml
-
-EXPOSE 8083 8086 2004
-
-ENTRYPOINT ["/usr/bin/influxdb"]
-CMD ["-config=/opt/influxdb/shared/config.toml"]
diff --git a/docker/blocks/influxdb/fig b/docker/blocks/influxdb/fig
index 3f247f756e2..931f8a2640a 100644
--- a/docker/blocks/influxdb/fig
+++ b/docker/blocks/influxdb/fig
@@ -1,5 +1,5 @@
influxdb:
- build: blocks/influxdb
+ image: tutum/influxdb:latest
ports:
- "2004:2004"
- "8083:8083"
diff --git a/docs/sources/reference/http_api.md b/docs/sources/reference/http_api.md
index 7cdd8a872d5..4b0dff6c4b6 100644
--- a/docs/sources/reference/http_api.md
+++ b/docs/sources/reference/http_api.md
@@ -1422,6 +1422,34 @@ Keys:
}
}
+### Grafana Stats
+
+`GET /api/admin/stats`
+
+**Example Request**:
+
+ GET /api/admin/stats
+ Accept: application/json
+ Content-Type: application/json
+ Authorization: Bearer eyJrIjoiT0tTcG1pUlY2RnVKZTFVaDFsNFZXdE9ZWmNrMkZYbk
+
+**Example Response**:
+
+ 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
+ }
+
### Global Users
`POST /api/admin/users`
diff --git a/docs/sources/reference/singlestat.md b/docs/sources/reference/singlestat.md
index 91724e89882..954bfeed8b7 100644
--- a/docs/sources/reference/singlestat.md
+++ b/docs/sources/reference/singlestat.md
@@ -31,7 +31,7 @@ The coloring options of the Singlestat Panel config allow you to dynamically cha
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 **3 comma-separated** values, corresponding to the three colors directly to the right.
+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.For example: Green, Orange, Red () will become Red, Orange, Green (
).
diff --git a/pkg/api/admin_settings.go b/pkg/api/admin.go
similarity index 66%
rename from pkg/api/admin_settings.go
rename to pkg/api/admin.go
index 1f800cfe558..d7f5a240416 100644
--- a/pkg/api/admin_settings.go
+++ b/pkg/api/admin.go
@@ -3,7 +3,9 @@ package api
import (
"strings"
+ "github.com/grafana/grafana/pkg/bus"
"github.com/grafana/grafana/pkg/middleware"
+ m "github.com/grafana/grafana/pkg/models"
"github.com/grafana/grafana/pkg/setting"
)
@@ -27,3 +29,15 @@ func AdminGetSettings(c *middleware.Context) {
c.JSON(200, settings)
}
+
+func AdminGetStats(c *middleware.Context) {
+
+ statsQuery := m.GetAdminStatsQuery{}
+
+ if err := bus.Dispatch(&statsQuery); err != nil {
+ c.JsonApiErr(500, "Failed to get admin stats from database", err)
+ return
+ }
+
+ c.JSON(200, statsQuery.Result)
+}
diff --git a/pkg/api/api.go b/pkg/api/api.go
index 8bbb64eb1b2..99ab8a51ff2 100644
--- a/pkg/api/api.go
+++ b/pkg/api/api.go
@@ -40,6 +40,7 @@ func Register(r *macaron.Macaron) {
r.Get("/admin/users/edit/:id", reqGrafanaAdmin, Index)
r.Get("/admin/orgs", reqGrafanaAdmin, Index)
r.Get("/admin/orgs/edit/:id", reqGrafanaAdmin, Index)
+ r.Get("/admin/stats", reqGrafanaAdmin, Index)
r.Get("/apps", reqSignedIn, Index)
r.Get("/apps/edit/*", reqSignedIn, Index)
@@ -210,6 +211,7 @@ func Register(r *macaron.Macaron) {
r.Delete("/users/:id", AdminDeleteUser)
r.Get("/users/:id/quotas", wrap(GetUserQuotas))
r.Put("/users/:id/quotas/:target", bind(m.UpdateUserQuotaCmd{}), wrap(UpdateUserQuota))
+ r.Get("/stats", AdminGetStats)
}, reqGrafanaAdmin)
// rendering
diff --git a/pkg/metrics/report_usage.go b/pkg/metrics/report_usage.go
index abda18d12b7..c5c2afed7f7 100644
--- a/pkg/metrics/report_usage.go
+++ b/pkg/metrics/report_usage.go
@@ -55,6 +55,7 @@ func sendUsageStats() {
metrics["stats.dashboards.count"] = statsQuery.Result.DashboardCount
metrics["stats.users.count"] = statsQuery.Result.UserCount
metrics["stats.orgs.count"] = statsQuery.Result.OrgCount
+ metrics["stats.playlist.count"] = statsQuery.Result.PlaylistCount
dsStats := m.GetDataSourceStatsQuery{}
if err := bus.Dispatch(&dsStats); err != nil {
diff --git a/pkg/models/stats.go b/pkg/models/stats.go
index 6a060137ac7..63f946b956b 100644
--- a/pkg/models/stats.go
+++ b/pkg/models/stats.go
@@ -4,6 +4,7 @@ type SystemStats struct {
DashboardCount int
UserCount int
OrgCount int
+ PlaylistCount int
}
type DataSourceStats struct {
@@ -18,3 +19,19 @@ type GetSystemStatsQuery struct {
type GetDataSourceStatsQuery struct {
Result []*DataSourceStats
}
+
+type AdminStats struct {
+ UserCount int `json:"user_count"`
+ OrgCount int `json:"org_count"`
+ DashboardCount int `json:"dashboard_count"`
+ DbSnapshotCount int `json:"db_snapshot_count"`
+ DbTagCount int `json:"db_tag_count"`
+ DataSourceCount int `json:"data_source_count"`
+ PlaylistCount int `json:"playlist_count"`
+ StarredDbCount int `json:"starred_db_count"`
+ GrafanaAdminCount int `json:"grafana_admin_count"`
+}
+
+type GetAdminStatsQuery struct {
+ Result *AdminStats
+}
diff --git a/pkg/services/sqlstore/stats.go b/pkg/services/sqlstore/stats.go
index 044aa185f19..0465c6999a9 100644
--- a/pkg/services/sqlstore/stats.go
+++ b/pkg/services/sqlstore/stats.go
@@ -8,6 +8,7 @@ import (
func init() {
bus.AddHandler("sql", GetSystemStats)
bus.AddHandler("sql", GetDataSourceStats)
+ bus.AddHandler("sql", GetAdminStats)
}
func GetDataSourceStats(query *m.GetDataSourceStatsQuery) error {
@@ -34,7 +35,11 @@ func GetSystemStats(query *m.GetSystemStatsQuery) error {
(
SELECT COUNT(*)
FROM ` + dialect.Quote("dashboard") + `
- ) AS dashboard_count
+ ) AS dashboard_count,
+ (
+ SELECT COUNT(*)
+ FROM ` + dialect.Quote("playlist") + `
+ ) AS playlist_count
`
var stats m.SystemStats
@@ -46,3 +51,54 @@ func GetSystemStats(query *m.GetSystemStatsQuery) error {
query.Result = &stats
return err
}
+
+func GetAdminStats(query *m.GetAdminStatsQuery) error {
+ var rawSql = `SELECT
+ (
+ SELECT COUNT(*)
+ FROM ` + dialect.Quote("user") + `
+ ) AS user_count,
+ (
+ SELECT COUNT(*)
+ FROM ` + dialect.Quote("org") + `
+ ) AS org_count,
+ (
+ SELECT COUNT(*)
+ FROM ` + dialect.Quote("dashboard") + `
+ ) AS dashboard_count,
+ (
+ SELECT COUNT(*)
+ FROM ` + dialect.Quote("dashboard_snapshot") + `
+ ) AS db_snapshot_count,
+ (
+ SELECT COUNT(*)
+ FROM ` + dialect.Quote("dashboard_tag") + `
+ ) AS db_tag_count,
+ (
+ SELECT COUNT(*)
+ FROM ` + dialect.Quote("data_source") + `
+ ) AS data_source_count,
+ (
+ SELECT COUNT(*)
+ FROM ` + dialect.Quote("playlist") + `
+ ) AS playlist_count,
+ (
+ SELECT COUNT (DISTINCT ` + dialect.Quote("dashboard_id") + ` )
+ FROM ` + dialect.Quote("star") + `
+ ) AS starred_db_count,
+ (
+ SELECT COUNT(*)
+ FROM ` + dialect.Quote("user") + `
+ WHERE ` + dialect.Quote("is_admin") + ` = 1
+ ) AS grafana_admin_count
+ `
+
+ var stats m.AdminStats
+ _, err := x.Sql(rawSql).Get(&stats)
+ if err != nil {
+ return err
+ }
+
+ query.Result = &stats
+ return err
+}
diff --git a/public/app/partials/search.html b/public/app/core/components/search/search.html
similarity index 50%
rename from public/app/partials/search.html
rename to public/app/core/components/search/search.html
index fab8fd9291a..7a78f1a4a3e 100644
--- a/public/app/partials/search.html
+++ b/public/app/core/components/search/search.html
@@ -1,24 +1,22 @@
-
Name | +Value | +
---|---|
Total dashboards | +{{ctrl.stats.dashboard_count}} | +
Total users | +{{ctrl.stats.user_count}} | +
Total grafana admins | +{{ctrl.stats.grafana_admin_count}} | +
Total organizations | +{{ctrl.stats.org_count}} | +
Total datasources | +{{ctrl.stats.data_source_count}} | +
Total playlists | +{{ctrl.stats.playlist_count}} | +
Total snapshots | +{{ctrl.stats.db_snapshot_count}} | +
Total dashboard tags | +{{ctrl.stats.db_tag_count}} | +
Total starred dashboards | +{{ctrl.stats.starred_db_count}} | +