mirror of
https://github.com/grafana/grafana.git
synced 2025-02-25 18:55:37 -06:00
AzureMonitor: Types for front-end queries (#24937)
This commit is contained in:
parent
139be3d7ba
commit
fcb9dcb0b8
@ -79,32 +79,30 @@ func (e *ApplicationInsightsDatasource) buildQueries(queries []*tsdb.Query, time
|
|||||||
}
|
}
|
||||||
|
|
||||||
for _, query := range queries {
|
for _, query := range queries {
|
||||||
applicationInsightsTarget := query.Model.Get("appInsights").MustMap()
|
queryBytes, err := query.Model.Encode()
|
||||||
azlog.Debug("Application Insights", "target", applicationInsightsTarget)
|
if err != nil {
|
||||||
|
return nil, fmt.Errorf("failed to re-encode the Azure Application Insights query into JSON: %w", err)
|
||||||
rawQuery := false
|
}
|
||||||
if asInterface, ok := applicationInsightsTarget["rawQuery"]; ok {
|
queryJSONModel := insightsJSONQuery{}
|
||||||
if asBool, ok := asInterface.(bool); ok {
|
err = json.Unmarshal(queryBytes, &queryJSONModel)
|
||||||
rawQuery = asBool
|
if err != nil {
|
||||||
} else {
|
return nil, fmt.Errorf("failed to decode the Azure Application Insights query object from JSON: %w", err)
|
||||||
return nil, errors.New("'rawQuery' should be a boolean")
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
return nil, errors.New("missing 'rawQuery' property")
|
|
||||||
}
|
}
|
||||||
|
|
||||||
if rawQuery {
|
insightsJSONModel := queryJSONModel.AppInsights
|
||||||
|
azlog.Debug("Application Insights", "target", insightsJSONModel)
|
||||||
|
|
||||||
|
if insightsJSONModel.RawQuery == nil {
|
||||||
|
return nil, fmt.Errorf("missing the 'rawQuery' property")
|
||||||
|
}
|
||||||
|
|
||||||
|
if *insightsJSONModel.RawQuery {
|
||||||
var rawQueryString string
|
var rawQueryString string
|
||||||
if asInterface, ok := applicationInsightsTarget["rawQueryString"]; ok {
|
if insightsJSONModel.RawQueryString == "" {
|
||||||
if asString, ok := asInterface.(string); ok {
|
|
||||||
rawQueryString = asString
|
|
||||||
}
|
|
||||||
}
|
|
||||||
if rawQueryString == "" {
|
|
||||||
return nil, errors.New("rawQuery requires rawQueryString")
|
return nil, errors.New("rawQuery requires rawQueryString")
|
||||||
}
|
}
|
||||||
|
|
||||||
rawQueryString, err := KqlInterpolate(query, timeRange, fmt.Sprintf("%v", rawQueryString))
|
rawQueryString, err := KqlInterpolate(query, timeRange, insightsJSONModel.RawQueryString)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
@ -117,20 +115,15 @@ func (e *ApplicationInsightsDatasource) buildQueries(queries []*tsdb.Query, time
|
|||||||
IsRaw: true,
|
IsRaw: true,
|
||||||
ApiURL: "query",
|
ApiURL: "query",
|
||||||
Params: params,
|
Params: params,
|
||||||
TimeColumnName: fmt.Sprintf("%v", applicationInsightsTarget["timeColumn"]),
|
TimeColumnName: insightsJSONModel.TimeColumn,
|
||||||
ValueColumnName: fmt.Sprintf("%v", applicationInsightsTarget["valueColumn"]),
|
ValueColumnName: insightsJSONModel.ValueColumn,
|
||||||
SegmentColumnName: fmt.Sprintf("%v", applicationInsightsTarget["segmentColumn"]),
|
SegmentColumnName: insightsJSONModel.SegmentColumn,
|
||||||
Target: params.Encode(),
|
Target: params.Encode(),
|
||||||
})
|
})
|
||||||
} else {
|
} else {
|
||||||
alias := ""
|
azureURL := fmt.Sprintf("metrics/%s", insightsJSONModel.MetricName)
|
||||||
if val, ok := applicationInsightsTarget["alias"]; ok {
|
timeGrain := insightsJSONModel.TimeGrain
|
||||||
alias = fmt.Sprintf("%v", val)
|
timeGrains := insightsJSONModel.AllowedTimeGrainsMs
|
||||||
}
|
|
||||||
|
|
||||||
azureURL := fmt.Sprintf("metrics/%s", fmt.Sprintf("%v", applicationInsightsTarget["metricName"]))
|
|
||||||
timeGrain := fmt.Sprintf("%v", applicationInsightsTarget["timeGrain"])
|
|
||||||
timeGrains := applicationInsightsTarget["allowedTimeGrainsMs"]
|
|
||||||
if timeGrain == "auto" {
|
if timeGrain == "auto" {
|
||||||
timeGrain, err = setAutoTimeGrain(query.IntervalMs, timeGrains)
|
timeGrain, err = setAutoTimeGrain(query.IntervalMs, timeGrains)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
@ -143,16 +136,17 @@ func (e *ApplicationInsightsDatasource) buildQueries(queries []*tsdb.Query, time
|
|||||||
if timeGrain != "none" {
|
if timeGrain != "none" {
|
||||||
params.Add("interval", timeGrain)
|
params.Add("interval", timeGrain)
|
||||||
}
|
}
|
||||||
params.Add("aggregation", fmt.Sprintf("%v", applicationInsightsTarget["aggregation"]))
|
params.Add("aggregation", insightsJSONModel.Aggregation)
|
||||||
|
|
||||||
dimension := strings.TrimSpace(fmt.Sprintf("%v", applicationInsightsTarget["dimension"]))
|
dimension := strings.TrimSpace(insightsJSONModel.Dimension)
|
||||||
if applicationInsightsTarget["dimension"] != nil && len(dimension) > 0 && !strings.EqualFold(dimension, "none") {
|
// Azure Monitor combines this and the following logic such that if dimensionFilter, must also Dimension, should that be done here as well?
|
||||||
|
if dimension != "" && !strings.EqualFold(dimension, "none") {
|
||||||
params.Add("segment", dimension)
|
params.Add("segment", dimension)
|
||||||
}
|
}
|
||||||
|
|
||||||
dimensionFilter := strings.TrimSpace(fmt.Sprintf("%v", applicationInsightsTarget["dimensionFilter"]))
|
dimensionFilter := strings.TrimSpace(insightsJSONModel.DimensionFilter)
|
||||||
if applicationInsightsTarget["dimensionFilter"] != nil && len(dimensionFilter) > 0 {
|
if dimensionFilter != "" {
|
||||||
params.Add("filter", fmt.Sprintf("%v", dimensionFilter))
|
params.Add("filter", dimensionFilter)
|
||||||
}
|
}
|
||||||
|
|
||||||
applicationInsightsQueries = append(applicationInsightsQueries, &ApplicationInsightsQuery{
|
applicationInsightsQueries = append(applicationInsightsQueries, &ApplicationInsightsQuery{
|
||||||
@ -160,7 +154,7 @@ func (e *ApplicationInsightsDatasource) buildQueries(queries []*tsdb.Query, time
|
|||||||
IsRaw: false,
|
IsRaw: false,
|
||||||
ApiURL: azureURL,
|
ApiURL: azureURL,
|
||||||
Params: params,
|
Params: params,
|
||||||
Alias: alias,
|
Alias: insightsJSONModel.Alias,
|
||||||
Target: params.Encode(),
|
Target: params.Encode(),
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
@ -1,7 +1,6 @@
|
|||||||
package azuremonitor
|
package azuremonitor
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"encoding/json"
|
|
||||||
"fmt"
|
"fmt"
|
||||||
"io/ioutil"
|
"io/ioutil"
|
||||||
"testing"
|
"testing"
|
||||||
@ -92,7 +91,7 @@ func TestApplicationInsightsDatasource(t *testing.T) {
|
|||||||
"metricName": "Percentage CPU",
|
"metricName": "Percentage CPU",
|
||||||
"alias": "testalias",
|
"alias": "testalias",
|
||||||
"queryType": "Application Insights",
|
"queryType": "Application Insights",
|
||||||
"allowedTimeGrainsMs": []interface{}{"auto", json.Number("60000"), json.Number("300000")},
|
"allowedTimeGrainsMs": []int64{60000, 300000},
|
||||||
},
|
},
|
||||||
})
|
})
|
||||||
tsdbQuery.Queries[0].IntervalMs = 400000
|
tsdbQuery.Queries[0].IntervalMs = 400000
|
||||||
|
@ -72,20 +72,31 @@ func (e *AzureLogAnalyticsDatasource) buildQueries(queries []*tsdb.Query, timeRa
|
|||||||
azureLogAnalyticsQueries := []*AzureLogAnalyticsQuery{}
|
azureLogAnalyticsQueries := []*AzureLogAnalyticsQuery{}
|
||||||
|
|
||||||
for _, query := range queries {
|
for _, query := range queries {
|
||||||
azureLogAnalyticsTarget := query.Model.Get("azureLogAnalytics").MustMap()
|
queryBytes, err := query.Model.Encode()
|
||||||
|
if err != nil {
|
||||||
|
return nil, fmt.Errorf("failed to re-encode the Azure Log Analytics query into JSON: %w", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
queryJSONModel := logJSONQuery{}
|
||||||
|
err = json.Unmarshal(queryBytes, &queryJSONModel)
|
||||||
|
if err != nil {
|
||||||
|
return nil, fmt.Errorf("failed to decode the Azure Log Analytics query object from JSON: %w", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
azureLogAnalyticsTarget := queryJSONModel.AzureLogAnalytics
|
||||||
azlog.Debug("AzureLogAnalytics", "target", azureLogAnalyticsTarget)
|
azlog.Debug("AzureLogAnalytics", "target", azureLogAnalyticsTarget)
|
||||||
|
|
||||||
resultFormat := fmt.Sprintf("%v", azureLogAnalyticsTarget["resultFormat"])
|
resultFormat := azureLogAnalyticsTarget.ResultFormat
|
||||||
if resultFormat == "" {
|
if resultFormat == "" {
|
||||||
resultFormat = "time_series"
|
resultFormat = "time_series"
|
||||||
}
|
}
|
||||||
|
|
||||||
urlComponents := map[string]string{}
|
urlComponents := map[string]string{}
|
||||||
urlComponents["workspace"] = fmt.Sprintf("%v", azureLogAnalyticsTarget["workspace"])
|
urlComponents["workspace"] = azureLogAnalyticsTarget.Workspace
|
||||||
apiURL := fmt.Sprintf("%s/query", urlComponents["workspace"])
|
apiURL := fmt.Sprintf("%s/query", urlComponents["workspace"])
|
||||||
|
|
||||||
params := url.Values{}
|
params := url.Values{}
|
||||||
rawQuery, err := KqlInterpolate(query, timeRange, fmt.Sprintf("%v", azureLogAnalyticsTarget["query"]), "TimeGenerated")
|
rawQuery, err := KqlInterpolate(query, timeRange, azureLogAnalyticsTarget.Query, "TimeGenerated")
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
|
@ -36,6 +36,8 @@ var (
|
|||||||
defaultAllowedIntervalsMS = []int64{60000, 300000, 900000, 1800000, 3600000, 21600000, 43200000, 86400000}
|
defaultAllowedIntervalsMS = []int64{60000, 300000, 900000, 1800000, 3600000, 21600000, 43200000, 86400000}
|
||||||
)
|
)
|
||||||
|
|
||||||
|
const azureMonitorAPIVersion = "2018-01-01"
|
||||||
|
|
||||||
// executeTimeSeriesQuery does the following:
|
// executeTimeSeriesQuery does the following:
|
||||||
// 1. build the AzureMonitor url and querystring for each query
|
// 1. build the AzureMonitor url and querystring for each query
|
||||||
// 2. executes each query by calling the Azure Monitor API
|
// 2. executes each query by calling the Azure Monitor API
|
||||||
@ -81,31 +83,38 @@ func (e *AzureMonitorDatasource) buildQueries(queries []*tsdb.Query, timeRange *
|
|||||||
|
|
||||||
for _, query := range queries {
|
for _, query := range queries {
|
||||||
var target string
|
var target string
|
||||||
|
queryBytes, err := query.Model.Encode()
|
||||||
|
if err != nil {
|
||||||
|
return nil, fmt.Errorf("failed to re-encode the Azure Monitor query into JSON: %w", err)
|
||||||
|
}
|
||||||
|
|
||||||
azureMonitorTarget := query.Model.Get("azureMonitor").MustMap()
|
queryJSONModel := azureMonitorJSONQuery{}
|
||||||
|
err = json.Unmarshal(queryBytes, &queryJSONModel)
|
||||||
|
if err != nil {
|
||||||
|
return nil, fmt.Errorf("failed to decode the Azure Monitor query object from JSON: %w", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
azJSONModel := queryJSONModel.AzureMonitor
|
||||||
|
|
||||||
urlComponents := map[string]string{}
|
urlComponents := map[string]string{}
|
||||||
urlComponents["subscription"] = fmt.Sprintf("%v", query.Model.Get("subscription").MustString())
|
urlComponents["subscription"] = queryJSONModel.Subscription
|
||||||
urlComponents["resourceGroup"] = fmt.Sprintf("%v", azureMonitorTarget["resourceGroup"])
|
urlComponents["resourceGroup"] = azJSONModel.ResourceGroup
|
||||||
urlComponents["metricDefinition"] = fmt.Sprintf("%v", azureMonitorTarget["metricDefinition"])
|
urlComponents["metricDefinition"] = azJSONModel.MetricDefinition
|
||||||
urlComponents["resourceName"] = fmt.Sprintf("%v", azureMonitorTarget["resourceName"])
|
urlComponents["resourceName"] = azJSONModel.ResourceName
|
||||||
|
|
||||||
ub := urlBuilder{
|
ub := urlBuilder{
|
||||||
DefaultSubscription: query.DataSource.JsonData.Get("subscriptionId").MustString(),
|
DefaultSubscription: query.DataSource.JsonData.Get("subscriptionId").MustString(),
|
||||||
Subscription: urlComponents["subscription"],
|
Subscription: queryJSONModel.Subscription,
|
||||||
ResourceGroup: urlComponents["resourceGroup"],
|
ResourceGroup: queryJSONModel.AzureMonitor.ResourceGroup,
|
||||||
MetricDefinition: urlComponents["metricDefinition"],
|
MetricDefinition: azJSONModel.MetricDefinition,
|
||||||
ResourceName: urlComponents["resourceName"],
|
ResourceName: azJSONModel.ResourceName,
|
||||||
}
|
}
|
||||||
azureURL := ub.Build()
|
azureURL := ub.Build()
|
||||||
|
|
||||||
alias := ""
|
alias := azJSONModel.Alias
|
||||||
if val, ok := azureMonitorTarget["alias"]; ok {
|
|
||||||
alias = fmt.Sprintf("%v", val)
|
|
||||||
}
|
|
||||||
|
|
||||||
timeGrain := fmt.Sprintf("%v", azureMonitorTarget["timeGrain"])
|
timeGrain := azJSONModel.TimeGrain
|
||||||
timeGrains := azureMonitorTarget["allowedTimeGrainsMs"]
|
timeGrains := azJSONModel.AllowedTimeGrainsMs
|
||||||
if timeGrain == "auto" {
|
if timeGrain == "auto" {
|
||||||
timeGrain, err = setAutoTimeGrain(query.IntervalMs, timeGrains)
|
timeGrain, err = setAutoTimeGrain(query.IntervalMs, timeGrains)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
@ -114,18 +123,18 @@ func (e *AzureMonitorDatasource) buildQueries(queries []*tsdb.Query, timeRange *
|
|||||||
}
|
}
|
||||||
|
|
||||||
params := url.Values{}
|
params := url.Values{}
|
||||||
params.Add("api-version", "2018-01-01")
|
params.Add("api-version", azureMonitorAPIVersion)
|
||||||
params.Add("timespan", fmt.Sprintf("%v/%v", startTime.UTC().Format(time.RFC3339), endTime.UTC().Format(time.RFC3339)))
|
params.Add("timespan", fmt.Sprintf("%v/%v", startTime.UTC().Format(time.RFC3339), endTime.UTC().Format(time.RFC3339)))
|
||||||
params.Add("interval", timeGrain)
|
params.Add("interval", timeGrain)
|
||||||
params.Add("aggregation", fmt.Sprintf("%v", azureMonitorTarget["aggregation"]))
|
params.Add("aggregation", azJSONModel.Aggregation)
|
||||||
params.Add("metricnames", fmt.Sprintf("%v", azureMonitorTarget["metricName"]))
|
params.Add("metricnames", azJSONModel.MetricName) // MetricName or MetricNames ?
|
||||||
params.Add("metricnamespace", fmt.Sprintf("%v", azureMonitorTarget["metricNamespace"]))
|
params.Add("metricnamespace", azJSONModel.MetricNamespace)
|
||||||
|
|
||||||
dimension := strings.TrimSpace(fmt.Sprintf("%v", azureMonitorTarget["dimension"]))
|
dimension := strings.TrimSpace(azJSONModel.Dimension)
|
||||||
dimensionFilter := strings.TrimSpace(fmt.Sprintf("%v", azureMonitorTarget["dimensionFilter"]))
|
dimensionFilter := strings.TrimSpace(azJSONModel.DimensionFilter)
|
||||||
if azureMonitorTarget["dimension"] != nil && azureMonitorTarget["dimensionFilter"] != nil && len(dimension) > 0 && len(dimensionFilter) > 0 && dimension != "None" {
|
if dimension != "" && dimensionFilter != "" && dimension != "None" {
|
||||||
params.Add("$filter", fmt.Sprintf("%s eq '%s'", dimension, dimensionFilter))
|
params.Add("$filter", fmt.Sprintf("%s eq '%s'", dimension, dimensionFilter))
|
||||||
params.Add("top", fmt.Sprintf("%v", azureMonitorTarget["top"]))
|
params.Add("top", azJSONModel.Top)
|
||||||
}
|
}
|
||||||
|
|
||||||
target = params.Encode()
|
target = params.Encode()
|
||||||
|
@ -104,7 +104,7 @@ func TestAzureMonitorDatasource(t *testing.T) {
|
|||||||
"metricName": "Percentage CPU",
|
"metricName": "Percentage CPU",
|
||||||
"alias": "testalias",
|
"alias": "testalias",
|
||||||
"queryType": "Azure Monitor",
|
"queryType": "Azure Monitor",
|
||||||
"allowedTimeGrainsMs": []interface{}{"auto", json.Number("60000"), json.Number("300000")},
|
"allowedTimeGrainsMs": []int64{60000, 300000},
|
||||||
},
|
},
|
||||||
})
|
})
|
||||||
tsdbQuery.Queries[0].IntervalMs = 400000
|
tsdbQuery.Queries[0].IntervalMs = 400000
|
||||||
|
@ -1,27 +1,10 @@
|
|||||||
package azuremonitor
|
package azuremonitor
|
||||||
|
|
||||||
import "encoding/json"
|
|
||||||
|
|
||||||
// setAutoTimeGrain tries to find the closest interval to the query's intervalMs value
|
// setAutoTimeGrain tries to find the closest interval to the query's intervalMs value
|
||||||
// if the metric has a limited set of possible intervals/time grains then use those
|
// if the metric has a limited set of possible intervals/time grains then use those
|
||||||
// instead of the default list of intervals
|
// instead of the default list of intervals
|
||||||
func setAutoTimeGrain(intervalMs int64, timeGrains interface{}) (string, error) {
|
func setAutoTimeGrain(intervalMs int64, timeGrains []int64) (string, error) {
|
||||||
// parses array of numbers from the timeGrains json field
|
autoInterval := findClosestAllowedIntervalMS(intervalMs, timeGrains)
|
||||||
allowedTimeGrains := []int64{}
|
|
||||||
tgs, ok := timeGrains.([]interface{})
|
|
||||||
if ok {
|
|
||||||
for _, v := range tgs {
|
|
||||||
jsonNumber, ok := v.(json.Number)
|
|
||||||
if ok {
|
|
||||||
tg, err := jsonNumber.Int64()
|
|
||||||
if err == nil {
|
|
||||||
allowedTimeGrains = append(allowedTimeGrains, tg)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
autoInterval := findClosestAllowedIntervalMS(intervalMs, allowedTimeGrains)
|
|
||||||
tg := &TimeGrain{}
|
tg := &TimeGrain{}
|
||||||
autoTimeGrain, err := tg.createISO8601DurationFromIntervalMS(autoInterval)
|
autoTimeGrain, err := tg.createISO8601DurationFromIntervalMS(autoInterval)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
|
@ -90,3 +90,51 @@ type column struct {
|
|||||||
Name string `json:"name"`
|
Name string `json:"name"`
|
||||||
Type string `json:"type"`
|
Type string `json:"type"`
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// azureMonitorJSONQuery is the frontend JSON query model for an Azure Monitor query.
|
||||||
|
type azureMonitorJSONQuery struct {
|
||||||
|
AzureMonitor struct {
|
||||||
|
Aggregation string `json:"aggregation"`
|
||||||
|
Alias string `json:"alias"`
|
||||||
|
AllowedTimeGrainsMs []int64 `json:"allowedTimeGrainsMs"`
|
||||||
|
Dimension string `json:"dimension"`
|
||||||
|
DimensionFilter string `json:"dimensionFilter"`
|
||||||
|
Format string `json:"format"`
|
||||||
|
MetricDefinition string `json:"metricDefinition"`
|
||||||
|
MetricName string `json:"metricName"`
|
||||||
|
MetricNamespace string `json:"metricNamespace"`
|
||||||
|
ResourceGroup string `json:"resourceGroup"`
|
||||||
|
ResourceName string `json:"resourceName"`
|
||||||
|
TimeGrain string `json:"timeGrain"`
|
||||||
|
Top string `json:"top"`
|
||||||
|
} `json:"azureMonitor"`
|
||||||
|
Subscription string `json:"subscription"`
|
||||||
|
}
|
||||||
|
|
||||||
|
// insightsJSONQuery is the frontend JSON query model for an Azure Application Insights query.
|
||||||
|
type insightsJSONQuery struct {
|
||||||
|
AppInsights struct {
|
||||||
|
Aggregation string `json:"aggregation"`
|
||||||
|
Alias string `json:"alias"`
|
||||||
|
AllowedTimeGrainsMs []int64 `json:"allowedTimeGrainsMs"`
|
||||||
|
Dimension string `json:"dimension"`
|
||||||
|
DimensionFilter string `json:"dimensionFilter"`
|
||||||
|
MetricName string `json:"metricName"`
|
||||||
|
RawQuery *bool `json:"rawQuery"`
|
||||||
|
RawQueryString string `json:"rawQueryString"`
|
||||||
|
TimeGrain string `json:"timeGrain"`
|
||||||
|
TimeColumn string `json:"timeColumn"`
|
||||||
|
ValueColumn string `json:"valueColumn"`
|
||||||
|
SegmentColumn string `json:"segmentColumn"`
|
||||||
|
} `json:"appInsights"`
|
||||||
|
Raw *bool `json:"raw"`
|
||||||
|
}
|
||||||
|
|
||||||
|
// logJSONQuery is the frontend JSON query model for an Azure Log Analytics query.
|
||||||
|
type logJSONQuery struct {
|
||||||
|
AzureLogAnalytics struct {
|
||||||
|
Query string `json:"query"`
|
||||||
|
ResultFormat string `json:"resultFormat"`
|
||||||
|
Workspace string `json:"workspace"`
|
||||||
|
} `json:"azureLogAnalytics"`
|
||||||
|
}
|
||||||
|
Loading…
Reference in New Issue
Block a user