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 @@ -