mirror of
https://github.com/fog/fog.git
synced 2022-11-09 13:51:43 -05:00
19cf9d7784
Without this patch it was difficult to figure out from the outside what vSphere server Fog connected to. The application using Fog should be able to easily print out the connection information without breaking the encapsulation Fog provides. This patch makes the connected vSphere server hostname and API username an attribute of the connection instance. The tests have been updated to validate these attribute accessor methods.
160 lines
6 KiB
Ruby
160 lines
6 KiB
Ruby
require 'digest/sha2'
|
|
|
|
module Fog
|
|
module Compute
|
|
class Vsphere < Fog::Service
|
|
|
|
requires :vsphere_username, :vsphere_password, :vsphere_server
|
|
recognizes :vsphere_port, :vsphere_path, :vsphere_ns
|
|
recognizes :vsphere_rev, :vsphere_ssl, :vsphere_expected_pubkey_hash
|
|
|
|
model_path 'fog/vsphere/models/compute'
|
|
model :server
|
|
collection :servers
|
|
|
|
request_path 'fog/vsphere/requests/compute'
|
|
request :current_time
|
|
request :find_vm_by_ref
|
|
request :list_virtual_machines
|
|
request :vm_power_off
|
|
request :vm_power_on
|
|
request :vm_reboot
|
|
request :vm_clone
|
|
request :vm_destroy
|
|
|
|
module Shared
|
|
|
|
attr_reader :vsphere_is_vcenter
|
|
attr_reader :vsphere_rev
|
|
attr_reader :vsphere_server
|
|
attr_reader :vsphere_username
|
|
|
|
# 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
|
|
# A cloning VM doesn't have a configuration yet. Unfortuantely we just get
|
|
# a RunTime exception.
|
|
begin
|
|
is_ready = vm_mob_ref.config ? true : false
|
|
rescue RuntimeError
|
|
is_ready = nil
|
|
end
|
|
{
|
|
'id' => is_ready ? vm_mob_ref.config.instanceUuid : vm_mob_ref._ref,
|
|
'mo_ref' => vm_mob_ref._ref,
|
|
'name' => vm_mob_ref.name,
|
|
'uuid' => is_ready ? vm_mob_ref.config.uuid : nil,
|
|
'instance_uuid' => is_ready ? vm_mob_ref.config.instanceUuid : nil,
|
|
'hostname' => vm_mob_ref.summary.guest.hostName,
|
|
'operatingsystem' => vm_mob_ref.summary.guest.guestFullName,
|
|
'ipaddress' => vm_mob_ref.summary.guest.ipAddress,
|
|
'power_state' => vm_mob_ref.runtime.powerState,
|
|
'connection_state' => vm_mob_ref.runtime.connectionState,
|
|
'hypervisor' => vm_mob_ref.runtime.host ? vm_mob_ref.runtime.host.name : nil,
|
|
'tools_state' => vm_mob_ref.summary.guest.toolsStatus,
|
|
'tools_version' => vm_mob_ref.summary.guest.toolsVersionStatus,
|
|
'mac_addresses' => is_ready ? vm_mob_ref.macs : nil,
|
|
'is_a_template' => is_ready ? vm_mob_ref.config.template : nil
|
|
}
|
|
end
|
|
|
|
end
|
|
|
|
class Mock
|
|
|
|
include Shared
|
|
|
|
def initialize(options={})
|
|
require 'mocha'
|
|
@vsphere_username = options[:vsphere_username]
|
|
@vsphere_password = 'REDACTED'
|
|
@vsphere_server = options[:vsphere_server]
|
|
@vsphere_expected_pubkey_hash = options[:vsphere_expected_pubkey_hash]
|
|
@vsphere_is_vcenter = true
|
|
@vsphere_rev = '4.0'
|
|
@connection = Mocha::Mock.new
|
|
end
|
|
|
|
end
|
|
|
|
class Real
|
|
|
|
include Shared
|
|
|
|
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
|
|
@vsphere_expected_pubkey_hash = options[:vsphere_expected_pubkey_hash]
|
|
@vsphere_must_reauthenticate = false
|
|
|
|
@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
|
|
|
|
# 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"
|
|
@vsphere_rev = @connection.rev
|
|
|
|
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
|
|
|
|
# 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
|
|
|
|
end
|
|
|
|
end
|
|
end
|
|
end
|