#
# Copyright (c) 2007-2009 RightScale Inc
#
# Permission is hereby granted, free of charge, to any person obtaining
# a copy of this software and associated documentation files (the
# "Software"), to deal in the Software without restriction, including
# without limitation the rights to use, copy, modify, merge, publish,
# distribute, sublicense, and/or sell copies of the Software, and to
# permit persons to whom the Software is furnished to do so, subject to
# the following conditions:
#
# The above copyright notice and this permission notice shall be
# included in all copies or substantial portions of the Software.
#
# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
# EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
# MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
# NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
# LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
# OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
# WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
#
module RightAws
# = RightAWS::AcwInterface -- RightScale Amazon Cloud Watch interface
# The RightAws::AcwInterface class provides a complete interface to Amazon Cloud Watch service.
#
# For explanations of the semantics of each call, please refer to Amazon's documentation at
# http://docs.amazonwebservices.com/AmazonCloudWatch/latest/DeveloperGuide/
#
class AcwInterface < RightAwsBase
include RightAwsBaseInterface
# Amazon ACW API version being used
API_VERSION = "2009-05-15"
DEFAULT_HOST = "monitoring.amazonaws.com"
DEFAULT_PATH = '/'
DEFAULT_PROTOCOL = 'https'
DEFAULT_PORT = 443
@@bench = AwsBenchmarkingBlock.new
def self.bench_xml
@@bench.xml
end
def self.bench_service
@@bench.service
end
# Create a new handle to an ACW account. All handles share the same per process or per thread
# HTTP connection to Amazon ACW. Each handle is for a specific account. The params have the
# following options:
# * :endpoint_url a fully qualified url to Amazon API endpoint (this overwrites: :server, :port, :service, :protocol). Example: 'https://monitoring.amazonaws.com/'
# * :server: ACW service host, default: DEFAULT_HOST
# * :port: ACW service port, default: DEFAULT_PORT
# * :protocol: 'http' or 'https', default: DEFAULT_PROTOCOL
# * :multi_thread: true=HTTP connection per thread, false=per process
# * :logger: for log messages, default: RAILS_DEFAULT_LOGGER else STDOUT
# * :signature_version: The signature version : '0','1' or '2'(default)
# * :cache: true/false(default): list_metrics
#
def initialize(aws_access_key_id=nil, aws_secret_access_key=nil, params={})
init({ :name => 'ACW',
:default_host => ENV['ACW_URL'] ? URI.parse(ENV['ACW_URL']).host : DEFAULT_HOST,
:default_port => ENV['ACW_URL'] ? URI.parse(ENV['ACW_URL']).port : DEFAULT_PORT,
:default_service => ENV['ACW_URL'] ? URI.parse(ENV['ACW_URL']).path : DEFAULT_PATH,
:default_protocol => ENV['ACW_URL'] ? URI.parse(ENV['ACW_URL']).scheme : DEFAULT_PROTOCOL,
:default_api_version => ENV['ACW_API_VERSION'] || API_VERSION },
aws_access_key_id || ENV['AWS_ACCESS_KEY_ID'] ,
aws_secret_access_key|| ENV['AWS_SECRET_ACCESS_KEY'],
params)
end
def generate_request(action, params={}) #:nodoc:
generate_request_impl(:get, action, params )
end
# Sends request to Amazon and parses the response
# Raises AwsError if any banana happened
def request_info(request, parser) #:nodoc:
request_info_impl(:ams_connection, @@bench, request, parser)
end
#-----------------------------------------------------------------
# MetricStatistics
#-----------------------------------------------------------------
# Get time-series data for one or more statistics of given a Metric
# Returns a hash of stat data.
#
# Options are:
#
# :period - x*60 seconds interval (where x > 0)
# :statistics - Average, Minimum. Maximum, Sum, Samples
# :start_time - The timestamp of the first datapoint to return, inclusive.
# :end_time - The timestamp to use for determining the last datapoint to return. This is the last datapoint to fetch, exclusive.
# :namespace - The namespace corresponding to the service of interest. For example, AWS/EC2 represents Amazon EC2.
# :unit - Seconds, Percent, Bytes, Bits, Count, Bytes/Second, Bits/Second, Count/Second, and None
# :custom_unit - The user-defined CustomUnit applied to a Measure. Please see the key term Unit.
#
# :dimentions
# Dimensions for EC2 Metrics:
# * ImageId - shows the requested metric for all instances running this EC2 Amazon Machine Image(AMI)
# * AvailabilityZone - shows the requested metric for all instances running in that EC2 Availability Zone
# * CapacityGroupName - shows the requested metric for all instances in the specified capacity group - this dimension is
# only available for EC2 metrics when the instances are in an Amazon Automatic Scaling Service
# Capacity Group
# * InstanceId - shows the requested metric for only the identified instance
# * InstanceType - shows the requested metric for all instances running with that instance type
# * Service (required) - the name of the service that reported the monitoring data - for EC2 metrics, use "EC2"
# * Namespace (required) - in private beta, the available metrics are all reported by AWS services, so set this to "AWS"
# Dimensions for Load Balancing Metrics:
# * AccessPointName - shows the requested metric for the specified AccessPoint name
# * AvailabilityZone - shows the requested metric for all instances running in that EC2 Availability Zone
# * Service (required) - the name of the service that reported the monitoring data - for LoadBalancing metrics, use "LBS"
# * Namespace (required) - in private beta, the available metrics are all reported by AWS services, so set this to "AWS"
#
# :measure_name
# EC2 Metrics:
# * CPUUtilization the percentage of allocated EC2 Compute Units that are currently in use on the instance. Units are Percent.
# * NetworkIn - the number of bytes received on all network interfaces by the instance. Units are Bytes.
# * NetworkOut - the number of bytes sent out on all network interfaces by the instance. Units are Bytes.
# * DiskReadOps - completed read operations from all disks available to the instance in one minute. Units are Count/Second.
# * DiskWriteOps - completed writes operations to all disks available to the instance in one minute. Units are Count/Second.
# * DiskReadBytes - bytes read from all disks available to the instance in one minute. Units are Bytes/Second.
# * DiskWriteBytes - bytes written to all disks available to the instance in one minute. Units are Bytes/Second.
# Load Balancing Metrics:
# * Latency - time taken between a request and the corresponding response as seen by the load balancer. Units are in
# seconds, and the available statistics include minimum, maximum, average and count.
# * RequestCount - number of requests processed by the AccessPoint over the valid period. Units are count per second, and
# the available statistics include minimum, maximum and sum. A valid period can be anything equal to or
# multiple of sixty (60) seconds.
# * HealthyHostCount - number of healthy EndPoints for the valid Period. A valid period can be anything equal to or a multiple
# of sixty (60) seconds. Units are the count of EndPoints. The meaningful statistic for HealthyHostCount
# is the average for an AccessPoint within an Availability Zone. Both Load Balancing dimensions,
# AccessPointName and AvailabilityZone, should be specified when retreiving HealthyHostCount.
# * UnHealthyHostCount - number of unhealthy EndPoints for the valid Period. A valid period can be anything equal to or a multiple
# of sixty (60) seconds. Units are the count of EndPoints. The meaningful statistic for UnHealthyHostCount
# is the average for an AccessPoint within Availability Amazon Monitoring Service Developer Guide Load
# Balancing Metrics Version PRIVATE BETA 2009-01-22 19 Zone. Both Load Balancing dimensions, AccessPointName
# and AvailabilityZone, should be specified when retreiving UnHealthyHostCount.
#
def get_metric_statistics(options={})
# Period (60 sec by default)
period = (options[:period] && options[:period].to_i) || 60
# Statistics ('Average' by default)
statistics = options[:statistics].to_a.flatten
statistics = statistics.blank? ? ['Average'] : statistics.map{|statistic| statistic.to_s.capitalize }
# Times (5.min.ago up to now by default)
start_time = options[:start_time] || (Time.now.utc - 5*60)
start_time = start_time.utc.strftime("%Y-%m-%dT%H:%M:%S+00:00") if start_time.is_a?(Time)
end_time = options[:end_time] || Time.now.utc
end_time = end_time.utc.strftime("%Y-%m-%dT%H:%M:%S+00:00") if end_time.is_a?(Time)
# Measure name
measure_name = options[:measure_name] || 'CPUUtilization'
# Dimentions (a hash, empty by default)
dimentions = options[:dimentions] || {}
#
request_hash = { 'Period' => period,
'StartTime' => start_time,
'EndTime' => end_time,
'MeasureName' => measure_name }
request_hash['Unit'] = options[:unit] if options[:unit]
request_hash['CustomUnit'] = options[:custom_unit] if options[:custom_unit]
request_hash['Namespace'] = options[:namespace] if options[:namespace]
request_hash.merge!(amazonize_list('Statistics.member', statistics))
# dimentions
dim = []
dimentions.each do |key, values|
values.to_a.each { |value| dim << [key, value] }
end
request_hash.merge!(amazonize_list(['Dimensions.member.?.Name', 'Dimensions.member.?.Value'], dim))
#
link = generate_request("GetMetricStatistics", request_hash)
request_info(link, GetMetricStatisticsParser.new(:logger => @logger))
end
# This call returns a list of the valid metrics for which there is recorded data available to a you.
#
# acw.list_metrics #=>
# [ { :namespace => "AWS/ELB",
# :measure_name => "HealthyHostCount",
# :dimentions => { "LoadBalancerName"=>"test-kd1" } },
# { :namespace => "AWS/ELB",
# :measure_name => "UnHealthyHostCount",
# :dimentions => { "LoadBalancerName"=>"test-kd1" } } ]
def list_metrics
link = generate_request("ListMetrics")
request_cache_or_info :list_metrics, link, ListMetricsParser, @@bench, true
end
#-----------------------------------------------------------------
# PARSERS: MetricStatistics
#-----------------------------------------------------------------
class GetMetricStatisticsParser < RightAWSParser #:nodoc:
def tagstart(name, attributes)
@item = {} if name == 'member'
end
def tagend(name)
case name
when 'Timestamp' then @item[:timestamp] = Time.parse(@text)
when 'Unit' then @item[:unit] = @text
when 'CustomUnit' then @item[:custom_unit] = @text
when 'Samples' then @item[:samples] = @text.to_f
when 'Average' then @item[:average] = @text.to_f
when 'Minimum' then @item[:minimum] = @text.to_f
when 'Maximum' then @item[:maximum] = @text.to_f
when 'Sum' then @item[:sum] = @text.to_f
when 'member' then @result[:datapoints] << @item
when 'Label' then @result[:label] = @text
end
end
def reset
@result = { :datapoints => [] }
end
end
class ListMetricsParser < RightAWSParser #:nodoc:
def tagstart(name, attributes)
case name
when 'member'
case @xmlpath
when @p then @item = { :dimentions => {} }
end
end
end
def tagend(name)
case name
when 'MeasureName' then @item[:measure_name] = @text
when 'Namespace' then @item[:namespace] = @text
when 'Name' then @dname = @text
when 'Value' then @dvalue = @text
when 'member'
case @xmlpath
when "#@p/member/Dimensions" then @item[:dimentions][@dname] = @dvalue
when @p then @result << @item
end
end
end
def reset
@p = 'ListMetricsResponse/ListMetricsResult/Metrics'
@result = []
end
end
end
end