Class: ChefProvisioningVsphere::VsphereHelper
- Inherits:
-
Object
- Object
- ChefProvisioningVsphere::VsphereHelper
- Defined in:
- lib/chef/provisioning/vsphere_driver/vsphere_helpers.rb
Overview
A namespace to use for vSphere Helpers
Instance Attribute Summary collapse
-
#connect_options ⇒ Object
readonly
Returns the value of attribute connect_options.
-
#datacenter_name ⇒ Object
readonly
Returns the value of attribute datacenter_name.
Instance Method Summary collapse
-
#add_extra_nic(action_handler, vm_template, options, vm) ⇒ Object
Add another NIC to the VM.
-
#backing_info_for(action_handler, network_name) ⇒ Object
Discover and identity network names.
-
#create_delta_disk(vm_template) ⇒ Object
Creates a delta disk for a vm template.
-
#datacenter ⇒ Object
Connects to the Datacenter and creates Object.
-
#find_customization_spec(customization_spec) ⇒ Object
Locate the Customization Spec in vSphere.
-
#find_datastore(datastore_name) ⇒ Object
Find the datastore name.
-
#find_entity(name, parent_folder) ⇒ Object
Locate the object/vm/whatever in the vSphere cluster.
-
#find_ethernet_cards_for(vm) ⇒ Object
Finds the Network cards from the VM.
-
#find_folder(folder_name) ⇒ Object
Find the folder it could be like: /Level1/Level2/folder_name.
-
#find_host(host_name) ⇒ Object
Find the (ESXi) host.
-
#find_network(name) ⇒ Object
Find the Network name.
-
#find_pool(pool_name) ⇒ Object
Find the Resource pool.
-
#find_vm(folder, vm_name) ⇒ Object
Finds the vm using RbVmomi.
-
#find_vm_by_id(uuid) ⇒ Object
Finds the vm using the UUID.
-
#initialize(connect_options, datacenter_name) ⇒ VsphereHelper
constructor
A new instance of VsphereHelper.
-
#network_adapter_for(operation, network_name, network_label, device_key, backing_info) ⇒ Object
Creates the network adapter for Rbvmomi.
-
#network_device_changes(action_handler, vm_template, options) ⇒ Object
Add a new network card via the boot_options.
-
#network_id_for(backing_info) ⇒ Object
Gets the network id for a specific thing?.
-
#set_additional_disks_for(vm, datastore, additional_disk_size_gb) ⇒ Object
Creates the additional virtual disk for the VM.
-
#set_initial_iso(vm, initial_iso_image) ⇒ Object
Mounts the an iso on the first virtual CD ROm.
-
#start_vm(vm, _wait_on_port = 22) ⇒ Object
Starts the VM.
-
#stop_vm(vm, timeout = 600) ⇒ Object
Stops the VM.
-
#traverse_folders_for_dc(folder, dcname) ⇒ False
Walks through the folders to if needed.
-
#traverse_folders_for_network(base, item) ⇒ Object
Search the item through the base’s children.
-
#update_main_disk_size_for(vm, size_gb) ⇒ Object
Updates the main virtual disk for the VM This can only add capacity to the main disk, it is not possible to reduce the capacity.
-
#upload_file_to_vm(vm, username, password, local, remote) ⇒ Object
Upload a file to the VM using RbVmomi.
-
#vim ⇒ Object
Establishes the connection to the vSphere cluster.
-
#virtual_disk_for(vm, datastore, size_gb) ⇒ Object
Creates a virtual disk for the VM.
Constructor Details
#initialize(connect_options, datacenter_name) ⇒ VsphereHelper
Returns a new instance of VsphereHelper.
13 14 15 16 17 18 19 20 21 |
# File 'lib/chef/provisioning/vsphere_driver/vsphere_helpers.rb', line 13 def initialize(, datacenter_name) http = Net::HTTP.new([:host], [:port]) if http.proxy? [:proxyHost] = http.proxy_address [:proxyPort] = http.proxy_port end @connect_options = @datacenter_name = datacenter_name end |
Instance Attribute Details
#connect_options ⇒ Object (readonly)
Returns the value of attribute connect_options.
23 24 25 |
# File 'lib/chef/provisioning/vsphere_driver/vsphere_helpers.rb', line 23 def @connect_options end |
#datacenter_name ⇒ Object (readonly)
Returns the value of attribute datacenter_name.
24 25 26 |
# File 'lib/chef/provisioning/vsphere_driver/vsphere_helpers.rb', line 24 def datacenter_name @datacenter_name end |
Instance Method Details
#add_extra_nic(action_handler, vm_template, options, vm) ⇒ Object
Add another NIC to the VM
178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 |
# File 'lib/chef/provisioning/vsphere_driver/vsphere_helpers.rb', line 178 def add_extra_nic(action_handler, vm_template, , vm) deviceAdditions, changes = network_device_changes(action_handler, vm_template, ) if deviceAdditions.count.positive? current_networks = find_ethernet_cards_for(vm).map { |card| network_id_for(card.backing) } new_devices = deviceAdditions.reject { |device| current_networks.include?(network_id_for(device.device.backing)) } if new_devices.count.positive? action_handler.report_progress 'Adding extra NICs' task = vm.ReconfigVM_Task(spec: RbVmomi::VIM.VirtualMachineConfigSpec(deviceChange: new_devices)) task.wait_for_completion new_devices end end end |
#backing_info_for(action_handler, network_name) ⇒ Object
Discover and identity network names
383 384 385 386 387 388 389 390 391 392 393 394 395 396 397 398 399 400 401 402 |
# File 'lib/chef/provisioning/vsphere_driver/vsphere_helpers.rb', line 383 def backing_info_for(action_handler, network_name) action_handler.report_progress('finding networks...') network = find_network(network_name) action_handler.report_progress( "network: #{network_name} is a #{network.class}" ) if network.is_a?(RbVmomi::VIM::DistributedVirtualPortgroup) port = RbVmomi::VIM::DistributedVirtualSwitchPortConnection( switchUuid: network.config.distributedVirtualSwitch.uuid, portgroupKey: network.key ) RbVmomi::VIM::VirtualEthernetCardDistributedVirtualPortBackingInfo( port: port ) else RbVmomi::VIM::VirtualEthernetCardNetworkBackingInfo( deviceName: network_name.split('/').last ) end end |
#create_delta_disk(vm_template) ⇒ Object
Creates a delta disk for a vm template
208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 |
# File 'lib/chef/provisioning/vsphere_driver/vsphere_helpers.rb', line 208 def create_delta_disk(vm_template) disks = vm_template.config.hardware.device.grep(RbVmomi::VIM::VirtualDisk) disks.select { |disk| disk.backing.parent.nil? }.each do |disk| spec = { deviceChange: [ { operation: :remove, device: disk }, { operation: :add, fileOperation: :create, device: disk.dup.tap do |new_disk| new_disk.backing = new_disk.backing.dup new_disk.backing.fileName = "[#{disk.backing.datastore.name}]" new_disk.backing.parent = disk.backing end } ] } vm_template.ReconfigVM_Task(spec: spec).wait_for_completion end end |
#datacenter ⇒ Object
Connects to the Datacenter and creates Object
132 133 134 135 136 137 138 |
# File 'lib/chef/provisioning/vsphere_driver/vsphere_helpers.rb', line 132 def datacenter vim # ensure connection is valid @datacenter ||= begin rootFolder = vim.serviceInstance.content.rootFolder dc = traverse_folders_for_dc(vim.rootFolder, datacenter_name) || abort("vSphere Datacenter not found [#{datacenter_name}]") end end |
#find_customization_spec(customization_spec) ⇒ Object
Locate the Customization Spec in vSphere.
517 518 519 520 521 522 523 |
# File 'lib/chef/provisioning/vsphere_driver/vsphere_helpers.rb', line 517 def find_customization_spec(customization_spec) csm = vim.serviceContent.customizationSpecManager csi = csm.GetCustomizationSpec(name: customization_spec) spec = csi.spec raise "Customization Spec not found [#{customization_spec}]" if spec.nil? spec end |
#find_datastore(datastore_name) ⇒ Object
Find the datastore name.
407 408 409 |
# File 'lib/chef/provisioning/vsphere_driver/vsphere_helpers.rb', line 407 def find_datastore(datastore_name) datacenter.datastore.find { |f| f.info.name == datastore_name } || raise("no such datastore #{datastore_name}") end |
#find_entity(name, parent_folder) ⇒ Object
Locate the object/vm/whatever in the vSphere cluster
415 416 417 418 419 420 421 422 423 424 425 426 427 |
# File 'lib/chef/provisioning/vsphere_driver/vsphere_helpers.rb', line 415 def find_entity(name, parent_folder) parts = name.split('/').reject(&:empty?) parts.each do |item| Chef::Log.debug("Identifying entity part: #{item} in folder type: #{parent_folder.class}") if parent_folder.is_a? RbVmomi::VIM::Folder Chef::Log.debug('Parent folder is a folder') parent_folder = parent_folder.childEntity.find { |f| f.name == item } else parent_folder = yield(parent_folder, item) end end parent_folder end |
#find_ethernet_cards_for(vm) ⇒ Object
Finds the Network cards from the VM
168 169 170 |
# File 'lib/chef/provisioning/vsphere_driver/vsphere_helpers.rb', line 168 def find_ethernet_cards_for(vm) vm.config.hardware.device.select { |d| d.is_a?(RbVmomi::VIM::VirtualEthernetCard) } end |
#find_folder(folder_name) ⇒ Object
Find the folder it could be like: /Level1/Level2/folder_name
100 101 102 103 104 105 106 107 108 109 |
# File 'lib/chef/provisioning/vsphere_driver/vsphere_helpers.rb', line 100 def find_folder(folder_name) base = datacenter.vmFolder unless folder_name.nil? folder_name.split('/').reject(&:empty?).each do |item| base = base.find(item, RbVmomi::VIM::Folder) || raise("vSphere Folder not found [#{folder_name}]") end end base end |
#find_host(host_name) ⇒ Object
Find the (ESXi) host.
432 433 434 435 436 437 438 439 440 441 442 443 444 445 446 |
# File 'lib/chef/provisioning/vsphere_driver/vsphere_helpers.rb', line 432 def find_host(host_name) host = find_entity(host_name, datacenter.hostFolder) do |parent, part| case parent when RbVmomi::VIM::ClusterComputeResource || RbVmomi::VIM::ComputeResource parent.host.find { |f| f.name == part } when RbVmomi::VIM::HostSystem parent.host.find { |f| f.name == part } end end raise "vSphere Host not found [#{host_name}]" if host.nil? host = host.host.first if host.is_a?(RbVmomi::VIM::ComputeResource) host end |
#find_network(name) ⇒ Object
Find the Network name.
482 483 484 485 486 487 488 489 490 491 492 |
# File 'lib/chef/provisioning/vsphere_driver/vsphere_helpers.rb', line 482 def find_network(name) base = datacenter.networkFolder entity_array = name.split('/').reject(&:empty?) entity_array.each do |item| base = traverse_folders_for_network(base, item) end raise "vSphere Network not found [#{name}]" if base.nil? base end |
#find_pool(pool_name) ⇒ Object
Find the Resource pool.
451 452 453 454 455 456 457 458 459 460 461 462 463 464 465 466 467 468 469 470 471 472 473 474 475 476 477 |
# File 'lib/chef/provisioning/vsphere_driver/vsphere_helpers.rb', line 451 def find_pool(pool_name) Chef::Log.debug("Finding pool: #{pool_name}") pool = find_entity(pool_name, datacenter.hostFolder) do |parent, part| case parent when RbVmomi::VIM::ClusterComputeResource, RbVmomi::VIM::ComputeResource Chef::Log.debug("finding #{part} in a #{parent.class}: #{parent.name}") Chef::Log.debug("Parent root pool has #{parent.resourcePool.resourcePool.count} pools") parent.resourcePool.resourcePool.each { |p| Chef::Log.debug(p.name) } parent.resourcePool.resourcePool.find { |f| f.name == part } when RbVmomi::VIM::ResourcePool Chef::Log.debug("finding #{part} in a Resource Pool: #{parent.name}") Chef::Log.debug("Pool has #{parent.resourcePool.count} pools") parent.resourcePool.each { |p| Chef::Log.debug(p.name) } parent.resourcePool.find { |f| f.name == part } else Chef::Log.debug("parent of #{part} is unexpected type: #{parent.class}") nil end end raise "vSphere ResourcePool not found [#{pool_name}]" if pool.nil? if !pool.is_a?(RbVmomi::VIM::ResourcePool) && pool.respond_to?(:resourcePool) pool = pool.resourcePool end pool end |
#find_vm(folder, vm_name) ⇒ Object
Finds the vm using RbVmomi
52 53 54 55 |
# File 'lib/chef/provisioning/vsphere_driver/vsphere_helpers.rb', line 52 def find_vm(folder, vm_name) folder = find_folder(folder) unless folder.is_a? RbVmomi::VIM::Folder folder.find(vm_name, RbVmomi::VIM::VirtualMachine) end |
#find_vm_by_id(uuid) ⇒ Object
Finds the vm using the UUID
60 61 62 63 64 65 66 |
# File 'lib/chef/provisioning/vsphere_driver/vsphere_helpers.rb', line 60 def find_vm_by_id(uuid) vm = vim.searchIndex.FindByUuid( uuid: uuid, vmSearch: true, instanceUuid: true ) end |
#network_adapter_for(operation, network_name, network_label, device_key, backing_info) ⇒ Object
Creates the network adapter for Rbvmomi
147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 |
# File 'lib/chef/provisioning/vsphere_driver/vsphere_helpers.rb', line 147 def network_adapter_for(operation, network_name, network_label, device_key, backing_info) connectable = RbVmomi::VIM::VirtualDeviceConnectInfo( allowGuestControl: true, connected: true, startConnected: true ) device = RbVmomi::VIM::VirtualVmxnet3( backing: backing_info, deviceInfo: RbVmomi::VIM::Description(label: network_label, summary: network_name.split('/').last), key: device_key, connectable: connectable ) RbVmomi::VIM::VirtualDeviceConfigSpec( operation: operation, device: device ) end |
#network_device_changes(action_handler, vm_template, options) ⇒ Object
Add a new network card via the boot_options
348 349 350 351 352 353 354 355 356 357 358 359 360 361 362 363 364 365 366 367 368 369 370 371 372 373 374 375 376 377 |
# File 'lib/chef/provisioning/vsphere_driver/vsphere_helpers.rb', line 348 def network_device_changes(action_handler, vm_template, ) additions = [] changes = [] networks = [:network_name] networks = [networks] if networks.is_a?(String) cards = find_ethernet_cards_for(vm_template) key = 4000 networks.each_index do |i| label = "Ethernet #{i + 1}" backing_info = backing_info_for(action_handler, networks[i]) if card = cards.shift key = card.key operation = RbVmomi::VIM::VirtualDeviceConfigSpecOperation('edit') action_handler.report_progress "changing template nic for #{networks[i]}" changes.push( network_adapter_for(operation, networks[i], label, key, backing_info) ) else key += 1 operation = RbVmomi::VIM::VirtualDeviceConfigSpecOperation('add') action_handler.report_progress "will be adding nic for #{networks[i]}" additions.push( network_adapter_for(operation, networks[i], label, key, backing_info) ) end end [additions, changes] end |
#network_id_for(backing_info) ⇒ Object
Gets the network id for a specific thing?
197 198 199 200 201 202 203 |
# File 'lib/chef/provisioning/vsphere_driver/vsphere_helpers.rb', line 197 def network_id_for(backing_info) if backing_info.is_a?(RbVmomi::VIM::VirtualEthernetCardDistributedVirtualPortBackingInfo) backing_info.port.portgroupKey else backing_info.deviceName end end |
#set_additional_disks_for(vm, datastore, additional_disk_size_gb) ⇒ Object
Creates the additional virtual disk for the VM
261 262 263 264 265 266 267 268 269 270 271 272 273 274 275 276 277 278 279 280 281 |
# File 'lib/chef/provisioning/vsphere_driver/vsphere_helpers.rb', line 261 def set_additional_disks_for(vm, datastore, additional_disk_size_gb) raise ':datastore must be specified when adding a disk to a cloned vm' if datastore.to_s.empty? && additional_disk_size_gb Array(additional_disk_size_gb).each do |size| size = size.to_i next if size <= 0 task = vm.ReconfigVM_Task( spec: RbVmomi::VIM.VirtualMachineConfigSpec( deviceChange: [ virtual_disk_for( vm, datastore, size ) ] ) ) task.wait_for_completion end end |
#set_initial_iso(vm, initial_iso_image) ⇒ Object
Mounts the an iso on the first virtual CD ROm
287 288 289 290 291 292 293 294 295 296 297 298 299 300 301 302 303 304 305 306 307 308 309 310 311 312 |
# File 'lib/chef/provisioning/vsphere_driver/vsphere_helpers.rb', line 287 def set_initial_iso(vm, initial_iso_image) return unless initial_iso_image d_obj = vm.config.hardware.device.select { |hw| hw.class == RbVmomi::VIM::VirtualCdrom }.first backing = RbVmomi::VIM::VirtualCdromIsoBackingInfo(fileName: initial_iso_image) task = vm.ReconfigVM_Task( spec: RbVmomi::VIM.VirtualMachineConfigSpec( deviceChange: [ operation: :edit, device: RbVmomi::VIM::VirtualCdrom( backing: backing, key: d_obj.key, controllerKey: d_obj.controllerKey, connectable: RbVmomi::VIM::VirtualDeviceConnectInfo( startConnected: true, connected: true, allowGuestControl: true ) ) ] ) ) task.wait_for_completion end |
#start_vm(vm, _wait_on_port = 22) ⇒ Object
Starts the VM
72 73 74 75 |
# File 'lib/chef/provisioning/vsphere_driver/vsphere_helpers.rb', line 72 def start_vm(vm, _wait_on_port = 22) state = vm.runtime.powerState vm.PowerOnVM_Task.wait_for_completion unless state == 'poweredOn' end |
#stop_vm(vm, timeout = 600) ⇒ Object
Stops the VM
81 82 83 84 85 86 87 88 89 90 91 92 93 94 |
# File 'lib/chef/provisioning/vsphere_driver/vsphere_helpers.rb', line 81 def stop_vm(vm, timeout = 600) start = Time.now.utc return if vm.runtime.powerState == 'poweredOff' begin vm.ShutdownGuest until (Time.now.utc - start) > timeout || vm.runtime.powerState == 'poweredOff' print '.' sleep 2 end rescue vm.PowerOffVM_Task.wait_for_completion end end |
#traverse_folders_for_dc(folder, dcname) ⇒ False
Walks through the folders to if needed
116 117 118 119 120 121 122 123 124 125 126 127 |
# File 'lib/chef/provisioning/vsphere_driver/vsphere_helpers.rb', line 116 def traverse_folders_for_dc(folder, dcname) children = folder.children.find_all children.each do |child| if child.class == RbVmomi::VIM::Datacenter && child.name == dcname return child elsif child.class == RbVmomi::VIM::Folder dc = traverse_folders_for_dc(child, dcname) return dc if dc end end false end |
#traverse_folders_for_network(base, item) ⇒ Object
Search the item through the base’s children
497 498 499 500 501 502 503 504 505 506 507 508 509 510 511 512 |
# File 'lib/chef/provisioning/vsphere_driver/vsphere_helpers.rb', line 497 def traverse_folders_for_network(base, item) Chef::Log.debug("Searching #{item} in #{base.name}") case base when RbVmomi::VIM::Folder res = base.find(item) return res unless res.nil? base.childEntity.each do |child| res = traverse_folders_for_network(child, item) return res unless res.nil? end nil when RbVmomi::VIM::VmwareDistributedVirtualSwitch idx = base.summary.portgroupName.find_index(item) idx.nil? ? nil : base.portgroup[idx] end end |
#update_main_disk_size_for(vm, size_gb) ⇒ Object
Updates the main virtual disk for the VM This can only add capacity to the main disk, it is not possible to reduce the capacity.
319 320 321 322 323 324 325 326 327 328 329 330 331 332 333 334 335 336 337 338 339 340 341 |
# File 'lib/chef/provisioning/vsphere_driver/vsphere_helpers.rb', line 319 def update_main_disk_size_for(vm, size_gb) disk = vm.disks.first size_kb = size_gb.to_i * 1024 * 1024 if disk.capacityInKB > size_kb if size_gb.to_i > 0 msg = "Specified disk size #{size_gb}GB is inferior to the template's disk size (#{disk.capacityInKB / 1024**2}GB)." msg += "\nThe VM disk size will remain the same." Chef::Log.warn(msg) end return false end disk.capacityInKB = size_kb vm.ReconfigVM_Task( spec: RbVmomi::VIM.VirtualMachineConfigSpec( deviceChange: [ { operation: :edit, device: disk } ] ) ).wait_for_completion end |
#upload_file_to_vm(vm, username, password, local, remote) ⇒ Object
Upload a file to the VM using RbVmomi
532 533 534 535 536 537 538 539 540 541 542 543 544 545 546 547 548 549 550 551 552 553 554 555 556 557 |
# File 'lib/chef/provisioning/vsphere_driver/vsphere_helpers.rb', line 532 def upload_file_to_vm(vm, username, password, local, remote) auth = RbVmomi::VIM::NamePasswordAuthentication(username: username, password: password, interactiveSession: false) size = File.size(local) endpoint = $guest_op_managers[vim.pretty_inspect].fileManager.InitiateFileTransferToGuest( vm: vm, auth: auth, guestFilePath: remote, overwrite: true, fileAttributes: RbVmomi::VIM::GuestWindowsFileAttributes.new, fileSize: size ) uri = URI.parse(endpoint) http = Net::HTTP.new(uri.host, uri.port) http.use_ssl = true http.verify_mode = OpenSSL::SSL::VERIFY_NONE req = Net::HTTP::Put.new("#{uri.path}?#{uri.query}") req.body_stream = File.open(local) req['Content-Type'] = 'application/octet-stream' req['Content-Length'] = size res = http.request(req) unless res.is_a?(Net::HTTPSuccess) raise "Error: #{res.inspect} :: #{res.body} :: sending #{local} to #{remote} at #{vm.name} via #{endpoint} with a size of #{size}" end end |
#vim ⇒ Object
Establishes the connection to the vSphere cluster
28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 |
# File 'lib/chef/provisioning/vsphere_driver/vsphere_helpers.rb', line 28 def vim if @current_connection.nil? || @current_connection.serviceContent.sessionManager.currentSession.nil? @datacenter = nil puts "establishing connection to #{[:host]}" @current_connection = RbVmomi::VIM.connect str_conn = @current_connection.pretty_inspect # a string in the format of VIM(host ip) # we are caching guest operation managers in a global variable...terrible i know # this object is available from the serviceContent object on API version 5 forward # Its a singleton and if another connection is made for the same host and user # that object is not available on any subsequent connection # I could find no documentation that discusses this unless $guest_op_managers.key?(str_conn) $guest_op_managers[str_conn] = @current_connection.serviceContent.guestOperationsManager end end @current_connection end |
#virtual_disk_for(vm, datastore, size_gb) ⇒ Object
Creates a virtual disk for the VM
237 238 239 240 241 242 243 244 245 246 247 248 249 250 251 252 253 254 |
# File 'lib/chef/provisioning/vsphere_driver/vsphere_helpers.rb', line 237 def virtual_disk_for(vm, datastore, size_gb) idx = vm.disks.count RbVmomi::VIM::VirtualDeviceConfigSpec( operation: :add, fileOperation: :create, device: RbVmomi::VIM.VirtualDisk( key: idx, backing: RbVmomi::VIM.VirtualDiskFlatVer2BackingInfo( fileName: "[#{datastore}]", diskMode: 'persistent', thinProvisioned: true ), capacityInKB: size_gb * 1024 * 1024, controllerKey: 1000, unitNumber: idx ) ) end |