]> git.donarmstrong.com Git - dsa-puppet.git/blob - modules/stdlib/lib/facter/facter_dot_d.rb
upgrade to concat 2.0.0
[dsa-puppet.git] / modules / stdlib / lib / facter / facter_dot_d.rb
1 # A Facter plugin that loads facts from /etc/facts.d.
2 #
3 # Facts can be in the form of JSON, YAML or Text files
4 # and any executable that returns key=value pairs.
5 #
6 # In the case of scripts you can also create a file that
7 # contains a cache TTL.  For foo.sh store the ttl as just
8 # a number in foo.sh.ttl
9 #
10 # The cache is stored in /tmp/facts_cache.yaml as a mode
11 # 600 file and will have the end result of not calling your
12 # fact scripts more often than is needed
13 class Facter::Util::DotD
14     require 'yaml'
15
16     def initialize(dir="/etc/facts.d", cache_file="/tmp/facts_cache.yml")
17         @dir = dir
18         @cache_file = cache_file
19         @cache = nil
20         @types = {".txt" => :txt, ".json" => :json, ".yaml" => :yaml}
21     end
22
23     def entries
24         Dir.entries(@dir).reject{|f| f =~ /^\.|\.ttl$/}.sort.map {|f| File.join(@dir, f) }
25     rescue
26         []
27     end
28
29     def fact_type(file)
30         extension = File.extname(file)
31
32         type = @types[extension] || :unknown
33
34         type = :script if type == :unknown && File.executable?(file)
35
36         return type
37     end
38
39     def txt_parser(file)
40         File.readlines(file).each do |line|
41             if line =~ /^(.+)=(.+)$/
42                 var = $1; val = $2
43
44                 Facter.add(var) do
45                     setcode { val }
46                 end
47             end
48         end
49     rescue Exception => e
50         Facter.warn("Failed to handle #{file} as text facts: #{e.class}: #{e}")
51     end
52
53     def json_parser(file)
54         begin
55             require 'json'
56         rescue LoadError
57             require 'rubygems'
58             retry
59         end
60
61         JSON.load(File.read(file)).each_pair do |f, v|
62             Facter.add(f) do
63                 setcode { v }
64             end
65         end
66     rescue Exception => e
67         Facter.warn("Failed to handle #{file} as json facts: #{e.class}: #{e}")
68     end
69
70     def yaml_parser(file)
71         require 'yaml'
72
73         YAML.load_file(file).each_pair do |f, v|
74             Facter.add(f) do
75                 setcode { v }
76             end
77         end
78     rescue Exception => e
79         Facter.warn("Failed to handle #{file} as yaml facts: #{e.class}: #{e}")
80     end
81
82     def script_parser(file)
83         result = cache_lookup(file)
84         ttl = cache_time(file)
85
86         unless result
87             result = Facter::Util::Resolution.exec(file)
88
89             if ttl > 0
90                 Facter.debug("Updating cache for #{file}")
91                 cache_store(file, result)
92                 cache_save!
93             end
94         else
95             Facter.debug("Using cached data for #{file}")
96         end
97
98         result.split("\n").each do |line|
99             if line =~ /^(.+)=(.+)$/
100                 var = $1; val = $2
101
102                 Facter.add(var) do
103                     setcode { val }
104                 end
105             end
106         end
107     rescue Exception => e
108         Facter.warn("Failed to handle #{file} as script facts: #{e.class}: #{e}")
109         Facter.debug(e.backtrace.join("\n\t"))
110     end
111
112     def cache_save!
113         cache = load_cache
114         File.open(@cache_file, "w", 0600) {|f| f.write(YAML.dump(cache)) }
115     rescue
116     end
117
118     def cache_store(file, data)
119         load_cache
120
121         @cache[file] = {:data => data, :stored => Time.now.to_i}
122     rescue
123     end
124
125     def cache_lookup(file)
126         cache = load_cache
127
128         return nil if cache.empty?
129
130         ttl = cache_time(file)
131
132         if cache[file]
133             now = Time.now.to_i
134
135             return cache[file][:data] if ttl == -1
136             return cache[file][:data] if (now - cache[file][:stored]) <= ttl
137             return nil
138         else
139             return nil
140         end
141     rescue
142         return nil
143     end
144
145     def cache_time(file)
146         meta = file + ".ttl"
147
148         return File.read(meta).chomp.to_i
149     rescue
150         return 0
151     end
152
153     def load_cache
154         unless @cache
155             if File.exist?(@cache_file)
156                 @cache = YAML.load_file(@cache_file)
157             else
158                 @cache = {}
159             end
160         end
161
162         return @cache
163     rescue
164         @cache = {}
165         return @cache
166     end
167
168     def create
169         entries.each do |fact|
170             type = fact_type(fact)
171             parser = "#{type}_parser"
172
173             if respond_to?("#{type}_parser")
174                 Facter.debug("Parsing #{fact} using #{parser}")
175
176                 send(parser, fact)
177             end
178         end
179     end
180 end
181
182 Facter::Util::DotD.new("/etc/facter/facts.d").create
183 Facter::Util::DotD.new("/etc/puppetlabs/facter/facts.d").create