2011-08-30 15:25:28 -04:00
require 'digest/sha2'
2011-08-29 23:04:04 -04:00
module Fog
module Compute
class Vsphere < Fog :: Service
requires :vsphere_username , :vsphere_password , :vsphere_server
recognizes :vsphere_port , :vsphere_path , :vsphere_ns
2011-08-30 15:25:28 -04:00
recognizes :vsphere_rev , :vsphere_ssl , :vsphere_expected_pubkey_hash
2011-08-29 23:04:04 -04:00
2011-09-01 18:10:57 -04:00
model_path 'fog/vsphere/models/compute'
model :server
collection :servers
2012-11-14 08:31:02 -05:00
model :datacenter
collection :datacenters
model :interface
collection :interfaces
model :volume
collection :volumes
model :template
collection :templates
model :cluster
collection :clusters
model :resource_pool
collection :resource_pools
model :network
collection :networks
model :datastore
collection :datastores
model :folder
collection :folders
2013-02-15 04:14:50 -05:00
model :customvalue
collection :customvalues
model :customfield
collection :customfields
2011-09-01 18:10:57 -04:00
2011-08-30 16:44:31 -04:00
request_path 'fog/vsphere/requests/compute'
request :current_time
2011-09-01 18:10:57 -04:00
request :list_virtual_machines
2011-09-02 14:04:24 -04:00
request :vm_power_off
request :vm_power_on
request :vm_reboot
2011-09-06 19:46:42 -04:00
request :vm_clone
2011-09-06 19:41:37 -04:00
request :vm_destroy
2012-02-24 23:47:41 -05:00
request :vm_migrate
2012-11-14 08:31:02 -05:00
request :list_datacenters
request :get_datacenter
request :list_clusters
request :get_cluster
request :list_resource_pools
request :get_resource_pool
request :list_networks
request :get_network
request :list_datastores
request :get_datastore
2013-03-21 11:48:30 -04:00
request :list_templates
request :get_template
2012-11-14 08:31:02 -05:00
request :get_folder
request :list_folders
request :create_vm
request :list_vm_interfaces
2013-04-23 04:52:52 -04:00
request :modify_vm_interface
2012-11-14 08:31:02 -05:00
request :list_vm_volumes
request :get_virtual_machine
2012-01-31 14:43:45 -05:00
request :vm_reconfig_hardware
request :vm_reconfig_memory
request :vm_reconfig_cpus
2012-03-28 06:34:36 -04:00
request :vm_config_vnc
2013-01-06 21:26:48 -05:00
request :create_folder
2013-02-15 04:14:50 -05:00
request :list_vm_customvalues
request :list_customfields
2011-09-10 16:27:52 -04:00
module Shared
attr_reader :vsphere_is_vcenter
attr_reader :vsphere_rev
2011-09-12 18:50:49 -04:00
attr_reader :vsphere_server
attr_reader :vsphere_username
2011-09-10 16:27:52 -04:00
2012-11-14 08:31:02 -05:00
protected
2011-10-18 00:46:30 -04:00
ATTR_TO_PROP = {
:id = > 'config.instanceUuid' ,
:name = > 'name' ,
:uuid = > 'config.uuid' ,
2013-03-31 12:43:06 -04:00
:template = > 'config.template' ,
:parent = > 'parent' ,
2011-10-18 00:46:30 -04:00
:hostname = > 'summary.guest.hostName' ,
:operatingsystem = > 'summary.guest.guestFullName' ,
:ipaddress = > 'guest.ipAddress' ,
:power_state = > 'runtime.powerState' ,
:connection_state = > 'runtime.connectionState' ,
2011-11-07 07:08:54 -05:00
:hypervisor = > 'runtime.host' ,
2011-10-18 00:46:30 -04:00
:tools_state = > 'guest.toolsStatus' ,
:tools_version = > 'guest.toolsVersionStatus' ,
2012-03-28 04:20:27 -04:00
:memory_mb = > 'config.hardware.memoryMB' ,
:cpus = > 'config.hardware.numCPU' ,
2012-11-14 08:31:02 -05:00
:overall_status = > 'overallStatus' ,
:guest_id = > 'summary.guest.guestId' ,
2011-10-18 00:46:30 -04:00
}
2013-03-31 12:43:06 -04:00
def convert_vm_view_to_attr_hash ( vms )
vms = @connection . serviceContent . propertyCollector . collectMultiple ( vms , * ATTR_TO_PROP . values . uniq )
vms . map { | vm | props_to_attr_hash ( * vm ) }
end
2011-09-10 16:27:52 -04:00
# Utility method to convert a VMware managed object into an attribute hash.
# This should only really be necessary for the real class.
# This method is expected to be called by the request methods
# in order to massage VMware Managed Object References into Attribute Hashes.
def convert_vm_mob_ref_to_attr_hash ( vm_mob_ref )
return nil unless vm_mob_ref
2011-10-18 00:46:30 -04:00
2012-05-14 14:45:03 -04:00
props = vm_mob_ref . collect! ( * ATTR_TO_PROP . values . uniq )
2013-03-31 12:43:06 -04:00
props_to_attr_hash vm_mob_ref , props
end
def props_to_attr_hash vm_mob_ref , props
2011-11-07 07:08:54 -05:00
# NOTE: Object.tap is in 1.8.7 and later.
# Here we create the hash object that this method returns, but first we need
# to add a few more attributes that require additional calls to the vSphere
# API. The hypervisor name and mac_addresses attributes may not be available
# so we need catch any exceptions thrown during lookup and set them to nil.
#
2012-03-28 04:20:27 -04:00
# The use of the "tap" method here is a convenience, it allows us to update the
# hash object without explicitly returning the hash at the end of the method.
2011-10-18 00:46:30 -04:00
Hash [ ATTR_TO_PROP . map { | k , v | [ k . to_s , props [ v ] ] } ] . tap do | attrs |
attrs [ 'id' ] || = vm_mob_ref . _ref
attrs [ 'mo_ref' ] = vm_mob_ref . _ref
2011-11-07 07:08:54 -05:00
# The name method "magically" appears after a VM is ready and
# finished cloning.
2012-11-14 08:31:02 -05:00
if attrs [ 'hypervisor' ] . kind_of? ( RbVmomi :: VIM :: HostSystem )
2013-03-31 20:00:44 -04:00
host = attrs [ 'hypervisor' ]
attrs [ 'datacenter' ] = Proc . new { parent_attribute ( host . path , :datacenter ) [ 1 ] rescue nil }
attrs [ 'cluster' ] = Proc . new { parent_attribute ( host . path , :cluster ) [ 1 ] rescue nil }
attrs [ 'hypervisor' ] = Proc . new { host . name rescue nil }
attrs [ 'resource_pool' ] = Proc . new { ( vm_mob_ref . resourcePool || host . resourcePool ) . name rescue nil }
2011-11-07 07:08:54 -05:00
end
# This inline rescue catches any standard error. While a VM is
# cloning, a call to the macs method will throw and NoMethodError
2013-03-31 20:00:44 -04:00
attrs [ 'mac_addresses' ] = Proc . new { vm_mob_ref . macs rescue nil }
2013-03-31 12:43:06 -04:00
# Rescue nil to catch testing while vm_mob_ref isn't reaL??
attrs [ 'path' ] = " / " + attrs [ 'parent' ] . path . map ( & :last ) . join ( '/' ) rescue nil
2012-12-01 13:23:23 -05:00
attrs [ 'relative_path' ] = ( attrs [ 'path' ] . split ( '/' ) . reject { | e | e . empty? } - [ " Datacenters " , attrs [ 'datacenter' ] , " vm " ] ) . join ( " / " ) rescue nil
2011-09-10 16:27:52 -04:00
end
end
2012-11-14 08:31:02 -05:00
# returns the parent object based on a type
# provides both real RbVmomi object and its name.
# e.g.
#[Datacenter("datacenter-2"), "dc-name"]
def parent_attribute path , type
element = case type
when :datacenter
RbVmomi :: VIM :: Datacenter
when :cluster
RbVmomi :: VIM :: ClusterComputeResource
when :host
RbVmomi :: VIM :: HostSystem
else
raise " Unknown type "
end
path . select { | x | x [ 0 ] . is_a? element } . flatten
rescue
nil
end
# returns vmware managed obj id string
def managed_obj_id obj
2013-03-20 04:59:53 -04:00
obj . to_s . match ( / \ ("([^"]+)" \ ) / ) [ 1 ]
2012-11-14 08:31:02 -05:00
end
2011-09-10 16:27:52 -04:00
end
2011-08-30 16:44:31 -04:00
2011-08-29 23:04:04 -04:00
class Mock
2011-09-10 16:27:52 -04:00
include Shared
2011-08-29 23:04:04 -04:00
def initialize ( options = { } )
2011-11-08 00:47:27 -05:00
require 'rbvmomi'
2011-08-30 17:55:21 -04:00
@vsphere_username = options [ :vsphere_username ]
2011-09-10 16:27:52 -04:00
@vsphere_password = 'REDACTED'
2011-08-30 17:55:21 -04:00
@vsphere_server = options [ :vsphere_server ]
@vsphere_expected_pubkey_hash = options [ :vsphere_expected_pubkey_hash ]
2011-09-10 16:27:52 -04:00
@vsphere_is_vcenter = true
@vsphere_rev = '4.0'
2011-08-29 23:04:04 -04:00
end
end
class Real
2011-09-10 16:27:52 -04:00
include Shared
2011-08-30 15:25:28 -04:00
2011-08-29 23:04:04 -04:00
def initialize ( options = { } )
require 'rbvmomi'
@vsphere_username = options [ :vsphere_username ]
@vsphere_password = options [ :vsphere_password ]
@vsphere_server = options [ :vsphere_server ]
@vsphere_port = options [ :vsphere_port ] || 443
@vsphere_path = options [ :vsphere_path ] || '/sdk'
@vsphere_ns = options [ :vsphere_ns ] || 'urn:vim25'
@vsphere_rev = options [ :vsphere_rev ] || '4.0'
@vsphere_ssl = options [ :vsphere_ssl ] || true
2011-08-30 15:25:28 -04:00
@vsphere_expected_pubkey_hash = options [ :vsphere_expected_pubkey_hash ]
2011-08-29 23:04:04 -04:00
@vsphere_must_reauthenticate = false
2011-08-30 15:25:28 -04:00
@connection = nil
# This is a state variable to allow digest validation of the SSL cert
bad_cert = false
loop do
begin
@connection = RbVmomi :: VIM . new :host = > @vsphere_server ,
:port = > @vsphere_port ,
:path = > @vsphere_path ,
:ns = > @vsphere_ns ,
:rev = > @vsphere_rev ,
:ssl = > @vsphere_ssl ,
:insecure = > bad_cert
break
rescue OpenSSL :: SSL :: SSLError
raise if bad_cert
bad_cert = true
end
end
if bad_cert then
validate_ssl_connection
end
2011-08-29 23:04:04 -04:00
# Negotiate the API revision
if not options [ :vsphere_rev ]
rev = @connection . serviceContent . about . apiVersion
@connection . rev = [ rev , ENV [ 'FOG_VSPHERE_REV' ] || '4.1' ] . min
end
@vsphere_is_vcenter = @connection . serviceContent . about . apiType == " VirtualCenter "
2011-09-12 18:50:49 -04:00
@vsphere_rev = @connection . rev
2011-08-29 23:04:04 -04:00
authenticate
end
private
def authenticate
begin
@connection . serviceContent . sessionManager . Login :userName = > @vsphere_username ,
:password = > @vsphere_password
rescue RbVmomi :: VIM :: InvalidLogin = > e
raise Fog :: Vsphere :: Errors :: ServiceError , e . message
end
end
2011-08-30 15:25:28 -04:00
# Verify a SSL certificate based on the hashed public key
def validate_ssl_connection
pubkey = @connection . http . peer_cert . public_key
pubkey_hash = Digest :: SHA2 . hexdigest ( pubkey . to_s )
expected_pubkey_hash = @vsphere_expected_pubkey_hash
if pubkey_hash != expected_pubkey_hash then
raise Fog :: Vsphere :: Errors :: SecurityError , " The remote system presented a public key with hash #{ pubkey_hash } but we're expecting a hash of #{ expected_pubkey_hash || '<unset>' } . If you are sure the remote system is authentic set vsphere_expected_pubkey_hash: <the hash printed in this message> in ~/.fog "
end
end
2011-08-29 23:04:04 -04:00
end
end
end
end