Skip to content

HTTPS clone URL

Subversion checkout URL

You can clone with HTTPS or Subversion.

Download ZIP
Browse files

Merge new 3.0 into current branch

  • Loading branch information...
commit acc080b9b96a93fdfbf8e3a9023cc595fe41a7b4 2 parents 6e63ad8 + 5ff01dd
@konstantin-dzreev konstantin-dzreev authored
Showing with 3,001 additions and 653 deletions.
  1. +2 −0  Gemfile
  2. +1 −0  Gemfile.lock
  3. +15 −3 History.txt
  4. +1 −0  Manifest.txt
  5. +34 −8 Rakefile
  6. +162 −4 lib/awsbase/right_awsbase.rb
  7. +2 −2 lib/awsbase/version.rb
  8. +108 −35 lib/ec2/right_ec2.rb
  9. +49 −39 lib/ec2/right_ec2_ebs.rb
  10. +73 −44 lib/ec2/right_ec2_images.rb
  11. +158 −159 lib/ec2/right_ec2_instances.rb
  12. +36 −26 lib/ec2/right_ec2_reserved_instances.rb
  13. +261 −166 lib/ec2/right_ec2_security_groups.rb
  14. +72 −75 lib/ec2/right_ec2_spot_instances.rb
  15. +15 −8 lib/ec2/right_ec2_vpc.rb
  16. +381 −0 lib/ec2/right_ec2_vpc2.rb
  17. +3 −1 lib/elb/right_elb_interface.rb
  18. +727 −0 lib/emr/right_emr_interface.rb
  19. +4 −1 lib/right_aws.rb
  20. +24 −14 lib/route_53/right_route_53_interface.rb
  21. +16 −15 lib/s3/right_s3.rb
  22. +41 −9 lib/s3/right_s3_interface.rb
  23. +14 −5 lib/sdb/right_sdb_interface.rb
  24. +286 −0 lib/sns/right_sns_interface.rb
  25. +39 −0 test/README.mdown
  26. +2 −0  test/elb/test_helper.rb
  27. +43 −0 test/elb/test_right_elb.rb
  28. +18 −0 test/route_53/fixtures/a_record.xml
  29. +18 −0 test/route_53/fixtures/alias_record.xml
  30. +2 −0  test/route_53/test_helper.rb
  31. +141 −0 test/route_53/test_right_route_53.rb
  32. +97 −39 test/s3/test_right_s3.rb
  33. +2 −0  test/sns/test_helper.rb
  34. +153 −0 test/sns/test_right_sns.rb
  35. +1 −0  test/ts_right_aws.rb
View
2  Gemfile
@@ -1,3 +1,5 @@
+source "http://rubygems.org"
+
gem 'right_http_connection', '1.2.5', :git => 'git@github.com:rightscale/right_http_connection.git'
gem 'libxml-ruby', '~> 0.5.2'
View
1  Gemfile.lock
@@ -5,6 +5,7 @@ GIT
right_http_connection (1.2.5)
GEM
+ remote: http://rubygems.org/
specs:
libxml-ruby (0.5.2.0)
rake (0.8.7)
View
18 History.txt
@@ -304,9 +304,16 @@ the source key.
- this gem requires right_http_connection 0bc3343232133bdb38c237d8285525d74495d3f5 or later
- "Raise On Timeout On Action" feature added to avoid duplicate resources creation if a timeout error occures and a retry is performed
-=== next
+=== 3.0.0
Release Notes:
- - Fixed:
+ - Fixed/Added:
+ - ClientToken (launch_instances, run_instances) is not used for Eucalyptus clouds
+ - VPC2, stage1. Next methods were updated:
+ - associate_address, modify_security_group, create_security_group, create_vpc, delete_security_group,
+ describe_addresses, describe_images, describe_instance_attribute, describe_regions, describe_reserved_instances_offerings,
+ describe_security_groups, describe_snapshots, describe_spot_instance_requests, describe_spot_price_history,
+ describe_vpcs, disassociate_address, modify_image_attribute, modify_snapshot_attribute, release_address,
+ request_spot_instances, stop_instances
- EC2: ClientToken (launch_instances, run_instances) is not used for Eucalyptus clouds
- RDS:
- RDS: API 2011-04-01
@@ -314,4 +321,9 @@ the source key.
- Issue 53: regression in latest master version of right_rds_interface
- Issue 73: Can't get list of instances with RdsInterface
- EBS: Issue 54: regression in right_ec2_ebs.rb
- - Add the port number with server name in the v2 signature string if it is not the RFC standard number (author: unakatsuo).
+ - Add the port number with server name in the v2 signature string if it is not the RFC standard number (author: unakatsuo).
+ - EMR (Elastic Map Reduce) support
+ - SNS (Simple Notification Service) support
+ - SDB: ConsistentRead support
+ - bunch of micro bugs
+
View
1  Manifest.txt
@@ -13,6 +13,7 @@ lib/ec2/right_ec2_spot_instances.rb
lib/ec2/right_ec2_ebs.rb
lib/ec2/right_ec2_reserved_instances.rb
lib/ec2/right_ec2_vpc.rb
+lib/ec2/right_ec2_vpc2.rb
lib/ec2/right_ec2_monitoring.rb
lib/ec2/right_ec2_placement_groups.rb
lib/ec2/right_ec2_windows_mobility.rb
View
42 Rakefile
@@ -2,7 +2,6 @@
require 'rubygems'
require "rake/testtask"
-require 'rcov/rcovtask'
require 'rake/gempackagetask'
require 'rake/clean'
$: << File.dirname(__FILE__)
@@ -13,7 +12,19 @@ begin
rescue LoadError => e
STDERR.puts("Bundler is not available, some rake tasks will not be defined: #{e.message}")
else
- Bundler::GemHelper.install_tasks
+ Bundler::GemHelper.install_tasks :name => 'right_aws'
+end
+
+begin
+ require 'rcov/rcovtask'
+rescue LoadError => e
+ STDERR.puts("RCov is not available, some rake tasks will not be defined: #{e.message}")
+else
+ desc "Analyze code coverage of the unit tests."
+ Rcov::RcovTask.new do |t|
+ t.test_files = FileList[testglobs]
+ #t.verbose = true # uncomment to see the executed command
+ end
end
# == Gem == #
@@ -28,12 +39,6 @@ directory gemtask.package_dir
CLEAN.include(gemtask.package_dir)
-desc "Analyze code coverage of the unit tests."
-Rcov::RcovTask.new do |t|
- t.test_files = FileList[testglobs]
- #t.verbose = true # uncomment to see the executed command
-end
-
desc "Test just the SQS interface"
task :testsqs do
require 'test/test_credentials'
@@ -101,4 +106,25 @@ task :testrds do
require 'test/rds/test_right_rds.rb'
end
+desc "Test just the SNS interface"
+task :testsns do
+ require 'test/test_credentials'
+ TestCredentials.get_credentials
+ require 'test/sns/test_right_sns.rb'
+end
+
+desc "Test Route 53 interface"
+task :testroute53 do
+ require 'test/test_credentials'
+ TestCredentials.get_credentials
+ require 'test/route_53/test_right_route_53'
+end
+
+desc "Test ELB interface"
+task :testelb do
+ require 'test/test_credentials'
+ TestCredentials.get_credentials
+ require 'test/elb/test_right_elb'
+end
+
# vim: syntax=Ruby
View
166 lib/awsbase/right_awsbase.rb
@@ -422,7 +422,7 @@ def get_connection(request) # :nodoc:
# ACF, AMS, EC2, LBS and SDB uses this guy
# SQS and S3 use their own methods
- def generate_request_impl(verb, action, options={}) #:nodoc:
+ def generate_request_impl(verb, action, options={}, custom_options={}) #:nodoc:
# Form a valid http verb: 'GET' or 'POST' (all the other are not supported now)
http_verb = verb.to_s.upcase
# remove empty keys from request options
@@ -430,7 +430,7 @@ def generate_request_impl(verb, action, options={}) #:nodoc:
# prepare service data
service_hash = {"Action" => action,
"AWSAccessKeyId" => @aws_access_key_id,
- "Version" => @params[:api_version] }
+ "Version" => custom_options[:api_version] || @params[:api_version] }
service_hash.merge!(options)
# Sign request options
service_params = signed_service_params(@aws_secret_access_key, service_hash, http_verb, @params[:host_to_sign], @params[:service])
@@ -620,10 +620,11 @@ def incrementally_list_items(action, parser_class, params={}, &block) # :nodoc:
# "Filter.2.Value.2"=>"bb"}
def amazonize_list(masks, list, options={}) #:nodoc:
groups = {}
- Array(list).each_with_index do |list_item, i|
+ list_idx = options[:index] || 1
+ Array(list).each do |list_item|
Array(masks).each_with_index do |mask, mask_idx|
key = mask[/\?/] ? mask.dup : mask.dup + '.?'
- key.sub!('?', (i+1).to_s)
+ key.sub!('?', list_idx.to_s)
value = Array(list_item)[mask_idx]
if value.is_a?(Array)
groups.merge!(amazonize_list(key, value, options))
@@ -639,6 +640,7 @@ def amazonize_list(masks, list, options={}) #:nodoc:
groups[key] = value
end
end
+ list_idx += 1
end
groups
end
@@ -668,6 +670,162 @@ def amazonize_block_device_mappings(block_device_mappings, key = 'BlockDeviceMap
result
end
+ # Build API request keys set.
+ #
+ # Options is a hash, expectations is a set of keys [and rules] how to represent options.
+ # Mappings is an Array (may include hashes) or a Hash.
+ #
+ # Example:
+ #
+ # options = { :valid_from => Time.now - 10,
+ # :instance_count => 3,
+ # :image_id => 'ami-08f41161',
+ # :spot_price => 0.059,
+ # :instance_type => 'c1.medium',
+ # :instance_count => 1,
+ # :key_name => 'tim',
+ # :availability_zone => 'us-east-1a',
+ # :monitoring_enabled => true,
+ # :launch_group => 'lg1',
+ # :availability_zone_group => 'azg1',
+ # :groups => ['a', 'b', 'c'],
+ # :group_ids => 'sg-1',
+ # :user_data => 'konstantin',
+ # :block_device_mappings => [ { :device_name => '/dev/sdk',
+ # :ebs_snapshot_id => 'snap-145cbc7d',
+ # :ebs_delete_on_termination => true,
+ # :ebs_volume_size => 3,
+ # :virtual_name => 'ephemeral2' }]}
+ # mappings = { :spot_price,
+ # :availability_zone_group,
+ # :launch_group,
+ # :type,
+ # :instance_count,
+ # :image_id => 'LaunchSpecification.ImageId',
+ # :instance_type => 'LaunchSpecification.InstanceType',
+ # :key_name => 'LaunchSpecification.KeyName',
+ # :addressing_type => 'LaunchSpecification.AddressingType',
+ # :kernel_id => 'LaunchSpecification.KernelId',
+ # :ramdisk_id => 'LaunchSpecification.RamdiskId',
+ # :subnet_id => 'LaunchSpecification.SubnetId',
+ # :availability_zone => 'LaunchSpecification.Placement.AvailabilityZone',
+ # :monitoring_enabled => 'LaunchSpecification.Monitoring.Enabled',
+ # :valid_from => { :value => Proc.new { !options[:valid_from].right_blank? && AwsUtils::utc_iso8601(options[:valid_from]) }},
+ # :valid_until => { :value => Proc.new { !options[:valid_until].right_blank? && AwsUtils::utc_iso8601(options[:valid_until]) }},
+ # :user_data => { :name => 'LaunchSpecification.UserData',
+ # :value => Proc.new { !options[:user_data].right_blank? && Base64.encode64(options[:user_data]).delete("\n") }},
+ # :groups => { :amazonize_list => 'LaunchSpecification.SecurityGroup'},
+ # :group_ids => { :amazonize_list => 'LaunchSpecification.SecurityGroupId'},
+ # :block_device_mappings => { :amazonize_bdm => 'LaunchSpecification.BlockDeviceMapping'})
+ #
+ # map_api_keys_and_values( options, mappings) #=>
+ # {"LaunchSpecification.BlockDeviceMapping.1.Ebs.DeleteOnTermination" => true,
+ # "LaunchSpecification.BlockDeviceMapping.1.VirtualName" => "ephemeral2",
+ # "LaunchSpecification.BlockDeviceMapping.1.Ebs.VolumeSize" => 3,
+ # "LaunchSpecification.BlockDeviceMapping.1.Ebs.SnapshotId" => "snap-145cbc7d",
+ # "LaunchSpecification.BlockDeviceMapping.1.DeviceName" => "/dev/sdk",
+ # "LaunchSpecification.SecurityGroupId.1" => "sg-1",
+ # "LaunchSpecification.InstanceType" => "c1.medium",
+ # "LaunchSpecification.KeyName" => "tim",
+ # "LaunchSpecification.ImageId" => "ami-08f41161",
+ # "LaunchSpecification.SecurityGroup.1" => "a",
+ # "LaunchSpecification.SecurityGroup.2" => "b",
+ # "LaunchSpecification.SecurityGroup.3" => "c",
+ # "LaunchSpecification.Placement.AvailabilityZone" => "us-east-1a",
+ # "LaunchSpecification.Monitoring.Enabled" => true,
+ # "LaunchGroup" => "lg1",
+ # "InstanceCount" => 1,
+ # "SpotPrice" => 0.059,
+ # "AvailabilityZoneGroup" => "azg1",
+ # "ValidFrom" => "2011-06-30T08:06:30.000Z",
+ # "LaunchSpecification.UserData" => "a29uc3RhbnRpbg=="}
+ #
+ def map_api_keys_and_values(options, *mappings) # :nodoc:
+ result = {}
+ vars = {}
+ # Fix inputs and make them all to be hashes
+ mappings.flatten.each do |mapping|
+ unless mapping.is_a?(Hash)
+ # mapping is just a :key_name
+ mapping = { mapping => { :name => mapping.to_s.right_camelize, :value => options[mapping] }}
+ else
+ mapping.each do |local_key, api_opts|
+ unless api_opts.is_a?(Hash)
+ # mapping is a { :key_name => 'ApiKeyName' }
+ mapping[local_key] = { :name => api_opts.to_s, :value => options[local_key]}
+ else
+ # mapping is a { :key_name => { :name => 'ApiKeyName', :value => 'Value', ... etc} }
+ api_opts[:name] = local_key.to_s.right_camelize if (api_opts.keys & [:name, :amazonize_list, :amazonize_bdm]).right_blank?
+ api_opts[:value] = options[local_key] unless api_opts.has_key?(:value)
+ end
+ end
+ end
+ vars.merge! mapping
+ end
+ # Build API keys set
+ # vars now is a Hash:
+ # { :key1 => { :name => 'ApiKey1', :value => 'BlahBlah'},
+ # :key2 => { :amazonize_list => 'ApiKey2.?', :value => [1, ...] },
+ # :key3 => { :amazonize_bdm => 'BDM', :value => [{..}, ...] }, ... }
+ #
+ vars.each do |local_key, api_opts|
+ if api_opts[:amazonize_list]
+ result.merge!(amazonize_list( api_opts[:amazonize_list], api_opts[:value] )) unless api_opts[:value].right_blank?
+ elsif api_opts[:amazonize_bdm]
+ result.merge!(amazonize_block_device_mappings( api_opts[:value], api_opts[:amazonize_bdm] )) unless api_opts[:value].right_blank?
+ else
+ api_key = api_opts[:name]
+ value = api_opts[:value]
+ value = value.call if value.is_a?(Proc)
+ next if value.right_blank?
+ result[api_key] = value
+ end
+ end
+ #
+ result
+ end
+
+ # Transform a hash of parameters into a hash suitable for sending
+ # to Amazon using a key mapping.
+ #
+ # amazonize_hash_with_key_mapping('Group.Filter',
+ # {:some_param => 'SomeParam'},
+ # {:some_param => 'value'}) #=> {'Group.Filter.SomeParam' => 'value'}
+ #
+ def amazonize_hash_with_key_mapping(key, mapping, hash, options={})
+ result = {}
+ unless hash.right_blank?
+ mapping.each do |local_name, remote_name|
+ value = hash[local_name]
+ next if value.nil?
+ result["#{key}.#{remote_name}"] = value
+ end
+ end
+ result
+ end
+
+ # Transform a list of hashes of parameters into a hash suitable for sending
+ # to Amazon using a key mapping.
+ #
+ # amazonize_list_with_key_mapping('Group.Filter',
+ # [{:some_param => 'SomeParam'}, {:some_param => 'SomeParam'}],
+ # {:some_param => 'value'}) #=>
+ # {'Group.Filter.1.SomeParam' => 'value',
+ # 'Group.Filter.2.SomeParam' => 'value'}
+ #
+ def amazonize_list_with_key_mapping(key, mapping, list, options={})
+ result = {}
+ unless list.right_blank?
+ list.each_with_index do |item, index|
+ mapping.each do |local_name, remote_name|
+ value = item[local_name]
+ next if value.nil?
+ result["#{key}.#{index+1}.#{remote_name}"] = value
+ end
+ end
+ end
+ end
+
# Execute a block of code with custom set of settings for right_http_connection.
# Accepts next options (see Rightscale::HttpConnection for explanation):
# :raise_on_timeout
View
4 lib/awsbase/version.rb
@@ -1,7 +1,7 @@
module RightAws #:nodoc:
module VERSION #:nodoc:
- MAJOR = 2 unless defined?(MAJOR)
- MINOR = 1 unless defined?(MINOR)
+ MAJOR = 3 unless defined?(MAJOR)
+ MINOR = 0 unless defined?(MINOR)
TINY = 0 unless defined?(TINY)
STRING = [MAJOR, MINOR, TINY].join('.') unless defined?(STRING)
View
143 lib/ec2/right_ec2.rb
@@ -68,7 +68,7 @@ class Ec2 < RightAwsBase
include RightAwsBaseInterface
# Amazon EC2 API version being used
- API_VERSION = "2010-08-31"
+ API_VERSION = "2011-02-28"
DEFAULT_HOST = "ec2.amazonaws.com"
DEFAULT_PATH = '/'
DEFAULT_PROTOCOL = 'https'
@@ -130,8 +130,8 @@ def initialize(aws_access_key_id=nil, aws_secret_access_key=nil, params={})
#end
end
- def generate_request(action, params={}) #:nodoc:
- generate_request_impl(:get, action, params )
+ def generate_request(action, params={}, custom_options={}) #:nodoc:
+ generate_request_impl(:get, action, params, custom_options)
end
# Sends request to Amazon and parses the response
@@ -236,32 +236,49 @@ def delete_key_pair(name)
#-----------------------------------------------------------------
# Acquire a new elastic IP address for use with your account.
- # Returns allocated IP address or an exception.
+ # Options: :domain.
+ # Returns allocated IP address or or an exception.
#
- # ec2.allocate_address #=> '75.101.154.140'
+ # ec2.allocate_address #=>
+ # { :public_ip => "50.19.214.224",
+ # :domain => "standard"}
#
- def allocate_address
- link = generate_request("AllocateAddress")
+ # ec2.allocate_address(:domain => 'vpc') #=>
+ # { :allocation_id => "eipalloc-c6abfeaf",
+ # :domain => "vpc",
+ # :public_ip => "184.72.112.39"}
+ #
+ def allocate_address(options={})
+ request_hash = {}
+ request_hash['Domain'] = options[:domain] unless options[:domain].right_blank?
+ link = generate_request("AllocateAddress", request_hash)
request_info(link, QEc2AllocateAddressParser.new(:logger => @logger))
rescue Exception
on_exception
end
# Associate an elastic IP address with an instance.
- # Returns +true+ or an exception.
+ # Options: :public_ip, :allocation_id.
+ # Returns a hash of data or an exception.
#
- # ec2.associate_address('i-d630cbbf', '75.101.154.140') #=> true
+ # ec2.associate_address('i-d630cbbf', :public_ip => '75.101.154.140') #=>
+ # { :return => true }
#
- def associate_address(instance_id, public_ip)
- link = generate_request("AssociateAddress",
- "InstanceId" => instance_id.to_s,
- "PublicIp" => public_ip.to_s)
- request_info(link, RightBoolResponseParser.new(:logger => @logger))
+ # ec2.associate_address(inst, :allocation_id => "eipalloc-c6abfeaf") #=>
+ # { :return => true,
+ # :association_id => 'eipassoc-fc5ca095'}
+ #
+ def associate_address(instance_id, options={})
+ request_hash = { "InstanceId" => instance_id.to_s }
+ request_hash['PublicIp'] = options[:public_ip] unless options[:public_ip].right_blank?
+ request_hash['AllocationId'] = options[:allocation_id] unless options[:allocation_id].right_blank?
+ link = generate_request("AssociateAddress", request_hash)
+ request_info(link, QEc2AssociateAddressParser.new(:logger => @logger))
rescue Exception
on_exception
end
- # List elastic IP addresses assigned to your account.
+ # List elastic IPs by public addresses.
#
# Accepts a list of addresses and/or a set of filters as the last parameter.
#
@@ -269,40 +286,62 @@ def associate_address(instance_id, public_ip)
#
# Returns an array of 2 keys (:instance_id and :public_ip) hashes:
#
- # ec2.describe_addresses #=> [{:instance_id=>"i-d630cbbf", :public_ip=>"75.101.154.140"},
- # {:instance_id=>nil, :public_ip=>"75.101.154.141"}]
+ # ec2.describe_addresses #=> [{:instance_id=>"i-75ebd41b", :domain=>"standard", :public_ip=>"50.17.211.96"},
+ # :domain=>"vpc", :public_ip=>"184.72.112.39", :allocation_id=>"eipalloc-c6abfeaf"}]
#
- # ec2.describe_addresses('75.101.154.140') #=> [{:instance_id=>"i-d630cbbf", :public_ip=>"75.101.154.140"}]
+ # ec2.describe_addresses('75.101.154.140') #=> [{:instance_id=>"i-d630cbbf", :public_ip=>"75.101.154.140", :domain=>"standard"}]
#
# ec2.describe_addresses(:filters => { 'public-ip' => "75.101.154.140" })
#
- # P.S. filters: http://docs.amazonwebservices.com/AWSEC2/latest/APIReference/index.html?ApiReference-query-DescribeAddresses.html
+ # P.S. http://docs.amazonwebservices.com/AWSEC2/latest/APIReference/index.html?ApiReference-query-DescribeAddresses.html
#
def describe_addresses(*list_and_options)
describe_resources_with_list_and_options('DescribeAddresses', 'PublicIp', QEc2DescribeAddressesParser, list_and_options)
end
+
+ # List elastic IPs by allocation ids.
+ #
+ # Accepts a list of allocations and/or a set of filters as the last parameter.
+ #
+ # describe_addresses_by_allocation_ids("eipalloc-c6abfeaf") #=>
+ # [{:domain=>"vpc",
+ # :public_ip=>"184.72.112.39",
+ # :allocation_id=>"eipalloc-c6abfeaf"}]
+ #
+ # P.S. http://docs.amazonwebservices.com/AWSEC2/latest/APIReference/index.html?ApiReference-query-DescribeAddresses.html
+ #
+ def describe_addresses_by_allocation_ids(*list_and_options)
+ describe_resources_with_list_and_options('DescribeAddresses', 'AllocationId', QEc2DescribeAddressesParser, list_and_options)
+ end
+
# Disassociate the specified elastic IP address from the instance to which it is assigned.
+ # Options: :public_ip, :association_id.
# Returns +true+ or an exception.
#
- # ec2.disassociate_address('75.101.154.140') #=> true
+ # ec2.disassociate_address(:public_ip => '75.101.154.140') #=> true
#
- def disassociate_address(public_ip)
- link = generate_request("DisassociateAddress",
- "PublicIp" => public_ip.to_s)
+ def disassociate_address(options = {})
+ request_hash = {}
+ request_hash['PublicIp'] = options[:public_ip] unless options[:public_ip].right_blank?
+ request_hash['AssociationId'] = options[:association_id] unless options[:association_id].right_blank?
+ link = generate_request("DisassociateAddress", request_hash)
request_info(link, RightBoolResponseParser.new(:logger => @logger))
rescue Exception
on_exception
end
# Release an elastic IP address associated with your account.
+ # Options: :public_ip, :allocation_id.
# Returns +true+ or an exception.
#
- # ec2.release_address('75.101.154.140') #=> true
+ # ec2.release_address(:public_ip => '75.101.154.140') #=> true
#
- def release_address(public_ip)
- link = generate_request("ReleaseAddress",
- "PublicIp" => public_ip.to_s)
+ def release_address(options = {})
+ request_hash = {}
+ request_hash['PublicIp'] = options[:public_ip] unless options[:public_ip].right_blank?
+ request_hash['AllocationId'] = options[:allocation_id] unless options[:allocation_id].right_blank?
+ link = generate_request("ReleaseAddress", request_hash)
request_info(link, RightBoolResponseParser.new(:logger => @logger))
rescue Exception
on_exception
@@ -344,7 +383,12 @@ def describe_availability_zones(*list_and_options)
#
# Filters: endpoint, region-name
#
- # ec2.describe_regions #=> ["eu-west-1", "us-east-1"]
+ # ec2.describe_regions #=>
+ # [{:region_endpoint=>"ec2.eu-west-1.amazonaws.com", :region_name=>"eu-west-1"},
+ # {:region_endpoint=>"ec2.us-east-1.amazonaws.com", :region_name=>"us-east-1"},
+ # {:region_endpoint=>"ec2.ap-northeast-1.amazonaws.com", :region_name=>"ap-northeast-1"},
+ # {:region_endpoint=>"ec2.us-west-1.amazonaws.com", :region_name=>"us-west-1"},
+ # {:region_endpoint=>"ec2.ap-southeast-1.amazonaws.com", :region_name=>"ap-southeast-1"}]
#
# P.S. filters: http://docs.amazonwebservices.com/AWSEC2/latest/APIReference/ApiReference-query-DescribeRegions.html
#
@@ -403,19 +447,41 @@ def tagend(name)
class QEc2AllocateAddressParser < RightAWSParser #:nodoc:
def tagend(name)
- @result = @text if name == 'publicIp'
+ case name
+ when 'publicIp' then @result[:public_ip] = @text
+ when 'allocationId' then @result[:allocation_id] = @text
+ when 'domain' then @result[:domain] = @text
+ end
+ end
+ def reset
+ @result = {}
end
end
-
+
+ class QEc2AssociateAddressParser < RightAWSParser #:nodoc:
+ def tagend(name)
+ case name
+ when 'return' then @result[:return] = @text == 'true'
+ when 'associationId' then @result[:association_id] = @text
+ end
+ end
+ def reset
+ @result = {}
+ end
+ end
+
class QEc2DescribeAddressesParser < RightAWSParser #:nodoc:
def tagstart(name, attributes)
- @address = {} if name == 'item'
+ @item = {} if name == 'item'
end
def tagend(name)
case name
- when 'instanceId' then @address[:instance_id] = @text.right_blank? ? nil : @text
- when 'publicIp' then @address[:public_ip] = @text
- when 'item' then @result << @address
+ when 'instanceId' then (@item[:instance_id] = @text unless @text.right_blank?)
+ when 'publicIp' then @item[:public_ip] = @text
+ when 'allocationId' then @item[:allocation_id] = @text
+ when 'associationId' then @item[:association_id] = @text
+ when 'domain' then @item[:domain] = @text
+ when 'item' then @result << @item
end
end
def reset
@@ -455,8 +521,15 @@ def reset
#-----------------------------------------------------------------
class QEc2DescribeRegionsParser < RightAWSParser #:nodoc:
+ def tagstart(name, attributes)
+ @item = {} if name == 'item'
+ end
def tagend(name)
- @result << @text if name == 'regionName'
+ case name
+ when 'regionName' then @item[:region_name] = @text
+ when 'regionEndpoint' then @item[:region_endpoint] = @text
+ when 'item' then @result << @item
+ end
end
def reset
@result = []
View
88 lib/ec2/right_ec2_ebs.rb
@@ -153,23 +153,28 @@ def detach_volume(volume_id, instance_id=nil, device=nil, force=nil)
# tag-value, tag:key, volume-id, volume-size
#
# ec2.describe_snapshots #=>
- # [ {:aws_volume_id=>"vol-545fac3d",
- # :aws_description=>"Wikipedia XML Backups (Linux)",
+ # [{:aws_volume_size=>2,
+ # :tags=>{},
+ # :aws_id=>"snap-d010f6b9",
+ # :owner_alias=>"amazon",
# :aws_progress=>"100%",
- # :aws_started_at=>"2009-09-28T23:49:50.000Z",
- # :aws_owner=>"amazon",
- # :aws_id=>"snap-8041f2e9",
- # :aws_volume_size=>500,
- # :aws_status=>"completed"},
- # {:aws_volume_id=>"vol-185fac71",
- # :aws_description=>"Sloan Digital Sky Survey DR6 Subset (Linux)",
+ # :aws_status=>"completed",
+ # :aws_description=>
+ # "Windows 2003 R2 Installation Media [Deprecated] - Enterprise Edition 64-bit",
+ # :aws_owner=>"711940113766",
+ # :aws_volume_id=>"vol-351efb5c",
+ # :aws_started_at=>"2008-10-20T18:23:59.000Z"},
+ # {:aws_volume_size=>2,
+ # :tags=>{},
+ # :aws_id=>"snap-a310f6ca",
+ # :owner_alias=>"amazon",
# :aws_progress=>"100%",
- # :aws_started_at=>"2009-09-28T23:56:10.000Z",
- # :aws_owner=>"amazon",
- # :aws_id=>"snap-3740f35e",
- # :aws_volume_size=>180,
- # :aws_status=>"completed"}, ...]
- #
+ # :aws_status=>"completed",
+ # :aws_description=>"Windows 2003 R2 Installation Media 64-bit",
+ # :aws_owner=>"711940113766",
+ # :aws_volume_id=>"vol-001efb69",
+ # :aws_started_at=>"2008-10-20T18:25:53.000Z"}, ... ]
+ #
# ec2.describe_snapshots("snap-e676e28a", "snap-e176e281")
#
# ec2.describe_snapshots(:restorable_by => ['123456781234'],
@@ -182,6 +187,10 @@ def describe_snapshots(*list_and_options)
describe_resources_with_list_and_options('DescribeSnapshots', 'SnapshotId', QEc2DescribeSnapshotsParser, list_and_options)
end
+ def describe_snapshots_by_restorable_by(*list_and_options)
+ describe_resources_with_list_and_options('DescribeSnapshots', 'RestorableBy', QEc2DescribeSnapshotsParser, list_and_options)
+ end
+
# Create a snapshot of specified volume.
#
# ec2.create_snapshot('vol-898a6fe0', 'KD: WooHoo!!') #=>
@@ -276,18 +285,18 @@ def reset_snapshot_attribute(snapshot_id, attribute='createVolumePermission')
# Modify snapshot attribute.
#
- # attribute : currently, only 'createVolumePermission' is supported.
- # operation_type : currently, only 'add' & 'remove' are supported.
- # vars:
- # :user_group : currently, only 'all' is supported.
- # :user_id : an array of user ids
- #
- def modify_snapshot_attribute(snapshot_id, attribute='createVolumePermission', operation_type='add', vars = {})
- params = {'SnapshotId' => snapshot_id,
- 'Attribute' => attribute,
- 'OperationType' => operation_type}
- params.update(amazonize_list('UserId', Array(vars[:user_id]))) if vars[:user_id]
- params.update(amazonize_list('UserGroup', Array(vars[:user_group]))) if vars[:user_group]
+ # Attribute can take only 'createVolumePermission' value.
+ # Value is a Hash {:add_user_ids, :add_groups, :remove_user_ids, :remove_groups }.
+ #
+ def modify_snapshot_attribute(snapshot_id, attribute, value)
+ params = { 'SnapshotId' => snapshot_id }
+ case attribute.to_s
+ when 'createVolumePermission'
+ params.update(amazonize_list('CreateVolumePermission.Add.?.UserId', value[:add_user_ids]))
+ params.update(amazonize_list('CreateVolumePermission.Add.?.Group', value[:add_groups]))
+ params.update(amazonize_list('CreateVolumePermission.Remove.?.UserId', value[:remove_user_ids]))
+ params.update(amazonize_list('CreateVolumePermission.Remove.?.Group', value[:remove_groups]))
+ end
link = generate_request("ModifySnapshotAttribute", params)
request_info(link, RightBoolResponseParser.new(:logger => @logger))
rescue Exception
@@ -298,36 +307,36 @@ def modify_snapshot_attribute(snapshot_id, attribute='createVolumePermission', o
#
# ec2.modify_snapshot_attribute_create_volume_permission_add_users('snap-36fe435f', '000000000000', '000000000001') #=> true
#
- def modify_snapshot_attribute_create_volume_permission_add_users(snapshot_id, *user_id)
- modify_snapshot_attribute(snapshot_id, 'createVolumePermission', 'add', :user_id => user_id.flatten )
+ def modify_snapshot_attribute_create_volume_permission_add_users(snapshot_id, *user_ids)
+ modify_snapshot_attribute(snapshot_id, 'createVolumePermission', :add_user_ids => user_ids.flatten )
end
# Revoke create volume permission for a list of users.
#
# ec2.modify_snapshot_attribute_create_volume_permission_remove_users('snap-36fe435f', '000000000000', '000000000001') #=> true
#
- def modify_snapshot_attribute_create_volume_permission_remove_users(snapshot_id, *user_id)
- modify_snapshot_attribute(snapshot_id, 'createVolumePermission', 'remove', :user_id => user_id.flatten )
+ def modify_snapshot_attribute_create_volume_permission_remove_users(snapshot_id, *user_ids)
+ modify_snapshot_attribute(snapshot_id, 'createVolumePermission', :remove_user_ids => user_ids.flatten )
end
# Grant create volume permission for user groups (currently only 'all' is supported).
#
# ec2.modify_snapshot_attribute_create_volume_permission_add_groups('snap-36fe435f') #=> true
#
- def modify_snapshot_attribute_create_volume_permission_add_groups(snapshot_id, *user_group)
- user_group.flatten!
- user_group = ['all'] if user_group.right_blank?
- modify_snapshot_attribute(snapshot_id, 'createVolumePermission', 'add', :user_group => user_group )
+ def modify_snapshot_attribute_create_volume_permission_add_groups(snapshot_id, *groups)
+ groups.flatten!
+ groups = ['all'] if groups.right_blank?
+ modify_snapshot_attribute(snapshot_id, 'createVolumePermission', :add_groups => groups )
end
# Remove create volume permission for user groups (currently only 'all' is supported).
#
# ec2.modify_snapshot_attribute_create_volume_permission_remove_groups('snap-36fe435f') #=> true
#
- def modify_snapshot_attribute_create_volume_permission_remove_groups(snapshot_id, *user_group)
- user_group.flatten!
- user_group = ['all'] if user_group.right_blank?
- modify_snapshot_attribute(snapshot_id, 'createVolumePermission', 'remove', :user_group => user_group )
+ def modify_snapshot_attribute_create_volume_permission_remove_groups(snapshot_id, *groups)
+ groups.flatten!
+ groups = ['all'] if groups.right_blank?
+ modify_snapshot_attribute(snapshot_id, 'createVolumePermission', :remove_groups => groups )
end
# Delete the specified snapshot.
@@ -433,6 +442,7 @@ def tagend(name)
when 'description' then @item[:aws_description] = @text
when 'ownerId' then @item[:aws_owner] = @text
when 'volumeSize' then @item[:aws_volume_size] = @text.to_i
+ when 'ownerAlias' then @item[:owner_alias] = @text
else
case full_tag_name
when %r{/tagSet/item/key$} then @aws_tag[:key] = @text
View
117 lib/ec2/right_ec2_images.rb
@@ -180,7 +180,7 @@ def register_image(options)
params['KernelId'] = options[:kernel_id] if options[:kernel_id]
params['RamdiskId'] = options[:ramdisk_id] if options[:ramdisk_id]
params['RootDeviceName'] = options[:root_device_name] if options[:root_device_name]
- params['VirtualizationType'] = options[:virtualization_type] if options[:virtualization_type]
+ params['VirtualizationType'] = options[:virtualization_type] if options[:virtualization_type]
# params['SnapshotId'] = options[:snapshot_id] if options[:snapshot_id]
params.merge!(amazonize_block_device_mappings(options[:block_device_mappings]))
link = generate_request("RegisterImage", params)
@@ -201,9 +201,19 @@ def deregister_image(image_id)
on_exception
end
- # Describe image attributes. Currently 'launchPermission', 'productCodes', 'kernel', 'ramdisk' and 'blockDeviceMapping' are supported.
- #
- # ec2.describe_image_attribute('ami-e444444d') #=> {:groups=>["all"], :users=>["000000000777"]}
+ # Describe image attributes.
+ #
+ # Returns: String (or nil) for 'description', 'kernel', 'ramdisk'; Hash for 'launchPermission'; Array for 'productCodes', 'blockDeviceMapping'
+ #
+ # ec2.describe_image_attribute('ami-00000000', 'description') #=> 'My cool Image'
+ # ec2.describe_image_attribute('ami-00000000', 'launchPermission') #=> {:user_ids=>["443739700000", "115864000000", "309179000000", "857501300000"]}
+ # ec2.describe_image_attribute('ami-00000000', 'productCodes') #=> ["8ED10000"]
+ # ec2.describe_image_attribute('ami-00000000', 'kernel') #=> "aki-9b00e5f2"
+ # ec2.describe_image_attribute('ami-00000000', 'ramdisk') #=> nil
+ # ec2.describe_image_attribute('ami-00000000', 'blockDeviceMapping') #=> [{:device_name=>"sda2", :virtual_name=>"ephemeral0"},
+ # {:device_name=>"sda1", :virtual_name=>"ami"},
+ # {:device_name=>"/dev/sda1", :virtual_name=>"root"},
+ # {:device_name=>"sda3", :virtual_name=>"swap"}]
#
def describe_image_attribute(image_id, attribute='launchPermission')
link = generate_request("DescribeImageAttribute",
@@ -232,19 +242,23 @@ def reset_image_attribute(image_id, attribute='launchPermission')
# instead of modify_image_attribute because the signature of
# modify_image_attribute may change with EC2 service changes.
#
- # attribute : currently, only 'launchPermission' is supported.
- # operation_type : currently, only 'add' & 'remove' are supported.
- # vars:
- # :user_group : currently, only 'all' is supported.
- # :user_id
- # :product_code
- def modify_image_attribute(image_id, attribute, operation_type = nil, vars = {})
- params = {'ImageId' => image_id,
- 'Attribute' => attribute}
- params['OperationType'] = operation_type if operation_type
- params.update(amazonize_list('UserId', vars[:user_id])) if vars[:user_id]
- params.update(amazonize_list('UserGroup', vars[:user_group])) if vars[:user_group]
- params.update(amazonize_list('ProductCode', vars[:product_code])) if vars[:product_code]
+ # Attribute can take next values: 'launchPermission', 'productCode', 'description'.
+ # Value is a String for'description'. is a String or an Array for 'productCode' and
+ # is a Hash {:add_user_ids, :add_groups, :remove_user_ids, :remove_groups } for 'launchPermission'.
+ #
+ def modify_image_attribute(image_id, attribute, value)
+ params = { 'ImageId' => image_id }
+ case attribute.to_s
+ when 'launchPermission'
+ params.update(amazonize_list('LaunchPermission.Add.?.UserId', value[:add_user_ids]))
+ params.update(amazonize_list('LaunchPermission.Add.?.Group', value[:add_groups]))
+ params.update(amazonize_list('LaunchPermission.Remove.?.UserId', value[:remove_user_ids]))
+ params.update(amazonize_list('LaunchPermission.Remove.?.Group', value[:remove_groups]))
+ when 'productCode'
+ params.update(amazonize_list('ProductCode', value))
+ when 'description'
+ params['Description.Value'] = value
+ end
link = generate_request("ModifyImageAttribute", params)
request_info(link, RightBoolResponseParser.new(:logger => @logger))
rescue Exception
@@ -256,16 +270,16 @@ def modify_image_attribute(image_id, attribute, operation_type = nil, vars = {})
# Returns +true+ or an exception.
#
# ec2.modify_image_launch_perm_add_users('ami-e444444d',['000000000777','000000000778']) #=> true
- def modify_image_launch_perm_add_users(image_id, user_id=[])
- modify_image_attribute(image_id, 'launchPermission', 'add', :user_id => user_id)
+ def modify_image_launch_perm_add_users(image_id, *user_ids)
+ modify_image_attribute(image_id, 'launchPermission', :add_user_ids => user_ids.flatten)
end
# Revokes image launch permissions for users. +user_id+ is a list of users AWS accounts ids. Returns +true+ or an exception.
#
# ec2.modify_image_launch_perm_remove_users('ami-e444444d',['000000000777','000000000778']) #=> true
#
- def modify_image_launch_perm_remove_users(image_id, user_id=[])
- modify_image_attribute(image_id, 'launchPermission', 'remove', :user_id => user_id)
+ def modify_image_launch_perm_remove_users(image_id, *user_ids)
+ modify_image_attribute(image_id, 'launchPermission', :remove_user_ids => user_ids.flatten)
end
# Add image launch permissions for users groups (currently only 'all' is supported, which gives public launch permissions).
@@ -273,24 +287,32 @@ def modify_image_launch_perm_remove_users(image_id, user_id=[])
#
# ec2.modify_image_launch_perm_add_groups('ami-e444444d') #=> true
#
- def modify_image_launch_perm_add_groups(image_id, user_group=['all'])
- modify_image_attribute(image_id, 'launchPermission', 'add', :user_group => user_group)
+ def modify_image_launch_perm_add_groups(image_id, *groups)
+ modify_image_attribute(image_id, 'launchPermission', :add_groups => groups.flatten)
end
# Remove image launch permissions for users groups (currently only 'all' is supported, which gives public launch permissions).
#
# ec2.modify_image_launch_perm_remove_groups('ami-e444444d') #=> true
#
- def modify_image_launch_perm_remove_groups(image_id, user_group=['all'])
- modify_image_attribute(image_id, 'launchPermission', 'remove', :user_group => user_group)
+ def modify_image_launch_perm_remove_groups(image_id, *groups)
+ modify_image_attribute(image_id, 'launchPermission', :remove_groups => groups.flatten)
end
# Add product code to image
#
# ec2.modify_image_product_code('ami-e444444d','0ABCDEF') #=> true
#
- def modify_image_product_code(image_id, product_code=[])
- modify_image_attribute(image_id, 'productCodes', nil, :product_code => product_code)
+ def modify_image_product_code(image_id, product_codes=[])
+ modify_image_attribute(image_id, 'productCodes',product_codes)
+ end
+
+ # Modify image description
+ #
+ # ec2.modify_image_product_code('ami-e444444d','My cool image') #=> true
+ #
+ def modify_image_description(image_id, description)
+ modify_image_attribute(image_id, 'description', description)
end
# Create a new image.
@@ -345,6 +367,7 @@ def tagend(name)
when 'rootDeviceName' then @item[:root_device_name] = @text
when 'imageClass' then @item[:image_class] = @text
when 'virtualizationType' then @item[:virtualization_type] = @text
+ when 'hypervisor' then @item [:hypervisor] = @text
else
case full_tag_name
when %r{/stateReason/code$} then @item[:state_reason_code] = @text.to_i
@@ -382,29 +405,35 @@ def tagend(name)
class QEc2DescribeImageAttributeParser < RightAWSParser #:nodoc:
def tagstart(name, attributes)
- case name
- when 'launchPermission'
- @result[:groups] = []
- @result[:users] = []
- when 'productCodes'
- @result[:aws_product_codes] = []
+ case full_tag_name
+ when %r{launchPermission$} then @result = {}
+ when %r{productCodes$} then @result = []
+ when %r{blockDeviceMapping$} then @result = []
+ when %r{blockDeviceMapping/item$} then @block_device_mapping = {}
end
end
def tagend(name)
- # right now only 'launchPermission' is supported by Amazon.
- # But nobody know what will they xml later as attribute. That is why we
- # check for 'group' and 'userId' inside of 'launchPermission/item'
- case name
- when 'imageId' then @result[:aws_id] = @text
- when 'group' then @result[:groups] << @text if @xmlpath == 'DescribeImageAttributeResponse/launchPermission/item'
- when 'userId' then @result[:users] << @text if @xmlpath == 'DescribeImageAttributeResponse/launchPermission/item'
- when 'productCode' then @result[:aws_product_codes] << @text
- when 'kernel' then @result[:aws_kernel] = @text
- when 'ramdisk' then @result[:aws_ramdisk] = @text
+ case full_tag_name
+ when %r{/kernel/value$} then @result = @text
+ when %r{/ramdisk/value$} then @result = @text
+ when %r{/description/value$} then @result = @text
+ when %r{/productCode$} then @result << @text
+ when %r{launchPermission/item/group$} then (@result[:groups] ||=[]) << @text
+ when %r{launchPermission/item/userId$} then (@result[:user_ids]||=[]) << @text
+ when %r{/blockDeviceMapping/item} # no trailing $
+ case name
+ when 'deviceName' then @block_device_mapping[:device_name] = @text
+ when 'virtualName' then @block_device_mapping[:virtual_name] = @text
+ when 'noDevice' then @block_device_mapping[:no_device] = @text
+ when 'snapshotId' then @block_device_mapping[:ebs_snapshot_id] = @text
+ when 'volumeSize' then @block_device_mapping[:ebs_volume_size] = @text
+ when 'deleteOnTermination' then @block_device_mapping[:ebs_delete_on_termination] = @text == 'true' ? true : false
+ when 'item' then @result << @block_device_mapping
+ end
end
end
def reset
- @result = {}
+ @result = nil
end
end
View
317 lib/ec2/right_ec2_instances.rb
@@ -38,7 +38,8 @@ def get_desc_instances(instances) # :nodoc:
instance[:aws_reason] = instance[:aws_reason].sub(/\(\d[^)]*GMT\) */, '')
instance[:aws_owner] = reservation[:aws_owner]
instance[:aws_reservation_id] = reservation[:aws_reservation_id]
- instance[:aws_groups] = reservation[:aws_groups]
+ # Security Groups
+ instance[:groups] = instance[:groups].right_blank? ? reservation[:aws_groups] : instance[:groups]
result << instance
end
end
@@ -61,38 +62,45 @@ def get_desc_instances(instances) # :nodoc:
#
#
# ec2.describe_instances #=>
- # [{:private_ip_address=>"10.240.7.99",
- # :aws_image_id=>"ami-c2a3f5d4",
- # :ip_address=>"174.129.134.109",
- # :dns_name=>"ec2-174-129-134-109.compute-1.amazonaws.com",
- # :aws_instance_type=>"m1.small",
- # :aws_owner=>"826693181925",
- # :root_device_name=>"/dev/sda1",
- # :instance_class=>"elastic",
- # :aws_state=>"running",
- # :private_dns_name=>"domU-12-31-39-04-00-95.compute-1.internal",
- # :aws_reason=>"",
- # :aws_launch_time=>"2009-11-18T14:03:25.000Z",
- # :aws_reservation_id=>"r-54d38542",
- # :aws_state_code=>16,
- # :ami_launch_index=>"0",
- # :aws_availability_zone=>"us-east-1a",
- # :aws_groups=>["default"],
- # :monitoring_state=>"disabled",
- # :aws_product_codes=>[],
- # :ssh_key_name=>"",
- # :block_device_mappings=>
- # [{:ebs_status=>"attached",
- # :ebs_delete_on_termination=>true,
- # :ebs_attach_time=>"2009-11-18T14:03:34.000Z",
- # :device_name=>"/dev/sda1",
- # :ebs_volume_id=>"vol-e600f98f"},
- # {:ebs_status=>"attached",
- # :ebs_delete_on_termination=>true,
- # :ebs_attach_time=>"2009-11-18T14:03:34.000Z",
- # :device_name=>"/dev/sdk",
- # :ebs_volume_id=>"vol-f900f990"}],
- # :aws_instance_id=>"i-8ce84ae4"} , ... ]
+ # [{:source_dest_check=>true,
+ # :subnet_id=>"subnet-da6cf9b3",
+ # :aws_kernel_id=>"aki-3932d150",
+ # :ami_launch_index=>"0",
+ # :tags=>{},
+ # :aws_reservation_id=>"r-7cd25c11",
+ # :aws_owner=>"826693181925",
+ # :state_reason_code=>"Client.UserInitiatedShutdown",
+ # :aws_instance_id=>"i-2d898e41",
+ # :hypervisor=>"xen",
+ # :root_device_name=>"/dev/sda1",
+ # :aws_ramdisk_id=>"ari-c515f6ac",
+ # :aws_instance_type=>"m1.large",
+ # :groups=>[{:group_name=>"2009-07-15-default", :group_id=>"sg-90c5d6fc"}],
+ # :block_device_mappings=>
+ # [{:device_name=>"/dev/sda1",
+ # :ebs_status=>"attached",
+ # :ebs_attach_time=>"2011-03-04T18:51:58.000Z",
+ # :ebs_delete_on_termination=>true,
+ # :ebs_volume_id=>"vol-38f2bd50"}],
+ # :state_reason_message=>
+ # "Client.UserInitiatedShutdown: User initiated shutdown",
+ # :aws_image_id=>"ami-a3638cca",
+ # :virtualization_type=>"paravirtual",
+ # :aws_launch_time=>"2011-03-04T18:13:59.000Z",
+ # :private_dns_name=>"",
+ # :aws_product_codes=>[],
+ # :aws_availability_zone=>"us-east-1a",
+ # :aws_state_code=>80,
+ # :architecture=>"x86_64",
+ # :dns_name=>"",
+ # :client_token=>"1299262447-684266-NNgyH-ouPTI-MzG6h-5AIRk",
+ # :root_device_type=>"ebs",
+ # :vpc_id=>"vpc-e16cf988",
+ # :monitoring_state=>"disabled",
+ # :ssh_key_name=>"default",
+ # :private_ip_address=>"192.168.0.52",
+ # :aws_reason=>"User initiated ",
+ # :aws_state=>"stopped"}, ...]
#
# ec2.describe_instances("i-8ce84ae6", "i-8ce84ae8", "i-8ce84ae0")
# ec2.describe_instances(:filters => { 'availability-zone' => 'us-east-1a', 'instance-type' => 'c1.medium' })
@@ -118,7 +126,7 @@ def confirm_product_instance(instance, product_code)
# Launch new EC2 instances. Returns a list of launched instances or an exception.
#
- # ec2.run_instances('ami-e444444d',1,1,['my_awesome_group'],'my_awesome_key', 'Woohoo!!!', 'public') #=>
+ # ec2.run_instances('ami-e444444d',1,1,['2009-07-15-default'],'my_awesome_key', 'Woohoo!!!', 'public') #=>
# [{:aws_image_id => "ami-e444444d",
# :aws_reason => "",
# :aws_state_code => "0",
@@ -128,7 +136,7 @@ def confirm_product_instance(instance, product_code)
# :aws_state => "pending",
# :dns_name => "",
# :ssh_key_name => "my_awesome_key",
- # :aws_groups => ["my_awesome_group"],
+ # :groups => [{:group_name=>"2009-07-15-default", :group_id=>"sg-90c5d6fc"}],
# :private_dns_name => "",
# :aws_instance_type => "m1.small",
# :aws_launch_time => "2008-1-1T00:00:00.000Z"
@@ -138,7 +146,7 @@ def confirm_product_instance(instance, product_code)
# :aws_availability_zone => "us-east-1b"
# }]
#
- def run_instances(image_id, min_count, max_count, group_ids, key_name, user_data='',
+ def run_instances(image_id, min_count, max_count, group_names, key_name, user_data='',
addressing_type = nil, instance_type = nil,
kernel_id = nil, ramdisk_id = nil, availability_zone = nil,
monitoring_enabled = nil, subnet_id = nil, disable_api_termination = nil,
@@ -147,7 +155,7 @@ def run_instances(image_id, min_count, max_count, group_ids, key_name, user_data
launch_instances(image_id, { :min_count => min_count,
:max_count => max_count,
:user_data => user_data,
- :group_ids => group_ids,
+ :group_names => group_names,
:key_name => key_name,
:instance_type => instance_type,
:addressing_type => addressing_type,
@@ -165,50 +173,74 @@ def run_instances(image_id, min_count, max_count, group_ids, key_name, user_data
end
# Launch new EC2 instances.
+ #
# Options: :image_id, :addressing_type, :min_count, max_count, :key_name, :kernel_id, :ramdisk_id,
# :availability_zone, :monitoring_enabled, :subnet_id, :disable_api_termination, :instance_initiated_shutdown_behavior,
- # :block_device_mappings, :placement_group_name, :license_pool
+ # :block_device_mappings, :placement_group_name, :license_pool, :group_ids, :group_names, :private_ip_address
#
# Returns a list of launched instances or an exception.
#
- # ec2.launch_instances( 'ami-c2a3f5d4',
+ # ec2.launch_instances( "ami-78779511",
# :min_count => 1,
- # :group_ids => 'default',
+ # :group_names => ["default", "eugeg223123123"],
# :user_data => 'Ohoho!',
# :availability_zone => "us-east-1a",
- # :disable_api_termination => true,
+ # :disable_api_termination => false,
# :instance_initiated_shutdown_behavior => 'terminate',
- # :block_device_mappings => [ {:ebs_snapshot_id=>"snap-7360871a",
+ # :block_device_mappings => [ {:ebs_snapshot_id=>"snap-e40fd188",
# :ebs_delete_on_termination=>true,
# :device_name => "/dev/sdk",
# :virtual_name => "mystorage"} ] ) #=>
- # [{:aws_image_id=>"ami-c2a3f5d4",
- # :dns_name=>"",
- # :aws_instance_type=>"m1.small",
- # :aws_owner=>"826693181925",
- # :root_device_name=>"/dev/sda1",
- # :instance_class=>"elastic",
- # :state_reason_code=>0,
- # :aws_state=>"pending",
+ # [{:hypervisor=>"xen",
# :private_dns_name=>"",
+ # :client_token=>"1309532374-551037-gcsBj-gEypk-piG06-ODfQm",
+ # :monitoring_state=>"disabled",
+ # :aws_availability_zone=>"us-east-1a",
+ # :root_device_name=>"/dev/sda1",
+ # :state_reason_code=>"pending",
+ # :dns_name=>"",
+ # :tags=>{},
# :aws_reason=>"",
- # :aws_launch_time=>"2009-11-18T14:03:25.000Z",
- # :aws_reservation_id=>"r-54d38542",
+ # :virtualization_type=>"paravirtual",
# :state_reason_message=>"pending",
- # :aws_state_code=>0,
+ # :aws_reservation_id=>"r-6fada703",
+ # :aws_ramdisk_id=>"ari-a51cf9cc",
# :ami_launch_index=>"0",
- # :aws_availability_zone=>"us-east-1a",
- # :aws_groups=>["default"],
- # :monitoring_state=>"disabled",
- # :aws_product_codes=>[],
- # :ssh_key_name=>"",
- # :aws_instance_id=>"i-8ce84ae4"}]
+ # :groups=>
+ # [{:group_id=>"sg-a0b85dc9", :group_name=>"default"},
+ # {:group_id=>"sg-70733019", :group_name=>"eugeg223123123"}],
+ # :aws_owner=>"826693181925",
+ # :aws_instance_type=>"m1.small",
+ # :aws_state=>"pending",
+ # :root_device_type=>"ebs",
+ # :aws_image_id=>"ami-78779511",
+ # :aws_kernel_id=>"aki-a71cf9ce",
+ # :aws_launch_time=>"2011-07-01T14:59:35.000Z",
+ # :aws_state_code=>0,
+ # :aws_instance_id=>"i-4f202621",
+ # :aws_product_codes=>[]}]
#
def launch_instances(image_id, options={})
- options[:image_id] = image_id
- options[:min_count] ||= 1
- options[:max_count] ||= options[:min_count]
- params = prepare_instance_launch_params(options)
+ options[:user_data] = options[:user_data].to_s
+ params = map_api_keys_and_values( options,
+ :key_name, :addressing_type, :kernel_id,
+ :ramdisk_id, :subnet_id, :instance_initiated_shutdown_behavior,
+ :private_ip_address, :additional_info, :license_pool,
+ :image_id => { :value => image_id },
+ :min_count => { :value => options[:min_count] || 1 },
+ :max_count => { :value => options[:max_count] || options[:min_count] || 1 },
+ :placement_tenancy => 'Placement.Tenancy',
+ :placement_group_name => 'Placement.GroupName',
+ :availability_zone => 'Placement.AvailabilityZone',
+ :group_names => { :amazonize_list => 'SecurityGroup' },
+ :group_ids => { :amazonize_list => 'SecurityGroupId' },
+ :block_device_mappings => { :amazonize_bdm => 'BlockDeviceMapping' },
+ :instance_type => { :value => options[:instance_type] || DEFAULT_INSTANCE_TYPE },
+ :disable_api_termination => { :value => Proc.new{ !options[:disable_api_termination].nil? && options[:disable_api_termination].to_s }},
+ :client_token => { :value => !@params[:eucalyptus] && (options[:client_token] || AwsUtils::generate_unique_token)},
+ :user_data => { :value => Proc.new { !options[:user_data].empty? && Base64.encode64(options[:user_data]).delete("\n") }},
+ :monitoring_enabled => { :name => 'Monitoring.Enabled',
+ :value => Proc.new{ options[:monitoring_enabled] && options[:monitoring_enabled].to_s }})
# Log debug information
@logger.info("Launching instance of image #{image_id}. Options: #{params.inspect}")
link = generate_request("RunInstances", params)
@@ -218,43 +250,6 @@ def launch_instances(image_id, options={})
on_exception
end
- def prepare_instance_launch_params(options={}) # :nodoc:
- params = amazonize_list('SecurityGroup', Array(options[:group_ids]))
- params['InstanceType'] = options[:instance_type] || DEFAULT_INSTANCE_TYPE
- params['ImageId'] = options[:image_id] unless options[:image_id].right_blank?
- params['AddressingType'] = options[:addressing_type] unless options[:addressing_type].right_blank?
- params['MinCount'] = options[:min_count] unless options[:min_count].right_blank?
- params['MaxCount'] = options[:max_count] unless options[:max_count].right_blank?
- params['KeyName'] = options[:key_name] unless options[:key_name].right_blank?
- params['KernelId'] = options[:kernel_id] unless options[:kernel_id].right_blank?
- params['RamdiskId'] = options[:ramdisk_id] unless options[:ramdisk_id].right_blank?
- params['Placement.AvailabilityZone'] = options[:availability_zone] unless options[:availability_zone].right_blank?
- params['Monitoring.Enabled'] = options[:monitoring_enabled].to_s if options[:monitoring_enabled]
- params['SubnetId'] = options[:subnet_id] unless options[:subnet_id].right_blank?
- params['AdditionalInfo'] = options[:additional_info] unless options[:additional_info].right_blank?
- params['DisableApiTermination'] = options[:disable_api_termination].to_s unless options[:disable_api_termination].nil?
- params['InstanceInitiatedShutdownBehavior'] = options[:instance_initiated_shutdown_behavior] unless options[:instance_initiated_shutdown_behavior].right_blank?
- params['Placement.GroupName'] = options[:placement_group_name] unless options[:placement_group_name].right_blank?
- params['License.Pool'] = options[:license_pool] unless options[:license_pool].right_blank?
- # Client token: do not set it automatically for Euca clouds (but let it go if it was set by a user)
- client_token = options[:client_token]
- client_token ||= AwsUtils::generate_unique_token unless @params[:eucalyptus]
- params['ClientToken'] = client_token if client_token
- #
- params.merge!(amazonize_block_device_mappings(options[:block_device_mappings]))
- # KD: https://github.com/rightscale/right_aws/issues#issue/11
- # Do not modify user data and pass it as is: one may pass there a hex-binary data
- options[:user_data] = options[:user_data].to_s
- unless options[:user_data].empty?
- # Do not use CGI::escape(encode64(...)) as it is done in Amazons EC2 library.
- # Amazon 169.254.169.254 does not like escaped symbols!
- # And it doesn't like "\n" inside of encoded string! Grrr....
- # Otherwise, some of UserData symbols will be lost...
- params['UserData'] = Base64.encode64(options[:user_data]).delete("\n")
- end
- params
- end
-
# Start instances.
#
# ec2.start_instances("i-36e84a5e") #=>
@@ -272,6 +267,8 @@ def start_instances(*instance_aws_ids)
# Stop instances.
#
+ # Options: :force => true|false
+ #
# ec2.stop_instances("i-36e84a5e") #=>
# [{:aws_prev_state_code=>16,
# :aws_prev_state_name=>"running",
@@ -279,9 +276,12 @@ def start_instances(*instance_aws_ids)
# :aws_current_state_code=>64,
# :aws_current_state_name=>"stopping"}]
#
- def stop_instances(*instance_aws_ids)
- instance_aws_ids = instance_aws_ids.flatten
- link = generate_request("StopInstances", amazonize_list('InstanceId', instance_aws_ids))
+ def stop_instances(*instance_aws_ids_and_options)
+ list, options = AwsUtils::split_items_and_params(instance_aws_ids_and_options)
+ request_hash = {}
+ request_hash['Force'] = true if options[:force]
+ request_hash.merge!(amazonize_list('InstanceId', list))
+ link = generate_request("StopInstances", request_hash)
request_info(link, QEc2TerminateInstancesParser.new(:logger => @logger))
end
@@ -328,52 +328,38 @@ def reboot_instances(*instances)
on_exception
end
- INSTANCE_ATTRIBUTE_MAPPING = {
- "instance_type" => "instanceType",
- "kernel" => "kernel",
- "ramdisk" => "ramdisk",
- "user_data" => "userData",
- "disable_api_termination" => "disableApiTermination",
- "instance_initiated_shutdown_behavior" => "instanceInitiatedShutdownBehavior",
- "root_device_name" => "rootDeviceName",
- "block_device_mapping" => "blockDeviceMapping"
- }
-
# Describe instance attribute.
- # Attributes: :instance_type, :kernel, :ramdisk, :user_data, :disable_api_termination, :instance_initiated_shutdown_behavior, :root_device_name, :block_device_mapping
#
- # ec2.describe_instance_attribute(instance, "BlockDeviceMapping") #=>
+ # Attributes: 'instanceType', 'kernel', 'ramdisk', 'userData', 'rootDeviceName', 'disableApiTermination',
+ # 'instanceInitiatedShutdownBehavior', 'sourceDestCheck', 'blockDeviceMapping', 'groupSet'
+ #
+ # ec2.describe_instance_attribute(instance, "blockDeviceMapping") #=>
# [{:ebs_delete_on_termination=>true,
# :ebs_volume_id=>"vol-683dc401",
# :device_name=>"/dev/sda1"}]
#
- # ec2.describe_instance_attribute(instance, "InstanceType") #=> "m1.small"
+ # ec2.describe_instance_attribute(instance, "instanceType") #=> "m1.small"
#
- # ec2.describe_instance_attribute(instance, "InstanceInitiatedShutdownBehavior") #=> "stop"
+ # ec2.describe_instance_attribute(instance, "instanceInitiatedShutdownBehavior") #=> "stop"
#
def describe_instance_attribute(instance_id, attribute)
- attribute = INSTANCE_ATTRIBUTE_MAPPING[attribute.to_s] || attribute.to_s
link = generate_request('DescribeInstanceAttribute',
'InstanceId' => instance_id,
'Attribute' => attribute)
value = request_info(link, QEc2DescribeInstanceAttributeParser.new(:logger => @logger))
- case attribute
- when "userData"
- Base64.decode64(value)
- else
- value
- end
+ value = Base64.decode64(value) if attribute == "userData" && !value.right_blank?
+ value
rescue Exception
on_exception
end
# Describe instance attribute.
- # Attributes: :kernel, :ramdisk
#
- # ec2.reset_instance_attribute(instance, :kernel) #=> true
+ # Attributes: 'kernel', 'ramdisk', 'sourceDestCheck'
+ #
+ # ec2.reset_instance_attribute(instance, 'kernel') #=> true
#
def reset_instance_attribute(instance_id, attribute)
- attribute = INSTANCE_ATTRIBUTE_MAPPING[attribute.to_s] || attribute.to_s
link = generate_request('ResetInstanceAttribute',
'InstanceId' => instance_id,
'Attribute' => attribute )
@@ -383,23 +369,21 @@ def reset_instance_attribute(instance_id, attribute)
end
# Modify instance attribute.
- # Attributes: :instance_type, :kernel, :ramdisk, :user_data, :disable_api_termination, :instance_initiated_shutdown_behavior, :root_device_name, :block_device_mapping
#
- # ec2.modify_instance_attribute(instance, :instance_initiated_shutdown_behavior, "stop") #=> true
+ # Attributes: 'InstanceType', 'Kernel', 'Ramdisk', 'UserData', 'DisableApiTermination',
+ # 'InstanceInitiatedShutdownBehavior', 'SourceDestCheck', 'GroupId'
+ #
+ # ec2.modify_instance_attribute(instance, 'instanceInitiatedShutdownBehavior", "stop") #=> true
#
def modify_instance_attribute(instance_id, attribute, value)
- attribute = INSTANCE_ATTRIBUTE_MAPPING[attribute.to_s] || attribute.to_s
- params = { 'InstanceId' => instance_id,
- 'Attribute' => attribute }
+ request_hash = {'InstanceId' => instance_id}
+ attribute = attribute.to_s.right_underscore.right_camelize
case attribute
- when "blockDeviceMapping"
- params.merge!(amazonize_block_device_mappings(value))
- when "userData"
- params['Value'] = Base64.encode64(value).delete("\n")
- else
- params['Value'] = value
+ when 'UserData' then request_hash["#{attribute}.Value"] = Base64.encode64(value).delete("\n")
+ when 'GroupId' then request_hash.merge!(amazonize_list('GroupId', value))
+ else request_hash["#{attribute}.Value"] = value
end
- link = generate_request('ModifyInstanceAttribute', params)
+ link = generate_request('ModifyInstanceAttribute', request_hash)
request_info(link, RightBoolResponseParser.new(:logger => @logger))
rescue Exception
on_exception
@@ -555,21 +539,16 @@ def cancel_bundle_task(bundle_id)
class QEc2DescribeInstancesParser < RightAWSParser #:nodoc:
def tagstart(name, attributes)
- # DescribeInstances property
case full_tag_name
- when 'DescribeInstancesResponse/reservationSet/item',
- 'RunInstancesResponse'
+ when %r{(RunInstancesResponse|DescribeInstancesResponse/reservationSet/item)$}
@reservation = { :aws_groups => [],
:instances_set => [] }
+ when %r{(/groupSet/item|instancesSet/item/placement)$}
+ @group = {}
when %r{instancesSet/item$}
# the optional params (sometimes are missing and we dont want them to be nil)
- @item = { :aws_reason => '',
- :dns_name => '',
- :private_dns_name => '',
- :ami_launch_index => '',
- :ssh_key_name => '',
- :aws_state => '',
- :aws_product_codes => [],
+ @item = { :aws_product_codes => [],
+ :groups => [],
:tags => {} }
when %r{blockDeviceMapping/item$}
@item[:block_device_mappings] ||= []
@@ -582,7 +561,6 @@ def tagend(name)
case name
when 'reservationId' then @reservation[:aws_reservation_id] = @text
when 'ownerId' then @reservation[:aws_owner] = @text
- when 'groupId' then @reservation[:aws_groups] << @text
when 'instanceId' then @item[:aws_instance_id] = @text
when 'imageId' then @item[:aws_image_id] = @text
when 'privateDnsName' then @item[:private_dns_name] = @text
@@ -608,11 +586,25 @@ def tagend(name)
when 'instanceLifecycle' then @item[:instance_lifecycle] = @text
when 'spotInstanceRequestId' then @item[:spot_instance_request_id] = @text
when 'requesterId' then @item[:requester_id] = @text
- when 'groupName' then @item[:placement_group_name] = @text
when 'virtualizationType' then @item[:virtualization_type] = @text
- when 'clientToken' then @item[:client_token] = @text
+ when 'clientToken' then @item[:client_token] = @text
+ when 'sourceDestCheck' then @item[:source_dest_check] = @text == 'true' ? true : false
+ when 'tenancy' then @item[:placement_tenancy] = @text
+ when 'hypervisor' then @item[:hypervisor] = @text
else
case full_tag_name
+ # EC2 Groups
+ when %r{(RunInstancesResponse|/reservationSet/item)/groupSet/item/groupId$} then @group[:group_id] = @text
+ when %r{(RunInstancesResponse|/reservationSet/item)/groupSet/item/groupName$} then @group[:group_name] = @text
+ when %r{(RunInstancesResponse|/reservationSet/item)/groupSet/item$} then @reservation[:aws_groups] << @group
+ # VPC Groups
+ # KD: It seems that these groups are always present when the groups above present for non VPC instances only
+ when %r{/instancesSet/item/groupSet/item/groupId$} then @group[:group_id] = @text
+ when %r{/instancesSet/item/groupSet/item/groupName$} then @group[:group_name] = @text
+ when %r{/instancesSet/item/groupSet/item$} then @item[:groups] << @group
+ # Placement Group Name
+ when %r{/placement/groupName$} then @group[:placement_group_name]= @text
+ # Codes
when %r{/stateReason/code$} then @item[:state_reason_code] = @text
when %r{/stateReason/message$} then @item[:state_reason_message] = @text
when %r{/instanceState/code$} then @item[:aws_state_code] = @text.to_i
@@ -633,9 +625,7 @@ def tagend(name)
when %r{/tagSet/item/key$} then @aws_tag[:key] = @text
when %r{/tagSet/item/value$} then @aws_tag[:value] = @text
when %r{/tagSet/item$} then @item[:tags][@aws_tag[:key]] = @aws_tag[:value]
- when 'DescribeInstancesResponse/reservationSet/item',
- 'RunInstancesResponse'
- @result << @reservation
+ when %r{(RunInstancesResponse|DescribeInstancesResponse/reservationSet/item)$} then @result << @reservation
end
end
end
@@ -672,6 +662,8 @@ def reset
class QEc2DescribeInstanceAttributeParser < RightAWSParser #:nodoc:
def tagstart(name, attributes)
case full_tag_name
+ when %r{groupSet$} then @result = []
+ when %r{groupSet/item$} then @group = {}
when %r{blockDeviceMapping$} then @result = []
when %r{blockDeviceMapping/item$} then @block_device_mapping = {}
end
@@ -679,12 +671,19 @@ def tagstart(name, attributes)
def tagend(name)
case full_tag_name
when %r{/instanceType/value$} then @result = @text
- when %r{/kernel$} then @result = @text
- when %r{/ramdisk$} then @result = @text
- when %r{/userData$} then @result = @text
+ when %r{/kernel/value$} then @result = @text
+ when %r{/ramdisk/value$} then @result = @text
+ when %r{/userData/value$} then @result = @text
when %r{/rootDeviceName/value$} then @result = @text
when %r{/disableApiTermination/value} then @result = @text == 'true' ? true : false
when %r{/instanceInitiatedShutdownBehavior/value$} then @result = @text
+ when %r{/sourceDestCheck/value$} then @result = @text == 'true' ? true : false
+ when %r{/groupSet/item} # no trailing $
+ case name
+ when 'groupId' then @group[:group_id] = @text
+ when 'groupName' then @group[:group_name] = @text
+ when 'item' then @result << @group
+ end
when %r{/blockDeviceMapping/item} # no trailing $
case name
when 'deviceName' then @block_device_mapping[:device_name] = @text
View
62 lib/ec2/right_ec2_reserved_instances.rb
@@ -37,16 +37,19 @@ class Ec2
# reserved-instances-id, start, state, tag-key, tag-value, tag:key, usage-price
#
# ec2.describe_reserved_instances #=>
- # [{:aws_id=>"1ba8e2e3-1c40-434c-a741-5ff16a4c542e",
- # :aws_duration=>31536000,
- # :aws_instance_type=>"m1.small",
- # :aws_usage_price=>0.03,
- # :aws_availability_zone=>"us-east-1b",
- # :aws_state=>"payment-pending",
- # :aws_product_description=>"Test",
- # :aws_fixed_price=>325.0,
- # :aws_start=>"2009-12-18T20:39:39.569Z"
- # :aws_instance_count=>1}]
+ # [{:currency_code=>"USD",
+ # :aws_fixed_price=>350.0,
+ # :aws_availability_zone=>"us-east-1c",
+ # :aws_instance_count=>1,
+ # :tags=>{},
+ # :aws_id=>"4357912c-ad94-4f57-8625-15ca71a8e66d",
+ # :aws_product_description=>"Linux/UNIX",
+ # :aws_state=>"active",
+ # :aws_start=>"2010-03-18T20:39:39.569Z",
+ # :aws_duration=>94608000,
+ # :aws_instance_type=>"m1.small",
+ # :instance_tenancy=>"default",
+ # :aws_usage_price=>0.03}]
#
# ec2.describe_reserved_instances(:filters => {'availability-zone' => 'us-east-1a'})
#
@@ -63,22 +66,25 @@ def describe_reserved_instances(*list_and_options)
# Filters: availability-zone, duration, fixed-price, instance-type, product-description, reserved-instances-offering-id, usage-price
#
# ec2.describe_reserved_instances_offerings #=>
- # [{:aws_instance_type=>"c1.medium",
- # :aws_availability_zone=>"us-east-1c",
- # :aws_duration=>94608000,
+ # [{:currency_code=>"USD",
+ # :aws_fixed_price=>700.0,
+ # :aws_id=>"248e7b75-933c-451b-b126-be25865e02a5",
# :aws_product_description=>"Linux/UNIX",
- # :aws_id=>"e5a2ff3b-f6eb-4b4e-83f8-b879d7060257",
+ # :aws_instance_type=>"c1.medium",
+ # :aws_duration=>94608000,
+ # :instance_tenancy=>"default",
# :aws_usage_price=>0.06,
- # :aws_fixed_price=>1000.0},
- # ...
- # {:aws_instance_type=>"m1.xlarge",
- # :aws_availability_zone=>"us-east-1a",
- # :aws_duration=>31536000,
- # :aws_product_description=>"Linux/UNIX",
- # :aws_id=>"c48ab04c-63ab-4cd6-b8f5-978a29eb9bcc",
- # :aws_usage_price=>0.24,
- # :aws_fixed_price=>2600.0}]
- #
+ # :aws_availability_zone=>"us-east-1a"},
+ # {:currency_code=>"USD",
+ # :aws_fixed_price=>700.0,
+ # :aws_id=>"c48ab04c-7e03-46b2-891a-2df1116e51a3",
+ # :aws_product_description=>"Linux/UNIX (Amazon VPC)",
+ # :aws_instance_type=>"c1.medium",
+ # :aws_duration=>94608000,
+ # :instance_tenancy=>"default",
+ # :aws_usage_price=>0.06,
+ # :aws_availability_zone=>"us-east-1a"}, ... ]
+ #
# ec2.describe_reserved_instances_offerings(:filters => {'availability-zone' => 'us-east-1c'})
#
# P.S. filters: http://docs.amazonwebservices.com/AWSEC2/latest/APIReference/index.html?ApiReference-query-DescribeReservedInstancesOfferings.html
@@ -123,6 +129,8 @@ def tagend(name)
when 'productDescription' then @item[:aws_product_description] = @text
when 'state' then @item[:aws_state] = @text
when 'start' then @item[:aws_start] = @text
+ when 'instanceTenancy' then @item[:instance_tenancy] = @text
+ when 'currencyCode' then @item[:currency_code] = @text
else
case full_tag_name
when %r{/tagSet/item/key$} then @aws_tag[:key] = @text
@@ -149,9 +157,11 @@ def tagend(name)
when 'duration' then @item[:aws_duration] = @text.to_i
when 'usagePrice' then @item[:aws_usage_price] = @text.to_f
when 'fixedPrice' then @item[:aws_fixed_price] = @text.to_f
+ when 'instanceTenancy' then @item[:instance_tenancy] = @text
+ when 'currencyCode' then @item[:currency_code] = @text
when 'productDescription' then @item[:aws_product_description] = @text
- when 'item' then @result << @item
- end
+ when 'item' then @result << @item
+ end
end
def reset
@result = []
View
427 lib/ec2/right_ec2_security_groups.rb
@@ -30,6 +30,7 @@ class Ec2
#-----------------------------------------------------------------
# Retrieve Security Groups information.
+ # Options: By default this methods expects security group ids but if you wanna pass their names then :describe_by => :group_name option must be set.
#
# Accepts a list of security groups and/or a set of filters as the last parameter.
#
@@ -40,35 +41,45 @@ class Ec2
# ec2 = Rightscale::Ec2.new(AWS_ACCESS_KEY_ID, AWS_SECRET_ACCESS_KEY)
# ec2.describe_security_groups #=>
# [{:aws_perms=>
- # [{:group=>"default", :owner=>"048291609141"},
- # {:to_port=>"22",
- # :protocol=>"tcp",
- # :from_port=>"22",
- # :cidr_ips=>"0.0.0.0/0"},
- # {:to_port=>"9997",
- # :protocol=>"tcp",
- # :from_port=>"9997",
- # :cidr_ips=>"0.0.0.0/0"}],
- # :aws_group_name=>"photo_us",
- # :aws_description=>"default group",
- # :aws_owner=>"826693181925"}]
+ # [{:protocol=>"-1", :cidr_ips=>"0.0.0.0/0", :direction=>:egress},
+ # {:protocol=>"tcp",
+ # :cidr_ips=>"127.0.0.2/32",
+ # :direction=>:egress,
+ # :from_port=>"1111",
+ # :to_port=>"1111"},
+ # {:protocol=>"tcp",
+ # :cidr_ips=>"127.0.0.1/32",
+ # :direction=>:egress,
+ # :from_port=>"1111",
+ # :to_port=>"1111"}],
+ # :aws_group_name=>"kd-vpc-egress-test-1",
+ # :vpc_id=>"vpc-e16cf988",
+ # :aws_description=>"vpc test",
+ # :aws_owner=>"826693181925",
+ # :group_id=>"sg-b72032db"}]
+ #
+ # # Describe by group ids
+ # ec2.describe_security_groups("sg-a0b85dc9", "sg-00b05d39", "sg-a1b86dc8")
+ #
+ # # Describe by group names
+ # ec2.describe_security_groups("default", "default1", "kd", :describe_by => :group_name)
#
# # Eucalyptus cloud:
# ec2 = Rightscale::Ec2.new(AWS_ACCESS_KEY_ID, AWS_SECRET_ACCESS_KEY, :eucalyptus => true)
# ec2.describe_security_groups #=>
# [{:aws_perms=>
# [{:to_port=>"65535",
- # :group=>"default",
+ # :group_name=>"default",
# :protocol=>"tcp",
# :owner=>"048291609141",
# :from_port=>"1"},
# {:to_port=>"65535",
- # :group=>"default",
+ # :group_name=>"default",
# :protocol=>"udp",
# :owner=>"048291609141",
# :from_port=>"1"},
# {:to_port=>"-1",
- # :group=>"default",
+ # :group_name=>"default",
# :protocol=>"icmp",
# :owner=>"048291609141",
# :from_port=>"-1"},
@@ -89,29 +100,38 @@ class Ec2
# P.S. filters: http://docs.amazonwebservices.com/AWSEC2/latest/APIReference/ApiReference-query-DescribeSecurityGroups.html
#
def describe_security_groups(*list_and_options)
- describe_resources_with_list_and_options('DescribeSecurityGroups', 'GroupName', QEc2DescribeSecurityGroupsParser, list_and_options) do |parser|
+ list, options = AwsUtils::split_items_and_params(list_and_options)
+ describe_by = options.delete(:describe_by) == :group_name ? 'GroupName' : 'GroupId'
+ describe_resources_with_list_and_options('DescribeSecurityGroups', describe_by, QEc2DescribeSecurityGroupsParser, list_and_options) do |parser|
result = []
parser.result.each do |item|
result_item = { :aws_owner => item[:owner_id],
:aws_group_name => item[:group_name],
:aws_description => item[:group_description] }
+ result_item[:group_id] = item[:group_id] unless item[:group_id].right_blank?
+ result_item[:vpc_id] = item[:vpc_id] unless item[:vpc_id].right_blank?
aws_perms = []
item[:ip_permissions].each do |permission|
result_perm = {}
- result_perm[:from_port] = permission[:from_port]
- result_perm[:to_port] = permission[:to_port]
+ result_perm[:from_port] = permission[:from_port] unless permission[:from_port].right_blank?
+ result_perm[:to_port] = permission[:to_port] unless permission[:to_port].right_blank?
result_perm[:protocol] = permission[:ip_protocol]
+ result_perm[:direction] = permission[:direction]
# IP permissions
Array(permission[:ip_ranges]).each do |ip_range|
perm = result_perm.dup
- perm[:cidr_ips] = ip_range
+ # Mhhh... For Eucalyptus we somehow get used to use ":cidr_ip" instead of ":cidr_ips"...
+ if @params[:eucalyptus] then perm[:cidr_ip] = ip_range
+ else perm[:cidr_ips] = ip_range
+ end
aws_perms << perm
end
# Group permissions
Array(permission[:groups]).each do |group|
perm = result_perm.dup
- perm[:group] = group[:group_name]
- perm[:owner] = group[:user_id]
+ perm[:group_name] = group[:group_name] unless group[:group_name].right_blank?
+ perm[:group_id] = group[:group_id] unless group[:group_id].right_blank?
+ perm[:owner] = group[:user_id] unless group[:user_id].right_blank?
aws_perms << perm
end
end
@@ -122,87 +142,68 @@ def describe_security_groups(*list_and_options)
end
end
+ def describe_security_groups_by_name(*list)
+ describe_security_groups(list, :describe_by => :group_name)
+ end
+
# Create new Security Group. Returns +true+ or an exception.
+ # Options: :vpc_id
#
- # ec2.create_security_group('default-1',"Default allowing SSH, HTTP, and HTTPS ingress") #=> true
+ # ec2.create_security_group('default-1',"Default allowing SSH, HTTP, and HTTPS ingress") #=>
+ # { :group_id=>"sg-f0227599", :return=>true }
#
- def create_security_group(name, description=nil)
- # EC2 doesn't like an empty description...
- description = "-" if description.right_blank?
- link = generate_request("CreateSecurityGroup",
- 'GroupName' => name.to_s,
- 'GroupDescription' => description.to_s)
- request_info(link, RightBoolResponseParser.new(:logger => @logger))
+ # ec2.create_security_group('default-2',"my VPC group", :vpc_id => 'vpc-e16c0000') #=>
+ # { :group_id=>"sg-76d1c31a", :return=>true }
+ #
+ def create_security_group(name, description = nil, options = {})
+ options = options.dup
+ options[:group_name] = name
+ options[:group_description] = description.right_blank? ? '-' : description # EC2 rejects an empty description...
+ link = generate_request("CreateSecurityGroup", map_api_keys_and_values(options, :group_name, :group_description, :vpc_id))
+ request_info(link, QEc2CreateSecurityGroupsParser.new(:logger => @logger))
rescue Exception
on_exception
end
# Remove Security Group. Returns +true+ or an exception.
+ # Options: :group_name, :group_id
+ #
+ # # Delete security group by group_id:
+ # ec2.delete_security_group('sg-90054ef9') #=> true
+ # ec2.delete_security_group(:group_id => 'sg-90054ef9') #=> true
#
- # ec2.delete_security_group('default-1') #=> true
+ # # Delete security group by name (EC2 only):
+ # ec2.delete_security_group(:group_name => 'my-group']) #=> true
#
- def delete_security_group(name)
- link = generate_request("DeleteSecurityGroup",
- 'GroupName' => name.to_s)
+ def delete_security_group(group_id_or_options={})
+ options = group_id_or_options.is_a?(Hash) ? group_id_or_options : { :group_id => group_id_or_options }
+ link = generate_request("DeleteSecurityGroup", map_api_keys_and_values(options, :group_name, :group_id))
request_info(link, RightBoolResponseParser.new(:logger => @logger))
rescue Exception
on_exception
end
- # Edit AWS/Eucaliptus security group permissions.
- #
- # Options:
- # action - :authorize (or :grant) | :revoke (or :remove)
- # group_name - security group name
- # permissions - a combination of options below:
- # :source_group_owner => UserId
- # :source_group => GroupName
- # :from_port => from port
- # :to_port => to port
- # :port => set both :from_port and to_port with the same value
- # :protocol => :tcp | :udp | :icmp
- # :cidr_ip => '0.0.0.0/0'
- #
- # ec2.edit_security_group( :grant,
- # 'kd-sg-test',
- # :source_group => "sketchy",
- # :source_group_owner => "600000000006",
- # :protocol => 'tcp',
- # :port => '80',
- # :cidr_ip => '127.0.0.1/32') #=> true
- #
- # P.S. This method is deprecated for AWS and but still good for Eucaliptus clouds.
- # Use +modify_security_group_ingress+ method for AWS clouds.
- #
- def edit_security_group(action, group_name, params)
- hash = {}
- case action
- when :authorize, :grant then action = "AuthorizeSecurityGroupIngress"
- when :revoke, :remove then action = "RevokeSecurityGroupIngress"
- else raise "Unknown action #{action.inspect}!"
- end
- hash['GroupName'] = group_name
- hash['SourceSecurityGroupName'] = params[:source_group] unless params[:source_group].right_blank?
- hash['SourceSecurityGroupOwnerId'] = params[:source_group_owner].to_s.gsub(/-/,'') unless params[:source_group_owner].right_blank?
- hash['IpProtocol'] = params[:protocol] unless params[:protocol].right_blank?
- unless params[:port].right_blank?
- hash['FromPort'] = params[:port]
- hash['ToPort'] = params[:port]
- end
- hash['FromPort'] = params[:from_port] unless params[:from_port].right_blank?
- hash['ToPort'] = params[:to_port] unless params[:to_port].right_blank?
- hash['CidrIp'] = params[:cidr_ip] unless params[:cidr_ip].right_blank?
- #
- link = generate_request(action, hash)
- request_info(link, RightBoolResponseParser.new(:logger => @logger))
- rescue Exception
- on_exception
+ def grant_security_group_ingress(group_id, permissions)
+ modify_security_group(:grant, :ingress, group_id, permissions)
+ end
+
+ def revoke_security_group_ingress(group_id, permissions)
+ modify_security_group(:revoke, :ingress, group_id, permissions)
+ end
+
+ def grant_security_group_egress(group_id, permissions)
+ modify_security_group(:grant, :egress, group_id, permissions)
+ end
+
+ def revoke_security_group_egress(group_id, permissions)
+ modify_security_group(:revoke, :egress, group_id, permissions)
end
# Modify AWS security group permissions.
#
# Options:
# action - :authorize (or :grant) | :revoke (or :remove)
+ # direction - :ingress | :egress
# group_name - security group name
# permissions - a combination of options below:
# # Ports:
@@ -210,70 +211,65 @@ def edit_security_group(action, group_name, params)
# :to_port => to port
# :port => set both :from_port and to_port with the same value
# # Protocol
- # :protocol => :tcp | :udp | :icmp
- # # Group(s)
- # :source_group_owner => UserId
- # :source_group => GroupName
- # # or
- # :source_groups => { UserId1 => GroupName1, UserName2 => GroupName2 }
- # :source_groups => [ [ UserId1, GroupName1 ], [ UserName2 => GroupName2 ] ]
+ # :protocol => :tcp | :udp | :icmp | -1
+ # # or (ingress)
+ # :groups => { UserId1 => GroupId1, UserName2 => GroupId2 }
+ # :groups => [ [ UserId1, GroupId1 ], [ UserName2 => GroupId2 ] ]
+ # # or (egress)
+ # :groups => [ GroupId1, GroupId2 ]
# # CidrIp(s)
# :cidr_ip => '0.0.0.0/0'
# :cidr_ips => ['1.1.1.1/1', '2.2.2.2/2']
#
# # CidrIP based permissions:
#
- # ec2.modify_security_group_ingress(:authorize, 'my_cool_group',
- # :cidr_ip => "127.0.0.0/31",
- # :port => 811,
- # :protocol => 'tcp' ) #=> true
+ # ec2.modify_security_group(:authorize, :ingress, 'sg-75d1c319',
+ # :cidr_ip => "127.0.0.0/31",
+ # :port => 811,
+ # :protocol => 'tcp' ) #=> true
#
- # ec2.modify_security_group_ingress(:revoke, 'my_cool_group',
- # :cidr_ips => ["127.0.0.1/32", "127.0.0.2/32"],
- # :port => 812,
- # :protocol => 'tcp' ) #=> true
+ # ec2.modify_security_group(:revoke, :ingress, 'sg-75d1c319',
+ # :cidr_ips => ["127.0.0.1/32", "127.0.0.2/32"],
+ # :port => 812,
+ # :protocol => 'tcp' ) #=> true
#
# # Group based permissions:
#
- # ec2.modify_security_group_ingress(:authorize, 'my_cool_group',
- # :source_group_owner => "586789340000",
- # :source_group => "sketchy-us",
- # :port => 800,
- # :protocol => 'tcp' ) #=> true
- #
- # ec2.modify_security_group_ingress(:authorize, 'my_cool_group',
- # :source_groups => { "586789340000" => "sketchy-us",
- # "635201710000" => "sketchy" },
- # :port => 801,
- # :protocol => 'tcp' ) #=> true
+ # ec2.modify_security_group(:authorize, :ingress, 'sg-75d1c319',
+ # :groups => { "586789340000" => "sg-75d1c300",
+ # "635201710000" => "sg-75d1c301" },
+ # :port => 801,
+ # :protocol => 'tcp' ) #=> true
#
- # ec2.modify_security_group_ingress(:revoke, 'my_cool_group',
- # :source_groups => [[ "586789340000", "sketchy-us" ],
- # [ "586789340000", "default" ]],
- # :port => 809,
- # :protocol => 'tcp' ) #=> true
+ # ec2.modify_security_group(:revoke, :ingress, 'sg-75d1c319',
+ # :groups => [[ "586789340000", "sg-75d1c300" ],
+ # [ "586789340000", "sg-75d1c302" ]],
+ # :port => 809,
+ # :protocol => 'tcp' ) #=> true
#
# # +Permissions+ can be an array of permission hashes:
#
- # ec2.modify_security_group_ingress(:authorize, 'my_cool_group',
- # [{ :source_groups => { "586789340000" => "sketchy-us",
- # "635201710000" => "sketchy" },
- # :port => 803,
- # :protocol => 'tcp'},
- # { :cidr_ips => ["127.0.0.1/32", "127.0.0.2/32"],
- # :port => 812,
- # :protocol => 'tcp' }]) #=> true
+ # ec2.modify_security_group(:authorize, :ingress, 'sg-75d1c319',
+ # [{ :groups => { "586789340000" => "sg-75d1c300",
+ # "635201710000" => "sg-75d1c301" },
+ # :port => 803,
+ # :protocol => 'tcp'},
+ # { :cidr_ips => ["127.0.0.1/32", "127.0.0.2/32"],
+ # :port => 812,
+ # :protocol => 'tcp' }]) #=> true
#
- def modify_security_group_ingress(action, group_name, permissions)
+ def modify_security_group(action, direction, group_id, permissions)
hash = {}
- case action
- when :authorize, :grant then action = "AuthorizeSecurityGroupIngress"
- when :revoke, :remove then action = "RevokeSecurityGroupIngress"
- else raise "Unknown action #{action.inspect}!"
- end
+ raise "Unknown action #{action.inspect}!" unless [:authorize, :grant, :revoke, :remove].include?(action)
+ raise "Unknown direction #{direction.inspect}!" unless [:ingress, :egress].include?(direction)
+ # Remote action
+ remote_action = case action
+ when :authorize, :grant then direction == :ingress ? "AuthorizeSecurityGroupIngress" : "AuthorizeSecurityGroupEgress"
+ when :revoke, :remove then direction == :ingress ? "RevokeSecurityGroupIngress" : "RevokeSecurityGroupEgress"
+ end
# Group Name
- hash["GroupName"] = group_name
- #
+ hash["GroupId"] = group_id
+ # Permissions
permissions = [permissions] unless permissions.is_a?(Array)
permissions.each_with_index do |permission, idx|
pid = idx+1
@@ -287,30 +283,112 @@ def modify_security_group_ingress(action, group_name, permissions)
hash["IpPermissions.#{pid}.FromPort"] = permission[:from_port]
hash["IpPermissions.#{pid}.ToPort"] = permission[:to_port]
end
- # Source Group(s)
- # Old way (if it is used):
- # :source_group_owner => UserId, :source_group => GroupName
- if !permission[:source_group].right_blank? && !permission[:source_group_owner].right_blank?
- permission[:source_groups] = { permission[:source_group_owner] => permission[:source_group]}
+ # Groups
+ case direction
+ when :ingress
+ # :groups => {UserId1 => GroupId1, ... UserIdN => GroupIdN}
+ # or (this allows using same UserId multiple times )
+ # :groups => [[UserId1, GroupId1], ... [UserIdN, GroupIdN]]
+ # or even (unset user is == current account user)
+ # :groups => [GroupId1, GroupId2, ... GroupIdN]
+ # :groups => [[UserId1, GroupId1], GroupId2, ... GroupIdN, ... [UserIdM, GroupIdM]]
+ #
+ index = 1
+ unless permission[:group_names].right_blank?
+ owner_and_groups = []
+ groups_only = []
+ Array(permission[:group_names]).each do |item|
+ if item.is_a?(Array) && item.size == 2
+ owner_and_groups << item
+ else
+ groups_only << item
+ end
+ end
+ hash.merge!(amazonize_list( ["IpPermissions.#{pid}.Groups.?.UserId", "IpPermissions.#{pid}.Groups.?.GroupName"], owner_and_groups, :index => index ))
+ index += owner_and_groups.size
+ groups_only = groups_only.flatten
+ hash.merge!(amazonize_list( "IpPermissions.#{pid}.Groups.?.GroupName", groups_only, :index => index ))
+ index += groups_only.size
+ end
+ unless permission[:groups].right_blank?
+ owner_and_groups = []
+ groups_only = []
+ Array(permission[:groups]).each do |item|
+ if item.is_a?(Array) && item.size == 2
+ owner_and_groups << item
+ else
+ groups_only << item
+ end
+ end
+ hash.merge!(amazonize_list( ["IpPermissions.#{pid}.Groups.?.UserId", "IpPermissions.#{pid}.Groups.?.GroupId"], owner_and_groups, :index => index ))
+ index += owner_and_groups.size
+ groups_only = groups_only.flatten
+ hash.merge!(amazonize_list( "IpPermissions.#{pid}.Groups.?.GroupId", groups_only, :index => index ))
+ end
+ when :egress
+ # :groups => [GroupId1, ... GroupIdN]
+ hash.merge!(amazonize_list( "IpPermissions.#{pid}.Groups.?.GroupId", permission[:groups] ))
end
-# # Fix UserId(s): '0000-0000-0000' => '000000000000'
-# permission[:source_groups] = Array(permission[:source_groups])
-# permission[:source_groups].each do |item|
-# item[0] = item[0].to_s.gsub(/-/,'')
-# end
- # New way:
- # :source_groups => {UserId1 => GroupName1, ... UserIdN => GroupNameN}
- # or (this allows using same UserId multiple times )
- # :source_groups => [[UserId1, GroupName1], ... [UserIdN, GroupNameN]]
- hash.merge!(amazonize_list( ["IpPermissions.#{pid}.Groups.?.UserId",
- "IpPermissions.#{pid}.Groups.?.GroupName"],
- permission[:source_groups] ))
# CidrIp(s)
cidr_ips = permission[:cidr_ips] unless permission[:cidr_ips].right_blank?
cidr_ips ||= permission[:cidr_ip] unless permission[:cidr_ip].right_blank?
hash.merge!(amazonize_list("IpPermissions.1.IpRanges.?.CidrIp", cidr_ips))
end
#
+ link = generate_request(remote_action, hash)
+ request_info(link, RightBoolResponseParser.new(:logger => @logger))
+ rescue Exception
+ on_exception
+ end
+
+ #-----------------------------------------------------------------
+ # Eucalyptus
+ #-----------------------------------------------------------------
+
+ # Edit AWS/Eucaliptus security group permissions.
+ #
+ # Options:
+ # action - :authorize (or :grant) | :revoke (or :remove)
+ # group_name - security group name
+ # permissions - a combination of options below:
+ # :source_group_owner => UserId
+ # :source_group => GroupName
+ # :from_port => from port
+ # :to_port => to port
+ # :port => set both :from_port and to_port with the same value
+ # :protocol => :tcp | :udp | :icmp
+ # :cidr_ip => '0.0.0.0/0'
+ #
+ # ec2.edit_security_group( :grant,
+ # 'kd-sg-test',
+ # :source_group => "sketchy",
+ # :source_group_owner => "600000000006",
+ # :protocol => 'tcp',
+ # :port => '80',
+ # :cidr_ip => '127.0.0.1/32') #=> true
+ #
+ # P.S. This method is deprecated for AWS and but still good for Eucaliptus clouds.
+ # Use +modify_security_group_ingress+ method for AWS clouds.
+ #
+ def edit_security_group(action, group_name, params)
+ hash = {}
+ case action
+ when :authorize, :grant then action = "AuthorizeSecurityGroupIngress"
+ when :revoke, :remove then action = "RevokeSecurityGroupIngress"
+ else raise "Unknown action #{action.inspect}!"
+ end
+ hash['GroupName'] = group_name
+ hash['SourceSecurityGroupName'] = params[:source_group] unless params[:source_group].right_blank?
+ hash['SourceSecurityGroupOwnerId'] = params[:source_group_owner].to_s.gsub(/-/,'') unless params[:source_group_owner].right_blank?
+ hash['IpProtocol'] = params[:protocol] unless params[:protocol].right_blank?
+ unless params[:port].right_blank?
+ hash['FromPort'] = params[:port]
+ hash['ToPort'] = params[:port]
+ end
+ hash['FromPort'] = params[:from_port] unless params[:from_port].right_blank?
+ hash['ToPort'] = params[:to_port] unless params[:to_port].right_blank?
+ hash['CidrIp'] = params[:cidr_ip] unless params[:cidr_ip].right_blank?
+ #