chef-provisioning-vsphere 0.10.0 → 1.0.0
Sign up to get free protection for your applications and to get access to all the features.
- 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
|