diff --git a/pkg/api/metrics.go b/pkg/api/metrics.go index c3bd9062737..0fa6003d67a 100644 --- a/pkg/api/metrics.go +++ b/pkg/api/metrics.go @@ -48,6 +48,7 @@ func GetTestDataScenarios(c *middleware.Context) Response { "id": scenario.Id, "name": scenario.Name, "description": scenario.Description, + "stringInput": scenario.StringInput, }) } diff --git a/pkg/tsdb/graphite/graphite.go b/pkg/tsdb/graphite/graphite.go index 5f14f0302f7..78685d52371 100644 --- a/pkg/tsdb/graphite/graphite.go +++ b/pkg/tsdb/graphite/graphite.go @@ -79,7 +79,7 @@ func (e *GraphiteExecutor) Execute(queries tsdb.QuerySlice, context *tsdb.QueryC } result.QueryResults = make(map[string]*tsdb.QueryResult) - queryRes := &tsdb.QueryResult{} + queryRes := tsdb.NewQueryResult() for _, series := range data { queryRes.Series = append(queryRes.Series, &tsdb.TimeSeries{ diff --git a/pkg/tsdb/models.go b/pkg/tsdb/models.go index 008be3efa28..bbf7bba7ac7 100644 --- a/pkg/tsdb/models.go +++ b/pkg/tsdb/models.go @@ -66,6 +66,12 @@ type TimePoint [2]null.Float type TimeSeriesPoints []TimePoint type TimeSeriesSlice []*TimeSeries +func NewQueryResult() *QueryResult { + return &QueryResult{ + Series: make(TimeSeriesSlice, 0), + } +} + func NewTimePoint(value float64, timestamp float64) TimePoint { return TimePoint{null.FloatFrom(value), null.FloatFrom(timestamp)} } diff --git a/pkg/tsdb/prometheus/prometheus.go b/pkg/tsdb/prometheus/prometheus.go index eba558ddaa7..f7e68662efa 100644 --- a/pkg/tsdb/prometheus/prometheus.go +++ b/pkg/tsdb/prometheus/prometheus.go @@ -132,7 +132,7 @@ func parseQuery(queries tsdb.QuerySlice, queryContext *tsdb.QueryContext) (*Prom func parseResponse(value pmodel.Value, query *PrometheusQuery) (map[string]*tsdb.QueryResult, error) { queryResults := make(map[string]*tsdb.QueryResult) - queryRes := &tsdb.QueryResult{} + queryRes := tsdb.NewQueryResult() data, ok := value.(pmodel.Matrix) if !ok { diff --git a/pkg/tsdb/testdata/scenarios.go b/pkg/tsdb/testdata/scenarios.go index f34db7ac931..e90b0d4df79 100644 --- a/pkg/tsdb/testdata/scenarios.go +++ b/pkg/tsdb/testdata/scenarios.go @@ -2,8 +2,11 @@ package testdata import ( "math/rand" + "strconv" + "strings" "time" + "github.com/grafana/grafana/pkg/log" "github.com/grafana/grafana/pkg/tsdb" ) @@ -12,6 +15,7 @@ type ScenarioHandler func(query *tsdb.Query, context *tsdb.QueryContext) *tsdb.Q type Scenario struct { Id string `json:"id"` Name string `json:"name"` + StringInput string `json:"stringOption"` Description string `json:"description"` Handler ScenarioHandler `json:"-"` } @@ -20,15 +24,17 @@ var ScenarioRegistry map[string]*Scenario func init() { ScenarioRegistry = make(map[string]*Scenario) - //logger := log.New("tsdb.testdata") + logger := log.New("tsdb.testdata") + + logger.Debug("Initializing TestData Scenario") registerScenario(&Scenario{ Id: "random_walk", Name: "Random Walk", Handler: func(query *tsdb.Query, context *tsdb.QueryContext) *tsdb.QueryResult { - to := context.TimeRange.MustGetTo().UnixNano() / int64(time.Millisecond) - timeWalkerMs := context.TimeRange.MustGetFrom().UnixNano() / int64(time.Millisecond) + timeWalkerMs := context.TimeRange.GetFromAsMsEpoch() + to := context.TimeRange.GetToAsMsEpoch() series := newSeriesForQuery(query) @@ -44,7 +50,7 @@ func init() { series.Points = points - queryRes := &tsdb.QueryResult{} + queryRes := tsdb.NewQueryResult() queryRes.Series = append(queryRes.Series, series) return queryRes }, @@ -54,9 +60,7 @@ func init() { Id: "no_data_points", Name: "No Data Points", Handler: func(query *tsdb.Query, context *tsdb.QueryContext) *tsdb.QueryResult { - return &tsdb.QueryResult{ - Series: make(tsdb.TimeSeriesSlice, 0), - } + return tsdb.NewQueryResult() }, }) @@ -64,7 +68,7 @@ func init() { Id: "datapoints_outside_range", Name: "Datapoints Outside Range", Handler: func(query *tsdb.Query, context *tsdb.QueryContext) *tsdb.QueryResult { - queryRes := &tsdb.QueryResult{} + queryRes := tsdb.NewQueryResult() series := newSeriesForQuery(query) outsideTime := context.TimeRange.MustGetFrom().Add(-1*time.Hour).Unix() * 1000 @@ -75,6 +79,41 @@ func init() { return queryRes }, }) + + registerScenario(&Scenario{ + Id: "csv_metric_values", + Name: "CSV Metric Values", + StringInput: "1,20,90,30,5,0", + Handler: func(query *tsdb.Query, context *tsdb.QueryContext) *tsdb.QueryResult { + queryRes := tsdb.NewQueryResult() + + stringInput := query.Model.Get("stringInput").MustString() + values := []float64{} + for _, strVal := range strings.Split(stringInput, ",") { + if val, err := strconv.ParseFloat(strVal, 64); err == nil { + values = append(values, val) + } + } + + if len(values) == 0 { + return queryRes + } + + series := newSeriesForQuery(query) + startTime := context.TimeRange.GetFromAsMsEpoch() + endTime := context.TimeRange.GetToAsMsEpoch() + step := (endTime - startTime) / int64(len(values)-1) + + for _, val := range values { + series.Points = append(series.Points, tsdb.NewTimePoint(val, float64(startTime))) + startTime += step + } + + queryRes.Series = append(queryRes.Series, series) + + return queryRes + }, + }) } func registerScenario(scenario *Scenario) { diff --git a/pkg/tsdb/time_range.go b/pkg/tsdb/time_range.go index 67227b834dc..cf6bc6a5048 100644 --- a/pkg/tsdb/time_range.go +++ b/pkg/tsdb/time_range.go @@ -21,6 +21,14 @@ type TimeRange struct { Now time.Time } +func (tr *TimeRange) GetFromAsMsEpoch() int64 { + return tr.MustGetFrom().UnixNano() / int64(time.Millisecond) +} + +func (tr *TimeRange) GetToAsMsEpoch() int64 { + return tr.MustGetTo().UnixNano() / int64(time.Millisecond) +} + func (tr *TimeRange) MustGetFrom() time.Time { if res, err := tr.ParseFrom(); err != nil { return time.Unix(0, 0) diff --git a/public/app/plugins/app/testdata/dashboards/alerts.json b/public/app/plugins/app/testdata/dashboards/alerts.json new file mode 100644 index 00000000000..c510ec220ea --- /dev/null +++ b/public/app/plugins/app/testdata/dashboards/alerts.json @@ -0,0 +1,286 @@ +{ + "title": "TestData - Alerts", + "tags": [ + "grafana-test" + ], + "style": "dark", + "timezone": "browser", + "editable": true, + "hideControls": false, + "sharedCrosshair": false, + "rows": [ + { + "collapse": false, + "editable": true, + "height": 255.625, + "panels": [ + { + "alert": { + "conditions": [ + { + "evaluator": { + "params": [ + 111 + ], + "type": "gt" + }, + "query": { + "params": [ + "A", + "5m", + "now" + ] + }, + "reducer": { + "params": [], + "type": "avg" + }, + "type": "query" + } + ], + "enabled": true, + "frequency": "60s", + "handler": 1, + "name": "Should always be green", + "noDataState": "no_data", + "notifications": [] + }, + "aliasColors": {}, + "bars": false, + "datasource": "Grafana TestData", + "editable": true, + "error": false, + "fill": 1, + "id": 3, + "isNew": true, + "legend": { + "avg": false, + "current": false, + "max": false, + "min": false, + "show": true, + "total": false, + "values": false + }, + "lines": true, + "linewidth": 2, + "links": [], + "nullPointMode": "connected", + "percentage": false, + "pointradius": 5, + "points": false, + "renderer": "flot", + "seriesOverrides": [], + "span": 6, + "stack": false, + "steppedLine": false, + "targets": [ + { + "refId": "A", + "scenario": "random_walk", + "scenarioId": "csv_metric_values", + "target": "", + "stringInput": "1,20,90,30,5,0" + } + ], + "thresholds": [ + { + "colorMode": "critical", + "fill": true, + "line": true, + "op": "gt", + "value": 111 + } + ], + "timeFrom": null, + "timeShift": null, + "title": "Always OK", + "tooltip": { + "msResolution": false, + "shared": true, + "sort": 0, + "value_type": "cumulative" + }, + "type": "graph", + "xaxis": { + "mode": "time", + "name": null, + "show": true, + "values": [] + }, + "yaxes": [ + { + "format": "short", + "label": "", + "logBase": 1, + "max": "125", + "min": "0", + "show": true + }, + { + "format": "short", + "label": null, + "logBase": 1, + "max": null, + "min": null, + "show": true + } + ] + }, + { + "alert": { + "conditions": [ + { + "evaluator": { + "params": [ + 177 + ], + "type": "gt" + }, + "query": { + "params": [ + "A", + "5m", + "now" + ] + }, + "reducer": { + "params": [], + "type": "avg" + }, + "type": "query" + } + ], + "enabled": true, + "frequency": "60s", + "handler": 1, + "name": "Always Alerting", + "noDataState": "no_data", + "notifications": [] + }, + "aliasColors": {}, + "bars": false, + "datasource": "Grafana TestData", + "editable": true, + "error": false, + "fill": 1, + "id": 4, + "isNew": true, + "legend": { + "avg": false, + "current": false, + "max": false, + "min": false, + "show": true, + "total": false, + "values": false + }, + "lines": true, + "linewidth": 2, + "links": [], + "nullPointMode": "connected", + "percentage": false, + "pointradius": 5, + "points": false, + "renderer": "flot", + "seriesOverrides": [], + "span": 6, + "stack": false, + "steppedLine": false, + "targets": [ + { + "refId": "A", + "scenario": "random_walk", + "scenarioId": "csv_metric_values", + "target": "", + "stringInput": "200,445,100,150,200,220,190" + } + ], + "thresholds": [ + { + "value": 177, + "op": "gt", + "fill": true, + "line": true, + "colorMode": "critical" + } + ], + "timeFrom": null, + "timeShift": null, + "title": "Always Alerting", + "tooltip": { + "msResolution": false, + "shared": true, + "sort": 0, + "value_type": "cumulative" + }, + "type": "graph", + "xaxis": { + "mode": "time", + "name": null, + "show": true, + "values": [] + }, + "yaxes": [ + { + "format": "short", + "label": "", + "logBase": 1, + "max": null, + "min": "0", + "show": true + }, + { + "format": "short", + "label": "", + "logBase": 1, + "max": null, + "min": null, + "show": true + } + ] + } + ], + "title": "New row" + } + ], + "time": { + "from": "now-6h", + "to": "now" + }, + "timepicker": { + "refresh_intervals": [ + "5s", + "10s", + "30s", + "1m", + "5m", + "15m", + "30m", + "1h", + "2h", + "1d" + ], + "time_options": [ + "5m", + "15m", + "1h", + "6h", + "12h", + "24h", + "2d", + "7d", + "30d" + ] + }, + "templating": { + "list": [] + }, + "annotations": { + "list": [] + }, + "schemaVersion": 13, + "version": 10, + "links": [], + "gnetId": null +} diff --git a/public/app/plugins/app/testdata/dashboards/graph_last_1h.json b/public/app/plugins/app/testdata/dashboards/graph_last_1h.json index 0cca7b6c331..757dd48e50f 100644 --- a/public/app/plugins/app/testdata/dashboards/graph_last_1h.json +++ b/public/app/plugins/app/testdata/dashboards/graph_last_1h.json @@ -1,5 +1,5 @@ { - "revision": 3, + "revision": 4, "title": "TestData - Graph Panel Last 1h", "tags": [ "grafana-test" @@ -284,7 +284,7 @@ } ], "thresholds": [], - "timeFrom": "5d", + "timeFrom": "2s", "timeShift": null, "title": "Millisecond res x-axis and tooltip", "tooltip": { @@ -318,9 +318,126 @@ "show": true } ] + }, + { + "title": "", + "error": false, + "span": 4, + "editable": true, + "type": "text", + "isNew": true, + "id": 6, + "mode": "markdown", + "content": "Just verify that the tooltip time has millisecond resolution ", + "links": [] } ], "title": "New row" + }, + { + "title": "New row", + "height": 336, + "editable": true, + "collapse": false, + "panels": [ + { + "title": "2 yaxis and axis lables", + "error": false, + "span": 7.99561403508772, + "editable": true, + "type": "graph", + "isNew": true, + "id": 5, + "targets": [ + { + "target": "", + "refId": "A", + "scenarioId": "csv_metric_values", + "stringInput": "1,20,90,30,5,0" + }, + { + "target": "", + "refId": "B", + "scenarioId": "csv_metric_values", + "stringInput": "2000,3000,4000,1000,3000,10000" + } + ], + "datasource": "Grafana TestData", + "renderer": "flot", + "yaxes": [ + { + "label": "Perecent", + "show": true, + "logBase": 1, + "min": null, + "max": null, + "format": "percent" + }, + { + "label": "Pressure", + "show": true, + "logBase": 1, + "min": null, + "max": null, + "format": "short" + } + ], + "xaxis": { + "show": true, + "mode": "time", + "name": null, + "values": [] + }, + "lines": true, + "fill": 1, + "linewidth": 2, + "points": false, + "pointradius": 5, + "bars": false, + "stack": false, + "percentage": false, + "legend": { + "show": true, + "values": false, + "min": false, + "max": false, + "current": false, + "total": false, + "avg": false + }, + "nullPointMode": "connected", + "steppedLine": false, + "tooltip": { + "value_type": "cumulative", + "shared": true, + "sort": 0, + "msResolution": false + }, + "timeFrom": null, + "timeShift": null, + "aliasColors": {}, + "seriesOverrides": [ + { + "alias": "B-series", + "yaxis": 2 + } + ], + "thresholds": [], + "links": [] + }, + { + "title": "", + "error": false, + "span": 4.00438596491228, + "editable": true, + "type": "text", + "isNew": true, + "id": 7, + "mode": "markdown", + "content": "Verify that axis labels look ok", + "links": [] + } + ] } ], "time": { @@ -360,7 +477,7 @@ }, "refresh": false, "schemaVersion": 13, - "version": 4, + "version": 3, "links": [], "gnetId": null } diff --git a/public/app/plugins/app/testdata/datasource/datasource.ts b/public/app/plugins/app/testdata/datasource/datasource.ts index a06daa19262..e0846d99ab6 100644 --- a/public/app/plugins/app/testdata/datasource/datasource.ts +++ b/public/app/plugins/app/testdata/datasource/datasource.ts @@ -1,6 +1,7 @@ /// import _ from 'lodash'; +import angular from 'angular'; class TestDataDatasource { @@ -16,6 +17,8 @@ class TestDataDatasource { scenarioId: item.scenarioId, intervalMs: options.intervalMs, maxDataPoints: options.maxDataPoints, + stringInput: item.stringInput, + jsonInput: angular.fromJson(item.jsonInput), }; }); diff --git a/public/app/plugins/app/testdata/datasource/query_ctrl.ts b/public/app/plugins/app/testdata/datasource/query_ctrl.ts index f22ef8948ad..6b0ad93f26c 100644 --- a/public/app/plugins/app/testdata/datasource/query_ctrl.ts +++ b/public/app/plugins/app/testdata/datasource/query_ctrl.ts @@ -1,5 +1,7 @@ /// +import _ from 'lodash'; + import {TestDataDatasource} from './datasource'; import {QueryCtrl} from 'app/plugins/sdk'; @@ -7,6 +9,7 @@ export class TestDataQueryCtrl extends QueryCtrl { static templateUrl = 'partials/query.editor.html'; scenarioList: any; + scenario: any; /** @ngInject **/ constructor($scope, $injector, private backendSrv) { @@ -19,7 +22,14 @@ export class TestDataQueryCtrl extends QueryCtrl { $onInit() { return this.backendSrv.get('/api/tsdb/testdata/scenarios').then(res => { this.scenarioList = res; + this.scenario = _.find(this.scenarioList, {id: this.target.scenarioId}); }); } + + scenarioChanged() { + this.scenario = _.find(this.scenarioList, {id: this.target.scenarioId}); + this.target.stringInput = this.scenario.stringInput; + this.refresh(); + } } diff --git a/public/app/plugins/app/testdata/partials/query.editor.html b/public/app/plugins/app/testdata/partials/query.editor.html index 53f84309661..a39582d5397 100644 --- a/public/app/plugins/app/testdata/partials/query.editor.html +++ b/public/app/plugins/app/testdata/partials/query.editor.html @@ -2,17 +2,17 @@
-
- +
+
-
- - +
+ +
- +
diff --git a/public/app/plugins/app/testdata/plugin.json b/public/app/plugins/app/testdata/plugin.json index 0bd65a0735d..efd4e6bb739 100644 --- a/public/app/plugins/app/testdata/plugin.json +++ b/public/app/plugins/app/testdata/plugin.json @@ -9,7 +9,7 @@ "name": "Grafana Project", "url": "http://grafana.org" }, - "version": "1.0.6", + "version": "1.0.7", "updated": "2016-09-26" },