beaker-vmware 0.1.0 → 0.2.0
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +8 -8
- data/README.md +1 -1
- data/beaker-vmware.gemspec +1 -0
- data/lib/beaker-vmware/version.rb +1 -1
- data/lib/beaker/hypervisor/vsphere.rb +86 -0
- data/lib/beaker/hypervisor/vsphere_helper.rb +204 -0
- data/spec/beaker/hypervisor/vsphere_helper_spec.rb +163 -0
- data/spec/beaker/hypervisor/vsphere_spec.rb +90 -0
- data/vsphere.md +54 -0
- metadata +21 -2
checksums.yaml
CHANGED
@@ -1,15 +1,15 @@
|
|
1
1
|
---
|
2
2
|
!binary "U0hBMQ==":
|
3
3
|
metadata.gz: !binary |-
|
4
|
-
|
4
|
+
N2JhNTJkYmI3ZWI5YTAxNmM5MTExOWI3ZGM3NDA4NzBlMTI4OTEyYg==
|
5
5
|
data.tar.gz: !binary |-
|
6
|
-
|
6
|
+
ZGE5MGIxYTE3OTU4YmZhNTU1MjZiNjFmN2M2YzRlMjM1ODE2MWMxMw==
|
7
7
|
SHA512:
|
8
8
|
metadata.gz: !binary |-
|
9
|
-
|
10
|
-
|
11
|
-
|
9
|
+
NjZmMjFkMDQzZTZlZjQzYjhkYzNkNGY4MjM0NmZmN2Y3YjMzZDk2MjhmZGM4
|
10
|
+
MjQ4MzgzNTA2YjBjY2QzYzQ3ZjJmZTBiMjZmMDIwMDA4NmQzNTMyZmRlOGE2
|
11
|
+
YWVjOWFjZGFlZGI2ODVhNTgyYzdlNDkzZGQwZmYyYTExN2M4YmQ=
|
12
12
|
data.tar.gz: !binary |-
|
13
|
-
|
14
|
-
|
15
|
-
|
13
|
+
NDhiZWVhYmE1MDAxYjY0MDYzNzViYjY2NDk5ZDZmOTg1MjRiMDJkOTEyNGVk
|
14
|
+
YzQ2NDczYTAxM2QwNjQ0YjRkMWZmZTAxZDMzMmQwZjg5MGFjODI1OTdmYTUw
|
15
|
+
ODNlM2E1Y2Q2NjVkM2UzMzg0N2Y4MTUwY2Q3NzZlMDJkNzA4ZGI=
|
data/README.md
CHANGED
@@ -4,7 +4,7 @@ Beaker library to use vmware fusion hypervisor
|
|
4
4
|
|
5
5
|
# How to use this wizardry
|
6
6
|
|
7
|
-
This gem that allows you to use hosts with [vmware_fusion](vmware_fusion.md) hypervisor with [beaker](https://github.com/puppetlabs/beaker).
|
7
|
+
This gem that allows you to use hosts with [vmware_fusion](vmware_fusion.md) and [vsphere](vsphere.md) hypervisor with [beaker](https://github.com/puppetlabs/beaker).
|
8
8
|
|
9
9
|
### Right Now? (beaker 3.x)
|
10
10
|
|
data/beaker-vmware.gemspec
CHANGED
@@ -0,0 +1,86 @@
|
|
1
|
+
require 'yaml' unless defined?(YAML)
|
2
|
+
require 'beaker/hypervisor/vsphere_helper'
|
3
|
+
|
4
|
+
module Beaker
|
5
|
+
class Vsphere < Beaker::Hypervisor
|
6
|
+
|
7
|
+
def initialize(vsphere_hosts, options)
|
8
|
+
@options = options
|
9
|
+
@logger = options[:logger]
|
10
|
+
@hosts = vsphere_hosts
|
11
|
+
end
|
12
|
+
|
13
|
+
def provision
|
14
|
+
vsphere_credentials = VsphereHelper.load_config(@options[:dot_fog])
|
15
|
+
|
16
|
+
@logger.notify "Connecting to vSphere at #{vsphere_credentials[:server]}" +
|
17
|
+
" with credentials for #{vsphere_credentials[:user]}"
|
18
|
+
|
19
|
+
vsphere_helper = VsphereHelper.new( vsphere_credentials )
|
20
|
+
|
21
|
+
vsphere_vms = {}
|
22
|
+
@hosts.each do |h|
|
23
|
+
name = h["vmname"] || h.name
|
24
|
+
vsphere_vms[name] = h["snapshot"]
|
25
|
+
end
|
26
|
+
vms = vsphere_helper.find_vms(vsphere_vms.keys)
|
27
|
+
vsphere_vms.each_pair do |name, snap|
|
28
|
+
unless vm = vms[name]
|
29
|
+
raise "Couldn't find VM #{name} in vSphere!"
|
30
|
+
end
|
31
|
+
|
32
|
+
if snap
|
33
|
+
snapshot = vsphere_helper.find_snapshot(vm, snap) or
|
34
|
+
raise "Could not find snapshot '#{snap}' for VM #{vm.name}!"
|
35
|
+
|
36
|
+
@logger.notify "Reverting #{vm.name} to snapshot '#{snap}'"
|
37
|
+
start = Time.now
|
38
|
+
# This will block for each snapshot...
|
39
|
+
# The code to issue them all and then wait until they are all done sucks
|
40
|
+
snapshot.RevertToSnapshot_Task.wait_for_completion
|
41
|
+
|
42
|
+
time = Time.now - start
|
43
|
+
@logger.notify "Spent %.2f seconds reverting" % time
|
44
|
+
end
|
45
|
+
|
46
|
+
unless vm.runtime.powerState == "poweredOn"
|
47
|
+
@logger.notify "Booting #{vm.name}"
|
48
|
+
start = Time.now
|
49
|
+
vm.PowerOnVM_Task.wait_for_completion
|
50
|
+
@logger.notify "Spent %.2f seconds booting #{vm.name}" % (Time.now - start)
|
51
|
+
end
|
52
|
+
end
|
53
|
+
|
54
|
+
vsphere_helper.close
|
55
|
+
end
|
56
|
+
|
57
|
+
def cleanup
|
58
|
+
@logger.notify "Destroying vsphere boxes"
|
59
|
+
vsphere_credentials = VsphereHelper.load_config(@options[:dot_fog])
|
60
|
+
|
61
|
+
@logger.notify "Connecting to vSphere at #{vsphere_credentials[:server]}" +
|
62
|
+
" with credentials for #{vsphere_credentials[:user]}"
|
63
|
+
|
64
|
+
vsphere_helper = VsphereHelper.new( vsphere_credentials )
|
65
|
+
|
66
|
+
vm_names = @hosts.map {|h| h['vmname'] || h.name }
|
67
|
+
vms = vsphere_helper.find_vms vm_names
|
68
|
+
vm_names.each do |name|
|
69
|
+
unless vm = vms[name]
|
70
|
+
raise "Couldn't find VM #{name} in vSphere!"
|
71
|
+
end
|
72
|
+
|
73
|
+
if vm.runtime.powerState == "poweredOn"
|
74
|
+
@logger.notify "Shutting down #{vm.name}"
|
75
|
+
start = Time.now
|
76
|
+
vm.PowerOffVM_Task.wait_for_completion
|
77
|
+
@logger.notify(
|
78
|
+
"Spent %.2f seconds halting #{vm.name}" % (Time.now - start) )
|
79
|
+
end
|
80
|
+
end
|
81
|
+
|
82
|
+
vsphere_helper.close
|
83
|
+
end
|
84
|
+
|
85
|
+
end
|
86
|
+
end
|
@@ -0,0 +1,204 @@
|
|
1
|
+
require 'yaml' unless defined?(YAML)
|
2
|
+
require 'rbvmomi'
|
3
|
+
require 'beaker/logger'
|
4
|
+
|
5
|
+
class VsphereHelper
|
6
|
+
def initialize vInfo
|
7
|
+
@logger = vInfo[:logger] || Beaker::Logger.new
|
8
|
+
@connection = RbVmomi::VIM.connect :host => vInfo[:server],
|
9
|
+
:user => vInfo[:user],
|
10
|
+
:password => vInfo[:pass],
|
11
|
+
:insecure => true
|
12
|
+
end
|
13
|
+
|
14
|
+
def self.load_config(dot_fog = '.fog')
|
15
|
+
# support Fog/Cloud Provisioner layout
|
16
|
+
# (ie, someplace besides my made up conf)
|
17
|
+
vsphere_credentials = nil
|
18
|
+
if File.exists?( dot_fog )
|
19
|
+
vsphere_credentials = load_fog_credentials(dot_fog)
|
20
|
+
else
|
21
|
+
raise ArgumentError, ".fog file '#{dot_fog}' does not exist"
|
22
|
+
end
|
23
|
+
|
24
|
+
return vsphere_credentials
|
25
|
+
end
|
26
|
+
|
27
|
+
def self.load_fog_credentials(dot_fog = '.fog')
|
28
|
+
vInfo = YAML.load_file( dot_fog )
|
29
|
+
|
30
|
+
vsphere_credentials = {}
|
31
|
+
vsphere_credentials[:server] = vInfo[:default][:vsphere_server]
|
32
|
+
vsphere_credentials[:user] = vInfo[:default][:vsphere_username]
|
33
|
+
vsphere_credentials[:pass] = vInfo[:default][:vsphere_password]
|
34
|
+
|
35
|
+
return vsphere_credentials
|
36
|
+
end
|
37
|
+
|
38
|
+
def find_snapshot vm, snapname
|
39
|
+
if vm.snapshot
|
40
|
+
search_child_snaps vm.snapshot.rootSnapshotList, snapname
|
41
|
+
else
|
42
|
+
raise "vm #{vm.name} has no snapshots to revert to"
|
43
|
+
end
|
44
|
+
end
|
45
|
+
|
46
|
+
def search_child_snaps tree, snapname
|
47
|
+
snapshot = nil
|
48
|
+
tree.each do |child|
|
49
|
+
if child.name == snapname
|
50
|
+
snapshot ||= child.snapshot
|
51
|
+
else
|
52
|
+
snapshot ||= search_child_snaps child.childSnapshotList, snapname
|
53
|
+
end
|
54
|
+
end
|
55
|
+
snapshot
|
56
|
+
end
|
57
|
+
|
58
|
+
def find_customization name
|
59
|
+
csm = @connection.serviceContent.customizationSpecManager
|
60
|
+
|
61
|
+
begin
|
62
|
+
customizationSpec = csm.GetCustomizationSpec({:name => name}).spec
|
63
|
+
rescue
|
64
|
+
customizationSpec = nil
|
65
|
+
end
|
66
|
+
|
67
|
+
return customizationSpec
|
68
|
+
end
|
69
|
+
|
70
|
+
# an easier wrapper around the horrid PropertyCollector interface,
|
71
|
+
# necessary for searching VMs in all Datacenters that may be nested
|
72
|
+
# within folders of arbitrary depth
|
73
|
+
# returns a hash array of <name> => <VirtualMachine ManagedObjects>
|
74
|
+
def find_vms names, connection = @connection
|
75
|
+
names = names.is_a?(Array) ? names : [ names ]
|
76
|
+
containerView = get_base_vm_container_from connection
|
77
|
+
propertyCollector = connection.propertyCollector
|
78
|
+
|
79
|
+
objectSet = [{
|
80
|
+
:obj => containerView,
|
81
|
+
:skip => true,
|
82
|
+
:selectSet => [ RbVmomi::VIM::TraversalSpec.new({
|
83
|
+
:name => 'gettingTheVMs',
|
84
|
+
:path => 'view',
|
85
|
+
:skip => false,
|
86
|
+
:type => 'ContainerView'
|
87
|
+
}) ]
|
88
|
+
}]
|
89
|
+
|
90
|
+
propSet = [{
|
91
|
+
:pathSet => [ 'name' ],
|
92
|
+
:type => 'VirtualMachine'
|
93
|
+
}]
|
94
|
+
|
95
|
+
results = propertyCollector.RetrievePropertiesEx({
|
96
|
+
:specSet => [{
|
97
|
+
:objectSet => objectSet,
|
98
|
+
:propSet => propSet
|
99
|
+
}],
|
100
|
+
:options => { :maxObjects => nil }
|
101
|
+
})
|
102
|
+
|
103
|
+
vms = {}
|
104
|
+
results.objects.each do |result|
|
105
|
+
name = result.propSet.first.val
|
106
|
+
next unless names.include? name
|
107
|
+
vms[name] = result.obj
|
108
|
+
end
|
109
|
+
|
110
|
+
while results.token do
|
111
|
+
results = propertyCollector.ContinueRetrievePropertiesEx({:token => results.token})
|
112
|
+
results.objects.each do |result|
|
113
|
+
name = result.propSet.first.val
|
114
|
+
next unless names.include? name
|
115
|
+
vms[name] = result.obj
|
116
|
+
end
|
117
|
+
end
|
118
|
+
vms
|
119
|
+
end
|
120
|
+
|
121
|
+
def find_datastore(dc,datastorename)
|
122
|
+
datacenter = @connection.serviceInstance.find_datacenter(dc)
|
123
|
+
datacenter.find_datastore(datastorename)
|
124
|
+
end
|
125
|
+
|
126
|
+
def find_folder(dc,foldername)
|
127
|
+
datacenter = @connection.serviceInstance.find_datacenter(dc)
|
128
|
+
base = datacenter.vmFolder.traverse(foldername)
|
129
|
+
if base != nil
|
130
|
+
base
|
131
|
+
else
|
132
|
+
abort "Failed to find folder #{foldername}"
|
133
|
+
end
|
134
|
+
end
|
135
|
+
|
136
|
+
def find_pool(dc,poolname)
|
137
|
+
datacenter = @connection.serviceInstance.find_datacenter(dc)
|
138
|
+
base = datacenter.hostFolder
|
139
|
+
pools = poolname.split('/')
|
140
|
+
pools.each do |pool|
|
141
|
+
case base
|
142
|
+
when RbVmomi::VIM::Folder
|
143
|
+
base = base.childEntity.find { |f| f.name == pool }
|
144
|
+
when RbVmomi::VIM::ClusterComputeResource
|
145
|
+
base = base.resourcePool.resourcePool.find { |f| f.name == pool }
|
146
|
+
when RbVmomi::VIM::ResourcePool
|
147
|
+
base = base.resourcePool.find { |f| f.name == pool }
|
148
|
+
else
|
149
|
+
abort "Unexpected object type encountered (#{base.class}) while finding resource pool"
|
150
|
+
end
|
151
|
+
end
|
152
|
+
|
153
|
+
base = base.resourcePool unless base.is_a?(RbVmomi::VIM::ResourcePool) and base.respond_to?(:resourcePool)
|
154
|
+
base
|
155
|
+
end
|
156
|
+
|
157
|
+
def get_base_vm_container_from connection
|
158
|
+
viewManager = connection.serviceContent.viewManager
|
159
|
+
viewManager.CreateContainerView({
|
160
|
+
:container => connection.serviceContent.rootFolder,
|
161
|
+
:recursive => true,
|
162
|
+
:type => [ 'VirtualMachine' ]
|
163
|
+
})
|
164
|
+
end
|
165
|
+
|
166
|
+
def wait_for_tasks tasks, try, attempts
|
167
|
+
obj_set = tasks.map { |task| { :obj => task } }
|
168
|
+
filter = @connection.propertyCollector.CreateFilter(
|
169
|
+
:spec => {
|
170
|
+
:propSet => [{ :type => 'Task',
|
171
|
+
:all => false,
|
172
|
+
:pathSet => ['info.state']}],
|
173
|
+
:objectSet => obj_set
|
174
|
+
},
|
175
|
+
:partialUpdates => false
|
176
|
+
)
|
177
|
+
ver = ''
|
178
|
+
while true
|
179
|
+
result = @connection.propertyCollector.WaitForUpdates(:version => ver)
|
180
|
+
ver = result.version
|
181
|
+
complete = 0
|
182
|
+
tasks.each do |task|
|
183
|
+
if ['success', 'error'].member? task.info.state
|
184
|
+
complete += 1
|
185
|
+
end
|
186
|
+
end
|
187
|
+
break if (complete == tasks.length)
|
188
|
+
if try <= attempts
|
189
|
+
sleep 5
|
190
|
+
try += 1
|
191
|
+
else
|
192
|
+
raise "unable to complete Vsphere tasks before timeout"
|
193
|
+
end
|
194
|
+
end
|
195
|
+
|
196
|
+
filter.DestroyPropertyFilter
|
197
|
+
tasks
|
198
|
+
end
|
199
|
+
|
200
|
+
def close
|
201
|
+
@connection.close
|
202
|
+
end
|
203
|
+
end
|
204
|
+
|
@@ -0,0 +1,163 @@
|
|
1
|
+
require 'spec_helper'
|
2
|
+
|
3
|
+
module Beaker
|
4
|
+
describe VsphereHelper do
|
5
|
+
let( :logger ) { double('logger').as_null_object }
|
6
|
+
let( :vInfo ) { { :server => "vsphere.labs.net", :user => "vsphere@labs.com", :pass => "supersekritpassword" } }
|
7
|
+
let( :vsphere_helper ) { VsphereHelper.new ( vInfo.merge( { :logger => logger } ) ) }
|
8
|
+
let( :snaplist ) { { 'snap1' => { 'snap1sub1' => nil ,
|
9
|
+
'snap1sub2' => nil },
|
10
|
+
'snap2' => nil,
|
11
|
+
'snap3' => { 'snap3sub1' => nil ,
|
12
|
+
'snap3sub2' => nil ,
|
13
|
+
'snap3sub3' => nil } } }
|
14
|
+
let( :vms ) { [ MockRbVmomiVM.new( 'mockvm1', snaplist ),
|
15
|
+
MockRbVmomiVM.new( 'mockvm2', snaplist ),
|
16
|
+
MockRbVmomiVM.new( 'mockvm3', snaplist ) ] }
|
17
|
+
|
18
|
+
before :each do
|
19
|
+
stub_const( "RbVmomi", MockRbVmomi )
|
20
|
+
end
|
21
|
+
|
22
|
+
describe "#load_config" do
|
23
|
+
|
24
|
+
it 'can load a .fog file' do
|
25
|
+
allow( File ).to receive( :exists? ).and_return( true )
|
26
|
+
allow( YAML ).to receive( :load_file ).and_return( fog_file_contents )
|
27
|
+
|
28
|
+
expect( VsphereHelper.load_config ).to be === vInfo
|
29
|
+
|
30
|
+
end
|
31
|
+
|
32
|
+
it 'raises an error when the .fog file is missing' do
|
33
|
+
allow( File ).to receive( :exists? ).and_return( false )
|
34
|
+
|
35
|
+
expect{ VsphereHelper.load_config }.to raise_error( ArgumentError )
|
36
|
+
|
37
|
+
end
|
38
|
+
|
39
|
+
end
|
40
|
+
|
41
|
+
describe "#find_snapshot" do
|
42
|
+
it 'can find a given snapshot name' do
|
43
|
+
mockvm = MockRbVmomiVM.new( 'mockvm', snaplist )
|
44
|
+
|
45
|
+
expect( vsphere_helper.find_snapshot( mockvm, 'snap2' ) ).to be === mockvm.get_snapshot( 'snap2' )
|
46
|
+
|
47
|
+
end
|
48
|
+
|
49
|
+
end
|
50
|
+
|
51
|
+
describe "#find_customization" do
|
52
|
+
it 'returns the customization spec' do
|
53
|
+
|
54
|
+
expect( vsphere_helper.find_customization( 'name' ) ).to be === true
|
55
|
+
|
56
|
+
end
|
57
|
+
|
58
|
+
end
|
59
|
+
|
60
|
+
describe "#find_vms" do
|
61
|
+
it 'finds the list of vms' do
|
62
|
+
connection = vsphere_helper.instance_variable_get( :@connection )
|
63
|
+
connection.set_info( vms )
|
64
|
+
|
65
|
+
expect( vsphere_helper.find_vms( 'mockvm1' ) ).to be === {vms[0].name => vms[0]}
|
66
|
+
end
|
67
|
+
|
68
|
+
it 'returns {} when no vm is found' do
|
69
|
+
connection = vsphere_helper.instance_variable_get( :@connection )
|
70
|
+
connection.set_info( vms )
|
71
|
+
|
72
|
+
expect( vsphere_helper.find_vms( 'novm' ) ).to be === {}
|
73
|
+
end
|
74
|
+
|
75
|
+
end
|
76
|
+
|
77
|
+
describe "#find_datastore" do
|
78
|
+
it 'finds the datastore from the connection object' do
|
79
|
+
connection = vsphere_helper.instance_variable_get( :@connection )
|
80
|
+
dc = connection.serviceInstance.find_datacenter('testdc')
|
81
|
+
expect(vsphere_helper.find_datastore( dc,'datastorename' ) ).to be === true
|
82
|
+
end
|
83
|
+
|
84
|
+
end
|
85
|
+
|
86
|
+
describe "#find_folder" do
|
87
|
+
it 'can find a folder in the datacenter' do
|
88
|
+
connection = vsphere_helper.instance_variable_get( :@connection )
|
89
|
+
expect(vsphere_helper.find_folder( 'testdc','root' ) ).to be === connection.serviceInstance.find_datacenter('testdc').vmFolder
|
90
|
+
end
|
91
|
+
|
92
|
+
end
|
93
|
+
|
94
|
+
describe "#find_pool" do
|
95
|
+
it 'can find a pool in a folder in the datacenter' do
|
96
|
+
connection = vsphere_helper.instance_variable_get( :@connection )
|
97
|
+
dc = connection.serviceInstance.find_datacenter('testdc')
|
98
|
+
dc.hostFolder = MockRbVmomi::VIM::Folder.new
|
99
|
+
dc.hostFolder.name = "/root"
|
100
|
+
|
101
|
+
expect(vsphere_helper.find_pool( 'testdc','root' ) ).to be === connection.serviceInstance.find_datacenter('testdc').hostFolder
|
102
|
+
|
103
|
+
end
|
104
|
+
it 'can find a pool in a clustercomputeresource in the datacenter' do
|
105
|
+
connection = vsphere_helper.instance_variable_get( :@connection )
|
106
|
+
dc = connection.serviceInstance.find_datacenter('testdc')
|
107
|
+
dc.hostFolder = MockRbVmomi::VIM::ClusterComputeResource.new
|
108
|
+
dc.hostFolder.name = "/root"
|
109
|
+
|
110
|
+
expect(vsphere_helper.find_pool( 'testdc','root' ) ).to be === connection.serviceInstance.find_datacenter('testdc').hostFolder
|
111
|
+
end
|
112
|
+
it 'can find a pool in a resourcepool in the datacenter' do
|
113
|
+
connection = vsphere_helper.instance_variable_get( :@connection )
|
114
|
+
dc = connection.serviceInstance.find_datacenter('testdc')
|
115
|
+
dc.hostFolder = MockRbVmomi::VIM::ResourcePool.new
|
116
|
+
dc.hostFolder.name = "/root"
|
117
|
+
|
118
|
+
expect(vsphere_helper.find_pool( 'testdc','root' ) ).to be === connection.serviceInstance.find_datacenter('testdc').hostFolder
|
119
|
+
end
|
120
|
+
|
121
|
+
end
|
122
|
+
|
123
|
+
describe "#wait_for_tasks" do
|
124
|
+
it "can wait for tasks to error" do
|
125
|
+
allow( vsphere_helper ).to receive( :sleep ).and_return( true )
|
126
|
+
vms.each do |vm|
|
127
|
+
vm.info.state = 'error'
|
128
|
+
end
|
129
|
+
|
130
|
+
expect(vsphere_helper.wait_for_tasks( vms, 0, 5 ) ).to be === vms
|
131
|
+
end
|
132
|
+
|
133
|
+
it "can wait for tasks to succeed" do
|
134
|
+
allow( vsphere_helper ).to receive( :sleep ).and_return( true )
|
135
|
+
vms.each do |vm|
|
136
|
+
vm.info.state = 'success'
|
137
|
+
end
|
138
|
+
|
139
|
+
expect(vsphere_helper.wait_for_tasks( vms, 0, 5 ) ).to be === vms
|
140
|
+
end
|
141
|
+
|
142
|
+
it "errors when tasks fail to error/success before timing out" do
|
143
|
+
allow( vsphere_helper ).to receive( :sleep ).and_return( true )
|
144
|
+
vms.each do |vm|
|
145
|
+
vm.info.state = 'nope'
|
146
|
+
end
|
147
|
+
|
148
|
+
expect{ vsphere_helper.wait_for_tasks( vms, 0, 5 ) }.to raise_error
|
149
|
+
end
|
150
|
+
|
151
|
+
end
|
152
|
+
|
153
|
+
describe "#close" do
|
154
|
+
it 'closes the connection' do
|
155
|
+
connection = vsphere_helper.instance_variable_get( :@connection )
|
156
|
+
expect( connection ).to receive( :close ).once
|
157
|
+
|
158
|
+
vsphere_helper.close
|
159
|
+
end
|
160
|
+
end
|
161
|
+
|
162
|
+
end
|
163
|
+
end
|
@@ -0,0 +1,90 @@
|
|
1
|
+
require 'spec_helper'
|
2
|
+
|
3
|
+
module Beaker
|
4
|
+
describe Vsphere do
|
5
|
+
|
6
|
+
before :each do
|
7
|
+
MockVsphereHelper.set_config( fog_file_contents )
|
8
|
+
MockVsphereHelper.set_vms( make_hosts() )
|
9
|
+
stub_const( "VsphereHelper", MockVsphereHelper )
|
10
|
+
end
|
11
|
+
|
12
|
+
describe "#provision" do
|
13
|
+
|
14
|
+
it 'provisions hosts' do
|
15
|
+
MockVsphereHelper.powerOff
|
16
|
+
vsphere = Beaker::Vsphere.new( make_hosts(), make_opts )
|
17
|
+
|
18
|
+
vsphere.provision
|
19
|
+
|
20
|
+
hosts = vsphere.instance_variable_get( :@hosts )
|
21
|
+
hosts.each do |host|
|
22
|
+
expect( MockVsphereHelper.find_vm( host.name ).powerState ) == "poweredOn"
|
23
|
+
end
|
24
|
+
|
25
|
+
end
|
26
|
+
|
27
|
+
it 'raises an error if a vm is missing in Vsphere' do
|
28
|
+
MockVsphereHelper.powerOff
|
29
|
+
hosts = make_hosts()
|
30
|
+
hosts[0][:vmname] = 'unknown'
|
31
|
+
vsphere = Beaker::Vsphere.new( hosts, make_opts )
|
32
|
+
|
33
|
+
expect{ vsphere.provision }.to raise_error
|
34
|
+
|
35
|
+
end
|
36
|
+
|
37
|
+
it 'raises an error if a vm does not have a given snapshot name' do
|
38
|
+
MockVsphereHelper.powerOff
|
39
|
+
hosts = make_hosts()
|
40
|
+
hosts[0]["snapshot"] = 'unknown'
|
41
|
+
vsphere = Beaker::Vsphere.new( hosts, make_opts )
|
42
|
+
|
43
|
+
expect{ vsphere.provision }.to raise_error
|
44
|
+
|
45
|
+
end
|
46
|
+
|
47
|
+
it 'provisions hosts if no snapshot is provided' do
|
48
|
+
MockVsphereHelper.powerOff
|
49
|
+
hosts = make_hosts()
|
50
|
+
hosts[0]["snapshot"] = nil
|
51
|
+
vsphere = Beaker::Vsphere.new( hosts, make_opts )
|
52
|
+
|
53
|
+
vsphere.provision
|
54
|
+
|
55
|
+
hosts.each do |host|
|
56
|
+
expect( MockVsphereHelper.find_vm( host.name ).powerState ) == "poweredOn"
|
57
|
+
end
|
58
|
+
|
59
|
+
end
|
60
|
+
|
61
|
+
end
|
62
|
+
|
63
|
+
describe "#cleanup" do
|
64
|
+
|
65
|
+
it "cleans up" do
|
66
|
+
MockVsphereHelper.powerOn
|
67
|
+
vsphere = Beaker::Vsphere.new( make_hosts(), make_opts )
|
68
|
+
vsphere.cleanup
|
69
|
+
|
70
|
+
hosts = vsphere.instance_variable_get( :@hosts )
|
71
|
+
hosts.each do |host|
|
72
|
+
expect( MockVsphereHelper.find_vm( host.name ).powerState ) == "poweredOff"
|
73
|
+
end
|
74
|
+
end
|
75
|
+
|
76
|
+
it 'raises an error if a vm is missing in Vsphere' do
|
77
|
+
MockVsphereHelper.powerOn
|
78
|
+
hosts = make_hosts()
|
79
|
+
hosts[0][:vmname] = 'unknown'
|
80
|
+
vsphere = Beaker::Vsphere.new( hosts, make_opts )
|
81
|
+
|
82
|
+
expect{ vsphere.cleanup }.to raise_error
|
83
|
+
|
84
|
+
end
|
85
|
+
|
86
|
+
end
|
87
|
+
|
88
|
+
end
|
89
|
+
|
90
|
+
end
|
data/vsphere.md
ADDED
@@ -0,0 +1,54 @@
|
|
1
|
+
This doc describes beaker's vSphere hypervisor. This is the interaction layer
|
2
|
+
that beaker will use to get Systems Under Test (SUTs) from any vSphere
|
3
|
+
infrastructure that you might have.
|
4
|
+
|
5
|
+
**Note** that if you're a puppet-internal user, or an external user that is
|
6
|
+
using the vmpooler hypervisor, please refer to our [vmpooler doc](https://github.com/puppetlabs/beaker-vmpooler)
|
7
|
+
for info, as it can be different than the information here.
|
8
|
+
|
9
|
+
The harness can use vms and snapshots that live within vSphere as well.
|
10
|
+
To do this create a `~/.fog` file with your vSphere credentials:
|
11
|
+
|
12
|
+
### example .fog file ###
|
13
|
+
|
14
|
+
:default:
|
15
|
+
:vsphere_server: 'vsphere.example.com'
|
16
|
+
:vsphere_username: 'joe'
|
17
|
+
:vsphere_password: 'MyP@$$w0rd'
|
18
|
+
|
19
|
+
These follow the conventions used by Cloud Provisioner and Fog.
|
20
|
+
|
21
|
+
>Note: Your fog credential file location may be specified in the 'CONFIG' section using the 'dot_fog' setting
|
22
|
+
|
23
|
+
There are two possible `hypervisor` hypervisor-types to use for vSphere testing, `vsphere` and `vcloud`.
|
24
|
+
|
25
|
+
### `hypervisor: vsphere`
|
26
|
+
This option locates an existing static VM, optionally reverts it to a pre-existing snapshot, and runs tests on it.
|
27
|
+
|
28
|
+
### `hypervisor: vcloud`
|
29
|
+
This option clones a new VM from a pre-existing template, runs tests on the newly-provisioned clone, then deletes the clone once testing completes.
|
30
|
+
|
31
|
+
The `vcloud` option requires a slightly-modified test configuration file, specifying both the target template as well as three additional parameters in the 'CONFIG' section ('datastore', 'datacenter', and 'folder'). Optionally, a resourcepool may be specified via the 'resourcepool' setting in the 'CONFIG' section. Template can be expressed in the 'HOSTS' section, or you can set the template to be used via the `BEAKER_vcloud_template` environment variable.
|
32
|
+
|
33
|
+
#### example vcloud hosts file ###
|
34
|
+
HOSTS:
|
35
|
+
master-vm:
|
36
|
+
roles:
|
37
|
+
- master
|
38
|
+
- agent
|
39
|
+
- dashboard
|
40
|
+
platform: ubuntu-10.04-amd64
|
41
|
+
template: ubuntu-1004-x86_64
|
42
|
+
hypervisor: vcloud
|
43
|
+
agent-vm:
|
44
|
+
roles:
|
45
|
+
- agent
|
46
|
+
platform: ubuntu-10.04-i386
|
47
|
+
template: ubuntu-1004-i386
|
48
|
+
hypervisor: vcloud
|
49
|
+
CONFIG:
|
50
|
+
consoleport: 443
|
51
|
+
datacenter: testdc
|
52
|
+
datastore: instance0
|
53
|
+
resourcepool: Delivery/Quality Assurance/FOSS/Dynamic
|
54
|
+
folder: delivery/Quality Assurance/FOSS/Dynamic
|
metadata
CHANGED
@@ -1,14 +1,14 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: beaker-vmware
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 0.
|
4
|
+
version: 0.2.0
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Rishi Javia, Kevin Imber, Tony Vu
|
8
8
|
autorequire:
|
9
9
|
bindir: bin
|
10
10
|
cert_chain: []
|
11
|
-
date: 2017-07-
|
11
|
+
date: 2017-07-19 00:00:00.000000000 Z
|
12
12
|
dependencies:
|
13
13
|
- !ruby/object:Gem::Dependency
|
14
14
|
name: rspec
|
@@ -164,6 +164,20 @@ dependencies:
|
|
164
164
|
- - ~>
|
165
165
|
- !ruby/object:Gem::Version
|
166
166
|
version: '0.4'
|
167
|
+
- !ruby/object:Gem::Dependency
|
168
|
+
name: rbvmomi
|
169
|
+
requirement: !ruby/object:Gem::Requirement
|
170
|
+
requirements:
|
171
|
+
- - ~>
|
172
|
+
- !ruby/object:Gem::Version
|
173
|
+
version: '1.9'
|
174
|
+
type: :runtime
|
175
|
+
prerelease: false
|
176
|
+
version_requirements: !ruby/object:Gem::Requirement
|
177
|
+
requirements:
|
178
|
+
- - ~>
|
179
|
+
- !ruby/object:Gem::Version
|
180
|
+
version: '1.9'
|
167
181
|
description: For use for the Beaker acceptance testing tool
|
168
182
|
email:
|
169
183
|
- rishi.javia@puppet.com, kevin.imber@puppet.com, tony.vu@puppet.com
|
@@ -183,9 +197,14 @@ files:
|
|
183
197
|
- bin/beaker-vmware
|
184
198
|
- lib/beaker-vmware/version.rb
|
185
199
|
- lib/beaker/hypervisor/fusion.rb
|
200
|
+
- lib/beaker/hypervisor/vsphere.rb
|
201
|
+
- lib/beaker/hypervisor/vsphere_helper.rb
|
186
202
|
- spec/beaker/hypervisor/fusion_spec.rb
|
203
|
+
- spec/beaker/hypervisor/vsphere_helper_spec.rb
|
204
|
+
- spec/beaker/hypervisor/vsphere_spec.rb
|
187
205
|
- spec/spec_helper.rb
|
188
206
|
- vmware_fusion.md
|
207
|
+
- vsphere.md
|
189
208
|
homepage: https://github.com/puppetlabs/beaker-vmware
|
190
209
|
licenses:
|
191
210
|
- Apache2
|