| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596 |
- define([
- 'angular',
- 'lodash',
- 'moment',
- './queryCtrl',
- './directives',
- ],
- function (angular, _) {
- 'use strict';
- var module = angular.module('grafana.services');
- module.factory('CloudWatchDatasource', function($q, $http, templateSrv) {
- function CloudWatchDatasource(datasource) {
- this.type = 'cloudwatch';
- this.name = datasource.name;
- this.supportMetrics = true;
- this.proxyUrl = datasource.url;
- this.defaultRegion = datasource.jsonData.defaultRegion;
- /* jshint -W101 */
- this.supportedRegion = [
- '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'
- ];
- this.supportedMetrics = {
- 'AWS/AutoScaling': [
- 'GroupMinSize', 'GroupMaxSize', 'GroupDesiredCapacity', 'GroupInServiceInstances', 'GroupPendingInstances', 'GroupStandbyInstances', 'GroupTerminatingInstances', 'GroupTotalInstances'
- ],
- 'AWS/Billing': [
- 'EstimatedCharges'
- ],
- 'AWS/CloudFront': [
- 'Requests', 'BytesDownloaded', 'BytesUploaded', 'TotalErrorRate', '4xxErrorRate', '5xxErrorRate'
- ],
- 'AWS/CloudSearch': [
- 'SuccessfulRequests', 'SearchableDocuments', 'IndexUtilization', 'Partitions'
- ],
- 'AWS/DynamoDB': [
- 'ConditionalCheckFailedRequests', 'ConsumedReadCapacityUnits', 'ConsumedWriteCapacityUnits', 'OnlineIndexConsumedWriteCapacity', 'OnlineIndexPercentageProgress', 'OnlineIndexThrottleEvents', 'ProvisionedReadCapacityUnits', 'ProvisionedWriteCapacityUnits', 'ReadThrottleEvents', 'ReturnedItemCount', 'SuccessfulRequestLatency', 'SystemErrors', 'ThrottledRequests', 'UserErrors', 'WriteThrottleEvents'
- ],
- 'AWS/ElastiCache': [
- 'CPUUtilization', 'SwapUsage', 'FreeableMemory', 'NetworkBytesIn', 'NetworkBytesOut',
- 'BytesUsedForCacheItems', 'BytesReadIntoMemcached', 'BytesWrittenOutFromMemcached', 'CasBadval', 'CasHits', 'CasMisses', 'CmdFlush', 'CmdGet', 'CmdSet', 'CurrConnections', 'CurrItems', 'DecrHits', 'DecrMisses', 'DeleteHits', 'DeleteMisses', 'Evictions', 'GetHits', 'GetMisses', 'IncrHits', 'IncrMisses', 'Reclaimed',
- 'CurrConnections', 'Evictions', 'Reclaimed', 'NewConnections', 'BytesUsedForCache', 'CacheHits', 'CacheMisses', 'ReplicationLag', 'GetTypeCmds', 'SetTypeCmds', 'KeyBasedCmds', 'StringBasedCmds', 'HashBasedCmds', 'ListBasedCmds', 'SetBasedCmds', 'SortedSetBasedCmds', 'CurrItems'
- ],
- 'AWS/EBS': [
- 'VolumeReadBytes', 'VolumeWriteBytes', 'VolumeReadOps', 'VolumeWriteOps', 'VolumeTotalReadTime', 'VolumeTotalWriteTime', 'VolumeIdleTime', 'VolumeQueueLength', 'VolumeThroughputPercentage', 'VolumeConsumedReadWriteOps'
- ],
- 'AWS/EC2': [
- 'CPUCreditUsage', 'CPUCreditBalance', 'CPUUtilization', 'DiskReadOps', 'DiskWriteOps', 'DiskReadBytes', 'DiskWriteBytes', 'NetworkIn', 'NetworkOut', 'StatusCheckFailed', 'StatusCheckFailed_Instance', 'StatusCheckFailed_System'
- ],
- 'AWS/ELB': [
- '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'
- ],
- 'AWS/ElasticMapReduce': [
- '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'
- ],
- 'AWS/Kinesis': [
- 'PutRecord.Bytes', 'PutRecord.Latency', 'PutRecord.Success', 'PutRecords.Bytes', 'PutRecords.Latency', 'PutRecords.Records', 'PutRecords.Success', 'IncomingBytes', 'IncomingRecords', 'GetRecords.Bytes', 'GetRecords.IteratorAgeMilliseconds', 'GetRecords.Latency', 'GetRecords.Success'
- ],
- 'AWS/ML': [
- 'PredictCount', 'PredictFailureCount'
- ],
- 'AWS/OpsWorks': [
- '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'
- ],
- 'AWS/Redshift': [
- 'CPUUtilization', 'DatabaseConnections', 'HealthStatus', 'MaintenanceMode', 'NetworkReceiveThroughput', 'NetworkTransmitThroughput', 'PercentageDiskSpaceUsed', 'ReadIOPS', 'ReadLatency', 'ReadThroughput', 'WriteIOPS', 'WriteLatency', 'WriteThroughput'
- ],
- 'AWS/RDS': [
- 'BinLogDiskUsage', 'CPUUtilization', 'DatabaseConnections', 'DiskQueueDepth', 'FreeableMemory', 'FreeStorageSpace', 'ReplicaLag', 'SwapUsage', 'ReadIOPS', 'WriteIOPS', 'ReadLatency', 'WriteLatency', 'ReadThroughput', 'WriteThroughput', 'NetworkReceiveThroughput', 'NetworkTransmitThroughput'
- ],
- 'AWS/Route53': [
- 'HealthCheckStatus', 'HealthCheckPercentageHealthy'
- ],
- 'AWS/SNS': [
- 'NumberOfMessagesPublished', 'PublishSize', 'NumberOfNotificationsDelivered', 'NumberOfNotificationsFailed'
- ],
- 'AWS/SQS': [
- 'NumberOfMessagesSent', 'SentMessageSize', 'NumberOfMessagesReceived', 'NumberOfEmptyReceives', 'NumberOfMessagesDeleted', 'ApproximateNumberOfMessagesDelayed', 'ApproximateNumberOfMessagesVisible', 'ApproximateNumberOfMessagesNotVisible'
- ],
- 'AWS/S3': [
- 'BucketSizeBytes', 'NumberOfObjects'
- ],
- 'AWS/SWF': [
- 'DecisionTaskScheduleToStartTime', 'DecisionTaskStartToCloseTime', 'DecisionTasksCompleted', 'StartedDecisionTasksTimedOutOnClose', 'WorkflowStartToCloseTime', 'WorkflowsCanceled', 'WorkflowsCompleted', 'WorkflowsContinuedAsNew', 'WorkflowsFailed', 'WorkflowsTerminated', 'WorkflowsTimedOut'
- ],
- 'AWS/StorageGateway': [
- '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'
- ],
- 'AWS/WorkSpaces': [
- 'Available', 'Unhealthy', 'ConnectionAttempt', 'ConnectionSuccess', 'ConnectionFailure', 'SessionLaunchTime', 'InSessionLatency', 'SessionDisconnect'
- ],
- };
- this.supportedDimensions = {
- 'AWS/AutoScaling': [
- 'AutoScalingGroupName'
- ],
- 'AWS/Billing': [
- 'ServiceName', 'LinkedAccount', 'Currency'
- ],
- 'AWS/CloudFront': [
- 'DistributionId', 'Region'
- ],
- 'AWS/CloudSearch': [
- ],
- 'AWS/DynamoDB': [
- 'TableName', 'GlobalSecondaryIndexName', 'Operation'
- ],
- 'AWS/ElastiCache': [
- 'CacheClusterId', 'CacheNodeId'
- ],
- 'AWS/EBS': [
- 'VolumeId'
- ],
- 'AWS/EC2': [
- 'AutoScalingGroupName', 'ImageId', 'InstanceId', 'InstanceType'
- ],
- 'AWS/ELB': [
- 'LoadBalancerName', 'AvailabilityZone'
- ],
- 'AWS/ElasticMapReduce': [
- 'ClusterId', 'JobId'
- ],
- 'AWS/Kinesis': [
- 'StreamName'
- ],
- 'AWS/ML': [
- 'MLModelId', 'RequestMode'
- ],
- 'AWS/OpsWorks': [
- 'StackId', 'LayerId', 'InstanceId'
- ],
- 'AWS/Redshift': [
- 'NodeID', 'ClusterIdentifier'
- ],
- 'AWS/RDS': [
- 'DBInstanceIdentifier', 'DatabaseClass', 'EngineName'
- ],
- 'AWS/Route53': [
- 'HealthCheckId'
- ],
- 'AWS/SNS': [
- 'Application', 'Platform', 'TopicName'
- ],
- 'AWS/SQS': [
- 'QueueName'
- ],
- 'AWS/S3': [
- 'BucketName', 'StorageType'
- ],
- 'AWS/SWF': [
- 'Domain', 'ActivityTypeName', 'ActivityTypeVersion'
- ],
- 'AWS/StorageGateway': [
- 'GatewayId', 'GatewayName', 'VolumeId'
- ],
- 'AWS/WorkSpaces': [
- 'DirectoryId', 'WorkspaceId'
- ],
- };
- /* jshint +W101 */
- /* load custom metrics definitions */
- var self = this;
- $q.all(
- _.chain(datasource.jsonData.customMetricsAttributes)
- .reject(function(u) {
- return _.isEmpty(u);
- })
- .map(function(u) {
- return $http({ method: 'GET', url: u });
- })
- )
- .then(function(allResponse) {
- _.chain(allResponse)
- .map(function(d) {
- return d.data.Metrics;
- })
- .flatten()
- .reject(function(metric) {
- return metric.Namespace.indexOf('AWS/') === 0;
- })
- .map(function(metric) {
- metric.Dimensions = _.chain(metric.Dimensions)
- .map(function(d) {
- return d.Name;
- })
- .value().sort();
- return metric;
- })
- .uniq(function(metric) {
- return metric.Namespace + metric.MetricName + metric.Dimensions.join('');
- })
- .each(function(metric) {
- if (!_.has(self.supportedMetrics, metric.Namespace)) {
- self.supportedMetrics[metric.Namespace] = [];
- }
- self.supportedMetrics[metric.Namespace].push(metric.MetricName);
- if (!_.has(self.supportedDimensions, metric.Namespace)) {
- self.supportedDimensions[metric.Namespace] = [];
- }
- self.supportedDimensions[metric.Namespace] = _.union(self.supportedDimensions[metric.Namespace], metric.Dimensions);
- });
- });
- }
- // Called once per panel (graph)
- 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) {
- if (!target.namespace || !target.metricName || _.isEmpty(target.statistics)) {
- return;
- }
- var query = {};
- 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);
- query.statistics = getActivatedStatistics(target.statistics);
- query.period = parseInt(target.period, 10);
- var range = end - start;
- // 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;
- }
- var allQueryPromise = _.map(queries, _.bind(function(query) {
- return this.performTimeSeriesQuery(query, start, end);
- }, this));
- return $q.all(allQueryPromise)
- .then(function(allResponse) {
- var result = [];
- _.each(allResponse, function(response, index) {
- var metrics = transformMetricData(response, options.targets[index]);
- _.each(metrics, function(m) {
- result.push(m);
- });
- });
- return { data: result };
- });
- };
- CloudWatchDatasource.prototype.performTimeSeriesQuery = function(query, start, end) {
- var cloudwatch = this.getAwsClient(query.region, 'CloudWatch');
- var params = {
- Namespace: query.namespace,
- MetricName: query.metricName,
- Dimensions: query.dimensions,
- Statistics: query.statistics,
- StartTime: start,
- EndTime: end,
- Period: query.period
- };
- var d = $q.defer();
- cloudwatch.getMetricStatistics(params, function(err, data) {
- if (err) {
- return d.reject(err);
- }
- return d.resolve(data);
- });
- return d.promise;
- };
- CloudWatchDatasource.prototype.performSuggestRegion = function() {
- return this.supportedRegion;
- };
- CloudWatchDatasource.prototype.performSuggestNamespace = function() {
- console.log(this.supportMetrics);
- return _.keys(this.supportedMetrics);
- };
- CloudWatchDatasource.prototype.performSuggestMetrics = function(namespace) {
- namespace = templateSrv.replace(namespace);
- return this.supportedMetrics[namespace] || [];
- };
- CloudWatchDatasource.prototype.performSuggestDimensionKeys = function(namespace) {
- namespace = templateSrv.replace(namespace);
- return this.supportedDimensions[namespace] || [];
- };
- CloudWatchDatasource.prototype.performSuggestDimensionValues = function(region, namespace, metricName, dimensions) {
- region = templateSrv.replace(region);
- namespace = templateSrv.replace(namespace);
- metricName = templateSrv.replace(metricName);
- var cloudwatch = this.getAwsClient(region, 'CloudWatch');
- var params = {
- Namespace: namespace,
- MetricName: metricName
- };
- if (!_.isEmpty(dimensions)) {
- params.Dimensions = convertDimensionFormat(dimensions);
- }
- var d = $q.defer();
- cloudwatch.listMetrics(params, function(err, data) {
- if (err) {
- return d.reject(err);
- }
- var suggestData = _.chain(data.Metrics)
- .map(function(metric) {
- return metric.Dimensions;
- })
- .reject(function(metric) {
- return _.isEmpty(metric);
- })
- .value();
- return d.resolve(suggestData);
- });
- return d.promise;
- };
- CloudWatchDatasource.prototype.performEC2DescribeInstances = function(region, filters, instanceIds) {
- var ec2 = this.getAwsClient(region, 'EC2');
- var params = {};
- if (filters.length > 0) {
- params.Filters = filters;
- }
- if (instanceIds.length > 0) {
- params.InstanceIds = instanceIds;
- }
- var d = $q.defer();
- ec2.describeInstances(params, function(err, data) {
- if (err) {
- return d.reject(err);
- }
- return d.resolve(data);
- });
- return d.promise;
- };
- CloudWatchDatasource.prototype.getTemplateVariableNames = function() {
- var variables = [];
- templateSrv.fillVariableValuesForUrl(variables);
- return _.map(_.keys(variables), function(k) {
- return k.replace(/var-/, '$');
- });
- };
- CloudWatchDatasource.prototype.metricFindQuery = function(query) {
- var region;
- var namespace;
- var metricName;
- var transformSuggestData = function(suggestData) {
- return _.map(suggestData, function(v) {
- return { text: v };
- });
- };
- var d = $q.defer();
- var regionQuery = query.match(/^region\(\)/);
- if (regionQuery) {
- d.resolve(transformSuggestData(this.performSuggestRegion()));
- return d.promise;
- }
- var namespaceQuery = query.match(/^namespace\(\)/);
- if (namespaceQuery) {
- d.resolve(transformSuggestData(this.performSuggestNamespace()));
- return d.promise;
- }
- var metricNameQuery = query.match(/^metrics\(([^\)]+?)\)/);
- if (metricNameQuery) {
- namespace = templateSrv.replace(metricNameQuery[1]);
- d.resolve(transformSuggestData(this.performSuggestMetrics(namespace)));
- return d.promise;
- }
- var dimensionKeysQuery = query.match(/^dimension_keys\(([^\)]+?)\)/);
- if (dimensionKeysQuery) {
- namespace = templateSrv.replace(dimensionKeysQuery[1]);
- d.resolve(transformSuggestData(this.performSuggestDimensionKeys(namespace)));
- return d.promise;
- }
- var dimensionValuesQuery = query.match(/^dimension_values\(([^,]+?),\s?([^,]+?),\s?([^,]+?)(,\s?([^)]*))?\)/);
- if (dimensionValuesQuery) {
- region = templateSrv.replace(dimensionValuesQuery[1]);
- namespace = templateSrv.replace(dimensionValuesQuery[2]);
- metricName = templateSrv.replace(dimensionValuesQuery[3]);
- var dimensionPart = templateSrv.replace(dimensionValuesQuery[5]);
- var dimensions = {};
- 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];
- });
- }
- return this.performSuggestDimensionValues(region, namespace, metricName, dimensions)
- .then(function(suggestData) {
- return _.map(suggestData, function(dimensions) {
- var result = _.chain(dimensions)
- .sortBy(function(dimension) {
- return dimension.Name;
- })
- .map(function(dimension) {
- return dimension.Name + '=' + dimension.Value;
- })
- .value().join(',');
- return { text: result };
- });
- });
- }
- var ebsVolumeIdsQuery = query.match(/^ebs_volume_ids\(([^,]+?),\s?([^,]+?)\)/);
- if (ebsVolumeIdsQuery) {
- region = templateSrv.replace(ebsVolumeIdsQuery[1]);
- var instanceId = templateSrv.replace(ebsVolumeIdsQuery[2]);
- var instanceIds = [
- instanceId
- ];
- return this.performEC2DescribeInstances(region, [], instanceIds)
- .then(function(result) {
- var volumeIds = _.map(result.Reservations[0].Instances[0].BlockDeviceMappings, function(mapping) {
- return mapping.EBS.VolumeID;
- });
- return transformSuggestData(volumeIds);
- });
- }
- return $q.when([]);
- };
- CloudWatchDatasource.prototype.testDatasource = function() {
- /* use billing metrics for test */
- var region = 'us-east-1';
- var namespace = 'AWS/Billing';
- var metricName = 'EstimatedCharges';
- var dimensions = {};
- return this.performSuggestDimensionValues(region, namespace, metricName, dimensions).then(function () {
- return { status: 'success', message: 'Data source is working', title: 'Success' };
- });
- };
- CloudWatchDatasource.prototype.getAwsClient = function(region, service) {
- var self = this;
- var generateRequestProxy = function(service, action) {
- return function(params, callback) {
- var data = {
- region: region,
- service: service,
- action: action,
- parameters: params
- };
- var options = {
- method: 'POST',
- url: self.proxyUrl,
- data: data
- };
- $http(options).then(function(response) {
- callback(null, response.data);
- }, function(err) {
- callback(err, []);
- });
- };
- };
- switch (service) {
- case 'CloudWatch': {
- return {
- getMetricStatistics: generateRequestProxy('CloudWatch', 'GetMetricStatistics'),
- listMetrics: generateRequestProxy('CloudWatch', 'ListMetrics')
- };
- }
- case 'EC2': {
- return {
- describeInstances: generateRequestProxy('EC2', 'DescribeInstances')
- };
- }
- }
- };
- CloudWatchDatasource.prototype.getDefaultRegion = function() {
- return this.defaultRegion;
- };
- function transformMetricData(md, options) {
- var result = [];
- var dimensionPart = templateSrv.replace(JSON.stringify(options.dimensions));
- _.each(getActivatedStatistics(options.statistics), function(s) {
- var originalSettings = _.templateSettings;
- _.templateSettings = {
- interpolate: /\{\{(.+?)\}\}/g
- };
- var template = _.template(options.legendFormat);
- var metricLabel;
- if (_.isEmpty(options.legendFormat)) {
- metricLabel = md.Label + '_' + s + dimensionPart;
- } else {
- var d = convertDimensionFormat(options.dimensions);
- metricLabel = template({
- Region: templateSrv.replace(options.region),
- Namespace: templateSrv.replace(options.namespace),
- MetricName: templateSrv.replace(options.metricName),
- Dimensions: d,
- Statistics: s
- });
- }
- _.templateSettings = originalSettings;
- var dps = _.map(md.Datapoints, function(value) {
- return [value[s], new Date(value.Timestamp).getTime()];
- });
- dps = _.sortBy(dps, function(dp) { return dp[1]; });
- result.push({ target: metricLabel, datapoints: dps });
- });
- return result;
- }
- function getActivatedStatistics(statistics) {
- var activatedStatistics = [];
- _.each(statistics, function(v, k) {
- if (v) {
- activatedStatistics.push(k);
- }
- });
- return activatedStatistics;
- }
- function convertToCloudWatchTime(date) {
- return Math.round(date.valueOf() / 1000);
- }
- function convertDimensionFormat(dimensions) {
- return _.map(_.keys(dimensions), function(key) {
- return {
- Name: templateSrv.replace(key),
- Value: templateSrv.replace(dimensions[key])
- };
- });
- }
- return CloudWatchDatasource;
- });
- });
|