diff --git a/CHANGELOG.md b/CHANGELOG.md index e97ab145c61..225e1db96ac 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -6,14 +6,15 @@ * **InfluxDB**: Support for policy selection in query editor, closes [#2018](https://github.com/grafana/grafana/issues/2018) ### Breaking changes -**Plugin API**: Both datasource and panel plugin api (and plugin.json schema) as been updated, requiring a minor update to plugins. See [plugin api](https://github.com/grafana/grafana/blob/master/public/app/plugins/plugin_api.md) for more info. -**InfluxDB 0.8.x** The data source for the old version of influxdb (0.8.x) is no longer included in default builds. Can easily be installed via improved plugin system, closes #3523 -**KairosDB** The data source is no longer included in default builds. Can easily be installed via improved plugin system, closes #3524 +* **Plugin API**: Both datasource and panel plugin api (and plugin.json schema) have been updated, requiring a minor update to plugins. See [plugin api](https://github.com/grafana/grafana/blob/master/public/app/plugins/plugin_api.md) for more info. +* **InfluxDB 0.8.x** The data source for the old version of influxdb (0.8.x) is no longer included in default builds, but can easily be installed via improved plugin system, closes [#3523](https://github.com/grafana/grafana/issues/3523) +* **KairosDB** The data source is no longer included in default builds, but can easily be installed via improved plugin system, closes [#3524](https://github.com/grafana/grafana/issues/3524) ### Enhancements * **Sessions**: Support for memcached as session storage, closes [#3458](https://github.com/grafana/grafana/pull/3458) * **mysql**: Grafana now supports ssl for mysql, closes [#3584](https://github.com/grafana/grafana/pull/3584) * **snapshot**: Annotations are now included in snapshots, closes [#3635](https://github.com/grafana/grafana/pull/3635) +* **Admin**: Admin can now have global overview of Grafana setup, closes [#3812](https://github.com/grafana/grafana/issues/3812) ### Bug fixes * **Playlist**: Fix for memory leak when running a playlist, closes [#3794](https://github.com/grafana/grafana/pull/3794) 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/graphite/Dockerfile b/docker/blocks/graphite/Dockerfile index 0c85798dd8a..aeac91b7a58 100644 --- a/docker/blocks/graphite/Dockerfile +++ b/docker/blocks/graphite/Dockerfile @@ -1,68 +1,50 @@ -from ubuntu:14.10 +from ubuntu:14.04 -run apt-get -y update +run apt-get -y update -run apt-get -y install software-properties-common - -run apt-get -y install python-software-properties &&\ - add-apt-repository ppa:chris-lea/node.js &&\ - apt-get -y update - -run apt-get -y install python-django-tagging python-simplejson python-memcache \ - python-ldap python-cairo python-django python-twisted \ - python-pysqlite2 python-support python-pip gunicorn \ - supervisor nginx-light nodejs git wget curl - -# Install statsd -run mkdir /src && git clone https://github.com/etsy/statsd.git /src/statsd +run apt-get -y install libcairo2-dev libffi-dev pkg-config python-dev python-pip fontconfig apache2 libapache2-mod-wsgi git-core collectd memcached gcc g++ make supervisor nginx-light gunicorn run cd /usr/local/src && git clone https://github.com/graphite-project/graphite-web.git run cd /usr/local/src && git clone https://github.com/graphite-project/carbon.git run cd /usr/local/src && git clone https://github.com/graphite-project/whisper.git run cd /usr/local/src/whisper && git checkout master && python setup.py install -run cd /usr/local/src/carbon && git checkout 0.9.x && python setup.py install -run cd /usr/local/src/graphite-web && git checkout 0.9.x && python check-dependencies.py; python setup.py install - -# statsd -add ./files/statsd_config.js /src/statsd/config.js +run cd /usr/local/src/carbon && git checkout 0.9.x && pip install -r requirements.txt; python setup.py install +run cd /usr/local/src/graphite-web && git checkout 0.9.x && pip install -r requirements.txt; python check-dependencies.py; python setup.py install # Add graphite config -add ./files/initial_data.json /opt/graphite/webapp/graphite/initial_data.json -add ./files/local_settings.py /opt/graphite/webapp/graphite/local_settings.py -add ./files/carbon.conf /opt/graphite/conf/carbon.conf -add ./files/storage-schemas.conf /opt/graphite/conf/storage-schemas.conf -add ./files/storage-aggregation.conf /opt/graphite/conf/storage-aggregation.conf -add ./files/events_views.py /opt/graphite/webapp/graphite/events/views.py +add ./files/initial_data.json /opt/graphite/webapp/graphite/initial_data.json +add ./files/local_settings.py /opt/graphite/webapp/graphite/local_settings.py +add ./files/carbon.conf /opt/graphite/conf/carbon.conf +add ./files/storage-schemas.conf /opt/graphite/conf/storage-schemas.conf +add ./files/storage-aggregation.conf /opt/graphite/conf/storage-aggregation.conf +add ./files/events_views.py /opt/graphite/webapp/graphite/events/views.py -run mkdir -p /opt/graphite/storage/whisper -run touch /opt/graphite/storage/graphite.db /opt/graphite/storage/index -run chown -R www-data /opt/graphite/storage -run chmod 0775 /opt/graphite/storage /opt/graphite/storage/whisper -run chmod 0664 /opt/graphite/storage/graphite.db -run cd /opt/graphite/webapp/graphite && python manage.py syncdb --noinput +run mkdir -p /opt/graphite/storage/whisper +run touch /opt/graphite/storage/graphite.db /opt/graphite/storage/index +run chown -R www-data /opt/graphite/storage +run chmod 0775 /opt/graphite/storage /opt/graphite/storage/whisper +run chmod 0664 /opt/graphite/storage/graphite.db +run cd /opt/graphite/webapp/graphite && python manage.py syncdb --noinput + +add ./files/my_htpasswd /etc/nginx/.htpasswd # Add system service config -add ./files/nginx.conf /etc/nginx/nginx.conf -add ./files/supervisord.conf /etc/supervisor/conf.d/supervisord.conf - +add ./files/nginx.conf /etc/nginx/nginx.conf +add ./files/supervisord.conf /etc/supervisor/conf.d/supervisord.conf +# Nginx +# # graphite -expose 80 +expose 80 # Carbon line receiver port -expose 2003 +expose 2003 # Carbon cache query port -expose 7002 +expose 7002 -# Statsd UDP port -expose 8125/udp -# Statsd Management port -expose 8126 - -VOLUME ["/var/lib/elasticsearch"] VOLUME ["/opt/graphite/storage/whisper"] VOLUME ["/var/lib/log/supervisor"] -cmd ["/usr/bin/supervisord"] +cmd ["/usr/bin/supervisord"] # vim:ts=8:noet: diff --git a/docker/blocks/graphite/fig b/docker/blocks/graphite/fig index 28e7d3c53a2..84da45341e1 100644 --- a/docker/blocks/graphite/fig +++ b/docker/blocks/graphite/fig @@ -1,4 +1,10 @@ graphite: build: blocks/graphite ports: - - "8776:80" + - "8080:80" + - "2003:2003" + volumes: + - /var/docker/gfdev/graphite:/opt/graphite/storage/whisper + - /etc/localtime:/etc/localtime:ro + - /etc/timezone:/etc/timezone:ro + diff --git a/docker/blocks/graphite/files/my_htpasswd b/docker/blocks/graphite/files/my_htpasswd new file mode 100644 index 00000000000..52a72d01b4c --- /dev/null +++ b/docker/blocks/graphite/files/my_htpasswd @@ -0,0 +1 @@ +grafana:$apr1$4R/20xhC$8t37jPP5dbcLr48btdkU// diff --git a/docker/blocks/graphite/files/supervisord.conf b/docker/blocks/graphite/files/supervisord.conf index 25ba39c8819..c9812bb16dc 100644 --- a/docker/blocks/graphite/files/supervisord.conf +++ b/docker/blocks/graphite/files/supervisord.conf @@ -24,10 +24,3 @@ stdout_logfile = /var/log/supervisor/%(program_name)s.log stderr_logfile = /var/log/supervisor/%(program_name)s.log autorestart = true -[program:statsd] -;user = www-data -command = /usr/bin/node /src/statsd/stats.js /src/statsd/config.js -stdout_logfile = /var/log/supervisor/%(program_name)s.log -stderr_logfile = /var/log/supervisor/%(program_name)s.log -autorestart = true - 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..92efab2015d 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( DISTINCT ( ` + dialect.Quote("term") + ` )) + 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 @@ -