class ChefProvisioningVsphere::VsphereDriver
Inherits the Chef::Provisioning::Driver attirbutes
Attributes
Public Class Methods
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
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
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.
# 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
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
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
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
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
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 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
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 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
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
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
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
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
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
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 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
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 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 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
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
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 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 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
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
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
# 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
# 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
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 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
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
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
# 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
# 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
# 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
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
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
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
# 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
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
# 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
# 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 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 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
# 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