diff --git a/lib/fog/rackspace/compute_v2.rb b/lib/fog/rackspace/compute_v2.rb index 55a9757a1..25812b072 100644 --- a/lib/fog/rackspace/compute_v2.rb +++ b/lib/fog/rackspace/compute_v2.rb @@ -44,19 +44,28 @@ module Fog recognizes :rackspace_compute_url model_path 'fog/rackspace/models/compute_v2' + model :server collection :servers + model :flavor collection :flavors + model :image collection :images + model :attachment collection :attachments + model :network collection :networks + model :key_pair collection :key_pairs + model :virtual_interface + collection :virtual_interfaces + request_path 'fog/rackspace/requests/compute_v2' request :list_servers request :get_server @@ -106,6 +115,10 @@ module Fog request :delete_keypair request :get_keypair + request :list_virtual_interfaces + request :create_virtual_interface + request :delete_virtual_interface + class Mock < Fog::Rackspace::Service include Fog::Rackspace::MockData @@ -174,7 +187,7 @@ module Fog end def request_id_header - "X-Compute-Request-Id" + "x-compute-request-id" end def region diff --git a/lib/fog/rackspace/models/compute_v2/server.rb b/lib/fog/rackspace/models/compute_v2/server.rb index e339007ba..6e1787e54 100644 --- a/lib/fog/rackspace/models/compute_v2/server.rb +++ b/lib/fog/rackspace/models/compute_v2/server.rb @@ -579,6 +579,10 @@ module Fog retry end + def virtual_interfaces + @virtual_interfaces ||= Fog::Compute::RackspaceV2::VirtualInterfaces.new :server => self, :service => service + end + private def adminPass=(new_admin_pass) diff --git a/lib/fog/rackspace/models/compute_v2/virtual_interface.rb b/lib/fog/rackspace/models/compute_v2/virtual_interface.rb new file mode 100644 index 000000000..f4bffb308 --- /dev/null +++ b/lib/fog/rackspace/models/compute_v2/virtual_interface.rb @@ -0,0 +1,87 @@ +require 'fog/core/collection' +require 'fog/rackspace/models/compute_v2/virtual_interface' + +module Fog + module Compute + class RackspaceV2 + class VirtualInterface < Fog::Model + + # @!attribute [r] id + # @return [String] The virtual interface id + identity :id + + # @!attribute [r] mac_address + # @return [String] The Media Access Control (MAC) address for the virtual interface. + # A MAC address is a unique identifier assigned to network interfaces for communications on the physical network segment. + attribute :mac_address + + # @!attribute [r] ip_addresses + # @return [Array] returns an array of hashes containing information about allocated ip addresses and their associated networks + # @example + # [ + # { + # "address": "2001:4800:7810:0512:d87b:9cbc:ff04:850c", + # "network_id": "ba122b32-dbcc-4c21-836e-b701996baeb3", + # "network_label": "public" + # }, + # { + # "address": "64.49.226.149", + # "network_id": "ba122b32-dbcc-4c21-836e-b701996baeb3", + # "network_label": "public" + # } + # ] + attribute :ip_addresses + + # Saves the virtual interface. + # This method can only create a virtual interface. Attempting to update interface will result an exception + # @return [Boolean] true if virtual interface has been saved + def save(attributes = {}) + if persisted? + raise Fog::Errors::Error.new("This virtual interface has already been created and it cannot be updated") + else + create + end + true + end + + # Creates Virtual interface for server + # * requires attributes: :network + # @return [Boolean] returns true if virtual network interface is being created + # @raise [Fog::Compute::RackspaceV2::NotFound] - HTTP 404 + # @raise [Fog::Compute::RackspaceV2::BadRequest] - HTTP 400 + # @raise [Fog::Compute::RackspaceV2::InternalServerError] - HTTP 500 + # @raise [Fog::Compute::RackspaceV2::ServiceError] + # @example To create a virtual interface; + # my_server.virtual_interfaces.create :network => my_network + # @see http://docs.rackspace.com/servers/api/v2/cn-devguide/content/api_create_virtual_interface.html + def create + data = service.create_virtual_interface(server_id, network_id) + merge_attributes(data.body['virtual_interfaces'].first) + end + + # Destroy the virtual interface + # @return [Boolean] returns true if virtual interface has been destroyed + # @raise [Fog::Compute::RackspaceV2::NotFound] - HTTP 404 + # @raise [Fog::Compute::RackspaceV2::BadRequest] - HTTP 400 + # @raise [Fog::Compute::RackspaceV2::InternalServerError] - HTTP 500 + # @raise [Fog::Compute::RackspaceV2::ServiceError] + # @see http://docs.rackspace.com/servers/api/v2/cn-devguide/content/delete_virt_interface_api.html + def destroy + service.delete_virtual_interface(server_id, id) + true + end + + private + + def server_id + attributes[:server].is_a?(Server) ? attributes[:server].id : attributes[:server] + end + + def network_id + attributes[:network].is_a?(Network) ? attributes[:network].id : attributes[:network] + end + + end + end + end +end diff --git a/lib/fog/rackspace/models/compute_v2/virtual_interfaces.rb b/lib/fog/rackspace/models/compute_v2/virtual_interfaces.rb new file mode 100644 index 000000000..9109437f5 --- /dev/null +++ b/lib/fog/rackspace/models/compute_v2/virtual_interfaces.rb @@ -0,0 +1,46 @@ +require 'fog/core/collection' +require 'fog/rackspace/models/compute_v2/virtual_interface' +require 'fog/rackspace/models/compute_v2/server' + +module Fog + module Compute + class RackspaceV2 + class VirtualInterfaces < Fog::Collection + model Fog::Compute::RackspaceV2::VirtualInterface + + attr_reader :server + + def server=(obj) + if obj.is_a?(Server) + @server = obj + else + @server = Fog::Compute::RackspaceV2::Server.new :id => obj, :service => service + end + end + + # Returns list of virtual interfaces for a server + # @return [Fog::Compute::RackspaceV2::Servers] Retrieves a list virtual interfaces for server. + # @raise [Fog::Compute::RackspaceV2::NotFound] - HTTP 404 + # @raise [Fog::Compute::RackspaceV2::BadRequest] - HTTP 400 + # @raise [Fog::Compute::RackspaceV2::InternalServerError] - HTTP 500 + # @raise [Fog::Compute::RackspaceV2::ServiceError] + # @note Fog's current implementation only returns 1000 servers + # @note The filter parameter on the method is just to maintain compatability with other providers that support filtering. + # @see http://docs.rackspace.com/servers/api/v2/cs-devguide/content/List_Servers-d1e2078.html + # @see Fog::Compute::RackspaceV2::Server#virtual_interfaces + def all + raise "Please access this collection via Server#virtual_interfaces" unless self.server + + data = service.list_virtual_interfaces(server.id).body['virtual_interfaces'] + objects = load(data) + objects.each {|obj| obj.attributes[:server] = self.server} + objects + end + + def new(attributes = {}) + super({ :server => server }.merge(attributes)) + end + end + end + end +end diff --git a/lib/fog/rackspace/requests/compute_v2/create_virtual_interface.rb b/lib/fog/rackspace/requests/compute_v2/create_virtual_interface.rb new file mode 100644 index 000000000..a2d2f7fb4 --- /dev/null +++ b/lib/fog/rackspace/requests/compute_v2/create_virtual_interface.rb @@ -0,0 +1,32 @@ +module Fog + module Compute + class RackspaceV2 + class Real + + # Creates virtual interface for a server + # @param [String] server_id The server id to create the virtual interface on + # @param [String] network_id The network id to attach the virtual interface to + # @raise [Fog::Compute::RackspaceV2::NotFound] - HTTP 404 + # @raise [Fog::Compute::RackspaceV2::BadRequest] - HTTP 400 + # @raise [Fog::Compute::RackspaceV2::InternalServerError] - HTTP 500 + # @raise [Fog::Compute::RackspaceV2::ServiceError] + # @see http://docs.rackspace.com/servers/api/v2/cn-devguide/content/api_create_virtual_interface.html + def create_virtual_interface(server_id, network_id) + data = { + :virtual_interface => { + :network_id => network_id + } + } + + request( + :expects => [200], + :method => 'POST', + :path => "/servers/#{server_id}/os-virtual-interfacesv2", + :body => Fog::JSON.encode(data) + ) + end + end + + end + end +end diff --git a/lib/fog/rackspace/requests/compute_v2/delete_virtual_interface.rb b/lib/fog/rackspace/requests/compute_v2/delete_virtual_interface.rb new file mode 100644 index 000000000..58877016c --- /dev/null +++ b/lib/fog/rackspace/requests/compute_v2/delete_virtual_interface.rb @@ -0,0 +1,25 @@ +module Fog + module Compute + class RackspaceV2 + class Real + + # Deletes virtual interface from server + # @param [String] server_id The server id that contains the virtual interface + # @param [String] interface_id The id of the virtual interface + # @raise [Fog::Compute::RackspaceV2::NotFound] - HTTP 404 + # @raise [Fog::Compute::RackspaceV2::BadRequest] - HTTP 400 + # @raise [Fog::Compute::RackspaceV2::InternalServerError] - HTTP 500 + # @raise [Fog::Compute::RackspaceV2::ServiceError] + # @see http://docs.rackspace.com/servers/api/v2/cn-devguide/content/delete_virt_interface_api.html + def delete_virtual_interface(server_id, interface_id) + request( + :expects => [200], + :method => 'DELETE', + :path => "/servers/#{server_id}/os-virtual-interfacesv2/#{interface_id}" + ) + end + end + + end + end +end diff --git a/lib/fog/rackspace/requests/compute_v2/list_virtual_interfaces.rb b/lib/fog/rackspace/requests/compute_v2/list_virtual_interfaces.rb new file mode 100644 index 000000000..11b57be64 --- /dev/null +++ b/lib/fog/rackspace/requests/compute_v2/list_virtual_interfaces.rb @@ -0,0 +1,24 @@ +module Fog + module Compute + class RackspaceV2 + class Real + + # Lists virtual interfaces for a server + # @param [String] server_id + # @raise [Fog::Compute::RackspaceV2::NotFound] - HTTP 404 + # @raise [Fog::Compute::RackspaceV2::BadRequest] - HTTP 400 + # @raise [Fog::Compute::RackspaceV2::InternalServerError] - HTTP 500 + # @raise [Fog::Compute::RackspaceV2::ServiceError] + # @see http://docs.rackspace.com/servers/api/v2/cn-devguide/content/list_virt_interfaces.html + def list_virtual_interfaces(server_id) + request( + :expects => [200], + :method => 'GET', + :path => "/servers/#{server_id}/os-virtual-interfacesv2" + ) + end + end + + end + end +end diff --git a/tests/rackspace/models/compute_v2/virtual_interface_tests.rb b/tests/rackspace/models/compute_v2/virtual_interface_tests.rb new file mode 100644 index 000000000..be439fff7 --- /dev/null +++ b/tests/rackspace/models/compute_v2/virtual_interface_tests.rb @@ -0,0 +1,33 @@ +Shindo.tests('Fog::Compute::RackspaceV2 | virtual_interface', ['rackspace']) do + service = Fog::Compute::RackspaceV2.new + + net_options = { + :label => "fog_network_#{Time.now.to_i.to_s}", + :cidr => '192.168.0.0/24' + } + + server_options = { + :name => "fog_server_#{Time.now.to_i.to_s}", + :flavor_id => rackspace_test_flavor_id(service), + :image_id => rackspace_test_image_id(service) + } + + tests('virtual_interface') do + pending if Fog.mocking? + begin + @server = service.servers.create server_options + @network = service.networks.create net_options + @server.wait_for { ready? } + + model_tests(@server.virtual_interfaces, {:network => @network}, false) + + ensure + if @server + @server.destroy + # wait_for_server_deletion(@server) if @server + delete_test_network(@network) if @network + end + end + end + +end diff --git a/tests/rackspace/models/compute_v2/virtual_interfaces_tests.rb b/tests/rackspace/models/compute_v2/virtual_interfaces_tests.rb new file mode 100644 index 000000000..9cdb1de32 --- /dev/null +++ b/tests/rackspace/models/compute_v2/virtual_interfaces_tests.rb @@ -0,0 +1,24 @@ +Shindo.tests('Fog::Compute::RackspaceV2 | virtual_interfaces', ['rackspace']) do + service = Fog::Compute::RackspaceV2.new + + options = { + :name => "fog_server_#{Time.now.to_i.to_s}", + :flavor_id => rackspace_test_flavor_id(service), + :image_id => rackspace_test_image_id(service) + } + + tests('virtual_interfaces') do + pending if Fog.mocking? + begin + @server = service.servers.create options + @server.wait_for { ready? } + + tests('#virtual_interfaces').succeeds do + @server.virtual_interfaces.all + end + + ensure + @server.destroy if @server + end + end +end diff --git a/tests/rackspace/requests/compute_v2/virtual_interface_tests.rb b/tests/rackspace/requests/compute_v2/virtual_interface_tests.rb new file mode 100644 index 000000000..2f079ef62 --- /dev/null +++ b/tests/rackspace/requests/compute_v2/virtual_interface_tests.rb @@ -0,0 +1,49 @@ +Shindo.tests('Fog::Compute::RackspaceV2 | virtual_interface_tests', ['rackspace']) do + @service = Fog::Compute.new(:provider => 'Rackspace', :version => 'V2') + + virtual_interface_format = { + "virtual_interfaces"=> [{ + "ip_addresses"=> [{ + "network_id"=> String, + "network_label"=> String, + "address"=> String + }], + "id"=> String, + "mac_address"=> String + }] + } + + begin + unless Fog.mocking? + network_id = nil + + @server = @service.servers.create(:name => "fog_virtual_interface_test_#{Time.now.to_i.to_s}", + :flavor_id => rackspace_test_flavor_id(@service), + :image_id => rackspace_test_image_id(@service)) + @server.wait_for { ready? } + + @network = @service.networks.create(:label => "fog_#{Time.now.to_i.to_s}", :cidr => '192.168.0.0/24') + end + + tests('success') do + pending if Fog.mocking? + + tests('#create_virtual_interface').formats(virtual_interface_format) do + response = @service.create_virtual_interface @server.id, @network.id + body = response.body + @virtual_network_interface_id = body["virtual_interfaces"].first["id"] + body + end + tests('#list_virtual_interfaces').formats(virtual_interface_format) do + @service.list_virtual_interfaces(@server.id).body + end + + tests('#delete_virtual_interfaces').succeeds do + @service.delete_virtual_interface(@server.id, @virtual_network_interface_id) + end + end + ensure + @server.destroy if @server + delete_test_network @network + end +end