mirror of
https://github.com/grafana/grafana.git
synced 2025-02-25 18:55:37 -06:00
Azure: Multiple dimension support for Azure Monitor Service (#25947)
Azure Monitor (metrics) support multiple dimensions instead of just one. Co-authored-by: Ryan McKinley <ryantxu@gmail.com>
This commit is contained in:
parent
72fa5ccb7b
commit
4be56cde0d
@ -9,6 +9,7 @@ import (
|
|||||||
"net/http"
|
"net/http"
|
||||||
"net/url"
|
"net/url"
|
||||||
"path"
|
"path"
|
||||||
|
"sort"
|
||||||
"strings"
|
"strings"
|
||||||
"time"
|
"time"
|
||||||
|
|
||||||
@ -21,7 +22,6 @@ import (
|
|||||||
opentracing "github.com/opentracing/opentracing-go"
|
opentracing "github.com/opentracing/opentracing-go"
|
||||||
"golang.org/x/net/context/ctxhttp"
|
"golang.org/x/net/context/ctxhttp"
|
||||||
|
|
||||||
"github.com/grafana/grafana/pkg/components/simplejson"
|
|
||||||
"github.com/grafana/grafana/pkg/tsdb"
|
"github.com/grafana/grafana/pkg/tsdb"
|
||||||
)
|
)
|
||||||
|
|
||||||
@ -57,7 +57,6 @@ func (e *AzureMonitorDatasource) executeTimeSeriesQuery(ctx context.Context, ori
|
|||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
// azlog.Debug("AzureMonitor", "Response", resp)
|
|
||||||
|
|
||||||
err = e.parseResponse(queryRes, resp, query)
|
err = e.parseResponse(queryRes, resp, query)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
@ -130,10 +129,25 @@ func (e *AzureMonitorDatasource) buildQueries(queries []*tsdb.Query, timeRange *
|
|||||||
params.Add("metricnames", azJSONModel.MetricName) // MetricName or MetricNames ?
|
params.Add("metricnames", azJSONModel.MetricName) // MetricName or MetricNames ?
|
||||||
params.Add("metricnamespace", azJSONModel.MetricNamespace)
|
params.Add("metricnamespace", azJSONModel.MetricNamespace)
|
||||||
|
|
||||||
|
// old model
|
||||||
dimension := strings.TrimSpace(azJSONModel.Dimension)
|
dimension := strings.TrimSpace(azJSONModel.Dimension)
|
||||||
dimensionFilter := strings.TrimSpace(azJSONModel.DimensionFilter)
|
dimensionFilter := strings.TrimSpace(azJSONModel.DimensionFilter)
|
||||||
if dimension != "" && dimensionFilter != "" && dimension != "None" {
|
|
||||||
params.Add("$filter", fmt.Sprintf("%s eq '%s'", dimension, dimensionFilter))
|
dimSB := strings.Builder{}
|
||||||
|
|
||||||
|
if dimension != "" && dimensionFilter != "" && dimension != "None" && len(azJSONModel.DimensionsFilters) == 0 {
|
||||||
|
dimSB.WriteString(fmt.Sprintf("%s eq '%s'", dimension, dimensionFilter))
|
||||||
|
} else {
|
||||||
|
for i, filter := range azJSONModel.DimensionsFilters {
|
||||||
|
dimSB.WriteString(filter.String())
|
||||||
|
if i != len(azJSONModel.DimensionsFilters)-1 {
|
||||||
|
dimSB.WriteString(" and ")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if dimSB.String() != "" {
|
||||||
|
params.Add("$filter", dimSB.String())
|
||||||
params.Add("top", azJSONModel.Top)
|
params.Add("top", azJSONModel.Top)
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -157,7 +171,7 @@ func (e *AzureMonitorDatasource) buildQueries(queries []*tsdb.Query, timeRange *
|
|||||||
}
|
}
|
||||||
|
|
||||||
func (e *AzureMonitorDatasource) executeQuery(ctx context.Context, query *AzureMonitorQuery, queries []*tsdb.Query, timeRange *tsdb.TimeRange) (*tsdb.QueryResult, AzureMonitorResponse, error) {
|
func (e *AzureMonitorDatasource) executeQuery(ctx context.Context, query *AzureMonitorQuery, queries []*tsdb.Query, timeRange *tsdb.TimeRange) (*tsdb.QueryResult, AzureMonitorResponse, error) {
|
||||||
queryResult := &tsdb.QueryResult{Meta: simplejson.New(), RefId: query.RefID}
|
queryResult := &tsdb.QueryResult{RefId: query.RefID}
|
||||||
|
|
||||||
req, err := e.createRequest(ctx, e.dsInfo)
|
req, err := e.createRequest(ctx, e.dsInfo)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
@ -167,7 +181,6 @@ func (e *AzureMonitorDatasource) executeQuery(ctx context.Context, query *AzureM
|
|||||||
|
|
||||||
req.URL.Path = path.Join(req.URL.Path, query.URL)
|
req.URL.Path = path.Join(req.URL.Path, query.URL)
|
||||||
req.URL.RawQuery = query.Params.Encode()
|
req.URL.RawQuery = query.Params.Encode()
|
||||||
queryResult.Meta.Set("rawQuery", req.URL.RawQuery)
|
|
||||||
|
|
||||||
span, ctx := opentracing.StartSpanFromContext(ctx, "azuremonitor query")
|
span, ctx := opentracing.StartSpanFromContext(ctx, "azuremonitor query")
|
||||||
span.SetTag("target", query.Target)
|
span.SetTag("target", query.Target)
|
||||||
@ -270,20 +283,23 @@ func (e *AzureMonitorDatasource) parseResponse(queryRes *tsdb.QueryResult, amr A
|
|||||||
|
|
||||||
frames := data.Frames{}
|
frames := data.Frames{}
|
||||||
for _, series := range amr.Value[0].Timeseries {
|
for _, series := range amr.Value[0].Timeseries {
|
||||||
metadataName := ""
|
labels := data.Labels{}
|
||||||
metadataValue := ""
|
for _, md := range series.Metadatavalues {
|
||||||
if len(series.Metadatavalues) > 0 {
|
labels[md.Name.LocalizedValue] = md.Value
|
||||||
metadataName = series.Metadatavalues[0].Name.LocalizedValue
|
|
||||||
metadataValue = series.Metadatavalues[0].Value
|
|
||||||
}
|
}
|
||||||
metricName := formatAzureMonitorLegendKey(query.Alias, query.UrlComponents["resourceName"], amr.Value[0].Name.LocalizedValue, metadataName, metadataValue, amr.Namespace, amr.Value[0].ID)
|
|
||||||
|
|
||||||
frame := data.NewFrameOfFieldTypes("", len(series.Data), data.FieldTypeTime, data.FieldTypeFloat64)
|
frame := data.NewFrameOfFieldTypes("", len(series.Data), data.FieldTypeTime, data.FieldTypeFloat64)
|
||||||
frame.RefID = query.RefID
|
frame.RefID = query.RefID
|
||||||
frame.Fields[1].Name = metricName
|
dataField := frame.Fields[1]
|
||||||
frame.Fields[1].SetConfig(&data.FieldConfig{
|
dataField.Name = amr.Value[0].Name.LocalizedValue
|
||||||
|
dataField.Labels = labels
|
||||||
|
dataField.SetConfig(&data.FieldConfig{
|
||||||
Unit: amr.Value[0].Unit,
|
Unit: amr.Value[0].Unit,
|
||||||
})
|
})
|
||||||
|
if query.Alias != "" {
|
||||||
|
dataField.Config.DisplayName = formatAzureMonitorLegendKey(query.Alias, query.UrlComponents["resourceName"],
|
||||||
|
amr.Value[0].Name.LocalizedValue, "", "", amr.Namespace, amr.Value[0].ID, labels)
|
||||||
|
}
|
||||||
|
|
||||||
requestedAgg := query.Params.Get("aggregation")
|
requestedAgg := query.Params.Get("aggregation")
|
||||||
|
|
||||||
@ -317,14 +333,7 @@ func (e *AzureMonitorDatasource) parseResponse(queryRes *tsdb.QueryResult, amr A
|
|||||||
|
|
||||||
// formatAzureMonitorLegendKey builds the legend key or timeseries name
|
// formatAzureMonitorLegendKey builds the legend key or timeseries name
|
||||||
// Alias patterns like {{resourcename}} are replaced with the appropriate data values.
|
// Alias patterns like {{resourcename}} are replaced with the appropriate data values.
|
||||||
func formatAzureMonitorLegendKey(alias string, resourceName string, metricName string, metadataName string, metadataValue string, namespace string, seriesID string) string {
|
func formatAzureMonitorLegendKey(alias string, resourceName string, metricName string, metadataName string, metadataValue string, namespace string, seriesID string, labels data.Labels) string {
|
||||||
if alias == "" {
|
|
||||||
if len(metadataName) > 0 {
|
|
||||||
return fmt.Sprintf("%s{%s=%s}.%s", resourceName, metadataName, metadataValue, metricName)
|
|
||||||
}
|
|
||||||
return fmt.Sprintf("%s.%s", resourceName, metricName)
|
|
||||||
}
|
|
||||||
|
|
||||||
startIndex := strings.Index(seriesID, "/resourceGroups/") + 16
|
startIndex := strings.Index(seriesID, "/resourceGroups/") + 16
|
||||||
endIndex := strings.Index(seriesID, "/providers")
|
endIndex := strings.Index(seriesID, "/providers")
|
||||||
resourceGroup := seriesID[startIndex:endIndex]
|
resourceGroup := seriesID[startIndex:endIndex]
|
||||||
@ -350,14 +359,25 @@ func formatAzureMonitorLegendKey(alias string, resourceName string, metricName s
|
|||||||
return []byte(metricName)
|
return []byte(metricName)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
keys := make([]string, 0, len(labels))
|
||||||
|
if metaPartName == "dimensionname" || metaPartName == "dimensionvalue" {
|
||||||
|
for k := range labels {
|
||||||
|
keys = append(keys, k)
|
||||||
|
}
|
||||||
|
keys = sort.StringSlice(keys)
|
||||||
|
}
|
||||||
|
|
||||||
if metaPartName == "dimensionname" {
|
if metaPartName == "dimensionname" {
|
||||||
return []byte(metadataName)
|
return []byte(keys[0])
|
||||||
}
|
}
|
||||||
|
|
||||||
if metaPartName == "dimensionvalue" {
|
if metaPartName == "dimensionvalue" {
|
||||||
return []byte(metadataValue)
|
return []byte(labels[keys[0]])
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if v, ok := labels[metaPartName]; ok {
|
||||||
|
return []byte(v)
|
||||||
|
}
|
||||||
return in
|
return in
|
||||||
})
|
})
|
||||||
|
|
||||||
|
@ -72,7 +72,7 @@ func TestAzureMonitorBuildQueries(t *testing.T) {
|
|||||||
azureMonitorQueryTarget: "%24filter=blob+eq+%27%2A%27&aggregation=Average&api-version=2018-01-01&interval=PT1M&metricnames=Percentage+CPU&metricnamespace=Microsoft.Compute-virtualMachines×pan=2018-03-15T13%3A00%3A00Z%2F2018-03-15T13%3A34%3A00Z&top=30",
|
azureMonitorQueryTarget: "%24filter=blob+eq+%27%2A%27&aggregation=Average&api-version=2018-01-01&interval=PT1M&metricnames=Percentage+CPU&metricnamespace=Microsoft.Compute-virtualMachines×pan=2018-03-15T13%3A00%3A00Z%2F2018-03-15T13%3A34%3A00Z&top=30",
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
name: "has a dimension filter",
|
name: "has a dimension filter and none Dimension",
|
||||||
azureMonitorVariedProperties: map[string]interface{}{
|
azureMonitorVariedProperties: map[string]interface{}{
|
||||||
"timeGrain": "PT1M",
|
"timeGrain": "PT1M",
|
||||||
"dimension": "None",
|
"dimension": "None",
|
||||||
@ -83,6 +83,28 @@ func TestAzureMonitorBuildQueries(t *testing.T) {
|
|||||||
expectedInterval: "PT1M",
|
expectedInterval: "PT1M",
|
||||||
azureMonitorQueryTarget: "aggregation=Average&api-version=2018-01-01&interval=PT1M&metricnames=Percentage+CPU&metricnamespace=Microsoft.Compute-virtualMachines×pan=2018-03-15T13%3A00%3A00Z%2F2018-03-15T13%3A34%3A00Z",
|
azureMonitorQueryTarget: "aggregation=Average&api-version=2018-01-01&interval=PT1M&metricnames=Percentage+CPU&metricnamespace=Microsoft.Compute-virtualMachines×pan=2018-03-15T13%3A00%3A00Z%2F2018-03-15T13%3A34%3A00Z",
|
||||||
},
|
},
|
||||||
|
{
|
||||||
|
name: "has dimensionFilter*s* property with one dimension",
|
||||||
|
azureMonitorVariedProperties: map[string]interface{}{
|
||||||
|
"timeGrain": "PT1M",
|
||||||
|
"dimensionsFilters": []azureMonitorDimensionFilter{{"blob", "eq", "*"}},
|
||||||
|
"top": "30",
|
||||||
|
},
|
||||||
|
queryIntervalMS: 400000,
|
||||||
|
expectedInterval: "PT1M",
|
||||||
|
azureMonitorQueryTarget: "%24filter=blob+eq+%27%2A%27&aggregation=Average&api-version=2018-01-01&interval=PT1M&metricnames=Percentage+CPU&metricnamespace=Microsoft.Compute-virtualMachines×pan=2018-03-15T13%3A00%3A00Z%2F2018-03-15T13%3A34%3A00Z&top=30",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "has dimensionFilter*s* property with two dimensions",
|
||||||
|
azureMonitorVariedProperties: map[string]interface{}{
|
||||||
|
"timeGrain": "PT1M",
|
||||||
|
"dimensionsFilters": []azureMonitorDimensionFilter{{"blob", "eq", "*"}, {"tier", "eq", "*"}},
|
||||||
|
"top": "30",
|
||||||
|
},
|
||||||
|
queryIntervalMS: 400000,
|
||||||
|
expectedInterval: "PT1M",
|
||||||
|
azureMonitorQueryTarget: "%24filter=blob+eq+%27%2A%27+and+tier+eq+%27%2A%27&aggregation=Average&api-version=2018-01-01&interval=PT1M&metricnames=Percentage+CPU&metricnamespace=Microsoft.Compute-virtualMachines×pan=2018-03-15T13%3A00%3A00Z%2F2018-03-15T13%3A34%3A00Z&top=30",
|
||||||
|
},
|
||||||
}
|
}
|
||||||
|
|
||||||
commonAzureModelProps := map[string]interface{}{
|
commonAzureModelProps := map[string]interface{}{
|
||||||
@ -139,9 +161,7 @@ func TestAzureMonitorBuildQueries(t *testing.T) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
queries, err := datasource.buildQueries(tsdbQuery.Queries, tsdbQuery.TimeRange)
|
queries, err := datasource.buildQueries(tsdbQuery.Queries, tsdbQuery.TimeRange)
|
||||||
if err != nil {
|
require.NoError(t, err)
|
||||||
t.Error(err)
|
|
||||||
}
|
|
||||||
if diff := cmp.Diff(azureMonitorQuery, queries[0], cmpopts.IgnoreUnexported(simplejson.Json{}), cmpopts.IgnoreFields(AzureMonitorQuery{}, "Params")); diff != "" {
|
if diff := cmp.Diff(azureMonitorQuery, queries[0], cmpopts.IgnoreUnexported(simplejson.Json{}), cmpopts.IgnoreFields(AzureMonitorQuery{}, "Params")); diff != "" {
|
||||||
t.Errorf("Result mismatch (-want +got):\n%s", diff)
|
t.Errorf("Result mismatch (-want +got):\n%s", diff)
|
||||||
}
|
}
|
||||||
@ -179,7 +199,7 @@ func TestAzureMonitorParseResponse(t *testing.T) {
|
|||||||
data.NewFrame("",
|
data.NewFrame("",
|
||||||
data.NewField("", nil,
|
data.NewField("", nil,
|
||||||
makeDates(time.Date(2019, 2, 8, 10, 13, 0, 0, time.UTC), 5, time.Minute)),
|
makeDates(time.Date(2019, 2, 8, 10, 13, 0, 0, time.UTC), 5, time.Minute)),
|
||||||
data.NewField("grafana.Percentage CPU", nil, []float64{
|
data.NewField("Percentage CPU", nil, []float64{
|
||||||
2.0875, 2.1525, 2.155, 3.6925, 2.44,
|
2.0875, 2.1525, 2.155, 3.6925, 2.44,
|
||||||
}).SetConfig(&data.FieldConfig{Unit: "Percent"})),
|
}).SetConfig(&data.FieldConfig{Unit: "Percent"})),
|
||||||
},
|
},
|
||||||
@ -199,7 +219,7 @@ func TestAzureMonitorParseResponse(t *testing.T) {
|
|||||||
data.NewFrame("",
|
data.NewFrame("",
|
||||||
data.NewField("", nil,
|
data.NewField("", nil,
|
||||||
makeDates(time.Date(2019, 2, 9, 13, 29, 0, 0, time.UTC), 5, time.Minute)),
|
makeDates(time.Date(2019, 2, 9, 13, 29, 0, 0, time.UTC), 5, time.Minute)),
|
||||||
data.NewField("grafana.Percentage CPU", nil, []float64{
|
data.NewField("Percentage CPU", nil, []float64{
|
||||||
8.26, 8.7, 14.82, 10.07, 8.52,
|
8.26, 8.7, 14.82, 10.07, 8.52,
|
||||||
}).SetConfig(&data.FieldConfig{Unit: "Percent"})),
|
}).SetConfig(&data.FieldConfig{Unit: "Percent"})),
|
||||||
},
|
},
|
||||||
@ -219,7 +239,7 @@ func TestAzureMonitorParseResponse(t *testing.T) {
|
|||||||
data.NewFrame("",
|
data.NewFrame("",
|
||||||
data.NewField("", nil,
|
data.NewField("", nil,
|
||||||
makeDates(time.Date(2019, 2, 9, 14, 26, 0, 0, time.UTC), 5, time.Minute)),
|
makeDates(time.Date(2019, 2, 9, 14, 26, 0, 0, time.UTC), 5, time.Minute)),
|
||||||
data.NewField("grafana.Percentage CPU", nil, []float64{
|
data.NewField("Percentage CPU", nil, []float64{
|
||||||
3.07, 2.92, 2.87, 2.27, 2.52,
|
3.07, 2.92, 2.87, 2.27, 2.52,
|
||||||
}).SetConfig(&data.FieldConfig{Unit: "Percent"})),
|
}).SetConfig(&data.FieldConfig{Unit: "Percent"})),
|
||||||
},
|
},
|
||||||
@ -239,7 +259,7 @@ func TestAzureMonitorParseResponse(t *testing.T) {
|
|||||||
data.NewFrame("",
|
data.NewFrame("",
|
||||||
data.NewField("", nil,
|
data.NewField("", nil,
|
||||||
makeDates(time.Date(2019, 2, 9, 14, 43, 0, 0, time.UTC), 5, time.Minute)),
|
makeDates(time.Date(2019, 2, 9, 14, 43, 0, 0, time.UTC), 5, time.Minute)),
|
||||||
data.NewField("grafana.Percentage CPU", nil, []float64{
|
data.NewField("Percentage CPU", nil, []float64{
|
||||||
1.51, 2.38, 1.69, 2.27, 1.96,
|
1.51, 2.38, 1.69, 2.27, 1.96,
|
||||||
}).SetConfig(&data.FieldConfig{Unit: "Percent"})),
|
}).SetConfig(&data.FieldConfig{Unit: "Percent"})),
|
||||||
},
|
},
|
||||||
@ -259,14 +279,14 @@ func TestAzureMonitorParseResponse(t *testing.T) {
|
|||||||
data.NewFrame("",
|
data.NewFrame("",
|
||||||
data.NewField("", nil,
|
data.NewField("", nil,
|
||||||
makeDates(time.Date(2019, 2, 9, 14, 44, 0, 0, time.UTC), 5, time.Minute)),
|
makeDates(time.Date(2019, 2, 9, 14, 44, 0, 0, time.UTC), 5, time.Minute)),
|
||||||
data.NewField("grafana.Percentage CPU", nil, []float64{
|
data.NewField("Percentage CPU", nil, []float64{
|
||||||
4, 4, 4, 4, 4,
|
4, 4, 4, 4, 4,
|
||||||
}).SetConfig(&data.FieldConfig{Unit: "Percent"})),
|
}).SetConfig(&data.FieldConfig{Unit: "Percent"})),
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
name: "multi dimension time series response",
|
name: "single dimension time series response",
|
||||||
responseFile: "6-azure-monitor-response-multi-dimension.json",
|
responseFile: "6-azure-monitor-response-single-dimension.json",
|
||||||
mockQuery: &AzureMonitorQuery{
|
mockQuery: &AzureMonitorQuery{
|
||||||
UrlComponents: map[string]string{
|
UrlComponents: map[string]string{
|
||||||
"resourceName": "grafana",
|
"resourceName": "grafana",
|
||||||
@ -275,31 +295,24 @@ func TestAzureMonitorParseResponse(t *testing.T) {
|
|||||||
"aggregation": {"Average"},
|
"aggregation": {"Average"},
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
// Regarding multi-dimensional response:
|
|
||||||
// - It seems they all share the same time index, so maybe can be a wide frame.
|
|
||||||
// - Due to the type for the Azure monitor response, nulls currently become 0.
|
|
||||||
// - blogtype=X should maybe become labels.
|
|
||||||
expectedFrames: data.Frames{
|
expectedFrames: data.Frames{
|
||||||
data.NewFrame("",
|
data.NewFrame("",
|
||||||
data.NewField("", nil,
|
data.NewField("", nil,
|
||||||
makeDates(time.Date(2019, 2, 9, 15, 21, 0, 0, time.UTC), 6, time.Hour)),
|
makeDates(time.Date(2019, 2, 9, 15, 21, 0, 0, time.UTC), 6, time.Hour)),
|
||||||
data.NewField("grafana{blobtype=PageBlob}.Blob Count", nil, []float64{
|
data.NewField("Blob Count", data.Labels{"blobtype": "PageBlob"},
|
||||||
3, 3, 3, 3, 3, 0,
|
[]float64{3, 3, 3, 3, 3, 0}).SetConfig(&data.FieldConfig{Unit: "Count"})),
|
||||||
}).SetConfig(&data.FieldConfig{Unit: "Count"})),
|
|
||||||
|
|
||||||
data.NewFrame("",
|
data.NewFrame("",
|
||||||
data.NewField("", nil,
|
data.NewField("", nil,
|
||||||
makeDates(time.Date(2019, 2, 9, 15, 21, 0, 0, time.UTC), 6, time.Hour)),
|
makeDates(time.Date(2019, 2, 9, 15, 21, 0, 0, time.UTC), 6, time.Hour)),
|
||||||
data.NewField("grafana{blobtype=BlockBlob}.Blob Count", nil, []float64{
|
data.NewField("Blob Count", data.Labels{"blobtype": "BlockBlob"},
|
||||||
1, 1, 1, 1, 1, 0,
|
[]float64{1, 1, 1, 1, 1, 0}).SetConfig(&data.FieldConfig{Unit: "Count"})),
|
||||||
}).SetConfig(&data.FieldConfig{Unit: "Count"})),
|
|
||||||
|
|
||||||
data.NewFrame("",
|
data.NewFrame("",
|
||||||
data.NewField("", nil,
|
data.NewField("", nil,
|
||||||
makeDates(time.Date(2019, 2, 9, 15, 21, 0, 0, time.UTC), 6, time.Hour)),
|
makeDates(time.Date(2019, 2, 9, 15, 21, 0, 0, time.UTC), 6, time.Hour)),
|
||||||
data.NewField("grafana{blobtype=Azure Data Lake Storage}.Blob Count", nil, []float64{
|
data.NewField("Blob Count", data.Labels{"blobtype": "Azure Data Lake Storage"},
|
||||||
0, 0, 0, 0, 0, 0,
|
[]float64{0, 0, 0, 0, 0, 0}).SetConfig(&data.FieldConfig{Unit: "Count"})),
|
||||||
}).SetConfig(&data.FieldConfig{Unit: "Count"})),
|
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
@ -318,14 +331,14 @@ func TestAzureMonitorParseResponse(t *testing.T) {
|
|||||||
data.NewFrame("",
|
data.NewFrame("",
|
||||||
data.NewField("", nil,
|
data.NewField("", nil,
|
||||||
makeDates(time.Date(2019, 2, 9, 13, 29, 0, 0, time.UTC), 5, time.Minute)),
|
makeDates(time.Date(2019, 2, 9, 13, 29, 0, 0, time.UTC), 5, time.Minute)),
|
||||||
data.NewField("custom grafanastaging Microsoft.Compute/virtualMachines grafana Percentage CPU", nil, []float64{
|
data.NewField("Percentage CPU", nil, []float64{
|
||||||
8.26, 8.7, 14.82, 10.07, 8.52,
|
8.26, 8.7, 14.82, 10.07, 8.52,
|
||||||
}).SetConfig(&data.FieldConfig{Unit: "Percent"})),
|
}).SetConfig(&data.FieldConfig{Unit: "Percent", DisplayName: "custom grafanastaging Microsoft.Compute/virtualMachines grafana Percentage CPU"})),
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
name: "multi dimension with alias",
|
name: "single dimension with alias",
|
||||||
responseFile: "6-azure-monitor-response-multi-dimension.json",
|
responseFile: "6-azure-monitor-response-single-dimension.json",
|
||||||
mockQuery: &AzureMonitorQuery{
|
mockQuery: &AzureMonitorQuery{
|
||||||
Alias: "{{dimensionname}}={{DimensionValue}}",
|
Alias: "{{dimensionname}}={{DimensionValue}}",
|
||||||
UrlComponents: map[string]string{
|
UrlComponents: map[string]string{
|
||||||
@ -339,23 +352,57 @@ func TestAzureMonitorParseResponse(t *testing.T) {
|
|||||||
data.NewFrame("",
|
data.NewFrame("",
|
||||||
data.NewField("", nil,
|
data.NewField("", nil,
|
||||||
makeDates(time.Date(2019, 2, 9, 15, 21, 0, 0, time.UTC), 6, time.Hour)),
|
makeDates(time.Date(2019, 2, 9, 15, 21, 0, 0, time.UTC), 6, time.Hour)),
|
||||||
data.NewField("blobtype=PageBlob", nil, []float64{
|
data.NewField("Blob Count", data.Labels{"blobtype": "PageBlob"},
|
||||||
3, 3, 3, 3, 3, 0,
|
[]float64{3, 3, 3, 3, 3, 0}).SetConfig(&data.FieldConfig{Unit: "Count", DisplayName: "blobtype=PageBlob"})),
|
||||||
}).SetConfig(&data.FieldConfig{Unit: "Count"})),
|
|
||||||
|
|
||||||
data.NewFrame("",
|
data.NewFrame("",
|
||||||
data.NewField("", nil,
|
data.NewField("", nil,
|
||||||
makeDates(time.Date(2019, 2, 9, 15, 21, 0, 0, time.UTC), 6, time.Hour)),
|
makeDates(time.Date(2019, 2, 9, 15, 21, 0, 0, time.UTC), 6, time.Hour)),
|
||||||
data.NewField("blobtype=BlockBlob", nil, []float64{
|
data.NewField("Blob Count", data.Labels{"blobtype": "BlockBlob"}, []float64{
|
||||||
1, 1, 1, 1, 1, 0,
|
1, 1, 1, 1, 1, 0,
|
||||||
}).SetConfig(&data.FieldConfig{Unit: "Count"})),
|
}).SetConfig(&data.FieldConfig{Unit: "Count", DisplayName: "blobtype=BlockBlob"})),
|
||||||
|
|
||||||
data.NewFrame("",
|
data.NewFrame("",
|
||||||
data.NewField("", nil,
|
data.NewField("", nil,
|
||||||
makeDates(time.Date(2019, 2, 9, 15, 21, 0, 0, time.UTC), 6, time.Hour)),
|
makeDates(time.Date(2019, 2, 9, 15, 21, 0, 0, time.UTC), 6, time.Hour)),
|
||||||
data.NewField("blobtype=Azure Data Lake Storage", nil, []float64{
|
data.NewField("Blob Count", data.Labels{"blobtype": "Azure Data Lake Storage"}, []float64{
|
||||||
0, 0, 0, 0, 0, 0,
|
0, 0, 0, 0, 0, 0,
|
||||||
}).SetConfig(&data.FieldConfig{Unit: "Count"})),
|
}).SetConfig(&data.FieldConfig{Unit: "Count", DisplayName: "blobtype=Azure Data Lake Storage"})),
|
||||||
|
},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "multiple dimension time series response with label alias",
|
||||||
|
responseFile: "7-azure-monitor-response-multi-dimension.json",
|
||||||
|
mockQuery: &AzureMonitorQuery{
|
||||||
|
Alias: "{{resourcegroup}} {Blob Type={{blobtype}}, Tier={{tier}}}",
|
||||||
|
UrlComponents: map[string]string{
|
||||||
|
"resourceName": "grafana",
|
||||||
|
},
|
||||||
|
Params: url.Values{
|
||||||
|
"aggregation": {"Average"},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
expectedFrames: data.Frames{
|
||||||
|
data.NewFrame("",
|
||||||
|
data.NewField("", nil,
|
||||||
|
makeDates(time.Date(2020, 06, 30, 9, 58, 0, 0, time.UTC), 3, time.Hour)),
|
||||||
|
data.NewField("Blob Capacity", data.Labels{"blobtype": "PageBlob", "tier": "Standard"},
|
||||||
|
[]float64{675530, 675530, 675530}).SetConfig(
|
||||||
|
&data.FieldConfig{Unit: "Bytes", DisplayName: "danieltest {Blob Type=PageBlob, Tier=Standard}"})),
|
||||||
|
|
||||||
|
data.NewFrame("",
|
||||||
|
data.NewField("", nil,
|
||||||
|
makeDates(time.Date(2020, 06, 30, 9, 58, 0, 0, time.UTC), 3, time.Hour)),
|
||||||
|
data.NewField("Blob Capacity", data.Labels{"blobtype": "BlockBlob", "tier": "Hot"},
|
||||||
|
[]float64{0, 0, 0}).SetConfig(
|
||||||
|
&data.FieldConfig{Unit: "Bytes", DisplayName: "danieltest {Blob Type=BlockBlob, Tier=Hot}"})),
|
||||||
|
|
||||||
|
data.NewFrame("",
|
||||||
|
data.NewField("", nil,
|
||||||
|
makeDates(time.Date(2020, 06, 30, 9, 58, 0, 0, time.UTC), 3, time.Hour)),
|
||||||
|
data.NewField("Blob Capacity", data.Labels{"blobtype": "Azure Data Lake Storage", "tier": "Cool"},
|
||||||
|
[]float64{0, 0, 0}).SetConfig(
|
||||||
|
&data.FieldConfig{Unit: "Bytes", DisplayName: "danieltest {Blob Type=Azure Data Lake Storage, Tier=Cool}"})),
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
|
1
pkg/tsdb/azuremonitor/testdata/applicationinsights/bigger.json
vendored
Normal file
1
pkg/tsdb/azuremonitor/testdata/applicationinsights/bigger.json
vendored
Normal file
File diff suppressed because one or more lines are too long
119
pkg/tsdb/azuremonitor/testdata/azuremonitor/7-azure-monitor-response-multi-dimension.json
vendored
Normal file
119
pkg/tsdb/azuremonitor/testdata/azuremonitor/7-azure-monitor-response-multi-dimension.json
vendored
Normal file
@ -0,0 +1,119 @@
|
|||||||
|
{
|
||||||
|
"cost": 0,
|
||||||
|
"timespan": "2020-06-30T09:58:58Z/2020-06-30T12:58:58Z",
|
||||||
|
"interval": "PT1H",
|
||||||
|
"value": [
|
||||||
|
{
|
||||||
|
"id": "/subscriptions/44693801-6ee6-49de-9b2d-9106972f9572/resourceGroups/danieltest/providers/Microsoft.Storage/storageAccounts/danieltestdiag187/blobServices/default/providers/Microsoft.Insights/metrics/BlobCapacity",
|
||||||
|
"type": "Microsoft.Insights/metrics",
|
||||||
|
"name": {
|
||||||
|
"value": "BlobCapacity",
|
||||||
|
"localizedValue": "Blob Capacity"
|
||||||
|
},
|
||||||
|
"displayDescription": "The amount of storage used by the storage account’s Blob service in bytes.",
|
||||||
|
"unit": "Bytes",
|
||||||
|
"timeseries": [
|
||||||
|
{
|
||||||
|
"metadatavalues": [
|
||||||
|
{
|
||||||
|
"name": {
|
||||||
|
"value": "blobtype",
|
||||||
|
"localizedValue": "blobtype"
|
||||||
|
},
|
||||||
|
"value": "PageBlob"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": {
|
||||||
|
"value": "tier",
|
||||||
|
"localizedValue": "tier"
|
||||||
|
},
|
||||||
|
"value": "Standard"
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"data": [
|
||||||
|
{
|
||||||
|
"timeStamp": "2020-06-30T09:58:00Z",
|
||||||
|
"average": 675530
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"timeStamp": "2020-06-30T10:58:00Z",
|
||||||
|
"average": 675530
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"timeStamp": "2020-06-30T11:58:00Z",
|
||||||
|
"average": 675530
|
||||||
|
}
|
||||||
|
]
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"metadatavalues": [
|
||||||
|
{
|
||||||
|
"name": {
|
||||||
|
"value": "blobtype",
|
||||||
|
"localizedValue": "blobtype"
|
||||||
|
},
|
||||||
|
"value": "BlockBlob"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": {
|
||||||
|
"value": "tier",
|
||||||
|
"localizedValue": "tier"
|
||||||
|
},
|
||||||
|
"value": "Hot"
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"data": [
|
||||||
|
{
|
||||||
|
"timeStamp": "2020-06-30T09:58:00Z",
|
||||||
|
"average": 0
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"timeStamp": "2020-06-30T10:58:00Z",
|
||||||
|
"average": 0
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"timeStamp": "2020-06-30T11:58:00Z",
|
||||||
|
"average": 0
|
||||||
|
}
|
||||||
|
]
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"metadatavalues": [
|
||||||
|
{
|
||||||
|
"name": {
|
||||||
|
"value": "blobtype",
|
||||||
|
"localizedValue": "blobtype"
|
||||||
|
},
|
||||||
|
"value": "Azure Data Lake Storage"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": {
|
||||||
|
"value": "tier",
|
||||||
|
"localizedValue": "tier"
|
||||||
|
},
|
||||||
|
"value": "Cool"
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"data": [
|
||||||
|
{
|
||||||
|
"timeStamp": "2020-06-30T09:58:00Z",
|
||||||
|
"average": 0
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"timeStamp": "2020-06-30T10:58:00Z",
|
||||||
|
"average": 0
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"timeStamp": "2020-06-30T11:58:00Z",
|
||||||
|
"average": 0
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"errorCode": "Success"
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"namespace": "Microsoft.Storage/storageAccounts/blobServices",
|
||||||
|
"resourceregion": "westeurope"
|
||||||
|
}
|
||||||
|
|
@ -87,8 +87,8 @@ type azureMonitorJSONQuery struct {
|
|||||||
Aggregation string `json:"aggregation"`
|
Aggregation string `json:"aggregation"`
|
||||||
Alias string `json:"alias"`
|
Alias string `json:"alias"`
|
||||||
AllowedTimeGrainsMs []int64 `json:"allowedTimeGrainsMs"`
|
AllowedTimeGrainsMs []int64 `json:"allowedTimeGrainsMs"`
|
||||||
Dimension string `json:"dimension"`
|
Dimension string `json:"dimension"` // old model
|
||||||
DimensionFilter string `json:"dimensionFilter"`
|
DimensionFilter string `json:"dimensionFilter"` // old model
|
||||||
Format string `json:"format"`
|
Format string `json:"format"`
|
||||||
MetricDefinition string `json:"metricDefinition"`
|
MetricDefinition string `json:"metricDefinition"`
|
||||||
MetricName string `json:"metricName"`
|
MetricName string `json:"metricName"`
|
||||||
@ -97,10 +97,24 @@ type azureMonitorJSONQuery struct {
|
|||||||
ResourceName string `json:"resourceName"`
|
ResourceName string `json:"resourceName"`
|
||||||
TimeGrain string `json:"timeGrain"`
|
TimeGrain string `json:"timeGrain"`
|
||||||
Top string `json:"top"`
|
Top string `json:"top"`
|
||||||
|
|
||||||
|
DimensionsFilters []azureMonitorDimensionFilter `json:"dimensionsFilters"` // new model
|
||||||
} `json:"azureMonitor"`
|
} `json:"azureMonitor"`
|
||||||
Subscription string `json:"subscription"`
|
Subscription string `json:"subscription"`
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// azureMonitorDimensionFilter is the model for the frontend sent for azureMonitor metric
|
||||||
|
// queries like "BlobType", "eq", "*"
|
||||||
|
type azureMonitorDimensionFilter struct {
|
||||||
|
Dimension string `json:"dimension"`
|
||||||
|
Operator string `json:"operator"`
|
||||||
|
Filter string `json:"filter"`
|
||||||
|
}
|
||||||
|
|
||||||
|
func (a azureMonitorDimensionFilter) String() string {
|
||||||
|
return fmt.Sprintf("%v %v '%v'", a.Dimension, a.Operator, a.Filter)
|
||||||
|
}
|
||||||
|
|
||||||
// insightsJSONQuery is the frontend JSON query model for an Azure Application Insights query.
|
// insightsJSONQuery is the frontend JSON query model for an Azure Application Insights query.
|
||||||
type insightsJSONQuery struct {
|
type insightsJSONQuery struct {
|
||||||
AppInsights struct {
|
AppInsights struct {
|
||||||
|
@ -74,11 +74,18 @@ export default class AzureMonitorDatasource extends DataSourceWithBackend<AzureM
|
|||||||
const aggregation = templateSrv.replace(item.aggregation, scopedVars);
|
const aggregation = templateSrv.replace(item.aggregation, scopedVars);
|
||||||
const top = templateSrv.replace(item.top || '', scopedVars);
|
const top = templateSrv.replace(item.top || '', scopedVars);
|
||||||
|
|
||||||
|
const dimensionsFilters = item.dimensionFilters.map(f => {
|
||||||
|
return {
|
||||||
|
dimension: templateSrv.replace(f.dimension, scopedVars),
|
||||||
|
operator: f.operator || 'eq',
|
||||||
|
filter: templateSrv.replace(f.filter, scopedVars),
|
||||||
|
};
|
||||||
|
});
|
||||||
|
|
||||||
return {
|
return {
|
||||||
refId: target.refId,
|
refId: target.refId,
|
||||||
subscription: subscriptionId,
|
subscription: subscriptionId,
|
||||||
queryType: AzureQueryType.AzureMonitor,
|
queryType: AzureQueryType.AzureMonitor,
|
||||||
type: 'timeSeriesQuery',
|
|
||||||
azureMonitor: {
|
azureMonitor: {
|
||||||
resourceGroup,
|
resourceGroup,
|
||||||
resourceName,
|
resourceName,
|
||||||
@ -89,9 +96,8 @@ export default class AzureMonitorDatasource extends DataSourceWithBackend<AzureM
|
|||||||
metricNamespace:
|
metricNamespace:
|
||||||
metricNamespace && metricNamespace !== defaultDropdownValue ? metricNamespace : metricDefinition,
|
metricNamespace && metricNamespace !== defaultDropdownValue ? metricNamespace : metricDefinition,
|
||||||
aggregation: aggregation,
|
aggregation: aggregation,
|
||||||
dimension: templateSrv.replace(item.dimension, scopedVars),
|
dimensionsFilters,
|
||||||
top: top || '10',
|
top: top || '10',
|
||||||
dimensionFilter: templateSrv.replace(item.dimensionFilter, scopedVars),
|
|
||||||
alias: item.alias,
|
alias: item.alias,
|
||||||
format: target.format,
|
format: target.format,
|
||||||
},
|
},
|
||||||
|
@ -13,6 +13,7 @@ import {
|
|||||||
import { Observable, of, from } from 'rxjs';
|
import { Observable, of, from } from 'rxjs';
|
||||||
import { DataSourceWithBackend } from '@grafana/runtime';
|
import { DataSourceWithBackend } from '@grafana/runtime';
|
||||||
import InsightsAnalyticsDatasource from './insights_analytics/insights_analytics_datasource';
|
import InsightsAnalyticsDatasource from './insights_analytics/insights_analytics_datasource';
|
||||||
|
import { migrateMetricsDimensionFilters } from './query_ctrl';
|
||||||
|
|
||||||
export default class Datasource extends DataSourceApi<AzureMonitorQuery, AzureDataSourceJsonData> {
|
export default class Datasource extends DataSourceApi<AzureMonitorQuery, AzureDataSourceJsonData> {
|
||||||
azureMonitorDatasource: AzureMonitorDatasource;
|
azureMonitorDatasource: AzureMonitorDatasource;
|
||||||
@ -64,6 +65,10 @@ export default class Datasource extends DataSourceApi<AzureMonitorQuery, AzureDa
|
|||||||
target.queryType = AzureQueryType.AzureMonitor;
|
target.queryType = AzureQueryType.AzureMonitor;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (target.queryType === AzureQueryType.AzureMonitor) {
|
||||||
|
migrateMetricsDimensionFilters(target.azureMonitor);
|
||||||
|
}
|
||||||
|
|
||||||
// Check that we have options
|
// Check that we have options
|
||||||
const opts = (target as any)[this.optionsKey[target.queryType]];
|
const opts = (target as any)[this.optionsKey[target.queryType]];
|
||||||
|
|
||||||
|
@ -132,44 +132,73 @@
|
|||||||
<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-show="ctrl.target.azureMonitor.dimensions.length > 0">
|
|
||||||
<div class="gf-form">
|
<!-- NO Filters-->
|
||||||
<label class="gf-form-label query-keyword width-9">Dimension</label>
|
<ng-container ng-if="ctrl.target.azureMonitor.dimensionFilters.length < 1">
|
||||||
<div class="gf-form-select-wrapper gf-form-select-wrapper--caret-indent">
|
<div class="gf-form-inline">
|
||||||
<select
|
<div class="gf-form">
|
||||||
class="gf-form-input min-width-12"
|
<label class="gf-form-label query-keyword width-9">Dimension</label>
|
||||||
ng-model="ctrl.target.azureMonitor.dimension"
|
</div>
|
||||||
ng-options="f.value as f.text for f in ctrl.target.azureMonitor.dimensions"
|
<div class="gf-form">
|
||||||
ng-change="ctrl.refresh()"
|
<a ng-click="ctrl.azureMonitorAddDimensionFilter()" class="gf-form-label query-part"><icon name="'plus'"></icon></a>
|
||||||
></select>
|
</div>
|
||||||
|
<div class="gf-form gf-form--grow">
|
||||||
|
<div class="gf-form-label gf-form-label--grow"></div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<div class="gf-form">
|
</ng-container>
|
||||||
<label class="gf-form-label query-keyword width-3">eq</label>
|
|
||||||
<input
|
<!-- YES Filters-->
|
||||||
type="text"
|
<ng-container ng-if="ctrl.target.azureMonitor.dimensionFilters.length > 0">
|
||||||
class="gf-form-input width-17"
|
<div ng-repeat="dim in ctrl.target.azureMonitor.dimensionFilters track by $index" class="gf-form-inline">
|
||||||
ng-model="ctrl.target.azureMonitor.dimensionFilter"
|
<div class="gf-form">
|
||||||
spellcheck="false"
|
<label class="gf-form-label query-keyword width-9">Dimension</label>
|
||||||
placeholder="auto"
|
<div class="gf-form-select-wrapper gf-form-select-wrapper--caret-indent">
|
||||||
ng-blur="ctrl.refresh()"
|
<select
|
||||||
/>
|
class="gf-form-input min-width-12"
|
||||||
|
ng-model="dim.dimension"
|
||||||
|
ng-options="f.value as f.text for f in ctrl.target.azureMonitor.dimensions"
|
||||||
|
ng-change="ctrl.refresh()"
|
||||||
|
></select>
|
||||||
|
</div>
|
||||||
|
<label class="gf-form-label query-keyword width-3">eq</label>
|
||||||
|
<input
|
||||||
|
type="text"
|
||||||
|
class="gf-form-input width-17"
|
||||||
|
ng-model="dim.filter"
|
||||||
|
spellcheck="false"
|
||||||
|
placeholder="Anything (*)"
|
||||||
|
ng-blur="ctrl.refresh()"
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
<div class="gf-form">
|
||||||
|
<a ng-click="ctrl.azureMonitorRemoveDimensionFilter($index)" class="gf-form-label query-part"><icon name="'minus'"></icon></a>
|
||||||
|
</div>
|
||||||
|
<div class="gf-form" ng-if="$last">
|
||||||
|
<a ng-click="ctrl.azureMonitorAddDimensionFilter()" class="gf-form-label query-part"><icon name="'plus'"></icon></a>
|
||||||
|
</div>
|
||||||
|
<div class="gf-form gf-form--grow">
|
||||||
|
<div class="gf-form-label gf-form-label--grow"></div>
|
||||||
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<div class="gf-form">
|
<div class="gf-form-inline">
|
||||||
<label class="gf-form-label query-keyword width-9">Top</label>
|
<div class="gf-form">
|
||||||
<input
|
<label class="gf-form-label query-keyword width-9">Top</label>
|
||||||
type="text"
|
<input
|
||||||
class="gf-form-input width-3"
|
type="text"
|
||||||
ng-model="ctrl.target.azureMonitor.top"
|
class="gf-form-input width-3"
|
||||||
spellcheck="false"
|
ng-model="ctrl.target.azureMonitor.top"
|
||||||
placeholder="10"
|
spellcheck="false"
|
||||||
ng-blur="ctrl.refresh()"
|
placeholder="10"
|
||||||
/>
|
ng-blur="ctrl.refresh()"
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
<div class="gf-form gf-form--grow">
|
||||||
|
<div class="gf-form-label gf-form-label--grow"></div>
|
||||||
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<div class="gf-form gf-form--grow">
|
</ng-container>
|
||||||
<div class="gf-form-label gf-form-label--grow"></div>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
<div class="gf-form-inline">
|
<div class="gf-form-inline">
|
||||||
<div class="gf-form">
|
<div class="gf-form">
|
||||||
<label class="gf-form-label query-keyword width-9">Legend Format</label>
|
<label class="gf-form-label query-keyword width-9">Legend Format</label>
|
||||||
|
@ -8,7 +8,7 @@ import kbn from 'app/core/utils/kbn';
|
|||||||
import { TemplateSrv } from 'app/features/templating/template_srv';
|
import { TemplateSrv } from 'app/features/templating/template_srv';
|
||||||
import { auto, IPromise } from 'angular';
|
import { auto, IPromise } from 'angular';
|
||||||
import { DataFrame, PanelEvents } from '@grafana/data';
|
import { DataFrame, PanelEvents } from '@grafana/data';
|
||||||
import { AzureQueryType } from './types';
|
import { AzureQueryType, AzureMetricQuery } from './types';
|
||||||
|
|
||||||
export interface ResultFormat {
|
export interface ResultFormat {
|
||||||
text: string;
|
text: string;
|
||||||
@ -27,23 +27,7 @@ export class AzureMonitorQueryCtrl extends QueryCtrl {
|
|||||||
refId: string;
|
refId: string;
|
||||||
queryType: AzureQueryType;
|
queryType: AzureQueryType;
|
||||||
subscription: string;
|
subscription: string;
|
||||||
azureMonitor: {
|
azureMonitor: AzureMetricQuery;
|
||||||
resourceGroup: string;
|
|
||||||
resourceName: string;
|
|
||||||
metricDefinition: string;
|
|
||||||
metricNamespace: string;
|
|
||||||
metricName: string;
|
|
||||||
dimensionFilter: string;
|
|
||||||
timeGrain: string;
|
|
||||||
timeGrainUnit: string;
|
|
||||||
allowedTimeGrainsMs: number[];
|
|
||||||
dimensions: any[];
|
|
||||||
dimension: any;
|
|
||||||
top: string;
|
|
||||||
aggregation: string;
|
|
||||||
aggOptions: string[];
|
|
||||||
timeGrains: Array<{ text: string; value: string }>;
|
|
||||||
};
|
|
||||||
azureLogAnalytics: {
|
azureLogAnalytics: {
|
||||||
query: string;
|
query: string;
|
||||||
resultFormat: string;
|
resultFormat: string;
|
||||||
@ -139,6 +123,8 @@ export class AzureMonitorQueryCtrl extends QueryCtrl {
|
|||||||
|
|
||||||
this.migrateApplicationInsightsDimensions();
|
this.migrateApplicationInsightsDimensions();
|
||||||
|
|
||||||
|
migrateMetricsDimensionFilters(this.target.azureMonitor);
|
||||||
|
|
||||||
this.panelCtrl.events.on(PanelEvents.dataReceived, this.onDataReceived.bind(this), $scope);
|
this.panelCtrl.events.on(PanelEvents.dataReceived, this.onDataReceived.bind(this), $scope);
|
||||||
this.panelCtrl.events.on(PanelEvents.dataError, this.onDataError.bind(this), $scope);
|
this.panelCtrl.events.on(PanelEvents.dataError, this.onDataError.bind(this), $scope);
|
||||||
this.resultFormats = [
|
this.resultFormats = [
|
||||||
@ -219,12 +205,13 @@ export class AzureMonitorQueryCtrl extends QueryCtrl {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
const oldAzureTimeGrains = (this.target.azureMonitor as any).timeGrains;
|
||||||
if (
|
if (
|
||||||
this.target.azureMonitor.timeGrains &&
|
oldAzureTimeGrains &&
|
||||||
this.target.azureMonitor.timeGrains.length > 0 &&
|
oldAzureTimeGrains.length > 0 &&
|
||||||
(!this.target.azureMonitor.allowedTimeGrainsMs || this.target.azureMonitor.allowedTimeGrainsMs.length === 0)
|
(!this.target.azureMonitor.allowedTimeGrainsMs || this.target.azureMonitor.allowedTimeGrainsMs.length === 0)
|
||||||
) {
|
) {
|
||||||
this.target.azureMonitor.allowedTimeGrainsMs = this.convertTimeGrainsToMs(this.target.azureMonitor.timeGrains);
|
this.target.azureMonitor.allowedTimeGrainsMs = this.convertTimeGrainsToMs(oldAzureTimeGrains);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (
|
if (
|
||||||
@ -328,10 +315,8 @@ export class AzureMonitorQueryCtrl extends QueryCtrl {
|
|||||||
this.target.azureMonitor.resourceName = this.defaultDropdownValue;
|
this.target.azureMonitor.resourceName = this.defaultDropdownValue;
|
||||||
this.target.azureMonitor.metricName = this.defaultDropdownValue;
|
this.target.azureMonitor.metricName = this.defaultDropdownValue;
|
||||||
this.target.azureMonitor.aggregation = '';
|
this.target.azureMonitor.aggregation = '';
|
||||||
this.target.azureMonitor.timeGrains = [];
|
|
||||||
this.target.azureMonitor.timeGrain = '';
|
this.target.azureMonitor.timeGrain = '';
|
||||||
this.target.azureMonitor.dimensions = [];
|
this.target.azureMonitor.dimensionFilters = [];
|
||||||
this.target.azureMonitor.dimension = '';
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -439,10 +424,8 @@ export class AzureMonitorQueryCtrl extends QueryCtrl {
|
|||||||
this.target.azureMonitor.metricNamespace = this.defaultDropdownValue;
|
this.target.azureMonitor.metricNamespace = this.defaultDropdownValue;
|
||||||
this.target.azureMonitor.metricName = this.defaultDropdownValue;
|
this.target.azureMonitor.metricName = this.defaultDropdownValue;
|
||||||
this.target.azureMonitor.aggregation = '';
|
this.target.azureMonitor.aggregation = '';
|
||||||
this.target.azureMonitor.timeGrains = [];
|
|
||||||
this.target.azureMonitor.timeGrain = '';
|
this.target.azureMonitor.timeGrain = '';
|
||||||
this.target.azureMonitor.dimensions = [];
|
this.target.azureMonitor.dimensionFilters = [];
|
||||||
this.target.azureMonitor.dimension = '';
|
|
||||||
this.refresh();
|
this.refresh();
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -451,27 +434,22 @@ export class AzureMonitorQueryCtrl extends QueryCtrl {
|
|||||||
this.target.azureMonitor.metricNamespace = this.defaultDropdownValue;
|
this.target.azureMonitor.metricNamespace = this.defaultDropdownValue;
|
||||||
this.target.azureMonitor.metricName = this.defaultDropdownValue;
|
this.target.azureMonitor.metricName = this.defaultDropdownValue;
|
||||||
this.target.azureMonitor.aggregation = '';
|
this.target.azureMonitor.aggregation = '';
|
||||||
this.target.azureMonitor.timeGrains = [];
|
|
||||||
this.target.azureMonitor.timeGrain = '';
|
this.target.azureMonitor.timeGrain = '';
|
||||||
this.target.azureMonitor.dimensions = [];
|
this.target.azureMonitor.dimensionFilters = [];
|
||||||
this.target.azureMonitor.dimension = '';
|
|
||||||
}
|
}
|
||||||
|
|
||||||
onResourceNameChange() {
|
onResourceNameChange() {
|
||||||
this.target.azureMonitor.metricNamespace = this.defaultDropdownValue;
|
this.target.azureMonitor.metricNamespace = this.defaultDropdownValue;
|
||||||
this.target.azureMonitor.metricName = this.defaultDropdownValue;
|
this.target.azureMonitor.metricName = this.defaultDropdownValue;
|
||||||
this.target.azureMonitor.aggregation = '';
|
this.target.azureMonitor.aggregation = '';
|
||||||
this.target.azureMonitor.timeGrains = [];
|
|
||||||
this.target.azureMonitor.timeGrain = '';
|
this.target.azureMonitor.timeGrain = '';
|
||||||
this.target.azureMonitor.dimensions = [];
|
this.target.azureMonitor.dimensionFilters = [];
|
||||||
this.target.azureMonitor.dimension = '';
|
|
||||||
this.refresh();
|
this.refresh();
|
||||||
}
|
}
|
||||||
|
|
||||||
onMetricNamespacesChange() {
|
onMetricNamespacesChange() {
|
||||||
this.target.azureMonitor.metricName = this.defaultDropdownValue;
|
this.target.azureMonitor.metricName = this.defaultDropdownValue;
|
||||||
this.target.azureMonitor.dimensions = [];
|
this.target.azureMonitor.dimensionFilters = [];
|
||||||
this.target.azureMonitor.dimension = '';
|
|
||||||
}
|
}
|
||||||
|
|
||||||
onMetricNameChange(): IPromise<void> {
|
onMetricNameChange(): IPromise<void> {
|
||||||
@ -489,16 +467,20 @@ export class AzureMonitorQueryCtrl extends QueryCtrl {
|
|||||||
this.replace(this.target.azureMonitor.metricName)
|
this.replace(this.target.azureMonitor.metricName)
|
||||||
)
|
)
|
||||||
.then((metadata: any) => {
|
.then((metadata: any) => {
|
||||||
this.target.azureMonitor.aggOptions = metadata.supportedAggTypes || [metadata.primaryAggType];
|
console.log('Update metadata', metadata);
|
||||||
this.target.azureMonitor.aggregation = metadata.primaryAggType;
|
|
||||||
this.target.azureMonitor.timeGrains = [{ text: 'auto', value: 'auto' }].concat(metadata.supportedTimeGrains);
|
|
||||||
this.target.azureMonitor.timeGrain = 'auto';
|
|
||||||
|
|
||||||
|
this.target.azureMonitor.aggregation = metadata.primaryAggType;
|
||||||
|
this.target.azureMonitor.timeGrain = 'auto';
|
||||||
this.target.azureMonitor.allowedTimeGrainsMs = this.convertTimeGrainsToMs(metadata.supportedTimeGrains || []);
|
this.target.azureMonitor.allowedTimeGrainsMs = this.convertTimeGrainsToMs(metadata.supportedTimeGrains || []);
|
||||||
|
|
||||||
this.target.azureMonitor.dimensions = metadata.dimensions;
|
// HACK: this saves the last metadata values in the panel json ¯\_(ツ)_/¯
|
||||||
|
const hackState = this.target.azureMonitor as any;
|
||||||
|
hackState.aggOptions = metadata.supportedAggTypes || [metadata.primaryAggType];
|
||||||
|
hackState.timeGrains = [{ text: 'auto', value: 'auto' }].concat(metadata.supportedTimeGrains);
|
||||||
|
hackState.dimensions = metadata.dimensions;
|
||||||
|
|
||||||
if (metadata.dimensions.length > 0) {
|
if (metadata.dimensions.length > 0) {
|
||||||
this.target.azureMonitor.dimension = metadata.dimensions[0].value;
|
// this.target.azureMonitor.dimension = metadata.dimensions[0].value;
|
||||||
}
|
}
|
||||||
|
|
||||||
return this.refresh();
|
return this.refresh();
|
||||||
@ -537,13 +519,29 @@ export class AzureMonitorQueryCtrl extends QueryCtrl {
|
|||||||
}
|
}
|
||||||
|
|
||||||
getAzureMonitorAutoInterval() {
|
getAzureMonitorAutoInterval() {
|
||||||
return this.generateAutoUnits(this.target.azureMonitor.timeGrain, this.target.azureMonitor.timeGrains);
|
return this.generateAutoUnits(this.target.azureMonitor.timeGrain, (this.target.azureMonitor as any).timeGrains);
|
||||||
}
|
}
|
||||||
|
|
||||||
getApplicationInsightAutoInterval() {
|
getApplicationInsightAutoInterval() {
|
||||||
return this.generateAutoUnits(this.target.appInsights.timeGrain, this.target.appInsights.timeGrains);
|
return this.generateAutoUnits(this.target.appInsights.timeGrain, this.target.appInsights.timeGrains);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
azureMonitorAddDimensionFilter() {
|
||||||
|
console.log('Add dimension', this.target.azureMonitor);
|
||||||
|
this.target.azureMonitor.dimensionFilters.push({
|
||||||
|
dimension: '',
|
||||||
|
operator: 'eq',
|
||||||
|
filter: '',
|
||||||
|
});
|
||||||
|
this.refresh();
|
||||||
|
}
|
||||||
|
|
||||||
|
azureMonitorRemoveDimensionFilter(index: number) {
|
||||||
|
this.target.azureMonitor.dimensionFilters.splice(index, 1);
|
||||||
|
this.refresh();
|
||||||
|
console.log('Remove dimension', index, this.target.azureMonitor);
|
||||||
|
}
|
||||||
|
|
||||||
/* Azure Log Analytics */
|
/* Azure Log Analytics */
|
||||||
|
|
||||||
getWorkspaces = () => {
|
getWorkspaces = () => {
|
||||||
@ -695,3 +693,20 @@ export class AzureMonitorQueryCtrl extends QueryCtrl {
|
|||||||
this.refresh();
|
this.refresh();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Modifies the actual query object
|
||||||
|
export function migrateMetricsDimensionFilters(item: AzureMetricQuery) {
|
||||||
|
if (!item.dimensionFilters) {
|
||||||
|
item.dimensionFilters = [];
|
||||||
|
}
|
||||||
|
const oldDimension = (item as any).dimension;
|
||||||
|
if (oldDimension && oldDimension !== 'None') {
|
||||||
|
item.dimensionFilters.push({
|
||||||
|
dimension: oldDimension,
|
||||||
|
operator: 'eq',
|
||||||
|
filter: (item as any).dimensionFilter,
|
||||||
|
});
|
||||||
|
delete (item as any).dimension;
|
||||||
|
delete (item as any).dimensionFilter;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
@ -45,6 +45,12 @@ export interface AzureDataSourceSecureJsonData {
|
|||||||
appInsightsApiKey?: string;
|
appInsightsApiKey?: string;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
export interface AzureMetricDimension {
|
||||||
|
dimension: string;
|
||||||
|
operator: 'eq'; // future proof
|
||||||
|
filter?: string; // *
|
||||||
|
}
|
||||||
|
|
||||||
export interface AzureMetricQuery {
|
export interface AzureMetricQuery {
|
||||||
resourceGroup: string;
|
resourceGroup: string;
|
||||||
resourceName: string;
|
resourceName: string;
|
||||||
@ -55,8 +61,7 @@ export interface AzureMetricQuery {
|
|||||||
timeGrain: string;
|
timeGrain: string;
|
||||||
allowedTimeGrainsMs: number[];
|
allowedTimeGrainsMs: number[];
|
||||||
aggregation: string;
|
aggregation: string;
|
||||||
dimension: string;
|
dimensionFilters: AzureMetricDimension[];
|
||||||
dimensionFilter: string;
|
|
||||||
alias: string;
|
alias: string;
|
||||||
top: string;
|
top: string;
|
||||||
}
|
}
|
||||||
|
Loading…
Reference in New Issue
Block a user