2015-07-29 21:37:31 -05:00
|
|
|
define([
|
|
|
|
'angular',
|
|
|
|
'lodash',
|
|
|
|
'moment',
|
2015-10-01 14:20:52 -05:00
|
|
|
'./query_ctrl',
|
2015-08-31 12:07:25 -05:00
|
|
|
'./directives',
|
2015-07-29 21:37:31 -05:00
|
|
|
],
|
2015-09-25 12:10:22 -05:00
|
|
|
function (angular, _) {
|
2015-07-29 21:37:31 -05:00
|
|
|
'use strict';
|
|
|
|
|
|
|
|
var module = angular.module('grafana.services');
|
|
|
|
|
2015-10-02 02:04:46 -05:00
|
|
|
module.factory('CloudWatchDatasource', function($q, backendSrv, templateSrv) {
|
2015-07-29 21:37:31 -05:00
|
|
|
|
|
|
|
function CloudWatchDatasource(datasource) {
|
|
|
|
this.type = 'cloudwatch';
|
|
|
|
this.name = datasource.name;
|
|
|
|
this.supportMetrics = true;
|
2015-08-13 07:20:47 -05:00
|
|
|
this.proxyUrl = datasource.url;
|
2015-08-10 09:15:25 -05:00
|
|
|
this.defaultRegion = datasource.jsonData.defaultRegion;
|
2015-07-29 21:37:31 -05:00
|
|
|
}
|
|
|
|
|
|
|
|
CloudWatchDatasource.prototype.query = function(options) {
|
|
|
|
var start = convertToCloudWatchTime(options.range.from);
|
|
|
|
var end = convertToCloudWatchTime(options.range.to);
|
|
|
|
|
|
|
|
var queries = [];
|
|
|
|
_.each(options.targets, _.bind(function(target) {
|
2015-10-02 02:04:46 -05:00
|
|
|
if (target.hide || !target.namespace || !target.metricName || _.isEmpty(target.statistics)) {
|
2015-07-29 21:37:31 -05:00
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
|
|
|
var query = {};
|
2015-08-10 09:15:25 -05:00
|
|
|
query.region = templateSrv.replace(target.region, options.scopedVars);
|
2015-07-29 21:37:31 -05:00
|
|
|
query.namespace = templateSrv.replace(target.namespace, options.scopedVars);
|
|
|
|
query.metricName = templateSrv.replace(target.metricName, options.scopedVars);
|
2015-08-25 01:45:09 -05:00
|
|
|
query.dimensions = convertDimensionFormat(target.dimensions);
|
2015-10-08 06:09:15 -05:00
|
|
|
query.statistics = target.statistics;
|
2015-08-13 07:20:47 -05:00
|
|
|
query.period = parseInt(target.period, 10);
|
2015-07-29 21:37:31 -05:00
|
|
|
|
2015-08-13 07:20:47 -05:00
|
|
|
var range = end - start;
|
2015-07-29 21:37:31 -05:00
|
|
|
// CloudWatch limit datapoints up to 1440
|
|
|
|
if (range / query.period >= 1440) {
|
|
|
|
query.period = Math.floor(range / 1440 / 60) * 60;
|
|
|
|
}
|
|
|
|
|
|
|
|
queries.push(query);
|
|
|
|
}, this));
|
|
|
|
|
|
|
|
// No valid targets, return the empty result to save a round trip.
|
|
|
|
if (_.isEmpty(queries)) {
|
|
|
|
var d = $q.defer();
|
|
|
|
d.resolve({ data: [] });
|
|
|
|
return d.promise;
|
|
|
|
}
|
|
|
|
|
2015-10-02 00:25:54 -05:00
|
|
|
var allQueryPromise = _.map(queries, function(query) {
|
2015-07-29 21:37:31 -05:00
|
|
|
return this.performTimeSeriesQuery(query, start, end);
|
2015-10-02 00:25:54 -05:00
|
|
|
}, this);
|
2015-07-29 21:37:31 -05:00
|
|
|
|
2015-10-02 00:25:54 -05:00
|
|
|
return $q.all(allQueryPromise).then(function(allResponse) {
|
|
|
|
var result = [];
|
2015-07-29 21:37:31 -05:00
|
|
|
|
2015-10-02 00:25:54 -05:00
|
|
|
_.each(allResponse, function(response, index) {
|
2015-10-02 00:32:21 -05:00
|
|
|
var metrics = transformMetricData(response, options.targets[index]);
|
2015-10-02 00:25:54 -05:00
|
|
|
result = result.concat(metrics);
|
2015-07-29 21:37:31 -05:00
|
|
|
});
|
2015-10-02 00:25:54 -05:00
|
|
|
|
|
|
|
return { data: result };
|
|
|
|
});
|
2015-07-29 21:37:31 -05:00
|
|
|
};
|
|
|
|
|
|
|
|
CloudWatchDatasource.prototype.performTimeSeriesQuery = function(query, start, end) {
|
2015-10-02 02:04:46 -05:00
|
|
|
return this.awsRequest({
|
|
|
|
region: query.region,
|
|
|
|
action: 'GetMetricStatistics',
|
|
|
|
parameters: {
|
|
|
|
namespace: query.namespace,
|
|
|
|
metricName: query.metricName,
|
|
|
|
dimensions: query.dimensions,
|
|
|
|
statistics: query.statistics,
|
|
|
|
startTime: start,
|
|
|
|
endTime: end,
|
|
|
|
period: query.period
|
|
|
|
}
|
|
|
|
});
|
2015-07-29 21:37:31 -05:00
|
|
|
};
|
|
|
|
|
2015-10-01 14:20:52 -05:00
|
|
|
CloudWatchDatasource.prototype.getRegions = function() {
|
2015-10-02 04:10:21 -05:00
|
|
|
return this.awsRequest({action: '__GetRegions'});
|
2015-08-10 20:25:25 -05:00
|
|
|
};
|
|
|
|
|
2015-10-01 14:20:52 -05:00
|
|
|
CloudWatchDatasource.prototype.getNamespaces = function() {
|
2015-10-02 04:54:35 -05:00
|
|
|
return this.awsRequest({action: '__GetNamespaces'});
|
2015-08-10 20:25:25 -05:00
|
|
|
};
|
|
|
|
|
2015-10-01 14:20:52 -05:00
|
|
|
CloudWatchDatasource.prototype.getMetrics = function(namespace) {
|
2015-10-02 04:54:35 -05:00
|
|
|
return this.awsRequest({
|
|
|
|
action: '__GetMetrics',
|
|
|
|
parameters: {
|
|
|
|
namespace: templateSrv.replace(namespace)
|
|
|
|
}
|
|
|
|
});
|
2015-08-10 20:25:25 -05:00
|
|
|
};
|
|
|
|
|
2015-10-01 14:20:52 -05:00
|
|
|
CloudWatchDatasource.prototype.getDimensionKeys = function(namespace) {
|
2015-10-02 13:25:28 -05:00
|
|
|
return this.awsRequest({
|
|
|
|
action: '__GetDimensions',
|
|
|
|
parameters: {
|
|
|
|
namespace: templateSrv.replace(namespace)
|
|
|
|
}
|
|
|
|
});
|
2015-08-10 20:25:25 -05:00
|
|
|
};
|
|
|
|
|
2015-10-01 14:20:52 -05:00
|
|
|
CloudWatchDatasource.prototype.getDimensionValues = function(region, namespace, metricName, dimensions) {
|
2015-10-02 02:04:46 -05:00
|
|
|
var request = {
|
|
|
|
region: templateSrv.replace(region),
|
|
|
|
action: 'ListMetrics',
|
|
|
|
parameters: {
|
|
|
|
namespace: templateSrv.replace(namespace),
|
|
|
|
metricName: templateSrv.replace(metricName),
|
|
|
|
dimensions: convertDimensionFormat(dimensions),
|
|
|
|
}
|
|
|
|
};
|
2015-08-10 20:25:25 -05:00
|
|
|
|
2015-10-02 02:04:46 -05:00
|
|
|
return this.awsRequest(request).then(function(result) {
|
2015-10-02 00:32:21 -05:00
|
|
|
return _.chain(result.Metrics).map(function(metric) {
|
2015-10-02 02:04:46 -05:00
|
|
|
return _.pluck(metric.Dimensions, 'Value');
|
|
|
|
}).flatten().uniq().sortBy(function(name) {
|
|
|
|
return name;
|
2015-10-08 04:37:47 -05:00
|
|
|
}).map(function(value) {
|
|
|
|
return {value: value, text: value};
|
2015-10-01 14:20:52 -05:00
|
|
|
}).value();
|
2015-07-29 21:37:31 -05:00
|
|
|
});
|
|
|
|
};
|
|
|
|
|
2015-09-18 06:00:11 -05:00
|
|
|
CloudWatchDatasource.prototype.performEC2DescribeInstances = function(region, filters, instanceIds) {
|
2015-10-02 02:04:46 -05:00
|
|
|
return this.awsRequest({
|
|
|
|
region: region,
|
|
|
|
action: 'DescribeInstances',
|
2015-10-08 04:37:47 -05:00
|
|
|
parameters: { filter: filters, instanceIds: instanceIds }
|
2015-10-02 02:04:46 -05:00
|
|
|
});
|
2015-08-18 00:09:42 -05:00
|
|
|
};
|
|
|
|
|
2015-08-10 10:09:25 -05:00
|
|
|
CloudWatchDatasource.prototype.metricFindQuery = function(query) {
|
|
|
|
var region;
|
|
|
|
var namespace;
|
|
|
|
var metricName;
|
|
|
|
|
|
|
|
var transformSuggestData = function(suggestData) {
|
|
|
|
return _.map(suggestData, function(v) {
|
|
|
|
return { text: v };
|
|
|
|
});
|
|
|
|
};
|
|
|
|
|
2015-10-02 04:10:21 -05:00
|
|
|
var regionQuery = query.match(/^regions\(\)/);
|
2015-08-10 10:09:25 -05:00
|
|
|
if (regionQuery) {
|
2015-10-02 04:10:21 -05:00
|
|
|
return this.getRegions();
|
2015-08-10 10:09:25 -05:00
|
|
|
}
|
|
|
|
|
2015-10-02 04:10:21 -05:00
|
|
|
var namespaceQuery = query.match(/^namespaces\(\)/);
|
2015-08-10 10:09:25 -05:00
|
|
|
if (namespaceQuery) {
|
2015-10-02 04:54:35 -05:00
|
|
|
return this.getNamespaces();
|
2015-08-10 10:09:25 -05:00
|
|
|
}
|
|
|
|
|
|
|
|
var metricNameQuery = query.match(/^metrics\(([^\)]+?)\)/);
|
|
|
|
if (metricNameQuery) {
|
2015-10-02 04:54:35 -05:00
|
|
|
return this.getMetrics(metricNameQuery[1]);
|
2015-08-10 10:09:25 -05:00
|
|
|
}
|
|
|
|
|
|
|
|
var dimensionKeysQuery = query.match(/^dimension_keys\(([^\)]+?)\)/);
|
|
|
|
if (dimensionKeysQuery) {
|
2015-10-02 13:25:28 -05:00
|
|
|
return this.getDimensionKeys(dimensionKeysQuery[1]);
|
2015-08-10 10:09:25 -05:00
|
|
|
}
|
|
|
|
|
2015-08-21 02:06:55 -05:00
|
|
|
var dimensionValuesQuery = query.match(/^dimension_values\(([^,]+?),\s?([^,]+?),\s?([^,]+?)(,\s?([^)]*))?\)/);
|
2015-08-10 10:09:25 -05:00
|
|
|
if (dimensionValuesQuery) {
|
2015-08-17 23:29:17 -05:00
|
|
|
region = templateSrv.replace(dimensionValuesQuery[1]);
|
|
|
|
namespace = templateSrv.replace(dimensionValuesQuery[2]);
|
|
|
|
metricName = templateSrv.replace(dimensionValuesQuery[3]);
|
2015-08-21 02:06:55 -05:00
|
|
|
var dimensionPart = templateSrv.replace(dimensionValuesQuery[5]);
|
|
|
|
|
2015-08-11 05:51:14 -05:00
|
|
|
var dimensions = {};
|
2015-08-21 02:06:55 -05:00
|
|
|
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];
|
|
|
|
});
|
|
|
|
}
|
2015-08-11 05:51:14 -05:00
|
|
|
|
2015-10-08 06:09:15 -05:00
|
|
|
return this.getDimensionValues(region, namespace, metricName, dimensions);
|
2015-08-10 10:09:25 -05:00
|
|
|
}
|
|
|
|
|
2015-09-17 04:07:40 -05:00
|
|
|
var ebsVolumeIdsQuery = query.match(/^ebs_volume_ids\(([^,]+?),\s?([^,]+?)\)/);
|
|
|
|
if (ebsVolumeIdsQuery) {
|
|
|
|
region = templateSrv.replace(ebsVolumeIdsQuery[1]);
|
|
|
|
var instanceId = templateSrv.replace(ebsVolumeIdsQuery[2]);
|
2015-09-18 06:00:11 -05:00
|
|
|
var instanceIds = [
|
|
|
|
instanceId
|
|
|
|
];
|
2015-09-17 04:07:40 -05:00
|
|
|
|
2015-10-01 14:20:52 -05:00
|
|
|
return this.performEC2DescribeInstances(region, [], instanceIds).then(function(result) {
|
2015-09-18 06:00:11 -05:00
|
|
|
var volumeIds = _.map(result.Reservations[0].Instances[0].BlockDeviceMappings, function(mapping) {
|
2015-09-17 04:07:40 -05:00
|
|
|
return mapping.EBS.VolumeID;
|
|
|
|
});
|
|
|
|
|
2015-09-18 06:00:11 -05:00
|
|
|
return transformSuggestData(volumeIds);
|
2015-09-17 04:07:40 -05:00
|
|
|
});
|
|
|
|
}
|
|
|
|
|
2015-08-10 10:09:25 -05:00
|
|
|
return $q.when([]);
|
|
|
|
};
|
|
|
|
|
2015-07-29 21:37:31 -05:00
|
|
|
CloudWatchDatasource.prototype.testDatasource = function() {
|
2015-08-10 20:25:25 -05:00
|
|
|
/* use billing metrics for test */
|
|
|
|
var region = 'us-east-1';
|
|
|
|
var namespace = 'AWS/Billing';
|
|
|
|
var metricName = 'EstimatedCharges';
|
|
|
|
var dimensions = {};
|
|
|
|
|
2015-10-06 07:08:48 -05:00
|
|
|
return this.getDimensionValues(region, namespace, metricName, dimensions).then(function () {
|
2015-07-29 21:37:31 -05:00
|
|
|
return { status: 'success', message: 'Data source is working', title: 'Success' };
|
|
|
|
});
|
|
|
|
};
|
|
|
|
|
2015-10-02 02:04:46 -05:00
|
|
|
CloudWatchDatasource.prototype.awsRequest = function(data) {
|
|
|
|
var options = {
|
|
|
|
method: 'POST',
|
|
|
|
url: this.proxyUrl,
|
|
|
|
data: data
|
2015-10-01 10:38:55 -05:00
|
|
|
};
|
2015-08-13 07:20:47 -05:00
|
|
|
|
2015-10-02 02:04:46 -05:00
|
|
|
return backendSrv.datasourceRequest(options).then(function(result) {
|
|
|
|
return result.data;
|
|
|
|
});
|
2015-08-10 09:15:25 -05:00
|
|
|
};
|
|
|
|
|
|
|
|
CloudWatchDatasource.prototype.getDefaultRegion = function() {
|
|
|
|
return this.defaultRegion;
|
|
|
|
};
|
|
|
|
|
2015-07-29 21:37:31 -05:00
|
|
|
function transformMetricData(md, options) {
|
2015-10-08 06:09:15 -05:00
|
|
|
var aliasRegex = /\{\{(.+?)\}\}/g;
|
|
|
|
var aliasPattern = options.alias || '{{metric}}_{{stat}}';
|
|
|
|
var aliasData = {
|
|
|
|
region: templateSrv.replace(options.region),
|
|
|
|
namespace: templateSrv.replace(options.namespace),
|
|
|
|
metric: templateSrv.replace(options.metricName),
|
|
|
|
};
|
|
|
|
_.extend(aliasData, options.dimensions);
|
|
|
|
|
|
|
|
return _.map(options.statistics, function(stat) {
|
|
|
|
var dps = _.chain(md.Datapoints).map(function(dp) {
|
|
|
|
return [dp[stat], new Date(dp.Timestamp).getTime()];
|
|
|
|
})
|
|
|
|
.sortBy(function(dp) {
|
|
|
|
return dp[1];
|
|
|
|
}).value();
|
2015-07-29 21:37:31 -05:00
|
|
|
|
2015-10-08 06:09:15 -05:00
|
|
|
aliasData.stat = stat;
|
|
|
|
var seriesName = aliasPattern.replace(aliasRegex, function(match, g1) {
|
|
|
|
if (aliasData[g1]) {
|
|
|
|
return aliasData[g1];
|
|
|
|
}
|
|
|
|
return g1;
|
2015-07-29 21:37:31 -05:00
|
|
|
});
|
|
|
|
|
2015-10-08 06:09:15 -05:00
|
|
|
return {target: seriesName, datapoints: dps};
|
2015-08-06 00:41:06 -05:00
|
|
|
});
|
|
|
|
}
|
|
|
|
|
2015-07-29 21:37:31 -05:00
|
|
|
function convertToCloudWatchTime(date) {
|
2015-09-25 12:15:27 -05:00
|
|
|
return Math.round(date.valueOf() / 1000);
|
2015-07-29 21:37:31 -05:00
|
|
|
}
|
|
|
|
|
2015-08-25 01:45:09 -05:00
|
|
|
function convertDimensionFormat(dimensions) {
|
2015-10-08 06:09:15 -05:00
|
|
|
return _.map(dimensions, function(value, key) {
|
2015-08-25 01:45:09 -05:00
|
|
|
return {
|
|
|
|
Name: templateSrv.replace(key),
|
2015-10-08 06:09:15 -05:00
|
|
|
Value: templateSrv.replace(value)
|
2015-08-25 01:45:09 -05:00
|
|
|
};
|
|
|
|
});
|
|
|
|
}
|
|
|
|
|
2015-07-29 21:37:31 -05:00
|
|
|
return CloudWatchDatasource;
|
|
|
|
});
|
|
|
|
|
|
|
|
});
|