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
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
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
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
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
# 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
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