From bca8428f639613e35e9ad61728e0f11841d6b6ad Mon Sep 17 00:00:00 2001 From: Andreas Christou Date: Mon, 22 May 2023 11:03:43 +0100 Subject: [PATCH] AzureMonitor: Support multi-resource aliases and subscription aliases (#68648) Update legend aliases - Add subscription response type - Update AzureMonitorQuery type - Update docs - Update tests - Add function to retrieve subscription display name if not present --- .../azure-monitor/query-editor/index.md | 2 + .../metrics/azuremonitor-datasource.go | 99 +++++++++++++++---- .../metrics/azuremonitor-datasource_test.go | 49 ++++++++- ...zure-monitor-response-multi-dimension.json | 2 +- ...ies-response-with-label-alias.golden.jsonc | 18 ++-- pkg/tsdb/azuremonitor/types/types.go | 25 +++-- 6 files changed, 158 insertions(+), 37 deletions(-) diff --git a/docs/sources/datasources/azure-monitor/query-editor/index.md b/docs/sources/datasources/azure-monitor/query-editor/index.md index 042c74f48d4..2d976ceaa7d 100644 --- a/docs/sources/datasources/azure-monitor/query-editor/index.md +++ b/docs/sources/datasources/azure-monitor/query-editor/index.md @@ -83,6 +83,8 @@ For example: | Alias pattern | Description | | ----------------------------- | ------------------------------------------------------------------------------------------------------ | +| `{{ subscriptionid }}` | Replaced with the subscription ID. | +| `{{ subscription }}` | Replaced with the subscription name. | | `{{ resourcegroup }}` | Replaced with the the resource group. | | `{{ namespace }}` | Replaced with the resource type or namespace, such as `Microsoft.Compute/virtualMachines`. | | `{{ resourcename }}` | Replaced with the resource name. | diff --git a/pkg/tsdb/azuremonitor/metrics/azuremonitor-datasource.go b/pkg/tsdb/azuremonitor/metrics/azuremonitor-datasource.go index 9608e625a9f..e4731898a67 100644 --- a/pkg/tsdb/azuremonitor/metrics/azuremonitor-datasource.go +++ b/pkg/tsdb/azuremonitor/metrics/azuremonitor-datasource.go @@ -42,6 +42,8 @@ func (e *AzureMonitorDatasource) ResourceRequest(rw http.ResponseWriter, req *ht e.Proxy.Do(rw, req, cli) } +var subscriptions = map[string]string{} + // executeTimeSeriesQuery does the following: // 1. build the AzureMonitor url and querystring for each query // 2. executes each query by calling the Azure Monitor API @@ -112,6 +114,7 @@ func (e *AzureMonitorDatasource) buildQueries(logger log.Logger, queries []backe params.Add("region", azJSONModel.Region) } resourceIDs := []string{} + resourceMap := map[string]types.AzureMonitorResource{} if hasOne, resourceGroup, resourceName := hasOneResource(queryJSONModel); hasOne { ub := urlBuilder{ ResourceURI: azJSONModel.ResourceURI, @@ -125,6 +128,7 @@ func (e *AzureMonitorDatasource) buildQueries(logger log.Logger, queries []backe azureURL = ub.BuildMetricsURL() // POST requests are only supported at the subscription level filterInBody = false + resourceMap[ub.buildResourceURI()] = types.AzureMonitorResource{ResourceGroup: resourceGroup, ResourceName: resourceName} } else { for _, r := range azJSONModel.Resources { ub := urlBuilder{ @@ -134,7 +138,9 @@ func (e *AzureMonitorDatasource) buildQueries(logger log.Logger, queries []backe MetricNamespace: azJSONModel.MetricNamespace, ResourceName: r.ResourceName, } - resourceIDs = append(resourceIDs, fmt.Sprintf("Microsoft.ResourceId eq '%s'", ub.buildResourceURI())) + resourceUri := ub.buildResourceURI() + resourceMap[resourceUri] = r + resourceIDs = append(resourceIDs, fmt.Sprintf("Microsoft.ResourceId eq '%s'", resourceUri)) } } @@ -180,13 +186,15 @@ func (e *AzureMonitorDatasource) buildQueries(logger log.Logger, queries []backe } query := &types.AzureMonitorQuery{ - URL: azureURL, - Target: target, - Params: params, - RefID: query.RefID, - Alias: alias, - TimeRange: query.TimeRange, - Dimensions: azJSONModel.DimensionFilters, + URL: azureURL, + Target: target, + Params: params, + RefID: query.RefID, + Alias: alias, + TimeRange: query.TimeRange, + Dimensions: azJSONModel.DimensionFilters, + Resources: resourceMap, + Subscription: queryJSONModel.Subscription, } if filterString != "" { if filterInBody { @@ -201,6 +209,51 @@ func (e *AzureMonitorDatasource) buildQueries(logger log.Logger, queries []backe return azureMonitorQueries, nil } +func retrieveSubscriptionDetails(e *AzureMonitorDatasource, cli *http.Client, ctx context.Context, logger log.Logger, tracer tracing.Tracer, subscriptionId string, baseUrl string, dsId int64, orgId int64) { + req, err := e.createRequest(ctx, logger, fmt.Sprintf("%s/subscriptions/%s", baseUrl, subscriptionId)) + if err != nil { + logger.Error("failed to retrieve subscription details for subscription %s: %s", subscriptionId, err) + } + values := req.URL.Query() + values.Add("api-version", "2022-12-01") + req.URL.RawQuery = values.Encode() + + ctx, span := tracer.Start(ctx, "azuremonitor query") + span.SetAttributes("subscription", subscriptionId, attribute.Key("subscription").String(subscriptionId)) + span.SetAttributes("datasource_id", dsId, attribute.Key("datasource_id").Int64(dsId)) + span.SetAttributes("org_id", orgId, attribute.Key("org_id").Int64(orgId)) + + defer span.End() + tracer.Inject(ctx, req.Header, span) + logger.Debug("AzureMonitor", "Subscription Details Req") + res, err := cli.Do(req) + if err != nil { + logger.Warn("failed to request subscription details: %s", err) + } + defer func() { + if err := res.Body.Close(); err != nil { + logger.Warn("Failed to close response body", "err", err) + } + }() + + body, err := io.ReadAll(res.Body) + if err != nil { + logger.Warn("failed to read response body: %s", err) + } + + if res.StatusCode/100 != 2 { + logger.Warn("request failed, status: %s, error: %s", res.Status, string(body)) + } + + var data types.SubscriptionsResponse + err = json.Unmarshal(body, &data) + if err != nil { + logger.Warn("Failed to unmarshal subscription detail response", "error", err, "status", res.Status, "body", string(body)) + } + + subscriptions[data.SubscriptionID] = data.DisplayName +} + func (e *AzureMonitorDatasource) executeQuery(ctx context.Context, logger log.Logger, query *types.AzureMonitorQuery, dsInfo types.DatasourceInfo, cli *http.Client, url string, tracer tracing.Tracer) backend.DataResponse { dataResponse := backend.DataResponse{} @@ -253,6 +306,10 @@ func (e *AzureMonitorDatasource) executeQuery(ctx context.Context, logger log.Lo return dataResponse } + if _, ok := subscriptions[query.Subscription]; !ok { + retrieveSubscriptionDetails(e, cli, ctx, logger, tracer, query.Subscription, dsInfo.Routes["Azure Monitor"].URL, dsInfo.DatasourceID, dsInfo.OrgID) + } + dataResponse.Frames, err = e.parseResponse(data, query, azurePortalUrl) if err != nil { dataResponse.Error = err @@ -340,8 +397,9 @@ func (e *AzureMonitorDatasource) parseResponse(amr types.AzureMonitorResponse, q labels["resourceName"] = resourceName } + currentResource := query.Resources[resourceID] if query.Alias != "" { - displayName := formatAzureMonitorLegendKey(query.Alias, resourceName, + displayName := formatAzureMonitorLegendKey(query.Alias, query.Subscription, currentResource, amr.Value[0].Name.LocalizedValue, "", "", amr.Namespace, amr.Value[0].ID, labels) if dataField.Config != nil { @@ -379,7 +437,6 @@ func (e *AzureMonitorDatasource) parseResponse(amr types.AzureMonitorResponse, q if err != nil { return nil, err } - frameWithLink := loganalytics.AddConfigLinks(*frame, queryUrl, nil) frames = append(frames, &frameWithLink) } @@ -495,12 +552,8 @@ func getQueryUrl(query *types.AzureMonitorQuery, azurePortalUrl, resourceID, res // formatAzureMonitorLegendKey builds the legend key or timeseries name // Alias patterns like {{resourcename}} are replaced with the appropriate data values. -func formatAzureMonitorLegendKey(alias string, resourceName string, metricName string, metadataName string, +func formatAzureMonitorLegendKey(alias string, subscriptionId string, resource types.AzureMonitorResource, metricName string, metadataName string, metadataValue string, namespace string, seriesID string, labels data.Labels) string { - startIndex := strings.Index(seriesID, "/resourceGroups/") + 16 - endIndex := strings.Index(seriesID, "/providers") - resourceGroup := seriesID[startIndex:endIndex] - // Could be a collision problem if there were two keys that varied only in case, but I don't think that would happen in azure. lowerLabels := data.Labels{} for k, v := range labels { @@ -517,8 +570,20 @@ func formatAzureMonitorLegendKey(alias string, resourceName string, metricName s metaPartName = strings.Replace(metaPartName, "}}", "", 1) metaPartName = strings.ToLower(strings.TrimSpace(metaPartName)) + if metaPartName == "subscriptionid" { + return []byte(subscriptionId) + } + + if metaPartName == "subscription" { + if subscription, ok := subscriptions[subscriptionId]; ok { + return []byte(subscription) + } else { + return []byte{} + } + } + if metaPartName == "resourcegroup" { - return []byte(resourceGroup) + return []byte(resource.ResourceGroup) } if metaPartName == "namespace" { @@ -526,7 +591,7 @@ func formatAzureMonitorLegendKey(alias string, resourceName string, metricName s } if metaPartName == "resourcename" { - return []byte(resourceName) + return []byte(resource.ResourceName) } if metaPartName == "metric" { diff --git a/pkg/tsdb/azuremonitor/metrics/azuremonitor-datasource_test.go b/pkg/tsdb/azuremonitor/metrics/azuremonitor-datasource_test.go index 61f8ed6a90a..bd73dae3306 100644 --- a/pkg/tsdb/azuremonitor/metrics/azuremonitor-datasource_test.go +++ b/pkg/tsdb/azuremonitor/metrics/azuremonitor-datasource_test.go @@ -49,6 +49,7 @@ func TestAzureMonitorBuildQueries(t *testing.T) { expectedBodyFilter string expectedParamFilter string expectedPortalURL *string + resources map[string]types.AzureMonitorResource }{ { name: "Parse queries from frontend and build AzureMonitor API queries", @@ -296,6 +297,16 @@ func TestAzureMonitorBuildQueries(t *testing.T) { queries, err := datasource.buildQueries(log.New("test"), tsdbQuery, dsInfo) require.NoError(t, err) + resources := map[string]types.AzureMonitorResource{} + if tt.azureMonitorVariedProperties["resources"] != nil { + resourceSlice := tt.azureMonitorVariedProperties["resources"].([]types.AzureMonitorResource) + for _, resource := range resourceSlice { + resources[fmt.Sprintf("/subscriptions/12345678-aaaa-bbbb-cccc-123456789abc/resourceGroups/%s/providers/Microsoft.Compute/virtualMachines/%s", resource.ResourceGroup, resource.ResourceName)] = resource + } + } else { + resources["/subscriptions/12345678-aaaa-bbbb-cccc-123456789abc/resourceGroups/grafanastaging/providers/Microsoft.Compute/virtualMachines/grafana"] = types.AzureMonitorResource{ResourceGroup: "grafanastaging", ResourceName: "grafana"} + } + azureMonitorQuery := &types.AzureMonitorQuery{ URL: tt.expectedURL, Target: tt.azureMonitorQueryTarget, @@ -305,7 +316,9 @@ func TestAzureMonitorBuildQueries(t *testing.T) { From: fromStart, To: fromStart.Add(34 * time.Minute), }, - BodyFilter: tt.expectedBodyFilter, + BodyFilter: tt.expectedBodyFilter, + Subscription: "12345678-aaaa-bbbb-cccc-123456789abc", + Resources: resources, } assert.Equal(t, tt.expectedParamFilter, queries[0].Params.Get("$filter")) @@ -354,6 +367,10 @@ func TestCustomNamespace(t *testing.T) { } func TestAzureMonitorParseResponse(t *testing.T) { + resources := map[string]types.AzureMonitorResource{} + resources["/subscriptions/12345678-aaaa-bbbb-cccc-123456789abc/resourceGroups/grafanastaging/providers/Microsoft.Compute/virtualMachines/grafana"] = types.AzureMonitorResource{ResourceGroup: "grafanastaging", ResourceName: "grafana"} + subscription := "12345678-aaaa-bbbb-cccc-123456789abc" + tests := []struct { name string responseFile string @@ -369,6 +386,8 @@ func TestAzureMonitorParseResponse(t *testing.T) { Params: url.Values{ "aggregation": {"Average"}, }, + Resources: resources, + Subscription: subscription, }, }, { @@ -379,6 +398,8 @@ func TestAzureMonitorParseResponse(t *testing.T) { Params: url.Values{ "aggregation": {"Total"}, }, + Resources: resources, + Subscription: subscription, }, }, { @@ -389,6 +410,8 @@ func TestAzureMonitorParseResponse(t *testing.T) { Params: url.Values{ "aggregation": {"Maximum"}, }, + Resources: resources, + Subscription: subscription, }, }, { @@ -399,6 +422,8 @@ func TestAzureMonitorParseResponse(t *testing.T) { Params: url.Values{ "aggregation": {"Minimum"}, }, + Resources: resources, + Subscription: subscription, }, }, { @@ -409,6 +434,8 @@ func TestAzureMonitorParseResponse(t *testing.T) { Params: url.Values{ "aggregation": {"Count"}, }, + Resources: resources, + Subscription: subscription, }, }, { @@ -419,6 +446,8 @@ func TestAzureMonitorParseResponse(t *testing.T) { Params: url.Values{ "aggregation": {"Average"}, }, + Resources: resources, + Subscription: subscription, }, }, { @@ -430,6 +459,8 @@ func TestAzureMonitorParseResponse(t *testing.T) { Params: url.Values{ "aggregation": {"Total"}, }, + Resources: resources, + Subscription: subscription, }, }, { @@ -441,17 +472,21 @@ func TestAzureMonitorParseResponse(t *testing.T) { Params: url.Values{ "aggregation": {"Average"}, }, + Resources: resources, + Subscription: subscription, }, }, { name: "multiple dimension time series response with label alias", responseFile: "azuremonitor/7-azure-monitor-response-multi-dimension.json", mockQuery: &types.AzureMonitorQuery{ - URL: "/subscriptions/12345678-aaaa-bbbb-cccc-123456789abc/resourceGroups/grafanastaging/providers/Microsoft.Compute/virtualMachines/grafana/providers/microsoft.insights/metrics", + URL: "/subscriptions/12345678-aaaa-bbbb-cccc-123456789abc/resourceGroups/grafanatest/providers/Microsoft.Storage/storageAccounts/testblobaccount/blobServices/default/providers/Microsoft.Insights/metrics", Alias: "{{resourcegroup}} {Blob Type={{blobtype}}, Tier={{Tier}}}", Params: url.Values{ "aggregation": {"Average"}, }, + Resources: map[string]types.AzureMonitorResource{"/subscriptions/12345678-aaaa-bbbb-cccc-123456789abc/resourceGroups/grafanatest/providers/Microsoft.Storage/storageAccounts/testblobaccount/blobServices/default/providers/Microsoft.Insights/metrics": {ResourceGroup: "grafanatest", ResourceName: "testblobaccount"}}, + Subscription: subscription, }, }, { @@ -463,6 +498,8 @@ func TestAzureMonitorParseResponse(t *testing.T) { Params: url.Values{ "aggregation": {"Average"}, }, + Resources: resources, + Subscription: subscription, }, }, { @@ -474,6 +511,8 @@ func TestAzureMonitorParseResponse(t *testing.T) { Params: url.Values{ "aggregation": {"Total"}, }, + Resources: resources, + Subscription: subscription, }, }, { @@ -485,6 +524,8 @@ func TestAzureMonitorParseResponse(t *testing.T) { Params: url.Values{ "aggregation": {"Total"}, }, + Resources: resources, + Subscription: subscription, }, }, { @@ -495,6 +536,8 @@ func TestAzureMonitorParseResponse(t *testing.T) { Params: url.Values{ "aggregation": {"Average"}, }, + Resources: resources, + Subscription: subscription, }, }, { @@ -505,6 +548,8 @@ func TestAzureMonitorParseResponse(t *testing.T) { Params: url.Values{ "aggregation": {"Average"}, }, + Resources: resources, + Subscription: subscription, }, }, } diff --git a/pkg/tsdb/azuremonitor/testdata/azuremonitor/7-azure-monitor-response-multi-dimension.json b/pkg/tsdb/azuremonitor/testdata/azuremonitor/7-azure-monitor-response-multi-dimension.json index 9adeec438eb..04c387b350f 100644 --- a/pkg/tsdb/azuremonitor/testdata/azuremonitor/7-azure-monitor-response-multi-dimension.json +++ b/pkg/tsdb/azuremonitor/testdata/azuremonitor/7-azure-monitor-response-multi-dimension.json @@ -4,7 +4,7 @@ "interval": "PT1H", "value": [ { - "id": "/subscriptions/44693801-6ee6-49de-9b2d-9106972f9572/resourceGroups/danieltest/providers/Microsoft.Storage/storageAccounts/danieltestdiag187/blobServices/default/providers/Microsoft.Insights/metrics/BlobCapacity", + "id": "/subscriptions/12345678-aaaa-bbbb-cccc-123456789abc/resourceGroups/grafanatest/providers/Microsoft.Storage/storageAccounts/testblobaccount/blobServices/default/providers/Microsoft.Insights/metrics/BlobCapacity", "type": "Microsoft.Insights/metrics", "name": { "value": "BlobCapacity", diff --git a/pkg/tsdb/azuremonitor/testdata/azuremonitor/7-azure-monitor-response-multi-dimension.json.multiple-dimension-time-series-response-with-label-alias.golden.jsonc b/pkg/tsdb/azuremonitor/testdata/azuremonitor/7-azure-monitor-response-multi-dimension.json.multiple-dimension-time-series-response-with-label-alias.golden.jsonc index f5df32b8670..356526ddeff 100644 --- a/pkg/tsdb/azuremonitor/testdata/azuremonitor/7-azure-monitor-response-multi-dimension.json.multiple-dimension-time-series-response-with-label-alias.golden.jsonc +++ b/pkg/tsdb/azuremonitor/testdata/azuremonitor/7-azure-monitor-response-multi-dimension.json.multiple-dimension-time-series-response-with-label-alias.golden.jsonc @@ -62,7 +62,7 @@ { "title": "View in Azure Portal", "targetBlank": true, - "url": "http://ds/#blade/Microsoft_Azure_MonitoringMetrics/Metrics.ReactView/Referer/MetricsExplorer/TimeContext/%7B%22absolute%22%3A%7B%22startTime%22%3A%220001-01-01T00%3A00%3A00Z%22%2C%22endTime%22%3A%220001-01-01T00%3A00%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%22%22%2C%22aggregationType%22%3A4%2C%22namespace%22%3A%22%22%2C%22metricVisualization%22%3A%7B%22displayName%22%3A%22%22%2C%22resourceDisplayName%22%3A%22grafana%22%7D%7D%5D%7D%5D%7D" + "url": "http://ds/#blade/Microsoft_Azure_MonitoringMetrics/Metrics.ReactView/Referer/MetricsExplorer/TimeContext/%7B%22absolute%22%3A%7B%22startTime%22%3A%220001-01-01T00%3A00%3A00Z%22%2C%22endTime%22%3A%220001-01-01T00%3A00%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%2Fgrafanatest%2Fproviders%2FMicrosoft.Storage%2FstorageAccounts%2Ftestblobaccount%2FblobServices%2Fdefault%2Fproviders%2FMicrosoft.Insights%2Fmetrics%22%7D%2C%22name%22%3A%22%22%2C%22aggregationType%22%3A4%2C%22namespace%22%3A%22%22%2C%22metricVisualization%22%3A%7B%22displayName%22%3A%22%22%2C%22resourceDisplayName%22%3A%22default%22%7D%7D%5D%7D%5D%7D" } ] } @@ -79,13 +79,13 @@ "tier": "Standard" }, "config": { - "displayName": "danieltest {Blob Type=PageBlob, Tier=Standard}", + "displayName": "grafanatest {Blob Type=PageBlob, Tier=Standard}", "unit": "decbytes", "links": [ { "title": "View in Azure Portal", "targetBlank": true, - "url": "http://ds/#blade/Microsoft_Azure_MonitoringMetrics/Metrics.ReactView/Referer/MetricsExplorer/TimeContext/%7B%22absolute%22%3A%7B%22startTime%22%3A%220001-01-01T00%3A00%3A00Z%22%2C%22endTime%22%3A%220001-01-01T00%3A00%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%22%22%2C%22aggregationType%22%3A4%2C%22namespace%22%3A%22%22%2C%22metricVisualization%22%3A%7B%22displayName%22%3A%22%22%2C%22resourceDisplayName%22%3A%22grafana%22%7D%7D%5D%7D%5D%7D" + "url": "http://ds/#blade/Microsoft_Azure_MonitoringMetrics/Metrics.ReactView/Referer/MetricsExplorer/TimeContext/%7B%22absolute%22%3A%7B%22startTime%22%3A%220001-01-01T00%3A00%3A00Z%22%2C%22endTime%22%3A%220001-01-01T00%3A00%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%2Fgrafanatest%2Fproviders%2FMicrosoft.Storage%2FstorageAccounts%2Ftestblobaccount%2FblobServices%2Fdefault%2Fproviders%2FMicrosoft.Insights%2Fmetrics%22%7D%2C%22name%22%3A%22%22%2C%22aggregationType%22%3A4%2C%22namespace%22%3A%22%22%2C%22metricVisualization%22%3A%7B%22displayName%22%3A%22%22%2C%22resourceDisplayName%22%3A%22default%22%7D%7D%5D%7D%5D%7D" } ] } @@ -121,7 +121,7 @@ { "title": "View in Azure Portal", "targetBlank": true, - "url": "http://ds/#blade/Microsoft_Azure_MonitoringMetrics/Metrics.ReactView/Referer/MetricsExplorer/TimeContext/%7B%22absolute%22%3A%7B%22startTime%22%3A%220001-01-01T00%3A00%3A00Z%22%2C%22endTime%22%3A%220001-01-01T00%3A00%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%22%22%2C%22aggregationType%22%3A4%2C%22namespace%22%3A%22%22%2C%22metricVisualization%22%3A%7B%22displayName%22%3A%22%22%2C%22resourceDisplayName%22%3A%22grafana%22%7D%7D%5D%7D%5D%7D" + "url": "http://ds/#blade/Microsoft_Azure_MonitoringMetrics/Metrics.ReactView/Referer/MetricsExplorer/TimeContext/%7B%22absolute%22%3A%7B%22startTime%22%3A%220001-01-01T00%3A00%3A00Z%22%2C%22endTime%22%3A%220001-01-01T00%3A00%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%2Fgrafanatest%2Fproviders%2FMicrosoft.Storage%2FstorageAccounts%2Ftestblobaccount%2FblobServices%2Fdefault%2Fproviders%2FMicrosoft.Insights%2Fmetrics%22%7D%2C%22name%22%3A%22%22%2C%22aggregationType%22%3A4%2C%22namespace%22%3A%22%22%2C%22metricVisualization%22%3A%7B%22displayName%22%3A%22%22%2C%22resourceDisplayName%22%3A%22default%22%7D%7D%5D%7D%5D%7D" } ] } @@ -138,13 +138,13 @@ "tier": "Hot" }, "config": { - "displayName": "danieltest {Blob Type=BlockBlob, Tier=Hot}", + "displayName": "grafanatest {Blob Type=BlockBlob, Tier=Hot}", "unit": "decbytes", "links": [ { "title": "View in Azure Portal", "targetBlank": true, - "url": "http://ds/#blade/Microsoft_Azure_MonitoringMetrics/Metrics.ReactView/Referer/MetricsExplorer/TimeContext/%7B%22absolute%22%3A%7B%22startTime%22%3A%220001-01-01T00%3A00%3A00Z%22%2C%22endTime%22%3A%220001-01-01T00%3A00%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%22%22%2C%22aggregationType%22%3A4%2C%22namespace%22%3A%22%22%2C%22metricVisualization%22%3A%7B%22displayName%22%3A%22%22%2C%22resourceDisplayName%22%3A%22grafana%22%7D%7D%5D%7D%5D%7D" + "url": "http://ds/#blade/Microsoft_Azure_MonitoringMetrics/Metrics.ReactView/Referer/MetricsExplorer/TimeContext/%7B%22absolute%22%3A%7B%22startTime%22%3A%220001-01-01T00%3A00%3A00Z%22%2C%22endTime%22%3A%220001-01-01T00%3A00%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%2Fgrafanatest%2Fproviders%2FMicrosoft.Storage%2FstorageAccounts%2Ftestblobaccount%2FblobServices%2Fdefault%2Fproviders%2FMicrosoft.Insights%2Fmetrics%22%7D%2C%22name%22%3A%22%22%2C%22aggregationType%22%3A4%2C%22namespace%22%3A%22%22%2C%22metricVisualization%22%3A%7B%22displayName%22%3A%22%22%2C%22resourceDisplayName%22%3A%22default%22%7D%7D%5D%7D%5D%7D" } ] } @@ -180,7 +180,7 @@ { "title": "View in Azure Portal", "targetBlank": true, - "url": "http://ds/#blade/Microsoft_Azure_MonitoringMetrics/Metrics.ReactView/Referer/MetricsExplorer/TimeContext/%7B%22absolute%22%3A%7B%22startTime%22%3A%220001-01-01T00%3A00%3A00Z%22%2C%22endTime%22%3A%220001-01-01T00%3A00%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%22%22%2C%22aggregationType%22%3A4%2C%22namespace%22%3A%22%22%2C%22metricVisualization%22%3A%7B%22displayName%22%3A%22%22%2C%22resourceDisplayName%22%3A%22grafana%22%7D%7D%5D%7D%5D%7D" + "url": "http://ds/#blade/Microsoft_Azure_MonitoringMetrics/Metrics.ReactView/Referer/MetricsExplorer/TimeContext/%7B%22absolute%22%3A%7B%22startTime%22%3A%220001-01-01T00%3A00%3A00Z%22%2C%22endTime%22%3A%220001-01-01T00%3A00%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%2Fgrafanatest%2Fproviders%2FMicrosoft.Storage%2FstorageAccounts%2Ftestblobaccount%2FblobServices%2Fdefault%2Fproviders%2FMicrosoft.Insights%2Fmetrics%22%7D%2C%22name%22%3A%22%22%2C%22aggregationType%22%3A4%2C%22namespace%22%3A%22%22%2C%22metricVisualization%22%3A%7B%22displayName%22%3A%22%22%2C%22resourceDisplayName%22%3A%22default%22%7D%7D%5D%7D%5D%7D" } ] } @@ -197,13 +197,13 @@ "tier": "Cool" }, "config": { - "displayName": "danieltest {Blob Type=Azure Data Lake Storage, Tier=Cool}", + "displayName": "grafanatest {Blob Type=Azure Data Lake Storage, Tier=Cool}", "unit": "decbytes", "links": [ { "title": "View in Azure Portal", "targetBlank": true, - "url": "http://ds/#blade/Microsoft_Azure_MonitoringMetrics/Metrics.ReactView/Referer/MetricsExplorer/TimeContext/%7B%22absolute%22%3A%7B%22startTime%22%3A%220001-01-01T00%3A00%3A00Z%22%2C%22endTime%22%3A%220001-01-01T00%3A00%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%22%22%2C%22aggregationType%22%3A4%2C%22namespace%22%3A%22%22%2C%22metricVisualization%22%3A%7B%22displayName%22%3A%22%22%2C%22resourceDisplayName%22%3A%22grafana%22%7D%7D%5D%7D%5D%7D" + "url": "http://ds/#blade/Microsoft_Azure_MonitoringMetrics/Metrics.ReactView/Referer/MetricsExplorer/TimeContext/%7B%22absolute%22%3A%7B%22startTime%22%3A%220001-01-01T00%3A00%3A00Z%22%2C%22endTime%22%3A%220001-01-01T00%3A00%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%2Fgrafanatest%2Fproviders%2FMicrosoft.Storage%2FstorageAccounts%2Ftestblobaccount%2FblobServices%2Fdefault%2Fproviders%2FMicrosoft.Insights%2Fmetrics%22%7D%2C%22name%22%3A%22%22%2C%22aggregationType%22%3A4%2C%22namespace%22%3A%22%22%2C%22metricVisualization%22%3A%7B%22displayName%22%3A%22%22%2C%22resourceDisplayName%22%3A%22default%22%7D%7D%5D%7D%5D%7D" } ] } diff --git a/pkg/tsdb/azuremonitor/types/types.go b/pkg/tsdb/azuremonitor/types/types.go index ab063400b99..ee464128897 100644 --- a/pkg/tsdb/azuremonitor/types/types.go +++ b/pkg/tsdb/azuremonitor/types/types.go @@ -65,14 +65,16 @@ 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 { - URL string - Target string - Params url.Values - RefID string - Alias string - TimeRange backend.TimeRange - BodyFilter string - Dimensions []AzureMonitorDimensionFilter + URL string + Target string + Params url.Values + RefID string + Alias string + TimeRange backend.TimeRange + BodyFilter string + Dimensions []AzureMonitorDimensionFilter + Resources map[string]AzureMonitorResource + Subscription string } // AzureMonitorResponse is the json response from the Azure Monitor API @@ -274,4 +276,11 @@ type LogAnalyticsWorkspaceResponse struct { RetentionInDays int `json:"retentionInDays"` } +type SubscriptionsResponse struct { + ID string `json:"id"` + SubscriptionID string `json:"subscriptionId"` + TenantID string `json:"tenantId"` + DisplayName string `json:"displayName"` +} + var ErrorAzureHealthCheck = errors.New("health check failed")