2015-07-30 11:37:31 +09:00
|
|
|
define([
|
|
|
|
|
'angular',
|
|
|
|
|
'lodash',
|
|
|
|
|
'moment',
|
2015-10-01 21:20:52 +02:00
|
|
|
'./query_ctrl',
|
2015-08-31 19:07:25 +02:00
|
|
|
'./directives',
|
2015-07-30 11:37:31 +09:00
|
|
|
],
|
2015-09-26 02:10:22 +09:00
|
|
|
function (angular, _) {
|
2015-07-30 11:37:31 +09:00
|
|
|
'use strict';
|
|
|
|
|
|
|
|
|
|
var module = angular.module('grafana.services');
|
|
|
|
|
|
2015-10-02 09:04:46 +02:00
|
|
|
module.factory('CloudWatchDatasource', function($q, backendSrv, templateSrv) {
|
2015-07-30 11:37:31 +09:00
|
|
|
|
|
|
|
|
function CloudWatchDatasource(datasource) {
|
|
|
|
|
this.type = 'cloudwatch';
|
|
|
|
|
this.name = datasource.name;
|
|
|
|
|
this.supportMetrics = true;
|
2015-08-13 21:20:47 +09:00
|
|
|
this.proxyUrl = datasource.url;
|
2015-08-10 23:15:25 +09:00
|
|
|
this.defaultRegion = datasource.jsonData.defaultRegion;
|
2015-07-30 11:37:31 +09: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 09:04:46 +02:00
|
|
|
if (target.hide || !target.namespace || !target.metricName || _.isEmpty(target.statistics)) {
|
2015-07-30 11:37:31 +09:00
|
|
|
return;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
var query = {};
|
2015-08-10 23:15:25 +09:00
|
|
|
query.region = templateSrv.replace(target.region, options.scopedVars);
|
2015-07-30 11:37:31 +09:00
|
|
|
query.namespace = templateSrv.replace(target.namespace, options.scopedVars);
|
|
|
|
|
query.metricName = templateSrv.replace(target.metricName, options.scopedVars);
|
2015-10-28 19:36:49 +09:00
|
|
|
query.dimensions = convertDimensionFormat(target.dimensions, options.scopedVars);
|
2015-10-08 13:09:15 +02:00
|
|
|
query.statistics = target.statistics;
|
2015-07-30 11:37:31 +09:00
|
|
|
|
2015-08-13 21:20:47 +09:00
|
|
|
var range = end - start;
|
2015-10-29 16:46:58 +01:00
|
|
|
query.period = parseInt(target.period, 10) || 60;
|
|
|
|
|
|
2015-07-30 11:37:31 +09:00
|
|
|
if (range / query.period >= 1440) {
|
2015-10-29 16:46:58 +01:00
|
|
|
query.period = Math.ceil(range / 1440 / 60) * 60;
|
2015-07-30 11:37:31 +09:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
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 07:25:54 +02:00
|
|
|
var allQueryPromise = _.map(queries, function(query) {
|
2015-07-30 11:37:31 +09:00
|
|
|
return this.performTimeSeriesQuery(query, start, end);
|
2015-10-02 07:25:54 +02:00
|
|
|
}, this);
|
2015-07-30 11:37:31 +09:00
|
|
|
|
2015-10-02 07:25:54 +02:00
|
|
|
return $q.all(allQueryPromise).then(function(allResponse) {
|
|
|
|
|
var result = [];
|
2015-07-30 11:37:31 +09:00
|
|
|
|
2015-10-02 07:25:54 +02:00
|
|
|
_.each(allResponse, function(response, index) {
|
2015-10-02 07:32:21 +02:00
|
|
|
var metrics = transformMetricData(response, options.targets[index]);
|
2015-10-02 07:25:54 +02:00
|
|
|
result = result.concat(metrics);
|
2015-07-30 11:37:31 +09:00
|
|
|
});
|
2015-10-02 07:25:54 +02:00
|
|
|
|
|
|
|
|
return { data: result };
|
|
|
|
|
});
|
2015-07-30 11:37:31 +09:00
|
|
|
};
|
|
|
|
|
|
|
|
|
|
CloudWatchDatasource.prototype.performTimeSeriesQuery = function(query, start, end) {
|
2015-10-02 09:04:46 +02: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-30 11:37:31 +09:00
|
|
|
};
|
|
|
|
|
|
2015-10-01 21:20:52 +02:00
|
|
|
CloudWatchDatasource.prototype.getRegions = function() {
|
2015-10-02 11:10:21 +02:00
|
|
|
return this.awsRequest({action: '__GetRegions'});
|
2015-08-11 10:25:25 +09:00
|
|
|
};
|
|
|
|
|
|
2015-10-01 21:20:52 +02:00
|
|
|
CloudWatchDatasource.prototype.getNamespaces = function() {
|
2015-10-02 11:54:35 +02:00
|
|
|
return this.awsRequest({action: '__GetNamespaces'});
|
2015-08-11 10:25:25 +09:00
|
|
|
};
|
|
|
|
|
|
2015-10-01 21:20:52 +02:00
|
|
|
CloudWatchDatasource.prototype.getMetrics = function(namespace) {
|
2015-10-02 11:54:35 +02:00
|
|
|
return this.awsRequest({
|
|
|
|
|
action: '__GetMetrics',
|
|
|
|
|
parameters: {
|
|
|
|
|
namespace: templateSrv.replace(namespace)
|
|
|
|
|
}
|
|
|
|
|
});
|
2015-08-11 10:25:25 +09:00
|
|
|
};
|
|
|
|
|
|
2015-10-01 21:20:52 +02:00
|
|
|
CloudWatchDatasource.prototype.getDimensionKeys = function(namespace) {
|
2015-10-02 20:25:28 +02:00
|
|
|
return this.awsRequest({
|
|
|
|
|
action: '__GetDimensions',
|
|
|
|
|
parameters: {
|
|
|
|
|
namespace: templateSrv.replace(namespace)
|
|
|
|
|
}
|
|
|
|
|
});
|
2015-08-11 10:25:25 +09:00
|
|
|
};
|
|
|
|
|
|
2015-10-01 21:20:52 +02:00
|
|
|
CloudWatchDatasource.prototype.getDimensionValues = function(region, namespace, metricName, dimensions) {
|
2015-10-02 09:04:46 +02:00
|
|
|
var request = {
|
|
|
|
|
region: templateSrv.replace(region),
|
|
|
|
|
action: 'ListMetrics',
|
|
|
|
|
parameters: {
|
|
|
|
|
namespace: templateSrv.replace(namespace),
|
|
|
|
|
metricName: templateSrv.replace(metricName),
|
2015-10-28 19:36:49 +09:00
|
|
|
dimensions: convertDimensionFormat(dimensions, {}),
|
2015-10-02 09:04:46 +02:00
|
|
|
}
|
|
|
|
|
};
|
2015-08-11 10:25:25 +09:00
|
|
|
|
2015-10-02 09:04:46 +02:00
|
|
|
return this.awsRequest(request).then(function(result) {
|
2015-11-10 19:25:59 +09:00
|
|
|
return _.pluck(result.Metrics, 'Dimensions');
|
2015-07-30 11:37:31 +09:00
|
|
|
});
|
|
|
|
|
};
|
|
|
|
|
|
2015-09-18 20:00:11 +09:00
|
|
|
CloudWatchDatasource.prototype.performEC2DescribeInstances = function(region, filters, instanceIds) {
|
2015-10-02 09:04:46 +02:00
|
|
|
return this.awsRequest({
|
|
|
|
|
region: region,
|
|
|
|
|
action: 'DescribeInstances',
|
2015-10-08 11:37:47 +02:00
|
|
|
parameters: { filter: filters, instanceIds: instanceIds }
|
2015-10-02 09:04:46 +02:00
|
|
|
});
|
2015-08-18 14:09:42 +09:00
|
|
|
};
|
|
|
|
|
|
2015-08-11 00:09:25 +09: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 11:10:21 +02:00
|
|
|
var regionQuery = query.match(/^regions\(\)/);
|
2015-08-11 00:09:25 +09:00
|
|
|
if (regionQuery) {
|
2015-10-02 11:10:21 +02:00
|
|
|
return this.getRegions();
|
2015-08-11 00:09:25 +09:00
|
|
|
}
|
|
|
|
|
|
2015-10-02 11:10:21 +02:00
|
|
|
var namespaceQuery = query.match(/^namespaces\(\)/);
|
2015-08-11 00:09:25 +09:00
|
|
|
if (namespaceQuery) {
|
2015-10-02 11:54:35 +02:00
|
|
|
return this.getNamespaces();
|
2015-08-11 00:09:25 +09:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
var metricNameQuery = query.match(/^metrics\(([^\)]+?)\)/);
|
|
|
|
|
if (metricNameQuery) {
|
2015-10-02 11:54:35 +02:00
|
|
|
return this.getMetrics(metricNameQuery[1]);
|
2015-08-11 00:09:25 +09:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
var dimensionKeysQuery = query.match(/^dimension_keys\(([^\)]+?)\)/);
|
|
|
|
|
if (dimensionKeysQuery) {
|
2015-10-02 20:25:28 +02:00
|
|
|
return this.getDimensionKeys(dimensionKeysQuery[1]);
|
2015-08-11 00:09:25 +09:00
|
|
|
}
|
|
|
|
|
|
2015-08-21 16:06:55 +09:00
|
|
|
var dimensionValuesQuery = query.match(/^dimension_values\(([^,]+?),\s?([^,]+?),\s?([^,]+?)(,\s?([^)]*))?\)/);
|
2015-08-11 00:09:25 +09:00
|
|
|
if (dimensionValuesQuery) {
|
2015-08-18 13:29:17 +09:00
|
|
|
region = templateSrv.replace(dimensionValuesQuery[1]);
|
|
|
|
|
namespace = templateSrv.replace(dimensionValuesQuery[2]);
|
|
|
|
|
metricName = templateSrv.replace(dimensionValuesQuery[3]);
|
2015-08-21 16:06:55 +09:00
|
|
|
var dimensionPart = templateSrv.replace(dimensionValuesQuery[5]);
|
|
|
|
|
|
2015-08-11 19:51:14 +09:00
|
|
|
var dimensions = {};
|
2015-08-21 16:06:55 +09: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 19:51:14 +09:00
|
|
|
|
2015-11-10 19:25:59 +09:00
|
|
|
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 };
|
|
|
|
|
});
|
|
|
|
|
});
|
2015-08-11 00:09:25 +09:00
|
|
|
}
|
|
|
|
|
|
2015-09-17 18:07:40 +09: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 20:00:11 +09:00
|
|
|
var instanceIds = [
|
|
|
|
|
instanceId
|
|
|
|
|
];
|
2015-09-17 18:07:40 +09:00
|
|
|
|
2015-10-01 21:20:52 +02:00
|
|
|
return this.performEC2DescribeInstances(region, [], instanceIds).then(function(result) {
|
2015-09-18 20:00:11 +09:00
|
|
|
var volumeIds = _.map(result.Reservations[0].Instances[0].BlockDeviceMappings, function(mapping) {
|
2015-11-11 19:04:51 +09:00
|
|
|
return mapping.Ebs.VolumeId;
|
2015-09-17 18:07:40 +09:00
|
|
|
});
|
|
|
|
|
|
2015-09-18 20:00:11 +09:00
|
|
|
return transformSuggestData(volumeIds);
|
2015-09-17 18:07:40 +09:00
|
|
|
});
|
|
|
|
|
}
|
|
|
|
|
|
2015-08-11 00:09:25 +09:00
|
|
|
return $q.when([]);
|
|
|
|
|
};
|
|
|
|
|
|
2015-07-30 11:37:31 +09:00
|
|
|
CloudWatchDatasource.prototype.testDatasource = function() {
|
2015-08-11 10:25:25 +09:00
|
|
|
/* use billing metrics for test */
|
2015-11-09 03:35:51 -08:00
|
|
|
var region = this.defaultRegion;
|
2015-08-11 10:25:25 +09:00
|
|
|
var namespace = 'AWS/Billing';
|
|
|
|
|
var metricName = 'EstimatedCharges';
|
|
|
|
|
var dimensions = {};
|
|
|
|
|
|
2015-10-06 14:08:48 +02:00
|
|
|
return this.getDimensionValues(region, namespace, metricName, dimensions).then(function () {
|
2015-07-30 11:37:31 +09:00
|
|
|
return { status: 'success', message: 'Data source is working', title: 'Success' };
|
|
|
|
|
});
|
|
|
|
|
};
|
|
|
|
|
|
2015-10-02 09:04:46 +02:00
|
|
|
CloudWatchDatasource.prototype.awsRequest = function(data) {
|
|
|
|
|
var options = {
|
|
|
|
|
method: 'POST',
|
|
|
|
|
url: this.proxyUrl,
|
|
|
|
|
data: data
|
2015-10-01 17:38:55 +02:00
|
|
|
};
|
2015-08-13 21:20:47 +09:00
|
|
|
|
2015-10-02 09:04:46 +02:00
|
|
|
return backendSrv.datasourceRequest(options).then(function(result) {
|
|
|
|
|
return result.data;
|
|
|
|
|
});
|
2015-08-10 23:15:25 +09:00
|
|
|
};
|
|
|
|
|
|
|
|
|
|
CloudWatchDatasource.prototype.getDefaultRegion = function() {
|
|
|
|
|
return this.defaultRegion;
|
|
|
|
|
};
|
|
|
|
|
|
2015-07-30 11:37:31 +09:00
|
|
|
function transformMetricData(md, options) {
|
2015-10-08 13:09:15 +02: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-30 11:37:31 +09:00
|
|
|
|
2015-10-08 13:09:15 +02:00
|
|
|
aliasData.stat = stat;
|
|
|
|
|
var seriesName = aliasPattern.replace(aliasRegex, function(match, g1) {
|
|
|
|
|
if (aliasData[g1]) {
|
|
|
|
|
return aliasData[g1];
|
|
|
|
|
}
|
|
|
|
|
return g1;
|
2015-07-30 11:37:31 +09:00
|
|
|
});
|
|
|
|
|
|
2015-10-08 13:09:15 +02:00
|
|
|
return {target: seriesName, datapoints: dps};
|
2015-08-06 14:41:06 +09:00
|
|
|
});
|
|
|
|
|
}
|
|
|
|
|
|
2015-07-30 11:37:31 +09:00
|
|
|
function convertToCloudWatchTime(date) {
|
2015-09-26 02:15:27 +09:00
|
|
|
return Math.round(date.valueOf() / 1000);
|
2015-07-30 11:37:31 +09:00
|
|
|
}
|
|
|
|
|
|
2015-10-28 19:36:49 +09:00
|
|
|
function convertDimensionFormat(dimensions, scopedVars) {
|
2015-10-08 13:09:15 +02:00
|
|
|
return _.map(dimensions, function(value, key) {
|
2015-08-25 15:45:09 +09:00
|
|
|
return {
|
2015-10-28 19:36:49 +09:00
|
|
|
Name: templateSrv.replace(key, scopedVars),
|
|
|
|
|
Value: templateSrv.replace(value, scopedVars)
|
2015-08-25 15:45:09 +09:00
|
|
|
};
|
|
|
|
|
});
|
|
|
|
|
}
|
|
|
|
|
|
2015-07-30 11:37:31 +09:00
|
|
|
return CloudWatchDatasource;
|
|
|
|
|
});
|
|
|
|
|
|
|
|
|
|
});
|