mirror of
https://github.com/puppetlabs/vmpooler.git
synced 2026-01-26 01:58:41 -05:00
(POOLER-72) Add Dummy Provider
Previously the only VM Provider was vSphere however this made testing and making changes difficult as it required a functioning vSphere instance. This commit adds a Dummy Provider which presents a VM provider to Pool Manager but manages provisioned "VM"s in a hashtable. The Dummy Provider can also be configured to randomly fail operations and take random amounts of time to perform operations, such as cloning a VM, which is useful to see how the Pool Manager copes with these events. This commit also updates the configuration YAML documentation and adds appropriate unit tests.
This commit is contained in:
parent
d9d45109f2
commit
77afc86aeb
4 changed files with 962 additions and 1 deletions
|
|
@ -1,4 +1,4 @@
|
|||
%w(base vsphere).each do |lib|
|
||||
%w(base dummy vsphere).each do |lib|
|
||||
begin
|
||||
require "vmpooler/providers/#{lib}"
|
||||
rescue LoadError
|
||||
|
|
|
|||
360
lib/vmpooler/providers/dummy.rb
Normal file
360
lib/vmpooler/providers/dummy.rb
Normal file
|
|
@ -0,0 +1,360 @@
|
|||
require 'yaml'
|
||||
|
||||
module Vmpooler
|
||||
class PoolManager
|
||||
class Provider
|
||||
class Dummy < Vmpooler::PoolManager::Provider::Base
|
||||
# Fake VM Provider for testing
|
||||
|
||||
def initialize(config, logger, metrics, name, options)
|
||||
super(config, logger, metrics, name, options)
|
||||
dummyfilename = provider_config['filename']
|
||||
|
||||
# This initial_state option is only intended to be used by spec tests
|
||||
@dummylist = provider_options['initial_state'].nil? ? {} : provider_options['initial_state']
|
||||
|
||||
@dummylist = YAML.load_file(dummyfilename) if !dummyfilename.nil? && File.exist?(dummyfilename)
|
||||
|
||||
# Even though this code is using Mutexes, it's still no 100% atomic i.e. it's still possible for
|
||||
# duplicate actions to put the @dummylist hashtable into a bad state, for example;
|
||||
# Deleting a VM while it's in the middle of adding a disk.
|
||||
@write_lock = Mutex.new
|
||||
end
|
||||
|
||||
def name
|
||||
'dummy'
|
||||
end
|
||||
|
||||
def vms_in_pool(pool_name)
|
||||
vmlist = []
|
||||
get_dummy_pool_object(pool_name).each do |vm|
|
||||
vmlist << { 'name' => vm['name'] }
|
||||
end
|
||||
|
||||
vmlist
|
||||
end
|
||||
|
||||
def get_vm_host(pool_name, vm_name)
|
||||
current_vm = get_dummy_vm(pool_name, vm_name)
|
||||
|
||||
current_vm.nil? ? raise("VM #{vm_name} does not exist") : current_vm['vm_host']
|
||||
end
|
||||
|
||||
def find_least_used_compatible_host(pool_name, vm_name)
|
||||
current_vm = get_dummy_vm(pool_name, vm_name)
|
||||
|
||||
# Unless migratevm_couldmove_percent is specified, don't migrate
|
||||
return current_vm['vm_host'] if provider_config['migratevm_couldmove_percent'].nil?
|
||||
|
||||
# Only migrate if migratevm_couldmove_percent is met
|
||||
return current_vm['vm_host'] if 1 + rand(100) > provider_config['migratevm_couldmove_percent']
|
||||
|
||||
# Simulate a 10 node cluster and randomly pick a different one
|
||||
new_host = 'HOST' + (1 + rand(10)).to_s while new_host == current_vm['vm_host']
|
||||
|
||||
new_host
|
||||
end
|
||||
|
||||
def migrate_vm_to_host(pool_name, vm_name, dest_host_name)
|
||||
current_vm = get_dummy_vm(pool_name, vm_name)
|
||||
|
||||
# Inject migration delay
|
||||
unless provider_config['migratevm_max_time'].nil?
|
||||
migrate_time = 1 + rand(provider_config['migratevm_max_time'])
|
||||
sleep(migrate_time)
|
||||
end
|
||||
|
||||
# Inject clone failure
|
||||
unless provider_config['migratevm_fail_percent'].nil?
|
||||
raise('Dummy Failure for migratevm_fail_percent') if 1 + rand(100) <= provider_config['migratevm_fail_percent']
|
||||
end
|
||||
|
||||
@write_lock.synchronize do
|
||||
current_vm = get_dummy_vm(pool_name, vm_name)
|
||||
current_vm['vm_host'] = dest_host_name
|
||||
write_backing_file
|
||||
end
|
||||
|
||||
true
|
||||
end
|
||||
|
||||
def get_vm(pool_name, vm_name)
|
||||
dummy = get_dummy_vm(pool_name, vm_name)
|
||||
return nil if dummy.nil?
|
||||
|
||||
# Randomly power off the VM
|
||||
unless dummy['powerstate'] != 'PoweredOn' || provider_config['getvm_poweroff_percent'].nil?
|
||||
if 1 + rand(100) <= provider_config['getvm_poweroff_percent']
|
||||
@write_lock.synchronize do
|
||||
dummy = get_dummy_vm(pool_name, vm_name)
|
||||
dummy['powerstate'] = 'PoweredOff'
|
||||
write_backing_file
|
||||
end
|
||||
logger.log('d', "[ ] [#{dummy['poolname']}] '#{dummy['name']}' is being Dummy Powered Off")
|
||||
end
|
||||
end
|
||||
|
||||
# Randomly rename the host
|
||||
unless dummy['hostname'] != dummy['name'] || provider_config['getvm_rename_percent'].nil?
|
||||
if 1 + rand(100) <= provider_config['getvm_rename_percent']
|
||||
@write_lock.synchronize do
|
||||
dummy = get_dummy_vm(pool_name, vm_name)
|
||||
dummy['hostname'] = 'DUMMY' + dummy['name']
|
||||
write_backing_file
|
||||
end
|
||||
logger.log('d', "[ ] [#{dummy['poolname']}] '#{dummy['name']}' is being Dummy renamed")
|
||||
end
|
||||
end
|
||||
|
||||
obj = {}
|
||||
obj['name'] = dummy['name']
|
||||
obj['hostname'] = dummy['hostname']
|
||||
obj['boottime'] = dummy['boottime']
|
||||
obj['template'] = dummy['template']
|
||||
obj['poolname'] = dummy['poolname']
|
||||
obj['powerstate'] = dummy['powerstate']
|
||||
obj['snapshots'] = dummy['snapshots']
|
||||
|
||||
obj
|
||||
end
|
||||
|
||||
def create_vm(pool_name, dummy_hostname)
|
||||
pool = pool_config(pool_name)
|
||||
raise("Pool #{pool_name} does not exist for the provider #{name}") if pool.nil?
|
||||
|
||||
template_name = pool['template']
|
||||
|
||||
vm = {}
|
||||
vm['name'] = dummy_hostname
|
||||
vm['hostname'] = dummy_hostname
|
||||
vm['domain'] = 'dummy.local'
|
||||
# 'vm_template' is the name of the template to use to clone the VM from <----- Do we need this?!?!?
|
||||
vm['vm_template'] = template_name
|
||||
# 'template' is the Template name in VM Pooler API, in our case that's the poolname.
|
||||
vm['template'] = pool_name
|
||||
vm['poolname'] = pool_name
|
||||
vm['ready'] = false
|
||||
vm['boottime'] = Time.now
|
||||
vm['powerstate'] = 'PoweredOn'
|
||||
vm['vm_host'] = 'HOST1'
|
||||
vm['dummy_state'] = 'UNKNOWN'
|
||||
vm['snapshots'] = []
|
||||
vm['disks'] = []
|
||||
|
||||
# Make sure the pool exists in the dummy list
|
||||
@write_lock.synchronize do
|
||||
get_dummy_pool_object(pool_name)
|
||||
@dummylist['pool'][pool_name] << vm
|
||||
write_backing_file
|
||||
end
|
||||
|
||||
logger.log('d', "[ ] [#{pool_name}] '#{dummy_hostname}' is being cloned from '#{template_name}'")
|
||||
|
||||
# Inject clone time delay
|
||||
unless provider_config['createvm_max_time'].nil?
|
||||
@write_lock.synchronize do
|
||||
vm['dummy_state'] = 'CLONING'
|
||||
write_backing_file
|
||||
end
|
||||
clone_time = 1 + rand(provider_config['createvm_max_time'])
|
||||
sleep(clone_time)
|
||||
end
|
||||
|
||||
begin
|
||||
# Inject clone failure
|
||||
unless provider_config['createvm_fail_percent'].nil?
|
||||
raise('Dummy Failure for createvm_fail_percent') if 1 + rand(100) <= provider_config['createvm_fail_percent']
|
||||
end
|
||||
|
||||
# Assert the VM is ready for use
|
||||
@write_lock.synchronize do
|
||||
vm['dummy_state'] = 'RUNNING'
|
||||
write_backing_file
|
||||
end
|
||||
rescue => _err
|
||||
@write_lock.synchronize do
|
||||
remove_dummy_vm(pool_name, dummy_hostname)
|
||||
write_backing_file
|
||||
end
|
||||
raise
|
||||
end
|
||||
|
||||
get_vm(pool_name, dummy_hostname)
|
||||
end
|
||||
|
||||
def create_disk(pool_name, vm_name, disk_size)
|
||||
vm_object = get_dummy_vm(pool_name, vm_name)
|
||||
raise("VM #{vm_name} does not exist in Pool #{pool_name} for the provider #{name}") if vm_object.nil?
|
||||
|
||||
# Inject create time delay
|
||||
unless provider_config['createdisk_max_time'].nil?
|
||||
delay = 1 + rand(provider_config['createdisk_max_time'])
|
||||
sleep(delay)
|
||||
end
|
||||
|
||||
# Inject create failure
|
||||
unless provider_config['createdisk_fail_percent'].nil?
|
||||
raise('Dummy Failure for createdisk_fail_percent') if 1 + rand(100) <= provider_config['createdisk_fail_percent']
|
||||
end
|
||||
|
||||
@write_lock.synchronize do
|
||||
vm_object = get_dummy_vm(pool_name, vm_name)
|
||||
vm_object['disks'] << disk_size
|
||||
write_backing_file
|
||||
end
|
||||
|
||||
true
|
||||
end
|
||||
|
||||
def create_snapshot(pool_name, vm_name, snapshot_name)
|
||||
vm_object = get_dummy_vm(pool_name, vm_name)
|
||||
raise("VM #{vm_name} does not exist in Pool #{pool_name} for the provider #{name}") if vm_object.nil?
|
||||
|
||||
# Inject create time delay
|
||||
unless provider_config['createsnapshot_max_time'].nil?
|
||||
delay = 1 + rand(provider_config['createsnapshot_max_time'])
|
||||
sleep(delay)
|
||||
end
|
||||
|
||||
# Inject create failure
|
||||
unless provider_config['createsnapshot_fail_percent'].nil?
|
||||
raise('Dummy Failure for createsnapshot_fail_percent') if 1 + rand(100) <= provider_config['createsnapshot_fail_percent']
|
||||
end
|
||||
|
||||
@write_lock.synchronize do
|
||||
vm_object = get_dummy_vm(pool_name, vm_name)
|
||||
vm_object['snapshots'] << snapshot_name
|
||||
write_backing_file
|
||||
end
|
||||
|
||||
true
|
||||
end
|
||||
|
||||
def revert_snapshot(pool_name, vm_name, snapshot_name)
|
||||
vm_object = get_dummy_vm(pool_name, vm_name)
|
||||
raise("VM #{vm_name} does not exist in Pool #{pool_name} for the provider #{name}") if vm_object.nil?
|
||||
|
||||
# Inject create time delay
|
||||
unless provider_config['revertsnapshot_max_time'].nil?
|
||||
delay = 1 + rand(provider_config['revertsnapshot_max_time'])
|
||||
sleep(delay)
|
||||
end
|
||||
|
||||
# Inject create failure
|
||||
unless provider_config['revertsnapshot_fail_percent'].nil?
|
||||
raise('Dummy Failure for revertsnapshot_fail_percent') if 1 + rand(100) <= provider_config['revertsnapshot_fail_percent']
|
||||
end
|
||||
|
||||
vm_object['snapshots'].include?(snapshot_name)
|
||||
end
|
||||
|
||||
def destroy_vm(pool_name, vm_name)
|
||||
vm = get_dummy_vm(pool_name, vm_name)
|
||||
return false if vm.nil?
|
||||
return false if vm['poolname'] != pool_name
|
||||
|
||||
# Shutdown down the VM if it's poweredOn
|
||||
if vm['powerstate'] == 'PoweredOn'
|
||||
logger.log('d', "[ ] [#{pool_name}] '#{vm_name}' is being shut down")
|
||||
|
||||
# Inject shutdown delay time
|
||||
unless provider_config['destroyvm_max_shutdown_time'].nil?
|
||||
shutdown_time = 1 + rand(provider_config['destroyvm_max_shutdown_time'])
|
||||
sleep(shutdown_time)
|
||||
end
|
||||
|
||||
@write_lock.synchronize do
|
||||
vm = get_dummy_vm(pool_name, vm_name)
|
||||
vm['powerstate'] = 'PoweredOff'
|
||||
write_backing_file
|
||||
end
|
||||
end
|
||||
|
||||
# Inject destroy VM delay
|
||||
unless provider_config['destroyvm_max_time'].nil?
|
||||
destroy_time = 1 + rand(provider_config['destroyvm_max_time'])
|
||||
sleep(destroy_time)
|
||||
end
|
||||
|
||||
# Inject destroy VM failure
|
||||
unless provider_config['destroyvm_fail_percent'].nil?
|
||||
raise('Dummy Failure for migratevm_fail_percent') if 1 + rand(100) <= provider_config['destroyvm_fail_percent']
|
||||
end
|
||||
|
||||
# 'Destroy' the VM
|
||||
@write_lock.synchronize do
|
||||
remove_dummy_vm(pool_name, vm_name)
|
||||
write_backing_file
|
||||
end
|
||||
|
||||
true
|
||||
end
|
||||
|
||||
def vm_ready?(pool_name, vm_name)
|
||||
vm_object = get_dummy_vm(pool_name, vm_name)
|
||||
return false if vm_object.nil?
|
||||
return false if vm_object['poolname'] != pool_name
|
||||
return true if vm_object['ready']
|
||||
|
||||
timeout = provider_config['is_ready_timeout'] || 5
|
||||
|
||||
Timeout.timeout(timeout) do
|
||||
while vm_object['dummy_state'] != 'RUNNING'
|
||||
sleep(2)
|
||||
vm_object = get_dummy_vm(pool_name, vm_name)
|
||||
end
|
||||
end
|
||||
|
||||
# Simulate how long it takes from a VM being powered on until
|
||||
# it's ready to receive a connection
|
||||
sleep(2)
|
||||
|
||||
unless provider_config['vmready_fail_percent'].nil?
|
||||
raise('Dummy Failure for vmready_fail_percent') if 1 + rand(100) <= provider_config['vmready_fail_percent']
|
||||
end
|
||||
|
||||
@write_lock.synchronize do
|
||||
vm_object['ready'] = true
|
||||
write_backing_file
|
||||
end
|
||||
|
||||
true
|
||||
end
|
||||
|
||||
private
|
||||
|
||||
# Note - NEVER EVER use the @write_lock object in the private methods!!!! Deadlocks will ensue
|
||||
|
||||
def remove_dummy_vm(pool_name, vm_name)
|
||||
return if @dummylist['pool'][pool_name].nil?
|
||||
new_poollist = @dummylist['pool'][pool_name].delete_if { |vm| vm['name'] == vm_name }
|
||||
@dummylist['pool'][pool_name] = new_poollist
|
||||
end
|
||||
|
||||
# Get's the pool config safely from the in-memory hashtable
|
||||
def get_dummy_pool_object(pool_name)
|
||||
@dummylist['pool'] = {} if @dummylist['pool'].nil?
|
||||
@dummylist['pool'][pool_name] = [] if @dummylist['pool'][pool_name].nil?
|
||||
|
||||
@dummylist['pool'][pool_name]
|
||||
end
|
||||
|
||||
def get_dummy_vm(pool_name, vm_name)
|
||||
return nil if @dummylist['pool'][pool_name].nil?
|
||||
|
||||
@dummylist['pool'][pool_name].each do |poolvm|
|
||||
return poolvm if poolvm['name'] == vm_name
|
||||
end
|
||||
|
||||
nil
|
||||
end
|
||||
|
||||
def write_backing_file
|
||||
dummyfilename = provider_config['filename']
|
||||
return if dummyfilename.nil?
|
||||
File.open(dummyfilename, 'w') { |file| file.write(YAML.dump(@dummylist)) }
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
509
spec/unit/providers/dummy_spec.rb
Normal file
509
spec/unit/providers/dummy_spec.rb
Normal file
|
|
@ -0,0 +1,509 @@
|
|||
require 'spec_helper'
|
||||
|
||||
describe 'Vmpooler::PoolManager::Provider::Dummy' do
|
||||
let(:logger) { MockLogger.new }
|
||||
let(:metrics) { Vmpooler::DummyStatsd.new }
|
||||
let(:pool_name) { 'pool1' }
|
||||
let(:other_pool_name) { 'pool2' }
|
||||
let(:vm_name) { 'vm1' }
|
||||
|
||||
let(:running_vm_name) { 'vm2' }
|
||||
let(:notready_vm_name) { 'vm3' }
|
||||
|
||||
let (:provider_options) {
|
||||
# Construct an initial state for testing
|
||||
dummylist = {}
|
||||
dummylist['pool'] = {}
|
||||
# pool1 is a pool of "normal" VMs
|
||||
dummylist['pool'][pool_name] = []
|
||||
# A normal running VM
|
||||
vm = {}
|
||||
vm['name'] = vm_name
|
||||
vm['hostname'] = vm_name
|
||||
vm['domain'] = 'dummy.local'
|
||||
vm['vm_template'] = 'template1'
|
||||
vm['template'] = pool_name
|
||||
vm['poolname'] = pool_name
|
||||
vm['ready'] = true
|
||||
vm['boottime'] = Time.now
|
||||
vm['powerstate'] = 'PoweredOn'
|
||||
vm['vm_host'] = 'HOST1'
|
||||
vm['snapshots'] = []
|
||||
vm['disks'] = []
|
||||
vm['dummy_state'] = 'RUNNING'
|
||||
dummylist['pool'][pool_name] << vm
|
||||
|
||||
# pool2 is a pool of "abnormal" VMs e.g. PoweredOff etc.
|
||||
dummylist['pool'][other_pool_name] = []
|
||||
# A freshly provisioned VM that is not ready
|
||||
vm = {}
|
||||
vm['name'] = running_vm_name
|
||||
vm['hostname'] = running_vm_name
|
||||
vm['domain'] = 'dummy.local'
|
||||
vm['vm_template'] = 'template1'
|
||||
vm['template'] = other_pool_name
|
||||
vm['poolname'] = other_pool_name
|
||||
vm['ready'] = false
|
||||
vm['boottime'] = Time.now
|
||||
vm['powerstate'] = 'PoweredOn'
|
||||
vm['vm_host'] = 'HOST1'
|
||||
vm['snapshots'] = []
|
||||
vm['disks'] = []
|
||||
vm['dummy_state'] = 'UNKNOWN'
|
||||
dummylist['pool'][other_pool_name] << vm
|
||||
# A freshly provisioned VM that is running but not ready
|
||||
vm = {}
|
||||
vm['name'] = notready_vm_name
|
||||
vm['hostname'] = notready_vm_name
|
||||
vm['domain'] = 'dummy.local'
|
||||
vm['vm_template'] = 'template1'
|
||||
vm['template'] = other_pool_name
|
||||
vm['poolname'] = other_pool_name
|
||||
vm['ready'] = false
|
||||
vm['boottime'] = Time.now
|
||||
vm['powerstate'] = 'PoweredOn'
|
||||
vm['vm_host'] = 'HOST1'
|
||||
vm['snapshots'] = []
|
||||
vm['disks'] = []
|
||||
vm['dummy_state'] = 'RUNNING'
|
||||
dummylist['pool'][other_pool_name] << vm
|
||||
|
||||
{
|
||||
'initial_state' => dummylist
|
||||
}
|
||||
}
|
||||
|
||||
let(:config) { YAML.load(<<-EOT
|
||||
---
|
||||
:config:
|
||||
max_tries: 3
|
||||
retry_factor: 10
|
||||
:providers:
|
||||
:dummy:
|
||||
key1: 'value1'
|
||||
:pools:
|
||||
- name: '#{pool_name}'
|
||||
size: 5
|
||||
- name: 'pool2'
|
||||
size: 5
|
||||
EOT
|
||||
)
|
||||
}
|
||||
|
||||
subject { Vmpooler::PoolManager::Provider::Dummy.new(config, logger, metrics, 'dummy', provider_options) }
|
||||
|
||||
describe '#name' do
|
||||
it 'should be dummy' do
|
||||
expect(subject.name).to eq('dummy')
|
||||
end
|
||||
end
|
||||
|
||||
describe '#vms_in_pool' do
|
||||
it 'should return [] when pool does not exist' do
|
||||
vm_list = subject.vms_in_pool('missing_pool')
|
||||
|
||||
expect(vm_list).to eq([])
|
||||
end
|
||||
|
||||
it 'should return an array of VMs when pool exists' do
|
||||
vm_list = subject.vms_in_pool(pool_name)
|
||||
|
||||
expect(vm_list.count).to eq(1)
|
||||
end
|
||||
end
|
||||
|
||||
describe '#get_vm_host' do
|
||||
it 'should return the hostname when VM exists' do
|
||||
expect(subject.get_vm_host(pool_name, vm_name)).to eq('HOST1')
|
||||
end
|
||||
|
||||
it 'should error when VM does not exist' do
|
||||
expect{subject.get_vm_host(pool_name, 'doesnotexist')}.to raise_error(RuntimeError)
|
||||
end
|
||||
end
|
||||
|
||||
describe '#find_least_used_compatible_host' do
|
||||
it 'should return the current host' do
|
||||
new_host = subject.find_least_used_compatible_host(pool_name, vm_name)
|
||||
expect(new_host).to eq('HOST1')
|
||||
end
|
||||
|
||||
context 'using migratevm_couldmove_percent' do
|
||||
describe 'of zero' do
|
||||
before(:each) do
|
||||
config[:providers][:dummy]['migratevm_couldmove_percent'] = 0
|
||||
end
|
||||
|
||||
it 'should return the current host' do
|
||||
new_host = subject.find_least_used_compatible_host(pool_name, vm_name)
|
||||
expect(new_host).to eq('HOST1')
|
||||
end
|
||||
end
|
||||
|
||||
describe 'of 100' do
|
||||
before(:each) do
|
||||
config[:providers][:dummy]['migratevm_couldmove_percent'] = 100
|
||||
end
|
||||
|
||||
it 'should return a different host' do
|
||||
new_host = subject.find_least_used_compatible_host(pool_name, vm_name)
|
||||
expect(new_host).to_not eq('HOST1')
|
||||
end
|
||||
end
|
||||
|
||||
end
|
||||
end
|
||||
|
||||
describe '#migrate_vm_to_host' do
|
||||
it 'should move to the new host' do
|
||||
expect(subject.migrate_vm_to_host(pool_name, 'vm1','NEWHOST')).to eq(true)
|
||||
expect(subject.get_vm_host(pool_name, 'vm1')).to eq('NEWHOST')
|
||||
end
|
||||
|
||||
context 'using migratevm_fail_percent' do
|
||||
describe 'of zero' do
|
||||
before(:each) do
|
||||
config[:providers][:dummy]['migratevm_fail_percent'] = 0
|
||||
end
|
||||
|
||||
it 'should move to the new host' do
|
||||
expect(subject.migrate_vm_to_host(pool_name, 'vm1','NEWHOST')).to eq(true)
|
||||
expect(subject.get_vm_host(pool_name, 'vm1')).to eq('NEWHOST')
|
||||
end
|
||||
end
|
||||
|
||||
describe 'of 100' do
|
||||
before(:each) do
|
||||
config[:providers][:dummy]['migratevm_fail_percent'] = 100
|
||||
end
|
||||
|
||||
it 'should raise an error' do
|
||||
expect{subject.migrate_vm_to_host(pool_name, 'vm1','NEWHOST')}.to raise_error(/migratevm_fail_percent/)
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
describe '#get_vm' do
|
||||
it 'should return the VM when VM exists' do
|
||||
vm = subject.get_vm(pool_name, vm_name)
|
||||
expect(vm['name']).to eq(vm_name)
|
||||
expect(vm['powerstate']).to eq('PoweredOn')
|
||||
expect(vm['hostname']).to eq(vm['name'])
|
||||
end
|
||||
|
||||
it 'should return nil when VM does not exist' do
|
||||
expect(subject.get_vm(pool_name, 'doesnotexist')).to eq(nil)
|
||||
end
|
||||
|
||||
context 'using getvm_poweroff_percent' do
|
||||
describe 'of zero' do
|
||||
before(:each) do
|
||||
config[:providers][:dummy]['getvm_poweroff_percent'] = 0
|
||||
end
|
||||
|
||||
it 'will not power off a VM' do
|
||||
vm = subject.get_vm(pool_name, vm_name)
|
||||
expect(vm['name']).to eq(vm_name)
|
||||
expect(vm['powerstate']).to eq('PoweredOn')
|
||||
end
|
||||
end
|
||||
|
||||
describe 'of 100' do
|
||||
before(:each) do
|
||||
config[:providers][:dummy]['getvm_poweroff_percent'] = 100
|
||||
end
|
||||
|
||||
it 'will power off a VM' do
|
||||
vm = subject.get_vm(pool_name, vm_name)
|
||||
expect(vm['name']).to eq(vm_name)
|
||||
expect(vm['powerstate']).to eq('PoweredOff')
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
context 'using getvm_rename_percent' do
|
||||
describe 'of zero' do
|
||||
before(:each) do
|
||||
config[:providers][:dummy]['getvm_rename_percent'] = 0
|
||||
end
|
||||
|
||||
it 'will not rename a VM' do
|
||||
vm = subject.get_vm(pool_name, vm_name)
|
||||
expect(vm['name']).to eq(vm_name)
|
||||
expect(vm['hostname']).to eq(vm['name'])
|
||||
end
|
||||
end
|
||||
|
||||
describe 'of 100' do
|
||||
before(:each) do
|
||||
config[:providers][:dummy]['getvm_rename_percent'] = 100
|
||||
end
|
||||
|
||||
it 'will rename a VM' do
|
||||
vm = subject.get_vm(pool_name, vm_name)
|
||||
expect(vm['name']).to eq(vm_name)
|
||||
expect(vm['hostname']).to_not eq(vm['name'])
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
describe '#create_vm' do
|
||||
let(:new_vm_name) { 'newvm' }
|
||||
|
||||
it 'should return a new VM' do
|
||||
expect(subject.create_vm(pool_name, new_vm_name)['name']).to eq(new_vm_name)
|
||||
end
|
||||
|
||||
it 'should increase the number of VMs in the pool' do
|
||||
old_pool_count = subject.vms_in_pool(pool_name).count
|
||||
|
||||
new_vm = subject.create_vm(pool_name, new_vm_name)
|
||||
|
||||
expect(subject.vms_in_pool(pool_name).count).to eq(old_pool_count + 1)
|
||||
end
|
||||
|
||||
context 'using createvm_fail_percent' do
|
||||
describe 'of zero' do
|
||||
before(:each) do
|
||||
config[:providers][:dummy]['createvm_fail_percent'] = 0
|
||||
end
|
||||
|
||||
it 'should return a new VM' do
|
||||
expect(subject.create_vm(pool_name, new_vm_name)['name']).to eq(new_vm_name)
|
||||
end
|
||||
end
|
||||
|
||||
describe 'of 100' do
|
||||
before(:each) do
|
||||
config[:providers][:dummy]['createvm_fail_percent'] = 100
|
||||
end
|
||||
|
||||
it 'should raise an error' do
|
||||
expect{subject.create_vm(pool_name, new_vm_name)}.to raise_error(/createvm_fail_percent/)
|
||||
end
|
||||
|
||||
it 'new VM should not exist' do
|
||||
begin
|
||||
subject.create_vm(pool_name, new_vm_name)
|
||||
rescue
|
||||
end
|
||||
expect(subject.get_vm(pool_name, new_vm_name)).to eq(nil)
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
describe '#create_disk' do
|
||||
let(:disk_size) { 10 }
|
||||
|
||||
it 'should return true when the disk is created' do
|
||||
expect(subject.create_disk(pool_name, vm_name,disk_size)).to be true
|
||||
end
|
||||
|
||||
it 'should raise an error when VM does not exist' do
|
||||
expect{ subject.create_disk(pool_name, 'doesnotexist',disk_size) }.to raise_error(/VM doesnotexist does not exist/)
|
||||
end
|
||||
|
||||
context 'using createdisk_fail_percent' do
|
||||
describe 'of zero' do
|
||||
before(:each) do
|
||||
config[:providers][:dummy]['createdisk_fail_percent'] = 0
|
||||
end
|
||||
|
||||
it 'should return true when the disk is created' do
|
||||
expect(subject.create_disk(pool_name, vm_name,disk_size)).to be true
|
||||
end
|
||||
end
|
||||
|
||||
describe 'of 100' do
|
||||
before(:each) do
|
||||
config[:providers][:dummy]['createdisk_fail_percent'] = 100
|
||||
end
|
||||
|
||||
it 'should raise an error' do
|
||||
expect{subject.create_disk(pool_name, vm_name,disk_size)}.to raise_error(/createdisk_fail_percent/)
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
describe '#create_snapshot' do
|
||||
let(:snapshot_name) { 'newsnapshot' }
|
||||
|
||||
it 'should return true when the snapshot is created' do
|
||||
expect(subject.create_snapshot(pool_name, vm_name, snapshot_name)).to be true
|
||||
end
|
||||
|
||||
it 'should raise an error when VM does not exist' do
|
||||
expect{ subject.create_snapshot(pool_name, 'doesnotexist', snapshot_name) }.to raise_error(/VM doesnotexist does not exist/)
|
||||
end
|
||||
|
||||
context 'using createsnapshot_fail_percent' do
|
||||
describe 'of zero' do
|
||||
before(:each) do
|
||||
config[:providers][:dummy]['createsnapshot_fail_percent'] = 0
|
||||
end
|
||||
|
||||
it 'should return true when the disk is created' do
|
||||
expect(subject.create_snapshot(pool_name, vm_name, snapshot_name)).to be true
|
||||
end
|
||||
end
|
||||
|
||||
describe 'of 100' do
|
||||
before(:each) do
|
||||
config[:providers][:dummy]['createsnapshot_fail_percent'] = 100
|
||||
end
|
||||
|
||||
it 'should raise an error' do
|
||||
expect{ subject.create_snapshot(pool_name, vm_name, snapshot_name) }.to raise_error(/createsnapshot_fail_percent/)
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
describe '#revert_snapshot' do
|
||||
let(:snapshot_name) { 'newsnapshot' }
|
||||
|
||||
before(:each) do
|
||||
# Create a snapshot
|
||||
subject.create_snapshot(pool_name, vm_name, snapshot_name)
|
||||
end
|
||||
|
||||
it 'should return true when the snapshot is reverted' do
|
||||
expect(subject.revert_snapshot(pool_name, vm_name, snapshot_name)).to be true
|
||||
end
|
||||
|
||||
it 'should raise an error when VM does not exist' do
|
||||
expect{ subject.revert_snapshot(pool_name, 'doesnotexist', snapshot_name) }.to raise_error(/VM doesnotexist does not exist/)
|
||||
end
|
||||
|
||||
it 'should return false when the snapshot does not exist' do
|
||||
expect(subject.revert_snapshot(pool_name, vm_name, 'doesnotexist')).to be false
|
||||
end
|
||||
|
||||
context 'using revertsnapshot_fail_percent' do
|
||||
describe 'of zero' do
|
||||
before(:each) do
|
||||
config[:providers][:dummy]['revertsnapshot_fail_percent'] = 0
|
||||
end
|
||||
|
||||
it 'should return true when the snapshot is reverted' do
|
||||
expect(subject.revert_snapshot(pool_name, vm_name, snapshot_name)).to be true
|
||||
end
|
||||
end
|
||||
|
||||
describe 'of 100' do
|
||||
before(:each) do
|
||||
config[:providers][:dummy]['revertsnapshot_fail_percent'] = 100
|
||||
end
|
||||
|
||||
it 'should raise an error when VM does not exist' do
|
||||
expect{ subject.revert_snapshot(pool_name, vm_name, snapshot_name) }.to raise_error(/revertsnapshot_fail_percent/)
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
describe '#destroy_vm' do
|
||||
it 'should return true when destroyed' do
|
||||
expect(subject.destroy_vm(pool_name, vm_name)).to eq(true)
|
||||
end
|
||||
|
||||
it 'should log if the VM is powered off' do
|
||||
allow(logger).to receive(:log)
|
||||
expect(logger).to receive(:log).with('d', "[ ] [pool1] 'vm1' is being shut down")
|
||||
expect(subject.destroy_vm(pool_name, vm_name)).to eq(true)
|
||||
end
|
||||
|
||||
it 'should return false if VM does not exist' do
|
||||
expect(subject.destroy_vm('doesnotexist',vm_name)).to eq(false)
|
||||
end
|
||||
|
||||
it 'should return false if VM is not in the correct pool' do
|
||||
expect(subject.destroy_vm(other_pool_name, vm_name)).to eq(false)
|
||||
end
|
||||
|
||||
context 'using destroyvm_fail_percent' do
|
||||
describe 'of zero' do
|
||||
before(:each) do
|
||||
config[:providers][:dummy]['destroyvm_fail_percent'] = 0
|
||||
end
|
||||
|
||||
it 'should return true when destroyed' do
|
||||
expect(subject.destroy_vm(pool_name, vm_name)).to eq(true)
|
||||
end
|
||||
end
|
||||
|
||||
describe 'of 100' do
|
||||
before(:each) do
|
||||
config[:providers][:dummy]['destroyvm_fail_percent'] = 100
|
||||
end
|
||||
|
||||
it 'should raise an error' do
|
||||
expect{subject.destroy_vm(pool_name, vm_name)}.to raise_error(/migratevm_fail_percent/)
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
describe '#vm_ready?' do
|
||||
before(:each) do
|
||||
# Speed up tests and ignore sleeping
|
||||
allow(subject).to receive(:sleep)
|
||||
end
|
||||
|
||||
it 'should return true if ready' do
|
||||
expect(subject.vm_ready?(pool_name, vm_name)).to eq(true)
|
||||
end
|
||||
|
||||
it 'should return false if VM does not exist' do
|
||||
expect(subject.vm_ready?(pool_name, 'doesnotexist')).to eq(false)
|
||||
end
|
||||
|
||||
it 'should return false if VM is not in the correct pool' do
|
||||
expect(subject.vm_ready?(other_pool_name, vm_name)).to eq(false)
|
||||
end
|
||||
|
||||
it 'should raise an error if timeout expires' do
|
||||
expect{subject.vm_ready?(other_pool_name, running_vm_name)}.to raise_error(Timeout::Error)
|
||||
end
|
||||
|
||||
it 'should return true if VM becomes ready' do
|
||||
expect(subject.vm_ready?(other_pool_name, notready_vm_name)).to eq(true)
|
||||
end
|
||||
|
||||
context 'using vmready_fail_percent' do
|
||||
describe 'of zero' do
|
||||
before(:each) do
|
||||
config[:providers][:dummy]['vmready_fail_percent'] = 0
|
||||
end
|
||||
|
||||
it 'should return true if VM becomes ready' do
|
||||
expect(subject.vm_ready?(other_pool_name, notready_vm_name)).to eq(true)
|
||||
end
|
||||
end
|
||||
|
||||
describe 'of 100' do
|
||||
before(:each) do
|
||||
config[:providers][:dummy]['vmready_fail_percent'] = 100
|
||||
end
|
||||
|
||||
it 'should raise an error' do
|
||||
expect{subject.vm_ready?(other_pool_name, notready_vm_name)}.to raise_error(/vmready_fail_percent/)
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
describe '#vm_exists?' do
|
||||
it 'should return true when VM exists' do
|
||||
expect(subject.vm_exists?(pool_name, vm_name)).to eq(true)
|
||||
end
|
||||
|
||||
it 'should return true when VM does not exist' do
|
||||
expect(subject.vm_exists?(pool_name, 'doesnotexist')).to eq(false)
|
||||
end
|
||||
end
|
||||
end
|
||||
|
|
@ -25,6 +25,98 @@
|
|||
username: 'vmpooler'
|
||||
password: 'swimsw1msw!m'
|
||||
|
||||
:providers:
|
||||
# :providers:
|
||||
#
|
||||
# This section contains the VM providers for VMs and Pools
|
||||
# The currently supported backing services are:
|
||||
# - dummy
|
||||
|
||||
# :dummy:
|
||||
#
|
||||
# The dummy backing service is a simple text file service that can be used
|
||||
# to test vmpooler operations in a development or test environment
|
||||
#
|
||||
# Available configuration parameters:
|
||||
#
|
||||
# - filename (Optional)
|
||||
# The filename used to store the backing text file. If this is not specified the VM state is only
|
||||
# kept in memory, and is lost when the Provider is shutdown
|
||||
#
|
||||
# - migratevm_couldmove_percent
|
||||
# Percent chance that a VM could be moved to another host
|
||||
# (optional; default 0%)
|
||||
#
|
||||
# - migratevm_max_time
|
||||
# Maximum amount of random time a VM migration action will take in seconds
|
||||
# (optional; default 0 seconds)
|
||||
#
|
||||
# - migratevm_fail_percent
|
||||
# Percent chance that a VM migration action will fail
|
||||
# (optional; default 0%)
|
||||
#
|
||||
# - getvm_poweroff_percent
|
||||
# Percent chance that when the VM information is gathered that the VM will be powered off
|
||||
# (optional; default 0%)
|
||||
#
|
||||
# - getvm_rename_percent
|
||||
# Percent chance that when the VM information is gathered that the VM will be renamed
|
||||
# (optional; default 0%)
|
||||
#
|
||||
# - createvm_max_time
|
||||
# Maximum amount of random time a VM creation action will take, in seconds
|
||||
# (optional; default 0 seconds)
|
||||
#
|
||||
# - createvm_fail_percent
|
||||
# Percent chance that a VM creation action will fail
|
||||
# (optional; default 0%)
|
||||
#
|
||||
# - createdisk_max_time
|
||||
# Maximum amount of random time a VM create disk action will take, in seconds
|
||||
# (optional; default 0 seconds)
|
||||
#
|
||||
# - createdisk_fail_percent
|
||||
# Percent chance that a VM create disk action will fail
|
||||
# (optional; default 0%)
|
||||
#
|
||||
# - createsnapshot_max_time
|
||||
# Maximum amount of random time a VM create snapshot action will take, in seconds
|
||||
# (optional; default 0 seconds)
|
||||
#
|
||||
# - createsnapshot_fail_percent
|
||||
# Percent chance that a VM create snapshot action will fail
|
||||
# (optional; default 0%)
|
||||
#
|
||||
# - revertsnapshot_max_time
|
||||
# Maximum amount of random time a VM revert snapshot action will take, in seconds
|
||||
# (optional; default 0 seconds)
|
||||
#
|
||||
# - revertsnapshot_fail_percent
|
||||
# Percent chance that a VM revert snapshot action will fail
|
||||
# (optional; default 0%)
|
||||
#
|
||||
# - destroyvm_max_shutdown_time
|
||||
# Maximum amount of random time a VM shutdown action will take during destroy, in seconds
|
||||
# (optional; default 0 seconds)
|
||||
#
|
||||
# - destroyvm_max_time
|
||||
# Maximum amount of random time a VM destroy action will take, in seconds
|
||||
# (optional; default 0 seconds)
|
||||
#
|
||||
# - destroyvm_fail_percent
|
||||
# Percent chance that a VM destroy action will fail
|
||||
# (optional; default 0%)
|
||||
#
|
||||
# - vmready_fail_percent
|
||||
# Percent chance that an error is raised when vm_ready? is called
|
||||
# (optional; default 0%)
|
||||
|
||||
# Example:
|
||||
|
||||
:dummy:
|
||||
filename: '/tmp/dummy-backing.yaml'
|
||||
|
||||
|
||||
# :redis:
|
||||
#
|
||||
# This section contains the server hostname and authentication credentials
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue