1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164
|
require 'time'
require_relative '../../puppet/node'
require_relative '../../puppet/indirector'
require_relative '../../puppet/util/psych_support'
# Manage a given node's facts. This either accepts facts and stores them, or
# returns facts for a given node.
class Puppet::Node::Facts
include Puppet::Util::PsychSupport
# Set up indirection, so that nodes can be looked for in
# the node sources.
extend Puppet::Indirector
# We want to expire any cached nodes if the facts are saved.
module NodeExpirer
def save(instance, key = nil, options={})
Puppet::Node.indirection.expire(instance.name, options)
super
end
end
indirects :facts, :terminus_setting => :facts_terminus, :extend => NodeExpirer
attr_accessor :name, :values, :timestamp
def add_local_facts
values["clientcert"] = Puppet.settings[:certname]
values["clientversion"] = Puppet.version.to_s
values["clientnoop"] = Puppet.settings[:noop]
end
def initialize(name, values = {})
@name = name
@values = values
add_timestamp
end
def initialize_from_hash(data)
@name = data['name']
@values = data['values']
# Timestamp will be here in YAML, e.g. when reading old reports
timestamp = @values.delete('_timestamp')
# Timestamp will be here in JSON
timestamp ||= data['timestamp']
if timestamp.is_a? String
@timestamp = Time.parse(timestamp)
else
@timestamp = timestamp
end
self.expiration = data['expiration']
if expiration.is_a? String
self.expiration = Time.parse(expiration)
end
end
# Add extra values, such as facts given to lookup on the command line. The
# extra values will override existing values.
# @param extra_values [Hash{String=>Object}] the values to add
# @api private
def add_extra_values(extra_values)
@values.merge!(extra_values)
nil
end
# Sanitize fact values by converting everything not a string, Boolean
# numeric, array or hash into strings.
def sanitize
values.each do |fact, value|
values[fact] = sanitize_fact value
end
end
def ==(other)
return false unless self.name == other.name
values == other.values
end
def self.from_data_hash(data)
new_facts = allocate
new_facts.initialize_from_hash(data)
new_facts
end
def to_data_hash
result = {
'name' => name,
'values' => values
}
if @timestamp
if @timestamp.is_a? Time
result['timestamp'] = @timestamp.iso8601(9)
else
result['timestamp'] = @timestamp
end
end
if expiration
if expiration.is_a? Time
result['expiration'] = expiration.iso8601(9)
else
result['expiration'] = expiration
end
end
result
end
def add_timestamp
@timestamp = Time.now
end
def to_yaml
facts_to_display = Psych.parse_stream(YAML.dump(self))
quote_special_strings(facts_to_display)
end
private
def quote_special_strings(fact_hash)
fact_hash.grep(Psych::Nodes::Scalar).each do |node|
next unless node.value =~ /:/
node.plain = false
node.quoted = true
node.style = Psych::Nodes::Scalar::DOUBLE_QUOTED
end
fact_hash.yaml
end
def sanitize_fact(fact)
if fact.is_a? Hash then
ret = {}
fact.each_pair { |k,v| ret[sanitize_fact k]=sanitize_fact v }
ret
elsif fact.is_a? Array then
fact.collect { |i| sanitize_fact i }
elsif fact.is_a? Numeric \
or fact.is_a? TrueClass \
or fact.is_a? FalseClass \
or fact.is_a? String
fact
else
result = fact.to_s
# The result may be ascii-8bit encoded without being a binary (low level object.inspect returns ascii-8bit string)
if result.encoding == Encoding::ASCII_8BIT
begin
result = result.encode(Encoding::UTF_8)
rescue
# return the ascii-8bit - it will be taken as a binary
result
end
end
result
end
end
end
|