Refactor requests to return simple hashes and add unit tests

This massive commit refactors all of the request methods on the
Fog::Compute[:vsphere] instance to return simple hashes.  The behavior
before this commit returned full vmware object references which was a
problem because it was difficult to unit test.

With this patch, it is much easier to add and maintain Mock
implementations of the request methods.  This makes adding behavior
tests for the server model much easier.

In addition, test coverage using Shindo has been added.  Previously
there was little test coverage of the behavior.

To run the tests:

    shindont tests/vsphere/
This commit is contained in:
Jeff McCune 2011-09-10 13:27:52 -07:00
parent dc9a2e4808
commit 743882f032
29 changed files with 647 additions and 257 deletions

View File

@ -14,36 +14,73 @@ module Fog
request_path 'fog/vsphere/requests/compute'
request :current_time
request :find_vm_by_ref
request :list_virtual_machines
request :vm_power_off
request :vm_power_on
request :vm_reboot
request :vm_clone
request :vm_destroy
request :find_all_by_uuid
request :find_all_by_instance_uuid
request :find_template_by_instance_uuid
request :find_vm_by_ref
module Shared
attr_reader :vsphere_is_vcenter
attr_reader :vsphere_rev
# Utility method to convert a VMware managed object into an attribute hash.
# This should only really be necessary for the real class.
# This method is expected to be called by the request methods
# in order to massage VMware Managed Object References into Attribute Hashes.
def convert_vm_mob_ref_to_attr_hash(vm_mob_ref)
return nil unless vm_mob_ref
# A cloning VM doesn't have a configuration yet. Unfortuantely we just get
# a RunTime exception.
begin
is_ready = vm_mob_ref.config ? true : false
rescue RuntimeError
is_ready = nil
end
{
'id' => is_ready ? vm_mob_ref.config.instanceUuid : vm_mob_ref._ref,
'mo_ref' => vm_mob_ref._ref,
'name' => vm_mob_ref.name,
'uuid' => is_ready ? vm_mob_ref.config.uuid : nil,
'instance_uuid' => is_ready ? vm_mob_ref.config.instanceUuid : nil,
'hostname' => vm_mob_ref.summary.guest.hostName,
'operatingsystem' => vm_mob_ref.summary.guest.guestFullName,
'ipaddress' => vm_mob_ref.summary.guest.ipAddress,
'power_state' => vm_mob_ref.runtime.powerState,
'connection_state' => vm_mob_ref.runtime.connectionState,
'hypervisor' => vm_mob_ref.runtime.host ? vm_mob_ref.runtime.host.name : nil,
'tools_state' => vm_mob_ref.summary.guest.toolsStatus,
'tools_version' => vm_mob_ref.summary.guest.toolsVersionStatus,
'mac_addresses' => is_ready ? vm_mob_ref.macs : nil,
'is_a_template' => is_ready ? vm_mob_ref.config.template : nil
}
end
end
class Mock
include Shared
def initialize(options={})
require 'mocha'
@vsphere_username = options[:vsphere_username]
@vsphere_password = options[:vsphere_password]
@vsphere_password = 'REDACTED'
@vsphere_server = options[:vsphere_server]
@vsphere_expected_pubkey_hash = options[:vsphere_expected_pubkey_hash]
@vsphere_is_vcenter = true
@vsphere_rev = '4.0'
@connection = Mocha::Mock.new
# This may, or may not, be a terrible idea:
@connection.stub_everything
end
end
class Real
attr_reader :vsphere_is_vcenter
attr_reader :vsphere_rev
include Shared
def initialize(options={})
require 'rbvmomi'
@ -92,14 +129,6 @@ module Fog
authenticate
end
def reload
raise NotImplementedError
end
def request
raise NotImplementedError
end
private
def authenticate

View File

@ -31,56 +31,29 @@ module Fog
attribute :mac_addresses, :aliases => 'macs'
attribute :hypervisor, :aliases => 'host'
attribute :is_a_template
attribute :connection_state
attribute :mo_ref
# Return an attribute hash suitable for the initializer given a
# vSphere Managed Object (mob) instance.
def self.attribute_hash_from_mob(vm)
return {} unless vm
# A cloning VM doesn't have a configuration yet. Unfortuantely we just get
# a RunTime exception.
begin
is_ready = vm.config ? true : false
rescue RuntimeError
is_ready = nil
end
{
:id => is_ready ? vm.config.instanceUuid : vm._ref,
:name => vm.name,
:uuid => is_ready ? vm.config.uuid : nil,
:instance_uuid => is_ready ? vm.config.instanceUuid : nil,
:hostname => vm.summary.guest.hostName,
:operatingsystem => vm.summary.guest.guestFullName,
:ipaddress => vm.summary.guest.ipAddress,
:power_state => vm.runtime.powerState,
:connection_state => vm.runtime.connectionState,
:hypervisor => vm.runtime.host ? vm.runtime.host.name : nil,
:tools_state => vm.summary.guest.toolsStatus,
:tools_version => vm.summary.guest.toolsVersionStatus,
:mac_addresses => is_ready ? vm.macs : nil,
:is_a_template => is_ready ? vm.config.template : nil
}
def start(options = {})
requires :instance_uuid
connection.vm_power_on('instance_uuid' => instance_uuid)
end
def start
def stop(options = {})
options = { :force => false }.merge(options)
requires :instance_uuid
connection.vm_power_on(:instance_uuid => instance_uuid)
connection.vm_power_off('instance_uuid' => instance_uuid, 'force' => options[:force])
end
def stop(params = {})
params = { :force => false }.merge(params)
def reboot(options = {})
options = { :force => false }.merge(options)
requires :instance_uuid
connection.vm_power_off(:instance_uuid => instance_uuid, :force => params[:force])
connection.vm_reboot('instance_uuid' => instance_uuid, 'force' => options[:force])
end
def reboot(params = {})
params = { :force => false }.merge(params)
def destroy(options = {})
requires :instance_uuid
connection.vm_reboot(:instance_uuid => instance_uuid, :force => params[:force])
end
def destroy(params = {})
requires :instance_uuid
connection.vm_destroy(:instance_uuid => instance_uuid)
connection.vm_destroy('instance_uuid' => instance_uuid)
end
end

View File

@ -10,13 +10,8 @@ module Fog
model Fog::Compute::Vsphere::Server
def all
# Virtual Machine Managed Objects (vm_mobs)
vm_mobs = connection.list_virtual_machines
vm_attributes = vm_mobs.collect do |vm_mob|
model.attribute_hash_from_mob(vm_mob)
end
load(vm_attributes)
response = connection.list_virtual_machines
load(response['virtual_machines'])
end
def get(id)
@ -24,13 +19,13 @@ module Fog
# a model of a VM in the process of being cloned, since it
# will not have a instance_uuid yet.
if id =~ /^vm-/
vm_mob = connection.find_vm_by_ref(:vm_ref => id)
response = connection.find_vm_by_ref('vm_ref' => id)
server_attributes = response['virtual_machine']
else
vm_mob = connection.find_all_by_instance_uuid(id).first
end
if server_attributes = model.attribute_hash_from_mob(vm_mob)
new(server_attributes)
response = connection.list_virtual_machines('instance_uuid' => id)
server_attributes = response['virtual_machines'].first
end
new(server_attributes)
rescue Fog::Compute::Vsphere::NotFound
nil
end

View File

@ -4,7 +4,8 @@ module Fog
class Real
def current_time
@connection.serviceInstance.CurrentTime
current_time = @connection.serviceInstance.CurrentTime
{ 'current_time' => current_time }
end
end
@ -12,7 +13,7 @@ module Fog
class Mock
def current_time
Time.now.utc
{ 'current_time' => Time.now.utc }
end
end

View File

@ -1,21 +0,0 @@
module Fog
module Compute
class Vsphere
class Real
def find_all_by_instance_uuid(uuid)
@connection.searchIndex.FindAllByUuid(:uuid => uuid, 'vmSearch' => true, 'instanceUuid' => true)
end
end
class Mock
def find_all_by_instance_uuid(uuid)
Fog::Mock.not_implmented
end
end
end
end
end

View File

@ -1,22 +0,0 @@
module Fog
module Compute
class Vsphere
class Real
def find_all_by_uuid(uuid)
@connection.searchIndex.FindAllByUuid(:uuid => uuid, 'vmSearch' => true)
end
end
class Mock
def find_all_by_uuid(uuid)
Fog::Mock.not_implmented
end
end
end
end
end

View File

@ -1,27 +0,0 @@
module Fog
module Compute
class Vsphere
class Real
def find_template_by_instance_uuid(instance_uuid)
vm = list_virtual_machines.detect(lambda { raise Fog::Vsphere::Errors::NotFound }) do |vm|
vm.config.instanceUuid == instance_uuid
end
unless vm.config.template
raise Fog::Vsphere::Errors::NotFound, "VM with Instance UUID #{instance_uuid} is not a template."
end
vm
end
end
class Mock
def find_template_by_instance_uuid(instance_uuid)
Fog::Mock.not_implmented
end
end
end
end
end

View File

@ -1,28 +1,41 @@
module Fog
module Compute
class Vsphere
class Real
module Shared
# REVISIT: This is a naive implementation and not very efficient since
# we find ALL VM's and then iterate over them looking for the managed object
# reference id... There should be an easier way to obtain a reference to a
# VM using only the name or the _ref. This request is primarily intended to
# reload the attributes of a cloning VM which does not yet have an instance_uuid
def find_vm_by_ref(params = {})
list_virtual_machines.detect(lambda { raise Fog::Vsphere::Errors::NotFound }) do |vm|
vm._ref == params[:vm_ref]
def find_vm_by_ref(options = {})
raise ArgumentError, "Must pass a vm_ref option" unless options['vm_ref']
# This is the inefficient call
all_vm_attributes = list_virtual_machines['virtual_machines']
# Find the VM attributes of the reference
if vm_attributes = all_vm_attributes.find { |vm| vm['mo_ref'] == options['vm_ref'] }
response = { 'virtual_machine' => vm_attributes }
else
raise Fog::Compute::Vsphere::NotFound, "VirtualMachine with Managed Object Reference #{options['vm_ref']} could not be found."
end
response
end
end
# The Real and Mock classes share the same method
# because list_virtual_machines will be properly mocked for us
class Real
include Shared
end
class Mock
def find_vm_by_ref(params = {})
Fog::Mock.not_implmented
end
include Shared
end
end
end
end

View File

@ -1,11 +1,47 @@
module Fog
module Compute
class Vsphere
class Real
def list_virtual_machines
virtual_machines = Array.new
def list_virtual_machines(options = {})
# First, determine if there's a search filter
if options['instance_uuid'] then
list_all_virtual_machines_by_instance_uuid(options)
else
list_all_virtual_machines
end
end
private
def list_all_virtual_machines_by_instance_uuid(options = {})
uuid = options['instance_uuid']
search_filter = { :uuid => uuid, 'vmSearch' => true, 'instanceUuid' => true }
vm_mob_ref = @connection.searchIndex.FindAllByUuid(search_filter).first
if vm_attribute_hash = convert_vm_mob_ref_to_attr_hash(vm_mob_ref) then
virtual_machines = [ vm_attribute_hash ]
else
virtual_machines = [ ]
end
{ 'virtual_machines' => virtual_machines }
end
def list_all_virtual_machines
virtual_machines = list_all_virtual_machine_mobs.collect do |vm_mob|
convert_vm_mob_ref_to_attr_hash(vm_mob)
end
{ 'virtual_machines' => virtual_machines }
end
# NOTE: This is a private instance method required by the vm_clone
# request. It's very hard to get the Managed Object Reference
# of a Template because we can't search for it by instance_uuid
# As a result, we need a list of all virtual machines, and we
# need them in "native" format, not filter attribute format.
def list_all_virtual_machine_mobs
virtual_machines = Array.new
# Find each datacenter
datacenters = @connection.rootFolder.children.find_all do |child|
child.kind_of? RbVmomi::VIM::Datacenter
end
@ -22,11 +58,105 @@ module Fog
class Mock
def list_virtual_machines
Fog::Mock.not_implmented
def list_virtual_machines(options = {})
case options['instance_uuid']
when nil
rval = YAML.load <<-'ENDvmLISTING'
---
virtual_machines:
- name: centos56gm
hypervisor: gunab.puppetlabs.lan
tools_version: guestToolsCurrent
ipaddress:
mo_ref: vm-698
power_state: poweredOff
uuid: 42322347-d791-cd34-80b9-e25fe28ad37c
is_a_template: true
id: 50323f93-6835-1178-8b8f-9e2109890e1a
tools_state: toolsNotRunning
connection_state: connected
instance_uuid: 50323f93-6835-1178-8b8f-9e2109890e1a
hostname:
mac_addresses:
Network adapter 1: 00:50:56:b2:00:a1
operatingsystem:
- name: centos56gm2
hypervisor: gunab.puppetlabs.lan
tools_version: guestToolsCurrent
ipaddress:
mo_ref: vm-640
power_state: poweredOff
uuid: 564ddcbe-853a-d29a-b329-a0a3693a004d
is_a_template: true
id: 5257dee8-050c-cbcd-ae25-db0e582ab530
tools_state: toolsNotRunning
connection_state: connected
instance_uuid: 5257dee8-050c-cbcd-ae25-db0e582ab530
hostname:
mac_addresses:
Network adapter 1: 00:0c:29:3a:00:4d
operatingsystem:
- name: dashboard_gm
hypervisor: gunab.puppetlabs.lan
tools_version: guestToolsCurrent
ipaddress: 192.168.100.184
mo_ref: vm-669
power_state: poweredOn
uuid: 564d3f91-3452-a509-a678-1246f7897979
is_a_template: false
id: 5032739c-c871-c0d2-034f-9700a0b5383e
tools_state: toolsOk
connection_state: connected
instance_uuid: 5032739c-c871-c0d2-034f-9700a0b5383e
hostname: compliance.puppetlabs.vm
mac_addresses:
Network adapter 1: 00:50:56:b2:00:96
operatingsystem: Red Hat Enterprise Linux 6 (64-bit)
- name: jefftest
hypervisor: gunab.puppetlabs.lan
tools_version: guestToolsCurrent
ipaddress: 192.168.100.187
mo_ref: vm-715
power_state: poweredOn
uuid: 42329da7-e8ab-29ec-1892-d6a4a964912a
is_a_template: false
id: 5032c8a5-9c5e-ba7a-3804-832a03e16381
tools_state: toolsOk
connection_state: connected
instance_uuid: 5032c8a5-9c5e-ba7a-3804-832a03e16381
hostname: centos56gm.localdomain
mac_addresses:
Network adapter 1: 00:50:56:b2:00:af
operatingsystem: CentOS 4/5 (32-bit)
ENDvmLISTING
when '5032c8a5-9c5e-ba7a-3804-832a03e16381'
YAML.load <<-'5032c8a5-9c5e-ba7a-3804-832a03e16381'
---
virtual_machines:
- name: jefftest
hypervisor: gunab.puppetlabs.lan
tools_version: guestToolsCurrent
ipaddress: 192.168.100.187
mo_ref: vm-715
power_state: poweredOn
uuid: 42329da7-e8ab-29ec-1892-d6a4a964912a
is_a_template: false
id: 5032c8a5-9c5e-ba7a-3804-832a03e16381
tools_state: toolsOk
connection_state: connected
instance_uuid: 5032c8a5-9c5e-ba7a-3804-832a03e16381
hostname: centos56gm.localdomain
mac_addresses:
Network adapter 1: 00:50:56:b2:00:af
operatingsystem: CentOS 4/5 (32-bit)
5032c8a5-9c5e-ba7a-3804-832a03e16381
when 'does-not-exist-and-is-not-a-uuid', '50323f93-6835-1178-8b8f-9e2109890e1a'
{ 'virtual_machines' => [] }
end
end
end
end
end
end

View File

@ -1,23 +1,39 @@
module Fog
module Compute
class Vsphere
class Real
def vm_clone(params = {})
params = { :force => false }.merge(params)
required_params = %w{ instance_uuid name }
required_params.each do |param|
raise ArgumentError, "#{required_params.join(', ')} are required" unless params.has_key? param.to_sym
module Shared
private
def vm_clone_check_options(options)
options = { 'force' => false }.merge(options)
required_options = %w{ instance_uuid name }
required_options.each do |param|
raise ArgumentError, "#{required_options.join(', ')} are required" unless options.has_key? param
end
# First, figure out if there's already a VM of the same name.
if not params[:force] and list_virtual_machines.detect { |vm| vm.name == params[:name] } then
raise Fog::Vsphere::Errors::ServiceError, "A VM already exists with name #{params[:name]}"
all_virtual_machines = list_virtual_machines['virtual_machines']
if not options['force'] and all_virtual_machines.detect { |vm| vm['name'] == options['name'] } then
raise Fog::Vsphere::Errors::ServiceError, "A VM already exists with name #{options['name']}"
end
# Find the Managed Object reference of the template VM
vm = find_template_by_instance_uuid(params[:instance_uuid])
options
end
end
class Real
include Shared
def vm_clone(options = {})
# Option handling
options = vm_clone_check_options(options)
# REVISIT: This will have horrible performance for large sites.
# Find the Managed Object reference of the template VM (Wish I could do this with the API)
vm_mob_ref = list_all_virtual_machine_mobs.find do |vm|
convert_vm_mob_ref_to_attr_hash(vm)['instance_uuid'] == options['instance_uuid']
end
# We need to locate the datacenter object to find the
# default resource pool.
container = vm.parent
container = vm_mob_ref.parent
until container.kind_of? RbVmomi::VIM::Datacenter
container = container.parent
end
@ -26,52 +42,46 @@ module Fog
resource_pool = dc.hostFolder.children.first.resourcePool
# Next, create a Relocation Spec instance
relocation_spec = RbVmomi::VIM.VirtualMachineRelocateSpec(:pool => resource_pool,
:transform => 'sparse')
:transform => options['transform'] || 'sparse')
# And the clone specification
clone_spec = RbVmomi::VIM.VirtualMachineCloneSpec(:location => relocation_spec,
:powerOn => true,
:powerOn => options['power_on'] || true,
:template => false)
task = vm.CloneVM_Task(:folder => vm.parent, :name => params[:name], :spec => clone_spec)
task = vm_mob_ref.CloneVM_Task(:folder => vm_mob_ref.parent, :name => options['name'], :spec => clone_spec)
# REVISIT: The task object contains a reference to the template but does
# not appear to contain a reference to the newly created VM.
# This is a really naive way to find the managed object reference
# of the newly created VM.
tries = 0
new_vm = begin
list_virtual_machines.detect(lambda { raise Fog::Vsphere::Errors::NotFound }) do |vm|
next false if vm.name != params[:name]
begin
vm.config ? true : false
rescue RuntimeError
# This rescue is here because we want to make sure we find
# a VM _without_ a config, which indicates the VM is still cloning.
true
end
list_virtual_machines['virtual_machines'].detect(lambda { raise Fog::Vsphere::Errors::NotFound }) do |vm|
vm['name'] == options['name']
end
rescue Fog::Vsphere::Errors::NotFound
tries += 1
if tries <= 10 then
sleep 2
sleep 1
retry
end
nil
end
# Taking a hint from wait_for we return a hash to indicate this is a
# managed object reference
# Return hash
{
:vm_ref => new_vm ? new_vm._ref : nil,
:task_ref => task._ref
'vm_ref' => new_vm ? new_vm['mo_ref'] : nil,
'task_ref' => task._ref
}
end
end
class Mock
def vm_clone(params = {})
include Shared
def vm_clone(options = {})
# Option handling
options = vm_clone_check_options(options)
{
:vm_ref => 'vm-123',
:task_ref => 'task-1234'
'vm_ref' => 'vm-123',
'task_ref' => 'task-1234'
}
end

View File

@ -3,23 +3,29 @@ module Fog
class Vsphere
class Real
def vm_destroy(params = {})
raise ArgumentError ":instance_uuid is a required parameter" unless params.has_key? :instance_uuid
vm = find_all_by_instance_uuid(params[:instance_uuid]).first
unless vm.kind_of? RbVmomi::VIM::VirtualMachine
raise Fog::Vsphere::Errors::NotFound, "Could not find VirtualMachine with instance uuid #{params[:instance_uuid]}"
def vm_destroy(options = {})
raise ArgumentError, "instance_uuid is a required parameter" unless options.has_key? 'instance_uuid'
# Find the VM Object
search_filter = { :uuid => options['instance_uuid'], 'vmSearch' => true, 'instanceUuid' => true }
vm_mob_ref = @connection.searchIndex.FindAllByUuid(search_filter).first
unless vm_mob_ref.kind_of? RbVmomi::VIM::VirtualMachine
raise Fog::Vsphere::Errors::NotFound,
"Could not find VirtualMachine with instance uuid #{options['instance_uuid']}"
end
task = vm.Destroy_Task
task = vm_mob_ref.Destroy_Task
task.wait_for_completion
task.info.state
{ 'task_state' => task.info.state }
end
end
class Mock
def vm_destroy(params = {})
"success"
def vm_destroy(options = {})
raise ArgumentError, "instance_uuid is a required parameter" unless options.has_key? 'instance_uuid'
{ 'task_state' => 'success' }
end
end

View File

@ -3,19 +3,23 @@ module Fog
class Vsphere
class Real
def vm_power_off(params = {})
params = { :force => false }.merge(params)
raise ArgumentError ":instance_uuid is a required parameter" unless params.has_key? :instance_uuid
unless vm = find_all_by_instance_uuid(params[:instance_uuid]).shift
raise Fog::Vsphere::Errors::NotFound, "Could not find a VM with instance UUID: #{params[:instance_uuid]}"
end
if params[:force] then
task = vm.PowerOffVM_Task
def vm_power_off(options = {})
options = { 'force' => false }.merge(options)
raise ArgumentError, "instance_uuid is a required parameter" unless options.has_key? 'instance_uuid'
search_filter = { :uuid => options['instance_uuid'], 'vmSearch' => true, 'instanceUuid' => true }
vm_mob_ref = @connection.searchIndex.FindAllByUuid(search_filter).first
if options['force'] then
task = vm_mob_ref.PowerOffVM_Task
task.wait_for_completion
task.info.result
{ 'task_state' => task.info.result, 'power_off_type' => 'cut_power' }
else
vm.ShutdownGuest
"running"
vm_mob_ref.ShutdownGuest
{
'task_state' => "running",
'power_off_type' => 'shutdown_guest',
}
end
end
@ -23,8 +27,12 @@ module Fog
class Mock
def vm_power_off(params = {})
"running"
def vm_power_off(options = {})
raise ArgumentError, "instance_uuid is a required parameter" unless options.has_key? 'instance_uuid'
{
'task_state' => "running",
'power_off_type' => options['force'] ? 'cut_power' : 'shutdown_guest',
}
end
end

View File

@ -3,24 +3,25 @@ module Fog
class Vsphere
class Real
def vm_power_on(params = {})
raise ArgumentError ":instance_uuid is a required parameter" unless params.has_key? :instance_uuid
unless vm = find_all_by_instance_uuid(params[:instance_uuid]).shift
raise Fog::Vsphere::Errors::NotFound, "Could not find a VM with instance UUID: #{params[:instance_uuid]}"
end
task = vm.PowerOnVM_Task
def vm_power_on(options = {})
raise ArgumentError, "instance_uuid is a required parameter" unless options.has_key? 'instance_uuid'
search_filter = { :uuid => options['instance_uuid'], 'vmSearch' => true, 'instanceUuid' => true }
vm_mob_ref = @connection.searchIndex.FindAllByUuid(search_filter).first
task = vm_mob_ref.PowerOnVM_Task
task.wait_for_completion
# 'success', 'running', 'queued', 'error'
task.info.state
{ 'task_state' => task.info.state }
end
end
class Mock
def vm_power_on(params = {})
# Mock the task.info.state object
"success"
def vm_power_on(options = {})
raise ArgumentError, "instance_uuid is a required parameter" unless options.has_key? 'instance_uuid'
{ 'task_state' => 'success' }
end
end

View File

@ -3,19 +3,20 @@ module Fog
class Vsphere
class Real
def vm_reboot(params = {})
params = { :force => false }.merge(params)
raise ArgumentError ":instance_uuid is a required parameter" unless params.has_key? :instance_uuid
unless vm = find_all_by_instance_uuid(params[:instance_uuid]).shift
raise Fog::Vsphere::Errors::NotFound, "Could not find a VM with instance UUID: #{params[:instance_uuid]}"
end
if params[:force] then
task = vm.ResetVM_Task
def vm_reboot(options = {})
options = { 'force' => false }.merge(options)
raise ArgumentError, "instance_uuid is a required parameter" unless options.has_key? 'instance_uuid'
search_filter = { :uuid => options['instance_uuid'], 'vmSearch' => true, 'instanceUuid' => true }
vm_mob_ref = @connection.searchIndex.FindAllByUuid(search_filter).first
if options['force'] then
task = vm_mob_ref.ResetVM_Task
task.wait_for_completion
task.info.state
{ 'task_state' => task.info.result, 'reboot_type' => 'reset_power' }
else
vm.RebootGuest
"running"
vm_mob_ref.ShutdownGuest
{ 'task_state' => "running", 'reboot_type' => 'reboot_guest' }
end
end
@ -23,8 +24,9 @@ module Fog
class Mock
def vm_reboot(params = {})
"running"
def vm_reboot(options = {})
raise ArgumentError, "instance_uuid is a required parameter" unless options.has_key? 'instance_uuid'
{ 'task_state' => "running", 'reboot_type' => options['force'] ? 'reset_power' : 'reboot_guest' }
end
end

View File

@ -1,10 +0,0 @@
Shindo.tests('Fog::Compute[:vsphere] | current_time request', ['vsphere']) do
compute = Fog::Compute[:vsphere]
tests('is a Time object').succeeds do
pending if Fog.mock?
compute.current_time.is_a?(Time)
end
end

View File

@ -1,16 +0,0 @@
class Vsphere
module Compute
module Formats
SUMMARY = {
'id' => Integer,
'name' => String
}
end
end
end

View File

@ -0,0 +1,47 @@
Shindo.tests('Fog::Compute[:vsphere]', ['vsphere']) do
compute = Fog::Compute[:vsphere]
tests("| convert_vm_mob_ref_to_attr_hash") do
require 'ostruct'
fake_vm = OpenStruct.new({
:_ref => 'vm-123',
:name => 'fakevm',
:summary => OpenStruct.new(:guest => OpenStruct.new),
:runtime => OpenStruct.new,
})
tests("When converting an incomplete vm object") do
test("it should return a Hash") do
compute.convert_vm_mob_ref_to_attr_hash(fake_vm).kind_of? Hash
end
tests("The converted Hash should") do
attr_hash = compute.convert_vm_mob_ref_to_attr_hash(fake_vm)
test("have a name") { attr_hash['name'] == 'fakevm' }
test("have a mo_ref") {attr_hash['mo_ref'] == 'vm-123' }
test("have an id") { attr_hash['id'] == 'vm-123' }
test("not have a instance_uuid") { attr_hash['instance_uuid'].nil? }
end
end
tests("When passed a nil object") do
attr_hash = compute.convert_vm_mob_ref_to_attr_hash(nil)
test("it should return a nil object") do
attr_hash.nil?
end
end
end
tests("Compute attributes") do
%w{ vsphere_is_vcenter vsphere_rev }.each do |attr|
test("it should respond to #{attr}") { compute.respond_to? attr }
end
end
tests("Compute collections") do
%w{ servers }.each do |collection|
test("it should respond to #{collection}") { compute.respond_to? collection }
end
end
end

View File

@ -0,0 +1,45 @@
Shindo.tests('Fog::Compute[:vsphere] | server model', ['vsphere']) do
servers = Fog::Compute[:vsphere].servers
server = servers.last
tests('The server model should') do
tests('have the action') do
test('reload') { server.respond_to? 'reload' }
%w{ stop start destroy reboot }.each do |action|
test(action) { server.respond_to? action }
test("#{action} returns successfully") { server.send(action.to_sym) ? true : false }
end
end
tests('have attributes') do
model_attribute_hash = server.attributes
attributes = [ :id,
:instance_uuid,
:uuid,
:power_state,
:tools_state,
:mo_ref,
:tools_version,
:hostname,
:mac_addresses,
:operatingsystem,
:connection_state,
:hypervisor,
:name,
:ipaddress,
:is_a_template]
tests("The server model should respond to") do
attributes.each do |attribute|
test("#{attribute}") { server.respond_to? attribute }
end
end
tests("The attributes hash should have key") do
attributes.each do |attribute|
test("#{attribute}") { model_attribute_hash.has_key? attribute }
end
end
end
test('be a kind of Fog::Compute::Vsphere::Server') { server.kind_of? Fog::Compute::Vsphere::Server }
end
end

View File

@ -0,0 +1,15 @@
Shindo.tests('Fog::Compute[:vsphere] | servers collection', ['vsphere']) do
servers = Fog::Compute[:vsphere].servers
tests('The servers collection') do
test('should not be empty') { not servers.empty? }
test('should be a kind of Fog::Compute::Vsphere::Servers') { servers.kind_of? Fog::Compute::Vsphere::Servers }
tests('should be able to reload itself').succeeds { servers.reload }
tests('should be able to get a model') do
tests('by managed object reference').succeeds { servers.get 'vm-715' }
tests('by instance uuid').succeeds { servers.get '5032c8a5-9c5e-ba7a-3804-832a03e16381' }
end
end
end

View File

@ -0,0 +1,12 @@
Shindo.tests('Fog::Compute[:vsphere] | current_time request', ['vsphere']) do
compute = Fog::Compute[:vsphere]
tests('The response should') do
response = compute.current_time
test('be a kind of Hash') { response.kind_of? Hash }
test('have a current_time key') { response.has_key? 'current_time' }
test('have a current_time key with a Time value') { response['current_time'].kind_of? Time }
end
end

View File

@ -0,0 +1,26 @@
Shindo.tests('Fog::Compute[:vsphere] | find_vm_by_ref request', ['vsphere']) do
compute = Fog::Compute[:vsphere]
tests("When missing arguments") do
raises(ArgumentError, "Should raise ArgumentError when missing :vm_ref") do
compute.find_vm_by_ref
end
raises(Fog::Compute::Vsphere::NotFound, "Should raise Fog::Compute::Vsphere::NotFound when the vm does not exist") do
compute.find_vm_by_ref('vm_ref' => 'vm-000')
end
end
# centos56gm is a template
existing_vms = { 'vm-715' => 'jefftest', 'vm-698' => 'centos56gm' }
tests("When looking for existing VM's the response") do
existing_vms.each do |ref,name|
response = compute.find_vm_by_ref('vm_ref' => ref)
test("should be a kind of Hash") { response.kind_of? Hash }
test("should have virtual_machine key") { response.has_key? 'virtual_machine' }
returns(name, "#{ref} should return #{name}") { response['virtual_machine']['name'] }
end
end
end

View File

@ -0,0 +1,46 @@
Shindo.tests('Fog::Compute[:vsphere] | list_virtual_machines request', ['vsphere']) do
key = 'virtual_machines'
tests("When listing all machines") do
response = Fog::Compute[:vsphere].list_virtual_machines
tests("The response data format ...") do
test("be a kind of Hash") { response.kind_of? Hash }
test("have a #{key} key") do
response.has_key? key
end
test("it should be a kind of Array") do
response[key].kind_of? Array
end
end
end
tests("When providing an instance_uuid") do
# pending unless Fog.mock?
tests("that does exist") do
uuid = "5032c8a5-9c5e-ba7a-3804-832a03e16381"
response = Fog::Compute[:vsphere].list_virtual_machines({'instance_uuid' => uuid})
tests("The response should") do
test("contain one vm") { response[key].length == 1 }
test("contain that is an attribute hash") { response[key][0].kind_of? Hash }
test("find jefftest") { response[key].first['name'] == 'jefftest' }
end
end
tests("that does not exist or is a template") do
%w{ does-not-exist-and-is-not-a-uuid 50323f93-6835-1178-8b8f-9e2109890e1a }.each do |uuid|
response = Fog::Compute[:vsphere].list_virtual_machines({'instance_uuid' => uuid})
tests("The response should") do
test("be empty") { response[key].empty? }
end
end
end
end
end

View File

@ -0,0 +1,19 @@
Shindo.tests("Fog::Compute[:servers] | vm_clone request") do
template = "50323f93-6835-1178-8b8f-9e2109890e1a"
compute = Fog::Compute[:vsphere]
tests("The return value should") do
response = compute.vm_clone('instance_uuid' => template, 'name' => 'cloning_vm')
test("be a kind of Hash") { response.kind_of? Hash }
%w{ vm_ref task_ref }.each do |key|
test("have a #{key} key") { response.has_key? key }
end
end
tests("When invalid input is presented") do
raises(ArgumentError, 'it should raise ArgumentError') { compute.vm_clone(:foo => 1) }
raises(Fog::Vsphere::Errors::ServiceError,
'it should raise ServiceError if a VM already exists with the provided name') do
compute.vm_clone('instance_uuid' => '123', 'name' => 'jefftest')
end
end
end

View File

@ -0,0 +1,17 @@
Shindo.tests('Fog::Compute[:vsphere] | vm_destroy request', ['vsphere']) do
compute = Fog::Compute[:vsphere]
booted_vm = '5032c8a5-9c5e-ba7a-3804-832a03e16381'
tests('The response should') do
response = compute.vm_destroy('instance_uuid' => booted_vm)
test('be a kind of Hash') { response.kind_of? Hash }
test('should have a task_state key') { response.has_key? 'task_state' }
end
tests('The expected options') do
raises(ArgumentError, 'raises ArgumentError when instance_uuid option is missing') { compute.vm_destroy }
end
end

View File

@ -0,0 +1,26 @@
Shindo.tests('Fog::Compute[:vsphere] | vm_power_off request', ['vsphere']) do
compute = Fog::Compute[:vsphere]
powered_on_vm = '5032c8a5-9c5e-ba7a-3804-832a03e16381'
tests('The response should') do
response = compute.vm_power_off('instance_uuid' => powered_on_vm)
test('be a kind of Hash') { response.kind_of? Hash }
test('should have a task_state key') { response.has_key? 'task_state' }
test('should have a power_off_type key') { response.has_key? 'power_off_type' }
end
# When forcing the shutdown, we expect the result to be
{ true => 'cut_power', false => 'shutdown_guest'}.each do |force, expected|
tests("When 'force' => #{force}") do
response = compute.vm_power_off('instance_uuid' => powered_on_vm, 'force' => force)
test('should retur power_off_type of #{expected}') { response['power_off_type'] == expected }
end
end
tests('The expected options') do
raises(ArgumentError, 'raises ArgumentError when instance_uuid option is missing') { compute.vm_power_off }
end
end

View File

@ -0,0 +1,17 @@
Shindo.tests('Fog::Compute[:vsphere] | vm_power_on request', ['vsphere']) do
compute = Fog::Compute[:vsphere]
powered_off_vm = nil
tests('The response should') do
response = compute.vm_power_off('instance_uuid' => powered_off_vm)
test('be a kind of Hash') { response.kind_of? Hash }
test('should have a task_state key') { response.has_key? 'task_state' }
end
tests('The expected options') do
raises(ArgumentError, 'raises ArgumentError when instance_uuid option is missing') { compute.vm_power_on }
end
end

View File

@ -0,0 +1,26 @@
Shindo.tests('Fog::Compute[:vsphere] | vm_reboot request', ['vsphere']) do
compute = Fog::Compute[:vsphere]
powered_on_vm = '5032c8a5-9c5e-ba7a-3804-832a03e16381'
tests('The response should') do
response = compute.vm_reboot('instance_uuid' => powered_on_vm)
test('be a kind of Hash') { response.kind_of? Hash }
test('should have a task_state key') { response.has_key? 'task_state' }
test('should have a reboot_type key') { response.has_key? 'reboot_type' }
end
# When forcing the shutdown, we expect the result to be
{ true => 'reset_power', false => 'reboot_guest'}.each do |force, expected|
tests("When force => #{force}") do
response = compute.vm_reboot('instance_uuid' => powered_on_vm, 'force' => force)
test("should return reboot_type of #{expected}") { response['reboot_type'] == expected }
end
end
tests('The expected options') do
raises(ArgumentError, 'raises ArgumentError when instance_uuid option is missing') { compute.vm_reboot }
end
end

22
tests/watchr.rb Normal file
View File

@ -0,0 +1,22 @@
ENV['FOG_MOCK'] ||= 'true'
ENV['AUTOTEST'] = 'true'
ENV['WATCHR'] = '1'
def file2shindo(file)
result = file.sub('lib/fog/', 'tests/').gsub(/\.rb$/, '_tests.rb')
end
def run_shindo_test(file)
if File.exist? file
system("shindont #{file}")
else
puts "FIXME: No test #{file} [#{Time.now}]"
end
end
watch( 'tests/.*_tests\.rb' ) do |md|
run_shindo_test(md[0])
end
watch( 'lib/.*\.rb' ) do |md|
run_shindo_test(file2shindo(md[0]))
end