cmeiklejohn-aws 2.3.8

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,1772 @@
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 = "2009-08-15"
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 = ['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_DEFAULT_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
+ request_hash['ImageType'] = image_type if image_type
190
+ link = generate_request("DescribeImages", request_hash)
191
+ request_cache_or_info cache_for, link, QEc2DescribeImagesParser, @@bench, cache_for
192
+ rescue Exception
193
+ on_exception
194
+ end
195
+
196
+ # Retrieve a list of images. Returns array of hashes describing the images or an exception:
197
+ # +image_type+ = 'machine' || 'kernel' || 'ramdisk'
198
+ #
199
+ # ec2.describe_images #=>
200
+ # [{:aws_owner => "522821470517",
201
+ # :aws_id => "ami-e4b6538d",
202
+ # :aws_state => "available",
203
+ # :aws_location => "marcins_cool_public_images/ubuntu-6.10.manifest.xml",
204
+ # :aws_is_public => true,
205
+ # :aws_architecture => "i386",
206
+ # :aws_image_type => "machine"},
207
+ # {...},
208
+ # {...} ]
209
+ #
210
+ # If +list+ param is set, then retrieve information about the listed images only:
211
+ #
212
+ # ec2.describe_images(['ami-e4b6538d']) #=>
213
+ # [{:aws_owner => "522821470517",
214
+ # :aws_id => "ami-e4b6538d",
215
+ # :aws_state => "available",
216
+ # :aws_location => "marcins_cool_public_images/ubuntu-6.10.manifest.xml",
217
+ # :aws_is_public => true,
218
+ # :aws_architecture => "i386",
219
+ # :aws_image_type => "machine"}]
220
+ #
221
+ def describe_images(list=[], image_type=nil)
222
+ list = list.to_a
223
+ cache_for = list.empty? && !image_type ? :describe_images : nil
224
+ ec2_describe_images({ 'ImageId' => list }, image_type, cache_for)
225
+ end
226
+
227
+ #
228
+ # Example:
229
+ #
230
+ # ec2.describe_images_by_owner('522821470517')
231
+ # ec2.describe_images_by_owner('self')
232
+ #
233
+ def describe_images_by_owner(list=['self'], image_type=nil)
234
+ list = list.to_a
235
+ cache_for = list==['self'] && !image_type ? :describe_images_by_owner : nil
236
+ ec2_describe_images({ 'Owner' => list }, image_type, cache_for)
237
+ end
238
+
239
+ #
240
+ # Example:
241
+ #
242
+ # ec2.describe_images_by_executable_by('522821470517')
243
+ # ec2.describe_images_by_executable_by('self')
244
+ # ec2.describe_images_by_executable_by('all')
245
+ #
246
+ def describe_images_by_executable_by(list=['self'], image_type=nil)
247
+ list = list.to_a
248
+ cache_for = list==['self'] && !image_type ? :describe_images_by_executable_by : nil
249
+ ec2_describe_images({ 'ExecutableBy' => list }, image_type, cache_for)
250
+ end
251
+
252
+
253
+ # Register new image at Amazon.
254
+ # Returns new image id or an exception.
255
+ #
256
+ # ec2.register_image('bucket/key/manifest') #=> 'ami-e444444d'
257
+ #
258
+ def register_image(image_location)
259
+ link = generate_request("RegisterImage",
260
+ 'ImageLocation' => image_location.to_s)
261
+ request_info(link, QEc2RegisterImageParser.new(:logger => @logger))
262
+ rescue Exception
263
+ on_exception
264
+ end
265
+
266
+ # Deregister image at Amazon. Returns +true+ or an exception.
267
+ #
268
+ # ec2.deregister_image('ami-e444444d') #=> true
269
+ #
270
+ def deregister_image(image_id)
271
+ link = generate_request("DeregisterImage",
272
+ 'ImageId' => image_id.to_s)
273
+ request_info(link, RightBoolResponseParser.new(:logger => @logger))
274
+ rescue Exception
275
+ on_exception
276
+ end
277
+
278
+
279
+ # Describe image attributes. Currently 'launchPermission', 'productCodes', 'kernel', 'ramdisk' and 'blockDeviceMapping' are supported.
280
+ #
281
+ # ec2.describe_image_attribute('ami-e444444d') #=> {:groups=>["all"], :users=>["000000000777"]}
282
+ #
283
+ def describe_image_attribute(image_id, attribute='launchPermission')
284
+ link = generate_request("DescribeImageAttribute",
285
+ 'ImageId' => image_id,
286
+ 'Attribute' => attribute)
287
+ request_info(link, QEc2DescribeImageAttributeParser.new(:logger => @logger))
288
+ rescue Exception
289
+ on_exception
290
+ end
291
+
292
+ # Reset image attribute. Currently, only 'launchPermission' is supported. Returns +true+ or an exception.
293
+ #
294
+ # ec2.reset_image_attribute('ami-e444444d') #=> true
295
+ #
296
+ def reset_image_attribute(image_id, attribute='launchPermission')
297
+ link = generate_request("ResetImageAttribute",
298
+ 'ImageId' => image_id,
299
+ 'Attribute' => attribute)
300
+ request_info(link, RightBoolResponseParser.new(:logger => @logger))
301
+ rescue Exception
302
+ on_exception
303
+ end
304
+
305
+ # Modify an image's attributes. It is recommended that you use
306
+ # modify_image_launch_perm_add_users, modify_image_launch_perm_remove_users, etc.
307
+ # instead of modify_image_attribute because the signature of
308
+ # modify_image_attribute may change with EC2 service changes.
309
+ #
310
+ # attribute : currently, only 'launchPermission' is supported.
311
+ # operation_type : currently, only 'add' & 'remove' are supported.
312
+ # vars:
313
+ # :user_group : currently, only 'all' is supported.
314
+ # :user_id
315
+ # :product_code
316
+ def modify_image_attribute(image_id, attribute, operation_type = nil, vars = {})
317
+ params = {'ImageId' => image_id,
318
+ 'Attribute' => attribute}
319
+ params['OperationType'] = operation_type if operation_type
320
+ params.update(hash_params('UserId', vars[:user_id].to_a)) if vars[:user_id]
321
+ params.update(hash_params('UserGroup', vars[:user_group].to_a)) if vars[:user_group]
322
+ params.update(hash_params('ProductCode', vars[:product_code])) if vars[:product_code]
323
+ link = generate_request("ModifyImageAttribute", params)
324
+ request_info(link, RightBoolResponseParser.new(:logger => @logger))
325
+ rescue Exception
326
+ on_exception
327
+ end
328
+
329
+ # Grant image launch permissions to users.
330
+ # Parameter +userId+ is a list of user AWS account ids.
331
+ # Returns +true+ or an exception.
332
+ #
333
+ # ec2.modify_image_launch_perm_add_users('ami-e444444d',['000000000777','000000000778']) #=> true
334
+ def modify_image_launch_perm_add_users(image_id, user_id=[])
335
+ modify_image_attribute(image_id, 'launchPermission', 'add', :user_id => user_id.to_a)
336
+ end
337
+
338
+ # Revokes image launch permissions for users. +userId+ is a list of users AWS accounts ids. Returns +true+ or an exception.
339
+ #
340
+ # ec2.modify_image_launch_perm_remove_users('ami-e444444d',['000000000777','000000000778']) #=> true
341
+ #
342
+ def modify_image_launch_perm_remove_users(image_id, user_id=[])
343
+ modify_image_attribute(image_id, 'launchPermission', 'remove', :user_id => user_id.to_a)
344
+ end
345
+
346
+ # Add image launch permissions for users groups (currently only 'all' is supported, which gives public launch permissions).
347
+ # Returns +true+ or an exception.
348
+ #
349
+ # ec2.modify_image_launch_perm_add_groups('ami-e444444d') #=> true
350
+ #
351
+ def modify_image_launch_perm_add_groups(image_id, user_group=['all'])
352
+ modify_image_attribute(image_id, 'launchPermission', 'add', :user_group => user_group.to_a)
353
+ end
354
+
355
+ # Remove image launch permissions for users groups (currently only 'all' is supported, which gives public launch permissions).
356
+ #
357
+ # ec2.modify_image_launch_perm_remove_groups('ami-e444444d') #=> true
358
+ #
359
+ def modify_image_launch_perm_remove_groups(image_id, user_group=['all'])
360
+ modify_image_attribute(image_id, 'launchPermission', 'remove', :user_group => user_group.to_a)
361
+ end
362
+
363
+ # Add product code to image
364
+ #
365
+ # ec2.modify_image_product_code('ami-e444444d','0ABCDEF') #=> true
366
+ #
367
+ def modify_image_product_code(image_id, product_code=[])
368
+ modify_image_attribute(image_id, 'productCodes', nil, :product_code => product_code.to_a)
369
+ end
370
+
371
+ #-----------------------------------------------------------------
372
+ # Instances
373
+ #-----------------------------------------------------------------
374
+
375
+ def get_desc_instances(instances) # :nodoc:
376
+ result = []
377
+ instances.each do |reservation|
378
+ reservation[:instances_set].each do |instance|
379
+ # Parse and remove timestamp from the reason string. The timestamp is of
380
+ # the request, not when EC2 took action, thus confusing & useless...
381
+ instance[:aws_reason] = instance[:aws_reason].sub(/\(\d[^)]*GMT\) */, '')
382
+ instance[:aws_owner] = reservation[:aws_owner]
383
+ instance[:aws_reservation_id] = reservation[:aws_reservation_id]
384
+ instance[:aws_groups] = reservation[:aws_groups]
385
+ result << instance
386
+ end
387
+ end
388
+ result
389
+ rescue Exception
390
+ on_exception
391
+ end
392
+
393
+ # Retrieve information about EC2 instances. If +list+ is omitted then returns the
394
+ # list of all instances.
395
+ #
396
+ # ec2.describe_instances #=>
397
+ # [{:aws_image_id => "ami-e444444d",
398
+ # :aws_reason => "",
399
+ # :aws_state_code => "16",
400
+ # :aws_owner => "000000000888",
401
+ # :aws_instance_id => "i-123f1234",
402
+ # :aws_reservation_id => "r-aabbccdd",
403
+ # :aws_state => "running",
404
+ # :dns_name => "domU-12-34-67-89-01-C9.usma2.compute.amazonaws.com",
405
+ # :ssh_key_name => "staging",
406
+ # :aws_groups => ["default"],
407
+ # :private_dns_name => "domU-12-34-67-89-01-C9.usma2.compute.amazonaws.com",
408
+ # :aws_instance_type => "m1.small",
409
+ # :aws_launch_time => "2008-1-1T00:00:00.000Z"},
410
+ # :aws_availability_zone => "us-east-1b",
411
+ # :aws_kernel_id => "aki-ba3adfd3",
412
+ # :aws_ramdisk_id => "ari-badbad00",
413
+ # :monitoring_state => ...,
414
+ # ..., {...}]
415
+ #
416
+ def describe_instances(list=[])
417
+ link = generate_request("DescribeInstances", hash_params('InstanceId',list.to_a))
418
+ request_cache_or_info(:describe_instances, link, QEc2DescribeInstancesParser, @@bench, list.blank?) do |parser|
419
+ get_desc_instances(parser.result)
420
+ end
421
+ rescue Exception
422
+ on_exception
423
+ end
424
+
425
+ # Return the product code attached to instance or +nil+ otherwise.
426
+ #
427
+ # ec2.confirm_product_instance('ami-e444444d','12345678') #=> nil
428
+ # ec2.confirm_product_instance('ami-e444444d','00001111') #=> "000000000888"
429
+ #
430
+ def confirm_product_instance(instance, product_code)
431
+ link = generate_request("ConfirmProductInstance", { 'ProductCode' => product_code,
432
+ 'InstanceId' => instance })
433
+ request_info(link, QEc2ConfirmProductInstanceParser.new(:logger => @logger))
434
+ end
435
+
436
+ # Launch new EC2 instances. Returns a list of launched instances or an exception.
437
+ #
438
+ # ec2.run_instances('ami-e444444d',1,1,['my_awesome_group'],'my_awesome_key', 'Woohoo!!!', 'public') #=>
439
+ # [{:aws_image_id => "ami-e444444d",
440
+ # :aws_reason => "",
441
+ # :aws_state_code => "0",
442
+ # :aws_owner => "000000000888",
443
+ # :aws_instance_id => "i-123f1234",
444
+ # :aws_reservation_id => "r-aabbccdd",
445
+ # :aws_state => "pending",
446
+ # :dns_name => "",
447
+ # :ssh_key_name => "my_awesome_key",
448
+ # :aws_groups => ["my_awesome_group"],
449
+ # :private_dns_name => "",
450
+ # :aws_instance_type => "m1.small",
451
+ # :aws_launch_time => "2008-1-1T00:00:00.000Z"
452
+ # :aws_ramdisk_id => "ari-8605e0ef"
453
+ # :aws_kernel_id => "aki-9905e0f0",
454
+ # :ami_launch_index => "0",
455
+ # :aws_availability_zone => "us-east-1b"
456
+ # }]
457
+ #
458
+ def run_instances(image_id, min_count, max_count, group_ids, key_name, user_data='',
459
+ addressing_type = nil, instance_type = nil,
460
+ kernel_id = nil, ramdisk_id = nil, availability_zone = nil,
461
+ block_device_mappings = nil)
462
+ launch_instances(image_id, { :min_count => min_count,
463
+ :max_count => max_count,
464
+ :user_data => user_data,
465
+ :group_ids => group_ids,
466
+ :key_name => key_name,
467
+ :instance_type => instance_type,
468
+ :addressing_type => addressing_type,
469
+ :kernel_id => kernel_id,
470
+ :ramdisk_id => ramdisk_id,
471
+ :availability_zone => availability_zone,
472
+ :block_device_mappings => block_device_mappings
473
+ })
474
+ end
475
+
476
+
477
+ # Launch new EC2 instances. Returns a list of launched instances or an exception.
478
+ #
479
+ # +lparams+ keys (default values in parenthesis):
480
+ # :min_count fixnum, (1)
481
+ # :max_count fixnum, (1)
482
+ # :group_ids array or string ([] == 'default')
483
+ # :instance_type string (DEFAULT_INSTACE_TYPE)
484
+ # :addressing_type string (DEFAULT_ADDRESSING_TYPE
485
+ # :key_name string
486
+ # :kernel_id string
487
+ # :ramdisk_id string
488
+ # :availability_zone string
489
+ # :block_device_mappings string
490
+ # :user_data string
491
+ # :monitoring_enabled boolean (default=false)
492
+ #
493
+ # ec2.launch_instances('ami-e444444d', :group_ids => 'my_awesome_group',
494
+ # :user_data => "Woohoo!!!",
495
+ # :addressing_type => "public",
496
+ # :key_name => "my_awesome_key",
497
+ # :availability_zone => "us-east-1c") #=>
498
+ # [{:aws_image_id => "ami-e444444d",
499
+ # :aws_reason => "",
500
+ # :aws_state_code => "0",
501
+ # :aws_owner => "000000000888",
502
+ # :aws_instance_id => "i-123f1234",
503
+ # :aws_reservation_id => "r-aabbccdd",
504
+ # :aws_state => "pending",
505
+ # :dns_name => "",
506
+ # :ssh_key_name => "my_awesome_key",
507
+ # :aws_groups => ["my_awesome_group"],
508
+ # :private_dns_name => "",
509
+ # :aws_instance_type => "m1.small",
510
+ # :aws_launch_time => "2008-1-1T00:00:00.000Z",
511
+ # :aws_ramdisk_id => "ari-8605e0ef"
512
+ # :aws_kernel_id => "aki-9905e0f0",
513
+ # :ami_launch_index => "0",
514
+ # :aws_availability_zone => "us-east-1c"
515
+ # }]
516
+ #
517
+ def launch_instances(image_id, lparams={})
518
+ @logger.info("Launching instance of image #{image_id} for #{@aws_access_key_id}, " +
519
+ "key: #{lparams[:key_name]}, groups: #{(lparams[:group_ids]).to_a.join(',')}")
520
+ # careful: keyName and securityGroups may be nil
521
+ params = hash_params('SecurityGroup', lparams[:group_ids].to_a)
522
+ params.update( {'ImageId' => image_id,
523
+ 'MinCount' => (lparams[:min_count] || 1).to_s,
524
+ 'MaxCount' => (lparams[:max_count] || 1).to_s,
525
+ 'AddressingType' => lparams[:addressing_type] || DEFAULT_ADDRESSING_TYPE,
526
+ 'InstanceType' => lparams[:instance_type] || DEFAULT_INSTANCE_TYPE })
527
+ # optional params
528
+ params['KeyName'] = lparams[:key_name] unless lparams[:key_name].blank?
529
+ params['KernelId'] = lparams[:kernel_id] unless lparams[:kernel_id].blank?
530
+ params['RamdiskId'] = lparams[:ramdisk_id] unless lparams[:ramdisk_id].blank?
531
+ params['Placement.AvailabilityZone'] = lparams[:availability_zone] unless lparams[:availability_zone].blank?
532
+ params['BlockDeviceMappings'] = lparams[:block_device_mappings] unless lparams[:block_device_mappings].blank?
533
+ params['Monitoring.Enabled'] = lparams[:monitoring_enabled] unless lparams[:monitoring_enabled].blank?
534
+ unless lparams[:user_data].blank?
535
+ lparams[:user_data].strip!
536
+ # Do not use CGI::escape(encode64(...)) as it is done in Amazons EC2 library.
537
+ # Amazon 169.254.169.254 does not like escaped symbols!
538
+ # And it doesn't like "\n" inside of encoded string! Grrr....
539
+ # Otherwise, some of UserData symbols will be lost...
540
+ params['UserData'] = Base64.encode64(lparams[:user_data]).delete("\n").strip unless lparams[:user_data].blank?
541
+ end
542
+ link = generate_request("RunInstances", params)
543
+ #debugger
544
+ instances = request_info(link, QEc2DescribeInstancesParser.new(:logger => @logger))
545
+ get_desc_instances(instances)
546
+ rescue Exception
547
+ on_exception
548
+ end
549
+
550
+ def monitor_instances(list=[])
551
+ link = generate_request("MonitorInstances", hash_params('InstanceId',list.to_a))
552
+ request_info(link, QEc2TerminateInstancesParser.new(:logger => @logger))
553
+ rescue Exception
554
+ on_exception
555
+ end
556
+
557
+ # Terminates EC2 instances. Returns a list of termination params or an exception.
558
+ #
559
+ # ec2.terminate_instances(['i-f222222d','i-f222222e']) #=>
560
+ # [{:aws_shutdown_state => "shutting-down",
561
+ # :aws_instance_id => "i-f222222d",
562
+ # :aws_shutdown_state_code => 32,
563
+ # :aws_prev_state => "running",
564
+ # :aws_prev_state_code => 16},
565
+ # {:aws_shutdown_state => "shutting-down",
566
+ # :aws_instance_id => "i-f222222e",
567
+ # :aws_shutdown_state_code => 32,
568
+ # :aws_prev_state => "running",
569
+ # :aws_prev_state_code => 16}]
570
+ #
571
+ def terminate_instances(list=[])
572
+ link = generate_request("TerminateInstances", hash_params('InstanceId',list.to_a))
573
+ request_info(link, QEc2TerminateInstancesParser.new(:logger => @logger))
574
+ rescue Exception
575
+ on_exception
576
+ end
577
+
578
+ # Retreive EC2 instance OS logs. Returns a hash of data or an exception.
579
+ #
580
+ # ec2.get_console_output('i-f222222d') =>
581
+ # {:aws_instance_id => 'i-f222222d',
582
+ # :aws_timestamp => "2007-05-23T14:36:07.000-07:00",
583
+ # :timestamp => Wed May 23 21:36:07 UTC 2007, # Time instance
584
+ # :aws_output => "Linux version 2.6.16-xenU (builder@patchbat.amazonsa) (gcc version 4.0.1 20050727 ..."
585
+ def get_console_output(instance_id)
586
+ link = generate_request("GetConsoleOutput", { 'InstanceId.1' => instance_id })
587
+ request_info(link, QEc2GetConsoleOutputParser.new(:logger => @logger))
588
+ rescue Exception
589
+ on_exception
590
+ end
591
+
592
+ # Reboot an EC2 instance. Returns +true+ or an exception.
593
+ #
594
+ # ec2.reboot_instances(['i-f222222d','i-f222222e']) #=> true
595
+ #
596
+ def reboot_instances(list)
597
+ link = generate_request("RebootInstances", hash_params('InstanceId', list.to_a))
598
+ request_info(link, RightBoolResponseParser.new(:logger => @logger))
599
+ rescue Exception
600
+ on_exception
601
+ end
602
+
603
+ #-----------------------------------------------------------------
604
+ # Instances: Windows addons
605
+ #-----------------------------------------------------------------
606
+
607
+ # Get initial Windows Server setup password from an instance console output.
608
+ #
609
+ # my_awesome_key = ec2.create_key_pair('my_awesome_key') #=>
610
+ # {:aws_key_name => "my_awesome_key",
611
+ # :aws_fingerprint => "01:02:03:f4:25:e6:97:e8:9b:02:1a:26:32:4e:58:6b:7a:8c:9f:03",
612
+ # :aws_material => "-----BEGIN RSA PRIVATE KEY-----\nMIIEpQIBAAK...Q8MDrCbuQ=\n-----END RSA PRIVATE KEY-----"}
613
+ #
614
+ # my_awesome_instance = ec2.run_instances('ami-a000000a',1,1,['my_awesome_group'],'my_awesome_key', 'WindowsInstance!!!') #=>
615
+ # [{:aws_image_id => "ami-a000000a",
616
+ # :aws_instance_id => "i-12345678",
617
+ # ...
618
+ # :aws_availability_zone => "us-east-1b"
619
+ # }]
620
+ #
621
+ # # wait until instance enters 'operational' state and get it's initial password
622
+ #
623
+ # puts ec2.get_initial_password(my_awesome_instance[:aws_instance_id], my_awesome_key[:aws_material]) #=> "MhjWcgZuY6"
624
+ #
625
+ def get_initial_password(instance_id, private_key)
626
+ console_output = get_console_output(instance_id)
627
+ crypted_password = console_output[:aws_output][%r{<Password>(.+)</Password>}m] && $1
628
+ unless crypted_password
629
+ raise AwsError.new("Initial password was not found in console output for #{instance_id}")
630
+ else
631
+ OpenSSL::PKey::RSA.new(private_key).private_decrypt(Base64.decode64(crypted_password))
632
+ end
633
+ rescue Exception
634
+ on_exception
635
+ end
636
+
637
+ # Bundle a Windows image.
638
+ # Internally, it queues the bundling task and shuts down the instance.
639
+ # It then takes a snapshot of the Windows volume bundles it, and uploads it to
640
+ # S3. After bundling completes, Aws::Ec2#register_image may be used to
641
+ # register the new Windows AMI for subsequent launches.
642
+ #
643
+ # ec2.bundle_instance('i-e3e24e8a', 'my-awesome-bucket', 'my-win-image-1') #=>
644
+ # [{:aws_update_time => "2008-10-16T13:58:25.000Z",
645
+ # :s3_bucket => "kd-win-1",
646
+ # :s3_prefix => "win2pr",
647
+ # :aws_state => "pending",
648
+ # :aws_id => "bun-26a7424f",
649
+ # :aws_instance_id => "i-878a25ee",
650
+ # :aws_start_time => "2008-10-16T13:58:02.000Z"}]
651
+ #
652
+ def bundle_instance(instance_id, s3_bucket, s3_prefix,
653
+ s3_owner_aws_access_key_id=nil, s3_owner_aws_secret_access_key=nil,
654
+ s3_expires = S3Interface::DEFAULT_EXPIRES_AFTER,
655
+ s3_upload_policy='ec2-bundle-read')
656
+ # S3 access and signatures
657
+ s3_owner_aws_access_key_id ||= @aws_access_key_id
658
+ s3_owner_aws_secret_access_key ||= @aws_secret_access_key
659
+ s3_expires = Time.now.utc + s3_expires if s3_expires.is_a?(Fixnum) && (s3_expires < S3Interface::ONE_YEAR_IN_SECONDS)
660
+ # policy
661
+ policy = { 'expiration' => s3_expires.strftime('%Y-%m-%dT%H:%M:%SZ'),
662
+ 'conditions' => [ { 'bucket' => s3_bucket },
663
+ { 'acl' => s3_upload_policy },
664
+ [ 'starts-with', '$key', s3_prefix ] ] }.to_json
665
+ policy64 = Base64.encode64(policy).gsub("\n","")
666
+ signed_policy64 = AwsUtils.sign(s3_owner_aws_secret_access_key, policy64)
667
+ # fill request params
668
+ params = { 'InstanceId' => instance_id,
669
+ 'Storage.S3.AWSAccessKeyId' => s3_owner_aws_access_key_id,
670
+ 'Storage.S3.UploadPolicy' => policy64,
671
+ 'Storage.S3.UploadPolicySignature' => signed_policy64,
672
+ 'Storage.S3.Bucket' => s3_bucket,
673
+ 'Storage.S3.Prefix' => s3_prefix,
674
+ }
675
+ link = generate_request("BundleInstance", params)
676
+ request_info(link, QEc2BundleInstanceParser.new)
677
+ rescue Exception
678
+ on_exception
679
+ end
680
+
681
+ # Describe the status of the Windows AMI bundlings.
682
+ # If +list+ is omitted the returns the whole list of tasks.
683
+ #
684
+ # ec2.describe_bundle_tasks(['bun-4fa74226']) #=>
685
+ # [{:s3_bucket => "my-awesome-bucket"
686
+ # :aws_id => "bun-0fa70206",
687
+ # :s3_prefix => "win1pr",
688
+ # :aws_start_time => "2008-10-14T16:27:57.000Z",
689
+ # :aws_update_time => "2008-10-14T16:37:10.000Z",
690
+ # :aws_error_code => "Client.S3Error",
691
+ # :aws_error_message =>
692
+ # "AccessDenied(403)- Invalid according to Policy: Policy Condition failed: [\"eq\", \"$acl\", \"aws-exec-read\"]",
693
+ # :aws_state => "failed",
694
+ # :aws_instance_id => "i-e3e24e8a"}]
695
+ #
696
+ def describe_bundle_tasks(list=[])
697
+ link = generate_request("DescribeBundleTasks", hash_params('BundleId', list.to_a))
698
+ request_info(link, QEc2DescribeBundleTasksParser.new)
699
+ rescue Exception
700
+ on_exception
701
+ end
702
+
703
+ # Cancel an in‐progress or pending bundle task by id.
704
+ #
705
+ # ec2.cancel_bundle_task('bun-73a7421a') #=>
706
+ # [{:s3_bucket => "my-awesome-bucket"
707
+ # :aws_id => "bun-0fa70206",
708
+ # :s3_prefix => "win02",
709
+ # :aws_start_time => "2008-10-14T13:00:29.000Z",
710
+ # :aws_error_message => "User has requested bundling operation cancellation",
711
+ # :aws_state => "failed",
712
+ # :aws_update_time => "2008-10-14T13:01:31.000Z",
713
+ # :aws_error_code => "Client.Cancelled",
714
+ # :aws_instance_id => "i-e3e24e8a"}
715
+ #
716
+ def cancel_bundle_task(bundle_id)
717
+ link = generate_request("CancelBundleTask", { 'BundleId' => bundle_id })
718
+ request_info(link, QEc2BundleInstanceParser.new)
719
+ rescue Exception
720
+ on_exception
721
+ end
722
+
723
+ #-----------------------------------------------------------------
724
+ # Security groups
725
+ #-----------------------------------------------------------------
726
+
727
+ # Retrieve Security Group information. If +list+ is omitted the returns the whole list of groups.
728
+ #
729
+ # ec2.describe_security_groups #=>
730
+ # [{:aws_group_name => "default-1",
731
+ # :aws_owner => "000000000888",
732
+ # :aws_description => "Default allowing SSH, HTTP, and HTTPS ingress",
733
+ # :aws_perms =>
734
+ # [{:owner => "000000000888", :group => "default"},
735
+ # {:owner => "000000000888", :group => "default-1"},
736
+ # {:to_port => "-1", :protocol => "icmp", :from_port => "-1", :cidr_ips => "0.0.0.0/0"},
737
+ # {:to_port => "22", :protocol => "tcp", :from_port => "22", :cidr_ips => "0.0.0.0/0"},
738
+ # {:to_port => "80", :protocol => "tcp", :from_port => "80", :cidr_ips => "0.0.0.0/0"},
739
+ # {:to_port => "443", :protocol => "tcp", :from_port => "443", :cidr_ips => "0.0.0.0/0"}]},
740
+ # ..., {...}]
741
+ #
742
+ def describe_security_groups(list=[])
743
+ link = generate_request("DescribeSecurityGroups", hash_params('GroupName',list.to_a))
744
+ request_cache_or_info( :describe_security_groups, link, QEc2DescribeSecurityGroupsParser, @@bench, list.blank?) do |parser|
745
+ result = []
746
+ parser.result.each do |item|
747
+ perms = []
748
+ item.ipPermissions.each do |perm|
749
+ perm.groups.each do |ngroup|
750
+ perms << {:group => ngroup.groupName,
751
+ :owner => ngroup.userId}
752
+ end
753
+ perm.ipRanges.each do |cidr_ip|
754
+ perms << {:from_port => perm.fromPort,
755
+ :to_port => perm.toPort,
756
+ :protocol => perm.ipProtocol,
757
+ :cidr_ips => cidr_ip}
758
+ end
759
+ end
760
+
761
+ # delete duplication
762
+ perms.each_index do |i|
763
+ (0...i).each do |j|
764
+ if perms[i] == perms[j] then perms[i] = nil; break; end
765
+ end
766
+ end
767
+ perms.compact!
768
+
769
+ result << {:aws_owner => item.ownerId,
770
+ :aws_group_name => item.groupName,
771
+ :aws_description => item.groupDescription,
772
+ :aws_perms => perms}
773
+
774
+ end
775
+ result
776
+ end
777
+ rescue Exception
778
+ on_exception
779
+ end
780
+
781
+ # Create new Security Group. Returns +true+ or an exception.
782
+ #
783
+ # ec2.create_security_group('default-1',"Default allowing SSH, HTTP, and HTTPS ingress") #=> true
784
+ #
785
+ def create_security_group(name, description)
786
+ # EC2 doesn't like an empty description...
787
+ description = " " if description.blank?
788
+ link = generate_request("CreateSecurityGroup",
789
+ 'GroupName' => name.to_s,
790
+ 'GroupDescription' => description.to_s)
791
+ request_info(link, RightBoolResponseParser.new(:logger => @logger))
792
+ rescue Exception
793
+ on_exception
794
+ end
795
+
796
+ # Remove Security Group. Returns +true+ or an exception.
797
+ #
798
+ # ec2.delete_security_group('default-1') #=> true
799
+ #
800
+ def delete_security_group(name)
801
+ link = generate_request("DeleteSecurityGroup",
802
+ 'GroupName' => name.to_s)
803
+ request_info(link, RightBoolResponseParser.new(:logger => @logger))
804
+ rescue Exception
805
+ on_exception
806
+ end
807
+
808
+ # Authorize named ingress for security group. Allows instances that are member of someone
809
+ # else's security group to open connections to instances in my group.
810
+ #
811
+ # ec2.authorize_security_group_named_ingress('my_awesome_group', '7011-0219-8268', 'their_group_name') #=> true
812
+ #
813
+ def authorize_security_group_named_ingress(name, owner, group)
814
+ link = generate_request("AuthorizeSecurityGroupIngress",
815
+ 'GroupName' => name.to_s,
816
+ 'SourceSecurityGroupName' => group.to_s,
817
+ 'SourceSecurityGroupOwnerId' => owner.to_s.gsub(/-/,''))
818
+ request_info(link, RightBoolResponseParser.new(:logger => @logger))
819
+ rescue Exception
820
+ on_exception
821
+ end
822
+
823
+ # Revoke named ingress for security group.
824
+ #
825
+ # ec2.revoke_security_group_named_ingress('my_awesome_group', aws_user_id, 'another_group_name') #=> true
826
+ #
827
+ def revoke_security_group_named_ingress(name, owner, group)
828
+ link = generate_request("RevokeSecurityGroupIngress",
829
+ 'GroupName' => name.to_s,
830
+ 'SourceSecurityGroupName' => group.to_s,
831
+ 'SourceSecurityGroupOwnerId' => owner.to_s.gsub(/-/,''))
832
+ request_info(link, RightBoolResponseParser.new(:logger => @logger))
833
+ rescue Exception
834
+ on_exception
835
+ end
836
+
837
+ # Add permission to a security group. Returns +true+ or an exception. +protocol+ is one of :'tcp'|'udp'|'icmp'.
838
+ #
839
+ # ec2.authorize_security_group_IP_ingress('my_awesome_group', 80, 82, 'udp', '192.168.1.0/8') #=> true
840
+ # ec2.authorize_security_group_IP_ingress('my_awesome_group', -1, -1, 'icmp') #=> true
841
+ #
842
+ def authorize_security_group_IP_ingress(name, from_port, to_port, protocol='tcp', cidr_ip='0.0.0.0/0')
843
+ link = generate_request("AuthorizeSecurityGroupIngress",
844
+ 'GroupName' => name.to_s,
845
+ 'IpProtocol' => protocol.to_s,
846
+ 'FromPort' => from_port.to_s,
847
+ 'ToPort' => to_port.to_s,
848
+ 'CidrIp' => cidr_ip.to_s)
849
+ request_info(link, RightBoolResponseParser.new(:logger => @logger))
850
+ rescue Exception
851
+ on_exception
852
+ end
853
+
854
+ # Remove permission from a security group. Returns +true+ or an exception. +protocol+ is one of :'tcp'|'udp'|'icmp' ('tcp' is default).
855
+ #
856
+ # ec2.revoke_security_group_IP_ingress('my_awesome_group', 80, 82, 'udp', '192.168.1.0/8') #=> true
857
+ #
858
+ def revoke_security_group_IP_ingress(name, from_port, to_port, protocol='tcp', cidr_ip='0.0.0.0/0')
859
+ link = generate_request("RevokeSecurityGroupIngress",
860
+ 'GroupName' => name.to_s,
861
+ 'IpProtocol' => protocol.to_s,
862
+ 'FromPort' => from_port.to_s,
863
+ 'ToPort' => to_port.to_s,
864
+ 'CidrIp' => cidr_ip.to_s)
865
+ request_info(link, RightBoolResponseParser.new(:logger => @logger))
866
+ rescue Exception
867
+ on_exception
868
+ end
869
+
870
+ #-----------------------------------------------------------------
871
+ # Keys
872
+ #-----------------------------------------------------------------
873
+
874
+ # Retrieve a list of SSH keys. Returns an array of keys or an exception. Each key is
875
+ # represented as a two-element hash.
876
+ #
877
+ # ec2.describe_key_pairs #=>
878
+ # [{: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"},
879
+ # {: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"},
880
+ # ..., {...} ]
881
+ #
882
+ def describe_key_pairs(list=[])
883
+ link = generate_request("DescribeKeyPairs", hash_params('KeyName',list.to_a))
884
+ request_cache_or_info :describe_key_pairs, link, QEc2DescribeKeyPairParser, @@bench, list.blank?
885
+ rescue Exception
886
+ on_exception
887
+ end
888
+
889
+ # Create new SSH key. Returns a hash of the key's data or an exception.
890
+ #
891
+ # ec2.create_key_pair('my_awesome_key') #=>
892
+ # {:aws_key_name => "my_awesome_key",
893
+ # :aws_fingerprint => "01:02:03:f4:25:e6:97:e8:9b:02:1a:26:32:4e:58:6b:7a:8c:9f:03",
894
+ # :aws_material => "-----BEGIN RSA PRIVATE KEY-----\nMIIEpQIBAAK...Q8MDrCbuQ=\n-----END RSA PRIVATE KEY-----"}
895
+ #
896
+ def create_key_pair(name)
897
+ link = generate_request("CreateKeyPair",
898
+ 'KeyName' => name.to_s)
899
+ request_info(link, QEc2CreateKeyPairParser.new(:logger => @logger))
900
+ rescue Exception
901
+ on_exception
902
+ end
903
+
904
+ # Delete a key pair. Returns +true+ or an exception.
905
+ #
906
+ # ec2.delete_key_pair('my_awesome_key') #=> true
907
+ #
908
+ def delete_key_pair(name)
909
+ link = generate_request("DeleteKeyPair",
910
+ 'KeyName' => name.to_s)
911
+ request_info(link, RightBoolResponseParser.new(:logger => @logger))
912
+ rescue Exception
913
+ on_exception
914
+ end
915
+
916
+ #-----------------------------------------------------------------
917
+ # Elastic IPs
918
+ #-----------------------------------------------------------------
919
+
920
+ # Acquire a new elastic IP address for use with your account.
921
+ # Returns allocated IP address or an exception.
922
+ #
923
+ # ec2.allocate_address #=> '75.101.154.140'
924
+ #
925
+ def allocate_address
926
+ link = generate_request("AllocateAddress")
927
+ request_info(link, QEc2AllocateAddressParser.new(:logger => @logger))
928
+ rescue Exception
929
+ on_exception
930
+ end
931
+
932
+ # Associate an elastic IP address with an instance.
933
+ # Returns +true+ or an exception.
934
+ #
935
+ # ec2.associate_address('i-d630cbbf', '75.101.154.140') #=> true
936
+ #
937
+ def associate_address(instance_id, public_ip)
938
+ link = generate_request("AssociateAddress",
939
+ "InstanceId" => instance_id.to_s,
940
+ "PublicIp" => public_ip.to_s)
941
+ request_info(link, RightBoolResponseParser.new(:logger => @logger))
942
+ rescue Exception
943
+ on_exception
944
+ end
945
+
946
+ # List elastic IP addresses assigned to your account.
947
+ # Returns an array of 2 keys (:instance_id and :public_ip) hashes:
948
+ #
949
+ # ec2.describe_addresses #=> [{:instance_id=>"i-d630cbbf", :public_ip=>"75.101.154.140"},
950
+ # {:instance_id=>nil, :public_ip=>"75.101.154.141"}]
951
+ #
952
+ # ec2.describe_addresses('75.101.154.140') #=> [{:instance_id=>"i-d630cbbf", :public_ip=>"75.101.154.140"}]
953
+ #
954
+ def describe_addresses(list=[])
955
+ link = generate_request("DescribeAddresses",
956
+ hash_params('PublicIp',list.to_a))
957
+ request_cache_or_info :describe_addresses, link, QEc2DescribeAddressesParser, @@bench, list.blank?
958
+ rescue Exception
959
+ on_exception
960
+ end
961
+
962
+ # Disassociate the specified elastic IP address from the instance to which it is assigned.
963
+ # Returns +true+ or an exception.
964
+ #
965
+ # ec2.disassociate_address('75.101.154.140') #=> true
966
+ #
967
+ def disassociate_address(public_ip)
968
+ link = generate_request("DisassociateAddress",
969
+ "PublicIp" => public_ip.to_s)
970
+ request_info(link, RightBoolResponseParser.new(:logger => @logger))
971
+ rescue Exception
972
+ on_exception
973
+ end
974
+
975
+ # Release an elastic IP address associated with your account.
976
+ # Returns +true+ or an exception.
977
+ #
978
+ # ec2.release_address('75.101.154.140') #=> true
979
+ #
980
+ def release_address(public_ip)
981
+ link = generate_request("ReleaseAddress",
982
+ "PublicIp" => public_ip.to_s)
983
+ request_info(link, RightBoolResponseParser.new(:logger => @logger))
984
+ rescue Exception
985
+ on_exception
986
+ end
987
+
988
+ #-----------------------------------------------------------------
989
+ # Availability zones
990
+ #-----------------------------------------------------------------
991
+
992
+ # Describes availability zones that are currently available to the account and their states.
993
+ # Returns an array of 2 keys (:zone_name and :zone_state) hashes:
994
+ #
995
+ # ec2.describe_availability_zones #=> [{:region_name=>"us-east-1",
996
+ # :zone_name=>"us-east-1a",
997
+ # :zone_state=>"available"}, ... ]
998
+ #
999
+ # ec2.describe_availability_zones('us-east-1c') #=> [{:region_name=>"us-east-1",
1000
+ # :zone_state=>"available",
1001
+ # :zone_name=>"us-east-1c"}]
1002
+ #
1003
+ def describe_availability_zones(list=[])
1004
+ link = generate_request("DescribeAvailabilityZones",
1005
+ hash_params('ZoneName',list.to_a))
1006
+ request_cache_or_info :describe_availability_zones, link, QEc2DescribeAvailabilityZonesParser, @@bench, list.blank?
1007
+ rescue Exception
1008
+ on_exception
1009
+ end
1010
+
1011
+ #-----------------------------------------------------------------
1012
+ # Regions
1013
+ #-----------------------------------------------------------------
1014
+
1015
+ # Describe regions.
1016
+ #
1017
+ # ec2.describe_regions #=> ["eu-west-1", "us-east-1"]
1018
+ #
1019
+ def describe_regions(list=[])
1020
+ link = generate_request("DescribeRegions",
1021
+ hash_params('RegionName',list.to_a))
1022
+ request_cache_or_info :describe_regions, link, QEc2DescribeRegionsParser, @@bench, list.blank?
1023
+ rescue Exception
1024
+ on_exception
1025
+ end
1026
+
1027
+
1028
+ #-----------------------------------------------------------------
1029
+ # EBS: Volumes
1030
+ #-----------------------------------------------------------------
1031
+
1032
+ # Describe all EBS volumes.
1033
+ #
1034
+ # ec2.describe_volumes #=>
1035
+ # [{:aws_size => 94,
1036
+ # :aws_device => "/dev/sdc",
1037
+ # :aws_attachment_status => "attached",
1038
+ # :zone => "merlot",
1039
+ # :snapshot_id => nil,
1040
+ # :aws_attached_at => Wed Jun 18 08:19:28 UTC 2008,
1041
+ # :aws_status => "in-use",
1042
+ # :aws_id => "vol-60957009",
1043
+ # :aws_created_at => Wed Jun 18 08:19:20s UTC 2008,
1044
+ # :aws_instance_id => "i-c014c0a9"},
1045
+ # {:aws_size => 1,
1046
+ # :zone => "merlot",
1047
+ # :snapshot_id => nil,
1048
+ # :aws_status => "available",
1049
+ # :aws_id => "vol-58957031",
1050
+ # :aws_created_at => Wed Jun 18 08:19:21 UTC 2008,}, ... ]
1051
+ #
1052
+ def describe_volumes(list=[])
1053
+ link = generate_request("DescribeVolumes",
1054
+ hash_params('VolumeId',list.to_a))
1055
+ request_cache_or_info :describe_volumes, link, QEc2DescribeVolumesParser, @@bench, list.blank?
1056
+ rescue Exception
1057
+ on_exception
1058
+ end
1059
+
1060
+ # Create new EBS volume based on previously created snapshot.
1061
+ # +Size+ in Gigabytes.
1062
+ #
1063
+ # ec2.create_volume('snap-000000', 10, zone) #=>
1064
+ # {:snapshot_id => "snap-e21df98b",
1065
+ # :aws_status => "creating",
1066
+ # :aws_id => "vol-fc9f7a95",
1067
+ # :zone => "merlot",
1068
+ # :aws_created_at => Tue Jun 24 18:13:32 UTC 2008,
1069
+ # :aws_size => 94}
1070
+ #
1071
+ def create_volume(snapshot_id, size, zone)
1072
+ link = generate_request("CreateVolume",
1073
+ "SnapshotId" => snapshot_id.to_s,
1074
+ "Size" => size.to_s,
1075
+ "AvailabilityZone" => zone.to_s )
1076
+ request_info(link, QEc2CreateVolumeParser.new(:logger => @logger))
1077
+ rescue Exception
1078
+ on_exception
1079
+ end
1080
+
1081
+ # Delete the specified EBS volume.
1082
+ # This does not deletes any snapshots created from this volume.
1083
+ #
1084
+ # ec2.delete_volume('vol-b48a6fdd') #=> true
1085
+ #
1086
+ def delete_volume(volume_id)
1087
+ link = generate_request("DeleteVolume",
1088
+ "VolumeId" => volume_id.to_s)
1089
+ request_info(link, RightBoolResponseParser.new(:logger => @logger))
1090
+ rescue Exception
1091
+ on_exception
1092
+ end
1093
+
1094
+ # Attach the specified EBS volume to a specified instance, exposing the
1095
+ # volume using the specified device name.
1096
+ #
1097
+ # ec2.attach_volume('vol-898a6fe0', 'i-7c905415', '/dev/sdh') #=>
1098
+ # { :aws_instance_id => "i-7c905415",
1099
+ # :aws_device => "/dev/sdh",
1100
+ # :aws_status => "attaching",
1101
+ # :aws_attached_at => "2008-03-28T14:14:39.000Z",
1102
+ # :aws_id => "vol-898a6fe0" }
1103
+ #
1104
+ def attach_volume(volume_id, instance_id, device)
1105
+ link = generate_request("AttachVolume",
1106
+ "VolumeId" => volume_id.to_s,
1107
+ "InstanceId" => instance_id.to_s,
1108
+ "Device" => device.to_s)
1109
+ request_info(link, QEc2AttachAndDetachVolumeParser.new(:logger => @logger))
1110
+ rescue Exception
1111
+ on_exception
1112
+ end
1113
+
1114
+ # Detach the specified EBS volume from the instance to which it is attached.
1115
+ #
1116
+ # ec2.detach_volume('vol-898a6fe0') #=>
1117
+ # { :aws_instance_id => "i-7c905415",
1118
+ # :aws_device => "/dev/sdh",
1119
+ # :aws_status => "detaching",
1120
+ # :aws_attached_at => "2008-03-28T14:38:34.000Z",
1121
+ # :aws_id => "vol-898a6fe0"}
1122
+ #
1123
+ def detach_volume(volume_id, instance_id=nil, device=nil, force=nil)
1124
+ hash = { "VolumeId" => volume_id.to_s }
1125
+ hash["InstanceId"] = instance_id.to_s unless instance_id.blank?
1126
+ hash["Device"] = device.to_s unless device.blank?
1127
+ hash["Force"] = 'true' if force
1128
+ #
1129
+ link = generate_request("DetachVolume", hash)
1130
+ request_info(link, QEc2AttachAndDetachVolumeParser.new(:logger => @logger))
1131
+ rescue Exception
1132
+ on_exception
1133
+ end
1134
+
1135
+
1136
+ #-----------------------------------------------------------------
1137
+ # EBS: Snapshots
1138
+ #-----------------------------------------------------------------
1139
+
1140
+ # Describe all EBS snapshots.
1141
+ #
1142
+ # ec2.describe_snapshots #=>
1143
+ # [ { :aws_progress => "100%",
1144
+ # :aws_status => "completed",
1145
+ # :aws_id => "snap-72a5401b",
1146
+ # :aws_volume_id => "vol-5582673c",
1147
+ # :aws_started_at => "2008-02-23T02:50:48.000Z"},
1148
+ # { :aws_progress => "100%",
1149
+ # :aws_status => "completed",
1150
+ # :aws_id => "snap-75a5401c",
1151
+ # :aws_volume_id => "vol-5582673c",
1152
+ # :aws_started_at => "2008-02-23T16:23:19.000Z" },...]
1153
+ #
1154
+ def describe_snapshots(list=[])
1155
+ link = generate_request("DescribeSnapshots",
1156
+ hash_params('SnapshotId',list.to_a))
1157
+ request_cache_or_info :describe_snapshots, link, QEc2DescribeSnapshotsParser, @@bench, list.blank?
1158
+ rescue Exception
1159
+ on_exception
1160
+ end
1161
+
1162
+ # Create a snapshot of specified volume.
1163
+ #
1164
+ # ec2.create_snapshot('vol-898a6fe0') #=>
1165
+ # {:aws_volume_id => "vol-fd9f7a94",
1166
+ # :aws_started_at => Tue Jun 24 18:40:40 UTC 2008,
1167
+ # :aws_progress => "",
1168
+ # :aws_status => "pending",
1169
+ # :aws_id => "snap-d56783bc"}
1170
+ #
1171
+ def create_snapshot(volume_id)
1172
+ link = generate_request("CreateSnapshot",
1173
+ "VolumeId" => volume_id.to_s)
1174
+ request_info(link, QEc2CreateSnapshotParser.new(:logger => @logger))
1175
+ rescue Exception
1176
+ on_exception
1177
+ end
1178
+
1179
+ # Create a snapshot of specified volume, but with the normal retry algorithms disabled.
1180
+ # This method will return immediately upon error. The user can specify connect and read timeouts (in s)
1181
+ # for the connection to AWS. If the user does not specify timeouts, try_create_snapshot uses the default values
1182
+ # in Rightscale::HttpConnection.
1183
+ #
1184
+ # ec2.try_create_snapshot('vol-898a6fe0') #=>
1185
+ # {:aws_volume_id => "vol-fd9f7a94",
1186
+ # :aws_started_at => Tue Jun 24 18:40:40 UTC 2008,
1187
+ # :aws_progress => "",
1188
+ # :aws_status => "pending",
1189
+ # :aws_id => "snap-d56783bc"}
1190
+ #
1191
+ def try_create_snapshot(volume_id, connect_timeout = nil, read_timeout = nil)
1192
+ # For safety in the ensure block...we don't want to restore values
1193
+ # if we never read them in the first place
1194
+ orig_reiteration_time = nil
1195
+ orig_http_params = nil
1196
+
1197
+ orig_reiteration_time = Aws::AWSErrorHandler::reiteration_time
1198
+ Aws::AWSErrorHandler::reiteration_time = 0
1199
+
1200
+ orig_http_params = Rightscale::HttpConnection::params()
1201
+ new_http_params = orig_http_params.dup
1202
+ new_http_params[:http_connection_retry_count] = 0
1203
+ new_http_params[:http_connection_open_timeout] = connect_timeout if !connect_timeout.nil?
1204
+ new_http_params[:http_connection_read_timeout] = read_timeout if !read_timeout.nil?
1205
+ Rightscale::HttpConnection::params = new_http_params
1206
+
1207
+ link = generate_request("CreateSnapshot",
1208
+ "VolumeId" => volume_id.to_s)
1209
+ request_info(link, QEc2CreateSnapshotParser.new(:logger => @logger))
1210
+
1211
+ rescue Exception
1212
+ on_exception
1213
+ ensure
1214
+ Aws::AWSErrorHandler::reiteration_time = orig_reiteration_time if orig_reiteration_time
1215
+ Rightscale::HttpConnection::params = orig_http_params if orig_http_params
1216
+ end
1217
+
1218
+ # Delete the specified snapshot.
1219
+ #
1220
+ # ec2.delete_snapshot('snap-55a5403c') #=> true
1221
+ #
1222
+ def delete_snapshot(snapshot_id)
1223
+ link = generate_request("DeleteSnapshot",
1224
+ "SnapshotId" => snapshot_id.to_s)
1225
+ request_info(link, RightBoolResponseParser.new(:logger => @logger))
1226
+ rescue Exception
1227
+ on_exception
1228
+ end
1229
+
1230
+ #-----------------------------------------------------------------
1231
+ # PARSERS: Boolean Response Parser
1232
+ #-----------------------------------------------------------------
1233
+
1234
+ class RightBoolResponseParser < AwsParser #:nodoc:
1235
+ def tagend(name)
1236
+ @result = @text=='true' ? true : false if name == 'return'
1237
+ end
1238
+ end
1239
+
1240
+ #-----------------------------------------------------------------
1241
+ # PARSERS: Key Pair
1242
+ #-----------------------------------------------------------------
1243
+
1244
+ class QEc2DescribeKeyPairParser < AwsParser #:nodoc:
1245
+ def tagstart(name, attributes)
1246
+ @item = {} if name == 'item'
1247
+ end
1248
+ def tagend(name)
1249
+ case name
1250
+ when 'keyName' then @item[:aws_key_name] = @text
1251
+ when 'keyFingerprint' then @item[:aws_fingerprint] = @text
1252
+ when 'item' then @result << @item
1253
+ end
1254
+ end
1255
+ def reset
1256
+ @result = [];
1257
+ end
1258
+ end
1259
+
1260
+ class QEc2CreateKeyPairParser < AwsParser #:nodoc:
1261
+ def tagstart(name, attributes)
1262
+ @result = {} if name == 'CreateKeyPairResponse'
1263
+ end
1264
+ def tagend(name)
1265
+ case name
1266
+ when 'keyName' then @result[:aws_key_name] = @text
1267
+ when 'keyFingerprint' then @result[:aws_fingerprint] = @text
1268
+ when 'keyMaterial' then @result[:aws_material] = @text
1269
+ end
1270
+ end
1271
+ end
1272
+
1273
+ #-----------------------------------------------------------------
1274
+ # PARSERS: Security Groups
1275
+ #-----------------------------------------------------------------
1276
+
1277
+ class QEc2UserIdGroupPairType #:nodoc:
1278
+ attr_accessor :userId
1279
+ attr_accessor :groupName
1280
+ end
1281
+
1282
+ class QEc2IpPermissionType #:nodoc:
1283
+ attr_accessor :ipProtocol
1284
+ attr_accessor :fromPort
1285
+ attr_accessor :toPort
1286
+ attr_accessor :groups
1287
+ attr_accessor :ipRanges
1288
+ end
1289
+
1290
+ class QEc2SecurityGroupItemType #:nodoc:
1291
+ attr_accessor :groupName
1292
+ attr_accessor :groupDescription
1293
+ attr_accessor :ownerId
1294
+ attr_accessor :ipPermissions
1295
+ end
1296
+
1297
+
1298
+ class QEc2DescribeSecurityGroupsParser < AwsParser #:nodoc:
1299
+ def tagstart(name, attributes)
1300
+ case name
1301
+ when 'item'
1302
+ if @xmlpath=='DescribeSecurityGroupsResponse/securityGroupInfo'
1303
+ @group = QEc2SecurityGroupItemType.new
1304
+ @group.ipPermissions = []
1305
+ elsif @xmlpath=='DescribeSecurityGroupsResponse/securityGroupInfo/item/ipPermissions'
1306
+ @perm = QEc2IpPermissionType.new
1307
+ @perm.ipRanges = []
1308
+ @perm.groups = []
1309
+ elsif @xmlpath=='DescribeSecurityGroupsResponse/securityGroupInfo/item/ipPermissions/item/groups'
1310
+ @sgroup = QEc2UserIdGroupPairType.new
1311
+ end
1312
+ end
1313
+ end
1314
+ def tagend(name)
1315
+ case name
1316
+ when 'ownerId' then @group.ownerId = @text
1317
+ when 'groupDescription' then @group.groupDescription = @text
1318
+ when 'groupName'
1319
+ if @xmlpath=='DescribeSecurityGroupsResponse/securityGroupInfo/item'
1320
+ @group.groupName = @text
1321
+ elsif @xmlpath=='DescribeSecurityGroupsResponse/securityGroupInfo/item/ipPermissions/item/groups/item'
1322
+ @sgroup.groupName = @text
1323
+ end
1324
+ when 'ipProtocol' then @perm.ipProtocol = @text
1325
+ when 'fromPort' then @perm.fromPort = @text
1326
+ when 'toPort' then @perm.toPort = @text
1327
+ when 'userId' then @sgroup.userId = @text
1328
+ when 'cidrIp' then @perm.ipRanges << @text
1329
+ when 'item'
1330
+ if @xmlpath=='DescribeSecurityGroupsResponse/securityGroupInfo/item/ipPermissions/item/groups'
1331
+ @perm.groups << @sgroup
1332
+ elsif @xmlpath=='DescribeSecurityGroupsResponse/securityGroupInfo/item/ipPermissions'
1333
+ @group.ipPermissions << @perm
1334
+ elsif @xmlpath=='DescribeSecurityGroupsResponse/securityGroupInfo'
1335
+ @result << @group
1336
+ end
1337
+ end
1338
+ end
1339
+ def reset
1340
+ @result = []
1341
+ end
1342
+ end
1343
+
1344
+ #-----------------------------------------------------------------
1345
+ # PARSERS: Images
1346
+ #-----------------------------------------------------------------
1347
+
1348
+ class QEc2DescribeImagesParser < AwsParser #:nodoc:
1349
+ def tagstart(name, attributes)
1350
+ if name == 'item' && @xmlpath[%r{.*/imagesSet$}]
1351
+ @image = {}
1352
+ end
1353
+ end
1354
+ def tagend(name)
1355
+ case name
1356
+ when 'imageId' then @image[:aws_id] = @text
1357
+ when 'imageLocation' then @image[:aws_location] = @text
1358
+ when 'imageState' then @image[:aws_state] = @text
1359
+ when 'imageOwnerId' then @image[:aws_owner] = @text
1360
+ when 'isPublic' then @image[:aws_is_public]= @text == 'true' ? true : false
1361
+ when 'productCode' then (@image[:aws_product_codes] ||= []) << @text
1362
+ when 'architecture' then @image[:aws_architecture] = @text
1363
+ when 'imageType' then @image[:aws_image_type] = @text
1364
+ when 'kernelId' then @image[:aws_kernel_id] = @text
1365
+ when 'ramdiskId' then @image[:aws_ramdisk_id] = @text
1366
+ when 'item' then @result << @image if @xmlpath[%r{.*/imagesSet$}]
1367
+ end
1368
+ end
1369
+ def reset
1370
+ @result = []
1371
+ end
1372
+ end
1373
+
1374
+ class QEc2RegisterImageParser < AwsParser #:nodoc:
1375
+ def tagend(name)
1376
+ @result = @text if name == 'imageId'
1377
+ end
1378
+ end
1379
+
1380
+ #-----------------------------------------------------------------
1381
+ # PARSERS: Image Attribute
1382
+ #-----------------------------------------------------------------
1383
+
1384
+ class QEc2DescribeImageAttributeParser < AwsParser #:nodoc:
1385
+ def tagstart(name, attributes)
1386
+ case name
1387
+ when 'launchPermission'
1388
+ @result[:groups] = []
1389
+ @result[:users] = []
1390
+ when 'productCodes'
1391
+ @result[:aws_product_codes] = []
1392
+ end
1393
+ end
1394
+ def tagend(name)
1395
+ # right now only 'launchPermission' is supported by Amazon.
1396
+ # But nobody know what will they xml later as attribute. That is why we
1397
+ # check for 'group' and 'userId' inside of 'launchPermission/item'
1398
+ case name
1399
+ when 'imageId' then @result[:aws_id] = @text
1400
+ when 'group' then @result[:groups] << @text if @xmlpath == 'DescribeImageAttributeResponse/launchPermission/item'
1401
+ when 'userId' then @result[:users] << @text if @xmlpath == 'DescribeImageAttributeResponse/launchPermission/item'
1402
+ when 'productCode' then @result[:aws_product_codes] << @text
1403
+ when 'kernel' then @result[:aws_kernel] = @text
1404
+ when 'ramdisk' then @result[:aws_ramdisk] = @text
1405
+ when 'blockDeviceMapping' then @result[:block_device_mapping] = @text
1406
+ end
1407
+ end
1408
+ def reset
1409
+ @result = {}
1410
+ end
1411
+ end
1412
+
1413
+ #-----------------------------------------------------------------
1414
+ # PARSERS: Instances
1415
+ #-----------------------------------------------------------------
1416
+
1417
+ class QEc2DescribeInstancesParser < AwsParser #:nodoc:
1418
+ def tagstart(name, attributes)
1419
+ # DescribeInstances property
1420
+ if (name == 'item' && @xmlpath == 'DescribeInstancesResponse/reservationSet') ||
1421
+ # RunInstances property
1422
+ (name == 'RunInstancesResponse')
1423
+ @reservation = { :aws_groups => [],
1424
+ :instances_set => [] }
1425
+
1426
+ elsif (name == 'item') &&
1427
+ # DescribeInstances property
1428
+ ( @xmlpath=='DescribeInstancesResponse/reservationSet/item/instancesSet' ||
1429
+ # RunInstances property
1430
+ @xmlpath=='RunInstancesResponse/instancesSet' )
1431
+ # the optional params (sometimes are missing and we dont want them to be nil)
1432
+ @instance = { :aws_reason => '',
1433
+ :dns_name => '',
1434
+ :private_dns_name => '',
1435
+ :ami_launch_index => '',
1436
+ :ssh_key_name => '',
1437
+ :aws_state => '',
1438
+ :aws_product_codes => [] }
1439
+ end
1440
+ end
1441
+ def tagend(name)
1442
+ case name
1443
+ # reservation
1444
+ when 'reservationId' then @reservation[:aws_reservation_id] = @text
1445
+ when 'ownerId' then @reservation[:aws_owner] = @text
1446
+ when 'groupId' then @reservation[:aws_groups] << @text
1447
+ # instance
1448
+ when 'instanceId' then @instance[:aws_instance_id] = @text
1449
+ when 'imageId' then @instance[:aws_image_id] = @text
1450
+ when 'dnsName' then @instance[:dns_name] = @text
1451
+ when 'privateDnsName' then @instance[:private_dns_name] = @text
1452
+ when 'reason' then @instance[:aws_reason] = @text
1453
+ when 'keyName' then @instance[:ssh_key_name] = @text
1454
+ when 'amiLaunchIndex' then @instance[:ami_launch_index] = @text
1455
+ when 'code' then @instance[:aws_state_code] = @text
1456
+ when 'name' then @instance[:aws_state] = @text
1457
+ when 'productCode' then @instance[:aws_product_codes] << @text
1458
+ when 'instanceType' then @instance[:aws_instance_type] = @text
1459
+ when 'launchTime' then @instance[:aws_launch_time] = @text
1460
+ when 'kernelId' then @instance[:aws_kernel_id] = @text
1461
+ when 'ramdiskId' then @instance[:aws_ramdisk_id] = @text
1462
+ when 'platform' then @instance[:aws_platform] = @text
1463
+ when 'availabilityZone' then @instance[:aws_availability_zone] = @text
1464
+ when 'state'
1465
+ if @xmlpath == 'DescribeInstancesResponse/reservationSet/item/instancesSet/item/monitoring' || # DescribeInstances property
1466
+ @xmlpath == 'RunInstancesResponse/instancesSet/item/monitoring' # RunInstances property
1467
+ @instance[:monitoring_state] = @text
1468
+ end
1469
+ when 'item'
1470
+ if @xmlpath == 'DescribeInstancesResponse/reservationSet/item/instancesSet' || # DescribeInstances property
1471
+ @xmlpath == 'RunInstancesResponse/instancesSet' # RunInstances property
1472
+ @reservation[:instances_set] << @instance
1473
+ elsif @xmlpath=='DescribeInstancesResponse/reservationSet' # DescribeInstances property
1474
+ @result << @reservation
1475
+ end
1476
+ when 'RunInstancesResponse' then @result << @reservation # RunInstances property
1477
+ end
1478
+ end
1479
+ def reset
1480
+ @result = []
1481
+ end
1482
+ end
1483
+
1484
+ class QEc2ConfirmProductInstanceParser < AwsParser #:nodoc:
1485
+ def tagend(name)
1486
+ @result = @text if name == 'ownerId'
1487
+ end
1488
+ end
1489
+
1490
+ class QEc2MonitorInstancesParser < AwsParser #:nodoc:
1491
+ def tagstart(name, attributes)
1492
+ @instance = {} if name == 'item'
1493
+ end
1494
+ def tagend(name)
1495
+ case name
1496
+ when 'instanceId' then @instance[:aws_instance_id] = @text
1497
+ when 'state' then @instance[:aws_monitoring_state] = @text
1498
+ when 'item' then @result << @instance
1499
+ end
1500
+ end
1501
+ def reset
1502
+ @result = []
1503
+ end
1504
+ end
1505
+
1506
+
1507
+ class QEc2TerminateInstancesParser < AwsParser #:nodoc:
1508
+ def tagstart(name, attributes)
1509
+ @instance = {} if name == 'item'
1510
+ end
1511
+ def tagend(name)
1512
+ case name
1513
+ when 'instanceId' then @instance[:aws_instance_id] = @text
1514
+ when 'code'
1515
+ if @xmlpath == 'TerminateInstancesResponse/instancesSet/item/shutdownState'
1516
+ @instance[:aws_shutdown_state_code] = @text.to_i
1517
+ else @instance[:aws_prev_state_code] = @text.to_i end
1518
+ when 'name'
1519
+ if @xmlpath == 'TerminateInstancesResponse/instancesSet/item/shutdownState'
1520
+ @instance[:aws_shutdown_state] = @text
1521
+ else @instance[:aws_prev_state] = @text end
1522
+ when 'item' then @result << @instance
1523
+ end
1524
+ end
1525
+ def reset
1526
+ @result = []
1527
+ end
1528
+ end
1529
+
1530
+ #-----------------------------------------------------------------
1531
+ # PARSERS: Console
1532
+ #-----------------------------------------------------------------
1533
+
1534
+ class QEc2GetConsoleOutputParser < AwsParser #:nodoc:
1535
+ def tagend(name)
1536
+ case name
1537
+ when 'instanceId' then @result[:aws_instance_id] = @text
1538
+ when 'timestamp' then @result[:aws_timestamp] = @text
1539
+ @result[:timestamp] = (Time.parse(@text)).utc
1540
+ when 'output' then @result[:aws_output] = Base64.decode64(@text)
1541
+ end
1542
+ end
1543
+ def reset
1544
+ @result = {}
1545
+ end
1546
+ end
1547
+
1548
+ #-----------------------------------------------------------------
1549
+ # Instances: Wondows related part
1550
+ #-----------------------------------------------------------------
1551
+ class QEc2DescribeBundleTasksParser < AwsParser #:nodoc:
1552
+ def tagstart(name, attributes)
1553
+ @bundle = {} if name == 'item'
1554
+ end
1555
+ def tagend(name)
1556
+ case name
1557
+ # when 'requestId' then @bundle[:request_id] = @text
1558
+ when 'instanceId' then @bundle[:aws_instance_id] = @text
1559
+ when 'bundleId' then @bundle[:aws_id] = @text
1560
+ when 'bucket' then @bundle[:s3_bucket] = @text
1561
+ when 'prefix' then @bundle[:s3_prefix] = @text
1562
+ when 'startTime' then @bundle[:aws_start_time] = @text
1563
+ when 'updateTime' then @bundle[:aws_update_time] = @text
1564
+ when 'state' then @bundle[:aws_state] = @text
1565
+ when 'progress' then @bundle[:aws_progress] = @text
1566
+ when 'code' then @bundle[:aws_error_code] = @text
1567
+ when 'message' then @bundle[:aws_error_message] = @text
1568
+ when 'item' then @result << @bundle
1569
+ end
1570
+ end
1571
+ def reset
1572
+ @result = []
1573
+ end
1574
+ end
1575
+
1576
+ class QEc2BundleInstanceParser < AwsParser #:nodoc:
1577
+ def tagend(name)
1578
+ case name
1579
+ # when 'requestId' then @result[:request_id] = @text
1580
+ when 'instanceId' then @result[:aws_instance_id] = @text
1581
+ when 'bundleId' then @result[:aws_id] = @text
1582
+ when 'bucket' then @result[:s3_bucket] = @text
1583
+ when 'prefix' then @result[:s3_prefix] = @text
1584
+ when 'startTime' then @result[:aws_start_time] = @text
1585
+ when 'updateTime' then @result[:aws_update_time] = @text
1586
+ when 'state' then @result[:aws_state] = @text
1587
+ when 'progress' then @result[:aws_progress] = @text
1588
+ when 'code' then @result[:aws_error_code] = @text
1589
+ when 'message' then @result[:aws_error_message] = @text
1590
+ end
1591
+ end
1592
+ def reset
1593
+ @result = {}
1594
+ end
1595
+ end
1596
+
1597
+ #-----------------------------------------------------------------
1598
+ # PARSERS: Elastic IPs
1599
+ #-----------------------------------------------------------------
1600
+
1601
+ class QEc2AllocateAddressParser < AwsParser #:nodoc:
1602
+ def tagend(name)
1603
+ @result = @text if name == 'publicIp'
1604
+ end
1605
+ end
1606
+
1607
+ class QEc2DescribeAddressesParser < AwsParser #:nodoc:
1608
+ def tagstart(name, attributes)
1609
+ @address = {} if name == 'item'
1610
+ end
1611
+ def tagend(name)
1612
+ case name
1613
+ when 'instanceId' then @address[:instance_id] = @text.blank? ? nil : @text
1614
+ when 'publicIp' then @address[:public_ip] = @text
1615
+ when 'item' then @result << @address
1616
+ end
1617
+ end
1618
+ def reset
1619
+ @result = []
1620
+ end
1621
+ end
1622
+
1623
+ #-----------------------------------------------------------------
1624
+ # PARSERS: AvailabilityZones
1625
+ #-----------------------------------------------------------------
1626
+
1627
+ class QEc2DescribeAvailabilityZonesParser < AwsParser #:nodoc:
1628
+ def tagstart(name, attributes)
1629
+ @zone = {} if name == 'item'
1630
+ end
1631
+ def tagend(name)
1632
+ case name
1633
+ when 'regionName' then @zone[:region_name] = @text
1634
+ when 'zoneName' then @zone[:zone_name] = @text
1635
+ when 'zoneState' then @zone[:zone_state] = @text
1636
+ when 'item' then @result << @zone
1637
+ end
1638
+ end
1639
+ def reset
1640
+ @result = []
1641
+ end
1642
+ end
1643
+
1644
+ #-----------------------------------------------------------------
1645
+ # PARSERS: Regions
1646
+ #-----------------------------------------------------------------
1647
+
1648
+ class QEc2DescribeRegionsParser < AwsParser #:nodoc:
1649
+ def tagend(name)
1650
+ @result << @text if name == 'regionName'
1651
+ end
1652
+ def reset
1653
+ @result = []
1654
+ end
1655
+ end
1656
+
1657
+ #-----------------------------------------------------------------
1658
+ # PARSERS: EBS - Volumes
1659
+ #-----------------------------------------------------------------
1660
+
1661
+ class QEc2CreateVolumeParser < AwsParser #:nodoc:
1662
+ def tagend(name)
1663
+ case name
1664
+ when 'volumeId' then @result[:aws_id] = @text
1665
+ when 'status' then @result[:aws_status] = @text
1666
+ when 'createTime' then @result[:aws_created_at] = Time.parse(@text)
1667
+ when 'size' then @result[:aws_size] = @text.to_i ###
1668
+ when 'snapshotId' then @result[:snapshot_id] = @text.blank? ? nil : @text ###
1669
+ when 'availabilityZone' then @result[:zone] = @text ###
1670
+ end
1671
+ end
1672
+ def reset
1673
+ @result = {}
1674
+ end
1675
+ end
1676
+
1677
+ class QEc2AttachAndDetachVolumeParser < AwsParser #:nodoc:
1678
+ def tagend(name)
1679
+ case name
1680
+ when 'volumeId' then @result[:aws_id] = @text
1681
+ when 'instanceId' then @result[:aws_instance_id] = @text
1682
+ when 'device' then @result[:aws_device] = @text
1683
+ when 'status' then @result[:aws_attachment_status] = @text
1684
+ when 'attachTime' then @result[:aws_attached_at] = Time.parse(@text)
1685
+ end
1686
+ end
1687
+ def reset
1688
+ @result = {}
1689
+ end
1690
+ end
1691
+
1692
+ class QEc2DescribeVolumesParser < AwsParser #:nodoc:
1693
+ def tagstart(name, attributes)
1694
+ case name
1695
+ when 'item'
1696
+ case @xmlpath
1697
+ when 'DescribeVolumesResponse/volumeSet' then @volume = {}
1698
+ end
1699
+ end
1700
+ end
1701
+ def tagend(name)
1702
+ case name
1703
+ when 'volumeId'
1704
+ case @xmlpath
1705
+ when 'DescribeVolumesResponse/volumeSet/item' then @volume[:aws_id] = @text
1706
+ end
1707
+ when 'status'
1708
+ case @xmlpath
1709
+ when 'DescribeVolumesResponse/volumeSet/item' then @volume[:aws_status] = @text
1710
+ when 'DescribeVolumesResponse/volumeSet/item/attachmentSet/item' then @volume[:aws_attachment_status] = @text
1711
+ end
1712
+ when 'size' then @volume[:aws_size] = @text.to_i
1713
+ when 'createTime' then @volume[:aws_created_at] = Time.parse(@text)
1714
+ when 'instanceId' then @volume[:aws_instance_id] = @text
1715
+ when 'device' then @volume[:aws_device] = @text
1716
+ when 'attachTime' then @volume[:aws_attached_at] = Time.parse(@text)
1717
+ when 'snapshotId' then @volume[:snapshot_id] = @text.blank? ? nil : @text
1718
+ when 'availabilityZone' then @volume[:zone] = @text
1719
+ when 'item'
1720
+ case @xmlpath
1721
+ when 'DescribeVolumesResponse/volumeSet' then @result << @volume
1722
+ end
1723
+ end
1724
+ end
1725
+ def reset
1726
+ @result = []
1727
+ end
1728
+ end
1729
+
1730
+ #-----------------------------------------------------------------
1731
+ # PARSERS: EBS - Snapshots
1732
+ #-----------------------------------------------------------------
1733
+
1734
+ class QEc2DescribeSnapshotsParser < AwsParser #:nodoc:
1735
+ def tagstart(name, attributes)
1736
+ @snapshot = {} if name == 'item'
1737
+ end
1738
+ def tagend(name)
1739
+ case name
1740
+ when 'volumeId' then @snapshot[:aws_volume_id] = @text
1741
+ when 'snapshotId' then @snapshot[:aws_id] = @text
1742
+ when 'status' then @snapshot[:aws_status] = @text
1743
+ when 'startTime' then @snapshot[:aws_started_at] = Time.parse(@text)
1744
+ when 'progress' then @snapshot[:aws_progress] = @text
1745
+ when 'item' then @result << @snapshot
1746
+ end
1747
+ end
1748
+ def reset
1749
+ @result = []
1750
+ end
1751
+ end
1752
+
1753
+ class QEc2CreateSnapshotParser < AwsParser #:nodoc:
1754
+ def tagend(name)
1755
+ case name
1756
+ when 'volumeId' then @result[:aws_volume_id] = @text
1757
+ when 'snapshotId' then @result[:aws_id] = @text
1758
+ when 'status' then @result[:aws_status] = @text
1759
+ when 'startTime' then @result[:aws_started_at] = Time.parse(@text)
1760
+ when 'progress' then @result[:aws_progress] = @text
1761
+ end
1762
+ end
1763
+ def reset
1764
+ @result = {}
1765
+ end
1766
+ end
1767
+
1768
+ end
1769
+
1770
+ end
1771
+
1772
+