datasource.js 20 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540
  1. define([
  2. 'angular',
  3. 'lodash',
  4. 'moment',
  5. './query_ctrl',
  6. './directives',
  7. ],
  8. function (angular, _) {
  9. 'use strict';
  10. var module = angular.module('grafana.services');
  11. module.factory('CloudWatchDatasource', function($q, $http, templateSrv) {
  12. function CloudWatchDatasource(datasource) {
  13. this.type = 'cloudwatch';
  14. this.name = datasource.name;
  15. this.supportMetrics = true;
  16. this.proxyUrl = datasource.url;
  17. this.defaultRegion = datasource.jsonData.defaultRegion;
  18. /* jshint -W101 */
  19. this.supportedRegion = [
  20. 'us-east-1', 'us-west-2', 'us-west-1', 'eu-west-1', 'eu-central-1', 'ap-southeast-1', 'ap-southeast-2', 'ap-northeast-1', 'sa-east-1'
  21. ];
  22. this.supportedMetrics = {
  23. 'AWS/AutoScaling': [
  24. 'GroupMinSize', 'GroupMaxSize', 'GroupDesiredCapacity', 'GroupInServiceInstances', 'GroupPendingInstances', 'GroupStandbyInstances', 'GroupTerminatingInstances', 'GroupTotalInstances'
  25. ],
  26. 'AWS/Billing': [
  27. 'EstimatedCharges'
  28. ],
  29. 'AWS/CloudFront': [
  30. 'Requests', 'BytesDownloaded', 'BytesUploaded', 'TotalErrorRate', '4xxErrorRate', '5xxErrorRate'
  31. ],
  32. 'AWS/CloudSearch': [
  33. 'SuccessfulRequests', 'SearchableDocuments', 'IndexUtilization', 'Partitions'
  34. ],
  35. 'AWS/DynamoDB': [
  36. 'ConditionalCheckFailedRequests', 'ConsumedReadCapacityUnits', 'ConsumedWriteCapacityUnits', 'OnlineIndexConsumedWriteCapacity', 'OnlineIndexPercentageProgress', 'OnlineIndexThrottleEvents', 'ProvisionedReadCapacityUnits', 'ProvisionedWriteCapacityUnits', 'ReadThrottleEvents', 'ReturnedItemCount', 'SuccessfulRequestLatency', 'SystemErrors', 'ThrottledRequests', 'UserErrors', 'WriteThrottleEvents'
  37. ],
  38. 'AWS/ElastiCache': [
  39. 'CPUUtilization', 'SwapUsage', 'FreeableMemory', 'NetworkBytesIn', 'NetworkBytesOut',
  40. 'BytesUsedForCacheItems', 'BytesReadIntoMemcached', 'BytesWrittenOutFromMemcached', 'CasBadval', 'CasHits', 'CasMisses', 'CmdFlush', 'CmdGet', 'CmdSet', 'CurrConnections', 'CurrItems', 'DecrHits', 'DecrMisses', 'DeleteHits', 'DeleteMisses', 'Evictions', 'GetHits', 'GetMisses', 'IncrHits', 'IncrMisses', 'Reclaimed',
  41. 'CurrConnections', 'Evictions', 'Reclaimed', 'NewConnections', 'BytesUsedForCache', 'CacheHits', 'CacheMisses', 'ReplicationLag', 'GetTypeCmds', 'SetTypeCmds', 'KeyBasedCmds', 'StringBasedCmds', 'HashBasedCmds', 'ListBasedCmds', 'SetBasedCmds', 'SortedSetBasedCmds', 'CurrItems'
  42. ],
  43. 'AWS/EBS': [
  44. 'VolumeReadBytes', 'VolumeWriteBytes', 'VolumeReadOps', 'VolumeWriteOps', 'VolumeTotalReadTime', 'VolumeTotalWriteTime', 'VolumeIdleTime', 'VolumeQueueLength', 'VolumeThroughputPercentage', 'VolumeConsumedReadWriteOps'
  45. ],
  46. 'AWS/EC2': [
  47. 'CPUCreditUsage', 'CPUCreditBalance', 'CPUUtilization', 'DiskReadOps', 'DiskWriteOps', 'DiskReadBytes', 'DiskWriteBytes', 'NetworkIn', 'NetworkOut', 'StatusCheckFailed', 'StatusCheckFailed_Instance', 'StatusCheckFailed_System'
  48. ],
  49. 'AWS/ELB': [
  50. 'HealthyHostCount', 'UnHealthyHostCount', 'RequestCount', 'Latency', 'HTTPCode_ELB_4XX', 'HTTPCode_ELB_5XX', 'HTTPCode_Backend_2XX', 'HTTPCode_Backend_3XX', 'HTTPCode_Backend_4XX', 'HTTPCode_Backend_5XX', 'BackendConnectionErrors', 'SurgeQueueLength', 'SpilloverCount'
  51. ],
  52. 'AWS/ElasticMapReduce': [
  53. 'CoreNodesPending', 'CoreNodesRunning', 'HBaseBackupFailed', 'HBaseMostRecentBackupDuration', 'HBaseTimeSinceLastSuccessfulBackup', 'HDFSBytesRead', 'HDFSBytesWritten', 'HDFSUtilization', 'IsIdle', 'JobsFailed', 'JobsRunning', 'LiveDataNodes', 'LiveTaskTrackers', 'MapSlotsOpen', 'MissingBlocks', 'ReduceSlotsOpen', 'RemainingMapTasks', 'RemainingMapTasksPerSlot', 'RemainingReduceTasks', 'RunningMapTasks', 'RunningReduceTasks', 'S3BytesRead', 'S3BytesWritten', 'TaskNodesPending', 'TaskNodesRunning', 'TotalLoad'
  54. ],
  55. 'AWS/Kinesis': [
  56. 'PutRecord.Bytes', 'PutRecord.Latency', 'PutRecord.Success', 'PutRecords.Bytes', 'PutRecords.Latency', 'PutRecords.Records', 'PutRecords.Success', 'IncomingBytes', 'IncomingRecords', 'GetRecords.Bytes', 'GetRecords.IteratorAgeMilliseconds', 'GetRecords.Latency', 'GetRecords.Success'
  57. ],
  58. 'AWS/ML': [
  59. 'PredictCount', 'PredictFailureCount'
  60. ],
  61. 'AWS/OpsWorks': [
  62. 'cpu_idle', 'cpu_nice', 'cpu_system', 'cpu_user', 'cpu_waitio', 'load_1', 'load_5', 'load_15', 'memory_buffers', 'memory_cached', 'memory_free', 'memory_swap', 'memory_total', 'memory_used', 'procs'
  63. ],
  64. 'AWS/Redshift': [
  65. 'CPUUtilization', 'DatabaseConnections', 'HealthStatus', 'MaintenanceMode', 'NetworkReceiveThroughput', 'NetworkTransmitThroughput', 'PercentageDiskSpaceUsed', 'ReadIOPS', 'ReadLatency', 'ReadThroughput', 'WriteIOPS', 'WriteLatency', 'WriteThroughput'
  66. ],
  67. 'AWS/RDS': [
  68. 'BinLogDiskUsage', 'CPUUtilization', 'DatabaseConnections', 'DiskQueueDepth', 'FreeableMemory', 'FreeStorageSpace', 'ReplicaLag', 'SwapUsage', 'ReadIOPS', 'WriteIOPS', 'ReadLatency', 'WriteLatency', 'ReadThroughput', 'WriteThroughput', 'NetworkReceiveThroughput', 'NetworkTransmitThroughput'
  69. ],
  70. 'AWS/Route53': [
  71. 'HealthCheckStatus', 'HealthCheckPercentageHealthy'
  72. ],
  73. 'AWS/SNS': [
  74. 'NumberOfMessagesPublished', 'PublishSize', 'NumberOfNotificationsDelivered', 'NumberOfNotificationsFailed'
  75. ],
  76. 'AWS/SQS': [
  77. 'NumberOfMessagesSent', 'SentMessageSize', 'NumberOfMessagesReceived', 'NumberOfEmptyReceives', 'NumberOfMessagesDeleted', 'ApproximateNumberOfMessagesDelayed', 'ApproximateNumberOfMessagesVisible', 'ApproximateNumberOfMessagesNotVisible'
  78. ],
  79. 'AWS/S3': [
  80. 'BucketSizeBytes', 'NumberOfObjects'
  81. ],
  82. 'AWS/SWF': [
  83. 'DecisionTaskScheduleToStartTime', 'DecisionTaskStartToCloseTime', 'DecisionTasksCompleted', 'StartedDecisionTasksTimedOutOnClose', 'WorkflowStartToCloseTime', 'WorkflowsCanceled', 'WorkflowsCompleted', 'WorkflowsContinuedAsNew', 'WorkflowsFailed', 'WorkflowsTerminated', 'WorkflowsTimedOut'
  84. ],
  85. 'AWS/StorageGateway': [
  86. 'CacheHitPercent', 'CachePercentUsed', 'CachePercentDirty', 'CloudBytesDownloaded', 'CloudDownloadLatency', 'CloudBytesUploaded', 'UploadBufferFree', 'UploadBufferPercentUsed', 'UploadBufferUsed', 'QueuedWrites', 'ReadBytes', 'ReadTime', 'TotalCacheSize', 'WriteBytes', 'WriteTime', 'WorkingStorageFree', 'WorkingStoragePercentUsed', 'WorkingStorageUsed', 'CacheHitPercent', 'CachePercentUsed', 'CachePercentDirty', 'ReadBytes', 'ReadTime', 'WriteBytes', 'WriteTime', 'QueuedWrites'
  87. ],
  88. 'AWS/WorkSpaces': [
  89. 'Available', 'Unhealthy', 'ConnectionAttempt', 'ConnectionSuccess', 'ConnectionFailure', 'SessionLaunchTime', 'InSessionLatency', 'SessionDisconnect'
  90. ],
  91. };
  92. this.supportedDimensions = {
  93. 'AWS/AutoScaling': [
  94. 'AutoScalingGroupName'
  95. ],
  96. 'AWS/Billing': [
  97. 'ServiceName', 'LinkedAccount', 'Currency'
  98. ],
  99. 'AWS/CloudFront': [
  100. 'DistributionId', 'Region'
  101. ],
  102. 'AWS/CloudSearch': [
  103. ],
  104. 'AWS/DynamoDB': [
  105. 'TableName', 'GlobalSecondaryIndexName', 'Operation'
  106. ],
  107. 'AWS/ElastiCache': [
  108. 'CacheClusterId', 'CacheNodeId'
  109. ],
  110. 'AWS/EBS': [
  111. 'VolumeId'
  112. ],
  113. 'AWS/EC2': [
  114. 'AutoScalingGroupName', 'ImageId', 'InstanceId', 'InstanceType'
  115. ],
  116. 'AWS/ELB': [
  117. 'LoadBalancerName', 'AvailabilityZone'
  118. ],
  119. 'AWS/ElasticMapReduce': [
  120. 'ClusterId', 'JobId'
  121. ],
  122. 'AWS/Kinesis': [
  123. 'StreamName'
  124. ],
  125. 'AWS/ML': [
  126. 'MLModelId', 'RequestMode'
  127. ],
  128. 'AWS/OpsWorks': [
  129. 'StackId', 'LayerId', 'InstanceId'
  130. ],
  131. 'AWS/Redshift': [
  132. 'NodeID', 'ClusterIdentifier'
  133. ],
  134. 'AWS/RDS': [
  135. 'DBInstanceIdentifier', 'DatabaseClass', 'EngineName'
  136. ],
  137. 'AWS/Route53': [
  138. 'HealthCheckId'
  139. ],
  140. 'AWS/SNS': [
  141. 'Application', 'Platform', 'TopicName'
  142. ],
  143. 'AWS/SQS': [
  144. 'QueueName'
  145. ],
  146. 'AWS/S3': [
  147. 'BucketName', 'StorageType'
  148. ],
  149. 'AWS/SWF': [
  150. 'Domain', 'ActivityTypeName', 'ActivityTypeVersion'
  151. ],
  152. 'AWS/StorageGateway': [
  153. 'GatewayId', 'GatewayName', 'VolumeId'
  154. ],
  155. 'AWS/WorkSpaces': [
  156. 'DirectoryId', 'WorkspaceId'
  157. ],
  158. };
  159. /* jshint +W101 */
  160. /* load custom metrics definitions */
  161. var self = this;
  162. $q.all(
  163. _.chain(datasource.jsonData.customMetricsAttributes)
  164. .reject(function(u) {
  165. return _.isEmpty(u);
  166. })
  167. .map(function(u) {
  168. return $http({ method: 'GET', url: u });
  169. })
  170. )
  171. .then(function(allResponse) {
  172. _.chain(allResponse)
  173. .map(function(d) {
  174. return d.data.Metrics;
  175. })
  176. .flatten()
  177. .reject(function(metric) {
  178. return metric.Namespace.indexOf('AWS/') === 0;
  179. })
  180. .map(function(metric) {
  181. metric.Dimensions = _.chain(metric.Dimensions)
  182. .map(function(d) {
  183. return d.Name;
  184. })
  185. .value().sort();
  186. return metric;
  187. })
  188. .uniq(function(metric) {
  189. return metric.Namespace + metric.MetricName + metric.Dimensions.join('');
  190. })
  191. .each(function(metric) {
  192. if (!_.has(self.supportedMetrics, metric.Namespace)) {
  193. self.supportedMetrics[metric.Namespace] = [];
  194. }
  195. self.supportedMetrics[metric.Namespace].push(metric.MetricName);
  196. if (!_.has(self.supportedDimensions, metric.Namespace)) {
  197. self.supportedDimensions[metric.Namespace] = [];
  198. }
  199. self.supportedDimensions[metric.Namespace] = _.union(self.supportedDimensions[metric.Namespace], metric.Dimensions);
  200. });
  201. });
  202. }
  203. // Called once per panel (graph)
  204. CloudWatchDatasource.prototype.query = function(options) {
  205. var start = convertToCloudWatchTime(options.range.from);
  206. var end = convertToCloudWatchTime(options.range.to);
  207. var queries = [];
  208. _.each(options.targets, _.bind(function(target) {
  209. if (!target.namespace || !target.metricName || _.isEmpty(target.statistics)) {
  210. return;
  211. }
  212. var query = {};
  213. query.region = templateSrv.replace(target.region, options.scopedVars);
  214. query.namespace = templateSrv.replace(target.namespace, options.scopedVars);
  215. query.metricName = templateSrv.replace(target.metricName, options.scopedVars);
  216. query.dimensions = convertDimensionFormat(target.dimensions);
  217. query.statistics = getActivatedStatistics(target.statistics);
  218. query.period = parseInt(target.period, 10);
  219. var range = end - start;
  220. // CloudWatch limit datapoints up to 1440
  221. if (range / query.period >= 1440) {
  222. query.period = Math.floor(range / 1440 / 60) * 60;
  223. }
  224. queries.push(query);
  225. }, this));
  226. // No valid targets, return the empty result to save a round trip.
  227. if (_.isEmpty(queries)) {
  228. var d = $q.defer();
  229. d.resolve({ data: [] });
  230. return d.promise;
  231. }
  232. var allQueryPromise = _.map(queries, function(query) {
  233. return this.performTimeSeriesQuery(query, start, end);
  234. }, this);
  235. return $q.all(allQueryPromise).then(function(allResponse) {
  236. var result = [];
  237. _.each(allResponse, function(response, index) {
  238. var metrics = transformMetricData(response, options.targets[index]);
  239. result = result.concat(metrics);
  240. });
  241. return { data: result };
  242. });
  243. };
  244. CloudWatchDatasource.prototype.performTimeSeriesQuery = function(query, start, end) {
  245. var cloudwatch = this.getAwsClient(query.region, 'CloudWatch');
  246. var params = {
  247. Namespace: query.namespace,
  248. MetricName: query.metricName,
  249. Dimensions: query.dimensions,
  250. Statistics: query.statistics,
  251. StartTime: start,
  252. EndTime: end,
  253. Period: query.period
  254. };
  255. return cloudwatch.getMetricStatistics(params);
  256. };
  257. CloudWatchDatasource.prototype.getRegions = function() {
  258. return $q.when(this.supportedRegion);
  259. };
  260. CloudWatchDatasource.prototype.getNamespaces = function() {
  261. return $q.when(_.keys(this.supportedMetrics));
  262. };
  263. CloudWatchDatasource.prototype.getMetrics = function(namespace) {
  264. namespace = templateSrv.replace(namespace);
  265. return $q.when(this.supportedMetrics[namespace] || []);
  266. };
  267. CloudWatchDatasource.prototype.getDimensionKeys = function(namespace) {
  268. namespace = templateSrv.replace(namespace);
  269. return $q.when(this.supportedDimensions[namespace] || []);
  270. };
  271. CloudWatchDatasource.prototype.getDimensionValues = function(region, namespace, metricName, dimensions) {
  272. region = templateSrv.replace(region);
  273. namespace = templateSrv.replace(namespace);
  274. metricName = templateSrv.replace(metricName);
  275. var cloudwatch = this.getAwsClient(region, 'CloudWatch');
  276. var params = {Namespace: namespace, MetricName: metricName};
  277. if (!_.isEmpty(dimensions)) {
  278. params.Dimensions = convertDimensionFormat(dimensions);
  279. }
  280. return cloudwatch.listMetrics(params).then(function(result) {
  281. return _.chain(result.Metrics).map(function(metric) {
  282. return metric.Dimensions;
  283. }).reject(function(metric) {
  284. return _.isEmpty(metric);
  285. }).value();
  286. });
  287. };
  288. CloudWatchDatasource.prototype.performEC2DescribeInstances = function(region, filters, instanceIds) {
  289. var ec2 = this.getAwsClient(region, 'EC2');
  290. var params = {};
  291. if (filters.length > 0) {
  292. params.Filters = filters;
  293. }
  294. if (instanceIds.length > 0) {
  295. params.InstanceIds = instanceIds;
  296. }
  297. return ec2.describeInstances(params);
  298. };
  299. CloudWatchDatasource.prototype.metricFindQuery = function(query) {
  300. var region;
  301. var namespace;
  302. var metricName;
  303. var transformSuggestData = function(suggestData) {
  304. return _.map(suggestData, function(v) {
  305. return { text: v };
  306. });
  307. };
  308. var regionQuery = query.match(/^region\(\)/);
  309. if (regionQuery) {
  310. return this.getRegions().then(transformSuggestData);
  311. }
  312. var namespaceQuery = query.match(/^namespace\(\)/);
  313. if (namespaceQuery) {
  314. return this.getNamespaces().then(transformSuggestData);
  315. }
  316. var metricNameQuery = query.match(/^metrics\(([^\)]+?)\)/);
  317. if (metricNameQuery) {
  318. namespace = templateSrv.replace(metricNameQuery[1]);
  319. return this.getMetrics(namespace).then(transformSuggestData);
  320. }
  321. var dimensionKeysQuery = query.match(/^dimension_keys\(([^\)]+?)\)/);
  322. if (dimensionKeysQuery) {
  323. namespace = templateSrv.replace(dimensionKeysQuery[1]);
  324. return this.getDimensionKeys(namespace).then(transformSuggestData);
  325. }
  326. var dimensionValuesQuery = query.match(/^dimension_values\(([^,]+?),\s?([^,]+?),\s?([^,]+?)(,\s?([^)]*))?\)/);
  327. if (dimensionValuesQuery) {
  328. region = templateSrv.replace(dimensionValuesQuery[1]);
  329. namespace = templateSrv.replace(dimensionValuesQuery[2]);
  330. metricName = templateSrv.replace(dimensionValuesQuery[3]);
  331. var dimensionPart = templateSrv.replace(dimensionValuesQuery[5]);
  332. var dimensions = {};
  333. if (!_.isEmpty(dimensionPart)) {
  334. _.each(dimensionPart.split(','), function(v) {
  335. var t = v.split('=');
  336. if (t.length !== 2) {
  337. throw new Error('Invalid query format');
  338. }
  339. dimensions[t[0]] = t[1];
  340. });
  341. }
  342. return this.getDimensionValues(region, namespace, metricName, dimensions)
  343. .then(function(suggestData) {
  344. return _.map(suggestData, function(dimensions) {
  345. var result = _.chain(dimensions)
  346. .sortBy(function(dimension) {
  347. return dimension.Name;
  348. })
  349. .map(function(dimension) {
  350. return dimension.Name + '=' + dimension.Value;
  351. })
  352. .value().join(',');
  353. return { text: result };
  354. });
  355. });
  356. }
  357. var ebsVolumeIdsQuery = query.match(/^ebs_volume_ids\(([^,]+?),\s?([^,]+?)\)/);
  358. if (ebsVolumeIdsQuery) {
  359. region = templateSrv.replace(ebsVolumeIdsQuery[1]);
  360. var instanceId = templateSrv.replace(ebsVolumeIdsQuery[2]);
  361. var instanceIds = [
  362. instanceId
  363. ];
  364. return this.performEC2DescribeInstances(region, [], instanceIds).then(function(result) {
  365. var volumeIds = _.map(result.Reservations[0].Instances[0].BlockDeviceMappings, function(mapping) {
  366. return mapping.EBS.VolumeID;
  367. });
  368. return transformSuggestData(volumeIds);
  369. });
  370. }
  371. return $q.when([]);
  372. };
  373. CloudWatchDatasource.prototype.testDatasource = function() {
  374. /* use billing metrics for test */
  375. var region = 'us-east-1';
  376. var namespace = 'AWS/Billing';
  377. var metricName = 'EstimatedCharges';
  378. var dimensions = {};
  379. return this.performSuggestDimensionValues(region, namespace, metricName, dimensions).then(function () {
  380. return { status: 'success', message: 'Data source is working', title: 'Success' };
  381. });
  382. };
  383. CloudWatchDatasource.prototype.getAwsClient = function(region, service) {
  384. var self = this;
  385. var generateRequestProxy = function(service, action) {
  386. return function(params) {
  387. var data = {
  388. region: region,
  389. service: service,
  390. action: action,
  391. parameters: params
  392. };
  393. var options = {
  394. method: 'POST',
  395. url: self.proxyUrl,
  396. data: data
  397. };
  398. return $http(options).then(function(result) {
  399. return result.data;
  400. });
  401. };
  402. };
  403. switch (service) {
  404. case 'CloudWatch': {
  405. return {
  406. getMetricStatistics: generateRequestProxy('CloudWatch', 'GetMetricStatistics'),
  407. listMetrics: generateRequestProxy('CloudWatch', 'ListMetrics')
  408. };
  409. }
  410. case 'EC2': {
  411. return {
  412. describeInstances: generateRequestProxy('EC2', 'DescribeInstances')
  413. };
  414. }
  415. }
  416. };
  417. CloudWatchDatasource.prototype.getDefaultRegion = function() {
  418. return this.defaultRegion;
  419. };
  420. function transformMetricData(md, options) {
  421. var result = [];
  422. var dimensionPart = templateSrv.replace(JSON.stringify(options.dimensions));
  423. _.each(getActivatedStatistics(options.statistics), function(s) {
  424. var originalSettings = _.templateSettings;
  425. _.templateSettings = {
  426. interpolate: /\{\{(.+?)\}\}/g
  427. };
  428. var template = _.template(options.legendFormat);
  429. var metricLabel;
  430. if (_.isEmpty(options.legendFormat)) {
  431. metricLabel = md.Label + '_' + s + dimensionPart;
  432. } else {
  433. var d = convertDimensionFormat(options.dimensions);
  434. metricLabel = template({
  435. Region: templateSrv.replace(options.region),
  436. Namespace: templateSrv.replace(options.namespace),
  437. MetricName: templateSrv.replace(options.metricName),
  438. Dimensions: d,
  439. Statistics: s
  440. });
  441. }
  442. _.templateSettings = originalSettings;
  443. var dps = _.map(md.Datapoints, function(value) {
  444. return [value[s], new Date(value.Timestamp).getTime()];
  445. });
  446. dps = _.sortBy(dps, function(dp) { return dp[1]; });
  447. result.push({ target: metricLabel, datapoints: dps });
  448. });
  449. return result;
  450. }
  451. function getActivatedStatistics(statistics) {
  452. var activatedStatistics = [];
  453. _.each(statistics, function(v, k) {
  454. if (v) {
  455. activatedStatistics.push(k);
  456. }
  457. });
  458. return activatedStatistics;
  459. }
  460. function convertToCloudWatchTime(date) {
  461. return Math.round(date.valueOf() / 1000);
  462. }
  463. function convertDimensionFormat(dimensions) {
  464. return _.map(_.keys(dimensions), function(key) {
  465. return {
  466. Name: templateSrv.replace(key),
  467. Value: templateSrv.replace(dimensions[key])
  468. };
  469. });
  470. }
  471. return CloudWatchDatasource;
  472. });
  473. });