module VagrantPlugins module SyncedFolderNFSGuest module Action class PrepareNFSGuestSettings include Vagrant::Util::Retryable # This is only required to deal with the hard coded :nfs_host_ip and :nfs_machine_ip symbols # not using the impl name this stops them being availabe in nfsopts because the impl is called # nfs_guest. There has to be a better way. def initialize(app,env) @app = app @logger = Log4r::Logger.new("vagrant::action::vm::nfs_guest") end def call(env) @machine = env[:machine] @app.call(env) if using_nfs? @logger.info("Using NFS_guest, preparing NFS settings by reading host IP and machine IP") add_nfs_settings_to_env!(env) end end # We're using NFS if we have any synced folder with NFS configured. If # we are not using NFS we don't need to do the extra work to # populate these fields in the environment. def using_nfs? @machine.config.vm.synced_folders.any? { |_, opts| opts[:type] == :nfs_guest } end # Extracts the proper host and guest IPs for NFS mounts and stores them # in the environment for the SyncedFolder action to use them in # mounting. # # The ! indicates that this method modifies its argument. def add_nfs_settings_to_env!(env) adapter, host_ip = find_host_only_adapter machine_ip = nil machine_ip = read_machine_ip(adapter) if adapter raise Vagrant::Errors::NFSNoHostonlyNetwork if !host_ip || !machine_ip env[:nfs_guest_host_ip] = host_ip env[:nfs_guest_machine_ip] = machine_ip end # Finds first host only network adapter and returns its adapter number # and IP address # # @return [Integer, String] adapter number, ip address of found host-only adapter def find_host_only_adapter @machine.provider.driver.read_network_interfaces.each do |adapter, opts| if opts[:type] == :hostonly @machine.provider.driver.read_host_only_interfaces.each do |interface| if interface[:name] == opts[:hostonly] return adapter, interface[:ip] end end end end nil end # Returns the IP address of the guest by looking at vbox guest property # for the appropriate guest adapter. # # For DHCP interfaces, the guest property will not be present until the # guest completes # # @param [Integer] adapter number to read IP for # @return [String] ip address of adapter def read_machine_ip(adapter) # vbox guest properties are 0-indexed, while showvminfo network # interfaces are 1-indexed. go figure. guestproperty_adapter = adapter - 1 # we need to wait for the guest's IP to show up as a guest property. # retry thresholds are relatively high since we might need to wait # for DHCP, but even static IPs can take a second or two to appear. retryable(retry_options.merge(on: Vagrant::Errors::VirtualBoxGuestPropertyNotFound)) do @machine.provider.driver.read_guest_ip(guestproperty_adapter) end rescue Vagrant::Errors::VirtualBoxGuestPropertyNotFound # this error is more specific with a better error message directing # the user towards the fact that it's probably a reportable bug raise Vagrant::Errors::NFSNoGuestIP end # Separating these out so we can stub out the sleep in tests def retry_options {tries: 15, sleep: 1} end end end end end