Rackspace neutron (networking) support

This commit is contained in:
Matt Darby 2014-10-23 16:21:33 -04:00
parent 92b5d6d741
commit 19416c9342
23 changed files with 1157 additions and 0 deletions

View File

@ -26,6 +26,8 @@ class Rackspace < Fog::Bin
Fog::Rackspace::Monitoring
when :queues
Fog::Rackspace::Queues
when :networking
Fog::Rackspace::Networking
else
raise ArgumentError, "Unrecognized service: #{key}"
end
@ -63,6 +65,8 @@ class Rackspace < Fog::Bin
Fog::Rackspace::Monitoring.new
when :queues
Fog::Rackspace::Queues.new
when :networking
Fog::Rackspace::Networking.new
else
raise ArgumentError, "Unrecognized service: #{key.inspect}"
end

View File

@ -10,3 +10,4 @@ require 'fog/rackspace/load_balancers'
require 'fog/rackspace/monitoring'
require 'fog/rackspace/queues'
require 'fog/rackspace/storage'
require 'fog/rackspace/networking'

View File

@ -97,6 +97,7 @@ module Fog
service(:databases, 'Databases')
service(:monitoring, 'Monitoring')
service(:queues, 'Queues')
service(:networking, 'Networking')
def self.authenticate(options, connection_options = {})
rackspace_auth_url = options[:rackspace_auth_url]

View File

@ -0,0 +1,315 @@
#Networking (neutron)
This document explains how to get started using Networking with Fog. It assumes you have read the [Getting Started with Fog and the Rackspace Open Cloud](getting_started.md) document.
## Starting irb console
Start by executing the following command:
irb
Once `irb` has launched you need to require the Fog library.
If using Ruby 1.8.x execute:
require 'rubygems'
require 'fog'
If using Ruby 1.9.x execute:
require 'fog'
## Create Service
Next, create a connection to Rackspace's Networking API:
Using a US-based account:
service = Fog::Rackspace::Networking.new({
:rackspace_username => RACKSPACE_USER_NAME, # Your Rackspace Username
:rackspace_api_key => RACKSPACE_API, # Your Rackspace API key
:rackspace_region => :ord, # Defaults to :dfw
})
Using a UK-based account:
service = Fog::Compute.new({
:rackspace_username => RACKSPACE_USER_NAME, # Your Rackspace Username
:rackspace_api_key => RACKSPACE_API, # Your Rackspace API key
:rackspace_auth_url => Fog::Rackspace::UK_AUTH_ENDPOINT,
:rackspace_region => :lon,
})
To learn more about obtaining cloud credentials refer to the [Getting Started with Fog and the Rackspace Open Cloud](getting_started.md) document.
By default `Fog::Rackspace::Networking` will authenticate against the US authentication endpoint and connect to the DFW region. You can specify alternative authentication endpoints using the key `:rackspace_auth_url`. Please refer to [Alternate Authentication Endpoints](http://docs.rackspace.com/auth/api/v2.0/auth-client-devguide/content/Endpoints-d1e180.html) for a list of alternative Rackspace authentication endpoints.
Alternative regions are specified using the key `:rackspace_region `. A list of regions available for Cloud Servers can be found by executing the following:
identity_service = Fog::Identity({
:provider => 'Rackspace', # Rackspace Fog provider
:rackspace_username => RACKSPACE_USER_NAME, # Your Rackspace Username
:rackspace_api_key => RACKSPACE_API, # Your Rackspace API key
:rackspace_auth_url => Fog::Rackspace::UK_AUTH_ENDPOINT # Not specified for US Cloud
})
identity_service.service_catalog.display_service_regions :cloudServersOpenStack
### Optional Connection Parameters
Fog supports passing additional connection parameters to its underlying HTTP library (Excon) using the `:connection_options` parameter.
<table>
<tr>
<th>Key</th>
<th>Description</th>
</tr>
<tr>
<td>:connect_timeout</td>
<td>Connection timeout (default: 60 seconds)</td>
</tr>
<tr>
<td>:read_timeout</td>
<td>Read timeout for connection (default: 60 seconds)</td> </tr>
<tr>
<td>:write_timeout</td>
<td>Write timeout for connection (default: 60 seconds)</td>
</tr>
<tr>
<td>:proxy</td>
<td>Proxy for HTTP and HTTPS connections</td>
</tr>
<tr>
<td>:ssl_ca_path</td>
<td>Path to SSL certificate authorities</td>
</tr>
<tr>
<td>:ssl_ca_file</td>
<td>SSL certificate authority file</td>
</tr>
<tr>
<td>:ssl_verify_peer</td>
<td>SSL verify peer (default: true)</td>
</tr>
</table>
## Fog Abstractions
Fog provides both a **model** and **request** abstraction. The request abstraction provides the most efficient interface and the model abstraction wraps the request abstraction to provide a convenient `ActiveModel` like interface.
### Request Layer
The request abstraction maps directly to the [Networking API](http://docs.rackspace.com/networks/api/v2/cn-gettingstarted/content/ch_overview.html). It provides the most efficient interface to the Rackspace Networking
To see a list of requests supported by the service:
service.requests
This returns:
[:list_networks, :get_network, :create_network, :delete_network, :list_virtual_interfaces, :create_virtual_interface, :delete_virtual_interface]
#### Example Request
To request a list of flavors:
response = service.list_networks
This returns in the following `Excon::Response`:
<Excon::Response:0x0000010231acd8 @data={:body=>{"networks"=>[{"cidr"=>"192.168.0.0/24", "id"=>"08df79ae-b714-425c-ba25-91b0a8a78b9e", "label"=>"something"}, {"cidr"=>"192.168.0.0/24", "id"=>"eb3ed4b8-21d4-478e-9ae4-a35c0cc0437c", "label"=>"something"}, {"id"=>"00000000-0000-0000-0000-000000000000", "label"=>"public"}, {"id"=>"11111111-1111-1111-1111-111111111111", "label"=>"private"}]}, :headers=>{"Content-Type"=>"application/json", "Via"=>"1.1 Repose (Repose/2.12)", "Content-Length"=>"341", "Date"=>"Thu, 23 Oct 2014 20:53:41 GMT", "x-compute-request-id"=>"req-d34ab53c-45ed-433f-8a9d-b3341896b7e5", "Server"=>"Jetty(8.0.y.z-SNAPSHOT)"}, :status=>200, :remote_ip=>"162.209.116.128", :local_port=>60153, :local_address=>"192.168.1.65"}, @body="{\"networks\": [{\"cidr\": \"192.168.0.0/24\", \"id\": \"08df79ae-b714-425c-ba25-91b0a8a78b9e\", \"label\": \"something\"}, {\"cidr\": \"192.168.0.0/24\", \"id\": \"eb3ed4b8-21d4-478e-9ae4-a35c0cc0437c\", \"label\": \"something\"}, {\"id\": \"00000000-0000-0000-0000-000000000000\", \"label\": \"public\"}, {\"id\": \"11111111-1111-1111-1111-111111111111\", \"label\": \"private\"}]}", @headers={"Content-Type"=>"application/json", "Via"=>"1.1 Repose (Repose/2.12)", "Content-Length"=>"341", "Date"=>"Thu, 23 Oct 2014 20:53:41 GMT", "x-compute-request-id"=>"req-d34ab53c-45ed-433f-8a9d-b3341896b7e5", "Server"=>"Jetty(8.0.y.z-SNAPSHOT)"}, @status=200, @remote_ip="162.209.116.128", @local_port=60153, @local_address="192.168.1.65">
To view the status of the response:
response.status
**Note**: Fog is aware of valid HTTP response statuses for each request type. If an unexpected HTTP response status occurs, Fog will raise an exception.
To view response body:
response.body
This will return:
{"networks"=>[{"cidr"=>"192.168.0.0/24", "id"=>"08df79ae-b714-425c-ba25-91b0a8a78b9e", "label"=>"something"}, {"cidr"=>"192.168.0.0/24", "id"=>"eb3ed4b8-21d4-478e-9ae4-a35c0cc0437c", "label"=>"something"}, {"id"=>"00000000-0000-0000-0000-000000000000", "label"=>"public"}, {"id"=>"11111111-1111-1111-1111-111111111111", "label"=>"private"}]}
To learn more about Networking request methods refer to [rdoc](http://www.rubydoc.info/gems/fog/Fog/Rackspace/Networking/Real). To learn more about Excon refer to [Excon GitHub repo](https://github.com/geemus/excon).
### Model Layer
Fog models behave in a manner similar to `ActiveModel`. Models will generally respond to `create`, `save`, `persisted?`, `destroy`, `reload` and `attributes` methods. Additionally, fog will automatically create attribute accessors.
Here is a summary of common model methods:
<table>
<tr>
<th>Method</th>
<th>Description</th>
</tr>
<tr>
<td>create</td>
<td>
Accepts hash of attributes and creates object.<br>
Note: creation is a non-blocking call and you will be required to wait for a valid state before using resulting object.
</td>
</tr>
<tr>
<td>save</td>
<td>Saves object.<br>
Note: not all objects support updating object.</td>
</tr>
<tr>
<td>persisted?</td>
<td>Returns true if the object has been persisted.</td>
</tr>
<tr>
<td>destroy</td>
<td>
Destroys object.<br>
Note: this is a non-blocking call and object deletion might not be instantaneous.
</td>
<tr>
<td>reload</td>
<td>Updates object with latest state from service.</td>
<tr>
<td>ready?</td>
<td>Returns true if object is in a ready state and able to perform actions. This method will raise an exception if object is in an error state.</td>
</tr>
<tr>
<td>attributes</td>
<td>Returns a hash containing the list of model attributes and values.</td>
</tr>
<td>identity</td>
<td>
Returns the identity of the object.<br>
Note: This might not always be equal to object.id.
</td>
</tr>
<tr>
<td>wait_for</td>
<td>This method periodically reloads model and then yields to specified block until block returns true or a timeout occurs.</td>
</tr>
</table>
The remainder of this document details the model abstraction.
## List Networks
To retrieve a list of available networks:
service.networks
This returns a collection of `Fog::Rackspace::Networking::Network` models:
<Fog::Rackspace::Networking::Networks
[
<Fog::Rackspace::Networking::Network
id="08df79ae-b714-425c-ba25-91b0a8a78b9e",
label="something",
cidr="192.168.0.0/24"
>,
<Fog::Rackspace::Networking::Network
id="eb3ed4b8-21d4-478e-9ae4-a35c0cc0437c",
label="something",
cidr="192.168.0.0/24"
>,
<Fog::Rackspace::Networking::Network
id="00000000-0000-0000-0000-000000000000",
label="public",
cidr=nil
>,
<Fog::Rackspace::Networking::Network
id="11111111-1111-1111-1111-111111111111",
label="private",
cidr=nil
>
]
>
## Create Network
Create a network:
service.networks.create(label: "new_network", cidr: "192.168.0.0/24")
## Get Network
To retrieve individual network:
service.networks.get "8a3a9f96-b997-46fd-b7a8-a9e740796ffd"
This returns an `Fog::Rackspace::Networking::Network` instance:
<Fog::Rackspace::Networking::Network
id="08df79ae-b714-425c-ba25-91b0a8a78b9e",
label="new_network",
cidr="192.168.0.0/24"
>
## Delete Network
To delete a network:
network.destroy
**Note**: The network is not immediately destroyed, but it does occur shortly there after.
## List Virtual Interfaces
To retrieve a list of available virtual interfaces:
service.virtual_interfaces.all(server: <server obj, or server id>)
This returns a collection of `Fog::Rackspace::Networking::VirtualInterface` models:
<Fog::Rackspace::Networking::VirtualInterfaces
[
<Fog::Rackspace::Networking::VirtualInterface
id="f063815f-e576-450e-92bd-ff3aeeeb11e0",
mac_address="BC:76:4E:20:A9:16",
ip_addresses=[{"network_id"=>"11111111-1111-1111-1111-111111111111", "network_label"=>"private", "address"=>"10.176.12.249"}]
>,
<Fog::Rackspace::Networking::VirtualInterface
id="f8196e20-788b-4447-80a5-32ca8fc9622f",
mac_address="BC:76:4E:20:A8:56",
ip_addresses=[{"network_id"=>"08df79ae-b714-425c-ba25-91b0a8a78b9e", "network_label"=>"new_network", "address"=>"192.168.0.1"}]
>
]
>
## Create Virtual Interface
Create a virtual interface:
service.virtual_interfaces.create(network: <network_id>, server: <server_id>)
## Delete Virtual Interface
To delete a virtual interface:
vis = service.virtual_interfaces.all(server: <server obj, or server id>)
vis.first.destroy
**Note**: The virtual interface is not immediately destroyed, but it does occur shortly there after.
## Examples
Example code using Networking can be found [here](https://github.com/fog/fog/tree/master/lib/fog/rackspace/examples).
## Additional Resources
* [fog.io](http://fog.io/)
* [Fog rdoc](http://rubydoc.info/gems/fog/)
* [Fog Github repo](https://github.com/fog/fog)
* [Fog Github Issues](https://github.com/fog/fog/issues)
* [Excon Github repo](https://github.com/geemus/excon)
* [Rackspace Networking API](http://docs.rackspace.com/networking/api/v2/cs-devguide/content/ch_preface.html)
## Support and Feedback
Your feedback is appreciated! If you have specific issues with the **fog** SDK, you should file an [issue via Github](https://github.com/fog/fog/issues).
For general feedback and support requests, send an email to: <sdk-support@rackspace.com>.

View File

@ -0,0 +1,27 @@
module Fog
module Rackspace
class Networking
class Network < Fog::Model
identity :id
attribute :label
attribute :cidr
def save
requires :label, :cidr
data = service.create_network(label, cidr)
merge_attributes(data.body['network'])
true
end
def destroy
requires :identity
service.delete_network(identity)
true
end
end
end
end
end

View File

@ -0,0 +1,23 @@
require 'fog/rackspace/models/compute_v2/network'
module Fog
module Rackspace
class Networking
class Networks < Fog::Collection
model Fog::Rackspace::Networking::Network
def all
data = service.list_networks.body['networks']
load(data)
end
def get(id)
data = service.get_network(id).body['network']
new(data)
rescue Fog::Rackspace::Networking::NotFound
nil
end
end
end
end
end

View File

@ -0,0 +1,85 @@
require 'fog/core/collection'
require 'fog/rackspace/models/networking/virtual_interface'
module Fog
module Rackspace
class Networking
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<Hash>] 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::Rackspace::Networking::NotFound] - HTTP 404
# @raise [Fog::Rackspace::Networking::BadRequest] - HTTP 400
# @raise [Fog::Rackspace::Networking::InternalServerError] - HTTP 500
# @raise [Fog::Rackspace::Networking::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::Rackspace::Networking::NotFound] - HTTP 404
# @raise [Fog::Rackspace::Networking::BadRequest] - HTTP 400
# @raise [Fog::Rackspace::Networking::InternalServerError] - HTTP 500
# @raise [Fog::Rackspace::Networking::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?(Fog::Compute::RackspaceV2::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

View File

@ -0,0 +1,45 @@
require 'fog/core/collection'
require 'fog/rackspace/models/networking/virtual_interface'
require 'fog/rackspace/models/compute_v2/server'
module Fog
module Rackspace
class Networking
class VirtualInterfaces < Fog::Collection
model Fog::Rackspace::Networking::VirtualInterface
# Returns list of virtual interfaces for a server
# @return [Fog::Rackspace::Networking::Servers] Retrieves a list virtual interfaces for server.
# @raise [Fog::Rackspace::Networking::NotFound] - HTTP 404
# @raise [Fog::Rackspace::Networking::BadRequest] - HTTP 400
# @raise [Fog::Rackspace::Networking::InternalServerError] - HTTP 500
# @raise [Fog::Rackspace::Networking::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::Rackspace::Networking::Server#virtual_interfaces
def all(options={})
server = server(options[:server])
data = service.list_virtual_interfaces(server.id).body['virtual_interfaces']
objects = load(data)
objects.each{ |obj| obj.attributes[:server] = server }
objects
end
private
def server(obj)
server = case obj.class.to_s
when "Fog::Compute::RackspaceV2::Server" then obj
when "String" then Fog::Compute::RackspaceV2::Server.new(:id => obj, :service => service)
else raise "Please pass in :server (either id or Fog::Compute::RackspaceV2::Server)"
end
raise "Server (#{server}) not found" unless server.present?
server
end
end
end
end
end

View File

@ -0,0 +1,192 @@
require 'fog/rackspace/core'
module Fog
module Rackspace
class Networking < Fog::Service
include Fog::Rackspace::Errors
class ServiceError < Fog::Rackspace::Errors::ServiceError; end
class InternalServerError < Fog::Rackspace::Errors::InternalServerError; end
class BadRequest < Fog::Rackspace::Errors::BadRequest; end
class InvalidStateException < ::RuntimeError
attr_reader :desired_state
attr_reader :current_state
def initialize(desired_state, current_state)
@desired_state = desired_state
@current_state = current_state
end
end
class InvalidServerStateException < InvalidStateException
def to_s
"Server should have transitioned to '#{desired_state}' not '#{current_state}'"
end
end
class InvalidImageStateException < InvalidStateException
def to_s
"Image should have transitioned to '#{desired_state}' not '#{current_state}'"
end
end
DFW_ENDPOINT = 'https://dfw.servers.api.rackspacecloud.com/v2'
ORD_ENDPOINT = 'https://ord.servers.api.rackspacecloud.com/v2'
LON_ENDPOINT = 'https://lon.servers.api.rackspacecloud.com/v2'
requires :rackspace_username, :rackspace_api_key
recognizes :rackspace_endpoint
recognizes :rackspace_auth_url
recognizes :rackspace_auth_token
recognizes :rackspace_region
recognizes :rackspace_compute_url
model_path 'fog/rackspace/models/networking'
model :network
collection :networks
model :virtual_interface
collection :virtual_interfaces
request_path 'fog/rackspace/requests/networking'
request :list_networks
request :get_network
request :create_network
request :delete_network
request :list_virtual_interfaces
request :create_virtual_interface
request :delete_virtual_interface
class Mock < Fog::Rackspace::Service
include Fog::Rackspace::MockData
def initialize(options)
@rackspace_api_key = options[:rackspace_api_key]
end
def request(params)
Fog::Mock.not_implemented
end
def response(params={})
body = params[:body] || {}
status = params[:status] || 200
headers = params[:headers] || {}
response = Excon::Response.new(:body => body, :headers => headers, :status => status)
if params.key?(:expects) && ![*params[:expects]].include?(response.status)
raise(Excon::Errors.status_error(params, response))
else response
end
end
end
class Real < Fog::Rackspace::Service
def initialize(options = {})
@rackspace_api_key = options[:rackspace_api_key]
@rackspace_username = options[:rackspace_username]
@rackspace_auth_url = options[:rackspace_auth_url]
setup_custom_endpoint(options)
@rackspace_must_reauthenticate = false
@connection_options = options[:connection_options] || {}
authenticate
deprecation_warnings(options)
@persistent = options[:persistent] || false
@connection = Fog::Core::Connection.new(endpoint_uri.to_s, @persistent, @connection_options)
end
def request(params, parse_json = true)
super
rescue Excon::Errors::NotFound => error
raise NotFound.slurp(error, self)
rescue Excon::Errors::BadRequest => error
raise BadRequest.slurp(error, self)
rescue Excon::Errors::InternalServerError => error
raise InternalServerError.slurp(error, self)
rescue Excon::Errors::HTTPStatusError => error
raise ServiceError.slurp(error, self)
end
def authenticate(options={})
super({
:rackspace_api_key => @rackspace_api_key,
:rackspace_username => @rackspace_username,
:rackspace_auth_url => @rackspace_auth_url,
:connection_options => @connection_options
})
end
def service_name
:cloudServersOpenStack
end
def request_id_header
"x-compute-request-id"
end
def region
@rackspace_region
end
def endpoint_uri(service_endpoint_url=nil)
@uri = super(@rackspace_endpoint || service_endpoint_url, :rackspace_compute_url)
end
private
def setup_custom_endpoint(options)
@rackspace_endpoint = Fog::Rackspace.normalize_url(options[:rackspace_compute_url] || options[:rackspace_endpoint])
if v2_authentication?
case @rackspace_endpoint
when DFW_ENDPOINT
@rackspace_endpoint = nil
@rackspace_region = :dfw
when ORD_ENDPOINT
@rackspace_endpoint = nil
@rackspace_region = :ord
when LON_ENDPOINT
@rackspace_endpoint = nil
@rackspace_region = :lon
else
# we are actually using a custom endpoint
@rackspace_region = options[:rackspace_region]
end
else
#if we are using auth1 and the endpoint is not set, default to DFW_ENDPOINT for historical reasons
@rackspace_endpoint ||= DFW_ENDPOINT
end
end
def deprecation_warnings(options)
Fog::Logger.deprecation("The :rackspace_endpoint option is deprecated. Please use :rackspace_compute_url for custom endpoints") if options[:rackspace_endpoint]
if [DFW_ENDPOINT, ORD_ENDPOINT, LON_ENDPOINT].include?(@rackspace_endpoint) && v2_authentication?
regions = @identity_service.service_catalog.display_service_regions(service_name)
Fog::Logger.deprecation("Please specify region using :rackspace_region rather than :rackspace_endpoint. Valid regions for :rackspace_region are #{regions}.")
end
end
def append_tenant_v1(credentials)
account_id = credentials['X-Server-Management-Url'].match(/.*\/([\d]+)$/)[1]
endpoint = @rackspace_endpoint || credentials['X-Server-Management-Url'] || DFW_ENDPOINT
@uri = URI.parse(endpoint)
@uri.path = "#{@uri.path}/#{account_id}"
end
def authenticate_v1(options)
credentials = Fog::Rackspace.authenticate(options, @connection_options)
append_tenant_v1 credentials
@auth_token = credentials['X-Auth-Token']
end
end
end
end
end

View File

@ -0,0 +1,36 @@
module Fog
module Rackspace
class Networking
class Real
def create_network(label, cidr)
data = {
'network' => {
'label' => label,
'cidr' => cidr
}
}
request(
:method => 'POST',
:body => Fog::JSON.encode(data),
:path => "os-networksv2",
:expects => 200
)
end
end
class Mock
def create_network(label, cidr)
network_id = Fog::Rackspace::MockData.uuid
self.data[:networks][network_id] = {
'id' => network_id,
'label' => label,
'cidr' => cidr
}
response(:body => { 'network' => self.data[:networks][network_id] })
end
end
end
end
end

View File

@ -0,0 +1,30 @@
module Fog
module Rackspace
class Networking
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::Rackspace::Networking::NotFound] - HTTP 404
# @raise [Fog::Rackspace::Networking::BadRequest] - HTTP 400
# @raise [Fog::Rackspace::Networking::InternalServerError] - HTTP 500
# @raise [Fog::Rackspace::Networking::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

View File

@ -0,0 +1,21 @@
module Fog
module Rackspace
class Networking
class Real
def delete_network(id)
request(:method => 'DELETE', :path => "os-networksv2/#{id}", :expects => 202)
end
end
class Mock
def delete_network(id)
unless self.data[:networks].key?(id)
raise Fog::Rackspace::Networking::NotFound
end
response(:body => '', :status => 202)
end
end
end
end
end

View File

@ -0,0 +1,23 @@
module Fog
module Rackspace
class Networking
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::Rackspace::Networking::NotFound] - HTTP 404
# @raise [Fog::Rackspace::Networking::BadRequest] - HTTP 400
# @raise [Fog::Rackspace::Networking::InternalServerError] - HTTP 500
# @raise [Fog::Rackspace::Networking::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

View File

@ -0,0 +1,21 @@
module Fog
module Rackspace
class Networking
class Real
def get_network(id)
request(:method => 'GET', :path => "os-networksv2/#{id}", :expects => 200)
end
end
class Mock
def get_network(id)
unless self.data[:networks].key?(id)
raise Fog::Rackspace::Networking::NotFound
end
response(:body => { 'network' => self.data[:networks][id] })
end
end
end
end
end

View File

@ -0,0 +1,18 @@
module Fog
module Rackspace
class Networking
class Real
def list_networks
request(:method => 'GET', :path => 'os-networksv2', :expects => 200)
end
end
class Mock
def list_networks
networks = self.data[:networks].values
response(:body => { 'networks' => networks })
end
end
end
end
end

View File

@ -0,0 +1,22 @@
module Fog
module Rackspace
class Networking
class Real
# Lists virtual interfaces for a server
# @param [String] server_id
# @raise [Fog::Rackspace::Networking::NotFound] - HTTP 404
# @raise [Fog::Rackspace::Networking::BadRequest] - HTTP 400
# @raise [Fog::Rackspace::Networking::InternalServerError] - HTTP 500
# @raise [Fog::Rackspace::Networking::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

View File

@ -0,0 +1,10 @@
Shindo.tests('Fog::Rackspace::Networking | network', ['rackspace']) do
service = Fog::Rackspace::Networking.new
options = {
:label => "fog_network_#{Time.now.to_i.to_s}",
:cidr => '192.168.0.0/24'
}
model_tests(service.networks, options, true)
end

View File

@ -0,0 +1,10 @@
Shindo.tests('Fog::Rackspace::Networking | networks', ['rackspace']) do
service = Fog::Rackspace::Networking.new
options = {
:label => "fog_network_#{Time.now.to_i.to_s}",
:cidr => '192.168.0.0/24'
}
collection_tests(service.networks, options, true)
end

View File

@ -0,0 +1,33 @@
Shindo.tests('Fog::Rackspace::Networking | virtual_interface', ['rackspace']) do
service = Fog::Rackspace::Networking.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

View File

@ -0,0 +1,24 @@
Shindo.tests('Fog::Rackspace::Networking | virtual_interfaces', ['rackspace']) do
service = Fog::Rackspace::Networking.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

View File

@ -0,0 +1,118 @@
Shindo.tests('Fog::Rackspace::Networking', ['rackspace']) do
def assert_method(url, method)
@service.instance_variable_set "@rackspace_auth_url", url
returns(method) { @service.send :authentication_method }
end
tests('#authentication_method') do
@service = Fog::Rackspace::Networking.new
assert_method nil, :authenticate_v2
assert_method 'auth.api.rackspacecloud.com', :authenticate_v1 # chef's default auth endpoint
assert_method 'https://identity.api.rackspacecloud.com', :authenticate_v1
assert_method 'https://identity.api.rackspacecloud.com/v1', :authenticate_v1
assert_method 'https://identity.api.rackspacecloud.com/v1.1', :authenticate_v1
assert_method 'https://identity.api.rackspacecloud.com/v2.0', :authenticate_v2
assert_method 'https://lon.identity.api.rackspacecloud.com', :authenticate_v1
assert_method 'https://lon.identity.api.rackspacecloud.com/v1', :authenticate_v1
assert_method 'https://lon.identity.api.rackspacecloud.com/v1.1', :authenticate_v1
assert_method 'https://lon.identity.api.rackspacecloud.com/v2.0', :authenticate_v2
end
tests('legacy authentication') do
pending if Fog.mocking?
tests('variables populated').succeeds do
@service = Fog::Rackspace::Networking.new :rackspace_auth_url => 'https://identity.api.rackspacecloud.com/v1.0'
returns(true, "auth token populated") { !@service.send(:auth_token).nil? }
returns(false, "path populated") { @service.instance_variable_get("@uri").path.nil? }
returns(true, "identity_service was not used") { @service.instance_variable_get("@identity_service").nil? }
@service.list_flavors
end
tests('custom endpoint') do
@service = Fog::Rackspace::Networking.new :rackspace_auth_url => 'https://identity.api.rackspacecloud.com/v1.0',
:rackspace_compute_url => 'https://my-custom-endpoint.com'
returns(true, "auth token populated") { !@service.send(:auth_token).nil? }
returns(true, "uses custom endpoint") { (@service.instance_variable_get("@uri").host =~ /my-custom-endpoint\.com/) != nil }
end
end
tests('current authentation') do
pending if Fog.mocking?
tests('variables populated').succeeds do
@service = Fog::Rackspace::Networking.new :rackspace_auth_url => 'https://identity.api.rackspacecloud.com/v2.0', :connection_options => {:ssl_verify_peer => true}
returns(true, "auth token populated") { !@service.send(:auth_token).nil? }
returns(false, "path populated") { @service.instance_variable_get("@uri").host.nil? }
identity_service = @service.instance_variable_get("@identity_service")
returns(false, "identity service was used") { identity_service.nil? }
returns(true, "connection_options are passed") { identity_service.instance_variable_get("@connection_options").key?(:ssl_verify_peer) }
@service.list_flavors
end
tests('dfw region').succeeds do
@service = Fog::Rackspace::Networking.new :rackspace_auth_url => 'https://identity.api.rackspacecloud.com/v2.0', :rackspace_region => :dfw
returns(true, "auth token populated") { !@service.send(:auth_token).nil? }
returns(true) { (@service.instance_variable_get("@uri").host =~ /dfw/) != nil }
@service.list_flavors
end
tests('ord region').succeeds do
@service = Fog::Rackspace::Networking.new :rackspace_auth_url => 'https://identity.api.rackspacecloud.com/v2.0', :rackspace_region => :ord
returns(true, "auth token populated") { !@service.send(:auth_token).nil? }
returns(true) { (@service.instance_variable_get("@uri").host =~ /ord/) != nil }
@service.list_flavors
end
tests('custom endpoint') do
@service = Fog::Rackspace::Networking.new :rackspace_auth_url => 'https://identity.api.rackspacecloud.com/v2.0',
:rackspace_compute_url => 'https://my-custom-endpoint.com'
returns(true, "auth token populated") { !@service.send(:auth_token).nil? }
returns(true, "uses custom endpoint") { (@service.instance_variable_get("@uri").host =~ /my-custom-endpoint\.com/) != nil }
end
end
tests('default auth') do
pending if Fog.mocking?
tests('no params').succeeds do
@service = Fog::Rackspace::Networking.new :rackspace_region => nil
returns(true, "auth token populated") { !@service.send(:auth_token).nil? }
returns(true) { (@service.instance_variable_get("@uri").host =~ /dfw/) != nil }
@service.list_flavors
end
tests('specify old contstant style service endoint').succeeds do
@service = Fog::Rackspace::Networking.new :rackspace_endpoint => Fog::Rackspace::Networking::ORD_ENDPOINT
returns(true) { (@service.instance_variable_get("@uri").to_s =~ /#{Fog::Rackspace::Networking::ORD_ENDPOINT}/ ) != nil }
@service.list_flavors
end
tests('specify region').succeeds do
@service = Fog::Rackspace::Networking.new :rackspace_region => :ord
returns(true, "auth token populated") { !@service.send(:auth_token).nil? }
returns(true) { (@service.instance_variable_get("@uri").host =~ /ord/ ) != nil }
@service.list_flavors
end
tests('custom endpoint') do
@service = Fog::Rackspace::Networking.new :rackspace_compute_url => 'https://my-custom-endpoint.com'
returns(true, "auth token populated") { !@service.send(:auth_token).nil? }
returns(true, "uses custom endpoint") { (@service.instance_variable_get("@uri").host =~ /my-custom-endpoint\.com/) != nil }
end
end
tests('reauthentication') do
pending if Fog.mocking?
tests('should reauth with valid credentials') do
@service = Fog::Rackspace::Networking.new
returns(true, "auth token populated") { !@service.send(:auth_token).nil? }
@service.instance_variable_set("@auth_token", "bad_token")
returns(true) { [200, 203].include? @service.list_flavors.status }
end
tests('should terminate with incorrect credentials') do
raises(Excon::Errors::Unauthorized) { Fog::Rackspace::Networking.new :rackspace_api_key => 'bad_key' }
end
end
end

View File

@ -0,0 +1,49 @@
Shindo.tests('Fog::Rackspace::Networking | network_tests', ['rackspace']) do
service = Fog::Rackspace::Networking.new
network_format = {
'id' => String,
'label' => String,
'cidr' => Fog::Nullable::String
}
get_network_format = {
'network' => network_format
}
list_networks_format = {
'networks' => [network_format]
}
tests('success') do
network_id = nil
tests('#create_network').formats(get_network_format) do
service.create_network("fog_#{Time.now.to_i.to_s}", '192.168.0.0/24').body.tap do |r|
network_id = r['network']['id']
end
end
tests('#list_networks').formats(list_networks_format) do
service.list_networks.body
end
tests('#get_network').formats(get_network_format) do
service.get_network(network_id).body
end
tests('#delete_network').succeeds do
service.delete_network(network_id)
end
end
test('failure') do
tests('#get_network').raises(Fog::Rackspace::Networking::NotFound) do
service.get_network(0)
end
tests('#delete_network').raises(Fog::Rackspace::Networking::NotFound) do
service.delete_network(0)
end
end
end

View File

@ -0,0 +1,49 @@
Shindo.tests('Fog::Rackspace::Networking | virtual_interface_tests', ['rackspace']) do
@service = Fog::Rackspace::Networking.new
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