mirror of
https://github.com/fog/fog.git
synced 2022-11-09 13:51:43 -05:00
30e28d704f
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.
372 lines
11 KiB
Ruby
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
|