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.
- data/.gemtest +0 -0
- data/History.txt +284 -0
- data/Manifest.txt +50 -0
- data/README.txt +167 -0
- data/Rakefile +110 -0
- data/lib/acf/right_acf_interface.rb +485 -0
- data/lib/acf/right_acf_origin_access_identities.rb +230 -0
- data/lib/acf/right_acf_streaming_interface.rb +236 -0
- data/lib/acw/right_acw_interface.rb +249 -0
- data/lib/as/right_as_interface.rb +699 -0
- data/lib/awsbase/benchmark_fix.rb +39 -0
- data/lib/awsbase/right_awsbase.rb +978 -0
- data/lib/awsbase/support.rb +115 -0
- data/lib/ec2/right_ec2.rb +395 -0
- data/lib/ec2/right_ec2_ebs.rb +452 -0
- data/lib/ec2/right_ec2_images.rb +373 -0
- data/lib/ec2/right_ec2_instances.rb +755 -0
- data/lib/ec2/right_ec2_monitoring.rb +70 -0
- data/lib/ec2/right_ec2_reserved_instances.rb +170 -0
- data/lib/ec2/right_ec2_security_groups.rb +277 -0
- data/lib/ec2/right_ec2_spot_instances.rb +399 -0
- data/lib/ec2/right_ec2_vpc.rb +571 -0
- data/lib/elb/right_elb_interface.rb +496 -0
- data/lib/rds/right_rds_interface.rb +998 -0
- data/lib/right_aws.rb +83 -0
- data/lib/s3/right_s3.rb +1126 -0
- data/lib/s3/right_s3_interface.rb +1199 -0
- data/lib/sdb/active_sdb.rb +1122 -0
- data/lib/sdb/right_sdb_interface.rb +721 -0
- data/lib/sqs/right_sqs.rb +388 -0
- data/lib/sqs/right_sqs_gen2.rb +343 -0
- data/lib/sqs/right_sqs_gen2_interface.rb +524 -0
- data/lib/sqs/right_sqs_interface.rb +594 -0
- data/test/acf/test_helper.rb +2 -0
- data/test/acf/test_right_acf.rb +138 -0
- data/test/ec2/test_helper.rb +2 -0
- data/test/ec2/test_right_ec2.rb +108 -0
- data/test/http_connection.rb +87 -0
- data/test/rds/test_helper.rb +2 -0
- data/test/rds/test_right_rds.rb +120 -0
- data/test/s3/test_helper.rb +2 -0
- data/test/s3/test_right_s3.rb +421 -0
- data/test/s3/test_right_s3_stubbed.rb +97 -0
- data/test/sdb/test_active_sdb.rb +357 -0
- data/test/sdb/test_helper.rb +3 -0
- data/test/sdb/test_right_sdb.rb +253 -0
- data/test/sqs/test_helper.rb +2 -0
- data/test/sqs/test_right_sqs.rb +291 -0
- data/test/sqs/test_right_sqs_gen2.rb +264 -0
- data/test/test_credentials.rb +37 -0
- data/test/ts_right_aws.rb +14 -0
- 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
|