revans_right_aws 2.0.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (52) hide show
  1. data/.gemtest +0 -0
  2. data/History.txt +284 -0
  3. data/Manifest.txt +50 -0
  4. data/README.txt +167 -0
  5. data/Rakefile +110 -0
  6. data/lib/acf/right_acf_interface.rb +485 -0
  7. data/lib/acf/right_acf_origin_access_identities.rb +230 -0
  8. data/lib/acf/right_acf_streaming_interface.rb +236 -0
  9. data/lib/acw/right_acw_interface.rb +249 -0
  10. data/lib/as/right_as_interface.rb +699 -0
  11. data/lib/awsbase/benchmark_fix.rb +39 -0
  12. data/lib/awsbase/right_awsbase.rb +978 -0
  13. data/lib/awsbase/support.rb +115 -0
  14. data/lib/ec2/right_ec2.rb +395 -0
  15. data/lib/ec2/right_ec2_ebs.rb +452 -0
  16. data/lib/ec2/right_ec2_images.rb +373 -0
  17. data/lib/ec2/right_ec2_instances.rb +755 -0
  18. data/lib/ec2/right_ec2_monitoring.rb +70 -0
  19. data/lib/ec2/right_ec2_reserved_instances.rb +170 -0
  20. data/lib/ec2/right_ec2_security_groups.rb +277 -0
  21. data/lib/ec2/right_ec2_spot_instances.rb +399 -0
  22. data/lib/ec2/right_ec2_vpc.rb +571 -0
  23. data/lib/elb/right_elb_interface.rb +496 -0
  24. data/lib/rds/right_rds_interface.rb +998 -0
  25. data/lib/right_aws.rb +83 -0
  26. data/lib/s3/right_s3.rb +1126 -0
  27. data/lib/s3/right_s3_interface.rb +1199 -0
  28. data/lib/sdb/active_sdb.rb +1122 -0
  29. data/lib/sdb/right_sdb_interface.rb +721 -0
  30. data/lib/sqs/right_sqs.rb +388 -0
  31. data/lib/sqs/right_sqs_gen2.rb +343 -0
  32. data/lib/sqs/right_sqs_gen2_interface.rb +524 -0
  33. data/lib/sqs/right_sqs_interface.rb +594 -0
  34. data/test/acf/test_helper.rb +2 -0
  35. data/test/acf/test_right_acf.rb +138 -0
  36. data/test/ec2/test_helper.rb +2 -0
  37. data/test/ec2/test_right_ec2.rb +108 -0
  38. data/test/http_connection.rb +87 -0
  39. data/test/rds/test_helper.rb +2 -0
  40. data/test/rds/test_right_rds.rb +120 -0
  41. data/test/s3/test_helper.rb +2 -0
  42. data/test/s3/test_right_s3.rb +421 -0
  43. data/test/s3/test_right_s3_stubbed.rb +97 -0
  44. data/test/sdb/test_active_sdb.rb +357 -0
  45. data/test/sdb/test_helper.rb +3 -0
  46. data/test/sdb/test_right_sdb.rb +253 -0
  47. data/test/sqs/test_helper.rb +2 -0
  48. data/test/sqs/test_right_sqs.rb +291 -0
  49. data/test/sqs/test_right_sqs_gen2.rb +264 -0
  50. data/test/test_credentials.rb +37 -0
  51. data/test/ts_right_aws.rb +14 -0
  52. metadata +169 -0
@@ -0,0 +1,755 @@
1
+ #
2
+ # Copyright (c) 2009 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 RightAws
25
+
26
+ class Ec2
27
+
28
+ #-----------------------------------------------------------------
29
+ # Instances
30
+ #-----------------------------------------------------------------
31
+
32
+ def get_desc_instances(instances) # :nodoc:
33
+ result = []
34
+ instances.each do |reservation|
35
+ reservation[:instances_set].each do |instance|
36
+ # Parse and remove timestamp from the reason string. The timestamp is of
37
+ # the request, not when EC2 took action, thus confusing & useless...
38
+ instance[:aws_reason] = instance[:aws_reason].sub(/\(\d[^)]*GMT\) */, '')
39
+ instance[:aws_owner] = reservation[:aws_owner]
40
+ instance[:aws_reservation_id] = reservation[:aws_reservation_id]
41
+ instance[:aws_groups] = reservation[:aws_groups]
42
+ result << instance
43
+ end
44
+ end
45
+ result
46
+ rescue Exception
47
+ on_exception
48
+ end
49
+
50
+ # Retrieve information about EC2 instances. If +list+ is omitted then returns the
51
+ # list of all instances.
52
+ #
53
+ # ec2.describe_instances #=>
54
+ # [{:private_ip_address=>"10.240.7.99",
55
+ # :aws_image_id=>"ami-c2a3f5d4",
56
+ # :ip_address=>"174.129.134.109",
57
+ # :dns_name=>"ec2-174-129-134-109.compute-1.amazonaws.com",
58
+ # :aws_instance_type=>"m1.small",
59
+ # :aws_owner=>"826693181925",
60
+ # :root_device_name=>"/dev/sda1",
61
+ # :instance_class=>"elastic",
62
+ # :aws_state=>"running",
63
+ # :private_dns_name=>"domU-12-31-39-04-00-95.compute-1.internal",
64
+ # :aws_reason=>"",
65
+ # :aws_launch_time=>"2009-11-18T14:03:25.000Z",
66
+ # :aws_reservation_id=>"r-54d38542",
67
+ # :aws_state_code=>16,
68
+ # :ami_launch_index=>"0",
69
+ # :aws_availability_zone=>"us-east-1a",
70
+ # :aws_groups=>["default"],
71
+ # :monitoring_state=>"disabled",
72
+ # :aws_product_codes=>[],
73
+ # :ssh_key_name=>"",
74
+ # :block_device_mappings=>
75
+ # [{:ebs_status=>"attached",
76
+ # :ebs_delete_on_termination=>true,
77
+ # :ebs_attach_time=>"2009-11-18T14:03:34.000Z",
78
+ # :device_name=>"/dev/sda1",
79
+ # :ebs_volume_id=>"vol-e600f98f"},
80
+ # {:ebs_status=>"attached",
81
+ # :ebs_delete_on_termination=>true,
82
+ # :ebs_attach_time=>"2009-11-18T14:03:34.000Z",
83
+ # :device_name=>"/dev/sdk",
84
+ # :ebs_volume_id=>"vol-f900f990"}],
85
+ # :aws_instance_id=>"i-8ce84ae4"} , ... ]
86
+ #
87
+ def describe_instances(*instances)
88
+ instances = instances.flatten
89
+ link = generate_request("DescribeInstances", amazonize_list('InstanceId', instances))
90
+ request_cache_or_info(:describe_instances, link, QEc2DescribeInstancesParser, @@bench, instances.blank?) do |parser|
91
+ get_desc_instances(parser.result)
92
+ end
93
+ rescue Exception
94
+ on_exception
95
+ end
96
+
97
+ # Return the product code attached to instance or +nil+ otherwise.
98
+ #
99
+ # ec2.confirm_product_instance('ami-e444444d','12345678') #=> nil
100
+ # ec2.confirm_product_instance('ami-e444444d','00001111') #=> "000000000888"
101
+ #
102
+ def confirm_product_instance(instance, product_code)
103
+ link = generate_request("ConfirmProductInstance", { 'ProductCode' => product_code,
104
+ 'InstanceId' => instance })
105
+ request_info(link, QEc2ConfirmProductInstanceParser.new(:logger => @logger))
106
+ end
107
+
108
+ # Launch new EC2 instances. Returns a list of launched instances or an exception.
109
+ #
110
+ # ec2.run_instances('ami-e444444d',1,1,['my_awesome_group'],'my_awesome_key', 'Woohoo!!!', 'public') #=>
111
+ # [{:aws_image_id => "ami-e444444d",
112
+ # :aws_reason => "",
113
+ # :aws_state_code => "0",
114
+ # :aws_owner => "000000000888",
115
+ # :aws_instance_id => "i-123f1234",
116
+ # :aws_reservation_id => "r-aabbccdd",
117
+ # :aws_state => "pending",
118
+ # :dns_name => "",
119
+ # :ssh_key_name => "my_awesome_key",
120
+ # :aws_groups => ["my_awesome_group"],
121
+ # :private_dns_name => "",
122
+ # :aws_instance_type => "m1.small",
123
+ # :aws_launch_time => "2008-1-1T00:00:00.000Z"
124
+ # :aws_ramdisk_id => "ari-8605e0ef"
125
+ # :aws_kernel_id => "aki-9905e0f0",
126
+ # :ami_launch_index => "0",
127
+ # :aws_availability_zone => "us-east-1b"
128
+ # }]
129
+ #
130
+ def run_instances(image_id, min_count, max_count, group_ids, key_name, user_data='',
131
+ addressing_type = nil, instance_type = nil,
132
+ kernel_id = nil, ramdisk_id = nil, availability_zone = nil,
133
+ monitoring_enabled = nil, subnet_id = nil, disable_api_termination = nil,
134
+ instance_initiated_shutdown_behavior = nil, block_device_mappings = nil)
135
+ launch_instances(image_id, { :min_count => min_count,
136
+ :max_count => max_count,
137
+ :user_data => user_data,
138
+ :group_ids => group_ids,
139
+ :key_name => key_name,
140
+ :instance_type => instance_type,
141
+ :addressing_type => addressing_type,
142
+ :kernel_id => kernel_id,
143
+ :ramdisk_id => ramdisk_id,
144
+ :availability_zone => availability_zone,
145
+ :monitoring_enabled => monitoring_enabled,
146
+ :subnet_id => subnet_id,
147
+ :disable_api_termination => disable_api_termination,
148
+ :instance_initiated_shutdown_behavior => instance_initiated_shutdown_behavior,
149
+ :block_device_mappings => block_device_mappings
150
+ })
151
+ end
152
+
153
+ # Launch new EC2 instances.
154
+ # Options: :image_id, :addressing_type, :min_count, max_count, :key_name, :kernel_id, :ramdisk_id,
155
+ # :availability_zone, :monitoring_enabled, :subnet_id, :disable_api_termination, :instance_initiated_shutdown_behavior,
156
+ # :block_device_mappings
157
+ #
158
+ # Returns a list of launched instances or an exception.
159
+ #
160
+ # ec2.launch_instances( 'ami-c2a3f5d4',
161
+ # :min_count => 1,
162
+ # :group_ids => 'default',
163
+ # :user_data => 'Ohoho!',
164
+ # :availability_zone => "us-east-1a",
165
+ # :disable_api_termination => true,
166
+ # :instance_initiated_shutdown_behavior => 'terminate',
167
+ # :block_device_mappings => [ {:ebs_snapshot_id=>"snap-7360871a",
168
+ # :ebs_delete_on_termination=>true,
169
+ # :device_name => "/dev/sdk",
170
+ # :virtual_name => "mystorage"} ] ) #=>
171
+ # [{:aws_image_id=>"ami-c2a3f5d4",
172
+ # :dns_name=>"",
173
+ # :aws_instance_type=>"m1.small",
174
+ # :aws_owner=>"826693181925",
175
+ # :root_device_name=>"/dev/sda1",
176
+ # :instance_class=>"elastic",
177
+ # :state_reason_code=>0,
178
+ # :aws_state=>"pending",
179
+ # :private_dns_name=>"",
180
+ # :aws_reason=>"",
181
+ # :aws_launch_time=>"2009-11-18T14:03:25.000Z",
182
+ # :aws_reservation_id=>"r-54d38542",
183
+ # :state_reason_message=>"pending",
184
+ # :aws_state_code=>0,
185
+ # :ami_launch_index=>"0",
186
+ # :aws_availability_zone=>"us-east-1a",
187
+ # :aws_groups=>["default"],
188
+ # :monitoring_state=>"disabled",
189
+ # :aws_product_codes=>[],
190
+ # :ssh_key_name=>"",
191
+ # :aws_instance_id=>"i-8ce84ae4"}]
192
+ #
193
+ def launch_instances(image_id, options={})
194
+ @logger.info("Launching instance of image #{image_id} for #{@aws_access_key_id}, " +
195
+ "key: #{options[:key_name]}, groups: #{Array(options[:group_ids]).join(',')}")
196
+ options[:image_id] = image_id
197
+ options[:min_count] ||= 1
198
+ options[:max_count] ||= options[:min_count]
199
+ params = prepare_instance_launch_params(options)
200
+ link = generate_request("RunInstances", params)
201
+ instances = request_info(link, QEc2DescribeInstancesParser.new(:logger => @logger))
202
+ get_desc_instances(instances)
203
+ rescue Exception
204
+ on_exception
205
+ end
206
+
207
+ def prepare_instance_launch_params(options={}) # :nodoc:
208
+ params = amazonize_list('SecurityGroup', Array(options[:group_ids]))
209
+ params['InstanceType'] = options[:instance_type] || DEFAULT_INSTANCE_TYPE
210
+ params['ImageId'] = options[:image_id] unless options[:image_id].blank?
211
+ params['AddressingType'] = options[:addressing_type] unless options[:addressing_type].blank?
212
+ params['MinCount'] = options[:min_count] unless options[:min_count].blank?
213
+ params['MaxCount'] = options[:max_count] unless options[:max_count].blank?
214
+ params['KeyName'] = options[:key_name] unless options[:key_name].blank?
215
+ params['KernelId'] = options[:kernel_id] unless options[:kernel_id].blank?
216
+ params['RamdiskId'] = options[:ramdisk_id] unless options[:ramdisk_id].blank?
217
+ params['Placement.AvailabilityZone'] = options[:availability_zone] unless options[:availability_zone].blank?
218
+ params['Monitoring.Enabled'] = options[:monitoring_enabled].to_s if options[:monitoring_enabled]
219
+ params['SubnetId'] = options[:subnet_id] unless options[:subnet_id].blank?
220
+ params['AdditionalInfo'] = options[:additional_info] unless options[:additional_info].blank?
221
+ params['DisableApiTermination'] = options[:disable_api_termination].to_s unless options[:disable_api_termination].nil?
222
+ params['InstanceInitiatedShutdownBehavior'] = options[:instance_initiated_shutdown_behavior] unless options[:instance_initiated_shutdown_behavior].blank?
223
+ # params['VolumeId'] = options[:volume_id] unless options[:volume_id].blank?
224
+ # params['RootDeviceName'] = options[:root_device_name] unless options[:root_device_name].blank?
225
+ # params['RootDeviceType'] = options[:root_device_type] unless options[:root_device_type].blank?
226
+ params.merge!(amazonize_block_device_mappings(options[:block_device_mappings]))
227
+ unless options[:user_data].blank?
228
+ options[:user_data].strip!
229
+ # Do not use CGI::escape(encode64(...)) as it is done in Amazons EC2 library.
230
+ # Amazon 169.254.169.254 does not like escaped symbols!
231
+ # And it doesn't like "\n" inside of encoded string! Grrr....
232
+ # Otherwise, some of UserData symbols will be lost...
233
+ params['UserData'] = Base64.encode64(options[:user_data]).delete("\n") unless options[:user_data].blank?
234
+ end
235
+ params
236
+ end
237
+
238
+ # Start instances.
239
+ #
240
+ # ec2.start_instances("i-36e84a5e") #=>
241
+ # [{:aws_prev_state_name=>"stopped",
242
+ # :aws_instance_id=>"i-36e84a5e",
243
+ # :aws_current_state_code=>16,
244
+ # :aws_current_state_name=>"running",
245
+ # :aws_prev_state_code=>80}]
246
+ #
247
+ def start_instances(*instance_aws_ids)
248
+ instance_aws_ids = instance_aws_ids.flatten
249
+ link = generate_request("StartInstances", amazonize_list('InstanceId', instance_aws_ids))
250
+ request_info(link, QEc2TerminateInstancesParser.new(:logger => @logger))
251
+ end
252
+
253
+ # Stop instances.
254
+ #
255
+ # ec2.stop_instances("i-36e84a5e") #=>
256
+ # [{:aws_prev_state_code=>16,
257
+ # :aws_prev_state_name=>"running",
258
+ # :aws_instance_id=>"i-36e84a5e",
259
+ # :aws_current_state_code=>64,
260
+ # :aws_current_state_name=>"stopping"}]
261
+ #
262
+ def stop_instances(*instance_aws_ids)
263
+ instance_aws_ids = instance_aws_ids.flatten
264
+ link = generate_request("StopInstances", amazonize_list('InstanceId', instance_aws_ids))
265
+ request_info(link, QEc2TerminateInstancesParser.new(:logger => @logger))
266
+ end
267
+
268
+ # Terminates EC2 instances. Returns a list of termination params or an exception.
269
+ #
270
+ # ec2.terminate_instances(['i-cceb49a4']) #=>
271
+ # [{:aws_instance_id=>"i-cceb49a4",
272
+ # :aws_current_state_code=>32,
273
+ # :aws_current_state_name=>"shutting-down",
274
+ # :aws_prev_state_code=>16,
275
+ # :aws_prev_state_name=>"running"}]
276
+ #
277
+ def terminate_instances(*instance_aws_ids)
278
+ instance_aws_ids = instance_aws_ids.flatten
279
+ link = generate_request("TerminateInstances", amazonize_list('InstanceId', instance_aws_ids))
280
+ request_info(link, QEc2TerminateInstancesParser.new(:logger => @logger))
281
+ rescue Exception
282
+ on_exception
283
+ end
284
+
285
+ # Retreive EC2 instance OS logs. Returns a hash of data or an exception.
286
+ #
287
+ # ec2.get_console_output('i-f222222d') =>
288
+ # {:aws_instance_id => 'i-f222222d',
289
+ # :aws_timestamp => "2007-05-23T14:36:07.000-07:00",
290
+ # :timestamp => Wed May 23 21:36:07 UTC 2007, # Time instance
291
+ # :aws_output => "Linux version 2.6.16-xenU (builder@patchbat.amazonsa) (gcc version 4.0.1 20050727 ..."
292
+ def get_console_output(instance_id)
293
+ link = generate_request("GetConsoleOutput", { 'InstanceId.1' => instance_id })
294
+ request_info(link, QEc2GetConsoleOutputParser.new(:logger => @logger))
295
+ rescue Exception
296
+ on_exception
297
+ end
298
+
299
+ # Reboot an EC2 instance. Returns +true+ or an exception.
300
+ #
301
+ # ec2.reboot_instances(['i-f222222d','i-f222222e']) #=> true
302
+ #
303
+ def reboot_instances(*instances)
304
+ instances = instances.flatten
305
+ link = generate_request("RebootInstances", amazonize_list('InstanceId', instances))
306
+ request_info(link, RightBoolResponseParser.new(:logger => @logger))
307
+ rescue Exception
308
+ on_exception
309
+ end
310
+
311
+ INSTANCE_ATTRIBUTE_MAPPING = {
312
+ "instance_type" => "instanceType",
313
+ "kernel" => "kernel",
314
+ "ramdisk" => "ramdisk",
315
+ "user_data" => "userData",
316
+ "disable_api_termination" => "disableApiTermination",
317
+ "instance_initiated_shutdown_behavior" => "instanceInitiatedShutdownBehavior",
318
+ "root_device_name" => "rootDeviceName",
319
+ "block_device_mapping" => "blockDeviceMapping"
320
+ }
321
+
322
+ # Describe instance attribute.
323
+ # Attributes: :instance_type, :kernel, :ramdisk, :user_data, :disable_api_termination, :instance_initiated_shutdown_behavior, :root_device_name, :block_device_mapping
324
+ #
325
+ # ec2.describe_instance_attribute(instance, "BlockDeviceMapping") #=>
326
+ # [{:ebs_delete_on_termination=>true,
327
+ # :ebs_volume_id=>"vol-683dc401",
328
+ # :device_name=>"/dev/sda1"}]
329
+ #
330
+ # ec2.describe_instance_attribute(instance, "InstanceType") #=> "m1.small"
331
+ #
332
+ # ec2.describe_instance_attribute(instance, "InstanceInitiatedShutdownBehavior") #=> "stop"
333
+ #
334
+ def describe_instance_attribute(instance_id, attribute)
335
+ attribute = INSTANCE_ATTRIBUTE_MAPPING[attribute.to_s] || attribute.to_s
336
+ link = generate_request('DescribeInstanceAttribute',
337
+ 'InstanceId' => instance_id,
338
+ 'Attribute' => attribute)
339
+ value = request_info(link, QEc2DescribeInstanceAttributeParser.new(:logger => @logger))
340
+ case attribute
341
+ when "userData"
342
+ Base64.decode64(value)
343
+ else
344
+ value
345
+ end
346
+ rescue Exception
347
+ on_exception
348
+ end
349
+
350
+ # Describe instance attribute.
351
+ # Attributes: :kernel, :ramdisk
352
+ #
353
+ # ec2.reset_instance_attribute(instance, :kernel) #=> true
354
+ #
355
+ def reset_instance_attribute(instance_id, attribute)
356
+ attribute = INSTANCE_ATTRIBUTE_MAPPING[attribute.to_s] || attribute.to_s
357
+ link = generate_request('ResetInstanceAttribute',
358
+ 'InstanceId' => instance_id,
359
+ 'Attribute' => attribute )
360
+ request_info(link, RightBoolResponseParser.new(:logger => @logger))
361
+ rescue Exception
362
+ on_exception
363
+ end
364
+
365
+ # Modify instance attribute.
366
+ # Attributes: :instance_type, :kernel, :ramdisk, :user_data, :disable_api_termination, :instance_initiated_shutdown_behavior, :root_device_name, :block_device_mapping
367
+ #
368
+ # ec2.modify_instance_attribute(instance, :instance_initiated_shutdown_behavior, "stop") #=> true
369
+ #
370
+ def modify_instance_attribute(instance_id, attribute, value)
371
+ attribute = INSTANCE_ATTRIBUTE_MAPPING[attribute.to_s] || attribute.to_s
372
+ params = { 'InstanceId' => instance_id,
373
+ 'Attribute' => attribute }
374
+ case attribute
375
+ when "blockDeviceMapping"
376
+ params.merge!(amazonize_block_device_mappings(value))
377
+ when "userData"
378
+ params['Value'] = Base64.encode64(value).delete("\n")
379
+ else
380
+ params['Value'] = value
381
+ end
382
+ link = generate_request('ModifyInstanceAttribute', params)
383
+ request_info(link, RightBoolResponseParser.new(:logger => @logger))
384
+ rescue Exception
385
+ on_exception
386
+ end
387
+
388
+ #-----------------------------------------------------------------
389
+ # Instances: Windows addons
390
+ #-----------------------------------------------------------------
391
+
392
+ # Get initial Windows Server setup password from an instance console output.
393
+ #
394
+ # my_awesome_key = ec2.create_key_pair('my_awesome_key') #=>
395
+ # {:aws_key_name => "my_awesome_key",
396
+ # :aws_fingerprint => "01:02:03:f4:25:e6:97:e8:9b:02:1a:26:32:4e:58:6b:7a:8c:9f:03",
397
+ # :aws_material => "-----BEGIN RSA PRIVATE KEY-----\nMIIEpQIBAAK...Q8MDrCbuQ=\n-----END RSA PRIVATE KEY-----"}
398
+ #
399
+ # my_awesome_instance = ec2.run_instances('ami-a000000a',1,1,['my_awesome_group'],'my_awesome_key', 'WindowsInstance!!!') #=>
400
+ # [{:aws_image_id => "ami-a000000a",
401
+ # :aws_instance_id => "i-12345678",
402
+ # ...
403
+ # :aws_availability_zone => "us-east-1b"
404
+ # }]
405
+ #
406
+ # # wait until instance enters 'operational' state and get it's initial password
407
+ #
408
+ # puts ec2.get_initial_password(my_awesome_instance[:aws_instance_id], my_awesome_key[:aws_material]) #=> "MhjWcgZuY6"
409
+ #
410
+ def get_initial_password(instance_id, private_key)
411
+ console_output = get_console_output(instance_id)
412
+ crypted_password = console_output[:aws_output][%r{<Password>(.+)</Password>}m] && $1
413
+ unless crypted_password
414
+ raise AwsError.new("Initial password was not found in console output for #{instance_id}")
415
+ else
416
+ OpenSSL::PKey::RSA.new(private_key).private_decrypt(Base64.decode64(crypted_password))
417
+ end
418
+ rescue Exception
419
+ on_exception
420
+ end
421
+
422
+ # Get Initial windows instance password using Amazon API call GetPasswordData.
423
+ #
424
+ # puts ec2.get_initial_password_v2(my_awesome_instance[:aws_instance_id], my_awesome_key[:aws_material]) #=> "MhjWcgZuY6"
425
+ #
426
+ # P.S. To say the truth there is absolutely no any speedup if to compare to the old get_initial_password method... ;(
427
+ #
428
+ def get_initial_password_v2(instance_id, private_key)
429
+ link = generate_request('GetPasswordData',
430
+ 'InstanceId' => instance_id )
431
+ response = request_info(link, QEc2GetPasswordDataParser.new(:logger => @logger))
432
+ if response[:password_data].blank?
433
+ raise AwsError.new("Initial password is not yet created for #{instance_id}")
434
+ else
435
+ OpenSSL::PKey::RSA.new(private_key).private_decrypt(Base64.decode64(response[:password_data]))
436
+ end
437
+ rescue Exception
438
+ on_exception
439
+ end
440
+
441
+ # Bundle a Windows image.
442
+ # Internally, it queues the bundling task and shuts down the instance.
443
+ # It then takes a snapshot of the Windows volume bundles it, and uploads it to
444
+ # S3. After bundling completes, Rightaws::Ec2#register_image may be used to
445
+ # register the new Windows AMI for subsequent launches.
446
+ #
447
+ # ec2.bundle_instance('i-e3e24e8a', 'my-awesome-bucket', 'my-win-image-1') #=>
448
+ # [{:aws_update_time => "2008-10-16T13:58:25.000Z",
449
+ # :s3_bucket => "kd-win-1",
450
+ # :s3_prefix => "win2pr",
451
+ # :aws_state => "pending",
452
+ # :aws_id => "bun-26a7424f",
453
+ # :aws_instance_id => "i-878a25ee",
454
+ # :aws_start_time => "2008-10-16T13:58:02.000Z"}]
455
+ #
456
+ def bundle_instance(instance_id, s3_bucket, s3_prefix,
457
+ s3_owner_aws_access_key_id=nil, s3_owner_aws_secret_access_key=nil,
458
+ s3_expires = S3Interface::DEFAULT_EXPIRES_AFTER,
459
+ s3_upload_policy='ec2-bundle-read')
460
+ # S3 access and signatures
461
+ s3_owner_aws_access_key_id ||= @aws_access_key_id
462
+ s3_owner_aws_secret_access_key ||= @aws_secret_access_key
463
+ s3_expires = Time.now.utc + s3_expires if s3_expires.is_a?(Fixnum) && (s3_expires < S3Interface::ONE_YEAR_IN_SECONDS)
464
+ # policy
465
+ policy = { 'expiration' => AwsUtils::utc_iso8601(s3_expires),
466
+ 'conditions' => [ { 'bucket' => s3_bucket },
467
+ { 'acl' => s3_upload_policy },
468
+ [ 'starts-with', '$key', s3_prefix ] ] }.to_json
469
+ policy64 = Base64.encode64(policy).gsub("\n","")
470
+ signed_policy64 = AwsUtils.sign(s3_owner_aws_secret_access_key, policy64)
471
+ # fill request params
472
+ params = { 'InstanceId' => instance_id,
473
+ 'Storage.S3.AWSAccessKeyId' => s3_owner_aws_access_key_id,
474
+ 'Storage.S3.UploadPolicy' => policy64,
475
+ 'Storage.S3.UploadPolicySignature' => signed_policy64,
476
+ 'Storage.S3.Bucket' => s3_bucket,
477
+ 'Storage.S3.Prefix' => s3_prefix,
478
+ }
479
+ link = generate_request("BundleInstance", params)
480
+ request_info(link, QEc2BundleInstanceParser.new)
481
+ rescue Exception
482
+ on_exception
483
+ end
484
+
485
+ # Describe the status of the Windows AMI bundlings.
486
+ # If +list+ is omitted the returns the whole list of tasks.
487
+ #
488
+ # ec2.describe_bundle_tasks(['bun-4fa74226']) #=>
489
+ # [{:s3_bucket => "my-awesome-bucket"
490
+ # :aws_id => "bun-0fa70206",
491
+ # :s3_prefix => "win1pr",
492
+ # :aws_start_time => "2008-10-14T16:27:57.000Z",
493
+ # :aws_update_time => "2008-10-14T16:37:10.000Z",
494
+ # :aws_error_code => "Client.S3Error",
495
+ # :aws_error_message =>
496
+ # "AccessDenied(403)- Invalid according to Policy: Policy Condition failed: [\"eq\", \"$acl\", \"aws-exec-read\"]",
497
+ # :aws_state => "failed",
498
+ # :aws_instance_id => "i-e3e24e8a"}]
499
+ #
500
+ def describe_bundle_tasks(*tasks)
501
+ tasks = tasks.flatten
502
+ link = generate_request("DescribeBundleTasks", amazonize_list('BundleId', tasks))
503
+ request_info(link, QEc2DescribeBundleTasksParser.new)
504
+ rescue Exception
505
+ on_exception
506
+ end
507
+
508
+ # Cancel an in‐progress or pending bundle task by id.
509
+ #
510
+ # ec2.cancel_bundle_task('bun-73a7421a') #=>
511
+ # [{:s3_bucket => "my-awesome-bucket"
512
+ # :aws_id => "bun-0fa70206",
513
+ # :s3_prefix => "win02",
514
+ # :aws_start_time => "2008-10-14T13:00:29.000Z",
515
+ # :aws_error_message => "User has requested bundling operation cancellation",
516
+ # :aws_state => "failed",
517
+ # :aws_update_time => "2008-10-14T13:01:31.000Z",
518
+ # :aws_error_code => "Client.Cancelled",
519
+ # :aws_instance_id => "i-e3e24e8a"}
520
+ #
521
+ def cancel_bundle_task(bundle_id)
522
+ link = generate_request("CancelBundleTask", { 'BundleId' => bundle_id })
523
+ request_info(link, QEc2BundleInstanceParser.new)
524
+ rescue Exception
525
+ on_exception
526
+ end
527
+
528
+ #-----------------------------------------------------------------
529
+ # PARSERS: Instances
530
+ #-----------------------------------------------------------------
531
+
532
+ class QEc2DescribeInstancesParser < RightAWSParser #:nodoc:
533
+ def tagstart(name, attributes)
534
+ # DescribeInstances property
535
+ case full_tag_name
536
+ when 'DescribeInstancesResponse/reservationSet/item',
537
+ 'RunInstancesResponse'
538
+ @reservation = { :aws_groups => [],
539
+ :instances_set => [] }
540
+ when %r{instancesSet/item$}
541
+ # the optional params (sometimes are missing and we dont want them to be nil)
542
+ @item = { :aws_reason => '',
543
+ :dns_name => '',
544
+ :private_dns_name => '',
545
+ :ami_launch_index => '',
546
+ :ssh_key_name => '',
547
+ :aws_state => '',
548
+ :aws_product_codes => [] }
549
+ when %r{blockDeviceMapping/item$}
550
+ @item[:block_device_mappings] ||= []
551
+ @block_device_mapping = {}
552
+ end
553
+ end
554
+ def tagend(name)
555
+ case name
556
+ when 'reservationId' then @reservation[:aws_reservation_id] = @text
557
+ when 'ownerId' then @reservation[:aws_owner] = @text
558
+ when 'groupId' then @reservation[:aws_groups] << @text
559
+ when 'instanceId' then @item[:aws_instance_id] = @text
560
+ when 'imageId' then @item[:aws_image_id] = @text
561
+ when 'privateDnsName' then @item[:private_dns_name] = @text
562
+ when 'dnsName' then @item[:dns_name] = @text
563
+ when 'reason' then @item[:aws_reason] = @text
564
+ when 'keyName' then @item[:ssh_key_name] = @text
565
+ when 'amiLaunchIndex' then @item[:ami_launch_index] = @text
566
+ when 'productCode' then @item[:aws_product_codes] << @text
567
+ when 'instanceType' then @item[:aws_instance_type] = @text
568
+ when 'launchTime' then @item[:aws_launch_time] = @text
569
+ when 'availabilityZone' then @item[:aws_availability_zone] = @text
570
+ when 'kernelId' then @item[:aws_kernel_id] = @text
571
+ when 'ramdiskId' then @item[:aws_ramdisk_id] = @text
572
+ when 'platform' then @item[:aws_platform] = @text
573
+ when 'subnetId' then @item[:subnet_id] = @text
574
+ when 'vpcId' then @item[:vpc_id] = @text
575
+ when 'privateIpAddress' then @item[:private_ip_address] = @text
576
+ when 'ipAddress' then @item[:ip_address] = @text
577
+ when 'architecture' then @item[:architecture] = @text
578
+ when 'rootDeviceType' then @item[:root_device_type] = @text
579
+ when 'rootDeviceName' then @item[:root_device_name] = @text
580
+ when 'instanceClass' then @item[:instance_class] = @text
581
+ when 'instanceLifecycle' then @item[:instance_lifecycle] = @text
582
+ when 'spotInstanceRequestId' then @item[:spot_instance_request_id] = @text
583
+ when 'requesterId' then @item[:requester_id] = @text
584
+ else
585
+ case full_tag_name
586
+ when %r{/stateReason/code$} then @item[:state_reason_code] = @text
587
+ when %r{/stateReason/message$} then @item[:state_reason_message] = @text
588
+ when %r{/instanceState/code$} then @item[:aws_state_code] = @text.to_i
589
+ when %r{/instanceState/name$} then @item[:aws_state] = @text
590
+ when %r{/monitoring/state$} then @item[:monitoring_state] = @text
591
+ when %r{/blockDeviceMapping/item} # no trailing $
592
+ case name
593
+ when 'deviceName' then @block_device_mapping[:device_name] = @text
594
+ when 'virtualName' then @block_device_mapping[:virtual_name] = @text
595
+ when 'volumeId' then @block_device_mapping[:ebs_volume_id] = @text
596
+ when 'status' then @block_device_mapping[:ebs_status] = @text
597
+ when 'attachTime' then @block_device_mapping[:ebs_attach_time] = @text
598
+ when 'deleteOnTermination' then @block_device_mapping[:ebs_delete_on_termination] = @text == 'true' ? true : false
599
+ when 'item' then @item[:block_device_mappings] << @block_device_mapping
600
+ end
601
+ when %r{/instancesSet/item$} then @reservation[:instances_set] << @item
602
+ when 'DescribeInstancesResponse/reservationSet/item',
603
+ 'RunInstancesResponse'
604
+ @result << @reservation
605
+ end
606
+ end
607
+ end
608
+ def reset
609
+ @result = []
610
+ end
611
+ end
612
+
613
+ class QEc2ConfirmProductInstanceParser < RightAWSParser #:nodoc:
614
+ def tagend(name)
615
+ @result = @text if name == 'ownerId'
616
+ end
617
+ end
618
+
619
+ class QEc2TerminateInstancesParser < RightAWSParser #:nodoc:
620
+ def tagstart(name, attributes)
621
+ @instance = {} if name == 'item'
622
+ end
623
+ def tagend(name)
624
+ case full_tag_name
625
+ when %r{/instanceId$} then @instance[:aws_instance_id] = @text
626
+ when %r{/currentState/code$} then @instance[:aws_current_state_code] = @text.to_i
627
+ when %r{/currentState/name$} then @instance[:aws_current_state_name] = @text
628
+ when %r{/previousState/code$} then @instance[:aws_prev_state_code] = @text.to_i
629
+ when %r{/previousState/name$} then @instance[:aws_prev_state_name] = @text
630
+ when %r{/item$} then @result << @instance
631
+ end
632
+ end
633
+ def reset
634
+ @result = []
635
+ end
636
+ end
637
+
638
+ class QEc2DescribeInstanceAttributeParser < RightAWSParser #:nodoc:
639
+ def tagstart(name, attributes)
640
+ case full_tag_name
641
+ when %r{blockDeviceMapping$} then @result = []
642
+ when %r{blockDeviceMapping/item$} then @block_device_mapping = {}
643
+ end
644
+ end
645
+ def tagend(name)
646
+ case full_tag_name
647
+ when %r{/instanceType/value$} then @result = @text
648
+ when %r{/kernel$} then @result = @text
649
+ when %r{/ramdisk$} then @result = @text
650
+ when %r{/userData$} then @result = @text
651
+ when %r{/rootDeviceName/value$} then @result = @text
652
+ when %r{/disableApiTermination/value} then @result = @text == 'true' ? true : false
653
+ when %r{/instanceInitiatedShutdownBehavior/value$} then @result = @text
654
+ when %r{/blockDeviceMapping/item} # no trailing $
655
+ case name
656
+ when 'deviceName' then @block_device_mapping[:device_name] = @text
657
+ when 'virtualName' then @block_device_mapping[:virtual_name] = @text
658
+ when 'noDevice' then @block_device_mapping[:no_device] = @text
659
+ when 'volumeId' then @block_device_mapping[:ebs_volume_id] = @text
660
+ when 'status' then @block_device_mapping[:ebs_status] = @text
661
+ when 'attachTime' then @block_device_mapping[:ebs_attach_time] = @text
662
+ when 'deleteOnTermination' then @block_device_mapping[:ebs_delete_on_termination] = @text == 'true' ? true : false
663
+ when 'item' then @result << @block_device_mapping
664
+ end
665
+ end
666
+ end
667
+ def reset
668
+ @result = nil
669
+ end
670
+ end
671
+
672
+ #-----------------------------------------------------------------
673
+ # PARSERS: Console
674
+ #-----------------------------------------------------------------
675
+
676
+ class QEc2GetConsoleOutputParser < RightAWSParser #:nodoc:
677
+ def tagend(name)
678
+ case name
679
+ when 'instanceId' then @result[:aws_instance_id] = @text
680
+ when 'timestamp' then @result[:aws_timestamp] = @text
681
+ @result[:timestamp] = (Time.parse(@text)).utc
682
+ when 'output' then @result[:aws_output] = Base64.decode64(@text)
683
+ end
684
+ end
685
+ def reset
686
+ @result = {}
687
+ end
688
+ end
689
+
690
+ #-----------------------------------------------------------------
691
+ # Instances: Windows related part
692
+ #-----------------------------------------------------------------
693
+
694
+ class QEc2DescribeBundleTasksParser < RightAWSParser #:nodoc:
695
+ def tagstart(name, attributes)
696
+ @bundle = {} if name == 'item'
697
+ end
698
+ def tagend(name)
699
+ case name
700
+ # when 'requestId' then @bundle[:request_id] = @text
701
+ when 'instanceId' then @bundle[:aws_instance_id] = @text
702
+ when 'bundleId' then @bundle[:aws_id] = @text
703
+ when 'bucket' then @bundle[:s3_bucket] = @text
704
+ when 'prefix' then @bundle[:s3_prefix] = @text
705
+ when 'startTime' then @bundle[:aws_start_time] = @text
706
+ when 'updateTime' then @bundle[:aws_update_time] = @text
707
+ when 'state' then @bundle[:aws_state] = @text
708
+ when 'progress' then @bundle[:aws_progress] = @text
709
+ when 'code' then @bundle[:aws_error_code] = @text
710
+ when 'message' then @bundle[:aws_error_message] = @text
711
+ when 'item' then @result << @bundle
712
+ end
713
+ end
714
+ def reset
715
+ @result = []
716
+ end
717
+ end
718
+
719
+ class QEc2BundleInstanceParser < RightAWSParser #:nodoc:
720
+ def tagend(name)
721
+ case name
722
+ # when 'requestId' then @result[:request_id] = @text
723
+ when 'instanceId' then @result[:aws_instance_id] = @text
724
+ when 'bundleId' then @result[:aws_id] = @text
725
+ when 'bucket' then @result[:s3_bucket] = @text
726
+ when 'prefix' then @result[:s3_prefix] = @text
727
+ when 'startTime' then @result[:aws_start_time] = @text
728
+ when 'updateTime' then @result[:aws_update_time] = @text
729
+ when 'state' then @result[:aws_state] = @text
730
+ when 'progress' then @result[:aws_progress] = @text
731
+ when 'code' then @result[:aws_error_code] = @text
732
+ when 'message' then @result[:aws_error_message] = @text
733
+ end
734
+ end
735
+ def reset
736
+ @result = {}
737
+ end
738
+ end
739
+
740
+ class QEc2GetPasswordDataParser < RightAWSParser #:nodoc:
741
+ def tagend(name)
742
+ case name
743
+ when 'instanceId' then @result[:aws_instance_id] = @text
744
+ when 'timestamp' then @result[:timestamp] = @text
745
+ when 'passwordData' then @result[:password_data] = @text
746
+ end
747
+ end
748
+ def reset
749
+ @result = {}
750
+ end
751
+ end
752
+
753
+ end
754
+
755
+ end