kerryb-right_aws 1.7.6 → 1.10.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/lib/ec2/right_ec2.rb CHANGED
@@ -25,7 +25,8 @@ module RightAws
25
25
 
26
26
  # = RightAWS::EC2 -- RightScale Amazon EC2 interface
27
27
  # The RightAws::EC2 class provides a complete interface to Amazon's
28
- # Elastic Compute Cloud service.
28
+ # Elastic Compute Cloud service, as well as the associated EBS (Elastic Block
29
+ # Store).
29
30
  # For explanations of the semantics
30
31
  # of each call, please refer to Amazon's documentation at
31
32
  # http://developer.amazonwebservices.com/connect/kbcategory.jspa?categoryID=87
@@ -67,7 +68,7 @@ module RightAws
67
68
  include RightAwsBaseInterface
68
69
 
69
70
  # Amazon EC2 API version being used
70
- API_VERSION = "2008-02-01"
71
+ API_VERSION = "2009-04-04"
71
72
  DEFAULT_HOST = "ec2.amazonaws.com"
72
73
  DEFAULT_PATH = '/'
73
74
  DEFAULT_PROTOCOL = 'https'
@@ -99,12 +100,18 @@ module RightAws
99
100
  # Create a new handle to an EC2 account. All handles share the same per process or per thread
100
101
  # HTTP connection to Amazon EC2. Each handle is for a specific account. The params have the
101
102
  # following options:
103
+ # * <tt>:endpoint_url</tt> a fully qualified url to Amazon API endpoint (this overwrites: :server, :port, :service, :protocol and :region). Example: 'https://eu-west-1.ec2.amazonaws.com/'
102
104
  # * <tt>:server</tt>: EC2 service host, default: DEFAULT_HOST
105
+ # * <tt>:region</tt>: EC2 region (North America by default)
103
106
  # * <tt>:port</tt>: EC2 service port, default: DEFAULT_PORT
104
107
  # * <tt>:protocol</tt>: 'http' or 'https', default: DEFAULT_PROTOCOL
105
108
  # * <tt>:multi_thread</tt>: true=HTTP connection per thread, false=per process
106
109
  # * <tt>:logger</tt>: for log messages, default: RAILS_DEFAULT_LOGGER else STDOUT
107
110
  # * <tt>:signature_version</tt>: The signature version : '0' or '1'(default)
111
+ # * <tt>:cache</tt>: true/false: caching for: ec2_describe_images, describe_instances,
112
+ # describe_images_by_owner, describe_images_by_executable_by, describe_availability_zones,
113
+ # describe_security_groups, describe_key_pairs, describe_addresses,
114
+ # describe_volumes, describe_snapshots methods, default: false.
108
115
  #
109
116
  def initialize(aws_access_key_id=nil, aws_secret_access_key=nil, params={})
110
117
  init({ :name => 'EC2',
@@ -115,31 +122,41 @@ module RightAws
115
122
  aws_access_key_id || ENV['AWS_ACCESS_KEY_ID'] ,
116
123
  aws_secret_access_key|| ENV['AWS_SECRET_ACCESS_KEY'],
117
124
  params)
125
+ # EC2 doesn't really define any transient errors to retry, and in fact,
126
+ # when they return a 503 it is usually for 'request limit exceeded' which
127
+ # we most certainly should not retry. So let's pare down the list of
128
+ # retryable errors to InternalError only (see RightAwsBase for the default
129
+ # list)
130
+ amazon_problems = ['InternalError']
118
131
  end
119
132
 
120
133
 
121
134
  def generate_request(action, params={}) #:nodoc:
122
- service_hash = {"Action" => action,
123
- "AWSAccessKeyId" => @aws_access_key_id,
124
- "Version" => @@api,
125
- # MODIFIED: from Time.now.utc.stf... for eucalyptus
126
- "Timestamp" => Time.now.strftime("%Y-%m-%dT%H:%M:%S.000Z"),
127
- "SignatureVersion" => signature_version }
135
+ service_hash = {"Action" => action,
136
+ "AWSAccessKeyId" => @aws_access_key_id,
137
+ "Version" => @@api }
128
138
  service_hash.update(params)
129
- # prepare string to sight
130
- string_to_sign = case signature_version
131
- when '0' then service_hash["Action"] + service_hash["Timestamp"]
132
- when '1' then service_hash.sort{|a,b| (a[0].to_s.downcase)<=>(b[0].to_s.downcase)}.to_s
133
- end
134
- service_hash.update('Signature' => AwsUtils::sign(@aws_secret_access_key, string_to_sign))
135
- request_params = service_hash.to_a.collect{|key,val| key + "=" + CGI::escape(val) }.join("&")
136
- request = Net::HTTP::Get.new("#{@params[:service]}?#{request_params}")
139
+ service_params = signed_service_params(@aws_secret_access_key, service_hash, :get, @params[:server], @params[:service])
140
+
141
+ # use POST method if the length of the query string is too large
142
+ if service_params.size > 2000
143
+ if signature_version == '2'
144
+ # resign the request because HTTP verb is included into signature
145
+ service_params = signed_service_params(@aws_secret_access_key, service_hash, :post, @params[:server], @params[:service])
146
+ end
147
+ request = Net::HTTP::Post.new(service)
148
+ request.body = service_params
149
+ request['Content-Type'] = 'application/x-www-form-urlencoded'
150
+ else
151
+ request = Net::HTTP::Get.new("#{@params[:service]}?#{service_params}")
152
+ end
137
153
  # prepare output hash
138
154
  { :request => request,
139
155
  :server => @params[:server],
140
156
  :port => @params[:port],
141
157
  :protocol => @params[:protocol],
142
- :proxy => @params[:proxy] }
158
+ :proxy => @params[:proxy] }
159
+
143
160
  end
144
161
 
145
162
  # Sends request to Amazon and parses the response
@@ -150,22 +167,6 @@ module RightAws
150
167
  request_info_impl(thread[:ec2_connection], @@bench, request, parser)
151
168
  end
152
169
 
153
- def request_cache_or_info(method, link, parser_class, use_cache=true) #:nodoc:
154
- # We do not want to break the logic of parsing hence will use a dummy parser to process all the standart
155
- # steps (errors checking etc). The dummy parser does nothig - just returns back the params it received.
156
- # If the caching is enabled and hit then throw AwsNoChange.
157
- # P.S. caching works for the whole images list only! (when the list param is blank) response, params = request_info(link, QEc2DummyParser.new)
158
- # check cache
159
- response, params = request_info(link, QEc2DummyParser.new)
160
- cache_hits?(method.to_sym, response.body) if use_cache
161
- parser = parser_class.new(:logger => @logger)
162
- @@bench.xml.add!{ parser.parse(response, params) }
163
- result = block_given? ? yield(parser) : parser.result
164
- # update parsed data
165
- update_cache(method.to_sym, :parsed => result) if use_cache
166
- result
167
- end
168
-
169
170
  def hash_params(prefix, list) #:nodoc:
170
171
  groups = {}
171
172
  list.each_index{|i| groups.update("#{prefix}.#{i+1}"=>list[i])} if list
@@ -176,11 +177,19 @@ module RightAws
176
177
  # Images
177
178
  #-----------------------------------------------------------------
178
179
 
179
- def ec2_describe_images(list, list_by='ImageId', image_type=nil) #:nodoc:
180
- request_hash = hash_params(list_by, list.to_a)
180
+ # params:
181
+ # { 'ImageId' => ['id1', ..., 'idN'],
182
+ # 'Owner' => ['self', ..., 'userN'],
183
+ # 'ExecutableBy' => ['self', 'all', ..., 'userN']
184
+ # }
185
+ def ec2_describe_images(params={}, image_type=nil, cache_for=nil) #:nodoc:
186
+ request_hash = {}
187
+ params.each do |list_by, list|
188
+ request_hash.merge! hash_params(list_by, list.to_a)
189
+ end
181
190
  request_hash['ImageType'] = image_type if image_type
182
191
  link = generate_request("DescribeImages", request_hash)
183
- request_cache_or_info :describe_images, link, QEc2DescribeImagesParser, (list.blank? && list_by == 'ImageId' && image_type.blank?)
192
+ request_cache_or_info cache_for, link, QEc2DescribeImagesParser, @@bench, cache_for
184
193
  rescue Exception
185
194
  on_exception
186
195
  end
@@ -211,7 +220,9 @@ module RightAws
211
220
  # :aws_image_type => "machine"}]
212
221
  #
213
222
  def describe_images(list=[], image_type=nil)
214
- ec2_describe_images(list, 'ImageId', image_type)
223
+ list = list.to_a
224
+ cache_for = list.empty? && !image_type ? :describe_images : nil
225
+ ec2_describe_images({ 'ImageId' => list }, image_type, cache_for)
215
226
  end
216
227
 
217
228
  #
@@ -220,8 +231,10 @@ module RightAws
220
231
  # ec2.describe_images_by_owner('522821470517')
221
232
  # ec2.describe_images_by_owner('self')
222
233
  #
223
- def describe_images_by_owner(list, image_type=nil)
224
- ec2_describe_images(list, 'Owner', image_type)
234
+ def describe_images_by_owner(list=['self'], image_type=nil)
235
+ list = list.to_a
236
+ cache_for = list==['self'] && !image_type ? :describe_images_by_owner : nil
237
+ ec2_describe_images({ 'Owner' => list }, image_type, cache_for)
225
238
  end
226
239
 
227
240
  #
@@ -229,9 +242,12 @@ module RightAws
229
242
  #
230
243
  # ec2.describe_images_by_executable_by('522821470517')
231
244
  # ec2.describe_images_by_executable_by('self')
245
+ # ec2.describe_images_by_executable_by('all')
232
246
  #
233
- def describe_images_by_executable_by(list, image_type=nil)
234
- ec2_describe_images(list, 'ExecutableBy', image_type)
247
+ def describe_images_by_executable_by(list=['self'], image_type=nil)
248
+ list = list.to_a
249
+ cache_for = list==['self'] && !image_type ? :describe_images_by_executable_by : nil
250
+ ec2_describe_images({ 'ExecutableBy' => list }, image_type, cache_for)
235
251
  end
236
252
 
237
253
 
@@ -399,7 +415,7 @@ module RightAws
399
415
  #
400
416
  def describe_instances(list=[])
401
417
  link = generate_request("DescribeInstances", hash_params('InstanceId',list.to_a))
402
- request_cache_or_info(:describe_instances, link, QEc2DescribeInstancesParser, list.blank?) do |parser|
418
+ request_cache_or_info(:describe_instances, link, QEc2DescribeInstancesParser, @@bench, list.blank?) do |parser|
403
419
  get_desc_instances(parser.result)
404
420
  end
405
421
  rescue Exception
@@ -413,7 +429,7 @@ module RightAws
413
429
  #
414
430
  def confirm_product_instance(instance, product_code)
415
431
  link = generate_request("ConfirmProductInstance", { 'ProductCode' => product_code,
416
- 'InstanceId' => instance })
432
+ 'InstanceId' => instance })
417
433
  request_info(link, QEc2ConfirmProductInstanceParser.new(:logger => @logger))
418
434
  end
419
435
 
@@ -519,7 +535,7 @@ module RightAws
519
535
  # Amazon 169.254.169.254 does not like escaped symbols!
520
536
  # And it doesn't like "\n" inside of encoded string! Grrr....
521
537
  # Otherwise, some of UserData symbols will be lost...
522
- params['UserData'] = Base64.encode64(lparams[:user_data]).delete("\n") unless lparams[:user_data].blank?
538
+ params['UserData'] = Base64.encode64(lparams[:user_data]).delete("\n").strip unless lparams[:user_data].blank?
523
539
  end
524
540
  link = generate_request("RunInstances", params)
525
541
  #debugger
@@ -574,7 +590,127 @@ module RightAws
574
590
  rescue Exception
575
591
  on_exception
576
592
  end
593
+
594
+ #-----------------------------------------------------------------
595
+ # Instances: Windows addons
596
+ #-----------------------------------------------------------------
597
+
598
+ # Get initial Windows Server setup password from an instance console output.
599
+ #
600
+ # my_awesome_key = ec2.create_key_pair('my_awesome_key') #=>
601
+ # {:aws_key_name => "my_awesome_key",
602
+ # :aws_fingerprint => "01:02:03:f4:25:e6:97:e8:9b:02:1a:26:32:4e:58:6b:7a:8c:9f:03",
603
+ # :aws_material => "-----BEGIN RSA PRIVATE KEY-----\nMIIEpQIBAAK...Q8MDrCbuQ=\n-----END RSA PRIVATE KEY-----"}
604
+ #
605
+ # my_awesome_instance = ec2.run_instances('ami-a000000a',1,1,['my_awesome_group'],'my_awesome_key', 'WindowsInstance!!!') #=>
606
+ # [{:aws_image_id => "ami-a000000a",
607
+ # :aws_instance_id => "i-12345678",
608
+ # ...
609
+ # :aws_availability_zone => "us-east-1b"
610
+ # }]
611
+ #
612
+ # # wait until instance enters 'operational' state and get it's initial password
613
+ #
614
+ # puts ec2.get_initial_password(my_awesome_instance[:aws_instance_id], my_awesome_key[:aws_material]) #=> "MhjWcgZuY6"
615
+ #
616
+ def get_initial_password(instance_id, private_key)
617
+ console_output = get_console_output(instance_id)
618
+ crypted_password = console_output[:aws_output][%r{<Password>(.+)</Password>}m] && $1
619
+ unless crypted_password
620
+ raise AwsError.new("Initial password was not found in console output for #{instance_id}")
621
+ else
622
+ OpenSSL::PKey::RSA.new(private_key).private_decrypt(Base64.decode64(crypted_password))
623
+ end
624
+ rescue Exception
625
+ on_exception
626
+ end
627
+
628
+ # Bundle a Windows image.
629
+ # Internally, it queues the bundling task and shuts down the instance.
630
+ # It then takes a snapshot of the Windows volume bundles it, and uploads it to
631
+ # S3. After bundling completes, Rightaws::Ec2#register_image may be used to
632
+ # register the new Windows AMI for subsequent launches.
633
+ #
634
+ # ec2.bundle_instance('i-e3e24e8a', 'my-awesome-bucket', 'my-win-image-1') #=>
635
+ # [{:aws_update_time => "2008-10-16T13:58:25.000Z",
636
+ # :s3_bucket => "kd-win-1",
637
+ # :s3_prefix => "win2pr",
638
+ # :aws_state => "pending",
639
+ # :aws_id => "bun-26a7424f",
640
+ # :aws_instance_id => "i-878a25ee",
641
+ # :aws_start_time => "2008-10-16T13:58:02.000Z"}]
642
+ #
643
+ def bundle_instance(instance_id, s3_bucket, s3_prefix,
644
+ s3_owner_aws_access_key_id=nil, s3_owner_aws_secret_access_key=nil,
645
+ s3_expires = S3Interface::DEFAULT_EXPIRES_AFTER,
646
+ s3_upload_policy='ec2-bundle-read')
647
+ # S3 access and signatures
648
+ s3_owner_aws_access_key_id ||= @aws_access_key_id
649
+ s3_owner_aws_secret_access_key ||= @aws_secret_access_key
650
+ s3_expires = Time.now.utc + s3_expires if s3_expires.is_a?(Fixnum) && (s3_expires < S3Interface::ONE_YEAR_IN_SECONDS)
651
+ # policy
652
+ policy = { 'expiration' => s3_expires.strftime('%Y-%m-%dT%H:%M:%SZ'),
653
+ 'conditions' => [ { 'bucket' => s3_bucket },
654
+ { 'acl' => s3_upload_policy },
655
+ [ 'starts-with', '$key', s3_prefix ] ] }.to_json
656
+ policy64 = Base64.encode64(policy).gsub("\n","")
657
+ signed_policy64 = AwsUtils.sign(s3_owner_aws_secret_access_key, policy64)
658
+ # fill request params
659
+ params = { 'InstanceId' => instance_id,
660
+ 'Storage.S3.AWSAccessKeyId' => s3_owner_aws_access_key_id,
661
+ 'Storage.S3.UploadPolicy' => policy64,
662
+ 'Storage.S3.UploadPolicySignature' => signed_policy64,
663
+ 'Storage.S3.Bucket' => s3_bucket,
664
+ 'Storage.S3.Prefix' => s3_prefix,
665
+ }
666
+ link = generate_request("BundleInstance", params)
667
+ request_info(link, QEc2BundleInstanceParser.new)
668
+ rescue Exception
669
+ on_exception
670
+ end
577
671
 
672
+ # Describe the status of the Windows AMI bundlings.
673
+ # If +list+ is omitted the returns the whole list of tasks.
674
+ #
675
+ # ec2.describe_bundle_tasks(['bun-4fa74226']) #=>
676
+ # [{:s3_bucket => "my-awesome-bucket"
677
+ # :aws_id => "bun-0fa70206",
678
+ # :s3_prefix => "win1pr",
679
+ # :aws_start_time => "2008-10-14T16:27:57.000Z",
680
+ # :aws_update_time => "2008-10-14T16:37:10.000Z",
681
+ # :aws_error_code => "Client.S3Error",
682
+ # :aws_error_message =>
683
+ # "AccessDenied(403)- Invalid according to Policy: Policy Condition failed: [\"eq\", \"$acl\", \"aws-exec-read\"]",
684
+ # :aws_state => "failed",
685
+ # :aws_instance_id => "i-e3e24e8a"}]
686
+ #
687
+ def describe_bundle_tasks(list=[])
688
+ link = generate_request("DescribeBundleTasks", hash_params('BundleId', list.to_a))
689
+ request_info(link, QEc2DescribeBundleTasksParser.new)
690
+ rescue Exception
691
+ on_exception
692
+ end
693
+
694
+ # Cancel an in‐progress or pending bundle task by id.
695
+ #
696
+ # ec2.cancel_bundle_task('bun-73a7421a') #=>
697
+ # [{:s3_bucket => "my-awesome-bucket"
698
+ # :aws_id => "bun-0fa70206",
699
+ # :s3_prefix => "win02",
700
+ # :aws_start_time => "2008-10-14T13:00:29.000Z",
701
+ # :aws_error_message => "User has requested bundling operation cancellation",
702
+ # :aws_state => "failed",
703
+ # :aws_update_time => "2008-10-14T13:01:31.000Z",
704
+ # :aws_error_code => "Client.Cancelled",
705
+ # :aws_instance_id => "i-e3e24e8a"}
706
+ #
707
+ def cancel_bundle_task(bundle_id)
708
+ link = generate_request("CancelBundleTask", { 'BundleId' => bundle_id })
709
+ request_info(link, QEc2BundleInstanceParser.new)
710
+ rescue Exception
711
+ on_exception
712
+ end
713
+
578
714
  #-----------------------------------------------------------------
579
715
  # Security groups
580
716
  #-----------------------------------------------------------------
@@ -596,7 +732,7 @@ module RightAws
596
732
  #
597
733
  def describe_security_groups(list=[])
598
734
  link = generate_request("DescribeSecurityGroups", hash_params('GroupName',list.to_a))
599
- request_cache_or_info( :describe_security_groups, link, QEc2DescribeSecurityGroupsParser, list.blank?) do |parser|
735
+ request_cache_or_info( :describe_security_groups, link, QEc2DescribeSecurityGroupsParser, @@bench, list.blank?) do |parser|
600
736
  result = []
601
737
  parser.result.each do |item|
602
738
  perms = []
@@ -641,7 +777,7 @@ module RightAws
641
777
  # EC2 doesn't like an empty description...
642
778
  description = " " if description.blank?
643
779
  link = generate_request("CreateSecurityGroup",
644
- 'GroupName' => name.to_s,
780
+ 'GroupName' => name.to_s,
645
781
  'GroupDescription' => description.to_s)
646
782
  request_info(link, RightBoolResponseParser.new(:logger => @logger))
647
783
  rescue Exception
@@ -667,8 +803,8 @@ module RightAws
667
803
  #
668
804
  def authorize_security_group_named_ingress(name, owner, group)
669
805
  link = generate_request("AuthorizeSecurityGroupIngress",
670
- 'GroupName' => name.to_s,
671
- 'SourceSecurityGroupName' => group.to_s,
806
+ 'GroupName' => name.to_s,
807
+ 'SourceSecurityGroupName' => group.to_s,
672
808
  'SourceSecurityGroupOwnerId' => owner.to_s.gsub(/-/,''))
673
809
  request_info(link, RightBoolResponseParser.new(:logger => @logger))
674
810
  rescue Exception
@@ -681,8 +817,8 @@ module RightAws
681
817
  #
682
818
  def revoke_security_group_named_ingress(name, owner, group)
683
819
  link = generate_request("RevokeSecurityGroupIngress",
684
- 'GroupName' => name.to_s,
685
- 'SourceSecurityGroupName' => group.to_s,
820
+ 'GroupName' => name.to_s,
821
+ 'SourceSecurityGroupName' => group.to_s,
686
822
  'SourceSecurityGroupOwnerId' => owner.to_s.gsub(/-/,''))
687
823
  request_info(link, RightBoolResponseParser.new(:logger => @logger))
688
824
  rescue Exception
@@ -696,10 +832,10 @@ module RightAws
696
832
  #
697
833
  def authorize_security_group_IP_ingress(name, from_port, to_port, protocol='tcp', cidr_ip='0.0.0.0/0')
698
834
  link = generate_request("AuthorizeSecurityGroupIngress",
699
- 'GroupName' => name.to_s,
700
- 'IpProtocol' => protocol.to_s,
701
- 'FromPort' => from_port.to_s,
702
- 'ToPort' => to_port.to_s,
835
+ 'GroupName' => name.to_s,
836
+ 'IpProtocol' => protocol.to_s,
837
+ 'FromPort' => from_port.to_s,
838
+ 'ToPort' => to_port.to_s,
703
839
  'CidrIp' => cidr_ip.to_s)
704
840
  request_info(link, RightBoolResponseParser.new(:logger => @logger))
705
841
  rescue Exception
@@ -712,10 +848,10 @@ module RightAws
712
848
  #
713
849
  def revoke_security_group_IP_ingress(name, from_port, to_port, protocol='tcp', cidr_ip='0.0.0.0/0')
714
850
  link = generate_request("RevokeSecurityGroupIngress",
715
- 'GroupName' => name.to_s,
716
- 'IpProtocol' => protocol.to_s,
717
- 'FromPort' => from_port.to_s,
718
- 'ToPort' => to_port.to_s,
851
+ 'GroupName' => name.to_s,
852
+ 'IpProtocol' => protocol.to_s,
853
+ 'FromPort' => from_port.to_s,
854
+ 'ToPort' => to_port.to_s,
719
855
  'CidrIp' => cidr_ip.to_s)
720
856
  request_info(link, RightBoolResponseParser.new(:logger => @logger))
721
857
  rescue Exception
@@ -736,7 +872,7 @@ module RightAws
736
872
  #
737
873
  def describe_key_pairs(list=[])
738
874
  link = generate_request("DescribeKeyPairs", hash_params('KeyName',list.to_a))
739
- request_cache_or_info :describe_key_pairs, link, QEc2DescribeKeyPairParser, list.blank?
875
+ request_cache_or_info :describe_key_pairs, link, QEc2DescribeKeyPairParser, @@bench, list.blank?
740
876
  rescue Exception
741
877
  on_exception
742
878
  end
@@ -755,7 +891,7 @@ module RightAws
755
891
  rescue Exception
756
892
  on_exception
757
893
  end
758
-
894
+
759
895
  # Delete a key pair. Returns +true+ or an exception.
760
896
  #
761
897
  # ec2.delete_key_pair('my_awesome_key') #=> true
@@ -809,7 +945,7 @@ module RightAws
809
945
  def describe_addresses(list=[])
810
946
  link = generate_request("DescribeAddresses",
811
947
  hash_params('PublicIp',list.to_a))
812
- request_cache_or_info :describe_addresses, link, QEc2DescribeAddressesParser, list.blank?
948
+ request_cache_or_info :describe_addresses, link, QEc2DescribeAddressesParser, @@bench, list.blank?
813
949
  rescue Exception
814
950
  on_exception
815
951
  end
@@ -847,21 +983,242 @@ module RightAws
847
983
  # Describes availability zones that are currently available to the account and their states.
848
984
  # Returns an array of 2 keys (:zone_name and :zone_state) hashes:
849
985
  #
850
- # ec2.describe_availability_zones #=> [{:zone_state=>"available", :zone_name=>"us-east-1a"},
851
- # {:zone_state=>"available", :zone_name=>"us-east-1b"},
852
- # {:zone_state=>"available", :zone_name=>"us-east-1c"}]
986
+ # ec2.describe_availability_zones #=> [{:region_name=>"us-east-1",
987
+ # :zone_name=>"us-east-1a",
988
+ # :zone_state=>"available"}, ... ]
853
989
  #
854
- # ec2.describe_availability_zones('us-east-1c') #=> [{:zone_state=>"available", :zone_name=>"us-east-1c"}]
990
+ # ec2.describe_availability_zones('us-east-1c') #=> [{:region_name=>"us-east-1",
991
+ # :zone_state=>"available",
992
+ # :zone_name=>"us-east-1c"}]
855
993
  #
856
994
  def describe_availability_zones(list=[])
857
995
  link = generate_request("DescribeAvailabilityZones",
858
996
  hash_params('ZoneName',list.to_a))
859
- request_cache_or_info :describe_availability_zones, link, QEc2DescribeAvailabilityZonesParser, list.blank?
997
+ request_cache_or_info :describe_availability_zones, link, QEc2DescribeAvailabilityZonesParser, @@bench, list.blank?
860
998
  rescue Exception
861
999
  on_exception
862
1000
  end
863
1001
 
1002
+ #-----------------------------------------------------------------
1003
+ # Regions
1004
+ #-----------------------------------------------------------------
1005
+
1006
+ # Describe regions.
1007
+ #
1008
+ # ec2.describe_regions #=> ["eu-west-1", "us-east-1"]
1009
+ #
1010
+ def describe_regions(list=[])
1011
+ link = generate_request("DescribeRegions",
1012
+ hash_params('RegionName',list.to_a))
1013
+ request_cache_or_info :describe_regions, link, QEc2DescribeRegionsParser, @@bench, list.blank?
1014
+ rescue Exception
1015
+ on_exception
1016
+ end
1017
+
1018
+
1019
+ #-----------------------------------------------------------------
1020
+ # EBS: Volumes
1021
+ #-----------------------------------------------------------------
864
1022
 
1023
+ # Describe all EBS volumes.
1024
+ #
1025
+ # ec2.describe_volumes #=>
1026
+ # [{:aws_size => 94,
1027
+ # :aws_device => "/dev/sdc",
1028
+ # :aws_attachment_status => "attached",
1029
+ # :zone => "merlot",
1030
+ # :snapshot_id => nil,
1031
+ # :aws_attached_at => Wed Jun 18 08:19:28 UTC 2008,
1032
+ # :aws_status => "in-use",
1033
+ # :aws_id => "vol-60957009",
1034
+ # :aws_created_at => Wed Jun 18 08:19:20s UTC 2008,
1035
+ # :aws_instance_id => "i-c014c0a9"},
1036
+ # {:aws_size => 1,
1037
+ # :zone => "merlot",
1038
+ # :snapshot_id => nil,
1039
+ # :aws_status => "available",
1040
+ # :aws_id => "vol-58957031",
1041
+ # :aws_created_at => Wed Jun 18 08:19:21 UTC 2008,}, ... ]
1042
+ #
1043
+ def describe_volumes(list=[])
1044
+ link = generate_request("DescribeVolumes",
1045
+ hash_params('VolumeId',list.to_a))
1046
+ request_cache_or_info :describe_volumes, link, QEc2DescribeVolumesParser, @@bench, list.blank?
1047
+ rescue Exception
1048
+ on_exception
1049
+ end
1050
+
1051
+ # Create new EBS volume based on previously created snapshot.
1052
+ # +Size+ in Gigabytes.
1053
+ #
1054
+ # ec2.create_volume('snap-000000', 10, zone) #=>
1055
+ # {:snapshot_id => "snap-e21df98b",
1056
+ # :aws_status => "creating",
1057
+ # :aws_id => "vol-fc9f7a95",
1058
+ # :zone => "merlot",
1059
+ # :aws_created_at => Tue Jun 24 18:13:32 UTC 2008,
1060
+ # :aws_size => 94}
1061
+ #
1062
+ def create_volume(zone, snapshot_id, size)
1063
+ hash = {"AvailabilityZone" => zone.to_s}
1064
+ hash["SnapshotId"] = snapshot_id.to_s unless snapshot_id.blank?
1065
+ hash["Size"] = size.to_s unless size.blank?
1066
+
1067
+ link = generate_request("CreateVolume", hash)
1068
+
1069
+ request_info(link, QEc2CreateVolumeParser.new(:logger => @logger))
1070
+ rescue Exception
1071
+ on_exception
1072
+ end
1073
+
1074
+ # Delete the specified EBS volume.
1075
+ # This does not deletes any snapshots created from this volume.
1076
+ #
1077
+ # ec2.delete_volume('vol-b48a6fdd') #=> true
1078
+ #
1079
+ def delete_volume(volume_id)
1080
+ link = generate_request("DeleteVolume",
1081
+ "VolumeId" => volume_id.to_s)
1082
+ request_info(link, RightBoolResponseParser.new(:logger => @logger))
1083
+ rescue Exception
1084
+ on_exception
1085
+ end
1086
+
1087
+ # Attach the specified EBS volume to a specified instance, exposing the
1088
+ # volume using the specified device name.
1089
+ #
1090
+ # ec2.attach_volume('vol-898a6fe0', 'i-7c905415', '/dev/sdh') #=>
1091
+ # { :aws_instance_id => "i-7c905415",
1092
+ # :aws_device => "/dev/sdh",
1093
+ # :aws_status => "attaching",
1094
+ # :aws_attached_at => "2008-03-28T14:14:39.000Z",
1095
+ # :aws_id => "vol-898a6fe0" }
1096
+ #
1097
+ def attach_volume(volume_id, instance_id, device)
1098
+ link = generate_request("AttachVolume",
1099
+ "VolumeId" => volume_id.to_s,
1100
+ "InstanceId" => instance_id.to_s,
1101
+ "Device" => device.to_s)
1102
+ request_info(link, QEc2AttachAndDetachVolumeParser.new(:logger => @logger))
1103
+ rescue Exception
1104
+ on_exception
1105
+ end
1106
+
1107
+ # Detach the specified EBS volume from the instance to which it is attached.
1108
+ #
1109
+ # ec2.detach_volume('vol-898a6fe0') #=>
1110
+ # { :aws_instance_id => "i-7c905415",
1111
+ # :aws_device => "/dev/sdh",
1112
+ # :aws_status => "detaching",
1113
+ # :aws_attached_at => "2008-03-28T14:38:34.000Z",
1114
+ # :aws_id => "vol-898a6fe0"}
1115
+ #
1116
+ def detach_volume(volume_id, instance_id=nil, device=nil, force=nil)
1117
+ hash = { "VolumeId" => volume_id.to_s }
1118
+ hash["InstanceId"] = instance_id.to_s unless instance_id.blank?
1119
+ hash["Device"] = device.to_s unless device.blank?
1120
+ hash["Force"] = 'true' if force
1121
+ #
1122
+ link = generate_request("DetachVolume", hash)
1123
+ request_info(link, QEc2AttachAndDetachVolumeParser.new(:logger => @logger))
1124
+ rescue Exception
1125
+ on_exception
1126
+ end
1127
+
1128
+
1129
+ #-----------------------------------------------------------------
1130
+ # EBS: Snapshots
1131
+ #-----------------------------------------------------------------
1132
+
1133
+ # Describe all EBS snapshots.
1134
+ #
1135
+ # ec2.describe_snapshots #=>
1136
+ # [ { :aws_progress => "100%",
1137
+ # :aws_status => "completed",
1138
+ # :aws_id => "snap-72a5401b",
1139
+ # :aws_volume_id => "vol-5582673c",
1140
+ # :aws_started_at => "2008-02-23T02:50:48.000Z"},
1141
+ # { :aws_progress => "100%",
1142
+ # :aws_status => "completed",
1143
+ # :aws_id => "snap-75a5401c",
1144
+ # :aws_volume_id => "vol-5582673c",
1145
+ # :aws_started_at => "2008-02-23T16:23:19.000Z" },...]
1146
+ #
1147
+ def describe_snapshots(list=[])
1148
+ link = generate_request("DescribeSnapshots",
1149
+ hash_params('SnapshotId',list.to_a))
1150
+ request_cache_or_info :describe_snapshots, link, QEc2DescribeSnapshotsParser, @@bench, list.blank?
1151
+ rescue Exception
1152
+ on_exception
1153
+ end
1154
+
1155
+ # Create a snapshot of specified volume.
1156
+ #
1157
+ # ec2.create_snapshot('vol-898a6fe0') #=>
1158
+ # {:aws_volume_id => "vol-fd9f7a94",
1159
+ # :aws_started_at => Tue Jun 24 18:40:40 UTC 2008,
1160
+ # :aws_progress => "",
1161
+ # :aws_status => "pending",
1162
+ # :aws_id => "snap-d56783bc"}
1163
+ #
1164
+ def create_snapshot(volume_id)
1165
+ link = generate_request("CreateSnapshot",
1166
+ "VolumeId" => volume_id.to_s)
1167
+ request_info(link, QEc2CreateSnapshotParser.new(:logger => @logger))
1168
+ rescue Exception
1169
+ on_exception
1170
+ end
1171
+
1172
+ # Create a snapshot of specified volume, but with the normal retry algorithms disabled.
1173
+ # This method will return immediately upon error. The user can specify connect and read timeouts (in s)
1174
+ # for the connection to AWS. If the user does not specify timeouts, try_create_snapshot uses the default values
1175
+ # in Rightscale::HttpConnection.
1176
+ #
1177
+ # ec2.try_create_snapshot('vol-898a6fe0') #=>
1178
+ # {:aws_volume_id => "vol-fd9f7a94",
1179
+ # :aws_started_at => Tue Jun 24 18:40:40 UTC 2008,
1180
+ # :aws_progress => "",
1181
+ # :aws_status => "pending",
1182
+ # :aws_id => "snap-d56783bc"}
1183
+ #
1184
+ def try_create_snapshot(volume_id, connect_timeout = nil, read_timeout = nil)
1185
+ # For safety in the ensure block...we don't want to restore values
1186
+ # if we never read them in the first place
1187
+ orig_reiteration_time = nil
1188
+ orig_http_params = nil
1189
+
1190
+ orig_reiteration_time = RightAws::AWSErrorHandler::reiteration_time
1191
+ RightAws::AWSErrorHandler::reiteration_time = 0
1192
+
1193
+ orig_http_params = Rightscale::HttpConnection::params()
1194
+ new_http_params = orig_http_params.dup
1195
+ new_http_params[:http_connection_retry_count] = 0
1196
+ new_http_params[:http_connection_open_timeout] = connect_timeout if !connect_timeout.nil?
1197
+ new_http_params[:http_connection_read_timeout] = read_timeout if !read_timeout.nil?
1198
+ Rightscale::HttpConnection::params = new_http_params
1199
+
1200
+ link = generate_request("CreateSnapshot",
1201
+ "VolumeId" => volume_id.to_s)
1202
+ request_info(link, QEc2CreateSnapshotParser.new(:logger => @logger))
1203
+
1204
+ rescue Exception
1205
+ on_exception
1206
+ ensure
1207
+ RightAws::AWSErrorHandler::reiteration_time = orig_reiteration_time if orig_reiteration_time
1208
+ Rightscale::HttpConnection::params = orig_http_params if orig_http_params
1209
+ end
1210
+
1211
+ # Delete the specified snapshot.
1212
+ #
1213
+ # ec2.delete_snapshot('snap-55a5403c') #=> true
1214
+ #
1215
+ def delete_snapshot(snapshot_id)
1216
+ link = generate_request("DeleteSnapshot",
1217
+ "SnapshotId" => snapshot_id.to_s)
1218
+ request_info(link, RightBoolResponseParser.new(:logger => @logger))
1219
+ rescue Exception
1220
+ on_exception
1221
+ end
865
1222
 
866
1223
  #-----------------------------------------------------------------
867
1224
  # PARSERS: Boolean Response Parser
@@ -1000,6 +1357,7 @@ module RightAws
1000
1357
  when 'kernelId' then @image[:aws_kernel_id] = @text
1001
1358
  when 'ramdiskId' then @image[:aws_ramdisk_id] = @text
1002
1359
  when 'item' then @result << @image if @xmlpath[%r{.*/imagesSet$}]
1360
+ when 'platform' then @image[:platform] = @text
1003
1361
  end
1004
1362
  end
1005
1363
  def reset
@@ -1095,6 +1453,7 @@ module RightAws
1095
1453
  when 'launchTime' then @instance[:aws_launch_time] = @text
1096
1454
  when 'kernelId' then @instance[:aws_kernel_id] = @text
1097
1455
  when 'ramdiskId' then @instance[:aws_ramdisk_id] = @text
1456
+ when 'platform' then @instance[:aws_platform] = @text
1098
1457
  when 'availabilityZone' then @instance[:aws_availability_zone] = @text
1099
1458
  when 'item'
1100
1459
  if @xmlpath == 'DescribeInstancesResponse/reservationSet/item/instancesSet' || # DescribeInstances property
@@ -1159,18 +1518,54 @@ module RightAws
1159
1518
  end
1160
1519
 
1161
1520
  #-----------------------------------------------------------------
1162
- # PARSERS: Fake
1521
+ # Instances: Wondows related part
1163
1522
  #-----------------------------------------------------------------
1164
-
1165
- # Dummy parser - does nothing
1166
- # Returns the original params back
1167
- class QEc2DummyParser # :nodoc:
1168
- attr_accessor :result
1169
- def parse(response, params={})
1170
- @result = [response, params]
1523
+ class QEc2DescribeBundleTasksParser < RightAWSParser #:nodoc:
1524
+ def tagstart(name, attributes)
1525
+ @bundle = {} if name == 'item'
1526
+ end
1527
+ def tagend(name)
1528
+ case name
1529
+ # when 'requestId' then @bundle[:request_id] = @text
1530
+ when 'instanceId' then @bundle[:aws_instance_id] = @text
1531
+ when 'bundleId' then @bundle[:aws_id] = @text
1532
+ when 'bucket' then @bundle[:s3_bucket] = @text
1533
+ when 'prefix' then @bundle[:s3_prefix] = @text
1534
+ when 'startTime' then @bundle[:aws_start_time] = @text
1535
+ when 'updateTime' then @bundle[:aws_update_time] = @text
1536
+ when 'state' then @bundle[:aws_state] = @text
1537
+ when 'progress' then @bundle[:aws_progress] = @text
1538
+ when 'code' then @bundle[:aws_error_code] = @text
1539
+ when 'message' then @bundle[:aws_error_message] = @text
1540
+ when 'item' then @result << @bundle
1541
+ end
1542
+ end
1543
+ def reset
1544
+ @result = []
1171
1545
  end
1172
1546
  end
1173
-
1547
+
1548
+ class QEc2BundleInstanceParser < RightAWSParser #:nodoc:
1549
+ def tagend(name)
1550
+ case name
1551
+ # when 'requestId' then @result[:request_id] = @text
1552
+ when 'instanceId' then @result[:aws_instance_id] = @text
1553
+ when 'bundleId' then @result[:aws_id] = @text
1554
+ when 'bucket' then @result[:s3_bucket] = @text
1555
+ when 'prefix' then @result[:s3_prefix] = @text
1556
+ when 'startTime' then @result[:aws_start_time] = @text
1557
+ when 'updateTime' then @result[:aws_update_time] = @text
1558
+ when 'state' then @result[:aws_state] = @text
1559
+ when 'progress' then @result[:aws_progress] = @text
1560
+ when 'code' then @result[:aws_error_code] = @text
1561
+ when 'message' then @result[:aws_error_message] = @text
1562
+ end
1563
+ end
1564
+ def reset
1565
+ @result = {}
1566
+ end
1567
+ end
1568
+
1174
1569
  #-----------------------------------------------------------------
1175
1570
  # PARSERS: Elastic IPs
1176
1571
  #-----------------------------------------------------------------
@@ -1207,8 +1602,9 @@ module RightAws
1207
1602
  end
1208
1603
  def tagend(name)
1209
1604
  case name
1210
- when 'zoneName' then @zone[:zone_name] = @text
1211
- when 'zoneState' then @zone[:zone_state] = @text
1605
+ when 'regionName' then @zone[:region_name] = @text
1606
+ when 'zoneName' then @zone[:zone_name] = @text
1607
+ when 'zoneState' then @zone[:zone_state] = @text
1212
1608
  when 'item' then @result << @zone
1213
1609
  end
1214
1610
  end
@@ -1217,7 +1613,129 @@ module RightAws
1217
1613
  end
1218
1614
  end
1219
1615
 
1616
+ #-----------------------------------------------------------------
1617
+ # PARSERS: Regions
1618
+ #-----------------------------------------------------------------
1619
+
1620
+ class QEc2DescribeRegionsParser < RightAWSParser #:nodoc:
1621
+ def tagend(name)
1622
+ @result << @text if name == 'regionName'
1623
+ end
1624
+ def reset
1625
+ @result = []
1626
+ end
1627
+ end
1628
+
1629
+ #-----------------------------------------------------------------
1630
+ # PARSERS: EBS - Volumes
1631
+ #-----------------------------------------------------------------
1220
1632
 
1633
+ class QEc2CreateVolumeParser < RightAWSParser #:nodoc:
1634
+ def tagend(name)
1635
+ case name
1636
+ when 'volumeId' then @result[:aws_id] = @text
1637
+ when 'status' then @result[:aws_status] = @text
1638
+ when 'createTime' then @result[:aws_created_at] = Time.parse(@text)
1639
+ when 'size' then @result[:aws_size] = @text.to_i ###
1640
+ when 'snapshotId' then @result[:snapshot_id] = @text.blank? ? nil : @text ###
1641
+ when 'availabilityZone' then @result[:zone] = @text ###
1642
+ end
1643
+ end
1644
+ def reset
1645
+ @result = {}
1646
+ end
1647
+ end
1648
+
1649
+ class QEc2AttachAndDetachVolumeParser < RightAWSParser #:nodoc:
1650
+ def tagend(name)
1651
+ case name
1652
+ when 'volumeId' then @result[:aws_id] = @text
1653
+ when 'instanceId' then @result[:aws_instance_id] = @text
1654
+ when 'device' then @result[:aws_device] = @text
1655
+ when 'status' then @result[:aws_attachment_status] = @text
1656
+ when 'attachTime' then @result[:aws_attached_at] = Time.parse(@text)
1657
+ end
1658
+ end
1659
+ def reset
1660
+ @result = {}
1661
+ end
1662
+ end
1663
+
1664
+ class QEc2DescribeVolumesParser < RightAWSParser #:nodoc:
1665
+ def tagstart(name, attributes)
1666
+ case name
1667
+ when 'item'
1668
+ case @xmlpath
1669
+ when 'DescribeVolumesResponse/volumeSet' then @volume = {}
1670
+ end
1671
+ end
1672
+ end
1673
+ def tagend(name)
1674
+ case name
1675
+ when 'volumeId'
1676
+ case @xmlpath
1677
+ when 'DescribeVolumesResponse/volumeSet/item' then @volume[:aws_id] = @text
1678
+ end
1679
+ when 'status'
1680
+ case @xmlpath
1681
+ when 'DescribeVolumesResponse/volumeSet/item' then @volume[:aws_status] = @text
1682
+ when 'DescribeVolumesResponse/volumeSet/item/attachmentSet/item' then @volume[:aws_attachment_status] = @text
1683
+ end
1684
+ when 'size' then @volume[:aws_size] = @text.to_i
1685
+ when 'createTime' then @volume[:aws_created_at] = Time.parse(@text)
1686
+ when 'instanceId' then @volume[:aws_instance_id] = @text
1687
+ when 'device' then @volume[:aws_device] = @text
1688
+ when 'attachTime' then @volume[:aws_attached_at] = Time.parse(@text)
1689
+ when 'snapshotId' then @volume[:snapshot_id] = @text.blank? ? nil : @text
1690
+ when 'availabilityZone' then @volume[:zone] = @text
1691
+ when 'item'
1692
+ case @xmlpath
1693
+ when 'DescribeVolumesResponse/volumeSet' then @result << @volume
1694
+ end
1695
+ end
1696
+ end
1697
+ def reset
1698
+ @result = []
1699
+ end
1700
+ end
1701
+
1702
+ #-----------------------------------------------------------------
1703
+ # PARSERS: EBS - Snapshots
1704
+ #-----------------------------------------------------------------
1705
+
1706
+ class QEc2DescribeSnapshotsParser < RightAWSParser #:nodoc:
1707
+ def tagstart(name, attributes)
1708
+ @snapshot = {} if name == 'item'
1709
+ end
1710
+ def tagend(name)
1711
+ case name
1712
+ when 'volumeId' then @snapshot[:aws_volume_id] = @text
1713
+ when 'snapshotId' then @snapshot[:aws_id] = @text
1714
+ when 'status' then @snapshot[:aws_status] = @text
1715
+ when 'startTime' then @snapshot[:aws_started_at] = Time.parse(@text)
1716
+ when 'progress' then @snapshot[:aws_progress] = @text
1717
+ when 'item' then @result << @snapshot
1718
+ end
1719
+ end
1720
+ def reset
1721
+ @result = []
1722
+ end
1723
+ end
1724
+
1725
+ class QEc2CreateSnapshotParser < RightAWSParser #:nodoc:
1726
+ def tagend(name)
1727
+ case name
1728
+ when 'volumeId' then @result[:aws_volume_id] = @text
1729
+ when 'snapshotId' then @result[:aws_id] = @text
1730
+ when 'status' then @result[:aws_status] = @text
1731
+ when 'startTime' then @result[:aws_started_at] = Time.parse(@text)
1732
+ when 'progress' then @result[:aws_progress] = @text
1733
+ end
1734
+ end
1735
+ def reset
1736
+ @result = {}
1737
+ end
1738
+ end
1221
1739
 
1222
1740
  end
1223
1741