chef-provisioning-vsphere 0.8.3.dev.2 → 0.8.3
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- checksums.yaml +4 -4
- data/.gitignore +2 -2
- data/Gemfile +5 -5
- data/LICENSE +19 -19
- data/LICENSE-Rally +20 -20
- data/README.md +269 -269
- data/Rakefile +22 -22
- data/chef-provisioning-vsphere.gemspec +28 -28
- data/contribution-notice +7 -7
- data/lib/chef/provisioning/driver_init/vsphere.rb +2 -2
- data/lib/chef/provisioning/vsphere_driver.rb +14 -14
- data/lib/chef/provisioning/vsphere_driver/clone_spec_builder.rb +205 -205
- data/lib/chef/provisioning/vsphere_driver/driver.rb +679 -679
- data/lib/chef/provisioning/vsphere_driver/version.rb +3 -3
- data/lib/chef/provisioning/vsphere_driver/vsphere_helpers.rb +341 -341
- data/lib/chef/provisioning/vsphere_driver/vsphere_url.rb +45 -45
- data/lib/kitchen/driver/vsphere.rb +104 -104
- data/spec/integration_tests/.gitignore +1 -1
- data/spec/integration_tests/vsphere_driver_spec.rb +158 -158
- data/spec/unit_tests/VsphereDriver_spec.rb +132 -132
- data/spec/unit_tests/VsphereUrl_spec.rb +65 -65
- data/spec/unit_tests/clone_spec_builder_spec.rb +161 -161
- data/spec/unit_tests/support/fake_action_handler.rb +7 -7
- data/spec/unit_tests/support/vsphere_helper_stub.rb +52 -52
- metadata +4 -4
@@ -1,3 +1,3 @@
|
|
1
|
-
module ChefProvisioningVsphere
|
2
|
-
VERSION = '0.8.3
|
3
|
-
end
|
1
|
+
module ChefProvisioningVsphere
|
2
|
+
VERSION = '0.8.3'
|
3
|
+
end
|
@@ -1,341 +1,341 @@
|
|
1
|
-
require 'rbvmomi'
|
2
|
-
|
3
|
-
module ChefProvisioningVsphere
|
4
|
-
class VsphereHelper
|
5
|
-
|
6
|
-
if !$guest_op_managers
|
7
|
-
$guest_op_managers = {}
|
8
|
-
end
|
9
|
-
|
10
|
-
def initialize(connect_options, datacenter_name)
|
11
|
-
@connect_options = connect_options
|
12
|
-
@datacenter_name = datacenter_name
|
13
|
-
end
|
14
|
-
|
15
|
-
attr_reader :connect_options
|
16
|
-
attr_reader :datacenter_name
|
17
|
-
|
18
|
-
def vim
|
19
|
-
if @current_connection.nil? or @current_connection.serviceContent.sessionManager.currentSession.nil?
|
20
|
-
puts "establishing connection to #{connect_options[:host]}"
|
21
|
-
@current_connection = RbVmomi::VIM.connect connect_options
|
22
|
-
str_conn = @current_connection.pretty_inspect # a string in the format of VIM(host ip)
|
23
|
-
|
24
|
-
# we are caching guest operation managers in a global variable...terrible i know
|
25
|
-
# this object is available from the serviceContent object on API version 5 forward
|
26
|
-
# Its a singleton and if another connection is made for the same host and user
|
27
|
-
# that object is not available on any subsequent connection
|
28
|
-
# I could find no documentation that discusses this
|
29
|
-
if !$guest_op_managers.has_key?(str_conn)
|
30
|
-
$guest_op_managers[str_conn] = @current_connection.serviceContent.guestOperationsManager
|
31
|
-
end
|
32
|
-
end
|
33
|
-
|
34
|
-
@current_connection
|
35
|
-
end
|
36
|
-
|
37
|
-
def find_vm(folder, vm_name)
|
38
|
-
folder = find_folder(folder) unless folder.is_a? RbVmomi::VIM::Folder
|
39
|
-
folder.find(vm_name, RbVmomi::VIM::VirtualMachine)
|
40
|
-
end
|
41
|
-
|
42
|
-
def find_vm_by_id(uuid)
|
43
|
-
vm = vim.searchIndex.FindByUuid(
|
44
|
-
uuid: uuid,
|
45
|
-
vmSearch: true,
|
46
|
-
instanceUuid: true
|
47
|
-
)
|
48
|
-
end
|
49
|
-
|
50
|
-
def start_vm(vm, wait_on_port = 22)
|
51
|
-
state = vm.runtime.powerState
|
52
|
-
unless state == 'poweredOn'
|
53
|
-
vm.PowerOnVM_Task.wait_for_completion
|
54
|
-
end
|
55
|
-
end
|
56
|
-
|
57
|
-
def stop_vm(vm, timeout = 600)
|
58
|
-
start = Time.now.utc
|
59
|
-
begin
|
60
|
-
vm.ShutdownGuest
|
61
|
-
until (Time.now.utc - start) > timeout ||
|
62
|
-
vm.runtime.powerState == 'poweredOff' do
|
63
|
-
print '.'
|
64
|
-
sleep 2
|
65
|
-
end
|
66
|
-
rescue
|
67
|
-
vm.PowerOffVM_Task.wait_for_completion
|
68
|
-
end
|
69
|
-
end
|
70
|
-
|
71
|
-
#folder could be like: /Level1/Level2/folder_name
|
72
|
-
def find_folder(folder_name)
|
73
|
-
base = datacenter.vmFolder
|
74
|
-
unless folder_name.nil?
|
75
|
-
folder_name.split('/').reject(&:empty?).each do |item|
|
76
|
-
base = base.find(item, RbVmomi::VIM::Folder) ||
|
77
|
-
raise("vSphere Folder not found [#{folder_name}]")
|
78
|
-
end
|
79
|
-
end
|
80
|
-
base
|
81
|
-
end
|
82
|
-
|
83
|
-
def datacenter
|
84
|
-
@datacenter ||= vim.serviceInstance.find_datacenter(datacenter_name) ||
|
85
|
-
raise("vSphere Datacenter not found [#{datacenter_name}]")
|
86
|
-
end
|
87
|
-
|
88
|
-
def network_adapter_for(operation, network_name, network_label, device_key, backing_info)
|
89
|
-
connectable = RbVmomi::VIM::VirtualDeviceConnectInfo(
|
90
|
-
:allowGuestControl => true,
|
91
|
-
:connected => true,
|
92
|
-
:startConnected => true)
|
93
|
-
device = RbVmomi::VIM::VirtualVmxnet3(
|
94
|
-
:backing => backing_info,
|
95
|
-
:deviceInfo => RbVmomi::VIM::Description(:label => network_label, :summary => network_name.split('/').last),
|
96
|
-
:key => device_key,
|
97
|
-
:connectable => connectable)
|
98
|
-
RbVmomi::VIM::VirtualDeviceConfigSpec(
|
99
|
-
:operation => operation,
|
100
|
-
:device => device)
|
101
|
-
end
|
102
|
-
|
103
|
-
def find_ethernet_cards_for(vm)
|
104
|
-
vm.config.hardware.device.select {|d| d.is_a?(RbVmomi::VIM::VirtualEthernetCard)}
|
105
|
-
end
|
106
|
-
|
107
|
-
def add_extra_nic(action_handler, vm_template, options, vm)
|
108
|
-
deviceAdditions, changes = network_device_changes(action_handler, vm_template, options)
|
109
|
-
|
110
|
-
if deviceAdditions.count > 0
|
111
|
-
current_networks = find_ethernet_cards_for(vm).map{|card| network_id_for(card.backing)}
|
112
|
-
new_devices = deviceAdditions.select { |device| !current_networks.include?(network_id_for(device.device.backing))}
|
113
|
-
|
114
|
-
if new_devices.count > 0
|
115
|
-
action_handler.report_progress "Adding extra NICs"
|
116
|
-
task = vm.ReconfigVM_Task(:spec => RbVmomi::VIM.VirtualMachineConfigSpec(:deviceChange => new_devices))
|
117
|
-
task.wait_for_completion
|
118
|
-
new_devices
|
119
|
-
end
|
120
|
-
end
|
121
|
-
end
|
122
|
-
|
123
|
-
def network_id_for(backing_info)
|
124
|
-
if backing_info.is_a?(RbVmomi::VIM::VirtualEthernetCardDistributedVirtualPortBackingInfo)
|
125
|
-
backing_info.port.portgroupKey
|
126
|
-
else
|
127
|
-
backing_info.deviceName
|
128
|
-
end
|
129
|
-
end
|
130
|
-
|
131
|
-
def create_delta_disk(vm_template)
|
132
|
-
disks = vm_template.config.hardware.device.grep(RbVmomi::VIM::VirtualDisk)
|
133
|
-
disks.select { |disk| disk.backing.parent == nil }.each do |disk|
|
134
|
-
spec = {
|
135
|
-
:deviceChange => [
|
136
|
-
{
|
137
|
-
:operation => :remove,
|
138
|
-
:device => disk
|
139
|
-
},
|
140
|
-
{
|
141
|
-
:operation => :add,
|
142
|
-
:fileOperation => :create,
|
143
|
-
:device => disk.dup.tap { |new_disk|
|
144
|
-
new_disk.backing = new_disk.backing.dup
|
145
|
-
new_disk.backing.fileName = "[#{disk.backing.datastore.name}]"
|
146
|
-
new_disk.backing.parent = disk.backing
|
147
|
-
},
|
148
|
-
}
|
149
|
-
]
|
150
|
-
}
|
151
|
-
vm_template.ReconfigVM_Task(:spec => spec).wait_for_completion
|
152
|
-
end
|
153
|
-
end
|
154
|
-
|
155
|
-
def virtual_disk_for(vm, datastore, size_gb)
|
156
|
-
idx = vm.disks.count
|
157
|
-
RbVmomi::VIM::VirtualDeviceConfigSpec(
|
158
|
-
:operation => :add,
|
159
|
-
:fileOperation => :create,
|
160
|
-
:device => RbVmomi::VIM.VirtualDisk(
|
161
|
-
:key => idx,
|
162
|
-
:backing => RbVmomi::VIM.VirtualDiskFlatVer2BackingInfo(
|
163
|
-
:fileName => "[#{datastore}]",
|
164
|
-
:diskMode => 'persistent',
|
165
|
-
:thinProvisioned => true
|
166
|
-
),
|
167
|
-
:capacityInKB => size_gb * 1024 * 1024,
|
168
|
-
:controllerKey => 1000,
|
169
|
-
:unitNumber => idx
|
170
|
-
)
|
171
|
-
)
|
172
|
-
end
|
173
|
-
|
174
|
-
def network_device_changes(action_handler, vm_template, options)
|
175
|
-
additions = []
|
176
|
-
changes = []
|
177
|
-
networks=options[:network_name]
|
178
|
-
if networks.kind_of?(String)
|
179
|
-
networks=[networks]
|
180
|
-
end
|
181
|
-
|
182
|
-
cards = find_ethernet_cards_for(vm_template)
|
183
|
-
|
184
|
-
key = 4000
|
185
|
-
networks.each_index do | i |
|
186
|
-
label = "Ethernet #{i+1}"
|
187
|
-
backing_info = backing_info_for(action_handler, networks[i])
|
188
|
-
if card = cards.shift
|
189
|
-
key = card.key
|
190
|
-
operation = RbVmomi::VIM::VirtualDeviceConfigSpecOperation('edit')
|
191
|
-
action_handler.report_progress "changing template nic for #{networks[i]}"
|
192
|
-
changes.push(
|
193
|
-
network_adapter_for(operation, networks[i], label, key, backing_info))
|
194
|
-
else
|
195
|
-
key = key + 1
|
196
|
-
operation = RbVmomi::VIM::VirtualDeviceConfigSpecOperation('add')
|
197
|
-
action_handler.report_progress "will be adding nic for #{networks[i]}"
|
198
|
-
additions.push(
|
199
|
-
network_adapter_for(operation, networks[i], label, key, backing_info))
|
200
|
-
end
|
201
|
-
end
|
202
|
-
[additions, changes]
|
203
|
-
end
|
204
|
-
|
205
|
-
def backing_info_for(action_handler, network_name)
|
206
|
-
action_handler.report_progress('finding networks...')
|
207
|
-
network = find_network(network_name)
|
208
|
-
action_handler.report_progress(
|
209
|
-
"network: #{network_name} is a #{network.class}")
|
210
|
-
if network.is_a?(RbVmomi::VIM::DistributedVirtualPortgroup)
|
211
|
-
port = RbVmomi::VIM::DistributedVirtualSwitchPortConnection(
|
212
|
-
:switchUuid => network.config.distributedVirtualSwitch.uuid,
|
213
|
-
:portgroupKey => network.key
|
214
|
-
)
|
215
|
-
RbVmomi::VIM::VirtualEthernetCardDistributedVirtualPortBackingInfo(
|
216
|
-
:port => port)
|
217
|
-
else
|
218
|
-
RbVmomi::VIM::VirtualEthernetCardNetworkBackingInfo(
|
219
|
-
deviceName: network_name.split('/').last)
|
220
|
-
end
|
221
|
-
end
|
222
|
-
|
223
|
-
def find_datastore(datastore_name)
|
224
|
-
datacenter.datastore.find { |f| f.info.name == datastore_name } or raise "no such datastore #{datastore_name}"
|
225
|
-
end
|
226
|
-
|
227
|
-
def find_entity(name, parent_folder, &block)
|
228
|
-
parts = name.split('/').reject(&:empty?)
|
229
|
-
parts.each do |item|
|
230
|
-
Chef::Log.debug("Identifying entity part: #{item} in folder type: #{parent_folder.class}")
|
231
|
-
if parent_folder.is_a? RbVmomi::VIM::Folder
|
232
|
-
Chef::Log.debug('Parent folder is a folder')
|
233
|
-
parent_folder = parent_folder.childEntity.find { |f| f.name == item }
|
234
|
-
else
|
235
|
-
parent_folder = block.call(parent_folder, item)
|
236
|
-
end
|
237
|
-
end
|
238
|
-
parent_folder
|
239
|
-
end
|
240
|
-
|
241
|
-
def find_host(host_name)
|
242
|
-
host = find_entity(host_name, datacenter.hostFolder) do |parent, part|
|
243
|
-
case parent
|
244
|
-
when RbVmomi::VIM::ClusterComputeResource || RbVmomi::VIM::ComputeResource
|
245
|
-
parent.host.find { |f| f.name == part }
|
246
|
-
when RbVmomi::VIM::HostSystem
|
247
|
-
parent.host.find { |f| f.name == part }
|
248
|
-
else
|
249
|
-
nil
|
250
|
-
end
|
251
|
-
end
|
252
|
-
|
253
|
-
raise "vSphere Host not found [#{host_name}]" if host.nil?
|
254
|
-
|
255
|
-
if host.is_a?(RbVmomi::VIM::ComputeResource)
|
256
|
-
host = host.host.first
|
257
|
-
end
|
258
|
-
host
|
259
|
-
end
|
260
|
-
|
261
|
-
def find_pool(pool_name)
|
262
|
-
Chef::Log.debug("Finding pool: #{pool_name}")
|
263
|
-
pool = find_entity(pool_name, datacenter.hostFolder) do |parent, part|
|
264
|
-
case parent
|
265
|
-
when RbVmomi::VIM::ClusterComputeResource, RbVmomi::VIM::ComputeResource
|
266
|
-
Chef::Log.debug("finding #{part} in a #{parent.class}: #{parent.name}")
|
267
|
-
Chef::Log.debug("Parent root pool has #{parent.resourcePool.resourcePool.count} pools")
|
268
|
-
parent.resourcePool.resourcePool.each { |p| Chef::Log.debug(p.name ) }
|
269
|
-
parent.resourcePool.resourcePool.find { |f| f.name == part }
|
270
|
-
when RbVmomi::VIM::ResourcePool
|
271
|
-
Chef::Log.debug("finding #{part} in a Resource Pool: #{parent.name}")
|
272
|
-
Chef::Log.debug("Pool has #{parent.resourcePool.count} pools")
|
273
|
-
parent.resourcePool.each { |p| Chef::Log.debug(p.name ) }
|
274
|
-
parent.resourcePool.find { |f| f.name == part }
|
275
|
-
else
|
276
|
-
Chef::Log.debug("parent of #{part} is unexpected type: #{parent.class}")
|
277
|
-
nil
|
278
|
-
end
|
279
|
-
end
|
280
|
-
|
281
|
-
raise "vSphere ResourcePool not found [#{pool_name}]" if pool.nil?
|
282
|
-
|
283
|
-
if !pool.is_a?(RbVmomi::VIM::ResourcePool) && pool.respond_to?(:resourcePool)
|
284
|
-
pool = pool.resourcePool
|
285
|
-
end
|
286
|
-
pool
|
287
|
-
end
|
288
|
-
|
289
|
-
def find_network(name)
|
290
|
-
base = datacenter.networkFolder
|
291
|
-
entity_array = name.split('/').reject(&:empty?)
|
292
|
-
entity_array.each do |item|
|
293
|
-
case base
|
294
|
-
when RbVmomi::VIM::Folder
|
295
|
-
base = base.find(item)
|
296
|
-
when RbVmomi::VIM::VmwareDistributedVirtualSwitch
|
297
|
-
idx = base.summary.portgroupName.find_index(item)
|
298
|
-
base = idx.nil? ? nil : base.portgroup[idx]
|
299
|
-
end
|
300
|
-
end
|
301
|
-
|
302
|
-
raise "vSphere Network not found [#{name}]" if base.nil?
|
303
|
-
|
304
|
-
base
|
305
|
-
end
|
306
|
-
|
307
|
-
def find_customization_spec(customization_spec)
|
308
|
-
csm = vim.serviceContent.customizationSpecManager
|
309
|
-
csi = csm.GetCustomizationSpec(:name => customization_spec)
|
310
|
-
spec = csi.spec
|
311
|
-
raise "Customization Spec not found [#{customization_spec}]" if spec.nil?
|
312
|
-
spec
|
313
|
-
end
|
314
|
-
|
315
|
-
def upload_file_to_vm(vm, username, password, local, remote)
|
316
|
-
auth = RbVmomi::VIM::NamePasswordAuthentication({:username => username, :password => password, :interactiveSession => false})
|
317
|
-
size = File.size(local)
|
318
|
-
endpoint = $guest_op_managers[vim.pretty_inspect].fileManager.InitiateFileTransferToGuest(
|
319
|
-
:vm => vm,
|
320
|
-
:auth => auth,
|
321
|
-
:guestFilePath => remote,
|
322
|
-
:overwrite => true,
|
323
|
-
:fileAttributes => RbVmomi::VIM::GuestWindowsFileAttributes.new,
|
324
|
-
:fileSize => size)
|
325
|
-
|
326
|
-
uri = URI.parse(endpoint)
|
327
|
-
http = Net::HTTP.new(uri.host, uri.port)
|
328
|
-
http.use_ssl = true
|
329
|
-
http.verify_mode = OpenSSL::SSL::VERIFY_NONE
|
330
|
-
|
331
|
-
req = Net::HTTP::Put.new("#{uri.path}?#{uri.query}")
|
332
|
-
req.body_stream = File.open(local)
|
333
|
-
req["Content-Type"] = "application/octet-stream"
|
334
|
-
req["Content-Length"] = size
|
335
|
-
res = http.request(req)
|
336
|
-
unless res.kind_of?(Net::HTTPSuccess)
|
337
|
-
raise "Error: #{res.inspect} :: #{res.body} :: sending #{local} to #{remote} at #{vm.name} via #{endpoint} with a size of #{size}"
|
338
|
-
end
|
339
|
-
end
|
340
|
-
end
|
341
|
-
end
|
1
|
+
require 'rbvmomi'
|
2
|
+
|
3
|
+
module ChefProvisioningVsphere
|
4
|
+
class VsphereHelper
|
5
|
+
|
6
|
+
if !$guest_op_managers
|
7
|
+
$guest_op_managers = {}
|
8
|
+
end
|
9
|
+
|
10
|
+
def initialize(connect_options, datacenter_name)
|
11
|
+
@connect_options = connect_options
|
12
|
+
@datacenter_name = datacenter_name
|
13
|
+
end
|
14
|
+
|
15
|
+
attr_reader :connect_options
|
16
|
+
attr_reader :datacenter_name
|
17
|
+
|
18
|
+
def vim
|
19
|
+
if @current_connection.nil? or @current_connection.serviceContent.sessionManager.currentSession.nil?
|
20
|
+
puts "establishing connection to #{connect_options[:host]}"
|
21
|
+
@current_connection = RbVmomi::VIM.connect connect_options
|
22
|
+
str_conn = @current_connection.pretty_inspect # a string in the format of VIM(host ip)
|
23
|
+
|
24
|
+
# we are caching guest operation managers in a global variable...terrible i know
|
25
|
+
# this object is available from the serviceContent object on API version 5 forward
|
26
|
+
# Its a singleton and if another connection is made for the same host and user
|
27
|
+
# that object is not available on any subsequent connection
|
28
|
+
# I could find no documentation that discusses this
|
29
|
+
if !$guest_op_managers.has_key?(str_conn)
|
30
|
+
$guest_op_managers[str_conn] = @current_connection.serviceContent.guestOperationsManager
|
31
|
+
end
|
32
|
+
end
|
33
|
+
|
34
|
+
@current_connection
|
35
|
+
end
|
36
|
+
|
37
|
+
def find_vm(folder, vm_name)
|
38
|
+
folder = find_folder(folder) unless folder.is_a? RbVmomi::VIM::Folder
|
39
|
+
folder.find(vm_name, RbVmomi::VIM::VirtualMachine)
|
40
|
+
end
|
41
|
+
|
42
|
+
def find_vm_by_id(uuid)
|
43
|
+
vm = vim.searchIndex.FindByUuid(
|
44
|
+
uuid: uuid,
|
45
|
+
vmSearch: true,
|
46
|
+
instanceUuid: true
|
47
|
+
)
|
48
|
+
end
|
49
|
+
|
50
|
+
def start_vm(vm, wait_on_port = 22)
|
51
|
+
state = vm.runtime.powerState
|
52
|
+
unless state == 'poweredOn'
|
53
|
+
vm.PowerOnVM_Task.wait_for_completion
|
54
|
+
end
|
55
|
+
end
|
56
|
+
|
57
|
+
def stop_vm(vm, timeout = 600)
|
58
|
+
start = Time.now.utc
|
59
|
+
begin
|
60
|
+
vm.ShutdownGuest
|
61
|
+
until (Time.now.utc - start) > timeout ||
|
62
|
+
vm.runtime.powerState == 'poweredOff' do
|
63
|
+
print '.'
|
64
|
+
sleep 2
|
65
|
+
end
|
66
|
+
rescue
|
67
|
+
vm.PowerOffVM_Task.wait_for_completion
|
68
|
+
end
|
69
|
+
end
|
70
|
+
|
71
|
+
#folder could be like: /Level1/Level2/folder_name
|
72
|
+
def find_folder(folder_name)
|
73
|
+
base = datacenter.vmFolder
|
74
|
+
unless folder_name.nil?
|
75
|
+
folder_name.split('/').reject(&:empty?).each do |item|
|
76
|
+
base = base.find(item, RbVmomi::VIM::Folder) ||
|
77
|
+
raise("vSphere Folder not found [#{folder_name}]")
|
78
|
+
end
|
79
|
+
end
|
80
|
+
base
|
81
|
+
end
|
82
|
+
|
83
|
+
def datacenter
|
84
|
+
@datacenter ||= vim.serviceInstance.find_datacenter(datacenter_name) ||
|
85
|
+
raise("vSphere Datacenter not found [#{datacenter_name}]")
|
86
|
+
end
|
87
|
+
|
88
|
+
def network_adapter_for(operation, network_name, network_label, device_key, backing_info)
|
89
|
+
connectable = RbVmomi::VIM::VirtualDeviceConnectInfo(
|
90
|
+
:allowGuestControl => true,
|
91
|
+
:connected => true,
|
92
|
+
:startConnected => true)
|
93
|
+
device = RbVmomi::VIM::VirtualVmxnet3(
|
94
|
+
:backing => backing_info,
|
95
|
+
:deviceInfo => RbVmomi::VIM::Description(:label => network_label, :summary => network_name.split('/').last),
|
96
|
+
:key => device_key,
|
97
|
+
:connectable => connectable)
|
98
|
+
RbVmomi::VIM::VirtualDeviceConfigSpec(
|
99
|
+
:operation => operation,
|
100
|
+
:device => device)
|
101
|
+
end
|
102
|
+
|
103
|
+
def find_ethernet_cards_for(vm)
|
104
|
+
vm.config.hardware.device.select {|d| d.is_a?(RbVmomi::VIM::VirtualEthernetCard)}
|
105
|
+
end
|
106
|
+
|
107
|
+
def add_extra_nic(action_handler, vm_template, options, vm)
|
108
|
+
deviceAdditions, changes = network_device_changes(action_handler, vm_template, options)
|
109
|
+
|
110
|
+
if deviceAdditions.count > 0
|
111
|
+
current_networks = find_ethernet_cards_for(vm).map{|card| network_id_for(card.backing)}
|
112
|
+
new_devices = deviceAdditions.select { |device| !current_networks.include?(network_id_for(device.device.backing))}
|
113
|
+
|
114
|
+
if new_devices.count > 0
|
115
|
+
action_handler.report_progress "Adding extra NICs"
|
116
|
+
task = vm.ReconfigVM_Task(:spec => RbVmomi::VIM.VirtualMachineConfigSpec(:deviceChange => new_devices))
|
117
|
+
task.wait_for_completion
|
118
|
+
new_devices
|
119
|
+
end
|
120
|
+
end
|
121
|
+
end
|
122
|
+
|
123
|
+
def network_id_for(backing_info)
|
124
|
+
if backing_info.is_a?(RbVmomi::VIM::VirtualEthernetCardDistributedVirtualPortBackingInfo)
|
125
|
+
backing_info.port.portgroupKey
|
126
|
+
else
|
127
|
+
backing_info.deviceName
|
128
|
+
end
|
129
|
+
end
|
130
|
+
|
131
|
+
def create_delta_disk(vm_template)
|
132
|
+
disks = vm_template.config.hardware.device.grep(RbVmomi::VIM::VirtualDisk)
|
133
|
+
disks.select { |disk| disk.backing.parent == nil }.each do |disk|
|
134
|
+
spec = {
|
135
|
+
:deviceChange => [
|
136
|
+
{
|
137
|
+
:operation => :remove,
|
138
|
+
:device => disk
|
139
|
+
},
|
140
|
+
{
|
141
|
+
:operation => :add,
|
142
|
+
:fileOperation => :create,
|
143
|
+
:device => disk.dup.tap { |new_disk|
|
144
|
+
new_disk.backing = new_disk.backing.dup
|
145
|
+
new_disk.backing.fileName = "[#{disk.backing.datastore.name}]"
|
146
|
+
new_disk.backing.parent = disk.backing
|
147
|
+
},
|
148
|
+
}
|
149
|
+
]
|
150
|
+
}
|
151
|
+
vm_template.ReconfigVM_Task(:spec => spec).wait_for_completion
|
152
|
+
end
|
153
|
+
end
|
154
|
+
|
155
|
+
def virtual_disk_for(vm, datastore, size_gb)
|
156
|
+
idx = vm.disks.count
|
157
|
+
RbVmomi::VIM::VirtualDeviceConfigSpec(
|
158
|
+
:operation => :add,
|
159
|
+
:fileOperation => :create,
|
160
|
+
:device => RbVmomi::VIM.VirtualDisk(
|
161
|
+
:key => idx,
|
162
|
+
:backing => RbVmomi::VIM.VirtualDiskFlatVer2BackingInfo(
|
163
|
+
:fileName => "[#{datastore}]",
|
164
|
+
:diskMode => 'persistent',
|
165
|
+
:thinProvisioned => true
|
166
|
+
),
|
167
|
+
:capacityInKB => size_gb * 1024 * 1024,
|
168
|
+
:controllerKey => 1000,
|
169
|
+
:unitNumber => idx
|
170
|
+
)
|
171
|
+
)
|
172
|
+
end
|
173
|
+
|
174
|
+
def network_device_changes(action_handler, vm_template, options)
|
175
|
+
additions = []
|
176
|
+
changes = []
|
177
|
+
networks=options[:network_name]
|
178
|
+
if networks.kind_of?(String)
|
179
|
+
networks=[networks]
|
180
|
+
end
|
181
|
+
|
182
|
+
cards = find_ethernet_cards_for(vm_template)
|
183
|
+
|
184
|
+
key = 4000
|
185
|
+
networks.each_index do | i |
|
186
|
+
label = "Ethernet #{i+1}"
|
187
|
+
backing_info = backing_info_for(action_handler, networks[i])
|
188
|
+
if card = cards.shift
|
189
|
+
key = card.key
|
190
|
+
operation = RbVmomi::VIM::VirtualDeviceConfigSpecOperation('edit')
|
191
|
+
action_handler.report_progress "changing template nic for #{networks[i]}"
|
192
|
+
changes.push(
|
193
|
+
network_adapter_for(operation, networks[i], label, key, backing_info))
|
194
|
+
else
|
195
|
+
key = key + 1
|
196
|
+
operation = RbVmomi::VIM::VirtualDeviceConfigSpecOperation('add')
|
197
|
+
action_handler.report_progress "will be adding nic for #{networks[i]}"
|
198
|
+
additions.push(
|
199
|
+
network_adapter_for(operation, networks[i], label, key, backing_info))
|
200
|
+
end
|
201
|
+
end
|
202
|
+
[additions, changes]
|
203
|
+
end
|
204
|
+
|
205
|
+
def backing_info_for(action_handler, network_name)
|
206
|
+
action_handler.report_progress('finding networks...')
|
207
|
+
network = find_network(network_name)
|
208
|
+
action_handler.report_progress(
|
209
|
+
"network: #{network_name} is a #{network.class}")
|
210
|
+
if network.is_a?(RbVmomi::VIM::DistributedVirtualPortgroup)
|
211
|
+
port = RbVmomi::VIM::DistributedVirtualSwitchPortConnection(
|
212
|
+
:switchUuid => network.config.distributedVirtualSwitch.uuid,
|
213
|
+
:portgroupKey => network.key
|
214
|
+
)
|
215
|
+
RbVmomi::VIM::VirtualEthernetCardDistributedVirtualPortBackingInfo(
|
216
|
+
:port => port)
|
217
|
+
else
|
218
|
+
RbVmomi::VIM::VirtualEthernetCardNetworkBackingInfo(
|
219
|
+
deviceName: network_name.split('/').last)
|
220
|
+
end
|
221
|
+
end
|
222
|
+
|
223
|
+
def find_datastore(datastore_name)
|
224
|
+
datacenter.datastore.find { |f| f.info.name == datastore_name } or raise "no such datastore #{datastore_name}"
|
225
|
+
end
|
226
|
+
|
227
|
+
def find_entity(name, parent_folder, &block)
|
228
|
+
parts = name.split('/').reject(&:empty?)
|
229
|
+
parts.each do |item|
|
230
|
+
Chef::Log.debug("Identifying entity part: #{item} in folder type: #{parent_folder.class}")
|
231
|
+
if parent_folder.is_a? RbVmomi::VIM::Folder
|
232
|
+
Chef::Log.debug('Parent folder is a folder')
|
233
|
+
parent_folder = parent_folder.childEntity.find { |f| f.name == item }
|
234
|
+
else
|
235
|
+
parent_folder = block.call(parent_folder, item)
|
236
|
+
end
|
237
|
+
end
|
238
|
+
parent_folder
|
239
|
+
end
|
240
|
+
|
241
|
+
def find_host(host_name)
|
242
|
+
host = find_entity(host_name, datacenter.hostFolder) do |parent, part|
|
243
|
+
case parent
|
244
|
+
when RbVmomi::VIM::ClusterComputeResource || RbVmomi::VIM::ComputeResource
|
245
|
+
parent.host.find { |f| f.name == part }
|
246
|
+
when RbVmomi::VIM::HostSystem
|
247
|
+
parent.host.find { |f| f.name == part }
|
248
|
+
else
|
249
|
+
nil
|
250
|
+
end
|
251
|
+
end
|
252
|
+
|
253
|
+
raise "vSphere Host not found [#{host_name}]" if host.nil?
|
254
|
+
|
255
|
+
if host.is_a?(RbVmomi::VIM::ComputeResource)
|
256
|
+
host = host.host.first
|
257
|
+
end
|
258
|
+
host
|
259
|
+
end
|
260
|
+
|
261
|
+
def find_pool(pool_name)
|
262
|
+
Chef::Log.debug("Finding pool: #{pool_name}")
|
263
|
+
pool = find_entity(pool_name, datacenter.hostFolder) do |parent, part|
|
264
|
+
case parent
|
265
|
+
when RbVmomi::VIM::ClusterComputeResource, RbVmomi::VIM::ComputeResource
|
266
|
+
Chef::Log.debug("finding #{part} in a #{parent.class}: #{parent.name}")
|
267
|
+
Chef::Log.debug("Parent root pool has #{parent.resourcePool.resourcePool.count} pools")
|
268
|
+
parent.resourcePool.resourcePool.each { |p| Chef::Log.debug(p.name ) }
|
269
|
+
parent.resourcePool.resourcePool.find { |f| f.name == part }
|
270
|
+
when RbVmomi::VIM::ResourcePool
|
271
|
+
Chef::Log.debug("finding #{part} in a Resource Pool: #{parent.name}")
|
272
|
+
Chef::Log.debug("Pool has #{parent.resourcePool.count} pools")
|
273
|
+
parent.resourcePool.each { |p| Chef::Log.debug(p.name ) }
|
274
|
+
parent.resourcePool.find { |f| f.name == part }
|
275
|
+
else
|
276
|
+
Chef::Log.debug("parent of #{part} is unexpected type: #{parent.class}")
|
277
|
+
nil
|
278
|
+
end
|
279
|
+
end
|
280
|
+
|
281
|
+
raise "vSphere ResourcePool not found [#{pool_name}]" if pool.nil?
|
282
|
+
|
283
|
+
if !pool.is_a?(RbVmomi::VIM::ResourcePool) && pool.respond_to?(:resourcePool)
|
284
|
+
pool = pool.resourcePool
|
285
|
+
end
|
286
|
+
pool
|
287
|
+
end
|
288
|
+
|
289
|
+
def find_network(name)
|
290
|
+
base = datacenter.networkFolder
|
291
|
+
entity_array = name.split('/').reject(&:empty?)
|
292
|
+
entity_array.each do |item|
|
293
|
+
case base
|
294
|
+
when RbVmomi::VIM::Folder
|
295
|
+
base = base.find(item)
|
296
|
+
when RbVmomi::VIM::VmwareDistributedVirtualSwitch
|
297
|
+
idx = base.summary.portgroupName.find_index(item)
|
298
|
+
base = idx.nil? ? nil : base.portgroup[idx]
|
299
|
+
end
|
300
|
+
end
|
301
|
+
|
302
|
+
raise "vSphere Network not found [#{name}]" if base.nil?
|
303
|
+
|
304
|
+
base
|
305
|
+
end
|
306
|
+
|
307
|
+
def find_customization_spec(customization_spec)
|
308
|
+
csm = vim.serviceContent.customizationSpecManager
|
309
|
+
csi = csm.GetCustomizationSpec(:name => customization_spec)
|
310
|
+
spec = csi.spec
|
311
|
+
raise "Customization Spec not found [#{customization_spec}]" if spec.nil?
|
312
|
+
spec
|
313
|
+
end
|
314
|
+
|
315
|
+
def upload_file_to_vm(vm, username, password, local, remote)
|
316
|
+
auth = RbVmomi::VIM::NamePasswordAuthentication({:username => username, :password => password, :interactiveSession => false})
|
317
|
+
size = File.size(local)
|
318
|
+
endpoint = $guest_op_managers[vim.pretty_inspect].fileManager.InitiateFileTransferToGuest(
|
319
|
+
:vm => vm,
|
320
|
+
:auth => auth,
|
321
|
+
:guestFilePath => remote,
|
322
|
+
:overwrite => true,
|
323
|
+
:fileAttributes => RbVmomi::VIM::GuestWindowsFileAttributes.new,
|
324
|
+
:fileSize => size)
|
325
|
+
|
326
|
+
uri = URI.parse(endpoint)
|
327
|
+
http = Net::HTTP.new(uri.host, uri.port)
|
328
|
+
http.use_ssl = true
|
329
|
+
http.verify_mode = OpenSSL::SSL::VERIFY_NONE
|
330
|
+
|
331
|
+
req = Net::HTTP::Put.new("#{uri.path}?#{uri.query}")
|
332
|
+
req.body_stream = File.open(local)
|
333
|
+
req["Content-Type"] = "application/octet-stream"
|
334
|
+
req["Content-Length"] = size
|
335
|
+
res = http.request(req)
|
336
|
+
unless res.kind_of?(Net::HTTPSuccess)
|
337
|
+
raise "Error: #{res.inspect} :: #{res.body} :: sending #{local} to #{remote} at #{vm.name} via #{endpoint} with a size of #{size}"
|
338
|
+
end
|
339
|
+
end
|
340
|
+
end
|
341
|
+
end
|