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,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