mirror of
https://github.com/grafana/grafana.git
synced 2026-07-30 08:18:10 -05:00
azuremonitor: handles timegrain set to auto on backend
This commit is contained in:
@@ -30,6 +30,11 @@ type AzureMonitorDatasource struct {
|
||||
dsInfo *models.DataSource
|
||||
}
|
||||
|
||||
var (
|
||||
// 1m, 5m, 15m, 30m, 1h, 6h, 12h, 1d in milliseconds
|
||||
allowedIntervalsMS = []int64{60000, 300000, 900000, 1800000, 3600000, 21600000, 43200000, 86400000}
|
||||
)
|
||||
|
||||
// executeTimeSeriesQuery does the following:
|
||||
// 1. build the AzureMonitor url and querystring for each query
|
||||
// 2. executes each query by calling the Azure Monitor API
|
||||
@@ -49,7 +54,7 @@ func (e *AzureMonitorDatasource) executeTimeSeriesQuery(ctx context.Context, ori
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
azlog.Debug("AzureMonitor", "Response", resp)
|
||||
// azlog.Debug("AzureMonitor", "Response", resp)
|
||||
|
||||
err = e.parseResponse(queryRes, resp, query)
|
||||
if err != nil {
|
||||
@@ -93,10 +98,20 @@ func (e *AzureMonitorDatasource) buildQueries(queries []*tsdb.Query, timeRange *
|
||||
|
||||
alias := fmt.Sprintf("%v", azureMonitorTarget["alias"])
|
||||
|
||||
timeGrain := fmt.Sprintf("%v", azureMonitorTarget["timeGrain"])
|
||||
if timeGrain == "auto" {
|
||||
autoInSeconds := e.findClosestAllowedIntervalMs(query.IntervalMs) / 1000
|
||||
tg := &TimeGrain{}
|
||||
timeGrain, err = tg.createISO8601DurationFromInterval(fmt.Sprintf("%vs", autoInSeconds))
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
}
|
||||
|
||||
params := url.Values{}
|
||||
params.Add("api-version", "2018-01-01")
|
||||
params.Add("timespan", fmt.Sprintf("%v/%v", startTime.UTC().Format(time.RFC3339), endTime.UTC().Format(time.RFC3339)))
|
||||
params.Add("interval", fmt.Sprintf("%v", azureMonitorTarget["timeGrain"]))
|
||||
params.Add("interval", timeGrain)
|
||||
params.Add("aggregation", fmt.Sprintf("%v", azureMonitorTarget["aggregation"]))
|
||||
params.Add("metricnames", fmt.Sprintf("%v", azureMonitorTarget["metricName"]))
|
||||
|
||||
@@ -269,3 +284,18 @@ func (e *AzureMonitorDatasource) parseResponse(queryRes *tsdb.QueryResult, data
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func (e *AzureMonitorDatasource) findClosestAllowedIntervalMs(intervalMs int64) int64 {
|
||||
closest := allowedIntervalsMS[0]
|
||||
|
||||
for i, allowed := range allowedIntervalsMS {
|
||||
if intervalMs > allowed {
|
||||
if i+1 < len(allowedIntervalsMS) {
|
||||
closest = allowedIntervalsMS[i+1]
|
||||
} else {
|
||||
closest = allowed
|
||||
}
|
||||
}
|
||||
}
|
||||
return closest
|
||||
}
|
||||
|
||||
@@ -229,6 +229,26 @@ func TestAzureMonitorDatasource(t *testing.T) {
|
||||
So(res.Series[2].Points[0][0].Float64, ShouldEqual, 0)
|
||||
})
|
||||
})
|
||||
|
||||
Convey("Find closest allowed interval for auto time grain", func() {
|
||||
intervals := map[string]int64{
|
||||
"3m": 180000,
|
||||
"5m": 300000,
|
||||
"10m": 600000,
|
||||
"15m": 900000,
|
||||
"1d": 86400000,
|
||||
"2d": 172800000,
|
||||
}
|
||||
|
||||
closest := datasource.findClosestAllowedIntervalMs(intervals["3m"])
|
||||
So(closest, ShouldEqual, intervals["5m"])
|
||||
|
||||
closest = datasource.findClosestAllowedIntervalMs(intervals["10m"])
|
||||
So(closest, ShouldEqual, intervals["15m"])
|
||||
|
||||
closest = datasource.findClosestAllowedIntervalMs(intervals["2d"])
|
||||
So(closest, ShouldEqual, intervals["1d"])
|
||||
})
|
||||
})
|
||||
}
|
||||
|
||||
|
||||
@@ -0,0 +1,53 @@
|
||||
package azuremonitor
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"strconv"
|
||||
"strings"
|
||||
)
|
||||
|
||||
// TimeGrain handles convertions between
|
||||
// the ISO 8601 Duration format (PT1H), Kbn units (1h) and Time Grains (1 hour)
|
||||
// Also handles using the automatic Grafana interval to calculate a ISO 8601 Duration.
|
||||
type TimeGrain struct{}
|
||||
|
||||
var (
|
||||
smallTimeUnits = []string{"hour", "minute", "h", "m"}
|
||||
)
|
||||
|
||||
func (tg *TimeGrain) createISO8601DurationFromInterval(interval string) (string, error) {
|
||||
if strings.Contains(interval, "ms") {
|
||||
return "PT1M", nil
|
||||
}
|
||||
|
||||
timeValueString := interval[0 : len(interval)-1]
|
||||
timeValue, err := strconv.Atoi(timeValueString)
|
||||
if err != nil {
|
||||
return "", fmt.Errorf("Could not parse interval %v to an ISO 8061 duration", interval)
|
||||
}
|
||||
|
||||
unit := interval[len(interval)-1:]
|
||||
|
||||
if unit == "s" {
|
||||
toMinutes := (timeValue * 60) % 60
|
||||
|
||||
// mimumum interval is 1m for Azure Monitor
|
||||
if toMinutes < 1 {
|
||||
toMinutes = 1
|
||||
}
|
||||
|
||||
return tg.createISO8601Duration(toMinutes, "m"), nil
|
||||
}
|
||||
|
||||
return tg.createISO8601Duration(timeValue, unit), nil
|
||||
}
|
||||
|
||||
func (tg *TimeGrain) createISO8601Duration(timeValue int, timeUnit string) string {
|
||||
for _, smallTimeUnit := range smallTimeUnits {
|
||||
if timeUnit == smallTimeUnit {
|
||||
return fmt.Sprintf("PT%v%v", timeValue, strings.ToUpper(timeUnit[0:1]))
|
||||
}
|
||||
}
|
||||
|
||||
return fmt.Sprintf("P%v%v", timeValue, strings.ToUpper(timeUnit[0:1]))
|
||||
}
|
||||
@@ -0,0 +1,60 @@
|
||||
package azuremonitor
|
||||
|
||||
import (
|
||||
"testing"
|
||||
|
||||
. "github.com/smartystreets/goconvey/convey"
|
||||
)
|
||||
|
||||
func TestTimeGrain(t *testing.T) {
|
||||
Convey("TimeGrain", t, func() {
|
||||
tgc := &TimeGrain{}
|
||||
|
||||
Convey("create ISO 8601 Duration", func() {
|
||||
Convey("when given a time unit smaller than a day", func() {
|
||||
minuteKbnDuration := tgc.createISO8601Duration(1, "m")
|
||||
hourKbnDuration := tgc.createISO8601Duration(2, "h")
|
||||
minuteDuration := tgc.createISO8601Duration(1, "minute")
|
||||
hourDuration := tgc.createISO8601Duration(2, "hour")
|
||||
|
||||
Convey("should convert it to a time duration", func() {
|
||||
So(minuteKbnDuration, ShouldEqual, "PT1M")
|
||||
So(hourKbnDuration, ShouldEqual, "PT2H")
|
||||
|
||||
So(minuteDuration, ShouldEqual, "PT1M")
|
||||
So(hourDuration, ShouldEqual, "PT2H")
|
||||
})
|
||||
})
|
||||
|
||||
Convey("when given the day time unit", func() {
|
||||
kbnDuration := tgc.createISO8601Duration(1, "d")
|
||||
duration := tgc.createISO8601Duration(2, "day")
|
||||
|
||||
Convey("should convert it to a date duration", func() {
|
||||
So(kbnDuration, ShouldEqual, "P1D")
|
||||
So(duration, ShouldEqual, "P2D")
|
||||
})
|
||||
})
|
||||
})
|
||||
|
||||
Convey("create ISO 8601 Duration from Grafana interval", func() {
|
||||
Convey("and interval is less than a minute", func() {
|
||||
durationMS, _ := tgc.createISO8601DurationFromInterval("100ms")
|
||||
durationS, _ := tgc.createISO8601DurationFromInterval("59s")
|
||||
Convey("should be rounded up to a minute as is the minimum interval for Azure Monitor", func() {
|
||||
So(durationMS, ShouldEqual, "PT1M")
|
||||
So(durationS, ShouldEqual, "PT1M")
|
||||
})
|
||||
})
|
||||
|
||||
Convey("and interval is more than a minute", func() {
|
||||
durationM, _ := tgc.createISO8601DurationFromInterval("10m")
|
||||
durationD, _ := tgc.createISO8601DurationFromInterval("2d")
|
||||
Convey("should be rounded up to a minute as is the minimum interval for Azure Monitor", func() {
|
||||
So(durationM, ShouldEqual, "PT10M")
|
||||
So(durationD, ShouldEqual, "P2D")
|
||||
})
|
||||
})
|
||||
})
|
||||
})
|
||||
}
|
||||
Reference in New Issue
Block a user