From cf23734d7deb8779859ed88e2b2d5e4a598c995b Mon Sep 17 00:00:00 2001 From: Mitsuhiro Tanda Date: Tue, 19 Sep 2017 18:09:57 +0900 Subject: [PATCH] re-implement ebs_volume_ids() --- pkg/tsdb/cloudwatch/metric_find_query.go | 68 +++++++++++++++++++ .../datasource/cloudwatch/datasource.js | 34 +++++++--- 2 files changed, 91 insertions(+), 11 deletions(-) diff --git a/pkg/tsdb/cloudwatch/metric_find_query.go b/pkg/tsdb/cloudwatch/metric_find_query.go index faf0b8b7ce2..42f5eed510e 100644 --- a/pkg/tsdb/cloudwatch/metric_find_query.go +++ b/pkg/tsdb/cloudwatch/metric_find_query.go @@ -12,6 +12,7 @@ import ( "github.com/aws/aws-sdk-go/aws/awsutil" "github.com/aws/aws-sdk-go/aws/session" "github.com/aws/aws-sdk-go/service/cloudwatch" + "github.com/aws/aws-sdk-go/service/ec2" cwapi "github.com/grafana/grafana/pkg/api/cloudwatch" "github.com/grafana/grafana/pkg/components/simplejson" "github.com/grafana/grafana/pkg/metrics" @@ -174,6 +175,9 @@ func (e *CloudWatchExecutor) executeMetricFindQuery(ctx context.Context, queries case "dimension_keys": data, err = e.handleGetDimensions(ctx, parameters, queryContext) break + case "ebs_volume_ids": + data, err = e.handleGetEbsVolumeIds(ctx, parameters, queryContext) + break } if err != nil { queryResult.Error = err @@ -320,6 +324,70 @@ func (e *CloudWatchExecutor) handleGetDimensions(ctx context.Context, parameters return result, nil } +func (e *CloudWatchExecutor) handleGetEbsVolumeIds(ctx context.Context, parameters *simplejson.Json, queryContext *tsdb.QueryContext) ([]suggestData, error) { + region := parameters.Get("region").MustString() + instanceId := parameters.Get("instanceId").MustString() + + instanceIds := []*string{aws.String(instanceId)} + instances, err := e.ec2DescribeInstances(region, nil, instanceIds) + if err != nil { + return nil, err + } + + result := make([]suggestData, 0) + for _, mapping := range instances.Reservations[0].Instances[0].BlockDeviceMappings { + result = append(result, suggestData{Text: *mapping.Ebs.VolumeId, Value: *mapping.Ebs.VolumeId}) + } + + return result, nil +} + +func getAwsConfig(dsInfo *cwapi.DatasourceInfo) (*aws.Config, error) { + creds, err := cwapi.GetCredentials(dsInfo) + if err != nil { + return nil, err + } + + cfg := &aws.Config{ + Region: aws.String(dsInfo.Region), + Credentials: creds, + } + return cfg, nil +} + +func (e *CloudWatchExecutor) ec2DescribeInstances(region string, filters []*ec2.Filter, instanceIds []*string) (*ec2.DescribeInstancesOutput, error) { + dsInfo := e.getDsInfo(region) + cfg, err := getAwsConfig(dsInfo) + if err != nil { + return nil, errors.New("Failed to call describe instances") + } + sess, err := session.NewSession(cfg) + if err != nil { + return nil, errors.New("Failed to call describe instances") + } + svc := ec2.New(sess, cfg) + + params := &ec2.DescribeInstancesInput{ + Filters: filters, + InstanceIds: instanceIds, + } + + var resp ec2.DescribeInstancesOutput + err = svc.DescribeInstancesPages(params, + func(page *ec2.DescribeInstancesOutput, lastPage bool) bool { + reservations, _ := awsutil.ValuesAtPath(page, "Reservations") + for _, reservation := range reservations { + resp.Reservations = append(resp.Reservations, reservation.(*ec2.Reservation)) + } + return !lastPage + }) + if err != nil { + return nil, errors.New("Failed to call describe instances") + } + + return &resp, nil +} + func getAllMetrics(cwData *cwapi.DatasourceInfo) (cloudwatch.ListMetricsOutput, error) { creds, err := cwapi.GetCredentials(cwData) if err != nil { diff --git a/public/app/plugins/datasource/cloudwatch/datasource.js b/public/app/plugins/datasource/cloudwatch/datasource.js index 916c95fbd77..40fba8af36f 100644 --- a/public/app/plugins/datasource/cloudwatch/datasource.js +++ b/public/app/plugins/datasource/cloudwatch/datasource.js @@ -248,6 +248,28 @@ function (angular, _, moment, dateMath, kbn, templatingVariable, CloudWatchAnnot }); }; + this.getEbsVolumeIds = function(region, instanceId) { + var range = timeSrv.timeRange(); + return backendSrv.post('/api/tsdb/query', { + from: range.from, + to: range.to, + queries: [ + { + refId: 'metricFindQuery', + intervalMs: 1, // dummy + maxDataPoints: 1, // dummy + datasourceId: this.instanceSettings.id, + type: 'metricFindQuery', + subtype: 'ebs_volume_ids', + parameters: { + region: region, + instanceId: instanceId + } + } + ] + }).then(function (r) { return transformSuggestDataFromTable(r); }); + }; + this.performEC2DescribeInstances = function(region, filters, instanceIds) { return this.awsRequest({ region: region, @@ -301,17 +323,7 @@ function (angular, _, moment, dateMath, kbn, templatingVariable, CloudWatchAnnot if (ebsVolumeIdsQuery) { region = templateSrv.replace(ebsVolumeIdsQuery[1]); var instanceId = templateSrv.replace(ebsVolumeIdsQuery[2]); - var instanceIds = [ - instanceId - ]; - - return this.performEC2DescribeInstances(region, [], instanceIds).then(function(result) { - var volumeIds = _.map(result.Reservations[0].Instances[0].BlockDeviceMappings, function(mapping) { - return mapping.Ebs.VolumeId; - }); - - return transformSuggestData(volumeIds); - }); + return this.getEbsVolumeIds(region, instanceId); } var ec2InstanceAttributeQuery = query.match(/^ec2_instance_attribute\(([^,]+?),\s?([^,]+?),\s?(.+?)\)/);