amazon-ec2 0.5.5 → 0.6.0
Sign up to get free protection for your applications and to get access to all the features.
- data/ChangeLog +3 -0
- data/Rakefile +1 -1
- data/VERSION +1 -1
- data/amazon-ec2.gemspec +10 -4
- data/lib/AWS.rb +1 -2
- data/lib/AWS/Autoscaling.rb +70 -0
- data/lib/AWS/Autoscaling/autoscaling.rb +273 -0
- data/lib/AWS/Cloudwatch.rb +32 -0
- data/lib/AWS/Cloudwatch/monitoring.rb +80 -0
- data/lib/AWS/EC2.rb +1 -1
- data/lib/AWS/EC2/instances.rb +35 -0
- data/lib/AWS/ELB.rb +39 -0
- data/test/test_Autoscaling_groups.rb +336 -0
- data/test/test_EC2_instances.rb +81 -0
- metadata +9 -3
data/ChangeLog
CHANGED
data/Rakefile
CHANGED
@@ -6,7 +6,7 @@ require 'jeweler'
|
|
6
6
|
Jeweler::Tasks.new do |gem|
|
7
7
|
gem.name = "amazon-ec2"
|
8
8
|
gem.summary = %Q{Amazon EC2 Ruby Gem}
|
9
|
-
gem.description = %Q{A Ruby library for accessing the Amazon Web Services Elastic Compute Cloud (EC2)
|
9
|
+
gem.description = %Q{A Ruby library for accessing the Amazon Web Services Elastic Compute Cloud (EC2), Elastic Load Balancer (ELB), Cloudwatch, and Autoscaling API's.}
|
10
10
|
gem.email = "glenn@rempe.us"
|
11
11
|
gem.homepage = "http://github.com/grempe/amazon-ec2"
|
12
12
|
gem.authors = ["Glenn Rempe"]
|
data/VERSION
CHANGED
@@ -1 +1 @@
|
|
1
|
-
0.
|
1
|
+
0.6.0
|
data/amazon-ec2.gemspec
CHANGED
@@ -5,12 +5,12 @@
|
|
5
5
|
|
6
6
|
Gem::Specification.new do |s|
|
7
7
|
s.name = %q{amazon-ec2}
|
8
|
-
s.version = "0.
|
8
|
+
s.version = "0.6.0"
|
9
9
|
|
10
10
|
s.required_rubygems_version = Gem::Requirement.new(">= 0") if s.respond_to? :required_rubygems_version=
|
11
11
|
s.authors = ["Glenn Rempe"]
|
12
|
-
s.date = %q{2009-
|
13
|
-
s.description = %q{A Ruby library for accessing the Amazon Web Services Elastic Compute Cloud (EC2)
|
12
|
+
s.date = %q{2009-09-30}
|
13
|
+
s.description = %q{A Ruby library for accessing the Amazon Web Services Elastic Compute Cloud (EC2), Elastic Load Balancer (ELB), Cloudwatch, and Autoscaling API's.}
|
14
14
|
s.email = %q{glenn@rempe.us}
|
15
15
|
s.executables = ["ec2-gem-example.rb", "ec2-gem-profile.rb", "ec2sh", "setup.rb"]
|
16
16
|
s.extra_rdoc_files = [
|
@@ -35,6 +35,10 @@ Gem::Specification.new do |s|
|
|
35
35
|
"bin/setup.rb",
|
36
36
|
"deps.rip",
|
37
37
|
"lib/AWS.rb",
|
38
|
+
"lib/AWS/Autoscaling.rb",
|
39
|
+
"lib/AWS/Autoscaling/autoscaling.rb",
|
40
|
+
"lib/AWS/Cloudwatch.rb",
|
41
|
+
"lib/AWS/Cloudwatch/monitoring.rb",
|
38
42
|
"lib/AWS/EC2.rb",
|
39
43
|
"lib/AWS/EC2/availability_zones.rb",
|
40
44
|
"lib/AWS/EC2/console.rb",
|
@@ -55,6 +59,7 @@ Gem::Specification.new do |s|
|
|
55
59
|
"perftools/ec2prof-results.dot",
|
56
60
|
"perftools/ec2prof-results.txt",
|
57
61
|
"perftools/ec2prof.symbols",
|
62
|
+
"test/test_Autoscaling_groups.rb",
|
58
63
|
"test/test_EC2.rb",
|
59
64
|
"test/test_EC2_availability_zones.rb",
|
60
65
|
"test/test_EC2_console.rb",
|
@@ -83,7 +88,8 @@ Gem::Specification.new do |s|
|
|
83
88
|
s.rubygems_version = %q{1.3.5}
|
84
89
|
s.summary = %q{Amazon EC2 Ruby Gem}
|
85
90
|
s.test_files = [
|
86
|
-
"test/
|
91
|
+
"test/test_Autoscaling_groups.rb",
|
92
|
+
"test/test_EC2.rb",
|
87
93
|
"test/test_EC2_availability_zones.rb",
|
88
94
|
"test/test_EC2_console.rb",
|
89
95
|
"test/test_EC2_elastic_ips.rb",
|
data/lib/AWS.rb
CHANGED
@@ -214,11 +214,10 @@ module AWS
|
|
214
214
|
req['User-Agent'] = "github-amazon-ec2-ruby-gem"
|
215
215
|
|
216
216
|
response = @http.request(req, query)
|
217
|
-
|
217
|
+
|
218
218
|
# Make a call to see if we need to throw an error based on the response given by EC2
|
219
219
|
# All error classes are defined in EC2/exceptions.rb
|
220
220
|
aws_error?(response)
|
221
|
-
|
222
221
|
return response
|
223
222
|
|
224
223
|
end
|
@@ -0,0 +1,70 @@
|
|
1
|
+
module AWS
|
2
|
+
module Autoscaling
|
3
|
+
# Which host FQDN will we connect to for all API calls to AWS?
|
4
|
+
# If AS_URL is defined in the users ENV we can override the default with that.
|
5
|
+
#
|
6
|
+
# @example
|
7
|
+
# export AS_URL='http://autoscaling.amazonaws.com'
|
8
|
+
if ENV['AS_URL']
|
9
|
+
AS_URL = ENV['AS_URL']
|
10
|
+
VALID_HOSTS = ['http://autoscaling.amazonaws.com']
|
11
|
+
raise ArgumentError, "Invalid AS_URL environment variable : #{AS_URL}" unless VALID_HOSTS.include?(AS_URL)
|
12
|
+
DEFAULT_HOST = URI.parse(AS_URL).host
|
13
|
+
else
|
14
|
+
# Default US API endpoint
|
15
|
+
DEFAULT_HOST = 'autoscaling.amazonaws.com'
|
16
|
+
end
|
17
|
+
|
18
|
+
API_VERSION = '2009-05-15'
|
19
|
+
|
20
|
+
class Base < AWS::Base
|
21
|
+
def api_version
|
22
|
+
API_VERSION
|
23
|
+
end
|
24
|
+
|
25
|
+
def default_host
|
26
|
+
DEFAULT_HOST
|
27
|
+
end
|
28
|
+
|
29
|
+
# Raises the appropriate error if the specified Net::HTTPResponse object
|
30
|
+
# contains an Amazon EC2 error; returns +false+ otherwise.
|
31
|
+
def aws_error?(response)
|
32
|
+
|
33
|
+
# return false if we got a HTTP 200 code,
|
34
|
+
# otherwise there is some type of error (40x,50x) and
|
35
|
+
# we should try to raise an appropriate exception
|
36
|
+
# from one of our exception classes defined in
|
37
|
+
# exceptions.rb
|
38
|
+
return false if response.is_a?(Net::HTTPSuccess)
|
39
|
+
|
40
|
+
# parse the XML document so we can walk through it
|
41
|
+
doc = REXML::Document.new(response.body)
|
42
|
+
|
43
|
+
# Check that the Error element is in the place we would expect.
|
44
|
+
# and if not raise a generic error exception
|
45
|
+
unless doc.root.elements[1].name == "Error"
|
46
|
+
raise Error, "Unexpected error format. response.body is: #{response.body}"
|
47
|
+
end
|
48
|
+
|
49
|
+
# An valid error response looks like this:
|
50
|
+
# <?xml version="1.0"?><Response><Errors><Error><Code>InvalidParameterCombination</Code><Message>Unknown parameter: foo</Message></Error></Errors><RequestID>291cef62-3e86-414b-900e-17246eccfae8</RequestID></Response>
|
51
|
+
# AWS EC2 throws some exception codes that look like Error.SubError. Since we can't name classes this way
|
52
|
+
# we need to strip out the '.' in the error 'Code' and we name the error exceptions with this
|
53
|
+
# non '.' name as well.
|
54
|
+
error_code = doc.root.elements['//ErrorResponse/Error/Code'].text.gsub('.', '')
|
55
|
+
error_message = doc.root.elements['//ErrorResponse/Error/Message'].text
|
56
|
+
|
57
|
+
# Raise one of our specific error classes if it exists.
|
58
|
+
# otherwise, throw a generic EC2 Error with a few details.
|
59
|
+
if AWS.const_defined?(error_code)
|
60
|
+
raise AWS.const_get(error_code), error_message
|
61
|
+
else
|
62
|
+
raise AWS::Error, error_message
|
63
|
+
end
|
64
|
+
|
65
|
+
end
|
66
|
+
|
67
|
+
end
|
68
|
+
|
69
|
+
end
|
70
|
+
end
|
@@ -0,0 +1,273 @@
|
|
1
|
+
module AWS
|
2
|
+
module Autoscaling
|
3
|
+
class Base < AWS::Base
|
4
|
+
|
5
|
+
# Create a launch configuration
|
6
|
+
# Creates a new Launch Configuration. Please note that the launch configuration name used must be unique, within the scope of your AWS account, and the maximum limit of launch configurations must not yet have been met, or else the call will fail.
|
7
|
+
# Once created, the new launch configuration is available for immediate use.
|
8
|
+
#
|
9
|
+
# @option options [String] :launch_configuration_name (nil) the name of the launch configuration
|
10
|
+
# @option options [String] :image_id (nil) the image id to use with this launch configuration
|
11
|
+
# @option options [String] :instance_type (nil) the type of instance to launch
|
12
|
+
# @option options [Array] :security_groups (nil) the names of security_groups to launch within
|
13
|
+
# @option options [String] :key_name (nil) the name of the EC2 key pair
|
14
|
+
# @option options [String] :user_data (nil) the user data available to the launched EC2 instances
|
15
|
+
# @option options [String] :kernel_id (nil) the ID of the kernel associated with the EC2 ami
|
16
|
+
# @option options [String] :ramdisk_id (nil) the name of the RAM disk associated with the EC2 ami
|
17
|
+
# @option options [Array] :block_device_mappings (nil) specifies how block devices are exposed to the instance
|
18
|
+
def create_launch_configuration( options = {})
|
19
|
+
raise ArgumentError, "No :image_id provided" if options[:image_id].nil? || options[:image_id].empty?
|
20
|
+
raise ArgumentError, "No :launch_configuration_name provided" if options[:launch_configuration_name].nil? || options[:launch_configuration_name].empty?
|
21
|
+
raise ArgumentError, "No :instance_type provided" if options[:instance_type].nil? || options[:instance_type].empty?
|
22
|
+
|
23
|
+
params = {}
|
24
|
+
params["ImageId"] = options[:image_id]
|
25
|
+
params["KeyName"] = options[:key_name] if options[:key_name]
|
26
|
+
params["LaunchConfigurationName"] = options[:launch_configuration_name]
|
27
|
+
params.merge!(pathlist('SecurityGroups.member', [options[:security_groups]].flatten)) if options[:security_groups]
|
28
|
+
params["UserData"] = options[:user_data] if options[:user_data]
|
29
|
+
params["InstanceType"] = options[:instance_type] if options[:instance_type]
|
30
|
+
params["KernelId"] = options[:kernel_id] if options[:kernel_id]
|
31
|
+
params["RamdiskId"] = options[:ramdisk_id] if options[:ramdisk_id]
|
32
|
+
params.merge!(pathlist('BlockDeviceMappings.member', [options[:block_device_mappings]].flatten)) if options[:block_device_mappings]
|
33
|
+
|
34
|
+
return response_generator(:action => "CreateLaunchConfiguration", :params => params)
|
35
|
+
end
|
36
|
+
|
37
|
+
# Creates a new AutoScalingGroup with the specified name.
|
38
|
+
# You must not have already used up your entire quota of AutoScalingGroups in order for this call to be successful. Once the creation request is completed, the AutoScalingGroup is ready to be used in other calls.
|
39
|
+
#
|
40
|
+
# @option options [String] :autoscaling_group_name (nil) the name of the autoscaling group
|
41
|
+
# @option options [Array] :availability_zones (nil) The availability_zones for the group
|
42
|
+
# @option options [String] :launch_configuration_name (nil) the name of the launch_configuration group
|
43
|
+
# @option options [String] :min_size (nil) minimum size of the group
|
44
|
+
# @option options [String] :max_size (nil) the maximum size of the group
|
45
|
+
# @option options [String] :load_balancer_names (nil) the names of the load balancers
|
46
|
+
# @option options [String] :cooldown (nil) the amount of time after a scaling activity complese before any further trigger-related scaling activities can start
|
47
|
+
def create_autoscaling_group( options = {} )
|
48
|
+
raise ArgumentError, "No :autoscaling_group_name provided" if options[:autoscaling_group_name].nil? || options[:autoscaling_group_name].empty?
|
49
|
+
raise ArgumentError, "No :availability_zones provided" if options[:availability_zones].nil? || options[:availability_zones].empty?
|
50
|
+
raise ArgumentError, "No :launch_configuration_name provided" if options[:launch_configuration_name].nil? || options[:launch_configuration_name].empty?
|
51
|
+
raise ArgumentError, "No :min_size provided" if options[:min_size].nil?
|
52
|
+
raise ArgumentError, "No :max_size provided" if options[:max_size].nil?
|
53
|
+
|
54
|
+
params = {}
|
55
|
+
|
56
|
+
params.merge!(pathlist('AvailabilityZones.member', [options[:availability_zones]].flatten))
|
57
|
+
params['LaunchConfigurationName'] = options[:launch_configuration_name]
|
58
|
+
params['AutoScalingGroupName'] = options[:autoscaling_group_name]
|
59
|
+
params['MinSize'] = options[:min_size].to_s
|
60
|
+
params['MaxSize'] = options[:max_size].to_s
|
61
|
+
params['LoadBalancerNames'] = options[:load_balancer_names] if options[:load_balancer_names]
|
62
|
+
params['CoolDown'] = options[:cooldown] if options[:cooldown]
|
63
|
+
|
64
|
+
return response_generator(:action => "CreateAutoScalingGroup", :params => params)
|
65
|
+
end
|
66
|
+
|
67
|
+
# Create or update scaling trigger
|
68
|
+
# This call sets the parameters that governs when and how to scale an AutoScalingGroup.
|
69
|
+
#
|
70
|
+
# @option options [String] :autoscaling_group_name (nil) the name of the autoscaling group
|
71
|
+
# @option options [Array|Hash] :dimensions (nil) The dimensions associated with the metric used by the trigger to determine whether to activate
|
72
|
+
# This must be given as either an array or a hash
|
73
|
+
# When called as a hash, the values must look like: {:name => "name", :value => "value"}
|
74
|
+
# In the array format, the first value is assumed to be the name and the second is assumed to be the value
|
75
|
+
# @option options [String] :measure_name (nil) the measure name associated with the metric used by the trigger
|
76
|
+
# @option options [String|Integer] :period (nil) the period associated with the metric in seconds
|
77
|
+
# @option options [String] :statistic (nil) The particular statistic used by the trigger when fetching metric statistics to examine. Must be one of the following: Minimum, Maximum, Sum, Average
|
78
|
+
# @option options [String] :trigger_name (nil) the name for this trigger
|
79
|
+
# @option options [String] :unit (nil) the standard unit of measurement for a given measure
|
80
|
+
# @option options [String|Integer] :lower_threshold (nil) the lower limit for the metric. If all datapoints in the last :breach_duration seconds fall below the lower threshold, the trigger will activate
|
81
|
+
# @option options [String|Integer] :lower_breach_scale_increment (nil) the incremental amount to use when performing scaling activities when the lower threshold has been breached
|
82
|
+
# @option options [String|Integer] :upper_threshold (nil) the upper limit for the metric. If all datapoints in the last :breach_duration seconds exceed the upper threshold, the trigger will activate
|
83
|
+
# @option options [String|Integer] :upper_breach_scale_increment (nil) the incremental amount to use when performing scaling activities when the upper threshold has been breached
|
84
|
+
def create_or_updated_scaling_trigger( options = {} )
|
85
|
+
if options[:dimensions].nil? || options[:dimensions].empty?
|
86
|
+
raise ArgumentError, "No :dimensions provided"
|
87
|
+
end
|
88
|
+
raise ArgumentError, "No :autoscaling_group_name provided" if options[:autoscaling_group_name].nil? || options[:autoscaling_group_name].empty?
|
89
|
+
raise ArgumentError, "No :measure_name provided" if options[:measure_name].nil? || options[:measure_name].empty?
|
90
|
+
raise ArgumentError, "No :statistic provided" if options[:statistic].nil? || options[:statistic].empty?
|
91
|
+
raise ArgumentError, "No :period provided" if options[:period].nil?
|
92
|
+
raise ArgumentError, "No :trigger_name provided" if options[:trigger_name].nil? || options[:trigger_name].empty?
|
93
|
+
raise ArgumentError, "No :lower_threshold provided" if options[:lower_threshold].nil?
|
94
|
+
raise ArgumentError, "No :lower_breach_scale_increment provided" if options[:lower_breach_scale_increment].nil?
|
95
|
+
raise ArgumentError, "No :upper_threshold provided" if options[:upper_threshold].nil?
|
96
|
+
raise ArgumentError, "No :upper_breach_scale_increment provided" if options[:upper_breach_scale_increment].nil?
|
97
|
+
raise ArgumentError, "No :breach_duration provided" if options[:breach_duration].nil?
|
98
|
+
|
99
|
+
statistic_option_list = %w(Minimum Maximum Average Sum)
|
100
|
+
unless statistic_option_list.include?(options[:statistic])
|
101
|
+
raise ArgumentError, "The statistic option must be one of the following: #{statistic_option_list.join(", ")}"
|
102
|
+
end
|
103
|
+
|
104
|
+
params = {}
|
105
|
+
params['Unit'] = options[:unit] || "Seconds"
|
106
|
+
params['AutoScalingGroupName'] = options[:autoscaling_group_name]
|
107
|
+
case options[:dimensions]
|
108
|
+
when Array
|
109
|
+
params["Dimensions.member.1.Name"] = options[:dimensions][0]
|
110
|
+
params["Dimensions.member.1.Value"] = options[:dimensions][1]
|
111
|
+
when Hash
|
112
|
+
params["Dimensions.member.1.Name"] = options[:dimensions][:name]
|
113
|
+
params["Dimensions.member.1.Value"] = options[:dimensions][:value]
|
114
|
+
else
|
115
|
+
raise ArgumentError, "Dimensionss must be either an array or a hash"
|
116
|
+
end
|
117
|
+
params['MeasureName'] = options[:measure_name]
|
118
|
+
params['Statistic'] = options[:statistic]
|
119
|
+
params['Period'] = options[:period].to_s
|
120
|
+
params['TriggerName'] = options[:trigger_name]
|
121
|
+
params['LowerThreshold'] = options[:lower_threshold].to_s
|
122
|
+
params['LowerBreachScaleIncrement'] = options[:lower_breach_scale_increment].to_s
|
123
|
+
params['UpperThreshold'] = options[:upper_threshold].to_s
|
124
|
+
params['UpperBreachScaleIncrement'] = options[:upper_breach_scale_increment].to_s
|
125
|
+
params['BreachDuration'] = options[:breach_duration].to_s
|
126
|
+
|
127
|
+
return response_generator(:action => "CreateOrUpdateScalingTrigger", :params => params)
|
128
|
+
end
|
129
|
+
|
130
|
+
# Deletes all configuration for this AutoScalingGroup and also deletes the group.
|
131
|
+
# In order to successfully call this API, no triggers (and therefore, Scaling Activity) can be currently in progress. Once this call successfully executes, no further triggers will begin and the AutoScalingGroup will not be available for use in other API calls. See key term Trigger.
|
132
|
+
#
|
133
|
+
# @option options [String] :autoscaling_group_name (nil) the name of the autoscaling group
|
134
|
+
def delete_autoscaling_group( options = {} )
|
135
|
+
raise ArgumentError, "No :autoscaling_group_name provided" if options[:autoscaling_group_name].nil? || options[:autoscaling_group_name].empty?
|
136
|
+
params = { 'AutoScalingGroupName' => options[:autoscaling_group_name] }
|
137
|
+
return response_generator(:action => "DeleteAutoScalingGroup", :params => params)
|
138
|
+
end
|
139
|
+
|
140
|
+
# Deletes the given Launch Configuration.
|
141
|
+
# The launch configuration to be deleted must not be currently attached to any AutoScalingGroup. Once this call completes, the launch configuration is no longer available for use by any other API call.
|
142
|
+
#
|
143
|
+
# @option options [String] :launch_configuration_name (nil) the name of the launch_configuration
|
144
|
+
def delete_launch_configuration( options = {} )
|
145
|
+
raise ArgumentError, "No :launch_configuration_name provided" if options[:launch_configuration_name].nil? || options[:launch_configuration_name].empty?
|
146
|
+
params = { 'LaunchConfigurationName' => options[:launch_configuration_name] }
|
147
|
+
return response_generator(:action => "DeleteLaunchConfiguration", :params => params)
|
148
|
+
end
|
149
|
+
|
150
|
+
# Deletes the given trigger
|
151
|
+
# If a trigger is currently in progress, it will continue to run until its activities are complete.
|
152
|
+
#
|
153
|
+
# @option options [String] :trigger_name (nil) the name of the trigger to delete
|
154
|
+
def delete_trigger( options = {} )
|
155
|
+
raise ArgumentError, "No :trigger_name provided" if options[:trigger_name].nil? || options[:trigger_name].empty?
|
156
|
+
raise ArgumentError, "No :autoscaling_group_name provided" if options[:autoscaling_group_name].nil? || options[:autoscaling_group_name].empty?
|
157
|
+
params = { 'TriggerName' => options[:trigger_name], 'AutoScalingGroupName' => options[:autoscaling_group_name] }
|
158
|
+
return response_generator(:action => "DeleteTrigger", :params => params)
|
159
|
+
end
|
160
|
+
|
161
|
+
# Describe autoscaling group
|
162
|
+
# Returns a full description of the AutoScalingGroups from the given list. This includes all EC2 instances that are members of the group. If a list of names is not provided, then the full details of all AutoScalingGroups is returned. This style conforms to the EC2 DescribeInstances API behavior. See key term AutoScalingGroup.
|
163
|
+
#
|
164
|
+
# @option options [Array] :autoscaling_group_names (nil) the name of the autoscaling groups to describe
|
165
|
+
def describe_autoscaling_groups( options = {} )
|
166
|
+
options = { :autoscaling_group_names => [] }.merge(options)
|
167
|
+
params = pathlist("AutoScalingGroupNames.member", options[:autoscaling_group_names])
|
168
|
+
return response_generator(:action => "DescribeAutoScalingGroups", :params => params)
|
169
|
+
end
|
170
|
+
|
171
|
+
# Describe launch configurations
|
172
|
+
# Returns a full description of the launch configurations given the specified names. If no names are specified, then the full details of all launch configurations are returned. See key term Launch Configuration.
|
173
|
+
#
|
174
|
+
# @option options [Array] :launch_configuration_names (nil) the name of the launch_configurations to describe
|
175
|
+
def describe_launch_configurations( options = {} )
|
176
|
+
options = { :launch_configuration_names => [] }.merge(options)
|
177
|
+
params = pathlist("AutoScalingGroupNames.member", options[:launch_configuration_names])
|
178
|
+
params['MaxRecords'] = options[:max_records].to_s if options.has_key?(:max_records)
|
179
|
+
return response_generator(:action => "DescribeLaunchConfigurations", :params => params)
|
180
|
+
end
|
181
|
+
|
182
|
+
# Describe autoscaling activities
|
183
|
+
# Returns the scaling activities specified for the given group. If the input list is empty, all the activities from the past six weeks will be returned. Activities will be sorted by completion time. Activities that have no completion time will be considered as using the most recent possible time. See key term Scaling Activity.
|
184
|
+
#
|
185
|
+
# @option options [String] :autoscaling_group_name (nil) the name of the autoscaling_group_name
|
186
|
+
# @option options [String] :max_records (nil) the maximum number of scaling activities to return
|
187
|
+
# @option options [String] :launch_configuration_names (nil) activity_ids to return
|
188
|
+
def describe_scaling_activities( options = {} )
|
189
|
+
raise ArgumentError, "No :autoscaling_group_name provided" if options[:autoscaling_group_name].nil? || options[:autoscaling_group_name].empty?
|
190
|
+
|
191
|
+
params = {}
|
192
|
+
params['AutoScalingGroupName'] = options[:autoscaling_group_name]
|
193
|
+
params['MaxRecords'] = options[:max_records] if options.has_key?(:max_records)
|
194
|
+
params['ActivityIds'] = options[:activity_ids] if options.has_key?(:activity_ids)
|
195
|
+
return response_generator(:action => "DescribeScalingActivities", :params => params)
|
196
|
+
end
|
197
|
+
|
198
|
+
# Describe triggers
|
199
|
+
# Returns a full description of the trigger (see Trigger), in the specified AutoScalingGroup.
|
200
|
+
#
|
201
|
+
# @option options [String] :autoscaling_group_name (nil) the name of the autoscaling_group_name
|
202
|
+
def describe_triggers( options = {} )
|
203
|
+
raise ArgumentError, "No :autoscaling_group_name provided" if options[:autoscaling_group_name].nil? || options[:autoscaling_group_name].empty?
|
204
|
+
params = {}
|
205
|
+
params['AutoScalingGroupName'] = options[:autoscaling_group_name]
|
206
|
+
|
207
|
+
return response_generator(:action => "DescribeTriggers", :params => params)
|
208
|
+
end
|
209
|
+
|
210
|
+
# Set desired capacity
|
211
|
+
# This API adjusts the desired size of the AutoScalingGroup by initiating scaling activities, as necessary. When adjusting the size of the group downward, it is not possible to define which EC2 instances will be terminated. This applies to any auto-scaling decisions that might result in the termination of instances.
|
212
|
+
# To check the scaling status of the system, query the desired capacity using DescribeAutoScalingGroups and then query the actual capacity using DescribeAutoScalingGroups and looking at the instance lifecycle state in order to understand how close the system is to the desired capacity at any given time.
|
213
|
+
#
|
214
|
+
# @option options [String] :autoscaling_group_name (nil) the name of the autoscaling_group_name
|
215
|
+
# @option options [String] :desired_capacity (nil) the new capacity setting for the group
|
216
|
+
def set_desired_capacity( options = {} )
|
217
|
+
raise ArgumentError, "No :autoscaling_group_name provided" if options[:autoscaling_group_name].nil? || options[:autoscaling_group_name].empty?
|
218
|
+
raise ArgumentError, "No :desired_capacity provided" if options[:desired_capacity].nil? || options[:desired_capacity].empty?
|
219
|
+
|
220
|
+
params = {}
|
221
|
+
params['AutoScalingGroupName'] = options[:autoscaling_group_name]
|
222
|
+
params['DesiredCapacity'] = options[:desired_capacity].to_s
|
223
|
+
|
224
|
+
return response_generator(:action => "SetDesiredCapacity", :params => params)
|
225
|
+
end
|
226
|
+
|
227
|
+
|
228
|
+
# Terminate instance in an autoscaling group
|
229
|
+
# This call will terminate the specified Instance. Optionally, the desired group size can be adjusted. If set to true, the default, the AutoScalingGroup size will decrease by one. If the AutoScalingGroup is associated with a LoadBalancer, the system will deregister the instance before terminating it.
|
230
|
+
# This call simply registers a termination request. The termination of the instance can not happen immediately.
|
231
|
+
#
|
232
|
+
# @option options [String] :instance_id (nil) the instance id to terminate
|
233
|
+
# @option options [String] :decrement_desired_capacity (nil) specified whether terminating this instance should also decrement the size of the autoscaling group
|
234
|
+
def terminate_instance_in_autoscaling_group( options = {} )
|
235
|
+
raise ArgumentError, "No :instance_id provided" if options[:instance_id].nil? || options[:instance_id].empty?
|
236
|
+
|
237
|
+
params = {}
|
238
|
+
params['InstanceId'] = options[:instance_id]
|
239
|
+
params['ShouldDecrementDesiredCapacity'] = options[:decrement_desired_capacity].to_s if options.has_key?(:decrement_desired_capacity)
|
240
|
+
|
241
|
+
return response_generator(:action => "TerminateInstanceInAutoScalingGroup", :params => params)
|
242
|
+
end
|
243
|
+
|
244
|
+
# Creates a new AutoScalingGroup with the specified name.
|
245
|
+
# Updates the configuration for the given AutoScalingGroup. If MaxSize is lower than the current size, then there will be an implicit call to SetDesiredCapacity to set the group to the new MaxSize. The same is true for MinSize there will also be an implicit call to SetDesiredCapacity. All optional parameters are left unchanged if not passed in the request.=
|
246
|
+
# The new settings are registered upon the completion of this call. Any launch configuration settings will take effect on any triggers after this call returns. However, triggers that are currently in progress can not be affected. See key term Trigger.
|
247
|
+
#
|
248
|
+
# @option options [String] :autoscaling_group_name (nil) the name of the autoscaling group
|
249
|
+
# @option options [Array] :availability_zones (nil) The availability_zones for the group
|
250
|
+
# @option options [String] :launch_configuration_name (nil) the name of the launch_configuration group
|
251
|
+
# @option options [String] :min_size (nil) minimum size of the group
|
252
|
+
# @option options [String] :max_size (nil) the maximum size of the group
|
253
|
+
# @option options [String] :cooldown (nil) the amount of time after a scaling activity complese before any further trigger-related scaling activities can start
|
254
|
+
def update_autoscaling_group( options = {})
|
255
|
+
raise ArgumentError, "No :autoscaling_group_name provided" if options[:autoscaling_group_name].nil? || options[:autoscaling_group_name].empty?
|
256
|
+
|
257
|
+
params = {}
|
258
|
+
|
259
|
+
params.merge!(pathlist('AvailabilityZones.member', [options[:availability_zones]].flatten)) if options.has_key?(:availability_zones)
|
260
|
+
params['LaunchConfigurationName'] = options[:launch_configuration_name] if options.has_key?(:launch_configuration_name)
|
261
|
+
params['AutoScalingGroupName'] = options[:autoscaling_group_name]
|
262
|
+
params['MinSize'] = options[:min_size] if options.has_key?(:min_size)
|
263
|
+
params['MaxSize'] = options[:max_size] if options.has_key?(:max_size)
|
264
|
+
params['CoolDown'] = options[:cooldown] if options.has_key?(:cooldown)
|
265
|
+
|
266
|
+
return response_generator(:action => "UpdateAutoScalingGroup", :params => params)
|
267
|
+
|
268
|
+
end
|
269
|
+
|
270
|
+
end
|
271
|
+
end
|
272
|
+
end
|
273
|
+
|
@@ -0,0 +1,32 @@
|
|
1
|
+
module AWS
|
2
|
+
module Cloudwatch
|
3
|
+
|
4
|
+
# Which host FQDN will we connect to for all API calls to AWS?
|
5
|
+
# If AWS_CLOUDWATCH_URL is defined in the users ENV we can override the default with that.
|
6
|
+
#
|
7
|
+
# @example
|
8
|
+
# export AWS_CLOUDWATCH_URL='https://montoring.amazonaws.com'
|
9
|
+
if ENV['AWS_CLOUDWATCH_URL']
|
10
|
+
AWS_CLOUDWATCH_URL = ENV['AWS_CLOUDWATCH_URL']
|
11
|
+
VALID_HOSTS = ['monitoring.amazonaws.com']
|
12
|
+
raise ArgumentError, "Invalid AWS_CLOUDWATCH_URL environment variable : #{AWS_CLOUDWATCH_URL}" unless VALID_HOSTS.include?(AWS_CLOUDWATCH_URL)
|
13
|
+
DEFAULT_HOST = URI.parse(AWS_CLOUDWATCH_URL).host
|
14
|
+
else
|
15
|
+
# Default US API endpoint
|
16
|
+
DEFAULT_HOST = 'monitoring.amazonaws.com'
|
17
|
+
end
|
18
|
+
|
19
|
+
API_VERSION = '2009-05-15'
|
20
|
+
|
21
|
+
class Base < AWS::Base
|
22
|
+
def api_version
|
23
|
+
API_VERSION
|
24
|
+
end
|
25
|
+
|
26
|
+
def default_host
|
27
|
+
DEFAULT_HOST
|
28
|
+
end
|
29
|
+
end
|
30
|
+
|
31
|
+
end
|
32
|
+
end
|
@@ -0,0 +1,80 @@
|
|
1
|
+
module AWS
|
2
|
+
module Cloudwatch
|
3
|
+
class Base < AWS::Base
|
4
|
+
|
5
|
+
# This method call lists available Cloudwatch metrics attached to your EC2
|
6
|
+
# account. To get further information from the metrics, you'll then need to
|
7
|
+
# call get_metric_statistics.
|
8
|
+
#
|
9
|
+
# there are no options available to this method.
|
10
|
+
|
11
|
+
def list_metrics
|
12
|
+
raise ArgumentError, "Server must be monitoring.amazonaws.com" if server != 'monitoring.amazonaws.com'
|
13
|
+
return response_generator(:action => 'ListMetrics', :params => {})
|
14
|
+
end
|
15
|
+
|
16
|
+
# get_metric_statistics pulls a hashed array from Cloudwatch with the stats
|
17
|
+
# of your requested metric.
|
18
|
+
# Once you get the data out, if you assign the results into an object like:
|
19
|
+
# res = @mon.get_metric_statistics(:measure_name => 'RequestCount', \
|
20
|
+
# :statistics => 'Average', :namespace => 'AWS/ELB')
|
21
|
+
#
|
22
|
+
# This call gets the average request count against your ELB at each sampling period
|
23
|
+
# for the last 24 hours. You can then attach a block to the following iterator
|
24
|
+
# to do whatever you need to:
|
25
|
+
# res['GetMetricStatisticsResult']['Datapoints']['member'].each
|
26
|
+
#
|
27
|
+
# @option options [String] :custom_unit (nil) not currently available, placeholder
|
28
|
+
# @option options [String] :dimensions (nil) Option to filter your data on. Check the developer guide
|
29
|
+
# @option options [Time] :end_time (Time.now()) Outer bound of the date range you want to view
|
30
|
+
# @option options [String] :measure_name (nil) The measure you want to check. Must correspond to
|
31
|
+
# => provided options
|
32
|
+
# @option options [String] :namespace ('AWS/EC2') The namespace of your measure_name. Currently, 'AWS/EC2' and 'AWS/ELB' are available
|
33
|
+
# @option options [Integer] :period (60) Granularity in seconds of the returned datapoints. Multiples of 60 only
|
34
|
+
# @option options [String] :statistics (nil) The statistics to be returned for your metric. See the developer guide for valid options. Required.
|
35
|
+
# @option options [Time] :start_time (Time.now() - 86400) Inner bound of the date range you want to view. Defaults to 24 hours ago
|
36
|
+
# @option options [String] :unit (nil) Standard unit for a given Measure. See the developer guide for valid options.
|
37
|
+
|
38
|
+
|
39
|
+
def get_metric_statistics ( options ={} )
|
40
|
+
options = { :custom_unit => nil,
|
41
|
+
:dimensions => nil,
|
42
|
+
:end_time => Time.now(), #req
|
43
|
+
:measure_name => "", #req
|
44
|
+
:namespace => "AWS/EC2",
|
45
|
+
:period => 60,
|
46
|
+
:statistics => "", # req
|
47
|
+
:start_time => (Time.now() - 86400), # Default to yesterday
|
48
|
+
:unit => "" }.merge(options)
|
49
|
+
|
50
|
+
raise ArgumentError, ":end_time must be provided" if options[:end_time].nil?
|
51
|
+
raise ArgumentError, ":end_time must be a Time object" if options[:end_time].class != Time
|
52
|
+
raise ArgumentError, ":start_time must be provided" if options[:start_time].nil?
|
53
|
+
raise ArgumentError, ":start_time must be a Time object" if options[:start_time].class != Time
|
54
|
+
raise ArgumentError, "Server must be monitoring.amazonaws.com" if server != 'monitoring.amazonaws.com'
|
55
|
+
raise ArgumentError, ":start_time must be before :end_time" if options[:start_time] > options[:end_time]
|
56
|
+
raise ArgumentError, ":measure_name must be provided" if options[:measure_name].nil? || options[:measure_name].empty?
|
57
|
+
raise ArgumentError, ":statistics must be provided" if options[:statistics].nil? || options[:statistics].empty?
|
58
|
+
|
59
|
+
params = {
|
60
|
+
"CustomUnit" => options[:custom_unit],
|
61
|
+
"Dimensions" => options[:dimensions],
|
62
|
+
"EndTime" => options[:end_time].iso8601,
|
63
|
+
"MeasureName" => options[:measure_name],
|
64
|
+
"Namespace" => options[:namespace],
|
65
|
+
"Period" => options[:period].to_s,
|
66
|
+
"Statistics.member.1" => options[:statistics],
|
67
|
+
"StartTime" => options[:start_time].iso8601,
|
68
|
+
"Unit" => options[:unit]
|
69
|
+
}
|
70
|
+
|
71
|
+
return response_generator(:action => 'GetMetricStatistics', :params => params)
|
72
|
+
|
73
|
+
end
|
74
|
+
|
75
|
+
end
|
76
|
+
|
77
|
+
end
|
78
|
+
|
79
|
+
end
|
80
|
+
|
data/lib/AWS/EC2.rb
CHANGED
data/lib/AWS/EC2/instances.rb
CHANGED
@@ -171,6 +171,41 @@ module AWS
|
|
171
171
|
|
172
172
|
end
|
173
173
|
|
174
|
+
|
175
|
+
# The MonitorInstances operation tells Cloudwatch to begin logging metrics from one or more EC2 instances
|
176
|
+
#
|
177
|
+
# @option options [Array] :instance_id ([])
|
178
|
+
#
|
179
|
+
def monitor_instances( options = {} )
|
180
|
+
|
181
|
+
options = { :instance_id => [] }.merge(options)
|
182
|
+
|
183
|
+
raise ArgumentError, "No :instance_id provided" if options[:instance_id].nil? || options[:instance_id].empty?
|
184
|
+
|
185
|
+
params = pathlist("InstanceId", options[:instance_id])
|
186
|
+
|
187
|
+
return response_generator(:action => "MonitorInstances", :params => params)
|
188
|
+
|
189
|
+
end
|
190
|
+
|
191
|
+
|
192
|
+
|
193
|
+
# The UnmonitorInstances operation tells Cloudwatch to stop logging metrics from one or more EC2 instances
|
194
|
+
#
|
195
|
+
# @option options [Array] :instance_id ([])
|
196
|
+
#
|
197
|
+
def unmonitor_instances( options = {} )
|
198
|
+
|
199
|
+
options = { :instance_id => [] }.merge(options)
|
200
|
+
|
201
|
+
raise ArgumentError, "No :instance_id provided" if options[:instance_id].nil? || options[:instance_id].empty?
|
202
|
+
|
203
|
+
params = pathlist("InstanceId", options[:instance_id])
|
204
|
+
|
205
|
+
return response_generator(:action => "UnmonitorInstances", :params => params)
|
206
|
+
|
207
|
+
end
|
208
|
+
|
174
209
|
end
|
175
210
|
|
176
211
|
end
|
data/lib/AWS/ELB.rb
CHANGED
@@ -26,6 +26,45 @@ module AWS
|
|
26
26
|
def default_host
|
27
27
|
DEFAULT_HOST
|
28
28
|
end
|
29
|
+
|
30
|
+
# Raises the appropriate error if the specified Net::HTTPResponse object
|
31
|
+
# contains an Amazon EC2 error; returns +false+ otherwise.
|
32
|
+
def aws_error?(response)
|
33
|
+
|
34
|
+
# return false if we got a HTTP 200 code,
|
35
|
+
# otherwise there is some type of error (40x,50x) and
|
36
|
+
# we should try to raise an appropriate exception
|
37
|
+
# from one of our exception classes defined in
|
38
|
+
# exceptions.rb
|
39
|
+
return false if response.is_a?(Net::HTTPSuccess)
|
40
|
+
|
41
|
+
# parse the XML document so we can walk through it
|
42
|
+
doc = REXML::Document.new(response.body)
|
43
|
+
|
44
|
+
# Check that the Error element is in the place we would expect.
|
45
|
+
# and if not raise a generic error exception
|
46
|
+
unless doc.root.elements[1].name == "Error"
|
47
|
+
raise Error, "Unexpected error format. response.body is: #{response.body}"
|
48
|
+
end
|
49
|
+
|
50
|
+
# An valid error response looks like this:
|
51
|
+
# <?xml version="1.0"?><Response><Errors><Error><Code>InvalidParameterCombination</Code><Message>Unknown parameter: foo</Message></Error></Errors><RequestID>291cef62-3e86-414b-900e-17246eccfae8</RequestID></Response>
|
52
|
+
# AWS EC2 throws some exception codes that look like Error.SubError. Since we can't name classes this way
|
53
|
+
# we need to strip out the '.' in the error 'Code' and we name the error exceptions with this
|
54
|
+
# non '.' name as well.
|
55
|
+
error_code = doc.root.elements['//ErrorResponse/Error/Code'].text.gsub('.', '')
|
56
|
+
error_message = doc.root.elements['//ErrorResponse/Error/Message'].text
|
57
|
+
|
58
|
+
# Raise one of our specific error classes if it exists.
|
59
|
+
# otherwise, throw a generic EC2 Error with a few details.
|
60
|
+
if AWS.const_defined?(error_code)
|
61
|
+
raise AWS.const_get(error_code), error_message
|
62
|
+
else
|
63
|
+
raise AWS::Error, error_message
|
64
|
+
end
|
65
|
+
|
66
|
+
end
|
67
|
+
|
29
68
|
end
|
30
69
|
|
31
70
|
end
|
@@ -0,0 +1,336 @@
|
|
1
|
+
require File.dirname(__FILE__) + '/test_helper.rb'
|
2
|
+
|
3
|
+
context "autoscaling " do
|
4
|
+
before do
|
5
|
+
@as = AWS::Autoscaling::Base.new( :access_key_id => "not a key", :secret_access_key => "not a secret" )
|
6
|
+
|
7
|
+
@valid_create_launch_configuration_params = {
|
8
|
+
:image_id => "ami-ed46a784",
|
9
|
+
:launch_configuration_name => "TestAutoscalingGroupName",
|
10
|
+
:instance_type => "m1.small"
|
11
|
+
}
|
12
|
+
|
13
|
+
@error_response_for_creation = <<-RESPONSE
|
14
|
+
<ErrorResponse xmlns="http://autoscaling.amazonaws.com/doc/2009-05-15/">
|
15
|
+
<Error>
|
16
|
+
<Type>Sender</Type>
|
17
|
+
<Code>AlreadyExists</Code>
|
18
|
+
<Message>Launch Configuration by this name already exists - A launch configuration already exists with the name TestAutoscalingGroupName</Message>
|
19
|
+
</Error>
|
20
|
+
<RequestId>31d00b03-ad6a-11de-a47f-a5c562feca13</RequestId>
|
21
|
+
</ErrorResponse>
|
22
|
+
RESPONSE
|
23
|
+
|
24
|
+
@delete_launch_configuration_response = <<-RESPONSE
|
25
|
+
<DeleteLaunchConfigurationResponse xmlns="http://autoscaling.amazonaws.com/doc/2009-05-15/">
|
26
|
+
<ResponseMetadata>
|
27
|
+
<RequestId>5f5717d3-ad6c-11de-b1c0-1b00aadc5f72</RequestId>
|
28
|
+
</ResponseMetadata>
|
29
|
+
</DeleteLaunchConfigurationResponse>
|
30
|
+
RESPONSE
|
31
|
+
|
32
|
+
@create_launch_configuration_response = "<CreateLaunchConfigurationResponse xmlns=\"http://autoscaling.amazonaws.com/doc/2009-05-15/\">\n <ResponseMetadata>\n <RequestId>6062466f-ad6c-11de-b82f-996c936914c5</RequestId>\n </ResponseMetadata>\n</CreateLaunchConfigurationResponse>\n"
|
33
|
+
|
34
|
+
@create_autoscaling_group_response = "<CreateAutoScalingGroupResponse xmlns=\"http://autoscaling.amazonaws.com/doc/2009-05-15/\">\n <ResponseMetadata>\n <RequestId>cc4f9960-ad6e-11de-b82f-996c936914c5</RequestId>\n </ResponseMetadata>\n</CreateAutoScalingGroupResponse>\n"
|
35
|
+
|
36
|
+
@delete_autoscaling_group = "<DeleteAutoScalingGroupResponse xmlns=\"http://autoscaling.amazonaws.com/doc/2009-05-15/\">
|
37
|
+
<ResponseMetadata><RequestId>e64fc4c3-e10b-11dd-a73e-2d774d6aee71</RequestId></ResponseMetadata></DeleteAutoScalingGroupResponse>"
|
38
|
+
@create_or_update_trigger_response = "<CreateOrUpdateScalingTriggerResponse xmlns=\"http://autoscaling.amazonaws.com/doc/2009-05-15/\">
|
39
|
+
<ResponseMetadata>
|
40
|
+
<RequestId>503d309d-e10b-11dd-a73e-2d774d6aee71</RequestId>
|
41
|
+
</ResponseMetadata>
|
42
|
+
</CreateOrUpdateScalingTriggerResponse>"
|
43
|
+
end
|
44
|
+
|
45
|
+
specify "AWS::Autoscaling::Base should give back a nice response if there is an error" do
|
46
|
+
@as.stubs(:make_request).with('CreateLaunchConfiguration', {
|
47
|
+
'ImageId' => 'ami-ed46a784',
|
48
|
+
'LaunchConfigurationName' => 'TestAutoscalingGroupName',
|
49
|
+
'InstanceType' => "m1.small"
|
50
|
+
}).returns stub(:body => @error_response_for_creation, :is_a? => true)
|
51
|
+
|
52
|
+
response = @as.create_launch_configuration( :image_id => "ami-ed46a784", :launch_configuration_name => "TestAutoscalingGroupName", :instance_type => "m1.small")
|
53
|
+
response.should.be.an.instance_of Hash
|
54
|
+
response["Error"]["Message"].should.equal "Launch Configuration by this name already exists - A launch configuration already exists with the name TestAutoscalingGroupName"
|
55
|
+
end
|
56
|
+
|
57
|
+
specify "AWS::Autoscaling::Base should destroy a launch configuration just fine" do
|
58
|
+
@as.stubs(:make_request).with('DeleteLaunchConfiguration', {
|
59
|
+
'LaunchConfigurationName' => 'TestAutoscalingGroupName1'
|
60
|
+
}).returns stub(:body => @delete_launch_configuration_response, :is_a? => true)
|
61
|
+
|
62
|
+
response = @as.delete_launch_configuration( :launch_configuration_name => "TestAutoscalingGroupName1" )
|
63
|
+
response.should.be.an.instance_of Hash
|
64
|
+
end
|
65
|
+
|
66
|
+
specify "AWS::Autoscaling::Base should create a launch configuration" do
|
67
|
+
@as.stubs(:make_request).with('CreateLaunchConfiguration', {
|
68
|
+
'ImageId' => 'ami-ed46a784',
|
69
|
+
'LaunchConfigurationName' => 'CustomTestAutoscalingGroupName',
|
70
|
+
'InstanceType' => "m1.small"
|
71
|
+
}).returns stub(:body => @create_launch_configuration_response, :is_a? => true)
|
72
|
+
|
73
|
+
response = @as.create_launch_configuration( :image_id => "ami-ed46a784", :launch_configuration_name => "CustomTestAutoscalingGroupName", :instance_type => "m1.small")
|
74
|
+
response.should.be.an.instance_of Hash
|
75
|
+
end
|
76
|
+
|
77
|
+
specify "AWS::Autoscaling::Base should be able to create a new autoscaling group" do
|
78
|
+
@as.stubs(:make_request).with("CreateAutoScalingGroup", {
|
79
|
+
'AutoScalingGroupName' => 'CloudteamTestAutoscalingGroup1',
|
80
|
+
'AvailabilityZones.member.1' => 'us-east-1a',
|
81
|
+
'LoadBalancerNames' => 'TestLoadBalancerName',
|
82
|
+
'LaunchConfigurationName' => 'CloudteamTestAutoscaling',
|
83
|
+
'MinSize' => "1", 'MaxSize' => "3"
|
84
|
+
}).returns stub(:body => @create_autoscaling_group_response, :is_a? => true)
|
85
|
+
response = @as.create_autoscaling_group(:autoscaling_group_name => "CloudteamTestAutoscalingGroup1", :availability_zones => "us-east-1a", :load_balancer_names => "TestLoadBalancerName", :launch_configuration_name => "CloudteamTestAutoscaling", :min_size => 1, :max_size => 3)
|
86
|
+
response.should.be.an.instance_of Hash
|
87
|
+
end
|
88
|
+
|
89
|
+
specify "AWS::Autoscaling::Base should destroy an autoscaling group" do
|
90
|
+
@as.stubs(:make_request).with('DeleteAutoScalingGroup', {
|
91
|
+
'AutoScalingGroupName' => 'TestAutoscalingGroupName1'
|
92
|
+
}).returns stub(:body => @delete_autoscaling_group, :is_a? => true)
|
93
|
+
|
94
|
+
response = @as.delete_autoscaling_group( :autoscaling_group_name => "TestAutoscalingGroupName1" )
|
95
|
+
response.should.be.an.instance_of Hash
|
96
|
+
end
|
97
|
+
|
98
|
+
specify "AWS::Autoscaling::Base should be able to create a new scaling trigger" do
|
99
|
+
@as.stubs(:make_request).with("CreateOrUpdateScalingTrigger", {
|
100
|
+
'AutoScalingGroupName' => 'AutoScalingGroupName',
|
101
|
+
'Unit' => "Seconds",
|
102
|
+
'Dimensions.member.1.Name' => "AutoScalingGroupName",
|
103
|
+
'Dimensions.member.1.Value' => "Bob",
|
104
|
+
'MeasureName' => "CPUUtilization",
|
105
|
+
'Statistic' => 'Average',
|
106
|
+
'Period' => '120',
|
107
|
+
'TriggerName' => "AFunNameForATrigger",
|
108
|
+
'LowerThreshold' => "0.2",
|
109
|
+
'LowerBreachScaleIncrement' => "-1",
|
110
|
+
'UpperThreshold' => "1.5",
|
111
|
+
'UpperBreachScaleIncrement' => "1",
|
112
|
+
'BreachDuration' => "120"
|
113
|
+
}).returns stub(:body => @create_or_update_trigger_response, :is_a? => true)
|
114
|
+
|
115
|
+
valid_create_or_update_scaling_trigger_params = {:autoscaling_group_name => "AutoScalingGroupName", :dimensions => {:name => "AutoScalingGroupName", :value => "Bob"}, :unit => "Seconds", :measure_name => "CPUUtilization", :statistic => "Average", :period => 120, :trigger_name => "AFunNameForATrigger", :lower_threshold => 0.2, :lower_breach_scale_increment => "-1", :upper_threshold => 1.5, :upper_breach_scale_increment => 1, :breach_duration => 120}
|
116
|
+
|
117
|
+
%w(dimensions autoscaling_group_name measure_name statistic period trigger_name lower_threshold lower_breach_scale_increment upper_threshold upper_breach_scale_increment breach_duration).each do |meth_str|
|
118
|
+
lambda { @as.create_or_updated_scaling_trigger(valid_create_or_update_scaling_trigger_params.merge(meth_str.to_sym=>nil)) }.should.raise(AWS::ArgumentError)
|
119
|
+
end
|
120
|
+
|
121
|
+
response = @as.create_or_updated_scaling_trigger(valid_create_or_update_scaling_trigger_params)
|
122
|
+
response.should.be.an.instance_of Hash
|
123
|
+
end
|
124
|
+
|
125
|
+
specify "AWS::Autoscaling::Base should destroy a launch configuration group" do
|
126
|
+
@as.stubs(:make_request).with('DeleteLaunchConfiguration', {
|
127
|
+
'LaunchConfigurationName' => 'LaunchConfiguration'
|
128
|
+
}).returns stub(:body => " <DeleteLaunchConfigurationResponse xmlns=\"http://autoscaling.amazonaws.com/doc/2009-05-15/\">
|
129
|
+
<ResponseMetadata><RequestId>e64fc4c3-e10b-11dd-a73e-2d774d6aee71</RequestId></ResponseMetadata>
|
130
|
+
</DeleteLaunchConfigurationResponse>", :is_a? => true)
|
131
|
+
|
132
|
+
response = @as.delete_launch_configuration( :launch_configuration_name => "LaunchConfiguration" )
|
133
|
+
response.should.be.an.instance_of Hash
|
134
|
+
end
|
135
|
+
|
136
|
+
specify "AWS::Autoscaling::Base should destroy a scaling trigger" do
|
137
|
+
@as.stubs(:make_request).with('DeleteTrigger', {
|
138
|
+
'TriggerName' => 'DeletingTrigger', 'AutoScalingGroupName' => "Name"
|
139
|
+
}).returns stub(:body => " <DeleteTriggerResponse xmlns=\"http://autoscaling.amazonaws.com/doc/2009-05-15/\">
|
140
|
+
<ResponseMetadata>
|
141
|
+
<RequestId>cca38097-e10b-11dd-a73e-2d774d6aee71</RequestId>
|
142
|
+
</ResponseMetadata>
|
143
|
+
</DeleteTriggerResponse>", :is_a? => true)
|
144
|
+
|
145
|
+
response = @as.delete_trigger( :trigger_name => "DeletingTrigger", :autoscaling_group_name => "Name" )
|
146
|
+
response.should.be.an.instance_of Hash
|
147
|
+
end
|
148
|
+
|
149
|
+
specify "AWS::Autoscaling::Base should describe the autoscaling groups" do
|
150
|
+
@as.stubs(:make_request).with('DescribeAutoScalingGroups', {
|
151
|
+
'AutoScalingGroupNames.member.1' => "webtier"
|
152
|
+
}).returns stub(:body => "<DescribeAutoScalingGroupsResponse xmlns=\"http://autoscaling.amazonaws.com/doc/2009-05-15/\">
|
153
|
+
<DescribeAutoScalingGroupsResult>
|
154
|
+
<AutoScalingGroups>
|
155
|
+
<member>
|
156
|
+
<MinSize>0</MinSize>
|
157
|
+
<CreatedTime>2009-01-13T00:38:54Z</CreatedTime>
|
158
|
+
<AvailabilityZones>
|
159
|
+
<member>us-east-1c</member>
|
160
|
+
</AvailabilityZones>
|
161
|
+
<Cooldown>0</Cooldown>
|
162
|
+
<LaunchConfigurationName>wt20080929</LaunchConfigurationName>
|
163
|
+
<AutoScalingGroupName>webtier</AutoScalingGroupName>
|
164
|
+
<DesiredCapacity>1</DesiredCapacity>
|
165
|
+
<Instances>
|
166
|
+
<member>
|
167
|
+
<InstanceId>i-8fa224e6</InstanceId>
|
168
|
+
<LifecycleState>InService</LifecycleState>
|
169
|
+
<AvailabilityZone>us-east-1c</AvailabilityZone>
|
170
|
+
</member>
|
171
|
+
</Instances>
|
172
|
+
<MaxSize>1</MaxSize>
|
173
|
+
</member>
|
174
|
+
</AutoScalingGroups>
|
175
|
+
</DescribeAutoScalingGroupsResult>
|
176
|
+
<ResponseMetadata>
|
177
|
+
<RequestId>70f2e8af-e10b-11dd-a73e-2d774d6aee71</RequestId>
|
178
|
+
</ResponseMetadata>
|
179
|
+
</DescribeAutoScalingGroupsResponse>", :is_a? => true)
|
180
|
+
|
181
|
+
response = @as.describe_autoscaling_groups( :autoscaling_group_names => ["webtier"] )
|
182
|
+
response.should.be.an.instance_of Hash
|
183
|
+
end
|
184
|
+
|
185
|
+
specify "AWS::Autoscaling::Base should describe the launch configurations" do
|
186
|
+
@as.stubs(:make_request).with('DescribeLaunchConfigurations', {
|
187
|
+
'AutoScalingGroupNames.member.1' => "webtier"
|
188
|
+
}).returns stub(:body => "<DescribeLaunchConfigurationsResponse xmlns=\"http://autoscaling.amazonaws.com/doc/2009-05-15/\">
|
189
|
+
<DescribeLaunchConfigurationsResult>
|
190
|
+
<LaunchConfigurations>
|
191
|
+
<member>
|
192
|
+
<InstanceType>m1.small</InstanceType>
|
193
|
+
<BlockDeviceMappings/>
|
194
|
+
<KeyName/>
|
195
|
+
<SecurityGroups/>
|
196
|
+
<ImageId>ami-f7c5219e</ImageId>
|
197
|
+
<RamdiskId/>
|
198
|
+
<CreatedTime>2009-01-13T00:35:31Z</CreatedTime>
|
199
|
+
<KernelId/>
|
200
|
+
<LaunchConfigurationName>wt20080929</LaunchConfigurationName>
|
201
|
+
<UserData/>
|
202
|
+
</member>
|
203
|
+
</LaunchConfigurations>
|
204
|
+
</DescribeLaunchConfigurationsResult>
|
205
|
+
<ResponseMetadata>
|
206
|
+
<RequestId>2e14cb6c-e10a-11dd-a73e-2d774d6aee71</RequestId>
|
207
|
+
</ResponseMetadata>
|
208
|
+
</DescribeLaunchConfigurationsResponse>", :is_a? => true)
|
209
|
+
|
210
|
+
response = @as.describe_launch_configurations( :launch_configuration_names => ["webtier"] )
|
211
|
+
response.should.be.an.instance_of Hash
|
212
|
+
end
|
213
|
+
|
214
|
+
|
215
|
+
specify "AWS::Autoscaling::Base should describe the launch configurations" do
|
216
|
+
@as.stubs(:make_request).with('DescribeScalingActivities', {
|
217
|
+
'AutoScalingGroupName' => "webtier"
|
218
|
+
}).returns stub(:body => "<DescribeScalingActivitiesResponse xmlns=\"http://autoscaling.amazonaws.com/doc/2009-05-15/\">
|
219
|
+
<DescribeScalingActivitiesResult>
|
220
|
+
<Activities>
|
221
|
+
<member>
|
222
|
+
<ActivityId>885b2900-0f2e-497a-8ec6-b1b90a9ddee0</ActivityId>
|
223
|
+
<StartTime>2009-03-29T04:07:07Z</StartTime>
|
224
|
+
<Progress>0</Progress>
|
225
|
+
<StatusCode>InProgress</StatusCode>
|
226
|
+
<Cause>Automated Capacity Adjustment</Cause>
|
227
|
+
<Description>Launching a new EC2 instance</Description>
|
228
|
+
</member>
|
229
|
+
</Activities>
|
230
|
+
</DescribeScalingActivitiesResult>
|
231
|
+
<ResponseMetadata>
|
232
|
+
<RequestId>f0321780-e10a-11dd-a73e-2d774d6aee71</RequestId>
|
233
|
+
</ResponseMetadata>
|
234
|
+
</DescribeScalingActivitiesResponse>", :is_a? => true)
|
235
|
+
|
236
|
+
response = @as.describe_scaling_activities( :autoscaling_group_name => "webtier" )
|
237
|
+
response.should.be.an.instance_of Hash
|
238
|
+
end
|
239
|
+
|
240
|
+
specify "AWS::Autoscaling::Base should describe triggers" do
|
241
|
+
@as.stubs(:make_request).with('DescribeTriggers', {
|
242
|
+
'AutoScalingGroupName' => "webtier"
|
243
|
+
}).returns stub(:body => " <DescribeTriggersResponse xmlns=\"http://autoscaling.amazonaws.com/doc/2009-05-15/\">
|
244
|
+
<DescribeTriggersResult>
|
245
|
+
<Triggers>
|
246
|
+
<member>
|
247
|
+
<BreachDuration>300</BreachDuration>
|
248
|
+
<UpperBreachScaleIncrement>1</UpperBreachScaleIncrement>
|
249
|
+
<CreatedTime>2009-01-13T00:44:19Z</CreatedTime>
|
250
|
+
<UpperThreshold>60.0</UpperThreshold>
|
251
|
+
<Status>NoData</Status>
|
252
|
+
<LowerThreshold>0.0</LowerThreshold>
|
253
|
+
<Period>60</Period>
|
254
|
+
<LowerBreachScaleIncrement>-1</LowerBreachScaleIncrement>
|
255
|
+
<TriggerName>tenpct</TriggerName>
|
256
|
+
<Statistic>Average</Statistic>
|
257
|
+
<Unit>None</Unit>
|
258
|
+
<Namespace>AWS/EC2</Namespace>
|
259
|
+
<Dimensions>
|
260
|
+
|
261
|
+
<member>
|
262
|
+
<Name>AutoScalingGroupName</Name>
|
263
|
+
<Value>webtier</Value>
|
264
|
+
</member>
|
265
|
+
</Dimensions>
|
266
|
+
<AutoScalingGroupName>webtier</AutoScalingGroupName>
|
267
|
+
<MeasureName>CPUUtilization</MeasureName>
|
268
|
+
</member>
|
269
|
+
</Triggers>
|
270
|
+
</DescribeTriggersResult>
|
271
|
+
<ResponseMetadata>
|
272
|
+
<RequestId>5c33c82a-e10b-11dd-a73e-2d774d6aee71</RequestId>
|
273
|
+
</ResponseMetadata>
|
274
|
+
</DescribeTriggersResponse>", :is_a? => true)
|
275
|
+
|
276
|
+
response = @as.describe_triggers( :autoscaling_group_name => "webtier" )
|
277
|
+
response.should.be.an.instance_of Hash
|
278
|
+
end
|
279
|
+
|
280
|
+
specify "AWS::Autoscaling::Base should describe triggers" do
|
281
|
+
@as.stubs(:make_request).with('SetDesiredCapacity', {
|
282
|
+
'AutoScalingGroupName' => "name", 'DesiredCapacity' => '10'
|
283
|
+
}).returns stub(:body => " <SetDesiredCapacityResponse xmlns=\"http://autoscaling.amazonaws.com/doc/2009-05-15/\">
|
284
|
+
<ResponseMetadata>
|
285
|
+
<RequestId>d3f2091c-e10a-11dd-a73e- 2d774d6aee71</RequestId>
|
286
|
+
</ResponseMetadata>
|
287
|
+
</SetDesiredCapacityResponse>
|
288
|
+
", :is_a? => true)
|
289
|
+
|
290
|
+
response = @as.set_desired_capacity( :autoscaling_group_name => "name", :desired_capacity => "10" )
|
291
|
+
response.should.be.an.instance_of Hash
|
292
|
+
end
|
293
|
+
|
294
|
+
|
295
|
+
specify "AWS::Autoscaling::Base should terminate an instance in an autoscaling group" do
|
296
|
+
@as.stubs(:make_request).with('TerminateInstanceInAutoScalingGroup', {
|
297
|
+
'InstanceId' => "i-instance1"
|
298
|
+
}).returns stub(:body => " <DescribeTriggersResponse xmlns=\"http://autoscaling.amazonaws.com/doc/2009-05-15/\">
|
299
|
+
<DescribeTriggersResult>
|
300
|
+
<Triggers>
|
301
|
+
<member>
|
302
|
+
<BreachDuration>300</BreachDuration>
|
303
|
+
<UpperBreachScaleIncrement>1</UpperBreachScaleIncrement>
|
304
|
+
<CreatedTime>2009-01-13T00:44:19Z</CreatedTime>
|
305
|
+
<UpperThreshold>60.0</UpperThreshold>
|
306
|
+
<Status>NoData</Status>
|
307
|
+
<LowerThreshold>0.0</LowerThreshold>
|
308
|
+
<Period>60</Period>
|
309
|
+
<LowerBreachScaleIncrement>-1</LowerBreachScaleIncrement>
|
310
|
+
<TriggerName>tenpct</TriggerName>
|
311
|
+
<Statistic>Average</Statistic>
|
312
|
+
<Unit>None</Unit>
|
313
|
+
<Namespace>AWS/EC2</Namespace>
|
314
|
+
<Dimensions>
|
315
|
+
|
316
|
+
<member>
|
317
|
+
<Name>AutoScalingGroupName</Name>
|
318
|
+
<Value>webtier</Value>
|
319
|
+
</member>
|
320
|
+
</Dimensions>
|
321
|
+
<AutoScalingGroupName>webtier</AutoScalingGroupName>
|
322
|
+
<MeasureName>CPUUtilization</MeasureName>
|
323
|
+
</member>
|
324
|
+
</Triggers>
|
325
|
+
</DescribeTriggersResult>
|
326
|
+
<ResponseMetadata>
|
327
|
+
<RequestId>5c33c82a-e10b-11dd-a73e-2d774d6aee71</RequestId>
|
328
|
+
</ResponseMetadata>
|
329
|
+
</DescribeTriggersResponse>", :is_a? => true)
|
330
|
+
|
331
|
+
response = @as.terminate_instance_in_autoscaling_group( :instance_id => "i-instance1" )
|
332
|
+
response.should.be.an.instance_of Hash
|
333
|
+
end
|
334
|
+
|
335
|
+
|
336
|
+
end
|
data/test/test_EC2_instances.rb
CHANGED
@@ -139,6 +139,47 @@ context "EC2 instances " do
|
|
139
139
|
</instancesSet>
|
140
140
|
</TerminateInstancesResponse>
|
141
141
|
RESPONSE
|
142
|
+
|
143
|
+
@monitor_instances_response_body = <<-RESPONSE
|
144
|
+
<MonitorInstancesResponse xmlns="http://ec2.amazonaws.com/doc/2009-07-15/">
|
145
|
+
<requestId>fe62a64c-49fb-4a3c-8d9b-61aba146d390</requestId>
|
146
|
+
<instancesSet>
|
147
|
+
<item>
|
148
|
+
<instanceId>i-138fc47a</instanceId>
|
149
|
+
<monitoring>
|
150
|
+
<state>pending</state>
|
151
|
+
</monitoring>
|
152
|
+
</item>
|
153
|
+
<item>
|
154
|
+
<instanceId>i-33457a5a</instanceId>
|
155
|
+
<monitoring>
|
156
|
+
<state>pending</state>
|
157
|
+
</monitoring>
|
158
|
+
</item>
|
159
|
+
</instancesSet>
|
160
|
+
</MonitorInstancesResponse>
|
161
|
+
RESPONSE
|
162
|
+
|
163
|
+
@unmonitor_instances_response_body = <<-RESPONSE
|
164
|
+
<UnmonitorInstancesResponse xmlns="http://ec2.amazonaws.com/doc/2009-07-15/">
|
165
|
+
<requestId>7dbc5095-f3ae-46d8-a5b1-19df118ceb05</requestId>
|
166
|
+
<instancesSet>
|
167
|
+
<item>
|
168
|
+
<instanceId>i-138fc47a</instanceId>
|
169
|
+
<monitoring>
|
170
|
+
<state>disabling</state>
|
171
|
+
</monitoring>
|
172
|
+
</item>
|
173
|
+
<item>
|
174
|
+
<instanceId>i-33457a5a</instanceId>
|
175
|
+
<monitoring>
|
176
|
+
<state>disabling</state>
|
177
|
+
</monitoring>
|
178
|
+
</item>
|
179
|
+
</instancesSet>
|
180
|
+
</UnmonitorInstancesResponse>
|
181
|
+
|
182
|
+
RESPONSE
|
142
183
|
end
|
143
184
|
|
144
185
|
|
@@ -345,4 +386,44 @@ context "EC2 instances " do
|
|
345
386
|
@response.instancesSet.item[1].previousState.name.should.equal "pending"
|
346
387
|
end
|
347
388
|
|
389
|
+
specify "method monitor_instances should raise an exception when called without nil/empty string arguments" do
|
390
|
+
lambda { @ec2.monitor_instances() }.should.raise(AWS::ArgumentError)
|
391
|
+
lambda { @ec2.monitor_instances( :instance_id => nil ) }.should.raise(AWS::ArgumentError)
|
392
|
+
lambda { @ec2.monitor_instances( :instance_id => "" ) }.should.raise(AWS::ArgumentError)
|
393
|
+
end
|
394
|
+
|
395
|
+
specify "should be able to be monitored when provided with an :instance_id" do
|
396
|
+
@ec2.stubs(:make_request).with('MonitorInstances', {"InstanceId.1"=>"i-138fc47a", "InstanceId.2"=>"i-33457a5a"}).
|
397
|
+
returns stub(:body => @monitor_instances_response_body, :is_a? => true)
|
398
|
+
@ec2.monitor_instances( :instance_id => ["i-138fc47a", "i-33457a5a"] ).class.should.equal Hash
|
399
|
+
|
400
|
+
@response = @ec2.monitor_instances( :instance_id => ["i-138fc47a", "i-33457a5a"] )
|
401
|
+
|
402
|
+
@response.instancesSet.item[0].instanceId.should.equal "i-138fc47a"
|
403
|
+
@response.instancesSet.item[0].monitoring.state.should.equal "pending"
|
404
|
+
|
405
|
+
@response.instancesSet.item[1].instanceId.should.equal "i-33457a5a"
|
406
|
+
@response.instancesSet.item[1].monitoring.state.should.equal "pending"
|
407
|
+
end
|
408
|
+
|
409
|
+
specify "method unmonitor_instances should raise an exception when called without nil/empty string arguments" do
|
410
|
+
lambda { @ec2.unmonitor_instances() }.should.raise(AWS::ArgumentError)
|
411
|
+
lambda { @ec2.unmonitor_instances( :instance_id => nil ) }.should.raise(AWS::ArgumentError)
|
412
|
+
lambda { @ec2.unmonitor_instances( :instance_id => "" ) }.should.raise(AWS::ArgumentError)
|
413
|
+
end
|
414
|
+
|
415
|
+
specify "should be able to be unmonitored when provided with an :instance_id" do
|
416
|
+
@ec2.stubs(:make_request).with('UnmonitorInstances', {"InstanceId.1"=>"i-138fc47a", "InstanceId.2"=>"i-33457a5a"}).
|
417
|
+
returns stub(:body => @unmonitor_instances_response_body, :is_a? => true)
|
418
|
+
@ec2.unmonitor_instances( :instance_id => ["i-138fc47a", "i-33457a5a"] ).class.should.equal Hash
|
419
|
+
|
420
|
+
@response = @ec2.unmonitor_instances( :instance_id => ["i-138fc47a", "i-33457a5a"] )
|
421
|
+
|
422
|
+
@response.instancesSet.item[0].instanceId.should.equal "i-138fc47a"
|
423
|
+
@response.instancesSet.item[0].monitoring.state.should.equal "disabling"
|
424
|
+
|
425
|
+
@response.instancesSet.item[1].instanceId.should.equal "i-33457a5a"
|
426
|
+
@response.instancesSet.item[1].monitoring.state.should.equal "disabling"
|
427
|
+
end
|
428
|
+
|
348
429
|
end
|
metadata
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: amazon-ec2
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 0.
|
4
|
+
version: 0.6.0
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Glenn Rempe
|
@@ -9,7 +9,7 @@ autorequire:
|
|
9
9
|
bindir: bin
|
10
10
|
cert_chain: []
|
11
11
|
|
12
|
-
date: 2009-
|
12
|
+
date: 2009-09-30 00:00:00 -07:00
|
13
13
|
default_executable:
|
14
14
|
dependencies:
|
15
15
|
- !ruby/object:Gem::Dependency
|
@@ -62,7 +62,7 @@ dependencies:
|
|
62
62
|
- !ruby/object:Gem::Version
|
63
63
|
version: 0.1.6
|
64
64
|
version:
|
65
|
-
description: A Ruby library for accessing the Amazon Web Services Elastic Compute Cloud (EC2)
|
65
|
+
description: A Ruby library for accessing the Amazon Web Services Elastic Compute Cloud (EC2), Elastic Load Balancer (ELB), Cloudwatch, and Autoscaling API's.
|
66
66
|
email: glenn@rempe.us
|
67
67
|
executables:
|
68
68
|
- ec2-gem-example.rb
|
@@ -92,6 +92,10 @@ files:
|
|
92
92
|
- bin/setup.rb
|
93
93
|
- deps.rip
|
94
94
|
- lib/AWS.rb
|
95
|
+
- lib/AWS/Autoscaling.rb
|
96
|
+
- lib/AWS/Autoscaling/autoscaling.rb
|
97
|
+
- lib/AWS/Cloudwatch.rb
|
98
|
+
- lib/AWS/Cloudwatch/monitoring.rb
|
95
99
|
- lib/AWS/EC2.rb
|
96
100
|
- lib/AWS/EC2/availability_zones.rb
|
97
101
|
- lib/AWS/EC2/console.rb
|
@@ -112,6 +116,7 @@ files:
|
|
112
116
|
- perftools/ec2prof-results.dot
|
113
117
|
- perftools/ec2prof-results.txt
|
114
118
|
- perftools/ec2prof.symbols
|
119
|
+
- test/test_Autoscaling_groups.rb
|
115
120
|
- test/test_EC2.rb
|
116
121
|
- test/test_EC2_availability_zones.rb
|
117
122
|
- test/test_EC2_console.rb
|
@@ -165,6 +170,7 @@ signing_key:
|
|
165
170
|
specification_version: 3
|
166
171
|
summary: Amazon EC2 Ruby Gem
|
167
172
|
test_files:
|
173
|
+
- test/test_Autoscaling_groups.rb
|
168
174
|
- test/test_EC2.rb
|
169
175
|
- test/test_EC2_availability_zones.rb
|
170
176
|
- test/test_EC2_console.rb
|