facter 1.7.6 → 2.0.1.rc1

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 (155) hide show
  1. checksums.yaml +7 -0
  2. data/COMMITTERS.md +194 -0
  3. data/CONTRIBUTING.md +63 -235
  4. data/Gemfile +12 -8
  5. data/README.md +1 -2
  6. data/Rakefile +1 -1
  7. data/bin/facter +0 -4
  8. data/ext/build_defaults.yaml +2 -2
  9. data/ext/project_data.yaml +18 -0
  10. data/install.rb +1 -16
  11. data/lib/facter.rb +171 -171
  12. data/lib/facter/application.rb +65 -54
  13. data/lib/facter/core/aggregate.rb +220 -0
  14. data/lib/facter/core/directed_graph.rb +46 -0
  15. data/lib/facter/core/execution.rb +100 -0
  16. data/lib/facter/core/execution/base.rb +73 -0
  17. data/lib/facter/core/execution/posix.rb +50 -0
  18. data/lib/facter/core/execution/windows.rb +57 -0
  19. data/lib/facter/core/logging.rb +169 -0
  20. data/lib/facter/core/resolvable.rb +94 -0
  21. data/lib/facter/core/suitable.rb +117 -0
  22. data/lib/facter/domain.rb +15 -9
  23. data/lib/facter/filesystems.rb +1 -1
  24. data/lib/facter/hardwaremodel.rb +1 -1
  25. data/lib/facter/hostname.rb +3 -3
  26. data/lib/facter/interfaces.rb +6 -1
  27. data/lib/facter/ipaddress.rb +2 -2
  28. data/lib/facter/kernel.rb +1 -1
  29. data/lib/facter/kernelrelease.rb +1 -1
  30. data/lib/facter/ldom.rb +1 -1
  31. data/lib/facter/lsbdistcodename.rb +1 -1
  32. data/lib/facter/lsbdistdescription.rb +1 -1
  33. data/lib/facter/lsbdistid.rb +1 -1
  34. data/lib/facter/lsbdistrelease.rb +1 -1
  35. data/lib/facter/lsbrelease.rb +1 -1
  36. data/lib/facter/macaddress.rb +1 -14
  37. data/lib/facter/macosx.rb +2 -2
  38. data/lib/facter/memory.rb +8 -19
  39. data/lib/facter/operatingsystem.rb +1 -1
  40. data/lib/facter/operatingsystemrelease.rb +34 -1
  41. data/lib/facter/physicalprocessorcount.rb +6 -6
  42. data/lib/facter/processor.rb +11 -10
  43. data/lib/facter/selinux.rb +4 -15
  44. data/lib/facter/ssh.rb +5 -2
  45. data/lib/facter/util/architecture.rb +2 -2
  46. data/lib/facter/util/collection.rb +42 -38
  47. data/lib/facter/util/config.rb +19 -9
  48. data/lib/facter/util/confine.rb +34 -4
  49. data/lib/facter/util/ec2.rb +1 -1
  50. data/lib/facter/util/fact.rb +108 -36
  51. data/lib/facter/util/file_read.rb +7 -6
  52. data/lib/facter/util/formatter.rb +38 -0
  53. data/lib/facter/util/ip.rb +3 -3
  54. data/lib/facter/util/loader.rb +62 -42
  55. data/lib/facter/util/macosx.rb +7 -8
  56. data/lib/facter/util/manufacturer.rb +3 -3
  57. data/lib/facter/util/memory.rb +13 -13
  58. data/lib/facter/util/monkey_patches.rb +0 -31
  59. data/lib/facter/util/netmask.rb +3 -3
  60. data/lib/facter/util/normalization.rb +94 -0
  61. data/lib/facter/util/nothing_loader.rb +3 -6
  62. data/lib/facter/util/parser.rb +3 -5
  63. data/lib/facter/util/plist/generator.rb +1 -1
  64. data/lib/facter/util/processor.rb +15 -15
  65. data/lib/facter/util/resolution.rb +112 -289
  66. data/lib/facter/util/solaris_zones.rb +4 -4
  67. data/lib/facter/util/uptime.rb +8 -3
  68. data/lib/facter/util/values.rb +67 -1
  69. data/lib/facter/util/virtual.rb +10 -10
  70. data/lib/facter/util/xendomains.rb +1 -1
  71. data/lib/facter/version.rb +42 -39
  72. data/lib/facter/virtual.rb +6 -7
  73. data/lib/facter/zfs_version.rb +3 -3
  74. data/lib/facter/zpool_version.rb +3 -3
  75. data/spec/fixtures/unit/netmask/darwin_10_8_5.txt +30 -0
  76. data/spec/unit/application_spec.rb +46 -1
  77. data/spec/unit/core/aggregate_spec.rb +125 -0
  78. data/spec/unit/core/directed_graph_spec.rb +79 -0
  79. data/spec/unit/core/execution/base_spec.rb +119 -0
  80. data/spec/unit/core/execution/posix_spec.rb +86 -0
  81. data/spec/unit/core/execution/windows_spec.rb +106 -0
  82. data/spec/unit/core/execution_spec.rb +37 -0
  83. data/spec/unit/core/logging_spec.rb +104 -0
  84. data/spec/unit/core/resolvable_spec.rb +81 -0
  85. data/spec/unit/core/suitable_spec.rb +96 -0
  86. data/spec/unit/domain_spec.rb +5 -5
  87. data/spec/unit/facter_spec.rb +61 -222
  88. data/spec/unit/filesystems_spec.rb +2 -2
  89. data/spec/unit/hardwareisa_spec.rb +5 -5
  90. data/spec/unit/hardwaremodel_spec.rb +1 -1
  91. data/spec/unit/hostname_spec.rb +4 -4
  92. data/spec/unit/id_spec.rb +3 -3
  93. data/spec/unit/interfaces_spec.rb +10 -0
  94. data/spec/unit/ipaddress6_spec.rb +4 -4
  95. data/spec/unit/ipaddress_spec.rb +1 -1
  96. data/spec/unit/kernel_spec.rb +2 -2
  97. data/spec/unit/kernelmajversion_spec.rb +1 -1
  98. data/spec/unit/kernelrelease_spec.rb +4 -4
  99. data/spec/unit/kernelversion_spec.rb +2 -2
  100. data/spec/unit/ldom_spec.rb +2 -2
  101. data/spec/unit/lsbdistcodename_spec.rb +2 -2
  102. data/spec/unit/lsbdistdescription_spec.rb +2 -2
  103. data/spec/unit/lsbdistid_spec.rb +2 -2
  104. data/spec/unit/lsbdistrelease_spec.rb +2 -2
  105. data/spec/unit/lsbrelease_spec.rb +2 -2
  106. data/spec/unit/manufacturer_spec.rb +1 -1
  107. data/spec/unit/memory_spec.rb +24 -31
  108. data/spec/unit/netmask_spec.rb +9 -0
  109. data/spec/unit/operatingsystem_spec.rb +1 -1
  110. data/spec/unit/operatingsystemrelease_spec.rb +62 -4
  111. data/spec/unit/physicalprocessorcount_spec.rb +10 -10
  112. data/spec/unit/processor_spec.rb +11 -11
  113. data/spec/unit/selinux_spec.rb +2 -8
  114. data/spec/unit/ssh_spec.rb +3 -2
  115. data/spec/unit/uniqueid_spec.rb +3 -3
  116. data/spec/unit/util/collection_spec.rb +37 -35
  117. data/spec/unit/util/config_spec.rb +20 -0
  118. data/spec/unit/util/confine_spec.rb +21 -0
  119. data/spec/unit/util/directory_loader_spec.rb +1 -0
  120. data/spec/unit/util/ec2_spec.rb +6 -6
  121. data/spec/unit/util/fact_spec.rb +92 -90
  122. data/spec/unit/util/ip_spec.rb +2 -2
  123. data/spec/unit/util/loader_spec.rb +127 -186
  124. data/spec/unit/util/macaddress_spec.rb +2 -2
  125. data/spec/unit/util/macosx_spec.rb +8 -8
  126. data/spec/unit/util/manufacturer_spec.rb +3 -3
  127. data/spec/unit/util/normalization_spec.rb +113 -0
  128. data/spec/unit/util/parser_spec.rb +25 -3
  129. data/spec/unit/util/processor_spec.rb +2 -2
  130. data/spec/unit/util/resolution_spec.rb +60 -631
  131. data/spec/unit/util/solaris_zones_spec.rb +5 -5
  132. data/spec/unit/util/uptime_spec.rb +1 -1
  133. data/spec/unit/util/values_spec.rb +131 -0
  134. data/spec/unit/util/virtual_spec.rb +16 -16
  135. data/spec/unit/util/xendomains_spec.rb +2 -2
  136. data/spec/unit/virtual_spec.rb +39 -39
  137. data/spec/unit/zfs_version_spec.rb +11 -11
  138. data/spec/unit/zonename_spec.rb +2 -2
  139. data/spec/unit/zones_spec.rb +1 -1
  140. data/spec/unit/zpool_version_spec.rb +11 -11
  141. metadata +466 -447
  142. data/lib/facter/util/cfpropertylist.rb +0 -6
  143. data/lib/facter/util/cfpropertylist/LICENSE +0 -19
  144. data/lib/facter/util/cfpropertylist/README +0 -44
  145. data/lib/facter/util/cfpropertylist/Rakefile +0 -44
  146. data/lib/facter/util/cfpropertylist/THANKS +0 -7
  147. data/lib/facter/util/cfpropertylist/lib/cfpropertylist.rb +0 -6
  148. data/lib/facter/util/cfpropertylist/lib/rbBinaryCFPropertyList.rb +0 -562
  149. data/lib/facter/util/cfpropertylist/lib/rbCFPlistError.rb +0 -26
  150. data/lib/facter/util/cfpropertylist/lib/rbCFPropertyList.rb +0 -407
  151. data/lib/facter/util/cfpropertylist/lib/rbCFTypes.rb +0 -244
  152. data/lib/facter/util/cfpropertylist/lib/rbLibXMLParser.rb +0 -135
  153. data/lib/facter/util/cfpropertylist/lib/rbNokogiriParser.rb +0 -140
  154. data/lib/facter/util/cfpropertylist/lib/rbREXMLParser.rb +0 -136
  155. data/spec/unit/util/monkey_patches_spec.rb +0 -42
@@ -6,8 +6,7 @@
6
6
  ##
7
7
 
8
8
  module Facter::Util::Macosx
9
- require 'thread'
10
- require 'facter/util/cfpropertylist'
9
+ require 'cfpropertylist'
11
10
  require 'facter/util/resolution'
12
11
 
13
12
  Plist_Xml_Doctype = '<!DOCTYPE plist PUBLIC "-//Apple Computer//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">'
@@ -16,7 +15,7 @@ module Facter::Util::Macosx
16
15
  # by looking at the _name key of the _items dict for each _dataType
17
16
 
18
17
  def self.profiler_xml(data_field)
19
- Facter::Util::Resolution.exec("/usr/sbin/system_profiler -xml #{data_field}")
18
+ Facter::Core::Execution.exec("/usr/sbin/system_profiler -xml #{data_field}")
20
19
  end
21
20
 
22
21
  def self.intern_xml(xml)
@@ -26,13 +25,13 @@ module Facter::Util::Macosx
26
25
  xml.gsub!( bad_xml_doctype, Plist_Xml_Doctype )
27
26
  Facter.debug("Had to fix plist with incorrect DOCTYPE declaration")
28
27
  end
29
- plist = Facter::Util::CFPropertyList::List.new
28
+ plist = CFPropertyList::List.new
30
29
  begin
31
30
  plist.load_str(xml)
32
- rescue => e
33
- fail("A plist file could not be properly read by Facter::Util::CFPropertyList: #{e.inspect}")
31
+ rescue CFFormatError => e
32
+ raise RuntimeError, "A plist file could not be properly read by CFPropertyList: #{e.message}", e.backtrace
34
33
  end
35
- Facter::Util::CFPropertyList.native_types(plist.value)
34
+ CFPropertyList.native_types(plist.value)
36
35
  end
37
36
 
38
37
  # Return an xml result, modified as we need it.
@@ -58,7 +57,7 @@ module Facter::Util::Macosx
58
57
  def self.sw_vers
59
58
  ver = Hash.new
60
59
  [ "productName", "productVersion", "buildVersion" ].each do |option|
61
- ver["macosx_#{option}"] = Facter::Util::Resolution.exec("/usr/bin/sw_vers -#{option}").strip
60
+ ver["macosx_#{option}"] = Facter::Core::Execution.exec("/usr/bin/sw_vers -#{option}").strip
62
61
  end
63
62
  productversion = ver["macosx_productVersion"]
64
63
  if not productversion.nil?
@@ -55,7 +55,7 @@ module Facter::Manufacturer
55
55
  Facter.add(facterkey) do
56
56
  confine :kernel => [:openbsd, :darwin]
57
57
  setcode do
58
- Facter::Util::Resolution.exec("sysctl -n #{sysctlkey} 2>/dev/null")
58
+ Facter::Core::Execution.exec("sysctl -n #{sysctlkey} 2>/dev/null")
59
59
  end
60
60
  end
61
61
  end
@@ -63,7 +63,7 @@ module Facter::Manufacturer
63
63
 
64
64
  def self.prtdiag_sparc_find_system_info()
65
65
  # Parses prtdiag for a SPARC architecture string, won't work with Solaris x86
66
- output = Facter::Util::Resolution.exec('/usr/sbin/prtdiag 2>/dev/null')
66
+ output = Facter::Core::Execution.exec('/usr/sbin/prtdiag 2>/dev/null')
67
67
 
68
68
  # System Configuration: Sun Microsystems sun4u Sun SPARC Enterprise M3000 Server
69
69
  if output and output =~ /^System Configuration:\s+(.+?)\s+(sun\d+\S+)\s+(.+)/
@@ -81,7 +81,7 @@ module Facter::Manufacturer
81
81
 
82
82
  Facter.add('serialnumber') do
83
83
  setcode do
84
- Facter::Util::Resolution.exec("/usr/sbin/sneep")
84
+ Facter::Core::Execution.exec("/usr/sbin/sneep")
85
85
  end
86
86
  end
87
87
  end
@@ -35,7 +35,7 @@ module Facter::Memory
35
35
  def self.vmstat_find_free_memory(args = [])
36
36
  cmd = 'vmstat'
37
37
  cmd += (' ' + args.join(' ')) unless args.empty?
38
- row = Facter::Util::Resolution.exec(cmd).split("\n")[-1]
38
+ row = Facter::Core::Execution.exec(cmd).split("\n")[-1]
39
39
  if row =~ /^\s*\d+\s*\d+\s*\d+\s*\d+\s*(\d+)/
40
40
  memfree = $1
41
41
  end
@@ -52,7 +52,7 @@ module Facter::Memory
52
52
  pagesize = 0
53
53
  memspecfree = 0
54
54
 
55
- vmstats = Facter::Util::Resolution.exec('vm_stat')
55
+ vmstats = Facter::Core::Execution.exec('vm_stat')
56
56
  vmstats.each_line do |vmline|
57
57
  case
58
58
  when vmline =~ /page\ssize\sof\s(\d+)\sbytes/
@@ -71,7 +71,7 @@ module Facter::Memory
71
71
  # it's the third value on the line starting with memory
72
72
  # svmon can be run by non root users
73
73
  def self.svmon_aix_find_free_memory()
74
- Facter::Util::Resolution.exec("/usr/bin/svmon -O unit=KB") =~ /^memory\s+\d+\s+\d+\s+(\d+)\s+/
74
+ Facter::Core::Execution.exec("/usr/bin/svmon -O unit=KB") =~ /^memory\s+\d+\s+\d+\s+(\d+)\s+/
75
75
  $1
76
76
  end
77
77
 
@@ -112,15 +112,15 @@ module Facter::Memory
112
112
  def self.mem_size_info(kernel = Facter.value(:kernel))
113
113
  case kernel
114
114
  when /OpenBSD/i
115
- Facter::Util::Resolution.exec("sysctl hw.physmem | cut -d'=' -f2")
115
+ Facter::Core::Execution.exec("sysctl hw.physmem | cut -d'=' -f2")
116
116
  when /FreeBSD/i
117
- Facter::Util::Resolution.exec("sysctl -n hw.physmem")
117
+ Facter::Core::Execution.exec("sysctl -n hw.physmem")
118
118
  when /Darwin/i
119
- Facter::Util::Resolution.exec("sysctl -n hw.memsize")
119
+ Facter::Core::Execution.exec("sysctl -n hw.memsize")
120
120
  when /Dragonfly/i
121
- Facter::Util::Resolution.exec("sysctl -n hw.physmem")
121
+ Facter::Core::Execution.exec("sysctl -n hw.physmem")
122
122
  when /AIX/i
123
- if Facter::Util::Resolution.exec("/usr/bin/svmon -O unit=KB") =~ /^memory\s+(\d+)\s+/
123
+ if Facter::Core::Execution.exec("/usr/bin/svmon -O unit=KB") =~ /^memory\s+(\d+)\s+/
124
124
  $1
125
125
  end
126
126
  end
@@ -150,15 +150,15 @@ module Facter::Memory
150
150
  def self.swap_info(kernel = Facter.value(:kernel))
151
151
  case kernel
152
152
  when /AIX/i
153
- (Facter.value(:id) == "root") ? Facter::Util::Resolution.exec('swap -l 2>/dev/null') : nil
153
+ (Facter.value(:id) == "root") ? Facter::Core::Execution.exec('swap -l 2>/dev/null') : nil
154
154
  when /OpenBSD/i
155
- Facter::Util::Resolution.exec('swapctl -s')
155
+ Facter::Core::Execution.exec('swapctl -s')
156
156
  when /FreeBSD/i
157
- Facter::Util::Resolution.exec('swapinfo -k')
157
+ Facter::Core::Execution.exec('swapinfo -k')
158
158
  when /Darwin/i
159
- Facter::Util::Resolution.exec('sysctl vm.swapusage')
159
+ Facter::Core::Execution.exec('sysctl vm.swapusage')
160
160
  when /SunOS/i
161
- Facter::Util::Resolution.exec('/usr/sbin/swap -l 2>/dev/null')
161
+ Facter::Core::Execution.exec('/usr/sbin/swap -l 2>/dev/null')
162
162
  end
163
163
  end
164
164
 
@@ -2,38 +2,7 @@
2
2
  # version 1.8.5. This allows us to use RbConfig in place of the older Config in
3
3
  # our code and still be compatible with at least Ruby 1.8.1.
4
4
  require 'rbconfig'
5
- require 'enumerator'
6
5
 
7
6
  unless defined? ::RbConfig
8
7
  ::RbConfig = ::Config
9
8
  end
10
-
11
- module Facter
12
- module Util
13
- module MonkeyPatches
14
- module Lines
15
- def lines(separator = $/)
16
- if block_given?
17
- self.each_line(separator) {|line| yield line }
18
- return self
19
- else
20
- return enum_for(:each_line, separator)
21
- end
22
- end
23
- end
24
- end
25
- end
26
- end
27
-
28
- public
29
- class String
30
- unless method_defined? :lines
31
- include Facter::Util::MonkeyPatches::Lines
32
- end
33
- end
34
-
35
- class IO
36
- unless method_defined? :lines
37
- include Facter::Util::MonkeyPatches::Lines
38
- end
39
- end
@@ -8,19 +8,19 @@ module Facter::NetMask
8
8
  when 'Linux'
9
9
  ops = {
10
10
  :ifconfig_opts => ['2>/dev/null'],
11
- :regex => %r{#{Facter.ipaddress}.*?(?:Mask:|netmask)\s*(#{ipregex})}x,
11
+ :regex => %r{#{Facter.value(:ipaddress)}.*?(?:Mask:|netmask)\s*(#{ipregex})}x,
12
12
  :munge => nil,
13
13
  }
14
14
  when 'SunOS'
15
15
  ops = {
16
16
  :ifconfig_opts => ['-a'],
17
- :regex => %r{\s+ inet \s #{Facter.ipaddress} \s netmask \s (\w{8})}x,
17
+ :regex => %r{\s+ inet \s #{Facter.value(:ipaddress)} \s netmask \s (\w{8})}x,
18
18
  :munge => Proc.new { |mask| mask.scan(/../).collect do |byte| byte.to_i(16) end.join('.') }
19
19
  }
20
20
  when 'FreeBSD','NetBSD','OpenBSD', 'Darwin', 'GNU/kFreeBSD', 'DragonFly'
21
21
  ops = {
22
22
  :ifconfig_opts => ['-a'],
23
- :regex => %r{\s+ inet \s #{Facter.ipaddress} \s netmask \s 0x(\w{8})}x,
23
+ :regex => %r{\s+ inet \s #{Facter.value(:ipaddress)} \s netmask \s 0x(\w{8})}x,
24
24
  :munge => Proc.new { |mask| mask.scan(/../).collect do |byte| byte.to_i(16) end.join('.') }
25
25
  }
26
26
  end
@@ -0,0 +1,94 @@
1
+ module Facter
2
+ module Util
3
+ module Normalization
4
+ class NormalizationError < StandardError; end
5
+
6
+ VALID_TYPES = [Integer, Float, TrueClass, FalseClass, NilClass, String, Array, Hash]
7
+
8
+ module_function
9
+
10
+ # Recursively normalize the given data structure
11
+ #
12
+ # @api public
13
+ # @raise [NormalizationError] If the data structure contained an invalid element.
14
+ # @return [void]
15
+ def normalize(value)
16
+ case value
17
+ when Integer, Float, TrueClass, FalseClass, NilClass
18
+ value
19
+ when String
20
+ normalize_string(value)
21
+ when Array
22
+ normalize_array(value)
23
+ when Hash
24
+ normalize_hash(value)
25
+ else
26
+ raise NormalizationError, "Expected #{value} to be one of #{VALID_TYPES.inspect}, but was #{value.class}"
27
+ end
28
+ end
29
+
30
+ # @!method normalize_string(value)
31
+ #
32
+ # Attempt to normalize and validate the given string.
33
+ #
34
+ # On Ruby 1.8 the string is checked by stripping out all non UTF-8
35
+ # characters and comparing the converted string to the original. If they
36
+ # do not match then the string is considered invalid.
37
+ #
38
+ # On Ruby 1.9+, the string is validate by checking that the string encoding
39
+ # is UTF-8 and that the string content matches the encoding. If the string
40
+ # is not an expected encoding then it is converted to UTF-8.
41
+ #
42
+ # @api public
43
+ # @raise [NormalizationError] If the string used an unsupported encoding or did not match its encoding
44
+ # @param value [String]
45
+ # @return [void]
46
+
47
+ if RUBY_VERSION =~ /^1\.8/
48
+ require 'iconv'
49
+
50
+ def normalize_string(value)
51
+ converted = Iconv.conv('UTF-8//IGNORE', 'UTF-8', value)
52
+ if converted != value
53
+ raise NormalizationError, "String #{value.inspect} is not valid UTF-8"
54
+ end
55
+ value
56
+ end
57
+ else
58
+ def normalize_string(value)
59
+ value = value.encode(Encoding::UTF_8)
60
+
61
+ unless value.valid_encoding?
62
+ raise NormalizationError, "String #{value.inspect} doesn't match the reported encoding #{value.encoding}"
63
+ end
64
+
65
+ value
66
+ rescue EncodingError
67
+ raise NormalizationError, "String encoding #{value.encoding} is not UTF-8 and could not be converted to UTF-8"
68
+ end
69
+ end
70
+
71
+ # Validate all elements of the array.
72
+ #
73
+ # @api public
74
+ # @raise [NormalizationError] If one of the elements failed validation
75
+ # @param value [Array]
76
+ # @return [void]
77
+ def normalize_array(value)
78
+ value.collect do |elem|
79
+ normalize(elem)
80
+ end
81
+ end
82
+
83
+ # Validate all keys and values of the hash.
84
+ #
85
+ # @api public
86
+ # @raise [NormalizationError] If one of the keys or values failed normalization
87
+ # @param value [Hash]
88
+ # @return [void]
89
+ def normalize_hash(value)
90
+ Hash[value.collect { |k, v| [ normalize(k), normalize(v) ] } ]
91
+ end
92
+ end
93
+ end
94
+ end
@@ -1,13 +1,10 @@
1
- # An external fact loader that doesn't load anything
2
-
3
- # This makes it possible to disable loading
4
- # of external facts
5
-
6
1
  module Facter
7
2
  module Util
8
3
 
4
+ # An external fact loader that doesn't load anything
5
+ # This makes it possible to disable loading
6
+ # of external facts
9
7
  class NothingLoader
10
-
11
8
  def load(collection)
12
9
  end
13
10
  end
@@ -55,9 +55,7 @@ module Facter::Util::Parser
55
55
  def results
56
56
  parse_results
57
57
  rescue Exception => detail
58
- Facter.warn("Failed to handle #{filename} as #{self.class} facts")
59
- Facter.warn("detail: #{detail.class}: #{detail.message}")
60
- Facter.debug(detail.backtrace.join("\n\t"))
58
+ Facter.log_exception(detail, "Failed to handle #{filename} as #{self.class} facts: #{detail.message}")
61
59
  nil
62
60
  end
63
61
 
@@ -119,7 +117,7 @@ module Facter::Util::Parser
119
117
 
120
118
  class ScriptParser < Base
121
119
  def parse_results
122
- KeyValuePairOutputFormat.parse Facter::Util::Resolution.exec(quote(filename))
120
+ KeyValuePairOutputFormat.parse Facter::Core::Execution.exec(quote(filename))
123
121
  end
124
122
 
125
123
  private
@@ -142,7 +140,7 @@ module Facter::Util::Parser
142
140
  # Returns a hash of facts from powershell output
143
141
  def parse_results
144
142
  shell_command = "powershell -NoProfile -NonInteractive -NoLogo -ExecutionPolicy Bypass -File \"#{filename}\""
145
- KeyValuePairOutputFormat.parse Facter::Util::Resolution.exec(shell_command)
143
+ KeyValuePairOutputFormat.parse Facter::Core::Execution.exec(shell_command)
146
144
  end
147
145
  end
148
146
 
@@ -51,7 +51,7 @@ module Plist
51
51
  # Writes the serialized object's plist to the specified filename.
52
52
  def self.save_plist(obj, filename)
53
53
  File.open(filename, 'wb') do |f|
54
- f.write(obj.to_plist)
54
+ f.write(Plist::Emit.dump(obj))
55
55
  end
56
56
  end
57
57
 
@@ -47,19 +47,19 @@ module Processor
47
47
  end
48
48
 
49
49
  ##
50
- # lsdev is intended to directly delegate to Facter::Util::Resolution.exec in an
50
+ # lsdev is intended to directly delegate to Facter::Core::Execution.exec in an
51
51
  # effort to make the processorX facts easier to test by stubbing only the
52
52
  # behaviors we need to stub to get the output of the system command.
53
53
  def self.lsdev(command="lsdev -Cc processor")
54
- Facter::Util::Resolution.exec(command)
54
+ Facter::Core::Execution.exec(command)
55
55
  end
56
56
 
57
57
  ##
58
- # lsattr is intended to directly delegate to Facter::Util::Resolution.exec in
58
+ # lsattr is intended to directly delegate to Facter::Core::Execution.exec in
59
59
  # an effort to make the processorX facts easier to test. See also the
60
60
  # {lsdev} method.
61
61
  def self.lsattr(command="lsattr -El proc0 -a type")
62
- Facter::Util::Resolution.exec(command)
62
+ Facter::Core::Execution.exec(command)
63
63
  end
64
64
 
65
65
  ##
@@ -181,34 +181,34 @@ module Processor
181
181
  private_class_method :read_unistd_h
182
182
 
183
183
  ##
184
- # machinfo delegates directly to Facter::Util::Resolution.exec, as with lsdev
184
+ # machinfo delegates directly to Facter::Core::Execution.exec, as with lsdev
185
185
  # above.
186
186
  def self.machinfo(command="/usr/contrib/bin/machinfo")
187
- Facter::Util::Resolution.exec(command)
187
+ Facter::Core::Execution.exec(command)
188
188
  end
189
189
 
190
190
  ##
191
- # model delegates directly to Facter::Util::Resolution.exec.
191
+ # model delegates directly to Facter::Core::Execution.exec.
192
192
  def self.model(command="model")
193
- Facter::Util::Resolution.exec(command)
193
+ Facter::Core::Execution.exec(command)
194
194
  end
195
195
 
196
196
  ##
197
- # ioscan delegates directly to Facter::Util::Resolution.exec.
197
+ # ioscan delegates directly to Facter::Core::Execution.exec.
198
198
  def self.ioscan(command="ioscan -fknCprocessor")
199
- Facter::Util::Resolution.exec(command)
199
+ Facter::Core::Execution.exec(command)
200
200
  end
201
201
 
202
202
  ##
203
- # getconf_cpu_version delegates directly to Facter::Util::Resolution.exec.
203
+ # getconf_cpu_version delegates directly to Facter::Core::Execution.exec.
204
204
  def self.getconf_cpu_version(command="getconf CPU_VERSION")
205
- Facter::Util::Resolution.exec(command)
205
+ Facter::Core::Execution.exec(command)
206
206
  end
207
207
 
208
208
  ##
209
- # getconf_cpu_chip_type delegates directly to Facter::Util::Resolution.exec.
209
+ # getconf_cpu_chip_type delegates directly to Facter::Core::Execution.exec.
210
210
  def self.getconf_cpu_chip_type(command="getconf CPU_CHIP_TYPE")
211
- Facter::Util::Resolution.exec(command)
211
+ Facter::Core::Execution.exec(command)
212
212
  end
213
213
 
214
214
  def self.enum_cpuinfo
@@ -272,7 +272,7 @@ module Processor
272
272
  processor_num = -1
273
273
  processor_list = []
274
274
  Thread::exclusive do
275
- kstat = Facter::Util::Resolution.exec('/usr/bin/kstat cpu_info')
275
+ kstat = Facter::Core::Execution.exec('/usr/bin/kstat cpu_info')
276
276
  if kstat
277
277
  kstat.each_line do |l|
278
278
  if l =~ /cpu_info(\d+)/
@@ -1,329 +1,152 @@
1
- # An actual fact resolution mechanism. These are largely just chunks of
2
- # code, with optional confinements restricting the mechanisms to only working on
3
- # specific systems. Note that the confinements are always ANDed, so any
4
- # confinements specified must all be true for the resolution to be
5
- # suitable.
6
1
  require 'facter/util/confine'
7
2
  require 'facter/util/config'
8
-
9
- require 'timeout'
10
-
3
+ require 'facter/util/normalization'
4
+ require 'facter/core/execution'
5
+ require 'facter/core/resolvable'
6
+ require 'facter/core/suitable'
7
+
8
+ # This represents a fact resolution. A resolution is a concrete
9
+ # implementation of a fact. A single fact can have many resolutions and
10
+ # the correct resolution will be chosen at runtime. Each time
11
+ # {Facter.add} is called, a new resolution is created and added to the
12
+ # set of resolutions for the fact named in the call. Each resolution
13
+ # has a {#has_weight weight}, which defines its priority over other
14
+ # resolutions, and a set of {#confine _confinements_}, which defines the
15
+ # conditions under which it will be chosen. All confinements must be
16
+ # satisfied for a fact to be considered _suitable_.
17
+ #
18
+ # @api public
11
19
  class Facter::Util::Resolution
12
- attr_accessor :interpreter, :code, :name, :timeout
13
- attr_writer :value, :weight
14
-
15
- INTERPRETER = Facter::Util::Config.is_windows? ? "cmd.exe" : "/bin/sh"
20
+ # @api private
21
+ attr_accessor :code
22
+ attr_writer :value
16
23
 
17
- # Returns the locations to be searched when looking for a binary. This
18
- # is currently determined by the +PATH+ environment variable plus
19
- # <tt>/sbin</tt> and <tt>/usr/sbin</tt> when run on unix
20
- def self.search_paths
21
- if Facter::Util::Config.is_windows?
22
- ENV['PATH'].split(File::PATH_SEPARATOR)
23
- else
24
- # Make sure facter is usable even for non-root users. Most commands
25
- # in /sbin (like ifconfig) can be run as non priviledged users as
26
- # long as they do not modify anything - which we do not do with facter
27
- ENV['PATH'].split(File::PATH_SEPARATOR) + [ '/sbin', '/usr/sbin' ]
28
- end
29
- end
24
+ extend Facter::Core::Execution
30
25
 
31
- # Determine the full path to a binary. If the supplied filename does not
32
- # already describe an absolute path then different locations (determined
33
- # by <tt>self.search_paths</tt>) will be searched for a match.
34
- #
35
- # Returns nil if no matching executable can be found otherwise returns
36
- # the expanded pathname.
37
- def self.which(bin)
38
- if absolute_path?(bin)
39
- return bin if File.executable?(bin)
40
- if Facter::Util::Config.is_windows? and File.extname(bin).empty?
41
- exts = ENV['PATHEXT']
42
- exts = exts ? exts.split(File::PATH_SEPARATOR) : %w[.COM .EXE .BAT .CMD]
43
- exts.each do |ext|
44
- destext = bin + ext
45
- if File.executable?(destext)
46
- Facter.warnonce("Using Facter::Util::Resolution.which with an absolute path like #{bin} but no fileextension is deprecated. Please add the correct extension (#{ext})")
47
- return destext
48
- end
49
- end
50
- end
51
- else
52
- search_paths.each do |dir|
53
- dest = File.join(dir, bin)
54
- if Facter::Util::Config.is_windows?
55
- dest.gsub!(File::SEPARATOR, File::ALT_SEPARATOR)
56
- if File.extname(dest).empty?
57
- exts = ENV['PATHEXT']
58
- exts = exts ? exts.split(File::PATH_SEPARATOR) : %w[.COM .EXE .BAT .CMD]
59
- exts.each do |ext|
60
- destext = dest + ext
61
- return destext if File.executable?(destext)
62
- end
63
- end
64
- end
65
- return dest if File.executable?(dest)
66
- end
67
- end
68
- nil
26
+ class << self
27
+ # Expose command execution methods that were extracted into
28
+ # Facter::Util::Execution from Facter::Util::Resolution in Facter 2.0.0 for
29
+ # compatibility.
30
+ #
31
+ # @deprecated
32
+ public :search_paths, :which, :absolute_path?, :expand_command, :with_env, :exec
69
33
  end
70
34
 
71
- # Determine in a platform-specific way whether a path is absolute. This
72
- # defaults to the local platform if none is specified.
73
- def self.absolute_path?(path, platform=nil)
74
- # Escape once for the string literal, and once for the regex.
75
- slash = '[\\\\/]'
76
- name = '[^\\\\/]+'
77
- regexes = {
78
- :windows => %r!^(([A-Z]:#{slash})|(#{slash}#{slash}#{name}#{slash}#{name})|(#{slash}#{slash}\?#{slash}#{name}))!i,
79
- :posix => %r!^/!,
80
- }
81
- platform ||= Facter::Util::Config.is_windows? ? :windows : :posix
35
+ include Facter::Core::Resolvable
36
+ include Facter::Core::Suitable
82
37
 
83
- !! (path =~ regexes[platform])
84
- end
38
+ # @!attribute [rw] name
39
+ # The name of this resolution. The resolution name should be unique with
40
+ # respect to the given fact.
41
+ # @return [String]
42
+ # @api public
43
+ attr_accessor :name
85
44
 
86
- # Expand the executable of a commandline to an absolute path. The executable
87
- # is the first word of the commandline. If the executable contains spaces,
88
- # it has be but in double quotes to be properly recognized.
89
- #
90
- # Returns the commandline with the expanded binary or nil if the binary
91
- # can't be found. If the path to the binary contains quotes, the whole binary
92
- # is put in quotes.
93
- def self.expand_command(command)
94
- if match = /^"(.+?)"(?:\s+(.*))?/.match(command)
95
- exe, arguments = match.captures
96
- exe = which(exe) and [ "\"#{exe}\"", arguments ].compact.join(" ")
97
- elsif match = /^'(.+?)'(?:\s+(.*))?/.match(command) and not Facter::Util::Config.is_windows?
98
- exe, arguments = match.captures
99
- exe = which(exe) and [ "'#{exe}'", arguments ].compact.join(" ")
100
- else
101
- exe, arguments = command.split(/ /,2)
102
- if exe = which(exe)
103
- # the binary was not quoted which means it contains no spaces. But the
104
- # full path to the binary may do so.
105
- exe = "\"#{exe}\"" if exe =~ /\s/ and Facter::Util::Config.is_windows?
106
- exe = "'#{exe}'" if exe =~ /\s/ and not Facter::Util::Config.is_windows?
107
- [ exe, arguments ].compact.join(" ")
108
- end
109
- end
110
- end
45
+ # @!attribute [r] fact
46
+ # @return [Facter::Util::Fact]
47
+ # @api private
48
+ attr_reader :fact
111
49
 
50
+ # Create a new resolution mechanism.
112
51
  #
113
- # Call this method with a block of code for which you would like to temporarily modify
114
- # one or more environment variables; the specified values will be set for the duration
115
- # of your block, after which the original values (if any) will be restored.
52
+ # @param name [String] The name of the resolution.
53
+ # @return [void]
116
54
  #
117
- # [values] a Hash containing the key/value pairs of any environment variables that you
118
- # would like to temporarily override
119
- def self.with_env(values)
120
- old = {}
121
- values.each do |var, value|
122
- # save the old value if it exists
123
- if old_val = ENV[var]
124
- old[var] = old_val
125
- end
126
- # set the new (temporary) value for the environment variable
127
- ENV[var] = value
128
- end
129
- # execute the caller's block, capture the return value
130
- rv = yield
131
- # use an ensure block to make absolutely sure we restore the variables
132
- ensure
133
- # restore the old values
134
- values.each do |var, value|
135
- if old.include?(var)
136
- ENV[var] = old[var]
137
- else
138
- # if there was no old value, delete the key from the current environment variables hash
139
- ENV.delete(var)
140
- end
141
- end
142
- # return the captured return value
143
- rv
55
+ # @api private
56
+ def initialize(name, fact)
57
+ @name = name
58
+ @fact = fact
59
+ @confines = []
60
+ @value = nil
61
+ @timeout = 0
62
+ @weight = nil
144
63
  end
145
64
 
146
- # Execute a program and return the output of that program.
147
- #
148
- # Returns nil if the program can't be found, or if there is a problem
149
- # executing the code.
65
+ # Evaluate the given block in the context of this resolution. If a block has
66
+ # already been evaluated emit a warning to that effect.
150
67
  #
151
- def self.exec(code, interpreter = nil)
152
- Facter.warnonce "The interpreter parameter to 'exec' is deprecated and will be removed in a future version." if interpreter
68
+ # @return [void]
69
+ def evaluate(&block)
70
+ if @last_evaluated
71
+ msg = "Already evaluated #{@name}"
72
+ msg << " at #{@last_evaluated}" if msg.is_a? String
73
+ msg << ", reevaluating anyways"
74
+ Facter.warn msg
75
+ end
153
76
 
154
- ## Set LANG to force i18n to C for the duration of this exec; this ensures that any code that parses the
155
- ## output of the command can expect it to be in a consistent / predictable format / locale
156
- with_env "LANG" => "C" do
157
-
158
- if expanded_code = expand_command(code)
159
- # if we can find the binary, we'll run the command with the expanded path to the binary
160
- code = expanded_code
161
- else
162
- # if we cannot find the binary return nil on posix. On windows we'll still try to run the
163
- # command in case it is a shell-builtin. In case it is not, windows will raise Errno::ENOENT
164
- return nil unless Facter::Util::Config.is_windows?
165
- return nil if absolute_path?(code)
166
- end
167
-
168
- out = nil
77
+ instance_eval(&block)
169
78
 
170
- begin
171
- out = %x{#{code}}.chomp
172
- Facter.warnonce "Using Facter::Util::Resolution.exec with a shell built-in is deprecated. Most built-ins can be replaced with native ruby commands. If you really have to run a built-in, pass \"cmd /c your_builtin\" as a command (command responsible for this message was \"#{code}\")" unless expanded_code
173
- rescue Errno::ENOENT => detail
174
- # command not found on Windows
175
- return nil
176
- rescue => detail
177
- Facter.warn(detail)
178
- return nil
179
- end
180
-
181
- if out == ""
182
- return nil
183
- else
184
- return out
185
- end
79
+ # Ruby 1.9+ provides the source location of procs which can provide useful
80
+ # debugging information if a resolution is being evaluated twice. Since 1.8
81
+ # doesn't support this we opportunistically provide this information.
82
+ if block.respond_to? :source_location
83
+ @last_evaluated = block.source_location.join(':')
84
+ else
85
+ @last_evaluated = true
186
86
  end
187
87
  end
188
88
 
189
- # Add a new confine to the resolution mechanism.
190
- def confine(confines)
191
- confines.each do |fact, values|
192
- @confines.push Facter::Util::Confine.new(fact, *values)
89
+ def set_options(options)
90
+ if options[:name]
91
+ @name = options.delete(:name)
193
92
  end
194
- end
195
-
196
- def has_weight(weight)
197
- @weight = weight
198
- end
199
93
 
200
- # Create a new resolution mechanism.
201
- def initialize(name)
202
- @name = name
203
- @confines = []
204
- @value = nil
205
- @timeout = 0
206
- @weight = nil
207
- end
94
+ if options.has_key?(:value)
95
+ @value = options.delete(:value)
96
+ end
208
97
 
209
- # Return the importance of this resolution.
210
- def weight
211
- if @weight
212
- @weight
213
- else
214
- @confines.length
98
+ if options.has_key?(:timeout)
99
+ @timeout = options.delete(:timeout)
215
100
  end
216
- end
217
101
 
218
- # We need this as a getter for 'timeout', because some versions
219
- # of ruby seem to already have a 'timeout' method and we can't
220
- # seem to override the instance methods, somehow.
221
- def limit
222
- @timeout
223
- end
102
+ if options.has_key?(:weight)
103
+ @weight = options.delete(:weight)
104
+ end
224
105
 
225
- # Set our code for returning a value.
226
- def setcode(string = nil, interp = nil, &block)
227
- Facter.warnonce "The interpreter parameter to 'setcode' is deprecated and will be removed in a future version." if interp
228
- if string
229
- @code = string
230
- @interpreter = interp || INTERPRETER
231
- else
232
- unless block_given?
233
- raise ArgumentError, "You must pass either code or a block"
234
- end
235
- @code = block
106
+ if not options.keys.empty?
107
+ raise ArgumentError, "Invalid resolution options #{options.keys.inspect}"
236
108
  end
237
109
  end
238
110
 
239
- ##
240
- # on_flush accepts a block and executes the block when the resolution's value
241
- # is flushed. This makes it possible to model a single, expensive system
242
- # call inside of a Ruby object and then define multiple dynamic facts which
243
- # resolve by sending messages to the model instance. If one of the dynamic
244
- # facts is flushed then it can, in turn, flush the data stored in the model
245
- # instance to keep all of the dynamic facts in sync without making multiple,
246
- # expensive, system calls.
247
- #
248
- # Please see the Solaris zones fact for an example of how this feature may be
249
- # used.
111
+ # Sets the code block or external program that will be evaluated to
112
+ # get the value of the fact.
250
113
  #
251
- # @see Facter::Util::Fact#flush
252
- # @see Facter::Util::Resolution#flush
114
+ # @return [void]
253
115
  #
254
- # @api public
255
- def on_flush(&block)
256
- @on_flush_block = block
257
- end
258
-
259
- ##
260
- # flush executes the block, if any, stored by the {on_flush} method
116
+ # @overload setcode(string)
117
+ # Sets an external program to call to get the value of the resolution
118
+ # @param [String] string the external program to run to get the
119
+ # value
261
120
  #
262
- # @see Facter::Util::Fact#flush
263
- # @see Facter::Util::Resolution#on_flush
121
+ # @overload setcode(&block)
122
+ # Sets the resolution's value by evaluating a block at runtime
123
+ # @param [Proc] block The block to determine the resolution's value.
124
+ # This block is run when the fact is evaluated. Errors raised from
125
+ # inside the block are rescued and printed to stderr.
264
126
  #
265
- # @api private
266
- def flush
267
- @on_flush_block.call if @on_flush_block
268
- end
269
-
270
- def interpreter
271
- Facter.warnonce "The 'Facter::Util::Resolution.interpreter' method is deprecated and will be removed in a future version."
272
- @interpreter
273
- end
274
-
275
- def interpreter=(interp)
276
- Facter.warnonce "The 'Facter::Util::Resolution.interpreter=' method is deprecated and will be removed in a future version."
277
- @interpreter = interp
278
- end
279
-
280
- # Is this resolution mechanism suitable on the system in question?
281
- def suitable?
282
- unless defined? @suitable
283
- @suitable = ! @confines.detect { |confine| ! confine.true? }
127
+ # @api public
128
+ def setcode(string = nil, &block)
129
+ if string
130
+ @code = Proc.new do
131
+ output = Facter::Core::Execution.exec(string)
132
+ output.empty? ? nil : output
133
+ end
134
+ elsif block_given?
135
+ @code = block
136
+ else
137
+ raise ArgumentError, "You must pass either code or a block"
284
138
  end
285
-
286
- return @suitable
287
- end
288
-
289
- def to_s
290
- return self.value()
291
139
  end
292
140
 
293
- # How we get a value for our resolution mechanism.
294
- def value
295
- return @value if @value
296
- result = nil
297
- return result if @code == nil
141
+ private
298
142
 
299
- starttime = Time.now.to_f
300
-
301
- begin
302
- Timeout.timeout(limit) do
303
- if @code.is_a?(Proc)
304
- result = @code.call()
305
- else
306
- result = Facter::Util::Resolution.exec(@code)
307
- end
308
- end
309
- rescue Timeout::Error => detail
310
- Facter.warn "Timed out seeking value for %s" % self.name
311
-
312
- # This call avoids zombies -- basically, create a thread that will
313
- # dezombify all of the child processes that we're ignoring because
314
- # of the timeout.
315
- Thread.new { Process.waitall }
316
- return nil
317
- rescue => details
318
- Facter.warn "Could not retrieve %s: %s" % [self.name, details]
319
- return nil
143
+ def resolve_value
144
+ if @value
145
+ @value
146
+ elsif @code.nil?
147
+ nil
148
+ elsif @code
149
+ @code.call()
320
150
  end
321
-
322
- finishtime = Time.now.to_f
323
- ms = (finishtime - starttime) * 1000
324
- Facter.show_time "#{self.name}: #{"%.2f" % ms}ms"
325
-
326
- return nil if result == ""
327
- return result
328
151
  end
329
152
  end