From 2ef2a53f4c2b273f2acfa142dfe4f9e8b6e687cf Mon Sep 17 00:00:00 2001 From: Wesley Beary Date: Thu, 2 Jul 2009 12:38:35 -0700 Subject: [PATCH] add functions for ebs volumes --- lib/fog/aws/ec2.rb | 94 ++++++++++++++++- lib/fog/aws/ec2/parsers.rb | 131 +++++++++++++++++++----- lib/fog/aws/simpledb.rb | 4 +- spec/aws/ec2/create_volume_spec.rb | 21 ++++ spec/aws/ec2/delete_volume_spec.rb | 15 +++ spec/aws/ec2/describe_addresses_spec.rb | 2 +- spec/aws/ec2/describe_volumes_spec.rb | 39 +++++++ 7 files changed, 275 insertions(+), 31 deletions(-) create mode 100644 spec/aws/ec2/create_volume_spec.rb create mode 100644 spec/aws/ec2/delete_volume_spec.rb create mode 100644 spec/aws/ec2/describe_volumes_spec.rb diff --git a/lib/fog/aws/ec2.rb b/lib/fog/aws/ec2.rb index 1d742b958..d07b32722 100644 --- a/lib/fog/aws/ec2.rb +++ b/lib/fog/aws/ec2.rb @@ -39,13 +39,65 @@ module Fog # Acquire an elastic IP address. # # ==== Returns - # Hash:: The acquired :public_ip address. + # body<~Hash>:: + # :public_ip<~String>:: The acquired address def allocate_address request({ 'Action' => 'AllocateAddress' }, Fog::Parsers::AWS::EC2::AllocateAddress.new) end + # Create an EBS volume + # + # ==== Parameters + # availability_zone<~String>:: availability zone to create volume in + # size<~Integer>:: Size in GiBs for volume. Must be between 1 and 1024. + # snapshot_id<~String>:: Optional, snapshot to create volume from + # + # ==== Returns + # response:: + # body<~Hash>:: + # :volume_id<~String>:: Reference to volume + # :size<~Integer>:: Size in GiBs for volume + # :status<~String>:: State of volume + # :create_time<~Time>:: Timestamp for creation + # :availability_zone<~String>:: Availability zone for volume + # :snapshot_id<~String>:: Snapshot volume was created from, if any + def create_volume(availability_zone, size, snapshot_id = nil) + request({ + 'Action' => 'CreateVolume', + 'AvailabilityZone' => availability_zone, + 'Size' => size, + 'SnapshotId' => snapshot_id + }, Fog::Parsers::AWS::EC2::CreateVolume.new) + end + + # Delete an EBS volume + # + # ==== Parameters + # volume_id<~String>:: Id of volume to delete. + # + # ==== Returns + # response:: + # body<~Hash>:: + # :return<~Boolean>:: success? + def delete_volume(volume_id) + request({ + 'Action' => 'DeleteVolume', + 'VolumeId' => volume_id + }, Fog::Parsers::AWS::EC2::Basic.new) + end + + # Describe all or specified IP addresses. + # + # ==== Parameters + # public_ips<~Array>:: List of ips to describe, defaults to all + # + # ==== Returns + # body<~Hash>:: + # :items<~Array>:: Addresses + # :instance_id<~String>:: instance for ip address + # :public_ip<~String>:: ip address for instance def describe_addresses(public_ips = []) params, index = {}, 1 for public_ip in [*public_ips] @@ -56,16 +108,50 @@ module Fog 'Action' => 'DescribeAddresses' }.merge!(params), Fog::Parsers::AWS::EC2::DescribeAddresses.new) end + + # Describe all or specified volumes. + # + # ==== Parameters + # volume_ids<~Array>:: List of volumes to describe, defaults to all + # + # ==== Returns + # response:: + # body<~Hash>:: + # volume_set<~Array>:: + # :volume_id<~String>:: Reference to volume + # :size<~Integer>:: Size in GiBs for volume + # :status<~String>:: State of volume + # :create_time<~Time>:: Timestamp for creation + # :availability_zone<~String>:: Availability zone for volume + # :snapshot_id<~String>:: Snapshot volume was created from, if any + # :attachment_set<~Array>:: + # :attachment_time<~Time>:: Timestamp for attachment + # :device<~String>:: How volue is exposed to instance + # :instance_id<~String>:: Reference to attached instance + # :status<~String>:: Attachment state + # :volume_id<~String>:: Reference to volume + def describe_volumes(volume_ids = []) + params, index = {}, 1 + for volume_id in [*volume_ids] + params["VolumeId.#{index}"] = volume_id + index += 1 + end + request({ + 'Action' => 'DescribeVolumes' + }.merge!(params), Fog::Parsers::AWS::EC2::DescribeVolumes.new) + end # Release an elastic IP address. # # ==== Returns - # Hash:: :return success boolean + # response:: + # body<~Hash>:: + # :return<~Boolean>:: success? def release_address(public_ip) request({ 'Action' => 'ReleaseAddress', 'PublicIp' => public_ip - }, Fog::Parsers::AWS::EC2::ReleaseAddress.new) + }, Fog::Parsers::AWS::EC2::Basic.new) end private @@ -82,7 +168,7 @@ module Fog body = '' for key in params.keys.sort unless (value = params[key]).nil? - body << "#{key}=#{CGI.escape(value).gsub(/\+/, '%20')}&" + body << "#{key}=#{CGI.escape(value.to_s).gsub(/\+/, '%20')}&" end end diff --git a/lib/fog/aws/ec2/parsers.rb b/lib/fog/aws/ec2/parsers.rb index 592968c99..da3183aa4 100644 --- a/lib/fog/aws/ec2/parsers.rb +++ b/lib/fog/aws/ec2/parsers.rb @@ -18,30 +18,7 @@ module Fog end - class DescribeAddresses < Fog::Parsers::Base - - def reset - @address = {} - @response = { :addresses => [] } - end - - def end_element(name) - case name - when 'instanceId' - @address[:instance_id] = @value - when 'item' - @response[:addresses] << @address - @address = [] - when 'publicIp' - @address[:public_ip] = @value - when 'requestId' - @response[:request_id] = @value - end - end - - end - - class ReleaseAddress < Fog::Parsers::Base + class Basic < Fog::Parsers::Base def end_element(name) case name @@ -58,6 +35,112 @@ module Fog end + class CreateVolume < Fog::Parsers::Base + + def end_element(name) + case name + when 'availabilityZone' + @response[:availability_zone] = @value + when 'createTime' + @response[:create_time] = Time.parse(@value) + when 'requestId' + @response[:request_id] = @value + when 'size' + @response[:size] = @value.to_i + when 'snapshotId' + @response[:snapshot_id] = @value + when 'status' + @response[:status] = @value + when 'volumeId' + @response[:volume_id] = @value + end + end + + end + + class DescribeAddresses < Fog::Parsers::Base + + def reset + @address = {} + @response = { :address_set => [] } + end + + def end_element(name) + case name + when 'instanceId' + @address[:instance_id] = @value + when 'item' + @response[:address_set] << @address + @address = [] + when 'publicIp' + @address[:public_ip] = @value + when 'requestId' + @response[:request_id] = @value + end + end + + end + + class DescribeVolumes < Fog::Parsers::Base + + def reset + @attachment = {} + @in_attachment_set = false + @response = { :volume_set => [] } + @volume = { :attachment_set => [] } + end + + def end_element(name) + if @in_attachment_set + case name + when 'attachmentSet' + @in_attachment_set = false + when 'attachTime' + @attachment[:attach_time] = Time.parse(@value) + when 'device' + @attachment[:device] = @value + when 'instanceId' + @attachment[:instance_id] = @value + when 'item' + @volume[:attachment_set] << @attachment + @attachment = {} + when 'status' + @attachment[:status] = @value + when 'volumeId' + @attachment[:volume_id] = @value + end + else + case name + when 'availabilityZone' + @volume[:availability_zone] = @value + when 'createTime' + @volume[:create_time] = Time.parse(@value) + when 'item' + @response[:volume_set] << @volume + @volume = { :attachment_set => [] } + when 'requestId' + @response[:request_id] = @value + when 'size' + @volume[:size] = @value.to_i + when 'snapshotId' + @volume[:snapshot_id] = @value + when 'status' + @volume[:status] = @value + when 'volumeId' + @volume[:volume_id] = @value + end + end + end + + def start_element(name, attrs = []) + if name == 'attachmentSet' + @in_attachment_set = true + end + @value = '' + end + + end + end end end diff --git a/lib/fog/aws/simpledb.rb b/lib/fog/aws/simpledb.rb index 43decd357..f41c12608 100644 --- a/lib/fog/aws/simpledb.rb +++ b/lib/fog/aws/simpledb.rb @@ -117,7 +117,7 @@ module Fog # ==== Parameters # domain_name<~String>:: Name of domain. Must be between 3 and 255 of the # following characters: a-z, A-Z, 0-9, '_', '-' and '.'. - # items<~Hash>:: Keys are the items names and may use any UTF-8 + # items<~Hash>:: Keys are the items names and may use any UTF-8 # characters valid in xml. Control characters and sequences not allowed # in xml are not valid. Can be up to 1024 bytes long. Values are the # attributes to add to the given item and may use any UTF-8 characters @@ -305,7 +305,7 @@ module Fog body = '' for key in params.keys.sort unless (value = params[key]).nil? - body << "#{key}=#{CGI.escape(value).gsub(/\+/, '%20')}&" + body << "#{key}=#{CGI.escape(value.to_s).gsub(/\+/, '%20')}&" end end diff --git a/spec/aws/ec2/create_volume_spec.rb b/spec/aws/ec2/create_volume_spec.rb new file mode 100644 index 000000000..d0ad8384a --- /dev/null +++ b/spec/aws/ec2/create_volume_spec.rb @@ -0,0 +1,21 @@ +require File.dirname(__FILE__) + '/../../spec_helper' + +describe 'EC2.create_volume' do + + after(:all) do + ec2.delete_volume(@volume_id) + end + + it "should return proper attributes" do + actual = ec2.create_volume('us-east-1a', 1) + actual.body[:availability_zone].should be_a(String) + actual.body[:create_time].should be_a(Time) + actual.body[:request_id].should be_a(String) + actual.body[:size].should == 1 + actual.body[:snapshot_id].should == '' + actual.body[:status].should be_a(String) + actual.body[:volume_id].should be_a(String) + @volume_id = actual.body[:volume_id] + end + +end diff --git a/spec/aws/ec2/delete_volume_spec.rb b/spec/aws/ec2/delete_volume_spec.rb new file mode 100644 index 000000000..b365fa106 --- /dev/null +++ b/spec/aws/ec2/delete_volume_spec.rb @@ -0,0 +1,15 @@ +require File.dirname(__FILE__) + '/../../spec_helper' + +describe 'EC2.create_volume' do + + before(:all) do + @volume_id = ec2.create_volume('us-east-1a', 1).body[:volume_id] + end + + it "should return proper attributes" do + actual = ec2.delete_volume(@volume_id) + actual.body[:request_id].should be_a(String) + actual.body[:return].should == true + end + +end diff --git a/spec/aws/ec2/describe_addresses_spec.rb b/spec/aws/ec2/describe_addresses_spec.rb index 48ba90328..dfbdfce95 100644 --- a/spec/aws/ec2/describe_addresses_spec.rb +++ b/spec/aws/ec2/describe_addresses_spec.rb @@ -20,7 +20,7 @@ describe 'EC2.describe_addresses' do it "should return proper attributes for specific ip" do actual = ec2.describe_addresses(@public_ip) actual.body[:request_id].should be_a(String) - item = actual.body[:addresses].select {|address| address[:public_ip] == @public_ip} + item = actual.body[:address_set].select {|address| address[:public_ip] == @public_ip} item.should_not be_nil end diff --git a/spec/aws/ec2/describe_volumes_spec.rb b/spec/aws/ec2/describe_volumes_spec.rb new file mode 100644 index 000000000..ad5f4f52d --- /dev/null +++ b/spec/aws/ec2/describe_volumes_spec.rb @@ -0,0 +1,39 @@ +require File.dirname(__FILE__) + '/../../spec_helper' + +describe 'EC2.describe_volumes' do + + before(:all) do + @volume_id = ec2.create_volume('us-east-1a', 1).body[:volume_id] + end + + after(:all) do + ec2.delete_volume(@volume_id) + end + + it "should return proper attributes with no params" do + actual = ec2.describe_volumes + actual.body[:request_id].should be_a(String) + volume = actual.body[:volume_set].select {|volume| volume[:volume_id] == @volume_id}.first + volume[:availability_zone].should be_a(String) + volume[:create_time].should be_a(Time) + volume[:size].should == 1 + volume[:snapshot_id].should == '' + volume[:status].should be_a(String) + volume[:volume_id].should == @volume_id + volume[:attachment_set].should == [] + end + + it "should return proper attributes for specific volume" do + actual = ec2.describe_volumes(@volume_id) + actual.body[:request_id].should be_a(String) + volume = actual.body[:volume_set].select {|volume| volume[:volume_id] == @volume_id}.first + volume[:availability_zone].should be_a(String) + volume[:create_time].should be_a(Time) + volume[:size].should == 1 + volume[:snapshot_id].should == '' + volume[:status].should be_a(String) + volume[:volume_id].should == @volume_id + volume[:attachment_set].should == [] + end + +end