瀏覽代碼

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
Raphael Couto 6 年之前
父節點
當前提交
bc0da1bbfc

+ 13 - 11
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:
 

+ 1 - 0
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"]))

+ 9 - 4
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&timespan=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&timespan=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&timespan=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&timespan=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&timespan=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&timespan=2018-03-15T13%3A00%3A00Z%2F2018-03-15T13%3A34%3A00Z")
 
 			})
 		})

+ 116 - 8
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) => {

+ 56 - 5
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
     );
 

+ 10 - 4
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;

+ 2 - 0
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',

+ 12 - 6
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'
       );
     });
   });

+ 31 - 2
public/app/plugins/datasource/grafana-azure-monitor-datasource/azure_monitor/url_builder.ts

@@ -1,10 +1,35 @@
 export default class UrlBuilder {
+  static buildAzureMonitorGetMetricNamespacesUrl(
+    baseUrl: string,
+    subscriptionId: string,
+    resourceGroup: string,
+    metricDefinition: string,
+    resourceName: 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/metricNamespaces?api-version=${apiVersion}`
+      );
+    }
+
+    return (
+      `${baseUrl}/${subscriptionId}/resourceGroups/${resourceGroup}/providers/${metricDefinition}/${resourceName}` +
+      `/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) {
@@ -13,13 +38,17 @@ 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/metricdefinitions?api-version=${apiVersion}&metricnamespace=${encodeURIComponent(
+          metricNamespace
+        )}`
       );
     }
 
     return (
       `${baseUrl}/${subscriptionId}/resourceGroups/${resourceGroup}/providers/${metricDefinition}/${resourceName}` +
-      `/providers/microsoft.insights/metricdefinitions?api-version=${apiVersion}`
+      `/providers/microsoft.insights/metricdefinitions?api-version=${apiVersion}&metricnamespace=${encodeURIComponent(
+        metricNamespace
+      )}`
     );
   }
 }

+ 25 - 2
public/app/plugins/datasource/grafana-azure-monitor-datasource/datasource.ts

@@ -159,8 +159,29 @@ export default class Datasource extends DataSourceApi<AzureMonitorQuery, AzureDa
     return this.azureMonitorDatasource.getResourceNames(subscriptionId, resourceGroup, metricDefinition);
   }
 
-  getMetricNames(subscriptionId: string, resourceGroup: string, metricDefinition: string, resourceName: string) {
-    return this.azureMonitorDatasource.getMetricNames(subscriptionId, resourceGroup, metricDefinition, resourceName);
+  getMetricNames(
+    subscriptionId: string,
+    resourceGroup: string,
+    metricDefinition: string,
+    resourceName: string,
+    metricNamespace: string
+  ) {
+    return this.azureMonitorDatasource.getMetricNames(
+      subscriptionId,
+      resourceGroup,
+      metricDefinition,
+      resourceName,
+      metricNamespace
+    );
+  }
+
+  getMetricNamespaces(subscriptionId: string, resourceGroup: string, metricDefinition: string, resourceName: string) {
+    return this.azureMonitorDatasource.getMetricNamespaces(
+      subscriptionId,
+      resourceGroup,
+      metricDefinition,
+      resourceName
+    );
   }
 
   getMetricMetadata(
@@ -168,6 +189,7 @@ export default class Datasource extends DataSourceApi<AzureMonitorQuery, AzureDa
     resourceGroup: string,
     metricDefinition: string,
     resourceName: string,
+    metricNamespace: string,
     metricName: string
   ) {
     return this.azureMonitorDatasource.getMetricMetadata(
@@ -175,6 +197,7 @@ export default class Datasource extends DataSourceApi<AzureMonitorQuery, AzureDa
       resourceGroup,
       metricDefinition,
       resourceName,
+      metricNamespace,
       metricName
     );
   }

+ 8 - 1
public/app/plugins/datasource/grafana-azure-monitor-datasource/partials/query.editor.html

@@ -43,6 +43,12 @@
     </div>
     <div class="gf-form-inline">
       <div class="gf-form">
+        <label class="gf-form-label query-keyword width-9">Metric Namespace</label>
+        <gf-form-dropdown model="ctrl.target.azureMonitor.metricNamespace" allow-custom="true" lookup-text="true"
+          get-options="ctrl.getMetricNamespaces($query)" on-change="ctrl.onMetricNamespacesChange()" css-class="min-width-12">
+        </gf-form-dropdown>
+      </div>
+      <div class="gf-form">        
         <label class="gf-form-label query-keyword width-9">Metric</label>
         <gf-form-dropdown model="ctrl.target.azureMonitor.metricName" allow-custom="true" lookup-text="true"
           get-options="ctrl.getMetricNames($query)" on-change="ctrl.onMetricNameChange()" css-class="min-width-12">
@@ -54,8 +60,9 @@
           <select class="gf-form-input width-11" ng-model="ctrl.target.azureMonitor.aggregation" ng-options="f as f for f in ctrl.target.azureMonitor.aggOptions"
             ng-change="ctrl.refresh()"></select>
         </div>
-        <div class="gf-form-label gf-form-label--grow"></div>
       </div>
+    </div>
+    <div class="gf-form-inline">      
       <div class="gf-form">
         <label class="gf-form-label query-keyword width-9">Time Grain</label>
         <div class="gf-form-select-wrapper gf-form-select-wrapper--caret-indent timegrainunit-dropdown-wrapper">

+ 11 - 3
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);
         };

+ 54 - 2
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) => {

+ 1 - 0
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;