facter 1.0.1
Sign up to get free protection for your applications and to get access to all the features.
Potentially problematic release.
This version of facter might be problematic. Click here for more details.
- data/CHANGES +9 -0
- data/COPYING +339 -0
- data/INSTALL +7 -0
- data/LICENSE +17 -0
- data/README +9 -0
- data/Rakefile +277 -0
- data/TODO +4 -0
- data/bin/facter +72 -0
- data/etc/facter.conf +5 -0
- data/install.rb +289 -0
- data/lib/facter.rb +771 -0
- metadata +59 -0
data/TODO
ADDED
data/bin/facter
ADDED
@@ -0,0 +1,72 @@
|
|
1
|
+
#!/usr/bin/env ruby
|
2
|
+
|
3
|
+
#--------------------
|
4
|
+
# duh, it's facter!
|
5
|
+
#
|
6
|
+
# $Id: facter,v 1.1.1.1 2004/03/21 21:06:27 luke Exp $
|
7
|
+
|
8
|
+
require 'getoptlong'
|
9
|
+
require 'facter'
|
10
|
+
|
11
|
+
Facter.load
|
12
|
+
|
13
|
+
$debug = 0
|
14
|
+
|
15
|
+
config = nil
|
16
|
+
|
17
|
+
result = GetoptLong.new(
|
18
|
+
[ "--version", "-v", GetoptLong::NO_ARGUMENT ],
|
19
|
+
[ "--help", "-h", GetoptLong::NO_ARGUMENT ],
|
20
|
+
[ "--debug", "-d", GetoptLong::NO_ARGUMENT ],
|
21
|
+
[ "--config", "-c", GetoptLong::REQUIRED_ARGUMENT ]
|
22
|
+
)
|
23
|
+
|
24
|
+
result.each { |opt,arg|
|
25
|
+
case opt
|
26
|
+
when "--version"
|
27
|
+
puts "%s" % Facter.version
|
28
|
+
exit
|
29
|
+
when "--debug"
|
30
|
+
Facter.debugging(1)
|
31
|
+
when "--help"
|
32
|
+
puts "There is no help yet"
|
33
|
+
exit
|
34
|
+
else
|
35
|
+
raise "Invalid option '#{opt}'"
|
36
|
+
end
|
37
|
+
}
|
38
|
+
|
39
|
+
names = []
|
40
|
+
|
41
|
+
unless config.nil?
|
42
|
+
File.open(config) { |file|
|
43
|
+
names = file.readlines.collect { |line|
|
44
|
+
line.chomp
|
45
|
+
}
|
46
|
+
}
|
47
|
+
end
|
48
|
+
|
49
|
+
ARGV.each { |item|
|
50
|
+
names.push item
|
51
|
+
}
|
52
|
+
|
53
|
+
facts = {}
|
54
|
+
|
55
|
+
if names.empty?
|
56
|
+
Facter.each { |name,fact|
|
57
|
+
facts[name] = fact
|
58
|
+
}
|
59
|
+
else
|
60
|
+
names.each { |name|
|
61
|
+
begin
|
62
|
+
facts[name] = Facter[name].value
|
63
|
+
rescue => error
|
64
|
+
STDERR.puts "Could not retrieve %s: #{error}" % name
|
65
|
+
exit 10
|
66
|
+
end
|
67
|
+
}
|
68
|
+
end
|
69
|
+
|
70
|
+
facts.each { |name,value|
|
71
|
+
puts "%s => %s" % [name,value]
|
72
|
+
}
|
data/etc/facter.conf
ADDED
data/install.rb
ADDED
@@ -0,0 +1,289 @@
|
|
1
|
+
#! /usr/bin/env ruby
|
2
|
+
#--
|
3
|
+
# Copyright 2004 Austin Ziegler <ruby-install@halostatue.ca>
|
4
|
+
# Install utility. Based on the original installation script for rdoc by the
|
5
|
+
# Pragmatic Programmers.
|
6
|
+
#
|
7
|
+
# This program is free software. It may be redistributed and/or modified under
|
8
|
+
# the terms of the GPL version 2 (or later) or the Ruby licence.
|
9
|
+
#
|
10
|
+
# Usage
|
11
|
+
# -----
|
12
|
+
# In most cases, if you have a typical project layout, you will need to do
|
13
|
+
# absolutely nothing to make this work for you. This layout is:
|
14
|
+
#
|
15
|
+
# bin/ # executable files -- "commands"
|
16
|
+
# lib/ # the source of the library
|
17
|
+
# tests/ # unit tests
|
18
|
+
#
|
19
|
+
# The default behaviour:
|
20
|
+
# 1) Run all unit test files (ending in .rb) found in all directories under
|
21
|
+
# tests/.
|
22
|
+
# 2) Build Rdoc documentation from all files in bin/ (excluding .bat and .cmd),
|
23
|
+
# all .rb files in lib/, ./README, ./ChangeLog, and ./Install.
|
24
|
+
# 3) Build ri documentation from all files in bin/ (excluding .bat and .cmd),
|
25
|
+
# and all .rb files in lib/. This is disabled by default on Win32.
|
26
|
+
# 4) Install commands from bin/ into the Ruby bin directory. On Windows, if a
|
27
|
+
# if a corresponding batch file (.bat or .cmd) exists in the bin directory,
|
28
|
+
# it will be copied over as well. Otherwise, a batch file (always .bat) will
|
29
|
+
# be created to run the specified command.
|
30
|
+
# 5) Install all library files ending in .rb from lib/ into Ruby's
|
31
|
+
# site_lib/version directory.
|
32
|
+
#
|
33
|
+
# $Id: install.rb,v 1.6 2004/08/08 20:33:09 austin Exp $
|
34
|
+
#++
|
35
|
+
|
36
|
+
require 'rbconfig'
|
37
|
+
require 'find'
|
38
|
+
require 'fileutils'
|
39
|
+
require 'optparse'
|
40
|
+
require 'ostruct'
|
41
|
+
|
42
|
+
InstallOptions = OpenStruct.new
|
43
|
+
|
44
|
+
$loadedrdoc = false
|
45
|
+
|
46
|
+
begin
|
47
|
+
require 'rdoc/rdoc'
|
48
|
+
$loadedrdoc = true
|
49
|
+
rescue LoadError => detail
|
50
|
+
$stderr.puts "Could not load rdoc/rdoc: %s" % detail
|
51
|
+
InstallOptions.rdoc = false
|
52
|
+
end
|
53
|
+
|
54
|
+
|
55
|
+
def glob(list)
|
56
|
+
g = list.map { |i| Dir.glob(i) }
|
57
|
+
g.flatten!
|
58
|
+
g.compact!
|
59
|
+
g.reject! { |e| e =~ /CVS/ }
|
60
|
+
g
|
61
|
+
end
|
62
|
+
|
63
|
+
# Set these values to what you want installed.
|
64
|
+
#bins = glob(%w{bin/**/*}).reject { |e| e =~ /\.(bat|cmd)$/ }
|
65
|
+
bins = ["bin/facter"]
|
66
|
+
rdoc = glob(%w{bin/**/* lib/**/*.rb README CHANGELOG INSTALL}).reject { |e| e=~ /\.(bat|cmd)$/ }
|
67
|
+
ri = glob(%w(bin/**/* lib/**/*.rb)).reject { |e| e=~ /\.(bat|cmd)$/ }
|
68
|
+
libs = glob(%w{lib/**/*.rb})
|
69
|
+
tests = glob(%w{tests/**/*.rb})
|
70
|
+
|
71
|
+
def do_bins(bins, target, strip = 'bin/')
|
72
|
+
bins.each do |bf|
|
73
|
+
obf = bf.gsub(/#{strip}/, '')
|
74
|
+
install_binfile(bf, obf, target)
|
75
|
+
end
|
76
|
+
end
|
77
|
+
|
78
|
+
def do_libs(libs, strip = 'lib/')
|
79
|
+
libs.each do |lf|
|
80
|
+
olf = File.join(InstallOptions.site_dir, lf.gsub(/#{strip}/, ''))
|
81
|
+
op = File.dirname(olf)
|
82
|
+
#if File.respond_to?(:makedirs)
|
83
|
+
FileUtils.makedirs(op)
|
84
|
+
#else
|
85
|
+
# recmkdir(op)
|
86
|
+
#end
|
87
|
+
File.chmod(0755, op)
|
88
|
+
FileUtils.install(lf, olf, :mode => 0755, :verbose => true)
|
89
|
+
end
|
90
|
+
end
|
91
|
+
|
92
|
+
##
|
93
|
+
# Prepare the file installation.
|
94
|
+
#
|
95
|
+
def prepare_installation
|
96
|
+
InstallOptions.rdoc = true
|
97
|
+
if RUBY_PLATFORM == "i386-mswin32"
|
98
|
+
InstallOptions.ri = false
|
99
|
+
else
|
100
|
+
InstallOptions.ri = true
|
101
|
+
end
|
102
|
+
InstallOptions.tests = true
|
103
|
+
|
104
|
+
ARGV.options do |opts|
|
105
|
+
opts.banner = "Usage: #{File.basename($0)} [options]"
|
106
|
+
opts.separator ""
|
107
|
+
opts.on('--[no-]rdoc', 'Prevents the creation of RDoc output.', 'Default on.') do |onrdoc|
|
108
|
+
InstallOptions.rdoc = onrdoc
|
109
|
+
end
|
110
|
+
opts.on('--[no-]ri', 'Prevents the creation of RI output.', 'Default off on mswin32.') do |onri|
|
111
|
+
InstallOptions.ri = onri
|
112
|
+
end
|
113
|
+
opts.on('--[no-]tests', 'Prevents the execution of unit tests.', 'Default on.') do |ontest|
|
114
|
+
InstallOptions.tests = ontest
|
115
|
+
end
|
116
|
+
opts.on('--quick', 'Performs a quick installation. Only the', 'installation is done.') do |quick|
|
117
|
+
InstallOptions.rdoc = false
|
118
|
+
InstallOptions.ri = false
|
119
|
+
InstallOptions.tests = false
|
120
|
+
end
|
121
|
+
opts.on('--full', 'Performs a full installation. All', 'optional installation steps are run.') do |full|
|
122
|
+
InstallOptions.rdoc = true
|
123
|
+
InstallOptions.ri = true
|
124
|
+
InstallOptions.tests = true
|
125
|
+
end
|
126
|
+
opts.separator("")
|
127
|
+
opts.on_tail('--help', "Shows this help text.") do
|
128
|
+
$stderr.puts opts
|
129
|
+
exit
|
130
|
+
end
|
131
|
+
|
132
|
+
opts.parse!
|
133
|
+
end
|
134
|
+
|
135
|
+
bds = [".", ENV['TMP'], ENV['TEMP'], "/tmp"]
|
136
|
+
|
137
|
+
version = [Config::CONFIG["MAJOR"], Config::CONFIG["MINOR"]].join(".")
|
138
|
+
ld = File.join(Config::CONFIG["libdir"], "ruby", version)
|
139
|
+
|
140
|
+
sd = Config::CONFIG["sitelibdir"]
|
141
|
+
if sd.nil?
|
142
|
+
sd = $:.find { |x| x =~ /site_ruby/ }
|
143
|
+
if sd.nil?
|
144
|
+
sd = File.join(ld, "site_ruby")
|
145
|
+
elsif sd !~ Regexp.quote(version)
|
146
|
+
sd = File.join(sd, version)
|
147
|
+
end
|
148
|
+
end
|
149
|
+
|
150
|
+
if (destdir = ENV['DESTDIR'])
|
151
|
+
bd = "#{destdir}#{Config::CONFIG['bindir']}"
|
152
|
+
sd = "#{destdir}#{sd}"
|
153
|
+
bds << bd
|
154
|
+
|
155
|
+
FileUtils.makedirs(bd)
|
156
|
+
FileUtils.makedirs(sd)
|
157
|
+
else
|
158
|
+
bds << Config::CONFIG['bindir']
|
159
|
+
end
|
160
|
+
|
161
|
+
InstallOptions.bin_dirs = bds.compact
|
162
|
+
InstallOptions.site_dir = sd
|
163
|
+
InstallOptions.bin_dir = bd
|
164
|
+
InstallOptions.lib_dir = ld
|
165
|
+
|
166
|
+
unless $loadedrdoc
|
167
|
+
InstallOptions.rdoc = false
|
168
|
+
InstallOptions.ri = false
|
169
|
+
end
|
170
|
+
end
|
171
|
+
|
172
|
+
##
|
173
|
+
# Build the rdoc documentation. Also, try to build the RI documentation.
|
174
|
+
#
|
175
|
+
def build_rdoc(files)
|
176
|
+
r = RDoc::RDoc.new
|
177
|
+
r.document(["--main", "README", "--title", "Facter -- A Fact Collecter",
|
178
|
+
"--line-numbers"] + files)
|
179
|
+
|
180
|
+
rescue RDoc::RDocError => e
|
181
|
+
$stderr.puts e.message
|
182
|
+
rescue Exception => e
|
183
|
+
$stderr.puts "Couldn't build RDoc documentation\n#{e.message}"
|
184
|
+
end
|
185
|
+
|
186
|
+
def build_ri(files)
|
187
|
+
ri = RDoc::RDoc.new
|
188
|
+
ri.document(["--ri-site", "--merge"] + files)
|
189
|
+
rescue RDoc::RDocError => e
|
190
|
+
$stderr.puts e.message
|
191
|
+
rescue Exception => e
|
192
|
+
$stderr.puts e.class
|
193
|
+
$stderr.puts "Couldn't build Ri documentation\n#{e.message}"
|
194
|
+
end
|
195
|
+
|
196
|
+
def run_tests(test_list)
|
197
|
+
begin
|
198
|
+
require 'test/unit/ui/console/testrunner'
|
199
|
+
$:.unshift "lib"
|
200
|
+
test_list.each do |test|
|
201
|
+
next if File.directory?(test)
|
202
|
+
require test
|
203
|
+
end
|
204
|
+
|
205
|
+
tests = []
|
206
|
+
ObjectSpace.each_object { |o| tests << o if o.kind_of?(Class) }
|
207
|
+
tests.delete_if { |o| !o.ancestors.include?(Test::Unit::TestCase) }
|
208
|
+
tests.delete_if { |o| o == Test::Unit::TestCase }
|
209
|
+
|
210
|
+
tests.each { |test| Test::Unit::UI::Console::TestRunner.run(test) }
|
211
|
+
$:.shift
|
212
|
+
rescue LoadError
|
213
|
+
puts "Missing testrunner library; skipping tests"
|
214
|
+
end
|
215
|
+
end
|
216
|
+
|
217
|
+
##
|
218
|
+
# Install file(s) from ./bin to Config::CONFIG['bindir']. Patch it on the way
|
219
|
+
# to insert a #! line; on a Unix install, the command is named as expected
|
220
|
+
# (e.g., bin/rdoc becomes rdoc); the shebang line handles running it. Under
|
221
|
+
# windows, we add an '.rb' extension and let file associations do their stuff.
|
222
|
+
def install_binfile(from, op_file, target)
|
223
|
+
tmp_dir = nil
|
224
|
+
InstallOptions.bin_dirs.each do |t|
|
225
|
+
if File.directory?(t) and File.writable_real?(t)
|
226
|
+
tmp_dir = t
|
227
|
+
break
|
228
|
+
end
|
229
|
+
end
|
230
|
+
|
231
|
+
fail "Cannot find a temporary directory" unless tmp_dir
|
232
|
+
tmp_file = File.join(tmp_dir, '_tmp')
|
233
|
+
ruby = File.join(Config::CONFIG['bindir'], Config::CONFIG['ruby_install_name'])
|
234
|
+
|
235
|
+
File.open(from) do |ip|
|
236
|
+
File.open(tmp_file, "w") do |op|
|
237
|
+
ruby = File.join(Config::CONFIG['bindir'], Config::CONFIG['ruby_install_name'])
|
238
|
+
op.puts "#!#{ruby}"
|
239
|
+
op.write ip.read
|
240
|
+
end
|
241
|
+
end
|
242
|
+
|
243
|
+
if Config::CONFIG["target_os"] =~ /win/io
|
244
|
+
installed_wrapper = false
|
245
|
+
|
246
|
+
if File.exists?("#{from}.bat")
|
247
|
+
FileUtils.install("#{from}.bat", File.join(target, "#{op_file}.bat"), :mode => 0755, :verbose => true)
|
248
|
+
installed_wrapper = true
|
249
|
+
end
|
250
|
+
|
251
|
+
if File.exists?("#{from}.cmd")
|
252
|
+
FileUtils.install("#{from}.cmd", File.join(target, "#{op_file}.cmd"), :mode => 0755, :verbose => true)
|
253
|
+
installed_wrapper = true
|
254
|
+
end
|
255
|
+
|
256
|
+
if not installed_wrapper
|
257
|
+
tmp_file2 = File.join(tmp_dir, '_tmp_wrapper')
|
258
|
+
cwn = File.join(Config::CONFIG['bindir'], op_file)
|
259
|
+
cwv = CMD_WRAPPER.gsub('<ruby>', ruby.gsub(%r{/}) { "\\" }).gsub!('<command>', cwn.gsub(%r{/}) { "\\" } )
|
260
|
+
|
261
|
+
File.open(tmp_file2, "wb") { |cw| cw.puts cwv }
|
262
|
+
FileUtils.install(tmp_file2, File.join(target, "#{op_file}.bat"), :mode => 0755, :verbose => true)
|
263
|
+
|
264
|
+
File.unlink(tmp_file2)
|
265
|
+
installed_wrapper = true
|
266
|
+
end
|
267
|
+
end
|
268
|
+
FileUtils.install(tmp_file, File.join(target, op_file), :mode => 0755, :verbose => true)
|
269
|
+
File.unlink(tmp_file)
|
270
|
+
end
|
271
|
+
|
272
|
+
CMD_WRAPPER = <<-EOS
|
273
|
+
@echo off
|
274
|
+
if "%OS%"=="Windows_NT" goto WinNT
|
275
|
+
<ruby> -x "<command>" %1 %2 %3 %4 %5 %6 %7 %8 %9
|
276
|
+
goto done
|
277
|
+
:WinNT
|
278
|
+
<ruby> -x "<command>" %*
|
279
|
+
goto done
|
280
|
+
:done
|
281
|
+
EOS
|
282
|
+
|
283
|
+
prepare_installation
|
284
|
+
|
285
|
+
run_tests(tests) if InstallOptions.tests
|
286
|
+
build_rdoc(rdoc) if InstallOptions.rdoc
|
287
|
+
build_ri(ri) if InstallOptions.ri
|
288
|
+
do_bins(bins, Config::CONFIG['bindir'])
|
289
|
+
do_libs(libs)
|
data/lib/facter.rb
ADDED
@@ -0,0 +1,771 @@
|
|
1
|
+
# $Id: facter.rb 63 2005-11-22 15:25:08Z luke $
|
2
|
+
#--
|
3
|
+
# Copyright 2004 Luke Kanies <luke@madstop.com>
|
4
|
+
#
|
5
|
+
# This program is free software. It may be redistributed and/or modified under
|
6
|
+
# the terms of the GPL version 2 (or later), the Perl Artistic licence, or the
|
7
|
+
# Ruby licence.
|
8
|
+
#
|
9
|
+
#--
|
10
|
+
|
11
|
+
#--------------------------------------------------------------------
|
12
|
+
class Facter
|
13
|
+
include Comparable
|
14
|
+
include Enumerable
|
15
|
+
|
16
|
+
FACTERVERSION="1.0.0"
|
17
|
+
# = Facter 1.0
|
18
|
+
# Functions as a hash of 'facts' you might care about about your
|
19
|
+
# system, such as mac address, IP address, Video card, etc.
|
20
|
+
# returns them dynamically
|
21
|
+
|
22
|
+
# == Synopsis
|
23
|
+
#
|
24
|
+
# Generally, treat <tt>Facter</tt> as a hash:
|
25
|
+
# == Example
|
26
|
+
# require 'facter'
|
27
|
+
# puts Facter['operatingsystem']
|
28
|
+
#
|
29
|
+
|
30
|
+
|
31
|
+
@@facts = {}
|
32
|
+
GREEN = "[0;32m"
|
33
|
+
RESET = "[0m"
|
34
|
+
@@debug = 0
|
35
|
+
@@os = nil
|
36
|
+
@@osrel = nil
|
37
|
+
|
38
|
+
attr_accessor :name, :os, :osrel, :hardware, :searching
|
39
|
+
|
40
|
+
#----------------------------------------------------------------
|
41
|
+
# module methods
|
42
|
+
#----------------------------------------------------------------
|
43
|
+
|
44
|
+
def Facter.version
|
45
|
+
return FACTERVERSION
|
46
|
+
end
|
47
|
+
|
48
|
+
def Facter.debug(string)
|
49
|
+
if string.nil?
|
50
|
+
return
|
51
|
+
end
|
52
|
+
if @@debug != 0
|
53
|
+
puts GREEN + string + RESET
|
54
|
+
end
|
55
|
+
end
|
56
|
+
|
57
|
+
#----------------------------------------------------------------
|
58
|
+
def Facter.[](name)
|
59
|
+
if @@facts.include?(name.downcase)
|
60
|
+
return @@facts[name.downcase]
|
61
|
+
else
|
62
|
+
return Facter.add(name)
|
63
|
+
end
|
64
|
+
end
|
65
|
+
#----------------------------------------------------------------
|
66
|
+
|
67
|
+
#----------------------------------------------------------------
|
68
|
+
def Facter.add(name)
|
69
|
+
fact = nil
|
70
|
+
dcname = name.downcase
|
71
|
+
|
72
|
+
if @@facts.include?(dcname)
|
73
|
+
fact = @@facts[dcname]
|
74
|
+
else
|
75
|
+
Facter.new(dcname)
|
76
|
+
fact = @@facts[dcname]
|
77
|
+
end
|
78
|
+
|
79
|
+
if block_given?
|
80
|
+
fact.add
|
81
|
+
end
|
82
|
+
|
83
|
+
return fact
|
84
|
+
end
|
85
|
+
#----------------------------------------------------------------
|
86
|
+
|
87
|
+
#----------------------------------------------------------------
|
88
|
+
class << self
|
89
|
+
include Enumerable
|
90
|
+
def each
|
91
|
+
@@facts.each { |name,fact|
|
92
|
+
if fact.suitable?
|
93
|
+
value = fact.value
|
94
|
+
unless value.nil?
|
95
|
+
yield name, fact.value
|
96
|
+
end
|
97
|
+
end
|
98
|
+
}
|
99
|
+
end
|
100
|
+
end
|
101
|
+
#----------------------------------------------------------------
|
102
|
+
|
103
|
+
#----------------------------------------------------------------
|
104
|
+
def Facter.reset
|
105
|
+
@@facts.each { |name,fact|
|
106
|
+
@@facts.delete(name)
|
107
|
+
}
|
108
|
+
end
|
109
|
+
#----------------------------------------------------------------
|
110
|
+
|
111
|
+
#----------------------------------------------------------------
|
112
|
+
#
|
113
|
+
def Facter.debugging(bit)
|
114
|
+
if bit
|
115
|
+
case bit
|
116
|
+
when TrueClass: @@debug = 1
|
117
|
+
when FalseClass: @@debug = 0
|
118
|
+
when Fixnum:
|
119
|
+
if bit > 0
|
120
|
+
@@debug = 1
|
121
|
+
else
|
122
|
+
@@debug = 0
|
123
|
+
end
|
124
|
+
when String:
|
125
|
+
if bit.downcase == 'off'
|
126
|
+
@@debug = 0
|
127
|
+
else
|
128
|
+
@@debug = 1
|
129
|
+
end
|
130
|
+
else
|
131
|
+
@@debug = 0
|
132
|
+
end
|
133
|
+
else
|
134
|
+
@@debug = 0
|
135
|
+
end
|
136
|
+
end
|
137
|
+
#----------------------------------------------------------------
|
138
|
+
|
139
|
+
#----------------------------------------------------------------
|
140
|
+
#def Facter.init
|
141
|
+
# @@os = @@facts["operatingsystem"].value
|
142
|
+
# @@release = @@facts["operatingsystemrelease"].value
|
143
|
+
#end
|
144
|
+
#----------------------------------------------------------------
|
145
|
+
|
146
|
+
#----------------------------------------------------------------
|
147
|
+
def Facter.list
|
148
|
+
return @@facts.keys
|
149
|
+
end
|
150
|
+
#----------------------------------------------------------------
|
151
|
+
|
152
|
+
#----------------------------------------------------------------
|
153
|
+
def Facter.os
|
154
|
+
return @@os
|
155
|
+
end
|
156
|
+
#----------------------------------------------------------------
|
157
|
+
|
158
|
+
#----------------------------------------------------------------
|
159
|
+
def Facter.release
|
160
|
+
return @@release
|
161
|
+
end
|
162
|
+
#----------------------------------------------------------------
|
163
|
+
|
164
|
+
#----------------------------------------------------------------
|
165
|
+
def <=>(other)
|
166
|
+
return self.value <=> other
|
167
|
+
end
|
168
|
+
#----------------------------------------------------------------
|
169
|
+
|
170
|
+
#----------------------------------------------------------------
|
171
|
+
def initialize(name)
|
172
|
+
@name = name.downcase
|
173
|
+
if @@facts.include?(@name)
|
174
|
+
raise "A fact named %s already exists" % name
|
175
|
+
else
|
176
|
+
@@facts[@name] = self
|
177
|
+
end
|
178
|
+
|
179
|
+
@resolves = []
|
180
|
+
@searching = false
|
181
|
+
end
|
182
|
+
#----------------------------------------------------------------
|
183
|
+
|
184
|
+
#----------------------------------------------------------------
|
185
|
+
# as long as this doesn't work over the 'net, it should probably
|
186
|
+
# be optimized to throw away any resolutions not valid for the machine
|
187
|
+
# it's running on
|
188
|
+
# but that's not currently done...
|
189
|
+
def add
|
190
|
+
resolve = Resolution.new(@name)
|
191
|
+
yield(resolve)
|
192
|
+
|
193
|
+
# skip resolves that will never be suitable for us
|
194
|
+
return unless resolve.suitable?
|
195
|
+
|
196
|
+
# insert resolves in order of number of tags
|
197
|
+
inserted = false
|
198
|
+
@resolves.each_with_index { |r,index|
|
199
|
+
if resolve.length > r.length
|
200
|
+
@resolves.insert(index,resolve)
|
201
|
+
inserted = true
|
202
|
+
break
|
203
|
+
end
|
204
|
+
}
|
205
|
+
|
206
|
+
unless inserted
|
207
|
+
@resolves.push resolve
|
208
|
+
end
|
209
|
+
end
|
210
|
+
#----------------------------------------------------------------
|
211
|
+
|
212
|
+
#----------------------------------------------------------------
|
213
|
+
# iterate across all of the currently configured resolves
|
214
|
+
# sort the resolves in order of most tags first, so we're getting
|
215
|
+
# the most specific answers first, hopefully
|
216
|
+
def each
|
217
|
+
# @resolves.sort { |a,b|
|
218
|
+
# puts "for tag %s, alength is %s and blength is %s" %
|
219
|
+
# [@name, a.length, b.length]
|
220
|
+
# b.length <=> a.length
|
221
|
+
# }.each { |resolve|
|
222
|
+
# yield resolve
|
223
|
+
# }
|
224
|
+
@resolves.each { |r| yield r }
|
225
|
+
end
|
226
|
+
#----------------------------------------------------------------
|
227
|
+
|
228
|
+
#----------------------------------------------------------------
|
229
|
+
# return a count of resolution mechanisms available
|
230
|
+
def count
|
231
|
+
return @resolves.length
|
232
|
+
end
|
233
|
+
#----------------------------------------------------------------
|
234
|
+
|
235
|
+
#----------------------------------------------------------------
|
236
|
+
# is this fact suitable for finding answers on this host?
|
237
|
+
# again, this should really be optimizing by throwing away everything
|
238
|
+
# not suitable, but...
|
239
|
+
def suitable?
|
240
|
+
return false if @resolves.length == 0
|
241
|
+
|
242
|
+
unless defined? @suitable
|
243
|
+
@suitable = false
|
244
|
+
self.each { |resolve|
|
245
|
+
if resolve.suitable?
|
246
|
+
@suitable = true
|
247
|
+
break
|
248
|
+
end
|
249
|
+
}
|
250
|
+
end
|
251
|
+
|
252
|
+
return @suitable
|
253
|
+
end
|
254
|
+
#----------------------------------------------------------------
|
255
|
+
|
256
|
+
#----------------------------------------------------------------
|
257
|
+
def value
|
258
|
+
# make sure we don't get stuck in recursive dependency loops
|
259
|
+
if @searching
|
260
|
+
Facter.debug "Caught recursion on %s" % @name
|
261
|
+
|
262
|
+
# return a cached value if we've got it
|
263
|
+
if @value
|
264
|
+
return @value
|
265
|
+
else
|
266
|
+
return nil
|
267
|
+
end
|
268
|
+
end
|
269
|
+
@value = nil
|
270
|
+
foundsuits = false
|
271
|
+
|
272
|
+
if @resolves.length == 0
|
273
|
+
Facter.debug "No resolves for %s" % @name
|
274
|
+
return nil
|
275
|
+
end
|
276
|
+
|
277
|
+
@searching = true
|
278
|
+
@resolves.each { |resolve|
|
279
|
+
#Facter.debug "Searching resolves for %s" % @name
|
280
|
+
if resolve.suitable?
|
281
|
+
@value = resolve.value
|
282
|
+
foundsuits = true
|
283
|
+
else
|
284
|
+
Facter.debug "Unsuitable resolve %s for %s" % [resolve,@name]
|
285
|
+
end
|
286
|
+
unless @value.nil? or @value == ""
|
287
|
+
break
|
288
|
+
end
|
289
|
+
}
|
290
|
+
@searching = false
|
291
|
+
|
292
|
+
unless foundsuits
|
293
|
+
Facter.debug "Found no suitable resolves of %s for %s" %
|
294
|
+
[@resolves.length,@name]
|
295
|
+
end
|
296
|
+
|
297
|
+
if @value.nil?
|
298
|
+
# nothing
|
299
|
+
Facter.debug("value for %s is still nil" % @name)
|
300
|
+
return nil
|
301
|
+
else
|
302
|
+
return @value
|
303
|
+
end
|
304
|
+
end
|
305
|
+
#----------------------------------------------------------------
|
306
|
+
|
307
|
+
#----------------------------------------------------------------
|
308
|
+
class Resolution
|
309
|
+
attr_accessor :interpreter, :code, :name
|
310
|
+
|
311
|
+
def Resolution.exec(code, interpreter = "/bin/sh")
|
312
|
+
if interpreter == "/bin/sh"
|
313
|
+
binary = code.split(/\s+/).shift
|
314
|
+
|
315
|
+
path = nil
|
316
|
+
if binary !~ /^\//
|
317
|
+
path = %x{which #{binary} 2>/dev/null}.chomp
|
318
|
+
if path == ""
|
319
|
+
# we don't have the binary necessary
|
320
|
+
return nil
|
321
|
+
end
|
322
|
+
else
|
323
|
+
path = binary
|
324
|
+
end
|
325
|
+
|
326
|
+
unless FileTest.exists?(path)
|
327
|
+
# our binary does not exist
|
328
|
+
return nil
|
329
|
+
end
|
330
|
+
out = nil
|
331
|
+
begin
|
332
|
+
out = %x{#{code}}.chomp
|
333
|
+
rescue => detail
|
334
|
+
$stderr.puts detail
|
335
|
+
end
|
336
|
+
if out == ""
|
337
|
+
return nil
|
338
|
+
else
|
339
|
+
return out
|
340
|
+
end
|
341
|
+
else
|
342
|
+
raise "non-sh interpreters are not currently supported"
|
343
|
+
end
|
344
|
+
end
|
345
|
+
|
346
|
+
def initialize(name)
|
347
|
+
@name = name
|
348
|
+
@tags = []
|
349
|
+
end
|
350
|
+
|
351
|
+
def length
|
352
|
+
@tags.length
|
353
|
+
end
|
354
|
+
|
355
|
+
def suitable?
|
356
|
+
unless defined? @suitable
|
357
|
+
@suitable = true
|
358
|
+
if @tags.length == 0
|
359
|
+
#Facter.debug "'%s' has no tags" % @name
|
360
|
+
return true
|
361
|
+
end
|
362
|
+
@tags.each { |tag|
|
363
|
+
unless tag.true?
|
364
|
+
#Facter.debug "'%s' is false" % tag
|
365
|
+
@suitable = false
|
366
|
+
end
|
367
|
+
}
|
368
|
+
end
|
369
|
+
|
370
|
+
return @suitable
|
371
|
+
end
|
372
|
+
|
373
|
+
def tag(fact,op,value)
|
374
|
+
@tags.push Tag.new(fact,op,value)
|
375
|
+
end
|
376
|
+
|
377
|
+
def to_s
|
378
|
+
return self.value()
|
379
|
+
end
|
380
|
+
|
381
|
+
# how we get a value for our resolution mechanism
|
382
|
+
def value
|
383
|
+
#puts "called %s value" % @name
|
384
|
+
value = nil
|
385
|
+
if @code.is_a?(Proc)
|
386
|
+
value = @code.call()
|
387
|
+
else
|
388
|
+
unless defined? @interpreter
|
389
|
+
@interpreter = "/bin/sh"
|
390
|
+
end
|
391
|
+
if @code.nil?
|
392
|
+
$stderr.puts "Code for %s is nil" % @name
|
393
|
+
else
|
394
|
+
value = Resolution.exec(@code,@interpreter)
|
395
|
+
end
|
396
|
+
end
|
397
|
+
|
398
|
+
if value == ""
|
399
|
+
value = nil
|
400
|
+
end
|
401
|
+
|
402
|
+
return value
|
403
|
+
end
|
404
|
+
end
|
405
|
+
#----------------------------------------------------------------
|
406
|
+
|
407
|
+
#----------------------------------------------------------------
|
408
|
+
class Tag
|
409
|
+
attr_accessor :fact, :op, :value
|
410
|
+
|
411
|
+
def initialize(fact,op,value)
|
412
|
+
@fact = fact
|
413
|
+
if op == "="
|
414
|
+
op = "=="
|
415
|
+
end
|
416
|
+
@op = op
|
417
|
+
@value = value
|
418
|
+
end
|
419
|
+
|
420
|
+
def to_s
|
421
|
+
return "'%s' %s '%s'" % [@fact,@op,@value]
|
422
|
+
end
|
423
|
+
|
424
|
+
def true?
|
425
|
+
value = Facter[@fact].value
|
426
|
+
|
427
|
+
if value.nil?
|
428
|
+
return false
|
429
|
+
end
|
430
|
+
|
431
|
+
str = "'%s' %s '%s'" % [value,@op,@value]
|
432
|
+
begin
|
433
|
+
if eval(str)
|
434
|
+
return true
|
435
|
+
else
|
436
|
+
return false
|
437
|
+
end
|
438
|
+
rescue => detail
|
439
|
+
$stderr.puts "Failed to test '%s': %s" % [str,detail]
|
440
|
+
return false
|
441
|
+
end
|
442
|
+
end
|
443
|
+
end
|
444
|
+
#----------------------------------------------------------------
|
445
|
+
|
446
|
+
# Load all of the default facts
|
447
|
+
def Facter.load
|
448
|
+
Facter["OperatingSystem"].add { |obj|
|
449
|
+
obj.code = 'uname -s'
|
450
|
+
}
|
451
|
+
|
452
|
+
Facter["OperatingSystemRelease"].add { |obj|
|
453
|
+
obj.code = 'uname -r'
|
454
|
+
}
|
455
|
+
|
456
|
+
Facter["HardwareModel"].add { |obj|
|
457
|
+
obj.code = 'uname -m'
|
458
|
+
#obj.os = "SunOS"
|
459
|
+
#obj.tag("operatingsystem","=","SunOS")
|
460
|
+
}
|
461
|
+
|
462
|
+
Facter["CfKey"].add { |obj|
|
463
|
+
obj.code = proc {
|
464
|
+
value = nil
|
465
|
+
["/usr/local/etc/cfkey.pub",
|
466
|
+
"/etc/cfkey.pub",
|
467
|
+
"/var/cfng/keys/localhost.pub",
|
468
|
+
"/var/cfengine/ppkeys/localhost.pub"
|
469
|
+
].each { |file|
|
470
|
+
if FileTest.file?(file)
|
471
|
+
File.open(file) { |openfile|
|
472
|
+
value = openfile.readlines.reject { |line|
|
473
|
+
line =~ /PUBLIC KEY/
|
474
|
+
}.collect { |line|
|
475
|
+
line.chomp
|
476
|
+
}.join("")
|
477
|
+
}
|
478
|
+
end
|
479
|
+
if value
|
480
|
+
break
|
481
|
+
end
|
482
|
+
}
|
483
|
+
|
484
|
+
value
|
485
|
+
}
|
486
|
+
}
|
487
|
+
|
488
|
+
Facter["Domain"].add { |obj|
|
489
|
+
obj.code = proc {
|
490
|
+
if defined? $domain and ! $domain.nil?
|
491
|
+
$domain
|
492
|
+
end
|
493
|
+
}
|
494
|
+
}
|
495
|
+
Facter["Domain"].add { |obj|
|
496
|
+
obj.code = proc {
|
497
|
+
domain = Resolution.exec('domainname')
|
498
|
+
# make sure it's a real domain
|
499
|
+
if domain =~ /.+\..+/
|
500
|
+
domain
|
501
|
+
else
|
502
|
+
nil
|
503
|
+
end
|
504
|
+
}
|
505
|
+
}
|
506
|
+
Facter["Domain"].add { |obj|
|
507
|
+
obj.code = proc {
|
508
|
+
value = nil
|
509
|
+
unless FileTest.exists?("/etc/resolv.conf")
|
510
|
+
return nil
|
511
|
+
end
|
512
|
+
File.open("/etc/resolv.conf") { |file|
|
513
|
+
# is the domain set?
|
514
|
+
file.each { |line|
|
515
|
+
if line =~ /domain\s+(\S+)/
|
516
|
+
value = $1
|
517
|
+
break
|
518
|
+
end
|
519
|
+
}
|
520
|
+
}
|
521
|
+
! value and File.open("/etc/resolv.conf") { |file|
|
522
|
+
# is the search path set?
|
523
|
+
file.each { |line|
|
524
|
+
if line =~ /search\s+(\S+)/
|
525
|
+
value = $1
|
526
|
+
break
|
527
|
+
end
|
528
|
+
}
|
529
|
+
}
|
530
|
+
value
|
531
|
+
}
|
532
|
+
}
|
533
|
+
Facter["Hostname"].add { |obj|
|
534
|
+
obj.code = proc {
|
535
|
+
hostname = nil
|
536
|
+
name = Resolution.exec('hostname')
|
537
|
+
if name =~ /^([\w-]+)\.(.+)$/
|
538
|
+
hostname = $1
|
539
|
+
# the Domain class uses this
|
540
|
+
$domain = $2
|
541
|
+
else
|
542
|
+
hostname = name
|
543
|
+
end
|
544
|
+
hostname
|
545
|
+
}
|
546
|
+
}
|
547
|
+
|
548
|
+
Facter["IPHostNumber"].add { |obj|
|
549
|
+
obj.code = proc {
|
550
|
+
require 'resolv'
|
551
|
+
|
552
|
+
begin
|
553
|
+
hostname = Facter["hostname"].value
|
554
|
+
ip = Resolv.getaddress(hostname)
|
555
|
+
unless ip == "127.0.0.1"
|
556
|
+
ip
|
557
|
+
end
|
558
|
+
rescue Resolv::ResolvError
|
559
|
+
nil
|
560
|
+
rescue NoMethodError # i think this is a bug in resolv.rb?
|
561
|
+
nil
|
562
|
+
end
|
563
|
+
}
|
564
|
+
}
|
565
|
+
Facter["IPHostNumber"].add { |obj|
|
566
|
+
obj.code = proc {
|
567
|
+
hostname = Facter["hostname"].value
|
568
|
+
# crap, we need Hostname to exist for this to
|
569
|
+
# work
|
570
|
+
list = Resolution.exec("host #{hostname}").chomp.split(/\s/)
|
571
|
+
if defined? list[-1] and list[-1] =~ /[0-9]+\.[0-9]+\.[0-9]+\.[0-9]+/
|
572
|
+
list[-1]
|
573
|
+
end
|
574
|
+
}
|
575
|
+
}
|
576
|
+
|
577
|
+
["/etc/ssh","/usr/local/etc/ssh","/etc","/usr/local/etc"].each { |dir|
|
578
|
+
{"SSHDSAKey" => "ssh_host_dsa_key.pub",
|
579
|
+
"SSHRSAKey" => "ssh_host_rsa_key.pub"}.each { |name,file|
|
580
|
+
Facter[name].add { |obj|
|
581
|
+
obj.code = proc {
|
582
|
+
value = nil
|
583
|
+
filepath = File.join(dir,file)
|
584
|
+
if FileTest.file?(filepath)
|
585
|
+
begin
|
586
|
+
value = File.open(filepath).read.chomp
|
587
|
+
rescue
|
588
|
+
return nil
|
589
|
+
end
|
590
|
+
end
|
591
|
+
return value
|
592
|
+
} # end of proc
|
593
|
+
} # end of add
|
594
|
+
} # end of hash each
|
595
|
+
} # end of dir each
|
596
|
+
|
597
|
+
Facter["UniqueId"].add { |obj|
|
598
|
+
obj.code = 'hostid'
|
599
|
+
obj.interpreter = '/bin/sh'
|
600
|
+
obj.tag("operatingsystem","=","SunOS")
|
601
|
+
#obj.os = "SunOS"
|
602
|
+
}
|
603
|
+
Facter["HardwareISA"].add { |obj|
|
604
|
+
obj.code = 'uname -p'
|
605
|
+
obj.interpreter = '/bin/sh'
|
606
|
+
#obj.os = "SunOS"
|
607
|
+
obj.tag("operatingsystem","=","SunOS")
|
608
|
+
}
|
609
|
+
Facter["MacAddress"].add { |obj|
|
610
|
+
#obj.os = "SunOS"
|
611
|
+
obj.tag("operatingsystem","=","SunOS")
|
612
|
+
obj.code = proc {
|
613
|
+
ether = nil
|
614
|
+
output = %x{/sbin/ifconfig -a}
|
615
|
+
|
616
|
+
output =~ /ether (\w{1,2}:\w{1,2}:\w{1,2}:\w{1,2}:\w{1,2}:\w{1,2})/
|
617
|
+
ether = $1
|
618
|
+
|
619
|
+
ether
|
620
|
+
}
|
621
|
+
}
|
622
|
+
Facter["MacAddress"].add { |obj|
|
623
|
+
#obj.os = "Darwin"
|
624
|
+
obj.tag("operatingsystem","=","Darwin")
|
625
|
+
obj.code = proc {
|
626
|
+
ether = nil
|
627
|
+
output = %x{/sbin/ifconfig}
|
628
|
+
|
629
|
+
output.split(/^\S/).each { |str|
|
630
|
+
if str =~ /10baseT/ # we're wired
|
631
|
+
str =~ /ether (\w\w:\w\w:\w\w:\w\w:\w\w:\w\w)/
|
632
|
+
ether = $1
|
633
|
+
end
|
634
|
+
}
|
635
|
+
|
636
|
+
ether
|
637
|
+
}
|
638
|
+
}
|
639
|
+
Facter["IPHostnumber"].add { |obj|
|
640
|
+
#obj.os = "Darwin"
|
641
|
+
obj.tag("operatingsystem","=","Darwin")
|
642
|
+
obj.code = proc {
|
643
|
+
ip = nil
|
644
|
+
output = %x{/sbin/ifconfig}
|
645
|
+
|
646
|
+
output.split(/^\S/).each { |str|
|
647
|
+
if str =~ /inet ([0-9]+\.[0-9]+\.[0-9]+\.[0-9]+)/
|
648
|
+
tmp = $1
|
649
|
+
unless tmp =~ /127\./
|
650
|
+
ip = tmp
|
651
|
+
break
|
652
|
+
end
|
653
|
+
end
|
654
|
+
}
|
655
|
+
|
656
|
+
ip
|
657
|
+
}
|
658
|
+
}
|
659
|
+
Facter["Hostname"].add { |obj|
|
660
|
+
#obj.os = "Darwin"
|
661
|
+
#obj.release = "R7"
|
662
|
+
obj.tag("operatingsystem","=","Darwin")
|
663
|
+
obj.tag("operatingsystemrelease","=","R7")
|
664
|
+
obj.code = proc {
|
665
|
+
hostname = nil
|
666
|
+
File.open(
|
667
|
+
"/Library/Preferences/SystemConfiguration/preferences.plist"
|
668
|
+
) { |file|
|
669
|
+
found = 0
|
670
|
+
file.each { |line|
|
671
|
+
if line =~ /ComputerName/i
|
672
|
+
found = 1
|
673
|
+
next
|
674
|
+
end
|
675
|
+
if found == 1
|
676
|
+
if line =~ /<string>([\w|-]+)<\/string>/
|
677
|
+
hostname = $1
|
678
|
+
break
|
679
|
+
end
|
680
|
+
end
|
681
|
+
}
|
682
|
+
}
|
683
|
+
|
684
|
+
if hostname != nil
|
685
|
+
hostname
|
686
|
+
end
|
687
|
+
}
|
688
|
+
}
|
689
|
+
Facter["IPHostnumber"].add { |obj|
|
690
|
+
#obj.os = "Darwin"
|
691
|
+
#obj.release = "R6"
|
692
|
+
obj.tag("operatingsystem","=","Darwin")
|
693
|
+
obj.tag("operatingsystemrelease","=","R6")
|
694
|
+
obj.code = proc {
|
695
|
+
hostname = nil
|
696
|
+
File.open(
|
697
|
+
"/var/db/SystemConfiguration/preferences.xml"
|
698
|
+
) { |file|
|
699
|
+
found = 0
|
700
|
+
file.each { |line|
|
701
|
+
if line =~ /ComputerName/i
|
702
|
+
found = 1
|
703
|
+
next
|
704
|
+
end
|
705
|
+
if found == 1
|
706
|
+
if line =~ /<string>([\w|-]+)<\/string>/
|
707
|
+
hostname = $1
|
708
|
+
break
|
709
|
+
end
|
710
|
+
end
|
711
|
+
}
|
712
|
+
}
|
713
|
+
|
714
|
+
if hostname != nil
|
715
|
+
hostname
|
716
|
+
end
|
717
|
+
}
|
718
|
+
}
|
719
|
+
Facter["IPHostnumber"].add { |obj|
|
720
|
+
#obj.os = "Darwin"
|
721
|
+
#obj.release = "R6"
|
722
|
+
obj.tag("operatingsystem","=","Darwin")
|
723
|
+
obj.tag("operatingsystemrelease","=","R6")
|
724
|
+
obj.code = proc {
|
725
|
+
ether = nil
|
726
|
+
output = %x{/sbin/ifconfig}
|
727
|
+
|
728
|
+
output =~ /HWaddr (\w\w:\w\w:\w\w:\w\w:\w\w:\w\w)/
|
729
|
+
ether = $1
|
730
|
+
|
731
|
+
ether
|
732
|
+
}
|
733
|
+
}
|
734
|
+
Facter["Distro"].add { |obj|
|
735
|
+
#obj.os = "Linux"
|
736
|
+
obj.tag("operatingsystem","=","Linux")
|
737
|
+
obj.code = proc {
|
738
|
+
if FileTest.exists?("/etc/debian_version")
|
739
|
+
return "Debian"
|
740
|
+
elsif FileTest.exists?("/etc/gentoo-release")
|
741
|
+
return "Gentoo"
|
742
|
+
elsif FileTest.exists?("/etc/fedora-release")
|
743
|
+
return "Fedora"
|
744
|
+
elsif FileTest.exists?("/etc/redhat-release")
|
745
|
+
return "RedHat"
|
746
|
+
end
|
747
|
+
}
|
748
|
+
}
|
749
|
+
Facter["ps"].add { |obj|
|
750
|
+
obj.code = "echo 'ps -ef'"
|
751
|
+
}
|
752
|
+
Facter["ps"].add { |obj|
|
753
|
+
obj.tag("operatingsystem","=","Darwin")
|
754
|
+
obj.code = "echo 'ps -auxwww'"
|
755
|
+
}
|
756
|
+
|
757
|
+
Facter["id"].add { |obj|
|
758
|
+
obj.tag("operatingsystem","=","Linux")
|
759
|
+
obj.code = "whoami"
|
760
|
+
}
|
761
|
+
end
|
762
|
+
|
763
|
+
Facter.load
|
764
|
+
end
|
765
|
+
|
766
|
+
# try to load a local fact library, if there happens to be one
|
767
|
+
begin
|
768
|
+
require 'facter/local'
|
769
|
+
rescue LoadError
|
770
|
+
# no worries
|
771
|
+
end
|