[vcloud|compute] duplicate ecloud code for vcloud, remove obvious terremark specific code.

This commit is contained in:
Lincoln Stoll 2011-05-24 11:17:16 +10:00
parent f7e4797834
commit 7e351ff91d
70 changed files with 4332 additions and 2 deletions

View File

@ -69,6 +69,7 @@ require 'fog/bin/rackspace'
require 'fog/bin/slicehost'
require 'fog/bin/stormondemand'
require 'fog/bin/terremark'
require 'fog/bin/vcloud'
require 'fog/bin/virtual_box'
require 'fog/bin/voxel'
require 'fog/bin/zerigo'

30
lib/fog/bin/vcloud.rb Normal file
View File

@ -0,0 +1,30 @@
class Vcloud < Fog::Bin
class << self
def class_for(key)
case key
when :compute
Fog::Vcloud::Compute
else
raise ArgumentError, "Unrecognized service: #{key}"
end
end
def [](service)
@@connections ||= Hash.new do |hash, key|
hash[key] = case key
when :compute
Fog::Compute.new(:provider => 'Vcloud')
else
raise ArgumentError, "Unrecognized service: #{key.inspect}"
end
end
@@connections[service]
end
def services
Fog::Vcloud.services
end
end
end

View File

@ -40,8 +40,11 @@ module Fog
Fog::Compute::Slicehost.new(attributes)
when :stormondemand
require 'fog/compute/storm_on_demand'
Fog::Compute::StormOnDemand.new(attributes)
when :virtualbox
Fog::StormOnDemand::Compute.new(attributes)
when 'Vcloud'
require 'fog/compute/vcloud'
Fog::Vcloud::Compute.new(attributes)
when 'VirtualBox'
require 'fog/compute/virtual_box'
Fog::Compute::VirtualBox.new(attributes)
when :voxel

View File

@ -0,0 +1,28 @@
module Fog
module Vcloud
class Compute
class Catalog < Fog::Vcloud::Collection
model Fog::Vcloud::Compute::CatalogItem
attribute :href, :aliases => :Href
def all
check_href!
if data = connection.get_catalog(href).body[:CatalogItems][:CatalogItem]
load(data)
end
end
def get(uri)
if data = connection.get_catalog_item(uri)
new(data.body)
end
rescue Fog::Errors::NotFound
nil
end
end
end
end
end

View File

@ -0,0 +1,29 @@
module Fog
module Vcloud
class Compute
class CatalogItem < Fog::Vcloud::Model
identity :href, :aliases => :Href
ignore_attributes :xmlns, :xmlns_i, :xmlns_xsi, :xmlns_xsd
attribute :type
attribute :name
attribute :entity, :aliases => :Entity
attribute :link, :aliases => :Link
attribute :property, :aliases => :Property
def customization_options
load_unless_loaded!
if data = connection.get_customization_options( link[:href] ).body
data.delete_if { |key, value| [:xmlns_i, :xmlns].include?(key) }
data
else
nil
end
end
end
end
end
end

View File

@ -0,0 +1,42 @@
module Fog
module Vcloud
class Compute
class Ip < Fog::Vcloud::Model
ignore_attributes :xmlns_i, :xmlns
identity :href, :aliases => :Href
attribute :name, :aliases => :Name
attribute :status, :aliases => :Status
attribute :server, :aliases => :Server
attribute :id, :aliases => :Id, :type => :integer
def save
if @changed
connection.configure_network_ip( href, _compose_network_ip_data )
end
true
end
def reload
super
@changed = false
self
end
private
def _compose_network_ip_data
{
:id => id,
:href => href,
:name => name,
:status => status,
:server => server
}
end
end
end
end
end

View File

@ -0,0 +1,32 @@
require 'fog/compute/models/vcloud/ip'
module Fog
module Vcloud
class Compute
class Ips < Fog::Vcloud::Collection
model Fog::Vcloud::Compute::Ip
undef_method :create
attribute :href
def all
check_href!( :messages => "Ips href of a Network you want to enumerate" )
if data = connection.get_network_ips(href).body[:IpAddress]
load(data)
end
end
def get(uri)
if data = connection.get_network_ip(uri).body
new(data)
end
rescue Fog::Errors::NotFound
nil
end
end
end
end
end

View File

@ -0,0 +1,65 @@
module Fog
module Vcloud
class Compute
class Network < Fog::Vcloud::Model
identity :href
ignore_attributes :xmlns, :xmlns_xsi, :xmlns_xsd, :xmlns_i, :Configuration, :Id
attribute :name, :aliases => :Name
#attribute :id, :aliases => :Id
attribute :features, :aliases => :Features, :type => :array
attribute :links, :aliases => :Link, :type => :array
attribute :type
attribute :gateway, :aliases => :GatewayAddress
attribute :broadcast, :aliases => :BroadcastAddress
attribute :address, :aliases => :Address
attribute :extension_href, :aliases => :Href
attribute :network_type, :aliases => :NetworkType
attribute :vlan, :aliases => :Vlan
attribute :friendly_name, :aliases => :FriendlyName
def ips
load_unless_loaded!
Fog::Vcloud::Compute::Ips.new( :connection => connection,
:href => links.detect { |link| link[:name] == "IP Addresses" }[:href] )
end
def save
if @changed
connection.configure_network( extension_href, _compose_network_data )
end
true
end
def reload
super
merge_attributes(extension_data.body)
self
end
private
def extension_data
connection.get_network_extensions( extensions_link[:href] )
end
def extensions_link
links.detect { |link| link[:name] == name }
end
def _compose_network_data
{
:id => id,
:href => extension_href,
:name => name,
:address => address,
:broadcast => broadcast,
:gateway => gateway
}
end
end
end
end
end

View File

@ -0,0 +1,33 @@
require 'fog/compute/models/vcloud/network'
module Fog
module Vcloud
class Compute
class Networks < Fog::Vcloud::Collection
undef_method :create
model Fog::Vcloud::Compute::Network
attribute :href
def all
check_href!("Vdc")
if data = connection.get_vdc(href).body[:AvailableNetworks][:Network]
load(data)
end
end
def get(uri)
if data = connection.get_network(uri)
new(data.body)
end
rescue Fog::Errors::NotFound
nil
end
end
end
end
end

View File

@ -0,0 +1,209 @@
module Fog
module Vcloud
class Compute
class Server < Fog::Vcloud::Model
identity :href, :aliases => :Href
ignore_attributes :xmlns, :xmlns_i, :xmlns_xsi, :xmlns_xsd
attribute :type
attribute :name
attribute :status
attribute :network_connections, :aliases => :NetworkConnectionSection, :squash => :NetworkConnection
attribute :os, :aliases => :OperatingSystemSection
attribute :virtual_hardware, :aliases => :VirtualHardwareSection
attribute :storage_size, :aliases => :size
attribute :links, :aliases => :Link, :type => :array
def friendly_status
load_unless_loaded!
case status
when '0'
'creating'
when '2'
'off'
when '4'
'on'
else
'unkown'
end
end
def ready?
load_unless_loaded!
status == '2'
end
def on?
load_unless_loaded!
status == '4'
end
def off?
load_unless_loaded!
status == '2'
end
def power_on
power_operation( :power_on => :powerOn )
end
def power_off
power_operation( :power_off => :powerOff )
end
def shutdown
power_operation( :power_shutdown => :shutdown )
end
def power_reset
power_operation( :power_reset => :reset )
end
def graceful_restart
requires :href
shutdown
wait_for { off? }
power_on
end
def delete
requires :href
connection.delete_vapp( href)
end
def name=(new_name)
attributes[:name] = new_name
@changed = true
end
def cpus
if cpu_mess
{ :count => cpu_mess[:VirtualQuantity].to_i,
:units => cpu_mess[:AllocationUnits] }
end
end
def cpus=(qty)
@changed = true
cpu_mess[:VirtualQuantity] = qty.to_s
end
def memory
if memory_mess
{ :amount => memory_mess[:VirtualQuantity].to_i,
:units => memory_mess[:AllocationUnits] }
end
end
def memory=(amount)
@changed = true
memory_mess[:VirtualQuantity] = amount.to_s
end
def disks
disk_mess.map do |dm|
{ :number => dm[:AddressOnParent], :size => dm[:VirtualQuantity].to_i, :resource => dm[:HostResource] }
end
end
def add_disk(size)
if @disk_change == :deleted
raise RuntimeError, "Can't add a disk w/o saving changes or reloading"
else
@disk_change = :added
load_unless_loaded!
virtual_hardware[:Item] << { :ResourceType => '17',
:AddressOnParent => (disk_mess.map { |dm| dm[:AddressOnParent] }.sort.last.to_i + 1).to_s,
:VirtualQuantity => size.to_s }
end
true
end
def delete_disk(number)
if @disk_change == :added
raise RuntimeError, "Can't delete a disk w/o saving changes or reloading"
else
@disk_change = :deleted
load_unless_loaded!
unless number == 0
virtual_hardware[:Item].delete_if { |vh| vh[:ResourceType] == '17' && vh[:AddressOnParent].to_i == number }
end
end
true
end
def reload
reset_tracking
super
end
def save
if new_record?
#Lame ...
raise RuntimeError, "Should not be here"
else
if on?
if @changed
raise RuntimeError, "Can't save cpu, name or memory changes while the VM is on."
end
end
connection.configure_vapp( href, _compose_vapp_data )
end
reset_tracking
end
private
def reset_tracking
@disk_change = false
@changed = false
end
def _compose_vapp_data
{ :name => name,
:cpus => cpus[:count],
:memory => memory[:amount],
:disks => disks
}
end
def memory_mess
load_unless_loaded!
if virtual_hardware && virtual_hardware[:Item]
virtual_hardware[:Item].detect { |item| item[:ResourceType] == "4" }
end
end
def cpu_mess
load_unless_loaded!
if virtual_hardware && virtual_hardware[:Item]
virtual_hardware[:Item].detect { |item| item[:ResourceType] == "3" }
end
end
def disk_mess
load_unless_loaded!
if virtual_hardware && virtual_hardware[:Item]
virtual_hardware[:Item].select { |item| item[:ResourceType] == "17" }
else
[]
end
end
def power_operation(op)
requires :href
begin
connection.send(op.keys.first, href + "/power/action/#{op.values.first}" )
rescue Excon::Errors::InternalServerError => e
#Frankly we shouldn't get here ...
raise e unless e.to_s =~ /because it is already powered o(n|ff)/
end
true
end
end
end
end
end

View File

@ -0,0 +1,57 @@
require 'fog/compute/models/vcloud/server'
module Fog
module Vcloud
class Compute
class Servers < Fog::Vcloud::Collection
undef_method :create
model Fog::Vcloud::Compute::Server
attribute :href, :aliases => :Href
def all
check_href!(:parent => "Vdc")
load(_vapps)
end
def get(uri)
if data = connection.get_vapp(uri)
new(data.body)
end
rescue Fog::Errors::NotFound
nil
end
def create( catalog_item_uri, options )
options[:vdc_uri] = href
options[:cpus] ||= 1
options[:memory] ||= 512
data = connection.instantiate_vapp_template( catalog_item_uri, options ).body
object = new(data)
object
end
private
def _resource_entities
if Hash === resource_entities = connection.get_vdc(href).body[:ResourceEntities]
resource_entities[:ResourceEntity]
end
end
def _vapps
resource_entities = _resource_entities
if resource_entities.nil?
[]
else
resource_entities
end
end
end
end
end
end

View File

@ -0,0 +1,21 @@
module Fog
module Vcloud
class Compute
class Task < Fog::Vcloud::Model
identity :href, :aliases => :Href
ignore_attributes :xmlns, :xmlns_i, :xmlns_xsi, :xmlns_xsd
attribute :status
attribute :type
attribute :result, :aliases => :Result
attribute :owner, :aliases => :Owner
attribute :start_time, :aliases => :startTime, :type => :time
attribute :end_time, :aliases => :endTime, :type => :time
attribute :error, :aliases => :Error
end
end
end
end

View File

@ -0,0 +1,31 @@
require 'fog/compute/models/vcloud/task'
module Fog
module Vcloud
class Compute
class Tasks < Fog::Vcloud::Collection
model Fog::Vcloud::Compute::Task
attribute :href, :aliases => :Href
def all
check_href!
if data = connection.get_task_list(href).body[:Task]
load(data)
end
end
def get(uri)
if data = connection.get_task(uri)
new(data.body)
end
rescue Fog::Errors::NotFound
nil
end
end
end
end
end

View File

@ -0,0 +1,60 @@
module Fog
module Vcloud
class Compute
class Vdc < Fog::Vcloud::Model
identity :href
ignore_attributes :xmlns, :xmlns_xsi, :xmlns_xsd
attribute :name
attribute :type
attribute :description, :aliases => :Description
attribute :other_links, :aliases => :Link
attribute :compute_capacity, :aliases => :ComputeCapacity
attribute :storage_capacity, :aliases => :StorageCapacity
attribute :available_networks, :aliases => :AvailableNetworks, :squash => :Network
attribute :resource_entities, :aliases => :ResourceEntities, :squash => :ResourceEntity
attribute :deployed_vm_quota
attribute :instantiated_vm_quota
def networks
@networks ||= Fog::Vcloud::Compute::Networks.
new( :connection => connection,
:href => href )
end
def servers
@servers ||= Fog::Vcloud::Compute::Servers.
new( :connection => connection,
:href => href )
end
def tasks
@tasks ||= Fog::Vcloud::Compute::Tasks.
new( :connection => connection,
:href => href + "/tasksList" )
end
def catalog
@catalog ||= collection_based_on_type("application/vnd.vmware.vcloud.catalog+xml")
end
private
def collection_based_on_type(type, klass = nil)
load_unless_loaded!
test_links = other_links.kind_of?(Array) ? other_links : [other_links]
if link = test_links.detect { |link| link[:type] == type }
case type
when "application/vnd.vmware.vcloud.catalog+xml"
Fog::Vcloud::Compute::Catalog
end.new( :connection => connection, :href => link[:href] )
else
[ ]
end
end
end
end
end
end

View File

@ -0,0 +1,40 @@
require 'fog/compute/models/vcloud/vdc'
module Fog
module Vcloud
class Compute
class Vdcs < Collection
model Fog::Vcloud::Compute::Vdc
undef_method :create
def all
data = connection.get_organization(organization_uri).body[:Link].select { |link| link[:type] == "application/vnd.vmware.vcloud.vdc+xml" }
data.each { |link| link.delete_if { |key, value| [:rel].include?(key) } }
load(data)
end
def get(uri)
if data = connection.get_vdc(uri)
new(data.body)
end
rescue Fog::Errors::NotFound
nil
end
def organization_uri
@organization_uri ||= connection.default_organization_uri
end
private
def organization_uri=(new_organization_uri)
@organization_uri = new_organization_uri
end
end
end
end
end

View File

@ -0,0 +1,40 @@
module Fog
module Vcloud
class Compute
class Real
def validate_clone_vapp_options(options)
valid_opts = [:name, :poweron]
unless valid_opts.all? { |opt| options.keys.include?(opt) }
raise ArgumentError.new("Required data missing: #{(valid_opts - options.keys).map(&:inspect).join(", ")}")
end
end
def generate_clone_vapp_request(uri, options)
xml = Builder::XmlMarkup.new
xml.CloneVAppParams(xmlns.merge!(:name => options[:name], :deploy => "true", :powerOn => options[:poweron])) {
xml.VApp( :href => uri, :type => "application/vnd.vmware.vcloud.vApp+xml",
:xmlns => "http://www.vmware.com/vcloud/v0.8")
}
end
def clone_vapp(vdc_uri, vapp_uri, options = {})
unless options.has_key?(:poweron)
options[:poweron] = "false"
end
validate_clone_vapp_options(options)
request(
:body => generate_clone_vapp_request(vapp_uri, options),
:expects => 202,
:headers => {'Content-Type' => 'application/vnd.vmware.vcloud.cloneVAppParams+xml'},
:method => 'POST',
:uri => vdc_uri + '/action/clonevapp',
:parse => true
)
end
end
end
end
end

View File

@ -0,0 +1,43 @@
module Fog
module Vcloud
class Compute
class Real
def validate_network_data(network_data, configure=false)
valid_opts = [:id, :href, :name, :address, :broadcast, :gateway]
unless valid_opts.all? { |opt| network_data.keys.include?(opt) }
raise ArgumentError.new("Required data missing: #{(valid_opts - network_data.keys).map(&:inspect).join(", ")}")
end
end
def configure_network(network_uri, network_data)
validate_network_data(network_data)
request(
:body => generate_configure_network_request(network_data),
:expects => 200,
:headers => {'Content-Type' => 'application/vnd.vmware.vcloud.networkService+xml'},
:method => 'PUT',
:uri => network_uri,
:parse => true
)
end
private
def generate_configure_network_request(network_data)
builder = Builder::XmlMarkup.new
builder.Network(xmlns) {
builder.Id(network_data[:id])
builder.Href(network_data[:href])
builder.Name(network_data[:name])
builder.Address(network_data[:address])
builder.BroadcastAddress(network_data[:broadcast])
builder.GatewayAddress(network_data[:gateway])
}
end
end
end
end
end

View File

@ -0,0 +1,64 @@
module Fog
module Vcloud
class Compute
module Shared
private
def validate_network_ip_data(network_ip_data)
valid_opts = [:id, :href, :name, :status, :server]
unless valid_opts.all? { |opt| network_ip_data.keys.include?(opt) }
raise ArgumentError.new("Required data missing: #{(valid_opts - network_ip_data.keys).map(&:inspect).join(", ")}")
end
end
end
class Real
include Shared
def configure_network_ip(network_ip_uri, network_ip_data)
validate_network_ip_data(network_ip_data)
request(
:body => generate_configure_network_ip_request(network_ip_data),
:expects => 200,
:headers => {'Content-Type' => 'application/vnd.vmware.vcloud.ip+xml' },
:method => 'PUT',
:uri => network_ip_uri,
:parse => true
)
end
private
def generate_configure_network_ip_request(network_ip_data)
builder = Builder::XmlMarkup.new
builder.IpAddress(xmlns) {
builder.Id(network_ip_data[:id])
builder.Href(network_ip_data[:href])
builder.Name(network_ip_data[:name])
builder.Status(network_ip_data[:status])
builder.Server(network_ip_data[:server])
}
end
end
class Mock
include Shared
def configure_network_ip(network_ip_uri, network_ip_data)
validate_network_ip_data(network_ip_data)
if network_ip = mock_data.network_ip_from_href(network_ip_uri)
builder = Builder::XmlMarkup.new
xml = network_ip_response(builder, network_ip, xmlns)
mock_it 200, xml, { 'Content-Type' => 'application/vnd.vmware.vcloud.ip+xml' }
else
mock_error 200, "401 Unauthorized"
end
end
end
end
end
end

View File

@ -0,0 +1,56 @@
module Fog
module Vcloud
class Compute
module Shared
private
def generate_configure_node_request(node_data)
builder = Builder::XmlMarkup.new
builder.NodeService(:"xmlns:i" => "http://www.w3.org/2001/XMLSchema-instance",
:xmlns => "urn:tmrk:eCloudExtensions-2.0") {
builder.Name(node_data[:name])
builder.Enabled(node_data[:enabled].to_s)
builder.Description(node_data[:description])
}
end
end
class Real
include Shared
def configure_node(node_uri, node_data)
validate_node_data(node_data, true)
request(
:body => generate_configure_node_request(node_data),
:expects => 200,
:headers => {'Content-Type' => 'application/vnd.vmware.vcloud.nodeService+xml'},
:method => 'PUT',
:uri => node_uri,
:parse => true
)
end
end
class Mock
include Shared
def configure_node(node_uri, node_data)
validate_node_data(node_data, true)
if node = mock_data.public_ip_internet_service_node_from_href(ensure_unparsed(node_uri))
node.update(node_data)
#if node_data[:enabled]
# node.enabled = (node_data[:enabled] == "true") ? true : false
#end
mock_it 200, mock_node_service_response(node), { 'Content-Type' => 'application/vnd.vmware.vcloud.nodeService+xml' }
else
mock_error 200, "401 Unauthorized"
end
end
end
end
end
end

View File

@ -0,0 +1,144 @@
module Fog
module Vcloud
class Compute
module Shared
private
def validate_vapp_data(vapp_data)
valid_opts = [:name, :cpus, :memory, :disks]
unless valid_opts.all? { |opt| vapp_data.keys.include?(opt) }
raise ArgumentError.new("Required Vapp data missing: #{(valid_opts - vapp_data.keys).map(&:inspect).join(", ")}")
end
end
end
class Real
include Shared
def generate_configure_vapp_request(vapp_uri, vapp_data)
rasd_xmlns = { "xmlns" => "http://schemas.dmtf.org/wbem/wscim/1/cim-schema/2/CIM_ResourceAllocationSettingData" }
xml = Nokogiri::XML(request( :uri => vapp_uri).body)
xml.root['name'] = vapp_data[:name]
#cpu
xml.at("//xmlns:ResourceType[.='3']/..", rasd_xmlns).at('.//xmlns:VirtualQuantity', rasd_xmlns).content = vapp_data[:cpus]
#memory
xml.at("//xmlns:ResourceType[.='4']/..", rasd_xmlns).at('.//xmlns:VirtualQuantity', rasd_xmlns).content = vapp_data[:memory]
#disks
real_disks = xml.xpath("//xmlns:ResourceType[ .='17']/..", rasd_xmlns)
real_disk_numbers = real_disks.map { |disk| disk.at('.//xmlns:AddressOnParent', rasd_xmlns).content }
disk_numbers = vapp_data[:disks].map { |vdisk| vdisk[:number].to_s }
if vapp_data[:disks].length < real_disks.length
#Assume we're removing a disk
remove_disk_numbers = real_disk_numbers - disk_numbers
remove_disk_numbers.each do |number|
if result = xml.at("//xmlns:ResourceType[ .='17']/../xmlns:AddressOnParent[.='#{number}']/..", rasd_xmlns)
result.remove
end
end
elsif vapp_data[:disks].length > real_disks.length
add_disk_numbers = disk_numbers - real_disk_numbers
add_disk_numbers.each do |number|
new_disk = real_disks.first.dup
new_disk.at('.//xmlns:AddressOnParent', rasd_xmlns).content = -1
new_disk.at('.//xmlns:VirtualQuantity', rasd_xmlns).content = vapp_data[:disks].detect { |disk| disk[:number].to_s == number.to_s }[:size]
real_disks.first.parent << new_disk
end
end
#puts xml.root.to_s
xml.root.to_s
#builder = Builder::XmlMarkup.new
#builder.Vapp(:href => vapp_uri.to_s,
# :type => 'application/vnd.vmware.vcloud.vApp+xml',
# :name => vapp_data[:name],
# :status => 2,
# :size => 0,
# :xmlns => 'http://www.vmware.com/vcloud/v0.8',
# :"xmlns:xsi" => 'http://www.w3.org/2001/XMLSchema-instance',
# :"xmlns:xsd" => 'http://www.w3.org/2001/XMLSchema') {
# #builder.VirtualHardwareSection(:xmlns => 'http://schemas.dmtf.org/ovf/envelope/1') {
# builder.Section(:"xsi:type" => "q2:VirtualHardwareSection_Type", :xmlns => "http://schemas.dmtf.org/ovf/envelope/1", :"xmlns:q2" => "http://www.vmware.com/vcloud/v0.8") {
# builder.Info('Virtual Hardware')
# builder.Item(:xmlns => 'http://schemas.dmtf.org/ovf/envelope/1') {
# #builder.Item {
# builder.InstanceID(1, :xmlns => 'http://schemas.dmtf.org/wbem/wscim/1/cim-schema/2/CIM_ResourceAllocationSettingData')
# builder.ResourceType(3, :xmlns => 'http://schemas.dmtf.org/wbem/wscim/1/cim-schema/2/CIM_ResourceAllocationSettingData')
# builder.VirtualQuantity(vapp_data[:cpus], :xmlns => 'http://schemas.dmtf.org/wbem/wscim/1/cim-schema/2/CIM_ResourceAllocationSettingData')
# }
# builder.Item(:xmlns => 'http://schemas.dmtf.org/ovf/envelope/1') {
# #builder.Item {
# builder.InstanceID(2, :xmlns => 'http://schemas.dmtf.org/wbem/wscim/1/cim-schema/2/CIM_ResourceAllocationSettingData')
# builder.ResourceType(4, :xmlns => 'http://schemas.dmtf.org/wbem/wscim/1/cim-schema/2/CIM_ResourceAllocationSettingData')
# builder.VirtualQuantity(vapp_data[:memory], :xmlns => 'http://schemas.dmtf.org/wbem/wscim/1/cim-schema/2/CIM_ResourceAllocationSettingData')
# }
# vapp_data[:disks].each do |disk_data|
# #builder.Item(:xmlns => 'http://schemas.dmtf.org/ovf/envelope/1') {
# builder.Item {
# builder.AddressOnParent(disk_data[:number], :xmlns => 'http://schemas.dmtf.org/wbem/wscim/1/cim-schema/2/CIM_ResourceAllocationSettingData')
# builder.HostResource(disk_data[:resource], :xmlns => 'http://schemas.dmtf.org/wbem/wscim/1/cim-schema/2/CIM_ResourceAllocationSettingData')
# builder.InstanceID(9, :xmlns => 'http://schemas.dmtf.org/wbem/wscim/1/cim-schema/2/CIM_ResourceAllocationSettingData')
# builder.ResourceType(17, :xmlns => 'http://schemas.dmtf.org/wbem/wscim/1/cim-schema/2/CIM_ResourceAllocationSettingData')
# builder.VirtualQuantity(disk_data[:size], :xmlns => 'http://schemas.dmtf.org/wbem/wscim/1/cim-schema/2/CIM_ResourceAllocationSettingData')
# }
# end
#
# }
#}
end
def configure_vapp(vapp_uri, vapp_data)
validate_vapp_data(vapp_data)
request(
:body => generate_configure_vapp_request(vapp_uri, vapp_data),
:expects => 202,
:headers => {'Content-Type' => 'application/vnd.vmware.vcloud.vApp+xml' },
:method => 'PUT',
:uri => vapp_uri,
:parse => true
)
end
end
class Mock
include Shared
def configure_vapp(vapp_uri, vapp_data)
validate_vapp_data(vapp_data)
if vapp = mock_data.virtual_machine_from_href(vapp_uri)
vapp_data.each do |key, value|
case key
when :cpus, :memory
vapp[key] = value
when :disks
addresses_to_delete = vapp.disks.map {|d| d.address } - value.map {|d| d[:number] }
addresses_to_delete.each do |address_to_delete|
vapp.disks.delete(vapp.disks.at_address(address_to_delete))
end
current_addresses = vapp.disks.map {|d| d.address }
disks_to_add = value.find_all {|d| !current_addresses.include?(d[:number]) }
disks_to_add.each do |disk_to_add|
vapp.disks << MockVirtualMachineDisk.new(:size => disk_to_add[:size] / 1024, :address => disk_to_add[:number])
end
end
end
mock_it 200, '', { "Location" => mock_data.base_url + "/some_tasks/1234" }
else
mock_error 200, "401 Unauthorized"
end
end
end
end
end
end

View File

@ -0,0 +1,22 @@
module Fog
module Vcloud
class Compute
class Real
basic_request :delete_node, 200, 'DELETE', {}, ""
end
class Mock
def delete_node(node_uri)
if node = mock_data.public_ip_internet_service_node_from_href(ensure_unparsed(node_uri))
node._parent.items.delete(node)
mock_it 200, '', {}
else
mock_error 200, "401 Unauthorized"
end
end
end
end
end
end

View File

@ -0,0 +1,27 @@
module Fog
module Vcloud
class Compute
class Real
basic_request :delete_vapp, 202, "DELETE"
end
class Mock
def delete_vapp(vapp_uri)
if virtual_machine = mock_data.virtual_machine_from_href(vapp_uri)
vdc = virtual_machine._parent
if virtual_machine.status != 2
mock_it 202, '', {}
else
vdc.virtual_machines.delete(virtual_machine)
mock_it 202, '', { "Location" => mock_data.base_url + "/some_tasks/1234" }
end
else
mock_error 200, "401 Unauthorized"
end
end
end
end
end
end

View File

@ -0,0 +1,44 @@
module Fog
module Vcloud
class Compute
class Real
basic_request :get_catalog
end
class Mock
def get_catalog(catalog_uri)
catalog_uri = ensure_unparsed(catalog_uri)
xml = nil
if catalog = mock_data.catalog_from_href(catalog_uri)
builder = Builder::XmlMarkup.new
xml = builder.Catalog(xmlns.merge(
:type => "application/vnd.vmware.vcloud.catalog+xml",
:href => catalog.href,
:name => catalog.name
)) do |xml|
xml.CatalogItems do |xml|
catalog.items.each do |catalog_item|
xml.CatalogItem(
:type => "application/vnd.vmware.vcloud.catalogItem+xml",
:href => catalog_item.href,
:name => catalog_item.name
)
end
end
end
end
if xml
mock_it 200,
xml, { 'Content-Type' => 'application/vnd.vmware.vcloud.catalog+xml' }
else
mock_error 200, "401 Unauthorized"
end
end
end
end
end
end

View File

@ -0,0 +1,42 @@
module Fog
module Vcloud
class Compute
class Real
basic_request :get_catalog_item
end
class Mock
def get_catalog_item(catalog_item_uri)
if catalog_item = mock_data.catalog_item_from_href(catalog_item_uri)
builder = Builder::XmlMarkup.new
xml = builder.CatalogItem(xmlns.merge(:href => catalog_item.href, :name => catalog_item.name)) do
builder.Link(
:rel => "down",
:href => catalog_item.customization.href,
:type => "application/vnd.vmware.vcloud.catalogItemCustomizationParameters+xml",
:name => catalog_item.customization.name
)
builder.Entity(
:href => catalog_item.vapp_template.href,
:type => "application/vnd.vmware.vcloud.vAppTemplate+xml",
:name => catalog_item.vapp_template.name
)
builder.Property(0, :key => "LicensingCost")
end
end
if xml
mock_it 200, xml, {'Content-Type' => 'application/vnd.vmware.vcloud.catalogItem+xml'}
else
mock_error 200, "401 Unauthorized"
end
end
end
end
end
end

View File

@ -0,0 +1,22 @@
module Fog
module Vcloud
class Compute
class Real
basic_request :get_customization_options
end
class Mock
def get_customization_options(options_uri)
builder = Builder::XmlMarkup.new
xml = builder.CustomizationParameters(xmlns) do
builder.CustomizeNetwork "true"
builder.CustomizePassword "false"
end
mock_it 200, xml, "Content-Type" => "application/vnd.vmware.vcloud.catalogItemCustomizationParameters+xml"
end
end
end
end
end

View File

@ -0,0 +1,41 @@
module Fog
module Vcloud
class Compute
class Real
basic_request :get_network
end
class Mock
def get_network(network_uri)
network_uri = ensure_unparsed(network_uri)
if network = mock_data.network_from_href(network_uri)
builder = Builder::XmlMarkup.new
xml = builder.Network(xmlns.merge(:href => network.href, :name => network.name, :type => "application/vnd.vmware.vcloud.network+xml")) {
builder.Link(:rel => "down", :href => network.ip_collection.href, :type => "application/xml", :name => network.ip_collection.name)
builder.Link(:rel => "down", :href => network.extensions.href, :type => "application/xml", :name => network.name)
builder.Configuration {
builder.Gateway(network.gateway)
builder.Netmask(network.netmask)
}
if network.features
builder.Features {
network.features.each do |feature|
builder.tag!(feature[:type], feature[:value])
end
}
end
}
mock_it 200, xml, { "Content-Type" => "application/vnd.vmware.vcloud.network+xml" }
else
mock_error 200, "401 Unauthorized"
end
end
end
end
end
end

View File

@ -0,0 +1,32 @@
module Fog
module Vcloud
class Compute
class Real
basic_request :get_network_extensions
end
class Mock
def get_network_extensions(network_extension_uri)
if network_extension = mock_data.network_extension_from_href(ensure_unparsed(network_extension_uri))
xml = Builder::XmlMarkup.new
mock_it 200, xml.Network(xmlns) {
xml.Address network_extension.address
xml.Href network_extension.href
xml.Id network_extension.object_id
xml.Name network_extension.name
xml.GatewayAddress network_extension.gateway
xml.BroadcastAddress network_extension.broadcast
xml.NetworkType network_extension.type
xml.Vlan network_extension.vlan
xml.FriendlyName network_extension.friendly_name
}, { 'Content-Type' => "application/vnd.vmware.vcloud.network+xml" }
else
mock_error 200, "401 Unauthorized"
end
end
end
end
end
end

View File

@ -0,0 +1,42 @@
#
# AFAICT this is basically undocumented ATM - 6/18/2010 - freeformz
#
module Fog
module Vcloud
class Compute
class Real
basic_request :get_network_ip
end
class Mock
def get_network_ip(network_ip_uri)
if network_ip = mock_data.network_ip_from_href(network_ip_uri)
builder = Builder::XmlMarkup.new
xml = network_ip_response(builder, network_ip, xmlns)
mock_it 200, xml, { 'Content-Type' => 'application/vnd.vmware.vcloud.ip+xml' }
else
mock_error 200, "401 Unauthorized"
end
end
def network_ip_response(builder, network_ip, xmlns = {})
builder.IpAddress(xmlns) do
builder.Id network_ip.object_id
builder.Href network_ip.href
builder.Name network_ip.name
builder.Status network_ip.status
if network_ip.used_by
builder.Server network_ip.used_by.name
end
end
end
end
end
end
end

View File

@ -0,0 +1,35 @@
#
# AFAICT - This is basically undocumented - 6/18/2010 - freeformz
#
module Fog
module Vcloud
class Compute
class Real
basic_request :get_network_ips
end
class Mock
def get_network_ips(network_ips_uri)
network_ips_uri = ensure_unparsed(network_ips_uri)
if network_ip_collection = mock_data.network_ip_collection_from_href(network_ips_uri)
builder = Builder::XmlMarkup.new
xml = builder.IpAddresses do
network_ip_collection.ordered_ips.each do |network_ip|
network_ip_response(builder, network_ip)
end
end
mock_it 200, xml, { 'Content-Type' => 'application/vnd.vmware.vcloud.ipAddressesList+xml' }
else
mock_error 200, "401 Unauthorized"
end
end
end
end
end
end

View File

@ -0,0 +1,43 @@
module Fog
module Vcloud
class Compute
class Real
basic_request :get_organization
end
class Mock
def get_organization(organization_uri)
organization_uri = ensure_unparsed(organization_uri)
if organization = mock_data.organization_from_href(organization_uri)
xml = Builder::XmlMarkup.new
mock_it 200,
xml.Org(xmlns.merge(:href => organization.href, :name => organization.name)) {
organization.vdcs.each do |vdc|
xml.Link(:rel => "down",
:href => vdc.href,
:type => "application/vnd.vmware.vcloud.vdc+xml",
:name => vdc.name)
xml.Link(:rel => "down",
:href => vdc.catalog.href,
:type => "application/vnd.vmware.vcloud.catalog+xml",
:name => vdc.catalog.name)
xml.Link(:rel => "down",
:href => vdc.task_list.href,
:type => "application/vnd.vmware.vcloud.tasksList+xml",
:name => vdc.task_list.name)
end
},
{'Content-Type' => "application/vnd.vmware.vcloud.org+xml" }
else
mock_error 200, "401 Unauthorized"
end
end
end
end
end
end

View File

@ -0,0 +1,11 @@
module Fog
module Vcloud
class Compute
class Real
basic_request :get_task
end
end
end
end

View File

@ -0,0 +1,11 @@
module Fog
module Vcloud
class Compute
class Real
basic_request :get_task_list
end
end
end
end

View File

@ -0,0 +1,99 @@
module Fog
module Vcloud
class Compute
class Real
basic_request :get_vapp
end
class Mock
def return_vapp_as_creating!(name)
vapps_to_return_as_creating[name] = true
end
def vapps_to_return_as_creating
@vapps_to_return_as_creating ||= {}
end
def get_vapp(vapp_uri)
xml = nil
if vapp = mock_data.virtual_machine_from_href(vapp_uri)
if vapps_to_return_as_creating[vapp.name]
xml = generate_instantiate_vapp_template_response(vapp)
else
xml = generate_get_vapp_response(vapp)
end
end
if xml
mock_it 200, xml, "Content-Type" => "application/vnd.vmware.vcloud.vApp+xml"
else
mock_error 200, "401 Unauthorized"
end
end
private
def generate_get_vapp_response(vapp)
builder = Builder::XmlMarkup.new
builder.VApp(xmlns.merge(
:href => vapp.href,
:type => "application/vnd.vmware.vcloud.vApp+xml",
:name => vapp.name,
:status => vapp.status,
:size => vapp.size
)) do
builder.Link(:rel => "up", :href => vapp._parent.href, :type => "application/vnd.vmware.vcloud.vdc+xml")
builder.NetworkConnectionSection(:xmlns => "http://schemas.dmtf.org/ovf/envelope/1") do
builder.NetworkConnection(:Network => "Internal", :xmlns => "http://www.vmware.com/vcloud/v0.8") do
builder.IpAddress vapp.ip
end
end
builder.OperatingSystemSection(
"d2p1:id" => 4,
:xmlns => "http://schemas.dmtf.org/ovf/envelope/1",
"xmlns:d2p1" => "http://schemas.dmtf.org/ovf/envelope/1") do
builder.Info "The kind of installed guest operating system"
builder.Description "Red Hat Enterprise Linux 5 (64-bit)"
end
builder.VirtualHardwareSection(:xmlns => "http://schemas.dmtf.org/ovf/envelope/1") do
builder.Info
builder.System
builder.Item do
# CPUs
builder.VirtualQuantity vapp.cpus
builder.ResourceType 3
end
builder.Item do
# memory
builder.VirtualQuantity vapp.memory
builder.ResourceType 4
end
builder.Item do
# SCSI controller
builder.Address 0
builder.ResourceType 6
builder.InstanceId 3
end
# Hard Disks
vapp.disks.each do |disk|
builder.Item do
builder.Parent 3
builder.VirtualQuantity disk.vcloud_size
builder.HostResource disk.vcloud_size
builder.ResourceType 17
builder.AddressOnParent disk.address
end
end
end
end
end
end
end
end
end

View File

@ -0,0 +1,11 @@
module Fog
module Vcloud
class Compute
class Real
basic_request :get_vapp_template
end
end
end
end

View File

@ -0,0 +1,72 @@
module Fog
module Vcloud
class Compute
class Real
basic_request :get_vdc
end
class Mock
def get_vdc(vdc_uri)
vdc_uri = ensure_unparsed(vdc_uri)
if vdc = mock_data.vdc_from_href(vdc_uri)
xml = Builder::XmlMarkup.new
mock_it 200,
xml.Vdc(xmlns.merge(:href => vdc.href, :name => vdc.name)) {
xml.Link(:rel => "down",
:href => vdc.catalog.href,
:type => "application/vnd.vmware.vcloud.catalog+xml",
:name => vdc.catalog.name)
# xml.Link(:rel => "down",
# :href => 'vdc.public_ip_collection.href',
# :type => "application/vnd.tmrk.ecloud.publicIpsList+xml",
# :name => 'vdc.public_ip_collection.name')
xml.Description("")
xml.StorageCapacity {
xml.Units("bytes * 10^9")
xml.Allocated(vdc.storage_allocated)
xml.Used(vdc.storage_used)
}
xml.ComputeCapacity {
xml.Cpu {
xml.Units("hz * 10^6")
xml.Allocated(vdc.cpu_allocated)
}
xml.Memory {
xml.Units("bytes * 2^20")
xml.Allocated(vdc.memory_allocated)
}
xml.DeployedVmsQuota {
xml.Limit("-1")
xml.Used("-1")
}
xml.InstantiatedVmsQuota {
xml.Limit("-1")
xml.Used("-1")
}
}
xml.ResourceEntities {
vdc.virtual_machines.each do |virtual_machine|
xml.ResourceEntity(:href => virtual_machine.href,
:type => "application/vnd.vmware.vcloud.vApp+xml",
:name => virtual_machine.name)
end
}
xml.AvailableNetworks {
vdc.networks.each do |network|
xml.Network(:href => network.href,
:type => "application/vnd.vmware.vcloud.network+xml",
:name => network.name)
end
}
}, { 'Content-Type' => 'application/vnd.vmware.vcloud.vdc+xml'}
else
mock_error 200, "401 Unauthorized"
end
end
end
end
end
end

View File

@ -0,0 +1,137 @@
module Fog
module Vcloud
class Compute
module Shared
private
def validate_instantiate_vapp_template_options(catalog_item_uri, options)
valid_opts = [:name, :vdc_uri, :network_uri, :cpus, :memory, :row, :group]
unless valid_opts.all? { |opt| options.keys.include?(opt) }
raise ArgumentError.new("Required data missing: #{(valid_opts - options.keys).map(&:inspect).join(", ")}")
end
# Figure out the template_uri
catalog_item = get_catalog_item( catalog_item_uri ).body
catalog_item[:Entity] = [ catalog_item[:Entity] ] if catalog_item[:Entity].is_a?(Hash)
catalog_item[:Link] = [ catalog_item[:Link] ] if catalog_item[:Link].is_a?(Hash)
options[:template_uri] = begin
catalog_item[:Entity].detect { |entity| entity[:type] == "application/vnd.vmware.vcloud.vAppTemplate+xml" }[:href]
rescue
raise RuntimeError.new("Unable to locate template uri for #{catalog_item_uri}")
end
customization_options = begin
customization_href = catalog_item[:Link].detect { |link| link[:type] == "application/vnd.vmware.vcloud.catalogItemCustomizationParameters+xml" }[:href]
get_customization_options( customization_href ).body
rescue
raise RuntimeError.new("Unable to get customization options for #{catalog_item_uri}")
end
# Check to see if we can set the password
if options[:password] and customization_options[:CustomizePassword] == "false"
raise ArgumentError.new("This catalog item (#{catalog_item_uri}) does not allow setting a password.")
end
# According to the docs if CustomizePassword is "true" then we NEED to set a password
if customization_options[:CustomizePassword] == "true" and ( options[:password].nil? or options[:password].empty? )
raise ArgumentError.new("This catalog item (#{catalog_item_uri}) requires a :password to instantiate.")
end
end
def generate_instantiate_vapp_template_request(options)
xml = Builder::XmlMarkup.new
xml.InstantiateVAppTemplateParams(xmlns.merge!(:name => options[:name], :"xml:lang" => "en")) {
xml.VAppTemplate(:href => options[:template_uri])
xml.InstantiationParams {
xml.ProductSection( :"xmlns:q1" => "http://www.vmware.com/vcloud/v0.8", :"xmlns:ovf" => "http://schemas.dmtf.org/ovf/envelope/1") {
if options[:password]
xml.Property( :xmlns => "http://schemas.dmtf.org/ovf/envelope/1", :"ovf:key" => "password", :"ovf:value" => options[:password] )
end
xml.Property( :xmlns => "http://schemas.dmtf.org/ovf/envelope/1", :"ovf:key" => "row", :"ovf:value" => options[:row] )
xml.Property( :xmlns => "http://schemas.dmtf.org/ovf/envelope/1", :"ovf:key" => "group", :"ovf:value" => options[:group] )
}
xml.VirtualHardwareSection( :"xmlns:q1" => "http://www.vmware.com/vcloud/v0.8" ) {
# # of CPUS
xml.Item( :xmlns => "http://schemas.dmtf.org/ovf/envelope/1" ) {
xml.InstanceID(1, :xmlns => "http://schemas.dmtf.org/wbem/wscim/1/cim-schema/2/CIM_ResourceAllocationSettingData")
xml.ResourceType(3, :xmlns => "http://schemas.dmtf.org/wbem/wscim/1/cim-schema/2/CIM_ResourceAllocationSettingData")
xml.VirtualQuantity(options[:cpus], :xmlns => "http://schemas.dmtf.org/wbem/wscim/1/cim-schema/2/CIM_ResourceAllocationSettingData")
}
# Memory
xml.Item( :xmlns => "http://schemas.dmtf.org/ovf/envelope/1" ) {
xml.InstanceID(2, :xmlns => "http://schemas.dmtf.org/wbem/wscim/1/cim-schema/2/CIM_ResourceAllocationSettingData")
xml.ResourceType(4, :xmlns => "http://schemas.dmtf.org/wbem/wscim/1/cim-schema/2/CIM_ResourceAllocationSettingData")
xml.VirtualQuantity(options[:memory], :xmlns => "http://schemas.dmtf.org/wbem/wscim/1/cim-schema/2/CIM_ResourceAllocationSettingData")
}
}
xml.NetworkConfigSection {
xml.NetworkConfig {
xml.NetworkAssociation( :href => options[:network_uri] )
}
}
}
}
end
end
class Real
include Shared
def instantiate_vapp_template(catalog_item_uri, options = {})
validate_instantiate_vapp_template_options(catalog_item_uri, options)
request(
:body => generate_instantiate_vapp_template_request(options),
:expects => 200,
:headers => {'Content-Type' => 'application/vnd.vmware.vcloud.instantiateVAppTemplateParams+xml'},
:method => 'POST',
:uri => options[:vdc_uri] + '/action/instantiatevAppTemplate',
:parse => true
)
end
end
class Mock
include Shared
def instantiate_vapp_template(catalog_item_uri, options = {})
validate_instantiate_vapp_template_options(catalog_item_uri, options)
catalog_item = mock_data.catalog_item_from_href(catalog_item_uri)
xml = nil
if vdc = mock_data.vdc_from_href(options[:vdc_uri])
if network = mock_data.network_from_href(options[:network_uri])
new_vm = MockVirtualMachine.new({ :name => options[:name], :ip => network.random_ip, :cpus => options[:cpus], :memory => options[:memory] }, vdc)
new_vm.disks.push(*catalog_item.disks.dup)
vdc.virtual_machines << new_vm
xml = generate_instantiate_vapp_template_response(new_vm)
end
end
if xml
mock_it 200, xml, {'Content-Type' => 'application/xml'}
else
mock_error 200, "401 Unauthorized"
end
end
private
def generate_instantiate_vapp_template_response(vapp)
builder = Builder::XmlMarkup.new
builder.VApp(xmlns.merge(
:href => vapp.href,
:type => "application/vnd.vmware.vcloud.vApp+xml",
:name => vapp.name,
:status => 0,
:size => 4
)) {
builder.Link(:rel => "up", :href => vapp._parent.href, :type => "application/vnd.vmware.vcloud.vdc+xml")
}
end
end
end
end
end

View File

@ -0,0 +1,42 @@
module Fog
module Vcloud
class Compute
class Real
def login
unauthenticated_request({
:expects => 200,
:headers => {
'Authorization' => authorization_header
},
:method => 'POST',
:parse => true,
:uri => login_uri
})
end
end
class Mock
def login
xml = Builder::XmlMarkup.new
mock_it 200,
xml.OrgList(xmlns) {
mock_data.organizations.each do |organization|
xml.Org( :type => "application/vnd.vmware.vcloud.org+xml", :href => organization.href, :name => organization.name )
end
},
{ 'Set-Cookie' => 'vcloud-token=fc020a05-21d7-4f33-9b2a-25d8cd05a44e; path=/',
'Content-Type' => 'application/vnd.vmware.vcloud.orgslist+xml' }
end
end
end
end
end

View File

@ -0,0 +1,23 @@
module Fog
module Vcloud
class Compute
class Real
basic_request :power_off, 202, 'POST'
end
class Mock
def power_off(vapp_uri)
if vapp = mock_data.virtual_machine_from_href(vapp_uri)
vapp.power_off!
builder = Builder::XmlMarkup.new
mock_it 200, builder.Task(xmlns)
else
mock_error 200, "401 Unauthorized"
end
end
end
end
end
end

View File

@ -0,0 +1,23 @@
module Fog
module Vcloud
class Compute
class Real
basic_request :power_on, 202, 'POST'
end
class Mock
def power_on(vapp_uri)
if vapp = mock_data.virtual_machine_from_href(vapp_uri)
vapp.power_on!
builder = Builder::XmlMarkup.new
mock_it 200, builder.Task(xmlns)
else
mock_error 200, "401 Unauthorized"
end
end
end
end
end
end

View File

@ -0,0 +1,11 @@
module Fog
module Vcloud
class Compute
class Real
basic_request :power_reset, 202, 'POST'
end
end
end
end

View File

@ -0,0 +1,11 @@
module Fog
module Vcloud
class Compute
class Real
basic_request :power_shutdown, 204, 'POST'
end
end
end
end

867
lib/fog/compute/vcloud.rb Normal file
View File

@ -0,0 +1,867 @@
require 'fog/core'
module Fog
module Vcloud
class Collection < Fog::Collection
def load(objects)
objects = [ objects ] if objects.is_a?(Hash)
super
end
def check_href!(opts = {})
unless href
if opts.is_a?(String)
t = Hash.new
t[:parent] = opts
opts = t
end
msg = ":href missing, call with a :href pointing to #{if opts[:message]
opts[:message]
elsif opts[:parent]
"the #{opts[:parent]} whos #{self.class.to_s.split('::').last.downcase} you want to enumerate"
else
"the resource"
end}"
raise Fog::Errors::Error.new(msg)
end
end
end
end
end
module Fog
module Vcloud
module MockDataClasses
class Base < Hash
def self.base_url=(url)
@base_url = url
end
self.base_url = "http://vcloud.example.com"
def self.base_url
@base_url
end
def first
raise "Don't do this"
end
def last
raise "Don't do this"
end
def initialize(data = {}, parent = nil)
@parent = parent
replace(data)
end
def _parent
@parent
end
def base_url
Base.base_url
end
def href
[base_url, self.class.name.split("::").last, object_id].join("/")
end
def inspect
"<#{self.class.name} #{object_id} data=#{super}>"
end
end
class MockData < Base
def organizations
@organizations ||= []
end
def organization_from_href(href)
find_href_in(href, organizations)
end
def all_vdcs
organizations.map(&:vdcs).flatten
end
def vdc_from_href(href)
find_href_in(href, all_vdcs)
end
def all_catalogs
all_vdcs.map(&:catalog).flatten
end
def catalog_from_href(href)
find_href_in(href, all_catalogs)
end
def all_catalog_items
all_catalogs.map(&:items).flatten
end
def catalog_item_from_href(href)
find_href_in(href, all_catalog_items)
end
def all_virtual_machines
all_vdcs.map(&:virtual_machines).flatten
end
def virtual_machine_from_href(href)
find_href_prefixed_in(href, all_virtual_machines)
end
def all_networks
all_vdcs.map(&:networks).flatten
end
def network_from_href(href)
find_href_in(href, all_networks)
end
def all_network_extensions
all_networks.map(&:extensions).flatten
end
def network_extension_from_href(href)
find_href_in(href, all_network_extensions)
end
def all_network_ip_collections
all_networks.map(&:ip_collection)
end
def network_ip_collection_from_href(href)
find_href_in(href, all_network_ip_collections)
end
def all_network_ips
all_network_ip_collections.map {|c| c.items.values }.flatten
end
def network_ip_from_href(href)
find_href_in(href, all_network_ips)
end
private
def find_href_in(href, objects)
objects.detect {|o| o.href == href }
end
def find_href_prefixed_in(href, objects)
objects.detect {|o| href =~ %r{^#{o.href}($|/)} }
end
end
class MockOrganization < Base
def name
self[:name]
end
def vdcs
@vdcs ||= []
end
end
class MockVdc < Base
def name
self[:name]
end
def storage_allocated
self[:storage_allocated] || 200
end
def storage_used
self[:storage_used] || 105
end
def cpu_allocated
self[:cpu_allocated] || 10000
end
def memory_allocated
self[:memory_allocated] || 20480
end
def catalog
@catalog ||= MockCatalog.new({}, self)
end
def networks
@networks ||= []
end
def virtual_machines
@virtual_machines ||= []
end
def task_list
@task_list ||= MockTaskList.new({}, self)
end
end
class MockTaskList < Base
def name
self[:name] || "Tasks List"
end
end
class MockCatalog < Base
def name
self[:name] || "Catalog"
end
def items
@items ||= []
end
end
class MockCatalogItem < Base
def name
self[:name]
end
def disks
@disks ||= MockVirtualMachineDisks.new(self)
end
def customization
@customization ||= MockCatalogItemCustomization.new({}, self)
end
def vapp_template
@vapp_template ||= MockCatalogItemVappTemplate.new({ :name => name }, self)
end
end
class MockCatalogItemCustomization < Base
def name
self[:name] || "Customization Options"
end
end
class MockCatalogItemVappTemplate < Base
def name
self[:name]
end
end
class MockNetwork < Base
def name
self[:name] || subnet
end
def subnet
self[:subnet]
end
def gateway
self[:gateway] || subnet_ips[1]
end
def netmask
self[:netmask] || subnet_ipaddr.mask
end
def dns
"8.8.8.8"
end
def features
[
{ :type => :FenceMode, :value => "isolated" }
]
end
def ip_collection
@ip_collection ||= MockNetworkIps.new({}, self)
end
def extensions
@extensions ||= MockNetworkExtensions.new({}, self)
end
def random_ip
usable_subnet_ips[rand(usable_subnet_ips.length)]
end
def usable_subnet_ips
subnet_ips[3..-2]
end
def address
subnet_ips.first
end
def broadcast
subnet_ips.last
end
private
def subnet_ipaddr
@ipaddr ||= IPAddr.new(subnet)
end
def subnet_ips
subnet_ipaddr.to_range.to_a.map(&:to_s)
end
end
class MockNetworkIps < Base
def items
@items ||= _parent.usable_subnet_ips.inject({}) do |out, subnet_ip|
out.update(subnet_ip => MockNetworkIp.new({ :ip => subnet_ip }, self))
end
end
def ordered_ips
items.values.sort_by {|i| i.ip.split(".").map(&:to_i) }
end
def name
"IP Addresses"
end
end
class MockNetworkIp < Base
def name
self[:name] || ip
end
def ip
self[:ip]
end
def used_by
self[:used_by] || _parent._parent._parent.virtual_machines.detect {|v| v.ip == ip }
end
def status
if used_by
"Assigned"
else
"Available"
end
end
end
class MockNetworkExtensions < Base
def name
_parent.name
end
def gateway
_parent.gateway
end
def broadcast
_parent.broadcast
end
def address
_parent.address
end
def type
self[:type] || "DMZ"
end
def vlan
object_id.to_s
end
def friendly_name
"#{name} (#{type}_#{object_id})"
end
end
class MockVirtualMachine < Base
def name
self[:name]
end
def ip
self[:ip]
end
def cpus
self[:cpus] || 1
end
def memory
self[:memory] || 1024
end
def disks
@disks ||= MockVirtualMachineDisks.new(self)
end
def status
self[:status] || 2
end
def power_off!
self[:status] = 2
end
def power_on!
self[:status] = 4
end
def size
disks.inject(0) {|s, d| s + d.vcloud_size }
end
def network_ip
if network = _parent.networks.detect {|n| n.ip_collection.items[ip] }
network.ip_collection.items[ip]
end
end
# from fog vcloud server's _compose_vapp_data
def to_configure_vapp_hash
{
:name => name,
:cpus => cpus,
:memory => memory,
:disks => disks.map {|d| { :number => d.address.to_s, :size => d.vcloud_size, :resource => d.vcloud_size.to_s } }
}
end
def href(purpose = :base)
case purpose
when :base
super()
when :power_on
super() + "/power/action/powerOn"
when :power_off
super() + "/power/action/powerOff"
end
end
end
class MockVirtualMachineDisks < Array
def initialize(parent = nil)
@parent = parent
end
def _parent
@parent
end
def <<(disk)
next_address = 0
disk_with_max_address = max {|a, b| a[:address] <=> b[:address] }
disk_with_max_address && next_address = disk_with_max_address.address + 1
disk[:address] ||= next_address
super(disk)
if (addresses = map {|d| d.address }).uniq.size != size
raise "Duplicate disk address in: #{addresses.inspect} (#{size})"
end
sort! {|a, b| a.address <=> b.address }
self
end
def at_address(address)
detect {|d| d.address == address }
end
end
class MockVirtualMachineDisk < Base
def size
self[:size].to_i
end
def vcloud_size
# kilobytes
size * 1024
end
def address
self[:address].to_i
end
end
end
end
end
module Fog
module Vcloud
class Model < Fog::Model
attr_accessor :loaded
alias_method :loaded?, :loaded
def reload
instance = super
@loaded = true
instance
end
def load_unless_loaded!
unless @loaded
reload
end
end
end
end
end
module Fog
module Vcloud
class Compute < Fog::Service
PATH = '/api/v1.0'
PORT = 443
SCHEME = 'https'
requires :vcloud_username, :vcloud_password, :vcloud_host
recognizes :vcloud_port, :vcloud_scheme, :vcloud_path
recognizes :provider # remove post deprecation
model_path 'fog/compute/models/vcloud'
model :catalog_item
model :catalog
model :ip
collection :ips
model :network
collection :networks
model :server
collection :servers
model :task
collection :tasks
model :vdc
collection :vdcs
request_path 'fog/compute/requests/vcloud'
request :clone_vapp
request :configure_network
request :configure_network_ip
request :configure_vapp
request :delete_vapp
request :get_catalog
request :get_catalog_item
request :get_customization_options
request :get_network
request :get_network_ip
request :get_network_ips
request :get_network_extensions
request :get_organization
request :get_task
request :get_task_list
request :get_vapp
request :get_vapp_template
request :get_vdc
request :instantiate_vapp_template
request :login
request :power_off
request :power_on
request :power_reset
request :power_shutdown
module Shared
def default_organization_uri
@default_organization_uri ||= begin
unless @login_results
do_login
end
case @login_results.body[:Org]
when Array
@login_results.body[:Org].first[:href]
when Hash
@login_results.body[:Org][:href]
else
nil
end
end
end
# login handles the auth, but we just need the Set-Cookie
# header from that call.
def do_login
@login_results = login
@cookie = @login_results.headers['Set-Cookie']
end
def ensure_unparsed(uri)
if uri.is_a?(String)
uri
else
uri.to_s
end
end
def xmlns
{ "xmlns" => "http://www.vmware.com/vcloud/v0.8",
"xmlns:xsi" => "http://www.w3.org/2001/XMLSchema-instance",
"xmlns:xsd" => "http://www.w3.org/2001/XMLSchema" }
end
end
class Mock
include Shared
include MockDataClasses
def self.base_url
"https://fakey.com/api/v0.8b-ext2.6"
end
def self.data_reset
@mock_data = nil
end
def self.data( base_url = self.base_url )
@mock_data ||= MockData.new.tap do |vcloud_mock_data|
vcloud_mock_data.organizations << MockOrganization.new(:name => "Boom Inc.").tap do |mock_organization|
mock_organization.vdcs << MockVdc.new(:name => "Boomstick").tap do |mock_vdc|
mock_vdc.catalog.items << MockCatalogItem.new(:name => "Item 0").tap do |mock_catalog_item|
mock_catalog_item.disks << MockVirtualMachineDisk.new(:size => 25 * 1024)
end
mock_vdc.catalog.items << MockCatalogItem.new(:name => "Item 1").tap do |mock_catalog_item|
mock_catalog_item.disks << MockVirtualMachineDisk.new(:size => 25 * 1024)
end
mock_vdc.catalog.items << MockCatalogItem.new(:name => "Item 2").tap do |mock_catalog_item|
mock_catalog_item.disks << MockVirtualMachineDisk.new(:size => 25 * 1024)
end
mock_vdc.networks << MockNetwork.new({ :subnet => "1.2.3.0/24" }, mock_vdc)
mock_vdc.networks << MockNetwork.new({ :subnet => "4.5.6.0/24" }, mock_vdc)
mock_vdc.virtual_machines << MockVirtualMachine.new({ :name => "Broom 1", :ip => "1.2.3.3" }, mock_vdc)
mock_vdc.virtual_machines << MockVirtualMachine.new({ :name => "Broom 2", :ip => "1.2.3.4" }, mock_vdc)
mock_vdc.virtual_machines << MockVirtualMachine.new({ :name => "Email!", :ip => "1.2.3.10" }, mock_vdc)
end
mock_organization.vdcs << MockVdc.new(:name => "Rock-n-Roll", :storage_allocated => 150, :storage_used => 40, :cpu_allocated => 1000, :memory_allocated => 2048).tap do |mock_vdc|
mock_vdc.networks << MockNetwork.new({ :subnet => "7.8.9.0/24" }, mock_vdc)
mock_vdc.virtual_machines << MockVirtualMachine.new({ :name => "Master Blaster", :ip => "7.8.9.10" }, mock_vdc)
end
end
vcloud_mock_data.organizations.each do |organization|
organization.vdcs.each do |vdc|
vdc.virtual_machines.each do |virtual_machine|
virtual_machine.disks << MockVirtualMachineDisk.new(:size => 25 * 1024)
virtual_machine.disks << MockVirtualMachineDisk.new(:size => 50 * 1024)
end
end
end
end
end
def initialize(options = {})
require 'builder'
require 'fog/core/parser'
end
def mock_data
Fog::Vcloud::Compute::Mock.data
end
def mock_error(expected, status, body='', headers={})
raise Excon::Errors::Unauthorized.new("Expected(#{expected}) <=> Actual(#{status})")
end
def mock_it(status, mock_data, mock_headers = {})
response = Excon::Response.new
#Parse the response body into a hash
if mock_data.empty?
response.body = mock_data
else
document = Fog::ToHashDocument.new
parser = Nokogiri::XML::SAX::PushParser.new(document)
parser << mock_data
parser.finish
response.body = document.body
end
response.status = status
response.headers = mock_headers
response
end
end
class Real
include Shared
class << self
def basic_request(*args)
self.class_eval <<-EOS, __FILE__,__LINE__
def #{args[0]}(uri)
request({
:expects => #{args[1] || 200},
:method => '#{args[2] || 'GET'}',
:headers => #{args[3] ? args[3].inspect : '{}'},
:body => '#{args[4] ? args[4] : ''}',
:parse => true,
:uri => uri })
end
EOS
end
def unauthenticated_basic_request(*args)
self.class_eval <<-EOS, __FILE__,__LINE__
def #{args[0]}(uri)
unauthenticated_request({
:expects => #{args[1] || 200},
:method => '#{args[2] || 'GET'}',
:headers => #{args[3] ? args[3].inspect : '{}'},
:parse => true,
:uri => uri })
end
EOS
end
end
def initialize(options = {})
require 'builder'
require 'fog/core/parser'
@connections = {}
@persistent = options[:persistent]
@host = options[:vcloud_host] || Fog::Vcloud::Compute::HOST
@path = options[:vcloud_path] || Fog::Vcloud::Compute::PATH
@port = options[:vcloud_port] || Fog::Vcloud::Compute::PORT
@scheme = options[:vcloud_scheme] || Fog::Vcloud::Compute::SCHEME
end
def default_organization_uri
@default_organization_uri ||= begin
unless @login_results
do_login
end
case @login_results.body[:Org]
when Array
@login_results.body[:Org].first[:href]
when Hash
@login_results.body[:Org][:href]
else
nil
end
end
end
def reload
@connections.each_value { |k,v| v.reset if v }
end
# If the cookie isn't set, do a get_organizations call to set it
# and try the request.
# If we get an Unauthorized error, we assume the token expired, re-auth and try again
def request(params)
unless @cookie
do_login
end
begin
do_request(params)
rescue Excon::Errors::Unauthorized => e
do_login
do_request(params)
end
end
private
def ensure_parsed(uri)
if uri.is_a?(String)
URI.parse(uri)
else
uri
end
end
# Don't need to set the cookie for these or retry them if the cookie timed out
def unauthenticated_request(params)
do_request(params)
end
# Use this to set the Authorization header for login
def authorization_header
"Basic #{Base64.encode64("#{@username}:#{@password}").chomp!}"
end
# login handles the auth, but we just need the Set-Cookie
# header from that call.
def do_login
@login_results = login
@cookie = @login_results.headers['Set-Cookie']
end
# Actually do the request
def do_request(params)
# Convert the uri to a URI if it's a string.
if params[:uri].is_a?(String)
params[:uri] = URI.parse(params[:uri])
end
host_url = "#{params[:uri].scheme}://#{params[:uri].host}#{params[:uri].port ? ":#{params[:uri].port}" : ''}"
# Hash connections on the host_url ... There's nothing to say we won't get URI's that go to
# different hosts.
@connections[host_url] ||= Fog::Connection.new(host_url, @persistent)
# Set headers to an empty hash if none are set.
headers = params[:headers] || {}
# Add our auth cookie to the headers
if @cookie
headers.merge!('Cookie' => @cookie)
end
# Make the request
response = @connections[host_url].request({
:body => params[:body] || '',
:expects => params[:expects] || 200,
:headers => headers,
:method => params[:method] || 'GET',
:path => params[:uri].path
})
# Parse the response body into a hash
#puts response.body
unless response.body.empty?
if params[:parse]
document = Fog::ToHashDocument.new
parser = Nokogiri::XML::SAX::PushParser.new(document)
parser << response.body
parser.finish
response.body = document.body
end
end
response
end
end
end
end
end

View File

@ -21,6 +21,7 @@ require 'fog/providers/ninefold'
require 'fog/providers/rackspace'
require 'fog/providers/slicehost'
require 'fog/providers/storm_on_demand'
require 'fog/providers/vcloud'
require 'fog/providers/virtual_box'
require 'fog/providers/voxel'
require 'fog/providers/zerigo'

View File

@ -0,0 +1,11 @@
require 'fog/core'
module Fog
module Vcloud
extend Fog::Provider
service(:compute, 'compute/vcloud')
end
end

22
spec/vcloud/bin_spec.rb Normal file
View File

@ -0,0 +1,22 @@
require 'vcloud/spec_helper'
describe 'Vcloud' do
it { Vcloud.should be_available }
it { Vcloud.should have_at_least(1).services }
describe "when indexing it like an array" do
describe "with a service that exists" do
it "should return something when indexed with a configured service" do
Vcloud[:compute].should_not be_nil
end
end
describe "with a service that does not exist" do
it "should raise an ArgumentError" do
lambda {Vcloud[:foozle]}.should raise_error(ArgumentError)
end
end
end
end

View File

@ -0,0 +1,33 @@
require 'vcloud/spec_helper'
if Fog.mocking?
describe "Fog::Vcloud::Compute::Ip", :type => :mock_vcloud_model do
subject { @vcloud }
describe :class do
subject { Fog::Vcloud::Compute::Ip }
it { should have_identity(:href) }
it { should have_only_these_attributes([:href, :name, :status, :server, :id]) }
end
context "with no uri" do
subject { Fog::Vcloud::Compute::Ip.new() }
it { should have_all_attributes_be_nil }
end
context "as a collection member" do
subject { @ip = @vcloud.vdcs[0].networks[0].ips[0] }
it { should be_an_instance_of(Fog::Vcloud::Compute::Ip) }
its(:name) { should == @mock_data.network_ip_from_href(@ip.href).name }
its(:status) { should == @mock_data.network_ip_from_href(@ip.href).status }
its(:server) { should == @mock_data.network_ip_from_href(@ip.href).used_by.name }
end
end
else
end

View File

@ -0,0 +1,28 @@
require 'vcloud/spec_helper'
if Fog.mocking?
describe "Fog::Vcloud::Compute::Ips", :type => :mock_vcloud_model do
subject { @vcloud }
it { should respond_to(:ips) }
describe :class do
subject { @vcloud.vdcs[0].networks[0].ips.class }
its(:model) { should == Fog::Vcloud::Compute::Ip }
end
describe :ips do
subject { @vcloud.vdcs[0].networks[0].ips.reload }
it { should_not respond_to(:create) }
it { should be_an_instance_of(Fog::Vcloud::Compute::Ips) }
its(:length) { should == 252 }
it { should have_members_of_the_right_model }
end
end
else
end

View File

@ -0,0 +1,70 @@
require 'vcloud/spec_helper'
if Fog.mocking?
describe "Fog::Vcloud::Compute::Network", :type => :mock_vcloud_model do
subject { @vcloud }
describe :class do
subject { Fog::Vcloud::Compute::Network }
it { should have_identity(:href) }
it { should have_only_these_attributes([:href, :name, :features, :links, :type, :gateway, :broadcast, :address, :extension_href, :network_type, :vlan, :friendly_name]) }
end
context "with no uri" do
subject { Fog::Vcloud::Compute::Network.new() }
it { should have_all_attributes_be_nil }
end
context "as a collection member" do
subject { @vcloud.vdcs[0].networks[0].reload }
it { should be_an_instance_of(Fog::Vcloud::Compute::Network) }
its(:href) { should == @mock_network.href }
its(:identity) { should == @mock_network.href }
its(:name) { should == @mock_network.name }
its(:type) { should == "application/vnd.vmware.vcloud.network+xml" }
its(:gateway) { should == @mock_network.gateway }
its(:broadcast) { should == @mock_network.broadcast }
its(:address) { should == @mock_network.address }
its(:extension_href) { should == @mock_network.extensions.href }
its(:network_type) { should == @mock_network.extensions.type }
its(:vlan) { should == @mock_network.extensions.vlan }
its(:friendly_name) { should == @mock_network.extensions.friendly_name }
it { should have(1).features }
describe :features do
let(:feature) { subject.features.first }
specify { feature.should be_an_instance_of(Array) }
specify { feature.last.should == @mock_network.features[0][:value] }
end
it { should have(2).links }
describe :links do
context "[0]" do
let(:link) { subject.links[0] }
specify { link[:rel].should == "down" }
specify { link[:href].should == @mock_network_ip_collection.href }
specify { link[:type].should == "application/xml" }
specify { link[:name].should == @mock_network_ip_collection.name }
end
context "[1]" do
let(:link) { subject.links[1] }
specify { link[:rel].should == "down" }
specify { link[:href].should == @mock_network.extensions.href }
specify { link[:type].should == "application/xml" }
specify { link[:name].should == @mock_network.name }
end
end
end
end
else
end

View File

@ -0,0 +1,27 @@
require 'vcloud/spec_helper'
if Fog.mocking?
describe "Fog::Vcloud::Compute::Networks", :type => :mock_vcloud_model do
subject { @vcloud }
it { should respond_to(:networks) }
describe :class do
subject { @vcloud.networks.class }
its(:model) { should == Fog::Vcloud::Compute::Network }
end
describe :networks do
subject { @vcloud.vdcs[0].networks }
it { should_not respond_to(:create) }
it { should be_an_instance_of(Fog::Vcloud::Compute::Networks) }
its(:length) { should == 2 }
it { should have_members_of_the_right_model }
end
end
else
end

View File

@ -0,0 +1,62 @@
require 'vcloud/spec_helper'
if Fog.mocking?
describe "Fog::Vcloud::Compute::Vdc", :type => :mock_vcloud_model do
subject { @vcloud }
describe :class do
subject { Fog::Vcloud::Compute::Server }
it { should have_identity(:href) }
it { should have_only_these_attributes([:href, :type, :name, :status, :network_connections, :os, :virtual_hardware, :storage_size, :links]) }
end
context "with no uri" do
subject { Fog::Vcloud::Compute::Server.new() }
it { should have_all_attributes_be_nil }
end
context "as a collection member" do
subject { @vcloud.vdcs[0].servers.first }
its(:href) { should == @mock_vm.href }
its(:identity) { should == @mock_vm.href }
its(:name) { should == @mock_vm.name }
its(:cpus) { should == { :count => @mock_vm.cpus, :units => nil } }
its(:memory) { should == { :amount => @mock_vm.memory, :units => nil } }
its(:disks) { should == @mock_vm.to_configure_vapp_hash[:disks] }
describe "question methods" do
describe "#ready?" do
before { subject.power_off }
it { should be_ready }
end
describe "#on?" do
before { subject.power_on }
it { should be_on }
end
describe "#off?" do
before { subject.power_off }
it { should be_off }
end
end
end
context "as a new server without all info" do
before { @vcloud.return_vapp_as_creating! "test123" }
subject { @vcloud.vdcs[0].servers.create(@mock_catalog_item.href, { :name => "test123", :row => "foo", :group => "bar", :network_uri => @mock_network.href }) }
its(:cpus) { should be_nil }
its(:memory) { should be_nil }
its(:disks) { should == [] }
end
end
else
end

View File

@ -0,0 +1,60 @@
require 'vcloud/spec_helper'
if Fog.mocking?
describe "Fog::Vcloud::Compute::Vdc", :type => :mock_vcloud_model do
subject { @vcloud }
it { should respond_to(:get_vdc) }
describe :class do
subject { Fog::Vcloud::Compute::Vdc }
it { should have_identity(:href) }
it { should have_only_these_attributes([:href, :name, :type, :description, :other_links, :compute_capacity, :storage_capacity, :available_networks,
:resource_entities, :deployed_vm_quota, :instantiated_vm_quota]) }
end
context "with no uri" do
subject { Fog::Vcloud::Compute::Vdc.new() }
it { should have_all_attributes_be_nil }
end
context "as a collection member" do
subject { @vcloud.vdcs[0].reload }
its(:href) { should == @mock_vdc.href }
its(:identity) { should == @mock_vdc.href }
its(:name) { should == @mock_vdc.name }
its(:other_links) { should have(4).items }
its(:resource_entities) { should have(3).items }
its(:available_networks) { should have(2).items }
its(:compute_capacity) { should == {:Memory =>
{:Allocated => @mock_vdc.memory_allocated.to_s, :Units => "bytes * 2^20"},
:DeployedVmsQuota =>
{:Limit => "-1", :Used => "-1"},
:InstantiatedVmsQuota =>
{:Limit => "-1", :Used => "-1"},
:Cpu =>
{:Allocated => @mock_vdc.cpu_allocated.to_s, :Units => "hz * 10^6"}} }
its(:storage_capacity) { should == {:Allocated => @mock_vdc.storage_allocated.to_s, :Used => @mock_vdc.storage_used.to_s, :Units => "bytes * 10^9"} }
its(:deployed_vm_quota) { should == nil }
its(:instantiated_vm_quota) { should == nil }
its(:networks) { should have(2).networks }
its(:servers) { should have(3).servers }
#FIXME: need to mock tasks related requests first
#its(:tasks) { should have(0).tasks }
#FIXME: need to mock catalog related requests first
#its(:catalog) { should have(0).entries }
end
end
else
end

View File

@ -0,0 +1,28 @@
require 'vcloud/spec_helper'
if Fog.mocking?
describe "Fog::Vcloud::Compute::Vdcs", :type => :mock_vcloud_model do
subject { @vcloud }
it { should respond_to(:vdcs) }
describe :class do
subject { @vcloud.vdcs.class }
its(:model) { should == Fog::Vcloud::Compute::Vdc }
end
describe :vdcs do
subject { @vcloud.vdcs }
it { should_not respond_to(:create) }
it { should be_an_instance_of(Fog::Vcloud::Compute::Vdcs) }
its(:length) { should == 2 }
it { should have_members_of_the_right_model }
its(:organization_uri) { should == @mock_organization.href }
end
end
else
end

View File

@ -0,0 +1,47 @@
require 'vcloud/spec_helper'
if Fog.mocking?
describe "Fog::Vcloud", :type => :mock_vcloud_request do
subject { @vcloud }
it { should respond_to(:configure_network_ip) }
describe "#configure_network_ip" do
let(:original_network_ip) { @vcloud.get_network_ip(@mock_network_ip.href).body }
let(:network_ip_data) do
{
:id => original_network_ip[:Id],
:href => original_network_ip[:Href],
:name => original_network_ip[:Name],
:status => original_network_ip[:Status],
:server => original_network_ip[:Server]
}
end
context "with a valid network ip uri" do
subject { @vcloud.configure_network_ip(@mock_network_ip.href, network_ip_data) }
it_should_behave_like "all responses"
describe "#body" do
subject { @vcloud.configure_network_ip(@mock_network_ip.href, network_ip_data).body }
#Stuff that shouldn't change
its(:Href) { should == @mock_network_ip.href }
its(:Id) { should == @mock_network_ip.object_id.to_s }
its(:Name) { should == @mock_network_ip.ip }
its(:Status) { should == @mock_network_ip.status }
end
end
context "with a nodes uri that doesn't exist" do
subject { lambda { @vcloud.configure_network_ip(URI.parse('https://www.fakey.c/piv8vc99'), network_ip_data) } }
it_should_behave_like "a request for a resource that doesn't exist"
end
end
end
else
end

View File

@ -0,0 +1,71 @@
require 'vcloud/spec_helper'
shared_examples_for "a successful configure vapp" do
specify { after_vapp_data.should == new_vapp_data }
describe "#body" do
its(:body) { should == '' }
end
describe "#headers" do
its(:headers) { should include("Location") }
end
end
if Fog.mocking?
describe "Fog::Vcloud", :type => :mock_vcloud_request do
subject { @vcloud }
it { should respond_to(:configure_vapp) }
let(:original_vapp_data) { vapp_data.dup }
let(:vapp_data) { @mock_vm.to_configure_vapp_hash }
let(:changed_vapp_data) { {} }
let(:new_vapp_data) { vapp_data.update(changed_vapp_data) }
let(:after_vapp_data) { @mock_vm.to_configure_vapp_hash }
describe "#configure_vapp" do
context "with a valid vapp uri" do
before { original_vapp_data; subject }
subject { @vcloud.configure_vapp(@mock_vm.href, new_vapp_data) }
context "when changing nothing" do
it_should_behave_like "a successful configure vapp"
end
context "when changing CPUs" do
let(:changed_vapp_data) { { :cpus => @mock_vm.cpus * 2 } }
it_should_behave_like "a successful configure vapp"
end
context "when changing memory" do
let(:changed_vapp_data) { { :memory => @mock_vm.memory * 2 } }
it_should_behave_like "a successful configure vapp"
end
context "when removing a disk" do
let(:changed_vapp_data) { { :disks => original_vapp_data[:disks][0...1] } }
it_should_behave_like "a successful configure vapp"
end
context "when adding a disk" do
let(:changed_vapp_data) { { :disks => original_vapp_data[:disks] + [{ :number => "5", :size => 10 * 1024 * 1024, :resource => (10 * 1024 * 1024).to_s }] } }
it_should_behave_like "a successful configure vapp"
end
end
context "with an internet_services_uri that doesn't exist" do
subject { lambda { @vcloud.configure_vapp(URI.parse('https://www.fakey.c/piv8vc99'), new_vapp_data) } }
it_should_behave_like "a request for a resource that doesn't exist"
end
end
end
else
end

View File

@ -0,0 +1,73 @@
require 'vcloud/spec_helper'
shared_examples_for "a failed vapp deletion" do
it "should not change the mock data" do
expect { subject }.to change { @mock_vdc.virtual_machines.count }.by(0)
end
it "should not change the model data" do
expect { subject }.to change { vdc.reload.servers.reload.count }.by(0)
end
describe "#body" do
its(:body) { should == '' }
end
describe "#headers" do
its(:headers) { should_not include("Location") }
end
end
#FIXME: Make this more sane with rspec2
if Fog.mocking?
describe "Fog::Vcloud", :type => :mock_vcloud_request do
subject { @vcloud }
it { should respond_to(:delete_vapp) }
describe "#delete_vapp" do
context "with a valid vapp uri" do
subject { @vcloud.delete_vapp(@mock_vm.href) }
let(:vdc) { @vcloud.vdcs.first }
context "when there are no internet service nodes attached" do
it_should_behave_like("all delete responses")
it "should change the mock data" do
expect { subject }.to change { @mock_vdc.virtual_machines.count }.by(-1)
end
it "should change the model data" do
expect { subject }.to change { vdc.reload.servers.reload.count }.by(-1)
end
describe "#body" do
its(:body) { should == '' }
end
describe "#headers" do
its(:headers) { should include("Location") }
end
end
context "when the VM is powered on" do
before do
@mock_vm.power_on!
end
it_should_behave_like "all delete responses"
it_should_behave_like "a failed vapp deletion"
end
end
context "with a vapp uri that doesn't exist" do
subject { lambda { @vcloud.delete_vapp(URI.parse('https://www.fakey.c/piv8vc99')) } }
it_should_behave_like "a request for a resource that doesn't exist"
end
end
end
else
end

View File

@ -0,0 +1,62 @@
require 'vcloud/spec_helper'
if Fog.mocking?
describe "Fog::Vcloud", :type => :mock_vcloud_request do
subject { @vcloud }
it { should respond_to(:get_catalog_item) }
describe "#get_catalog_item" do
context "with a valid catalog_item_uri" do
before { @catalog_item = @vcloud.get_catalog_item(@vcloud.vdcs.first.catalog.first.href) }
subject { @catalog_item }
let(:mock_data_catalog_item) { @mock_data.catalog_item_from_href(@catalog_item.body[:href]) }
it_should_behave_like "all responses"
it { should have_headers_denoting_a_content_type_of("application/vnd.vmware.vcloud.catalogItem+xml") }
describe "#body" do
subject { @catalog_item.body }
it { should have(8).items }
it_should_behave_like("it has the standard vcloud v0.8 xmlns attributes") # 3 keys
its(:name) { should == "Item 0" }
it { should include(:Entity) }
it { should include(:Link) }
it { should include(:Property) }
describe "Entity" do
subject { @catalog_item.body[:Entity] }
let(:mock_data_catalog_item) { @mock_data.catalog_item_from_href(@catalog_item.body[:href]) }
it { should have(3).items }
its(:name) { should == mock_data_catalog_item.name }
its(:type) { should == "application/vnd.vmware.vcloud.vAppTemplate+xml" }
its(:href) { should == mock_data_catalog_item.vapp_template.href }
end
describe "Link" do
subject { @catalog_item.body[:Link] }
it { should have(4).items }
its(:rel) { should == "down" }
its(:href) { should == mock_data_catalog_item.customization.href }
its(:name) { should == mock_data_catalog_item.customization.name }
its(:type) { should == "application/vnd.vmware.vcloud.catalogItemCustomizationParameters+xml" }
end
end
end
context "with a catalog uri that doesn't exist" do
subject { lambda { @vcloud.get_catalog(URI.parse('https://www.fakey.com/api/v0.8/vdc/999/catalog')) } }
it_should_behave_like("a request for a resource that doesn't exist")
end
end
end
end

View File

@ -0,0 +1,44 @@
require 'vcloud/spec_helper'
if Fog.mocking?
describe "Fog::Vcloud", :type => :mock_vcloud_request do
subject { @vcloud }
it { should respond_to(:get_catalog) }
describe "#get_catalog" do
context "with a valid vdc catalog_uri" do
before { @catalog = @vcloud.get_catalog(@mock_vdc.catalog.href) }
subject { @catalog }
it_should_behave_like "all responses"
it { should have_headers_denoting_a_content_type_of("application/vnd.vmware.vcloud.catalog+xml") }
describe "#body" do
subject { @catalog.body }
it { should have(7).items }
it_should_behave_like "it has the standard vcloud v0.8 xmlns attributes" # 3 keys
its(:name) { should == @mock_vdc.catalog.name }
it { should include(:CatalogItems) }
describe "CatalogItems" do
subject { @catalog.body[:CatalogItems] }
it { should have(1).items }
end
end
end
context "with a catalog uri that doesn't exist" do
subject { lambda { @vcloud.get_catalog(URI.parse('https://www.fakey.com/api/v0.8/vdc/999/catalog')) } }
it_should_behave_like "a request for a resource that doesn't exist"
end
end
end
else
end

View File

@ -0,0 +1,39 @@
require 'vcloud/spec_helper'
if Fog.mocking?
describe "Fog::Vcloud", :type => :mock_vcloud_request do
subject { @vcloud }
it { should respond_to(:get_customization_options) }
describe "#get_customization_options" do
context "with a valid catalog_item customizations uri" do
let(:catalog_item) { @vcloud.get_catalog_item(@vcloud.vdcs.first.catalog.first.href) }
before { @customization_options = @vcloud.get_customization_options(catalog_item.body[:Link][:href]) }
subject { @customization_options }
it_should_behave_like "all responses"
it { should have_headers_denoting_a_content_type_of("application/vnd.vmware.vcloud.catalogItemCustomizationParameters+xml") }
describe "#body" do
subject { @customization_options.body }
it { should have(5).items }
it_should_behave_like "it has the standard vcloud v0.8 xmlns attributes" # 3 keys
specify { subject[:CustomizeNetwork].should == "true" }
specify { subject[:CustomizePassword].should == "false" }
end
end
context "with a catalog uri that doesn't exist" do
subject { lambda { @vcloud.get_catalog(URI.parse('https://www.fakey.com/api/v0.8/vdc/999/catalog')) } }
it_should_behave_like "a request for a resource that doesn't exist"
end
end
end
else
end

View File

@ -0,0 +1,39 @@
require 'vcloud/spec_helper'
if Fog.mocking?
describe "Fog::Vcloud", :type => :mock_vcloud_request do
subject { @vcloud }
it { should respond_to(:get_network_ip) }
describe "#get_network_ip" do
context "with a valid ip_uri" do
before do
@ip = @vcloud.get_network_ip(@mock_network_ip.href)
end
subject { @ip }
it_should_behave_like "all responses"
it { should have_headers_denoting_a_content_type_of("application/vnd.vmware.vcloud.ip+xml") }
describe "#body" do
subject { @ip.body }
its(:Name) { should == @mock_network_ip.name }
its(:Href) { should == @mock_network_ip.href }
its(:Id) { should == @mock_network_ip.object_id.to_s }
its(:Status) { should == @mock_network_ip.status }
end
end
context "with an ip_uri that doesn't exist" do
subject { lambda { @vcloud.get_network_ip('https://www.fakey.c/piv89') } }
it_should_behave_like "a request for a resource that doesn't exist"
end
end
end
else
end

View File

@ -0,0 +1,56 @@
require 'vcloud/spec_helper'
if Fog.mocking?
describe "Fog::Vcloud", :type => :mock_vcloud_request do
subject { @vcloud }
it { should respond_to(:get_network_ips) }
describe "#get_network_ips" do
context "with a valid VDC network ips_uri" do
before { @ips = @vcloud.get_network_ips(@mock_network_ip_collection.href) }
subject { @ips }
it_should_behave_like "all responses"
it { should have_headers_denoting_a_content_type_of("application/vnd.vmware.vcloud.ipAddressesList+xml") }
describe "#body" do
subject { @ips.body }
it { should have(1).item }
context "[:IpAddress]" do
subject { @ips.body[:IpAddress] }
# Note the real TMRK API returns only "assigned" ips currently
# This is a bug they've slated to fix in the next release.
it { should have(252).addresses }
end
context "one we know is assigned" do
let(:address) { @ips.body[:IpAddress][0] }
specify { address.should have(5).keys }
specify { address[:Status].should == "Assigned" }
specify { address[:Server].should == "Broom 1" }
specify { address[:Name].should == "1.2.3.3" }
end
context "one we know is not assigned" do
let(:address) { @ips.body[:IpAddress][100] }
specify { address.should have(4).keys }
specify { address[:Status].should == "Available" }
specify { address.has_key?(:Server).should be_false }
specify { address[:Name].should == "1.2.3.103" }
end
end
end
context "with a network ips uri that doesn't exist" do
subject { lambda { @vcloud.get_network_ips(URI.parse('https://www.fakey.c/piv8vc99')) } }
it_should_behave_like("a request for a resource that doesn't exist")
end
end
end
end

View File

@ -0,0 +1,49 @@
require 'vcloud/spec_helper'
if Fog.mocking?
describe "Fog::Vcloud", :type => :mock_vcloud_request do
subject { @vcloud }
it { should respond_to(:get_network) }
describe "#get_network" do
context "with a valid network uri" do
before { @network = @vcloud.get_network(@mock_network.href) }
subject { @network }
it_should_behave_like "all responses"
it { should have_headers_denoting_a_content_type_of("application/vnd.vmware.vcloud.network+xml") }
describe "#body" do
subject { @network.body }
it { should have(9).keys }
it_should_behave_like "it has the standard vcloud v0.8 xmlns attributes" # 3 keys
its(:type) { should == "application/vnd.vmware.vcloud.network+xml" }
its(:Features) { should == @mock_network.features.map {|f| { f[:type] => f[:value] } }.first }
its(:href) { should == @mock_network.href }
its(:name) { should == @mock_network.name }
its(:Configuration) { should == { :Gateway => @mock_network.gateway, :Netmask => @mock_network.netmask } }
its(:Link) { should ==
[{:type => "application/xml",
:rel => "down",
:href => @mock_network_ip_collection.href,
:name => "IP Addresses"},
{:type => "application/xml",
:rel => "down",
:href => @mock_network_extensions.href,
:name => @mock_network_extensions.name}]}
end
end
context "with a network uri that doesn't exist" do
subject { lambda { @vcloud.get_network(URI.parse('https://www.fakey.com/api/v0.8/network/999')) } }
it_should_behave_like "a request for a resource that doesn't exist"
end
end
end
else
end

View File

@ -0,0 +1,52 @@
require 'vcloud/spec_helper'
if Fog.mocking?
describe Fog::Vcloud, :type => :mock_vcloud_request do
subject { @vcloud }
it { should respond_to(:get_organization) }
describe "#get_organization" do
context "with a valid organization uri" do
before { @organization = @vcloud.get_organization(@vcloud.default_organization_uri) }
subject { @organization }
it_should_behave_like "all responses"
it { should have_headers_denoting_a_content_type_of("application/vnd.vmware.vcloud.org+xml") }
describe "#body" do
subject { @organization.body }
let(:links) { subject[:Link] }
it { should have(6).keys }
it_should_behave_like "it has the standard vcloud v0.8 xmlns attributes" # 3 keys
it { should have_key_with_value(:href, @mock_organization.href)}
it { should have_key_with_value(:name, @mock_organization.name) }
it { should have_key_with_array(:Link, @mock_organization.vdcs.map { |vdc|
[{ :type => "application/vnd.vmware.vcloud.vdc+xml",
:href => vdc.href,
:name => vdc.name,
:rel => "down" },
{ :type => "application/vnd.vmware.vcloud.catalog+xml",
:href => vdc.catalog.href,
:name => vdc.catalog.name,
:rel => "down" },
{ :type => "application/vnd.vmware.vcloud.tasksList+xml",
:href => vdc.task_list.href,
:name => vdc.task_list.name,
:rel => "down" }]
}.flatten) }
end
end
context "with an organization uri that doesn't exist" do
subject { lambda { @vcloud.get_organization(URI.parse('https://www.fakey.com/api/v0.8/org/999')) } }
it_should_behave_like("a request for a resource that doesn't exist")
end
end
end
end

View File

@ -0,0 +1,125 @@
require 'vcloud/spec_helper'
if Fog.mocking?
describe "Fog::Vcloud", :type => :mock_vcloud_request do
subject { @vcloud }
it { should respond_to(:get_vapp) }
describe "#get_vapp" do
context "with a valid vapp_uri" do
before { @vapp = @vcloud.get_vapp(@mock_vm.href) }
subject { @vapp }
let(:vapp_id) { @vapp.body[:href].split("/").last }
it_should_behave_like "all responses"
it { should have_headers_denoting_a_content_type_of("application/vnd.vmware.vcloud.vApp+xml") }
describe "#body" do
subject { @vapp.body }
specify { subject.keys.sort_by(&:to_s).should == [:Link, :NetworkConnectionSection,
:OperatingSystemSection, :VirtualHardwareSection,
:href, :name,
:size, :status,
:type, :xmlns,
:xmlns_xsd, :xmlns_xsi] }
it_should_behave_like("it has the standard vcloud v0.8 xmlns attributes") # 3 keys
its(:href) { should == @mock_vm.href }
its(:name) { should == @mock_vm.name }
its(:status) { should == @mock_vm.status.to_s }
its(:size) { should == (@mock_vm.disks.inject(0) {|s, d| s += d[:size].to_i } * 1024).to_s }
describe "Link" do
subject { @vapp.body[:Link] }
its(:rel) { should == "up" }
its(:type) { should == "application/vnd.vmware.vcloud.vdc+xml" }
its(:href) { should == @mock_vdc.href }
end
describe "NetworkConnectionSection" do
subject { @vapp.body[:NetworkConnectionSection] }
it { should include(:NetworkConnection) }
describe "NetworkConnection" do
subject { @vapp.body[:NetworkConnectionSection][:NetworkConnection] }
its(:IpAddress) { should == @mock_vm.ip }
end
end
describe "OperatingSystemSection" do
subject { @vapp.body[:OperatingSystemSection] }
its(:Info) { should == "The kind of installed guest operating system" }
its(:Description) { should == "Red Hat Enterprise Linux 5 (64-bit)" }
end
describe "VirtualHardwareSection" do
subject { @vapp.body[:VirtualHardwareSection] }
specify { subject.keys.sort_by(&:to_s).should == [:Info, :Item, :System, :xmlns] }
describe "Item" do
subject { @vapp.body[:VirtualHardwareSection][:Item] }
it { should have(5).items }
specify { subject.map {|i| i[:ResourceType] }.uniq.sort.should == %w(3 4 6 17).sort }
describe "CPU" do
subject { @vapp.body[:VirtualHardwareSection][:Item].detect {|i| i[:ResourceType] == "3" } }
its(:VirtualQuantity) { should == @mock_vm.cpus.to_s }
end
describe "memory" do
subject { @vapp.body[:VirtualHardwareSection][:Item].detect {|i| i[:ResourceType] == "4" } }
its(:VirtualQuantity) { should == @mock_vm.memory.to_s }
end
describe "SCSI controller" do
subject { @vapp.body[:VirtualHardwareSection][:Item].detect {|i| i[:ResourceType] == "6" } }
its(:Address) { should == "0" }
end
describe "Hard Disks" do
subject { @vapp.body[:VirtualHardwareSection][:Item].find_all {|i| i[:ResourceType] == "17" } }
it { should have(2).disks }
describe "#1" do
subject { @vapp.body[:VirtualHardwareSection][:Item].find_all {|i| i[:ResourceType] == "17" }[0] }
its(:AddressOnParent) { should == "0" }
its(:VirtualQuantity) { should == (1024 * @mock_vm.disks[0][:size].to_i).to_s }
its(:HostResource) { should == (1024 * @mock_vm.disks[0][:size].to_i).to_s }
end
describe "#2" do
subject { @vapp.body[:VirtualHardwareSection][:Item].find_all {|i| i[:ResourceType] == "17" }[1] }
its(:AddressOnParent) { should == "1" }
its(:VirtualQuantity) { should == (1024 * @mock_vm.disks[1][:size].to_i).to_s }
its(:HostResource) { should == (1024 * @mock_vm.disks[1][:size].to_i).to_s }
end
end
end
end
end
end
context "with a vapp uri that doesn't exist" do
subject { lambda { @vcloud.get_vapp(URI.parse('https://www.fakey.com/api/v0.8/vApp/99999')) } }
it_should_behave_like("a request for a resource that doesn't exist")
end
end
end
end

View File

@ -0,0 +1,86 @@
require 'vcloud/spec_helper'
if Fog.mocking?
describe "Fog::Vcloud", :type => :mock_vcloud_request do
subject { @vcloud }
it { should respond_to(:get_vdc) }
describe "#get_vdc" do
context "with a valid vdc uri" do
before { @vdc = @vcloud.get_vdc(URI.parse(@mock_vdc.href)) }
subject { @vdc }
it_should_behave_like "all responses"
it { should have_headers_denoting_a_content_type_of("application/vnd.vmware.vcloud.vdc+xml") }
describe "#body" do
subject { @vdc.body }
it { should have(11).items }
it_should_behave_like("it has the standard vcloud v0.8 xmlns attributes") # 3 keys
its(:href) { should == @mock_vdc.href }
its(:name) { should == @mock_vdc.name }
its(:Description) { should == "" }
its(:StorageCapacity) { should == {:Units => "bytes * 10^9", :Allocated => @mock_vdc.storage_allocated.to_s, :Used => @mock_vdc.storage_used.to_s } }
its(:ComputeCapacity) { should == {:InstantiatedVmsQuota => { :Limit => "-1", :Used => "-1" },
:Memory => { :Units => "bytes * 2^20", :Allocated => @mock_vdc.memory_allocated.to_s },
:Cpu => { :Units => "hz * 10^6", :Allocated => @mock_vdc.cpu_allocated.to_s },
:DeployedVmsQuota => { :Limit => "-1", :Used => "-1" } } }
describe "link 0" do
subject { @vdc.body[:Link] }
it { should have(4).attributes }
its(:type) { should == "application/vnd.vmware.vcloud.catalog+xml" }
its(:rel) { should == "down" }
its(:href) { should == @mock_vdc.catalog.href }
its(:name) { should == @mock_vdc.catalog.name }
end
let(:resource_entities) { subject[:ResourceEntities][:ResourceEntity] }
specify { resource_entities.should have(@mock_vdc.virtual_machines.length).vapps }
describe "[:ResourceEntities][:ResourceEntity]" do
context "[0]" do
subject { @vdc.body[:ResourceEntities][:ResourceEntity][0] }
it { should be_a_vapp_link_to(@mock_vdc.virtual_machines[0]) }
end
context "[1]" do
subject { @vdc.body[:ResourceEntities][:ResourceEntity][1] }
it { should be_a_vapp_link_to(@mock_vdc.virtual_machines[1]) }
end
context "[2]" do
subject { @vdc.body[:ResourceEntities][:ResourceEntity][2] }
it { should be_a_vapp_link_to(@mock_vdc.virtual_machines[2]) }
end
end
its(:name) { should == @mock_vdc[:name] }
let(:available_networks) { subject[:AvailableNetworks][:Network] }
specify { available_networks.should have(2).networks }
describe "[:AvailableNetworks][:Network]" do
context "[0]" do
subject { @vdc.body[:AvailableNetworks][:Network][0] }
it { should be_a_network_link_to(@mock_vdc.networks[0]) }
end
context "[1]" do
subject { @vdc.body[:AvailableNetworks][:Network][1] }
it { should be_a_network_link_to(@mock_vdc.networks[1]) }
end
end
end
end
context "with a vdc uri that doesn't exist" do
subject { lambda { @vcloud.get_vdc(URI.parse('https://www.fakey.com/api/v0.8/vdc/999')) } }
it_should_behave_like("a request for a resource that doesn't exist")
end
end
end
end

View File

@ -0,0 +1,95 @@
require 'vcloud/spec_helper'
if Fog.mocking?
describe "Fog::Vcloud", :type => :mock_vcloud_request do
subject { @vcloud }
it { should respond_to(:instantiate_vapp_template) }
describe "#instantiate_vapp_template" do
let(:vdc) { @vcloud.vdcs.first }
let(:mock_vdc) { @mock_vdc }
let(:catalog_item) { vdc.catalog.first }
let(:mock_catalog_item) { @vcloud.mock_data.catalog_item_from_href(catalog_item.href) }
let(:new_vapp_data) do
{
:name => "foobar",
:network_uri => @mock_network.href,
:row => "test row",
:group => "test group",
:memory => 1024,
:cpus => 2,
:vdc_uri => @mock_vdc.href
}
end
let(:added_mock_data) { mock_vdc.virtual_machines.last }
context "with a valid data" do
let(:template_instantiation) { @vcloud.instantiate_vapp_template(catalog_item.href, new_vapp_data) }
subject { template_instantiation }
it_should_behave_like "all responses"
it { should have_headers_denoting_a_content_type_of("application/xml") }
it "updates the mock data properly" do
expect { subject }.to change { mock_vdc.virtual_machines.size }.by(1)
end
describe "added mock data" do
before { template_instantiation }
subject { added_mock_data }
it { should be_an_instance_of(Fog::Vcloud::MockDataClasses::MockVirtualMachine) }
its(:name) { should == new_vapp_data[:name] }
its(:memory) { should == new_vapp_data[:memory] }
its(:cpus) { should == new_vapp_data[:cpus] }
# WHAT
specify { subject._parent.should == mock_vdc }
specify { subject.status.should == 2 }
specify { subject.disks.should == mock_catalog_item.disks }
# its(:_parent) { should == mock_vdc }
#its(:status) { should == 2 }
#its(:disks) { should == mock_catalog_item.disks }
end
describe "server based on added mock data" do
before { template_instantiation }
subject { vdc.servers.reload.detect {|s| s.href == added_mock_data.href }.reload }
its(:name) { should == new_vapp_data[:name] }
end
describe "#body" do
subject { template_instantiation.body }
it { should have(9).items }
it_should_behave_like("it has the standard vcloud v0.8 xmlns attributes") # 3 keys
its(:href) { should == added_mock_data.href }
its(:type) { should == "application/vnd.vmware.vcloud.vApp+xml" }
its(:name) { should == new_vapp_data[:name] }
its(:status) { should == "0" }
its(:size) { should == "4" }
it { should include(:Link) }
describe "Link" do
subject { template_instantiation.body[:Link] }
it { should have(3).keys }
its(:rel) { should == "up" }
its(:type) { should == "application/vnd.vmware.vcloud.vdc+xml" }
# WHAT
its(:href) { blah = vdc.href; should == blah }
end
end
end
end
end
end

View File

@ -0,0 +1,10 @@
require 'vcloud/spec_helper'
if Fog.mocking?
describe Fog::Vcloud, :type => :mock_vcloud_request do
subject { @vcloud }
it_should_behave_like "all login requests"
end
else
end

View File

@ -0,0 +1,34 @@
require 'vcloud/spec_helper'
if Fog.mocking?
describe Fog::Vcloud, :type => :mock_vcloud_request do
subject { @vcloud }
it { should respond_to(:power_off) }
describe :power_off, :type => :vcloud_request do
context "with a valid vapp uri" do
before { @mock_vm.power_on!; @power_off = @vcloud.power_off(@mock_vm.href(:power_off)) }
subject { @power_off }
it_should_behave_like "all responses"
#it { should have_headers_denoting_a_content_type_of "application/vnd.vmware.vcloud.network+xml" }
specify { @mock_vm.status.should == 2 }
describe :body do
subject { @power_off.body }
it_should_behave_like "it has the standard vcloud v0.8 xmlns attributes" # 3 keys
end
end
context "with a vapp uri that doesn't exist" do
subject { lambda { @vcloud.power_off(URI.parse('https://www.fakey.com/api/v0.8/vapp/9999')) } }
it_should_behave_like "a request for a resource that doesn't exist"
end
end
end
else
end

View File

@ -0,0 +1,34 @@
require 'vcloud/spec_helper'
if Fog.mocking?
describe Fog::Vcloud, :type => :mock_vcloud_request do
subject { @vcloud }
it { should respond_to(:power_on) }
describe :power_on, :type => :vcloud_request do
context "with a valid vapp uri" do
before { @mock_vm.power_off!; @power_on = @vcloud.power_on(@mock_vm.href(:power_on)) }
subject { @power_on }
it_should_behave_like "all responses"
#it { should have_headers_denoting_a_content_type_of "application/vnd.vmware.vcloud.network+xml" }
specify { @mock_vm.status.should == 4 }
describe :body do
subject { @power_on.body }
it_should_behave_like "it has the standard vcloud v0.8 xmlns attributes" # 3 keys
end
end
context "with a vapp uri that doesn't exist" do
subject { lambda { @vcloud.power_on(URI.parse('https://www.fakey.com/api/v0.8/vapp/9999')) } }
it_should_behave_like "a request for a resource that doesn't exist"
end
end
end
else
end

362
spec/vcloud/spec_helper.rb Normal file
View File

@ -0,0 +1,362 @@
require 'spec'
require 'pp'
require 'fog'
require 'fog/bin'
require 'fog/bin/vcloud'
module Spec
module Example
module Subject
module ExampleGroupMethods
def its(attribute, &block)
describe(attribute) do
define_method(:subject) { s = super(); s.is_a?(Hash) ? s[attribute] : s.send(attribute) }
it(&block)
end
end
end
end
end
end
#Initialize this to a known seed
srand 1234
Fog.mock! if ENV['FOG_MOCK']
def arrayify(item)
item.is_a?(Array) ? item : [ item ]
end
def vcloud_disabled_default_monitor
{:url_send_string=>nil, :receive_string=>nil, :response_timeout=>2, :retries=>3, :is_enabled=>"true", :down_time=>30, :type=>"Disabled", :http_headers=>nil, :interval=>5, :downtime=>nil}
end
shared_examples_for "all responses" do
it { should be_an_instance_of(Excon::Response) }
it { should respond_to(:body) }
it { should respond_to(:headers) }
it { should have_at_least(1).body }
it { should have_at_least(0).headers }
its(:body) { should be_an_instance_of(Hash) }
its(:headers) { should be_an_instance_of(Hash) }
end
shared_examples_for "all delete responses" do
it { should be_an_instance_of(Excon::Response) }
it { should respond_to(:body) }
it { should respond_to(:headers) }
its(:headers) { should be_an_instance_of(Hash) }
end
shared_examples_for "it has a Content-Type header" do
its(:headers) { should include("Content-Type") }
end
shared_examples_for "all rel=down vcloud links" do
it { should be_an_instance_of(Struct::VcloudLink) }
specify { subject.rel.should == "down" }
end
shared_examples_for "all vcloud links w/o a rel" do
it { should be_an_instance_of(Struct::VcloudLink) }
specify { subject.rel.should == nil }
end
shared_examples_for "all vcloud catalog links" do
specify { subject.type.should == "application/vnd.vmware.vcloud.catalog+xml" }
end
shared_examples_for "all vcloud application/xml types" do
specify { subject.type.should == "application/xml" }
end
shared_examples_for "a vapp type" do
specify { subject.type.should == "application/vnd.vmware.vcloud.vApp+xml" }
end
shared_examples_for "all vcloud network types" do
specify { subject.type.should == "application/vnd.vmware.vcloud.network+xml" }
end
shared_examples_for "all login requests" do
it { should respond_to(:login) }
describe "#login" do
before { @login = @vcloud.login }
subject { @login }
it_should_behave_like("all responses")
its(:headers) { should include("Set-Cookie") }
describe "#body" do
subject { @login.body }
it { should have(4).items }
it_should_behave_like("it has the standard vcloud v0.8 xmlns attributes") # 3 keys
it { should include(:Org) }
describe ":Org" do
subject { arrayify(@login.body[:Org]) }
specify do
subject.each do |org|
org.should include(:type)
org[:type].should be_of_type("application/vnd.vmware.vcloud.org+xml")
org.should include(:name)
org[:name].should be_an_instance_of(String)
org.should include(:href)
org[:href].should(be_a_url)
end
end
end
end
end
end
shared_examples_for "it has a vcloud v0.8 xmlns" do
its(:xmlns) { should == 'http://www.vmware.com/vcloud/v0.8' }
end
shared_examples_for "it has the proper xmlns_xsi" do
its(:xmlns_xsi) { should == "http://www.w3.org/2001/XMLSchema-instance" }
end
shared_examples_for "it has the proper xmlns_xsd" do
its(:xmlns_xsd) { should == "http://www.w3.org/2001/XMLSchema" }
end
shared_examples_for "it has the standard xmlns attributes" do
it_should_behave_like("it has the proper xmlns_xsi")
it_should_behave_like("it has the proper xmlns_xsd")
end
shared_examples_for "it has the standard vcloud v0.8 xmlns attributes" do
it_should_behave_like("it has a vcloud v0.8 xmlns")
it_should_behave_like("it has the standard xmlns attributes")
end
shared_examples_for "a request for a resource that doesn't exist" do
it { should raise_error(Excon::Errors::Unauthorized) }
end
shared_examples_for "a vdc catalog link" do
it_should_behave_like "all rel=down vcloud links"
it_should_behave_like "all vcloud catalog links"
its(:href) { should == URI.parse(@mock_vdc[:href] + "/catalog") }
end
shared_examples_for "a network link" do
it_should_behave_like("all vcloud links w/o a rel")
it_should_behave_like("all vcloud network types")
end
shared_examples_for "the mocked network links" do
it { should have(2).networks }
describe "[0]" do
subject { @vdc.body.networks[0] }
it_should_behave_like "a network link"
its(:href) { should == URI.parse(@mock_vdc[:networks][0][:href]) }
its(:name) { should == @mock_vdc[:networks][0][:name] }
end
describe "[1]" do
subject { @vdc.body.networks[1] }
it_should_behave_like "a network link"
its(:href) { should == URI.parse(@mock_vdc[:networks][1][:href]) }
its(:name) { should == @mock_vdc[:networks][1][:name] }
end
end
shared_examples_for "the mocked tmrk resource entity links" do
it { should have(3).resource_entities }
describe "[0]" do
subject { @vdc.body.resource_entities[0] }
it_should_behave_like("a vapp type")
it_should_behave_like("all vcloud links w/o a rel")
its(:href) { should == URI.parse(@mock_vdc[:vms][0][:href]) }
its(:name) { should == @mock_vdc[:vms][0][:name] }
end
describe "[1]" do
subject { @vdc.body.resource_entities[1] }
it_should_behave_like("a vapp type")
it_should_behave_like("all vcloud links w/o a rel")
its(:href) { should == URI.parse(@mock_vdc[:vms][1][:href]) }
its(:name) { should == @mock_vdc[:vms][1][:name] }
end
describe "[2]" do
subject { @vdc.body.resource_entities[2] }
it_should_behave_like("a vapp type")
it_should_behave_like("all vcloud links w/o a rel")
its(:href) { should == URI.parse(@mock_vdc[:vms][2][:href]) }
its(:name) { should == @mock_vdc[:vms][2][:name] }
end
end
Spec::Example::ExampleGroupFactory.register(:mock_vcloud_request, Class.new(Spec::Example::ExampleGroup))
Spec::Example::ExampleGroupFactory.register(:mock_vcloud_model, Class.new(Spec::Example::ExampleGroup))
Spec::Example::ExampleGroupFactory.register(:mock_vcloud_request, Class.new(Spec::Example::ExampleGroup))
Spec::Example::ExampleGroupFactory.register(:mock_vcloud_model, Class.new(Spec::Example::ExampleGroup))
Spec::Example::ExampleGroupFactory.register(:vcloud_request, Class.new(Spec::Example::ExampleGroup))
Spec::Example::ExampleGroupFactory.register(:vcloud_request, Class.new(Spec::Example::ExampleGroup))
def setup_generic_mock_data
@mock_organization = @mock_data.organizations.first
@mock_vdc = @mock_organization.vdcs.first
@mock_vm = @mock_vdc.virtual_machines.first
@mock_network = @mock_vdc.networks.first
end
def setup_vcloud_mock_data
@base_url = Fog::Vcloud::Compute::Mock.base_url
@mock_data = Fog::Vcloud::Compute::Mock.data
setup_generic_mock_data
# @mock_service = @mock_service_collection.items.first
@mock_catalog = @mock_vdc.catalog
@mock_catalog_item = @mock_catalog.items.first
@mock_network_ip_collection = @mock_network.ip_collection
@mock_network_ip = @mock_network_ip_collection.items.values.first
@mock_network_extensions = @mock_network.extensions
@base_url = Fog::Vcloud::Compute::Mock.base_url
@mock_data = Fog::Vcloud::Compute::Mock.data
end
Spec::Runner.configure do |config|
config.after(:all) do
Fog::Vcloud::Compute::Mock.data_reset
end
config.before(:each, :type => :vcloud_request) do
@vcloud = Fog::Vcloud::Compute.new(Fog.credentials[:vcloud][:vcloud])
end
config.before(:each, :type => :mock_vcloud_request) do
Fog::Vcloud::Compute::Mock.data_reset
setup_vcloud_mock_data
@vcloud = Fog::Vcloud::Compute.new(:vcloud_username => "foo", :vcloud_password => "bar", :vcloud_host => "fakey.com")
end
config.before(:each, :type => :mock_vcloud_model) do
Fog::Vcloud::Compute::Mock.data_reset
setup_vcloud_mock_data
@vcloud = Fog::Vcloud::Compute.new(:vcloud_username => "foo", :vcloud_password => "bar", :vcloud_host => "fakey.com")
end
end
Spec::Matchers.define :have_only_these_attributes do |expected|
match do |actual|
attributes = actual.instance_variable_get('@attributes')
attributes.all? { |attribute| expected.include?(attribute) } && ( expected.length == attributes.length )
end
failure_message_for_should do |actual|
msg = "Expected: [#{expected.map{|e| ":#{e}"}.join(", ")}]\n"
msg += "Got: [#{actual.instance_variable_get('@attributes').map{|a| ":#{a}"}.join(", ")}]"
msg
end
end
Spec::Matchers.define :have_identity do |expected|
match do |actual|
actual.instance_variable_get('@identity').should == expected
end
failure_message_for_should do |actual|
"Expected: '#{expected}', but got: '#{actual.instance_variable_get('@identity')}'"
end
end
Spec::Matchers.define :have_members_of_the_right_model do
match do |actual|
actual.all? { |member| member.is_a?(actual.model) }
end
end
Spec::Matchers.define :have_key_with_value do |expected_key, expected_value|
match do |actual|
actual.has_key?(expected_key) && actual[expected_key] == expected_value
end
end
Spec::Matchers.define :have_key_with_array do |expected_key, expected_array|
match do |actual|
actual[expected_key].all? { |item| expected_array.include?(item) } && actual[expected_key].length == expected_array.length
end
failure_message_for_should do |actual|
"Items not found in array:\n#{expected_array.select { |expected_item| !actual[expected_key].include?(expected_item) }.map { |item| item.inspect }.join("\n")}\n" +
"Original items:\n#{actual[expected_key].map { |item| item.inspect }.join("\n") }\n"+
"Length Difference: #{expected_array.length - actual[expected_key].length}"
end
end
Spec::Matchers.define :have_headers_denoting_a_content_type_of do |expected|
match do |actual|
actual.headers["Content-Type"] == expected
end
end
Spec::Matchers.define :have_keys_with_values do |expected|
match do |actual|
actual.each_pair.all? do |key, value|
expected.keys.include?(key) && expected[key] == value
end
end
end
Spec::Matchers.define :be_a_vapp_link_to do |expected|
match do |actual|
actual.is_a?(Hash) and
actual[:type] == "application/vnd.vmware.vcloud.vApp+xml" and
actual[:href] == expected.href and
actual[:name] == expected.name
end
end
Spec::Matchers.define :be_a_network_link_to do |expected|
match do |actual|
actual.is_a?(Hash) and
actual[:type] == "application/vnd.vmware.vcloud.network+xml" and
actual[:href] == expected.href and
actual[:name] == expected.name
end
end
Spec::Matchers.define :have_all_attributes_be_nil do
match do |actual|
actual.class.attributes.all? { |attribute| actual.send(attribute.to_sym) == nil }
end
end
Spec::Matchers.define :be_a_url do
match do |actual|
actual.match(/^(http|https):\/\/[a-z0-9]+([\-\.]{1}[a-z0-9]+)*\.[a-z]{2,5}(([0-9]{1,5})?\/.*)?$/ix)
end
end
Spec::Matchers.define :be_either_a_hash_or_array do
match do |actual|
actual.is_a?(Hash) || actual.is_a?(Array)
end
end
Spec::Matchers.define :be_a_known_vmware_type do
match do |actual|
["application/vnd.vmware.vcloud.org+xml"].include?(actual)
end
end
Spec::Matchers.define :be_of_type do |type|
match do |actual|
actual == type ||
if actual.is_a?(Hash) && actual[:type]
actual[:type] == type
end ||
if actual.respond_to(:type)
actual.type == type
end
end
end

View File

@ -0,0 +1,14 @@
require 'vcloud/spec_helper'
if Fog.mocking?
describe Fog::Vcloud, :type => :mock_vcloud_request do
subject { @vcloud }
it { should be_an_instance_of(Fog::Vcloud::Compute::Mock) }
it { should respond_to(:default_organization_uri) }
its(:default_organization_uri) { should == @mock_organization.href }
end
end