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