right_aws 1.10.0 → 2.0.0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (39) hide show
  1. data/History.txt +53 -15
  2. data/Manifest.txt +16 -0
  3. data/README.txt +10 -9
  4. data/Rakefile +13 -15
  5. data/lib/acf/right_acf_interface.rb +224 -118
  6. data/lib/acf/right_acf_origin_access_identities.rb +230 -0
  7. data/lib/acf/right_acf_streaming_interface.rb +236 -0
  8. data/lib/acw/right_acw_interface.rb +249 -0
  9. data/lib/as/right_as_interface.rb +699 -0
  10. data/lib/awsbase/right_awsbase.rb +232 -51
  11. data/lib/awsbase/support.rb +4 -0
  12. data/lib/ec2/right_ec2.rb +33 -1375
  13. data/lib/ec2/right_ec2_ebs.rb +452 -0
  14. data/lib/ec2/right_ec2_images.rb +373 -0
  15. data/lib/ec2/right_ec2_instances.rb +755 -0
  16. data/lib/ec2/right_ec2_monitoring.rb +70 -0
  17. data/lib/ec2/right_ec2_reserved_instances.rb +170 -0
  18. data/lib/ec2/right_ec2_security_groups.rb +280 -0
  19. data/lib/ec2/right_ec2_spot_instances.rb +399 -0
  20. data/lib/ec2/right_ec2_vpc.rb +571 -0
  21. data/lib/elb/right_elb_interface.rb +496 -0
  22. data/lib/rds/right_rds_interface.rb +998 -0
  23. data/lib/right_aws.rb +18 -4
  24. data/lib/s3/right_s3.rb +39 -7
  25. data/lib/s3/right_s3_interface.rb +77 -53
  26. data/lib/sdb/active_sdb.rb +203 -11
  27. data/lib/sdb/right_sdb_interface.rb +68 -45
  28. data/lib/sqs/right_sqs_gen2.rb +73 -16
  29. data/lib/sqs/right_sqs_gen2_interface.rb +131 -51
  30. data/lib/sqs/right_sqs_interface.rb +2 -4
  31. data/test/acf/test_right_acf.rb +10 -18
  32. data/test/rds/test_helper.rb +2 -0
  33. data/test/rds/test_right_rds.rb +120 -0
  34. data/test/s3/test_right_s3.rb +10 -8
  35. data/test/s3/test_right_s3_stubbed.rb +6 -4
  36. data/test/sdb/test_active_sdb.rb +70 -12
  37. data/test/sdb/test_right_sdb.rb +13 -7
  38. data/test/sqs/test_right_sqs_gen2.rb +104 -49
  39. metadata +103 -14
@@ -0,0 +1,452 @@
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
+ # EBS: Volumes
30
+ #-----------------------------------------------------------------
31
+
32
+ # Describe all EBS volumes.
33
+ #
34
+ # ec2.describe_volumes #=>
35
+ # [{:aws_size => 94,
36
+ # :aws_device => "/dev/sdc",
37
+ # :aws_attachment_status => "attached",
38
+ # :zone => "merlot",
39
+ # :snapshot_id => nil,
40
+ # :aws_attached_at => "2008-06-18T08:19:28.000Z",
41
+ # :aws_status => "in-use",
42
+ # :aws_id => "vol-60957009",
43
+ # :aws_created_at => "2008-06-18T08:19:20.000Z",
44
+ # :aws_instance_id => "i-c014c0a9"},
45
+ # {:aws_size => 1,
46
+ # :zone => "merlot",
47
+ # :snapshot_id => nil,
48
+ # :aws_status => "available",
49
+ # :aws_id => "vol-58957031",
50
+ # :aws_created_at => Wed Jun 18 08:19:21 UTC 2008,}, ... ]
51
+ #
52
+ def describe_volumes(*volumes)
53
+ volumes = volumes.flatten
54
+ link = generate_request("DescribeVolumes", amazonize_list('VolumeId', volumes))
55
+ request_cache_or_info :describe_volumes, link, QEc2DescribeVolumesParser, @@bench, volumes.blank?
56
+ rescue Exception
57
+ on_exception
58
+ end
59
+
60
+ # Create new EBS volume based on previously created snapshot.
61
+ # +Size+ in Gigabytes.
62
+ #
63
+ # ec2.create_volume('snap-000000', 10, zone) #=>
64
+ # {:snapshot_id => "snap-e21df98b",
65
+ # :aws_status => "creating",
66
+ # :aws_id => "vol-fc9f7a95",
67
+ # :zone => "merlot",
68
+ # :aws_created_at => "2008-06-24T18:13:32.000Z",
69
+ # :aws_size => 94}
70
+ #
71
+ def create_volume(snapshot_id, size, zone)
72
+ hash = { "Size" => size.to_s,
73
+ "AvailabilityZone" => zone.to_s }
74
+ # Get rig of empty snapshot: e8s guys do not like it
75
+ hash["SnapshotId"] = snapshot_id.to_s unless snapshot_id.blank?
76
+ link = generate_request("CreateVolume", hash )
77
+ request_info(link, QEc2CreateVolumeParser.new(:logger => @logger))
78
+ rescue Exception
79
+ on_exception
80
+ end
81
+
82
+ # Delete the specified EBS volume.
83
+ # This does not deletes any snapshots created from this volume.
84
+ #
85
+ # ec2.delete_volume('vol-b48a6fdd') #=> true
86
+ #
87
+ def delete_volume(volume_id)
88
+ link = generate_request("DeleteVolume",
89
+ "VolumeId" => volume_id.to_s)
90
+ request_info(link, RightBoolResponseParser.new(:logger => @logger))
91
+ rescue Exception
92
+ on_exception
93
+ end
94
+
95
+ # Attach the specified EBS volume to a specified instance, exposing the
96
+ # volume using the specified device name.
97
+ #
98
+ # ec2.attach_volume('vol-898a6fe0', 'i-7c905415', '/dev/sdh') #=>
99
+ # { :aws_instance_id => "i-7c905415",
100
+ # :aws_device => "/dev/sdh",
101
+ # :aws_status => "attaching",
102
+ # :aws_attached_at => "2008-03-28T14:14:39.000Z",
103
+ # :aws_id => "vol-898a6fe0" }
104
+ #
105
+ def attach_volume(volume_id, instance_id, device)
106
+ link = generate_request("AttachVolume",
107
+ "VolumeId" => volume_id.to_s,
108
+ "InstanceId" => instance_id.to_s,
109
+ "Device" => device.to_s)
110
+ request_info(link, QEc2AttachAndDetachVolumeParser.new(:logger => @logger))
111
+ rescue Exception
112
+ on_exception
113
+ end
114
+
115
+ # Detach the specified EBS volume from the instance to which it is attached.
116
+ #
117
+ # ec2.detach_volume('vol-898a6fe0') #=>
118
+ # { :aws_instance_id => "i-7c905415",
119
+ # :aws_device => "/dev/sdh",
120
+ # :aws_status => "detaching",
121
+ # :aws_attached_at => "2008-03-28T14:38:34.000Z",
122
+ # :aws_id => "vol-898a6fe0"}
123
+ #
124
+ def detach_volume(volume_id, instance_id=nil, device=nil, force=nil)
125
+ hash = { "VolumeId" => volume_id.to_s }
126
+ hash["InstanceId"] = instance_id.to_s unless instance_id.blank?
127
+ hash["Device"] = device.to_s unless device.blank?
128
+ hash["Force"] = 'true' if force
129
+ #
130
+ link = generate_request("DetachVolume", hash)
131
+ request_info(link, QEc2AttachAndDetachVolumeParser.new(:logger => @logger))
132
+ rescue Exception
133
+ on_exception
134
+ end
135
+
136
+
137
+ #-----------------------------------------------------------------
138
+ # EBS: Snapshots
139
+ #-----------------------------------------------------------------
140
+
141
+ # Describe all EBS snapshots.
142
+ #
143
+ # ec2.describe_snapshots #=>
144
+ # [ {:aws_volume_id=>"vol-545fac3d",
145
+ # :aws_description=>"Wikipedia XML Backups (Linux)",
146
+ # :aws_progress=>"100%",
147
+ # :aws_started_at=>"2009-09-28T23:49:50.000Z",
148
+ # :aws_owner=>"amazon",
149
+ # :aws_id=>"snap-8041f2e9",
150
+ # :aws_volume_size=>500,
151
+ # :aws_status=>"completed"},
152
+ # {:aws_volume_id=>"vol-185fac71",
153
+ # :aws_description=>"Sloan Digital Sky Survey DR6 Subset (Linux)",
154
+ # :aws_progress=>"100%",
155
+ # :aws_started_at=>"2009-09-28T23:56:10.000Z",
156
+ # :aws_owner=>"amazon",
157
+ # :aws_id=>"snap-3740f35e",
158
+ # :aws_volume_size=>180,
159
+ # :aws_status=>"completed"}, ...]
160
+ #
161
+ def describe_snapshots(*snapshots)
162
+ snapshots = snapshots.flatten
163
+ link = generate_request("DescribeSnapshots", amazonize_list('SnapshotId', snapshots))
164
+ request_cache_or_info :describe_snapshots, link, QEc2DescribeSnapshotsParser, @@bench, snapshots.blank?
165
+ rescue Exception
166
+ on_exception
167
+ end
168
+
169
+ # Create a snapshot of specified volume.
170
+ #
171
+ # ec2.create_snapshot('vol-898a6fe0', 'KD: WooHoo!!') #=>
172
+ # {:aws_volume_id=>"vol-e429db8d",
173
+ # :aws_started_at=>"2009-10-01T09:23:38.000Z",
174
+ # :aws_description=>"KD: WooHoo!!",
175
+ # :aws_owner=>"648770000000",
176
+ # :aws_progress=>"",
177
+ # :aws_status=>"pending",
178
+ # :aws_volume_size=>1,
179
+ # :aws_id=>"snap-3df54854"}
180
+ #
181
+ def create_snapshot(volume_id, description='')
182
+ link = generate_request("CreateSnapshot",
183
+ "VolumeId" => volume_id.to_s,
184
+ "Description" => description)
185
+ request_info(link, QEc2DescribeSnapshotsParser.new(:logger => @logger)).first
186
+ rescue Exception
187
+ on_exception
188
+ end
189
+
190
+ # Create a snapshot of specified volume, but with the normal retry algorithms disabled.
191
+ # This method will return immediately upon error. The user can specify connect and read timeouts (in s)
192
+ # for the connection to AWS. If the user does not specify timeouts, try_create_snapshot uses the default values
193
+ # in Rightscale::HttpConnection.
194
+ #
195
+ # ec2.try_create_snapshot('vol-898a6fe0', 'KD: WooHoo!!') #=>
196
+ # {:aws_volume_id=>"vol-e429db8d",
197
+ # :aws_started_at=>"2009-10-01T09:23:38.000Z",
198
+ # :aws_description=>"KD: WooHoo!!",
199
+ # :aws_owner=>"648770000000",
200
+ # :aws_progress=>"",
201
+ # :aws_status=>"pending",
202
+ # :aws_volume_size=>1,
203
+ # :aws_id=>"snap-3df54854"}
204
+ #
205
+ def try_create_snapshot(volume_id, connect_timeout = nil, read_timeout = nil, description='')
206
+ # For safety in the ensure block...we don't want to restore values
207
+ # if we never read them in the first place
208
+ orig_reiteration_time = nil
209
+ orig_http_params = nil
210
+
211
+ orig_reiteration_time = RightAws::AWSErrorHandler::reiteration_time
212
+ RightAws::AWSErrorHandler::reiteration_time = 0
213
+
214
+ orig_http_params = Rightscale::HttpConnection::params()
215
+ new_http_params = orig_http_params.dup
216
+ new_http_params[:http_connection_retry_count] = 0
217
+ new_http_params[:http_connection_open_timeout] = connect_timeout if !connect_timeout.nil?
218
+ new_http_params[:http_connection_read_timeout] = read_timeout if !read_timeout.nil?
219
+ Rightscale::HttpConnection::params = new_http_params
220
+
221
+ link = generate_request("CreateSnapshot",
222
+ "VolumeId" => volume_id.to_s,
223
+ "Description" => description)
224
+ request_info(link, QEc2DescribeSnapshotsParser.new(:logger => @logger)).first
225
+
226
+ rescue Exception
227
+ on_exception
228
+ ensure
229
+ RightAws::AWSErrorHandler::reiteration_time = orig_reiteration_time if orig_reiteration_time
230
+ Rightscale::HttpConnection::params = orig_http_params if orig_http_params
231
+ end
232
+
233
+ # Describe snapshot attribute.
234
+ #
235
+ # ec2.describe_snapshot_attribute('snap-36fe435f') #=>
236
+ # {:create_volume_permission=>
237
+ # {:users=>["826690000000", "826690000001"], :groups=>['all']}}
238
+ #
239
+ def describe_snapshot_attribute(snapshot_id, attribute='createVolumePermission')
240
+ link = generate_request("DescribeSnapshotAttribute",
241
+ 'SnapshotId'=> snapshot_id,
242
+ 'Attribute' => attribute)
243
+ request_info(link, QEc2DescribeSnapshotAttributeParser.new(:logger => @logger))
244
+ rescue Exception
245
+ on_exception
246
+ end
247
+
248
+ # Reset permission settings for the specified snapshot.
249
+ #
250
+ # ec2.reset_snapshot_attribute('snap-cecd29a7') #=> true
251
+ #
252
+ def reset_snapshot_attribute(snapshot_id, attribute='createVolumePermission')
253
+ link = generate_request("ResetSnapshotAttribute",
254
+ 'SnapshotId' => snapshot_id,
255
+ 'Attribute' => attribute)
256
+ request_info(link, RightBoolResponseParser.new(:logger => @logger))
257
+ rescue Exception
258
+ on_exception
259
+ end
260
+
261
+ # Modify snapshot attribute.
262
+ #
263
+ # attribute : currently, only 'createVolumePermission' is supported.
264
+ # operation_type : currently, only 'add' & 'remove' are supported.
265
+ # vars:
266
+ # :user_group : currently, only 'all' is supported.
267
+ # :user_id : an array of user ids
268
+ #
269
+ def modify_snapshot_attribute(snapshot_id, attribute='createVolumePermission', operation_type='add', vars = {})
270
+ params = {'SnapshotId' => snapshot_id,
271
+ 'Attribute' => attribute,
272
+ 'OperationType' => operation_type}
273
+ params.update(amazonize_list('UserId', Array(vars[:user_id]))) if vars[:user_id]
274
+ params.update(amazonize_list('UserGroup', Array(vars[:user_group]))) if vars[:user_group]
275
+ link = generate_request("ModifySnapshotAttribute", params)
276
+ request_info(link, RightBoolResponseParser.new(:logger => @logger))
277
+ rescue Exception
278
+ on_exception
279
+ end
280
+
281
+ # Grant create volume permission for a list of users.
282
+ #
283
+ # ec2.modify_snapshot_attribute_create_volume_permission_add_users('snap-36fe435f', '000000000000', '000000000001') #=> true
284
+ #
285
+ def modify_snapshot_attribute_create_volume_permission_add_users(snapshot_id, *user_id)
286
+ modify_snapshot_attribute(snapshot_id, 'createVolumePermission', 'add', :user_id => user_id.flatten )
287
+ end
288
+
289
+ # Revoke create volume permission for a list of users.
290
+ #
291
+ # ec2.modify_snapshot_attribute_create_volume_permission_remove_users('snap-36fe435f', '000000000000', '000000000001') #=> true
292
+ #
293
+ def modify_snapshot_attribute_create_volume_permission_remove_users(snapshot_id, *user_id)
294
+ modify_snapshot_attribute(snapshot_id, 'createVolumePermission', 'remove', :user_id => user_id.flatten )
295
+ end
296
+
297
+ # Grant create volume permission for user groups (currently only 'all' is supported).
298
+ #
299
+ # ec2.modify_snapshot_attribute_create_volume_permission_add_groups('snap-36fe435f') #=> true
300
+ #
301
+ def modify_snapshot_attribute_create_volume_permission_add_groups(snapshot_id, *user_group)
302
+ user_group.flatten!
303
+ user_group = ['all'] if user_group.blank?
304
+ modify_snapshot_attribute(snapshot_id, 'createVolumePermission', 'add', :user_group => user_group )
305
+ end
306
+
307
+ # Remove create volume permission for user groups (currently only 'all' is supported).
308
+ #
309
+ # ec2.modify_snapshot_attribute_create_volume_permission_remove_groups('snap-36fe435f') #=> true
310
+ #
311
+ def modify_snapshot_attribute_create_volume_permission_remove_groups(snapshot_id, *user_group)
312
+ user_group.flatten!
313
+ user_group = ['all'] if user_group.blank?
314
+ modify_snapshot_attribute(snapshot_id, 'createVolumePermission', 'remove', :user_group => user_group )
315
+ end
316
+
317
+ # Delete the specified snapshot.
318
+ #
319
+ # ec2.delete_snapshot('snap-55a5403c') #=> true
320
+ #
321
+ def delete_snapshot(snapshot_id)
322
+ link = generate_request("DeleteSnapshot",
323
+ "SnapshotId" => snapshot_id.to_s)
324
+ request_info(link, RightBoolResponseParser.new(:logger => @logger))
325
+ rescue Exception
326
+ on_exception
327
+ end
328
+
329
+ #-----------------------------------------------------------------
330
+ # PARSERS: EBS - Volumes
331
+ #-----------------------------------------------------------------
332
+
333
+ class QEc2CreateVolumeParser < RightAWSParser #:nodoc:
334
+ def tagend(name)
335
+ case name
336
+ when 'volumeId' then @result[:aws_id] = @text
337
+ when 'status' then @result[:aws_status] = @text
338
+ when 'createTime' then @result[:aws_created_at] = @text
339
+ when 'size' then @result[:aws_size] = @text.to_i ###
340
+ when 'snapshotId' then @result[:snapshot_id] = @text.blank? ? nil : @text ###
341
+ when 'availabilityZone' then @result[:zone] = @text ###
342
+ end
343
+ end
344
+ def reset
345
+ @result = {}
346
+ end
347
+ end
348
+
349
+ class QEc2AttachAndDetachVolumeParser < RightAWSParser #:nodoc:
350
+ def tagend(name)
351
+ case name
352
+ when 'volumeId' then @result[:aws_id] = @text
353
+ when 'instanceId' then @result[:aws_instance_id] = @text
354
+ when 'device' then @result[:aws_device] = @text
355
+ when 'status' then @result[:aws_attachment_status] = @text
356
+ when 'attachTime' then @result[:aws_attached_at] = @text
357
+ end
358
+ end
359
+ def reset
360
+ @result = {}
361
+ end
362
+ end
363
+
364
+ class QEc2DescribeVolumesParser < RightAWSParser #:nodoc:
365
+ def tagstart(name, attributes)
366
+ case name
367
+ when 'item'
368
+ case @xmlpath
369
+ when 'DescribeVolumesResponse/volumeSet' then @volume = {}
370
+ end
371
+ end
372
+ end
373
+ def tagend(name)
374
+ case name
375
+ when 'volumeId'
376
+ case @xmlpath
377
+ when 'DescribeVolumesResponse/volumeSet/item' then @volume[:aws_id] = @text
378
+ end
379
+ when 'status'
380
+ case @xmlpath
381
+ when 'DescribeVolumesResponse/volumeSet/item' then @volume[:aws_status] = @text
382
+ when 'DescribeVolumesResponse/volumeSet/item/attachmentSet/item' then @volume[:aws_attachment_status] = @text
383
+ end
384
+ when 'size' then @volume[:aws_size] = @text.to_i
385
+ when 'createTime' then @volume[:aws_created_at] = @text
386
+ when 'instanceId' then @volume[:aws_instance_id] = @text
387
+ when 'device' then @volume[:aws_device] = @text
388
+ when 'attachTime' then @volume[:aws_attached_at] = @text
389
+ when 'snapshotId' then @volume[:snapshot_id] = @text.blank? ? nil : @text
390
+ when 'availabilityZone' then @volume[:zone] = @text
391
+ when 'deleteOnTermination' then @volume[:delete_on_termination] = (@text == 'true')
392
+ when 'item'
393
+ case @xmlpath
394
+ when 'DescribeVolumesResponse/volumeSet' then @result << @volume
395
+ end
396
+ end
397
+ end
398
+ def reset
399
+ @result = []
400
+ end
401
+ end
402
+
403
+ #-----------------------------------------------------------------
404
+ # PARSERS: EBS - Snapshots
405
+ #-----------------------------------------------------------------
406
+
407
+ class QEc2DescribeSnapshotsParser < RightAWSParser #:nodoc:
408
+ def tagstart(name, attributes)
409
+ case name
410
+ when *@each then @snapshot = {}
411
+ end
412
+ end
413
+ def tagend(name)
414
+ case name
415
+ when 'volumeId' then @snapshot[:aws_volume_id] = @text
416
+ when 'snapshotId' then @snapshot[:aws_id] = @text
417
+ when 'status' then @snapshot[:aws_status] = @text
418
+ when 'startTime' then @snapshot[:aws_started_at] = @text
419
+ when 'progress' then @snapshot[:aws_progress] = @text
420
+ when 'description' then @snapshot[:aws_description] = @text
421
+ when 'ownerId' then @snapshot[:aws_owner] = @text
422
+ when 'volumeSize' then @snapshot[:aws_volume_size] = @text.to_i
423
+ when *@each then @result << @snapshot
424
+ end
425
+ end
426
+ def reset
427
+ @each = ['item', 'CreateSnapshotResponse']
428
+ @result = []
429
+ end
430
+ end
431
+
432
+ class QEc2DescribeSnapshotAttributeParser < RightAWSParser #:nodoc:
433
+ def tagstart(name, attributes)
434
+ case name
435
+ when 'createVolumePermission' then @result[:create_volume_permission] = { :groups => [], :users => [] }
436
+ end
437
+ end
438
+ def tagend(name)
439
+ case full_tag_name
440
+ when "#{@create_volume_permission}/group" then @result[:create_volume_permission][:groups] << @text
441
+ when "#{@create_volume_permission}/userId" then @result[:create_volume_permission][:users] << @text
442
+ end
443
+ end
444
+ def reset
445
+ @create_volume_permission = "DescribeSnapshotAttributeResponse/createVolumePermission/item"
446
+ @result = {}
447
+ end
448
+ end
449
+
450
+ end
451
+
452
+ end
@@ -0,0 +1,373 @@
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
+ # Images
30
+ #-----------------------------------------------------------------
31
+
32
+ # Describe images helper
33
+ # params:
34
+ # { 'ImageId' => ['id1', ..., 'idN'],
35
+ # 'Owner' => ['self', ..., 'userN'],
36
+ # 'ExecutableBy' => ['self', 'all', ..., 'userN']
37
+ # }
38
+ def ec2_describe_images(params={}, image_type=nil, cache_for=nil) #:nodoc:
39
+ request_hash = {}
40
+ params.each do |list_by, list|
41
+ request_hash.merge! amazonize_list(list_by, Array(list))
42
+ end
43
+ request_hash['ImageType'] = image_type if image_type
44
+ link = generate_request("DescribeImages", request_hash)
45
+ request_cache_or_info cache_for, link, QEc2DescribeImagesParser, @@bench, cache_for
46
+ rescue Exception
47
+ on_exception
48
+ end
49
+
50
+ # Retrieve a list of images. Returns array of hashes describing the images or an exception:
51
+ # +image_type+ = 'machine' || 'kernel' || 'ramdisk'
52
+ #
53
+ # ec2.describe_images #=>
54
+ # [{:aws_id=>"ami-b0a1f7a6",
55
+ # :aws_image_type=>"machine",
56
+ # :root_device_name=>"/dev/sda1",
57
+ # :image_class=>"static",
58
+ # :aws_owner=>"826693181925",
59
+ # :aws_location=>"bucket_for_k_dzreyev/image_bundles/kd__CentOS_1_10_2009_10_21_13_30_43_MSD/image.manifest.xml",
60
+ # :aws_state=>"available",
61
+ # :aws_is_public=>false,
62
+ # :aws_architecture=>"i386"},
63
+ # {:description=>"EBS backed Fedora core 8 i386",
64
+ # :aws_architecture=>"i386",
65
+ # :aws_id=>"ami-c2a3f5d4",
66
+ # :aws_image_type=>"machine",
67
+ # :root_device_name=>"/dev/sda1",
68
+ # :image_class=>"elastic",
69
+ # :aws_owner=>"937766719418",
70
+ # :aws_location=>"937766719418/EBS backed FC8 i386",
71
+ # :aws_state=>"available",
72
+ # :block_device_mappings=>
73
+ # [{:ebs_snapshot_id=>"snap-829a20eb",
74
+ # :ebs_delete_on_termination=>true,
75
+ # :device_name=>"/dev/sda1"}],
76
+ # :name=>"EBS backed FC8 i386",
77
+ # :aws_is_public=>true}, ... ]
78
+ #
79
+ # If +list+ param is set, then retrieve information about the listed images only:
80
+ #
81
+ # ec2.describe_images(['ami-5aa1f74c'])
82
+ #
83
+ def describe_images(images=[], image_type=nil)
84
+ images = Array(images)
85
+ cache_for = images.empty? && !image_type ? :describe_images : nil
86
+ ec2_describe_images({ 'ImageId' => images }, image_type, cache_for)
87
+ end
88
+
89
+ # Example:
90
+ #
91
+ # ec2.describe_images_by_owner('522821470517')
92
+ # ec2.describe_images_by_owner('self')
93
+ #
94
+ def describe_images_by_owner(owners=['self'], image_type=nil)
95
+ owners = Array(owners)
96
+ cache_for = (owners == ['self']) && (!image_type ? :describe_images_by_owner : nil)
97
+ ec2_describe_images({ 'Owner' => owners }, image_type, cache_for)
98
+ end
99
+
100
+ # Example:
101
+ #
102
+ # ec2.describe_images_by_executable_by('522821470517')
103
+ # ec2.describe_images_by_executable_by('self')
104
+ # ec2.describe_images_by_executable_by('all')
105
+ #
106
+ def describe_images_by_executable_by(executable_by=['self'], image_type=nil)
107
+ executable_by = Array(executable_by)
108
+ cache_for = (executable_by==['self']) && (!image_type ? :describe_images_by_executable_by : nil)
109
+ ec2_describe_images({ 'ExecutableBy' => executable_by }, image_type, cache_for)
110
+ end
111
+
112
+
113
+ # Register new image at Amazon.
114
+ # Options: :image_location, :name, :description, :architecture, :kernel_id, :ramdisk_id, :root_device_name, :block_device_mappings.
115
+ #
116
+ # Returns new image id.
117
+ #
118
+ # # Register S3 image
119
+ # ec2.register_image('bucket_for_k_dzreyev/image_bundles/kd__CentOS_1_10_2009_10_21_13_30_43_MSD/image.manifest.xml') #=> 'ami-e444444d'
120
+ #
121
+ # # or
122
+ # image_reg_params = { :image_location => 'bucket_for_k_dzreyev/image_bundles/kd__CentOS_1_10_2009_10_21_13_30_43_MSD/image.manifest.xml',
123
+ # :name => 'my-test-one-1',
124
+ # :description => 'My first test image' }
125
+ # ec2.register_image(image_reg_params) #=> "ami-bca1f7aa"
126
+ #
127
+ # # Register EBS image
128
+ # image_reg_params = { :name => 'my-test-image',
129
+ # :description => 'My first test image',
130
+ # :root_device_name => "/dev/sda1",
131
+ # :block_device_mappings => [ { :ebs_snapshot_id=>"snap-7360871a",
132
+ # :ebs_delete_on_termination=>true,
133
+ # :device_name=>"/dev/sda1"} ] }
134
+ # ec2.register_image(image_reg_params) #=> "ami-b2a1f7a4"
135
+ #
136
+ def register_image(options)
137
+ case
138
+ when options.is_a?(String)
139
+ options = { :image_location => options }
140
+ when !options.is_a?(Hash)
141
+ raise "Unsupported options type"
142
+ end
143
+ params = {}
144
+ params['ImageLocation'] = options[:image_location] if options[:image_location]
145
+ params['Name'] = options[:name] if options[:name]
146
+ params['Description'] = options[:description] if options[:description]
147
+ params['Architecture'] = options[:architecture] if options[:architecture]
148
+ params['KernelId'] = options[:kernel_id] if options[:kernel_id]
149
+ params['RamdiskId'] = options[:ramdisk_id] if options[:ramdisk_id]
150
+ params['RootDeviceName'] = options[:root_device_name] if options[:root_device_name]
151
+ # params['SnapshotId'] = options[:snapshot_id] if options[:snapshot_id]
152
+ params.merge!(amazonize_block_device_mappings(options[:block_device_mappings]))
153
+ link = generate_request("RegisterImage", params)
154
+ request_info(link, QEc2RegisterImageParser.new(:logger => @logger))
155
+ rescue Exception
156
+ on_exception
157
+ end
158
+
159
+ # Deregister image at Amazon. Returns +true+ or an exception.
160
+ #
161
+ # ec2.deregister_image('ami-e444444d') #=> true
162
+ #
163
+ def deregister_image(image_id)
164
+ link = generate_request("DeregisterImage",
165
+ 'ImageId' => image_id.to_s)
166
+ request_info(link, RightBoolResponseParser.new(:logger => @logger))
167
+ rescue Exception
168
+ on_exception
169
+ end
170
+
171
+ # Describe image attributes. Currently 'launchPermission', 'productCodes', 'kernel', 'ramdisk' and 'blockDeviceMapping' are supported.
172
+ #
173
+ # ec2.describe_image_attribute('ami-e444444d') #=> {:groups=>["all"], :users=>["000000000777"]}
174
+ #
175
+ def describe_image_attribute(image_id, attribute='launchPermission')
176
+ link = generate_request("DescribeImageAttribute",
177
+ 'ImageId' => image_id,
178
+ 'Attribute' => attribute)
179
+ request_info(link, QEc2DescribeImageAttributeParser.new(:logger => @logger))
180
+ rescue Exception
181
+ on_exception
182
+ end
183
+
184
+ # Reset image attribute. Currently, only 'launchPermission' is supported. Returns +true+ or an exception.
185
+ #
186
+ # ec2.reset_image_attribute('ami-e444444d') #=> true
187
+ #
188
+ def reset_image_attribute(image_id, attribute='launchPermission')
189
+ link = generate_request("ResetImageAttribute",
190
+ 'ImageId' => image_id,
191
+ 'Attribute' => attribute)
192
+ request_info(link, RightBoolResponseParser.new(:logger => @logger))
193
+ rescue Exception
194
+ on_exception
195
+ end
196
+
197
+ # Modify an image's attributes. It is recommended that you use
198
+ # modify_image_launch_perm_add_users, modify_image_launch_perm_remove_users, etc.
199
+ # instead of modify_image_attribute because the signature of
200
+ # modify_image_attribute may change with EC2 service changes.
201
+ #
202
+ # attribute : currently, only 'launchPermission' is supported.
203
+ # operation_type : currently, only 'add' & 'remove' are supported.
204
+ # vars:
205
+ # :user_group : currently, only 'all' is supported.
206
+ # :user_id
207
+ # :product_code
208
+ def modify_image_attribute(image_id, attribute, operation_type = nil, vars = {})
209
+ params = {'ImageId' => image_id,
210
+ 'Attribute' => attribute}
211
+ params['OperationType'] = operation_type if operation_type
212
+ params.update(amazonize_list('UserId', vars[:user_id])) if vars[:user_id]
213
+ params.update(amazonize_list('UserGroup', vars[:user_group])) if vars[:user_group]
214
+ params.update(amazonize_list('ProductCode', vars[:product_code])) if vars[:product_code]
215
+ link = generate_request("ModifyImageAttribute", params)
216
+ request_info(link, RightBoolResponseParser.new(:logger => @logger))
217
+ rescue Exception
218
+ on_exception
219
+ end
220
+
221
+ # Grant image launch permissions to users.
222
+ # Parameter +user_id+ is a list of user AWS account ids.
223
+ # Returns +true+ or an exception.
224
+ #
225
+ # ec2.modify_image_launch_perm_add_users('ami-e444444d',['000000000777','000000000778']) #=> true
226
+ def modify_image_launch_perm_add_users(image_id, user_id=[])
227
+ modify_image_attribute(image_id, 'launchPermission', 'add', :user_id => user_id)
228
+ end
229
+
230
+ # Revokes image launch permissions for users. +user_id+ is a list of users AWS accounts ids. Returns +true+ or an exception.
231
+ #
232
+ # ec2.modify_image_launch_perm_remove_users('ami-e444444d',['000000000777','000000000778']) #=> true
233
+ #
234
+ def modify_image_launch_perm_remove_users(image_id, user_id=[])
235
+ modify_image_attribute(image_id, 'launchPermission', 'remove', :user_id => user_id)
236
+ end
237
+
238
+ # Add image launch permissions for users groups (currently only 'all' is supported, which gives public launch permissions).
239
+ # Returns +true+ or an exception.
240
+ #
241
+ # ec2.modify_image_launch_perm_add_groups('ami-e444444d') #=> true
242
+ #
243
+ def modify_image_launch_perm_add_groups(image_id, user_group=['all'])
244
+ modify_image_attribute(image_id, 'launchPermission', 'add', :user_group => user_group)
245
+ end
246
+
247
+ # Remove image launch permissions for users groups (currently only 'all' is supported, which gives public launch permissions).
248
+ #
249
+ # ec2.modify_image_launch_perm_remove_groups('ami-e444444d') #=> true
250
+ #
251
+ def modify_image_launch_perm_remove_groups(image_id, user_group=['all'])
252
+ modify_image_attribute(image_id, 'launchPermission', 'remove', :user_group => user_group)
253
+ end
254
+
255
+ # Add product code to image
256
+ #
257
+ # ec2.modify_image_product_code('ami-e444444d','0ABCDEF') #=> true
258
+ #
259
+ def modify_image_product_code(image_id, product_code=[])
260
+ modify_image_attribute(image_id, 'productCodes', nil, :product_code => product_code)
261
+ end
262
+
263
+ # Create a new image.
264
+ # Options: :name, :description, :no_reboot(bool)
265
+ #
266
+ # ec2.create_image(instance, :description => 'KD: test#1',
267
+ # :no_reboot => true,
268
+ # :name => 'kd-1' ) #=> "ami-84a1f792"
269
+ #
270
+ def create_image(instance_aws_id, options={})
271
+ params = { 'InstanceId' => instance_aws_id }
272
+ params['Name'] = options[:name] unless options[:name].blank?
273
+ params['Description'] = options[:description] unless options[:description].blank?
274
+ params['NoReboot'] = options[:no_reboot].to_s unless options[:no_reboot].nil?
275
+ link = generate_request("CreateImage", params)
276
+ request_info(link, QEc2RegisterImageParser.new(:logger => @logger))
277
+ end
278
+
279
+ #-----------------------------------------------------------------
280
+ # PARSERS: Images
281
+ #-----------------------------------------------------------------
282
+
283
+ class QEc2DescribeImagesParser < RightAWSParser #:nodoc:
284
+ def tagstart(name, attributes)
285
+ case full_tag_name
286
+ when %r{/imagesSet/item$} then @item = {}
287
+ when %r{/blockDeviceMapping/item$}
288
+ @item[:block_device_mappings] ||= []
289
+ @block_device_mapping = {}
290
+ end
291
+ end
292
+ def tagend(name)
293
+ case name
294
+ when 'imageId' then @item[:aws_id] = @text
295
+ when 'imageLocation' then @item[:aws_location] = @text
296
+ when 'imageState' then @item[:aws_state] = @text
297
+ when 'imageOwnerId' then @item[:aws_owner] = @text
298
+ when 'isPublic' then @item[:aws_is_public] = @text == 'true' ? true : false
299
+ when 'productCode' then (@item[:aws_product_codes] ||= []) << @text
300
+ when 'architecture' then @item[:aws_architecture] = @text
301
+ when 'imageType' then @item[:aws_image_type] = @text
302
+ when 'kernelId' then @item[:aws_kernel_id] = @text
303
+ when 'ramdiskId' then @item[:aws_ramdisk_id] = @text
304
+ when 'platform' then @item[:aws_platform] = @text
305
+ when 'imageOwnerAlias' then @item[:image_owner_alias] = @text
306
+ when 'name' then @item[:name] = @text
307
+ when 'description' then @item[:description] = @text
308
+ when 'rootDeviceType' then @item[:root_device_type] = @text
309
+ when 'rootDeviceName' then @item[:root_device_name] = @text
310
+ when 'imageClass' then @item[:image_class] = @text
311
+ else
312
+ case full_tag_name
313
+ when %r{/stateReason/code$} then @item[:state_reason_code] = @text.to_i
314
+ when %r{/stateReason/message$} then @item[:state_reason_message] = @text
315
+ when %r{/blockDeviceMapping/item} # no trailing $
316
+ case name
317
+ when 'deviceName' then @block_device_mapping[:device_name] = @text
318
+ when 'virtualName' then @block_device_mapping[:virtual_name] = @text
319
+ when 'volumeSize' then @block_device_mapping[:ebs_volume_size] = @text.to_i
320
+ when 'snapshotId' then @block_device_mapping[:ebs_snapshot_id] = @text
321
+ when 'deleteOnTermination' then @block_device_mapping[:ebs_delete_on_termination] = @text == 'true' ? true : false
322
+ when 'item' then @item[:block_device_mappings] << @block_device_mapping
323
+ end
324
+ when %r{/imagesSet/item$} then @result << @item
325
+ end
326
+ end
327
+ end
328
+ def reset
329
+ @result = []
330
+ end
331
+ end
332
+
333
+ class QEc2RegisterImageParser < RightAWSParser #:nodoc:
334
+ def tagend(name)
335
+ @result = @text if name == 'imageId'
336
+ end
337
+ end
338
+
339
+ #-----------------------------------------------------------------
340
+ # PARSERS: Image Attribute
341
+ #-----------------------------------------------------------------
342
+
343
+ class QEc2DescribeImageAttributeParser < RightAWSParser #:nodoc:
344
+ def tagstart(name, attributes)
345
+ case name
346
+ when 'launchPermission'
347
+ @result[:groups] = []
348
+ @result[:users] = []
349
+ when 'productCodes'
350
+ @result[:aws_product_codes] = []
351
+ end
352
+ end
353
+ def tagend(name)
354
+ # right now only 'launchPermission' is supported by Amazon.
355
+ # But nobody know what will they xml later as attribute. That is why we
356
+ # check for 'group' and 'userId' inside of 'launchPermission/item'
357
+ case name
358
+ when 'imageId' then @result[:aws_id] = @text
359
+ when 'group' then @result[:groups] << @text if @xmlpath == 'DescribeImageAttributeResponse/launchPermission/item'
360
+ when 'userId' then @result[:users] << @text if @xmlpath == 'DescribeImageAttributeResponse/launchPermission/item'
361
+ when 'productCode' then @result[:aws_product_codes] << @text
362
+ when 'kernel' then @result[:aws_kernel] = @text
363
+ when 'ramdisk' then @result[:aws_ramdisk] = @text
364
+ end
365
+ end
366
+ def reset
367
+ @result = {}
368
+ end
369
+ end
370
+
371
+ end
372
+
373
+ end