You are viewing a plain text version of this content. The canonical link for it is here.
Posted to dev@deltacloud.apache.org by lu...@redhat.com on 2012/08/21 01:28:08 UTC
[PATCH 1/2] EC2: list subnets as realms
From: David Lutterkort <lu...@redhat.com>
Subnets (from VPC's) are listed as realms.
Note that this patch monkey patches the aws gem; the corresponding patch
has been sent upstream, it is included here to make testing the patch easier.
---
.../deltacloud/drivers/ec2/aws_vpc_monkey_patch.rb | 294 ++++++++++++++++++++
server/lib/deltacloud/drivers/ec2/ec2_driver.rb | 29 ++-
2 files changed, 319 insertions(+), 4 deletions(-)
create mode 100644 server/lib/deltacloud/drivers/ec2/aws_vpc_monkey_patch.rb
diff --git a/server/lib/deltacloud/drivers/ec2/aws_vpc_monkey_patch.rb b/server/lib/deltacloud/drivers/ec2/aws_vpc_monkey_patch.rb
new file mode 100644
index 0000000..3f1d491
--- /dev/null
+++ b/server/lib/deltacloud/drivers/ec2/aws_vpc_monkey_patch.rb
@@ -0,0 +1,294 @@
+# This is a copy of code that has been submitted upstream
+# https://github.com/appoxy/aws/pull/116
+#
+# If you make changes here, make sure they go upstream, too
+
+unless Aws::Ec2::method_defined?(:create_vpc)
+ class Aws::Ec2
+ #-----------------------------------------------------------------
+ # VPC related
+ #-----------------------------------------------------------------
+
+ # Create VPC
+ # http://docs.amazonwebservices.com/AWSEC2/latest/APIReference/ApiReference-query-CreateVpc.html
+ #
+ # ec2.create_vpc("10.0.0.0/16")
+ # FIXME: EVen though the EC2 docs describe the parameter instanceTenancy,
+ # I could not get it to recognize that
+ def create_vpc(cidr_block = "10.0.0.0/16")
+ params = { "CidrBlock" => cidr_block }
+ link = generate_request("CreateVpc", params)
+ request_info(link, QEc2VpcsParser.new("vpc", :logger => @logger))
+ rescue Exception
+ on_exception
+ end
+
+
+ # Describe VPC's
+ # http://docs.amazonwebservices.com/AWSEC2/latest/APIReference/ApiReference-query-DescribeVpcs.html
+ #
+ # ec2.describe_vpcs
+ # ec2.describe_vpcs(vpcId1, vpcId2, 'Filter.1.Name' => 'state', 'Filter.1.Value' = > 'pending', ...)
+ def describe_vpcs(*args)
+ if args.last.is_a?(Hash)
+ params = args.pop.dup
+ else
+ params = {}
+ end
+ 1.upto(args.size) { |i| params["VpcId.#{i}"] = args[i-1] }
+ link = generate_request("DescribeVpcs", params)
+ request_info(link, QEc2VpcsParser.new("item", :logger => @logger))
+ rescue Exception
+ on_exception
+ end
+
+ # Delete VPC
+ # http://docs.amazonwebservices.com/AWSEC2/latest/APIReference/ApiReference-query-DeleteVpc.html
+ #
+ # ec2.delete_vpc(vpc_id)
+ def delete_vpc(vpc_id)
+ params = { "VpcId" => vpc_id }
+ link = generate_request("DeleteVpc", params)
+ request_info(link, RightBoolResponseParser.new(:logger => @logger))
+ rescue Exception
+ on_exception
+ end
+
+ # Create subnet in a VPC
+ # http://docs.amazonwebservices.com/AWSEC2/latest/APIReference/ApiReference-query-CreateSubnet.html
+ #
+ # ec2.create_subnet(vpc_id, cidr_block)
+ # ec2.create_subnet(vpc_id, cidr_block, availability_zone))
+ def create_subnet(vpc_id, cidr_block, availability_zone = nil)
+ params = { "VpcId" => vpc_id, "CidrBlock" => cidr_block }
+ params["AvailabilityZone"] = availability_zone if availability_zone
+ link = generate_request("CreateSubnet", params)
+ request_info(link, QEc2SubnetsParser.new("subnet", :logger => @logger))
+ rescue Exception
+ on_exception
+ end
+
+ # Describe subnets
+ # http://docs.amazonwebservices.com/AWSEC2/latest/APIReference/ApiReference-query-DescribeSubnets.html
+ #
+ # ec2.describe_subnets
+ # ecs.describe_subnets(subnetId1, SubnetId2, ...,
+ # 'Filter.1.Name' => 'state',
+ # 'Filter.1.Value.1' => 'pending',
+ # 'Filter.2.Name' => ...)
+ def describe_subnets(*args)
+ if args.last.is_a?(Hash)
+ params = args.pop.dup
+ else
+ params = {}
+ end
+ 1.upto(args.size) { |i| params["SubnetId.#{i}"] = args[i-1] }
+ link = generate_request("DescribeSubnets", params)
+ request_info(link, QEc2SubnetsParser.new("item", :logger => @logger))
+ rescue Exception
+ on_exception
+ end
+
+ # Delete Subnet
+ # http://docs.amazonwebservices.com/AWSEC2/latest/APIReference/ApiReference-query-DeleteSubnet.html
+ #
+ # ec2.delete_subnet(subnet_id)
+ def delete_subnet(subnet_id)
+ params = { "SubnetId" => subnet_id }
+ link = generate_request("DeleteSubnet", params)
+ request_info(link, RightBoolResponseParser.new(:logger => @logger))
+ rescue Exception
+ on_exception
+ end
+
+ # The only change in this class compared to upstream is
+ # that we parse out subnetId and vpcId
+ class QEc2DescribeInstancesParser < Aws::AwsParser #:nodoc:
+ def tagstart(name, attributes)
+ # DescribeInstances property
+ if (name == 'item' && @xmlpath == 'DescribeInstancesResponse/reservationSet') ||
+ # RunInstances property
+ (name == 'RunInstancesResponse')
+ @reservation = {:aws_groups => [],
+ :instances_set => []}
+
+ elsif (name == 'item') &&
+ # DescribeInstances property
+ (@xmlpath=='DescribeInstancesResponse/reservationSet/item/instancesSet' ||
+ # RunInstances property
+ @xmlpath=='RunInstancesResponse/instancesSet')
+ # the optional params (sometimes are missing and we dont want them to be nil)
+ @instance = {:aws_reason => '',
+ :dns_name => '',
+ :private_dns_name => '',
+ :ami_launch_index => '',
+ :ssh_key_name => '',
+ :aws_state => '',
+ :root_device_type => '',
+ :root_device_name => '',
+ :architecture => '',
+ :subnet_id => '',
+ :vpc_id => '',
+ :block_device_mappings => [],
+ :aws_product_codes => [],
+ :tags => {}}
+ end
+ end
+
+ def tagend(name)
+ case name
+ when 'rootDeviceType' then
+ @instance[:root_device_type] = @text
+ when 'architecture' then
+ @instance[:architecture] = @text
+ when 'rootDeviceName' then
+ @instance[:root_device_name] = @text
+ # reservation
+ when 'reservationId' then
+ @reservation[:aws_reservation_id] = @text
+ when 'ownerId' then
+ @reservation[:aws_owner] = @text
+ when 'groupId' then
+ @reservation[:aws_groups] << @text
+ # instance
+ when 'instanceId' then
+ @instance[:aws_instance_id] = @text
+ when 'imageId' then
+ @instance[:aws_image_id] = @text
+ when 'dnsName' then
+ @instance[:dns_name] = @text
+ when 'privateDnsName' then
+ @instance[:private_dns_name] = @text
+ when 'reason' then
+ @instance[:aws_reason] = @text
+ when 'keyName' then
+ @instance[:ssh_key_name] = @text
+ when 'amiLaunchIndex' then
+ @instance[:ami_launch_index] = @text
+ when 'code' then
+ @instance[:aws_state_code] = @text
+ when 'name' then
+ @instance[:aws_state] = @text
+ when 'productCode' then
+ @instance[:aws_product_codes] << @text
+ when 'instanceType' then
+ @instance[:aws_instance_type] = @text
+ when 'launchTime' then
+ @instance[:aws_launch_time] = @text
+ when 'kernelId' then
+ @instance[:aws_kernel_id] = @text
+ when 'ramdiskId' then
+ @instance[:aws_ramdisk_id] = @text
+ when 'platform' then
+ @instance[:aws_platform] = @text
+ when 'availabilityZone' then
+ @instance[:aws_availability_zone] = @text
+ when 'privateIpAddress' then
+ @instance[:aws_private_ip_address] = @text
+ when 'subnetId' then
+ @instance[:subnet_id] = @text
+ when 'vpcId' then
+ @instance[:vpc_id] = @text
+ when 'key' then
+ @tag_key = @text
+ when 'value' then
+ @tag_value = @text
+ when 'deviceName' then
+ @device_name = @text
+ when 'volumeId' then
+ @volume_id = @text
+ when 'state'
+ if @xmlpath == 'DescribeInstancesResponse/reservationSet/item/instancesSet/item/monitoring' || # DescribeInstances property
+ @xmlpath == 'RunInstancesResponse/instancesSet/item/monitoring' # RunInstances property
+ @instance[:monitoring_state] = @text
+ end
+ when 'item'
+ if @xmlpath=='DescribeInstancesResponse/reservationSet/item/instancesSet/item/tagSet' # Tags
+ @instance[:tags][@tag_key] = @tag_value
+ elsif @xmlpath == 'DescribeInstancesResponse/reservationSet/item/instancesSet/item/blockDeviceMapping' # Block device mappings
+ @instance[:block_device_mappings] << { @device_name => @volume_id }
+ elsif @xmlpath == 'DescribeInstancesResponse/reservationSet/item/instancesSet' || # DescribeInstances property
+ @xmlpath == 'RunInstancesResponse/instancesSet' # RunInstances property
+ @reservation[:instances_set] << @instance
+ elsif @xmlpath=='DescribeInstancesResponse/reservationSet' # DescribeInstances property
+ @result << @reservation
+ end
+ when 'RunInstancesResponse' then
+ @result << @reservation # RunInstances property
+ end
+ end
+
+ def reset
+ @result = []
+ end
+ end
+
+ #-----------------------------------------------------------------
+ # PARSERS: Vpc
+ #-----------------------------------------------------------------
+
+ class QEc2VpcsParser < Aws::AwsParser #:nodoc:
+ def initialize(wrapper, opts = {})
+ super(opts)
+ @wrapper = wrapper
+ end
+
+ def tagstart(name, attribute)
+ @vpc = {} if name == @wrapper
+ end
+
+ def tagend(name)
+ case name
+ when 'vpcId' then
+ @vpc[:vpc_id] = @text
+ when 'state' then
+ @vpc[:state] = @text
+ when 'cidrBlock' then
+ @vpc[:cidr_block] = @text
+ when 'dhcpOptionsId' then
+ @vpc[:dhcp_options_id] = @text
+ when @wrapper
+ @result << @vpc
+ end
+ end
+
+ def reset
+ @result = []
+ end
+ end
+
+ class QEc2SubnetsParser < Aws::AwsParser #:nodoc
+ def initialize(wrapper, opts = {})
+ super(opts)
+ @wrapper = wrapper
+ end
+
+ def tagstart(name, attribute)
+ @subnet = {} if name == @wrapper
+ end
+
+ def tagend(name)
+ case name
+ when 'subnetId' then
+ @subnet[:subnet_id] = @text
+ when 'state' then
+ @subnet[:state] = @text
+ when 'vpcId' then
+ @subnet[:vpc_id] = @text
+ when 'cidrBlock' then
+ @subnet[:cidr_block] = @text
+ when 'availableIpAddressCount' then
+ @subnet[:available_ip_address_count] = @text
+ when 'availabilityZone' then
+ @subnet[:availability_zone] = @text
+ when @wrapper
+ @result << @subnet
+ end
+ end
+
+ def reset
+ @result = []
+ end
+ end
+ end
+end
diff --git a/server/lib/deltacloud/drivers/ec2/ec2_driver.rb b/server/lib/deltacloud/drivers/ec2/ec2_driver.rb
index 8847034..31fcfa5 100644
--- a/server/lib/deltacloud/drivers/ec2/ec2_driver.rb
+++ b/server/lib/deltacloud/drivers/ec2/ec2_driver.rb
@@ -15,6 +15,8 @@
#
require 'aws'
+# Delete this once VPC support is merged upstream
+require_relative 'aws_vpc_monkey_patch'
require_relative '../../runner'
@@ -164,22 +166,35 @@ module Deltacloud
end
def realms(credentials, opts={})
+ # We have two different kinds of realms:
+ # (1) Availability Zones
+ # (2) Subnets in VPC's (scoped to an AZ)
+ # For the latter, the ID is AZ:SUBNET, and we can tell that we
+ # are looking at such a realm by checking if the id contains a colon
ec2 = new_client(credentials)
realms = []
safely do
if opts[:id] and !opts[:id].empty?
+ az, sn = opts[:id].split(":")
begin
- ec2.describe_availability_zones([opts[:id]]).collect do |realm|
- realms << convert_realm(realm) unless realm.empty?
+ if sn
+ subnet = ec2.describe_subnets(sn).first
+ realms << convert_realm(subnet) if subnet
+ else
+ ec2.describe_availability_zones(az).collect do |realm|
+ realms << convert_realm(realm) unless realm.empty?
+ end
end
rescue => e
raise e unless e.message =~ /Invalid availability zone/
realms = []
end
else
- ec2.describe_availability_zones.collect do |realm|
- realms << convert_realm(realm) unless realm.empty?
+ realms = ec2.describe_availability_zones.collect do |realm|
+ convert_realm(realm) unless realm.empty?
end
+ realms = realms +
+ ec2.describe_subnets.map { |sn| convert_realm(sn) }
end
end
realms
@@ -871,6 +886,12 @@ module Deltacloud
end
def convert_realm(realm)
+ # We also allow subnets as realms
+ if realm[:subnet_id]
+ realm[:zone_name] =
+ "#{realm[:availability_zone]}:#{realm[:subnet_id]}"
+ realm[:zone_state] = realm[:state]
+ end
Realm.new(
:id => realm[:zone_name],
:name => realm[:zone_name],
--
1.7.7.6