2013-08-27 05:19:54 -04:00
|
|
|
require 'fog/vcloud_director'
|
2013-06-18 10:57:32 -04:00
|
|
|
require 'fog/compute'
|
2013-08-27 05:19:54 -04:00
|
|
|
require 'fog/vcloud_director/requests/compute/helper'
|
2013-06-11 09:06:30 -04:00
|
|
|
|
2013-08-27 05:19:54 -04:00
|
|
|
class VcloudDirectorParser < Fog::Parsers::Base
|
2013-06-18 10:57:32 -04:00
|
|
|
def extract_attributes(attributes_xml)
|
|
|
|
attributes = {}
|
|
|
|
until attributes_xml.empty?
|
|
|
|
if attributes_xml.first.is_a?(Array)
|
|
|
|
until attributes_xml.first.empty?
|
|
|
|
attribute = attributes_xml.first.shift
|
2013-07-09 09:00:15 -04:00
|
|
|
attributes[attribute.localname.to_sym] = attribute.value
|
2013-06-18 10:57:32 -04:00
|
|
|
end
|
|
|
|
else
|
|
|
|
attribute = attributes_xml.shift
|
2013-07-09 09:00:15 -04:00
|
|
|
attributes[attribute.localname.to_sym] = attribute.value
|
2013-06-18 10:57:32 -04:00
|
|
|
end
|
|
|
|
end
|
|
|
|
attributes
|
|
|
|
end
|
|
|
|
end
|
2013-06-11 09:06:30 -04:00
|
|
|
|
2013-07-08 14:31:37 -04:00
|
|
|
class NonLoaded
|
|
|
|
end
|
2013-06-11 09:06:30 -04:00
|
|
|
|
2013-06-18 10:57:32 -04:00
|
|
|
module Fog
|
|
|
|
module Compute
|
2013-08-27 05:19:54 -04:00
|
|
|
class VcloudDirector < Fog::Service
|
2013-06-11 09:06:30 -04:00
|
|
|
|
2013-09-16 13:13:09 -04:00
|
|
|
module Defaults
|
|
|
|
PATH = '/api'
|
|
|
|
PORT = 443
|
|
|
|
SCHEME = 'https'
|
|
|
|
API_VERSION = '5.1'
|
|
|
|
end
|
|
|
|
|
|
|
|
module Errors
|
|
|
|
class ServiceError < Fog::Errors::Error; end
|
|
|
|
class Task < ServiceError; end
|
|
|
|
end
|
|
|
|
|
|
|
|
requires :vcloud_director_username, :vcloud_director_password, :vcloud_director_host
|
|
|
|
recognizes :vcloud_director_api_version
|
|
|
|
|
|
|
|
secrets :vcloud_director_password
|
|
|
|
|
|
|
|
model_path 'fog/vcloud_director/models/compute'
|
|
|
|
model :catalog
|
|
|
|
collection :catalogs
|
|
|
|
model :organization
|
|
|
|
collection :organizations
|
|
|
|
model :catalog_item
|
|
|
|
collection :catalog_items
|
|
|
|
model :vdc
|
|
|
|
collection :vdcs
|
|
|
|
model :vapp
|
|
|
|
collection :vapps
|
|
|
|
model :task
|
|
|
|
collection :tasks
|
|
|
|
model :vm
|
|
|
|
collection :vms
|
|
|
|
model :vm_customization
|
|
|
|
collection :vm_customizations
|
|
|
|
model :network
|
|
|
|
collection :networks
|
|
|
|
model :disk
|
|
|
|
collection :disks
|
|
|
|
model :vm_network
|
|
|
|
collection :vm_networks
|
|
|
|
model :tag # this is called metadata in vcloud
|
|
|
|
collection :tags
|
2013-09-30 21:36:20 -04:00
|
|
|
model :media
|
|
|
|
collection :medias # sic
|
2013-09-16 13:13:09 -04:00
|
|
|
|
|
|
|
request_path 'fog/vcloud_director/requests/compute'
|
2013-10-01 03:43:28 -04:00
|
|
|
request :delete_media
|
|
|
|
request :delete_vapp
|
|
|
|
request :delete_vapp_metadata_item_metadata
|
2013-09-16 13:13:09 -04:00
|
|
|
request :get_catalog
|
|
|
|
request :get_catalog_item
|
2013-10-01 03:43:28 -04:00
|
|
|
request :get_cpu_rasd_item
|
|
|
|
request :get_current_session
|
|
|
|
request :get_disks_rasd_items_list
|
|
|
|
request :get_guest_customization_system_section_vapp
|
|
|
|
request :get_href # this is used for manual testing
|
|
|
|
request :get_media
|
|
|
|
request :get_media_owner
|
|
|
|
request :get_memory_rasd_item
|
|
|
|
request :get_metadata
|
|
|
|
request :get_network
|
|
|
|
request :get_network_connection_system_section_vapp
|
|
|
|
request :get_organization
|
|
|
|
request :get_organizations
|
|
|
|
request :get_request # this is used for manual testing
|
2013-10-01 04:45:01 -04:00
|
|
|
request :get_supported_systems_info
|
2013-10-01 03:43:28 -04:00
|
|
|
request :get_supported_versions
|
2013-09-16 13:13:09 -04:00
|
|
|
request :get_task
|
2013-10-01 03:43:28 -04:00
|
|
|
request :get_task_list
|
|
|
|
request :get_vapp
|
|
|
|
request :get_vapp_metadata
|
2013-10-01 21:14:52 -04:00
|
|
|
request :get_vapp_ovf_descriptor
|
2013-10-01 03:43:28 -04:00
|
|
|
request :get_vapp_template
|
2013-10-01 21:14:52 -04:00
|
|
|
request :get_vapp_template_ovf_descriptor
|
2013-10-01 03:43:28 -04:00
|
|
|
request :get_vdc
|
|
|
|
request :get_vm
|
2013-09-16 13:13:09 -04:00
|
|
|
request :get_vm_customization
|
|
|
|
request :get_vm_disks
|
|
|
|
request :get_vm_network
|
2013-10-01 03:43:28 -04:00
|
|
|
request :get_vms
|
2013-09-16 13:13:09 -04:00
|
|
|
request :get_vms_by_metadata
|
2013-10-01 03:43:28 -04:00
|
|
|
request :instantiate_vapp_template
|
|
|
|
request :post_cancel_task
|
2013-10-01 21:58:20 -04:00
|
|
|
request :post_capture_vapp
|
2013-10-01 03:43:28 -04:00
|
|
|
request :post_power_off_vapp
|
|
|
|
request :post_power_on_vapp
|
2013-10-01 05:23:57 -04:00
|
|
|
request :post_reboot_vapp
|
|
|
|
request :post_reset_vapp
|
|
|
|
request :post_shutdown_vapp
|
|
|
|
request :post_suspend_vapp
|
2013-10-01 03:43:28 -04:00
|
|
|
request :post_undeploy_vapp
|
2013-09-30 20:47:57 -04:00
|
|
|
request :post_upload_media
|
2013-10-01 03:43:28 -04:00
|
|
|
request :post_vapp_metadata_item_metadata
|
|
|
|
request :put_cpu
|
|
|
|
request :put_disks
|
|
|
|
request :put_guest_customization_section_vapp
|
|
|
|
request :put_memory
|
|
|
|
request :put_network_connection_system_section_vapp
|
|
|
|
request :put_vapp_metadata_item_metadata
|
2013-09-16 13:13:09 -04:00
|
|
|
|
|
|
|
class Model < Fog::Model
|
|
|
|
def initialize(attrs={})
|
|
|
|
super(attrs)
|
|
|
|
lazy_load_attrs.each do |attr|
|
|
|
|
attributes[attr]= NonLoaded if attributes[attr].nil?
|
|
|
|
make_lazy_load_method(attr)
|
|
|
|
end
|
|
|
|
self.class.attributes.each {|attr| make_attr_loaded_method(attr)}
|
|
|
|
end
|
|
|
|
|
|
|
|
def lazy_load_attrs
|
|
|
|
@lazy_load_attrs ||= self.class.attributes - attributes.keys
|
|
|
|
end
|
|
|
|
|
|
|
|
def make_lazy_load_method(attr)
|
|
|
|
self.class.instance_eval do
|
|
|
|
define_method(attr) do
|
|
|
|
reload if attributes[attr] == NonLoaded and !@inspecting
|
|
|
|
attributes[attr]
|
|
|
|
end
|
|
|
|
end
|
|
|
|
end
|
|
|
|
|
|
|
|
# it adds an attr_loaded? method to know if the value has been loaded
|
|
|
|
# yet or not: ie description_loaded?
|
|
|
|
def make_attr_loaded_method(attr)
|
|
|
|
self.class.instance_eval do
|
|
|
|
define_method("#{attr}_loaded?") do
|
|
|
|
attributes[attr] != NonLoaded
|
|
|
|
end
|
|
|
|
end
|
|
|
|
end
|
|
|
|
|
|
|
|
def inspect
|
|
|
|
@inspecting = true
|
|
|
|
out = super
|
|
|
|
@inspecting = false
|
|
|
|
out
|
|
|
|
end
|
|
|
|
end
|
|
|
|
|
|
|
|
class Collection < Fog::Collection
|
|
|
|
def all(lazy_load=true)
|
|
|
|
lazy_load ? index : get_everyone
|
|
|
|
end
|
|
|
|
|
|
|
|
def get(item_id)
|
|
|
|
item = get_by_id(item_id)
|
|
|
|
return nil unless item
|
|
|
|
new(item)
|
|
|
|
end
|
|
|
|
|
|
|
|
def get_by_name(item_name)
|
|
|
|
item_found = item_list.detect {|item| item[:name] == item_name}
|
|
|
|
return nil unless item_found
|
|
|
|
get(item_found[:id])
|
|
|
|
end
|
|
|
|
|
|
|
|
def index
|
|
|
|
load(item_list)
|
|
|
|
end
|
|
|
|
|
|
|
|
def get_everyone
|
|
|
|
items = item_list.map {|item| get_by_id(item[:id])}
|
|
|
|
load(items)
|
|
|
|
end
|
2013-09-20 10:29:46 -04:00
|
|
|
|
|
|
|
def ensure_list(items)
|
|
|
|
items.is_a?(Hash) ? [items] : items
|
|
|
|
end
|
2013-09-16 13:13:09 -04:00
|
|
|
end
|
2013-06-11 09:06:30 -04:00
|
|
|
|
2013-09-16 13:13:09 -04:00
|
|
|
class Real
|
|
|
|
include Fog::Compute::Helper
|
|
|
|
|
|
|
|
attr_reader :end_point, :api_version
|
|
|
|
|
|
|
|
def initialize(options={})
|
|
|
|
@vcloud_director_password = options[:vcloud_director_password]
|
|
|
|
@vcloud_director_username = options[:vcloud_director_username]
|
|
|
|
@connection_options = options[:connection_options] || {}
|
|
|
|
@host = options[:vcloud_director_host]
|
|
|
|
@path = options[:path] || Fog::Compute::VcloudDirector::Defaults::PATH
|
|
|
|
@persistent = options[:persistent] || false
|
|
|
|
@port = options[:port] || Fog::Compute::VcloudDirector::Defaults::PORT
|
|
|
|
@scheme = options[:scheme] || Fog::Compute::VcloudDirector::Defaults::SCHEME
|
|
|
|
@connection = Fog::Connection.new("#{@scheme}://#{@host}:#{@port}", @persistent, @connection_options)
|
|
|
|
@end_point = "#{@scheme}://#{@host}#{@path}/"
|
|
|
|
@api_version = options[:vcloud_director_api_version] || Fog::Compute::VcloudDirector::Defaults::API_VERSION
|
|
|
|
end
|
|
|
|
|
|
|
|
def auth_token
|
|
|
|
login if @auth_token.nil?
|
|
|
|
@auth_token
|
|
|
|
end
|
|
|
|
|
|
|
|
def org_name
|
|
|
|
login if @org_name.nil?
|
|
|
|
@org_name
|
|
|
|
end
|
|
|
|
|
|
|
|
def reload
|
|
|
|
@cookie = nil # verify that this makes the connection to be restored, if so use Excon::Errors::Forbidden instead of Excon::Errors::Unauthorized
|
|
|
|
@connection.reset
|
|
|
|
end
|
|
|
|
|
|
|
|
def request(params)
|
|
|
|
unless @cookie
|
|
|
|
@cookie = auth_token
|
|
|
|
end
|
|
|
|
begin
|
|
|
|
do_request(params)
|
|
|
|
# this is to know if Excon::Errors::Unauthorized really happens
|
|
|
|
#rescue Excon::Errors::Unauthorized
|
|
|
|
# @cookie = auth_token
|
|
|
|
# do_request(params)
|
|
|
|
end
|
|
|
|
end
|
|
|
|
|
2013-09-27 11:32:49 -04:00
|
|
|
# @api private
|
2013-09-16 13:13:09 -04:00
|
|
|
def do_request(params)
|
|
|
|
headers = { 'Accept' => 'application/*+xml;version=' + @api_version }
|
|
|
|
if @cookie
|
|
|
|
headers.merge!('Cookie' => @cookie)
|
|
|
|
end
|
|
|
|
if params[:path]
|
|
|
|
if params[:override_path] == true
|
|
|
|
path = params[:path]
|
|
|
|
else
|
|
|
|
path = "#{@path}/#{params[:path]}"
|
|
|
|
end
|
|
|
|
else
|
|
|
|
path = "#{@path}"
|
|
|
|
end
|
|
|
|
@connection.request({
|
|
|
|
:body => params[:body],
|
|
|
|
:expects => params[:expects],
|
|
|
|
:headers => headers.merge!(params[:headers] || {}),
|
|
|
|
:host => @host,
|
|
|
|
:method => params[:method],
|
|
|
|
:parser => params[:parser],
|
|
|
|
:path => path
|
|
|
|
})
|
|
|
|
rescue => @e
|
|
|
|
raise @e unless @e.class.to_s =~ /^Excon::Errors/
|
|
|
|
if @e.respond_to?(:response)
|
|
|
|
puts @e.response.status
|
|
|
|
puts CGI::unescapeHTML(@e.response.body)
|
|
|
|
end
|
|
|
|
raise @e
|
|
|
|
end
|
|
|
|
|
|
|
|
def process_task(response_body)
|
|
|
|
task = make_task_object(response_body)
|
|
|
|
wait_and_raise_unless_success(task)
|
|
|
|
true
|
|
|
|
end
|
|
|
|
|
|
|
|
def make_task_object(task_response)
|
|
|
|
task_response[:id] = task_response[:href].split('/').last
|
|
|
|
tasks.new(task_response)
|
|
|
|
end
|
|
|
|
|
|
|
|
def wait_and_raise_unless_success(task)
|
|
|
|
task.wait_for { non_running? }
|
|
|
|
raise Errors::Task.new "status: #{task.status}, error: #{task.error}" unless task.success?
|
|
|
|
end
|
|
|
|
|
|
|
|
def add_id_from_href!(data={})
|
|
|
|
data[:id] = data[:href].split('/').last
|
|
|
|
end
|
|
|
|
|
|
|
|
private
|
|
|
|
|
|
|
|
def login
|
|
|
|
headers = {
|
|
|
|
'Authorization' => "Basic #{Base64.encode64("#{@vcloud_director_username}:#{@vcloud_director_password}").delete("\r\n")}",
|
|
|
|
'Accept' => 'application/*+xml;version=' + @api_version
|
|
|
|
}
|
|
|
|
response = @connection.request({
|
|
|
|
:expects => 200,
|
|
|
|
:headers => headers,
|
|
|
|
:host => @host,
|
|
|
|
:method => 'POST',
|
|
|
|
:parser => Fog::ToHashDocument.new,
|
|
|
|
:path => '/api/sessions' # curl http://example.com/api/versions | grep LoginUrl
|
|
|
|
})
|
|
|
|
@auth_token = response.headers['Set-Cookie'] || response.headers['set-cookie']
|
|
|
|
@org_name = response.body[:org]
|
|
|
|
end
|
|
|
|
end
|
|
|
|
|
|
|
|
class Mock
|
2013-09-26 06:29:51 -04:00
|
|
|
attr_reader :end_point, :api_version
|
|
|
|
|
|
|
|
def data
|
|
|
|
@@data ||= Hash.new do |hash, key|
|
|
|
|
hash[key] = {
|
2013-09-29 16:27:58 -04:00
|
|
|
:catalogs => {
|
|
|
|
uuid => {
|
|
|
|
:name => 'Default Catalog'
|
|
|
|
}
|
|
|
|
},
|
|
|
|
:catalog_items => {
|
|
|
|
uuid => {
|
|
|
|
:type => 'vAppTemplate',
|
|
|
|
:name => 'vAppTemplate 1'
|
|
|
|
}
|
|
|
|
},
|
|
|
|
:medias => {},
|
|
|
|
:networks => {
|
|
|
|
uuid => {
|
|
|
|
:Description => 'Network for mocking',
|
|
|
|
:Dns1 => '8.8.8.8',
|
|
|
|
:Dns2 => '8.8.4.4',
|
|
|
|
:DnsSuffix => 'example.com',
|
|
|
|
:Gateway => '192.168.1.1',
|
|
|
|
:IpRanges => [{
|
|
|
|
:StartAddress=>'192.168.1.2',
|
|
|
|
:EndAddress=>'192.168.1.254'
|
|
|
|
}],
|
|
|
|
:IsInherited => false,
|
|
|
|
:Netmask => '255.255.255.0',
|
|
|
|
:name => 'Default Network'
|
|
|
|
}
|
2013-09-26 06:29:51 -04:00
|
|
|
},
|
|
|
|
:org => {
|
|
|
|
:description => 'Organization for mocking',
|
|
|
|
:full_name => 'Mock Organization',
|
|
|
|
:name => org_name,
|
|
|
|
:uuid => uuid
|
|
|
|
},
|
2013-09-29 16:27:58 -04:00
|
|
|
:tasks => {},
|
|
|
|
:vdcs => {
|
|
|
|
uuid => {
|
|
|
|
:description => 'vDC for mocking',
|
|
|
|
:name => 'MockVDC'
|
|
|
|
}
|
2013-09-26 06:29:51 -04:00
|
|
|
}
|
|
|
|
}
|
|
|
|
end[@vcloud_director_username]
|
|
|
|
end
|
|
|
|
|
|
|
|
def initialize(options={})
|
|
|
|
@vcloud_director_password = options[:vcloud_director_password]
|
|
|
|
@vcloud_director_username = options[:vcloud_director_username]
|
|
|
|
#@connection_options = options[:connection_options] || {}
|
|
|
|
@host = options[:vcloud_director_host]
|
|
|
|
@path = options[:path] || Fog::Compute::VcloudDirector::Defaults::PATH
|
|
|
|
@persistent = options[:persistent] || false
|
|
|
|
@port = options[:port] || Fog::Compute::VcloudDirector::Defaults::PORT
|
|
|
|
@scheme = options[:scheme] || Fog::Compute::VcloudDirector::Defaults::SCHEME
|
|
|
|
#@connection = Fog::Connection.new("#{@scheme}://#{@host}:#{@port}", @persistent, @connection_options)
|
|
|
|
@end_point = "#{@scheme}://#{@host}#{@path}/"
|
|
|
|
@api_version = options[:vcloud_director_api_version] || Fog::Compute::VcloudDirector::Defaults::API_VERSION
|
|
|
|
end
|
|
|
|
|
|
|
|
def auth_token
|
|
|
|
@auth_token || Fog::Mock.random_base64(32)
|
|
|
|
end
|
|
|
|
|
|
|
|
def org_name
|
|
|
|
@org_name ||=
|
|
|
|
begin
|
|
|
|
username = @vcloud_director_username.split('@')
|
|
|
|
unless username.size == 2
|
|
|
|
Fog::Logger.warning('vcloud_director_username should be in the form user@org_name')
|
|
|
|
end
|
|
|
|
username.last || 'vcd_org_name'
|
|
|
|
end
|
|
|
|
end
|
2013-09-16 13:13:09 -04:00
|
|
|
|
2013-09-26 06:29:51 -04:00
|
|
|
def user_name
|
|
|
|
@user_name ||= @vcloud_director_username.split('@').first
|
|
|
|
end
|
|
|
|
|
2013-09-30 20:47:57 -04:00
|
|
|
def user_uuid
|
|
|
|
@user_uuid ||= uuid
|
|
|
|
end
|
|
|
|
|
2013-09-26 06:29:51 -04:00
|
|
|
def uuid
|
|
|
|
[8,4,4,4,12].map {|i| Fog::Mock.random_hex(i)}.join('-')
|
|
|
|
end
|
|
|
|
|
|
|
|
def add_id_from_href!(data={})
|
|
|
|
data[:id] = data[:href].split('/').last
|
|
|
|
end
|
|
|
|
|
|
|
|
private
|
|
|
|
|
|
|
|
def make_href(path)
|
|
|
|
"#{@end_point}#{path}"
|
|
|
|
end
|
|
|
|
|
2013-09-29 16:27:58 -04:00
|
|
|
def valid_uuid?(uuid)
|
|
|
|
/^[\da-f]{8}(?:-[\da-f]{4}){3}-[\da-f]{12}$/.match(uuid.downcase)
|
|
|
|
end
|
|
|
|
|
2013-09-26 06:29:51 -04:00
|
|
|
def xmlns
|
|
|
|
'http://www.vmware.com/vcloud/v1.5'
|
|
|
|
end
|
|
|
|
|
|
|
|
def xmlns_xsi
|
|
|
|
'http://www.w3.org/2001/XMLSchema-instance'
|
|
|
|
end
|
|
|
|
|
|
|
|
def xsi_schema_location
|
|
|
|
"http://www.vmware.com/vcloud/v1.5 http://#{@host}#{@path}/v1.5/schema/master.xsd"
|
|
|
|
end
|
2013-09-27 11:32:49 -04:00
|
|
|
|
2013-09-26 06:29:51 -04:00
|
|
|
end
|
2013-09-16 13:13:09 -04:00
|
|
|
end
|
|
|
|
end
|
|
|
|
end
|