facter 1.1.1 → 1.3.3

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.

Files changed (10) hide show
  1. data/CHANGELOG +63 -0
  2. data/COPYING +416 -251
  3. data/Rakefile +30 -277
  4. data/bin/facter +60 -7
  5. data/install.rb +303 -289
  6. data/lib/facter.rb +280 -137
  7. data/lib/facter/memory.rb +62 -0
  8. data/lib/facter/processor.rb +52 -0
  9. metadata +32 -34
  10. data/CHANGES +0 -21
@@ -1,9 +1,20 @@
1
- # $Id: facter.rb 78 2006-01-10 22:57:50Z luke $
1
+ # $Id: facter.rb 153 2006-06-28 17:38:18Z luke $
2
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 Apache license.
3
+ # Copyright 2006 Luke Kanies <luke@madstop.com>
4
+ #
5
+ # This library is free software; you can redistribute it and/or
6
+ # modify it under the terms of the GNU Lesser General Public
7
+ # License as published by the Free Software Foundation; either
8
+ # version 2.1 of the License, or (at your option) any later version.
9
+ #
10
+ # This library is distributed in the hope that it will be useful,
11
+ # but WITHOUT ANY WARRANTY; without even the implied warranty of
12
+ # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
13
+ # Lesser General Public License for more details.
14
+ #
15
+ # You should have received a copy of the GNU Lesser General Public
16
+ # License along with this library; if not, write to the Free Software
17
+ # Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
7
18
  #
8
19
  #--
9
20
 
@@ -11,8 +22,8 @@ class Facter
11
22
  include Comparable
12
23
  include Enumerable
13
24
 
14
- FACTERVERSION = '1.1.1'
15
- # = Facter 1.0
25
+ FACTERVERSION = '1.3.3'
26
+ # = Facter
16
27
  # Functions as a hash of 'facts' you might care about about your
17
28
  # system, such as mac address, IP address, Video card, etc.
18
29
  # returns them dynamically
@@ -30,10 +41,8 @@ FACTERVERSION = '1.1.1'
30
41
  GREEN = ""
31
42
  RESET = ""
32
43
  @@debug = 0
33
- @@os = nil
34
- @@osrel = nil
35
44
 
36
- attr_accessor :name, :os, :osrel, :hardware, :searching
45
+ attr_accessor :name, :searching, :ldapname
37
46
 
38
47
  # module methods
39
48
 
@@ -52,17 +61,18 @@ FACTERVERSION = '1.1.1'
52
61
  end
53
62
  end
54
63
 
55
- # Return a fact object by name. If you use this, you still have to call 'value'
56
- # on it to retrieve the actual value.
57
- def Facter.[](name)
58
- @@facts[name.to_s.downcase]
64
+ # Return a fact object by name. If you use this, you still have to call
65
+ # 'value' on it to retrieve the actual value.
66
+ def Facter.[](name)
67
+ name = name.to_s.downcase
68
+ @@facts[name]
59
69
  end
60
70
 
61
- # Add a resolution mechanism for a named fact. This does not distinguish between
62
- # adding a new fact and adding a new way to resolve a fact.
71
+ # Add a resolution mechanism for a named fact. This does not distinguish
72
+ # between adding a new fact and adding a new way to resolve a fact.
63
73
  def Facter.add(name, &block)
64
74
  fact = nil
65
- dcname = name.downcase
75
+ dcname = name.to_s.downcase
66
76
 
67
77
  if @@facts.include?(dcname)
68
78
  fact = @@facts[dcname]
@@ -93,26 +103,42 @@ FACTERVERSION = '1.1.1'
93
103
  end
94
104
  }
95
105
  end
96
- end
97
106
 
98
- def Facter.value(name)
99
- if @@facts.include?(name.to_s.downcase)
100
- @@facts[name.to_s.downcase].value
101
- else
102
- nil
103
- end
104
- end
107
+ # Allow users to call fact names directly on the Facter class,
108
+ # either retrieving the value or comparing it to an existing value.
109
+ def method_missing(name, *args)
110
+ question = false
111
+ if name.to_s =~ /\?$/
112
+ question = true
113
+ name = name.to_s.sub(/\?$/,'')
114
+ end
105
115
 
106
- # Flush all cached values.
107
- def Facter.flush
108
- @@facts.each { |fact| fact.flush }
116
+ if fact = self[name]
117
+ if question
118
+ value = fact.value.downcase
119
+ args.each do |arg|
120
+ if arg.to_s.downcase == value
121
+ return true
122
+ end
123
+ end
124
+
125
+ # If we got this far, there was no match.
126
+ return false
127
+ else
128
+ return fact.value
129
+ end
130
+ else
131
+ # Else, fail like a normal missing method.
132
+ return super
133
+ end
134
+ end
109
135
  end
110
136
 
111
- # Remove them all.
112
- def Facter.reset
113
- @@facts.each { |name,fact|
114
- @@facts.delete(name)
115
- }
137
+ # Clear all facts. Mostly used for testing.
138
+ def Facter.clear
139
+ Facter.reset
140
+ Facter.flush
141
+ @@facts.clear
116
142
  end
117
143
 
118
144
  # Set debugging on or off.
@@ -141,11 +167,44 @@ FACTERVERSION = '1.1.1'
141
167
  end
142
168
  end
143
169
 
170
+ # Flush all cached values.
171
+ def Facter.flush
172
+ @@facts.each { |fact| fact.flush }
173
+ end
174
+
144
175
  # Return a list of all of the facts.
145
176
  def Facter.list
146
177
  return @@facts.keys
147
178
  end
148
179
 
180
+ # Remove them all.
181
+ def Facter.reset
182
+ @@facts.each { |name,fact|
183
+ @@facts.delete(name)
184
+ }
185
+ end
186
+
187
+ # Return a hash of all of our facts.
188
+ def Facter.to_hash(*tags)
189
+ @@facts.inject({}) do |h, ary|
190
+ if ary[1].suitable? and (tags.empty? or ary[1].tagged?(*tags))
191
+ value = ary[1].value
192
+ if value
193
+ h[ary[0]] = value
194
+ end
195
+ end
196
+ h
197
+ end
198
+ end
199
+
200
+ def Facter.value(name)
201
+ if @@facts.include?(name.to_s.downcase)
202
+ @@facts[name.to_s.downcase].value
203
+ else
204
+ nil
205
+ end
206
+ end
207
+
149
208
  # Compare one value to another.
150
209
  def <=>(other)
151
210
  return self.value <=> other
@@ -158,7 +217,7 @@ FACTERVERSION = '1.1.1'
158
217
 
159
218
  # Create a new fact, with no resolution mechanisms.
160
219
  def initialize(name)
161
- @name = name.downcase
220
+ @name = name.downcase if name.is_a? String
162
221
  if @@facts.include?(@name)
163
222
  raise ArgumentError, "A fact named %s already exists" % name
164
223
  else
@@ -166,9 +225,12 @@ FACTERVERSION = '1.1.1'
166
225
  end
167
226
 
168
227
  @resolves = []
228
+ @tags = []
169
229
  @searching = false
170
230
 
171
231
  @value = nil
232
+
233
+ @ldapname = name
172
234
  end
173
235
 
174
236
  # Add a new resolution mechanism. This requires a block, which will then
@@ -180,6 +242,8 @@ FACTERVERSION = '1.1.1'
180
242
 
181
243
  resolve = Resolution.new(@name)
182
244
 
245
+ resolve.fact = self
246
+
183
247
  resolve.instance_eval(&block)
184
248
 
185
249
  # skip resolves that will never be suitable for us
@@ -187,7 +251,7 @@ FACTERVERSION = '1.1.1'
187
251
  return
188
252
  end
189
253
 
190
- # insert resolves in order of number of tags
254
+ # insert resolves in order of number of confinements
191
255
  inserted = false
192
256
  @resolves.each_with_index { |r,index|
193
257
  if resolve.length > r.length
@@ -208,7 +272,7 @@ FACTERVERSION = '1.1.1'
208
272
  end
209
273
 
210
274
  # Iterate across all of the fact resolution mechanisms and yield each in
211
- # turn. These are inserted in order of most tags.
275
+ # turn. These are inserted in order of most confinements.
212
276
  def each
213
277
  @resolves.each { |r| yield r }
214
278
  end
@@ -239,6 +303,29 @@ FACTERVERSION = '1.1.1'
239
303
  return @suitable
240
304
  end
241
305
 
306
+ # Add one ore more tags
307
+ def tag(*tags)
308
+ tags.each do |t|
309
+ t = t.to_s.downcase.intern
310
+ @tags << t unless @tags.include?(t)
311
+ end
312
+ end
313
+
314
+ # Is our fact tagged with all of the specified tags?
315
+ def tagged?(*tags)
316
+ tags.each do |t|
317
+ unless @tags.include? t.to_s.downcase.intern
318
+ return false
319
+ end
320
+ end
321
+
322
+ return true
323
+ end
324
+
325
+ def tags
326
+ @tags.dup
327
+ end
328
+
242
329
  # Return the value for a given fact. Searches through all of the mechanisms
243
330
  # and returns either the first value or nil.
244
331
  def value
@@ -290,12 +377,13 @@ FACTERVERSION = '1.1.1'
290
377
  end
291
378
  end
292
379
 
293
- # An actual fact resolution mechanism. These are largely just chunks of code,
294
- # with optional tags restricting the mechanisms to only working on specific
295
- # systems. Note that the tags are always ANDed, so any tags specified
296
- # must all be true for the resolution to be suitable.
380
+ # An actual fact resolution mechanism. These are largely just chunks of
381
+ # code, with optional confinements restricting the mechanisms to only working on
382
+ # specific systems. Note that the confinements are always ANDed, so any
383
+ # confinements specified must all be true for the resolution to be
384
+ # suitable.
297
385
  class Resolution
298
- attr_accessor :interpreter, :code, :name
386
+ attr_accessor :interpreter, :code, :name, :fact
299
387
 
300
388
  # Execute a chunk of code.
301
389
  def Resolution.exec(code, interpreter = "/bin/sh")
@@ -330,20 +418,33 @@ FACTERVERSION = '1.1.1'
330
418
  return out
331
419
  end
332
420
  else
333
- raise ArgumentError, "non-sh interpreters are not currently supported"
421
+ raise ArgumentError,
422
+ "non-sh interpreters are not currently supported"
423
+ end
424
+ end
425
+
426
+ # Add a new confine to the resolution mechanism.
427
+ def confine(*args)
428
+ if args[0].is_a? Hash
429
+ args[0].each do |fact, values|
430
+ @confines.push Confine.new(fact,*values)
431
+ end
432
+ else
433
+ fact = args.shift
434
+ @confines.push Confine.new(fact,*args)
334
435
  end
335
436
  end
336
437
 
337
438
  # Create a new resolution mechanism.
338
439
  def initialize(name)
339
440
  @name = name
340
- @tags = []
441
+ @confines = []
341
442
  @value = nil
342
443
  end
343
444
 
344
- # Return the number of tags.
445
+ # Return the number of confines.
345
446
  def length
346
- @tags.length
447
+ @confines.length
347
448
  end
348
449
 
349
450
  # Set our code for returning a value.
@@ -359,15 +460,21 @@ FACTERVERSION = '1.1.1'
359
460
  end
360
461
  end
361
462
 
463
+ # Set the name by which this parameter is known in LDAP. The default
464
+ # is just the fact name.
465
+ def setldapname(name)
466
+ @fact.ldapname = name
467
+ end
468
+
362
469
  # Is this resolution mechanism suitable on the system in question?
363
470
  def suitable?
364
471
  unless defined? @suitable
365
472
  @suitable = true
366
- if @tags.length == 0
473
+ if @confines.length == 0
367
474
  return true
368
475
  end
369
- @tags.each { |tag|
370
- unless tag.true?
476
+ @confines.each { |confine|
477
+ unless confine.true?
371
478
  @suitable = false
372
479
  end
373
480
  }
@@ -376,9 +483,9 @@ FACTERVERSION = '1.1.1'
376
483
  return @suitable
377
484
  end
378
485
 
379
- # Add a new tag to the resolution mechanism.
380
- def tag(fact,*values)
381
- @tags.push Tag.new(fact,*values)
486
+ # Set tags on our parent fact.
487
+ def tag(*values)
488
+ @fact.tag(*values)
382
489
  end
383
490
 
384
491
  def to_s
@@ -413,14 +520,21 @@ FACTERVERSION = '1.1.1'
413
520
 
414
521
  # A restricting tag for fact resolution mechanisms. The tag must be true
415
522
  # for the resolution mechanism to be suitable.
416
- class Tag
523
+ class Confine
417
524
  attr_accessor :fact, :op, :value
418
525
 
419
- # Add the tag. Requires the fact name, an operator, and the value we're
420
- # comparing to.
526
+ # Add the tag. Requires the fact name, an operator, and the value
527
+ # we're comparing to.
421
528
  def initialize(fact, *values)
529
+ fact = fact.to_s if fact.is_a? Symbol
422
530
  @fact = fact
423
- @values = values
531
+ @values = values.collect do |value|
532
+ if value.is_a? String
533
+ value
534
+ else
535
+ value.to_s
536
+ end
537
+ end
424
538
  end
425
539
 
426
540
  def to_s
@@ -441,7 +555,7 @@ FACTERVERSION = '1.1.1'
441
555
  end
442
556
 
443
557
  retval = @values.find { |v|
444
- if value == v
558
+ if value.downcase == v.downcase
445
559
  break true
446
560
  end
447
561
  }
@@ -457,7 +571,35 @@ FACTERVERSION = '1.1.1'
457
571
  end
458
572
 
459
573
  # Load all of the default facts
460
- def Facter.load
574
+ def Facter.loadfacts
575
+ Facter.add("FacterVersion") do
576
+ setcode { FACTERVERSION.to_s }
577
+ end
578
+
579
+ Facter.add("RubyVersion") do
580
+ setcode { RUBY_VERSION.to_s }
581
+ end
582
+
583
+ Facter.add("PuppetVersion") do
584
+ setcode {
585
+ begin
586
+ require 'puppet'
587
+ Puppet::PUPPETVERSION.to_s
588
+ rescue LoadError
589
+ nil
590
+ end
591
+ }
592
+ end
593
+
594
+ Facter.add :rubysitedir do
595
+ setcode do
596
+ version = RUBY_VERSION.to_s.sub(/\.\d+$/, '')
597
+ $:.find do |dir|
598
+ dir =~ /#{File.join("site_ruby", version)}$/
599
+ end
600
+ end
601
+ end
602
+
461
603
  Facter.add("Kernel") do
462
604
  setcode 'uname -s'
463
605
  end
@@ -477,13 +619,13 @@ FACTERVERSION = '1.1.1'
477
619
 
478
620
  Facter.add("OperatingSystem") do
479
621
  #obj.os = "Linux"
480
- tag("kernel","SunOS")
622
+ confine :kernel => :sunos
481
623
  setcode do "Solaris" end
482
624
  end
483
625
 
484
626
  Facter.add("OperatingSystem") do
485
627
  #obj.os = "Linux"
486
- tag("kernel","Linux")
628
+ confine :kernel => :linux
487
629
  setcode do
488
630
  if FileTest.exists?("/etc/debian_version")
489
631
  "Debian"
@@ -492,7 +634,12 @@ FACTERVERSION = '1.1.1'
492
634
  elsif FileTest.exists?("/etc/fedora-release")
493
635
  "Fedora"
494
636
  elsif FileTest.exists?("/etc/redhat-release")
495
- "RedHat"
637
+ txt = File.read("/etc/redhat-release")
638
+ if txt =~ /centos/i
639
+ "CentOS"
640
+ else
641
+ "RedHat"
642
+ end
496
643
  elsif FileTest.exists?("/etc/SuSE-release")
497
644
  "SuSE"
498
645
  end
@@ -501,7 +648,19 @@ FACTERVERSION = '1.1.1'
501
648
 
502
649
  Facter.add("HardwareModel") do
503
650
  setcode 'uname -m'
504
- #tag("operatingsystem","SunOS")
651
+ end
652
+
653
+ Facter.add("Architecture") do
654
+ confine :operatingsystem => :debian
655
+ setcode do
656
+ model = Facter.hardwaremodel
657
+ case model
658
+ when 'x86_64': "amd64"
659
+ when /(i[3456]86|pentium)/: "i386"
660
+ else
661
+ model
662
+ end
663
+ end
505
664
  end
506
665
 
507
666
  Facter.add("CfKey") do
@@ -539,6 +698,18 @@ FACTERVERSION = '1.1.1'
539
698
  end
540
699
  end
541
700
  end
701
+ # Look for the DNS domain name command first.
702
+ Facter.add("Domain") do
703
+ setcode do
704
+ domain = Resolution.exec('dnsdomainname') or nil
705
+ # make sure it's a real domain
706
+ if domain and domain =~ /.+\..+/
707
+ domain
708
+ else
709
+ nil
710
+ end
711
+ end
712
+ end
542
713
  Facter.add("Domain") do
543
714
  setcode do
544
715
  domain = Resolution.exec('domainname') or nil
@@ -579,6 +750,7 @@ FACTERVERSION = '1.1.1'
579
750
  end
580
751
  end
581
752
  Facter.add("Hostname") do
753
+ setldapname "cn"
582
754
  setcode do
583
755
  hostname = nil
584
756
  name = Resolution.exec('hostname') or nil
@@ -597,7 +769,8 @@ FACTERVERSION = '1.1.1'
597
769
  end
598
770
  end
599
771
 
600
- Facter.add("IPHostNumber") do
772
+ Facter.add("IPAddress") do
773
+ setldapname "iphostnumber"
601
774
  setcode do
602
775
  require 'resolv'
603
776
 
@@ -617,7 +790,7 @@ FACTERVERSION = '1.1.1'
617
790
  end
618
791
  end
619
792
  end
620
- Facter.add("IPHostNumber") do
793
+ Facter.add("IPAddress") do
621
794
  setcode do
622
795
  if hostname = Facter["hostname"].value
623
796
  # we need Hostname to exist for this to work
@@ -658,16 +831,16 @@ FACTERVERSION = '1.1.1'
658
831
 
659
832
  Facter.add("UniqueId") do
660
833
  setcode 'hostid', '/bin/sh'
661
- tag("operatingsystem","Solaris")
834
+ confine :operatingsystem => :solaris
662
835
  end
663
836
 
664
837
  Facter.add("HardwareISA") do
665
838
  setcode 'uname -p', '/bin/sh'
666
- tag("operatingsystem","Solaris")
839
+ confine :operatingsystem => :solaris
667
840
  end
668
841
 
669
842
  Facter.add("MacAddress") do
670
- tag("operatingsystem","Solaris")
843
+ confine :operatingsystem => :solaris
671
844
  setcode do
672
845
  ether = nil
673
846
  output = %x{/sbin/ifconfig -a}
@@ -680,7 +853,7 @@ FACTERVERSION = '1.1.1'
680
853
  end
681
854
 
682
855
  Facter.add("MacAddress") do
683
- tag("Kernel","Darwin")
856
+ confine :kernel => :darwin
684
857
  setcode do
685
858
  ether = nil
686
859
  output = %x{/sbin/ifconfig}
@@ -695,8 +868,8 @@ FACTERVERSION = '1.1.1'
695
868
  ether
696
869
  end
697
870
  end
698
- Facter.add("IPHostnumber") do
699
- tag("Kernel","Darwin")
871
+ Facter.add("IPAddress") do
872
+ confine :kernel => :darwin
700
873
  setcode do
701
874
  ip = nil
702
875
  output = %x{/sbin/ifconfig}
@@ -715,72 +888,19 @@ FACTERVERSION = '1.1.1'
715
888
  end
716
889
  end
717
890
  Facter.add("Hostname") do
718
- tag("Kernel","Darwin")
719
- tag("KernelRelease","R7")
891
+ confine :kernel => :darwin, :kernelrelease => "R7"
720
892
  setcode do
721
- hostname = nil
722
- if FileTest.exists?("/Library/Preferences/SystemConfiguration/preferences.plist")
723
- File.open(
724
- "/Library/Preferences/SystemConfiguration/preferences.plist"
725
- ) { |file|
726
- found = 0
727
- file.each { |line|
728
- if line =~ /ComputerName/i
729
- found = 1
730
- next
731
- end
732
- if found == 1
733
- if line =~ /<string>([\w|-]+)<\/string>/
734
- hostname = $1
735
- break
736
- end
737
- end
738
- }
739
- }
740
- end
741
-
742
- if hostname != nil
743
- hostname
744
- else
745
- nil
746
- end
893
+ %x{/usr/sbin/scutil --get LocalHostName}
747
894
  end
748
895
  end
749
896
  Facter.add("IPHostnumber") do
750
- tag("Kernel","Darwin")
751
- tag("KernelRelease","R6")
897
+ confine :kernel => :darwin, :kernelrelease => "R6"
752
898
  setcode do
753
- hostname = nil
754
- if FileTest.exists?("/var/db/SystemConfiguration/preferences.xml")
755
- File.open(
756
- "/var/db/SystemConfiguration/preferences.xml"
757
- ) { |file|
758
- found = 0
759
- file.each { |line|
760
- if line =~ /ComputerName/i
761
- found = 1
762
- next
763
- end
764
- if found == 1
765
- if line =~ /<string>([\w|-]+)<\/string>/
766
- hostname = $1
767
- break
768
- end
769
- end
770
- }
771
- }
772
- end
773
-
774
- if hostname != nil
775
- hostname
776
- else
777
- nil
778
- end
899
+ %x{/usr/sbin/scutil --get LocalHostName}
779
900
  end
780
901
  end
781
902
  Facter.add("IPHostnumber") do
782
- tag("Kernel","Darwin")
783
- tag("KernelRelease","R6")
903
+ confine :kernel => :darwin, :kernelrelease => "R6"
784
904
  setcode do
785
905
  ether = nil
786
906
  output = %x{/sbin/ifconfig}
@@ -797,22 +917,45 @@ FACTERVERSION = '1.1.1'
797
917
  end
798
918
 
799
919
  Facter.add("ps") do
800
- tag("operatingsystem","FreeBSD", "NetBSD", "OpenBSD", "Darwin")
920
+ confine :operatingsystem => %w{FreeBSD NetBSD OpenBSD Darwin}
801
921
  setcode do 'ps -auxwww' end
802
922
  end
803
923
 
804
924
  Facter.add("id") do
805
- tag("operatingsystem","Linux")
925
+ confine :operatingsystem => :linux
806
926
  setcode "whoami"
807
927
  end
808
- end
809
928
 
810
- Facter.load
811
- end
929
+ locals = []
930
+
931
+ # Now see if we can find any other facts
932
+ $:.each do |dir|
933
+ fdir = File.join(dir, "facter")
934
+ if FileTest.exists?(fdir) and FileTest.directory?(fdir)
935
+ Dir.glob("#{fdir}/*.rb").each do |file|
936
+ # Load here, rather than require, because otherwise
937
+ # the facts won't get reloaded if someone calls
938
+ # "loadfacts". Really only important in testing, but,
939
+ # well, it's important in testing.
940
+ begin
941
+ load file
942
+ rescue => detail
943
+ warn "Could not load %s: %s" %
944
+ [file, detail]
945
+ end
946
+ end
947
+ end
948
+ end
949
+
950
+ # Now try to get facts from the environment
951
+ ENV.each do |name, value|
952
+ if name =~ /^facter_?(\w+)$/i
953
+ Facter.add($1) do
954
+ setcode { value }
955
+ end
956
+ end
957
+ end
958
+ end
812
959
 
813
- # try to load a local fact library, if there happens to be one
814
- begin
815
- require 'facter/local'
816
- rescue LoadError
817
- # no worries
960
+ Facter.loadfacts
818
961
  end