diff --git a/docs/sources/installation/mac.md b/docs/sources/installation/mac.md index b68cadbd948..640db6d0d6f 100644 --- a/docs/sources/installation/mac.md +++ b/docs/sources/installation/mac.md @@ -24,4 +24,17 @@ brew update brew reinstall grafana ``` +------------- +You can also install the latest unstable grafana from git: + + +``` +brew install --HEAD grafana/grafana/grafana +``` + +To upgrade grafana if you've installed from HEAD: + +``` +brew reinstall --HEAD grafana/grafana/grafana +``` diff --git a/docs/sources/plugins/installation.md b/docs/sources/plugins/installation.md index c83a54bdce3..e8e69af6e00 100644 --- a/docs/sources/plugins/installation.md +++ b/docs/sources/plugins/installation.md @@ -9,7 +9,7 @@ page_keywords: grafana, plugins, documentation The easiest way to install plugins is by using the CLI tool grafana-cli which is bundled with grafana. Before any modification take place after modifying plugins, grafana-server needs to be restarted. ### Grafana plugin directory -On Linux systems the grafana-cli will assume that the grafana plugin directory is `/var/lib/grafana/plugins`. It's possible to override the directory which grafana-cli will operate on by specifying the --path flag. On Windows systems this parameter have to be specified for every call. +On Linux systems the grafana-cli will assume that the grafana plugin directory is `/var/lib/grafana/plugins`. It's possible to override the directory which grafana-cli will operate on by specifying the --pluginsDir flag. On Windows systems this parameter have to be specified for every call. ### Grafana-cli commands diff --git a/packaging/mac/bin/grafana b/packaging/mac/bin/grafana new file mode 100755 index 00000000000..fb33079079e --- /dev/null +++ b/packaging/mac/bin/grafana @@ -0,0 +1,26 @@ +#!/usr/bin/env bash +DAEMON=grafana-server +EXECUTABLE=/usr/local/bin/grafana-server +CONFIG=/usr/local/etc/grafana/grafana.ini +HOMEPATH=/usr/local/share/grafana +LOGPATH=/usr/local/var/log/grafana +DATAPATH=/usr/local/var/lib/grafana +PLUGINPATH=/usr/local/var/lib/grafana/plugins + +case "$1" in +start) + $EXECUTABLE --config=$CONFIG --homepath=$HOMEPATH cfg:default.paths.logs=$LOGPATH cfg:default.paths.data=$DATAPATH cfg:default.paths.plugins=$PLUGINPATH 2> /dev/null & + [ $? -eq 0 ] && echo "$DAEMON started" +;; +stop) + killall $DAEMON + [ $? -eq 0 ] && echo "$DAEMON stopped" +;; +restart) + $0 stop + $0 start +;; +*) + echo "Usage: $0 (start|stop|restart)" +;; +esac diff --git a/pkg/services/alerting/conditions/reducer.go b/pkg/services/alerting/conditions/reducer.go index d75d1ff9167..92ba268b13a 100644 --- a/pkg/services/alerting/conditions/reducer.go +++ b/pkg/services/alerting/conditions/reducer.go @@ -19,6 +19,29 @@ func (s *SimpleReducer) Reduce(series *tsdb.TimeSeries) float64 { value += point[0] } value = value / float64(len(series.Points)) + case "sum": + for _, point := range series.Points { + value += point[0] + } + case "min": + for i, point := range series.Points { + if i == 0 { + value = point[0] + } + + if value > point[0] { + value = point[0] + } + } + case "max": + for _, point := range series.Points { + if value < point[0] { + value = point[0] + } + } + case "mean": + meanPosition := int64(len(series.Points) / 2) + value = series.Points[meanPosition][0] } return value diff --git a/pkg/services/alerting/conditions/reducer_test.go b/pkg/services/alerting/conditions/reducer_test.go new file mode 100644 index 00000000000..ed3c89fbbdd --- /dev/null +++ b/pkg/services/alerting/conditions/reducer_test.go @@ -0,0 +1,54 @@ +package conditions + +import ( + "testing" + + "github.com/grafana/grafana/pkg/tsdb" + . "github.com/smartystreets/goconvey/convey" +) + +func TestSimpleReducer(t *testing.T) { + Convey("Test simple reducer by calculating", t, func() { + Convey("avg", func() { + result := testReducer("avg", 1, 2, 3) + So(result, ShouldEqual, float64(2)) + }) + + Convey("sum", func() { + result := testReducer("sum", 1, 2, 3) + So(result, ShouldEqual, float64(6)) + }) + + Convey("min", func() { + result := testReducer("min", 3, 2, 1) + So(result, ShouldEqual, float64(1)) + }) + + Convey("max", func() { + result := testReducer("max", 1, 2, 3) + So(result, ShouldEqual, float64(3)) + }) + + Convey("mean odd numbers", func() { + result := testReducer("mean", 1, 2, 3000) + So(result, ShouldEqual, float64(2)) + }) + + }) +} + +func testReducer(typ string, datapoints ...float64) float64 { + reducer := NewSimpleReducer(typ) + var timeserie [][2]float64 + dummieTimestamp := float64(521452145) + + for _, v := range datapoints { + timeserie = append(timeserie, [2]float64{v, dummieTimestamp}) + } + + tsdb := &tsdb.TimeSeries{ + Name: "test time serie", + Points: timeserie, + } + return reducer.Reduce(tsdb) +} diff --git a/pkg/services/alerting/evaluator.go b/pkg/services/alerting/evaluator.go deleted file mode 100644 index bed5ce9709b..00000000000 --- a/pkg/services/alerting/evaluator.go +++ /dev/null @@ -1,15 +0,0 @@ -package alerting - -type compareFn func(float64, float64) bool - -func evalCondition(level Level, result float64) bool { - return operators[level.Operator](result, level.Value) -} - -var operators = map[string]compareFn{ - ">": func(num1, num2 float64) bool { return num1 > num2 }, - ">=": func(num1, num2 float64) bool { return num1 >= num2 }, - "<": func(num1, num2 float64) bool { return num1 < num2 }, - "<=": func(num1, num2 float64) bool { return num1 <= num2 }, - "": func(num1, num2 float64) bool { return false }, -} diff --git a/public/app/core/services/alert_srv.ts b/public/app/core/services/alert_srv.ts index 135f6aa1e21..4ef329f975e 100644 --- a/public/app/core/services/alert_srv.ts +++ b/public/app/core/services/alert_srv.ts @@ -27,10 +27,21 @@ export class AlertSrv { this.set(alert[0], alert[1], 'success', 3000); }, this.$rootScope); + appEvents.on('alert-error', options => { + this.set(options[0], options[1], 'error', 7000); + }); + appEvents.on('confirm-modal', this.showConfirmModal.bind(this)); } set(title, text, severity, timeout) { + if (_.isObject(text)) { + console.log('alert error', text); + if (text.statusText) { + text = `HTTP Error (${text.status}) ${text.statusText}`; + } + } + var newAlert = { title: title || '', text: text || '', diff --git a/public/app/features/dashboard/dashnav/dashnav.ts b/public/app/features/dashboard/dashnav/dashnav.ts index 0f09ee5f5b9..23e490f3e81 100644 --- a/public/app/features/dashboard/dashnav/dashnav.ts +++ b/public/app/features/dashboard/dashnav/dashnav.ts @@ -209,7 +209,7 @@ export class DashNavCtrl { $scope.viewJson = function() { var clone = $scope.dashboard.getSaveModelClone(); var html = angular.toJson(clone, true); - var uri = "data:application/json," + encodeURIComponent(html); + var uri = "data:application/json;charset=utf-8," + encodeURIComponent(html); var newWindow = window.open(uri); }; diff --git a/public/app/plugins/datasource/graphite/query_ctrl.ts b/public/app/plugins/datasource/graphite/query_ctrl.ts index 84b71685f17..93977dc143d 100644 --- a/public/app/plugins/datasource/graphite/query_ctrl.ts +++ b/public/app/plugins/datasource/graphite/query_ctrl.ts @@ -9,6 +9,7 @@ import moment from 'moment'; import gfunc from './gfunc'; import {Parser} from './parser'; import {QueryCtrl} from 'app/plugins/sdk'; +import appEvents from 'app/core/app_events'; export class GraphiteQueryCtrl extends QueryCtrl { static templateUrl = 'partials/query.editor.html'; @@ -141,7 +142,7 @@ export class GraphiteQueryCtrl extends QueryCtrl { } } }).catch(err => { - this.error = err.message || 'Failed to issue metric query'; + appEvents.emit('alert-error', ['Error', err]); }); } @@ -178,7 +179,7 @@ export class GraphiteQueryCtrl extends QueryCtrl { altSegments.unshift(this.uiSegmentSrv.newSegment('*')); return altSegments; }).catch(err => { - this.error = err.message || 'Failed to issue metric query'; + appEvents.emit('alert-error', ['Error', err]); return []; }); }