mirror of
https://github.com/grafana/grafana.git
synced 2025-02-25 18:55:37 -06:00
TestData: Adds important new features to the random walk scenario (#21613)
* test data refactor * new random walk options * fixed test * Added automatic names
This commit is contained in:
parent
79255fafaf
commit
2732aca05c
@ -101,7 +101,15 @@ func init() {
|
|||||||
Name: "Random Walk",
|
Name: "Random Walk",
|
||||||
|
|
||||||
Handler: func(query *tsdb.Query, context *tsdb.TsdbQuery) *tsdb.QueryResult {
|
Handler: func(query *tsdb.Query, context *tsdb.TsdbQuery) *tsdb.QueryResult {
|
||||||
return getRandomWalk(query, context)
|
queryRes := tsdb.NewQueryResult()
|
||||||
|
|
||||||
|
seriesCount := query.Model.Get("seriesCount").MustInt(1)
|
||||||
|
|
||||||
|
for i := 0; i < seriesCount; i++ {
|
||||||
|
queryRes.Series = append(queryRes.Series, getRandomWalk(query, context, i))
|
||||||
|
}
|
||||||
|
|
||||||
|
return queryRes
|
||||||
},
|
},
|
||||||
})
|
})
|
||||||
|
|
||||||
@ -139,7 +147,10 @@ func init() {
|
|||||||
stringInput := query.Model.Get("stringInput").MustString()
|
stringInput := query.Model.Get("stringInput").MustString()
|
||||||
parsedInterval, _ := time.ParseDuration(stringInput)
|
parsedInterval, _ := time.ParseDuration(stringInput)
|
||||||
time.Sleep(parsedInterval)
|
time.Sleep(parsedInterval)
|
||||||
return getRandomWalk(query, context)
|
|
||||||
|
queryRes := tsdb.NewQueryResult()
|
||||||
|
queryRes.Series = append(queryRes.Series, getRandomWalk(query, context, 0))
|
||||||
|
return queryRes
|
||||||
},
|
},
|
||||||
})
|
})
|
||||||
|
|
||||||
@ -157,7 +168,7 @@ func init() {
|
|||||||
Handler: func(query *tsdb.Query, context *tsdb.TsdbQuery) *tsdb.QueryResult {
|
Handler: func(query *tsdb.Query, context *tsdb.TsdbQuery) *tsdb.QueryResult {
|
||||||
queryRes := tsdb.NewQueryResult()
|
queryRes := tsdb.NewQueryResult()
|
||||||
|
|
||||||
series := newSeriesForQuery(query)
|
series := newSeriesForQuery(query, 0)
|
||||||
outsideTime := context.TimeRange.MustGetFrom().Add(-1*time.Hour).Unix() * 1000
|
outsideTime := context.TimeRange.MustGetFrom().Add(-1*time.Hour).Unix() * 1000
|
||||||
|
|
||||||
series.Points = append(series.Points, tsdb.NewTimePoint(null.FloatFrom(10), float64(outsideTime)))
|
series.Points = append(series.Points, tsdb.NewTimePoint(null.FloatFrom(10), float64(outsideTime)))
|
||||||
@ -175,7 +186,7 @@ func init() {
|
|||||||
|
|
||||||
points := query.Model.Get("points").MustArray()
|
points := query.Model.Get("points").MustArray()
|
||||||
|
|
||||||
series := newSeriesForQuery(query)
|
series := newSeriesForQuery(query, 0)
|
||||||
startTime := context.TimeRange.GetFromAsMsEpoch()
|
startTime := context.TimeRange.GetFromAsMsEpoch()
|
||||||
endTime := context.TimeRange.GetToAsMsEpoch()
|
endTime := context.TimeRange.GetToAsMsEpoch()
|
||||||
|
|
||||||
@ -230,7 +241,7 @@ func init() {
|
|||||||
return queryRes
|
return queryRes
|
||||||
}
|
}
|
||||||
|
|
||||||
series := newSeriesForQuery(query)
|
series := newSeriesForQuery(query, 0)
|
||||||
startTime := context.TimeRange.GetFromAsMsEpoch()
|
startTime := context.TimeRange.GetFromAsMsEpoch()
|
||||||
endTime := context.TimeRange.GetToAsMsEpoch()
|
endTime := context.TimeRange.GetToAsMsEpoch()
|
||||||
step := (endTime - startTime) / int64(len(values)-1)
|
step := (endTime - startTime) / int64(len(values)-1)
|
||||||
@ -288,7 +299,8 @@ func init() {
|
|||||||
Name: "Random Walk (with error)",
|
Name: "Random Walk (with error)",
|
||||||
|
|
||||||
Handler: func(query *tsdb.Query, context *tsdb.TsdbQuery) *tsdb.QueryResult {
|
Handler: func(query *tsdb.Query, context *tsdb.TsdbQuery) *tsdb.QueryResult {
|
||||||
queryRes := getRandomWalk(query, context)
|
queryRes := tsdb.NewQueryResult()
|
||||||
|
queryRes.Series = append(queryRes.Series, getRandomWalk(query, context, 0))
|
||||||
queryRes.ErrorString = "This is an error. It can include URLs http://grafana.com/"
|
queryRes.ErrorString = "This is an error. It can include URLs http://grafana.com/"
|
||||||
return queryRes
|
return queryRes
|
||||||
},
|
},
|
||||||
@ -441,10 +453,11 @@ func getPredictablePulse(query *tsdb.Query, context *tsdb.TsdbQuery) *tsdb.Query
|
|||||||
return queryRes
|
return queryRes
|
||||||
}
|
}
|
||||||
|
|
||||||
series := newSeriesForQuery(query)
|
series := newSeriesForQuery(query, 0)
|
||||||
series.Points = *points
|
series.Points = *points
|
||||||
|
series.Tags = parseLabels(query)
|
||||||
|
|
||||||
queryRes.Series = append(queryRes.Series, series)
|
queryRes.Series = append(queryRes.Series, series)
|
||||||
attachLabels(query, queryRes)
|
|
||||||
return queryRes
|
return queryRes
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -491,10 +504,11 @@ func getPredictableCSVWave(query *tsdb.Query, context *tsdb.TsdbQuery) *tsdb.Que
|
|||||||
return queryRes
|
return queryRes
|
||||||
}
|
}
|
||||||
|
|
||||||
series := newSeriesForQuery(query)
|
series := newSeriesForQuery(query, 0)
|
||||||
series.Points = *points
|
series.Points = *points
|
||||||
|
series.Tags = parseLabels(query)
|
||||||
|
|
||||||
queryRes.Series = append(queryRes.Series, series)
|
queryRes.Series = append(queryRes.Series, series)
|
||||||
attachLabels(query, queryRes)
|
|
||||||
return queryRes
|
return queryRes
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -520,28 +534,42 @@ func predictableSeries(timeRange *tsdb.TimeRange, timeStep, length int64, getVal
|
|||||||
return &points, nil
|
return &points, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func getRandomWalk(query *tsdb.Query, tsdbQuery *tsdb.TsdbQuery) *tsdb.QueryResult {
|
func getRandomWalk(query *tsdb.Query, tsdbQuery *tsdb.TsdbQuery, index int) *tsdb.TimeSeries {
|
||||||
timeWalkerMs := tsdbQuery.TimeRange.GetFromAsMsEpoch()
|
timeWalkerMs := tsdbQuery.TimeRange.GetFromAsMsEpoch()
|
||||||
to := tsdbQuery.TimeRange.GetToAsMsEpoch()
|
to := tsdbQuery.TimeRange.GetToAsMsEpoch()
|
||||||
|
series := newSeriesForQuery(query, index)
|
||||||
|
|
||||||
series := newSeriesForQuery(query)
|
startValue := query.Model.Get("startValue").MustFloat64(rand.Float64() * 100)
|
||||||
|
spread := query.Model.Get("spread").MustFloat64(1)
|
||||||
|
noise := query.Model.Get("noise").MustFloat64(0)
|
||||||
|
min, hasMin := query.Model.Get("min").Float64()
|
||||||
|
max, hasMax := query.Model.Get("max").Float64()
|
||||||
|
|
||||||
points := make(tsdb.TimeSeriesPoints, 0)
|
points := make(tsdb.TimeSeriesPoints, 0)
|
||||||
walker := query.Model.Get("startValue").MustFloat64(rand.Float64() * 100)
|
walker := startValue
|
||||||
|
|
||||||
for i := int64(0); i < 10000 && timeWalkerMs < to; i++ {
|
for i := int64(0); i < 10000 && timeWalkerMs < to; i++ {
|
||||||
points = append(points, tsdb.NewTimePoint(null.FloatFrom(walker), float64(timeWalkerMs)))
|
nextValue := walker + (rand.Float64() * noise)
|
||||||
|
|
||||||
walker += rand.Float64() - 0.5
|
if hasMin == nil && nextValue < min {
|
||||||
|
nextValue = min
|
||||||
|
walker = min
|
||||||
|
}
|
||||||
|
|
||||||
|
if hasMax == nil && nextValue > max {
|
||||||
|
nextValue = max
|
||||||
|
walker = max
|
||||||
|
}
|
||||||
|
|
||||||
|
points = append(points, tsdb.NewTimePoint(null.FloatFrom(nextValue), float64(timeWalkerMs)))
|
||||||
|
|
||||||
|
walker += (rand.Float64() - 0.5) * spread
|
||||||
timeWalkerMs += query.IntervalMs
|
timeWalkerMs += query.IntervalMs
|
||||||
}
|
}
|
||||||
|
|
||||||
series.Points = points
|
series.Points = points
|
||||||
|
series.Tags = parseLabels(query)
|
||||||
queryRes := tsdb.NewQueryResult()
|
return series
|
||||||
queryRes.Series = append(queryRes.Series, series)
|
|
||||||
attachLabels(query, queryRes)
|
|
||||||
return queryRes
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -549,30 +577,19 @@ func getRandomWalk(query *tsdb.Query, tsdbQuery *tsdb.TsdbQuery) *tsdb.QueryResu
|
|||||||
*
|
*
|
||||||
* '{job="foo", instance="bar"} => {job: "foo", instance: "bar"}`
|
* '{job="foo", instance="bar"} => {job: "foo", instance: "bar"}`
|
||||||
*/
|
*/
|
||||||
func attachLabels(query *tsdb.Query, queryRes *tsdb.QueryResult) {
|
func parseLabels(query *tsdb.Query) map[string]string {
|
||||||
|
tags := map[string]string{}
|
||||||
|
|
||||||
labelText := query.Model.Get("labels").MustString("")
|
labelText := query.Model.Get("labels").MustString("")
|
||||||
if labelText == "" {
|
if labelText == "" {
|
||||||
return
|
return map[string]string{}
|
||||||
}
|
}
|
||||||
|
|
||||||
tags := parseLabels(labelText)
|
text := strings.Trim(labelText, `{}`)
|
||||||
for _, series := range queryRes.Series {
|
|
||||||
series.Tags = tags
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// generous parser:
|
|
||||||
// {job="foo", instance="bar"}
|
|
||||||
// job="foo", instance="bar"
|
|
||||||
// job=foo, instance=bar
|
|
||||||
// should all equal {job=foo, instance=bar}
|
|
||||||
|
|
||||||
func parseLabels(text string) map[string]string {
|
|
||||||
var tags map[string]string
|
|
||||||
text = strings.Trim(text, `{}`)
|
|
||||||
if len(text) < 2 {
|
if len(text) < 2 {
|
||||||
return tags
|
return tags
|
||||||
}
|
}
|
||||||
|
|
||||||
tags = make(map[string]string)
|
tags = make(map[string]string)
|
||||||
|
|
||||||
for _, keyval := range strings.Split(text, ",") {
|
for _, keyval := range strings.Split(text, ",") {
|
||||||
@ -648,10 +665,24 @@ func registerScenario(scenario *Scenario) {
|
|||||||
ScenarioRegistry[scenario.Id] = scenario
|
ScenarioRegistry[scenario.Id] = scenario
|
||||||
}
|
}
|
||||||
|
|
||||||
func newSeriesForQuery(query *tsdb.Query) *tsdb.TimeSeries {
|
func newSeriesForQuery(query *tsdb.Query, index int) *tsdb.TimeSeries {
|
||||||
alias := query.Model.Get("alias").MustString("")
|
alias := query.Model.Get("alias").MustString("")
|
||||||
|
suffix := ""
|
||||||
|
|
||||||
|
if index > 0 {
|
||||||
|
suffix = strconv.Itoa(index)
|
||||||
|
}
|
||||||
|
|
||||||
if alias == "" {
|
if alias == "" {
|
||||||
alias = query.RefId + "-series"
|
alias = fmt.Sprintf("%s-series%s", query.RefId, suffix)
|
||||||
|
}
|
||||||
|
|
||||||
|
if alias == "__server_names" && len(serverNames) > index {
|
||||||
|
alias = serverNames[index]
|
||||||
|
}
|
||||||
|
|
||||||
|
if alias == "__house_locations" && len(houseLocations) > index {
|
||||||
|
alias = houseLocations[index]
|
||||||
}
|
}
|
||||||
|
|
||||||
return &tsdb.TimeSeries{Name: alias}
|
return &tsdb.TimeSeries{Name: alias}
|
||||||
@ -671,3 +702,49 @@ func fromStringOrNumber(val *simplejson.Json) (null.Float, error) {
|
|||||||
return null.Float{}, fmt.Errorf("failed to extract value")
|
return null.Float{}, fmt.Errorf("failed to extract value")
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
var serverNames = []string{
|
||||||
|
"Backend-ops-01",
|
||||||
|
"Backend-ops-02",
|
||||||
|
"Backend-ops-03",
|
||||||
|
"Backend-ops-04",
|
||||||
|
"Frontend-web-01",
|
||||||
|
"Frontend-web-02",
|
||||||
|
"Frontend-web-03",
|
||||||
|
"Frontend-web-04",
|
||||||
|
"MySQL-01",
|
||||||
|
"MySQL-02",
|
||||||
|
"MySQL-03",
|
||||||
|
"MySQL-04",
|
||||||
|
"Postgres-01",
|
||||||
|
"Postgres-02",
|
||||||
|
"Postgres-03",
|
||||||
|
"Postgres-04",
|
||||||
|
"DB-01",
|
||||||
|
"DB-02",
|
||||||
|
"SAN-01",
|
||||||
|
"SAN-02",
|
||||||
|
"SAN-02",
|
||||||
|
"SAN-04",
|
||||||
|
"Kaftka-01",
|
||||||
|
"Kaftka-02",
|
||||||
|
"Kaftka-03",
|
||||||
|
"Zookeeper-01",
|
||||||
|
"Zookeeper-02",
|
||||||
|
"Zookeeper-03",
|
||||||
|
"Zookeeper-04",
|
||||||
|
}
|
||||||
|
|
||||||
|
var houseLocations = []string{
|
||||||
|
"Cellar",
|
||||||
|
"Living room",
|
||||||
|
"Porch",
|
||||||
|
"Bedroom",
|
||||||
|
"Guest room",
|
||||||
|
"Kitchen",
|
||||||
|
"Playroom",
|
||||||
|
"Bathroom",
|
||||||
|
"Outside",
|
||||||
|
"Roof",
|
||||||
|
"Terrace",
|
||||||
|
}
|
||||||
|
@ -99,9 +99,28 @@ func TestToLabels(t *testing.T) {
|
|||||||
tags["job"] = "foo"
|
tags["job"] = "foo"
|
||||||
tags["instance"] = "bar"
|
tags["instance"] = "bar"
|
||||||
|
|
||||||
So(parseLabels(`{job="foo", instance="bar"}`), ShouldResemble, tags)
|
query1 := tsdb.Query{
|
||||||
So(parseLabels(`job="foo", instance="bar"`), ShouldResemble, tags)
|
Model: simplejson.NewFromAny(map[string]interface{}{
|
||||||
So(parseLabels(`job=foo, instance=bar`), ShouldResemble, tags)
|
"labels": `{job="foo", instance="bar"}`,
|
||||||
So(parseLabels(`job = foo,instance = bar`), ShouldResemble, tags)
|
}),
|
||||||
|
}
|
||||||
|
|
||||||
|
So(parseLabels(&query1), ShouldResemble, tags)
|
||||||
|
|
||||||
|
query2 := tsdb.Query{
|
||||||
|
Model: simplejson.NewFromAny(map[string]interface{}{
|
||||||
|
"labels": `job=foo, instance=bar`,
|
||||||
|
}),
|
||||||
|
}
|
||||||
|
|
||||||
|
So(parseLabels(&query2), ShouldResemble, tags)
|
||||||
|
|
||||||
|
query3 := tsdb.Query{
|
||||||
|
Model: simplejson.NewFromAny(map[string]interface{}{
|
||||||
|
"labels": `job = foo,instance = bar`,
|
||||||
|
}),
|
||||||
|
}
|
||||||
|
|
||||||
|
So(parseLabels(&query3), ShouldResemble, tags)
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
@ -12,7 +12,7 @@
|
|||||||
</div>
|
</div>
|
||||||
<div class="gf-form">
|
<div class="gf-form">
|
||||||
<label class="gf-form-label query-keyword">Alias</label>
|
<label class="gf-form-label query-keyword">Alias</label>
|
||||||
<input type="text" class="gf-form-input max-width-7" placeholder="optional" ng-model="ctrl.target.alias" ng-change="ctrl.refresh()" ng-model-onblur>
|
<input type="text" class="gf-form-input width-14" placeholder="optional" ng-model="ctrl.target.alias" ng-change="ctrl.refresh()" ng-model-onblur>
|
||||||
</div>
|
</div>
|
||||||
<div ng-if="ctrl.showLabels" class="gf-form gf-form--grow">
|
<div ng-if="ctrl.showLabels" class="gf-form gf-form--grow">
|
||||||
<label class="gf-form-label query-keyword">
|
<label class="gf-form-label query-keyword">
|
||||||
@ -48,6 +48,68 @@
|
|||||||
<div class="gf-form-label gf-form-label--grow"></div>
|
<div class="gf-form-label gf-form-label--grow"></div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
<div class="gf-form-inline" ng-if="ctrl.scenario.id === 'random_walk'">
|
||||||
|
<div class="gf-form">
|
||||||
|
<label class="gf-form-label query-keyword width-7">Series count</label>
|
||||||
|
<input type="number"
|
||||||
|
class="gf-form-input width-7"
|
||||||
|
placeholder="1"
|
||||||
|
ng-model="ctrl.target.seriesCount"
|
||||||
|
min="1"
|
||||||
|
step="1"
|
||||||
|
ng-change="ctrl.refresh()" />
|
||||||
|
</div>
|
||||||
|
<div class="gf-form">
|
||||||
|
<label class="gf-form-label query-keyword width-7">Start value</label>
|
||||||
|
<input type="number"
|
||||||
|
class="gf-form-input width-6"
|
||||||
|
placeholder="auto"
|
||||||
|
ng-model="ctrl.target.startValue"
|
||||||
|
min="1"
|
||||||
|
step="1"
|
||||||
|
ng-change="ctrl.refresh()" />
|
||||||
|
</div>
|
||||||
|
<div class="gf-form">
|
||||||
|
<label class="gf-form-label query-keyword width-7">Spread</label>
|
||||||
|
<input type="number"
|
||||||
|
class="gf-form-input width-6"
|
||||||
|
placeholder="1"
|
||||||
|
ng-model="ctrl.target.spread"
|
||||||
|
min="0.5"
|
||||||
|
step="0.1"
|
||||||
|
ng-change="ctrl.refresh()" />
|
||||||
|
</div>
|
||||||
|
<div class="gf-form">
|
||||||
|
<label class="gf-form-label query-keyword width-7">Noise</label>
|
||||||
|
<input type="number"
|
||||||
|
class="gf-form-input width-6"
|
||||||
|
placeholder="0"
|
||||||
|
ng-model="ctrl.target.noise"
|
||||||
|
min="0"
|
||||||
|
step="0.1"
|
||||||
|
ng-change="ctrl.refresh()" />
|
||||||
|
</div>
|
||||||
|
<div class="gf-form">
|
||||||
|
<label class="gf-form-label query-keyword width-7">Min</label>
|
||||||
|
<input type="number"
|
||||||
|
class="gf-form-input width-6"
|
||||||
|
placeholder="none"
|
||||||
|
ng-model="ctrl.target.min"
|
||||||
|
min="0"
|
||||||
|
step="0.1"
|
||||||
|
ng-change="ctrl.refresh()" />
|
||||||
|
</div>
|
||||||
|
<div class="gf-form">
|
||||||
|
<label class="gf-form-label query-keyword width-7">Max</label>
|
||||||
|
<input type="number"
|
||||||
|
class="gf-form-input width-6"
|
||||||
|
placeholder="none"
|
||||||
|
ng-model="ctrl.target.max"
|
||||||
|
min="0"
|
||||||
|
step="0.1"
|
||||||
|
ng-change="ctrl.refresh()" />
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
<div class="gf-form-inline" ng-if="ctrl.scenario.id === 'streaming_client'">
|
<div class="gf-form-inline" ng-if="ctrl.scenario.id === 'streaming_client'">
|
||||||
<div class="gf-form gf-form">
|
<div class="gf-form gf-form">
|
||||||
<label class="gf-form-label query-keyword width-7">Type</label>
|
<label class="gf-form-label query-keyword width-7">Type</label>
|
||||||
|
Loading…
Reference in New Issue
Block a user