module Vagrant::CapabilityHost

This module enables a class to host capabilities. Prior to being able to use any capabilities, the ‘initialize_capabilities!` method must be called.

Capabilities allow small pieces of functionality to be plugged in using the Vagrant plugin model. Capabilities even allow for a certain amount of inheritance, where only a subset of capabilities may be implemented but a parent implements the rest.

Capabilities are used heavily in Vagrant for host/guest interactions. For example, “mount_nfs_folder” is a guest-OS specific operation, so capabilities defer these operations to the guest.

Public Instance Methods

capability(cap_name, *args) click to toggle source

Executes the capability with the given name, optionally passing more arguments onwards to the capability. If the capability returns a value, it will be returned.

@param [Symbol] cap_name Name of the capability

# File lib/vagrant/capability_host.rb, line 91
def capability(cap_name, *args)
  cap_mod = capability_module(cap_name.to_sym)
  if !cap_mod
    raise Errors::CapabilityNotFound,
      cap:  cap_name.to_s,
      host: @cap_host_chain[0][0].to_s
  end

  cap_method = nil
  begin
    cap_method = cap_mod.method(cap_name)
  rescue NameError
    raise Errors::CapabilityInvalid,
      cap: cap_name.to_s,
      host: @cap_host_chain[0][0].to_s
  end

  args = @cap_args + args
  @cap_logger.info(
    "Execute capability: #{cap_name} #{args.inspect} (#{@cap_host_chain[0][0]})")
  cap_method.call(*args)
end
capability?(cap_name) click to toggle source

Tests whether the given capability is possible.

@param [Symbol] cap_name Capability name @return [Boolean]

# File lib/vagrant/capability_host.rb, line 82
def capability?(cap_name)
  !capability_module(cap_name.to_sym).nil?
end
capability_host_chain() click to toggle source

Returns the chain of hosts that will be checked for capabilities.

@return [Array<Array<Symbol, Class>>]

# File lib/vagrant/capability_host.rb, line 74
def capability_host_chain
  @cap_host_chain
end
initialize_capabilities!(host, hosts, capabilities, *args) click to toggle source

Initializes the capability system by detecting the proper capability host to execute on and building the chain of capabilities to execute.

@param [Symbol] host The host to use for the capabilities, or nil if

we should auto-detect it.

@param [Hash<Symbol, Array<Class, Symbol>>] hosts Potential capability

hosts. The key is the name of the host, value[0] is a class that
implements `#detect?` and value[1] is a parent host (if any).

@param [Hash<Symbol, Hash<Symbol, Class>>] capabilities The capabilities

that are supported. The key is the host of the capability. Within that
is a hash where the key is the name of the capability and the value
is the class/module implementing it.
# File lib/vagrant/capability_host.rb, line 27
def initialize_capabilities!(host, hosts, capabilities, *args)
  @cap_logger = Log4r::Logger.new(
    "vagrant::capability_host::#{self.class.to_s.downcase}")

  if host && !hosts[host]
    raise Errors::CapabilityHostExplicitNotDetected, value: host.to_s
  end

  if !host
    host = autodetect_capability_host(hosts, *args) if !host
    raise Errors::CapabilityHostNotDetected if !host
  end

  if !hosts[host]
    # This should never happen because the autodetect above uses the
    # hosts hash to look up hosts. And if an explicit host is specified,
    # we do another check higher up.
    raise "Internal error. Host not found: #{host}"
  end

  name      = host
  host_info = hosts[name]
  host      = host_info[0].new
  chain     = []
  chain << [name, host]

  # Build the proper chain of parents if there are any.
  # This allows us to do "inheritance" of capabilities later
  if host_info[1]
    parent_name = host_info[1]
    parent_info = hosts[parent_name]
    while parent_info
      chain << [parent_name, parent_info[0].new]
      parent_name = parent_info[1]
      parent_info = hosts[parent_name]
    end
  end

  @cap_host_chain = chain
  @cap_args       = args
  @cap_caps       = capabilities
  true
end

Protected Instance Methods

autodetect_capability_host(hosts, *args) click to toggle source
# File lib/vagrant/capability_host.rb, line 116
def autodetect_capability_host(hosts, *args)
  @cap_logger.info("Autodetecting host type for #{args.inspect}")

  # Get the mapping of hosts with the most parents. We start searching
  # with the hosts with the most parents first.
  parent_count = {}
  hosts.each do |name, parts|
    parent_count[name] = 0

    parent = parts[1]
    while parent
      parent_count[name] += 1
      parent = hosts[parent]
      parent = parent[1] if parent
    end
  end

  # Now swap around the mapping so that it is a mapping of
  # count to the actual list of host names
  parent_count_to_hosts = {}
  parent_count.each do |name, count|
    parent_count_to_hosts[count] ||= []
    parent_count_to_hosts[count] << name
  end

  sorted_counts = parent_count_to_hosts.keys.sort.reverse
  sorted_counts.each do |count|
    parent_count_to_hosts[count].each do |name|
      @cap_logger.debug("Trying: #{name}")
      host_info = hosts[name]
      host      = host_info[0].new

      if host.detect?(*args)
        @cap_logger.info("Detected: #{name}!")
        return name
      end
    end
  end

  return nil
end
capability_module(cap_name) click to toggle source

Returns the registered module for a capability with the given name.

@param [Symbol] cap_name @return [Module]

# File lib/vagrant/capability_host.rb, line 162
def capability_module(cap_name)
  @cap_logger.debug("Searching for cap: #{cap_name}")
  @cap_host_chain.each do |host_name, host|
    @cap_logger.debug("Checking in: #{host_name}")
    caps = @cap_caps[host_name]

    if caps && caps.key?(cap_name)
      @cap_logger.debug("Found cap: #{cap_name} in #{host_name}")
      return caps[cap_name]
    end
  end

  nil
end