steamcannon-aws 2.3.26.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -0,0 +1,129 @@
1
+ # If ActiveSupport is loaded, then great - use it. But we don't
2
+ # want a dependency on it, so if it's not present, define the few
3
+ # extensions that we want to use...
4
+ unless defined? ActiveSupport
5
+ # These are ActiveSupport-;like extensions to do a few handy things in the gems
6
+ # Derived from ActiveSupport, so the AS copyright notice applies:
7
+ #
8
+ #
9
+ #
10
+ # Copyright (c) 2005 David Heinemeier Hansson
11
+ #
12
+ # Permission is hereby granted, free of charge, to any person obtaining
13
+ # a copy of this software and associated documentation files (the
14
+ # "Software"), to deal in the Software without restriction, including
15
+ # without limitation the rights to use, copy, modify, merge, publish,
16
+ # distribute, sublicense, and/or sell copies of the Software, and to
17
+ # permit persons to whom the Software is furnished to do so, subject to
18
+ # the following conditions:
19
+ #
20
+ # The above copyright notice and this permission notice shall be
21
+ # included in all copies or substantial portions of the Software.
22
+ #
23
+ # THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
24
+ # EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
25
+ # MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
26
+ # NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
27
+ # LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
28
+ # OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
29
+ # WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
30
+ #++
31
+ #
32
+ #
33
+ class String #:nodoc:
34
+ # Ruby 1.9 introduces an inherit argument for Module#const_get and
35
+ # #const_defined? and changes their default behavior.
36
+ if Module.method(:const_get).arity == 1
37
+ # Constantize tries to find a declared constant with the name specified
38
+ # in the string. It raises a NameError when the name is not in CamelCase
39
+ # or is not initialized.
40
+ #
41
+ # Examples
42
+ # "Module".constantize #=> Module
43
+ # "Class".constantize #=> Class
44
+ def constantize()
45
+ camel_cased_word = self
46
+ names = camel_cased_word.split('::')
47
+ names.shift if names.empty? || names.first.empty?
48
+
49
+ constant = Object
50
+ names.each do |name|
51
+ constant = constant.const_defined?(name) ? constant.const_get(name) : constant.const_missing(name)
52
+ end
53
+ constant
54
+ end
55
+ else
56
+ def constantize() #:nodoc:
57
+ camel_cased_word = self
58
+ names = camel_cased_word.split('::')
59
+ names.shift if names.empty? || names.first.empty?
60
+
61
+ constant = Object
62
+ names.each do |name|
63
+ constant = constant.const_get(name, false) || constant.const_missing(name)
64
+ end
65
+ constant
66
+ end
67
+ end
68
+ end
69
+
70
+
71
+ class Object #:nodoc:
72
+ # "", " ", nil, [], and {} are blank
73
+ def blank?
74
+ if respond_to?(:empty?) && respond_to?(:strip)
75
+ empty? or strip.empty?
76
+ elsif respond_to?(:empty?)
77
+ empty?
78
+ else
79
+ !self
80
+ end
81
+ end
82
+ end
83
+
84
+ class NilClass #:nodoc:
85
+ def blank?
86
+ true
87
+ end
88
+ end
89
+
90
+ class FalseClass #:nodoc:
91
+ def blank?
92
+ true
93
+ end
94
+ end
95
+
96
+ class TrueClass #:nodoc:
97
+ def blank?
98
+ false
99
+ end
100
+ end
101
+
102
+ class Array #:nodoc:
103
+ alias_method :blank?, :empty?
104
+ end
105
+
106
+ class Hash #:nodoc:
107
+ alias_method :blank?, :empty?
108
+
109
+ # Return a new hash with all keys converted to symbols.
110
+ def symbolize_keys
111
+ inject({}) do |options, (key, value)|
112
+ options[key.to_sym] = value
113
+ options
114
+ end
115
+ end
116
+ end
117
+
118
+ class String #:nodoc:
119
+ def blank?
120
+ empty? || strip.empty?
121
+ end
122
+ end
123
+
124
+ class Numeric #:nodoc:
125
+ def blank?
126
+ false
127
+ end
128
+ end
129
+ end
@@ -0,0 +1,1879 @@
1
+ #
2
+ # Copyright (c) 2007-2008 RightScale Inc
3
+ #
4
+ # Permission is hereby granted, free of charge, to any person obtaining
5
+ # a copy of this software and associated documentation files (the
6
+ # "Software"), to deal in the Software without restriction, including
7
+ # without limitation the rights to use, copy, modify, merge, publish,
8
+ # distribute, sublicense, and/or sell copies of the Software, and to
9
+ # permit persons to whom the Software is furnished to do so, subject to
10
+ # the following conditions:
11
+ #
12
+ # The above copyright notice and this permission notice shall be
13
+ # included in all copies or substantial portions of the Software.
14
+ #
15
+ # THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
16
+ # EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
17
+ # MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
18
+ # NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
19
+ # LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
20
+ # OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
21
+ # WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
22
+ #
23
+
24
+ module Aws
25
+
26
+ # = Aws::EC2 -- RightScale Amazon EC2 interface
27
+ # The Aws::EC2 class provides a complete interface to Amazon's
28
+ # Elastic Compute Cloud service, as well as the associated EBS (Elastic Block
29
+ # Store).
30
+ # For explanations of the semantics
31
+ # of each call, please refer to Amazon's documentation at
32
+ # http://developer.amazonwebservices.com/connect/kbcategory.jspa?categoryID=87
33
+ #
34
+ # Examples:
35
+ #
36
+ # Create an EC2 interface handle:
37
+ #
38
+ # @ec2 = Aws::Ec2.new(aws_access_key_id,
39
+ # aws_secret_access_key)
40
+ # Create a new SSH key pair:
41
+ # @key = 'right_ec2_awesome_test_key'
42
+ # new_key = @ec2.create_key_pair(@key)
43
+ # keys = @ec2.describe_key_pairs
44
+ #
45
+ # Create a security group:
46
+ # @group = 'right_ec2_awesome_test_security_group'
47
+ # @ec2.create_security_group(@group,'My awesome test group')
48
+ # group = @ec2.describe_security_groups([@group])[0]
49
+ #
50
+ # Configure a security group:
51
+ # @ec2.authorize_security_group_named_ingress(@group, account_number, 'default')
52
+ # @ec2.authorize_security_group_IP_ingress(@group, 80,80,'udp','192.168.1.0/8')
53
+ #
54
+ # Describe the available images:
55
+ # images = @ec2.describe_images
56
+ #
57
+ # Launch an instance:
58
+ # ec2.run_instances('ami-9a9e7bf3', 1, 1, ['default'], @key, 'SomeImportantUserData', 'public')
59
+ #
60
+ #
61
+ # Describe running instances:
62
+ # @ec2.describe_instances
63
+ #
64
+ # Error handling: all operations raise an Aws::AwsError in case
65
+ # of problems. Note that transient errors are automatically retried.
66
+
67
+ class Ec2 < AwsBase
68
+ include AwsBaseInterface
69
+
70
+ # Amazon EC2 API version being used
71
+ API_VERSION = "2010-08-31"
72
+ DEFAULT_HOST = "ec2.amazonaws.com"
73
+ DEFAULT_PATH = '/'
74
+ DEFAULT_PROTOCOL = 'https'
75
+ DEFAULT_PORT = 443
76
+
77
+ # Default addressing type (public=NAT, direct=no-NAT) used when launching instances.
78
+ DEFAULT_ADDRESSING_TYPE = 'public'
79
+ DNS_ADDRESSING_SET = ['public','direct']
80
+
81
+ # Amazon EC2 Instance Types : http://www.amazon.com/b?ie=UTF8&node=370375011
82
+ # Default EC2 instance type (platform)
83
+ DEFAULT_INSTANCE_TYPE = 'm1.small'
84
+ INSTANCE_TYPES = ['t1.micro', 'm1.small','c1.medium','m1.large','m1.xlarge','c1.xlarge']
85
+
86
+ @@bench = AwsBenchmarkingBlock.new
87
+ def self.bench_xml
88
+ @@bench.xml
89
+ end
90
+ def self.bench_ec2
91
+ @@bench.service
92
+ end
93
+
94
+ # Current API version (sometimes we have to check it outside the GEM).
95
+ @@api = ENV['EC2_API_VERSION'] || API_VERSION
96
+ def self.api
97
+ @@api
98
+ end
99
+
100
+ # Create a new handle to an EC2 account. All handles share the same per process or per thread
101
+ # HTTP connection to Amazon EC2. Each handle is for a specific account. The params have the
102
+ # following options:
103
+ # * <tt>:endpoint_url</tt> a fully qualified url to Amazon API endpoint (this overwrites: :server, :port, :service, :protocol and :region). Example: 'https://eu-west-1.ec2.amazonaws.com/'
104
+ # * <tt>:server</tt>: EC2 service host, default: DEFAULT_HOST
105
+ # * <tt>:region</tt>: EC2 region (North America by default)
106
+ # * <tt>:port</tt>: EC2 service port, default: DEFAULT_PORT
107
+ # * <tt>:protocol</tt>: 'http' or 'https', default: DEFAULT_PROTOCOL
108
+ # * <tt>:multi_thread</tt>: true=HTTP connection per thread, false=per process
109
+ # * <tt>:logger</tt>: for log messages, default: Rails.logger else STDOUT
110
+ # * <tt>:signature_version</tt>: The signature version : '0' or '1'(default)
111
+ # * <tt>:cache</tt>: true/false: caching for: ec2_describe_images, describe_instances,
112
+ # describe_images_by_owner, describe_images_by_executable_by, describe_availability_zones,
113
+ # describe_security_groups, describe_key_pairs, describe_addresses,
114
+ # describe_volumes, describe_snapshots methods, default: false.
115
+ #
116
+ def initialize(aws_access_key_id=nil, aws_secret_access_key=nil, params={})
117
+ init({ :name => 'EC2',
118
+ :default_host => ENV['EC2_URL'] ? URI.parse(ENV['EC2_URL']).host : DEFAULT_HOST,
119
+ :default_port => ENV['EC2_URL'] ? URI.parse(ENV['EC2_URL']).port : DEFAULT_PORT,
120
+ :default_service => ENV['EC2_URL'] ? URI.parse(ENV['EC2_URL']).path : DEFAULT_PATH,
121
+ :default_protocol => ENV['EC2_URL'] ? URI.parse(ENV['EC2_URL']).scheme : DEFAULT_PROTOCOL,
122
+ :api_version => API_VERSION },
123
+ aws_access_key_id || ENV['AWS_ACCESS_KEY_ID'] ,
124
+ aws_secret_access_key|| ENV['AWS_SECRET_ACCESS_KEY'],
125
+ params)
126
+ # EC2 doesn't really define any transient errors to retry, and in fact,
127
+ # when they return a 503 it is usually for 'request limit exceeded' which
128
+ # we most certainly should not retry. So let's pare down the list of
129
+ # retryable errors to InternalError only (see AwsBase for the default
130
+ # list)
131
+ amazon_problems = ['InternalError']
132
+ end
133
+
134
+
135
+ def generate_request(action, params={}) #:nodoc:
136
+ service_hash = {"Action" => action,
137
+ "AWSAccessKeyId" => @aws_access_key_id,
138
+ "Version" => @@api }
139
+ service_hash.update(params)
140
+ service_params = signed_service_params(@aws_secret_access_key, service_hash, :get, @params[:server], @params[:service])
141
+
142
+ # use POST method if the length of the query string is too large
143
+ if service_params.size > 2000
144
+ if signature_version == '2'
145
+ # resign the request because HTTP verb is included into signature
146
+ service_params = signed_service_params(@aws_secret_access_key, service_hash, :post, @params[:server], @params[:service])
147
+ end
148
+ request = Net::HTTP::Post.new(@params[:service])
149
+ request.body = service_params
150
+ request['Content-Type'] = 'application/x-www-form-urlencoded'
151
+ else
152
+ request = Net::HTTP::Get.new("#{@params[:service]}?#{service_params}")
153
+ end
154
+ # prepare output hash
155
+ { :request => request,
156
+ :server => @params[:server],
157
+ :port => @params[:port],
158
+ :protocol => @params[:protocol] }
159
+ end
160
+
161
+ # Sends request to Amazon and parses the response
162
+ # Raises AwsError if any banana happened
163
+ def request_info(request, parser) #:nodoc:
164
+ thread = @params[:multi_thread] ? Thread.current : Thread.main
165
+ thread[:ec2_connection] ||= Rightscale::HttpConnection.new(:exception => AwsError, :logger => @logger)
166
+ request_info_impl(thread[:ec2_connection], @@bench, request, parser)
167
+ end
168
+
169
+ def hash_params(prefix, list) #:nodoc:
170
+ groups = {}
171
+ list.each_index{|i| groups.update("#{prefix}.#{i+1}"=>list[i])} if list
172
+ return groups
173
+ end
174
+
175
+ #-----------------------------------------------------------------
176
+ # Images
177
+ #-----------------------------------------------------------------
178
+
179
+ # params:
180
+ # { 'ImageId' => ['id1', ..., 'idN'],
181
+ # 'Owner' => ['self', ..., 'userN'],
182
+ # 'ExecutableBy' => ['self', 'all', ..., 'userN']
183
+ # }
184
+ def ec2_describe_images(params={}, image_type=nil, cache_for=nil) #:nodoc:
185
+ request_hash = {}
186
+ params.each do |list_by, list|
187
+ request_hash.merge! hash_params(list_by, list.to_a)
188
+ end
189
+ if image_type
190
+ request_hash['Filter.1.Name'] = "image-type"
191
+ request_hash['Filter.1.Value.1'] = image_type
192
+ end
193
+ link = generate_request("DescribeImages", request_hash)
194
+ request_cache_or_info cache_for, link, QEc2DescribeImagesParser, @@bench, cache_for
195
+ rescue Exception
196
+ on_exception
197
+ end
198
+
199
+ # Retrieve a list of images. Returns array of hashes describing the images or an exception:
200
+ # +image_type+ = 'machine' || 'kernel' || 'ramdisk'
201
+ #
202
+ # ec2.describe_images #=>
203
+ # [{:aws_owner => "522821470517",
204
+ # :aws_id => "ami-e4b6538d",
205
+ # :aws_state => "available",
206
+ # :aws_location => "marcins_cool_public_images/ubuntu-6.10.manifest.xml",
207
+ # :aws_is_public => true,
208
+ # :aws_architecture => "i386",
209
+ # :aws_image_type => "machine"},
210
+ # {...},
211
+ # {...} ]
212
+ #
213
+ # If +list+ param is set, then retrieve information about the listed images only:
214
+ #
215
+ # ec2.describe_images(['ami-e4b6538d']) #=>
216
+ # [{:aws_owner => "522821470517",
217
+ # :aws_id => "ami-e4b6538d",
218
+ # :aws_state => "available",
219
+ # :aws_location => "marcins_cool_public_images/ubuntu-6.10.manifest.xml",
220
+ # :aws_is_public => true,
221
+ # :aws_architecture => "i386",
222
+ # :aws_image_type => "machine"}]
223
+ #
224
+ def describe_images(list=[], image_type=nil)
225
+ list = list.to_a
226
+ cache_for = list.empty? && !image_type ? :describe_images : nil
227
+ ec2_describe_images({ 'ImageId' => list }, image_type, cache_for)
228
+ end
229
+
230
+ #
231
+ # Example:
232
+ #
233
+ # ec2.describe_images_by_owner('522821470517')
234
+ # ec2.describe_images_by_owner('self')
235
+ #
236
+ def describe_images_by_owner(list=['self'], image_type=nil)
237
+ list = list.to_a
238
+ cache_for = list==['self'] && !image_type ? :describe_images_by_owner : nil
239
+ ec2_describe_images({ 'Owner' => list }, image_type, cache_for)
240
+ end
241
+
242
+ #
243
+ # Example:
244
+ #
245
+ # ec2.describe_images_by_executable_by('522821470517')
246
+ # ec2.describe_images_by_executable_by('self')
247
+ # ec2.describe_images_by_executable_by('all')
248
+ #
249
+ def describe_images_by_executable_by(list=['self'], image_type=nil)
250
+ list = list.to_a
251
+ cache_for = list==['self'] && !image_type ? :describe_images_by_executable_by : nil
252
+ ec2_describe_images({ 'ExecutableBy' => list }, image_type, cache_for)
253
+ end
254
+
255
+
256
+ # Register new image at Amazon.
257
+ # Returns new image id or an exception.
258
+ #
259
+ # ec2.register_image('bucket/key/manifest') #=> 'ami-e444444d'
260
+ #
261
+ def register_image(image_location)
262
+ link = generate_request("RegisterImage",
263
+ 'ImageLocation' => image_location.to_s)
264
+ request_info(link, QEc2RegisterImageParser.new(:logger => @logger))
265
+ rescue Exception
266
+ on_exception
267
+ end
268
+
269
+ # Deregister image at Amazon. Returns +true+ or an exception.
270
+ #
271
+ # ec2.deregister_image('ami-e444444d') #=> true
272
+ #
273
+ def deregister_image(image_id)
274
+ link = generate_request("DeregisterImage",
275
+ 'ImageId' => image_id.to_s)
276
+ request_info(link, RightBoolResponseParser.new(:logger => @logger))
277
+ rescue Exception
278
+ on_exception
279
+ end
280
+
281
+
282
+ # Describe image attributes. Currently 'launchPermission', 'productCodes', 'kernel', 'ramdisk' and 'blockDeviceMapping' are supported.
283
+ #
284
+ # ec2.describe_image_attribute('ami-e444444d') #=> {:groups=>["all"], :users=>["000000000777"]}
285
+ #
286
+ def describe_image_attribute(image_id, attribute='launchPermission')
287
+ link = generate_request("DescribeImageAttribute",
288
+ 'ImageId' => image_id,
289
+ 'Attribute' => attribute)
290
+ request_info(link, QEc2DescribeImageAttributeParser.new(:logger => @logger))
291
+ rescue Exception
292
+ on_exception
293
+ end
294
+
295
+ # Reset image attribute. Currently, only 'launchPermission' is supported. Returns +true+ or an exception.
296
+ #
297
+ # ec2.reset_image_attribute('ami-e444444d') #=> true
298
+ #
299
+ def reset_image_attribute(image_id, attribute='launchPermission')
300
+ link = generate_request("ResetImageAttribute",
301
+ 'ImageId' => image_id,
302
+ 'Attribute' => attribute)
303
+ request_info(link, RightBoolResponseParser.new(:logger => @logger))
304
+ rescue Exception
305
+ on_exception
306
+ end
307
+
308
+ # Modify an image's attributes. It is recommended that you use
309
+ # modify_image_launch_perm_add_users, modify_image_launch_perm_remove_users, etc.
310
+ # instead of modify_image_attribute because the signature of
311
+ # modify_image_attribute may change with EC2 service changes.
312
+ #
313
+ # attribute : currently, only 'launchPermission' is supported.
314
+ # operation_type : currently, only 'add' & 'remove' are supported.
315
+ # vars:
316
+ # :user_group : currently, only 'all' is supported.
317
+ # :user_id
318
+ # :product_code
319
+ def modify_image_attribute(image_id, attribute, operation_type = nil, vars = {})
320
+ params = {'ImageId' => image_id,
321
+ 'Attribute' => attribute}
322
+ params['OperationType'] = operation_type if operation_type
323
+ params.update(hash_params('UserId', vars[:user_id].to_a)) if vars[:user_id]
324
+ params.update(hash_params('UserGroup', vars[:user_group].to_a)) if vars[:user_group]
325
+ params.update(hash_params('ProductCode', vars[:product_code])) if vars[:product_code]
326
+ link = generate_request("ModifyImageAttribute", params)
327
+ request_info(link, RightBoolResponseParser.new(:logger => @logger))
328
+ rescue Exception
329
+ on_exception
330
+ end
331
+
332
+ # Grant image launch permissions to users.
333
+ # Parameter +userId+ is a list of user AWS account ids.
334
+ # Returns +true+ or an exception.
335
+ #
336
+ # ec2.modify_image_launch_perm_add_users('ami-e444444d',['000000000777','000000000778']) #=> true
337
+ def modify_image_launch_perm_add_users(image_id, user_id=[])
338
+ modify_image_attribute(image_id, 'launchPermission', 'add', :user_id => user_id.to_a)
339
+ end
340
+
341
+ # Revokes image launch permissions for users. +userId+ is a list of users AWS accounts ids. Returns +true+ or an exception.
342
+ #
343
+ # ec2.modify_image_launch_perm_remove_users('ami-e444444d',['000000000777','000000000778']) #=> true
344
+ #
345
+ def modify_image_launch_perm_remove_users(image_id, user_id=[])
346
+ modify_image_attribute(image_id, 'launchPermission', 'remove', :user_id => user_id.to_a)
347
+ end
348
+
349
+ # Add image launch permissions for users groups (currently only 'all' is supported, which gives public launch permissions).
350
+ # Returns +true+ or an exception.
351
+ #
352
+ # ec2.modify_image_launch_perm_add_groups('ami-e444444d') #=> true
353
+ #
354
+ def modify_image_launch_perm_add_groups(image_id, user_group=['all'])
355
+ modify_image_attribute(image_id, 'launchPermission', 'add', :user_group => user_group.to_a)
356
+ end
357
+
358
+ # Remove image launch permissions for users groups (currently only 'all' is supported, which gives public launch permissions).
359
+ #
360
+ # ec2.modify_image_launch_perm_remove_groups('ami-e444444d') #=> true
361
+ #
362
+ def modify_image_launch_perm_remove_groups(image_id, user_group=['all'])
363
+ modify_image_attribute(image_id, 'launchPermission', 'remove', :user_group => user_group.to_a)
364
+ end
365
+
366
+ # Add product code to image
367
+ #
368
+ # ec2.modify_image_product_code('ami-e444444d','0ABCDEF') #=> true
369
+ #
370
+ def modify_image_product_code(image_id, product_code=[])
371
+ modify_image_attribute(image_id, 'productCodes', nil, :product_code => product_code.to_a)
372
+ end
373
+
374
+ #-----------------------------------------------------------------
375
+ # Instances
376
+ #-----------------------------------------------------------------
377
+
378
+ def get_desc_instances(instances) # :nodoc:
379
+ result = []
380
+ instances.each do |reservation|
381
+ reservation[:instances_set].each do |instance|
382
+ # Parse and remove timestamp from the reason string. The timestamp is of
383
+ # the request, not when EC2 took action, thus confusing & useless...
384
+ instance[:aws_reason] = instance[:aws_reason].sub(/\(\d[^)]*GMT\) */, '')
385
+ instance[:aws_owner] = reservation[:aws_owner]
386
+ instance[:aws_reservation_id] = reservation[:aws_reservation_id]
387
+ instance[:aws_groups] = reservation[:aws_groups]
388
+ result << instance
389
+ end
390
+ end
391
+ result
392
+ rescue Exception
393
+ on_exception
394
+ end
395
+
396
+ def describe_availability_zones(options={})
397
+ link = generate_request("DescribeAvailabilityZones", options={})
398
+ request_info_xml_simple(:rds_connection, @params, link, @logger,
399
+ :group_tags=>{"DBInstances"=>"DBInstance",
400
+ "DBParameterGroups"=>"DBParameterGroup",
401
+ "DBSecurityGroups"=>"DBSecurityGroup",
402
+ "EC2SecurityGroups"=>"EC2SecurityGroup",
403
+ "IPRanges"=>"IPRange"},
404
+ :force_array=>["DBInstances",
405
+ "DBParameterGroups",
406
+ "DBSecurityGroups",
407
+ "EC2SecurityGroups",
408
+ "IPRanges"],
409
+ :pull_out_array=>options[:pull_out_array],
410
+ :pull_out_single=>options[:pull_out_single],
411
+ :wrapper=>options[:wrapper])
412
+ rescue Exception
413
+ on_exception
414
+ end
415
+
416
+ # Retrieve information about EC2 instances. If +list+ is omitted then returns the
417
+ # list of all instances.
418
+ #
419
+ # ec2.describe_instances #=>
420
+ # [{:aws_image_id => "ami-e444444d",
421
+ # :aws_reason => "",
422
+ # :aws_state_code => "16",
423
+ # :aws_owner => "000000000888",
424
+ # :aws_instance_id => "i-123f1234",
425
+ # :aws_reservation_id => "r-aabbccdd",
426
+ # :aws_state => "running",
427
+ # :dns_name => "domU-12-34-67-89-01-C9.usma2.compute.amazonaws.com",
428
+ # :ssh_key_name => "staging",
429
+ # :aws_groups => ["default"],
430
+ # :private_dns_name => "domU-12-34-67-89-01-C9.usma2.compute.amazonaws.com",
431
+ # :aws_instance_type => "m1.small",
432
+ # :aws_launch_time => "2008-1-1T00:00:00.000Z"},
433
+ # :aws_availability_zone => "us-east-1b",
434
+ # :aws_kernel_id => "aki-ba3adfd3",
435
+ # :aws_ramdisk_id => "ari-badbad00",
436
+ # :monitoring_state => ...,
437
+ # ..., {...}]
438
+ #
439
+ def describe_instances(list=[])
440
+ link = generate_request("DescribeInstances", hash_params('InstanceId',list.to_a))
441
+ request_cache_or_info(:describe_instances, link, QEc2DescribeInstancesParser, @@bench, list.blank?) do |parser|
442
+ get_desc_instances(parser.result)
443
+ end
444
+ rescue Exception
445
+ on_exception
446
+ end
447
+
448
+ # Return the product code attached to instance or +nil+ otherwise.
449
+ #
450
+ # ec2.confirm_product_instance('ami-e444444d','12345678') #=> nil
451
+ # ec2.confirm_product_instance('ami-e444444d','00001111') #=> "000000000888"
452
+ #
453
+ def confirm_product_instance(instance, product_code)
454
+ link = generate_request("ConfirmProductInstance", { 'ProductCode' => product_code,
455
+ 'InstanceId' => instance })
456
+ request_info(link, QEc2ConfirmProductInstanceParser.new(:logger => @logger))
457
+ end
458
+
459
+ # DEPRECATED, USE launch_instances instead.
460
+ #
461
+ # Launch new EC2 instances. Returns a list of launched instances or an exception.
462
+ #
463
+ # ec2.run_instances('ami-e444444d',1,1,['my_awesome_group'],'my_awesome_key', 'Woohoo!!!', 'public') #=>
464
+ # [{:aws_image_id => "ami-e444444d",
465
+ # :aws_reason => "",
466
+ # :aws_state_code => "0",
467
+ # :aws_owner => "000000000888",
468
+ # :aws_instance_id => "i-123f1234",
469
+ # :aws_reservation_id => "r-aabbccdd",
470
+ # :aws_state => "pending",
471
+ # :dns_name => "",
472
+ # :ssh_key_name => "my_awesome_key",
473
+ # :aws_groups => ["my_awesome_group"],
474
+ # :private_dns_name => "",
475
+ # :aws_instance_type => "m1.small",
476
+ # :aws_launch_time => "2008-1-1T00:00:00.000Z"
477
+ # :aws_ramdisk_id => "ari-8605e0ef"
478
+ # :aws_kernel_id => "aki-9905e0f0",
479
+ # :ami_launch_index => "0",
480
+ # :aws_availability_zone => "us-east-1b"
481
+ # }]
482
+ #
483
+ def run_instances(image_id, min_count, max_count, group_ids, key_name, user_data='',
484
+ addressing_type = nil, instance_type = nil,
485
+ kernel_id = nil, ramdisk_id = nil, availability_zone = nil,
486
+ block_device_mappings = nil)
487
+ launch_instances(image_id, { :min_count => min_count,
488
+ :max_count => max_count,
489
+ :user_data => user_data,
490
+ :group_ids => group_ids,
491
+ :key_name => key_name,
492
+ :instance_type => instance_type,
493
+ :addressing_type => addressing_type,
494
+ :kernel_id => kernel_id,
495
+ :ramdisk_id => ramdisk_id,
496
+ :availability_zone => availability_zone,
497
+ :block_device_mappings => block_device_mappings
498
+ })
499
+ end
500
+
501
+
502
+ # Launch new EC2 instances. Returns a list of launched instances or an exception.
503
+ #
504
+ # +lparams+ keys (default values in parenthesis):
505
+ # :min_count fixnum, (1)
506
+ # :max_count fixnum, (1)
507
+ # :group_ids array or string ([] == 'default')
508
+ # :instance_type string (DEFAULT_INSTACE_TYPE)
509
+ # :addressing_type string (DEFAULT_ADDRESSING_TYPE
510
+ # :key_name string
511
+ # :kernel_id string
512
+ # :ramdisk_id string
513
+ # :availability_zone string
514
+ # :block_device_mappings string
515
+ # :user_data string
516
+ # :monitoring_enabled boolean (default=false)
517
+ #
518
+ # ec2.launch_instances('ami-e444444d', :group_ids => 'my_awesome_group',
519
+ # :user_data => "Woohoo!!!",
520
+ # :addressing_type => "public",
521
+ # :key_name => "my_awesome_key",
522
+ # :availability_zone => "us-east-1c") #=>
523
+ # [{:aws_image_id => "ami-e444444d",
524
+ # :aws_reason => "",
525
+ # :aws_state_code => "0",
526
+ # :aws_owner => "000000000888",
527
+ # :aws_instance_id => "i-123f1234",
528
+ # :aws_reservation_id => "r-aabbccdd",
529
+ # :aws_state => "pending",
530
+ # :dns_name => "",
531
+ # :ssh_key_name => "my_awesome_key",
532
+ # :aws_groups => ["my_awesome_group"],
533
+ # :private_dns_name => "",
534
+ # :aws_instance_type => "m1.small",
535
+ # :aws_launch_time => "2008-1-1T00:00:00.000Z",
536
+ # :aws_ramdisk_id => "ari-8605e0ef"
537
+ # :aws_kernel_id => "aki-9905e0f0",
538
+ # :ami_launch_index => "0",
539
+ # :aws_availability_zone => "us-east-1c"
540
+ # }]
541
+ #
542
+ def launch_instances(image_id, options={})
543
+ @logger.info("Launching instance of image #{image_id} for #{@aws_access_key_id}, " +
544
+ "key: #{options[:key_name]}, groups: #{(options[:group_ids]).to_a.join(',')}")
545
+ # careful: keyName and securityGroups may be nil
546
+ params = hash_params('SecurityGroup', options[:group_ids].to_a)
547
+ params.update( {'ImageId' => image_id,
548
+ 'MinCount' => (options[:min_count] || 1).to_s,
549
+ 'MaxCount' => (options[:max_count] || 1).to_s,
550
+ 'AddressingType' => options[:addressing_type] || DEFAULT_ADDRESSING_TYPE,
551
+ 'InstanceType' => options[:instance_type] || DEFAULT_INSTANCE_TYPE })
552
+ # optional params
553
+ params['KeyName'] = options[:key_name] unless options[:key_name].blank?
554
+ params['KernelId'] = options[:kernel_id] unless options[:kernel_id].blank?
555
+ params['RamdiskId'] = options[:ramdisk_id] unless options[:ramdisk_id].blank?
556
+ params['Placement.AvailabilityZone'] = options[:availability_zone] unless options[:availability_zone].blank?
557
+ params['BlockDeviceMappings'] = options[:block_device_mappings] unless options[:block_device_mappings].blank?
558
+ params['Monitoring.Enabled'] = options[:monitoring_enabled] unless options[:monitoring_enabled].blank?
559
+ params['SubnetId'] = options[:subnet_id] unless options[:subnet_id].blank?
560
+ params['AdditionalInfo'] = options[:additional_info] unless options[:additional_info].blank?
561
+ params['DisableApiTermination'] = options[:disable_api_termination].to_s unless options[:disable_api_termination].nil?
562
+ params['InstanceInitiatedShutdownBehavior'] = options[:instance_initiated_shutdown_behavior] unless options[:instance_initiated_shutdown_behavior].blank?
563
+ unless options[:user_data].blank?
564
+ options[:user_data].strip!
565
+ # Do not use CGI::escape(encode64(...)) as it is done in Amazons EC2 library.
566
+ # Amazon 169.254.169.254 does not like escaped symbols!
567
+ # And it doesn't like "\n" inside of encoded string! Grrr....
568
+ # Otherwise, some of UserData symbols will be lost...
569
+ params['UserData'] = Base64.encode64(options[:user_data]).delete("\n").strip unless options[:user_data].blank?
570
+ end
571
+ link = generate_request("RunInstances", params)
572
+ #debugger
573
+ instances = request_info(link, QEc2DescribeInstancesParser.new(:logger => @logger))
574
+ get_desc_instances(instances)
575
+ rescue Exception
576
+ on_exception
577
+ end
578
+
579
+ def monitor_instances(list=[])
580
+ link = generate_request("MonitorInstances", hash_params('InstanceId',list.to_a))
581
+ request_info(link, QEc2TerminateInstancesParser.new(:logger => @logger))
582
+ rescue Exception
583
+ on_exception
584
+ end
585
+
586
+ # Terminates EC2 instances. Returns a list of termination params or an exception.
587
+ #
588
+ # ec2.terminate_instances(['i-f222222d','i-f222222e']) #=>
589
+ # [{:aws_shutdown_state => "shutting-down",
590
+ # :aws_instance_id => "i-f222222d",
591
+ # :aws_shutdown_state_code => 32,
592
+ # :aws_prev_state => "running",
593
+ # :aws_prev_state_code => 16},
594
+ # {:aws_shutdown_state => "shutting-down",
595
+ # :aws_instance_id => "i-f222222e",
596
+ # :aws_shutdown_state_code => 32,
597
+ # :aws_prev_state => "running",
598
+ # :aws_prev_state_code => 16}]
599
+ #
600
+ def terminate_instances(list=[])
601
+ link = generate_request("TerminateInstances", hash_params('InstanceId',list.to_a))
602
+ request_info(link, QEc2TerminateInstancesParser.new(:logger => @logger))
603
+ rescue Exception
604
+ on_exception
605
+ end
606
+
607
+ # Retreive EC2 instance OS logs. Returns a hash of data or an exception.
608
+ #
609
+ # ec2.get_console_output('i-f222222d') =>
610
+ # {:aws_instance_id => 'i-f222222d',
611
+ # :aws_timestamp => "2007-05-23T14:36:07.000-07:00",
612
+ # :timestamp => Wed May 23 21:36:07 UTC 2007, # Time instance
613
+ # :aws_output => "Linux version 2.6.16-xenU (builder@patchbat.amazonsa) (gcc version 4.0.1 20050727 ..."
614
+ def get_console_output(instance_id)
615
+ link = generate_request("GetConsoleOutput", { 'InstanceId.1' => instance_id })
616
+ request_info(link, QEc2GetConsoleOutputParser.new(:logger => @logger))
617
+ rescue Exception
618
+ on_exception
619
+ end
620
+
621
+ # Reboot an EC2 instance. Returns +true+ or an exception.
622
+ #
623
+ # ec2.reboot_instances(['i-f222222d','i-f222222e']) #=> true
624
+ #
625
+ def reboot_instances(list)
626
+ link = generate_request("RebootInstances", hash_params('InstanceId', list.to_a))
627
+ request_info(link, RightBoolResponseParser.new(:logger => @logger))
628
+ rescue Exception
629
+ on_exception
630
+ end
631
+
632
+ #-----------------------------------------------------------------
633
+ # Instances: Windows addons
634
+ #-----------------------------------------------------------------
635
+
636
+ # Get initial Windows Server setup password from an instance console output.
637
+ #
638
+ # my_awesome_key = ec2.create_key_pair('my_awesome_key') #=>
639
+ # {:aws_key_name => "my_awesome_key",
640
+ # :aws_fingerprint => "01:02:03:f4:25:e6:97:e8:9b:02:1a:26:32:4e:58:6b:7a:8c:9f:03",
641
+ # :aws_material => "-----BEGIN RSA PRIVATE KEY-----\nMIIEpQIBAAK...Q8MDrCbuQ=\n-----END RSA PRIVATE KEY-----"}
642
+ #
643
+ # my_awesome_instance = ec2.run_instances('ami-a000000a',1,1,['my_awesome_group'],'my_awesome_key', 'WindowsInstance!!!') #=>
644
+ # [{:aws_image_id => "ami-a000000a",
645
+ # :aws_instance_id => "i-12345678",
646
+ # ...
647
+ # :aws_availability_zone => "us-east-1b"
648
+ # }]
649
+ #
650
+ # # wait until instance enters 'operational' state and get it's initial password
651
+ #
652
+ # puts ec2.get_initial_password(my_awesome_instance[:aws_instance_id], my_awesome_key[:aws_material]) #=> "MhjWcgZuY6"
653
+ #
654
+ def get_initial_password(instance_id, private_key)
655
+ console_output = get_console_output(instance_id)
656
+ crypted_password = console_output[:aws_output][%r{<Password>(.+)</Password>}m] && $1
657
+ unless crypted_password
658
+ raise AwsError.new("Initial password was not found in console output for #{instance_id}")
659
+ else
660
+ OpenSSL::PKey::RSA.new(private_key).private_decrypt(Base64.decode64(crypted_password))
661
+ end
662
+ rescue Exception
663
+ on_exception
664
+ end
665
+
666
+ # Bundle a Windows image.
667
+ # Internally, it queues the bundling task and shuts down the instance.
668
+ # It then takes a snapshot of the Windows volume bundles it, and uploads it to
669
+ # S3. After bundling completes, Aws::Ec2#register_image may be used to
670
+ # register the new Windows AMI for subsequent launches.
671
+ #
672
+ # ec2.bundle_instance('i-e3e24e8a', 'my-awesome-bucket', 'my-win-image-1') #=>
673
+ # [{:aws_update_time => "2008-10-16T13:58:25.000Z",
674
+ # :s3_bucket => "kd-win-1",
675
+ # :s3_prefix => "win2pr",
676
+ # :aws_state => "pending",
677
+ # :aws_id => "bun-26a7424f",
678
+ # :aws_instance_id => "i-878a25ee",
679
+ # :aws_start_time => "2008-10-16T13:58:02.000Z"}]
680
+ #
681
+ def bundle_instance(instance_id, s3_bucket, s3_prefix,
682
+ s3_owner_aws_access_key_id=nil, s3_owner_aws_secret_access_key=nil,
683
+ s3_expires = S3Interface::DEFAULT_EXPIRES_AFTER,
684
+ s3_upload_policy='ec2-bundle-read')
685
+ # S3 access and signatures
686
+ s3_owner_aws_access_key_id ||= @aws_access_key_id
687
+ s3_owner_aws_secret_access_key ||= @aws_secret_access_key
688
+ s3_expires = Time.now.utc + s3_expires if s3_expires.is_a?(Fixnum) && (s3_expires < S3Interface::ONE_YEAR_IN_SECONDS)
689
+ # policy
690
+ policy = { 'expiration' => s3_expires.strftime('%Y-%m-%dT%H:%M:%SZ'),
691
+ 'conditions' => [ { 'bucket' => s3_bucket },
692
+ { 'acl' => s3_upload_policy },
693
+ [ 'starts-with', '$key', s3_prefix ] ] }.to_json
694
+ policy64 = Base64.encode64(policy).gsub("\n","")
695
+ signed_policy64 = AwsUtils.sign(s3_owner_aws_secret_access_key, policy64)
696
+ # fill request params
697
+ params = { 'InstanceId' => instance_id,
698
+ 'Storage.S3.AWSAccessKeyId' => s3_owner_aws_access_key_id,
699
+ 'Storage.S3.UploadPolicy' => policy64,
700
+ 'Storage.S3.UploadPolicySignature' => signed_policy64,
701
+ 'Storage.S3.Bucket' => s3_bucket,
702
+ 'Storage.S3.Prefix' => s3_prefix,
703
+ }
704
+ link = generate_request("BundleInstance", params)
705
+ request_info(link, QEc2BundleInstanceParser.new)
706
+ rescue Exception
707
+ on_exception
708
+ end
709
+
710
+ # Describe the status of the Windows AMI bundlings.
711
+ # If +list+ is omitted the returns the whole list of tasks.
712
+ #
713
+ # ec2.describe_bundle_tasks(['bun-4fa74226']) #=>
714
+ # [{:s3_bucket => "my-awesome-bucket"
715
+ # :aws_id => "bun-0fa70206",
716
+ # :s3_prefix => "win1pr",
717
+ # :aws_start_time => "2008-10-14T16:27:57.000Z",
718
+ # :aws_update_time => "2008-10-14T16:37:10.000Z",
719
+ # :aws_error_code => "Client.S3Error",
720
+ # :aws_error_message =>
721
+ # "AccessDenied(403)- Invalid according to Policy: Policy Condition failed: [\"eq\", \"$acl\", \"aws-exec-read\"]",
722
+ # :aws_state => "failed",
723
+ # :aws_instance_id => "i-e3e24e8a"}]
724
+ #
725
+ def describe_bundle_tasks(list=[])
726
+ link = generate_request("DescribeBundleTasks", hash_params('BundleId', list.to_a))
727
+ request_info(link, QEc2DescribeBundleTasksParser.new)
728
+ rescue Exception
729
+ on_exception
730
+ end
731
+
732
+ # Cancel an in‐progress or pending bundle task by id.
733
+ #
734
+ # ec2.cancel_bundle_task('bun-73a7421a') #=>
735
+ # [{:s3_bucket => "my-awesome-bucket"
736
+ # :aws_id => "bun-0fa70206",
737
+ # :s3_prefix => "win02",
738
+ # :aws_start_time => "2008-10-14T13:00:29.000Z",
739
+ # :aws_error_message => "User has requested bundling operation cancellation",
740
+ # :aws_state => "failed",
741
+ # :aws_update_time => "2008-10-14T13:01:31.000Z",
742
+ # :aws_error_code => "Client.Cancelled",
743
+ # :aws_instance_id => "i-e3e24e8a"}
744
+ #
745
+ def cancel_bundle_task(bundle_id)
746
+ link = generate_request("CancelBundleTask", { 'BundleId' => bundle_id })
747
+ request_info(link, QEc2BundleInstanceParser.new)
748
+ rescue Exception
749
+ on_exception
750
+ end
751
+
752
+ #-----------------------------------------------------------------
753
+ # Security groups
754
+ #-----------------------------------------------------------------
755
+
756
+ # Retrieve Security Group information. If +list+ is omitted the returns the whole list of groups.
757
+ #
758
+ # ec2.describe_security_groups #=>
759
+ # [{:aws_group_name => "default-1",
760
+ # :aws_owner => "000000000888",
761
+ # :aws_description => "Default allowing SSH, HTTP, and HTTPS ingress",
762
+ # :aws_perms =>
763
+ # [{:owner => "000000000888", :group => "default"},
764
+ # {:owner => "000000000888", :group => "default-1"},
765
+ # {:to_port => "-1", :protocol => "icmp", :from_port => "-1", :cidr_ips => "0.0.0.0/0"},
766
+ # {:to_port => "22", :protocol => "tcp", :from_port => "22", :cidr_ips => "0.0.0.0/0"},
767
+ # {:to_port => "80", :protocol => "tcp", :from_port => "80", :cidr_ips => "0.0.0.0/0"},
768
+ # {:to_port => "443", :protocol => "tcp", :from_port => "443", :cidr_ips => "0.0.0.0/0"}]},
769
+ # ..., {...}]
770
+ #
771
+ def describe_security_groups(list=[])
772
+ link = generate_request("DescribeSecurityGroups", hash_params('GroupName',list.to_a))
773
+ request_cache_or_info( :describe_security_groups, link, QEc2DescribeSecurityGroupsParser, @@bench, list.blank?) do |parser|
774
+ result = []
775
+ parser.result.each do |item|
776
+ perms = []
777
+ item.ipPermissions.each do |perm|
778
+ perm.groups.each do |ngroup|
779
+ perms << {:group => ngroup.groupName,
780
+ :owner => ngroup.userId}
781
+ end
782
+ perm.ipRanges.each do |cidr_ip|
783
+ perms << {:from_port => perm.fromPort,
784
+ :to_port => perm.toPort,
785
+ :protocol => perm.ipProtocol,
786
+ :cidr_ips => cidr_ip}
787
+ end
788
+ end
789
+
790
+ # delete duplication
791
+ perms.each_index do |i|
792
+ (0...i).each do |j|
793
+ if perms[i] == perms[j] then perms[i] = nil; break; end
794
+ end
795
+ end
796
+ perms.compact!
797
+
798
+ result << {:aws_owner => item.ownerId,
799
+ :aws_group_name => item.groupName,
800
+ :aws_description => item.groupDescription,
801
+ :aws_perms => perms}
802
+
803
+ end
804
+ result
805
+ end
806
+ rescue Exception
807
+ on_exception
808
+ end
809
+
810
+ # Create new Security Group. Returns +true+ or an exception.
811
+ #
812
+ # ec2.create_security_group('default-1',"Default allowing SSH, HTTP, and HTTPS ingress") #=> true
813
+ #
814
+ def create_security_group(name, description)
815
+ # EC2 doesn't like an empty description...
816
+ description = " " if description.blank?
817
+ link = generate_request("CreateSecurityGroup",
818
+ 'GroupName' => name.to_s,
819
+ 'GroupDescription' => description.to_s)
820
+ request_info(link, RightBoolResponseParser.new(:logger => @logger))
821
+ rescue Exception
822
+ on_exception
823
+ end
824
+
825
+ # Remove Security Group. Returns +true+ or an exception.
826
+ #
827
+ # ec2.delete_security_group('default-1') #=> true
828
+ #
829
+ def delete_security_group(name)
830
+ link = generate_request("DeleteSecurityGroup",
831
+ 'GroupName' => name.to_s)
832
+ request_info(link, RightBoolResponseParser.new(:logger => @logger))
833
+ rescue Exception
834
+ on_exception
835
+ end
836
+
837
+ # Authorize named ingress for security group. Allows instances that are member of someone
838
+ # else's security group to open connections to instances in my group.
839
+ #
840
+ # ec2.authorize_security_group_named_ingress('my_awesome_group', '7011-0219-8268', 'their_group_name') #=> true
841
+ #
842
+ def authorize_security_group_named_ingress(name, owner, group)
843
+ link = generate_request("AuthorizeSecurityGroupIngress",
844
+ 'GroupName' => name.to_s,
845
+ 'SourceSecurityGroupName' => group.to_s,
846
+ 'SourceSecurityGroupOwnerId' => owner.to_s.gsub(/-/,''))
847
+ request_info(link, RightBoolResponseParser.new(:logger => @logger))
848
+ rescue Exception
849
+ on_exception
850
+ end
851
+
852
+ # Revoke named ingress for security group.
853
+ #
854
+ # ec2.revoke_security_group_named_ingress('my_awesome_group', aws_user_id, 'another_group_name') #=> true
855
+ #
856
+ def revoke_security_group_named_ingress(name, owner, group)
857
+ link = generate_request("RevokeSecurityGroupIngress",
858
+ 'GroupName' => name.to_s,
859
+ 'SourceSecurityGroupName' => group.to_s,
860
+ 'SourceSecurityGroupOwnerId' => owner.to_s.gsub(/-/,''))
861
+ request_info(link, RightBoolResponseParser.new(:logger => @logger))
862
+ rescue Exception
863
+ on_exception
864
+ end
865
+
866
+ # Add permission to a security group. Returns +true+ or an exception. +protocol+ is one of :'tcp'|'udp'|'icmp'.
867
+ #
868
+ # ec2.authorize_security_group_IP_ingress('my_awesome_group', 80, 82, 'udp', '192.168.1.0/8') #=> true
869
+ # ec2.authorize_security_group_IP_ingress('my_awesome_group', -1, -1, 'icmp') #=> true
870
+ #
871
+ def authorize_security_group_IP_ingress(name, from_port, to_port, protocol='tcp', cidr_ip='0.0.0.0/0')
872
+ link = generate_request("AuthorizeSecurityGroupIngress",
873
+ 'GroupName' => name.to_s,
874
+ 'IpProtocol' => protocol.to_s,
875
+ 'FromPort' => from_port.to_s,
876
+ 'ToPort' => to_port.to_s,
877
+ 'CidrIp' => cidr_ip.to_s)
878
+ request_info(link, RightBoolResponseParser.new(:logger => @logger))
879
+ rescue Exception
880
+ on_exception
881
+ end
882
+
883
+ # Remove permission from a security group. Returns +true+ or an exception. +protocol+ is one of :'tcp'|'udp'|'icmp' ('tcp' is default).
884
+ #
885
+ # ec2.revoke_security_group_IP_ingress('my_awesome_group', 80, 82, 'udp', '192.168.1.0/8') #=> true
886
+ #
887
+ def revoke_security_group_IP_ingress(name, from_port, to_port, protocol='tcp', cidr_ip='0.0.0.0/0')
888
+ link = generate_request("RevokeSecurityGroupIngress",
889
+ 'GroupName' => name.to_s,
890
+ 'IpProtocol' => protocol.to_s,
891
+ 'FromPort' => from_port.to_s,
892
+ 'ToPort' => to_port.to_s,
893
+ 'CidrIp' => cidr_ip.to_s)
894
+ request_info(link, RightBoolResponseParser.new(:logger => @logger))
895
+ rescue Exception
896
+ on_exception
897
+ end
898
+
899
+ #-----------------------------------------------------------------
900
+ # Keys
901
+ #-----------------------------------------------------------------
902
+
903
+ # Retrieve a list of SSH keys. Returns an array of keys or an exception. Each key is
904
+ # represented as a two-element hash.
905
+ #
906
+ # ec2.describe_key_pairs #=>
907
+ # [{:aws_fingerprint=> "01:02:03:f4:25:e6:97:e8:9b:02:1a:26:32:4e:58:6b:7a:8c:9f:03", :aws_key_name=>"key-1"},
908
+ # {:aws_fingerprint=> "1e:29:30:47:58:6d:7b:8c:9f:08:11:20:3c:44:52:69:74:80:97:08", :aws_key_name=>"key-2"},
909
+ # ..., {...} ]
910
+ #
911
+ def describe_key_pairs(list=[])
912
+ link = generate_request("DescribeKeyPairs", hash_params('KeyName',list.to_a))
913
+ request_cache_or_info :describe_key_pairs, link, QEc2DescribeKeyPairParser, @@bench, list.blank?
914
+ rescue Exception
915
+ on_exception
916
+ end
917
+
918
+ # Create new SSH key. Returns a hash of the key's data or an exception.
919
+ #
920
+ # ec2.create_key_pair('my_awesome_key') #=>
921
+ # {:aws_key_name => "my_awesome_key",
922
+ # :aws_fingerprint => "01:02:03:f4:25:e6:97:e8:9b:02:1a:26:32:4e:58:6b:7a:8c:9f:03",
923
+ # :aws_material => "-----BEGIN RSA PRIVATE KEY-----\nMIIEpQIBAAK...Q8MDrCbuQ=\n-----END RSA PRIVATE KEY-----"}
924
+ #
925
+ def create_key_pair(name)
926
+ link = generate_request("CreateKeyPair",
927
+ 'KeyName' => name.to_s)
928
+ request_info(link, QEc2CreateKeyPairParser.new(:logger => @logger))
929
+ rescue Exception
930
+ on_exception
931
+ end
932
+
933
+ # Delete a key pair. Returns +true+ or an exception.
934
+ #
935
+ # ec2.delete_key_pair('my_awesome_key') #=> true
936
+ #
937
+ def delete_key_pair(name)
938
+ link = generate_request("DeleteKeyPair",
939
+ 'KeyName' => name.to_s)
940
+ request_info(link, RightBoolResponseParser.new(:logger => @logger))
941
+ rescue Exception
942
+ on_exception
943
+ end
944
+
945
+ #-----------------------------------------------------------------
946
+ # Elastic IPs
947
+ #-----------------------------------------------------------------
948
+
949
+ # Acquire a new elastic IP address for use with your account.
950
+ # Returns allocated IP address or an exception.
951
+ #
952
+ # ec2.allocate_address #=> '75.101.154.140'
953
+ #
954
+ def allocate_address
955
+ link = generate_request("AllocateAddress")
956
+ request_info(link, QEc2AllocateAddressParser.new(:logger => @logger))
957
+ rescue Exception
958
+ on_exception
959
+ end
960
+
961
+ # Associate an elastic IP address with an instance.
962
+ # Returns +true+ or an exception.
963
+ #
964
+ # ec2.associate_address('i-d630cbbf', '75.101.154.140') #=> true
965
+ #
966
+ def associate_address(instance_id, public_ip)
967
+ link = generate_request("AssociateAddress",
968
+ "InstanceId" => instance_id.to_s,
969
+ "PublicIp" => public_ip.to_s)
970
+ request_info(link, RightBoolResponseParser.new(:logger => @logger))
971
+ rescue Exception
972
+ on_exception
973
+ end
974
+
975
+ # List elastic IP addresses assigned to your account.
976
+ # Returns an array of 2 keys (:instance_id and :public_ip) hashes:
977
+ #
978
+ # ec2.describe_addresses #=> [{:instance_id=>"i-d630cbbf", :public_ip=>"75.101.154.140"},
979
+ # {:instance_id=>nil, :public_ip=>"75.101.154.141"}]
980
+ #
981
+ # ec2.describe_addresses('75.101.154.140') #=> [{:instance_id=>"i-d630cbbf", :public_ip=>"75.101.154.140"}]
982
+ #
983
+ def describe_addresses(list=[])
984
+ link = generate_request("DescribeAddresses",
985
+ hash_params('PublicIp',list.to_a))
986
+ request_cache_or_info :describe_addresses, link, QEc2DescribeAddressesParser, @@bench, list.blank?
987
+ rescue Exception
988
+ on_exception
989
+ end
990
+
991
+ # Disassociate the specified elastic IP address from the instance to which it is assigned.
992
+ # Returns +true+ or an exception.
993
+ #
994
+ # ec2.disassociate_address('75.101.154.140') #=> true
995
+ #
996
+ def disassociate_address(public_ip)
997
+ link = generate_request("DisassociateAddress",
998
+ "PublicIp" => public_ip.to_s)
999
+ request_info(link, RightBoolResponseParser.new(:logger => @logger))
1000
+ rescue Exception
1001
+ on_exception
1002
+ end
1003
+
1004
+ # Release an elastic IP address associated with your account.
1005
+ # Returns +true+ or an exception.
1006
+ #
1007
+ # ec2.release_address('75.101.154.140') #=> true
1008
+ #
1009
+ def release_address(public_ip)
1010
+ link = generate_request("ReleaseAddress",
1011
+ "PublicIp" => public_ip.to_s)
1012
+ request_info(link, RightBoolResponseParser.new(:logger => @logger))
1013
+ rescue Exception
1014
+ on_exception
1015
+ end
1016
+
1017
+ #-----------------------------------------------------------------
1018
+ # Availability zones
1019
+ #-----------------------------------------------------------------
1020
+
1021
+ # Describes availability zones that are currently available to the account and their states.
1022
+ # Returns an array of 2 keys (:zone_name and :zone_state) hashes:
1023
+ #
1024
+ # ec2.describe_availability_zones #=> [{:region_name=>"us-east-1",
1025
+ # :zone_name=>"us-east-1a",
1026
+ # :zone_state=>"available"}, ... ]
1027
+ #
1028
+ # ec2.describe_availability_zones('us-east-1c') #=> [{:region_name=>"us-east-1",
1029
+ # :zone_state=>"available",
1030
+ # :zone_name=>"us-east-1c"}]
1031
+ #
1032
+ def describe_availability_zones(list=[])
1033
+ link = generate_request("DescribeAvailabilityZones",
1034
+ hash_params('ZoneName',list.to_a))
1035
+ request_cache_or_info :describe_availability_zones, link, QEc2DescribeAvailabilityZonesParser, @@bench, list.blank?
1036
+ rescue Exception
1037
+ on_exception
1038
+ end
1039
+
1040
+ #-----------------------------------------------------------------
1041
+ # Regions
1042
+ #-----------------------------------------------------------------
1043
+
1044
+ # Describe regions.
1045
+ #
1046
+ # ec2.describe_regions #=> ["eu-west-1", "us-east-1"]
1047
+ #
1048
+ def describe_regions(list=[])
1049
+ link = generate_request("DescribeRegions",
1050
+ hash_params('RegionName',list.to_a))
1051
+ request_cache_or_info :describe_regions, link, QEc2DescribeRegionsParser, @@bench, list.blank?
1052
+ rescue Exception
1053
+ on_exception
1054
+ end
1055
+
1056
+
1057
+ #-----------------------------------------------------------------
1058
+ # EBS: Volumes
1059
+ #-----------------------------------------------------------------
1060
+
1061
+ # Describe all EBS volumes.
1062
+ #
1063
+ # ec2.describe_volumes #=>
1064
+ # [{:aws_size => 94,
1065
+ # :aws_device => "/dev/sdc",
1066
+ # :aws_attachment_status => "attached",
1067
+ # :zone => "merlot",
1068
+ # :snapshot_id => nil,
1069
+ # :aws_attached_at => Wed Jun 18 08:19:28 UTC 2008,
1070
+ # :aws_status => "in-use",
1071
+ # :aws_id => "vol-60957009",
1072
+ # :aws_created_at => Wed Jun 18 08:19:20s UTC 2008,
1073
+ # :aws_instance_id => "i-c014c0a9"},
1074
+ # {:aws_size => 1,
1075
+ # :zone => "merlot",
1076
+ # :snapshot_id => nil,
1077
+ # :aws_status => "available",
1078
+ # :aws_id => "vol-58957031",
1079
+ # :aws_created_at => Wed Jun 18 08:19:21 UTC 2008,}, ... ]
1080
+ #
1081
+ def describe_volumes(list=[])
1082
+ link = generate_request("DescribeVolumes",
1083
+ hash_params('VolumeId',list.to_a))
1084
+ request_cache_or_info :describe_volumes, link, QEc2DescribeVolumesParser, @@bench, list.blank?
1085
+ rescue Exception
1086
+ on_exception
1087
+ end
1088
+
1089
+ # Create new EBS volume based on previously created snapshot.
1090
+ # +Size+ in Gigabytes.
1091
+ #
1092
+ # ec2.create_volume('snap-000000', 10, zone) #=>
1093
+ # {:snapshot_id => "snap-e21df98b",
1094
+ # :aws_status => "creating",
1095
+ # :aws_id => "vol-fc9f7a95",
1096
+ # :zone => "merlot",
1097
+ # :aws_created_at => Tue Jun 24 18:13:32 UTC 2008,
1098
+ # :aws_size => 94}
1099
+ #
1100
+ def create_volume(snapshot_id, size, zone)
1101
+ link = generate_request("CreateVolume",
1102
+ "SnapshotId" => snapshot_id.to_s,
1103
+ "Size" => size.to_s,
1104
+ "AvailabilityZone" => zone.to_s )
1105
+ request_info(link, QEc2CreateVolumeParser.new(:logger => @logger))
1106
+ rescue Exception
1107
+ on_exception
1108
+ end
1109
+
1110
+ # Delete the specified EBS volume.
1111
+ # This does not deletes any snapshots created from this volume.
1112
+ #
1113
+ # ec2.delete_volume('vol-b48a6fdd') #=> true
1114
+ #
1115
+ def delete_volume(volume_id)
1116
+ link = generate_request("DeleteVolume",
1117
+ "VolumeId" => volume_id.to_s)
1118
+ request_info(link, RightBoolResponseParser.new(:logger => @logger))
1119
+ rescue Exception
1120
+ on_exception
1121
+ end
1122
+
1123
+ # Attach the specified EBS volume to a specified instance, exposing the
1124
+ # volume using the specified device name.
1125
+ #
1126
+ # ec2.attach_volume('vol-898a6fe0', 'i-7c905415', '/dev/sdh') #=>
1127
+ # { :aws_instance_id => "i-7c905415",
1128
+ # :aws_device => "/dev/sdh",
1129
+ # :aws_status => "attaching",
1130
+ # :aws_attached_at => "2008-03-28T14:14:39.000Z",
1131
+ # :aws_id => "vol-898a6fe0" }
1132
+ #
1133
+ def attach_volume(volume_id, instance_id, device)
1134
+ link = generate_request("AttachVolume",
1135
+ "VolumeId" => volume_id.to_s,
1136
+ "InstanceId" => instance_id.to_s,
1137
+ "Device" => device.to_s)
1138
+ request_info(link, QEc2AttachAndDetachVolumeParser.new(:logger => @logger))
1139
+ rescue Exception
1140
+ on_exception
1141
+ end
1142
+
1143
+ # Detach the specified EBS volume from the instance to which it is attached.
1144
+ #
1145
+ # ec2.detach_volume('vol-898a6fe0') #=>
1146
+ # { :aws_instance_id => "i-7c905415",
1147
+ # :aws_device => "/dev/sdh",
1148
+ # :aws_status => "detaching",
1149
+ # :aws_attached_at => "2008-03-28T14:38:34.000Z",
1150
+ # :aws_id => "vol-898a6fe0"}
1151
+ #
1152
+ def detach_volume(volume_id, instance_id=nil, device=nil, force=nil)
1153
+ hash = { "VolumeId" => volume_id.to_s }
1154
+ hash["InstanceId"] = instance_id.to_s unless instance_id.blank?
1155
+ hash["Device"] = device.to_s unless device.blank?
1156
+ hash["Force"] = 'true' if force
1157
+ #
1158
+ link = generate_request("DetachVolume", hash)
1159
+ request_info(link, QEc2AttachAndDetachVolumeParser.new(:logger => @logger))
1160
+ rescue Exception
1161
+ on_exception
1162
+ end
1163
+
1164
+
1165
+ #-----------------------------------------------------------------
1166
+ # EBS: Snapshots
1167
+ #-----------------------------------------------------------------
1168
+
1169
+ # Describe all EBS snapshots.
1170
+ #
1171
+ # ec2.describe_snapshots #=>
1172
+ # [ { :aws_progress => "100%",
1173
+ # :aws_status => "completed",
1174
+ # :aws_id => "snap-72a5401b",
1175
+ # :aws_volume_id => "vol-5582673c",
1176
+ # :aws_started_at => "2008-02-23T02:50:48.000Z"},
1177
+ # { :aws_progress => "100%",
1178
+ # :aws_status => "completed",
1179
+ # :aws_id => "snap-75a5401c",
1180
+ # :aws_volume_id => "vol-5582673c",
1181
+ # :aws_started_at => "2008-02-23T16:23:19.000Z" },...]
1182
+ #
1183
+ def describe_snapshots(list=[])
1184
+ link = generate_request("DescribeSnapshots",
1185
+ hash_params('SnapshotId',list.to_a))
1186
+ request_cache_or_info :describe_snapshots, link, QEc2DescribeSnapshotsParser, @@bench, list.blank?
1187
+ rescue Exception
1188
+ on_exception
1189
+ end
1190
+
1191
+ # Create a snapshot of specified volume.
1192
+ #
1193
+ # ec2.create_snapshot('vol-898a6fe0') #=>
1194
+ # {:aws_volume_id => "vol-fd9f7a94",
1195
+ # :aws_started_at => Tue Jun 24 18:40:40 UTC 2008,
1196
+ # :aws_progress => "",
1197
+ # :aws_status => "pending",
1198
+ # :aws_id => "snap-d56783bc"}
1199
+ #
1200
+ def create_snapshot(volume_id)
1201
+ link = generate_request("CreateSnapshot",
1202
+ "VolumeId" => volume_id.to_s)
1203
+ request_info(link, QEc2CreateSnapshotParser.new(:logger => @logger))
1204
+ rescue Exception
1205
+ on_exception
1206
+ end
1207
+
1208
+ # Create a snapshot of specified volume, but with the normal retry algorithms disabled.
1209
+ # This method will return immediately upon error. The user can specify connect and read timeouts (in s)
1210
+ # for the connection to AWS. If the user does not specify timeouts, try_create_snapshot uses the default values
1211
+ # in Rightscale::HttpConnection.
1212
+ #
1213
+ # ec2.try_create_snapshot('vol-898a6fe0') #=>
1214
+ # {:aws_volume_id => "vol-fd9f7a94",
1215
+ # :aws_started_at => Tue Jun 24 18:40:40 UTC 2008,
1216
+ # :aws_progress => "",
1217
+ # :aws_status => "pending",
1218
+ # :aws_id => "snap-d56783bc"}
1219
+ #
1220
+ def try_create_snapshot(volume_id, connect_timeout = nil, read_timeout = nil)
1221
+ # For safety in the ensure block...we don't want to restore values
1222
+ # if we never read them in the first place
1223
+ orig_reiteration_time = nil
1224
+ orig_http_params = nil
1225
+
1226
+ orig_reiteration_time = Aws::AWSErrorHandler::reiteration_time
1227
+ Aws::AWSErrorHandler::reiteration_time = 0
1228
+
1229
+ orig_http_params = Rightscale::HttpConnection::params()
1230
+ new_http_params = orig_http_params.dup
1231
+ new_http_params[:http_connection_retry_count] = 0
1232
+ new_http_params[:http_connection_open_timeout] = connect_timeout if !connect_timeout.nil?
1233
+ new_http_params[:http_connection_read_timeout] = read_timeout if !read_timeout.nil?
1234
+ Rightscale::HttpConnection::params = new_http_params
1235
+
1236
+ link = generate_request("CreateSnapshot",
1237
+ "VolumeId" => volume_id.to_s)
1238
+ request_info(link, QEc2CreateSnapshotParser.new(:logger => @logger))
1239
+
1240
+ rescue Exception
1241
+ on_exception
1242
+ ensure
1243
+ Aws::AWSErrorHandler::reiteration_time = orig_reiteration_time if orig_reiteration_time
1244
+ Rightscale::HttpConnection::params = orig_http_params if orig_http_params
1245
+ end
1246
+
1247
+ # Delete the specified snapshot.
1248
+ #
1249
+ # ec2.delete_snapshot('snap-55a5403c') #=> true
1250
+ #
1251
+ def delete_snapshot(snapshot_id)
1252
+ link = generate_request("DeleteSnapshot",
1253
+ "SnapshotId" => snapshot_id.to_s)
1254
+ request_info(link, RightBoolResponseParser.new(:logger => @logger))
1255
+ rescue Exception
1256
+ on_exception
1257
+ end
1258
+
1259
+ # Add/replace one tag to a resource
1260
+ # http://docs.amazonwebservices.com/AWSEC2/latest/APIReference/index.html?ApiReference_query_CreateTags.html
1261
+ #
1262
+ # ec2.create_tag('ami-1a2b3c4d', 'webserver') #=> true
1263
+ # ec2.create_tag('i-7f4d3a2b', 'stack', 'Production') #=> true
1264
+ #
1265
+ def create_tag(resource_id, key, value = nil)
1266
+ link = generate_request("CreateTags",
1267
+ "ResourceId.1" => resource_id.to_s,
1268
+ "Tag.1.Key" => key.to_s,
1269
+ "Tag.1.Value" => value.to_s)
1270
+ request_info(link, RightBoolResponseParser.new(:logger => @logger))
1271
+ rescue Exception
1272
+ on_exception
1273
+ end
1274
+
1275
+ # Describe tags
1276
+ # http://docs.amazonwebservices.com/AWSEC2/latest/APIReference/index.html?ApiReference_query_DescribeTags.html
1277
+ #
1278
+ # ec2.describe_tags
1279
+ # ec2.describe_tags(
1280
+ # 'Filter.1.Name' => 'resource-type', 'Filter.1.Value.1' => 'instance',
1281
+ # 'Filter.2.Name' => 'value', 'Filter.2.Value.1' => 'Test', 'Filter.2.Value.2' => 'Production'
1282
+ # )
1283
+ #
1284
+ def describe_tags(filters = {})
1285
+ link = generate_request("DescribeTags", filters)
1286
+ request_info(link, QEc2DescribeTagsParser.new(:logger => @logger))
1287
+ rescue Exception
1288
+ on_exception
1289
+ end
1290
+
1291
+ # Delete one or all tags from a resource
1292
+ # http://docs.amazonwebservices.com/AWSEC2/latest/APIReference/index.html?ApiReference_query_DeleteTags.html
1293
+ #
1294
+ # ec2.delete_tag('i-7f4d3a2b', 'stack') #=> true
1295
+ # ec2.delete_tag('i-7f4d3a2b', 'stack', 'Production') #=> true
1296
+ #
1297
+ # "If you omit Tag.n.Value, we delete the tag regardless of its value. If
1298
+ # you specify this parameter with an empty string as the value, we delete the
1299
+ # key only if its value is an empty string."
1300
+ # http://docs.amazonwebservices.com/AWSEC2/latest/APIReference/ApiReference_query_DeleteTags.html
1301
+ #
1302
+ def delete_tag(resource_id, key, value = nil)
1303
+ request_args = {"ResourceId.1" => resource_id.to_s, "Tag.1.Key" => key.to_s}
1304
+ request_args["Tag.1.Value"] = value.to_s if value
1305
+
1306
+ link = generate_request("DeleteTags", request_args)
1307
+ request_info(link, RightBoolResponseParser.new(:logger => @logger))
1308
+ rescue Exception
1309
+ on_exception
1310
+ end
1311
+
1312
+ #-----------------------------------------------------------------
1313
+ # PARSERS: Boolean Response Parser
1314
+ #-----------------------------------------------------------------
1315
+
1316
+ class RightBoolResponseParser < AwsParser #:nodoc:
1317
+ def tagend(name)
1318
+ @result = @text=='true' ? true : false if name == 'return'
1319
+ end
1320
+ end
1321
+
1322
+ #-----------------------------------------------------------------
1323
+ # PARSERS: Key Pair
1324
+ #-----------------------------------------------------------------
1325
+
1326
+ class QEc2DescribeKeyPairParser < AwsParser #:nodoc:
1327
+ def tagstart(name, attributes)
1328
+ @item = {} if name == 'item'
1329
+ end
1330
+ def tagend(name)
1331
+ case name
1332
+ when 'keyName' then @item[:aws_key_name] = @text
1333
+ when 'keyFingerprint' then @item[:aws_fingerprint] = @text
1334
+ when 'item' then @result << @item
1335
+ end
1336
+ end
1337
+ def reset
1338
+ @result = [];
1339
+ end
1340
+ end
1341
+
1342
+ class QEc2CreateKeyPairParser < AwsParser #:nodoc:
1343
+ def tagstart(name, attributes)
1344
+ @result = {} if name == 'CreateKeyPairResponse'
1345
+ end
1346
+ def tagend(name)
1347
+ case name
1348
+ when 'keyName' then @result[:aws_key_name] = @text
1349
+ when 'keyFingerprint' then @result[:aws_fingerprint] = @text
1350
+ when 'keyMaterial' then @result[:aws_material] = @text
1351
+ end
1352
+ end
1353
+ end
1354
+
1355
+ #-----------------------------------------------------------------
1356
+ # PARSERS: Security Groups
1357
+ #-----------------------------------------------------------------
1358
+
1359
+ class QEc2UserIdGroupPairType #:nodoc:
1360
+ attr_accessor :userId
1361
+ attr_accessor :groupName
1362
+ end
1363
+
1364
+ class QEc2IpPermissionType #:nodoc:
1365
+ attr_accessor :ipProtocol
1366
+ attr_accessor :fromPort
1367
+ attr_accessor :toPort
1368
+ attr_accessor :groups
1369
+ attr_accessor :ipRanges
1370
+ end
1371
+
1372
+ class QEc2SecurityGroupItemType #:nodoc:
1373
+ attr_accessor :groupName
1374
+ attr_accessor :groupDescription
1375
+ attr_accessor :ownerId
1376
+ attr_accessor :ipPermissions
1377
+ end
1378
+
1379
+
1380
+ class QEc2DescribeSecurityGroupsParser < AwsParser #:nodoc:
1381
+ def tagstart(name, attributes)
1382
+ case name
1383
+ when 'item'
1384
+ if @xmlpath=='DescribeSecurityGroupsResponse/securityGroupInfo'
1385
+ @group = QEc2SecurityGroupItemType.new
1386
+ @group.ipPermissions = []
1387
+ elsif @xmlpath=='DescribeSecurityGroupsResponse/securityGroupInfo/item/ipPermissions'
1388
+ @perm = QEc2IpPermissionType.new
1389
+ @perm.ipRanges = []
1390
+ @perm.groups = []
1391
+ elsif @xmlpath=='DescribeSecurityGroupsResponse/securityGroupInfo/item/ipPermissions/item/groups'
1392
+ @sgroup = QEc2UserIdGroupPairType.new
1393
+ end
1394
+ end
1395
+ end
1396
+ def tagend(name)
1397
+ case name
1398
+ when 'ownerId' then @group.ownerId = @text
1399
+ when 'groupDescription' then @group.groupDescription = @text
1400
+ when 'groupName'
1401
+ if @xmlpath=='DescribeSecurityGroupsResponse/securityGroupInfo/item'
1402
+ @group.groupName = @text
1403
+ elsif @xmlpath=='DescribeSecurityGroupsResponse/securityGroupInfo/item/ipPermissions/item/groups/item'
1404
+ @sgroup.groupName = @text
1405
+ end
1406
+ when 'ipProtocol' then @perm.ipProtocol = @text
1407
+ when 'fromPort' then @perm.fromPort = @text
1408
+ when 'toPort' then @perm.toPort = @text
1409
+ when 'userId' then @sgroup.userId = @text
1410
+ when 'cidrIp' then @perm.ipRanges << @text
1411
+ when 'item'
1412
+ if @xmlpath=='DescribeSecurityGroupsResponse/securityGroupInfo/item/ipPermissions/item/groups'
1413
+ @perm.groups << @sgroup
1414
+ elsif @xmlpath=='DescribeSecurityGroupsResponse/securityGroupInfo/item/ipPermissions'
1415
+ @group.ipPermissions << @perm
1416
+ elsif @xmlpath=='DescribeSecurityGroupsResponse/securityGroupInfo'
1417
+ @result << @group
1418
+ end
1419
+ end
1420
+ end
1421
+ def reset
1422
+ @result = []
1423
+ end
1424
+ end
1425
+
1426
+ #-----------------------------------------------------------------
1427
+ # PARSERS: Images
1428
+ #-----------------------------------------------------------------
1429
+
1430
+ class QEc2DescribeImagesParser < AwsParser #:nodoc:
1431
+ def tagstart(name, attributes)
1432
+ if name == 'item' && @xmlpath[%r{.*/imagesSet$}]
1433
+ @image = {}
1434
+ end
1435
+ end
1436
+ def tagend(name)
1437
+ case name
1438
+ when 'imageId' then @image[:aws_id] = @text
1439
+ when 'name' then @image[:aws_name] = @text
1440
+ when 'description' then @image[:aws_description] = @text
1441
+ when 'imageLocation' then @image[:aws_location] = @text
1442
+ when 'imageState' then @image[:aws_state] = @text
1443
+ when 'imageOwnerId' then @image[:aws_owner] = @text
1444
+ when 'isPublic' then @image[:aws_is_public]= @text == 'true' ? true : false
1445
+ when 'productCode' then (@image[:aws_product_codes] ||= []) << @text
1446
+ when 'architecture' then @image[:aws_architecture] = @text
1447
+ when 'imageType' then @image[:aws_image_type] = @text
1448
+ when 'kernelId' then @image[:aws_kernel_id] = @text
1449
+ when 'ramdiskId' then @image[:aws_ramdisk_id] = @text
1450
+ when 'item' then @result << @image if @xmlpath[%r{.*/imagesSet$}]
1451
+ end
1452
+ end
1453
+ def reset
1454
+ @result = []
1455
+ end
1456
+ end
1457
+
1458
+ class QEc2RegisterImageParser < AwsParser #:nodoc:
1459
+ def tagend(name)
1460
+ @result = @text if name == 'imageId'
1461
+ end
1462
+ end
1463
+
1464
+ #-----------------------------------------------------------------
1465
+ # PARSERS: Image Attribute
1466
+ #-----------------------------------------------------------------
1467
+
1468
+ class QEc2DescribeImageAttributeParser < AwsParser #:nodoc:
1469
+ def tagstart(name, attributes)
1470
+ case name
1471
+ when 'launchPermission'
1472
+ @result[:groups] = []
1473
+ @result[:users] = []
1474
+ when 'productCodes'
1475
+ @result[:aws_product_codes] = []
1476
+ end
1477
+ end
1478
+ def tagend(name)
1479
+ # right now only 'launchPermission' is supported by Amazon.
1480
+ # But nobody know what will they xml later as attribute. That is why we
1481
+ # check for 'group' and 'userId' inside of 'launchPermission/item'
1482
+ case name
1483
+ when 'imageId' then @result[:aws_id] = @text
1484
+ when 'group' then @result[:groups] << @text if @xmlpath == 'DescribeImageAttributeResponse/launchPermission/item'
1485
+ when 'userId' then @result[:users] << @text if @xmlpath == 'DescribeImageAttributeResponse/launchPermission/item'
1486
+ when 'productCode' then @result[:aws_product_codes] << @text
1487
+ when 'kernel' then @result[:aws_kernel] = @text
1488
+ when 'ramdisk' then @result[:aws_ramdisk] = @text
1489
+ when 'blockDeviceMapping' then @result[:block_device_mapping] = @text
1490
+ end
1491
+ end
1492
+ def reset
1493
+ @result = {}
1494
+ end
1495
+ end
1496
+
1497
+ #-----------------------------------------------------------------
1498
+ # PARSERS: Instances
1499
+ #-----------------------------------------------------------------
1500
+
1501
+ class QEc2DescribeInstancesParser < AwsParser #:nodoc:
1502
+ def tagstart(name, attributes)
1503
+ # DescribeInstances property
1504
+ if (name == 'item' && @xmlpath == 'DescribeInstancesResponse/reservationSet') ||
1505
+ # RunInstances property
1506
+ (name == 'RunInstancesResponse')
1507
+ @reservation = { :aws_groups => [],
1508
+ :instances_set => [] }
1509
+
1510
+ elsif (name == 'item') &&
1511
+ # DescribeInstances property
1512
+ ( @xmlpath=='DescribeInstancesResponse/reservationSet/item/instancesSet' ||
1513
+ # RunInstances property
1514
+ @xmlpath=='RunInstancesResponse/instancesSet' )
1515
+ # the optional params (sometimes are missing and we dont want them to be nil)
1516
+ @instance = { :aws_reason => '',
1517
+ :dns_name => '',
1518
+ :private_dns_name => '',
1519
+ :ami_launch_index => '',
1520
+ :ssh_key_name => '',
1521
+ :aws_state => '',
1522
+ :aws_product_codes => [] }
1523
+ end
1524
+ end
1525
+ def tagend(name)
1526
+ case name
1527
+ # reservation
1528
+ when 'reservationId' then @reservation[:aws_reservation_id] = @text
1529
+ when 'ownerId' then @reservation[:aws_owner] = @text
1530
+ when 'groupId' then @reservation[:aws_groups] << @text
1531
+ # instance
1532
+ when 'instanceId' then @instance[:aws_instance_id] = @text
1533
+ when 'imageId' then @instance[:aws_image_id] = @text
1534
+ when 'dnsName' then @instance[:dns_name] = @text
1535
+ when 'privateDnsName' then @instance[:private_dns_name] = @text
1536
+ when 'reason' then @instance[:aws_reason] = @text
1537
+ when 'keyName' then @instance[:ssh_key_name] = @text
1538
+ when 'amiLaunchIndex' then @instance[:ami_launch_index] = @text
1539
+ when 'code' then @instance[:aws_state_code] = @text
1540
+ when 'name' then @instance[:aws_state] = @text
1541
+ when 'productCode' then @instance[:aws_product_codes] << @text
1542
+ when 'instanceType' then @instance[:aws_instance_type] = @text
1543
+ when 'launchTime' then @instance[:aws_launch_time] = @text
1544
+ when 'kernelId' then @instance[:aws_kernel_id] = @text
1545
+ when 'ramdiskId' then @instance[:aws_ramdisk_id] = @text
1546
+ when 'platform' then @instance[:aws_platform] = @text
1547
+ when 'availabilityZone' then @instance[:aws_availability_zone] = @text
1548
+ when 'privateIpAddress' then @instance[:aws_private_ip_address] = @text
1549
+ when 'state'
1550
+ if @xmlpath == 'DescribeInstancesResponse/reservationSet/item/instancesSet/item/monitoring' || # DescribeInstances property
1551
+ @xmlpath == 'RunInstancesResponse/instancesSet/item/monitoring' # RunInstances property
1552
+ @instance[:monitoring_state] = @text
1553
+ end
1554
+ when 'item'
1555
+ if @xmlpath == 'DescribeInstancesResponse/reservationSet/item/instancesSet' || # DescribeInstances property
1556
+ @xmlpath == 'RunInstancesResponse/instancesSet' # RunInstances property
1557
+ @reservation[:instances_set] << @instance
1558
+ elsif @xmlpath=='DescribeInstancesResponse/reservationSet' # DescribeInstances property
1559
+ @result << @reservation
1560
+ end
1561
+ when 'RunInstancesResponse' then @result << @reservation # RunInstances property
1562
+ end
1563
+ end
1564
+ def reset
1565
+ @result = []
1566
+ end
1567
+ end
1568
+
1569
+ class QEc2ConfirmProductInstanceParser < AwsParser #:nodoc:
1570
+ def tagend(name)
1571
+ @result = @text if name == 'ownerId'
1572
+ end
1573
+ end
1574
+
1575
+ class QEc2MonitorInstancesParser < AwsParser #:nodoc:
1576
+ def tagstart(name, attributes)
1577
+ @instance = {} if name == 'item'
1578
+ end
1579
+ def tagend(name)
1580
+ case name
1581
+ when 'instanceId' then @instance[:aws_instance_id] = @text
1582
+ when 'state' then @instance[:aws_monitoring_state] = @text
1583
+ when 'item' then @result << @instance
1584
+ end
1585
+ end
1586
+ def reset
1587
+ @result = []
1588
+ end
1589
+ end
1590
+
1591
+
1592
+ class QEc2TerminateInstancesParser < AwsParser #:nodoc:
1593
+ def tagstart(name, attributes)
1594
+ @instance = {} if name == 'item'
1595
+ end
1596
+ def tagend(name)
1597
+ case name
1598
+ when 'instanceId' then @instance[:aws_instance_id] = @text
1599
+ when 'code'
1600
+ if @xmlpath == 'TerminateInstancesResponse/instancesSet/item/shutdownState'
1601
+ @instance[:aws_shutdown_state_code] = @text.to_i
1602
+ else @instance[:aws_prev_state_code] = @text.to_i end
1603
+ when 'name'
1604
+ if @xmlpath == 'TerminateInstancesResponse/instancesSet/item/shutdownState'
1605
+ @instance[:aws_shutdown_state] = @text
1606
+ else @instance[:aws_prev_state] = @text end
1607
+ when 'item' then @result << @instance
1608
+ end
1609
+ end
1610
+ def reset
1611
+ @result = []
1612
+ end
1613
+ end
1614
+
1615
+ #-----------------------------------------------------------------
1616
+ # PARSERS: Console
1617
+ #-----------------------------------------------------------------
1618
+
1619
+ class QEc2GetConsoleOutputParser < AwsParser #:nodoc:
1620
+ def tagend(name)
1621
+ case name
1622
+ when 'instanceId' then @result[:aws_instance_id] = @text
1623
+ when 'timestamp' then @result[:aws_timestamp] = @text
1624
+ @result[:timestamp] = (Time.parse(@text)).utc
1625
+ when 'output' then @result[:aws_output] = Base64.decode64(@text)
1626
+ end
1627
+ end
1628
+ def reset
1629
+ @result = {}
1630
+ end
1631
+ end
1632
+
1633
+ #-----------------------------------------------------------------
1634
+ # Instances: Wondows related part
1635
+ #-----------------------------------------------------------------
1636
+ class QEc2DescribeBundleTasksParser < AwsParser #:nodoc:
1637
+ def tagstart(name, attributes)
1638
+ @bundle = {} if name == 'item'
1639
+ end
1640
+ def tagend(name)
1641
+ case name
1642
+ # when 'requestId' then @bundle[:request_id] = @text
1643
+ when 'instanceId' then @bundle[:aws_instance_id] = @text
1644
+ when 'bundleId' then @bundle[:aws_id] = @text
1645
+ when 'bucket' then @bundle[:s3_bucket] = @text
1646
+ when 'prefix' then @bundle[:s3_prefix] = @text
1647
+ when 'startTime' then @bundle[:aws_start_time] = @text
1648
+ when 'updateTime' then @bundle[:aws_update_time] = @text
1649
+ when 'state' then @bundle[:aws_state] = @text
1650
+ when 'progress' then @bundle[:aws_progress] = @text
1651
+ when 'code' then @bundle[:aws_error_code] = @text
1652
+ when 'message' then @bundle[:aws_error_message] = @text
1653
+ when 'item' then @result << @bundle
1654
+ end
1655
+ end
1656
+ def reset
1657
+ @result = []
1658
+ end
1659
+ end
1660
+
1661
+ class QEc2BundleInstanceParser < AwsParser #:nodoc:
1662
+ def tagend(name)
1663
+ case name
1664
+ # when 'requestId' then @result[:request_id] = @text
1665
+ when 'instanceId' then @result[:aws_instance_id] = @text
1666
+ when 'bundleId' then @result[:aws_id] = @text
1667
+ when 'bucket' then @result[:s3_bucket] = @text
1668
+ when 'prefix' then @result[:s3_prefix] = @text
1669
+ when 'startTime' then @result[:aws_start_time] = @text
1670
+ when 'updateTime' then @result[:aws_update_time] = @text
1671
+ when 'state' then @result[:aws_state] = @text
1672
+ when 'progress' then @result[:aws_progress] = @text
1673
+ when 'code' then @result[:aws_error_code] = @text
1674
+ when 'message' then @result[:aws_error_message] = @text
1675
+ end
1676
+ end
1677
+ def reset
1678
+ @result = {}
1679
+ end
1680
+ end
1681
+
1682
+ #-----------------------------------------------------------------
1683
+ # PARSERS: Elastic IPs
1684
+ #-----------------------------------------------------------------
1685
+
1686
+ class QEc2AllocateAddressParser < AwsParser #:nodoc:
1687
+ def tagend(name)
1688
+ @result = @text if name == 'publicIp'
1689
+ end
1690
+ end
1691
+
1692
+ class QEc2DescribeAddressesParser < AwsParser #:nodoc:
1693
+ def tagstart(name, attributes)
1694
+ @address = {} if name == 'item'
1695
+ end
1696
+ def tagend(name)
1697
+ case name
1698
+ when 'instanceId' then @address[:instance_id] = @text.blank? ? nil : @text
1699
+ when 'publicIp' then @address[:public_ip] = @text
1700
+ when 'item' then @result << @address
1701
+ end
1702
+ end
1703
+ def reset
1704
+ @result = []
1705
+ end
1706
+ end
1707
+
1708
+ #-----------------------------------------------------------------
1709
+ # PARSERS: AvailabilityZones
1710
+ #-----------------------------------------------------------------
1711
+
1712
+ class QEc2DescribeAvailabilityZonesParser < AwsParser #:nodoc:
1713
+ def tagstart(name, attributes)
1714
+ @zone = {} if name == 'item'
1715
+ end
1716
+ def tagend(name)
1717
+ case name
1718
+ when 'regionName' then @zone[:region_name] = @text
1719
+ when 'zoneName' then @zone[:zone_name] = @text
1720
+ when 'zoneState' then @zone[:zone_state] = @text
1721
+ when 'item' then @result << @zone
1722
+ end
1723
+ end
1724
+ def reset
1725
+ @result = []
1726
+ end
1727
+ end
1728
+
1729
+ #-----------------------------------------------------------------
1730
+ # PARSERS: Regions
1731
+ #-----------------------------------------------------------------
1732
+
1733
+ class QEc2DescribeRegionsParser < AwsParser #:nodoc:
1734
+ def tagend(name)
1735
+ @result << @text if name == 'regionName'
1736
+ end
1737
+ def reset
1738
+ @result = []
1739
+ end
1740
+ end
1741
+
1742
+ #-----------------------------------------------------------------
1743
+ # PARSERS: EBS - Volumes
1744
+ #-----------------------------------------------------------------
1745
+
1746
+ class QEc2CreateVolumeParser < AwsParser #:nodoc:
1747
+ def tagend(name)
1748
+ case name
1749
+ when 'volumeId' then @result[:aws_id] = @text
1750
+ when 'status' then @result[:aws_status] = @text
1751
+ when 'createTime' then @result[:aws_created_at] = Time.parse(@text)
1752
+ when 'size' then @result[:aws_size] = @text.to_i ###
1753
+ when 'snapshotId' then @result[:snapshot_id] = @text.blank? ? nil : @text ###
1754
+ when 'availabilityZone' then @result[:zone] = @text ###
1755
+ end
1756
+ end
1757
+ def reset
1758
+ @result = {}
1759
+ end
1760
+ end
1761
+
1762
+ class QEc2AttachAndDetachVolumeParser < AwsParser #:nodoc:
1763
+ def tagend(name)
1764
+ case name
1765
+ when 'volumeId' then @result[:aws_id] = @text
1766
+ when 'instanceId' then @result[:aws_instance_id] = @text
1767
+ when 'device' then @result[:aws_device] = @text
1768
+ when 'status' then @result[:aws_attachment_status] = @text
1769
+ when 'attachTime' then @result[:aws_attached_at] = Time.parse(@text)
1770
+ end
1771
+ end
1772
+ def reset
1773
+ @result = {}
1774
+ end
1775
+ end
1776
+
1777
+ class QEc2DescribeVolumesParser < AwsParser #:nodoc:
1778
+ def tagstart(name, attributes)
1779
+ case name
1780
+ when 'item'
1781
+ case @xmlpath
1782
+ when 'DescribeVolumesResponse/volumeSet' then @volume = {}
1783
+ end
1784
+ end
1785
+ end
1786
+ def tagend(name)
1787
+ case name
1788
+ when 'volumeId'
1789
+ case @xmlpath
1790
+ when 'DescribeVolumesResponse/volumeSet/item' then @volume[:aws_id] = @text
1791
+ end
1792
+ when 'status'
1793
+ case @xmlpath
1794
+ when 'DescribeVolumesResponse/volumeSet/item' then @volume[:aws_status] = @text
1795
+ when 'DescribeVolumesResponse/volumeSet/item/attachmentSet/item' then @volume[:aws_attachment_status] = @text
1796
+ end
1797
+ when 'size' then @volume[:aws_size] = @text.to_i
1798
+ when 'createTime' then @volume[:aws_created_at] = Time.parse(@text)
1799
+ when 'instanceId' then @volume[:aws_instance_id] = @text
1800
+ when 'device' then @volume[:aws_device] = @text
1801
+ when 'attachTime' then @volume[:aws_attached_at] = Time.parse(@text)
1802
+ when 'snapshotId' then @volume[:snapshot_id] = @text.blank? ? nil : @text
1803
+ when 'availabilityZone' then @volume[:zone] = @text
1804
+ when 'item'
1805
+ case @xmlpath
1806
+ when 'DescribeVolumesResponse/volumeSet' then @result << @volume
1807
+ end
1808
+ end
1809
+ end
1810
+ def reset
1811
+ @result = []
1812
+ end
1813
+ end
1814
+
1815
+ #-----------------------------------------------------------------
1816
+ # PARSERS: EBS - Snapshots
1817
+ #-----------------------------------------------------------------
1818
+
1819
+ class QEc2DescribeSnapshotsParser < AwsParser #:nodoc:
1820
+ def tagstart(name, attributes)
1821
+ @snapshot = {} if name == 'item'
1822
+ end
1823
+ def tagend(name)
1824
+ case name
1825
+ when 'volumeId' then @snapshot[:aws_volume_id] = @text
1826
+ when 'snapshotId' then @snapshot[:aws_id] = @text
1827
+ when 'status' then @snapshot[:aws_status] = @text
1828
+ when 'startTime' then @snapshot[:aws_started_at] = Time.parse(@text)
1829
+ when 'progress' then @snapshot[:aws_progress] = @text
1830
+ when 'item' then @result << @snapshot
1831
+ end
1832
+ end
1833
+ def reset
1834
+ @result = []
1835
+ end
1836
+ end
1837
+
1838
+ class QEc2CreateSnapshotParser < AwsParser #:nodoc:
1839
+ def tagend(name)
1840
+ case name
1841
+ when 'volumeId' then @result[:aws_volume_id] = @text
1842
+ when 'snapshotId' then @result[:aws_id] = @text
1843
+ when 'status' then @result[:aws_status] = @text
1844
+ when 'startTime' then @result[:aws_started_at] = Time.parse(@text)
1845
+ when 'progress' then @result[:aws_progress] = @text
1846
+ end
1847
+ end
1848
+ def reset
1849
+ @result = {}
1850
+ end
1851
+ end
1852
+
1853
+ #-----------------------------------------------------------------
1854
+ # PARSERS: Tags
1855
+ #-----------------------------------------------------------------
1856
+
1857
+ class QEc2DescribeTagsParser < AwsParser #:nodoc:
1858
+ def tagstart(name, attributes)
1859
+ @tag = {} if name == 'item'
1860
+ end
1861
+ def tagend(name)
1862
+ case name
1863
+ when 'resourceId' then @tag[:aws_resource_id] = @text
1864
+ when 'resourceType' then @tag[:aws_resource_type] = @text
1865
+ when 'key' then @tag[:aws_key] = @text
1866
+ when 'value' then @tag[:aws_value] = @text
1867
+ when 'item' then @result << @tag
1868
+ end
1869
+ end
1870
+ def reset
1871
+ @result = []
1872
+ end
1873
+ end
1874
+
1875
+ end
1876
+
1877
+ end
1878
+
1879
+