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 75
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 90
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 101
def clear
  @already_searched = {}
  @debug_once = []
  @warn_once = []
  LegacyFacter.clear
  Options[:custom_dir] = []
  LegacyFacter.collection.invalidate_custom_facts
  LegacyFacter.collection.reload_custom_facts
  SessionCache.invalidate_all_caches
  nil
end
core_value(user_query) click to toggle source

Gets the value for a core fact, external or custom facts are

not returned with this call. Returns `nil` if no such fact exists.

@return [FactCollection] hash with fact names and values

@api private

# File lib/facter.rb, line 119
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 133
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 197
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 187
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 146
def debugonce(message)
  return unless debugging?

  message_string = message.to_s
  return if @debug_once.include? message_string

  @debug_once << message_string
  logger.debug(message_string)
  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 165
def define_fact(name, options = {}, &block)
  options[:fact_type] = :custom
  LegacyFacter.define_fact(name, options, &block)
end
disable_sequential() click to toggle source
# File lib/facter.rb, line 205
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 221
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
# File lib/facter.rb, line 201
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 242
def fact(user_query)
  user_query = user_query.to_s
  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 269
def flush
  LegacyFacter.flush
  SessionCache.invalidate_all_caches
  nil
end
list() click to toggle source

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

@api public

# File lib/facter.rb, line 461
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 291
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 280
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 447
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 177
def on_message(&block)
  Facter::Log.on_message(&block)
  nil
end
puppet_facts() click to toggle source
# File lib/facter.rb, line 47
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 255
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 26
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)

  if cli.args.include?(:version)
    cli.invoke(:version, [])
  elsif cli.args.include?('--list-cache-groups')
    cli.invoke(:list_cache_groups, [])
  elsif cli.args.include?('--list-block-groups')
    cli.invoke(:list_block_groups, [])
  else
    cli.invoke(:arg_parser)
  end
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 323
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 333
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 342
def search_path
  Options.custom_dir
end
sequential?() click to toggle source
# File lib/facter.rb, line 209
def sequential?
  Facter::Options[:sequential]
end
to_hash() click to toggle source

Gets a hash mapping fact names to their values The hash contains core facts, legacy facts, custom facts and external facts (all facts that can be resolved).

@return [FactCollection] hash with fact names and values

@api public

# File lib/facter.rb, line 352
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 430
def to_user_output(cli_options, *args)
  init_cli_options(cli_options, args)
  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 377
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 367
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 387
def value(user_query)
  user_query = user_query.to_s
  resolve_fact(user_query)
  @already_searched[user_query]&.value
end
values(options, user_queries) click to toggle source

Gets the values for multiple facts.

@param options [Hash] parameters for the fact - attributes

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

@param user_queries [Array] the fact names

@return [FactCollection] hash with fact names and values

@api public

# File lib/facter.rb, line 403
def values(options, user_queries)
  init_cli_options(options, user_queries)
  log_blocked_facts
  resolved_facts = Facter::FactManager.instance.resolve_facts(user_queries)
  resolved_facts.reject! { |fact| fact.type == :custom && fact.value.nil? }

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

Returns Facter version

@return [String] Current version

@api public

# File lib/facter.rb, line 421
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 471
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 482
def warnonce(message)
  message_string = message.to_s
  return if @warn_once.include? message_string

  @warn_once << message_string
  logger.warn(message_string)
  nil
end

Private Class Methods

add_fact_to_searched_facts(user_query, value) click to toggle source
# File lib/facter.rb, line 524
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 559
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, args) click to toggle source
# File lib/facter.rb, line 519
def init_cli_options(options, args)
  options = options.map { |(k, v)| [k.to_sym, v] }.to_h
  Facter::Options.init_from_cli(options, args)
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 578
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 593
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 515
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 608
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 502
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
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 533
def resolve_fact(user_query)
  user_query = user_query.to_s
  resolved_facts = Facter::FactManager.instance.resolve_facts([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)
  splitted_user_query = Facter::Utils.split_user_query(user_query)

  begin
    value = fact_collection.value(*splitted_user_query)
    add_fact_to_searched_facts(user_query, value)
  rescue KeyError
    nil
  end
end
resolve_facts_for_user_query(user_query) click to toggle source
# File lib/facter.rb, line 493
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