aws 2.3.34 → 2.4.0

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