1
0
Fork 0
mirror of https://github.com/fog/fog.git synced 2022-11-09 13:51:43 -05:00
fog--fog/lib/fog/vcloud/compute.rb
Peter Meier 30e28d704f [vcloud|compute] add API version 1.5 compability
vCloud version API 1.5 requires us to adapt certain calls. This
patch does all the necessary things to scan a vCloud 1.5 completely
and read all the necessary attributes, as we could already before.
Furthermore, it add tests for the version 1.5 calls.

The API version can now be set as a version param to the vcloud
compute resource. By default it chooses version 1.5.

This has been tested against a vCloud 1.5 and 1.0.
2012-01-30 19:04:19 +01:00

372 lines
11 KiB
Ruby

require File.expand_path(File.join(File.dirname(__FILE__), '..', 'vcloud'))
require 'fog/compute'
module Fog
module Vcloud
class Collection < Fog::Collection
def load(objects)
objects = [ objects ] if objects.is_a?(Hash)
super
end
def check_href!(opts = {})
self.href = connection.default_vdc_href unless href
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
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
def link_up
load_unless_loaded!
self.links.find{|l| l[:rel] == 'up' }
end
def self.has_up(item)
class_eval <<-EOS, __FILE__,__LINE__
def #{item}
load_unless_loaded!
connection.get_#{item}(link_up[:href])
end
EOS
end
end
end
end
module Fog
module Vcloud
class Compute < Fog::Service
BASE_PATH = '/api'
DEFAULT_VERSION = '1.5'
SUPPORTED_VERSIONS = [ '1.5', '1.0' ]
PORT = 443
SCHEME = 'https'
attr_writer :default_organization_uri
requires :vcloud_username, :vcloud_password, :vcloud_host
recognizes :vcloud_port, :vcloud_scheme, :vcloud_path, :vcloud_default_vdc, :vcloud_version, :vcloud_base_path
recognizes :provider # remove post deprecation
model_path 'fog/vcloud/models/compute'
model :catalog
collection :catalogs
model :catalog_item
model :catalog_items
model :ip
collection :ips
model :network
collection :networks
model :server
collection :servers
model :task
collection :tasks
model :vapp
collection :vapps
model :vdc
collection :vdcs
model :organization
collection :organizations
request_path 'fog/vcloud/requests/compute'
request :clone_vapp
request :configure_network
request :configure_network_ip
request :configure_vapp
request :configure_vm_memory
request :configure_vm_name_description
request :configure_vm_disks
request :delete_vapp
request :get_catalog_item
request :get_customization_options
request :get_network_ip
request :get_network_ips
request :get_network_extensions
request :get_task_list
request :get_vapp_template
request :get_vm_disks
request :get_vm_memory
request :instantiate_vapp_template
request :login
request :power_off
request :power_on
request :power_reset
request :power_shutdown
request :undeploy
class Mock
def initialize(options={})
Fog::Mock.not_implemented
end
end
class Real
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
attr_reader :version
def initialize(options = {})
require 'builder'
require 'fog/core/parser'
@connections = {}
@connection_options = options[:connection_options] || {}
@persistent = options[:persistent]
@username = options[:vcloud_username]
@password = options[:vcloud_password]
@host = options[:vcloud_host]
@vdc_href = options[:vcloud_default_vdc]
@base_path = options[:vcloud_base_path] || Fog::Vcloud::Compute::BASE_PATH
@version = options[:vcloud_version] || Fog::Vcloud::Compute::DEFAULT_VERSION
@path = options[:vcloud_path] || "#{@base_path}/v#{@version}"
@port = options[:vcloud_port] || Fog::Vcloud::Compute::PORT
@scheme = options[:vcloud_scheme] || Fog::Vcloud::Compute::SCHEME
end
def reload
@connections.each_value { |k,v| v.reset if v }
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 default_vdc_href
@vdc_href
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
if version == '1.0'
{ "xmlns" => "http://www.vmware.com/vcloud/v1",
"xmlns:ovf" => "http://schemas.dmtf.org/ovf/envelope/1",
"xmlns:xsi" => "http://www.w3.org/2001/XMLSchema-instance",
"xmlns:xsd" => "http://www.w3.org/2001/XMLSchema" }
else
{ 'xmlns' => "http://www.vmware.com/vcloud/v1.5",
"xmlns:ovf" => "http://schemas.dmtf.org/ovf/envelope/1",
"xmlns:xsi" => "http://www.w3.org/2001/XMLSchema-instance",
"xmlns:xsd" => "http://www.w3.org/2001/XMLSchema" }
end
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
def basic_request_params(uri,*args)
{
:expects => args[0] || 200,
:method => args[1] || 'GET',
:headers => args[2] ? args[2].inspect : {},
:body => args[3] ? args[3] : '',
:parse => true,
:uri => uri
}
end
def base_path_url
"#{@scheme}://#{@host}:#{@port}#{@base_path}"
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
def base_url
"#{@scheme}://#{@host}:#{@port}#{@path}"
end
# Use this to set the Authorization header for login
def authorization_header
"Basic #{Base64.encode64("#{@username}:#{@password}").chomp!}"
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, @connection_options)
# Set headers to an empty hash if none are set.
headers = params[:headers] || {}
headers['Accept'] = 'application/*+xml;version=1.5' if version == '1.5'
# 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
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
def self.item_requests(*types)
types.each{|t| item_request(t) }
end
def self.item_request(type)
Fog::Vcloud::Compute::Real.class_eval <<-EOS, __FILE__,__LINE__
def get_#{type}(uri)
Fog::Vcloud::Compute::#{type.to_s.capitalize}.new(
self.request(basic_request_params(uri)).body.merge(
:connection => self,
:collection => Fog::Vcloud::Compute::#{type.to_s.capitalize}s.new(
:connection => self
)
)
)
end
EOS
end
item_requests :organization, :vdc, :network, :vapp, :server, :catalog, :task
end
end
end