Files
grafana/public/app/plugins/datasource/cloudwatch/datasource.js

372 lines
12 KiB
JavaScript
Raw Normal View History

2015-07-30 11:37:31 +09:00
define([
'angular',
'lodash',
'moment',
2015-10-28 12:14:06 +09:00
'app/core/utils/datemath',
2015-07-30 11:37:31 +09:00
],
2015-10-28 12:14:06 +09:00
function (angular, _, moment, dateMath) {
2015-07-30 11:37:31 +09:00
'use strict';
/** @ngInject */
function CloudWatchDatasource(instanceSettings, $q, backendSrv, templateSrv) {
this.type = 'cloudwatch';
this.name = instanceSettings.name;
this.supportMetrics = true;
this.proxyUrl = instanceSettings.url;
this.defaultRegion = instanceSettings.jsonData.defaultRegion;
2015-07-30 11:37:31 +09:00
this.query = function(options) {
2015-10-28 12:14:06 +09:00
var start = convertToCloudWatchTime(options.range.from, false);
var end = convertToCloudWatchTime(options.range.to, true);
2015-07-30 11:37:31 +09:00
var queries = [];
2015-12-07 13:40:54 +09:00
options = angular.copy(options);
2015-07-30 11:37:31 +09:00
_.each(options.targets, _.bind(function(target) {
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);
query.statistics = target.statistics;
2015-07-30 11:37:31 +09:00
2015-08-13 21:20:47 +09:00
var range = end - start;
query.period = parseInt(target.period, 10) || (query.namespace === 'AWS/EC2' ? 300 : 60);
2015-07-30 11:37:31 +09:00
if (range / query.period >= 1440) {
query.period = Math.ceil(range / 1440 / 60) * 60;
2015-07-30 11:37:31 +09:00
}
target.period = query.period;
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;
}
var allQueryPromise = _.map(queries, function(query) {
2015-07-30 11:37:31 +09:00
return this.performTimeSeriesQuery(query, start, end);
}, this);
2015-07-30 11:37:31 +09:00
return $q.all(allQueryPromise).then(function(allResponse) {
var result = [];
2015-07-30 11:37:31 +09:00
_.each(allResponse, function(response, index) {
var metrics = transformMetricData(response, options.targets[index], options.scopedVars);
result = result.concat(metrics);
2015-07-30 11:37:31 +09:00
});
return { data: result };
});
2015-07-30 11:37:31 +09:00
};
this.performTimeSeriesQuery = function(query, start, end) {
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
};
this.getRegions = function() {
return this.awsRequest({action: '__GetRegions'});
2015-08-11 10:25:25 +09:00
};
this.getNamespaces = function() {
return this.awsRequest({action: '__GetNamespaces'});
2015-08-11 10:25:25 +09:00
};
2016-01-14 19:02:04 +09:00
this.getMetrics = function(namespace, region) {
return this.awsRequest({
action: '__GetMetrics',
2016-01-14 19:02:04 +09:00
region: region,
parameters: {
namespace: templateSrv.replace(namespace)
}
});
2015-08-11 10:25:25 +09:00
};
2016-01-14 19:02:04 +09:00
this.getDimensionKeys = function(namespace, region) {
return this.awsRequest({
action: '__GetDimensions',
2016-01-14 19:02:04 +09:00
region: region,
parameters: {
namespace: templateSrv.replace(namespace)
}
});
2015-08-11 10:25:25 +09:00
};
this.getDimensionValues = function(region, namespace, metricName, dimensionKey, filterDimensions) {
var request = {
region: templateSrv.replace(region),
action: 'ListMetrics',
parameters: {
namespace: templateSrv.replace(namespace),
metricName: templateSrv.replace(metricName),
dimensions: convertDimensionFormat(filterDimensions, {}),
}
};
2015-08-11 10:25:25 +09:00
return this.awsRequest(request).then(function(result) {
2015-11-17 22:49:46 +09:00
return _.chain(result.Metrics)
.pluck('Dimensions')
.flatten()
.filter(function(dimension) {
2015-12-04 20:16:31 +09:00
return dimension !== null && dimension.Name === dimensionKey;
})
2015-11-16 16:47:28 +09:00
.pluck('Value')
.uniq()
.sortBy()
.map(function(value) {
return {value: value, text: value};
}).value();
2015-07-30 11:37:31 +09:00
});
};
this.performEC2DescribeInstances = function(region, filters, instanceIds) {
return this.awsRequest({
region: region,
action: 'DescribeInstances',
parameters: { filter: filters, instanceIds: instanceIds }
});
};
this.metricFindQuery = function(query) {
2015-08-11 00:09:25 +09:00
var region;
var namespace;
var metricName;
var transformSuggestData = function(suggestData) {
return _.map(suggestData, function(v) {
return { text: v };
});
};
var regionQuery = query.match(/^regions\(\)/);
2015-08-11 00:09:25 +09:00
if (regionQuery) {
return this.getRegions();
2015-08-11 00:09:25 +09:00
}
var namespaceQuery = query.match(/^namespaces\(\)/);
2015-08-11 00:09:25 +09:00
if (namespaceQuery) {
return this.getNamespaces();
2015-08-11 00:09:25 +09:00
}
2016-01-14 19:02:04 +09:00
var metricNameQuery = query.match(/^metrics\(([^\)]+?)(,\s?([^,]+?))?\)/);
2015-08-11 00:09:25 +09:00
if (metricNameQuery) {
2016-01-14 19:02:04 +09:00
return this.getMetrics(metricNameQuery[1], metricNameQuery[3]);
2015-08-11 00:09:25 +09:00
}
2016-01-14 19:02:04 +09:00
var dimensionKeysQuery = query.match(/^dimension_keys\(([^\)]+?)(,\s?([^,]+?))?\)/);
2015-08-11 00:09:25 +09:00
if (dimensionKeysQuery) {
2016-01-14 19:02:04 +09:00
return this.getDimensionKeys(dimensionKeysQuery[1], dimensionKeysQuery[3]);
2015-08-11 00:09:25 +09:00
}
2015-12-04 01:02:25 +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]);
var dimensionKey = templateSrv.replace(dimensionValuesQuery[4]);
2015-12-04 01:02:25 +09:00
return this.getDimensionValues(region, namespace, metricName, dimensionKey, {});
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
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([]);
};
this.performDescribeAlarmsForMetric = function(region, namespace, metricName, dimensions, statistic, period) {
2015-10-26 15:47:34 +09:00
return this.awsRequest({
region: region,
action: 'DescribeAlarmsForMetric',
parameters: { namespace: namespace, metricName: metricName, dimensions: dimensions, statistic: statistic, period: period }
});
};
this.performDescribeAlarmHistory = function(region, alarmName, startDate, endDate) {
2015-10-26 15:47:34 +09:00
return this.awsRequest({
region: region,
action: 'DescribeAlarmHistory',
parameters: { alarmName: alarmName, startDate: startDate, endDate: endDate }
});
};
this.annotationQuery = function(options) {
var annotation = options.annotation;
2015-10-26 15:47:34 +09:00
var region = templateSrv.replace(annotation.region);
var namespace = templateSrv.replace(annotation.namespace);
var metricName = templateSrv.replace(annotation.metricName);
2015-11-29 22:25:36 +09:00
var dimensions = convertDimensionFormat(annotation.dimensions);
var statistics = _.map(annotation.statistics, function(s) { return templateSrv.replace(s); });
2015-10-26 15:47:34 +09:00
var period = annotation.period || '300';
period = parseInt(period, 10);
2015-11-29 22:25:36 +09:00
if (!region || !namespace || !metricName || _.isEmpty(statistics)) { return $q.when([]); }
2015-10-26 15:47:34 +09:00
var d = $q.defer();
var self = this;
2015-11-29 22:25:36 +09:00
var allQueryPromise = _.map(statistics, function(statistic) {
return self.performDescribeAlarmsForMetric(region, namespace, metricName, dimensions, statistic, period);
});
$q.all(allQueryPromise).then(function(alarms) {
2015-10-26 15:47:34 +09:00
var eventList = [];
var start = convertToCloudWatchTime(options.range.from, false);
var end = convertToCloudWatchTime(options.range.to, true);
2015-11-29 22:25:36 +09:00
_.chain(alarms)
.pluck('MetricAlarms')
.flatten()
.each(function(alarm) {
if (!alarm) {
d.resolve(eventList);
return;
}
2015-10-26 15:47:34 +09:00
self.performDescribeAlarmHistory(region, alarm.AlarmName, start, end).then(function(history) {
_.each(history.AlarmHistoryItems, function(h) {
var event = {
annotation: annotation,
time: Date.parse(h.Timestamp),
title: h.AlarmName,
tags: [h.HistoryItemType],
text: h.HistorySummary
};
eventList.push(event);
});
d.resolve(eventList);
});
});
});
return d.promise;
};
this.testDatasource = function() {
2015-08-11 10:25:25 +09:00
/* use billing metrics for test */
var region = this.defaultRegion;
2015-08-11 10:25:25 +09:00
var namespace = 'AWS/Billing';
var metricName = 'EstimatedCharges';
var dimensions = {};
2015-11-17 22:49:46 +09:00
return this.getDimensionValues(region, namespace, metricName, 'ServiceName', dimensions).then(function () {
2015-07-30 11:37:31 +09:00
return { status: 'success', message: 'Data source is working', title: 'Success' };
});
};
this.awsRequest = function(data) {
var options = {
method: 'POST',
url: this.proxyUrl,
data: data
};
2015-08-13 21:20:47 +09:00
return backendSrv.datasourceRequest(options).then(function(result) {
return result.data;
});
2015-08-10 23:15:25 +09:00
};
this.getDefaultRegion = function() {
2015-08-10 23:15:25 +09:00
return this.defaultRegion;
};
function transformMetricData(md, options, scopedVars) {
var aliasRegex = /\{\{(.+?)\}\}/g;
var aliasPattern = options.alias || '{{metric}}_{{stat}}';
var aliasData = {
region: templateSrv.replace(options.region, scopedVars),
namespace: templateSrv.replace(options.namespace, scopedVars),
metric: templateSrv.replace(options.metricName, scopedVars),
};
var aliasDimensions = {};
_.each(_.keys(options.dimensions), function(origKey) {
var key = templateSrv.replace(origKey, scopedVars);
var value = templateSrv.replace(options.dimensions[origKey], scopedVars);
aliasDimensions[key] = value;
});
_.extend(aliasData, aliasDimensions);
var periodMs = options.period * 1000;
return _.map(options.statistics, function(stat) {
var dps = [];
var lastTimestamp = null;
_.chain(md.Datapoints)
.sortBy(function(dp) {
return dp.Timestamp;
})
.each(function(dp) {
var timestamp = new Date(dp.Timestamp).getTime();
2015-11-20 13:06:25 +09:00
if (lastTimestamp && (timestamp - lastTimestamp) > periodMs) {
dps.push([null, lastTimestamp + periodMs]);
}
lastTimestamp = timestamp;
dps.push([dp[stat], timestamp]);
});
2015-07-30 11:37:31 +09: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
});
return {target: seriesName, datapoints: dps};
2015-08-06 14:41:06 +09:00
});
}
2015-10-28 12:14:06 +09:00
function convertToCloudWatchTime(date, roundUp) {
if (_.isString(date)) {
date = dateMath.parse(date, roundUp);
}
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) {
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: CloudWatchDatasource
};
2015-07-30 11:37:31 +09:00
});