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/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/models/stats.go b/pkg/models/stats.go
index 30c09deb768..63f946b956b 100644
--- a/pkg/models/stats.go
+++ b/pkg/models/stats.go
@@ -19,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 6c5dfaea906..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 {
@@ -50,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/core/components/sidemenu/sidemenu.ts b/public/app/core/components/sidemenu/sidemenu.ts
index d2a640c1345..8f0c57bfade 100644
--- a/public/app/core/components/sidemenu/sidemenu.ts
+++ b/public/app/core/components/sidemenu/sidemenu.ts
@@ -107,6 +107,12 @@ export class SideMenuCtrl {
url: this.getUrl("/admin/settings"),
});
+ this.mainLinks.push({
+ text: "Grafana stats",
+ icon: "fa fa-fw fa-bar-chart",
+ url: this.getUrl("/admin/stats"),
+ });
+
this.mainLinks.push({
text: "Global Users",
icon: "fa fa-fw fa-user",
@@ -118,6 +124,7 @@ export class SideMenuCtrl {
icon: "fa fa-fw fa-users",
url: this.getUrl("/admin/orgs"),
});
+
}
updateMenu() {
diff --git a/public/app/core/routes/all.js b/public/app/core/routes/all.js
index cc4d73ef708..d9726ee782c 100644
--- a/public/app/core/routes/all.js
+++ b/public/app/core/routes/all.js
@@ -112,6 +112,11 @@ define([
templateUrl: 'app/features/admin/partials/edit_org.html',
controller : 'AdminEditOrgCtrl',
})
+ .when('/admin/stats', {
+ templateUrl: 'app/features/admin/partials/stats.html',
+ controller : 'AdminStatsCtrl',
+ controllerAs: 'ctrl',
+ })
.when('/login', {
templateUrl: 'app/partials/login.html',
controller : 'LoginCtrl',
diff --git a/public/app/features/admin/adminStatsCtrl.ts b/public/app/features/admin/adminStatsCtrl.ts
new file mode 100644
index 00000000000..aa3ed6de343
--- /dev/null
+++ b/public/app/features/admin/adminStatsCtrl.ts
@@ -0,0 +1,18 @@
+///
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}} | +