diff --git a/pkg/api/cloudwatch/cloudwatch.go b/pkg/api/cloudwatch/cloudwatch.go
index babcf9fddb0..88d22800d65 100644
--- a/pkg/api/cloudwatch/cloudwatch.go
+++ b/pkg/api/cloudwatch/cloudwatch.go
@@ -33,6 +33,7 @@ func init() {
actionHandlers = map[string]actionHandler{
"GetMetricStatistics": handleGetMetricStatistics,
"ListMetrics": handleListMetrics,
+ "DescribeAlarms": handleDescribeAlarms,
"DescribeAlarmsForMetric": handleDescribeAlarmsForMetric,
"DescribeAlarmHistory": handleDescribeAlarmHistory,
"DescribeInstances": handleDescribeInstances,
@@ -142,6 +143,49 @@ func handleListMetrics(req *cwRequest, c *middleware.Context) {
c.JSON(200, resp)
}
+func handleDescribeAlarms(req *cwRequest, c *middleware.Context) {
+ cfg := &aws.Config{
+ Region: aws.String(req.Region),
+ Credentials: getCredentials(req.DataSource.Database),
+ }
+
+ svc := cloudwatch.New(session.New(cfg), cfg)
+
+ reqParam := &struct {
+ Parameters struct {
+ ActionPrefix string `json:"actionPrefix"`
+ AlarmNamePrefix string `json:"alarmNamePrefix"`
+ AlarmNames []*string `json:"alarmNames"`
+ StateValue string `json:"stateValue"`
+ } `json:"parameters"`
+ }{}
+ json.Unmarshal(req.Body, reqParam)
+
+ params := &cloudwatch.DescribeAlarmsInput{
+ MaxRecords: aws.Int64(100),
+ }
+ if reqParam.Parameters.ActionPrefix != "" {
+ params.ActionPrefix = aws.String(reqParam.Parameters.ActionPrefix)
+ }
+ if reqParam.Parameters.AlarmNamePrefix != "" {
+ params.AlarmNamePrefix = aws.String(reqParam.Parameters.AlarmNamePrefix)
+ }
+ if len(reqParam.Parameters.AlarmNames) != 0 {
+ params.AlarmNames = reqParam.Parameters.AlarmNames
+ }
+ if reqParam.Parameters.StateValue != "" {
+ params.StateValue = aws.String(reqParam.Parameters.StateValue)
+ }
+
+ resp, err := svc.DescribeAlarms(params)
+ if err != nil {
+ c.JsonApiErr(500, "Unable to call AWS API", err)
+ return
+ }
+
+ c.JSON(200, resp)
+}
+
func handleDescribeAlarmsForMetric(req *cwRequest, c *middleware.Context) {
cfg := &aws.Config{
Region: aws.String(req.Region),
diff --git a/public/app/plugins/datasource/cloudwatch/annotation_query.d.ts b/public/app/plugins/datasource/cloudwatch/annotation_query.d.ts
new file mode 100644
index 00000000000..c3318b8e133
--- /dev/null
+++ b/public/app/plugins/datasource/cloudwatch/annotation_query.d.ts
@@ -0,0 +1,2 @@
+declare var test: any;
+export default test;
diff --git a/public/app/plugins/datasource/cloudwatch/annotation_query.js b/public/app/plugins/datasource/cloudwatch/annotation_query.js
new file mode 100644
index 00000000000..46fbd3a87a9
--- /dev/null
+++ b/public/app/plugins/datasource/cloudwatch/annotation_query.js
@@ -0,0 +1,105 @@
+define([
+ 'lodash',
+],
+function (_) {
+ 'use strict';
+
+ function CloudWatchAnnotationQuery(datasource, annotation, $q, templateSrv) {
+ this.datasource = datasource;
+ this.annotation = annotation;
+ this.$q = $q;
+ this.templateSrv = templateSrv;
+ }
+
+ CloudWatchAnnotationQuery.prototype.process = function(from, to) {
+ var self = this;
+ var usePrefixMatch = this.annotation.prefixMatching;
+ var region = this.templateSrv.replace(this.annotation.region);
+ var namespace = this.templateSrv.replace(this.annotation.namespace);
+ var metricName = this.templateSrv.replace(this.annotation.metricName);
+ var dimensions = this.datasource.convertDimensionFormat(this.annotation.dimensions);
+ var statistics = _.map(this.annotation.statistics, function(s) { return self.templateSrv.replace(s); });
+ var defaultPeriod = usePrefixMatch ? '' : '300';
+ var period = this.annotation.period || defaultPeriod;
+ period = parseInt(period, 10);
+ var actionPrefix = this.annotation.actionPrefix || '';
+ var alarmNamePrefix = this.annotation.alarmNamePrefix || '';
+
+ var d = this.$q.defer();
+ var allQueryPromise;
+ if (usePrefixMatch) {
+ allQueryPromise = [
+ this.datasource.performDescribeAlarms(region, actionPrefix, alarmNamePrefix, [], '').then(function(alarms) {
+ alarms.MetricAlarms = self.filterAlarms(alarms, namespace, metricName, dimensions, statistics, period);
+ return alarms;
+ })
+ ];
+ } else {
+ if (!region || !namespace || !metricName || _.isEmpty(statistics)) { return this.$q.when([]); }
+
+ allQueryPromise = _.map(statistics, function(statistic) {
+ return self.datasource.performDescribeAlarmsForMetric(region, namespace, metricName, dimensions, statistic, period);
+ });
+ }
+ this.$q.all(allQueryPromise).then(function(alarms) {
+ var eventList = [];
+
+ var start = self.datasource.convertToCloudWatchTime(from, false);
+ var end = self.datasource.convertToCloudWatchTime(to, true);
+ _.chain(alarms)
+ .pluck('MetricAlarms')
+ .flatten()
+ .each(function(alarm) {
+ if (!alarm) {
+ d.resolve(eventList);
+ return;
+ }
+
+ self.datasource.performDescribeAlarmHistory(region, alarm.AlarmName, start, end).then(function(history) {
+ _.each(history.AlarmHistoryItems, function(h) {
+ var event = {
+ annotation: self.annotation,
+ time: Date.parse(h.Timestamp),
+ title: h.AlarmName,
+ tags: [h.HistoryItemType],
+ text: h.HistorySummary
+ };
+
+ eventList.push(event);
+ });
+
+ d.resolve(eventList);
+ });
+ });
+ });
+
+ return d.promise;
+ };
+
+ CloudWatchAnnotationQuery.prototype.filterAlarms = function(alarms, namespace, metricName, dimensions, statistics, period) {
+ return _.filter(alarms.MetricAlarms, function(alarm) {
+ if (!_.isEmpty(namespace) && alarm.Namespace !== namespace) {
+ return false;
+ }
+ if (!_.isEmpty(metricName) && alarm.MetricName !== metricName) {
+ return false;
+ }
+ var sd = function(d) {
+ return d.Name;
+ };
+ var isSameDimensions = JSON.stringify(_.sortBy(alarm.Dimensions, sd)) === JSON.stringify(_.sortBy(dimensions, sd));
+ if (!_.isEmpty(dimensions) && !isSameDimensions) {
+ return false;
+ }
+ if (!_.isEmpty(statistics) && !_.contains(statistics, alarm.Statistic)) {
+ return false;
+ }
+ if (!_.isNaN(period) && alarm.Period !== period) {
+ return false;
+ }
+ return true;
+ });
+ };
+
+ return CloudWatchAnnotationQuery;
+});
diff --git a/public/app/plugins/datasource/cloudwatch/datasource.js b/public/app/plugins/datasource/cloudwatch/datasource.js
index a2e6469014d..06c723e416a 100644
--- a/public/app/plugins/datasource/cloudwatch/datasource.js
+++ b/public/app/plugins/datasource/cloudwatch/datasource.js
@@ -3,8 +3,9 @@ define([
'lodash',
'moment',
'app/core/utils/datemath',
+ './annotation_query',
],
-function (angular, _, moment, dateMath) {
+function (angular, _, moment, dateMath, CloudWatchAnnotationQuery) {
'use strict';
/** @ngInject */
@@ -15,9 +16,10 @@ function (angular, _, moment, dateMath) {
this.proxyUrl = instanceSettings.url;
this.defaultRegion = instanceSettings.jsonData.defaultRegion;
+ var self = this;
this.query = function(options) {
- var start = convertToCloudWatchTime(options.range.from, false);
- var end = convertToCloudWatchTime(options.range.to, true);
+ var start = self.convertToCloudWatchTime(options.range.from, false);
+ var end = self.convertToCloudWatchTime(options.range.to, true);
var queries = [];
options = angular.copy(options);
@@ -30,7 +32,7 @@ function (angular, _, moment, dateMath) {
query.region = templateSrv.replace(target.region, options.scopedVars);
query.namespace = templateSrv.replace(target.namespace, options.scopedVars);
query.metricName = templateSrv.replace(target.metricName, options.scopedVars);
- query.dimensions = convertDimensionFormat(target.dimensions, options.scopedVars);
+ query.dimensions = self.convertDimensionFormat(target.dimensions, options.scopedVars);
query.statistics = target.statistics;
var range = end - start;
@@ -117,7 +119,7 @@ function (angular, _, moment, dateMath) {
parameters: {
namespace: templateSrv.replace(namespace),
metricName: templateSrv.replace(metricName),
- dimensions: convertDimensionFormat(filterDimensions, {}),
+ dimensions: this.convertDimensionFormat(filterDimensions, {}),
}
};
@@ -206,6 +208,14 @@ function (angular, _, moment, dateMath) {
return $q.when([]);
};
+ this.performDescribeAlarms = function(region, actionPrefix, alarmNamePrefix, alarmNames, stateValue) {
+ return this.awsRequest({
+ region: region,
+ action: 'DescribeAlarms',
+ parameters: { actionPrefix: actionPrefix, alarmNamePrefix: alarmNamePrefix, alarmNames: alarmNames, stateValue: stateValue }
+ });
+ };
+
this.performDescribeAlarmsForMetric = function(region, namespace, metricName, dimensions, statistic, period) {
return this.awsRequest({
region: region,
@@ -223,55 +233,8 @@ function (angular, _, moment, dateMath) {
};
this.annotationQuery = function(options) {
- var annotation = options.annotation;
- var region = templateSrv.replace(annotation.region);
- var namespace = templateSrv.replace(annotation.namespace);
- var metricName = templateSrv.replace(annotation.metricName);
- var dimensions = convertDimensionFormat(annotation.dimensions);
- var statistics = _.map(annotation.statistics, function(s) { return templateSrv.replace(s); });
- var period = annotation.period || '300';
- period = parseInt(period, 10);
-
- if (!region || !namespace || !metricName || _.isEmpty(statistics)) { return $q.when([]); }
-
- var d = $q.defer();
- var self = this;
- var allQueryPromise = _.map(statistics, function(statistic) {
- return self.performDescribeAlarmsForMetric(region, namespace, metricName, dimensions, statistic, period);
- });
- $q.all(allQueryPromise).then(function(alarms) {
- var eventList = [];
-
- var start = convertToCloudWatchTime(options.range.from, false);
- var end = convertToCloudWatchTime(options.range.to, true);
- _.chain(alarms)
- .pluck('MetricAlarms')
- .flatten()
- .each(function(alarm) {
- if (!alarm) {
- d.resolve(eventList);
- return;
- }
-
- 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;
+ var annotationQuery = new CloudWatchAnnotationQuery(this, options.annotation, $q, templateSrv);
+ return annotationQuery.process(options.range.from, options.range.to);
};
this.testDatasource = function() {
@@ -347,21 +310,21 @@ function (angular, _, moment, dateMath) {
});
}
- function convertToCloudWatchTime(date, roundUp) {
+ this.convertToCloudWatchTime = function(date, roundUp) {
if (_.isString(date)) {
date = dateMath.parse(date, roundUp);
}
return Math.round(date.valueOf() / 1000);
- }
+ };
- function convertDimensionFormat(dimensions, scopedVars) {
+ this.convertDimensionFormat = function(dimensions, scopedVars) {
return _.map(dimensions, function(value, key) {
return {
Name: templateSrv.replace(key, scopedVars),
Value: templateSrv.replace(value, scopedVars)
};
});
- }
+ };
}
diff --git a/public/app/plugins/datasource/cloudwatch/partials/annotations.editor.html b/public/app/plugins/datasource/cloudwatch/partials/annotations.editor.html
index 050698f3ff2..a82f82edf96 100644
--- a/public/app/plugins/datasource/cloudwatch/partials/annotations.editor.html
+++ b/public/app/plugins/datasource/cloudwatch/partials/annotations.editor.html
@@ -1 +1,19 @@