def machine_config(name, provider, boxes, data_path=nil)
      keys = @keys.dup

      sub_machine = @config.vm.defined_vms[name]
      if !sub_machine
        raise Errors::MachineNotFound,
          name: name, provider: provider
      end

      provider_plugin  = nil
      provider_cls     = nil
      provider_options = {}
      box_formats      = nil
      if provider != nil
        provider_plugin  = Vagrant.plugin("2").manager.providers[provider]
        if !provider_plugin
          providers  = Vagrant.plugin("2").manager.providers.to_hash.keys
          if providers
            providers_str = providers.join(', ')
          else
            providers_str = "N/A"
          end

          if providers.include? provider.downcase
            raise Errors::ProviderNotFoundSuggestion,
              machine: name, provider: provider,
              suggestion: provider.downcase, providers: providers_str
          end

          raise Errors::ProviderNotFound,
            machine: name, provider: provider, providers: providers_str
        end

        provider_cls     = provider_plugin[0]
        provider_options = provider_plugin[1]
        box_formats      = provider_options[:box_format] || provider

        # Test if the provider is usable or not
        begin
          provider_cls.usable?(true)
        rescue Errors::VagrantError => e
          raise Errors::ProviderNotUsable,
            machine: name.to_s,
            provider: provider.to_s,
            message: e.to_s
        end
      end

      # Add the sub-machine configuration to the loader and keys
      vm_config_key = "#{object_id}_machine_#{name}"
      @loader.set(vm_config_key, sub_machine.config_procs)
      keys << vm_config_key

      # Load once so that we can get the proper box value
      config, config_warnings, config_errors = @loader.load(keys)

      # Track the original box so we know if we changed
      box = nil
      initial_box = original_box = config.vm.box
      initial_version = original_version = config.vm.box_version

      # Check if this machine has a local box metadata file
      # describing the existing guest. If so, load it and
      # set the box name and version to allow the actual
      # box in use to be discovered.
      if data_path
        meta_file = data_path.join("box_meta")
        if meta_file.file?
          box_meta = JSON.parse(meta_file.read)
          config.vm.box = box_meta["name"]
          config.vm.box_version = box_meta["version"]
        end
      end

      # The proc below loads the box and provider overrides. This is
      # in a proc because it may have to recurse if the provider override
      # changes the box.
      load_box_proc = lambda do
        local_keys = keys.dup

        # Load the box Vagrantfile, if there is one
        if config.vm.box && boxes
          box = boxes.find(config.vm.box, box_formats, config.vm.box_version)
          if box
            box_vagrantfile = find_vagrantfile(box.directory)
            if box_vagrantfile && !config.vm.ignore_box_vagrantfile
              box_config_key =
                "#{boxes.object_id}_#{box.name}_#{box.provider}".to_sym
              @loader.set(box_config_key, box_vagrantfile)
              local_keys.unshift(box_config_key)
              config, config_warnings, config_errors = @loader.load(local_keys)
            elsif box_vagrantfile && config.vm.ignore_box_vagrantfile
              @logger.warn("Ignoring #{box.name} provided Vagrantfile inside box")
            end
          end
        end

        # Load provider overrides
        provider_overrides = config.vm.get_provider_overrides(provider)
        if !provider_overrides.empty?
          config_key =
            "#{object_id}_vm_#{name}_#{config.vm.box}_#{provider}".to_sym
          @loader.set(config_key, provider_overrides)
          local_keys << config_key
          config, config_warnings, config_errors = @loader.load(local_keys)
        end

        # If the box changed, then we need to reload
        if original_box != config.vm.box || original_version != config.vm.box_version
          # TODO: infinite loop protection?

          original_box = config.vm.box
          original_version = config.vm.box_version
          load_box_proc.call
        end
      end

      # Load the box and provider overrides
      load_box_proc.call

      # NOTE: In cases where the box_meta file contains stale information
      #       and the reference box no longer exists, fall back to initial
      #       configuration and attempt to load that
      if box.nil?
        @logger.warn("Failed to locate #{config.vm.box} with version #{config.vm.box_version}")
        @logger.warn("Performing lookup with inital values #{initial_box} with version #{initial_version}")
        config.vm.box = original_box = initial_box
        config.vm.box_version = original_box = initial_version
        load_box_proc.call
      end

      # Ensure box attributes are set to original values in
      # case they were modified by the local box metadata
      config.vm.box = original_box
      config.vm.box_version = original_version

      return {
        box: box,
        provider_cls: provider_cls,
        provider_options: provider_options.dup,
        config: config,
        config_warnings: config_warnings,
        config_errors: config_errors,
      }
    end
def machine_config(name, provider, boxes, data_path=nil)
      keys = @keys.dup

      sub_machine = @config.vm.defined_vms[name]
      if !sub_machine
        raise Errors::MachineNotFound,
          name: name, provider: provider
      end

      provider_plugin  = nil
      provider_cls     = nil
      provider_options = {}
      box_formats      = nil
      if provider != nil
        provider_plugin  = Vagrant.plugin("2").manager.providers[provider]
        if !provider_plugin
          providers  = Vagrant.plugin("2").manager.providers.to_hash.keys
          if providers
            providers_str = providers.join(', ')
          else
            providers_str = "N/A"
          end

          if providers.include? provider.downcase
            raise Errors::ProviderNotFoundSuggestion,
              machine: name, provider: provider,
              suggestion: provider.downcase, providers: providers_str
          end

          raise Errors::ProviderNotFound,
            machine: name, provider: provider, providers: providers_str
        end

        provider_cls     = provider_plugin[0]
        provider_options = provider_plugin[1]
        box_formats      = provider_options[:box_format] || provider

        # Test if the provider is usable or not
        begin
          provider_cls.usable?(true)
        rescue Errors::VagrantError => e
          raise Errors::ProviderNotUsable,
            machine: name.to_s,
            provider: provider.to_s,
            message: e.to_s
        end
      end

      # Add the sub-machine configuration to the loader and keys
      vm_config_key = "#{object_id}_machine_#{name}"
      @loader.set(vm_config_key, sub_machine.config_procs)
      keys << vm_config_key

      # Load once so that we can get the proper box value
      config, config_warnings, config_errors = @loader.load(keys)

      # Track the original box so we know if we changed
      box = nil
      initial_box = original_box = config.vm.box
      initial_version = original_version = config.vm.box_version

      # Check if this machine has a local box metadata file
      # describing the existing guest. If so, load it and
      # set the box name and version to allow the actual
      # box in use to be discovered.
      if data_path
        meta_file = data_path.join("box_meta")
        if meta_file.file?
          box_meta = JSON.parse(meta_file.read)
          config.vm.box = box_meta["name"]
          config.vm.box_version = box_meta["version"]
        end
      end

      # The proc below loads the box and provider overrides. This is
      # in a proc because it may have to recurse if the provider override
      # changes the box.
      load_box_proc = lambda do
        local_keys = keys.dup

        # Load the box Vagrantfile, if there is one
        if config.vm.box && boxes
          box = boxes.find(config.vm.box, box_formats, config.vm.box_version)
          if box
            box_vagrantfile = find_vagrantfile(box.directory)
            if box_vagrantfile && !config.vm.ignore_box_vagrantfile
              box_config_key =
                "#{boxes.object_id}_#{box.name}_#{box.provider}".to_sym
              @loader.set(box_config_key, box_vagrantfile)
              local_keys.unshift(box_config_key)
              config, config_warnings, config_errors = @loader.load(local_keys)
            elsif box_vagrantfile && config.vm.ignore_box_vagrantfile
              @logger.warn("Ignoring #{box.name} provided Vagrantfile inside box")
            end
          end
        end

        # Load provider overrides
        provider_overrides = config.vm.get_provider_overrides(provider)
        if !provider_overrides.empty?
          config_key =
            "#{object_id}_vm_#{name}_#{config.vm.box}_#{provider}".to_sym
          @loader.set(config_key, provider_overrides)
          local_keys << config_key
          config, config_warnings, config_errors = @loader.load(local_keys)
        end

        # If the box changed, then we need to reload
        if original_box != config.vm.box || original_version != config.vm.box_version
          # TODO: infinite loop protection?

          original_box = config.vm.box
          original_version = config.vm.box_version
          load_box_proc.call
        end
      end

      # Load the box and provider overrides
      load_box_proc.call

      # NOTE: In cases where the box_meta file contains stale information
      #       and the reference box no longer exists, fall back to initial
      #       configuration and attempt to load that
      if box.nil?
        @logger.warn("Failed to locate #{config.vm.box} with version #{config.vm.box_version}")
        @logger.warn("Performing lookup with inital values #{initial_box} with version #{initial_version}")
        config.vm.box = original_box = initial_box
        config.vm.box_version = original_box = initial_version
        load_box_proc.call
      end

      # Ensure box attributes are set to original values in
      # case they were modified by the local box metadata
      config.vm.box = original_box
      config.vm.box_version = original_version

      return {
        box: box,
        provider_cls: provider_cls,
        provider_options: provider_options.dup,
        config: config,
        config_warnings: config_warnings,
        config_errors: config_errors,
      }
    end
Returns the configuration for a single machine.

 When loading a box Vagrantfile, it will be prepended to the
 key order specified when initializing this class. Sub-machine
 and provider-specific overrides are appended at the end. The
 actual order is:

 - box
 - keys specified for #initialize
 - sub-machine
 - provider

 The return value is a hash with the following keys (symbols)
 and values:

   - box: the {Box} backing the machine
   - config: the actual configuration
   - config_errors: list of errors, if any
   - config_warnings: list of warnings, if any
   - provider_cls: class of the provider backing the machine
   - provider_options: options for the provider

 @param [Symbol] name Name of the machine.
 @param [Symbol] provider The provider the machine should
   be backed by (required for provider overrides).
 @param [BoxCollection] boxes BoxCollection to look up the
   box Vagrantfile.
 @param [Pathname] data_path Machine data path
 @return [Hash<Symbol, Object>] Various configuration parameters for a
   machine. See the main documentation body for more info.