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
pull/68794/head
Andreas Christou 2 years ago committed by GitHub
parent a44ac0ed63
commit bca8428f63
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
  1. 2
      docs/sources/datasources/azure-monitor/query-editor/index.md
  2. 99
      pkg/tsdb/azuremonitor/metrics/azuremonitor-datasource.go
  3. 49
      pkg/tsdb/azuremonitor/metrics/azuremonitor-datasource_test.go
  4. 2
      pkg/tsdb/azuremonitor/testdata/azuremonitor/7-azure-monitor-response-multi-dimension.json
  5. 18
      pkg/tsdb/azuremonitor/testdata/azuremonitor/7-azure-monitor-response-multi-dimension.json.multiple-dimension-time-series-response-with-label-alias.golden.jsonc
  6. 25
      pkg/tsdb/azuremonitor/types/types.go

@ -83,6 +83,8 @@ For example:
| Alias pattern | Description | | Alias pattern | Description |
| ----------------------------- | ------------------------------------------------------------------------------------------------------ | | ----------------------------- | ------------------------------------------------------------------------------------------------------ |
| `{{ subscriptionid }}` | Replaced with the subscription ID. |
| `{{ subscription }}` | Replaced with the subscription name. |
| `{{ resourcegroup }}` | Replaced with the the resource group. | | `{{ resourcegroup }}` | Replaced with the the resource group. |
| `{{ namespace }}` | Replaced with the resource type or namespace, such as `Microsoft.Compute/virtualMachines`. | | `{{ namespace }}` | Replaced with the resource type or namespace, such as `Microsoft.Compute/virtualMachines`. |
| `{{ resourcename }}` | Replaced with the resource name. | | `{{ resourcename }}` | Replaced with the resource name. |

@ -42,6 +42,8 @@ func (e *AzureMonitorDatasource) ResourceRequest(rw http.ResponseWriter, req *ht
e.Proxy.Do(rw, req, cli) e.Proxy.Do(rw, req, cli)
} }
var subscriptions = map[string]string{}
// executeTimeSeriesQuery does the following: // executeTimeSeriesQuery does the following:
// 1. build the AzureMonitor url and querystring for each query // 1. build the AzureMonitor url and querystring for each query
// 2. executes each query by calling the Azure Monitor API // 2. executes each query by calling the Azure Monitor API
@ -112,6 +114,7 @@ func (e *AzureMonitorDatasource) buildQueries(logger log.Logger, queries []backe
params.Add("region", azJSONModel.Region) params.Add("region", azJSONModel.Region)
} }
resourceIDs := []string{} resourceIDs := []string{}
resourceMap := map[string]types.AzureMonitorResource{}
if hasOne, resourceGroup, resourceName := hasOneResource(queryJSONModel); hasOne { if hasOne, resourceGroup, resourceName := hasOneResource(queryJSONModel); hasOne {
ub := urlBuilder{ ub := urlBuilder{
ResourceURI: azJSONModel.ResourceURI, ResourceURI: azJSONModel.ResourceURI,
@ -125,6 +128,7 @@ func (e *AzureMonitorDatasource) buildQueries(logger log.Logger, queries []backe
azureURL = ub.BuildMetricsURL() azureURL = ub.BuildMetricsURL()
// POST requests are only supported at the subscription level // POST requests are only supported at the subscription level
filterInBody = false filterInBody = false
resourceMap[ub.buildResourceURI()] = types.AzureMonitorResource{ResourceGroup: resourceGroup, ResourceName: resourceName}
} else { } else {
for _, r := range azJSONModel.Resources { for _, r := range azJSONModel.Resources {
ub := urlBuilder{ ub := urlBuilder{
@ -134,7 +138,9 @@ func (e *AzureMonitorDatasource) buildQueries(logger log.Logger, queries []backe
MetricNamespace: azJSONModel.MetricNamespace, MetricNamespace: azJSONModel.MetricNamespace,
ResourceName: r.ResourceName, 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{ query := &types.AzureMonitorQuery{
URL: azureURL, URL: azureURL,
Target: target, Target: target,
Params: params, Params: params,
RefID: query.RefID, RefID: query.RefID,
Alias: alias, Alias: alias,
TimeRange: query.TimeRange, TimeRange: query.TimeRange,
Dimensions: azJSONModel.DimensionFilters, Dimensions: azJSONModel.DimensionFilters,
Resources: resourceMap,
Subscription: queryJSONModel.Subscription,
} }
if filterString != "" { if filterString != "" {
if filterInBody { if filterInBody {
@ -201,6 +209,51 @@ func (e *AzureMonitorDatasource) buildQueries(logger log.Logger, queries []backe
return azureMonitorQueries, nil 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, 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 { url string, tracer tracing.Tracer) backend.DataResponse {
dataResponse := backend.DataResponse{} dataResponse := backend.DataResponse{}
@ -253,6 +306,10 @@ func (e *AzureMonitorDatasource) executeQuery(ctx context.Context, logger log.Lo
return dataResponse 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) dataResponse.Frames, err = e.parseResponse(data, query, azurePortalUrl)
if err != nil { if err != nil {
dataResponse.Error = err dataResponse.Error = err
@ -340,8 +397,9 @@ func (e *AzureMonitorDatasource) parseResponse(amr types.AzureMonitorResponse, q
labels["resourceName"] = resourceName labels["resourceName"] = resourceName
} }
currentResource := query.Resources[resourceID]
if query.Alias != "" { 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) amr.Value[0].Name.LocalizedValue, "", "", amr.Namespace, amr.Value[0].ID, labels)
if dataField.Config != nil { if dataField.Config != nil {
@ -379,7 +437,6 @@ func (e *AzureMonitorDatasource) parseResponse(amr types.AzureMonitorResponse, q
if err != nil { if err != nil {
return nil, err return nil, err
} }
frameWithLink := loganalytics.AddConfigLinks(*frame, queryUrl, nil) frameWithLink := loganalytics.AddConfigLinks(*frame, queryUrl, nil)
frames = append(frames, &frameWithLink) frames = append(frames, &frameWithLink)
} }
@ -495,12 +552,8 @@ func getQueryUrl(query *types.AzureMonitorQuery, azurePortalUrl, resourceID, res
// 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, func formatAzureMonitorLegendKey(alias string, subscriptionId string, resource types.AzureMonitorResource, metricName string, metadataName string,
metadataValue string, namespace string, seriesID string, labels data.Labels) 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. // 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{} lowerLabels := data.Labels{}
for k, v := range 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.Replace(metaPartName, "}}", "", 1)
metaPartName = strings.ToLower(strings.TrimSpace(metaPartName)) 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" { if metaPartName == "resourcegroup" {
return []byte(resourceGroup) return []byte(resource.ResourceGroup)
} }
if metaPartName == "namespace" { if metaPartName == "namespace" {
@ -526,7 +591,7 @@ func formatAzureMonitorLegendKey(alias string, resourceName string, metricName s
} }
if metaPartName == "resourcename" { if metaPartName == "resourcename" {
return []byte(resourceName) return []byte(resource.ResourceName)
} }
if metaPartName == "metric" { if metaPartName == "metric" {

@ -49,6 +49,7 @@ func TestAzureMonitorBuildQueries(t *testing.T) {
expectedBodyFilter string expectedBodyFilter string
expectedParamFilter string expectedParamFilter string
expectedPortalURL *string expectedPortalURL *string
resources map[string]types.AzureMonitorResource
}{ }{
{ {
name: "Parse queries from frontend and build AzureMonitor API queries", 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) queries, err := datasource.buildQueries(log.New("test"), tsdbQuery, dsInfo)
require.NoError(t, err) 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{ azureMonitorQuery := &types.AzureMonitorQuery{
URL: tt.expectedURL, URL: tt.expectedURL,
Target: tt.azureMonitorQueryTarget, Target: tt.azureMonitorQueryTarget,
@ -305,7 +316,9 @@ func TestAzureMonitorBuildQueries(t *testing.T) {
From: fromStart, From: fromStart,
To: fromStart.Add(34 * time.Minute), 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")) assert.Equal(t, tt.expectedParamFilter, queries[0].Params.Get("$filter"))
@ -354,6 +367,10 @@ func TestCustomNamespace(t *testing.T) {
} }
func TestAzureMonitorParseResponse(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 { tests := []struct {
name string name string
responseFile string responseFile string
@ -369,6 +386,8 @@ func TestAzureMonitorParseResponse(t *testing.T) {
Params: url.Values{ Params: url.Values{
"aggregation": {"Average"}, "aggregation": {"Average"},
}, },
Resources: resources,
Subscription: subscription,
}, },
}, },
{ {
@ -379,6 +398,8 @@ func TestAzureMonitorParseResponse(t *testing.T) {
Params: url.Values{ Params: url.Values{
"aggregation": {"Total"}, "aggregation": {"Total"},
}, },
Resources: resources,
Subscription: subscription,
}, },
}, },
{ {
@ -389,6 +410,8 @@ func TestAzureMonitorParseResponse(t *testing.T) {
Params: url.Values{ Params: url.Values{
"aggregation": {"Maximum"}, "aggregation": {"Maximum"},
}, },
Resources: resources,
Subscription: subscription,
}, },
}, },
{ {
@ -399,6 +422,8 @@ func TestAzureMonitorParseResponse(t *testing.T) {
Params: url.Values{ Params: url.Values{
"aggregation": {"Minimum"}, "aggregation": {"Minimum"},
}, },
Resources: resources,
Subscription: subscription,
}, },
}, },
{ {
@ -409,6 +434,8 @@ func TestAzureMonitorParseResponse(t *testing.T) {
Params: url.Values{ Params: url.Values{
"aggregation": {"Count"}, "aggregation": {"Count"},
}, },
Resources: resources,
Subscription: subscription,
}, },
}, },
{ {
@ -419,6 +446,8 @@ func TestAzureMonitorParseResponse(t *testing.T) {
Params: url.Values{ Params: url.Values{
"aggregation": {"Average"}, "aggregation": {"Average"},
}, },
Resources: resources,
Subscription: subscription,
}, },
}, },
{ {
@ -430,6 +459,8 @@ func TestAzureMonitorParseResponse(t *testing.T) {
Params: url.Values{ Params: url.Values{
"aggregation": {"Total"}, "aggregation": {"Total"},
}, },
Resources: resources,
Subscription: subscription,
}, },
}, },
{ {
@ -441,17 +472,21 @@ func TestAzureMonitorParseResponse(t *testing.T) {
Params: url.Values{ Params: url.Values{
"aggregation": {"Average"}, "aggregation": {"Average"},
}, },
Resources: resources,
Subscription: subscription,
}, },
}, },
{ {
name: "multiple dimension time series response with label alias", name: "multiple dimension time series response with label alias",
responseFile: "azuremonitor/7-azure-monitor-response-multi-dimension.json", responseFile: "azuremonitor/7-azure-monitor-response-multi-dimension.json",
mockQuery: &types.AzureMonitorQuery{ 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}}}", Alias: "{{resourcegroup}} {Blob Type={{blobtype}}, Tier={{Tier}}}",
Params: url.Values{ Params: url.Values{
"aggregation": {"Average"}, "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{ Params: url.Values{
"aggregation": {"Average"}, "aggregation": {"Average"},
}, },
Resources: resources,
Subscription: subscription,
}, },
}, },
{ {
@ -474,6 +511,8 @@ func TestAzureMonitorParseResponse(t *testing.T) {
Params: url.Values{ Params: url.Values{
"aggregation": {"Total"}, "aggregation": {"Total"},
}, },
Resources: resources,
Subscription: subscription,
}, },
}, },
{ {
@ -485,6 +524,8 @@ func TestAzureMonitorParseResponse(t *testing.T) {
Params: url.Values{ Params: url.Values{
"aggregation": {"Total"}, "aggregation": {"Total"},
}, },
Resources: resources,
Subscription: subscription,
}, },
}, },
{ {
@ -495,6 +536,8 @@ func TestAzureMonitorParseResponse(t *testing.T) {
Params: url.Values{ Params: url.Values{
"aggregation": {"Average"}, "aggregation": {"Average"},
}, },
Resources: resources,
Subscription: subscription,
}, },
}, },
{ {
@ -505,6 +548,8 @@ func TestAzureMonitorParseResponse(t *testing.T) {
Params: url.Values{ Params: url.Values{
"aggregation": {"Average"}, "aggregation": {"Average"},
}, },
Resources: resources,
Subscription: subscription,
}, },
}, },
} }

@ -4,7 +4,7 @@
"interval": "PT1H", "interval": "PT1H",
"value": [ "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", "type": "Microsoft.Insights/metrics",
"name": { "name": {
"value": "BlobCapacity", "value": "BlobCapacity",

@ -62,7 +62,7 @@
{ {
"title": "View in Azure Portal", "title": "View in Azure Portal",
"targetBlank": true, "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" "tier": "Standard"
}, },
"config": { "config": {
"displayName": "danieltest {Blob Type=PageBlob, Tier=Standard}", "displayName": "grafanatest {Blob Type=PageBlob, Tier=Standard}",
"unit": "decbytes", "unit": "decbytes",
"links": [ "links": [
{ {
"title": "View in Azure Portal", "title": "View in Azure Portal",
"targetBlank": true, "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", "title": "View in Azure Portal",
"targetBlank": true, "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" "tier": "Hot"
}, },
"config": { "config": {
"displayName": "danieltest {Blob Type=BlockBlob, Tier=Hot}", "displayName": "grafanatest {Blob Type=BlockBlob, Tier=Hot}",
"unit": "decbytes", "unit": "decbytes",
"links": [ "links": [
{ {
"title": "View in Azure Portal", "title": "View in Azure Portal",
"targetBlank": true, "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", "title": "View in Azure Portal",
"targetBlank": true, "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" "tier": "Cool"
}, },
"config": { "config": {
"displayName": "danieltest {Blob Type=Azure Data Lake Storage, Tier=Cool}", "displayName": "grafanatest {Blob Type=Azure Data Lake Storage, Tier=Cool}",
"unit": "decbytes", "unit": "decbytes",
"links": [ "links": [
{ {
"title": "View in Azure Portal", "title": "View in Azure Portal",
"targetBlank": true, "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"
} }
] ]
} }

@ -65,14 +65,16 @@ type DatasourceInfo struct {
// AzureMonitorQuery is the query for all the services as they have similar queries // AzureMonitorQuery is the query for all the services as they have similar queries
// with a url, a querystring and an alias field // with a url, a querystring and an alias field
type AzureMonitorQuery struct { type AzureMonitorQuery struct {
URL string URL string
Target string Target string
Params url.Values Params url.Values
RefID string RefID string
Alias string Alias string
TimeRange backend.TimeRange TimeRange backend.TimeRange
BodyFilter string BodyFilter string
Dimensions []AzureMonitorDimensionFilter Dimensions []AzureMonitorDimensionFilter
Resources map[string]AzureMonitorResource
Subscription string
} }
// AzureMonitorResponse is the json response from the Azure Monitor API // AzureMonitorResponse is the json response from the Azure Monitor API
@ -274,4 +276,11 @@ type LogAnalyticsWorkspaceResponse struct {
RetentionInDays int `json:"retentionInDays"` 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") var ErrorAzureHealthCheck = errors.New("health check failed")

Loading…
Cancel
Save