mirror of
https://github.com/puppetlabs/vmpooler.git
synced 2026-01-27 02:18:41 -05:00
f dummy.rb (backingservice)
This commit is contained in:
parent
91a2207b3f
commit
c806cfaf76
1 changed files with 130 additions and 92 deletions
|
|
@ -6,11 +6,16 @@ module Vmpooler
|
||||||
class Dummy < Vmpooler::PoolManager::BackingService::Base
|
class Dummy < Vmpooler::PoolManager::BackingService::Base
|
||||||
|
|
||||||
# Fake VM backing service for testing, with initial configuration set in a simple text YAML filename
|
# Fake VM backing service for testing, with initial configuration set in a simple text YAML filename
|
||||||
|
# or via YAML in the config file
|
||||||
def initialize(options)
|
def initialize(options)
|
||||||
|
super(options)
|
||||||
|
|
||||||
dummyfilename = options['filename']
|
dummyfilename = options['filename']
|
||||||
|
|
||||||
# TODO Accessing @dummylist is not thread safe :-( Mutexes?
|
# TODO Accessing @dummylist is not thread safe :-( Mutexes?
|
||||||
@dummylist = {}
|
|
||||||
|
# This initial_state option is only intended to be used by spec tests
|
||||||
|
@dummylist = options['initial_state'].nil? ? {} : options['initial_state']
|
||||||
|
|
||||||
if !dummyfilename.nil? && File.exists?(dummyfilename)
|
if !dummyfilename.nil? && File.exists?(dummyfilename)
|
||||||
@dummylist ||= YAML.load_file(dummyfilename)
|
@dummylist ||= YAML.load_file(dummyfilename)
|
||||||
|
|
@ -23,14 +28,33 @@ module Vmpooler
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
|
def name
|
||||||
|
'dummy'
|
||||||
|
end
|
||||||
|
|
||||||
def get_vm(vm)
|
def get_vm(vm)
|
||||||
dummy = get_dummy_vm(vm)
|
dummy = get_dummy_vm(vm)
|
||||||
return nil if dummy.nil?
|
return nil if dummy.nil?
|
||||||
|
|
||||||
|
# Randomly power off the VM
|
||||||
|
unless dummy['powerstate'] != 'PoweredOn' || @options['getvm_poweroff_percent'].nil?
|
||||||
|
if 1 + rand(100) <= @options['getvm_poweroff_percent']
|
||||||
|
dummy['powerstate'] = 'PoweredOff'
|
||||||
|
$logger.log('d', "[ ] [#{dummy['poolname']}] '#{dummy['name']}' is being Dummy Powered Off")
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
# Randomly rename the host
|
||||||
|
unless dummy['hostname'] != dummy['name'] || @options['getvm_rename_percent'].nil?
|
||||||
|
if 1 + rand(100) <= @options['getvm_rename_percent']
|
||||||
|
dummy['hostname'] = 'DUMMY' + dummy['name']
|
||||||
|
$logger.log('d', "[ ] [#{dummy['poolname']}] '#{dummy['name']}' is being Dummy renamed")
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
obj = {}
|
obj = {}
|
||||||
# TODO Randomly power off the vm?
|
obj['name'] = dummy['name']
|
||||||
# TODO Randomly change the hostname of the vm?
|
obj['hostname'] = dummy['hostname']
|
||||||
obj['hostname'] = dummy['name']
|
|
||||||
obj['boottime'] = dummy['boottime']
|
obj['boottime'] = dummy['boottime']
|
||||||
obj['template'] = dummy['template']
|
obj['template'] = dummy['template']
|
||||||
obj['poolname'] = dummy['poolname']
|
obj['poolname'] = dummy['poolname']
|
||||||
|
|
@ -39,33 +63,41 @@ module Vmpooler
|
||||||
obj
|
obj
|
||||||
end
|
end
|
||||||
|
|
||||||
def vm_exists?(vm)
|
|
||||||
!get_vm(vm).nil?
|
|
||||||
end
|
|
||||||
|
|
||||||
def find_least_used_compatible_host(vm_name)
|
def find_least_used_compatible_host(vm_name)
|
||||||
current_vm = get_dummy_vm(vm_name)
|
current_vm = get_dummy_vm(vm_name)
|
||||||
|
|
||||||
# TODO parameterise this (75% chance it will not migrate)
|
# Unless migratevm_couldmove_percent is specified, don't migrate
|
||||||
return current_vm['vm_host'] if 1 + rand(100) < 75
|
return current_vm['vm_host'] if @options['migratevm_couldmove_percent'].nil?
|
||||||
|
|
||||||
# TODO paramtise this (Simulates a 10 node cluster)
|
# Only migrate if migratevm_couldmove_percent is met
|
||||||
(1 + rand(10)).to_s
|
return current_vm['vm_host'] if 1 + rand(100) > @options['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
|
end
|
||||||
|
|
||||||
def get_vm_host(vm_name)
|
def get_vm_host(vm_name)
|
||||||
current_vm = get_dummy_vm(vm_name)
|
current_vm = get_dummy_vm(vm_name)
|
||||||
|
|
||||||
current_vm['vm_host']
|
current_vm.nil? ? fail("VM #{vm_name} does not exist") : current_vm['vm_host']
|
||||||
end
|
end
|
||||||
|
|
||||||
def migrate_vm_to_host(vm_name, dest_host_name)
|
def migrate_vm_to_host(vm_name, dest_host_name)
|
||||||
current_vm = get_dummy_vm(vm_name)
|
current_vm = get_dummy_vm(vm_name)
|
||||||
|
|
||||||
# TODO do I need fake a random sleep for ready?
|
# Inject migration delay
|
||||||
# TODO Should I inject a random error?
|
unless @options['migratevm_max_time'].nil?
|
||||||
|
migrate_time = 1 + rand(@options['migratevm_max_time'])
|
||||||
|
sleep(migrate_time)
|
||||||
|
end
|
||||||
|
|
||||||
|
# Inject clone failure
|
||||||
|
unless @options['migratevm_fail_percent'].nil?
|
||||||
|
fail "Dummy Failure for migratevm_fail_percent" if 1 + rand(100) <= @options['migratevm_fail_percent']
|
||||||
|
end
|
||||||
|
|
||||||
sleep(1)
|
|
||||||
current_vm['vm_host'] = dest_host_name
|
current_vm['vm_host'] = dest_host_name
|
||||||
|
|
||||||
true
|
true
|
||||||
|
|
@ -75,77 +107,70 @@ module Vmpooler
|
||||||
host = get_dummy_vm(vm)
|
host = get_dummy_vm(vm)
|
||||||
if !host then return false end
|
if !host then return false end
|
||||||
if host['poolname'] != pool then return false end
|
if host['poolname'] != pool then return false end
|
||||||
if vm['ready'] then return true end
|
if host['ready'] then return true end
|
||||||
# TODO do I need fake a random sleep for ready?
|
|
||||||
# TODO Should I inject a random error?
|
|
||||||
sleep(2)
|
|
||||||
host['ready'] = true
|
|
||||||
|
|
||||||
|
Timeout.timeout(timeout) do
|
||||||
|
while host['dummy_state'] != 'RUNNING'
|
||||||
|
sleep(2)
|
||||||
|
host = get_dummy_vm(vm)
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
# Simulate how long it takes from a VM being powered on until
|
||||||
|
# it's ready to receive a connection
|
||||||
|
sleep(2)
|
||||||
|
|
||||||
|
unless @options['vmready_fail_percent'].nil?
|
||||||
|
fail "Dummy Failure for vmready_fail_percent" if 1 + rand(100) <= @options['vmready_fail_percent']
|
||||||
|
end
|
||||||
|
|
||||||
|
host['ready'] = true
|
||||||
true
|
true
|
||||||
end
|
end
|
||||||
|
|
||||||
def create_vm(pool)
|
def create_vm(pool,dummy_hostname)
|
||||||
# This is an async operation
|
|
||||||
# This code just clones a VM and starts it
|
|
||||||
# Later checking will move it from the pending to ready queue
|
|
||||||
Thread.new do
|
|
||||||
begin
|
|
||||||
template_name = pool['template']
|
template_name = pool['template']
|
||||||
pool_name = pool['name']
|
pool_name = pool['name']
|
||||||
|
|
||||||
# Generate a randomized hostname
|
|
||||||
o = [('a'..'z'), ('0'..'9')].map(&:to_a).flatten
|
|
||||||
dummy_hostname = $config[:config]['prefix'] + o[rand(25)] + (0...14).map { o[rand(o.length)] }.join
|
|
||||||
|
|
||||||
vm = {}
|
vm = {}
|
||||||
vm['name'] = dummy_hostname
|
vm['name'] = dummy_hostname
|
||||||
vm['hostname'] = dummy_hostname
|
vm['hostname'] = dummy_hostname
|
||||||
vm['domain'] = 'dummy.local'
|
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
|
vm['vm_template'] = template_name
|
||||||
# 'template' is the Template in API, not the template to create the VM ('vm_template')
|
# 'template' is the Template name in VM Pooler API, in our case that's the poolname.
|
||||||
vm['template'] = pool_name
|
vm['template'] = pool_name
|
||||||
vm['poolname'] = pool_name
|
vm['poolname'] = pool_name
|
||||||
vm['ready'] = false
|
vm['ready'] = false
|
||||||
vm['boottime'] = Time.now
|
vm['boottime'] = Time.now
|
||||||
vm['powerstate'] = 'PoweredOn'
|
vm['powerstate'] = 'PoweredOn'
|
||||||
vm['vm_host'] = '1'
|
vm['vm_host'] = 'HOST1'
|
||||||
|
vm['dummy_state'] = 'UNKNOWN'
|
||||||
get_pool_object(pool_name)
|
get_pool_object(pool_name)
|
||||||
@dummylist['pool'][pool_name] << vm
|
@dummylist['pool'][pool_name] << vm
|
||||||
|
|
||||||
# Add VM to Redis inventory ('pending' pool)
|
|
||||||
$redis.sadd('vmpooler__pending__' + pool_name, vm['hostname'])
|
|
||||||
$redis.hset('vmpooler__vm__' + vm['hostname'], 'clone', Time.now)
|
|
||||||
$redis.hset('vmpooler__vm__' + vm['hostname'], 'template', vm['template'])
|
|
||||||
|
|
||||||
$logger.log('d', "[ ] [#{pool_name}] '#{dummy_hostname}' is being cloned from '#{template_name}'")
|
$logger.log('d', "[ ] [#{pool_name}] '#{dummy_hostname}' is being cloned from '#{template_name}'")
|
||||||
begin
|
begin
|
||||||
start = Time.now
|
# Inject clone time delay
|
||||||
|
unless @options['createvm_max_time'].nil?
|
||||||
|
vm['dummy_state'] = 'CLONING'
|
||||||
|
clone_time = 1 + rand(@options['createvm_max_time'])
|
||||||
|
sleep(clone_time)
|
||||||
|
end
|
||||||
|
|
||||||
# TODO do I need fake a random sleep to clone
|
# Inject clone failure
|
||||||
sleep(2)
|
unless @options['createvm_fail_percent'].nil?
|
||||||
|
fail "Dummy Failure for createvm_fail_percent" if 1 + rand(100) <= @options['createvm_fail_percent']
|
||||||
|
end
|
||||||
|
|
||||||
# TODO Inject random clone failure
|
# Assert the VM is ready for use
|
||||||
finish = '%.2f' % (Time.now - start)
|
vm['dummy_state'] = 'RUNNING'
|
||||||
|
|
||||||
$redis.hset('vmpooler__clone__' + Date.today.to_s, vm['template'] + ':' + vm['hostname'], finish)
|
|
||||||
$redis.hset('vmpooler__vm__' + vm['hostname'], 'clone_time', finish)
|
|
||||||
|
|
||||||
$logger.log('s', "[+] [#{vm['template']}] '#{vm['hostname']}' cloned from '#{vm['template']}' in #{finish} seconds")
|
|
||||||
rescue => err
|
rescue => err
|
||||||
$logger.log('s', "[!] [#{vm['template']}] '#{vm['hostname']}' clone failed with an error: #{err}")
|
remove_dummy_vm(dummy_hostname,pool_name)
|
||||||
$redis.srem('vmpooler__pending__' + vm['template'], vm['hostname'])
|
|
||||||
raise
|
raise
|
||||||
end
|
end
|
||||||
|
|
||||||
$redis.decr('vmpooler__tasks__clone')
|
get_vm(dummy_hostname)
|
||||||
|
|
||||||
$metrics.timing("clone.#{vm['template']}", finish)
|
|
||||||
dummy_hostname
|
|
||||||
rescue => err
|
|
||||||
$logger.log('s', "[!] [#{vm['template']}] '#{vm['hostname']}' failed while preparing to clone with an error: #{err}")
|
|
||||||
raise
|
|
||||||
end
|
|
||||||
end
|
|
||||||
end
|
end
|
||||||
|
|
||||||
def destroy_vm(vm_name,pool)
|
def destroy_vm(vm_name,pool)
|
||||||
|
|
@ -153,30 +178,43 @@ module Vmpooler
|
||||||
if !vm then return false end
|
if !vm then return false end
|
||||||
if vm['poolname'] != pool then return false end
|
if vm['poolname'] != pool then return false end
|
||||||
|
|
||||||
start = Time.now
|
|
||||||
|
|
||||||
# Shutdown down the VM if it's poweredOn
|
# Shutdown down the VM if it's poweredOn
|
||||||
if vm['powerstate'] = 'PoweredOn'
|
if vm['powerstate'] = 'PoweredOn'
|
||||||
$logger.log('d', "[ ] [#{pool}] '#{vm_name}' is being shut down")
|
$logger.log('d', "[ ] [#{pool}] '#{vm_name}' is being shut down")
|
||||||
# TODO Use random shutdown interval
|
|
||||||
sleep(2)
|
# Inject shutdown delay time
|
||||||
|
unless @options['destroyvm_max_shutdown_time'].nil?
|
||||||
|
shutdown_time = 1 + rand(@options['destroyvm_max_shutdown_time'])
|
||||||
|
sleep(shutdown_time)
|
||||||
|
end
|
||||||
|
|
||||||
vm['powerstate'] = 'PoweredOff'
|
vm['powerstate'] = 'PoweredOff'
|
||||||
end
|
end
|
||||||
|
|
||||||
|
# Inject destroy VM delay
|
||||||
|
unless @options['destroyvm_max_time'].nil?
|
||||||
|
destroy_time = 1 + rand(@options['destroyvm_max_time'])
|
||||||
|
sleep(destroy_time)
|
||||||
|
end
|
||||||
|
|
||||||
|
# Inject destroy VM failure
|
||||||
|
unless @options['destroyvm_fail_percent'].nil?
|
||||||
|
fail "Dummy Failure for migratevm_fail_percent" if 1 + rand(100) <= @options['destroyvm_fail_percent']
|
||||||
|
end
|
||||||
|
|
||||||
# 'Destroy' the VM
|
# 'Destroy' the VM
|
||||||
new_poollist = @dummylist['pool'][pool].delete_if { |vm| vm['name'] == vm_name }
|
remove_dummy_vm(vm_name,pool)
|
||||||
@dummylist['pool'][pool] = new_poollist
|
|
||||||
|
|
||||||
# TODO Use random destruction interval
|
true
|
||||||
sleep(2)
|
|
||||||
|
|
||||||
finish = '%.2f' % (Time.now - start)
|
|
||||||
|
|
||||||
$logger.log('s', "[-] [#{pool}] '#{vm_name}' destroyed in #{finish} seconds")
|
|
||||||
$metrics.timing("destroy.#{pool}", finish)
|
|
||||||
end
|
end
|
||||||
|
|
||||||
private
|
private
|
||||||
|
def remove_dummy_vm(vm_name,pool)
|
||||||
|
return if @dummylist['pool'][pool].nil?
|
||||||
|
new_poollist = @dummylist['pool'][pool].delete_if { |vm| vm['name'] == vm_name }
|
||||||
|
@dummylist['pool'][pool] = new_poollist
|
||||||
|
end
|
||||||
|
|
||||||
# Get's the pool config safely from the in-memory hashtable
|
# Get's the pool config safely from the in-memory hashtable
|
||||||
def get_pool_object(pool_name)
|
def get_pool_object(pool_name)
|
||||||
@dummylist['pool'] = {} if @dummylist['pool'].nil?
|
@dummylist['pool'] = {} if @dummylist['pool'].nil?
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue