chef-provisioning-vsphere 2.0.2 → 2.0.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.
@@ -1,4 +1,6 @@
1
1
  # frozen_string_literal: false
2
+ # Provisions machines in vSphere.
2
3
  module ChefProvisioningVsphere
3
- VERSION = '2.0.2'.freeze
4
+ # The version of this awesome Gem. BOOM.
5
+ VERSION = "2.0.3".freeze
4
6
  end
@@ -1,11 +1,13 @@
1
1
  # frozen_string_literal: true
2
- require 'chef/provisioning/driver'
2
+ require "chef/provisioning/driver"
3
3
 
4
+ # Provisions machines in vSphere.
4
5
  module ChefProvisioningVsphere
5
6
  # Helps save data in provisioning a machine
6
7
  class VmHelper
7
8
  attr_accessor :ip, :port
8
9
 
10
+ # An array of all the known EXCEPTIONS for connecting via Chef-Provisioning-vSphere
9
11
  RESCUE_EXCEPTIONS_ON_ESTABLISH = [
10
12
  Errno::EACCES, Errno::EADDRINUSE, Errno::ECONNREFUSED, Errno::ETIMEDOUT,
11
13
  Errno::ECONNRESET, Errno::ENETUNREACH, Errno::EHOSTUNREACH, Errno::EPIPE,
@@ -14,31 +16,46 @@ module ChefProvisioningVsphere
14
16
  Timeout::Error, IPAddr::AddressFamilyError
15
17
  ].freeze
16
18
 
19
+ # If the IP is true
20
+ #
17
21
  def ip?
18
22
  @ip
19
23
  end
20
24
 
25
+ # If the port is true
26
+ #
21
27
  def port?
22
28
  @port
23
29
  end
24
30
 
31
+ # Finds the port for to connect to the vm
32
+ #
33
+ # @param [Object] vm Uses the VM object from Chef provisioning.
34
+ # @param [Hash] options Uses the VM options from Chef provisioning.
35
+ # @return [true] The port to connect to the VM whether Windows or *nix.
25
36
  def find_port?(vm, options)
26
37
  @port = options[:ssh][:port]
27
38
  customization_spec = options[:customization_spec]
28
- if vm.config.guestId.start_with?('win')
29
- unless customization_spec.nil? && customization_spec.is_a?(Hash)
39
+ if vm.config.guestId.start_with?("win")
40
+ if customization_spec.is_a?(Hash)
30
41
  winrm_transport =
31
42
  customization_spec[:winrm_transport].nil? ? :negotiate : customization_spec[:winrm_transport].to_sym
32
43
  end
33
44
  winrm_transport ||= :negotiate
34
- default_win_port = winrm_transport == :ssl ? '5986' : '5985'
45
+ default_win_port = winrm_transport == :ssl ? "5986" : "5985"
35
46
  @port = default_win_port if @port.nil?
36
47
  elsif port.nil?
37
- @port = '22'
48
+ @port = "22"
38
49
  end
39
50
  true
40
51
  end
41
52
 
53
+ # Attempt to connects to the open port
54
+ #
55
+ # @param [String] host Uses the host string to connect.
56
+ # @param [String] port Uses the port number to connect.
57
+ # @param [Integer] timeout The number of seconds before timeout.
58
+ # @return [true] Returns true when the socket is available to connect.
42
59
  def open_port?(host, port, timeout = 5)
43
60
  true if ::Socket.tcp(host, port, connect_timeout: timeout)
44
61
  rescue *RESCUE_EXCEPTIONS_ON_ESTABLISH
@@ -1,9 +1,11 @@
1
1
  # frozen_string_literal: true
2
- require 'rbvmomi'
3
- require 'uri'
4
- require 'net/http'
2
+ require "rbvmomi"
3
+ require "uri"
4
+ require "net/http"
5
5
 
6
+ # Provisions machines in vSphere.
6
7
  module ChefProvisioningVsphere
8
+ # A namespace to use for vSphere Helpers
7
9
  class VsphereHelper
8
10
  $guest_op_managers = {} unless $guest_op_managers
9
11
 
@@ -20,6 +22,8 @@ module ChefProvisioningVsphere
20
22
  attr_reader :connect_options
21
23
  attr_reader :datacenter_name
22
24
 
25
+ # Establishes the connection to the vSphere cluster
26
+ #
23
27
  def vim
24
28
  if @current_connection.nil? || @current_connection.serviceContent.sessionManager.currentSession.nil?
25
29
  @datacenter = nil
@@ -40,11 +44,18 @@ module ChefProvisioningVsphere
40
44
  @current_connection
41
45
  end
42
46
 
47
+ # Finds the vm using RbVmomi
48
+ #
49
+ # @param [String] folder the folder name for to look into.
50
+ # @param [String] vm_name the name of the machine.
43
51
  def find_vm(folder, vm_name)
44
52
  folder = find_folder(folder) unless folder.is_a? RbVmomi::VIM::Folder
45
53
  folder.find(vm_name, RbVmomi::VIM::VirtualMachine)
46
54
  end
47
55
 
56
+ # Finds the vm using the UUID
57
+ #
58
+ # @param [String] uuid the UUID of the machine
48
59
  def find_vm_by_id(uuid)
49
60
  vm = vim.searchIndex.FindByUuid(
50
61
  uuid: uuid,
@@ -53,18 +64,26 @@ module ChefProvisioningVsphere
53
64
  )
54
65
  end
55
66
 
67
+ # Starts the VM
68
+ #
69
+ # @param [Object] vm the main VM object to talk to vSphere.
70
+ # @param [Object] _wait_on_port Defaults to port 22, to connect and verify it's up.
56
71
  def start_vm(vm, _wait_on_port = 22)
57
72
  state = vm.runtime.powerState
58
- vm.PowerOnVM_Task.wait_for_completion unless state == 'poweredOn'
73
+ vm.PowerOnVM_Task.wait_for_completion unless state == "poweredOn"
59
74
  end
60
75
 
76
+ # Stops the VM
77
+ #
78
+ # @param [Object] vm the main VM object to talk to vSphere.
79
+ # @param [Object] timeout Defaults to 600 seconds or 10 mins before giving up.
61
80
  def stop_vm(vm, timeout = 600)
62
81
  start = Time.now.utc
63
82
  begin
64
83
  vm.ShutdownGuest
65
84
  until (Time.now.utc - start) > timeout ||
66
- vm.runtime.powerState == 'poweredOff'
67
- print '.'
85
+ vm.runtime.powerState == "poweredOff"
86
+ print "."
68
87
  sleep 2
69
88
  end
70
89
  rescue
@@ -72,18 +91,26 @@ module ChefProvisioningVsphere
72
91
  end
73
92
  end
74
93
 
75
- # folder could be like: /Level1/Level2/folder_name
94
+ # Find the folder it could be like: /Level1/Level2/folder_name
95
+ #
96
+ # @param [String] folder_name the name of the folder.
97
+ # @return [Object] base the location of the folder.
76
98
  def find_folder(folder_name)
77
99
  base = datacenter.vmFolder
78
100
  unless folder_name.nil?
79
- folder_name.split('/').reject(&:empty?).each do |item|
101
+ folder_name.split("/").reject(&:empty?).each do |item|
80
102
  base = base.find(item, RbVmomi::VIM::Folder) ||
81
- raise("vSphere Folder not found [#{folder_name}]")
103
+ raise("vSphere Folder not found [#{folder_name}]")
82
104
  end
83
105
  end
84
106
  base
85
107
  end
86
108
 
109
+ # Walks through the folders to if needed
110
+ #
111
+ # @param [String] folder the name of the folder.
112
+ # @param [String] dcname the name of the DC.
113
+ # @return [False] Returns false if not needed.
87
114
  def traverse_folders_for_dc(folder, dcname)
88
115
  children = folder.children.find_all
89
116
  children.each do |child|
@@ -97,6 +124,9 @@ module ChefProvisioningVsphere
97
124
  false
98
125
  end
99
126
 
127
+ # Connects to the Datacenter and creates Object
128
+ #
129
+ # @return [Object] Creates the dc object to know where to create the VMs.
100
130
  def datacenter
101
131
  vim # ensure connection is valid
102
132
  @datacenter ||= begin
@@ -105,6 +135,13 @@ module ChefProvisioningVsphere
105
135
  end
106
136
  end
107
137
 
138
+ # Creates the network adapter for Rbvmomi
139
+ #
140
+ # @param [Object] operation what you need to do to RBvmomi
141
+ # @param [String] network_name the name of the network the adapter needs to connect to
142
+ # @param [String] network_label the more verbose name of the network the adapter needs to connect to
143
+ # @param [String] device_key TODO
144
+ # @param [String] backing_info TODO
108
145
  def network_adapter_for(operation, network_name, network_label, device_key, backing_info)
109
146
  connectable = RbVmomi::VIM::VirtualDeviceConnectInfo(
110
147
  allowGuestControl: true,
@@ -113,7 +150,7 @@ module ChefProvisioningVsphere
113
150
  )
114
151
  device = RbVmomi::VIM::VirtualVmxnet3(
115
152
  backing: backing_info,
116
- deviceInfo: RbVmomi::VIM::Description(label: network_label, summary: network_name.split('/').last),
153
+ deviceInfo: RbVmomi::VIM::Description(label: network_label, summary: network_name.split("/").last),
117
154
  key: device_key,
118
155
  connectable: connectable
119
156
  )
@@ -123,10 +160,19 @@ module ChefProvisioningVsphere
123
160
  )
124
161
  end
125
162
 
163
+ # Finds the Network cards from the VM
164
+ #
165
+ # @param [Object] vm the VM object to do the query against.
126
166
  def find_ethernet_cards_for(vm)
127
167
  vm.config.hardware.device.select { |d| d.is_a?(RbVmomi::VIM::VirtualEthernetCard) }
128
168
  end
129
169
 
170
+ # Add another NIC to the VM
171
+ #
172
+ # @param [Object] action_handler TODO
173
+ # @param [String] vm_template the Name of the Template to clone
174
+ # @param [Object] options the options from Chef Provisioning to help configure the VM.
175
+ # @param [Object] vm the actual VM object to connect the VM to.
130
176
  def add_extra_nic(action_handler, vm_template, options, vm)
131
177
  deviceAdditions, changes = network_device_changes(action_handler, vm_template, options)
132
178
 
@@ -135,7 +181,7 @@ module ChefProvisioningVsphere
135
181
  new_devices = deviceAdditions.select { |device| !current_networks.include?(network_id_for(device.device.backing)) }
136
182
 
137
183
  if new_devices.count > 0
138
- action_handler.report_progress 'Adding extra NICs'
184
+ action_handler.report_progress "Adding extra NICs"
139
185
  task = vm.ReconfigVM_Task(spec: RbVmomi::VIM.VirtualMachineConfigSpec(deviceChange: new_devices))
140
186
  task.wait_for_completion
141
187
  new_devices
@@ -143,6 +189,9 @@ module ChefProvisioningVsphere
143
189
  end
144
190
  end
145
191
 
192
+ # Gets the network id for a specific thing?
193
+ #
194
+ # @param [String] backing_info TODO
146
195
  def network_id_for(backing_info)
147
196
  if backing_info.is_a?(RbVmomi::VIM::VirtualEthernetCardDistributedVirtualPortBackingInfo)
148
197
  backing_info.port.portgroupKey
@@ -151,6 +200,9 @@ module ChefProvisioningVsphere
151
200
  end
152
201
  end
153
202
 
203
+ # Creates a delta disk for a vm template
204
+ #
205
+ # @param [String] vm_template the Name of the Template to clone.
154
206
  def create_delta_disk(vm_template)
155
207
  disks = vm_template.config.hardware.device.grep(RbVmomi::VIM::VirtualDisk)
156
208
  disks.select { |disk| disk.backing.parent.nil? }.each do |disk|
@@ -158,7 +210,7 @@ module ChefProvisioningVsphere
158
210
  deviceChange: [
159
211
  {
160
212
  operation: :remove,
161
- device: disk
213
+ device: disk,
162
214
  },
163
215
  {
164
216
  operation: :add,
@@ -167,14 +219,19 @@ module ChefProvisioningVsphere
167
219
  new_disk.backing = new_disk.backing.dup
168
220
  new_disk.backing.fileName = "[#{disk.backing.datastore.name}]"
169
221
  new_disk.backing.parent = disk.backing
170
- end
171
- }
172
- ]
222
+ end,
223
+ },
224
+ ],
173
225
  }
174
226
  vm_template.ReconfigVM_Task(spec: spec).wait_for_completion
175
227
  end
176
- end
228
+ end
177
229
 
230
+ # Creates a virtual disk for the VM
231
+ #
232
+ # @param [Object] vm the VM object.
233
+ # @param [Subject] datastore the datastore the disk will be created on.
234
+ # @param [Subject] size_gb the size of the disk.
178
235
  def virtual_disk_for(vm, datastore, size_gb)
179
236
  idx = vm.disks.count
180
237
  RbVmomi::VIM::VirtualDeviceConfigSpec(
@@ -184,7 +241,7 @@ module ChefProvisioningVsphere
184
241
  key: idx,
185
242
  backing: RbVmomi::VIM.VirtualDiskFlatVer2BackingInfo(
186
243
  fileName: "[#{datastore}]",
187
- diskMode: 'persistent',
244
+ diskMode: "persistent",
188
245
  thinProvisioned: true
189
246
  ),
190
247
  capacityInKB: size_gb * 1024 * 1024,
@@ -194,6 +251,11 @@ module ChefProvisioningVsphere
194
251
  )
195
252
  end
196
253
 
254
+ # Add a new network card via the boot_options
255
+ #
256
+ # @param [Object] action_handler TODO
257
+ # @param [String] vm_template the Name of the Template to clone
258
+ # @param [Object] options the options from Chef Provisioning to help configure the VM.
197
259
  def network_device_changes(action_handler, vm_template, options)
198
260
  additions = []
199
261
  changes = []
@@ -208,14 +270,14 @@ module ChefProvisioningVsphere
208
270
  backing_info = backing_info_for(action_handler, networks[i])
209
271
  if card = cards.shift
210
272
  key = card.key
211
- operation = RbVmomi::VIM::VirtualDeviceConfigSpecOperation('edit')
273
+ operation = RbVmomi::VIM::VirtualDeviceConfigSpecOperation("edit")
212
274
  action_handler.report_progress "changing template nic for #{networks[i]}"
213
275
  changes.push(
214
276
  network_adapter_for(operation, networks[i], label, key, backing_info)
215
277
  )
216
278
  else
217
279
  key += 1
218
- operation = RbVmomi::VIM::VirtualDeviceConfigSpecOperation('add')
280
+ operation = RbVmomi::VIM::VirtualDeviceConfigSpecOperation("add")
219
281
  action_handler.report_progress "will be adding nic for #{networks[i]}"
220
282
  additions.push(
221
283
  network_adapter_for(operation, networks[i], label, key, backing_info)
@@ -225,8 +287,12 @@ module ChefProvisioningVsphere
225
287
  [additions, changes]
226
288
  end
227
289
 
290
+ # Discover and identity network names
291
+ #
292
+ # @param [Object] action_handler TODO
293
+ # @param [String] network_name The network name to attach to
228
294
  def backing_info_for(action_handler, network_name)
229
- action_handler.report_progress('finding networks...')
295
+ action_handler.report_progress("finding networks...")
230
296
  network = find_network(network_name)
231
297
  action_handler.report_progress(
232
298
  "network: #{network_name} is a #{network.class}"
@@ -241,21 +307,28 @@ module ChefProvisioningVsphere
241
307
  )
242
308
  else
243
309
  RbVmomi::VIM::VirtualEthernetCardNetworkBackingInfo(
244
- deviceName: network_name.split('/').last
310
+ deviceName: network_name.split("/").last
245
311
  )
246
312
  end
247
313
  end
248
314
 
315
+ # Find the datastore name.
316
+ #
317
+ # @param [String] datastore_name The datastore name.
249
318
  def find_datastore(datastore_name)
250
319
  datacenter.datastore.find { |f| f.info.name == datastore_name } || raise("no such datastore #{datastore_name}")
251
320
  end
252
321
 
322
+ # Locate the object/vm/whatever in the vSphere cluster
323
+ #
324
+ # @param [String] name The name of the "thing."
325
+ # @param [String] parent_folder The name of the folder to start from.
253
326
  def find_entity(name, parent_folder)
254
- parts = name.split('/').reject(&:empty?)
327
+ parts = name.split("/").reject(&:empty?)
255
328
  parts.each do |item|
256
329
  Chef::Log.debug("Identifying entity part: #{item} in folder type: #{parent_folder.class}")
257
330
  if parent_folder.is_a? RbVmomi::VIM::Folder
258
- Chef::Log.debug('Parent folder is a folder')
331
+ Chef::Log.debug("Parent folder is a folder")
259
332
  parent_folder = parent_folder.childEntity.find { |f| f.name == item }
260
333
  else
261
334
  parent_folder = yield(parent_folder, item)
@@ -264,6 +337,9 @@ module ChefProvisioningVsphere
264
337
  parent_folder
265
338
  end
266
339
 
340
+ # Find the (ESXi) host.
341
+ #
342
+ # @param [String] host_name Name of the (ESXi) VM host.
267
343
  def find_host(host_name)
268
344
  host = find_entity(host_name, datacenter.hostFolder) do |parent, part|
269
345
  case parent
@@ -280,6 +356,9 @@ module ChefProvisioningVsphere
280
356
  host
281
357
  end
282
358
 
359
+ # Find the Resource pool.
360
+ #
361
+ # @param [String] pool_name Name of the Resource Pool.
283
362
  def find_pool(pool_name)
284
363
  Chef::Log.debug("Finding pool: #{pool_name}")
285
364
  pool = find_entity(pool_name, datacenter.hostFolder) do |parent, part|
@@ -308,9 +387,12 @@ module ChefProvisioningVsphere
308
387
  pool
309
388
  end
310
389
 
390
+ # Find the Network name.
391
+ #
392
+ # @param [String] name Name of the Network.
311
393
  def find_network(name)
312
394
  base = datacenter.networkFolder
313
- entity_array = name.split('/').reject(&:empty?)
395
+ entity_array = name.split("/").reject(&:empty?)
314
396
  entity_array.each do |item|
315
397
  case base
316
398
  when RbVmomi::VIM::Folder
@@ -326,6 +408,9 @@ module ChefProvisioningVsphere
326
408
  base
327
409
  end
328
410
 
411
+ # Locate the Customization Spec in vSphere.
412
+ #
413
+ # @param [String] customization_spec The name of the Customization Spec.
329
414
  def find_customization_spec(customization_spec)
330
415
  csm = vim.serviceContent.customizationSpecManager
331
416
  csi = csm.GetCustomizationSpec(name: customization_spec)
@@ -334,6 +419,13 @@ module ChefProvisioningVsphere
334
419
  spec
335
420
  end
336
421
 
422
+ # Upload a file to the VM using RbVmomi
423
+ #
424
+ # @param [Object] vm The VM object.
425
+ # @param [String] username The username to access the machine.
426
+ # @param [String] password The password to access the machine.
427
+ # @param [String] local The local file to upload.
428
+ # @param [String] remote The remote file to upload location.
337
429
  def upload_file_to_vm(vm, username, password, local, remote)
338
430
  auth = RbVmomi::VIM::NamePasswordAuthentication(username: username, password: password, interactiveSession: false)
339
431
  size = File.size(local)
@@ -353,8 +445,8 @@ module ChefProvisioningVsphere
353
445
 
354
446
  req = Net::HTTP::Put.new("#{uri.path}?#{uri.query}")
355
447
  req.body_stream = File.open(local)
356
- req['Content-Type'] = 'application/octet-stream'
357
- req['Content-Length'] = size
448
+ req["Content-Type"] = "application/octet-stream"
449
+ req["Content-Length"] = size
358
450
  res = http.request(req)
359
451
  unless res.is_a?(Net::HTTPSuccess)
360
452
  raise "Error: #{res.inspect} :: #{res.body} :: sending #{local} to #{remote} at #{vm.name} via #{endpoint} with a size of #{size}"
@@ -1,46 +1,56 @@
1
1
  # frozen_string_literal: false
2
- require 'uri'
2
+ require "uri"
3
3
 
4
+ # The main URI Module
4
5
  module URI
6
+ # Creates the vSphereURL, extended by the Generic Class
5
7
  class VsphereUrl < Generic
8
+ # Default port for connecting to the vSphere cluster Webserver
6
9
  DEFAULT_PORT = 443
7
- DEFAULT_PATH = '/sdk'.freeze
10
+ # Default path for connecting to the vSphere cluster URL
11
+ DEFAULT_PATH = "/sdk".freeze
8
12
 
13
+ # Creates the URL from options that are decided
14
+ #
9
15
  def self.from_config(options)
10
16
  parts = []
11
- parts << 'vsphere://'
17
+ parts << "vsphere://"
12
18
  parts << options[:host]
13
- parts << ':'
19
+ parts << ":"
14
20
  parts << (options[:port] || DEFAULT_PORT)
15
21
  parts << (options[:path] || DEFAULT_PATH)
16
- parts << '?use_ssl='
22
+ parts << "?use_ssl="
17
23
  parts << (options[:use_ssl] == false ? false : true)
18
- parts << '&insecure='
24
+ parts << "&insecure="
19
25
  parts << (options[:insecure] || false)
20
26
  URI parts.join
21
27
  end
22
28
 
29
+ # Converts URL to SSL if needed
30
+ #
23
31
  def use_ssl
24
32
  if query
25
- ssl_query = query.split('&').each.select do |q|
26
- q.start_with?('use_ssl=')
33
+ ssl_query = query.split("&").each.select do |q|
34
+ q.start_with?("use_ssl=")
27
35
  end.first
28
- ssl_query == 'use_ssl=true'
36
+ ssl_query == "use_ssl=true"
29
37
  else
30
38
  true
31
39
  end
32
40
  end
33
41
 
42
+ # Converts URL to insecure if needed
43
+ #
34
44
  def insecure
35
45
  if query
36
- insecure_query = query.split('&').each.select do |q|
37
- q.start_with?('insecure=')
46
+ insecure_query = query.split("&").each.select do |q|
47
+ q.start_with?("insecure=")
38
48
  end.first
39
- insecure_query == 'insecure=true'
49
+ insecure_query == "insecure=true"
40
50
  else
41
51
  false
42
52
  end
43
53
  end
44
54
  end
45
- @@schemes['VSPHERE'] = VsphereUrl
55
+ @@schemes["VSPHERE"] = VsphereUrl
46
56
  end