Просмотр исходного кода

(cloudwatch) percentile support (#6634)

* support extended statistics

* handle different response format for extended statistics
Mitsuhiro Tanda 9 лет назад
Родитель
Сommit
be26c017f6

+ 31 - 19
pkg/api/cloudwatch/cloudwatch.go

@@ -181,25 +181,33 @@ func handleGetMetricStatistics(req *cwRequest, c *middleware.Context) {
 
 	reqParam := &struct {
 		Parameters struct {
-			Namespace  string                  `json:"namespace"`
-			MetricName string                  `json:"metricName"`
-			Dimensions []*cloudwatch.Dimension `json:"dimensions"`
-			Statistics []*string               `json:"statistics"`
-			StartTime  int64                   `json:"startTime"`
-			EndTime    int64                   `json:"endTime"`
-			Period     int64                   `json:"period"`
+			Namespace          string                  `json:"namespace"`
+			MetricName         string                  `json:"metricName"`
+			Dimensions         []*cloudwatch.Dimension `json:"dimensions"`
+			Statistics         []*string               `json:"statistics"`
+			ExtendedStatistics []*string               `json:"extendedStatistics"`
+			StartTime          int64                   `json:"startTime"`
+			EndTime            int64                   `json:"endTime"`
+			Period             int64                   `json:"period"`
 		} `json:"parameters"`
 	}{}
 	json.Unmarshal(req.Body, reqParam)
 
 	params := &cloudwatch.GetMetricStatisticsInput{
-		Namespace:  aws.String(reqParam.Parameters.Namespace),
-		MetricName: aws.String(reqParam.Parameters.MetricName),
-		Dimensions: reqParam.Parameters.Dimensions,
-		Statistics: reqParam.Parameters.Statistics,
-		StartTime:  aws.Time(time.Unix(reqParam.Parameters.StartTime, 0)),
-		EndTime:    aws.Time(time.Unix(reqParam.Parameters.EndTime, 0)),
-		Period:     aws.Int64(reqParam.Parameters.Period),
+		Namespace:          aws.String(reqParam.Parameters.Namespace),
+		MetricName:         aws.String(reqParam.Parameters.MetricName),
+		Dimensions:         reqParam.Parameters.Dimensions,
+		Statistics:         reqParam.Parameters.Statistics,
+		ExtendedStatistics: reqParam.Parameters.ExtendedStatistics,
+		StartTime:          aws.Time(time.Unix(reqParam.Parameters.StartTime, 0)),
+		EndTime:            aws.Time(time.Unix(reqParam.Parameters.EndTime, 0)),
+		Period:             aws.Int64(reqParam.Parameters.Period),
+	}
+	if len(reqParam.Parameters.Statistics) != 0 {
+		params.Statistics = reqParam.Parameters.Statistics
+	}
+	if len(reqParam.Parameters.ExtendedStatistics) != 0 {
+		params.ExtendedStatistics = reqParam.Parameters.ExtendedStatistics
 	}
 
 	resp, err := svc.GetMetricStatistics(params)
@@ -292,11 +300,12 @@ func handleDescribeAlarmsForMetric(req *cwRequest, c *middleware.Context) {
 
 	reqParam := &struct {
 		Parameters struct {
-			Namespace  string                  `json:"namespace"`
-			MetricName string                  `json:"metricName"`
-			Dimensions []*cloudwatch.Dimension `json:"dimensions"`
-			Statistic  string                  `json:"statistic"`
-			Period     int64                   `json:"period"`
+			Namespace         string                  `json:"namespace"`
+			MetricName        string                  `json:"metricName"`
+			Dimensions        []*cloudwatch.Dimension `json:"dimensions"`
+			Statistic         string                  `json:"statistic"`
+			ExtendedStatistic string                  `json:"extendedStatistic"`
+			Period            int64                   `json:"period"`
 		} `json:"parameters"`
 	}{}
 	json.Unmarshal(req.Body, reqParam)
@@ -312,6 +321,9 @@ func handleDescribeAlarmsForMetric(req *cwRequest, c *middleware.Context) {
 	if reqParam.Parameters.Statistic != "" {
 		params.Statistic = aws.String(reqParam.Parameters.Statistic)
 	}
+	if reqParam.Parameters.ExtendedStatistic != "" {
+		params.ExtendedStatistic = aws.String(reqParam.Parameters.ExtendedStatistic)
+	}
 
 	resp, err := svc.DescribeAlarmsForMetric(params)
 	if err != nil {

+ 27 - 3
public/app/plugins/datasource/cloudwatch/datasource.js

@@ -16,6 +16,13 @@ function (angular, _, moment, dateMath, kbn, CloudWatchAnnotationQuery) {
     this.supportMetrics = true;
     this.proxyUrl = instanceSettings.url;
     this.defaultRegion = instanceSettings.jsonData.defaultRegion;
+    this.standardStatistics = [
+      'Average',
+      'Maximum',
+      'Minimum',
+      'Sum',
+      'SampleCount'
+    ];
 
     var self = this;
     this.query = function(options) {
@@ -98,6 +105,8 @@ function (angular, _, moment, dateMath, kbn, CloudWatchAnnotationQuery) {
     };
 
     this.performTimeSeriesQuery = function(query, start, end) {
+      var statistics = _.filter(query.statistics, function(s) { return _.includes(self.standardStatistics, s); });
+      var extendedStatistics = _.reject(query.statistics, function(s) { return _.includes(self.standardStatistics, s); });
       return this.awsRequest({
         region: query.region,
         action: 'GetMetricStatistics',
@@ -105,7 +114,8 @@ function (angular, _, moment, dateMath, kbn, CloudWatchAnnotationQuery) {
           namespace: query.namespace,
           metricName: query.metricName,
           dimensions: query.dimensions,
-          statistics: query.statistics,
+          statistics: statistics,
+          extendedStatistics: extendedStatistics,
           startTime: start,
           endTime: end,
           period: query.period
@@ -268,10 +278,19 @@ function (angular, _, moment, dateMath, kbn, CloudWatchAnnotationQuery) {
     };
 
     this.performDescribeAlarmsForMetric = function(region, namespace, metricName, dimensions, statistic, period) {
+      var s = _.includes(self.standardStatistics, statistic) ? statistic : '';
+      var es = _.includes(self.standardStatistics, statistic) ? '' : statistic;
       return this.awsRequest({
         region: region,
         action: 'DescribeAlarmsForMetric',
-        parameters: { namespace: namespace, metricName: metricName, dimensions: dimensions, statistic: statistic, period: period }
+        parameters: {
+          namespace: namespace,
+          metricName: metricName,
+          dimensions: dimensions,
+          statistic: s,
+          extendedStatistic: es,
+          period: period
+        }
       });
     };
 
@@ -338,6 +357,7 @@ function (angular, _, moment, dateMath, kbn, CloudWatchAnnotationQuery) {
       var periodMs = options.period * 1000;
 
       return _.map(options.statistics, function(stat) {
+        var extended = !_.includes(self.standardStatistics, stat);
         var dps = [];
         var lastTimestamp = null;
         _.chain(md.Datapoints)
@@ -350,7 +370,11 @@ function (angular, _, moment, dateMath, kbn, CloudWatchAnnotationQuery) {
             dps.push([null, lastTimestamp + periodMs]);
           }
           lastTimestamp = timestamp;
-          dps.push([dp[stat], timestamp]);
+          if (!extended) {
+            dps.push([dp[stat], timestamp]);
+          } else {
+            dps.push([dp.ExtendedStatistics[stat], timestamp]);
+          }
         })
         .value();
 

+ 6 - 7
public/app/plugins/datasource/cloudwatch/query_parameter_ctrl.js

@@ -61,14 +61,13 @@ function (angular, _) {
     };
 
     $scope.getStatSegments = function() {
-      return $q.when([
+      return $q.when(_.flatten([
         angular.copy($scope.removeStatSegment),
-        uiSegmentSrv.getSegmentForValue('Average'),
-        uiSegmentSrv.getSegmentForValue('Maximum'),
-        uiSegmentSrv.getSegmentForValue('Minimum'),
-        uiSegmentSrv.getSegmentForValue('Sum'),
-        uiSegmentSrv.getSegmentForValue('SampleCount'),
-      ]);
+        _.map($scope.datasource.standardStatistics, function(s) {
+          return uiSegmentSrv.getSegmentForValue(s);
+        }),
+        uiSegmentSrv.getSegmentForValue('pNN.NN'),
+      ]));
     };
 
     $scope.statSegmentChanged = function(segment, index) {

+ 61 - 0
public/app/plugins/datasource/cloudwatch/specs/datasource_specs.ts

@@ -161,6 +161,67 @@ describe('CloudWatchDatasource', function() {
     });
   });
 
+  describe('When performing CloudWatch query for extended statistics', function() {
+    var requestParams;
+
+    var query = {
+      range: { from: 'now-1h', to: 'now' },
+      targets: [
+        {
+          region: 'us-east-1',
+          namespace: 'AWS/ApplicationELB',
+          metricName: 'TargetResponseTime',
+          dimensions: {
+            LoadBalancer: 'lb',
+            TargetGroup: 'tg'
+          },
+          statistics: ['p90.00'],
+          period: 300
+        }
+      ]
+    };
+
+    var response = {
+      Datapoints: [
+        {
+          ExtendedStatistics: {
+            'p90.00': 1
+          },
+          Timestamp: 'Wed Dec 31 1969 16:00:00 GMT-0800 (PST)'
+        },
+        {
+          ExtendedStatistics: {
+            'p90.00': 2
+          },
+          Timestamp: 'Wed Dec 31 1969 16:05:00 GMT-0800 (PST)'
+        },
+        {
+          ExtendedStatistics: {
+            'p90.00': 5
+          },
+          Timestamp: 'Wed Dec 31 1969 16:15:00 GMT-0800 (PST)'
+        }
+      ],
+      Label: 'TargetResponseTime'
+    };
+
+    beforeEach(function() {
+      ctx.backendSrv.datasourceRequest = function(params) {
+        requestParams = params;
+        return ctx.$q.when({data: response});
+      };
+    });
+
+    it('should return series list', function(done) {
+      ctx.ds.query(query).then(function(result) {
+        expect(result.data[0].target).to.be('TargetResponseTime_p90.00');
+        expect(result.data[0].datapoints[0][0]).to.be(response.Datapoints[0].ExtendedStatistics['p90.00']);
+        done();
+      });
+      ctx.$rootScope.$apply();
+    });
+  });
+
   function describeMetricFindQuery(query, func) {
     describe('metricFindQuery ' + query, () => {
       let scenario: any = {};