module Facter

Aggregates provide a mechanism for facts to be resolved in multiple steps.

Aggregates are evaluated in two parts: generating individual chunks and then aggregating all chunks together. Each chunk is a block of code that generates a value, and may depend on other chunks when it runs. After all chunks have been evaluated they are passed to the aggregate block as Hash<name, result>. The aggregate block converts the individual chunks into a single value that is returned as the final value of the aggregate.

@api public @since 2.0.0

Because Open3 uses Process.detach the env $? is not set so this class reimplements Open3.popen3 with Process.wait instead.

FACT-2934

When calling Facter::Core::Execution, $? and $CHILD_STATUS

ruby env variables should be set.

This class represents a fact. Each fact has a name and multiple {Facter::Util::Resolution resolutions}.

Create facts using {Facter.add}

@api public

This represents a fact resolution. A resolution is a concrete implementation of a fact. A single fact can have many resolutions and the correct resolution will be chosen at runtime. Each time {Facter.add} is called, a new resolution is created and added to the set of resolutions for the fact named in the call. Each resolution has a {#has_weight weight}, which defines its priority over other resolutions, and a set of {#confine confinements}, which defines the conditions under which it will be chosen. All confinements must be satisfied for a fact to be considered suitable.

@api public

CuAt (Customized Attributes) non-default attribute values CuDv (Customized Devices) the devices present on this machine PdAt (Predefined Attributes) default values for all device attributes PdDv (Predefined Devices) the list of all devices supported by this release of AIX

Constants

CYAN
DEFAULT_LOG_LEVEL
GREEN
RED
RESET
VERSION
YELLOW

Public Class Methods

[](name) click to toggle source

Alias method for Facter.fact() @param name [string] fact name

@return [Facter::Util::Fact, nil] The fact object, or nil if no fact

is found.

@api public

# File lib/facter.rb, line 82
def [](name)
  fact(name)
end
add(name, options = {}, &block) click to toggle source

Add custom facts to fact collection @param name [String] Custom fact name @param options = {} [Hash] optional parameters for the fact - attributes

of {Facter::Util::Fact} and {Facter::Util::Resolution} can be
supplied here

@param block [Proc] a block defining a fact resolution

@return [Facter::Util::Fact] the fact object, which includes any previously

defined resolutions

@api public

# File lib/facter.rb, line 97
def add(name, options = {}, &block)
  options[:fact_type] = :custom
  LegacyFacter.add(name, options, &block)
  LegacyFacter.collection.invalidate_custom_facts
end
clear() click to toggle source

Clears all cached values and removes all facts from memory.

@return [nil]

@api public

# File lib/facter.rb, line 108
def clear
  @already_searched = {}
  Facter.clear_messages
  LegacyFacter.clear
  Options[:custom_dir] = []
  LegacyFacter.collection.invalidate_custom_facts
  LegacyFacter.collection.reload_custom_facts
  SessionCache.invalidate_all_caches
  nil
end
clear_messages() click to toggle source

Clears the seen state of debug and warning messages.

@return [nil]

# File lib/facter.rb, line 122
def clear_messages
  Facter::Log.clear_messages
end
core_value(user_query) click to toggle source

Retrieves the value of a core fact. External or custom facts are not returned with this call. Returns `nil` if no such fact exists.

@return [FactCollection] A hash with fact names and values

@api private

# File lib/facter.rb, line 132
def core_value(user_query)
  user_query = user_query.to_s
  resolved_facts = Facter::FactManager.instance.resolve_core([user_query])
  fact_collection = FactCollection.new.build_fact_collection!(resolved_facts)
  splitted_user_query = Facter::Utils.split_user_query(user_query)
  fact_collection.dig(*splitted_user_query)
end
debug(message) click to toggle source

Logs debug message when debug option is set to true @param message [Object] Message object to be logged

@return [nil]

@api public

# File lib/facter.rb, line 146
def debug(message)
  return unless debugging?

  logger.debug(message.to_s)
  nil
end
debugging(debug_bool) click to toggle source

Enable or disable debugging @param debug_bool [bool] State which debugging should have

@return [type] [description]

@api public

# File lib/facter.rb, line 204
def debugging(debug_bool)
  Facter::Options[:debug] = debug_bool
end
debugging?() click to toggle source

Check whether debugging is enabled

@return [bool]

@api public

# File lib/facter.rb, line 194
def debugging?
  Options[:debug]
end
debugonce(message) click to toggle source

Logs the same debug message only once when debug option is set to true @param message [Object] Message object to be logged

@return [nil]

@api public

# File lib/facter.rb, line 159
def debugonce(message)
  logger.debugonce(message)
  nil
end
define_fact(name, options = {}, &block) click to toggle source

Define a new fact or extend an existing fact.

@param name [Symbol] The name of the fact to define @param options [Hash] A hash of options to set on the fact

@return [Facter::Util::Fact] The fact that was defined

@api public

# File lib/facter.rb, line 172
def define_fact(name, options = {}, &block)
  options[:fact_type] = :custom
  LegacyFacter.define_fact(name, options, &block)
end
disable_sequential() click to toggle source

Disable sequential resolving of facts

@return [bool]

@api public

# File lib/facter.rb, line 241
def disable_sequential
  Facter::Options[:sequential] = false
end
each() { |name, value| ... } click to toggle source

Iterates over fact names and values

@yieldparam [String] name the fact name @yieldparam [String] value the current value of the fact

@return [Facter]

@api public

# File lib/facter.rb, line 262
def each
  log_blocked_facts
  resolved_facts = Facter::FactManager.instance.resolve_facts

  resolved_facts.each do |fact|
    yield(fact.name, fact.value)
  end

  self
end
enable_sequential() click to toggle source

Enable sequential resolving of facts

@return [bool]

@api public

# File lib/facter.rb, line 232
def enable_sequential
  Facter::Options[:sequential] = true
end
fact(user_query) click to toggle source

Returns a fact object by name. If you use this, you still have to call {Facter::Util::Fact#value `value`} on it to retrieve the actual value.

@param user_query [String] the name of the fact

@return [Facter::Util::Fact, nil] The fact object, or nil if no fact

is found.

@api public

# File lib/facter.rb, line 428
def fact(user_query)
  user_query = user_query.to_s.downcase
  resolve_fact(user_query)

  @already_searched[user_query]
end
flush() click to toggle source

Flushes cached values for all facts. This does not cause code to be reloaded; it only clears the cached results.

@return [void]

@api public

# File lib/facter.rb, line 293
def flush
  LegacyFacter.flush
  SessionCache.invalidate_all_caches
  nil
end
http_debug(http_debug_bool) click to toggle source

Enable or disable http debugging @param debug_bool [bool] State which http debugging should have

@return [type] [description]

@api public

# File lib/facter.rb, line 223
def http_debug(http_debug_bool)
  Facter::Options[:http_debug] = http_debug_bool
end
http_debug?() click to toggle source

Check whether http debugging is enabled

@return [bool]

@api public

# File lib/facter.rb, line 213
def http_debug?
  Options[:http_debug]
end
list() click to toggle source

Returns a list with the names of all resolved facts @return [Array] the list with all the fact names

@api public

# File lib/facter.rb, line 480
def list
  to_hash.keys.sort
end
load_external(enable_external) click to toggle source

Enables/Disables external facts. @param enable_external [boolean]

@return nil

@api public

# File lib/facter.rb, line 315
def load_external(enable_external)
  # enable_external param needs negation because behind the scene
  # no_external_facts= method is negating the parameter again.
  Options[:no_external_facts] = !enable_external

  if enable_external
    logger.debug('Facter.load_external(true) called. External facts will be loaded')
  else
    logger.debug('Facter.load_external(false) called. External facts will NOT be loaded')
  end

  nil
end
loadfacts() click to toggle source

Loads all facts

@return [nil]

@api public

# File lib/facter.rb, line 304
def loadfacts
  LegacyFacter.loadfacts
  nil
end
log_exception(exception, message = nil) click to toggle source

Logs an exception and an optional message

@return [nil]

@api public

# File lib/facter.rb, line 466
def log_exception(exception, message = nil)
  error_message = []

  error_message << message.to_s unless message.nil? || (message.is_a?(String) && message.empty?)

  parse_exception(exception, error_message)
  logger.error(error_message.flatten.join("\n"))
  nil
end
on_message(&block) click to toggle source

Stores a proc that will be used to output custom messages.

The proc must receive one parameter that will be the message to log.

@param block [Proc] a block defining messages handler

@return [nil]

@api public

# File lib/facter.rb, line 184
def on_message(&block)
  Facter::Log.on_message(&block)
  nil
end
puppet_facts() click to toggle source

Method used by cli to set puppet paths in order to retrieve puppet custom and external facts

@return nil

@api private

# File lib/facter.rb, line 54
def puppet_facts
  require 'puppet'

  # don't allow puppet logger to be injected in Facter
  Options[:allow_external_loggers] = false

  Puppet.initialize_settings
  $LOAD_PATH << Puppet[:libdir] unless $LOAD_PATH.include?(Puppet[:libdir])
  Facter.reset
  Facter.search_external([Puppet[:pluginfactdest]])
  if Puppet.respond_to? :initialize_facts
    Puppet.initialize_facts
  else
    Facter.add(:puppetversion) do
      setcode { Puppet.version.to_s }
    end
  end
rescue LoadError => e
  logger.error("Could not load puppet gem, got #{e}")
end
reset() click to toggle source

Reset search paths for custom and external facts If config file is set custom and external facts will be reloaded

@return [nil]

@api public

# File lib/facter.rb, line 279
def reset
  LegacyFacter.reset
  Options[:custom_dir] = []
  Options[:external_dir] = []
  SessionCache.invalidate_all_caches
  nil
end
resolve(args_as_string) click to toggle source

Method used by puppet-agent to retrieve facts @param args_as_string [string] facter cli arguments

@return query result

@api private

# File lib/facter.rb, line 24
def resolve(args_as_string)
  require 'facter/framework/cli/cli_launcher'

  args = args_as_string.split(' ')
  Facter::OptionsValidator.validate(args)
  processed_arguments = CliLauncher.prepare_arguments(args, nil)
  cli = Facter::Cli.new([], processed_arguments)
  cli_options = cli.options.dup

  # config file options
  config_file = cli_options.delete(:config)
  if config_file
    Facter::OptionStore.set(:config, config_file)
    Facter::ConfigFileOptions.init(config_file)
    Facter::Options.store(ConfigFileOptions.get)
  end

  # user provided options
  cli_options[:show_legacy] ||= false
  Facter::Options.store(cli_options)

  queried_facts(cli.args)
end
search_external(dirs) click to toggle source

Registers directories to be searched for external facts. @param dirs [Array<String>] An array of searched directories

@return [nil]

@api public

# File lib/facter.rb, line 347
def search_external(dirs)
  Options[:external_dir] += dirs
  nil
end
search_external_path() click to toggle source

Returns the registered search directories.for external facts.

@return [Array<String>] An array of searched directories

@api public

# File lib/facter.rb, line 357
def search_external_path
  Options.external_dir
end
search_path() click to toggle source

Returns the registered search directories for custom facts.

@return [Array<String>] An array of the directories searched

@api public

# File lib/facter.rb, line 366
def search_path
  Options.custom_dir
end
sequential?() click to toggle source

Check if facts are resolved sequentially or not

@return [bool]

@api public

# File lib/facter.rb, line 250
def sequential?
  Facter::Options[:sequential]
end
to_hash() click to toggle source

Retrieves a fact's value. Returns `nil` if no such fact exists.

@param user_query [String] the fact name @return [String] the value of the fact, or nil if no fact is found

@api public

# File lib/facter.rb, line 376
def to_hash
  log_blocked_facts
  logger.debug("Facter version: #{Facter::VERSION}")

  resolved_facts = Facter::FactManager.instance.resolve_facts
  resolved_facts.reject! { |fact| fact.type == :custom && fact.value.nil? }
  collection = Facter::FactCollection.new.build_fact_collection!(resolved_facts)
  Hash[collection]
end
to_user_output(cli_options, *args) click to toggle source

Gets a hash mapping fact names to their values

@return [Array] the hash of fact names and values

@api private

# File lib/facter.rb, line 449
def to_user_output(cli_options, *args)
  init_cli_options(cli_options)
  logger.info("executed with command line: #{ARGV.drop(1).join(' ')}")
  logger.debug("Facter version: #{Facter::VERSION}")
  log_blocked_facts
  resolved_facts = resolve_facts_for_user_query(args)
  fact_formatter = Facter::FormatterFactory.build(Facter::Options.get)
  status = error_check(resolved_facts)

  [fact_formatter.format(resolved_facts), status]
end
trace(bool) click to toggle source

Enable or disable trace @param bool [bool] Set trace on debug state

@return [bool] Value of trace debug state

@api public

# File lib/facter.rb, line 401
def trace(bool)
  Options[:trace] = bool
end
trace?() click to toggle source

Check whether printing stack trace is enabled

@return [bool]

@api public

# File lib/facter.rb, line 391
def trace?
  Options[:trace]
end
value(user_query) click to toggle source

Gets the value for a fact. Returns `nil` if no such fact exists.

@param user_query [String] the fact name @return [String] the value of the fact, or nil if no fact is found

@api public

# File lib/facter.rb, line 411
def value(user_query)
  user_query = user_query.to_s.downcase
  resolve_fact(user_query)

  @already_searched[user_query]&.value
end
version() click to toggle source

Returns Facter version

@return [String] Current version

@api public

# File lib/facter.rb, line 440
def version
  Facter::VERSION
end
warn(message) click to toggle source

Logs the message parameter as a warning. @param message [Object] the warning object to be displayed

@return [nil]

@api public

# File lib/facter.rb, line 490
def warn(message)
  logger.warn(message.to_s)
  nil
end
warnonce(message) click to toggle source

Logs only once the same warning message. @param message [Object] the warning message object

@return [nil]

@api public

# File lib/facter.rb, line 501
def warnonce(message)
  logger.warnonce(message)
  nil
end

Private Class Methods

add_fact_to_searched_facts(user_query, value) click to toggle source
# File lib/facter.rb, line 551
def add_fact_to_searched_facts(user_query, value)
  @already_searched[user_query] ||= ResolvedFact.new(user_query, value)
  @already_searched[user_query].value = value
end
error_check(resolved_facts) click to toggle source

Returns exit status when user query contains facts that do

not exist

@param resolved_facts [Array] List of resolved facts

@return [1/nil] Will return status 1 if user query contains

facts that are not found or resolved, otherwise it will return nil

@api private

# File lib/facter.rb, line 585
def error_check(resolved_facts)
  status = 0
  if Options[:strict]
    missing_names = resolved_facts.select { |fact| fact.type == :nil }.map(&:user_query)

    if missing_names.count.positive?
      status = 1
      log_errors(missing_names)
    end
  end

  status
end
init_cli_options(options) click to toggle source
# File lib/facter.rb, line 546
def init_cli_options(options)
  options = options.map { |(k, v)| [k.to_sym, v] }.to_h
  Facter::Options.init_from_cli(options)
end
log_blocked_facts() click to toggle source

Prints out blocked facts before to_hash or to_user_output is called

@return [nil]

@api private

# File lib/facter.rb, line 604
def log_blocked_facts
  block_list = Options[:block_list]
  return unless block_list.any? && Facter::Options[:block]

  logger.debug("blocking collection of #{block_list.join("\s")} facts")
end
log_errors(missing_names) click to toggle source

Used for printing errors regarding CLI user input validation

@param missing_names [Array] List of facts that were requested

but not found

@return [nil]

@api private

# File lib/facter.rb, line 619
def log_errors(missing_names)
  missing_names.each do |missing_name|
    logger.error("fact \"#{missing_name}\" does not exist.", true)
  end
end
logger() click to toggle source
# File lib/facter.rb, line 542
def logger
  @logger ||= Log.new(self)
end
method_missing(name, *args, &block) click to toggle source

Proxy method that catches not yet implemented method calls

@param name [type] [description] @param *args [type] [description] @param &block [type] [description]

@return [type] [description]

@api private

# File lib/facter.rb, line 634
def method_missing(name, *args, &block)
  logger.error(
    "--#{name}-- not implemented but required \n" \
    'with params: ' \
    "#{args.inspect} \n" \
    'with block: ' \
    "#{block.inspect}  \n" \
    "called by:  \n" \
    "#{caller} \n"
  )
  nil
end
parse_exception(exception, error_message) click to toggle source
# File lib/facter.rb, line 529
def parse_exception(exception, error_message)
  if exception.is_a?(Exception)
    error_message << exception.message if error_message.empty?

    if Options[:trace] && !exception.backtrace.nil?
      error_message << 'backtrace:'
      error_message.concat(exception.backtrace)
    end
  elsif error_message.empty?
    error_message << exception.to_s
  end
end
queried_facts(user_query) click to toggle source
# File lib/facter.rb, line 508
def queried_facts(user_query)
  log_blocked_facts
  resolved_facts = Facter::FactManager.instance.resolve_facts(user_query)
  resolved_facts.reject! { |fact| fact.type == :custom && fact.value.nil? }

  if user_query.count.zero?
    Facter::FactCollection.new.build_fact_collection!(resolved_facts)
  else
    FormatterHelper.retrieve_facts_to_display_for_user_query(user_query, resolved_facts)
  end
end
resolve_fact(user_query) click to toggle source

Returns a ResolvedFact and saves the result in @already_searched array that is used as a global collection. @param user_query [String] Fact that needs resolution

@return [ResolvedFact]

# File lib/facter.rb, line 560
def resolve_fact(user_query)
  user_query = user_query.to_s
  resolved_facts = Facter::FactManager.instance.resolve_fact(user_query)
  # we must make a distinction between custom facts that return nil and nil facts
  # Nil facts should not be packaged as ResolvedFacts! (add_fact_to_searched_facts packages facts)
  resolved_facts = resolved_facts.reject { |fact| fact.type == :nil }
  fact_collection = FactCollection.new.build_fact_collection!(resolved_facts)

  begin
    value = fact_collection.value(user_query)
    add_fact_to_searched_facts(user_query, value)
  rescue KeyError, TypeError
    nil
  end
end
resolve_facts_for_user_query(user_query) click to toggle source
# File lib/facter.rb, line 520
def resolve_facts_for_user_query(user_query)
  resolved_facts = Facter::FactManager.instance.resolve_facts(user_query)
  user_querie = resolved_facts.uniq(&:user_query).map(&:user_query).first

  resolved_facts.reject! { |fact| fact.type == :custom && fact.value.nil? } if user_querie&.empty?

  resolved_facts
end