Skip to content

Commit 743882f

Browse files
author
Jeff McCune
committed
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/
1 parent dc9a2e4 commit 743882f

29 files changed

+645
-255
lines changed

lib/fog/vsphere/compute.rb

Lines changed: 46 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -14,36 +14,73 @@ class Vsphere < Fog::Service
1414

1515
request_path 'fog/vsphere/requests/compute'
1616
request :current_time
17+
request :find_vm_by_ref
1718
request :list_virtual_machines
1819
request :vm_power_off
1920
request :vm_power_on
2021
request :vm_reboot
2122
request :vm_clone
2223
request :vm_destroy
23-
request :find_all_by_uuid
24-
request :find_all_by_instance_uuid
25-
request :find_template_by_instance_uuid
26-
request :find_vm_by_ref
24+
25+
module Shared
26+
27+
attr_reader :vsphere_is_vcenter
28+
attr_reader :vsphere_rev
29+
30+
# Utility method to convert a VMware managed object into an attribute hash.
31+
# This should only really be necessary for the real class.
32+
# This method is expected to be called by the request methods
33+
# in order to massage VMware Managed Object References into Attribute Hashes.
34+
def convert_vm_mob_ref_to_attr_hash(vm_mob_ref)
35+
return nil unless vm_mob_ref
36+
# A cloning VM doesn't have a configuration yet. Unfortuantely we just get
37+
# a RunTime exception.
38+
begin
39+
is_ready = vm_mob_ref.config ? true : false
40+
rescue RuntimeError
41+
is_ready = nil
42+
end
43+
{
44+
'id' => is_ready ? vm_mob_ref.config.instanceUuid : vm_mob_ref._ref,
45+
'mo_ref' => vm_mob_ref._ref,
46+
'name' => vm_mob_ref.name,
47+
'uuid' => is_ready ? vm_mob_ref.config.uuid : nil,
48+
'instance_uuid' => is_ready ? vm_mob_ref.config.instanceUuid : nil,
49+
'hostname' => vm_mob_ref.summary.guest.hostName,
50+
'operatingsystem' => vm_mob_ref.summary.guest.guestFullName,
51+
'ipaddress' => vm_mob_ref.summary.guest.ipAddress,
52+
'power_state' => vm_mob_ref.runtime.powerState,
53+
'connection_state' => vm_mob_ref.runtime.connectionState,
54+
'hypervisor' => vm_mob_ref.runtime.host ? vm_mob_ref.runtime.host.name : nil,
55+
'tools_state' => vm_mob_ref.summary.guest.toolsStatus,
56+
'tools_version' => vm_mob_ref.summary.guest.toolsVersionStatus,
57+
'mac_addresses' => is_ready ? vm_mob_ref.macs : nil,
58+
'is_a_template' => is_ready ? vm_mob_ref.config.template : nil
59+
}
60+
end
61+
62+
end
2763

2864
class Mock
2965

66+
include Shared
67+
3068
def initialize(options={})
3169
require 'mocha'
3270
@vsphere_username = options[:vsphere_username]
33-
@vsphere_password = options[:vsphere_password]
71+
@vsphere_password = 'REDACTED'
3472
@vsphere_server = options[:vsphere_server]
3573
@vsphere_expected_pubkey_hash = options[:vsphere_expected_pubkey_hash]
74+
@vsphere_is_vcenter = true
75+
@vsphere_rev = '4.0'
3676
@connection = Mocha::Mock.new
37-
# This may, or may not, be a terrible idea:
38-
@connection.stub_everything
3977
end
4078

4179
end
4280

4381
class Real
4482

45-
attr_reader :vsphere_is_vcenter
46-
attr_reader :vsphere_rev
83+
include Shared
4784

4885
def initialize(options={})
4986
require 'rbvmomi'
@@ -92,14 +129,6 @@ def initialize(options={})
92129
authenticate
93130
end
94131

95-
def reload
96-
raise NotImplementedError
97-
end
98-
99-
def request
100-
raise NotImplementedError
101-
end
102-
103132
private
104133

105134
def authenticate

lib/fog/vsphere/models/compute/server.rb

Lines changed: 12 additions & 39 deletions
Original file line numberDiff line numberDiff line change
@@ -31,56 +31,29 @@ class Server < Fog::Compute::Server
3131
attribute :mac_addresses, :aliases => 'macs'
3232
attribute :hypervisor, :aliases => 'host'
3333
attribute :is_a_template
34+
attribute :connection_state
35+
attribute :mo_ref
3436

35-
# Return an attribute hash suitable for the initializer given a
36-
# vSphere Managed Object (mob) instance.
37-
def self.attribute_hash_from_mob(vm)
38-
return {} unless vm
39-
# A cloning VM doesn't have a configuration yet. Unfortuantely we just get
40-
# a RunTime exception.
41-
begin
42-
is_ready = vm.config ? true : false
43-
rescue RuntimeError
44-
is_ready = nil
45-
end
46-
{
47-
:id => is_ready ? vm.config.instanceUuid : vm._ref,
48-
:name => vm.name,
49-
:uuid => is_ready ? vm.config.uuid : nil,
50-
:instance_uuid => is_ready ? vm.config.instanceUuid : nil,
51-
:hostname => vm.summary.guest.hostName,
52-
:operatingsystem => vm.summary.guest.guestFullName,
53-
:ipaddress => vm.summary.guest.ipAddress,
54-
:power_state => vm.runtime.powerState,
55-
:connection_state => vm.runtime.connectionState,
56-
:hypervisor => vm.runtime.host ? vm.runtime.host.name : nil,
57-
:tools_state => vm.summary.guest.toolsStatus,
58-
:tools_version => vm.summary.guest.toolsVersionStatus,
59-
:mac_addresses => is_ready ? vm.macs : nil,
60-
:is_a_template => is_ready ? vm.config.template : nil
61-
}
62-
end
63-
64-
def start
37+
def start(options = {})
6538
requires :instance_uuid
66-
connection.vm_power_on(:instance_uuid => instance_uuid)
39+
connection.vm_power_on('instance_uuid' => instance_uuid)
6740
end
6841

69-
def stop(params = {})
70-
params = { :force => false }.merge(params)
42+
def stop(options = {})
43+
options = { :force => false }.merge(options)
7144
requires :instance_uuid
72-
connection.vm_power_off(:instance_uuid => instance_uuid, :force => params[:force])
45+
connection.vm_power_off('instance_uuid' => instance_uuid, 'force' => options[:force])
7346
end
7447

75-
def reboot(params = {})
76-
params = { :force => false }.merge(params)
48+
def reboot(options = {})
49+
options = { :force => false }.merge(options)
7750
requires :instance_uuid
78-
connection.vm_reboot(:instance_uuid => instance_uuid, :force => params[:force])
51+
connection.vm_reboot('instance_uuid' => instance_uuid, 'force' => options[:force])
7952
end
8053

81-
def destroy(params = {})
54+
def destroy(options = {})
8255
requires :instance_uuid
83-
connection.vm_destroy(:instance_uuid => instance_uuid)
56+
connection.vm_destroy('instance_uuid' => instance_uuid)
8457
end
8558

8659
end

lib/fog/vsphere/models/compute/servers.rb

Lines changed: 7 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -10,27 +10,22 @@ class Servers < Fog::Collection
1010
model Fog::Compute::Vsphere::Server
1111

1212
def all
13-
# Virtual Machine Managed Objects (vm_mobs)
14-
vm_mobs = connection.list_virtual_machines
15-
vm_attributes = vm_mobs.collect do |vm_mob|
16-
model.attribute_hash_from_mob(vm_mob)
17-
end
18-
19-
load(vm_attributes)
13+
response = connection.list_virtual_machines
14+
load(response['virtual_machines'])
2015
end
2116

2217
def get(id)
2318
# Is the id a managed_object_reference? This may be the case if we're reloading
2419
# a model of a VM in the process of being cloned, since it
2520
# will not have a instance_uuid yet.
2621
if id =~ /^vm-/
27-
vm_mob = connection.find_vm_by_ref(:vm_ref => id)
22+
response = connection.find_vm_by_ref('vm_ref' => id)
23+
server_attributes = response['virtual_machine']
2824
else
29-
vm_mob = connection.find_all_by_instance_uuid(id).first
30-
end
31-
if server_attributes = model.attribute_hash_from_mob(vm_mob)
32-
new(server_attributes)
25+
response = connection.list_virtual_machines('instance_uuid' => id)
26+
server_attributes = response['virtual_machines'].first
3327
end
28+
new(server_attributes)
3429
rescue Fog::Compute::Vsphere::NotFound
3530
nil
3631
end

lib/fog/vsphere/requests/compute/current_time.rb

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -4,15 +4,16 @@ class Vsphere
44
class Real
55

66
def current_time
7-
@connection.serviceInstance.CurrentTime
7+
current_time = @connection.serviceInstance.CurrentTime
8+
{ 'current_time' => current_time }
89
end
910

1011
end
1112

1213
class Mock
1314

1415
def current_time
15-
Time.now.utc
16+
{ 'current_time' => Time.now.utc }
1617
end
1718

1819
end

lib/fog/vsphere/requests/compute/find_all_by_instance_uuid.rb

Lines changed: 0 additions & 21 deletions
This file was deleted.

lib/fog/vsphere/requests/compute/find_all_by_uuid.rb

Lines changed: 0 additions & 22 deletions
This file was deleted.

lib/fog/vsphere/requests/compute/find_template_by_instance_uuid.rb

Lines changed: 0 additions & 27 deletions
This file was deleted.
Lines changed: 21 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -1,28 +1,41 @@
11
module Fog
22
module Compute
33
class Vsphere
4-
class Real
4+
5+
module Shared
56

67
# REVISIT: This is a naive implementation and not very efficient since
78
# we find ALL VM's and then iterate over them looking for the managed object
89
# reference id... There should be an easier way to obtain a reference to a
910
# VM using only the name or the _ref. This request is primarily intended to
1011
# reload the attributes of a cloning VM which does not yet have an instance_uuid
11-
def find_vm_by_ref(params = {})
12-
list_virtual_machines.detect(lambda { raise Fog::Vsphere::Errors::NotFound }) do |vm|
13-
vm._ref == params[:vm_ref]
12+
def find_vm_by_ref(options = {})
13+
raise ArgumentError, "Must pass a vm_ref option" unless options['vm_ref']
14+
15+
# This is the inefficient call
16+
all_vm_attributes = list_virtual_machines['virtual_machines']
17+
# Find the VM attributes of the reference
18+
if vm_attributes = all_vm_attributes.find { |vm| vm['mo_ref'] == options['vm_ref'] }
19+
response = { 'virtual_machine' => vm_attributes }
20+
else
21+
raise Fog::Compute::Vsphere::NotFound, "VirtualMachine with Managed Object Reference #{options['vm_ref']} could not be found."
1422
end
23+
response
1524
end
1625

1726
end
1827

19-
class Mock
28+
# The Real and Mock classes share the same method
29+
# because list_virtual_machines will be properly mocked for us
2030

21-
def find_vm_by_ref(params = {})
22-
Fog::Mock.not_implmented
23-
end
31+
class Real
32+
include Shared
33+
end
2434

35+
class Mock
36+
include Shared
2537
end
38+
2639
end
2740
end
2841
end

0 commit comments

Comments
 (0)