(POOLER-52) Use a Connection Pooler for vSphere connections

Previously the vSphere Provider would share a single vSphere connection for all
pools under management.  This would cause issues in large environments as this
would cause errors to be thrown or operations to slow down.  This commit
modifies the vSphere Provider to use a connection pool when communicating with
the vSphere API
- Uses the GenericConnectionPool object to manage the connection pool
- Uses a default connection pool size of:
	Whatever is biggest from:
	- How many pools this provider services
	- Maximum number of cloning tasks allowed
	- Need at least 2 connections so that a pool can have inventory functions
	  performed while cloning etc.
- A large connection_pool_timeout is used as a connection object is consumed
  during a VM clone, which can take up to 2 minutes
- Removes the `get_connection` method as that is now obsolete due to the
  connection pool
- Removes the `close` method as it is now obsolete
- Modified the spec tests slightly, to stop mocking get_connection as it no
  longer exists, and set a super low pool timeout so that if a test fails, it
  will fail quickly instead of taking the default time of 60+ seconds
This commit is contained in:
Glenn Sarti 2017-04-18 16:21:34 -07:00
parent 2f37c1e9b5
commit df783f0ed0
2 changed files with 185 additions and 249 deletions

View file

@ -2,159 +2,188 @@ module Vmpooler
class PoolManager class PoolManager
class Provider class Provider
class VSphere < Vmpooler::PoolManager::Provider::Base class VSphere < Vmpooler::PoolManager::Provider::Base
def initialize(config, logger, metrics, name, options)
super(config, logger, metrics, name, options)
task_limit = global_config[:config].nil? || global_config[:config]['task_limit'].nil? ? 10 : global_config[:config]['task_limit'].to_i
# The default connection pool size is:
# Whatever is biggest from:
# - How many pools this provider services
# - Maximum number of cloning tasks allowed
# - Need at least 2 connections so that a pool can have inventory functions performed while cloning etc.
default_connpool_size = [provided_pools.count, task_limit, 2].max
connpool_size = provider_config['connection_pool_size'].nil? ? default_connpool_size : provider_config['connection_pool_size'].to_i
# The default connection pool timeout should be quite large - 60 seconds
connpool_timeout = provider_config['connection_pool_timeout'].nil? ? 60 : provider_config['connection_pool_timeout'].to_i
logger.log('d', "[#{name}] ConnPool - Creating a connection pool of size #{connpool_size} with timeout #{connpool_timeout}")
@connection_pool = Vmpooler::PoolManager::GenericConnectionPool.new(
metrics: metrics,
metric_prefix: "#{name}_provider_connection_pool",
size: connpool_size,
timeout: connpool_timeout
) do
logger.log('d', "[#{name}] Connection Pool - Creating a connection object")
new_conn = connect_to_vsphere
new_conn
end
end
def name def name
'vsphere' 'vsphere'
end end
def vms_in_pool(pool_name) def vms_in_pool(pool_name)
connection = get_connection
foldername = pool_config(pool_name)['folder']
folder_object = find_folder(foldername, connection)
vms = [] vms = []
@connection_pool.with_metrics do |connection|
foldername = pool_config(pool_name)['folder']
folder_object = find_folder(foldername, connection)
return vms if folder_object.nil? return vms if folder_object.nil?
folder_object.childEntity.each do |vm| folder_object.childEntity.each do |vm|
vms << { 'name' => vm.name } vms << { 'name' => vm.name }
end
end end
vms vms
end end
def get_vm_host(_pool_name, vm_name) def get_vm_host(_pool_name, vm_name)
connection = get_connection
vm_object = find_vm(vm_name, connection)
return nil if vm_object.nil?
host_name = nil host_name = nil
host_name = vm_object.summary.runtime.host.name if vm_object.summary && vm_object.summary.runtime && vm_object.summary.runtime.host
@connection_pool.with_metrics do |connection|
vm_object = find_vm(vm_name, connection)
return host_name if vm_object.nil?
host_name = vm_object.summary.runtime.host.name if vm_object.summary && vm_object.summary.runtime && vm_object.summary.runtime.host
end
host_name host_name
end end
def find_least_used_compatible_host(_pool_name, vm_name) def find_least_used_compatible_host(_pool_name, vm_name)
connection = get_connection hostname = nil
@connection_pool.with_metrics do |connection|
vm_object = find_vm(vm_name, connection)
vm_object = find_vm(vm_name, connection) return hostname if vm_object.nil?
host_object = find_least_used_vpshere_compatible_host(vm_object)
return nil if vm_object.nil? return hostname if host_object.nil?
host_object = find_least_used_vpshere_compatible_host(vm_object) hostname = host_object[0].name
end
return nil if host_object.nil? hostname
host_object[0].name
end end
def migrate_vm_to_host(pool_name, vm_name, dest_host_name) def migrate_vm_to_host(pool_name, vm_name, dest_host_name)
pool = pool_config(pool_name) pool = pool_config(pool_name)
raise("Pool #{pool_name} does not exist for the provider #{name}") if pool.nil? raise("Pool #{pool_name} does not exist for the provider #{name}") if pool.nil?
connection = get_connection @connection_pool.with_metrics do |connection|
vm_object = find_vm(vm_name, connection)
raise("VM #{vm_name} does not exist in Pool #{pool_name} for the provider #{name}") if vm_object.nil?
vm_object = find_vm(vm_name, connection) target_cluster_name = get_target_cluster_from_config(pool_name)
raise("VM #{vm_name} does not exist in Pool #{pool_name} for the provider #{name}") if vm_object.nil? cluster = find_cluster(target_cluster_name, connection)
raise("Pool #{pool_name} specifies cluster #{target_cluster_name} which does not exist for the provider #{name}") if cluster.nil?
target_cluster_name = get_target_cluster_from_config(pool_name) # Go through each host and initiate a migration when the correct host name is found
cluster = find_cluster(target_cluster_name, connection) cluster.host.each do |host|
raise("Pool #{pool_name} specifies cluster #{target_cluster_name} which does not exist for the provider #{name}") if cluster.nil? if host.name == dest_host_name
migrate_vm_host(vm_object, host)
# Go through each host and initiate a migration when the correct host name is found return true
cluster.host.each do |host| end
if host.name == dest_host_name
migrate_vm_host(vm_object, host)
return true
end end
end end
false false
end end
def get_vm(_pool_name, vm_name) def get_vm(_pool_name, vm_name)
connection = get_connection vm_hash = nil
@connection_pool.with_metrics do |connection|
vm_object = find_vm(vm_name, connection)
return vm_hash if vm_object.nil?
vm_object = find_vm(vm_name, connection) vm_folder_path = get_vm_folder_path(vm_object)
return nil if vm_object.nil? # Find the pool name based on the folder path
pool_name = nil
vm_folder_path = get_vm_folder_path(vm_object) template_name = nil
# Find the pool name based on the folder path global_config[:pools].each do |pool|
pool_name = nil if pool['folder'] == vm_folder_path
template_name = nil pool_name = pool['name']
global_config[:pools].each do |pool| template_name = pool['template']
if pool['folder'] == vm_folder_path end
pool_name = pool['name']
template_name = pool['template']
end end
end
generate_vm_hash(vm_object, template_name, pool_name) vm_hash = generate_vm_hash(vm_object, template_name, pool_name)
end
vm_hash
end end
def create_vm(pool_name, new_vmname) def create_vm(pool_name, new_vmname)
pool = pool_config(pool_name) pool = pool_config(pool_name)
raise("Pool #{pool_name} does not exist for the provider #{name}") if pool.nil? raise("Pool #{pool_name} does not exist for the provider #{name}") if pool.nil?
vm_hash = nil
@connection_pool.with_metrics do |connection|
# Assume all pool config is valid i.e. not missing
template_path = pool['template']
target_folder_path = pool['folder']
target_datastore = pool['datastore']
target_cluster_name = get_target_cluster_from_config(pool_name)
connection = get_connection # Extract the template VM name from the full path
raise("Pool #{pool_name} did specify a full path for the template for the provider #{name}") unless template_path =~ /\//
templatefolders = template_path.split('/')
template_name = templatefolders.pop
# Assume all pool config is valid i.e. not missing # Get the actual objects from vSphere
template_path = pool['template'] template_folder_object = find_folder(templatefolders.join('/'), connection)
target_folder_path = pool['folder'] raise("Pool #{pool_name} specifies a template folder of #{templatefolders.join('/')} which does not exist for the provider #{name}") if template_folder_object.nil?
target_datastore = pool['datastore']
target_cluster_name = get_target_cluster_from_config(pool_name)
# Extract the template VM name from the full path template_vm_object = template_folder_object.find(template_name)
raise("Pool #{pool_name} did specify a full path for the template for the provider #{name}") unless template_path =~ /\// raise("Pool #{pool_name} specifies a template VM of #{template_name} which does not exist for the provider #{name}") if template_vm_object.nil?
templatefolders = template_path.split('/')
template_name = templatefolders.pop
# Get the actual objects from vSphere # Annotate with creation time, origin template, etc.
template_folder_object = find_folder(templatefolders.join('/'), connection) # Add extraconfig options that can be queried by vmtools
raise("Pool #{pool_name} specifies a template folder of #{templatefolders.join('/')} which does not exist for the provider #{name}") if template_folder_object.nil? config_spec = RbVmomi::VIM.VirtualMachineConfigSpec(
annotation: JSON.pretty_generate(
name: new_vmname,
created_by: provider_config['username'],
base_template: template_path,
creation_timestamp: Time.now.utc
),
extraConfig: [
{ key: 'guestinfo.hostname', value: new_vmname }
]
)
template_vm_object = template_folder_object.find(template_name) # Choose a cluster/host to place the new VM on
raise("Pool #{pool_name} specifies a template VM of #{template_name} which does not exist for the provider #{name}") if template_vm_object.nil? target_host_object = find_least_used_host(target_cluster_name, connection)
# Annotate with creation time, origin template, etc. # Put the VM in the specified folder and resource pool
# Add extraconfig options that can be queried by vmtools relocate_spec = RbVmomi::VIM.VirtualMachineRelocateSpec(
config_spec = RbVmomi::VIM.VirtualMachineConfigSpec( datastore: find_datastore(target_datastore, connection),
annotation: JSON.pretty_generate( host: target_host_object,
diskMoveType: :moveChildMostDiskBacking
)
# Create a clone spec
clone_spec = RbVmomi::VIM.VirtualMachineCloneSpec(
location: relocate_spec,
config: config_spec,
powerOn: true,
template: false
)
# Create the new VM
new_vm_object = template_vm_object.CloneVM_Task(
folder: find_folder(target_folder_path, connection),
name: new_vmname, name: new_vmname,
created_by: provider_config['username'], spec: clone_spec
base_template: template_path, ).wait_for_completion
creation_timestamp: Time.now.utc
),
extraConfig: [
{ key: 'guestinfo.hostname', value: new_vmname }
]
)
# Choose a cluster/host to place the new VM on vm_hash = generate_vm_hash(new_vm_object, template_path, pool_name)
target_host_object = find_least_used_host(target_cluster_name, connection) end
vm_hash
# Put the VM in the specified folder and resource pool
relocate_spec = RbVmomi::VIM.VirtualMachineRelocateSpec(
datastore: find_datastore(target_datastore, connection),
host: target_host_object,
diskMoveType: :moveChildMostDiskBacking
)
# Create a clone spec
clone_spec = RbVmomi::VIM.VirtualMachineCloneSpec(
location: relocate_spec,
config: config_spec,
powerOn: true,
template: false
)
# Create the new VM
new_vm_object = template_vm_object.CloneVM_Task(
folder: find_folder(target_folder_path, connection),
name: new_vmname,
spec: clone_spec
).wait_for_completion
generate_vm_hash(new_vm_object, template_path, pool_name)
end end
def create_disk(pool_name, vm_name, disk_size) def create_disk(pool_name, vm_name, disk_size)
@ -164,62 +193,58 @@ module Vmpooler
datastore_name = pool['datastore'] datastore_name = pool['datastore']
raise("Pool #{pool_name} does not have a datastore defined for the provider #{name}") if datastore_name.nil? raise("Pool #{pool_name} does not have a datastore defined for the provider #{name}") if datastore_name.nil?
connection = get_connection @connection_pool.with_metrics do |connection|
vm_object = find_vm(vm_name, connection)
vm_object = find_vm(vm_name, connection) raise("VM #{vm_name} in pool #{pool_name} does not exist for the provider #{name}") if vm_object.nil?
raise("VM #{vm_name} in pool #{pool_name} does not exist for the provider #{name}") if vm_object.nil?
add_disk(vm_object, disk_size, datastore_name, connection)
add_disk(vm_object, disk_size, datastore_name, connection)
end
true true
end end
def create_snapshot(pool_name, vm_name, new_snapshot_name) def create_snapshot(pool_name, vm_name, new_snapshot_name)
connection = get_connection @connection_pool.with_metrics do |connection|
vm_object = find_vm(vm_name, connection)
raise("VM #{vm_name} in pool #{pool_name} does not exist for the provider #{name}") if vm_object.nil?
vm_object = find_vm(vm_name, connection) old_snap = find_snapshot(vm_object, new_snapshot_name)
raise("VM #{vm_name} in pool #{pool_name} does not exist for the provider #{name}") if vm_object.nil? raise("Snapshot #{new_snapshot_name} for VM #{vm_name} in pool #{pool_name} already exists for the provider #{name}") unless old_snap.nil?
old_snap = find_snapshot(vm_object, new_snapshot_name)
raise("Snapshot #{new_snapshot_name} for VM #{vm_name} in pool #{pool_name} already exists for the provider #{name}") unless old_snap.nil?
vm_object.CreateSnapshot_Task(
name: new_snapshot_name,
description: 'vmpooler',
memory: true,
quiesce: true
).wait_for_completion
vm_object.CreateSnapshot_Task(
name: new_snapshot_name,
description: 'vmpooler',
memory: true,
quiesce: true
).wait_for_completion
end
true true
end end
def revert_snapshot(pool_name, vm_name, snapshot_name) def revert_snapshot(pool_name, vm_name, snapshot_name)
connection = get_connection @connection_pool.with_metrics do |connection|
vm_object = find_vm(vm_name, connection)
raise("VM #{vm_name} in pool #{pool_name} does not exist for the provider #{name}") if vm_object.nil?
vm_object = find_vm(vm_name, connection) snapshot_object = find_snapshot(vm_object, snapshot_name)
raise("VM #{vm_name} in pool #{pool_name} does not exist for the provider #{name}") if vm_object.nil? raise("Snapshot #{snapshot_name} for VM #{vm_name} in pool #{pool_name} does not exist for the provider #{name}") if snapshot_object.nil?
snapshot_object = find_snapshot(vm_object, snapshot_name)
raise("Snapshot #{snapshot_name} for VM #{vm_name} in pool #{pool_name} does not exist for the provider #{name}") if snapshot_object.nil?
snapshot_object.RevertToSnapshot_Task.wait_for_completion
snapshot_object.RevertToSnapshot_Task.wait_for_completion
end
true true
end end
def destroy_vm(_pool_name, vm_name) def destroy_vm(_pool_name, vm_name)
connection = get_connection @connection_pool.with_metrics do |connection|
vm_object = find_vm(vm_name, connection)
# If a VM doesn't exist then it is effectively deleted
return true if vm_object.nil?
vm_object = find_vm(vm_name, connection) # Poweroff the VM if it's running
# If a VM doesn't exist then it is effectively deleted vm_object.PowerOffVM_Task.wait_for_completion if vm_object.runtime && vm_object.runtime.powerState && vm_object.runtime.powerState == 'poweredOn'
return true if vm_object.nil?
# Poweroff the VM if it's running
vm_object.PowerOffVM_Task.wait_for_completion if vm_object.runtime && vm_object.runtime.powerState && vm_object.runtime.powerState == 'poweredOn'
# Kill it with fire
vm_object.Destroy_Task.wait_for_completion
# Kill it with fire
vm_object.Destroy_Task.wait_for_completion
end
true true
end end
@ -263,16 +288,6 @@ module Vmpooler
DISK_TYPE = 'thin'.freeze DISK_TYPE = 'thin'.freeze
DISK_MODE = 'persistent'.freeze DISK_MODE = 'persistent'.freeze
def get_connection
begin
@connection.serviceInstance.CurrentTime
rescue
@connection = connect_to_vsphere
end
@connection
end
def connect_to_vsphere def connect_to_vsphere
max_tries = global_config[:config]['max_tries'] || 3 max_tries = global_config[:config]['max_tries'] || 3
retry_factor = global_config[:config]['retry_factor'] || 10 retry_factor = global_config[:config]['retry_factor'] || 10
@ -667,10 +682,6 @@ module Vmpooler
relospec = RbVmomi::VIM.VirtualMachineRelocateSpec(host: host) relospec = RbVmomi::VIM.VirtualMachineRelocateSpec(host: host)
vm.RelocateVM_Task(spec: relospec).wait_for_completion vm.RelocateVM_Task(spec: relospec).wait_for_completion
end end
def close
@connection.close
end
end end
end end
end end

View file

@ -53,6 +53,8 @@ describe 'Vmpooler::PoolManager::Provider::VSphere' do
username: "vcenter_user" username: "vcenter_user"
password: "vcenter_password" password: "vcenter_password"
insecure: true insecure: true
# Drop the connection pool timeout way down for spec tests so they fail fast
connection_pool_timeout: 1
:pools: :pools:
- name: '#{poolname}' - name: '#{poolname}'
alias: [ 'mockpool' ] alias: [ 'mockpool' ]
@ -84,7 +86,7 @@ EOT
let(:pool_config) { config[:pools][0] } let(:pool_config) { config[:pools][0] }
before(:each) do before(:each) do
allow(subject).to receive(:get_connection).and_return(connection) allow(subject).to receive(:connect_to_vsphere).and_return(connection)
end end
context 'Given a pool folder that is missing' do context 'Given a pool folder that is missing' do
@ -93,7 +95,7 @@ EOT
end end
it 'should get a connection' do it 'should get a connection' do
expect(subject).to receive(:get_connection).and_return(connection) expect(subject).to receive(:connect_to_vsphere).and_return(connection)
subject.vms_in_pool(poolname) subject.vms_in_pool(poolname)
end end
@ -111,7 +113,7 @@ EOT
end end
it 'should get a connection' do it 'should get a connection' do
expect(subject).to receive(:get_connection).and_return(connection) expect(subject).to receive(:connect_to_vsphere).and_return(connection)
subject.vms_in_pool(poolname) subject.vms_in_pool(poolname)
end end
@ -140,7 +142,7 @@ EOT
end end
it 'should get a connection' do it 'should get a connection' do
expect(subject).to receive(:get_connection).and_return(connection) expect(subject).to receive(:connect_to_vsphere).and_return(connection)
subject.vms_in_pool(poolname) subject.vms_in_pool(poolname)
end end
@ -155,7 +157,7 @@ EOT
describe '#get_vm_host' do describe '#get_vm_host' do
before(:each) do before(:each) do
allow(subject).to receive(:get_connection).and_return(connection) allow(subject).to receive(:connect_to_vsphere).and_return(connection)
expect(subject).to receive(:find_vm).with(vmname,connection).and_return(vm_object) expect(subject).to receive(:find_vm).with(vmname,connection).and_return(vm_object)
end end
@ -163,7 +165,7 @@ EOT
let(:vm_object) { nil } let(:vm_object) { nil }
it 'should get a connection' do it 'should get a connection' do
expect(subject).to receive(:get_connection).and_return(connection) expect(subject).to receive(:connect_to_vsphere).and_return(connection)
subject.get_vm_host(poolname,vmname) subject.get_vm_host(poolname,vmname)
end end
@ -185,7 +187,7 @@ EOT
end end
it 'should get a connection' do it 'should get a connection' do
expect(subject).to receive(:get_connection).and_return(connection) expect(subject).to receive(:connect_to_vsphere).and_return(connection)
subject.get_vm_host(poolname,vmname) subject.get_vm_host(poolname,vmname)
end end
@ -208,7 +210,7 @@ EOT
end end
it 'should get a connection' do it 'should get a connection' do
expect(subject).to receive(:get_connection).and_return(connection) expect(subject).to receive(:connect_to_vsphere).and_return(connection)
subject.get_vm_host(poolname,vmname) subject.get_vm_host(poolname,vmname)
end end
@ -223,7 +225,7 @@ EOT
let(:vm_object) { nil } let(:vm_object) { nil }
before(:each) do before(:each) do
allow(subject).to receive(:get_connection).and_return(connection) allow(subject).to receive(:connect_to_vsphere).and_return(connection)
expect(subject).to receive(:find_vm).with(vmname,connection).and_return(vm_object) expect(subject).to receive(:find_vm).with(vmname,connection).and_return(vm_object)
end end
@ -231,7 +233,7 @@ EOT
let(:vm_object) { nil } let(:vm_object) { nil }
it 'should get a connection' do it 'should get a connection' do
expect(subject).to receive(:get_connection).and_return(connection) expect(subject).to receive(:connect_to_vsphere).and_return(connection)
subject.find_least_used_compatible_host(poolname,vmname) subject.find_least_used_compatible_host(poolname,vmname)
end end
@ -250,7 +252,7 @@ EOT
end end
it 'should get a connection' do it 'should get a connection' do
expect(subject).to receive(:get_connection).and_return(connection) expect(subject).to receive(:connect_to_vsphere).and_return(connection)
subject.find_least_used_compatible_host(poolname,vmname) subject.find_least_used_compatible_host(poolname,vmname)
end end
@ -272,7 +274,7 @@ EOT
end end
it 'should get a connection' do it 'should get a connection' do
expect(subject).to receive(:get_connection).and_return(connection) expect(subject).to receive(:connect_to_vsphere).and_return(connection)
subject.find_least_used_compatible_host(poolname,vmname) subject.find_least_used_compatible_host(poolname,vmname)
end end
@ -293,7 +295,7 @@ EOT
before(:each) do before(:each) do
config[:pools][0]['clone_target'] = cluster_name config[:pools][0]['clone_target'] = cluster_name
allow(subject).to receive(:get_connection).and_return(connection) allow(subject).to receive(:connect_to_vsphere).and_return(connection)
allow(subject).to receive(:find_vm).and_return(vm_object) allow(subject).to receive(:find_vm).and_return(vm_object)
end end
@ -389,7 +391,7 @@ EOT
describe '#get_vm' do describe '#get_vm' do
let(:vm_object) { nil } let(:vm_object) { nil }
before(:each) do before(:each) do
allow(subject).to receive(:get_connection).and_return(connection) allow(subject).to receive(:connect_to_vsphere).and_return(connection)
expect(subject).to receive(:find_vm).with(vmname,connection).and_return(vm_object) expect(subject).to receive(:find_vm).with(vmname,connection).and_return(vm_object)
end end
@ -510,7 +512,7 @@ EOT
let(:new_vm_object) { mock_RbVmomi_VIM_VirtualMachine({ :name => vmname }) } let(:new_vm_object) { mock_RbVmomi_VIM_VirtualMachine({ :name => vmname }) }
before(:each) do before(:each) do
allow(subject).to receive(:get_connection).and_return(connection) allow(subject).to receive(:connect_to_vsphere).and_return(connection)
allow(connection.serviceInstance).to receive(:find_datacenter).and_return(datacenter_object) allow(connection.serviceInstance).to receive(:find_datacenter).and_return(datacenter_object)
end end
@ -585,7 +587,7 @@ EOT
let(:datastorename) { 'datastore0' } let(:datastorename) { 'datastore0' }
let(:disk_size) { 10 } let(:disk_size) { 10 }
before(:each) do before(:each) do
allow(subject).to receive(:get_connection).and_return(connection) allow(subject).to receive(:connect_to_vsphere).and_return(connection)
allow(subject).to receive(:find_vm).with(vmname, connection).and_return(vm_object) allow(subject).to receive(:find_vm).with(vmname, connection).and_return(vm_object)
end end
@ -643,7 +645,7 @@ EOT
let(:vm_object) { mock_RbVmomi_VIM_VirtualMachine({ :name => vmname, :snapshot_tree => snapshot_tree }) } let(:vm_object) { mock_RbVmomi_VIM_VirtualMachine({ :name => vmname, :snapshot_tree => snapshot_tree }) }
before(:each) do before(:each) do
allow(subject).to receive(:get_connection).and_return(connection) allow(subject).to receive(:connect_to_vsphere).and_return(connection)
allow(subject).to receive(:find_vm).with(vmname,connection).and_return(vm_object) allow(subject).to receive(:find_vm).with(vmname,connection).and_return(vm_object)
end end
@ -698,7 +700,7 @@ EOT
let(:vm_object) { mock_RbVmomi_VIM_VirtualMachine({ :name => vmname, :snapshot_tree => snapshot_tree }) } let(:vm_object) { mock_RbVmomi_VIM_VirtualMachine({ :name => vmname, :snapshot_tree => snapshot_tree }) }
before(:each) do before(:each) do
allow(subject).to receive(:get_connection).and_return(connection) allow(subject).to receive(:connect_to_vsphere).and_return(connection)
allow(subject).to receive(:find_vm).with(vmname,connection).and_return(vm_object) allow(subject).to receive(:find_vm).with(vmname,connection).and_return(vm_object)
end end
@ -747,7 +749,7 @@ EOT
let(:destroy_task) { mock_RbVmomi_VIM_Task() } let(:destroy_task) { mock_RbVmomi_VIM_Task() }
before(:each) do before(:each) do
allow(subject).to receive(:get_connection).and_return(connection) allow(subject).to receive(:connect_to_vsphere).and_return(connection)
end end
context 'Given a missing VM name' do context 'Given a missing VM name' do
@ -876,57 +878,6 @@ EOT
end end
# vSphere helper methods # vSphere helper methods
describe '#get_connection' do
before(:each) do
# NOTE - Using instance_variable_set is a code smell of code that is not testable
subject.instance_variable_set("@connection",connection)
end
context 'when connection is ok' do
it 'should not attempt to reconnect' do
expect(subject).to receive(:connect_to_vsphere).exactly(0).times
subject.get_connection()
end
it 'should return a connection' do
result = subject.get_connection()
expect(result).to be(connection)
end
end
context 'when connection has broken' do
before(:each) do
expect(connection.serviceInstance).to receive(:CurrentTime).and_raise(RuntimeError,'MockConnectionError')
end
it 'should not increment the connect.open metric' do
# https://github.com/puppetlabs/vmpooler/issues/195
expect(metrics).to receive(:increment).with('connect.open').exactly(0).times
allow(subject).to receive(:connect_to_vsphere)
subject.get_connection()
end
it 'should call connect_to_vsphere to reconnect' do
allow(metrics).to receive(:increment)
expect(subject).to receive(:connect_to_vsphere).with(no_args)
subject.get_connection()
end
it 'should return a new connection' do
new_connection = mock_RbVmomi_VIM_Connection(connection_options)
expect(subject).to receive(:connect_to_vsphere).with(no_args).and_return(new_connection)
result = subject.get_connection()
expect(result).to be(new_connection)
end
end
end
describe '#connect_to_vsphere' do describe '#connect_to_vsphere' do
before(:each) do before(:each) do
allow(RbVmomi::VIM).to receive(:connect).and_return(connection) allow(RbVmomi::VIM).to receive(:connect).and_return(connection)
@ -2828,30 +2779,4 @@ EOT
expect(subject.migrate_vm_host(vm_object,host_object)).to eq('RELOCATE_RESULT') expect(subject.migrate_vm_host(vm_object,host_object)).to eq('RELOCATE_RESULT')
end end
end end
describe '#close' do
context 'no connection has been made' do
before(:each) do
# NOTE - Using instance_variable_set is a code smell of code that is not testable
subject.instance_variable_set("@connection",nil)
end
it 'should not error' do
pending('https://github.com/puppetlabs/vmpooler/issues/211')
subject.close
end
end
context 'on an open connection' do
before(:each) do
# NOTE - Using instance_variable_set is a code smell of code that is not testable
subject.instance_variable_set("@connection",connection)
end
it 'should close the underlying connection object' do
expect(connection).to receive(:close)
subject.close
end
end
end
end end