chef-provisioning-vsphere 2.0.2 → 2.0.3

Sign up to get free protection for your applications and to get access to all the features.
@@ -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