class ChefProvisioningVsphere::VsphereDriver

Inherits the Chef::Provisioning::Driver attirbutes

Attributes

connect_options[R]

Public Class Methods

canonicalize_url(driver_url, config) click to toggle source

Create a new Vsphere provisioner. ## Parameters connect_options - hash of options to be passed to RbVmomi::VIM.connect

:host       - required - hostname of the vSphere API server
:port       - optional - port on the vSphere API server (default: 443)
:path        - optional - path on the vSphere API server (default: /sdk)
:use_ssl        - optional - true to use ssl in connection to vSphere API server (default: true)
:insecure   - optional - true to ignore ssl certificate validation errors in connection to vSphere API server (default: false)
:user       - required - user name to use in connection to vSphere API server
:password   - required - password to use in connection to vSphere API server
# File lib/chef/provisioning/vsphere_driver/driver.rb, line 38
def self.canonicalize_url(driver_url, config)
  config = symbolize_keys(config)
  [driver_url || URI::VsphereUrl.from_config(config).to_s, config]
end
from_url(driver_url, config) click to toggle source

Creates the new object via the URL

@param [String] driver_url The driver url to connect via. @param [Object] config The config to get to the machine you are creating.

# File lib/chef/provisioning/vsphere_driver/driver.rb, line 24
def self.from_url(driver_url, config)
  VsphereDriver.new(driver_url, config)
end
new(driver_url, config) click to toggle source

Initialize method for this class

@param [String] driver_url Location of where the driver needs to connect to. @param [Object] config The complete config of the machine options.

Calls superclass method
# File lib/chef/provisioning/vsphere_driver/driver.rb, line 73
def initialize(driver_url, config)
  super(driver_url, config)

  uri = URI(driver_url)
  @connect_options = {
    provider: "vsphere",
    host: uri.host,
    port: uri.port,
    use_ssl: uri.use_ssl,
    insecure: uri.insecure,
    path: uri.path,
  }

  if driver_options
    @connect_options[:user] = driver_options[:user]
    @connect_options[:password] = driver_options[:password]
  end
end
symbolize_keys(h) click to toggle source

Converts the keys from strings to symbols

@param [Object] h converts to key:value

# File lib/chef/provisioning/vsphere_driver/driver.rb, line 46
def self.symbolize_keys(h)
  Hash === h ? # rubocop:disable Style/MultilineTernaryOperator
    Hash[
      h.map do |k, v|
        [k.respond_to?(:to_sym) ? k.to_sym : k, symbolize_keys(v)] # rubocop:disable Style/NestedTernaryOperator
      end
    ] : h
end

Public Instance Methods

add_machine_spec_location(vm, machine_spec) click to toggle source

Adds machine spec location from other options.

@param [Object] machine_spec taken from Chef provisioning for all the `machine_spec`. @param [Object] vm taken from Chef provisioning for all the vm state.

# File lib/chef/provisioning/vsphere_driver/driver.rb, line 197
def add_machine_spec_location(vm, machine_spec)
  machine_spec.location = {
    "driver_url" => driver_url,
    "driver_version" => VERSION,
    "server_id" => vm.config.instanceUuid,
    "is_windows" => is_windows?(vm),
    "allocated_at" => Time.now.utc.to_s,
    "ipaddress" => vm.guest.ipAddress,
  }
end
all_ips_for(vm) click to toggle source

Flatten all IPs for a VM

@param [Object] vm The VM object.

# File lib/chef/provisioning/vsphere_driver/driver.rb, line 494
def all_ips_for(vm)
  vm.guest.net.map(&:ipAddress).flatten
end
allocate_machine(action_handler, machine_spec, machine_options) click to toggle source

Acquire a machine, generally by provisioning it. Returns a Machine object pointing at the machine, allowing useful actions like setup, converge, execute, file and directory. The Machine object will have a “node” property which must be saved to the server (if it is any different from the original node object).

## Parameters action_handler - the action_handler object that is calling this method; this

is generally a action_handler, but could be anything that can support the
ChefMetal::ActionHandler interface (i.e., in the case of the test
kitchen metal driver for acquiring and destroying VMs; see the base
class for what needs providing).

node - node object (deserialized json) representing this machine. If

the node has a provisioner_options hash in it, these will be used
instead of options provided by the provisioner.  TODO compare and
fail if different?
node will have node['normal']['provisioner_options'] in it with any options.
It is a hash with this format:
   -- provisioner_url: vsphere://host:port?ssl=[true|false]&insecure=[true|false]
   -- bootstrap_options: hash of options to pass to RbVmomi::VIM::VirtualMachine::CloneTask()
        :datacenter
        :resource_pool
        :cluster
        :datastore
        :template_name
        :template_folder
        :vm_folder
        :winrm {...} (not yet implemented)
        :ssh {...}

Example bootstrap_options for vSphere:
  TODO: add other CloneTask params, e.g.: datastore, annotation, resource_pool, ...
  'bootstrap_options' => {
    'template_name' =>'centos6.small',
    'template_folder' =>'Templates',
    'vm_folder' => 'MyApp'
  }

node['normal']['provisioner_output'] will be populated with information
about the created machine.  For vSphere, it is a hash with this
format:
   -- provisioner_url: vsphere:host:port?ssl=[true|false]&insecure=[true|false]
   -- vm_folder: name of the vSphere folder containing the VM
# File lib/chef/provisioning/vsphere_driver/driver.rb, line 144
def allocate_machine(action_handler, machine_spec, machine_options)
  machine_options = deep_symbolize(machine_options)
  merge_options! machine_options

  if machine_spec.location
    Chef::Log.warn(
      "Checking to see if #{machine_spec.location} has been created..."
    )
    vm = vm_for(machine_spec)
    if vm
      Chef::Log.warn "returning existing machine"
      return vm
    else
      Chef::Log.warn machine_msg(
        machine_spec.name,
        machine_spec.location["server_id"],
        "no longer exists.  Recreating ..."
      )
    end
  end
  bootstrap_options = machine_options[:bootstrap_options]

  action_handler.report_progress full_description(
    machine_spec, bootstrap_options
  )

  vm = find_or_create_vm(bootstrap_options, machine_spec, action_handler)

  add_machine_spec_location(vm, machine_spec)

  action_handler.performed_action(machine_msg(
                                    machine_spec.name,
                                    vm.config.instanceUuid,
                                    "created"
  ))
  vm
end
attempt_ip(machine_options, action_handler, vm, machine_spec) click to toggle source

Test out the IP to connect to.

@param [Object] action_handler TODO @param [Object] machine_options taken from Chef provisioning for all the bootstrap options. @param [Object] vm The VM object. @param [Object] machine_spec The spec required to talk to the VM.

# File lib/chef/provisioning/vsphere_driver/driver.rb, line 394
def attempt_ip(machine_options, action_handler, vm, machine_spec)
  vm_ip = ip_to_bootstrap(machine_options[:bootstrap_options], vm)
  machine_spec.location["ipaddress"] = vm_ip

  wait_for_ip(vm, machine_options, machine_spec, action_handler)

  unless has_ip?(vm_ip, vm)
    action_handler.report_progress "rebooting..."
    if vm.guest.toolsRunningStatus != "guestToolsRunning"
      msg = "tools have stopped. current power state is "
      msg << vm.runtime.powerState
      msg << " and tools state is "
      msg << vm.guest.toolsRunningStatus
      msg << ". powering up server..."
      action_handler.report_progress(msg)
      vsphere_helper.start_vm(vm)
    else
      restart_server(action_handler, machine_spec, machine_options)
    end
    wait_for_ip(vm, machine_options, machine_spec, action_handler)
  end
end
connect_to_machine(machine_spec, machine_options) click to toggle source

Connect to machine without acquiring it

@param [Object] machine_options The machine options required to start the VM. @param [Object] machine_spec The machine spec required to start the VM.

# File lib/chef/provisioning/vsphere_driver/driver.rb, line 510
def connect_to_machine(machine_spec, machine_options)
  machine_options = deep_symbolize(machine_options)
  merge_options! machine_options
  machine_for(machine_spec, machine_options)
end
deep_symbolize(hash_like) click to toggle source

Converts the keys from strings to symbols

@param [Object] hash_like converts to key:value

# File lib/chef/provisioning/vsphere_driver/driver.rb, line 58
def deep_symbolize(hash_like)
  return {} if hash_like.nil?

  r = {}
  hash_like.each do |key, value|
    value = deep_symbolize(value) if value.respond_to?(:values)
    r[key.to_sym] = value
  end
  r
end
destroy_machine(action_handler, machine_spec, machine_options) click to toggle source

Destroy the Machine

@param [Object] machine_options The machine options required to start the VM. @param [Object] machine_spec The machine spec required to start the VM. @param [Object] action_handler TODO

# File lib/chef/provisioning/vsphere_driver/driver.rb, line 521
def destroy_machine(action_handler, machine_spec, machine_options)
  machine_options = deep_symbolize(machine_options)
  merge_options! machine_options
  vm = vm_for(machine_spec)
  if vm
    action_handler.perform_action "Delete VM [#{vm.parent.name}/#{vm.name}]" do
      begin
        vsphere_helper.stop_vm(vm, machine_options[:stop_timeout])
        vm.Destroy_Task.wait_for_completion
      rescue RbVmomi::Fault => fault
        raise fault unless fault.fault.class.wsdl_name == "ManagedObjectNotFound"
      ensure
        machine_spec.location = nil
      end
    end
  end
  strategy = convergence_strategy_for(machine_spec, machine_options)
  strategy.cleanup_convergence(action_handler, machine_spec)
end
find_or_create_vm(bootstrap_options, machine_spec, action_handler) click to toggle source

Verify if machine is there or create it.

@param [Object] bootstrap_options taken from Chef provisioning for all the bootstrap options. @param [Object] machine_spec taken from Chef provisioning for all the `machine_spec`. @param [Object] action_handler taken from Chef provisioning for TODO.

# File lib/chef/provisioning/vsphere_driver/driver.rb, line 213
def find_or_create_vm(bootstrap_options, machine_spec, action_handler)
  vm = vsphere_helper.find_vm(
    bootstrap_options[:vm_folder],
    machine_spec.name
  )
  if vm
    Chef::Log.info machine_msg(
      machine_spec.name,
      vm.config.instanceUuid,
      "already created"
    )
  else
    vm = clone_vm(
      action_handler,
      bootstrap_options,
      machine_spec
    )
  end
  vm
end
full_description(machine_spec, bootstrap_options) click to toggle source

Creates a full description of the machine.

@param [Object] bootstrap_options taken from Chef provisioning for all the bootstrap options. @param [Object] machine_spec taken from Chef provisioning for all the `machine_spec`.

# File lib/chef/provisioning/vsphere_driver/driver.rb, line 238
def full_description(machine_spec, bootstrap_options)
  description = ["creating machine #{machine_spec.name} on #{driver_url}"]
  bootstrap_options.to_hash.each_pair do |key, value|
    if value.is_a?(Hash)
      temp_value = value.clone
      temp_value[:password] = "*********" if value.key?(:password)
    else
      temp_value = value
    end
    description << "  #{key}: #{temp_value.inspect}"
  end
  description
end
has_ip?(ip, vm) click to toggle source

Does the VM have this IP?

@param [Object] vm The VM object. @param [String] ip IP to see if it's in the map.

# File lib/chef/provisioning/vsphere_driver/driver.rb, line 502
def has_ip?(ip, vm)
  all_ips_for(vm).include?(ip)
end
machine_msg(name, id, action) click to toggle source

Creates a string of specific Machine information

@param [String] name The name of the machine @param [String] id The ID of the machine @param [Object] action TODO @return [String] “Machine - ACTION - NAME (UUID on URL)”

# File lib/chef/provisioning/vsphere_driver/driver.rb, line 258
def machine_msg(name, id, action)
  "Machine - #{action} - #{name} (#{id} on #{driver_url})"
end
merge_options!(machine_options) click to toggle source

Squishes the options to one large hash

@param [Object] machine_options taken from Chef provisioning for all the `machine_options`

# File lib/chef/provisioning/vsphere_driver/driver.rb, line 185
def merge_options!(machine_options)
  @config = Cheffish::MergedConfig.new(
    { machine_options: machine_options },
    @config
  )
  @config = deep_symbolize(@config.to_h)
end
ready_machine(action_handler, machine_spec, machine_options) click to toggle source

Sets the machine to ready state. (Creates the machine but does not bootstrap Chef into it.)

@param [Object] action_handler TODO @param [Object] machine_options taken from Chef provisioning for all the bootstrap options. @param [Object] machine_spec taken from Chef provisioning for all the `machine_spec`.

# File lib/chef/provisioning/vsphere_driver/driver.rb, line 267
def ready_machine(action_handler, machine_spec, machine_options)
  machine_options = deep_symbolize(machine_options)
  merge_options! machine_options

  vm = start_machine(action_handler, machine_spec, machine_options)
  if vm.nil?
    raise "Machine #{machine_spec.name} does not have a server "\
    "associated with it, or server does not exist."
  end

  bootstrap_options = machine_options[:bootstrap_options]

  transport_respond?(
    machine_options,
    vm,
    action_handler,
    machine_spec
  )

  machine = machine_for(machine_spec, machine_options)

  setup_extra_nics(action_handler, bootstrap_options, vm, machine, machine_spec)

  if has_static_ip(bootstrap_options) && !is_windows?(vm)
    setup_ubuntu_dns(machine, bootstrap_options, machine_spec)
  end

  ## Check if true available after added nic
  vm_helper.open_port?(machine_spec.location["ipaddress"], vm_helper.port) unless machine_spec.location["ipaddress"].nil?
  machine
end
restart_server(action_handler, machine_spec, machine_options) click to toggle source

Restart the Machine

@param [Object] machine_options The machine options required to start the VM. @param [Object] machine_spec The machine spec required to start the VM. @param [Object] action_handler TODO

# File lib/chef/provisioning/vsphere_driver/driver.rb, line 579
def restart_server(action_handler, machine_spec, machine_options)
  action_handler.perform_action "restart machine #{machine_spec.name} (#{driver_url})" do
    stop_machine(action_handler, machine_spec, machine_options)
    start_machine(action_handler, machine_spec, machine_options)
    machine_spec.location["started_at"] = Time.now.utc.to_s
  end
end
setup_extra_nics(action_handler, bootstrap_options, vm, machine, machine_spec) click to toggle source

Creates new NICs for the machine

@param [Object] action_handler TODO @param [Object] bootstrap_options taken from Chef provisioning for all the bootstrap options. @param [Object] vm The VM object. @param [Object] machine The machine object.

# File lib/chef/provisioning/vsphere_driver/driver.rb, line 305
def setup_extra_nics(action_handler, bootstrap_options, vm, machine, machine_spec)
  networks = bootstrap_options[:network_name]
  networks = [networks] if networks.is_a?(String)
  return if networks.nil? || networks.count < 2

  new_nics = vsphere_helper.add_extra_nic(
    action_handler,
    vm_template_for(bootstrap_options),
    bootstrap_options,
    vm
  )
  if is_windows?(vm) && !new_nics.nil? && vm_helper.open_port?(machine_spec.location["ipaddress"], vm_helper.port)
    new_nics.each do |nic|
      nic_label = nic.device.deviceInfo.label
      machine.execute_always(
        "Disable-Netadapter -Name '#{nic_label}' -Confirm:$false"
      )
    end
  end
end
start_machine(action_handler, machine_spec, machine_options) click to toggle source

Start the Machine

@param [Object] machine_options The machine options required to start the VM. @param [Object] machine_spec The machine spec required to start the VM. @param [Object] action_handler TODO

# File lib/chef/provisioning/vsphere_driver/driver.rb, line 562
def start_machine(action_handler, machine_spec, machine_options)
  machine_options = deep_symbolize(machine_options)
  merge_options! machine_options
  vm = vm_for(machine_spec)
  if vm
    action_handler.perform_action "Power on VM [#{vm.parent.name}/#{vm.name}]" do
      vsphere_helper.start_vm(vm, machine_options[:bootstrap_options][:ssh][:port])
    end
  end
  vm
end
stop_machine(action_handler, machine_spec, machine_options) click to toggle source

Stop the Machine

@param [Object] machine_options The machine options required to start the VM. @param [Object] machine_spec The machine spec required to start the VM. @param [Object] action_handler TODO

# File lib/chef/provisioning/vsphere_driver/driver.rb, line 546
def stop_machine(action_handler, machine_spec, machine_options)
  machine_options = deep_symbolize(machine_options)
  merge_options! machine_options
  vm = vm_for(machine_spec)
  if vm
    action_handler.perform_action "Shutdown guest OS and power off VM [#{vm.parent.name}/#{vm.name}]" do
      vsphere_helper.stop_vm(vm, machine_options[:stop_timeout])
    end
  end
end
transport_respond?( machine_options, vm, action_handler, machine_spec ) click to toggle source

Is the transport layer ready?

@param [Object] action_handler TODO @param [Object] machine_spec taken from Chef provisioning for all the bootstrap options. @param [Object] vm The VM object. @param [Object] machine_options The options required to talk to the VM.

# File lib/chef/provisioning/vsphere_driver/driver.rb, line 332
def transport_respond?(
  machine_options,
  vm,
  action_handler,
  machine_spec
)
  bootstrap_options = machine_options[:bootstrap_options]

  # this waits for vmware tools to start and the vm to presebnt an ip
  # This may just be the ip of a newly cloned machine
  # Customization below may change this to a valid ip
  wait_until_ready(action_handler, machine_spec, machine_options, vm)

  if !machine_spec.location["ipaddress"] || !has_ip?(machine_spec.location["ipaddress"], vm)
    # find the ip we actually want
    # this will be the static ip to assign
    # or the ip reported back by the vm if using dhcp
    # it *may* be nil if just cloned
    vm_ip = ip_to_bootstrap(bootstrap_options, vm) || vm.guest.ipAddress
    machine_spec.location["ipaddress"] = vm_ip
    transport = nil
    unless vm_ip.nil?
      transport = transport_for(machine_spec, bootstrap_options[:ssh], vm_ip)
    end

    unless !transport.nil? && transport.available? && has_ip?(vm_ip, vm)
      attempt_ip(machine_options, action_handler, vm, machine_spec)
    end
    machine_spec.location["ipaddress"] = vm_ip # vm.guest.ipAddress vmWare ip_address here can be 0.0.0.0
    action_handler.report_progress(
      "IP address obtained: #{machine_spec.location['ipaddress']}"
    )
  end

  wait_for_domain(bootstrap_options, vm, machine_spec, action_handler)

  begin
    wait_for_transport(action_handler, machine_spec, machine_options, vm)
  rescue Timeout::Error
    # Only ever reboot once, and only if it's been less than 10 minutes since we stopped waiting
    if machine_spec.location["started_at"] ||
        remaining_wait_time(machine_spec, machine_options) < -(10 * 60)
      raise
    else
      Chef::Log.warn(machine_msg(
                       machine_spec.name,
                       vm.config.instanceUuid,
                       "started but SSH did not come up.  Rebooting..."
      ))
      restart_server(action_handler, machine_spec, machine_options)
      wait_until_ready(action_handler, machine_spec, machine_options, vm)
      wait_for_transport(action_handler, machine_spec, machine_options, vm)
    end
  end
end
vm_helper() click to toggle source

Creates a new vm_helper if not already there

# File lib/chef/provisioning/vsphere_driver/driver.rb, line 96
def vm_helper
  @vm_helper ||= ChefProvisioningVsphere::VmHelper.new
end
wait_for_domain(bootstrap_options, vm, machine_spec, action_handler) click to toggle source

Wait for the Windows Domain.

@param [Object] action_handler TODO @param [Object] bootstrap_options taken from Chef provisioning for all the bootstrap options. @param [Object] vm The VM object. @param [Object] machine_spec The specs to connect to bootstrap machine

# File lib/chef/provisioning/vsphere_driver/driver.rb, line 423
def wait_for_domain(bootstrap_options, vm, machine_spec, action_handler)
  return unless bootstrap_options[:customization_spec]

  domain = if bootstrap_options[:customization_spec].is_a?(String) && is_windows?(vm)
             spec = vsphere_helper.find_customization_spec(bootstrap_options[:customization_spec])
             spec.identity.identification.joinDomain
           elsif bootstrap_options[:customization_spec].is_a?(String) && !is_windows?(vm)
             spec = vsphere_helper.find_customization_spec(bootstrap_options[:customization_spec])
             spec.identity.domain
           else
             bootstrap_options[:customization_spec][:domain]
           end

  return unless domain

  if is_windows?(vm) && domain != "local"
    start = Time.now.utc
    trimmed_name = machine_spec.name.byteslice(0, 15)
    expected_name = "#{trimmed_name}.#{domain}"
    action_handler.report_progress(
      "waiting to domain join and be named #{expected_name}"
    )
    until (Time.now.utc - start) > 30 ||
        (vm.guest.hostName == expected_name)
      print "."
      sleep 5
    end
  end
end
wait_for_ip(vm, machine_options, machine_spec, action_handler) click to toggle source

Wait for another IP allocation.

@param [Object] action_handler TODO @param [Object] vm The VM object. @param [Object] machine_options The machine options required to start the VM. @param [Object] machine_spec The machine spec required to start the VM.

# File lib/chef/provisioning/vsphere_driver/driver.rb, line 459
def wait_for_ip(vm, machine_options, machine_spec, action_handler)
  bootstrap_options = machine_options[:bootstrap_options]
  ip_to_bootstrap(bootstrap_options, vm)
  ready_timeout = machine_options[:ready_timeout] || 300
  msg1 = "waiting up to #{ready_timeout} seconds for customization"
  msg2 = " and find #{machine_spec.location['ipaddress']}" unless machine_spec.location["ipaddress"].nil? # unless vm_ip == vm.guest.ipAddress # RuntimeError: can't modify frozen String
  msg = [msg1, msg2].join
  action_handler.report_progress msg

  vm_ip = ip_to_bootstrap(bootstrap_options, vm) || vm.guest.ipAddress
  machine_spec.location["ipaddress"] = vm_ip
  until transport_for(
    machine_spec,
    machine_options[:bootstrap_options][:ssh],
    vm_ip
  ).available? || remaining_wait_time(machine_spec, machine_options) < 0
    action_handler.report_progress(
      "IP addresses found: #{all_ips_for(vm)}"
    )
    vm_ip = ip_to_bootstrap(bootstrap_options, vm) || vm.guest.ipAddress
    machine_spec.location["ipaddress"] = vm_ip
    if has_ip?(vm_ip, vm)
      transport_for(
        machine_spec,
        machine_options[:bootstrap_options][:ssh],
        vm_ip
      ).available?
    end
    sleep 5
  end
end

Protected Instance Methods

bootstrap_ip_ready_timeout(bootstrap_options) click to toggle source

Setting a bootstrap ip timeout, default: 300

@param [Object] bootstrap_options The bootstrap options from Chef-Provisioning

# File lib/chef/provisioning/vsphere_driver/driver.rb, line 919
def bootstrap_ip_ready_timeout(bootstrap_options)
  bootstrap_options.key?(:ip_ready_timeout) ? bootstrap_options[:ip_ready_timeout].to_i : 300
end
bootstrap_ipv4_timeout(bootstrap_options) click to toggle source

Setting a bootstrap ipv4 timeout, default: 30

@param [Object] bootstrap_options The bootstrap options from Chef-Provisioning

# File lib/chef/provisioning/vsphere_driver/driver.rb, line 912
def bootstrap_ipv4_timeout(bootstrap_options)
  bootstrap_options.key?(:ipv4_timeout) ? bootstrap_options[:ipv4_timeout].to_i : 30
end
clone_vm(action_handler, bootstrap_options, machine_spec) click to toggle source
# File lib/chef/provisioning/vsphere_driver/driver.rb, line 666
def clone_vm(action_handler, bootstrap_options, machine_spec)
  machine_name = machine_spec.name
  vm_template = vm_template_for(bootstrap_options)

  spec_builder = CloneSpecBuilder.new(vsphere_helper, action_handler)
  clone_spec = spec_builder.build(vm_template, machine_name, bootstrap_options)
  Chef::Log.debug("Clone spec: #{clone_spec.pretty_inspect}")

  vm_folder = vsphere_helper.find_folder(bootstrap_options[:vm_folder])
  last_progress = 0
  vm_template.CloneVM_Task(
    name: machine_name,
    folder: vm_folder,
    spec: clone_spec
  ).wait_for_progress do |progress|
    if (progress.is_a? Numeric) && (progress / 10).floor != (last_progress / 10).floor
      print "\n#{machine_name} progress: #{progress}%"
      last_progress = progress
    end
  end
  print "\n#{machine_name} done!\n\n"

  vm = vsphere_helper.find_vm(vm_folder, machine_name)
  add_machine_spec_location(vm, machine_spec)

  vsphere_helper.update_main_disk_size_for(vm, bootstrap_options[:main_disk_size_gb])
  vsphere_helper.set_additional_disks_for(vm, bootstrap_options[:datastore], bootstrap_options[:additional_disk_size_gb])
  vsphere_helper.set_initial_iso(vm, bootstrap_options[:initial_iso_file])

  vm
end
convergence_strategy_for(machine_spec, machine_options) click to toggle source
# File lib/chef/provisioning/vsphere_driver/driver.rb, line 739
def convergence_strategy_for(machine_spec, machine_options)
  require "chef/provisioning/convergence_strategy/install_msi"
  require "chef/provisioning/convergence_strategy/install_cached"
  require "chef/provisioning/convergence_strategy/no_converge"

  mopts = machine_options[:convergence_options].to_hash.dup
  if mopts[:chef_server]
    mopts[:chef_server] = mopts[:chef_server].to_hash.dup
    mopts[:chef_server][:options] = mopts[:chef_server][:options].to_hash.dup if mopts[:chef_server][:options]
  end

  unless machine_spec.location
    return Chef::Provisioning::ConvergenceStrategy::NoConverge.new(
      mopts, config
    )
  end

  if machine_spec.location["is_windows"]
    Chef::Provisioning::ConvergenceStrategy::InstallMsi.new(
      mopts, config
    )
  else
    Chef::Provisioning::ConvergenceStrategy::InstallCached.new(
      mopts, config
    )
  end
end
create_ssh_transport(host, options) click to toggle source

Return a SSH transport for the machine

@param [String] host The host the VM is connecting to @param [Object] options Options that are required to connect to the host from Chef-Provisioning

# File lib/chef/provisioning/vsphere_driver/driver.rb, line 852
def create_ssh_transport(host, options)
  require "chef/provisioning/transport/ssh"
  ssh_user = options[:user]
  options = options.each_with_object({}) { |(k, v), memo| memo[k.to_sym] = v; }
  Chef::Provisioning::Transport::SSH.new(
    host,
    ssh_user,
    options.to_hash,
    @config[:machine_options][:sudo] ? { prefix: "sudo " } : {},
    config
  )
end
create_winrm_transport(host, options) click to toggle source

Create a robust WinRM transport connect, so so many options to make a simple connection

@param [String] host The host the VM is connecting to @param [Object] options Options that are required to connect to the host from Chef-Provisioning

# File lib/chef/provisioning/vsphere_driver/driver.rb, line 814
def create_winrm_transport(host, options)
  require "chef/provisioning/transport/winrm"
  winrm_transport = if options[:port] == 5986
                      :ssl
                    else
                      options[:winrm_transport].nil? ? :negotiate : options[:winrm_transport].to_sym
                    end
  port = options[:port] || vm_helper.port
  winrm_options = {
    user: (options[:user]).to_s,
    pass: options[:password],
  }
  if options[:winrm_opts].nil?
    opt = options[:user].include?('\\') ? :disable_sspi : :basic_auth_only
    winrm_options[opt] = true
  else
    winrm_options.merge!(options[:winrm_opts])
  end
  scheme = winrm_transport == :ssl ? "https" : "http"
  endpoint = URI::Generic.build(
    scheme: scheme,
    host: host,
    port: port,
    path: "/wsman"
  ).to_s

  Chef::Provisioning::Transport::WinRM.new(
    endpoint,
    winrm_transport,
    winrm_options,
    config
  )
end
has_static_ip(bootstrap_options) click to toggle source

Verify static IP.

@param [Object] bootstrap_options The bootstrap options required to start the VM.

# File lib/chef/provisioning/vsphere_driver/driver.rb, line 621
def has_static_ip(bootstrap_options)
  if bootstrap_options.key?(:customization_spec)
    bootstrap_options = bootstrap_options[:customization_spec]

    if bootstrap_options.is_a?(String)
      spec = vsphere_helper.find_customization_spec(bootstrap_options)
      return spec.nicSettingMap[0].adapter.ip.is_a?(RbVmomi::VIM::CustomizationFixedIp)
    elsif bootstrap_options.key?(:ipsettings)
      bootstrap_options = bootstrap_options[:ipsettings]
      return bootstrap_options.key?(:ip)
    end
  end
  false
end
ip_to_bootstrap(bootstrap_options, vm) click to toggle source

Find the IP to bootstrap against

@param [Object] bootstrap_options The bootstrap options from Chef-Provisioning @param [Object] vm The VM object from Chef-Provisioning

# File lib/chef/provisioning/vsphere_driver/driver.rb, line 869
def ip_to_bootstrap(bootstrap_options, vm)
  start_time = Time.now.utc
  timeout = bootstrap_ip_ready_timeout(bootstrap_options)
  vm_helper.find_port?(vm, bootstrap_options) unless vm_helper.port?

  # First get the IP to be tested
  if has_static_ip(bootstrap_options)
    if bootstrap_options[:customization_spec].is_a?(String)
      spec = vsphere_helper.find_customization_spec(bootstrap_options[:customization_spec])
      vm_ip = spec.nicSettingMap[0].adapter.ip.ipAddress
    else
      ## Check if true available
      vm_ip = bootstrap_options[:customization_spec][:ipsettings][:ip]
    end
  elsif use_ipv4_during_bootstrap?(bootstrap_options)
    vm_ip = wait_for_ipv4(bootstrap_ipv4_timeout(bootstrap_options), vm)
  else
    until vm_guest_ip?(vm) || Time.now.utc - start_time > timeout
      print "."
      sleep 1
    end
    vm_ip = vm.guest.ipAddress
  end

  # Then check that it is reachable
  until Time.now.utc - start_time > timeout
    print "."
    return vm_ip.to_s if vm_helper.open_port?(vm_ip, vm_helper.port, 1)
    sleep 1
  end
  raise "Timed out (#{timeout}s) waiting for ip #{vm_ip} to be connectable"
end
is_windows?(vm) click to toggle source
# File lib/chef/provisioning/vsphere_driver/driver.rb, line 734
def is_windows?(vm)
  return false if vm.nil?
  vm.config.guestId.start_with?("win")
end
machine_for(machine_spec, machine_options) click to toggle source
# File lib/chef/provisioning/vsphere_driver/driver.rb, line 712
def machine_for(machine_spec, machine_options)
  if machine_spec.location.nil?
    raise "Server for node #{machine_spec.name} has not been created!"
  end

  transport = transport_for(
    machine_spec,
    machine_options[:bootstrap_options][:ssh]
  )
  strategy = convergence_strategy_for(machine_spec, machine_options)

  if machine_spec.location["is_windows"]
    Chef::Provisioning::Machine::WindowsMachine.new(
      machine_spec, transport, strategy
    )
  else
    Chef::Provisioning::Machine::UnixMachine.new(
      machine_spec, transport, strategy
    )
  end
end
remaining_wait_time(machine_spec, machine_options) click to toggle source
# File lib/chef/provisioning/vsphere_driver/driver.rb, line 636
def remaining_wait_time(machine_spec, machine_options)
  if machine_spec.location["started_at"]
    (machine_options[:start_timeout] || 600) -
      (Time.now.utc - Time.parse(machine_spec.location["started_at"]))
  else
    (machine_options[:create_timeout] || 600) -
      (Time.now.utc - Time.parse(machine_spec.location["allocated_at"]))
  end
end
setup_ubuntu_dns(machine, bootstrap_options, _machine_spec) click to toggle source

Ubuntu requires some…hacking to get DNS to work.

@param [Object] bootstrap_options The bootstrap options required to start the VM. @param [Object] _machine_spec The machine spec required to start the VM. @param [Object] machine Machine object to connect to.

# File lib/chef/provisioning/vsphere_driver/driver.rb, line 594
def setup_ubuntu_dns(machine, bootstrap_options, _machine_spec)
  host_lookup = machine.execute_always("host google.com")
  if host_lookup.exitstatus != 0
    if host_lookup.stdout.include?("setlocale: LC_ALL")
      machine.execute_always("locale-gen en_US && update-locale LANG=en_US")
    end
    distro = machine.execute_always("lsb_release -i | sed -e 's/Distributor ID://g'").stdout.strip
    Chef::Log.info "Found distro:#{distro}"
    if distro == "Ubuntu"
      distro_version = machine.execute_always("lsb_release -r | sed -e s/[^0-9.]//g").stdout.strip.to_f
      Chef::Log.info "Found distro version:#{distro_version}"
      if distro_version >= 12.04
        Chef::Log.info "Ubuntu version 12.04 or greater. Need to patch DNS."
        interfaces_file = "/etc/network/interfaces"
        nameservers = bootstrap_options[:customization_spec][:ipsettings][:dnsServerList].join(" ")
        machine.execute_always("if ! cat #{interfaces_file} | grep -q dns-search ; then echo 'dns-search #{bootstrap_options[:customization_spec][:domain]}' >> #{interfaces_file} ; fi")
        machine.execute_always("if ! cat #{interfaces_file} | grep -q dns-nameservers ; then echo 'dns-nameservers #{nameservers}' >> #{interfaces_file} ; fi")
        machine.execute_always("/etc/init.d/networking restart")
        machine.execute_always("apt-get -qq update")
      end
    end
  end
end
transport_for( machine_spec, remoting_options, ip = machine_spec.location["ipaddress"] ) click to toggle source

Figure out the transport system to send to the VM

@param [Object] machine_spec The machine_spec from Chef-Provisioning @param [Object] remoting_options Options for remote connections @param [String] ip The IP to connect to.

# File lib/chef/provisioning/vsphere_driver/driver.rb, line 798
def transport_for(
  machine_spec,
  remoting_options,
  ip = machine_spec.location["ipaddress"]
)
  if machine_spec.location["is_windows"]
    create_winrm_transport(ip, remoting_options)
  else
    create_ssh_transport(ip, remoting_options)
  end
end
use_ipv4_during_bootstrap?(bootstrap_options) click to toggle source

Force IPv4 a bootstrap, default: false

@param [Object] bootstrap_options The bootstrap options from Chef-Provisioning

# File lib/chef/provisioning/vsphere_driver/driver.rb, line 905
def use_ipv4_during_bootstrap?(bootstrap_options)
  bootstrap_options.key?(:bootstrap_ipv4) && bootstrap_options[:bootstrap_ipv4] == true
end
vm_for(machine_spec) click to toggle source
# File lib/chef/provisioning/vsphere_driver/driver.rb, line 660
def vm_for(machine_spec)
  if machine_spec.location
    vsphere_helper.find_vm_by_id(machine_spec.location["server_id"])
  end
end
vm_guest_ip?(vm) click to toggle source

What is the VM guest IP

@param [Object] vm The VM object from Chef-Provisioning

# File lib/chef/provisioning/vsphere_driver/driver.rb, line 950
def vm_guest_ip?(vm)
  vm.guest.guestState == "running" && vm.guest.toolsRunningStatus == "guestToolsRunning" && !vm.guest.ipAddress.nil?
end
vm_template_for(bootstrap_options) click to toggle source
# File lib/chef/provisioning/vsphere_driver/driver.rb, line 705
def vm_template_for(bootstrap_options)
  template_folder = bootstrap_options[:template_folder]
  template_name   = bootstrap_options[:template_name]
  vsphere_helper.find_vm(template_folder, template_name) ||
    raise("vSphere VM Template not found [#{template_folder}/#{template_name}]")
end
vsphere_helper() click to toggle source
# File lib/chef/provisioning/vsphere_driver/driver.rb, line 698
def vsphere_helper
  @vsphere_helper ||= VsphereHelper.new(
    connect_options,
    config[:machine_options][:bootstrap_options][:datacenter]
  )
end
wait_for_ipv4(timeout_seconds, vm) click to toggle source

Wait for IPv4 address

@param [String] timeout_seconds A timeout in seconds, an error will be raised if it is reached @param [Object] vm The VM object from Chef-Provisioning

# File lib/chef/provisioning/vsphere_driver/driver.rb, line 927
def wait_for_ipv4(timeout_seconds, vm)
  Chef::Log.info("Waiting #{timeout_seconds}s for ipv4 address.")

  start_time = Time.now.utc
  while (Time.now.utc - start_time) <= timeout_seconds
    # print '.'
    sleep 5
    vm_ips = all_ips_for(vm)
    Chef::Log.info("Found IP address(es): #{vm_ips}")
    next unless vm_guest_ip?(vm)
    vm_ips.each do |vm_ip|
      if IPAddr.new(vm_ip).ipv4?
        Chef::Log.info("Found ipv4 address: #{vm_ip}")
        return vm_ip
      end
    end
  end
  raise "Timed out waiting for ipv4 address!"
end
wait_for_transport(action_handler, machine_spec, machine_options, vm) click to toggle source

Wait for the transport connection to happen

@param [Object] machine_spec The machine_spec from Chef-Provisioning @param [Object] machine_options Options for remote connections @param [Object] vm The VM object from Chef-Provisioning @param [Object] action_handler taken from Chef provisioning for TODO.

# File lib/chef/provisioning/vsphere_driver/driver.rb, line 773
def wait_for_transport(action_handler, machine_spec, machine_options, vm)
  vm_helper.find_port?(vm, machine_options[:bootstrap_options]) if vm_helper.port.nil?
  transport = transport_for(
    machine_spec,
    machine_options[:bootstrap_options][:ssh]
  )
  unless transport.available?
    if action_handler.should_perform_actions
      action_handler.report_progress "waiting for #{machine_spec.name} (#{vm.config.instanceUuid} on #{driver_url}) to be connectable (transport up and running) ..."

      until remaining_wait_time(machine_spec, machine_options) < 0 || transport.available?
        print "."
        sleep 5
      end

      action_handler.report_progress "#{machine_spec.name} is now connectable"
    end
  end
end
wait_until_ready(action_handler, machine_spec, machine_options, vm) click to toggle source
# File lib/chef/provisioning/vsphere_driver/driver.rb, line 646
def wait_until_ready(action_handler, machine_spec, machine_options, vm)
  if vm.guest.toolsRunningStatus != "guestToolsRunning"
    if action_handler.should_perform_actions
      action_handler.report_progress "waiting for #{machine_spec.name} (#{vm.config.instanceUuid} on #{driver_url}) to be ready ..."
      until remaining_wait_time(machine_spec, machine_options) < 0 ||
          (vm.guest.toolsRunningStatus == "guestToolsRunning" && vm.guest.ipAddress && !vm.guest.ipAddress.empty?)
        print "."
        sleep 5
      end
      action_handler.report_progress "#{machine_spec.name} is now ready"
    end
  end
end