mirror of
https://github.com/puppetlabs/vmpooler.git
synced 2026-01-26 01:58:41 -05:00
Previously, all calls to the vSphere API assumed an instance variable called `@connection`. This commit prepares the provider for a connection pooler: - Removes all references to `@connection` where needed and funnels all calls to get a vSphere connection through the newly renamed method `get_connection`. For the moment, this still uses `@connection` behind the scenes but will make it easier in the future to migrate to a connection pooler - Removes all references and tests for the ensure_connected method as it's no longer required - All methods that explicitly need a connection object will have this as part of the method parameters - The connect_to_vsphere method has been changed so that instead of setting the instance level `@connection` object it just returns the newly created connection. This can then be easily consumed by a connection pooler later.
1968 lines
70 KiB
Ruby
1968 lines
70 KiB
Ruby
require 'spec_helper'
|
|
|
|
RSpec::Matchers.define :relocation_spec_with_host do |value|
|
|
match { |actual| actual[:spec].host == value }
|
|
end
|
|
|
|
RSpec::Matchers.define :create_virtual_disk_with_size do |value|
|
|
match { |actual| actual[:spec].capacityKb == value * 1024 * 1024 }
|
|
end
|
|
|
|
describe 'Vmpooler::PoolManager::Provider::VSphere' do
|
|
let(:metrics) { Vmpooler::DummyStatsd.new }
|
|
let(:config) { YAML.load(<<-EOT
|
|
---
|
|
:config:
|
|
max_tries: 3
|
|
retry_factor: 10
|
|
:vsphere:
|
|
server: "vcenter.domain.local"
|
|
username: "vcenter_user"
|
|
password: "vcenter_password"
|
|
insecure: true
|
|
EOT
|
|
)
|
|
}
|
|
|
|
let(:fake_vm) {
|
|
fake_vm = {}
|
|
fake_vm['name'] = 'vm1'
|
|
fake_vm['hostname'] = 'vm1'
|
|
fake_vm['template'] = 'pool1'
|
|
fake_vm['boottime'] = Time.now
|
|
fake_vm['powerstate'] = 'PoweredOn'
|
|
|
|
fake_vm
|
|
}
|
|
|
|
subject { Vmpooler::PoolManager::Provider::VSphere.new({:config => config, :metrics => metrics}) }
|
|
|
|
describe '#name' do
|
|
it 'should be vsphere' do
|
|
expect(subject.name).to eq('vsphere')
|
|
end
|
|
end
|
|
|
|
describe '#vms_in_pool' do
|
|
it 'should raise error' do
|
|
expect{subject.vms_in_pool('pool')}.to raise_error(/does not implement vms_in_pool/)
|
|
end
|
|
end
|
|
|
|
describe '#get_vm_host' do
|
|
it 'should raise error' do
|
|
expect{subject.get_vm_host('vm')}.to raise_error(/does not implement get_vm_host/)
|
|
end
|
|
end
|
|
|
|
describe '#find_least_used_compatible_host' do
|
|
it 'should raise error' do
|
|
expect{subject.find_least_used_compatible_host('vm')}.to raise_error(/does not implement find_least_used_compatible_host/)
|
|
end
|
|
end
|
|
|
|
describe '#migrate_vm_to_host' do
|
|
it 'should raise error' do
|
|
expect{subject.migrate_vm_to_host('vm','host')}.to raise_error(/does not implement migrate_vm_to_host/)
|
|
end
|
|
end
|
|
|
|
describe '#get_vm' do
|
|
it 'should raise error' do
|
|
expect{subject.get_vm('vm')}.to raise_error(/does not implement get_vm/)
|
|
end
|
|
end
|
|
|
|
describe '#create_vm' do
|
|
it 'should raise error' do
|
|
expect{subject.create_vm('pool','newname')}.to raise_error(/does not implement create_vm/)
|
|
end
|
|
end
|
|
|
|
describe '#destroy_vm' do
|
|
it 'should raise error' do
|
|
expect{subject.destroy_vm('vm','pool')}.to raise_error(/does not implement destroy_vm/)
|
|
end
|
|
end
|
|
|
|
describe '#vm_ready?' do
|
|
it 'should raise error' do
|
|
expect{subject.vm_ready?('vm','pool','timeout')}.to raise_error(/does not implement vm_ready?/)
|
|
end
|
|
end
|
|
|
|
describe '#vm_exists?' do
|
|
it 'should raise error' do
|
|
expect{subject.vm_exists?('vm')}.to raise_error(/does not implement/)
|
|
end
|
|
|
|
it 'should return true when get_vm returns an object' do
|
|
allow(subject).to receive(:get_vm).with('vm').and_return(fake_vm)
|
|
|
|
expect(subject.vm_exists?('vm')).to eq(true)
|
|
end
|
|
|
|
it 'should return false when get_vm returns nil' do
|
|
allow(subject).to receive(:get_vm).with('vm').and_return(nil)
|
|
|
|
expect(subject.vm_exists?('vm')).to eq(false)
|
|
end
|
|
end
|
|
|
|
# vSphere helper methods
|
|
let(:credentials) { config[:vsphere] }
|
|
|
|
let(:connection_options) {{}}
|
|
let(:connection) { mock_RbVmomi_VIM_Connection(connection_options) }
|
|
let(:vmname) { 'vm1' }
|
|
|
|
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(credentials)
|
|
|
|
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(credentials).and_return(new_connection)
|
|
|
|
result = subject.get_connection()
|
|
|
|
expect(result).to be(new_connection)
|
|
end
|
|
end
|
|
end
|
|
|
|
describe '#connect_to_vsphere' do
|
|
before(:each) do
|
|
allow(RbVmomi::VIM).to receive(:connect).and_return(connection)
|
|
end
|
|
|
|
context 'succesful connection' do
|
|
it 'should use the supplied credentials' do
|
|
expect(RbVmomi::VIM).to receive(:connect).with({
|
|
:host => credentials['server'],
|
|
:user => credentials['username'],
|
|
:password => credentials['password'],
|
|
:insecure => credentials['insecure']
|
|
}).and_return(connection)
|
|
subject.connect_to_vsphere(credentials)
|
|
end
|
|
|
|
it 'should honor the insecure setting' do
|
|
pending('Resolution of issue https://github.com/puppetlabs/vmpooler/issues/207')
|
|
config[:vsphere][:insecure] = false
|
|
|
|
expect(RbVmomi::VIM).to receive(:connect).with({
|
|
:host => credentials['server'],
|
|
:user => credentials['username'],
|
|
:password => credentials['password'],
|
|
:insecure => false,
|
|
}).and_return(connection)
|
|
subject.connect_to_vsphere(credentials)
|
|
end
|
|
|
|
it 'should default to an insecure connection' do
|
|
config[:vsphere][:insecure] = nil
|
|
|
|
expect(RbVmomi::VIM).to receive(:connect).with({
|
|
:host => credentials['server'],
|
|
:user => credentials['username'],
|
|
:password => credentials['password'],
|
|
:insecure => true
|
|
}).and_return(connection)
|
|
|
|
subject.connect_to_vsphere(credentials)
|
|
end
|
|
|
|
it 'should return the connection object' do
|
|
result = subject.connect_to_vsphere(credentials)
|
|
|
|
expect(result).to be(connection)
|
|
end
|
|
|
|
it 'should increment the connect.open counter' do
|
|
expect(metrics).to receive(:increment).with('connect.open')
|
|
subject.connect_to_vsphere(credentials)
|
|
end
|
|
end
|
|
|
|
context 'connection is initially unsuccessful' do
|
|
before(:each) do
|
|
# Simulate a failure and then success
|
|
expect(RbVmomi::VIM).to receive(:connect).and_raise(RuntimeError,'MockError').ordered
|
|
expect(RbVmomi::VIM).to receive(:connect).and_return(connection).ordered
|
|
|
|
allow(subject).to receive(:sleep)
|
|
end
|
|
|
|
it 'should return the connection object' do
|
|
result = subject.connect_to_vsphere(credentials)
|
|
|
|
expect(result).to be(connection)
|
|
end
|
|
|
|
it 'should increment the connect.fail and then connect.open counter' do
|
|
expect(metrics).to receive(:increment).with('connect.fail').exactly(1).times
|
|
expect(metrics).to receive(:increment).with('connect.open').exactly(1).times
|
|
subject.connect_to_vsphere(credentials)
|
|
end
|
|
end
|
|
|
|
context 'connection is always unsuccessful' do
|
|
before(:each) do
|
|
allow(RbVmomi::VIM).to receive(:connect).and_raise(RuntimeError,'MockError')
|
|
allow(subject).to receive(:sleep)
|
|
end
|
|
|
|
it 'should raise an error' do
|
|
expect{subject.connect_to_vsphere(credentials)}.to raise_error(RuntimeError,'MockError')
|
|
end
|
|
|
|
it 'should retry the connection attempt config.max_tries times' do
|
|
pending('Resolution of issue https://github.com/puppetlabs/vmpooler/issues/199')
|
|
expect(RbVmomi::VIM).to receive(:connect).exactly(config[:config]['max_tries']).times.and_raise(RuntimeError,'MockError')
|
|
|
|
begin
|
|
# Swallow any errors
|
|
subject.connect_to_vsphere(credentials)
|
|
rescue
|
|
end
|
|
end
|
|
|
|
it 'should increment the connect.fail counter config.max_tries times' do
|
|
pending('Resolution of issue https://github.com/puppetlabs/vmpooler/issues/199')
|
|
expect(metrics).to receive(:increment).with('connect.fail').exactly(config[:config]['max_tries']).times
|
|
|
|
begin
|
|
# Swallow any errors
|
|
subject.connect_to_vsphere(credentials)
|
|
rescue
|
|
end
|
|
end
|
|
|
|
[{:max_tries => 5, :retry_factor => 1},
|
|
{:max_tries => 8, :retry_factor => 5},
|
|
].each do |testcase|
|
|
context "Configuration set for max_tries of #{testcase[:max_tries]} and retry_facter of #{testcase[:retry_factor]}" do
|
|
it "should sleep #{testcase[:max_tries] - 1} times between attempts with increasing timeout" do
|
|
pending('Resolution of issue https://github.com/puppetlabs/vmpooler/issues/199')
|
|
config[:config]['max_tries'] = testcase[:max_tries]
|
|
config[:config]['retry_factor'] = testcase[:retry_factor]
|
|
|
|
(1..testcase[:max_tries] - 1).each do |try|
|
|
expect(subject).to receive(:sleep).with(testcase[:retry_factor] * try).ordered
|
|
end
|
|
|
|
begin
|
|
# Swallow any errors
|
|
subject.connect_to_vsphere(credentials)
|
|
rescue
|
|
end
|
|
end
|
|
end
|
|
end
|
|
end
|
|
end
|
|
|
|
describe '#add_disk' do
|
|
let(:datastorename) { 'datastore' }
|
|
let(:disk_size) { 30 }
|
|
let(:collectMultiple_response) { {} }
|
|
|
|
let(:vm_scsi_controller) { mock_RbVmomi_VIM_VirtualSCSIController() }
|
|
|
|
# Require at least one SCSI Controller
|
|
let(:vm_object) {
|
|
mock_vm = mock_RbVmomi_VIM_VirtualMachine({
|
|
:name => vmname,
|
|
})
|
|
mock_vm.config.hardware.device << vm_scsi_controller
|
|
|
|
mock_vm
|
|
}
|
|
|
|
# Require at least one DC with the requried datastore
|
|
let(:connection_options) {{
|
|
:serviceContent => {
|
|
:datacenters => [
|
|
{ :name => 'MockDC', :datastores => [datastorename] }
|
|
]
|
|
}
|
|
}}
|
|
|
|
let(:create_virtual_disk_task) { mock_RbVmomi_VIM_Task() }
|
|
let(:reconfig_vm_task) { mock_RbVmomi_VIM_Task() }
|
|
|
|
before(:each) do
|
|
# NOTE - This method should not be using `_connection`, instead it should be using `@conection`
|
|
# This should not be required once https://github.com/puppetlabs/vmpooler/issues/213 is resolved
|
|
mock_ds = subject.find_datastore(datastorename,connection)
|
|
allow(mock_ds).to receive(:_connection).and_return(connection) unless mock_ds.nil?
|
|
|
|
# Mocking for find_vmdks
|
|
allow(connection.serviceContent.propertyCollector).to receive(:collectMultiple).and_return(collectMultiple_response)
|
|
|
|
# Mocking for creating the disk
|
|
allow(connection.serviceContent.virtualDiskManager).to receive(:CreateVirtualDisk_Task).and_return(create_virtual_disk_task)
|
|
allow(create_virtual_disk_task).to receive(:wait_for_completion).and_return(true)
|
|
|
|
# Mocking for adding disk to the VM
|
|
allow(vm_object).to receive(:ReconfigVM_Task).and_return(reconfig_vm_task)
|
|
allow(reconfig_vm_task).to receive(:wait_for_completion).and_return(true)
|
|
end
|
|
|
|
context 'Succesfully addding disk' do
|
|
it 'should return true' do
|
|
expect(subject.add_disk(vm_object,disk_size,datastorename,connection)).to be true
|
|
end
|
|
|
|
it 'should request a disk of appropriate size' do
|
|
expect(connection.serviceContent.virtualDiskManager).to receive(:CreateVirtualDisk_Task)
|
|
.with(create_virtual_disk_with_size(disk_size))
|
|
.and_return(create_virtual_disk_task)
|
|
|
|
|
|
subject.add_disk(vm_object,disk_size,datastorename,connection)
|
|
end
|
|
end
|
|
|
|
context 'Requested disk size is 0' do
|
|
it 'should raise an error' do
|
|
expect(subject.add_disk(vm_object,0,datastorename,connection)).to be false
|
|
end
|
|
end
|
|
|
|
context 'No datastores or datastore missing' do
|
|
let(:connection_options) {{
|
|
:serviceContent => {
|
|
:datacenters => [
|
|
{ :name => 'MockDC', :datastores => ['missing_datastore'] }
|
|
]
|
|
}
|
|
}}
|
|
|
|
it 'should return false' do
|
|
expect{ subject.add_disk(vm_object,disk_size,datastorename,connection) }.to raise_error(NoMethodError)
|
|
end
|
|
end
|
|
|
|
context 'VM does not have a SCSI Controller' do
|
|
let(:vm_object) {
|
|
mock_vm = mock_RbVmomi_VIM_VirtualMachine({
|
|
:name => vmname,
|
|
})
|
|
|
|
mock_vm
|
|
}
|
|
|
|
it 'should raise an error' do
|
|
expect{ subject.add_disk(vm_object,disk_size,datastorename,connection) }.to raise_error(NoMethodError)
|
|
end
|
|
end
|
|
end
|
|
|
|
describe '#find_datastore' do
|
|
let(:datastorename) { 'datastore' }
|
|
let(:datastore_list) { [] }
|
|
|
|
context 'No datastores in the datacenter' do
|
|
let(:connection_options) {{
|
|
:serviceContent => {
|
|
:datacenters => [
|
|
{ :name => 'MockDC', :datastores => [] }
|
|
]
|
|
}
|
|
}}
|
|
|
|
it 'should return nil if the datastore is not found' do
|
|
result = subject.find_datastore(datastorename,connection)
|
|
expect(result).to be_nil
|
|
end
|
|
end
|
|
|
|
context 'Many datastores in the datacenter' do
|
|
let(:connection_options) {{
|
|
:serviceContent => {
|
|
:datacenters => [
|
|
{ :name => 'MockDC', :datastores => ['ds1','ds2',datastorename,'ds3'] }
|
|
]
|
|
}
|
|
}}
|
|
|
|
it 'should return nil if the datastore is not found' do
|
|
result = subject.find_datastore('missing_datastore',connection)
|
|
expect(result).to be_nil
|
|
end
|
|
|
|
it 'should find the datastore in the datacenter' do
|
|
result = subject.find_datastore(datastorename,connection)
|
|
|
|
expect(result).to_not be_nil
|
|
expect(result.is_a?(RbVmomi::VIM::Datastore)).to be true
|
|
expect(result.name).to eq(datastorename)
|
|
end
|
|
end
|
|
end
|
|
|
|
describe '#find_device' do
|
|
let(:devicename) { 'device1' }
|
|
let(:vm_object) {
|
|
mock_vm = mock_RbVmomi_VIM_VirtualMachine()
|
|
mock_vm.config.hardware.device << mock_RbVmomi_VIM_VirtualMachineDevice({:label => 'device1'})
|
|
mock_vm.config.hardware.device << mock_RbVmomi_VIM_VirtualMachineDevice({:label => 'device2'})
|
|
|
|
mock_vm
|
|
}
|
|
|
|
it 'should return a device if the device name matches' do
|
|
result = subject.find_device(vm_object,devicename)
|
|
|
|
expect(result.deviceInfo.label).to eq(devicename)
|
|
end
|
|
|
|
it 'should return nil if the device name does not match' do
|
|
result = subject.find_device(vm_object,'missing_device')
|
|
|
|
expect(result).to be_nil
|
|
end
|
|
end
|
|
|
|
describe '#find_disk_controller' do
|
|
let(:vm_object) {
|
|
mock_vm = mock_RbVmomi_VIM_VirtualMachine()
|
|
|
|
mock_vm
|
|
}
|
|
|
|
it 'should return nil when there are no devices' do
|
|
result = subject.find_disk_controller(vm_object)
|
|
|
|
expect(result).to be_nil
|
|
end
|
|
|
|
[0,1,14].each do |testcase|
|
|
it "should return a device for a single VirtualSCSIController with #{testcase} attached disks" do
|
|
mock_scsi = mock_RbVmomi_VIM_VirtualSCSIController()
|
|
vm_object.config.hardware.device << mock_scsi
|
|
vm_object.config.hardware.device << mock_RbVmomi_VIM_VirtualMachineDevice({:label => 'device1'})
|
|
vm_object.config.hardware.device << mock_RbVmomi_VIM_VirtualMachineDevice({:label => 'device2'})
|
|
|
|
# Add the disks
|
|
(1..testcase).each do
|
|
vm_object.config.hardware.device << mock_RbVmomi_VIM_VirtualDisk({ :controllerKey => mock_scsi.key })
|
|
end
|
|
|
|
result = subject.find_disk_controller(vm_object)
|
|
|
|
expect(result).to eq(mock_scsi)
|
|
end
|
|
end
|
|
|
|
[15].each do |testcase|
|
|
it "should return nil for a single VirtualSCSIController with #{testcase} attached disks" do
|
|
mock_scsi = mock_RbVmomi_VIM_VirtualSCSIController()
|
|
vm_object.config.hardware.device << mock_scsi
|
|
vm_object.config.hardware.device << mock_RbVmomi_VIM_VirtualMachineDevice({:label => 'device1'})
|
|
vm_object.config.hardware.device << mock_RbVmomi_VIM_VirtualMachineDevice({:label => 'device2'})
|
|
|
|
# Add the disks
|
|
(1..testcase).each do
|
|
vm_object.config.hardware.device << mock_RbVmomi_VIM_VirtualDisk({ :controllerKey => mock_scsi.key })
|
|
end
|
|
|
|
result = subject.find_disk_controller(vm_object)
|
|
|
|
expect(result).to be_nil
|
|
end
|
|
end
|
|
|
|
it 'should raise if a VirtualDisk is missing a controller' do
|
|
# Note - Typically this is not possible as a VirtualDisk requires a controller (SCSI, PVSCSI or IDE)
|
|
mock_scsi = mock_RbVmomi_VIM_VirtualDisk()
|
|
vm_object.config.hardware.device << mock_scsi
|
|
vm_object.config.hardware.device << mock_RbVmomi_VIM_VirtualMachineDevice({:label => 'device1'})
|
|
vm_object.config.hardware.device << mock_RbVmomi_VIM_VirtualMachineDevice({:label => 'device2'})
|
|
|
|
expect{subject.find_disk_controller(vm_object)}.to raise_error(NoMethodError)
|
|
end
|
|
end
|
|
|
|
describe '#find_disk_devices' do
|
|
let(:vm_object) {
|
|
mock_vm = mock_RbVmomi_VIM_VirtualMachine()
|
|
|
|
mock_vm
|
|
}
|
|
|
|
it 'should return empty hash when there are no devices' do
|
|
result = subject.find_disk_devices(vm_object)
|
|
|
|
expect(result).to eq({})
|
|
end
|
|
|
|
it 'should return empty hash when there are no VirtualSCSIController or VirtualDisk devices' do
|
|
vm_object.config.hardware.device << mock_RbVmomi_VIM_VirtualMachineDevice({:label => 'device1'})
|
|
vm_object.config.hardware.device << mock_RbVmomi_VIM_VirtualMachineDevice({:label => 'device2'})
|
|
|
|
result = subject.find_disk_devices(vm_object)
|
|
|
|
expect(result).to eq({})
|
|
end
|
|
|
|
it 'should return a device for a VirtualSCSIController device with no children' do
|
|
mock_scsi = mock_RbVmomi_VIM_VirtualSCSIController()
|
|
vm_object.config.hardware.device << mock_scsi
|
|
vm_object.config.hardware.device << mock_RbVmomi_VIM_VirtualMachineDevice({:label => 'device1'})
|
|
|
|
result = subject.find_disk_devices(vm_object)
|
|
|
|
expect(result.count).to eq(1)
|
|
expect(result[mock_scsi.key]).to_not be_nil
|
|
expect(result[mock_scsi.key]['children']).to eq([])
|
|
expect(result[mock_scsi.key]['device']).to eq(mock_scsi)
|
|
end
|
|
|
|
it 'should return a device for a VirtualDisk device' do
|
|
mock_disk = mock_RbVmomi_VIM_VirtualDisk()
|
|
vm_object.config.hardware.device << mock_disk
|
|
vm_object.config.hardware.device << mock_RbVmomi_VIM_VirtualMachineDevice({:label => 'device1'})
|
|
|
|
result = subject.find_disk_devices(vm_object)
|
|
|
|
expect(result.count).to eq(1)
|
|
expect(result[mock_disk.controllerKey]).to_not be_nil
|
|
expect(result[mock_disk.controllerKey]['children'][0]).to eq(mock_disk)
|
|
end
|
|
|
|
it 'should return one device for many VirtualDisk devices on the same controller' do
|
|
controller1Key = rand(2000)
|
|
controller2Key = controller1Key + 1
|
|
mock_disk1 = mock_RbVmomi_VIM_VirtualDisk({:controllerKey => controller1Key})
|
|
mock_disk2 = mock_RbVmomi_VIM_VirtualDisk({:controllerKey => controller1Key})
|
|
mock_disk3 = mock_RbVmomi_VIM_VirtualDisk({:controllerKey => controller2Key})
|
|
|
|
vm_object.config.hardware.device << mock_disk2
|
|
vm_object.config.hardware.device << mock_disk1
|
|
vm_object.config.hardware.device << mock_disk3
|
|
|
|
result = subject.find_disk_devices(vm_object)
|
|
|
|
expect(result.count).to eq(2)
|
|
|
|
expect(result[controller1Key]).to_not be_nil
|
|
expect(result[controller2Key]).to_not be_nil
|
|
|
|
expect(result[controller1Key]['children']).to contain_exactly(mock_disk1,mock_disk2)
|
|
expect(result[controller2Key]['children']).to contain_exactly(mock_disk3)
|
|
end
|
|
end
|
|
|
|
describe '#find_disk_unit_number' do
|
|
let(:vm_object) {
|
|
mock_vm = mock_RbVmomi_VIM_VirtualMachine()
|
|
|
|
mock_vm
|
|
}
|
|
let(:controller) { mock_RbVmomi_VIM_VirtualSCSIController() }
|
|
|
|
it 'should return 0 when there are no devices' do
|
|
result = subject.find_disk_unit_number(vm_object,controller)
|
|
|
|
expect(result).to eq(0)
|
|
end
|
|
|
|
context 'with a single SCSI Controller' do
|
|
before(:each) do
|
|
vm_object.config.hardware.device << controller
|
|
end
|
|
|
|
it 'should return 1 when the host bus controller is at 0' do
|
|
controller.scsiCtlrUnitNumber = 0
|
|
|
|
result = subject.find_disk_unit_number(vm_object,controller)
|
|
|
|
expect(result).to eq(1)
|
|
end
|
|
|
|
it 'should return the next lowest id when disks are attached' do
|
|
expected_id = 9
|
|
controller.scsiCtlrUnitNumber = 0
|
|
|
|
(1..expected_id-1).each do |disk_id|
|
|
mock_disk = mock_RbVmomi_VIM_VirtualDisk({
|
|
:controllerKey => controller.key,
|
|
:unitNumber => disk_id,
|
|
})
|
|
vm_object.config.hardware.device << mock_disk
|
|
end
|
|
result = subject.find_disk_unit_number(vm_object,controller)
|
|
|
|
expect(result).to eq(expected_id)
|
|
end
|
|
|
|
it 'should return nil when there are no spare units' do
|
|
controller.scsiCtlrUnitNumber = 0
|
|
|
|
(1..15).each do |disk_id|
|
|
mock_disk = mock_RbVmomi_VIM_VirtualDisk({
|
|
:controllerKey => controller.key,
|
|
:unitNumber => disk_id,
|
|
})
|
|
vm_object.config.hardware.device << mock_disk
|
|
end
|
|
result = subject.find_disk_unit_number(vm_object,controller)
|
|
|
|
expect(result).to eq(nil)
|
|
end
|
|
end
|
|
end
|
|
|
|
describe '#find_folder' do
|
|
let(:foldername) { 'folder'}
|
|
let(:missing_foldername) { 'missing_folder'}
|
|
|
|
before(:each) do
|
|
allow(connection.serviceInstance).to receive(:find_datacenter).and_return(datacenter_object)
|
|
end
|
|
|
|
context 'with no folder hierarchy' do
|
|
let(:datacenter_object) { mock_RbVmomi_VIM_Datacenter() }
|
|
|
|
it 'should return nil if the folder is not found' do
|
|
expect(subject.find_folder(missing_foldername,connection)).to be_nil
|
|
end
|
|
end
|
|
|
|
context 'with a single layer folder hierarchy' do
|
|
let(:datacenter_object) { mock_RbVmomi_VIM_Datacenter({
|
|
:vmfolder_tree => {
|
|
'folder1' => nil,
|
|
'folder2' => nil,
|
|
foldername => nil,
|
|
'folder3' => nil,
|
|
}
|
|
}) }
|
|
|
|
it 'should return the folder when found' do
|
|
result = subject.find_folder(foldername,connection)
|
|
expect(result).to_not be_nil
|
|
expect(result.name).to eq(foldername)
|
|
end
|
|
|
|
it 'should return nil if the folder is not found' do
|
|
expect(subject.find_folder(missing_foldername,connection)).to be_nil
|
|
end
|
|
end
|
|
|
|
context 'with a VM with the same name as a folder in a single layer folder hierarchy' do
|
|
# The folder hierarchy should include a VM with same name as folder, and appear BEFORE the
|
|
# folder in the child list.
|
|
let(:datacenter_object) { mock_RbVmomi_VIM_Datacenter({
|
|
:vmfolder_tree => {
|
|
'folder1' => nil,
|
|
'vm1' => { :object_type => 'vm', :name => foldername },
|
|
foldername => nil,
|
|
'folder3' => nil,
|
|
}
|
|
}) }
|
|
|
|
it 'should not return a VM' do
|
|
pending('https://github.com/puppetlabs/vmpooler/issues/204')
|
|
result = subject.find_folder(foldername,connection)
|
|
expect(result).to_not be_nil
|
|
expect(result.name).to eq(foldername)
|
|
expect(result.is_a? RbVmomi::VIM::VirtualMachine).to be false
|
|
end
|
|
end
|
|
|
|
context 'with a multi layer folder hierarchy' do
|
|
let(:end_folder_name) { 'folder'}
|
|
let(:foldername) { 'folder2/folder4/' + end_folder_name}
|
|
let(:datacenter_object) { mock_RbVmomi_VIM_Datacenter({
|
|
:vmfolder_tree => {
|
|
'folder1' => nil,
|
|
'folder2' => {
|
|
:children => {
|
|
'folder3' => nil,
|
|
'folder4' => {
|
|
:children => {
|
|
end_folder_name => nil,
|
|
},
|
|
}
|
|
},
|
|
},
|
|
'folder5' => nil,
|
|
}
|
|
}) }
|
|
|
|
it 'should return the folder when found' do
|
|
result = subject.find_folder(foldername,connection)
|
|
expect(result).to_not be_nil
|
|
expect(result.name).to eq(end_folder_name)
|
|
end
|
|
|
|
it 'should return nil if the folder is not found' do
|
|
expect(subject.find_folder(missing_foldername,connection)).to be_nil
|
|
end
|
|
end
|
|
|
|
context 'with a VM with the same name as a folder in a multi layer folder hierarchy' do
|
|
# The folder hierarchy should include a VM with same name as folder mid-hierarchy (i.e. not at the end level)
|
|
# and appear BEFORE the folder in the child list.
|
|
let(:end_folder_name) { 'folder'}
|
|
let(:foldername) { 'folder2/folder4/' + end_folder_name}
|
|
let(:datacenter_object) { mock_RbVmomi_VIM_Datacenter({
|
|
:vmfolder_tree => {
|
|
'folder1' => nil,
|
|
'folder2' => {
|
|
:children => {
|
|
'folder3' => nil,
|
|
'vm1' => { :object_type => 'vm', :name => 'folder4' },
|
|
'folder4' => {
|
|
:children => {
|
|
end_folder_name => nil,
|
|
},
|
|
}
|
|
},
|
|
},
|
|
'folder5' => nil,
|
|
}
|
|
}) }
|
|
|
|
it 'should not return a VM' do
|
|
pending('https://github.com/puppetlabs/vmpooler/issues/204')
|
|
result = subject.find_folder(foldername,connection)
|
|
expect(result).to_not be_nil
|
|
expect(result.name).to eq(foldername)
|
|
expect(result.is_a? RbVmomi::VIM::VirtualMachine).to be false
|
|
end
|
|
end
|
|
end
|
|
|
|
describe '#get_host_utilization' do
|
|
let(:cpu_model) { 'vendor line type sku v4 speed' }
|
|
let(:model) { 'v4' }
|
|
let(:different_model) { 'different_model' }
|
|
let(:limit) { 80 }
|
|
let(:default_limit) { 90 }
|
|
|
|
context "host with a different model" do
|
|
let(:host) { mock_RbVmomi_VIM_HostSystem() }
|
|
it 'should return nil' do
|
|
expect(subject.get_host_utilization(host,different_model,limit)).to be_nil
|
|
end
|
|
end
|
|
|
|
context "host in maintenance mode" do
|
|
let(:host) { mock_RbVmomi_VIM_HostSystem({
|
|
:maintenance_mode => true,
|
|
})
|
|
}
|
|
it 'should return nil' do
|
|
host.runtime.inMaintenanceMode = true
|
|
|
|
expect(subject.get_host_utilization(host,model,limit)).to be_nil
|
|
end
|
|
end
|
|
|
|
context "host with status of not green" do
|
|
let(:host) { mock_RbVmomi_VIM_HostSystem({
|
|
:overall_status => 'purple_alert',
|
|
})
|
|
}
|
|
it 'should return nil' do
|
|
expect(subject.get_host_utilization(host,model,limit)).to be_nil
|
|
end
|
|
end
|
|
|
|
# CPU utilization
|
|
context "host which exceeds limit in CPU utilization" do
|
|
let(:host) { mock_RbVmomi_VIM_HostSystem({
|
|
:overall_cpu_usage => 100,
|
|
:overall_memory_usage => 1,
|
|
:cpu_speed => 100, :num_cores_per_cpu => 1, :num_cpu => 1, :memory_size => 100.0 * 1024 * 1024,
|
|
})
|
|
}
|
|
it 'should return nil' do
|
|
expect(subject.get_host_utilization(host,model,limit)).to be_nil
|
|
end
|
|
end
|
|
|
|
context "host which exceeds default limit in CPU utilization" do
|
|
let(:host) { mock_RbVmomi_VIM_HostSystem({
|
|
:overall_cpu_usage => default_limit + 1.0,
|
|
:overall_memory_usage => 1,
|
|
:cpu_speed => 100, :num_cores_per_cpu => 1, :num_cpu => 1, :memory_size => 100.0 * 1024 * 1024,
|
|
})
|
|
}
|
|
it 'should return nil' do
|
|
expect(subject.get_host_utilization(host,model)).to be_nil
|
|
end
|
|
end
|
|
|
|
context "host which does not exceed default limit in CPU utilization" do
|
|
let(:host) { mock_RbVmomi_VIM_HostSystem({
|
|
:overall_cpu_usage => default_limit,
|
|
:overall_memory_usage => 1,
|
|
:cpu_speed => 100, :num_cores_per_cpu => 1, :num_cpu => 1, :memory_size => 100.0 * 1024 * 1024,
|
|
})
|
|
}
|
|
it 'should not return nil' do
|
|
expect(subject.get_host_utilization(host,model)).to_not be_nil
|
|
end
|
|
end
|
|
|
|
# Memory utilization
|
|
context "host which exceeds limit in Memory utilization" do
|
|
let(:host) { mock_RbVmomi_VIM_HostSystem({
|
|
:overall_cpu_usage => 1,
|
|
:overall_memory_usage => 100,
|
|
:cpu_speed => 100, :num_cores_per_cpu => 1, :num_cpu => 1, :memory_size => 100.0 * 1024 * 1024,
|
|
})
|
|
}
|
|
it 'should return nil' do
|
|
# Set the Memory Usage to 100%
|
|
expect(subject.get_host_utilization(host,model,limit)).to be_nil
|
|
end
|
|
end
|
|
|
|
context "host which exceeds default limit in Memory utilization" do
|
|
let(:host) { mock_RbVmomi_VIM_HostSystem({
|
|
:overall_cpu_usage => 1,
|
|
:overall_memory_usage => default_limit + 1.0,
|
|
:cpu_speed => 100, :num_cores_per_cpu => 1, :num_cpu => 1, :memory_size => 100.0 * 1024 * 1024,
|
|
})
|
|
}
|
|
it 'should return nil' do
|
|
expect(subject.get_host_utilization(host,model)).to be_nil
|
|
end
|
|
end
|
|
|
|
context "host which does not exceed default limit in Memory utilization" do
|
|
let(:host) { mock_RbVmomi_VIM_HostSystem({
|
|
:overall_cpu_usage => 1,
|
|
:overall_memory_usage => default_limit,
|
|
:cpu_speed => 100, :num_cores_per_cpu => 1, :num_cpu => 1, :memory_size => 100.0 * 1024 * 1024,
|
|
})
|
|
}
|
|
it 'should not return nil' do
|
|
expect(subject.get_host_utilization(host,model)).to_not be_nil
|
|
end
|
|
end
|
|
|
|
context "host which does not exceed limits" do
|
|
# Set CPU to 10%
|
|
# Set Memory to 20%
|
|
let(:host) { mock_RbVmomi_VIM_HostSystem({
|
|
:overall_cpu_usage => 10,
|
|
:overall_memory_usage => 20,
|
|
:cpu_speed => 100, :num_cores_per_cpu => 1, :num_cpu => 1, :memory_size => 100.0 * 1024 * 1024,
|
|
})
|
|
}
|
|
it 'should return the sum of CPU and Memory utilization' do
|
|
expect(subject.get_host_utilization(host,model,limit)[0]).to eq(10 + 20)
|
|
end
|
|
|
|
it 'should return the host' do
|
|
expect(subject.get_host_utilization(host,model,limit)[1]).to eq(host)
|
|
end
|
|
end
|
|
end
|
|
|
|
describe '#host_has_cpu_model?' do
|
|
let(:cpu_model) { 'vendor line type sku v4 speed' }
|
|
let(:model) { 'v4' }
|
|
let(:different_model) { 'different_model' }
|
|
let(:host) { mock_RbVmomi_VIM_HostSystem({
|
|
:cpu_model => cpu_model,
|
|
})
|
|
}
|
|
|
|
it 'should return true if the model matches' do
|
|
expect(subject.host_has_cpu_model?(host,model)).to eq(true)
|
|
end
|
|
|
|
it 'should return false if the model is different' do
|
|
expect(subject.host_has_cpu_model?(host,different_model)).to eq(false)
|
|
end
|
|
end
|
|
|
|
describe '#get_host_cpu_arch_version' do
|
|
let(:cpu_model) { 'vendor line type sku v4 speed' }
|
|
let(:model) { 'v4' }
|
|
let(:different_model) { 'different_model' }
|
|
let(:host) { mock_RbVmomi_VIM_HostSystem({
|
|
:cpu_model => cpu_model,
|
|
:num_cpu => 2,
|
|
})
|
|
}
|
|
|
|
it 'should return the fifth element in the string delimited by spaces' do
|
|
expect(subject.get_host_cpu_arch_version(host)).to eq(model)
|
|
end
|
|
|
|
it 'should use the description of the first CPU' do
|
|
host.hardware.cpuPkg[0].description = 'vendor line type sku v6 speed'
|
|
expect(subject.get_host_cpu_arch_version(host)).to eq('v6')
|
|
end
|
|
end
|
|
|
|
describe '#cpu_utilization_for' do
|
|
[{ :cpu_usage => 10.0,
|
|
:core_speed => 10.0,
|
|
:num_cores => 2,
|
|
:expected_value => 50.0,
|
|
},
|
|
{ :cpu_usage => 10.0,
|
|
:core_speed => 10.0,
|
|
:num_cores => 4,
|
|
:expected_value => 25.0,
|
|
},
|
|
{ :cpu_usage => 14.0,
|
|
:core_speed => 12.0,
|
|
:num_cores => 5,
|
|
:expected_value => 23.0 + 1.0/3.0,
|
|
},
|
|
].each do |testcase|
|
|
context "CPU Usage of #{testcase[:cpu_usage]}MHz with #{testcase[:num_cores]} x #{testcase[:core_speed]}MHz cores" do
|
|
it "should be #{testcase[:expected_value]}%" do
|
|
host = mock_RbVmomi_VIM_HostSystem({
|
|
:num_cores_per_cpu => testcase[:num_cores],
|
|
:cpu_speed => testcase[:core_speed],
|
|
:overall_cpu_usage => testcase[:cpu_usage],
|
|
})
|
|
|
|
expect(subject.cpu_utilization_for(host)).to eq(testcase[:expected_value])
|
|
end
|
|
end
|
|
end
|
|
end
|
|
|
|
describe '#memory_utilization_for' do
|
|
[{ :memory_usage_gigbytes => 10.0,
|
|
:memory_size_bytes => 10.0 * 1024 * 1024,
|
|
:expected_value => 100.0,
|
|
},
|
|
{ :memory_usage_gigbytes => 15.0,
|
|
:memory_size_bytes => 25.0 * 1024 * 1024,
|
|
:expected_value => 60.0,
|
|
},
|
|
{ :memory_usage_gigbytes => 9.0,
|
|
:memory_size_bytes => 31.0 * 1024 * 1024,
|
|
:expected_value => 29.03225806451613,
|
|
},
|
|
].each do |testcase|
|
|
context "Memory Usage of #{testcase[:memory_usage_gigbytes]}GBytes with #{testcase[:memory_size_bytes]}Bytes of total memory" do
|
|
it "should be #{testcase[:expected_value]}%" do
|
|
host = mock_RbVmomi_VIM_HostSystem({
|
|
:memory_size => testcase[:memory_size_bytes],
|
|
:overall_memory_usage => testcase[:memory_usage_gigbytes],
|
|
})
|
|
|
|
expect(subject.memory_utilization_for(host)).to eq(testcase[:expected_value])
|
|
end
|
|
end
|
|
end
|
|
end
|
|
|
|
describe '#find_least_used_host' do
|
|
let(:cluster_name) { 'cluster' }
|
|
let(:missing_cluster_name) { 'missing_cluster' }
|
|
let(:datacenter_object) { mock_RbVmomi_VIM_Datacenter() }
|
|
|
|
before(:each) do
|
|
# This mocking is a little fragile but hard to do without a real vCenter instance
|
|
allow(connection.serviceInstance).to receive(:find_datacenter).and_return(datacenter_object)
|
|
datacenter_object.hostFolder.childEntity = [cluster_object]
|
|
end
|
|
|
|
context 'missing cluster' do
|
|
let(:cluster_object) { mock_RbVmomi_VIM_ComputeResource({
|
|
:name => cluster_name,
|
|
:hosts => [{
|
|
:name => cluster_name,
|
|
}]})}
|
|
let(:expected_host) { cluster_object.host[0] }
|
|
|
|
it 'should raise an error' do
|
|
expect{subject.find_least_used_host(missing_cluster_name,connection)}.to raise_error(NoMethodError,/undefined method/)
|
|
end
|
|
end
|
|
|
|
context 'standalone host within limits' do
|
|
let(:cluster_object) { mock_RbVmomi_VIM_ComputeResource({
|
|
:name => cluster_name,
|
|
:hosts => [{
|
|
:name => cluster_name,
|
|
}]})}
|
|
let(:expected_host) { cluster_object.host[0] }
|
|
|
|
it 'should return the standalone host' do
|
|
result = subject.find_least_used_host(cluster_name,connection)
|
|
|
|
expect(result).to be(expected_host)
|
|
end
|
|
end
|
|
|
|
context 'standalone host outside the limits' do
|
|
let(:cluster_object) { mock_RbVmomi_VIM_ComputeResource({
|
|
:name => cluster_name,
|
|
:hosts => [{
|
|
:name => cluster_name,
|
|
:overall_cpu_usage => 100, :overall_memory_usage => 100, :cpu_speed => 100, :num_cores_per_cpu => 1, :num_cpu => 1, :memory_size => 100.0 * 1024 * 1024,
|
|
}]})}
|
|
let(:expected_host) { cluster_object.host[0] }
|
|
|
|
it 'should raise an error' do
|
|
expect{subject.find_least_used_host(missing_cluster_name,connection)}.to raise_error(NoMethodError,/undefined method/)
|
|
end
|
|
end
|
|
|
|
context 'cluster of 3 hosts within limits' do
|
|
let(:cluster_object) { mock_RbVmomi_VIM_ComputeResource({
|
|
:name => cluster_name,
|
|
:hosts => [
|
|
{ :overall_cpu_usage => 11, :overall_memory_usage => 11, :cpu_speed => 100, :num_cores_per_cpu => 1, :num_cpu => 1, :memory_size => 100.0 * 1024 * 1024 },
|
|
{ :overall_cpu_usage => 1, :overall_memory_usage => 1, :cpu_speed => 100, :num_cores_per_cpu => 1, :num_cpu => 1, :memory_size => 100.0 * 1024 * 1024 },
|
|
{ :overall_cpu_usage => 21, :overall_memory_usage => 21, :cpu_speed => 100, :num_cores_per_cpu => 1, :num_cpu => 1, :memory_size => 100.0 * 1024 * 1024 },
|
|
]}) }
|
|
let(:expected_host) { cluster_object.host[1] }
|
|
|
|
it 'should return the standalone host' do
|
|
result = subject.find_least_used_host(cluster_name,connection)
|
|
|
|
expect(result).to be(expected_host)
|
|
end
|
|
end
|
|
|
|
context 'cluster of 3 hosts all outside of the limits' do
|
|
let(:cluster_object) { mock_RbVmomi_VIM_ComputeResource({
|
|
:name => cluster_name,
|
|
:hosts => [
|
|
{ :overall_cpu_usage => 100, :overall_memory_usage => 100, :cpu_speed => 100, :num_cores_per_cpu => 1, :num_cpu => 1, :memory_size => 100.0 * 1024 * 1024 },
|
|
{ :overall_cpu_usage => 100, :overall_memory_usage => 100, :cpu_speed => 100, :num_cores_per_cpu => 1, :num_cpu => 1, :memory_size => 100.0 * 1024 * 1024 },
|
|
{ :overall_cpu_usage => 100, :overall_memory_usage => 100, :cpu_speed => 100, :num_cores_per_cpu => 1, :num_cpu => 1, :memory_size => 100.0 * 1024 * 1024 },
|
|
]}) }
|
|
let(:expected_host) { cluster_object.host[1] }
|
|
|
|
it 'should raise an error' do
|
|
expect{subject.find_least_used_host(missing_cluster_name,connection)}.to raise_error(NoMethodError,/undefined method/)
|
|
end
|
|
end
|
|
|
|
context 'cluster of 5 hosts of which one is out of limits and one has wrong CPU type' do
|
|
let(:cluster_object) { mock_RbVmomi_VIM_ComputeResource({
|
|
:name => cluster_name,
|
|
:hosts => [
|
|
{ :overall_cpu_usage => 31, :overall_memory_usage => 31, :cpu_speed => 100, :num_cores_per_cpu => 1, :num_cpu => 1, :memory_size => 100.0 * 1024 * 1024 },
|
|
{ :cpu_model => 'different cpu model', :overall_cpu_usage => 1, :overall_memory_usage => 1, :cpu_speed => 100, :num_cores_per_cpu => 1, :num_cpu => 1, :memory_size => 100.0 * 1024 * 1024 },
|
|
{ :overall_cpu_usage => 11, :overall_memory_usage => 11, :cpu_speed => 100, :num_cores_per_cpu => 1, :num_cpu => 1, :memory_size => 100.0 * 1024 * 1024 },
|
|
{ :overall_cpu_usage => 100, :overall_memory_usage => 100, :cpu_speed => 100, :num_cores_per_cpu => 1, :num_cpu => 1, :memory_size => 100.0 * 1024 * 1024 },
|
|
{ :overall_cpu_usage => 21, :overall_memory_usage => 21, :cpu_speed => 100, :num_cores_per_cpu => 1, :num_cpu => 1, :memory_size => 100.0 * 1024 * 1024 },
|
|
]}) }
|
|
let(:expected_host) { cluster_object.host[1] }
|
|
|
|
it 'should return the standalone host' do
|
|
result = subject.find_least_used_host(cluster_name,connection)
|
|
|
|
expect(result).to be(expected_host)
|
|
end
|
|
end
|
|
|
|
context 'cluster of 3 hosts all outside of the limits' do
|
|
let(:cluster_object) { mock_RbVmomi_VIM_ComputeResource({
|
|
:name => cluster_name,
|
|
:hosts => [
|
|
{ :overall_cpu_usage => 10, :overall_memory_usage => 10, :cpu_speed => 100, :num_cores_per_cpu => 1, :num_cpu => 1, :memory_size => 100.0 * 1024 * 1024 },
|
|
{ :overall_cpu_usage => 10, :overall_memory_usage => 10, :cpu_speed => 100, :num_cores_per_cpu => 1, :num_cpu => 1, :memory_size => 100.0 * 1024 * 1024 },
|
|
{ :overall_cpu_usage => 10, :overall_memory_usage => 10, :cpu_speed => 100, :num_cores_per_cpu => 1, :num_cpu => 1, :memory_size => 100.0 * 1024 * 1024 },
|
|
]}) }
|
|
let(:expected_host) { cluster_object.host[1] }
|
|
|
|
it 'should return a host' do
|
|
pending('https://github.com/puppetlabs/vmpooler/issues/206')
|
|
result = subject.find_least_used_host(missing_cluster_name,connection)
|
|
expect(result).to_not be_nil
|
|
end
|
|
end
|
|
end
|
|
|
|
describe '#find_cluster' do
|
|
let(:cluster) {'cluster'}
|
|
let(:missing_cluster) {'missing_cluster'}
|
|
|
|
before(:each) do
|
|
allow(connection.serviceInstance).to receive(:find_datacenter).and_return(datacenter_object)
|
|
end
|
|
|
|
context 'no clusters in the datacenter' do
|
|
let(:datacenter_object) { mock_RbVmomi_VIM_Datacenter() }
|
|
|
|
before(:each) do
|
|
end
|
|
|
|
it 'should return nil if the cluster is not found' do
|
|
expect(subject.find_cluster(missing_cluster,connection)).to be_nil
|
|
end
|
|
end
|
|
|
|
context 'with a single layer folder hierarchy' do
|
|
let(:datacenter_object) { mock_RbVmomi_VIM_Datacenter({
|
|
:hostfolder_tree => {
|
|
'cluster1' => {:object_type => 'compute_resource'},
|
|
'cluster2' => {:object_type => 'compute_resource'},
|
|
cluster => {:object_type => 'compute_resource'},
|
|
'cluster3' => {:object_type => 'compute_resource'},
|
|
}
|
|
}) }
|
|
|
|
it 'should return the cluster when found' do
|
|
result = subject.find_cluster(cluster,connection)
|
|
|
|
expect(result).to_not be_nil
|
|
expect(result.name).to eq(cluster)
|
|
end
|
|
|
|
it 'should return nil if the cluster is not found' do
|
|
expect(subject.find_cluster(missing_cluster,connection)).to be_nil
|
|
end
|
|
end
|
|
|
|
context 'with a multi layer folder hierarchy' do
|
|
let(:datacenter_object) { mock_RbVmomi_VIM_Datacenter({
|
|
:hostfolder_tree => {
|
|
'cluster1' => {:object_type => 'compute_resource'},
|
|
'folder2' => {
|
|
:children => {
|
|
cluster => {:object_type => 'compute_resource'},
|
|
}
|
|
},
|
|
'cluster3' => {:object_type => 'compute_resource'},
|
|
}
|
|
}) }
|
|
|
|
it 'should return the cluster when found' do
|
|
pending('https://github.com/puppetlabs/vmpooler/issues/205')
|
|
result = subject.find_cluster(cluster,connection)
|
|
|
|
expect(result).to_not be_nil
|
|
expect(result.name).to eq(cluster)
|
|
end
|
|
|
|
it 'should return nil if the cluster is not found' do
|
|
expect(subject.find_cluster(missing_cluster,connection)).to be_nil
|
|
end
|
|
end
|
|
end
|
|
|
|
describe '#get_cluster_host_utilization' do
|
|
context 'standalone host within limits' do
|
|
let(:cluster_object) { mock_RbVmomi_VIM_ComputeResource({:hosts => [{}]}) }
|
|
|
|
it 'should return array with one element' do
|
|
result = subject.get_cluster_host_utilization(cluster_object)
|
|
expect(result).to_not be_nil
|
|
expect(result.count).to eq(1)
|
|
end
|
|
end
|
|
|
|
context 'standalone host which is out the limits' do
|
|
let(:cluster_object) { mock_RbVmomi_VIM_ComputeResource({:hosts => [
|
|
{ :overall_cpu_usage => 100, :overall_memory_usage => 100, :cpu_speed => 100, :num_cores_per_cpu => 1, :num_cpu => 1, :memory_size => 100.0 * 1024 * 1024 },
|
|
]}) }
|
|
|
|
it 'should return array with 0 elements' do
|
|
result = subject.get_cluster_host_utilization(cluster_object)
|
|
expect(result).to_not be_nil
|
|
expect(result.count).to eq(0)
|
|
end
|
|
end
|
|
|
|
context 'cluster with 3 hosts within limits' do
|
|
let(:cluster_object) { mock_RbVmomi_VIM_ComputeResource({:hosts => [
|
|
{ :overall_cpu_usage => 1, :overall_memory_usage => 1, :cpu_speed => 100, :num_cores_per_cpu => 1, :num_cpu => 1, :memory_size => 100.0 * 1024 * 1024 },
|
|
{ :overall_cpu_usage => 11, :overall_memory_usage => 11, :cpu_speed => 100, :num_cores_per_cpu => 1, :num_cpu => 1, :memory_size => 100.0 * 1024 * 1024 },
|
|
{ :overall_cpu_usage => 21, :overall_memory_usage => 21, :cpu_speed => 100, :num_cores_per_cpu => 1, :num_cpu => 1, :memory_size => 100.0 * 1024 * 1024 },
|
|
]}) }
|
|
|
|
it 'should return array with 3 elements' do
|
|
result = subject.get_cluster_host_utilization(cluster_object)
|
|
expect(result).to_not be_nil
|
|
expect(result.count).to eq(3)
|
|
end
|
|
end
|
|
|
|
context 'cluster with 5 hosts of which 3 within limits' do
|
|
let(:cluster_object) { mock_RbVmomi_VIM_ComputeResource({:hosts => [
|
|
{ :overall_cpu_usage => 1, :overall_memory_usage => 1, :cpu_speed => 100, :num_cores_per_cpu => 1, :num_cpu => 1, :memory_size => 100.0 * 1024 * 1024 },
|
|
{ :overall_cpu_usage => 100, :overall_memory_usage => 100, :cpu_speed => 100, :num_cores_per_cpu => 1, :num_cpu => 1, :memory_size => 100.0 * 1024 * 1024 },
|
|
{ :overall_cpu_usage => 11, :overall_memory_usage => 11, :cpu_speed => 100, :num_cores_per_cpu => 1, :num_cpu => 1, :memory_size => 100.0 * 1024 * 1024 },
|
|
{ :overall_cpu_usage => 21, :overall_memory_usage => 21, :cpu_speed => 100, :num_cores_per_cpu => 1, :num_cpu => 1, :memory_size => 100.0 * 1024 * 1024 },
|
|
{ :overall_cpu_usage => 100, :overall_memory_usage => 100, :cpu_speed => 100, :num_cores_per_cpu => 1, :num_cpu => 1, :memory_size => 100.0 * 1024 * 1024 },
|
|
]}) }
|
|
|
|
it 'should return array with 3 elements' do
|
|
result = subject.get_cluster_host_utilization(cluster_object)
|
|
expect(result).to_not be_nil
|
|
expect(result.count).to eq(3)
|
|
end
|
|
end
|
|
|
|
context 'cluster with 3 hosts of which none are within the limits' do
|
|
let(:cluster_object) { mock_RbVmomi_VIM_ComputeResource({:hosts => [
|
|
{ :overall_cpu_usage => 100, :overall_memory_usage => 100, :cpu_speed => 100, :num_cores_per_cpu => 1, :num_cpu => 1, :memory_size => 100.0 * 1024 * 1024 },
|
|
{ :overall_cpu_usage => 100, :overall_memory_usage => 100, :cpu_speed => 100, :num_cores_per_cpu => 1, :num_cpu => 1, :memory_size => 100.0 * 1024 * 1024 },
|
|
{ :overall_cpu_usage => 100, :overall_memory_usage => 100, :cpu_speed => 100, :num_cores_per_cpu => 1, :num_cpu => 1, :memory_size => 100.0 * 1024 * 1024 },
|
|
]}) }
|
|
|
|
it 'should return array with 0 elements' do
|
|
result = subject.get_cluster_host_utilization(cluster_object)
|
|
expect(result).to_not be_nil
|
|
expect(result.count).to eq(0)
|
|
end
|
|
end
|
|
end
|
|
|
|
describe '#find_least_used_vpshere_compatible_host' do
|
|
let(:vm) { mock_RbVmomi_VIM_VirtualMachine() }
|
|
|
|
context 'standalone host within limits' do
|
|
let(:cluster_object) { mock_RbVmomi_VIM_ComputeResource({:hosts => [{}]}) }
|
|
let(:standalone_host) { cluster_object.host[0] }
|
|
|
|
before(:each) do
|
|
# This mocking is a little fragile but hard to do without a real vCenter instance
|
|
vm.summary.runtime.host = standalone_host
|
|
end
|
|
|
|
it 'should return the standalone host' do
|
|
result = subject.find_least_used_vpshere_compatible_host(vm)
|
|
|
|
expect(result).to_not be_nil
|
|
expect(result[0]).to be(standalone_host)
|
|
expect(result[1]).to eq(standalone_host.name)
|
|
end
|
|
end
|
|
|
|
context 'standalone host outside of limits' do
|
|
let(:cluster_object) { mock_RbVmomi_VIM_ComputeResource({:hosts => [
|
|
{ :overall_cpu_usage => 100, :overall_memory_usage => 100, :cpu_speed => 100, :num_cores_per_cpu => 1, :num_cpu => 1, :memory_size => 100.0 * 1024 * 1024 },
|
|
]}) }
|
|
let(:standalone_host) { cluster_object.host[0] }
|
|
|
|
before(:each) do
|
|
# This mocking is a little fragile but hard to do without a real vCenter instance
|
|
vm.summary.runtime.host = standalone_host
|
|
end
|
|
|
|
it 'should raise error' do
|
|
expect{subject.find_least_used_vpshere_compatible_host(vm)}.to raise_error(NoMethodError,/undefined method/)
|
|
end
|
|
end
|
|
|
|
context 'cluster of 3 hosts within limits' do
|
|
let(:cluster_object) { mock_RbVmomi_VIM_ComputeResource({:hosts => [
|
|
{ :overall_cpu_usage => 11, :overall_memory_usage => 11, :cpu_speed => 100, :num_cores_per_cpu => 1, :num_cpu => 1, :memory_size => 100.0 * 1024 * 1024 },
|
|
{ :overall_cpu_usage => 1, :overall_memory_usage => 1, :cpu_speed => 100, :num_cores_per_cpu => 1, :num_cpu => 1, :memory_size => 100.0 * 1024 * 1024 },
|
|
{ :overall_cpu_usage => 21, :overall_memory_usage => 21, :cpu_speed => 100, :num_cores_per_cpu => 1, :num_cpu => 1, :memory_size => 100.0 * 1024 * 1024 },
|
|
]}) }
|
|
let(:expected_host) { cluster_object.host[1] }
|
|
|
|
before(:each) do
|
|
# This mocking is a little fragile but hard to do without a real vCenter instance
|
|
vm.summary.runtime.host = expected_host
|
|
end
|
|
|
|
it 'should return the least used host' do
|
|
result = subject.find_least_used_vpshere_compatible_host(vm)
|
|
|
|
expect(result).to_not be_nil
|
|
expect(result[0]).to be(expected_host)
|
|
expect(result[1]).to eq(expected_host.name)
|
|
end
|
|
end
|
|
|
|
context 'cluster of 3 hosts all outside of the limits' do
|
|
let(:cluster_object) { mock_RbVmomi_VIM_ComputeResource({:hosts => [
|
|
{ :overall_cpu_usage => 100, :overall_memory_usage => 100, :cpu_speed => 100, :num_cores_per_cpu => 1, :num_cpu => 1, :memory_size => 100.0 * 1024 * 1024 },
|
|
{ :overall_cpu_usage => 100, :overall_memory_usage => 100, :cpu_speed => 100, :num_cores_per_cpu => 1, :num_cpu => 1, :memory_size => 100.0 * 1024 * 1024 },
|
|
{ :overall_cpu_usage => 100, :overall_memory_usage => 100, :cpu_speed => 100, :num_cores_per_cpu => 1, :num_cpu => 1, :memory_size => 100.0 * 1024 * 1024 },
|
|
]}) }
|
|
let(:expected_host) { cluster_object.host[1] }
|
|
|
|
before(:each) do
|
|
# This mocking is a little fragile but hard to do without a real vCenter instance
|
|
vm.summary.runtime.host = expected_host
|
|
end
|
|
|
|
it 'should raise error' do
|
|
expect{subject.find_least_used_vpshere_compatible_host(vm)}.to raise_error(NoMethodError,/undefined method/)
|
|
end
|
|
end
|
|
|
|
context 'cluster of 5 hosts of which one is out of limits and one has wrong CPU type' do
|
|
let(:cluster_object) { mock_RbVmomi_VIM_ComputeResource({:hosts => [
|
|
{ :overall_cpu_usage => 31, :overall_memory_usage => 31, :cpu_speed => 100, :num_cores_per_cpu => 1, :num_cpu => 1, :memory_size => 100.0 * 1024 * 1024 },
|
|
{ :cpu_model => 'different cpu model', :overall_cpu_usage => 1, :overall_memory_usage => 1, :cpu_speed => 100, :num_cores_per_cpu => 1, :num_cpu => 1, :memory_size => 100.0 * 1024 * 1024 },
|
|
{ :overall_cpu_usage => 11, :overall_memory_usage => 11, :cpu_speed => 100, :num_cores_per_cpu => 1, :num_cpu => 1, :memory_size => 100.0 * 1024 * 1024 },
|
|
{ :overall_cpu_usage => 100, :overall_memory_usage => 100, :cpu_speed => 100, :num_cores_per_cpu => 1, :num_cpu => 1, :memory_size => 100.0 * 1024 * 1024 },
|
|
{ :overall_cpu_usage => 21, :overall_memory_usage => 21, :cpu_speed => 100, :num_cores_per_cpu => 1, :num_cpu => 1, :memory_size => 100.0 * 1024 * 1024 },
|
|
]}) }
|
|
let(:expected_host) { cluster_object.host[2] }
|
|
|
|
before(:each) do
|
|
# This mocking is a little fragile but hard to do without a real vCenter instance
|
|
vm.summary.runtime.host = expected_host
|
|
end
|
|
|
|
it 'should return the least used host' do
|
|
result = subject.find_least_used_vpshere_compatible_host(vm)
|
|
|
|
expect(result).to_not be_nil
|
|
expect(result[0]).to be(expected_host)
|
|
expect(result[1]).to eq(expected_host.name)
|
|
end
|
|
end
|
|
|
|
context 'cluster of 3 hosts all with the same utilisation' do
|
|
let(:cluster_object) { mock_RbVmomi_VIM_ComputeResource({:hosts => [
|
|
{ :overall_cpu_usage => 10, :overall_memory_usage => 10, :cpu_speed => 100, :num_cores_per_cpu => 1, :num_cpu => 1, :memory_size => 100.0 * 1024 * 1024 },
|
|
{ :overall_cpu_usage => 10, :overall_memory_usage => 10, :cpu_speed => 100, :num_cores_per_cpu => 1, :num_cpu => 1, :memory_size => 100.0 * 1024 * 1024 },
|
|
{ :overall_cpu_usage => 10, :overall_memory_usage => 10, :cpu_speed => 100, :num_cores_per_cpu => 1, :num_cpu => 1, :memory_size => 100.0 * 1024 * 1024 },
|
|
]}) }
|
|
let(:expected_host) { cluster_object.host[1] }
|
|
|
|
before(:each) do
|
|
# This mocking is a little fragile but hard to do without a real vCenter instance
|
|
vm.summary.runtime.host = expected_host
|
|
end
|
|
|
|
it 'should return a host' do
|
|
pending('https://github.com/puppetlabs/vmpooler/issues/206 is fixed')
|
|
result = subject.find_least_used_vpshere_compatible_host(vm)
|
|
|
|
expect(result).to_not be_nil
|
|
end
|
|
end
|
|
end
|
|
|
|
describe '#find_pool' do
|
|
let(:poolname) { 'pool'}
|
|
let(:missing_poolname) { 'missing_pool'}
|
|
|
|
before(:each) do
|
|
allow(connection.serviceInstance).to receive(:find_datacenter).and_return(datacenter_object)
|
|
end
|
|
|
|
context 'with empty folder hierarchy' do
|
|
let(:datacenter_object) { mock_RbVmomi_VIM_Datacenter() }
|
|
|
|
it 'should ensure the connection' do
|
|
pending('https://github.com/puppetlabs/vmpooler/issues/209')
|
|
expect(subject).to receive(:ensure_connected)
|
|
|
|
subject.find_pool(poolname,connection)
|
|
end
|
|
|
|
it 'should return nil if the pool is not found' do
|
|
pending('https://github.com/puppetlabs/vmpooler/issues/209')
|
|
expect(subject.find_pool(missing_poolname,connection)).to be_nil
|
|
end
|
|
end
|
|
|
|
[
|
|
# Single layer Host folder hierarchy
|
|
{
|
|
:context => 'single layer folder hierarchy with a resource pool',
|
|
:poolpath => 'pool',
|
|
:poolname => 'pool',
|
|
:hostfolder_tree => {
|
|
'folder1' => nil,
|
|
'folder2' => nil,
|
|
'pool' => {:object_type => 'resource_pool'},
|
|
'folder3' => nil,
|
|
},
|
|
},
|
|
{
|
|
:context => 'single layer folder hierarchy with a child resource pool',
|
|
:poolpath => 'parentpool/pool',
|
|
:poolname => 'pool',
|
|
:hostfolder_tree => {
|
|
'folder1' => nil,
|
|
'folder2' => nil,
|
|
'parentpool' => {:object_type => 'resource_pool', :children => {
|
|
'pool' => {:object_type => 'resource_pool'},
|
|
}},
|
|
'folder3' => nil,
|
|
},
|
|
},
|
|
{
|
|
:context => 'single layer folder hierarchy with a resource pool within a cluster',
|
|
:poolpath => 'cluster/pool',
|
|
:poolname => 'pool',
|
|
:hostfolder_tree => {
|
|
'folder1' => nil,
|
|
'folder2' => nil,
|
|
'cluster' => {:object_type => 'cluster_compute_resource', :children => {
|
|
'pool' => {:object_type => 'resource_pool'},
|
|
}},
|
|
'folder3' => nil,
|
|
},
|
|
},
|
|
# Multi layer Host folder hierarchy
|
|
{
|
|
:context => 'multi layer folder hierarchy with a resource pool',
|
|
:poolpath => 'folder2/folder4/pool',
|
|
:poolname => 'pool',
|
|
:hostfolder_tree => {
|
|
'folder1' => nil,
|
|
'folder2' => { :children => {
|
|
'folder3' => nil,
|
|
'folder4' => { :children => {
|
|
'pool' => {:object_type => 'resource_pool'},
|
|
}},
|
|
}},
|
|
'folder5' => nil,
|
|
},
|
|
},
|
|
{
|
|
:context => 'multi layer folder hierarchy with a child resource pool',
|
|
:poolpath => 'folder2/folder4/parentpool/pool',
|
|
:poolname => 'pool',
|
|
:hostfolder_tree => {
|
|
'folder1' => nil,
|
|
'folder2' => { :children => {
|
|
'folder3' => nil,
|
|
'folder4' => { :children => {
|
|
'parentpool' => {:object_type => 'resource_pool', :children => {
|
|
'pool' => {:object_type => 'resource_pool'},
|
|
}},
|
|
}},
|
|
}},
|
|
'folder5' => nil,
|
|
},
|
|
},
|
|
{
|
|
:context => 'multi layer folder hierarchy with a resource pool within a cluster',
|
|
:poolpath => 'folder2/folder4/cluster/pool',
|
|
:poolname => 'pool',
|
|
:hostfolder_tree => {
|
|
'folder1' => nil,
|
|
'folder2' => { :children => {
|
|
'folder3' => nil,
|
|
'folder4' => { :children => {
|
|
'cluster' => {:object_type => 'cluster_compute_resource', :children => {
|
|
'pool' => {:object_type => 'resource_pool'},
|
|
}},
|
|
}},
|
|
}},
|
|
'folder5' => nil,
|
|
},
|
|
},
|
|
].each do |testcase|
|
|
context testcase[:context] do
|
|
let(:datacenter_object) { mock_RbVmomi_VIM_Datacenter({ :hostfolder_tree => testcase[:hostfolder_tree]}) }
|
|
|
|
it 'should return the pool when found' do
|
|
result = subject.find_pool(testcase[:poolpath],connection)
|
|
|
|
expect(result).to_not be_nil
|
|
expect(result.name).to eq(testcase[:poolname])
|
|
expect(result.is_a?(RbVmomi::VIM::ResourcePool)).to be true
|
|
end
|
|
|
|
it 'should return nil if the poolname is not found' do
|
|
pending('https://github.com/puppetlabs/vmpooler/issues/209')
|
|
expect(subject.find_pool(missing_poolname,connection)).to be_nil
|
|
end
|
|
end
|
|
end
|
|
|
|
# Tests for issue https://github.com/puppetlabs/vmpooler/issues/210
|
|
[
|
|
{
|
|
:context => 'multi layer folder hierarchy with a resource pool the same name as a folder',
|
|
:poolpath => 'folder2/folder4/cluster/pool',
|
|
:poolname => 'pool',
|
|
:hostfolder_tree => {
|
|
'folder1' => nil,
|
|
'folder2' => { :children => {
|
|
'folder3' => nil,
|
|
'bad_pool' => {:object_type => 'resource_pool', :name => 'folder4'},
|
|
'folder4' => { :children => {
|
|
'cluster' => {:object_type => 'cluster_compute_resource', :children => {
|
|
'pool' => {:object_type => 'resource_pool'},
|
|
}},
|
|
}},
|
|
}},
|
|
'folder5' => nil,
|
|
},
|
|
},
|
|
{
|
|
:context => 'multi layer folder hierarchy with a cluster the same name as a folder',
|
|
:poolpath => 'folder2/folder4/cluster/pool',
|
|
:poolname => 'pool',
|
|
:hostfolder_tree => {
|
|
'folder1' => nil,
|
|
'folder2' => { :children => {
|
|
'folder3' => nil,
|
|
'bad_cluster' => {:object_type => 'cluster_compute_resource', :name => 'folder4'},
|
|
'folder4' => { :children => {
|
|
'cluster' => {:object_type => 'cluster_compute_resource', :children => {
|
|
'pool' => {:object_type => 'resource_pool'},
|
|
}},
|
|
}},
|
|
}},
|
|
'folder5' => nil,
|
|
},
|
|
},
|
|
].each do |testcase|
|
|
context testcase[:context] do
|
|
let(:datacenter_object) { mock_RbVmomi_VIM_Datacenter({ :hostfolder_tree => testcase[:hostfolder_tree]}) }
|
|
|
|
it 'should ensure the connection' do
|
|
pending('https://github.com/puppetlabs/vmpooler/issues/210')
|
|
expect(subject).to receive(:ensure_connected)
|
|
|
|
subject.find_pool(testcase[:poolpath])
|
|
end
|
|
|
|
it 'should return the pool when found' do
|
|
pending('https://github.com/puppetlabs/vmpooler/issues/210')
|
|
result = subject.find_pool(testcase[:poolpath])
|
|
|
|
expect(result).to_not be_nil
|
|
expect(result.name).to eq(testcase[:poolname])
|
|
expect(result.is_a?(RbVmomi::VIM::ResourcePool)).to be true
|
|
end
|
|
end
|
|
end
|
|
end
|
|
|
|
describe '#find_snapshot' do
|
|
let(:snapshot_name) {'snapshot'}
|
|
let(:missing_snapshot_name) {'missing_snapshot'}
|
|
let(:vm) { mock_RbVmomi_VIM_VirtualMachine(mock_options) }
|
|
let(:snapshot_object) { mock_RbVmomi_VIM_VirtualMachine() }
|
|
|
|
context 'VM with no snapshots' do
|
|
let(:mock_options) {{ :snapshot_tree => nil }}
|
|
it 'should return nil' do
|
|
expect(subject.find_snapshot(vm,snapshot_name)).to be_nil
|
|
end
|
|
end
|
|
|
|
context 'VM with a single layer of snapshots' do
|
|
let(:mock_options) {{
|
|
:snapshot_tree => {
|
|
'snapshot1' => nil,
|
|
'snapshot2' => nil,
|
|
'snapshot3' => nil,
|
|
'snapshot4' => nil,
|
|
snapshot_name => { :ref => snapshot_object},
|
|
}
|
|
}}
|
|
|
|
it 'should return snapshot which matches the name' do
|
|
result = subject.find_snapshot(vm,snapshot_name)
|
|
expect(result).to be(snapshot_object)
|
|
end
|
|
|
|
it 'should return nil which no matches are found' do
|
|
result = subject.find_snapshot(vm,missing_snapshot_name)
|
|
expect(result).to be_nil
|
|
end
|
|
end
|
|
|
|
context 'VM with a nested layers of snapshots' do
|
|
let(:mock_options) {{
|
|
:snapshot_tree => {
|
|
'snapshot1' => nil,
|
|
'snapshot2' => nil,
|
|
'snapshot3' => { :children => {
|
|
'snapshot4' => nil,
|
|
'snapshot5' => { :children => {
|
|
snapshot_name => { :ref => snapshot_object},
|
|
}},
|
|
}},
|
|
'snapshot6' => nil,
|
|
}
|
|
}}
|
|
|
|
it 'should return snapshot which matches the name' do
|
|
result = subject.find_snapshot(vm,snapshot_name)
|
|
expect(result).to be(snapshot_object)
|
|
end
|
|
|
|
it 'should return nil which no matches are found' do
|
|
result = subject.find_snapshot(vm,missing_snapshot_name)
|
|
expect(result).to be_nil
|
|
end
|
|
end
|
|
end
|
|
|
|
describe '#find_vm' do
|
|
before(:each) do
|
|
allow(subject).to receive(:find_vm_light).and_return('vmlight')
|
|
allow(subject).to receive(:find_vm_heavy).and_return( { vmname => 'vmheavy' })
|
|
end
|
|
|
|
it 'should call find_vm_light' do
|
|
expect(subject).to receive(:find_vm_light).and_return('vmlight')
|
|
|
|
expect(subject.find_vm(vmname,connection)).to eq('vmlight')
|
|
end
|
|
|
|
it 'should not call find_vm_heavy if find_vm_light finds the VM' do
|
|
expect(subject).to receive(:find_vm_light).and_return('vmlight')
|
|
expect(subject).to receive(:find_vm_heavy).exactly(0).times
|
|
|
|
expect(subject.find_vm(vmname,connection)).to eq('vmlight')
|
|
end
|
|
|
|
it 'should call find_vm_heavy when find_vm_light returns nil' do
|
|
expect(subject).to receive(:find_vm_light).and_return(nil)
|
|
expect(subject).to receive(:find_vm_heavy).and_return( { vmname => 'vmheavy' })
|
|
|
|
expect(subject.find_vm(vmname,connection)).to eq('vmheavy')
|
|
end
|
|
end
|
|
|
|
describe '#find_vm_light' do
|
|
let(:missing_vm) { 'missing_vm' }
|
|
|
|
before(:each) do
|
|
allow(connection.searchIndex).to receive(:FindByDnsName).and_return(nil)
|
|
end
|
|
|
|
it 'should call FindByDnsName with the correct parameters' do
|
|
expect(connection.searchIndex).to receive(:FindByDnsName).with({
|
|
:vmSearch => true,
|
|
dnsName: vmname,
|
|
})
|
|
|
|
subject.find_vm_light(vmname,connection)
|
|
end
|
|
|
|
it 'should return the VM object when found' do
|
|
vm_object = mock_RbVmomi_VIM_VirtualMachine()
|
|
expect(connection.searchIndex).to receive(:FindByDnsName).with({
|
|
:vmSearch => true,
|
|
dnsName: vmname,
|
|
}).and_return(vm_object)
|
|
|
|
expect(subject.find_vm_light(vmname,connection)).to be(vm_object)
|
|
end
|
|
|
|
it 'should return nil if the VM is not found' do
|
|
expect(connection.searchIndex).to receive(:FindByDnsName).with({
|
|
:vmSearch => true,
|
|
dnsName: missing_vm,
|
|
}).and_return(nil)
|
|
|
|
expect(subject.find_vm_light(missing_vm,connection)).to be_nil
|
|
end
|
|
end
|
|
|
|
describe '#find_vm_heavy' do
|
|
let(:missing_vm) { 'missing_vm' }
|
|
# Return an empty result by default
|
|
let(:retrieve_result) {{}}
|
|
|
|
before(:each) do
|
|
allow(connection.propertyCollector).to receive(:RetrievePropertiesEx).and_return(mock_RbVmomi_VIM_RetrieveResult(retrieve_result))
|
|
end
|
|
|
|
context 'Search result is empty' do
|
|
it 'should return empty hash' do
|
|
expect(subject.find_vm_heavy(vmname,connection)).to eq({})
|
|
end
|
|
end
|
|
|
|
context 'Search result contains VMs but no matches' do
|
|
let(:retrieve_result) {
|
|
{ :response => [
|
|
{ 'name' => 'no_match001'},
|
|
{ 'name' => 'no_match002'},
|
|
{ 'name' => 'no_match003'},
|
|
{ 'name' => 'no_match004'},
|
|
]
|
|
}
|
|
}
|
|
|
|
it 'should return empty hash' do
|
|
expect(subject.find_vm_heavy(vmname,connection)).to eq({})
|
|
end
|
|
end
|
|
|
|
context 'Search contains a single match' do
|
|
let(:vm_object) { mock_RbVmomi_VIM_VirtualMachine({ :name => vmname })}
|
|
let(:retrieve_result) {
|
|
{ :response => [
|
|
{ 'name' => 'no_match001'},
|
|
{ 'name' => 'no_match002'},
|
|
{ 'name' => vmname, :object => vm_object },
|
|
{ 'name' => 'no_match003'},
|
|
{ 'name' => 'no_match004'},
|
|
]
|
|
}
|
|
}
|
|
|
|
it 'should return single result' do
|
|
result = subject.find_vm_heavy(vmname,connection)
|
|
expect(result.keys.count).to eq(1)
|
|
end
|
|
|
|
it 'should return the matching VM Object' do
|
|
result = subject.find_vm_heavy(vmname,connection)
|
|
expect(result[vmname]).to be(vm_object)
|
|
end
|
|
end
|
|
|
|
context 'Search contains a two matches' do
|
|
let(:vm_object1) { mock_RbVmomi_VIM_VirtualMachine({ :name => vmname })}
|
|
let(:vm_object2) { mock_RbVmomi_VIM_VirtualMachine({ :name => vmname })}
|
|
let(:retrieve_result) {
|
|
{ :response => [
|
|
{ 'name' => 'no_match001'},
|
|
{ 'name' => 'no_match002'},
|
|
{ 'name' => vmname, :object => vm_object1 },
|
|
{ 'name' => 'no_match003'},
|
|
{ 'name' => 'no_match004'},
|
|
{ 'name' => vmname, :object => vm_object2 },
|
|
]
|
|
}
|
|
}
|
|
|
|
it 'should return one result' do
|
|
result = subject.find_vm_heavy(vmname,connection)
|
|
expect(result.keys.count).to eq(1)
|
|
end
|
|
|
|
it 'should return the last matching VM Object' do
|
|
result = subject.find_vm_heavy(vmname,connection)
|
|
expect(result[vmname]).to be(vm_object2)
|
|
end
|
|
end
|
|
end
|
|
|
|
describe '#find_vmdks' do
|
|
let(:datastorename) { 'datastore' }
|
|
let(:connection_options) {{
|
|
:serviceContent => {
|
|
:datacenters => [
|
|
{ :name => 'MockDC', :datastores => [datastorename] }
|
|
]
|
|
}
|
|
}}
|
|
|
|
let(:collectMultiple_response) { {} }
|
|
|
|
before(:each) do
|
|
# NOTE - This method should not be using `_connection`, instead it should be using `@conection`
|
|
mock_ds = subject.find_datastore(datastorename,connection)
|
|
allow(mock_ds).to receive(:_connection).and_return(connection)
|
|
allow(connection.serviceContent.propertyCollector).to receive(:collectMultiple).and_return(collectMultiple_response)
|
|
end
|
|
|
|
it 'should not use _connction to get the underlying connection object' do
|
|
pending('https://github.com/puppetlabs/vmpooler/issues/213')
|
|
|
|
mock_ds = subject.find_datastore(datastorename)
|
|
expect(mock_ds).to receive(:_connection).exactly(0).times
|
|
|
|
begin
|
|
# ignore all errors. What's important is that it doesn't call _connection
|
|
subject.find_vmdks(vmname,datastorename,connection)
|
|
rescue
|
|
end
|
|
end
|
|
|
|
context 'Searching all files for all VMs on a Datastore' do
|
|
# This is fairly fragile mocking
|
|
let(:collectMultiple_response) { {
|
|
'FakeVMObject1' => { 'layoutEx.file' =>
|
|
[
|
|
mock_RbVmomi_VIM_VirtualMachineFileLayoutExFileInfo({ :key => 101, :name => "[#{datastorename}] mock1/mock1_0.vmdk"})
|
|
]},
|
|
vmname => { 'layoutEx.file' =>
|
|
[
|
|
# VMDKs which should match
|
|
mock_RbVmomi_VIM_VirtualMachineFileLayoutExFileInfo({ :key => 1, :name => "[#{datastorename}] #{vmname}/#{vmname}_0.vmdk"}),
|
|
mock_RbVmomi_VIM_VirtualMachineFileLayoutExFileInfo({ :key => 2, :name => "[#{datastorename}] #{vmname}/#{vmname}_1.vmdk"}),
|
|
# VMDKs which should not match
|
|
mock_RbVmomi_VIM_VirtualMachineFileLayoutExFileInfo({ :key => 102, :name => "[otherdatastore] #{vmname}/#{vmname}_0.vmdk"}),
|
|
mock_RbVmomi_VIM_VirtualMachineFileLayoutExFileInfo({ :key => 103, :name => "[otherdatastore] #{vmname}/#{vmname}.vmdk"}),
|
|
mock_RbVmomi_VIM_VirtualMachineFileLayoutExFileInfo({ :key => 104, :name => "[otherdatastore] #{vmname}/#{vmname}_abc.vmdk"}),
|
|
]},
|
|
} }
|
|
|
|
it 'should return empty array if no VMDKs match the VM name' do
|
|
expect(subject.find_vmdks('missing_vm_name',datastorename,connection)).to eq([])
|
|
end
|
|
|
|
it 'should return matching VMDKs for the VM' do
|
|
result = subject.find_vmdks(vmname,datastorename,connection)
|
|
expect(result).to_not be_nil
|
|
expect(result.count).to eq(2)
|
|
# The keys for each VMDK should be less that 100 as per the mocks
|
|
result.each do |fileinfo|
|
|
expect(fileinfo.key).to be < 100
|
|
end
|
|
end
|
|
end
|
|
end
|
|
|
|
describe '#get_base_vm_container_from' do
|
|
it 'should return a recursive view of type VirtualMachine' do
|
|
result = subject.get_base_vm_container_from(connection)
|
|
|
|
expect(result.recursive).to be true
|
|
expect(result.type).to eq(['VirtualMachine'])
|
|
end
|
|
end
|
|
|
|
describe '#get_snapshot_list' do
|
|
let(:snapshot_name) {'snapshot'}
|
|
let(:snapshot_tree) { mock_RbVmomi_VIM_VirtualMachine(mock_options).snapshot.rootSnapshotList }
|
|
let(:snapshot_object) { mock_RbVmomi_VIM_VirtualMachine() }
|
|
|
|
it 'should raise if the snapshot tree is nil' do
|
|
expect{ subject.get_snapshot_list(nil,snapshot_name)}.to raise_error(NoMethodError)
|
|
end
|
|
|
|
context 'VM with a single layer of snapshots' do
|
|
let(:mock_options) {{
|
|
:snapshot_tree => {
|
|
'snapshot1' => nil,
|
|
'snapshot2' => nil,
|
|
'snapshot3' => nil,
|
|
'snapshot4' => nil,
|
|
snapshot_name => { :ref => snapshot_object},
|
|
}
|
|
}}
|
|
|
|
it 'should return snapshot which matches the name' do
|
|
result = subject.get_snapshot_list(snapshot_tree,snapshot_name)
|
|
expect(result).to be(snapshot_object)
|
|
end
|
|
end
|
|
|
|
context 'VM with a nested layers of snapshots' do
|
|
let(:mock_options) {{
|
|
:snapshot_tree => {
|
|
'snapshot1' => nil,
|
|
'snapshot2' => nil,
|
|
'snapshot3' => { :children => {
|
|
'snapshot4' => nil,
|
|
'snapshot5' => { :children => {
|
|
snapshot_name => { :ref => snapshot_object},
|
|
}},
|
|
}},
|
|
'snapshot6' => nil,
|
|
}
|
|
}}
|
|
|
|
it 'should return snapshot which matches the name' do
|
|
result = subject.get_snapshot_list(snapshot_tree,snapshot_name)
|
|
expect(result).to be(snapshot_object)
|
|
end
|
|
end
|
|
end
|
|
|
|
describe '#migrate_vm_host' do
|
|
let(:vm_object) { mock_RbVmomi_VIM_VirtualMachine({ :name => vmname })}
|
|
let(:host_object) { mock_RbVmomi_VIM_HostSystem({ :name => 'HOST' })}
|
|
let(:relocate_task) { mock_RbVmomi_VIM_Task() }
|
|
|
|
before(:each) do
|
|
allow(vm_object).to receive(:RelocateVM_Task).and_return(relocate_task)
|
|
allow(relocate_task).to receive(:wait_for_completion)
|
|
end
|
|
|
|
it 'should call RelovateVM_Task' do
|
|
expect(vm_object).to receive(:RelocateVM_Task).and_return(relocate_task)
|
|
|
|
subject.migrate_vm_host(vm_object,host_object)
|
|
end
|
|
|
|
it 'should use a Relocation Spec object with correct host' do
|
|
expect(vm_object).to receive(:RelocateVM_Task).with(relocation_spec_with_host(host_object))
|
|
|
|
subject.migrate_vm_host(vm_object,host_object)
|
|
end
|
|
|
|
it 'should wait for the relocation to complete' do
|
|
expect(relocate_task).to receive(:wait_for_completion)
|
|
|
|
subject.migrate_vm_host(vm_object,host_object)
|
|
end
|
|
|
|
it 'should return the result of the relocation' do
|
|
expect(relocate_task).to receive(:wait_for_completion).and_return('RELOCATE_RESULT')
|
|
|
|
expect(subject.migrate_vm_host(vm_object,host_object)).to eq('RELOCATE_RESULT')
|
|
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
|