mirror of
https://github.com/grafana/grafana.git
synced 2025-02-25 18:55:37 -06:00
cloudwatch: Add resource_arns template query function
Implements feature request #8207
This commit is contained in:
@@ -74,6 +74,12 @@ Here is a minimal policy example:
|
|||||||
"ec2:DescribeRegions"
|
"ec2:DescribeRegions"
|
||||||
],
|
],
|
||||||
"Resource": "*"
|
"Resource": "*"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"Sid": "AllowReadingResourcesForTags",
|
||||||
|
"Effect" : "Allow",
|
||||||
|
"Action" : "tag:GetResources",
|
||||||
|
"Resource" : "*"
|
||||||
}
|
}
|
||||||
]
|
]
|
||||||
}
|
}
|
||||||
@@ -128,6 +134,7 @@ Name | Description
|
|||||||
*dimension_values(region, namespace, metric, dimension_key, [filters])* | Returns a list of dimension values matching the specified `region`, `namespace`, `metric`, `dimension_key` or you can use dimension `filters` to get more specific result as well.
|
*dimension_values(region, namespace, metric, dimension_key, [filters])* | Returns a list of dimension values matching the specified `region`, `namespace`, `metric`, `dimension_key` or you can use dimension `filters` to get more specific result as well.
|
||||||
*ebs_volume_ids(region, instance_id)* | Returns a list of volume ids matching the specified `region`, `instance_id`.
|
*ebs_volume_ids(region, instance_id)* | Returns a list of volume ids matching the specified `region`, `instance_id`.
|
||||||
*ec2_instance_attribute(region, attribute_name, filters)* | Returns a list of attributes matching the specified `region`, `attribute_name`, `filters`.
|
*ec2_instance_attribute(region, attribute_name, filters)* | Returns a list of attributes matching the specified `region`, `attribute_name`, `filters`.
|
||||||
|
*resource_arns(region, resource_type, tags)* | Returns a list of ARNs matching the specified `region`, `resource_type` and `tags`.
|
||||||
|
|
||||||
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).
|
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).
|
||||||
|
|
||||||
@@ -143,6 +150,8 @@ Query | Service
|
|||||||
*dimension_values(us-east-1,AWS/RDS,CPUUtilization,DBInstanceIdentifier)* | RDS
|
*dimension_values(us-east-1,AWS/RDS,CPUUtilization,DBInstanceIdentifier)* | RDS
|
||||||
*dimension_values(us-east-1,AWS/S3,BucketSizeBytes,BucketName)* | S3
|
*dimension_values(us-east-1,AWS/S3,BucketSizeBytes,BucketName)* | S3
|
||||||
*dimension_values(us-east-1,CWAgent,disk_used_percent,device,{"InstanceId":"$instance_id"})* | CloudWatch Agent
|
*dimension_values(us-east-1,CWAgent,disk_used_percent,device,{"InstanceId":"$instance_id"})* | CloudWatch Agent
|
||||||
|
*resource_arns(eu-west-1,elasticloadbalancing:loadbalancer,{"elasticbeanstalk:environment-name":["myApp-dev","myApp-prod"]})* | ELB
|
||||||
|
*resource_arns(eu-west-1,ec2:instance,{"elasticbeanstalk:environment-name":["myApp-dev","myApp-prod"]})* | EC2
|
||||||
|
|
||||||
## ec2_instance_attribute examples
|
## ec2_instance_attribute examples
|
||||||
|
|
||||||
|
|||||||
@@ -21,6 +21,7 @@ import (
|
|||||||
"github.com/aws/aws-sdk-go/aws/request"
|
"github.com/aws/aws-sdk-go/aws/request"
|
||||||
"github.com/aws/aws-sdk-go/service/cloudwatch"
|
"github.com/aws/aws-sdk-go/service/cloudwatch"
|
||||||
"github.com/aws/aws-sdk-go/service/ec2/ec2iface"
|
"github.com/aws/aws-sdk-go/service/ec2/ec2iface"
|
||||||
|
"github.com/aws/aws-sdk-go/service/resourcegroupstaggingapi/resourcegroupstaggingapiiface"
|
||||||
"github.com/grafana/grafana/pkg/components/null"
|
"github.com/grafana/grafana/pkg/components/null"
|
||||||
"github.com/grafana/grafana/pkg/components/simplejson"
|
"github.com/grafana/grafana/pkg/components/simplejson"
|
||||||
"github.com/grafana/grafana/pkg/metrics"
|
"github.com/grafana/grafana/pkg/metrics"
|
||||||
@@ -28,7 +29,8 @@ import (
|
|||||||
|
|
||||||
type CloudWatchExecutor struct {
|
type CloudWatchExecutor struct {
|
||||||
*models.DataSource
|
*models.DataSource
|
||||||
ec2Svc ec2iface.EC2API
|
ec2Svc ec2iface.EC2API
|
||||||
|
rgtaSvc resourcegroupstaggingapiiface.ResourceGroupsTaggingAPIAPI
|
||||||
}
|
}
|
||||||
|
|
||||||
type DatasourceInfo struct {
|
type DatasourceInfo struct {
|
||||||
|
|||||||
@@ -15,6 +15,7 @@ import (
|
|||||||
"github.com/aws/aws-sdk-go/aws/session"
|
"github.com/aws/aws-sdk-go/aws/session"
|
||||||
"github.com/aws/aws-sdk-go/service/cloudwatch"
|
"github.com/aws/aws-sdk-go/service/cloudwatch"
|
||||||
"github.com/aws/aws-sdk-go/service/ec2"
|
"github.com/aws/aws-sdk-go/service/ec2"
|
||||||
|
"github.com/aws/aws-sdk-go/service/resourcegroupstaggingapi"
|
||||||
"github.com/grafana/grafana/pkg/components/simplejson"
|
"github.com/grafana/grafana/pkg/components/simplejson"
|
||||||
"github.com/grafana/grafana/pkg/metrics"
|
"github.com/grafana/grafana/pkg/metrics"
|
||||||
"github.com/grafana/grafana/pkg/tsdb"
|
"github.com/grafana/grafana/pkg/tsdb"
|
||||||
@@ -200,6 +201,8 @@ func (e *CloudWatchExecutor) executeMetricFindQuery(ctx context.Context, queryCo
|
|||||||
data, err = e.handleGetEbsVolumeIds(ctx, parameters, queryContext)
|
data, err = e.handleGetEbsVolumeIds(ctx, parameters, queryContext)
|
||||||
case "ec2_instance_attribute":
|
case "ec2_instance_attribute":
|
||||||
data, err = e.handleGetEc2InstanceAttribute(ctx, parameters, queryContext)
|
data, err = e.handleGetEc2InstanceAttribute(ctx, parameters, queryContext)
|
||||||
|
case "resource_arns":
|
||||||
|
data, err = e.handleGetResourceArns(ctx, parameters, queryContext)
|
||||||
}
|
}
|
||||||
|
|
||||||
transformToTable(data, queryResult)
|
transformToTable(data, queryResult)
|
||||||
@@ -536,6 +539,65 @@ func (e *CloudWatchExecutor) handleGetEc2InstanceAttribute(ctx context.Context,
|
|||||||
return result, nil
|
return result, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (e *CloudWatchExecutor) ensureRGTAClientSession(region string) error {
|
||||||
|
if e.rgtaSvc == nil {
|
||||||
|
dsInfo := e.getDsInfo(region)
|
||||||
|
cfg, err := e.getAwsConfig(dsInfo)
|
||||||
|
if err != nil {
|
||||||
|
return fmt.Errorf("Failed to call ec2:getAwsConfig, %v", err)
|
||||||
|
}
|
||||||
|
sess, err := session.NewSession(cfg)
|
||||||
|
if err != nil {
|
||||||
|
return fmt.Errorf("Failed to call ec2:NewSession, %v", err)
|
||||||
|
}
|
||||||
|
e.rgtaSvc = resourcegroupstaggingapi.New(sess, cfg)
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (e *CloudWatchExecutor) handleGetResourceArns(ctx context.Context, parameters *simplejson.Json, queryContext *tsdb.TsdbQuery) ([]suggestData, error) {
|
||||||
|
region := parameters.Get("region").MustString()
|
||||||
|
resourceType := parameters.Get("resourceType").MustString()
|
||||||
|
filterJson := parameters.Get("tags").MustMap()
|
||||||
|
|
||||||
|
err := e.ensureRGTAClientSession(region)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
var filters []*resourcegroupstaggingapi.TagFilter
|
||||||
|
for k, v := range filterJson {
|
||||||
|
if vv, ok := v.([]interface{}); ok {
|
||||||
|
var vvvvv []*string
|
||||||
|
for _, vvv := range vv {
|
||||||
|
if vvvv, ok := vvv.(string); ok {
|
||||||
|
vvvvv = append(vvvvv, &vvvv)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
filters = append(filters, &resourcegroupstaggingapi.TagFilter{
|
||||||
|
Key: aws.String(k),
|
||||||
|
Values: vvvvv,
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
var resourceTypes []*string
|
||||||
|
resourceTypes = append(resourceTypes, &resourceType)
|
||||||
|
|
||||||
|
resources, err := e.resourceGroupsGetResources(region, filters, resourceTypes)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
result := make([]suggestData, 0)
|
||||||
|
for _, resource := range resources.ResourceTagMappingList {
|
||||||
|
data := *resource.ResourceARN
|
||||||
|
result = append(result, suggestData{Text: data, Value: data})
|
||||||
|
}
|
||||||
|
|
||||||
|
return result, nil
|
||||||
|
}
|
||||||
|
|
||||||
func (e *CloudWatchExecutor) cloudwatchListMetrics(region string, namespace string, metricName string, dimensions []*cloudwatch.DimensionFilter) (*cloudwatch.ListMetricsOutput, error) {
|
func (e *CloudWatchExecutor) cloudwatchListMetrics(region string, namespace string, metricName string, dimensions []*cloudwatch.DimensionFilter) (*cloudwatch.ListMetricsOutput, error) {
|
||||||
svc, err := e.getClient(region)
|
svc, err := e.getClient(region)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
@@ -587,6 +649,28 @@ func (e *CloudWatchExecutor) ec2DescribeInstances(region string, filters []*ec2.
|
|||||||
return &resp, nil
|
return &resp, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (e *CloudWatchExecutor) resourceGroupsGetResources(region string, filters []*resourcegroupstaggingapi.TagFilter, resourceTypes []*string) (*resourcegroupstaggingapi.GetResourcesOutput, error) {
|
||||||
|
params := &resourcegroupstaggingapi.GetResourcesInput{
|
||||||
|
ResourceTypeFilters: resourceTypes,
|
||||||
|
TagFilters: filters,
|
||||||
|
}
|
||||||
|
|
||||||
|
var resp resourcegroupstaggingapi.GetResourcesOutput
|
||||||
|
err := e.rgtaSvc.GetResourcesPages(params,
|
||||||
|
func(page *resourcegroupstaggingapi.GetResourcesOutput, lastPage bool) bool {
|
||||||
|
resources, _ := awsutil.ValuesAtPath(page, "ResourceTagMappingList")
|
||||||
|
for _, resource := range resources {
|
||||||
|
resp.ResourceTagMappingList = append(resp.ResourceTagMappingList, resource.(*resourcegroupstaggingapi.ResourceTagMapping))
|
||||||
|
}
|
||||||
|
return !lastPage
|
||||||
|
})
|
||||||
|
if err != nil {
|
||||||
|
return nil, errors.New("Failed to call tags:GetResources")
|
||||||
|
}
|
||||||
|
|
||||||
|
return &resp, nil
|
||||||
|
}
|
||||||
|
|
||||||
func getAllMetrics(cwData *DatasourceInfo) (cloudwatch.ListMetricsOutput, error) {
|
func getAllMetrics(cwData *DatasourceInfo) (cloudwatch.ListMetricsOutput, error) {
|
||||||
creds, err := GetCredentials(cwData)
|
creds, err := GetCredentials(cwData)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
|
|||||||
@@ -232,6 +232,14 @@ export default class CloudWatchDatasource {
|
|||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
getResourceARNs(region, resourceType, tags) {
|
||||||
|
return this.doMetricQueryRequest('resource_arns', {
|
||||||
|
region: this.templateSrv.replace(this.getActualRegion(region)),
|
||||||
|
resourceType: this.templateSrv.replace(resourceType),
|
||||||
|
tags: tags,
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
metricFindQuery(query) {
|
metricFindQuery(query) {
|
||||||
let region;
|
let region;
|
||||||
let namespace;
|
let namespace;
|
||||||
@@ -293,6 +301,15 @@ export default class CloudWatchDatasource {
|
|||||||
return this.getEc2InstanceAttribute(region, targetAttributeName, filterJson);
|
return this.getEc2InstanceAttribute(region, targetAttributeName, filterJson);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
const resourceARNsQuery = query.match(/^resource_arns\(([^,]+?),\s?([^,]+?),\s?(.+?)\)/);
|
||||||
|
if (resourceARNsQuery) {
|
||||||
|
region = resourceARNsQuery[1];
|
||||||
|
const resourceType = resourceARNsQuery[2];
|
||||||
|
const tagsJSON = JSON.parse(this.templateSrv.replace(resourceARNsQuery[3]));
|
||||||
|
return this.getResourceARNs(region, resourceType, tagsJSON);
|
||||||
|
}
|
||||||
|
|
||||||
return this.$q.when([]);
|
return this.$q.when([]);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user