From df3e56aedb3305aa42808f8c9e33beb90a15ff8b Mon Sep 17 00:00:00 2001 From: "grafana-delivery-bot[bot]" <132647405+grafana-delivery-bot[bot]@users.noreply.github.com> Date: Fri, 30 Jun 2023 19:01:03 +0300 Subject: [PATCH] [v10.0.x] AzureMonitor: Fix metric names for multi-resources. (#70994) AzureMonitor: Fix metric names for multi-resources. (#70864) When building a query for multiple resources only a subset of metrics are valid and that selection is only available via the API version `2017-12-01-preview`. fixes #68603 (cherry picked from commit f32f1859a05dac0de37187a04ff15666f5318993) Co-authored-by: Adam Simpson --- .../azure_monitor_datasource.test.ts | 32 +++++++++++++-- .../azure_monitor/azure_monitor_datasource.ts | 18 ++++++--- .../azure_monitor/url_builder.test.ts | 18 +++++++++ .../azuremonitor/azure_monitor/url_builder.ts | 36 +++++++++++------ .../MetricsQueryEditor/dataHooks.ts | 39 ++++++++++++++----- 5 files changed, 111 insertions(+), 32 deletions(-) diff --git a/public/app/plugins/datasource/azuremonitor/azure_monitor/azure_monitor_datasource.test.ts b/public/app/plugins/datasource/azuremonitor/azure_monitor/azure_monitor_datasource.test.ts index 0f84b62322a..e37ddffcc43 100644 --- a/public/app/plugins/datasource/azuremonitor/azure_monitor/azure_monitor_datasource.test.ts +++ b/public/app/plugins/datasource/azuremonitor/azure_monitor/azure_monitor_datasource.test.ts @@ -317,7 +317,7 @@ describe('AzureMonitorDatasource', () => { ], }; - beforeEach(() => { + it('should return list of Metric Names', () => { ctx.ds.azureMonitorDatasource.getResource = jest.fn().mockImplementation((path: string) => { const basePath = 'azuremonitor/subscriptions/mock-subscription-id/resourceGroups/nodeapp'; const expected = @@ -327,9 +327,6 @@ describe('AzureMonitorDatasource', () => { expect(path).toBe(expected); return Promise.resolve(response); }); - }); - - it('should return list of Metric Names', () => { return ctx.ds.azureMonitorDatasource .getMetricNames({ resourceUri: @@ -344,6 +341,33 @@ describe('AzureMonitorDatasource', () => { expect(results[1].value).toEqual('FreeCapacity'); }); }); + + it('should return list of Metric Names appropriate when multiple resources are selected', () => { + ctx.ds.azureMonitorDatasource.getResource = jest.fn().mockImplementation((path: string) => { + const basePath = 'azuremonitor/subscriptions/mock-subscription-id/resourceGroups/nodeapp'; + const expected = + basePath + + '/providers/microsoft.insights/metricdefinitions?api-version=2017-12-01-preview&metricnamespace=microsoft.insights%2Fcomponents®ion=region'; + expect(path).toBe(expected); + return Promise.resolve(response); + }); + return ctx.ds.azureMonitorDatasource + .getMetricNames( + { + resourceUri: '/subscriptions/mock-subscription-id/resourceGroups/nodeapp', + metricNamespace: 'microsoft.insights/components', + }, + true, + 'region' + ) + .then((results: Array<{ text: string; value: string }>) => { + expect(results.length).toEqual(2); + expect(results[0].text).toEqual('Used capacity'); + expect(results[0].value).toEqual('UsedCapacity'); + expect(results[1].text).toEqual('Free capacity'); + expect(results[1].value).toEqual('FreeCapacity'); + }); + }); }); describe('When performing getMetricMetadata', () => { diff --git a/public/app/plugins/datasource/azuremonitor/azure_monitor/azure_monitor_datasource.ts b/public/app/plugins/datasource/azuremonitor/azure_monitor/azure_monitor_datasource.ts index 5621e34d3cc..687f38e60c3 100644 --- a/public/app/plugins/datasource/azuremonitor/azure_monitor/azure_monitor_datasource.ts +++ b/public/app/plugins/datasource/azuremonitor/azure_monitor/azure_monitor_datasource.ts @@ -265,27 +265,33 @@ export default class AzureMonitorDatasource extends DataSourceWithBackend { return ResponseParser.parseResponseValues(result, 'name.localizedValue', 'name.value'); }); } - getMetricMetadata(query: GetMetricMetadataQuery) { + getMetricMetadata(query: GetMetricMetadataQuery, multipleResources?: boolean, region?: string) { const { metricName } = query; + const apiVersion = multipleResources ? this.apiPreviewVersion : this.apiVersion; const url = UrlBuilder.buildAzureMonitorGetMetricNamesUrl( this.resourcePath, - this.apiVersion, + apiVersion, // Only use the first query, as the metric metadata should be the same for all queries this.replaceSingleTemplateVariables(query), - this.templateSrv + this.templateSrv, + multipleResources, + region ); return this.getResource(url).then((result: AzureMonitorMetricsMetadataResponse) => { return ResponseParser.parseMetadata(result, this.templateSrv.replace(metricName)); diff --git a/public/app/plugins/datasource/azuremonitor/azure_monitor/url_builder.test.ts b/public/app/plugins/datasource/azuremonitor/azure_monitor/url_builder.test.ts index a741a2f149f..bcb86a35b5c 100644 --- a/public/app/plugins/datasource/azuremonitor/azure_monitor/url_builder.test.ts +++ b/public/app/plugins/datasource/azuremonitor/azure_monitor/url_builder.test.ts @@ -398,5 +398,23 @@ describe('AzureMonitorUrlBuilder', () => { ); }); }); + describe('when multiple resources are selected', () => { + it('passes metricNamespace as query param', () => { + const url = UrlBuilder.buildAzureMonitorGetMetricNamesUrl( + '', + '2017-05-01-preview', + { + resourceUri: '/subscriptions/sub/resource-uri/resource', + metricNamespace: 'Microsoft.Sql/servers', + }, + templateSrv, + true, + 'region' + ); + expect(url).toBe( + '/subscriptions/sub/resource-uri/resource/providers/microsoft.insights/metricdefinitions?api-version=2017-05-01-preview&metricnamespace=Microsoft.Sql%2Fservers®ion=region' + ); + }); + }); }); }); diff --git a/public/app/plugins/datasource/azuremonitor/azure_monitor/url_builder.ts b/public/app/plugins/datasource/azuremonitor/azure_monitor/url_builder.ts index 319df598796..1b5aae85712 100644 --- a/public/app/plugins/datasource/azuremonitor/azure_monitor/url_builder.ts +++ b/public/app/plugins/datasource/azuremonitor/azure_monitor/url_builder.ts @@ -3,14 +3,12 @@ import { TemplateSrv } from '@grafana/runtime'; import { AzureMonitorResource, GetMetricNamespacesQuery, GetMetricNamesQuery } from '../types'; export default class UrlBuilder { - static buildResourceUri(templateSrv: TemplateSrv, resource: AzureMonitorResource) { + static buildResourceUri(templateSrv: TemplateSrv, resource: AzureMonitorResource, multipleResources?: boolean) { const urlArray = []; const { subscription, resourceGroup, metricNamespace, resourceName } = resource; - if (subscription) { urlArray.push('/subscriptions', subscription); - - if (resourceGroup) { + if (resourceGroup && !multipleResources) { urlArray.push('resourceGroups', resourceGroup); if (metricNamespace && resourceName) { @@ -78,26 +76,40 @@ export default class UrlBuilder { baseUrl: string, apiVersion: string, query: GetMetricNamesQuery, - templateSrv: TemplateSrv + templateSrv: TemplateSrv, + multipleResources?: boolean, + region?: string ) { let resourceUri: string; - const { customNamespace } = query; + const { customNamespace, metricNamespace } = query; if ('resourceUri' in query) { resourceUri = query.resourceUri; } else { const { subscription, resourceGroup, metricNamespace, resourceName } = query; - resourceUri = UrlBuilder.buildResourceUri(templateSrv, { - subscription, - resourceGroup, - metricNamespace, - resourceName, - }); + resourceUri = UrlBuilder.buildResourceUri( + templateSrv, + { + subscription, + resourceGroup, + metricNamespace, + resourceName, + }, + multipleResources + ); } let url = `${baseUrl}${resourceUri}/providers/microsoft.insights/metricdefinitions?api-version=${apiVersion}`; if (customNamespace) { url += `&metricnamespace=${encodeURIComponent(customNamespace)}`; } + if (multipleResources && !customNamespace && metricNamespace) { + url += `&metricnamespace=${encodeURIComponent(metricNamespace)}`; + + if (region) { + url += `®ion=${region}`; + } + } + return url; } } diff --git a/public/app/plugins/datasource/azuremonitor/components/MetricsQueryEditor/dataHooks.ts b/public/app/plugins/datasource/azuremonitor/components/MetricsQueryEditor/dataHooks.ts index b6edf825d7c..b21cfc9c8d5 100644 --- a/public/app/plugins/datasource/azuremonitor/components/MetricsQueryEditor/dataHooks.ts +++ b/public/app/plugins/datasource/azuremonitor/components/MetricsQueryEditor/dataHooks.ts @@ -88,25 +88,31 @@ export const useMetricNames: DataHook = (query, datasource, onChange, setError) const { subscription } = query; const { metricNamespace, metricName, resources, customNamespace } = query.azureMonitor ?? {}; const { resourceGroup, resourceName } = getResourceGroupAndName(resources); + const multipleResources = (resources && resources.length > 1) ?? false; + const region = query.azureMonitor?.region ?? ''; return useAsyncState( async () => { if (!subscription || !metricNamespace || !resourceGroup || !resourceName) { return; } - const results = await datasource.azureMonitorDatasource.getMetricNames({ - subscription, - resourceGroup, - resourceName, - metricNamespace, - customNamespace, - }); + const results = await datasource.azureMonitorDatasource.getMetricNames( + { + subscription, + resourceGroup, + resourceName, + metricNamespace, + customNamespace, + }, + multipleResources, + region + ); const options = formatOptions(results, metricName); return options; }, setError, - [subscription, resourceGroup, resourceName, metricNamespace, customNamespace] + [subscription, resourceGroup, resourceName, metricNamespace, customNamespace, multipleResources] ); }; @@ -124,6 +130,7 @@ export const useMetricMetadata = (query: AzureMonitorQuery, datasource: Datasour const { subscription } = query; const { resources, metricNamespace, metricName, aggregation, timeGrain, customNamespace } = query.azureMonitor ?? {}; const { resourceGroup, resourceName } = getResourceGroupAndName(resources); + const multipleResources = (resources && resources.length > 1) ?? false; // Fetch new metric metadata when the fields change useEffect(() => { @@ -133,7 +140,10 @@ export const useMetricMetadata = (query: AzureMonitorQuery, datasource: Datasour } datasource.azureMonitorDatasource - .getMetricMetadata({ subscription, resourceGroup, resourceName, metricNamespace, metricName, customNamespace }) + .getMetricMetadata( + { subscription, resourceGroup, resourceName, metricNamespace, metricName, customNamespace }, + multipleResources + ) .then((metadata) => { // TODO: Move the aggregationTypes and timeGrain defaults into `getMetricMetadata` const aggregations = (metadata.supportedAggTypes || [metadata.primaryAggType]).map((v) => ({ @@ -150,7 +160,16 @@ export const useMetricMetadata = (query: AzureMonitorQuery, datasource: Datasour primaryAggType: metadata.primaryAggType, }); }); - }, [datasource, subscription, resourceGroup, resourceName, metricNamespace, metricName, customNamespace]); + }, [ + datasource, + subscription, + resourceGroup, + resourceName, + metricNamespace, + metricName, + customNamespace, + multipleResources, + ]); // Update the query state in response to the meta data changing useEffect(() => {