From ef4bec1c6d870a38dd2f47b4543be8b6fef7715b Mon Sep 17 00:00:00 2001 From: Mitsuhiro Tanda Date: Tue, 10 Nov 2015 19:25:59 +0900 Subject: [PATCH 1/6] fix CloudWatch dimension value suggestion --- .../datasource/cloudwatch/datasource.js | 23 ++++++++++++------- .../datasource/cloudwatch/query_ctrl.js | 19 +++++++++++++-- .../cloudwatch/specs/datasource_specs.ts | 2 +- 3 files changed, 33 insertions(+), 11 deletions(-) diff --git a/public/app/plugins/datasource/cloudwatch/datasource.js b/public/app/plugins/datasource/cloudwatch/datasource.js index eef2d77f5e2..d4c622eac55 100644 --- a/public/app/plugins/datasource/cloudwatch/datasource.js +++ b/public/app/plugins/datasource/cloudwatch/datasource.js @@ -124,13 +124,7 @@ function (angular, _) { }; return this.awsRequest(request).then(function(result) { - return _.chain(result.Metrics).map(function(metric) { - return _.pluck(metric.Dimensions, 'Value'); - }).flatten().uniq().sortBy(function(name) { - return name; - }).map(function(value) { - return {value: value, text: value}; - }).value(); + return _.pluck(result.Metrics, 'Dimensions'); }); }; @@ -191,7 +185,20 @@ function (angular, _) { }); } - return this.getDimensionValues(region, namespace, metricName, dimensions); + return this.getDimensionValues(region, namespace, metricName, dimensions).then(function(result) { + return _.map(result, function(dimensions) { + var values = _.chain(dimensions) + .sortBy(function(dimension) { + return dimension.Name; + }) + .map(function(dimension) { + return dimension.Name + '=' + dimension.Value; + }) + .value().join(','); + + return { text: values }; + }); + }); } var ebsVolumeIdsQuery = query.match(/^ebs_volume_ids\(([^,]+?),\s?([^,]+?)\)/); diff --git a/public/app/plugins/datasource/cloudwatch/query_ctrl.js b/public/app/plugins/datasource/cloudwatch/query_ctrl.js index 3869a5ec715..7bcc73b4323 100644 --- a/public/app/plugins/datasource/cloudwatch/query_ctrl.js +++ b/public/app/plugins/datasource/cloudwatch/query_ctrl.js @@ -76,7 +76,7 @@ function (angular, _) { } }; - $scope.getDimSegments = function(segment) { + $scope.getDimSegments = function(segment, $index) { if (segment.type === 'operator') { return $q.when([]); } var target = $scope.target; @@ -88,7 +88,22 @@ function (angular, _) { query = $scope.datasource.getDimensionValues(target.region, target.namespace, target.metricName, {}); } - return query.then($scope.transformToSegments(true)).then(function(results) { + return query.then(function(results) { + if (segment.type === 'value') { + results = _.chain(results) + .flatten(true) + .filter(function(dimension) { + return dimension.Name === templateSrv.replace($scope.dimSegments[$index-2].value); + }) + .pluck('Value') + .uniq() + .map(function(value) { + return {value: value, text: value}; + }) + .value(); + } + return $scope.transformToSegments(true)(results); + }).then(function(results) { if (segment.type === 'key') { results.splice(0, 0, angular.copy($scope.removeDimSegment)); } diff --git a/public/app/plugins/datasource/cloudwatch/specs/datasource_specs.ts b/public/app/plugins/datasource/cloudwatch/specs/datasource_specs.ts index 4714a642d30..a6d4330a37e 100644 --- a/public/app/plugins/datasource/cloudwatch/specs/datasource_specs.ts +++ b/public/app/plugins/datasource/cloudwatch/specs/datasource_specs.ts @@ -165,7 +165,7 @@ describe('CloudWatchDatasource', function() { }); it('should call __ListMetrics and return result', () => { - expect(scenario.result[0].text).to.be('i-12345678'); + expect(scenario.result[0].text).to.be('InstanceId=i-12345678'); expect(scenario.request.data.action).to.be('ListMetrics'); }); }); From add5bb47d5a84a620abfe599e04c749b1901c1de Mon Sep 17 00:00:00 2001 From: Mitsuhiro Tanda Date: Sat, 14 Nov 2015 19:54:24 +0900 Subject: [PATCH 2/6] add dimensions() to CloudWatch templating query --- .../datasource/cloudwatch/datasource.js | 64 ++++++++++++++----- .../datasource/cloudwatch/query_ctrl.js | 20 +----- .../cloudwatch/specs/datasource_specs.ts | 26 +++++++- 3 files changed, 77 insertions(+), 33 deletions(-) diff --git a/public/app/plugins/datasource/cloudwatch/datasource.js b/public/app/plugins/datasource/cloudwatch/datasource.js index d4c622eac55..4512aed972b 100644 --- a/public/app/plugins/datasource/cloudwatch/datasource.js +++ b/public/app/plugins/datasource/cloudwatch/datasource.js @@ -112,14 +112,14 @@ function (angular, _) { }); }; - CloudWatchDatasource.prototype.getDimensionValues = function(region, namespace, metricName, dimensions) { + CloudWatchDatasource.prototype.getDimensions = function(region, namespace, metricName, filterDimensions) { var request = { region: templateSrv.replace(region), action: 'ListMetrics', parameters: { namespace: templateSrv.replace(namespace), metricName: templateSrv.replace(metricName), - dimensions: convertDimensionFormat(dimensions, {}), + dimensions: convertDimensionFormat(filterDimensions, {}), } }; @@ -128,6 +128,17 @@ function (angular, _) { }); }; + CloudWatchDatasource.prototype.getDimensionValues = function(region, namespace, metricName, dimensionKey, filterDimensions) { + return this.getDimensions(region, namespace, metricName, filterDimensions).then(function(dimensions) { + return _.chain(dimensions) + .flatten() + .filter(function(dimension) { + return dimension.Name === dimensionKey; + }) + .pluck('Value').uniq().sortBy().value(); + }); + }; + CloudWatchDatasource.prototype.performEC2DescribeInstances = function(region, filters, instanceIds) { return this.awsRequest({ region: region, @@ -140,6 +151,8 @@ function (angular, _) { var region; var namespace; var metricName; + var dimensionPart; + var dimensions; var transformSuggestData = function(suggestData) { return _.map(suggestData, function(v) { @@ -147,6 +160,21 @@ function (angular, _) { }); }; + var parseDimensions = function(dimensionPart) { + if (_.isEmpty(dimensionPart)) { + return {}; + } + var dimensions = {}; + _.each(dimensionPart.split(','), function(v) { + var t = v.split('='); + if (t.length !== 2) { + throw new Error('Invalid query format'); + } + dimensions[t[0]] = t[1]; + }); + return dimensions; + }; + var regionQuery = query.match(/^regions\(\)/); if (regionQuery) { return this.getRegions(); @@ -167,25 +195,31 @@ function (angular, _) { return this.getDimensionKeys(dimensionKeysQuery[1]); } - var dimensionValuesQuery = query.match(/^dimension_values\(([^,]+?),\s?([^,]+?),\s?([^,]+?)(,\s?([^)]*))?\)/); + var dimensionValuesQuery = query.match(/^dimension_values\(([^,]+?),\s?([^,]+?),\s?([^,]+?),\s?([^,]+?)(,\s?([^)]*))?\)/); if (dimensionValuesQuery) { region = templateSrv.replace(dimensionValuesQuery[1]); namespace = templateSrv.replace(dimensionValuesQuery[2]); metricName = templateSrv.replace(dimensionValuesQuery[3]); - var dimensionPart = templateSrv.replace(dimensionValuesQuery[5]); + var dimensionKey = templateSrv.replace(dimensionValuesQuery[4]); + dimensionPart = templateSrv.replace(dimensionValuesQuery[6]); - var dimensions = {}; - if (!_.isEmpty(dimensionPart)) { - _.each(dimensionPart.split(','), function(v) { - var t = v.split('='); - if (t.length !== 2) { - throw new Error('Invalid query format'); - } - dimensions[t[0]] = t[1]; + dimensions = parseDimensions(dimensionPart); + return this.getDimensionValues(region, namespace, metricName, dimensionKey, dimensions).then(function(result) { + return _.map(result, function(dimension_value) { + return { text: dimension_value }; }); - } + }); + } + + var dimensionsQuery = query.match(/^dimensions\(([^,]+?),\s?([^,]+?),\s?([^,]+?)(,\s?([^)]*))?\)/); + if (dimensionsQuery) { + region = templateSrv.replace(dimensionsQuery[1]); + namespace = templateSrv.replace(dimensionsQuery[2]); + metricName = templateSrv.replace(dimensionsQuery[3]); + dimensionPart = templateSrv.replace(dimensionsQuery[5]); - return this.getDimensionValues(region, namespace, metricName, dimensions).then(function(result) { + dimensions = parseDimensions(dimensionPart); + return this.getDimensions(region, namespace, metricName, dimensions).then(function(result) { return _.map(result, function(dimensions) { var values = _.chain(dimensions) .sortBy(function(dimension) { @@ -228,7 +262,7 @@ function (angular, _) { var metricName = 'EstimatedCharges'; var dimensions = {}; - return this.getDimensionValues(region, namespace, metricName, dimensions).then(function () { + return this.getDimensions(region, namespace, metricName, dimensions).then(function () { return { status: 'success', message: 'Data source is working', title: 'Success' }; }); }; diff --git a/public/app/plugins/datasource/cloudwatch/query_ctrl.js b/public/app/plugins/datasource/cloudwatch/query_ctrl.js index 7bcc73b4323..d0f6fe5b52a 100644 --- a/public/app/plugins/datasource/cloudwatch/query_ctrl.js +++ b/public/app/plugins/datasource/cloudwatch/query_ctrl.js @@ -85,25 +85,11 @@ function (angular, _) { if (segment.type === 'key' || segment.type === 'plus-button') { query = $scope.datasource.getDimensionKeys($scope.target.namespace); } else if (segment.type === 'value') { - query = $scope.datasource.getDimensionValues(target.region, target.namespace, target.metricName, {}); + var dimensionKey = $scope.dimSegments[$index-2].value; + query = $scope.datasource.getDimensionValues(target.region, target.namespace, target.metricName, dimensionKey, {}); } - return query.then(function(results) { - if (segment.type === 'value') { - results = _.chain(results) - .flatten(true) - .filter(function(dimension) { - return dimension.Name === templateSrv.replace($scope.dimSegments[$index-2].value); - }) - .pluck('Value') - .uniq() - .map(function(value) { - return {value: value, text: value}; - }) - .value(); - } - return $scope.transformToSegments(true)(results); - }).then(function(results) { + return query.then($scope.transformToSegments(true)).then(function(results) { if (segment.type === 'key') { results.splice(0, 0, angular.copy($scope.removeDimSegment)); } diff --git a/public/app/plugins/datasource/cloudwatch/specs/datasource_specs.ts b/public/app/plugins/datasource/cloudwatch/specs/datasource_specs.ts index a6d4330a37e..6c55eab55ca 100644 --- a/public/app/plugins/datasource/cloudwatch/specs/datasource_specs.ts +++ b/public/app/plugins/datasource/cloudwatch/specs/datasource_specs.ts @@ -146,7 +146,7 @@ describe('CloudWatchDatasource', function() { }); }); - describeMetricFindQuery('dimension_values(us-east-1,AWS/EC2,CPUUtilization)', scenario => { + describeMetricFindQuery('dimensions(us-east-1,AWS/EC2,CPUUtilization)', scenario => { scenario.setup(() => { scenario.requestResponse = { Metrics: [ @@ -170,4 +170,28 @@ describe('CloudWatchDatasource', function() { }); }); + describeMetricFindQuery('dimension_values(us-east-1,AWS/EC2,CPUUtilization,InstanceId)', scenario => { + scenario.setup(() => { + scenario.requestResponse = { + Metrics: [ + { + Namespace: 'AWS/EC2', + MetricName: 'CPUUtilization', + Dimensions: [ + { + Name: 'InstanceId', + Value: 'i-12345678' + } + ] + } + ] + }; + }); + + it('should call __ListMetrics and return result', () => { + expect(scenario.result[0].text).to.be('i-12345678'); + expect(scenario.request.data.action).to.be('ListMetrics'); + }); + }); + }); From 1bbd0567975c266f7884ee9b45acf3a4090e1511 Mon Sep 17 00:00:00 2001 From: Mitsuhiro Tanda Date: Mon, 16 Nov 2015 16:47:28 +0900 Subject: [PATCH 3/6] fix templating --- .../app/plugins/datasource/cloudwatch/datasource.js | 13 +++++++------ 1 file changed, 7 insertions(+), 6 deletions(-) diff --git a/public/app/plugins/datasource/cloudwatch/datasource.js b/public/app/plugins/datasource/cloudwatch/datasource.js index 4512aed972b..9b4bed35e90 100644 --- a/public/app/plugins/datasource/cloudwatch/datasource.js +++ b/public/app/plugins/datasource/cloudwatch/datasource.js @@ -135,7 +135,12 @@ function (angular, _) { .filter(function(dimension) { return dimension.Name === dimensionKey; }) - .pluck('Value').uniq().sortBy().value(); + .pluck('Value') + .uniq() + .sortBy() + .map(function(value) { + return {value: value, text: value}; + }).value(); }); }; @@ -204,11 +209,7 @@ function (angular, _) { dimensionPart = templateSrv.replace(dimensionValuesQuery[6]); dimensions = parseDimensions(dimensionPart); - return this.getDimensionValues(region, namespace, metricName, dimensionKey, dimensions).then(function(result) { - return _.map(result, function(dimension_value) { - return { text: dimension_value }; - }); - }); + return this.getDimensionValues(region, namespace, metricName, dimensionKey, dimensions); } var dimensionsQuery = query.match(/^dimensions\(([^,]+?),\s?([^,]+?),\s?([^,]+?)(,\s?([^)]*))?\)/); From ae7e7e96565055dadfca9fbbcaf42428d37c23ff Mon Sep 17 00:00:00 2001 From: Mitsuhiro Tanda Date: Tue, 17 Nov 2015 22:49:46 +0900 Subject: [PATCH 4/6] remove getDimensions() --- .../datasource/cloudwatch/datasource.js | 37 ++----------------- .../cloudwatch/specs/datasource_specs.ts | 24 ------------ 2 files changed, 4 insertions(+), 57 deletions(-) diff --git a/public/app/plugins/datasource/cloudwatch/datasource.js b/public/app/plugins/datasource/cloudwatch/datasource.js index 9b4bed35e90..7297346b33c 100644 --- a/public/app/plugins/datasource/cloudwatch/datasource.js +++ b/public/app/plugins/datasource/cloudwatch/datasource.js @@ -112,7 +112,7 @@ function (angular, _) { }); }; - CloudWatchDatasource.prototype.getDimensions = function(region, namespace, metricName, filterDimensions) { + CloudWatchDatasource.prototype.getDimensionValues = function(region, namespace, metricName, dimensionKey, filterDimensions) { var request = { region: templateSrv.replace(region), action: 'ListMetrics', @@ -124,13 +124,8 @@ function (angular, _) { }; return this.awsRequest(request).then(function(result) { - return _.pluck(result.Metrics, 'Dimensions'); - }); - }; - - CloudWatchDatasource.prototype.getDimensionValues = function(region, namespace, metricName, dimensionKey, filterDimensions) { - return this.getDimensions(region, namespace, metricName, filterDimensions).then(function(dimensions) { - return _.chain(dimensions) + return _.chain(result.Metrics) + .pluck('Dimensions') .flatten() .filter(function(dimension) { return dimension.Name === dimensionKey; @@ -212,30 +207,6 @@ function (angular, _) { return this.getDimensionValues(region, namespace, metricName, dimensionKey, dimensions); } - var dimensionsQuery = query.match(/^dimensions\(([^,]+?),\s?([^,]+?),\s?([^,]+?)(,\s?([^)]*))?\)/); - if (dimensionsQuery) { - region = templateSrv.replace(dimensionsQuery[1]); - namespace = templateSrv.replace(dimensionsQuery[2]); - metricName = templateSrv.replace(dimensionsQuery[3]); - dimensionPart = templateSrv.replace(dimensionsQuery[5]); - - dimensions = parseDimensions(dimensionPart); - return this.getDimensions(region, namespace, metricName, dimensions).then(function(result) { - return _.map(result, function(dimensions) { - var values = _.chain(dimensions) - .sortBy(function(dimension) { - return dimension.Name; - }) - .map(function(dimension) { - return dimension.Name + '=' + dimension.Value; - }) - .value().join(','); - - return { text: values }; - }); - }); - } - var ebsVolumeIdsQuery = query.match(/^ebs_volume_ids\(([^,]+?),\s?([^,]+?)\)/); if (ebsVolumeIdsQuery) { region = templateSrv.replace(ebsVolumeIdsQuery[1]); @@ -263,7 +234,7 @@ function (angular, _) { var metricName = 'EstimatedCharges'; var dimensions = {}; - return this.getDimensions(region, namespace, metricName, dimensions).then(function () { + return this.getDimensionValues(region, namespace, metricName, 'ServiceName', dimensions).then(function () { return { status: 'success', message: 'Data source is working', title: 'Success' }; }); }; diff --git a/public/app/plugins/datasource/cloudwatch/specs/datasource_specs.ts b/public/app/plugins/datasource/cloudwatch/specs/datasource_specs.ts index 6c55eab55ca..b97dff5ce3b 100644 --- a/public/app/plugins/datasource/cloudwatch/specs/datasource_specs.ts +++ b/public/app/plugins/datasource/cloudwatch/specs/datasource_specs.ts @@ -146,30 +146,6 @@ describe('CloudWatchDatasource', function() { }); }); - describeMetricFindQuery('dimensions(us-east-1,AWS/EC2,CPUUtilization)', scenario => { - scenario.setup(() => { - scenario.requestResponse = { - Metrics: [ - { - Namespace: 'AWS/EC2', - MetricName: 'CPUUtilization', - Dimensions: [ - { - Name: 'InstanceId', - Value: 'i-12345678' - } - ] - } - ] - }; - }); - - it('should call __ListMetrics and return result', () => { - expect(scenario.result[0].text).to.be('InstanceId=i-12345678'); - expect(scenario.request.data.action).to.be('ListMetrics'); - }); - }); - describeMetricFindQuery('dimension_values(us-east-1,AWS/EC2,CPUUtilization,InstanceId)', scenario => { scenario.setup(() => { scenario.requestResponse = { From 154d70e4e260314b90e1b120ee674095d989e95a Mon Sep 17 00:00:00 2001 From: Mitsuhiro Tanda Date: Fri, 4 Dec 2015 01:02:25 +0900 Subject: [PATCH 5/6] remove dimensionPart --- .../datasource/cloudwatch/datasource.js | 23 ++----------------- 1 file changed, 2 insertions(+), 21 deletions(-) diff --git a/public/app/plugins/datasource/cloudwatch/datasource.js b/public/app/plugins/datasource/cloudwatch/datasource.js index 7297346b33c..8245d327bdd 100644 --- a/public/app/plugins/datasource/cloudwatch/datasource.js +++ b/public/app/plugins/datasource/cloudwatch/datasource.js @@ -151,8 +151,6 @@ function (angular, _) { var region; var namespace; var metricName; - var dimensionPart; - var dimensions; var transformSuggestData = function(suggestData) { return _.map(suggestData, function(v) { @@ -160,21 +158,6 @@ function (angular, _) { }); }; - var parseDimensions = function(dimensionPart) { - if (_.isEmpty(dimensionPart)) { - return {}; - } - var dimensions = {}; - _.each(dimensionPart.split(','), function(v) { - var t = v.split('='); - if (t.length !== 2) { - throw new Error('Invalid query format'); - } - dimensions[t[0]] = t[1]; - }); - return dimensions; - }; - var regionQuery = query.match(/^regions\(\)/); if (regionQuery) { return this.getRegions(); @@ -195,16 +178,14 @@ function (angular, _) { return this.getDimensionKeys(dimensionKeysQuery[1]); } - var dimensionValuesQuery = query.match(/^dimension_values\(([^,]+?),\s?([^,]+?),\s?([^,]+?),\s?([^,]+?)(,\s?([^)]*))?\)/); + var dimensionValuesQuery = query.match(/^dimension_values\(([^,]+?),\s?([^,]+?),\s?([^,]+?),\s?([^,]+?)\)/); if (dimensionValuesQuery) { region = templateSrv.replace(dimensionValuesQuery[1]); namespace = templateSrv.replace(dimensionValuesQuery[2]); metricName = templateSrv.replace(dimensionValuesQuery[3]); var dimensionKey = templateSrv.replace(dimensionValuesQuery[4]); - dimensionPart = templateSrv.replace(dimensionValuesQuery[6]); - dimensions = parseDimensions(dimensionPart); - return this.getDimensionValues(region, namespace, metricName, dimensionKey, dimensions); + return this.getDimensionValues(region, namespace, metricName, dimensionKey, {}); } var ebsVolumeIdsQuery = query.match(/^ebs_volume_ids\(([^,]+?),\s?([^,]+?)\)/); From eb5822bc7c766a746246770930167bd2284f7249 Mon Sep 17 00:00:00 2001 From: Mitsuhiro Tanda Date: Fri, 4 Dec 2015 01:04:50 +0900 Subject: [PATCH 6/6] update cloudwatch docs --- docs/sources/datasources/cloudwatch.md | 7 +------ 1 file changed, 1 insertion(+), 6 deletions(-) diff --git a/docs/sources/datasources/cloudwatch.md b/docs/sources/datasources/cloudwatch.md index 012da9d843a..4d87c4a0743 100644 --- a/docs/sources/datasources/cloudwatch.md +++ b/docs/sources/datasources/cloudwatch.md @@ -63,15 +63,10 @@ Name | Description `namespaces()` | Returns a list of namespaces CloudWatch support. `metrics(namespace)` | Returns a list of metrics in the namespace. `dimension_keys(namespace)` | Returns a list of dimension keys in the namespace. -`dimension_values(region, namespace, metric)` | Returns a list of dimension values matching the specified `region`, `namespace` and `metric`. +`dimension_values(region, namespace, metric, dimension_key)` | Returns a list of dimension values matching the specified `region`, `namespace`, `metric` and `dimension_key`. For details about the metrics CloudWatch provides, please refer to the [CloudWatch documentation](https://docs.aws.amazon.com/AmazonCloudWatch/latest/DeveloperGuide/CW_Support_For_AWS.html). -If you want to filter dimension values by other dimension key/value pair, you can specify optional parameter like this. -```sql -dimension_values(region, namespace, metric, dim_key1=dim_val1,dim_key2=dim_val2,...) -``` - ![](/img/v2/cloudwatch_templating.png) ## Cost