mirror of
https://github.com/fog/fog.git
synced 2022-11-09 13:51:43 -05:00
66fc952c5a
The <Vsphere::Server>.relative_path attribute depends on having initialized the <server>.datacenter field (which is lazy loaded). When the relative_path is computed from the attributes returned by the underlying rbvmomi call, it is actually an absolute path to the instance (that is, it includes the Datacenter). However, the path is meant to be relative to the Datacenter. The patch proposes the following solution: * remove the relative_path attribute * add a <Vsphere::Server>.relative_path method This works well with the lazy initialization and restores functionality to methods that rely on the path actually being relative to the Datacenter. Fixes: #3041
419 lines
17 KiB
Ruby
419 lines
17 KiB
Ruby
require 'fog/vsphere/core'
|
|
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
|
|
model :servertype
|
|
collection :servertypes
|
|
model :datacenter
|
|
collection :datacenters
|
|
model :interface
|
|
collection :interfaces
|
|
model :interfacetype
|
|
collection :interfacetypes
|
|
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
|
|
model :customvalue
|
|
collection :customvalues
|
|
model :customfield
|
|
collection :customfields
|
|
model :scsicontroller
|
|
|
|
request_path 'fog/vsphere/requests/compute'
|
|
request :current_time
|
|
request :list_virtual_machines
|
|
request :vm_power_off
|
|
request :vm_power_on
|
|
request :vm_reboot
|
|
request :vm_clone
|
|
request :vm_destroy
|
|
request :vm_migrate
|
|
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
|
|
request :list_templates
|
|
request :get_template
|
|
request :get_folder
|
|
request :list_folders
|
|
request :create_vm
|
|
request :list_vm_interfaces
|
|
request :modify_vm_interface
|
|
request :modify_vm_volume
|
|
request :list_vm_volumes
|
|
request :get_virtual_machine
|
|
request :vm_reconfig_hardware
|
|
request :vm_reconfig_memory
|
|
request :vm_reconfig_cpus
|
|
request :vm_config_vnc
|
|
request :create_folder
|
|
request :list_server_types
|
|
request :get_server_type
|
|
request :list_interface_types
|
|
request :get_interface_type
|
|
request :list_vm_customvalues
|
|
request :list_customfields
|
|
request :get_vm_first_scsi_controller
|
|
request :set_vm_customvalue
|
|
|
|
module Shared
|
|
attr_reader :vsphere_is_vcenter
|
|
attr_reader :vsphere_rev
|
|
attr_reader :vsphere_server
|
|
attr_reader :vsphere_username
|
|
|
|
protected
|
|
|
|
ATTR_TO_PROP = {
|
|
:id => 'config.instanceUuid',
|
|
:name => 'name',
|
|
:uuid => 'config.uuid',
|
|
:template => 'config.template',
|
|
:parent => 'parent',
|
|
:hostname => 'summary.guest.hostName',
|
|
:operatingsystem => 'summary.guest.guestFullName',
|
|
:ipaddress => 'guest.ipAddress',
|
|
:power_state => 'runtime.powerState',
|
|
:connection_state => 'runtime.connectionState',
|
|
:hypervisor => 'runtime.host',
|
|
:tools_state => 'guest.toolsStatus',
|
|
:tools_version => 'guest.toolsVersionStatus',
|
|
:memory_mb => 'config.hardware.memoryMB',
|
|
:cpus => 'config.hardware.numCPU',
|
|
:corespersocket => 'config.hardware.numCoresPerSocket',
|
|
:overall_status => 'overallStatus',
|
|
:guest_id => 'config.guestId',
|
|
:hardware_version => 'config.version',
|
|
}
|
|
|
|
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
|
|
|
|
# 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
|
|
|
|
props = vm_mob_ref.collect!(*ATTR_TO_PROP.values.uniq)
|
|
props_to_attr_hash vm_mob_ref, props
|
|
end
|
|
|
|
def props_to_attr_hash vm_mob_ref, props
|
|
# 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.
|
|
#
|
|
# 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.
|
|
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
|
|
# The name method "magically" appears after a VM is ready and
|
|
# finished cloning.
|
|
if attrs['hypervisor'].kind_of?(RbVmomi::VIM::HostSystem)
|
|
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}
|
|
end
|
|
# This inline rescue catches any standard error. While a VM is
|
|
# cloning, a call to the macs method will throw and NoMethodError
|
|
attrs['mac_addresses'] = Proc.new {vm_mob_ref.macs rescue nil}
|
|
# Rescue nil to catch testing while vm_mob_ref isn't reaL??
|
|
attrs['path'] = "/"+attrs['parent'].path.map(&:last).join('/') rescue nil
|
|
end
|
|
end
|
|
# 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
|
|
obj.to_s.match(/\("([^"]+)"\)/)[1]
|
|
end
|
|
|
|
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
|
|
end
|
|
|
|
class Mock
|
|
include Shared
|
|
|
|
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
|
|
|
|
def initialize(options={})
|
|
require 'rbvmomi'
|
|
@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'
|
|
end
|
|
|
|
def data
|
|
self.class.data[@vsphere_username]
|
|
end
|
|
|
|
def reset_data
|
|
self.class.data.delete(@vsphere_username)
|
|
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
|
|
@vsphere_is_vcenter = nil
|
|
@connection = nil
|
|
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
|
|
# 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
|
|
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
|
|
|
|
# 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
|