2011-08-30 12:25:28 -07:00
require 'digest/sha2'
2011-08-29 20:04:04 -07: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 12:25:28 -07:00
recognizes :vsphere_rev , :vsphere_ssl , :vsphere_expected_pubkey_hash
2011-08-29 20:04:04 -07:00
2011-09-01 15:10:57 -07:00
model_path 'fog/vsphere/models/compute'
model :server
collection :servers
2013-09-27 11:48:31 +02:00
model :servertype
collection :servertypes
2012-11-14 15:31:02 +02:00
model :datacenter
collection :datacenters
model :interface
collection :interfaces
2013-09-27 11:48:31 +02:00
model :interfacetype
collection :interfacetypes
2012-11-14 15:31:02 +02:00
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 10:14:50 +01:00
model :customvalue
collection :customvalues
model :customfield
collection :customfields
2013-10-24 08:41:05 +02:00
model :scsicontroller
2011-09-01 15:10:57 -07:00
2011-08-30 13:44:31 -07:00
request_path 'fog/vsphere/requests/compute'
request :current_time
2011-09-01 15:10:57 -07:00
request :list_virtual_machines
2011-09-02 11:04:24 -07:00
request :vm_power_off
request :vm_power_on
request :vm_reboot
2011-09-06 16:46:42 -07:00
request :vm_clone
2011-09-06 16:41:37 -07:00
request :vm_destroy
2012-02-24 23:47:41 -05:00
request :vm_migrate
2012-11-14 15:31:02 +02: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 23:48:30 +08:00
request :list_templates
request :get_template
2012-11-14 15:31:02 +02:00
request :get_folder
request :list_folders
request :create_vm
request :list_vm_interfaces
2013-04-23 10:52:52 +02:00
request :modify_vm_interface
2013-10-28 17:57:55 -04:00
request :modify_vm_volume
2012-11-14 15:31:02 +02: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 12:34:36 +02:00
request :vm_config_vnc
2013-01-06 21:26:48 -05:00
request :create_folder
2013-09-27 11:48:31 +02:00
request :list_server_types
request :get_server_type
request :list_interface_types
request :get_interface_type
2013-02-15 10:14:50 +01:00
request :list_vm_customvalues
request :list_customfields
2013-10-24 08:41:05 +02:00
request :get_vm_first_scsi_controller
2013-11-19 10:49:08 +01:00
request :set_vm_customvalue
2011-09-10 13:27:52 -07:00
module Shared
attr_reader :vsphere_is_vcenter
attr_reader :vsphere_rev
2011-09-12 15:50:49 -07:00
attr_reader :vsphere_server
attr_reader :vsphere_username
2011-09-10 13:27:52 -07:00
2012-11-14 15:31:02 +02:00
protected
2011-10-17 21:46:30 -07:00
ATTR_TO_PROP = {
:id = > 'config.instanceUuid' ,
:name = > 'name' ,
:uuid = > 'config.uuid' ,
2013-03-31 09:43:06 -07:00
:template = > 'config.template' ,
:parent = > 'parent' ,
2011-10-17 21:46:30 -07: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-17 21:46:30 -07:00
:tools_state = > 'guest.toolsStatus' ,
:tools_version = > 'guest.toolsVersionStatus' ,
2012-03-28 10:20:27 +02:00
:memory_mb = > 'config.hardware.memoryMB' ,
:cpus = > 'config.hardware.numCPU' ,
2013-10-21 15:07:24 +02:00
:corespersocket = > 'config.hardware.numCoresPerSocket' ,
2012-11-14 15:31:02 +02:00
:overall_status = > 'overallStatus' ,
2013-09-27 11:48:31 +02:00
:guest_id = > 'config.guestId' ,
2011-10-17 21:46:30 -07:00
}
2013-03-31 09:43:06 -07: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 13:27:52 -07: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-17 21:46:30 -07:00
2012-05-14 14:45:03 -04:00
props = vm_mob_ref . collect! ( * ATTR_TO_PROP . values . uniq )
2013-03-31 09:43:06 -07: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 10:20:27 +02: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-17 21:46:30 -07: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 15:31:02 +02:00
if attrs [ 'hypervisor' ] . kind_of? ( RbVmomi :: VIM :: HostSystem )
2013-03-31 17:00:44 -07: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 17:00:44 -07:00
attrs [ 'mac_addresses' ] = Proc . new { vm_mob_ref . macs rescue nil }
2013-03-31 09:43:06 -07: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 11:23:23 -07:00
attrs [ 'relative_path' ] = ( attrs [ 'path' ] . split ( '/' ) . reject { | e | e . empty? } - [ " Datacenters " , attrs [ 'datacenter' ] , " vm " ] ) . join ( " / " ) rescue nil
2011-09-10 13:27:52 -07:00
end
end
2012-11-14 15:31:02 +02: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 09:59:53 +01:00
obj . to_s . match ( / \ ("([^"]+)" \ ) / ) [ 1 ]
2012-11-14 15:31:02 +02:00
end
2013-07-31 14:24:09 +02:00
def is_uuid? ( id )
! ( id =~ / [a-fA-F0-9]{8}-[a-fA-F0-9]{4}-[a-fA-F0-9]{4}-[a-fA-F0-9]{4}-[a-fA-F0-9]{12} / ) . nil?
end
2011-09-10 13:27:52 -07:00
end
2011-08-30 13:44:31 -07:00
2011-08-29 20:04:04 -07:00
class Mock
2011-09-10 13:27:52 -07:00
include Shared
2013-07-31 14:24:09 +02:00
def self . data
@data || = Hash . new do | hash , key |
hash [ key ] = {
:servers = > {
" 5032c8a5-9c5e-ba7a-3804-832a03e16381 " = > {
" resource_pool " = > " Resources " ,
" memory_mb " = > 2196 ,
" mac_addresses " = > { " Network adapter 1 " = > " 00:50:56:a9:00:28 " } ,
" power_state " = > " poweredOn " ,
" cpus " = > 1 ,
" hostname " = > " dhcp75-197.virt.bos.redhat.com " ,
" mo_ref " = > " vm-562 " ,
" connection_state " = > " connected " ,
" overall_status " = > " green " ,
" datacenter " = > " Solutions " ,
" volumes " = >
[ {
" id " = > " 6000C29c-a47d-4cd9-5249-c371de775f06 " ,
" datastore " = > " Storage1 " ,
" mode " = > " persistent " ,
" size " = > 8388608 ,
" thin " = > true ,
" name " = > " Hard disk 1 " ,
" filename " = > " [Storage1] rhel6-mfojtik/rhel6-mfojtik.vmdk " ,
" size_gb " = > 8
} ] ,
" interfaces " = >
[ { " mac " = > " 00:50:56:a9:00:28 " ,
" network " = > " VM Network " ,
" name " = > " Network adapter 1 " ,
" status " = > " ok " ,
" summary " = > " VM Network " ,
} ] ,
" hypervisor " = > " gunab.puppetlabs.lan " ,
" guest_id " = > " rhel6_64Guest " ,
" tools_state " = > " toolsOk " ,
" cluster " = > " Solutionscluster " ,
" name " = > " rhel64 " ,
" operatingsystem " = > " Red Hat Enterprise Linux 6 (64-bit) " ,
" path " = > " /Datacenters/Solutions/vm " ,
" uuid " = > " 4229f0e9-bfdc-d9a7-7bac-12070772e6dc " ,
" instance_uuid " = > " 5032c8a5-9c5e-ba7a-3804-832a03e16381 " ,
" id " = > " 5032c8a5-9c5e-ba7a-3804-832a03e16381 " ,
" tools_version " = > " guestToolsUnmanaged " ,
" ipaddress " = > " 192.168.100.184 " ,
" template " = > false
} ,
" 502916a3-b42e-17c7-43ce-b3206e9524dc " = > {
" resource_pool " = > " Resources " ,
" memory_mb " = > 512 ,
" power_state " = > " poweredOn " ,
" mac_addresses " = > { " Network adapter 1 " = > " 00:50:56:a9:00:00 " } ,
" hostname " = > nil ,
" cpus " = > 1 ,
" connection_state " = > " connected " ,
" mo_ref " = > " vm-621 " ,
" overall_status " = > " green " ,
" datacenter " = > " Solutions " ,
" volumes " = >
[ { " thin " = > false ,
" size_gb " = > 10 ,
" datastore " = > " datastore1 " ,
" filename " = > " [datastore1] i-1342439683/i-1342439683.vmdk " ,
" size " = > 10485762 ,
" name " = > " Hard disk 1 " ,
" mode " = > " persistent " ,
" id " = > " 6000C29b-f364-d073-8316-8e98ac0a0eae " } ] ,
" interfaces " = >
[ { " summary " = > " VM Network " ,
" mac " = > " 00:50:56:a9:00:00 " ,
" status " = > " ok " ,
" network " = > " VM Network " ,
" name " = > " Network adapter 1 " } ] ,
" hypervisor " = > " gunab.puppetlabs.lan " ,
" guest_id " = > nil ,
" cluster " = > " Solutionscluster " ,
" tools_state " = > " toolsNotInstalled " ,
" name " = > " i-1342439683 " ,
" operatingsystem " = > nil ,
" path " = > " / " ,
" tools_version " = > " guestToolsNotInstalled " ,
" uuid " = > " 4229e0de-30cb-ceb2-21f9-4d8d8beabb52 " ,
" instance_uuid " = > " 502916a3-b42e-17c7-43ce-b3206e9524dc " ,
" id " = > " 502916a3-b42e-17c7-43ce-b3206e9524dc " ,
" ipaddress " = > nil ,
" template " = > false
} ,
" 5029c440-85ee-c2a1-e9dd-b63e39364603 " = > {
" resource_pool " = > " Resources " ,
" memory_mb " = > 2196 ,
" power_state " = > " poweredOn " ,
" mac_addresses " = > { " Network adapter 1 " = > " 00:50:56:b2:00:af " } ,
" hostname " = > " centos56gm.localdomain " ,
" cpus " = > 1 ,
" connection_state " = > " connected " ,
" mo_ref " = > " vm-715 " ,
" overall_status " = > " green " ,
" datacenter " = > " Solutions " ,
" hypervisor " = > " gunab.puppetlabs.lan " ,
" guest_id " = > " rhel6_64Guest " ,
" cluster " = > " Solutionscluster " ,
" tools_state " = > " toolsOk " ,
" name " = > " jefftest " ,
" operatingsystem " = > " Red Hat Enterprise Linux 6 (64-bit) " ,
" path " = > " / " ,
" tools_version " = > " guestToolsUnmanaged " ,
" ipaddress " = > " 192.168.100.187 " ,
" uuid " = > " 42329da7-e8ab-29ec-1892-d6a4a964912a " ,
" instance_uuid " = > " 5029c440-85ee-c2a1-e9dd-b63e39364603 " ,
" id " = > " 5029c440-85ee-c2a1-e9dd-b63e39364603 " ,
" template " = > false
}
} ,
:datacenters = > {
" Solutions " = > { :name = > " Solutions " , :status = > " grey " }
}
}
end
end
2011-08-29 20:04:04 -07:00
def initialize ( options = { } )
2011-11-08 00:47:27 -05:00
require 'rbvmomi'
2011-08-30 14:55:21 -07:00
@vsphere_username = options [ :vsphere_username ]
2011-09-10 13:27:52 -07:00
@vsphere_password = 'REDACTED'
2011-08-30 14:55:21 -07:00
@vsphere_server = options [ :vsphere_server ]
@vsphere_expected_pubkey_hash = options [ :vsphere_expected_pubkey_hash ]
2011-09-10 13:27:52 -07:00
@vsphere_is_vcenter = true
@vsphere_rev = '4.0'
2011-08-29 20:04:04 -07:00
end
2013-07-31 14:24:09 +02:00
def data
self . class . data [ @vsphere_username ]
end
def reset_data
self . class . data . delete ( @vsphere_username )
end
2011-08-29 20:04:04 -07:00
end
class Real
2011-09-10 13:27:52 -07:00
include Shared
2011-08-30 12:25:28 -07:00
2011-08-29 20:04:04 -07: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 12:25:28 -07:00
@vsphere_expected_pubkey_hash = options [ :vsphere_expected_pubkey_hash ]
2011-08-29 20:04:04 -07:00
@vsphere_must_reauthenticate = false
2013-10-22 09:28:25 -07:00
@vsphere_is_vcenter = nil
2011-08-30 12:25:28 -07:00
@connection = nil
2013-10-22 09:28:25 -07:00
connect
negotiate_revision ( options [ :vsphere_rev ] )
authenticate
end
def reload
connect
# Check if the negotiation was ever run
if @vsphere_is_vcenter . nil?
negotiate
end
authenticate
end
private
def negotiate_revision ( revision = nil )
# Negotiate the API revision
if not revision
rev = @connection . serviceContent . about . apiVersion
@connection . rev = [ rev , ENV [ 'FOG_VSPHERE_REV' ] || '4.1' ] . min
end
@vsphere_is_vcenter = @connection . serviceContent . about . apiType == " VirtualCenter "
@vsphere_rev = @connection . rev
end
def connect
2011-08-30 12:25:28 -07:00
# 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 20:04:04 -07:00
end
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 12:25:28 -07: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 20:04:04 -07:00
end
end
end
end