require 'puppet/indirector'
# A class for managing nodes, including their facts and environment.
class Puppet::Node
require 'puppet/node/facts'
require 'puppet/node/environment'
# Set up indirection, so that nodes can be looked for in
# the node sources.
extend Puppet::Indirector
# Asymmetric serialization/deserialization required in this class via to/from datahash
include Puppet::Util::PsychSupport
# Use the node source as the indirection terminus.
indirects :node, :terminus_setting => :node_terminus, :doc => "Where to find node information.
A node is composed of its name, its facts, and its environment."
attr_accessor :name, :classes, :source, :ipaddress, :parameters, :trusted_data, :environment_name
attr_reader :time, :facts
attr_reader :server_facts
ENVIRONMENT = 'environment'.freeze
def initialize_from_hash(data)
@name = data['name'] || (raise ArgumentError, _("No name provided in serialized data"))
@classes = data['classes'] || []
@parameters = data['parameters'] || {}
env_name = data['environment'] || @parameters[ENVIRONMENT]
unless env_name.nil?
@parameters[ENVIRONMENT] = env_name
@environment_name = env_name.intern
end
end
def self.from_data_hash(data)
node = new(name)
node.initialize_from_hash(data)
node
end
def to_data_hash
result = {
'name' => name,
'environment' => environment.name.to_s,
}
result['classes'] = classes unless classes.empty?
serialized_params = self.serializable_parameters
result['parameters'] = serialized_params unless serialized_params.empty?
result
end
def serializable_parameters
new_params = parameters.dup
new_params.delete(ENVIRONMENT)
new_params
end
def environment
if @environment
@environment
else
env = parameters[ENVIRONMENT]
if env
self.environment = env
elsif environment_name
self.environment = environment_name
else
# This should not be :current_environment, this is the default
# for a node when it has not specified its environment
# it will be used to establish what the current environment is.
#
self.environment = Puppet.lookup(:environments).get!(Puppet[:environment])
end
@environment
end
end
def environment=(env)
if env.is_a?(String) or env.is_a?(Symbol)
@environment = Puppet.lookup(:environments).get!(env)
else
@environment = env
end
# Keep environment_name attribute and parameter in sync if they have been set
unless @environment.nil?
# always set the environment parameter. It becomes top scope $environment for a manifest during catalog compilation.
@parameters[ENVIRONMENT] = @environment.name.to_s
self.environment_name = @environment.name if instance_variable_defined?(:@environment_name)
end
@environment
end
def has_environment_instance?
!@environment.nil?
end
def initialize(name, options = {})
raise ArgumentError, _("Node names cannot be nil") unless name
@name = name
classes = options[:classes]
if classes
if classes.is_a?(String)
@classes = [classes]
else
@classes = classes
end
else
@classes = []
end
@parameters = options[:parameters] || {}
@facts = options[:facts]
@server_facts = {}
env = options[:environment]
if env
self.environment = env
end
@time = Time.now
end
# Merge the node facts with parameters from the node source.
# @api public
# @param facts [optional, Puppet::Node::Facts] facts to merge into node parameters.
# Will query Facts indirection if not supplied.
# @raise [Puppet::Error] Raise on failure to retrieve facts if not supplied
# @return [nil]
def fact_merge(facts = nil)
begin
@facts = facts.nil? ? Puppet::Node::Facts.indirection.find(name, :environment => environment) : facts
rescue => detail
error = Puppet::Error.new(_("Could not retrieve facts for %{name}: %{detail}") % { name: name, detail: detail }, detail)
error.set_backtrace(detail.backtrace)
raise error
end
if !@facts.nil?
@facts.sanitize
# facts should never modify the environment parameter
orig_param_env = @parameters[ENVIRONMENT]
merge(@facts.values)
@parameters[ENVIRONMENT] = orig_param_env
end
end
# Merge any random parameters into our parameter list.
def merge(params)
params.each do |name, value|
if @parameters.include?(name)
Puppet::Util::Warnings.warnonce(_("The node parameter '%{param_name}' for node '%{node_name}' was already set to '%{value}'. It could not be set to '%{desired_value}'") % { param_name: name, node_name: @name, value: @parameters[name], desired_value: value })
else
@parameters[name] = value
end
end
end
# Add extra facts, such as facts given to lookup on the command line The
# extra facts will override existing ones.
# @param extra_facts [Hash{String=>Object}] the facts to tadd
# @api private
def add_extra_facts(extra_facts)
@facts.add_extra_values(extra_facts)
@parameters.merge!(extra_facts)
nil
end
def add_server_facts(facts)
# Append the current environment to the list of server facts
@server_facts = facts.merge({ "environment" => self.environment.name.to_s})
# Merge the server facts into the parameters for the node
merge(facts)
end
# Calculate the list of names we might use for looking
# up our node. This is only used for AST nodes.
def names
return [name] if Puppet.settings[:strict_hostname_checking]
names = []
names += split_name(name) if name.include?(".")
# First, get the fqdn
fqdn = parameters["fqdn"]
unless fqdn
if parameters["hostname"] and parameters["domain"]
fqdn = parameters["hostname"] + "." + parameters["domain"]
else
Puppet.warning _("Host is missing hostname and/or domain: %{name}") % { name: name }
end
end
# Now that we (might) have the fqdn, add each piece to the name
# list to search, in order of longest to shortest.
names += split_name(fqdn) if fqdn
# And make sure the node name is first, since that's the most
# likely usage.
# The name is usually the Certificate CN, but it can be
# set to the 'facter' hostname instead.
if Puppet[:node_name] == 'cert'
names.unshift name
else
names.unshift parameters["hostname"]
end
names.uniq
end
def split_name(name)
list = name.split(".")
tmp = []
list.each_with_index do |short, i|
tmp << list[0..i].join(".")
end
tmp.reverse
end
# Ensures the data is frozen
#
def trusted_data=(data)
Puppet.warning(_("Trusted node data modified for node %{name}") % { name: name }) unless @trusted_data.nil?
@trusted_data = data.freeze
end
# Resurrects and sanitizes trusted information in the node by modifying it and setting
# the trusted_data in the node from parameters.
# This modifies the node
#
def sanitize
# Resurrect "trusted information" that comes from node/fact terminus.
# The current way this is done in puppet db (currently the only one)
# is to store the node parameter 'trusted' as a hash of the trusted information.
#
# Thus here there are two main cases:
# 1. This terminus was used in a real agent call (only meaningful if someone curls the request as it would
# fail since the result is a hash of two catalogs).
# 2 It is a command line call with a given node that use a terminus that:
# 2.1 does not include a 'trusted' fact - use local from node trusted information
# 2.2 has a 'trusted' fact - this in turn could be
# 2.2.1 puppet db having stored trusted node data as a fact (not a great design)
# 2.2.2 some other terminus having stored a fact called "trusted" (most likely that would have failed earlier, but could
# be spoofed).
#
# For the reasons above, the resurrection of trusted node data with authenticated => true is only performed
# if user is running as root, else it is resurrected as unauthenticated.
#
trusted_param = @parameters['trusted']
if trusted_param
# Blows up if it is a parameter as it will be set as $trusted by the compiler as if it was a variable
@parameters.delete('trusted')
unless trusted_param.is_a?(Hash) && %w{authenticated certname extensions}.all? {|key| trusted_param.has_key?(key) }
# trusted is some kind of garbage, do not resurrect
trusted_param = nil
end
else
# trusted may be Boolean false if set as a fact by someone
trusted_param = nil
end
# The options for node.trusted_data in priority order are:
# 1) node came with trusted_data so use that
# 2) else if there is :trusted_information in the puppet context
# 3) else if the node provided a 'trusted' parameter (parsed out above)
# 4) last, fallback to local node trusted information
#
# Note that trusted_data should be a hash, but (2) and (4) are not
# hashes, so we to_h at the end
if !trusted_data
trusted = Puppet.lookup(:trusted_information) do
trusted_param || Puppet::Context::TrustedInformation.local(self)
end
self.trusted_data = trusted.to_h
end
end
end
Copyright 2K16 - 2K18 Indonesian Hacker Rulez