From bc0da1bbfc078d2ddb0034a0effef6f0c87e2c58 Mon Sep 17 00:00:00 2001 From: Raphael Couto Date: Fri, 19 Jul 2019 20:43:52 -0300 Subject: [PATCH] Plugin: AzureMonitor - Reapply MetricNamespace support (#17282) * Reapply MetricNamespace support * Fixing tests * refactor: move metricnamespace param to backend * refactor: remove unused function * azuremonitor: migration for new metric namespace field * azuremonitor: add template query for metric namespace with a sub * docs: template queries for azure monitor Adds new lines for the metricnamespace template function and fixes some messed up lines --- .../features/datasources/azuremonitor.md | 24 ++-- .../azuremonitor/azuremonitor-datasource.go | 1 + .../azuremonitor-datasource_test.go | 13 +- .../azure_monitor_datasource.test.ts | 124 ++++++++++++++++-- .../azure_monitor/azure_monitor_datasource.ts | 61 ++++++++- .../azure_monitor/response_parser.ts | 14 +- .../azure_monitor/supported_namespaces.ts | 2 + .../azure_monitor/url_builder.test.ts | 18 ++- .../azure_monitor/url_builder.ts | 35 ++++- .../datasource.ts | 27 +++- .../partials/query.editor.html | 9 +- .../query_ctrl.test.ts | 14 +- .../query_ctrl.ts | 56 +++++++- .../grafana-azure-monitor-datasource/types.ts | 1 + 14 files changed, 350 insertions(+), 49 deletions(-) diff --git a/docs/sources/features/datasources/azuremonitor.md b/docs/sources/features/datasources/azuremonitor.md index 22d40a333bb..e449c0b444f 100644 --- a/docs/sources/features/datasources/azuremonitor.md +++ b/docs/sources/features/datasources/azuremonitor.md @@ -123,17 +123,19 @@ Note that the Azure Monitor service does not support multiple values yet. If you The Azure Monitor Datasource Plugin provides the following queries you can specify in the `Query` field in the Variable edit view. They allow you to fill a variable's options list. -| Name | Description | -| -------------------------------------------------------------------------------------------- | ------------------------------------------------------------------------------- | -| _Subscriptions()_ | Returns a list of subscriptions. | -| _ResourceGroups()_ | Returns a list of resource groups. | -| _ResourceGroups(12345678-aaaa-bbbb-cccc-123456789aaa)_ | Returns a list of resource groups for a specified subscription. | -| _Namespaces(aResourceGroup)_ | Returns a list of namespaces for the specified resource group. | -| _Namespaces(12345678-aaaa-bbbb-cccc-123456789aaa, aResourceGroup)_ | Returns a list of namespaces for the specified resource group and subscription. | -| _ResourceNames(aResourceGroup, aNamespace)_ | Returns a list of resource names. | -| _ResourceNames(12345678-aaaa-bbbb-cccc-123456789aaaaResourceGroup, aNamespace)_ | Returns a list of resource names for a specified subscription. | -| _MetricNames(aResourceGroup, aNamespace, aResourceName)_ | Returns a list of metric names. | -| _MetricNames(12345678-aaaa-bbbb-cccc-123456789aaaaResourceGroup, aNamespace, aResourceName)_ | Returns a list of metric names for a specified subscription. | +| Name | Description | +| -------------------------------------------------------------------------------------------------- | ------------------------------------------------------------------------------- | +| _Subscriptions()_ | Returns a list of subscriptions. | +| _ResourceGroups()_ | Returns a list of resource groups. | +| _ResourceGroups(12345678-aaaa-bbbb-cccc-123456789aaa)_ | Returns a list of resource groups for a specified subscription. | +| _Namespaces(aResourceGroup)_ | Returns a list of namespaces for the specified resource group. | +| _Namespaces(12345678-aaaa-bbbb-cccc-123456789aaa, aResourceGroup)_ | Returns a list of namespaces for the specified resource group and subscription. | +| _ResourceNames(aResourceGroup, aNamespace)_ | Returns a list of resource names. | +| _ResourceNames(12345678-aaaa-bbbb-cccc-123456789aaa, aResourceGroup, aNamespace)_ | Returns a list of resource names for a specified subscription. | +| _MetricNamespace(aResourceGroup, aNamespace, aResourceName)_ | Returns a list of metric namespaces. | +| _MetricNamespace(12345678-aaaa-bbbb-cccc-123456789aaa, aResourceGroup, aNamespace, aResourceName)_ | Returns a list of metric namespaces for a specified subscription. | +| _MetricNames(aResourceGroup, aNamespace, aResourceName)_ | Returns a list of metric names. | +| _MetricNames(12345678-aaaa-bbbb-cccc-123456789aaa, aResourceGroup, aNamespace, aResourceName)_ | Returns a list of metric names for a specified subscription. | Examples: diff --git a/pkg/tsdb/azuremonitor/azuremonitor-datasource.go b/pkg/tsdb/azuremonitor/azuremonitor-datasource.go index 68c5fd5b7ae..527313b0260 100644 --- a/pkg/tsdb/azuremonitor/azuremonitor-datasource.go +++ b/pkg/tsdb/azuremonitor/azuremonitor-datasource.go @@ -119,6 +119,7 @@ func (e *AzureMonitorDatasource) buildQueries(queries []*tsdb.Query, timeRange * params.Add("interval", timeGrain) params.Add("aggregation", fmt.Sprintf("%v", azureMonitorTarget["aggregation"])) params.Add("metricnames", fmt.Sprintf("%v", azureMonitorTarget["metricName"])) + params.Add("metricnamespace", fmt.Sprintf("%v", azureMonitorTarget["metricNamespace"])) dimension := strings.TrimSpace(fmt.Sprintf("%v", azureMonitorTarget["dimension"])) dimensionFilter := strings.TrimSpace(fmt.Sprintf("%v", azureMonitorTarget["dimensionFilter"])) diff --git a/pkg/tsdb/azuremonitor/azuremonitor-datasource_test.go b/pkg/tsdb/azuremonitor/azuremonitor-datasource_test.go index 51f7b3f3f86..ada82190d11 100644 --- a/pkg/tsdb/azuremonitor/azuremonitor-datasource_test.go +++ b/pkg/tsdb/azuremonitor/azuremonitor-datasource_test.go @@ -41,6 +41,7 @@ func TestAzureMonitorDatasource(t *testing.T) { "resourceGroup": "grafanastaging", "resourceName": "grafana", "metricDefinition": "Microsoft.Compute/virtualMachines", + "metricNamespace": "Microsoft.Compute-virtualMachines", "metricName": "Percentage CPU", "alias": "testalias", "queryType": "Azure Monitor", @@ -57,8 +58,8 @@ func TestAzureMonitorDatasource(t *testing.T) { So(len(queries), ShouldEqual, 1) So(queries[0].RefID, ShouldEqual, "A") So(queries[0].URL, ShouldEqual, "12345678-aaaa-bbbb-cccc-123456789abc/resourceGroups/grafanastaging/providers/Microsoft.Compute/virtualMachines/grafana/providers/microsoft.insights/metrics") - So(queries[0].Target, ShouldEqual, "aggregation=Average&api-version=2018-01-01&interval=PT1M&metricnames=Percentage+CPU×pan=2018-03-15T13%3A00%3A00Z%2F2018-03-15T13%3A34%3A00Z") - So(len(queries[0].Params), ShouldEqual, 5) + So(queries[0].Target, ShouldEqual, "aggregation=Average&api-version=2018-01-01&interval=PT1M&metricnames=Percentage+CPU&metricnamespace=Microsoft.Compute-virtualMachines×pan=2018-03-15T13%3A00%3A00Z%2F2018-03-15T13%3A34%3A00Z") + So(len(queries[0].Params), ShouldEqual, 6) So(queries[0].Params["timespan"][0], ShouldEqual, "2018-03-15T13:00:00Z/2018-03-15T13:34:00Z") So(queries[0].Params["api-version"][0], ShouldEqual, "2018-01-01") So(queries[0].Params["aggregation"][0], ShouldEqual, "Average") @@ -75,6 +76,7 @@ func TestAzureMonitorDatasource(t *testing.T) { "resourceGroup": "grafanastaging", "resourceName": "grafana", "metricDefinition": "Microsoft.Compute/virtualMachines", + "metricNamespace": "Microsoft.Compute-virtualMachines", "metricName": "Percentage CPU", "alias": "testalias", "queryType": "Azure Monitor", @@ -96,6 +98,7 @@ func TestAzureMonitorDatasource(t *testing.T) { "resourceGroup": "grafanastaging", "resourceName": "grafana", "metricDefinition": "Microsoft.Compute/virtualMachines", + "metricNamespace": "Microsoft.Compute-virtualMachines", "metricName": "Percentage CPU", "alias": "testalias", "queryType": "Azure Monitor", @@ -118,6 +121,7 @@ func TestAzureMonitorDatasource(t *testing.T) { "resourceGroup": "grafanastaging", "resourceName": "grafana", "metricDefinition": "Microsoft.Compute/virtualMachines", + "metricNamespace": "Microsoft.Compute-virtualMachines", "metricName": "Percentage CPU", "alias": "testalias", "queryType": "Azure Monitor", @@ -129,7 +133,7 @@ func TestAzureMonitorDatasource(t *testing.T) { queries, err := datasource.buildQueries(tsdbQuery.Queries, tsdbQuery.TimeRange) So(err, ShouldBeNil) - So(queries[0].Target, ShouldEqual, "%24filter=blob+eq+%27%2A%27&aggregation=Average&api-version=2018-01-01&interval=PT1M&metricnames=Percentage+CPU×pan=2018-03-15T13%3A00%3A00Z%2F2018-03-15T13%3A34%3A00Z") + So(queries[0].Target, ShouldEqual, "%24filter=blob+eq+%27%2A%27&aggregation=Average&api-version=2018-01-01&interval=PT1M&metricnames=Percentage+CPU&metricnamespace=Microsoft.Compute-virtualMachines×pan=2018-03-15T13%3A00%3A00Z%2F2018-03-15T13%3A34%3A00Z") }) @@ -141,6 +145,7 @@ func TestAzureMonitorDatasource(t *testing.T) { "resourceGroup": "grafanastaging", "resourceName": "grafana", "metricDefinition": "Microsoft.Compute/virtualMachines", + "metricNamespace": "Microsoft.Compute-virtualMachines", "metricName": "Percentage CPU", "alias": "testalias", "queryType": "Azure Monitor", @@ -152,7 +157,7 @@ func TestAzureMonitorDatasource(t *testing.T) { queries, err := datasource.buildQueries(tsdbQuery.Queries, tsdbQuery.TimeRange) So(err, ShouldBeNil) - So(queries[0].Target, ShouldEqual, "aggregation=Average&api-version=2018-01-01&interval=PT1M&metricnames=Percentage+CPU×pan=2018-03-15T13%3A00%3A00Z%2F2018-03-15T13%3A34%3A00Z") + So(queries[0].Target, ShouldEqual, "aggregation=Average&api-version=2018-01-01&interval=PT1M&metricnames=Percentage+CPU&metricnamespace=Microsoft.Compute-virtualMachines×pan=2018-03-15T13%3A00%3A00Z%2F2018-03-15T13%3A34%3A00Z") }) }) diff --git a/public/app/plugins/datasource/grafana-azure-monitor-datasource/azure_monitor/azure_monitor_datasource.test.ts b/public/app/plugins/datasource/grafana-azure-monitor-datasource/azure_monitor/azure_monitor_datasource.test.ts index 96967e674c6..2f2d4b2d5ed 100644 --- a/public/app/plugins/datasource/grafana-azure-monitor-datasource/azure_monitor/azure_monitor_datasource.test.ts +++ b/public/app/plugins/datasource/grafana-azure-monitor-datasource/azure_monitor/azure_monitor_datasource.test.ts @@ -92,6 +92,7 @@ describe('AzureMonitorDatasource', () => { resourceGroup: 'testRG', resourceName: 'testRN', metricDefinition: 'Microsoft.Compute/virtualMachines', + metricNamespace: 'default', metricName: 'Percentage CPU', timeGrain: 'PT1H', alias: '{{metric}}', @@ -400,7 +401,7 @@ describe('AzureMonitorDatasource', () => { expect(options.url).toBe( baseUrl + '/nodeapp/providers/microsoft.insights/components/rn/providers/microsoft.insights/' + - 'metricdefinitions?api-version=2018-01-01' + 'metricdefinitions?api-version=2018-01-01&metricnamespace=default' ); return ctx.$q.when(response); }; @@ -408,7 +409,7 @@ describe('AzureMonitorDatasource', () => { it('should return a list of metric names', () => { return ctx.ds - .metricFindQuery('Metricnames(nodeapp, microsoft.insights/components, rn)') + .metricFindQuery('Metricnames(nodeapp, microsoft.insights/components, rn, default)') .then((results: Array<{ text: string; value: string }>) => { expect(results.length).toEqual(2); expect(results[0].text).toEqual('Percentage CPU'); @@ -449,7 +450,7 @@ describe('AzureMonitorDatasource', () => { expect(options.url).toBe( baseUrl + '/nodeapp/providers/microsoft.insights/components/rn/providers/microsoft.insights/' + - 'metricdefinitions?api-version=2018-01-01' + 'metricdefinitions?api-version=2018-01-01&metricnamespace=default' ); return ctx.$q.when(response); }; @@ -458,7 +459,7 @@ describe('AzureMonitorDatasource', () => { it('should return a list of metric names', () => { return ctx.ds .metricFindQuery( - 'Metricnames(11112222-eeee-4949-9b2d-9106972f9123, nodeapp, microsoft.insights/components, rn)' + 'Metricnames(11112222-eeee-4949-9b2d-9106972f9123, nodeapp, microsoft.insights/components, rn, default)' ) .then((results: Array<{ text: string; value: string }>) => { expect(results.length).toEqual(2); @@ -470,6 +471,104 @@ describe('AzureMonitorDatasource', () => { }); }); }); + + describe('with metric namespace query', () => { + const response = { + data: { + value: [ + { + name: 'Microsoft.Compute-virtualMachines', + properties: { + metricNamespaceName: 'Microsoft.Compute/virtualMachines', + }, + }, + { + name: 'Telegraf-mem', + properties: { + metricNamespaceName: 'Telegraf/mem', + }, + }, + ], + }, + status: 200, + statusText: 'OK', + }; + + beforeEach(() => { + ctx.backendSrv.datasourceRequest = (options: { url: string }) => { + const baseUrl = + 'http://azuremonitor.com/azuremonitor/subscriptions/9935389e-9122-4ef9-95f9-1513dd24753f/resourceGroups'; + expect(options.url).toBe( + baseUrl + + '/nodeapp/providers/Microsoft.Compute/virtualMachines/rn/providers/microsoft.insights/metricNamespaces?api-version=2017-12-01-preview' + ); + return ctx.$q.when(response); + }; + }); + + it('should return a list of metric names', () => { + return ctx.ds + .metricFindQuery('Metricnamespace(nodeapp, Microsoft.Compute/virtualMachines, rn)') + .then((results: Array<{ text: string; value: string }>) => { + expect(results.length).toEqual(2); + expect(results[0].text).toEqual('Microsoft.Compute-virtualMachines'); + expect(results[0].value).toEqual('Microsoft.Compute/virtualMachines'); + + expect(results[1].text).toEqual('Telegraf-mem'); + expect(results[1].value).toEqual('Telegraf/mem'); + }); + }); + }); + + describe('with metric namespace query and specifies a subscription id', () => { + const response = { + data: { + value: [ + { + name: 'Microsoft.Compute-virtualMachines', + properties: { + metricNamespaceName: 'Microsoft.Compute/virtualMachines', + }, + }, + { + name: 'Telegraf-mem', + properties: { + metricNamespaceName: 'Telegraf/mem', + }, + }, + ], + }, + status: 200, + statusText: 'OK', + }; + + beforeEach(() => { + ctx.backendSrv.datasourceRequest = (options: { url: string }) => { + const baseUrl = + 'http://azuremonitor.com/azuremonitor/subscriptions/11112222-eeee-4949-9b2d-9106972f9123/resourceGroups'; + expect(options.url).toBe( + baseUrl + + '/nodeapp/providers/Microsoft.Compute/virtualMachines/rn/providers/microsoft.insights/metricNamespaces?api-version=2017-12-01-preview' + ); + return ctx.$q.when(response); + }; + }); + + it('should return a list of metric namespaces', () => { + return ctx.ds + .metricFindQuery( + 'Metricnamespace(11112222-eeee-4949-9b2d-9106972f9123, nodeapp, Microsoft.Compute/virtualMachines, rn)' + ) + .then((results: Array<{ text: string; value: string }>) => { + expect(results.length).toEqual(2); + expect(results[0].text).toEqual('Microsoft.Compute-virtualMachines'); + expect(results[0].value).toEqual('Microsoft.Compute/virtualMachines'); + + expect(results[1].text).toEqual('Telegraf-mem'); + expect(results[1].value).toEqual('Telegraf/mem'); + }); + }); + }); }); describe('When performing getSubscriptions', () => { @@ -733,7 +832,7 @@ describe('AzureMonitorDatasource', () => { const expected = baseUrl + '/providers/microsoft.insights/components/resource1' + - '/providers/microsoft.insights/metricdefinitions?api-version=2018-01-01'; + '/providers/microsoft.insights/metricdefinitions?api-version=2018-01-01&metricnamespace=default'; expect(options.url).toBe(expected); return ctx.$q.when(response); }; @@ -741,7 +840,13 @@ describe('AzureMonitorDatasource', () => { it('should return list of Metric Definitions', () => { return ctx.ds - .getMetricNames('9935389e-9122-4ef9-95f9-1513dd24753f', 'nodeapp', 'microsoft.insights/components', 'resource1') + .getMetricNames( + '9935389e-9122-4ef9-95f9-1513dd24753f', + 'nodeapp', + 'microsoft.insights/components', + 'resource1', + 'default' + ) .then((results: Array<{ text: string; value: string }>) => { expect(results.length).toEqual(2); expect(results[0].text).toEqual('Used capacity'); @@ -799,7 +904,7 @@ describe('AzureMonitorDatasource', () => { const expected = baseUrl + '/providers/microsoft.insights/components/resource1' + - '/providers/microsoft.insights/metricdefinitions?api-version=2018-01-01'; + '/providers/microsoft.insights/metricdefinitions?api-version=2018-01-01&metricnamespace=default'; expect(options.url).toBe(expected); return ctx.$q.when(response); }; @@ -812,6 +917,7 @@ describe('AzureMonitorDatasource', () => { 'nodeapp', 'microsoft.insights/components', 'resource1', + 'default', 'UsedCapacity' ) .then((results: any) => { @@ -872,7 +978,7 @@ describe('AzureMonitorDatasource', () => { const expected = baseUrl + '/providers/microsoft.insights/components/resource1' + - '/providers/microsoft.insights/metricdefinitions?api-version=2018-01-01'; + '/providers/microsoft.insights/metricdefinitions?api-version=2018-01-01&metricnamespace=default'; expect(options.url).toBe(expected); return ctx.$q.when(response); }; @@ -885,6 +991,7 @@ describe('AzureMonitorDatasource', () => { 'nodeapp', 'microsoft.insights/components', 'resource1', + 'default', 'Transactions' ) .then((results: any) => { @@ -903,6 +1010,7 @@ describe('AzureMonitorDatasource', () => { 'nodeapp', 'microsoft.insights/components', 'resource1', + 'default', 'FreeCapacity' ) .then((results: any) => { diff --git a/public/app/plugins/datasource/grafana-azure-monitor-datasource/azure_monitor/azure_monitor_datasource.ts b/public/app/plugins/datasource/grafana-azure-monitor-datasource/azure_monitor/azure_monitor_datasource.ts index a17dc1cf6a3..09a2a2f4617 100644 --- a/public/app/plugins/datasource/grafana-azure-monitor-datasource/azure_monitor/azure_monitor_datasource.ts +++ b/public/app/plugins/datasource/grafana-azure-monitor-datasource/azure_monitor/azure_monitor_datasource.ts @@ -17,6 +17,7 @@ import { TemplateSrv } from 'app/features/templating/template_srv'; export default class AzureMonitorDatasource { apiVersion = '2018-01-01'; + apiPreviewVersion = '2017-12-01-preview'; id: number; subscriptionId: string; baseUrl: string; @@ -70,6 +71,7 @@ export default class AzureMonitorDatasource { const subscriptionId = this.templateSrv.replace(target.subscription || this.subscriptionId, options.scopedVars); const resourceGroup = this.templateSrv.replace(item.resourceGroup, options.scopedVars); const resourceName = this.templateSrv.replace(item.resourceName, options.scopedVars); + const metricNamespace = this.templateSrv.replace(item.metricNamespace, options.scopedVars); const metricDefinition = this.templateSrv.replace(item.metricDefinition, options.scopedVars); const timeGrain = this.templateSrv.replace((item.timeGrain || '').toString(), options.scopedVars); const aggregation = this.templateSrv.replace(item.aggregation, options.scopedVars); @@ -89,6 +91,8 @@ export default class AzureMonitorDatasource { timeGrain: timeGrain, allowedTimeGrainsMs: item.allowedTimeGrainsMs, metricName: this.templateSrv.replace(item.metricName, options.scopedVars), + metricNamespace: + metricNamespace && metricNamespace !== this.defaultDropdownValue ? metricNamespace : metricDefinition, aggregation: aggregation, dimension: this.templateSrv.replace(item.dimension, options.scopedVars), dimensionFilter: this.templateSrv.replace(item.dimensionFilter, options.scopedVars), @@ -182,25 +186,48 @@ export default class AzureMonitorDatasource { return this.getResourceNames(subscription, resourceGroup, metricDefinition); } - const metricNamesQuery = query.match(/^MetricNames\(([^,]+?),\s?([^,]+?),\s?(.+?)\)/i); + const metricNamespaceQuery = query.match(/^MetricNamespace\(([^,]+?),\s?([^,]+?),\s?([^,]+?)\)/i); + if (metricNamespaceQuery) { + const resourceGroup = this.toVariable(metricNamespaceQuery[1]); + const metricDefinition = this.toVariable(metricNamespaceQuery[2]); + const resourceName = this.toVariable(metricNamespaceQuery[3]); + return this.getMetricNamespaces(this.subscriptionId, resourceGroup, metricDefinition, resourceName); + } + const metricNamespaceQueryWithSub = query.match( + /^metricnamespace\(([^,]+?),\s?([^,]+?),\s?([^,]+?),\s?([^,]+?)\)/i + ); + if (metricNamespaceQueryWithSub) { + const subscription = this.toVariable(metricNamespaceQueryWithSub[1]); + const resourceGroup = this.toVariable(metricNamespaceQueryWithSub[2]); + const metricDefinition = this.toVariable(metricNamespaceQueryWithSub[3]); + const resourceName = this.toVariable(metricNamespaceQueryWithSub[4]); + console.log(metricNamespaceQueryWithSub); + return this.getMetricNamespaces(subscription, resourceGroup, metricDefinition, resourceName); + } + + const metricNamesQuery = query.match(/^MetricNames\(([^,]+?),\s?([^,]+?),\s?([^,]+?),\s?([^,]+?)\)/i); if (metricNamesQuery) { if (metricNamesQuery[3].indexOf(',') === -1) { const resourceGroup = this.toVariable(metricNamesQuery[1]); const metricDefinition = this.toVariable(metricNamesQuery[2]); const resourceName = this.toVariable(metricNamesQuery[3]); - return this.getMetricNames(this.subscriptionId, resourceGroup, metricDefinition, resourceName); + const metricNamespace = this.toVariable(metricNamesQuery[4]); + return this.getMetricNames(this.subscriptionId, resourceGroup, metricDefinition, resourceName, metricNamespace); } } - const metricNamesQueryWithSub = query.match(/^MetricNames\(([^,]+?),\s?([^,]+?),\s?([^,]+?),\s?(.+?)\)/i); + const metricNamesQueryWithSub = query.match( + /^MetricNames\(([^,]+?),\s?([^,]+?),\s?([^,]+?),\s?([^,]+?),\s?(.+?)\)/i + ); if (metricNamesQueryWithSub) { const subscription = this.toVariable(metricNamesQueryWithSub[1]); const resourceGroup = this.toVariable(metricNamesQueryWithSub[2]); const metricDefinition = this.toVariable(metricNamesQueryWithSub[3]); const resourceName = this.toVariable(metricNamesQueryWithSub[4]); - return this.getMetricNames(subscription, resourceGroup, metricDefinition, resourceName); + const metricNamespace = this.toVariable(metricNamesQueryWithSub[5]); + return this.getMetricNames(subscription, resourceGroup, metricDefinition, resourceName, metricNamespace); } return undefined; @@ -295,13 +322,35 @@ export default class AzureMonitorDatasource { }); } - getMetricNames(subscriptionId: string, resourceGroup: string, metricDefinition: string, resourceName: string) { + getMetricNamespaces(subscriptionId: string, resourceGroup: string, metricDefinition: string, resourceName: string) { + const url = UrlBuilder.buildAzureMonitorGetMetricNamespacesUrl( + this.baseUrl, + subscriptionId, + resourceGroup, + metricDefinition, + resourceName, + this.apiPreviewVersion + ); + + return this.doRequest(url).then(result => { + return ResponseParser.parseResponseValues(result, 'name', 'properties.metricNamespaceName'); + }); + } + + getMetricNames( + subscriptionId: string, + resourceGroup: string, + metricDefinition: string, + resourceName: string, + metricNamespace: string + ) { const url = UrlBuilder.buildAzureMonitorGetMetricNamesUrl( this.baseUrl, subscriptionId, resourceGroup, metricDefinition, resourceName, + metricNamespace, this.apiVersion ); @@ -315,6 +364,7 @@ export default class AzureMonitorDatasource { resourceGroup: string, metricDefinition: string, resourceName: string, + metricNamespace: string, metricName: string ) { const url = UrlBuilder.buildAzureMonitorGetMetricNamesUrl( @@ -323,6 +373,7 @@ export default class AzureMonitorDatasource { resourceGroup, metricDefinition, resourceName, + metricNamespace, this.apiVersion ); diff --git a/public/app/plugins/datasource/grafana-azure-monitor-datasource/azure_monitor/response_parser.ts b/public/app/plugins/datasource/grafana-azure-monitor-datasource/azure_monitor/response_parser.ts index bff5c1c45ec..d67b1d74c1b 100644 --- a/public/app/plugins/datasource/grafana-azure-monitor-datasource/azure_monitor/response_parser.ts +++ b/public/app/plugins/datasource/grafana-azure-monitor-datasource/azure_monitor/response_parser.ts @@ -14,9 +14,12 @@ export default class ResponseParser { for (let i = 0; i < result.data.value.length; i++) { if (!_.find(list, ['value', _.get(result.data.value[i], valueFieldName)])) { + const value = _.get(result.data.value[i], valueFieldName); + const text = _.get(result.data.value[i], textFieldName, value); + list.push({ - text: _.get(result.data.value[i], textFieldName), - value: _.get(result.data.value[i], valueFieldName), + text: text, + value: value, }); } } @@ -94,9 +97,12 @@ export default class ResponseParser { } for (let i = 0; i < metricData.dimensions.length; i++) { + const text = metricData.dimensions[i].localizedValue; + const value = metricData.dimensions[i].value; + dimensions.push({ - text: metricData.dimensions[i].localizedValue, - value: metricData.dimensions[i].value, + text: !text ? value : text, + value: value, }); } return dimensions; diff --git a/public/app/plugins/datasource/grafana-azure-monitor-datasource/azure_monitor/supported_namespaces.ts b/public/app/plugins/datasource/grafana-azure-monitor-datasource/azure_monitor/supported_namespaces.ts index 12149778223..328843f042d 100644 --- a/public/app/plugins/datasource/grafana-azure-monitor-datasource/azure_monitor/supported_namespaces.ts +++ b/public/app/plugins/datasource/grafana-azure-monitor-datasource/azure_monitor/supported_namespaces.ts @@ -55,6 +55,7 @@ export default class SupportedNamespaces { 'Microsoft.Network/networkWatchers/connectionMonitors', 'Microsoft.Network/frontdoors', 'Microsoft.NotificationHubs/namespaces/notificationHubs', + 'Microsoft.OperationalInsights/workspaces', 'Microsoft.PowerBIDedicated/capacities', 'Microsoft.Relay/namespaces', 'Microsoft.Search/searchServices', @@ -163,6 +164,7 @@ export default class SupportedNamespaces { 'Microsoft.Network/networkWatchers/connectionMonitors', 'Microsoft.Network/frontdoors', 'Microsoft.NotificationHubs/namespaces/notificationHubs', + 'Microsoft.OperationalInsights/workspaces', 'Microsoft.PowerBIDedicated/capacities', 'Microsoft.Relay/namespaces', 'Microsoft.ServiceBus/namespaces', diff --git a/public/app/plugins/datasource/grafana-azure-monitor-datasource/azure_monitor/url_builder.test.ts b/public/app/plugins/datasource/grafana-azure-monitor-datasource/azure_monitor/url_builder.test.ts index 59099cf050d..0762266dc32 100644 --- a/public/app/plugins/datasource/grafana-azure-monitor-datasource/azure_monitor/url_builder.test.ts +++ b/public/app/plugins/datasource/grafana-azure-monitor-datasource/azure_monitor/url_builder.test.ts @@ -9,11 +9,12 @@ describe('AzureMonitorUrlBuilder', () => { 'rg', 'Microsoft.Sql/servers/databases', 'rn1/rn2', + 'default', '2017-05-01-preview' ); expect(url).toBe( '/sub1/resourceGroups/rg/providers/Microsoft.Sql/servers/rn1/databases/rn2/' + - 'providers/microsoft.insights/metricdefinitions?api-version=2017-05-01-preview' + 'providers/microsoft.insights/metricdefinitions?api-version=2017-05-01-preview&metricnamespace=default' ); }); }); @@ -26,11 +27,12 @@ describe('AzureMonitorUrlBuilder', () => { 'rg', 'Microsoft.Sql/servers', 'rn', + 'default', '2017-05-01-preview' ); expect(url).toBe( '/sub1/resourceGroups/rg/providers/Microsoft.Sql/servers/rn/' + - 'providers/microsoft.insights/metricdefinitions?api-version=2017-05-01-preview' + 'providers/microsoft.insights/metricdefinitions?api-version=2017-05-01-preview&metricnamespace=default' ); }); }); @@ -43,11 +45,12 @@ describe('AzureMonitorUrlBuilder', () => { 'rg', 'Microsoft.Storage/storageAccounts/blobServices', 'rn1/default', + 'default', '2017-05-01-preview' ); expect(url).toBe( '/sub1/resourceGroups/rg/providers/Microsoft.Storage/storageAccounts/rn1/blobServices/default/' + - 'providers/microsoft.insights/metricdefinitions?api-version=2017-05-01-preview' + 'providers/microsoft.insights/metricdefinitions?api-version=2017-05-01-preview&metricnamespace=default' ); }); }); @@ -60,11 +63,12 @@ describe('AzureMonitorUrlBuilder', () => { 'rg', 'Microsoft.Storage/storageAccounts/fileServices', 'rn1/default', + 'default', '2017-05-01-preview' ); expect(url).toBe( '/sub1/resourceGroups/rg/providers/Microsoft.Storage/storageAccounts/rn1/fileServices/default/' + - 'providers/microsoft.insights/metricdefinitions?api-version=2017-05-01-preview' + 'providers/microsoft.insights/metricdefinitions?api-version=2017-05-01-preview&metricnamespace=default' ); }); }); @@ -77,11 +81,12 @@ describe('AzureMonitorUrlBuilder', () => { 'rg', 'Microsoft.Storage/storageAccounts/tableServices', 'rn1/default', + 'default', '2017-05-01-preview' ); expect(url).toBe( '/sub1/resourceGroups/rg/providers/Microsoft.Storage/storageAccounts/rn1/tableServices/default/' + - 'providers/microsoft.insights/metricdefinitions?api-version=2017-05-01-preview' + 'providers/microsoft.insights/metricdefinitions?api-version=2017-05-01-preview&metricnamespace=default' ); }); }); @@ -94,11 +99,12 @@ describe('AzureMonitorUrlBuilder', () => { 'rg', 'Microsoft.Storage/storageAccounts/queueServices', 'rn1/default', + 'default', '2017-05-01-preview' ); expect(url).toBe( '/sub1/resourceGroups/rg/providers/Microsoft.Storage/storageAccounts/rn1/queueServices/default/' + - 'providers/microsoft.insights/metricdefinitions?api-version=2017-05-01-preview' + 'providers/microsoft.insights/metricdefinitions?api-version=2017-05-01-preview&metricnamespace=default' ); }); }); diff --git a/public/app/plugins/datasource/grafana-azure-monitor-datasource/azure_monitor/url_builder.ts b/public/app/plugins/datasource/grafana-azure-monitor-datasource/azure_monitor/url_builder.ts index 278a12ea98f..e8cb3afcc1c 100644 --- a/public/app/plugins/datasource/grafana-azure-monitor-datasource/azure_monitor/url_builder.ts +++ b/public/app/plugins/datasource/grafana-azure-monitor-datasource/azure_monitor/url_builder.ts @@ -1,5 +1,5 @@ export default class UrlBuilder { - static buildAzureMonitorGetMetricNamesUrl( + static buildAzureMonitorGetMetricNamespacesUrl( baseUrl: string, subscriptionId: string, resourceGroup: string, @@ -13,13 +13,42 @@ export default class UrlBuilder { const md = metricDefinition.substring(0, metricDefinition.lastIndexOf('/')); return ( `${baseUrl}/${subscriptionId}/resourceGroups/${resourceGroup}/providers/${md}/${rn[0]}/${service}/${rn[1]}` + - `/providers/microsoft.insights/metricdefinitions?api-version=${apiVersion}` + `/providers/microsoft.insights/metricNamespaces?api-version=${apiVersion}` ); } return ( `${baseUrl}/${subscriptionId}/resourceGroups/${resourceGroup}/providers/${metricDefinition}/${resourceName}` + - `/providers/microsoft.insights/metricdefinitions?api-version=${apiVersion}` + `/providers/microsoft.insights/metricNamespaces?api-version=${apiVersion}` + ); + } + + static buildAzureMonitorGetMetricNamesUrl( + baseUrl: string, + subscriptionId: string, + resourceGroup: string, + metricDefinition: string, + resourceName: string, + metricNamespace: string, + apiVersion: string + ) { + if ((metricDefinition.match(/\//g) || []).length > 1) { + const rn = resourceName.split('/'); + const service = metricDefinition.substring(metricDefinition.lastIndexOf('/') + 1); + const md = metricDefinition.substring(0, metricDefinition.lastIndexOf('/')); + return ( + `${baseUrl}/${subscriptionId}/resourceGroups/${resourceGroup}/providers/${md}/${rn[0]}/${service}/${rn[1]}` + + `/providers/microsoft.insights/metricdefinitions?api-version=${apiVersion}&metricnamespace=${encodeURIComponent( + metricNamespace + )}` + ); + } + + return ( + `${baseUrl}/${subscriptionId}/resourceGroups/${resourceGroup}/providers/${metricDefinition}/${resourceName}` + + `/providers/microsoft.insights/metricdefinitions?api-version=${apiVersion}&metricnamespace=${encodeURIComponent( + metricNamespace + )}` ); } } diff --git a/public/app/plugins/datasource/grafana-azure-monitor-datasource/datasource.ts b/public/app/plugins/datasource/grafana-azure-monitor-datasource/datasource.ts index a2c47d7c56d..094bc3bbe09 100644 --- a/public/app/plugins/datasource/grafana-azure-monitor-datasource/datasource.ts +++ b/public/app/plugins/datasource/grafana-azure-monitor-datasource/datasource.ts @@ -159,8 +159,29 @@ export default class Datasource extends DataSourceApi
+ + + +
+
@@ -54,8 +60,9 @@
-
+ +
diff --git a/public/app/plugins/datasource/grafana-azure-monitor-datasource/query_ctrl.test.ts b/public/app/plugins/datasource/grafana-azure-monitor-datasource/query_ctrl.test.ts index 1f39a72291c..9c83b5d9312 100644 --- a/public/app/plugins/datasource/grafana-azure-monitor-datasource/query_ctrl.test.ts +++ b/public/app/plugins/datasource/grafana-azure-monitor-datasource/query_ctrl.test.ts @@ -39,6 +39,7 @@ describe('AzureMonitorQueryCtrl', () => { expect(queryCtrl.target.azureMonitor.resourceGroup).toBe('select'); expect(queryCtrl.target.azureMonitor.metricDefinition).toBe('select'); expect(queryCtrl.target.azureMonitor.resourceName).toBe('select'); + expect(queryCtrl.target.azureMonitor.metricNamespace).toBe('select'); expect(queryCtrl.target.azureMonitor.metricName).toBe('select'); expect(queryCtrl.target.appInsights.groupBy).toBe('none'); }); @@ -143,7 +144,7 @@ describe('AzureMonitorQueryCtrl', () => { }); describe('when getOptions for the Metric Names dropdown is called', () => { - describe('and resourceGroup, metricDefinition and resourceName have values', () => { + describe('and resourceGroup, metricDefinition, resourceName and metricNamespace have values', () => { const response = [{ text: 'metric1', value: 'metric1' }, { text: 'metric2', value: 'metric2' }]; beforeEach(() => { @@ -151,16 +152,19 @@ describe('AzureMonitorQueryCtrl', () => { queryCtrl.target.azureMonitor.resourceGroup = 'test'; queryCtrl.target.azureMonitor.metricDefinition = 'Microsoft.Compute/virtualMachines'; queryCtrl.target.azureMonitor.resourceName = 'test'; + queryCtrl.target.azureMonitor.metricNamespace = 'test'; queryCtrl.datasource.getMetricNames = function( subscriptionId: any, resourceGroup: any, metricDefinition: any, - resourceName: any + resourceName: any, + metricNamespace: any ) { expect(subscriptionId).toBe('sub1'); expect(resourceGroup).toBe('test'); expect(metricDefinition).toBe('Microsoft.Compute/virtualMachines'); expect(resourceName).toBe('test'); + expect(metricNamespace).toBe('test'); return this.$q.when(response); }; }); @@ -173,11 +177,12 @@ describe('AzureMonitorQueryCtrl', () => { }); }); - describe('and resourceGroup, metricDefinition and resourceName do not have values', () => { + describe('and resourceGroup, metricDefinition, resourceName and metricNamespace do not have values', () => { beforeEach(() => { queryCtrl.target.azureMonitor.resourceGroup = 'select'; queryCtrl.target.azureMonitor.metricDefinition = 'select'; queryCtrl.target.azureMonitor.resourceName = 'select'; + queryCtrl.target.azureMonitor.metricNamespace = 'select'; }); it('should return without making a call to datasource', () => { @@ -199,18 +204,21 @@ describe('AzureMonitorQueryCtrl', () => { queryCtrl.target.azureMonitor.resourceGroup = 'test'; queryCtrl.target.azureMonitor.metricDefinition = 'Microsoft.Compute/virtualMachines'; queryCtrl.target.azureMonitor.resourceName = 'test'; + queryCtrl.target.azureMonitor.metricNamespace = 'test'; queryCtrl.target.azureMonitor.metricName = 'Percentage CPU'; queryCtrl.datasource.getMetricMetadata = function( subscription: any, resourceGroup: any, metricDefinition: any, resourceName: any, + metricNamespace: any, metricName: any ) { expect(subscription).toBe('sub1'); expect(resourceGroup).toBe('test'); expect(metricDefinition).toBe('Microsoft.Compute/virtualMachines'); expect(resourceName).toBe('test'); + expect(metricNamespace).toBe('test'); expect(metricName).toBe('Percentage CPU'); return this.$q.when(response); }; diff --git a/public/app/plugins/datasource/grafana-azure-monitor-datasource/query_ctrl.ts b/public/app/plugins/datasource/grafana-azure-monitor-datasource/query_ctrl.ts index 55d5db938ac..0e799d27518 100644 --- a/public/app/plugins/datasource/grafana-azure-monitor-datasource/query_ctrl.ts +++ b/public/app/plugins/datasource/grafana-azure-monitor-datasource/query_ctrl.ts @@ -27,6 +27,7 @@ export class AzureMonitorQueryCtrl extends QueryCtrl { resourceGroup: string; resourceName: string; metricDefinition: string; + metricNamespace: string; metricName: string; dimensionFilter: string; timeGrain: string; @@ -66,6 +67,7 @@ export class AzureMonitorQueryCtrl extends QueryCtrl { resourceGroup: this.defaultDropdownValue, metricDefinition: this.defaultDropdownValue, resourceName: this.defaultDropdownValue, + metricNamespace: this.defaultDropdownValue, metricName: this.defaultDropdownValue, dimensionFilter: '*', timeGrain: 'auto', @@ -118,6 +120,8 @@ export class AzureMonitorQueryCtrl extends QueryCtrl { this.migrateToFromTimes(); + this.migrateToDefaultNamespace(); + this.panelCtrl.events.on('data-received', this.onDataReceived.bind(this), $scope); this.panelCtrl.events.on('data-error', this.onDataError.bind(this), $scope); this.resultFormats = [{ text: 'Time series', value: 'time_series' }, { text: 'Table', value: 'table' }]; @@ -192,6 +196,18 @@ export class AzureMonitorQueryCtrl extends QueryCtrl { this.target.azureLogAnalytics.query = this.target.azureLogAnalytics.query.replace(/\$__to\s/gi, '$__timeTo() '); } + async migrateToDefaultNamespace() { + if ( + this.target.azureMonitor.metricNamespace && + this.target.azureMonitor.metricNamespace !== this.defaultDropdownValue && + this.target.azureMonitor.metricDefinition + ) { + return; + } + + this.target.azureMonitor.metricNamespace = this.target.azureMonitor.metricDefinition; + } + replace(variable: string) { return this.templateSrv.replace(variable, this.panelCtrl.panel.scopedVars); } @@ -290,7 +306,7 @@ export class AzureMonitorQueryCtrl extends QueryCtrl { .catch(this.handleQueryCtrlError.bind(this)); } - getMetricNames(query: any) { + getMetricNamespaces() { if ( this.target.queryType !== 'Azure Monitor' || !this.target.azureMonitor.resourceGroup || @@ -304,7 +320,7 @@ export class AzureMonitorQueryCtrl extends QueryCtrl { } return this.datasource - .getMetricNames( + .getMetricNamespaces( this.replace(this.target.subscription || this.datasource.azureMonitorDatasource.subscriptionId), this.replace(this.target.azureMonitor.resourceGroup), this.replace(this.target.azureMonitor.metricDefinition), @@ -313,9 +329,36 @@ export class AzureMonitorQueryCtrl extends QueryCtrl { .catch(this.handleQueryCtrlError.bind(this)); } + getMetricNames() { + if ( + this.target.queryType !== 'Azure Monitor' || + !this.target.azureMonitor.resourceGroup || + this.target.azureMonitor.resourceGroup === this.defaultDropdownValue || + !this.target.azureMonitor.metricDefinition || + this.target.azureMonitor.metricDefinition === this.defaultDropdownValue || + !this.target.azureMonitor.resourceName || + this.target.azureMonitor.resourceName === this.defaultDropdownValue || + !this.target.azureMonitor.metricNamespace || + this.target.azureMonitor.metricNamespace === this.defaultDropdownValue + ) { + return; + } + + return this.datasource + .getMetricNames( + this.replace(this.target.subscription || this.datasource.azureMonitorDatasource.subscriptionId), + this.replace(this.target.azureMonitor.resourceGroup), + this.replace(this.target.azureMonitor.metricDefinition), + this.replace(this.target.azureMonitor.resourceName), + this.replace(this.target.azureMonitor.metricNamespace) + ) + .catch(this.handleQueryCtrlError.bind(this)); + } + onResourceGroupChange() { this.target.azureMonitor.metricDefinition = this.defaultDropdownValue; this.target.azureMonitor.resourceName = this.defaultDropdownValue; + this.target.azureMonitor.metricNamespace = this.defaultDropdownValue; this.target.azureMonitor.metricName = this.defaultDropdownValue; this.target.azureMonitor.aggregation = ''; this.target.azureMonitor.timeGrains = []; @@ -327,6 +370,7 @@ export class AzureMonitorQueryCtrl extends QueryCtrl { onMetricDefinitionChange() { this.target.azureMonitor.resourceName = this.defaultDropdownValue; + this.target.azureMonitor.metricNamespace = this.defaultDropdownValue; this.target.azureMonitor.metricName = this.defaultDropdownValue; this.target.azureMonitor.aggregation = ''; this.target.azureMonitor.timeGrains = []; @@ -336,6 +380,7 @@ export class AzureMonitorQueryCtrl extends QueryCtrl { } onResourceNameChange() { + this.target.azureMonitor.metricNamespace = this.defaultDropdownValue; this.target.azureMonitor.metricName = this.defaultDropdownValue; this.target.azureMonitor.aggregation = ''; this.target.azureMonitor.timeGrains = []; @@ -345,6 +390,12 @@ export class AzureMonitorQueryCtrl extends QueryCtrl { this.refresh(); } + onMetricNamespacesChange() { + this.target.azureMonitor.metricName = this.defaultDropdownValue; + this.target.azureMonitor.dimensions = []; + this.target.azureMonitor.dimension = ''; + } + onMetricNameChange() { if (!this.target.azureMonitor.metricName || this.target.azureMonitor.metricName === this.defaultDropdownValue) { return; @@ -356,6 +407,7 @@ export class AzureMonitorQueryCtrl extends QueryCtrl { this.replace(this.target.azureMonitor.resourceGroup), this.replace(this.target.azureMonitor.metricDefinition), this.replace(this.target.azureMonitor.resourceName), + this.replace(this.target.azureMonitor.metricNamespace), this.replace(this.target.azureMonitor.metricName) ) .then((metadata: any) => { diff --git a/public/app/plugins/datasource/grafana-azure-monitor-datasource/types.ts b/public/app/plugins/datasource/grafana-azure-monitor-datasource/types.ts index 5db97068d86..7be4bad2c86 100644 --- a/public/app/plugins/datasource/grafana-azure-monitor-datasource/types.ts +++ b/public/app/plugins/datasource/grafana-azure-monitor-datasource/types.ts @@ -31,6 +31,7 @@ export interface AzureMetricQuery { resourceGroup: string; resourceName: string; metricDefinition: string; + metricNamespace: string; metricName: string; timeGrainUnit: string; timeGrain: string;