AzureMonitor: Allow multiple resources in the API for Metrics (#56118)

oneSingleBinary
Andres Martinez Gotor 3 years ago committed by GitHub
parent 7595ed0668
commit 76868bad04
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
  1. 2
      pkg/tsdb/azuremonitor/azuremonitor_test.go
  2. 105
      pkg/tsdb/azuremonitor/metrics/azuremonitor-datasource.go
  3. 158
      pkg/tsdb/azuremonitor/metrics/azuremonitor-datasource_test.go
  4. 6
      pkg/tsdb/azuremonitor/metrics/url-builder.go
  5. 53
      pkg/tsdb/azuremonitor/testdata/azuremonitor/9-azure-monitor-response-multi.json
  6. 32
      pkg/tsdb/azuremonitor/types/types.go

@ -425,7 +425,7 @@ func TestCheckHealth(t *testing.T) {
Status: backend.HealthStatusError,
Message: "One or more health checks failed. See details below.",
JSONDetails: []byte(
`{"verboseMessage": "1. Error connecting to Azure Monitor endpoint: health check failed: Get \"https://management.azure.com/subscriptions?api-version=2018-01-01\": not found\n2. Error connecting to Azure Log Analytics endpoint: health check failed: Get \"https://management.azure.com/subscriptions//providers/Microsoft.OperationalInsights/workspaces?api-version=2017-04-26-preview\": not found\n3. Error connecting to Azure Resource Graph endpoint: health check failed: Post \"https://management.azure.com/providers/Microsoft.ResourceGraph/resources?api-version=2021-06-01-preview\": not found" }`),
`{"verboseMessage": "1. Error connecting to Azure Monitor endpoint: health check failed: Get \"https://management.azure.com/subscriptions?api-version=2021-05-01\": not found\n2. Error connecting to Azure Log Analytics endpoint: health check failed: Get \"https://management.azure.com/subscriptions//providers/Microsoft.OperationalInsights/workspaces?api-version=2017-04-26-preview\": not found\n3. Error connecting to Azure Resource Graph endpoint: health check failed: Post \"https://management.azure.com/providers/Microsoft.ResourceGraph/resources?api-version=2021-06-01-preview\": not found" }`),
},
customServices: map[string]types.DatasourceService{
azureMonitor: {

@ -36,7 +36,7 @@ var (
resourceNameLandmark = regexp.MustCompile(`(?i)(/(?P<resourceName>[\w-\.]+)/providers/Microsoft\.Insights/metrics)`)
)
const AzureMonitorAPIVersion = "2018-01-01"
const AzureMonitorAPIVersion = "2021-05-01"
func (e *AzureMonitorDatasource) ResourceRequest(rw http.ResponseWriter, req *http.Request, cli *http.Client) {
e.Proxy.Do(rw, req, cli)
@ -79,22 +79,7 @@ func (e *AzureMonitorDatasource) buildQueries(queries []backend.DataQuery, dsInf
azJSONModel.MetricNamespace = azJSONModel.MetricDefinition
}
ub := urlBuilder{
ResourceURI: azJSONModel.ResourceURI,
// Alternative, used to reconstruct resource URI if it's not present
DefaultSubscription: dsInfo.Settings.SubscriptionId,
Subscription: queryJSONModel.Subscription,
ResourceGroup: azJSONModel.ResourceGroup,
MetricNamespace: azJSONModel.MetricNamespace,
ResourceName: azJSONModel.ResourceName,
}
azJSONModel.DimensionFilters = MigrateDimensionFilters(azJSONModel.DimensionFilters)
azureURL := ub.BuildMetricsURL()
if azJSONModel.ResourceName == "" {
azJSONModel.ResourceName = extractResourceNameFromMetricsURL(azureURL)
}
alias := azJSONModel.Alias
@ -121,6 +106,23 @@ func (e *AzureMonitorDatasource) buildQueries(queries []backend.DataQuery, dsInf
params.Add("metricnamespace", azJSONModel.MetricNamespace)
}
azureURL := BuildSubscriptionMetricsURL(queryJSONModel.Subscription)
if azJSONModel.Region != "" {
params.Add("region", azJSONModel.Region)
} else {
// Deprecated, if no region is specified, only one resource group and name is supported
ub := urlBuilder{
ResourceURI: azJSONModel.ResourceURI,
// Alternative, used to reconstruct resource URI if it's not present
DefaultSubscription: dsInfo.Settings.SubscriptionId,
Subscription: queryJSONModel.Subscription,
ResourceGroup: azJSONModel.ResourceGroup,
MetricNamespace: azJSONModel.MetricNamespace,
ResourceName: azJSONModel.ResourceName,
}
azureURL = ub.BuildMetricsURL()
}
// old model
dimension := strings.TrimSpace(azJSONModel.Dimension)
dimensionFilter := strings.TrimSpace(azJSONModel.DimensionFilter)
@ -142,8 +144,25 @@ func (e *AzureMonitorDatasource) buildQueries(queries []backend.DataQuery, dsInf
}
}
resourceIDs := []string{}
for _, r := range azJSONModel.Resources {
ub := urlBuilder{
DefaultSubscription: dsInfo.Settings.SubscriptionId,
Subscription: queryJSONModel.Subscription,
ResourceGroup: r.ResourceGroup,
MetricNamespace: azJSONModel.MetricNamespace,
ResourceName: r.ResourceName,
}
resourceIDs = append(resourceIDs, fmt.Sprintf("Microsoft.ResourceId eq '%s'", ub.buildResourceURI()))
}
filterString := strings.Join(resourceIDs, " or ")
if dimSB.String() != "" {
params.Add("$filter", dimSB.String())
if filterString != "" {
filterString = fmt.Sprintf("(%s) and (%s)", filterString, dimSB.String())
} else {
filterString = dimSB.String()
}
if azJSONModel.Top != "" {
params.Add("top", azJSONModel.Top)
}
@ -156,14 +175,13 @@ func (e *AzureMonitorDatasource) buildQueries(queries []backend.DataQuery, dsInf
}
azureMonitorQueries = append(azureMonitorQueries, &types.AzureMonitorQuery{
ResourceName: azJSONModel.ResourceName,
ResourceURI: ub.buildResourceURI(),
URL: azureURL,
Target: target,
Params: params,
RefID: query.RefID,
Alias: alias,
TimeRange: query.TimeRange,
URL: azureURL,
Target: target,
Params: params,
RefID: query.RefID,
Alias: alias,
TimeRange: query.TimeRange,
Filter: filterString,
})
}
@ -182,6 +200,10 @@ func (e *AzureMonitorDatasource) executeQuery(ctx context.Context, query *types.
req.URL.Path = path.Join(req.URL.Path, query.URL)
req.URL.RawQuery = query.Params.Encode()
if query.Filter != "" {
req.Method = http.MethodPost
req.Body = io.NopCloser(strings.NewReader(fmt.Sprintf(`{"filter": "%s"}`, query.Filter)))
}
ctx, span := tracer.Start(ctx, "azuremonitor query")
span.SetAttributes("target", query.Target, attribute.Key("target").String(query.Target))
@ -264,11 +286,6 @@ func (e *AzureMonitorDatasource) parseResponse(amr types.AzureMonitorResponse, q
return nil, nil
}
queryUrl, err := getQueryUrl(query, azurePortalUrl)
if err != nil {
return nil, err
}
frames := data.Frames{}
for _, series := range amr.Value[0].Timeseries {
labels := data.Labels{}
@ -288,8 +305,19 @@ func (e *AzureMonitorDatasource) parseResponse(amr types.AzureMonitorResponse, q
Unit: toGrafanaUnit(amr.Value[0].Unit),
})
}
resourceID := labels["microsoft.resourceid"]
resourceIDSlice := strings.Split(resourceID, "/")
resourceName := ""
if len(resourceIDSlice) > 1 {
resourceName = resourceIDSlice[len(resourceIDSlice)-1]
} else {
// Deprecated: This is for backward compatibility, the URL should contain
// the resource ID
resourceName = extractResourceNameFromMetricsURL(query.URL)
resourceID = extractResourceIDFromMetricsURL(query.URL)
}
if query.Alias != "" {
displayName := formatAzureMonitorLegendKey(query.Alias, query.ResourceName,
displayName := formatAzureMonitorLegendKey(query.Alias, resourceName,
amr.Value[0].Name.LocalizedValue, "", "", amr.Namespace, amr.Value[0].ID, labels)
if dataField.Config != nil {
@ -323,6 +351,11 @@ func (e *AzureMonitorDatasource) parseResponse(amr types.AzureMonitorResponse, q
frame.SetRow(i, point.TimeStamp, value)
}
queryUrl, err := getQueryUrl(query, azurePortalUrl, resourceID, resourceName)
if err != nil {
return nil, err
}
frameWithLink := resourcegraph.AddConfigLinks(*frame, queryUrl)
frames = append(frames, &frameWithLink)
}
@ -331,7 +364,7 @@ func (e *AzureMonitorDatasource) parseResponse(amr types.AzureMonitorResponse, q
}
// Gets the deep link for the given query
func getQueryUrl(query *types.AzureMonitorQuery, azurePortalUrl string) (string, error) {
func getQueryUrl(query *types.AzureMonitorQuery, azurePortalUrl, resourceID, resourceName string) (string, error) {
aggregationType := aggregationTypeMap["Average"]
aggregation := query.Params.Get("aggregation")
if aggregation != "" {
@ -360,14 +393,14 @@ func getQueryUrl(query *types.AzureMonitorQuery, azurePortalUrl string) (string,
"metrics": []types.MetricChartDefinition{
{
ResourceMetadata: map[string]string{
"id": query.ResourceURI,
"id": resourceID,
},
Name: query.Params.Get("metricnames"),
AggregationType: aggregationType,
Namespace: query.Params.Get("metricnamespace"),
MetricVisualization: types.MetricVisualization{
DisplayName: query.Params.Get("metricnames"),
ResourceDisplayName: query.ResourceName,
ResourceDisplayName: resourceName,
},
},
},
@ -497,3 +530,7 @@ func extractResourceNameFromMetricsURL(url string) string {
return resourceName
}
func extractResourceIDFromMetricsURL(url string) string {
return strings.Split(url, "/providers/microsoft.insights/metrics")[0]
}

@ -39,8 +39,9 @@ func TestAzureMonitorBuildQueries(t *testing.T) {
azureMonitorVariedProperties map[string]interface{}
azureMonitorQueryTarget string
expectedInterval string
resourceURI string
queryInterval time.Duration
expectedURL string
expectedFilter string
}{
{
name: "Parse queries from frontend and build AzureMonitor API queries",
@ -49,9 +50,8 @@ func TestAzureMonitorBuildQueries(t *testing.T) {
"timeGrain": "PT1M",
"top": "10",
},
resourceURI: "/subscriptions/12345678-aaaa-bbbb-cccc-123456789abc/resourceGroups/grafanastaging/providers/Microsoft.Compute/virtualMachines/grafana",
expectedInterval: "PT1M",
azureMonitorQueryTarget: "aggregation=Average&api-version=2018-01-01&interval=PT1M&metricnames=Percentage+CPU&metricnamespace=Microsoft.Compute%2FvirtualMachines&timespan=2018-03-15T13%3A00%3A00Z%2F2018-03-15T13%3A34%3A00Z",
azureMonitorQueryTarget: "aggregation=Average&api-version=2021-05-01&interval=PT1M&metricnames=Percentage+CPU&metricnamespace=Microsoft.Compute%2FvirtualMachines&timespan=2018-03-15T13%3A00%3A00Z%2F2018-03-15T13%3A34%3A00Z",
},
{
name: "legacy query without resourceURI and time grain set to auto",
@ -61,7 +61,7 @@ func TestAzureMonitorBuildQueries(t *testing.T) {
},
queryInterval: duration,
expectedInterval: "PT15M",
azureMonitorQueryTarget: "aggregation=Average&api-version=2018-01-01&interval=PT15M&metricnames=Percentage+CPU&metricnamespace=Microsoft.Compute%2FvirtualMachines&timespan=2018-03-15T13%3A00%3A00Z%2F2018-03-15T13%3A34%3A00Z",
azureMonitorQueryTarget: "aggregation=Average&api-version=2021-05-01&interval=PT15M&metricnames=Percentage+CPU&metricnamespace=Microsoft.Compute%2FvirtualMachines&timespan=2018-03-15T13%3A00%3A00Z%2F2018-03-15T13%3A34%3A00Z",
},
{
name: "legacy query without resourceURI and time grain set to auto",
@ -72,7 +72,7 @@ func TestAzureMonitorBuildQueries(t *testing.T) {
},
queryInterval: duration,
expectedInterval: "PT5M",
azureMonitorQueryTarget: "aggregation=Average&api-version=2018-01-01&interval=PT5M&metricnames=Percentage+CPU&metricnamespace=Microsoft.Compute%2FvirtualMachines&timespan=2018-03-15T13%3A00%3A00Z%2F2018-03-15T13%3A34%3A00Z",
azureMonitorQueryTarget: "aggregation=Average&api-version=2021-05-01&interval=PT5M&metricnames=Percentage+CPU&metricnamespace=Microsoft.Compute%2FvirtualMachines&timespan=2018-03-15T13%3A00%3A00Z%2F2018-03-15T13%3A34%3A00Z",
},
{
name: "legacy query without resourceURI and has a dimension filter",
@ -84,7 +84,8 @@ func TestAzureMonitorBuildQueries(t *testing.T) {
},
queryInterval: duration,
expectedInterval: "PT1M",
azureMonitorQueryTarget: "%24filter=blob+eq+%27%2A%27&aggregation=Average&api-version=2018-01-01&interval=PT1M&metricnames=Percentage+CPU&metricnamespace=Microsoft.Compute%2FvirtualMachines&timespan=2018-03-15T13%3A00%3A00Z%2F2018-03-15T13%3A34%3A00Z&top=30",
azureMonitorQueryTarget: "aggregation=Average&api-version=2021-05-01&interval=PT1M&metricnames=Percentage+CPU&metricnamespace=Microsoft.Compute%2FvirtualMachines&timespan=2018-03-15T13%3A00%3A00Z%2F2018-03-15T13%3A34%3A00Z&top=30",
expectedFilter: "blob eq '*'",
},
{
name: "legacy query without resourceURI and has a dimension filter and none Dimension",
@ -96,7 +97,7 @@ func TestAzureMonitorBuildQueries(t *testing.T) {
},
queryInterval: duration,
expectedInterval: "PT1M",
azureMonitorQueryTarget: "aggregation=Average&api-version=2018-01-01&interval=PT1M&metricnames=Percentage+CPU&metricnamespace=Microsoft.Compute%2FvirtualMachines&timespan=2018-03-15T13%3A00%3A00Z%2F2018-03-15T13%3A34%3A00Z",
azureMonitorQueryTarget: "aggregation=Average&api-version=2021-05-01&interval=PT1M&metricnames=Percentage+CPU&metricnamespace=Microsoft.Compute%2FvirtualMachines&timespan=2018-03-15T13%3A00%3A00Z%2F2018-03-15T13%3A34%3A00Z",
},
{
name: "legacy query without resourceURI and has dimensionFilter*s* property with one dimension",
@ -107,7 +108,8 @@ func TestAzureMonitorBuildQueries(t *testing.T) {
},
queryInterval: duration,
expectedInterval: "PT1M",
azureMonitorQueryTarget: "%24filter=blob+eq+%27%2A%27&aggregation=Average&api-version=2018-01-01&interval=PT1M&metricnames=Percentage+CPU&metricnamespace=Microsoft.Compute%2FvirtualMachines&timespan=2018-03-15T13%3A00%3A00Z%2F2018-03-15T13%3A34%3A00Z&top=30",
azureMonitorQueryTarget: "aggregation=Average&api-version=2021-05-01&interval=PT1M&metricnames=Percentage+CPU&metricnamespace=Microsoft.Compute%2FvirtualMachines&timespan=2018-03-15T13%3A00%3A00Z%2F2018-03-15T13%3A34%3A00Z&top=30",
expectedFilter: "blob eq '*'",
},
{
name: "legacy query without resourceURI and has dimensionFilter*s* property with two dimensions",
@ -118,7 +120,8 @@ func TestAzureMonitorBuildQueries(t *testing.T) {
},
queryInterval: duration,
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%2FvirtualMachines&timespan=2018-03-15T13%3A00%3A00Z%2F2018-03-15T13%3A34%3A00Z&top=30",
azureMonitorQueryTarget: "aggregation=Average&api-version=2021-05-01&interval=PT1M&metricnames=Percentage+CPU&metricnamespace=Microsoft.Compute%2FvirtualMachines&timespan=2018-03-15T13%3A00%3A00Z%2F2018-03-15T13%3A34%3A00Z&top=30",
expectedFilter: "blob eq '*' and tier eq '*'",
},
{
name: "legacy query without resourceURI and has a dimension filter without specifying a top",
@ -129,7 +132,8 @@ func TestAzureMonitorBuildQueries(t *testing.T) {
},
queryInterval: duration,
expectedInterval: "PT1M",
azureMonitorQueryTarget: "%24filter=blob+eq+%27%2A%27&aggregation=Average&api-version=2018-01-01&interval=PT1M&metricnames=Percentage+CPU&metricnamespace=Microsoft.Compute%2FvirtualMachines&timespan=2018-03-15T13%3A00%3A00Z%2F2018-03-15T13%3A34%3A00Z",
azureMonitorQueryTarget: "aggregation=Average&api-version=2021-05-01&interval=PT1M&metricnames=Percentage+CPU&metricnamespace=Microsoft.Compute%2FvirtualMachines&timespan=2018-03-15T13%3A00%3A00Z%2F2018-03-15T13%3A34%3A00Z",
expectedFilter: "blob eq '*'",
},
{
name: "has dimensionFilter*s* property with not equals operator",
@ -140,7 +144,8 @@ func TestAzureMonitorBuildQueries(t *testing.T) {
},
queryInterval: duration,
expectedInterval: "PT1M",
azureMonitorQueryTarget: "%24filter=blob+ne+%27test%27&aggregation=Average&api-version=2018-01-01&interval=PT1M&metricnames=Percentage+CPU&metricnamespace=Microsoft.Compute%2FvirtualMachines&timespan=2018-03-15T13%3A00%3A00Z%2F2018-03-15T13%3A34%3A00Z&top=30",
azureMonitorQueryTarget: "aggregation=Average&api-version=2021-05-01&interval=PT1M&metricnames=Percentage+CPU&metricnamespace=Microsoft.Compute%2FvirtualMachines&timespan=2018-03-15T13%3A00%3A00Z%2F2018-03-15T13%3A34%3A00Z&top=30",
expectedFilter: "blob ne 'test'",
},
{
name: "has dimensionFilter*s* property with startsWith operator",
@ -151,7 +156,8 @@ func TestAzureMonitorBuildQueries(t *testing.T) {
},
queryInterval: duration,
expectedInterval: "PT1M",
azureMonitorQueryTarget: "%24filter=blob+sw+%27test%27&aggregation=Average&api-version=2018-01-01&interval=PT1M&metricnames=Percentage+CPU&metricnamespace=Microsoft.Compute%2FvirtualMachines&timespan=2018-03-15T13%3A00%3A00Z%2F2018-03-15T13%3A34%3A00Z&top=30",
azureMonitorQueryTarget: "aggregation=Average&api-version=2021-05-01&interval=PT1M&metricnames=Percentage+CPU&metricnamespace=Microsoft.Compute%2FvirtualMachines&timespan=2018-03-15T13%3A00%3A00Z%2F2018-03-15T13%3A34%3A00Z&top=30",
expectedFilter: "blob sw 'test'",
},
{
name: "correctly sets dimension operator to eq (irrespective of operator) when filter value is '*'",
@ -162,7 +168,8 @@ func TestAzureMonitorBuildQueries(t *testing.T) {
},
queryInterval: duration,
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%2FvirtualMachines&timespan=2018-03-15T13%3A00%3A00Z%2F2018-03-15T13%3A34%3A00Z&top=30",
azureMonitorQueryTarget: "aggregation=Average&api-version=2021-05-01&interval=PT1M&metricnames=Percentage+CPU&metricnamespace=Microsoft.Compute%2FvirtualMachines&timespan=2018-03-15T13%3A00%3A00Z%2F2018-03-15T13%3A34%3A00Z&top=30",
expectedFilter: "blob eq '*' and tier eq '*'",
},
{
name: "correctly constructs target when multiple filter values are provided for the 'eq' operator",
@ -173,7 +180,8 @@ func TestAzureMonitorBuildQueries(t *testing.T) {
},
queryInterval: duration,
expectedInterval: "PT1M",
azureMonitorQueryTarget: "%24filter=blob+eq+%27test%27+or+blob+eq+%27test2%27&aggregation=Average&api-version=2018-01-01&interval=PT1M&metricnames=Percentage+CPU&metricnamespace=Microsoft.Compute%2FvirtualMachines&timespan=2018-03-15T13%3A00%3A00Z%2F2018-03-15T13%3A34%3A00Z&top=30",
azureMonitorQueryTarget: "aggregation=Average&api-version=2021-05-01&interval=PT1M&metricnames=Percentage+CPU&metricnamespace=Microsoft.Compute%2FvirtualMachines&timespan=2018-03-15T13%3A00%3A00Z%2F2018-03-15T13%3A34%3A00Z&top=30",
expectedFilter: "blob eq 'test' or blob eq 'test2'",
},
{
name: "correctly constructs target when multiple filter values are provided for ne 'eq' operator",
@ -184,7 +192,43 @@ func TestAzureMonitorBuildQueries(t *testing.T) {
},
queryInterval: duration,
expectedInterval: "PT1M",
azureMonitorQueryTarget: "%24filter=blob+ne+%27test%27+and+blob+ne+%27test2%27&aggregation=Average&api-version=2018-01-01&interval=PT1M&metricnames=Percentage+CPU&metricnamespace=Microsoft.Compute%2FvirtualMachines&timespan=2018-03-15T13%3A00%3A00Z%2F2018-03-15T13%3A34%3A00Z&top=30",
azureMonitorQueryTarget: "aggregation=Average&api-version=2021-05-01&interval=PT1M&metricnames=Percentage+CPU&metricnamespace=Microsoft.Compute%2FvirtualMachines&timespan=2018-03-15T13%3A00%3A00Z%2F2018-03-15T13%3A34%3A00Z&top=30",
expectedFilter: "blob ne 'test' and blob ne 'test2'",
},
{
name: "Includes a region",
azureMonitorVariedProperties: map[string]interface{}{
"timeGrain": "PT1M",
"top": "10",
"region": "westus",
},
expectedInterval: "PT1M",
azureMonitorQueryTarget: "aggregation=Average&api-version=2021-05-01&interval=PT1M&metricnames=Percentage+CPU&metricnamespace=Microsoft.Compute%2FvirtualMachines&region=westus&timespan=2018-03-15T13%3A00%3A00Z%2F2018-03-15T13%3A34%3A00Z",
expectedURL: "/subscriptions/12345678-aaaa-bbbb-cccc-123456789abc/providers/microsoft.insights/metrics",
},
{
name: "includes a resource as a filter",
azureMonitorVariedProperties: map[string]interface{}{
"timeGrain": "PT1M",
"resources": []types.AzureMonitorResource{{ResourceGroup: "rg", ResourceName: "vm"}},
},
queryInterval: duration,
expectedInterval: "PT1M",
azureMonitorQueryTarget: "aggregation=Average&api-version=2021-05-01&interval=PT1M&metricnames=Percentage+CPU&metricnamespace=Microsoft.Compute%2FvirtualMachines&timespan=2018-03-15T13%3A00%3A00Z%2F2018-03-15T13%3A34%3A00Z",
expectedFilter: "Microsoft.ResourceId eq '/subscriptions/12345678-aaaa-bbbb-cccc-123456789abc/resourceGroups/rg/providers/Microsoft.Compute/virtualMachines/vm'",
},
{
name: "includes a resource and a dimesion as filters",
azureMonitorVariedProperties: map[string]interface{}{
"timeGrain": "PT1M",
"resources": []types.AzureMonitorResource{{ResourceGroup: "rg", ResourceName: "vm"}},
"dimensionFilters": []types.AzureMonitorDimensionFilter{{Dimension: "blob", Operator: "ne", Filter: &wildcardFilter, Filters: []string{"test", "test2"}}},
"top": "30",
},
queryInterval: duration,
expectedInterval: "PT1M",
azureMonitorQueryTarget: "aggregation=Average&api-version=2021-05-01&interval=PT1M&metricnames=Percentage+CPU&metricnamespace=Microsoft.Compute%2FvirtualMachines&timespan=2018-03-15T13%3A00%3A00Z%2F2018-03-15T13%3A34%3A00Z&top=30",
expectedFilter: "(Microsoft.ResourceId eq '/subscriptions/12345678-aaaa-bbbb-cccc-123456789abc/resourceGroups/rg/providers/Microsoft.Compute/virtualMachines/vm') and (blob ne 'test' and blob ne 'test2')",
},
}
@ -221,16 +265,18 @@ func TestAzureMonitorBuildQueries(t *testing.T) {
}
azureMonitorQuery := &types.AzureMonitorQuery{
ResourceName: "grafana",
ResourceURI: "/subscriptions/12345678-aaaa-bbbb-cccc-123456789abc/resourceGroups/grafanastaging/providers/Microsoft.Compute/virtualMachines/grafana",
URL: "/subscriptions/12345678-aaaa-bbbb-cccc-123456789abc/resourceGroups/grafanastaging/providers/Microsoft.Compute/virtualMachines/grafana/providers/microsoft.insights/metrics",
Target: tt.azureMonitorQueryTarget,
RefID: "A",
Alias: "testalias",
URL: tt.expectedURL,
Target: tt.azureMonitorQueryTarget,
RefID: "A",
Alias: "testalias",
TimeRange: backend.TimeRange{
From: fromStart,
To: fromStart.Add(34 * time.Minute),
},
Filter: tt.expectedFilter,
}
if azureMonitorQuery.URL == "" {
azureMonitorQuery.URL = "/subscriptions/12345678-aaaa-bbbb-cccc-123456789abc/resourceGroups/grafanastaging/providers/Microsoft.Compute/virtualMachines/grafana/providers/microsoft.insights/metrics"
}
queries, err := datasource.buildQueries(tsdbQuery, dsInfo)
@ -243,7 +289,7 @@ func TestAzureMonitorBuildQueries(t *testing.T) {
`TimeContext/%7B%22absolute%22%3A%7B%22startTime%22%3A%222018-03-15T13%3A00%3A00Z%22%2C%22endTime%22%3A%222018-03-15T13%3A34%3A00Z%22%7D%7D/` +
`ChartDefinition/%7B%22v2charts%22%3A%5B%7B%22metrics%22%3A%5B%7B%22resourceMetadata%22%3A%7B%22id%22%3A%22%2Fsubscriptions%2F12345678-aaaa-bbbb-cccc-123456789abc%2FresourceGroups%2Fgrafanastaging%2Fproviders%2FMicrosoft.Compute%2FvirtualMachines%2Fgrafana%22%7D%2C` +
`%22name%22%3A%22Percentage%20CPU%22%2C%22aggregationType%22%3A4%2C%22namespace%22%3A%22Microsoft.Compute%2FvirtualMachines%22%2C%22metricVisualization%22%3A%7B%22displayName%22%3A%22Percentage%20CPU%22%2C%22resourceDisplayName%22%3A%22grafana%22%7D%7D%5D%7D%5D%7D`
actual, err := getQueryUrl(queries[0], "http://ds")
actual, err := getQueryUrl(queries[0], "http://ds", "/subscriptions/12345678-aaaa-bbbb-cccc-123456789abc/resourceGroups/grafanastaging/providers/Microsoft.Compute/virtualMachines/grafana", "grafana")
require.NoError(t, err)
require.Equal(t, expected, actual)
})
@ -315,8 +361,7 @@ func TestAzureMonitorParseResponse(t *testing.T) {
name: "average aggregate time series response",
responseFile: "1-azure-monitor-response-avg.json",
mockQuery: &types.AzureMonitorQuery{
ResourceName: "grafana",
ResourceURI: "/subscriptions/12345678-aaaa-bbbb-cccc-123456789abc/resourceGroups/grafanastaging/providers/Microsoft.Compute/virtualMachines/grafana",
URL: "/subscriptions/12345678-aaaa-bbbb-cccc-123456789abc/resourceGroups/grafanastaging/providers/Microsoft.Compute/virtualMachines/grafana/providers/microsoft.insights/metrics",
Params: url.Values{
"aggregation": {"Average"},
},
@ -335,8 +380,7 @@ func TestAzureMonitorParseResponse(t *testing.T) {
name: "total aggregate time series response",
responseFile: "2-azure-monitor-response-total.json",
mockQuery: &types.AzureMonitorQuery{
ResourceName: "grafana",
ResourceURI: "/subscriptions/12345678-aaaa-bbbb-cccc-123456789abc/resourceGroups/grafanastaging/providers/Microsoft.Compute/virtualMachines/grafana",
URL: "/subscriptions/12345678-aaaa-bbbb-cccc-123456789abc/resourceGroups/grafanastaging/providers/Microsoft.Compute/virtualMachines/grafana/providers/microsoft.insights/metrics",
Params: url.Values{
"aggregation": {"Total"},
},
@ -355,8 +399,7 @@ func TestAzureMonitorParseResponse(t *testing.T) {
name: "maximum aggregate time series response",
responseFile: "3-azure-monitor-response-maximum.json",
mockQuery: &types.AzureMonitorQuery{
ResourceName: "grafana",
ResourceURI: "/subscriptions/12345678-aaaa-bbbb-cccc-123456789abc/resourceGroups/grafanastaging/providers/Microsoft.Compute/virtualMachines/grafana",
URL: "/subscriptions/12345678-aaaa-bbbb-cccc-123456789abc/resourceGroups/grafanastaging/providers/Microsoft.Compute/virtualMachines/grafana/providers/microsoft.insights/metrics",
Params: url.Values{
"aggregation": {"Maximum"},
},
@ -375,8 +418,7 @@ func TestAzureMonitorParseResponse(t *testing.T) {
name: "minimum aggregate time series response",
responseFile: "4-azure-monitor-response-minimum.json",
mockQuery: &types.AzureMonitorQuery{
ResourceName: "grafana",
ResourceURI: "/subscriptions/12345678-aaaa-bbbb-cccc-123456789abc/resourceGroups/grafanastaging/providers/Microsoft.Compute/virtualMachines/grafana",
URL: "/subscriptions/12345678-aaaa-bbbb-cccc-123456789abc/resourceGroups/grafanastaging/providers/Microsoft.Compute/virtualMachines/grafana/providers/microsoft.insights/metrics",
Params: url.Values{
"aggregation": {"Minimum"},
},
@ -395,8 +437,7 @@ func TestAzureMonitorParseResponse(t *testing.T) {
name: "count aggregate time series response",
responseFile: "5-azure-monitor-response-count.json",
mockQuery: &types.AzureMonitorQuery{
ResourceName: "grafana",
ResourceURI: "/subscriptions/12345678-aaaa-bbbb-cccc-123456789abc/resourceGroups/grafanastaging/providers/Microsoft.Compute/virtualMachines/grafana",
URL: "/subscriptions/12345678-aaaa-bbbb-cccc-123456789abc/resourceGroups/grafanastaging/providers/Microsoft.Compute/virtualMachines/grafana/providers/microsoft.insights/metrics",
Params: url.Values{
"aggregation": {"Count"},
},
@ -415,8 +456,7 @@ func TestAzureMonitorParseResponse(t *testing.T) {
name: "single dimension time series response",
responseFile: "6-azure-monitor-response-single-dimension.json",
mockQuery: &types.AzureMonitorQuery{
ResourceName: "grafana",
ResourceURI: "/subscriptions/12345678-aaaa-bbbb-cccc-123456789abc/resourceGroups/grafanastaging/providers/Microsoft.Compute/virtualMachines/grafana",
URL: "/subscriptions/12345678-aaaa-bbbb-cccc-123456789abc/resourceGroups/grafanastaging/providers/Microsoft.Compute/virtualMachines/grafana/providers/microsoft.insights/metrics",
Params: url.Values{
"aggregation": {"Average"},
},
@ -448,9 +488,8 @@ func TestAzureMonitorParseResponse(t *testing.T) {
name: "with alias patterns in the query",
responseFile: "2-azure-monitor-response-total.json",
mockQuery: &types.AzureMonitorQuery{
ResourceName: "grafana",
ResourceURI: "/subscriptions/12345678-aaaa-bbbb-cccc-123456789abc/resourceGroups/grafanastaging/providers/Microsoft.Compute/virtualMachines/grafana",
Alias: "custom {{resourcegroup}} {{namespace}} {{resourceName}} {{metric}}",
URL: "/subscriptions/12345678-aaaa-bbbb-cccc-123456789abc/resourceGroups/grafanastaging/providers/Microsoft.Compute/virtualMachines/grafana/providers/microsoft.insights/metrics",
Alias: "custom {{resourcegroup}} {{namespace}} {{resourceName}} {{metric}}",
Params: url.Values{
"aggregation": {"Total"},
},
@ -469,9 +508,8 @@ func TestAzureMonitorParseResponse(t *testing.T) {
name: "single dimension with alias",
responseFile: "6-azure-monitor-response-single-dimension.json",
mockQuery: &types.AzureMonitorQuery{
ResourceName: "grafana",
ResourceURI: "/subscriptions/12345678-aaaa-bbbb-cccc-123456789abc/resourceGroups/grafanastaging/providers/Microsoft.Compute/virtualMachines/grafana",
Alias: "{{dimensionname}}={{DimensionValue}}",
URL: "/subscriptions/12345678-aaaa-bbbb-cccc-123456789abc/resourceGroups/grafanastaging/providers/Microsoft.Compute/virtualMachines/grafana/providers/microsoft.insights/metrics",
Alias: "{{dimensionname}}={{DimensionValue}}",
Params: url.Values{
"aggregation": {"Average"},
},
@ -505,9 +543,8 @@ func TestAzureMonitorParseResponse(t *testing.T) {
name: "multiple dimension time series response with label alias",
responseFile: "7-azure-monitor-response-multi-dimension.json",
mockQuery: &types.AzureMonitorQuery{
ResourceName: "grafana",
ResourceURI: "/subscriptions/12345678-aaaa-bbbb-cccc-123456789abc/resourceGroups/grafanastaging/providers/Microsoft.Compute/virtualMachines/grafana",
Alias: "{{resourcegroup}} {Blob Type={{blobtype}}, Tier={{Tier}}}",
URL: "/subscriptions/12345678-aaaa-bbbb-cccc-123456789abc/resourceGroups/grafanastaging/providers/Microsoft.Compute/virtualMachines/grafana/providers/microsoft.insights/metrics",
Alias: "{{resourcegroup}} {Blob Type={{blobtype}}, Tier={{Tier}}}",
Params: url.Values{
"aggregation": {"Average"},
},
@ -542,9 +579,8 @@ func TestAzureMonitorParseResponse(t *testing.T) {
name: "unspecified unit with alias should not panic",
responseFile: "8-azure-monitor-response-unspecified-unit.json",
mockQuery: &types.AzureMonitorQuery{
ResourceName: "grafana",
ResourceURI: "/subscriptions/12345678-aaaa-bbbb-cccc-123456789abc/resourceGroups/grafanastaging/providers/Microsoft.Compute/virtualMachines/grafana",
Alias: "custom",
URL: "/subscriptions/12345678-aaaa-bbbb-cccc-123456789abc/resourceGroups/grafanastaging/providers/Microsoft.Compute/virtualMachines/grafana/providers/microsoft.insights/metrics",
Alias: "custom",
Params: url.Values{
"aggregation": {"Average"},
},
@ -563,9 +599,8 @@ func TestAzureMonitorParseResponse(t *testing.T) {
name: "with legacy azure monitor query properties and without a resource uri",
responseFile: "2-azure-monitor-response-total.json",
mockQuery: &types.AzureMonitorQuery{
ResourceName: "grafana",
ResourceURI: "/subscriptions/12345678-aaaa-bbbb-cccc-123456789abc/resourceGroups/grafanastaging/providers/Microsoft.Compute/virtualMachines/grafana",
Alias: "custom {{resourcegroup}} {{namespace}} {{resourceName}} {{metric}}",
URL: "/subscriptions/12345678-aaaa-bbbb-cccc-123456789abc/resourceGroups/grafanastaging/providers/Microsoft.Compute/virtualMachines/grafana/providers/microsoft.insights/metrics",
Alias: "custom {{resourcegroup}} {{namespace}} {{resourceName}} {{metric}}",
Params: url.Values{
"aggregation": {"Total"},
},
@ -584,9 +619,8 @@ func TestAzureMonitorParseResponse(t *testing.T) {
name: "with legacy azure monitor query properties and with a resource uri it should use the resource uri",
responseFile: "2-azure-monitor-response-total.json",
mockQuery: &types.AzureMonitorQuery{
ResourceName: "grafana",
ResourceURI: "/subscriptions/12345678-aaaa-bbbb-cccc-123456789abc/resourceGroups/grafanastaging/providers/Microsoft.Compute/virtualMachines/grafana",
Alias: "custom {{resourcegroup}} {{namespace}} {{resourceName}} {{metric}}",
URL: "/subscriptions/12345678-aaaa-bbbb-cccc-123456789abc/resourceGroups/grafanastaging/providers/Microsoft.Compute/virtualMachines/grafana/providers/microsoft.insights/metrics",
Alias: "custom {{resourcegroup}} {{namespace}} {{resourceName}} {{metric}}",
Params: url.Values{
"aggregation": {"Total"},
},
@ -601,6 +635,26 @@ func TestAzureMonitorParseResponse(t *testing.T) {
}).SetConfig(&data.FieldConfig{Unit: "percent", DisplayName: "custom grafanastaging Microsoft.Compute/virtualMachines grafana Percentage CPU", Links: []data.DataLink{totalLink}})),
},
},
{
name: "multiple time series response",
responseFile: "9-azure-monitor-response-multi.json",
mockQuery: &types.AzureMonitorQuery{
URL: "/subscriptions/12345678-aaaa-bbbb-cccc-123456789abc/providers/microsoft.insights/metrics",
Params: url.Values{
"aggregation": {"Average"},
},
},
expectedFrames: data.Frames{
data.NewFrame("",
data.NewField("Time", nil,
makeDates(time.Date(2019, 2, 8, 10, 13, 0, 0, time.UTC), 5, time.Minute),
).SetConfig(&data.FieldConfig{Links: []data.DataLink{averageLink}}),
data.NewField("Percentage CPU", data.Labels{"microsoft.resourceid": "/subscriptions/12345678-aaaa-bbbb-cccc-123456789abc/resourceGroups/grafanastaging/providers/Microsoft.Compute/virtualMachines/grafana"}, []*float64{
ptr.Float64(2.0875), ptr.Float64(2.1525), ptr.Float64(2.155), ptr.Float64(3.6925), ptr.Float64(2.44),
}).SetConfig(&data.FieldConfig{Unit: "percent", Links: []data.DataLink{averageLink}}),
),
},
},
}
datasource := &AzureMonitorDatasource{}

@ -67,3 +67,9 @@ func (params *urlBuilder) BuildMetricsURL() string {
return fmt.Sprintf("%s/providers/microsoft.insights/metrics", resourceURI)
}
// BuildSubscriptionMetricsURL returns a URL for querying metrics for all resources in a subscription
// It requires to set a $filter and a region parameter
func BuildSubscriptionMetricsURL(subscription string) string {
return fmt.Sprintf("/subscriptions/%s/providers/microsoft.insights/metrics", subscription)
}

@ -0,0 +1,53 @@
{
"cost": 1439,
"timespan": "2022-09-28T15:33:05Z/2022-09-29T15:33:05Z",
"interval": "PT5M",
"value": [
{
"id": "subscriptions/12345678-aaaa-bbbb-cccc-123456789abc/providers/Microsoft.Insights/metrics/Percentage CPU",
"type": "Microsoft.Insights/metrics",
"name": {
"value": "Percentage CPU",
"localizedValue": "Percentage CPU"
},
"unit": "Percent",
"timeseries": [
{
"metadatavalues": [
{
"name": {
"value": "microsoft.resourceid",
"localizedValue": "microsoft.resourceid"
},
"value": "/subscriptions/12345678-aaaa-bbbb-cccc-123456789abc/resourceGroups/grafanastaging/providers/Microsoft.Compute/virtualMachines/grafana"
}
],
"data": [
{
"timeStamp": "2019-02-08T10:13:00Z",
"average": 2.0875
},
{
"timeStamp": "2019-02-08T10:14:00Z",
"average": 2.1525
},
{
"timeStamp": "2019-02-08T10:15:00Z",
"average": 2.155
},
{
"timeStamp": "2019-02-08T10:16:00Z",
"average": 3.6925
},
{
"timeStamp": "2019-02-08T10:17:00Z",
"average": 2.44
}
]
}
]
}
],
"namespace": "Microsoft.Compute/virtualMachines",
"resourceregion": "northeurope"
}

@ -59,14 +59,13 @@ type DatasourceInfo struct {
// AzureMonitorQuery is the query for all the services as they have similar queries
// with a url, a querystring and an alias field
type AzureMonitorQuery struct {
ResourceName string
ResourceURI string
URL string
Target string
Params url.Values
RefID string
Alias string
TimeRange backend.TimeRange
URL string
Target string
Params url.Values
RefID string
Alias string
TimeRange backend.TimeRange
Filter string
}
// AzureMonitorResponse is the json response from the Azure Monitor API
@ -114,16 +113,21 @@ type AzureResponseTable struct {
Rows [][]interface{} `json:"rows"`
}
type AzureMonitorResource struct {
ResourceGroup string `json:"resourceGroup"`
ResourceName string `json:"resourceName"`
}
// AzureMonitorJSONQuery is the frontend JSON query model for an Azure Monitor query.
type AzureMonitorJSONQuery struct {
AzureMonitor struct {
ResourceURI string `json:"resourceUri"`
// These are used to reconstruct a resource URI
MetricNamespace string `json:"metricNamespace"`
CustomNamespace string `json:"customNamespace"`
MetricName string `json:"metricName"`
ResourceGroup string `json:"resourceGroup"`
ResourceName string `json:"resourceName"`
MetricNamespace string `json:"metricNamespace"`
CustomNamespace string `json:"customNamespace"`
MetricName string `json:"metricName"`
Region string `json:"region"`
Resources []AzureMonitorResource `json:"resources"`
Aggregation string `json:"aggregation"`
Alias string `json:"alias"`
@ -138,6 +142,8 @@ type AzureMonitorJSONQuery struct {
// Deprecated, MetricNamespace should be used instead
MetricDefinition string `json:"metricDefinition"`
// Deprecated: Use Resources with a single element instead
AzureMonitorResource
} `json:"azureMonitor"`
Subscription string `json:"subscription"`
}

Loading…
Cancel
Save