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_director/compute.rb
2015-05-04 14:35:47 -07:00

889 lines
31 KiB
Ruby

require 'fog/vcloud_director/core'
require 'fog/vcloud_director/query'
class VcloudDirectorParser < Fog::Parsers::Base
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
attributes[attribute.localname.to_sym] = attribute.value
end
else
attribute = attributes_xml.shift
attributes[attribute.localname.to_sym] = attribute.value
end
end
attributes
end
end
class NonLoaded
end
module Fog
module Compute
class VcloudDirector < Fog::Service
module Defaults
PATH = '/api'
PORT = 443
SCHEME = 'https'
API_VERSION = '5.1'
end
class ServiceError < Fog::VcloudDirector::Errors::ServiceError; end
class BadRequest < Fog::VcloudDirector::Errors::BadRequest; end
class Unauthorized < Fog::VcloudDirector::Errors::Unauthorized; end
class Forbidden < Fog::VcloudDirector::Errors::Forbidden; end
class Conflict < Fog::VcloudDirector::Errors::Conflict; end
class MalformedResponse < Fog::VcloudDirector::Errors::MalformedResponse; end
class DuplicateName < Fog::VcloudDirector::Errors::DuplicateName; end
class TaskError < Fog::VcloudDirector::Errors::TaskError; end
requires :vcloud_director_username, :vcloud_director_password, :vcloud_director_host
recognizes :vcloud_director_api_version, :vcloud_director_show_progress, :path, :vcloud_token
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 :custom_field
collection :custom_fields
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
model :media
collection :medias # sic
request_path 'fog/vcloud_director/requests/compute'
request :delete_catalog_item
request :delete_catalog_item_metadata_item_metadata
request :delete_disk
request :delete_disk_metadata_item_metadata
request :delete_logout
request :delete_media
request :delete_media_metadata_item_metadata
request :delete_network
request :delete_shadow_vm
request :delete_vapp
request :delete_vapp_metadata_item_metadata
request :delete_vapp_template
request :delete_vapp_template_metadata_item_metadata
request :get_allocated_ip_addresses
request :get_catalog
request :get_catalog_item
request :get_catalog_item_metadata
request :get_catalog_item_metadata_item_metadata
request :get_catalog_metadata
request :get_catalog_metadata_item_metadata
request :get_catalogs_from_query
request :get_control_access_params_catalog
request :get_control_access_params_vapp
request :get_cpu_rasd_item
request :get_current_session
request :get_disk
request :get_disk_metadata
request :get_disk_metadata_item_metadata
request :get_disk_owner
request :get_disks_from_query
request :get_disks_rasd_items_list
request :get_edge_gateway
request :get_entity
request :get_execute_query
request :get_groups_from_query
request :get_guest_customization_system_section_vapp
request :get_guest_customization_system_section_vapp_template
request :get_href # this is used for manual testing
request :get_lease_settings_section_vapp
request :get_lease_settings_section_vapp_template
request :get_media
request :get_media_drives_rasd_items_list
request :get_media_metadata
request :get_media_metadata_item_metadata
request :get_media_owner
request :get_medias_from_query
request :get_memory_rasd_item
request :get_metadata
request :get_network
request :get_network_cards_items_list
request :get_network_complete
request :get_network_config_section_vapp
request :get_network_config_section_vapp_template
request :get_network_connection_system_section_vapp
request :get_network_connection_system_section_vapp_template
request :get_network_metadata
request :get_network_metadata_item_metadata
request :get_network_section_vapp
request :get_network_section_vapp_template
request :get_operating_system_section
request :get_org_settings
request :get_org_vdc_gateways
request :get_organization
request :get_organization_metadata
request :get_organization_metadata_item_metadata
request :get_organizations
request :get_organizations_from_query
request :get_product_sections_vapp
request :get_product_sections_vapp_template
request :get_request # this is used for manual testing
request :get_runtime_info_section_type
request :get_serial_ports_items_list
request :get_shadow_vm
request :get_snapshot_section
request :get_startup_section
request :get_supported_systems_info
request :get_supported_versions
request :get_task
request :get_task_list
request :get_thumbnail
request :get_users_from_query
request :get_vapp
request :get_vapp_metadata
request :get_vapp_metadata_item_metadata
request :get_vapp_ovf_descriptor
request :get_vapp_owner
request :get_vapp_template
request :get_vapp_template_customization_system_section
request :get_vapp_template_metadata
request :get_vapp_template_metadata_item_metadata
request :get_vapp_template_ovf_descriptor
request :get_vapp_template_owner
request :get_vapp_templates_from_query
request :get_vapps_in_lease_from_query
request :get_vcloud
request :get_vdc
request :get_vdc_metadata
request :get_vdc_metadata_item_metadata
request :get_vdc_storage_class
request :get_vdc_storage_class_metadata
request :get_vdc_storage_class_metadata_item_metadata
request :get_vdcs_from_query
request :get_virtual_hardware_section
request :get_vm
request :get_vm_capabilities
request :get_vm_compliance_results
request :get_vm_customization
request :get_vm_disks
request :get_vm_network
request :get_vm_pending_question
request :get_vms
request :get_vms_by_metadata
request :get_vms_disk_attached_to
request :get_vms_in_lease_from_query
request :instantiate_vapp_template # to be deprecated
request :post_acquire_ticket
request :post_answer_vm_pending_question
request :post_attach_disk
request :post_cancel_task
request :post_capture_vapp
request :post_check_vm_compliance
request :post_clone_media
request :post_clone_vapp
request :post_clone_vapp_template
request :post_configure_edge_gateway_services
request :post_consolidate_vm_vapp
request :post_consolidate_vm_vapp_template
request :post_create_catalog_item
request :post_create_org_vdc_network
request :post_deploy_vapp
request :post_detach_disk
request :post_disable_nested_hv
request :post_disable_vapp_template_download
request :post_discard_vapp_state
request :post_eject_cd_rom
request :post_enable_nested_hv
request :post_enable_vapp_template_download
request :post_enter_maintenance_mode
request :post_exit_maintenance_mode
request :post_insert_cd_rom
request :post_install_vmware_tools
request :post_instantiate_vapp_template
request :post_login_session
request :post_power_off_vapp
request :post_power_on_vapp
request :post_reboot_vapp
request :post_remove_all_snapshots
request :post_reset_vapp
request :post_revert_snapshot
request :post_shutdown_vapp
request :post_suspend_vapp
request :post_undeploy_vapp
request :post_update_catalog_item_metadata
request :post_update_disk_metadata
request :post_update_media_metadata
request :post_update_vapp_metadata
request :post_update_vapp_template_metadata
request :post_upgrade_hw_version
request :post_upload_disk
request :post_upload_media
request :post_upload_vapp_template
request :put_catalog_item_metadata_item_metadata
request :put_cpu
request :put_disk_metadata_item_metadata
request :put_disks
request :put_guest_customization_section_vapp
request :put_media_metadata_item_metadata
request :put_memory
request :put_metadata_value # deprecated
request :put_network
request :put_network_connection_system_section_vapp
request :put_product_sections
request :put_vapp_metadata_item_metadata
request :put_vapp_name_and_description
request :put_vapp_template_metadata_item_metadata
request :put_vm
request :put_vm_capabilities
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.find {|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
end
class Real
extend Fog::Deprecation
deprecate :auth_token, :vcloud_token
attr_reader :end_point, :api_version, :show_progress
alias_method :show_progress?, :show_progress
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::XML::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
@show_progress = options[:vcloud_director_show_progress]
@show_progress = $stdin.tty? if @show_progress.nil?
@vcloud_token = options[:vcloud_token]
end
def vcloud_token
login if @vcloud_token.nil?
@vcloud_token
end
def org_name
login if @org_name.nil?
@org_name
end
def user_name
login if @user_name.nil?
@user_name
end
def reload
@connection.reset
end
def request(params)
begin
do_request(params)
rescue EOFError
# This error can occur if Vcloud receives a request from a network
# it deems to be unauthorized; no HTTP response is sent, but the
# connection is sent a signal to terminate early.
raise(
MalformedResponse, "Connection unexpectedly terminated by vcloud"
)
# this is to know if Excon::Errors::Unauthorized really happens
#rescue Excon::Errors::Unauthorized
# login
# do_request(params)
end
end
# @api private
def do_request(params)
headers = {
'Accept' => "application/*+xml;version=#{@api_version}",
'x-vcloud-authorization' => vcloud_token
}
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] || {}),
:idempotent => params[:idempotent],
:method => params[:method],
:parser => params[:parser],
:path => path,
:query => params[:query]
})
rescue Excon::Errors::HTTPStatusError => error
raise case error
when Excon::Errors::BadRequest then BadRequest.slurp(error);
when Excon::Errors::Unauthorized then Unauthorized.slurp(error);
when Excon::Errors::Forbidden then Forbidden.slurp(error);
when Excon::Errors::Conflict then Conflict.slurp(error);
else ServiceError.slurp(error)
end
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 TaskError.new "status: #{task.status}, error: #{task.error}" unless task.success?
end
def add_id_from_href!(data={})
data[:id] = data[:href].split('/').last
end
# Compensate for Fog::ToHashDocument shortcomings.
# @api private
# @param [Hash] hash
# @param [String,Symbol] key1
# @param [String,Symbol] key2
# @return [Hash]
def ensure_list!(hash, key1, key2=nil)
if key2.nil?
hash[key1] ||= []
hash[key1] = [hash[key1]] if hash[key1].is_a?(Hash)
else
hash[key1] ||= {key2 => []}
hash[key1] = {key2 => []} if hash[key1].empty?
hash[key1][key2] = [hash[key1][key2]] if hash[key1][key2].is_a?(Hash)
end
hash
end
private
def login
@vcloud_token ||= ENV['FOG_VCLOUD_TOKEN']
if @vcloud_token
response = get_current_session
session_org = response.body[:org]
session_user = response.body[:user]
check_session_matches_credentials(session_org, session_user)
else
response = post_login_session
x_vcloud_authorization = response.headers.keys.find do |key|
key.downcase == 'x-vcloud-authorization'
end
@vcloud_token = response.headers[x_vcloud_authorization]
end
@org_name = response.body[:org]
@user_name = response.body[:user]
end
# @note This isn't needed.
def logout
delete_logout
@vcloud_token = nil
@org_name = nil
end
def check_session_matches_credentials(session_org, session_user)
fog_credential_org = @vcloud_director_username.split('@').last
fog_credential_user = @vcloud_director_username.split('@')[0...-1].join('@')
if session_org != fog_credential_org
raise Fog::Errors::Error.new "FOG_CREDENTIAL specified is for vCloud organisation '#{fog_credential_org}' but " +
"your current session is for '#{session_org}'. You should generate a new FOG_VCLOUD_TOKEN."
end
if session_user != fog_credential_user
raise Fog::Errors::Error.new "FOG_CREDENTIAL specified is for user '#{fog_credential_user}' but " +
"your current session is for '#{session_user}'. You should generate a new FOG_VCLOUD_TOKEN."
end
end
end
class Mock
attr_reader :end_point, :api_version
def data
@@data ||= Hash.new do |hash, key|
vdc1_uuid = uuid
vdc2_uuid = uuid
default_network_uuid = uuid
uplink_network_uuid = uuid
isolated_vdc1_network_uuid = uuid
isolated_vdc2_network_uuid = uuid
vapp1_id = "vapp-#{uuid}"
vapp2_id = "vapp-#{uuid}"
vapp1vm1_id = "vm-#{uuid}"
vapp2vm1_id = "vm-#{uuid}"
vapp2vm2_id = "vm-#{uuid}"
catalog_uuid = uuid
hash[key] = {
:catalogs => {
catalog_uuid => {
:name => 'Default Catalog'
}
},
:catalog_items => {
uuid => {
:type => 'vAppTemplate',
:name => 'vAppTemplate 1',
:catalog => catalog_uuid,
}
},
:disks => {},
:edge_gateways => {
uuid => {
:name => 'MockEdgeGateway',
:networks => [uplink_network_uuid, default_network_uuid],
:vdc => vdc1_uuid,
:Configuration => {
:GatewayBackingConfig => "compact",
:GatewayInterfaces => {
:GatewayInterface => []},
:EdgeGatewayServiceConfiguration => {
:FirewallService => {
:IsEnabled => "true",
:DefaultAction => "drop",
:LogDefaultAction => "false",
:FirewallRule => []
},
:NatService => {:IsEnabled => "true"}},
:HaEnabled => "false",
:UseDefaultRouteForDnsRelay => "false"
}
}
},
:medias => {},
:networks => {
default_network_uuid => {
:IsShared => true,
:vdc => vdc1_uuid,
:FenceMode => 'natRouted',
:ApplyRateLimit => "false",
:Description => 'Org Network for mocking',
:Dns1 => '8.8.8.8',
:Dns2 => '8.8.4.4',
:DnsSuffix => 'example.com',
:Gateway => '192.168.1.1',
:InterfaceType => "internal",
:IpRanges => [{
:StartAddress=>'192.168.1.2',
:EndAddress=>'192.168.1.254'
}],
:IsInherited => false,
:Netmask => '255.255.255.0',
:name => 'vDC1 Default Network',
:SubnetParticipation => {
:Gateway => "192.168.1.0",
:Netmask => "255.255.0.0",
:IpAddress => "192.168.1.0"
},
:UseForDefaultRoute => "false"
},
isolated_vdc1_network_uuid => {
:IsShared => false,
:vdc => vdc1_uuid,
:ApplyRateLimit => "false",
:FenceMode => 'isolated',
:Description => 'Org Network for mocking',
:Dns1 => '8.8.8.8',
:Dns2 => '8.8.4.4',
:DnsSuffix => 'example.com',
:InterfaceType => "internal",
:IpRanges => [{
:StartAddress=>'10.1.0.100',
:EndAddress=>'10.1.0.200'
}],
:IsInherited => false,
:Netmask => '255.255.255.0',
:name => 'vDC1 backend network',
:SubnetParticipation => {
:Gateway => "192.168.1.0",
:Netmask => "255.255.0.0",
:IpAddress => "192.168.1.0"
},
:UseForDefaultRoute => "false"
},
isolated_vdc2_network_uuid => {
:IsShared => false,
:vdc => vdc2_uuid,
:ApplyRateLimit => "false",
:Description => 'Org Network for mocking',
:Dns1 => '8.8.8.8',
:Dns2 => '8.8.4.4',
:DnsSuffix => 'example.com',
:InterfaceType => "internal",
:IpRanges => [{
:StartAddress=>'10.2.0.100',
:EndAddress=>'10.2.0.200'
}],
:IsInherited => false,
:Netmask => '255.255.255.0',
:name => 'vDC2 backend network',
:SubnetParticipation => {
:Gateway => "192.168.1.0",
:Netmask => "255.255.0.0",
:IpAddress => "192.168.1.0"
},
:UseForDefaultRoute => "false"
},
uplink_network_uuid => {
:IsShared => false,
:vdc => vdc1_uuid,
:ApplyRateLimit => "false",
:FenceMode => 'bridged',
:Description => 'Uplink Network for mocking',
:Dns1 => '8.8.8.8',
:Dns2 => '8.8.4.4',
:DnsSuffix => 'example.com',
:Gateway => '198.51.100.1',
:InterfaceType => "uplink",
:IpRanges => [{
:StartAddress=>'198.51.100.2',
:EndAddress=>'198.51.100.254'
}],
:IsInherited => false,
:Netmask => '255.255.255.0',
:name => 'uplink Network',
:SubnetParticipation => {
:Gateway => "198.51.100.81",
:Netmask => "255.255.255.248",
:IpAddress => "198.51.100.83",
:IpRanges => {
:IpRange => {
:StartAddress => "198.51.100.84",
:EndAddress => "198.51.100.86"
}
}
},
:UseForDefaultRoute => "true"
}
},
:org => {
:description => 'Organization for mocking',
:full_name => 'Mock Organization',
:name => org_name,
:uuid => uuid
},
:tasks => {},
:vapps => {
vapp1_id => {
:name => 'mock-vapp-1',
:vdc_id => vdc1_uuid,
:description => "Mock vApp 1",
:networks => [
{ :parent_id => default_network_uuid, },
],
},
vapp2_id => {
:name => 'mock-vapp-2',
:vdc_id => vdc2_uuid,
:description => "Mock vApp 2",
:networks => [
{ :parent_id => default_network_uuid },
]
},
},
:vdc_storage_classes => {
uuid => {
:default => true,
:enabled => true,
:limit => 2 * 1024 * 1024,
:name => 'DefaultMockStorageClass',
:units => 'MB',
:vdc => vdc1_uuid,
}
},
:vdcs => {
vdc1_uuid => {
:description => 'vDC1 for mocking',
:name => 'MockVDC 1'
},
vdc2_uuid => {
:description => 'vDC2 for mocking',
:name => 'MockVDC 2'
},
},
:vms => {
vapp1vm1_id => {
:name => 'mock-vm-1-1',
:parent_vapp => vapp1_id,
:nics => [
{
:network_name => 'Default Network',
:mac_address => "00:50:56:aa:bb:01",
:ip_address => "192.168.1.33",
},
],
},
vapp2vm1_id => {
:name => 'mock-vm-2-1',
:parent_vapp => vapp2_id,
:nics => [
{
:network_name => 'Default Network',
:mac_address => "00:50:56:aa:bb:02",
:ip_address => "192.168.1.34",
},
],
},
vapp2vm2_id => {
:name => 'mock-vm-2-2',
:parent_vapp => vapp2_id,
:nics => [
{
:network_name => 'Default Network',
:mac_address => "00:50:56:aa:bb:03",
:ip_address => "192.168.1.35",
},
],
},
},
}
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::XML::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
@vcloud_token = options[:vcloud_token]
end
def vcloud_token
@vcloud_token || Fog::Mock.random_base64(32)
end
def org_name
@org_name ||=
begin
username = @vcloud_director_username.split('@')
if username.size >= 2 # may be 'email_address@org_name'
username.last
else
Fog::Logger.warning('vcloud_director_username should be in the form user@org_name')
'vcd_org_name'
end
end
end
def user_name
@user_name ||= @vcloud_director_username.split('@').first
end
def user_uuid
@user_uuid ||= uuid
end
def uuid
[8,4,4,4,12].map {|i| Fog::Mock.random_hex(i)}.join('-')
end
# Create a task.
#
# @param [String] operation A message describing the operation that is
# tracked by this task.
# @param [String] operation_name The short name of the operation that
# is tracked by this task.
# @param [Hash] owner Reference to the owner of the task. This is
# typically the object that the task is creating or updating.
# * :href<~String> - Contains the URI to the entity.
# * :type<~String> - Contains the MIME type of the entity.
# @return [String] Object identifier of the task.
def enqueue_task(operation, operation_name, owner, options={})
task_id = uuid
now = DateTime.now
data[:tasks][task_id] = {
:cancel_requested => false,
:details => '',
:expiry_time => now + 86400, # in 24 hours
:name => 'task',
:progress => 1,
:service_namespace => 'com.vmware.vcloud',
:start_time => now,
:end_time => now + 86400,
:status => 'running',
}.merge(options).merge(
:operation => operation,
:operation_name => operation_name,
:owner => owner
)
task_id
end
# @note As far as this method is concerned, the only possible task
# states are 'running' and 'success'.
#
# @param [Hash] response_body
# @return [Boolean]
def process_task(response_body)
task_id = response_body[:href].split('/').last
task = data[:tasks][task_id]
if task[:status] == 'running'
task[:end_time] = DateTime.now
task[:progress] = 100
task[:status] = 'success'
if task[:on_success]
task[:on_success].call
task.delete(:on_success)
end
end
return true if task[:status] == 'success'
raise TaskError.new "status: #{task[:status]}, error: #{task[:Error]}"
end
def add_id_from_href!(data={})
data[:id] = data[:href].split('/').last
end
private
def make_href(path)
"#{@end_point}#{path}"
end
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
end
end
end
end