facter 1.6.18 → 1.7.0.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 (126) hide show
  1. data/Gemfile +23 -8
  2. data/bin/facter +4 -1
  3. data/ext/build_defaults.yaml +1 -1
  4. data/ext/debian/changelog.erb +4 -22
  5. data/ext/debian/control +2 -2
  6. data/ext/project_data.yaml +1 -1
  7. data/ext/redhat/facter.spec.erb +10 -9
  8. data/install.rb +27 -53
  9. data/lib/facter.rb +23 -5
  10. data/lib/facter/application.rb +21 -0
  11. data/lib/facter/blockdevices.rb +105 -0
  12. data/lib/facter/domain.rb +19 -7
  13. data/lib/facter/filesystems.rb +38 -0
  14. data/lib/facter/hardwaremodel.rb +3 -2
  15. data/lib/facter/ipaddress6.rb +1 -2
  16. data/lib/facter/kernelrelease.rb +10 -2
  17. data/lib/facter/ldom.rb +47 -0
  18. data/lib/facter/macaddress.rb +1 -1
  19. data/lib/facter/manufacturer.rb +7 -1
  20. data/lib/facter/memory.rb +58 -160
  21. data/lib/facter/operatingsystem.rb +17 -2
  22. data/lib/facter/operatingsystemmajrelease.rb +33 -0
  23. data/lib/facter/operatingsystemrelease.rb +88 -37
  24. data/lib/facter/osfamily.rb +6 -2
  25. data/lib/facter/processor.rb +2 -2
  26. data/lib/facter/ps.rb +5 -0
  27. data/lib/facter/ssh.rb +50 -12
  28. data/lib/facter/util/cfpropertylist.rb +6 -0
  29. data/lib/facter/util/cfpropertylist/LICENSE +19 -0
  30. data/lib/facter/util/cfpropertylist/README +44 -0
  31. data/lib/facter/util/cfpropertylist/Rakefile +44 -0
  32. data/lib/facter/util/cfpropertylist/THANKS +7 -0
  33. data/lib/facter/util/cfpropertylist/lib/cfpropertylist.rb +6 -0
  34. data/lib/facter/util/cfpropertylist/lib/rbBinaryCFPropertyList.rb +562 -0
  35. data/lib/facter/util/cfpropertylist/lib/rbCFPlistError.rb +26 -0
  36. data/lib/facter/util/cfpropertylist/lib/rbCFPropertyList.rb +402 -0
  37. data/lib/facter/util/cfpropertylist/lib/rbCFTypes.rb +244 -0
  38. data/lib/facter/util/cfpropertylist/lib/rbLibXMLParser.rb +135 -0
  39. data/lib/facter/util/cfpropertylist/lib/rbNokogiriParser.rb +140 -0
  40. data/lib/facter/util/cfpropertylist/lib/rbREXMLParser.rb +136 -0
  41. data/lib/facter/util/collection.rb +36 -14
  42. data/lib/facter/util/composite_loader.rb +12 -0
  43. data/lib/facter/util/config.rb +36 -0
  44. data/lib/facter/util/confine.rb +1 -6
  45. data/lib/facter/util/directory_loader.rb +83 -0
  46. data/lib/facter/util/fact.rb +49 -42
  47. data/lib/facter/util/file_read.rb +32 -0
  48. data/lib/facter/util/ip.rb +2 -9
  49. data/lib/facter/util/loader.rb +16 -2
  50. data/lib/facter/util/macosx.rb +15 -2
  51. data/lib/facter/util/memory.rb +154 -27
  52. data/lib/facter/util/nothing_loader.rb +15 -0
  53. data/lib/facter/util/parser.rb +141 -0
  54. data/lib/facter/util/processor.rb +27 -35
  55. data/lib/facter/util/resolution.rb +97 -26
  56. data/lib/facter/util/solaris_zones.rb +153 -0
  57. data/lib/facter/util/virtual.rb +32 -3
  58. data/lib/facter/version.rb +72 -2
  59. data/lib/facter/virtual.rb +56 -3
  60. data/lib/facter/zfs_version.rb +10 -0
  61. data/lib/facter/zonename.rb +6 -0
  62. data/lib/facter/zones.rb +17 -0
  63. data/lib/facter/zpool_version.rb +10 -0
  64. data/spec/fixtures/ifconfig/ifconfig_net_tools_1.60.txt +19 -0
  65. data/spec/fixtures/ifconfig/ifconfig_ubuntu_1204.txt +16 -0
  66. data/spec/fixtures/ldom/ldom_v1 +6 -0
  67. data/spec/fixtures/unit/filesystems/linux +28 -0
  68. data/spec/fixtures/unit/util/manufacturer/intel_linux_dmidecode +549 -0
  69. data/spec/fixtures/unit/virtual/sysfs_dmi_entries_raw.txt +0 -0
  70. data/spec/fixtures/unit/zfs_version/freebsd_8.2 +14 -0
  71. data/spec/fixtures/unit/zfs_version/freebsd_9.0 +13 -0
  72. data/spec/fixtures/unit/zfs_version/linux-fuse_0.6.9 +14 -0
  73. data/spec/fixtures/unit/zfs_version/solaris_10 +10 -0
  74. data/spec/fixtures/unit/zfs_version/solaris_11 +12 -0
  75. data/spec/fixtures/unit/zpool_version/freebsd_8.2 +26 -0
  76. data/spec/fixtures/unit/zpool_version/freebsd_9.0 +38 -0
  77. data/spec/fixtures/unit/zpool_version/linux-fuse_0.6.9 +35 -0
  78. data/spec/fixtures/unit/zpool_version/solaris_10 +31 -0
  79. data/spec/fixtures/unit/zpool_version/solaris_11 +43 -0
  80. data/spec/integration/facter_spec.rb +12 -0
  81. data/spec/spec_helper.rb +9 -0
  82. data/spec/unit/architecture_spec.rb +1 -1
  83. data/spec/unit/blockdevices_spec.rb +109 -0
  84. data/spec/unit/domain_spec.rb +189 -81
  85. data/spec/unit/ec2_spec.rb +15 -8
  86. data/spec/unit/filesystems_spec.rb +50 -0
  87. data/spec/unit/hardwaremodel_spec.rb +8 -1
  88. data/spec/unit/id_spec.rb +6 -5
  89. data/spec/unit/ipaddress6_spec.rb +14 -2
  90. data/spec/unit/ipaddress_spec.rb +1 -1
  91. data/spec/unit/kernel_spec.rb +24 -0
  92. data/spec/unit/kernelmajversion_spec.rb +17 -0
  93. data/spec/unit/kernelrelease_spec.rb +53 -0
  94. data/spec/unit/kernelversion_spec.rb +32 -0
  95. data/spec/unit/ldom_spec.rb +74 -0
  96. data/spec/unit/macaddress_spec.rb +3 -1
  97. data/spec/unit/manufacturer_spec.rb +115 -0
  98. data/spec/unit/memory_spec.rb +442 -75
  99. data/spec/unit/operatingsystem_spec.rb +16 -2
  100. data/spec/unit/operatingsystemmajrelease_spec.rb +16 -0
  101. data/spec/unit/operatingsystemrelease_spec.rb +110 -1
  102. data/spec/unit/processor_spec.rb +22 -7
  103. data/spec/unit/ps_spec.rb +42 -0
  104. data/spec/unit/ssh_spec.rb +76 -0
  105. data/spec/unit/util/collection_spec.rb +94 -118
  106. data/spec/unit/util/config_spec.rb +36 -5
  107. data/spec/unit/util/confine_spec.rb +31 -43
  108. data/spec/unit/util/directory_loader_spec.rb +87 -0
  109. data/spec/unit/util/fact_spec.rb +37 -25
  110. data/spec/unit/util/file_read_spec.rb +29 -0
  111. data/spec/unit/util/ip_spec.rb +4 -2
  112. data/spec/unit/util/loader_spec.rb +102 -45
  113. data/spec/unit/util/macosx_spec.rb +40 -9
  114. data/spec/unit/util/manufacturer_spec.rb +12 -1
  115. data/spec/unit/util/parser_spec.rb +135 -0
  116. data/spec/unit/util/resolution_spec.rb +136 -4
  117. data/spec/unit/util/solaris_zones_spec.rb +127 -0
  118. data/spec/unit/util/virtual_spec.rb +54 -0
  119. data/spec/unit/version_spec.rb +42 -0
  120. data/spec/unit/virtual_spec.rb +102 -27
  121. data/spec/unit/zfs_version_spec.rb +76 -0
  122. data/spec/unit/zonename_spec.rb +14 -0
  123. data/spec/unit/zones_spec.rb +55 -0
  124. data/spec/unit/zpool_version_spec.rb +76 -0
  125. metadata +113 -11
  126. data/lib/facter/arp.rb +0 -28
@@ -0,0 +1,15 @@
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
+ module Facter
7
+ module Util
8
+
9
+ class NothingLoader
10
+
11
+ def load(collection)
12
+ end
13
+ end
14
+ end
15
+ end
@@ -0,0 +1,141 @@
1
+ # This class acts as the factory and parent class for parsed
2
+ # facts such as scripts, text, json and yaml files.
3
+ #
4
+ # Parsers must subclass this class and provide their own #results method.
5
+ require 'facter'
6
+ require 'yaml'
7
+
8
+ module Facter::Util::Parser
9
+ @parsers = []
10
+
11
+ # For support mutliple extensions you can pass an array of extensions as
12
+ # +ext+.
13
+ def self.extension_matches?(filename, ext)
14
+ extension = case ext
15
+ when String
16
+ ext.downcase
17
+ when Enumerable
18
+ ext.collect {|x| x.downcase }
19
+ end
20
+ [extension].flatten.to_a.include?(file_extension(filename).downcase)
21
+ end
22
+
23
+ def self.file_extension(filename)
24
+ File.extname(filename).sub(".", '')
25
+ end
26
+
27
+ def self.register(klass, &suitable)
28
+ @parsers << [klass, suitable]
29
+ end
30
+
31
+ def self.parser_for(filename)
32
+ registration = @parsers.detect { |k| k[1].call(filename) }
33
+
34
+ if registration.nil?
35
+ NothingParser.new
36
+ else
37
+ registration[0].new(filename)
38
+ end
39
+ end
40
+
41
+ class Base
42
+ attr_reader :filename
43
+
44
+ def initialize(filename, content = nil)
45
+ @filename = filename
46
+ @content = content
47
+ end
48
+
49
+ def content
50
+ @content ||= File.read(filename)
51
+ end
52
+
53
+ # results on the base class is really meant to be just an exception handler
54
+ # wrapper.
55
+ def results
56
+ parse_results
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"))
61
+ nil
62
+ end
63
+
64
+ def parse_results
65
+ raise ArgumentError, "Subclasses must respond to parse_results"
66
+ end
67
+ end
68
+
69
+ class YamlParser < Base
70
+ def parse_results
71
+ YAML.load(content)
72
+ end
73
+ end
74
+
75
+ register(YamlParser) do |filename|
76
+ extension_matches?(filename, "yaml")
77
+ end
78
+
79
+ class TextParser < Base
80
+ def parse_results
81
+ re = /^(.+?)=(.+)$/
82
+ result = {}
83
+ content.each_line do |line|
84
+ if match_data = re.match(line.chomp)
85
+ result[match_data[1]] = match_data[2]
86
+ end
87
+ end
88
+ result
89
+ end
90
+ end
91
+
92
+ register(TextParser) do |filename|
93
+ extension_matches?(filename, "txt")
94
+ end
95
+
96
+ class JsonParser < Base
97
+ def results
98
+ if Facter.json?
99
+ JSON.load(content)
100
+ else
101
+ Facter.warnonce "Cannot parse JSON data file #{filename} without the json library."
102
+ Facter.warnonce "Suggested next step is `gem install json` to install the json library."
103
+ nil
104
+ end
105
+ end
106
+ end
107
+
108
+ register(JsonParser) do |filename|
109
+ extension_matches?(filename, "json")
110
+ end
111
+
112
+ class ScriptParser < Base
113
+ def results
114
+ output = Facter::Util::Resolution.exec(filename)
115
+
116
+ result = {}
117
+ re = /^(.+)=(.+)$/
118
+ output.each_line do |line|
119
+ if match_data = re.match(line.chomp)
120
+ result[match_data[1]] = match_data[2]
121
+ end
122
+ end
123
+ result
124
+ end
125
+ end
126
+
127
+ register(ScriptParser) do |filename|
128
+ if not Facter::Util::Config.is_windows?
129
+ File.executable?(filename) && File.file?(filename)
130
+ end
131
+ end
132
+
133
+
134
+ # A parser that is used when there is no other parser that can handle the file
135
+ # The return from results indicates to the caller the file was not parsed correctly.
136
+ class NothingParser
137
+ def results
138
+ nil
139
+ end
140
+ end
141
+ end
@@ -218,55 +218,47 @@ module Processor
218
218
  model = Facter.value(:architecture)
219
219
  case model
220
220
  when "x86_64", "amd64", "i386", "x86", /parisc/, "hppa", "ia64"
221
- Thread::exclusive do
222
- File.readlines(cpuinfo).each do |l|
223
- if l =~ /processor\s+:\s+(\d+)/
224
- processor_num = $1.to_i
225
- elsif l =~ /model name\s+:\s+(.*)\s*$/
226
- processor_list[processor_num] = $1 unless processor_num == -1
227
- processor_num = -1
228
- elsif l =~ /processor\s+(\d+):\s+(.*)/
229
- processor_num = $1.to_i
230
- processor_list[processor_num] = $2 unless processor_num == -1
231
- end
221
+ File.readlines(cpuinfo).each do |l|
222
+ if l =~ /processor\s+:\s+(\d+)/
223
+ processor_num = $1.to_i
224
+ elsif l =~ /model name\s+:\s+(.*)\s*$/
225
+ processor_list[processor_num] = $1 unless processor_num == -1
226
+ processor_num = -1
227
+ elsif l =~ /processor\s+(\d+):\s+(.*)/
228
+ processor_num = $1.to_i
229
+ processor_list[processor_num] = $2 unless processor_num == -1
232
230
  end
233
231
  end
234
232
 
235
233
  when "ppc64"
236
- Thread::exclusive do
237
- File.readlines(cpuinfo).each do |l|
238
- if l =~ /processor\s+:\s+(\d+)/
239
- processor_num = $1.to_i
240
- elsif l =~ /cpu\s+:\s+(.*)\s*$/
241
- processor_list[processor_num] = $1 unless processor_num == -1
242
- processor_num = -1
243
- end
234
+ File.readlines(cpuinfo).each do |l|
235
+ if l =~ /processor\s+:\s+(\d+)/
236
+ processor_num = $1.to_i
237
+ elsif l =~ /cpu\s+:\s+(.*)\s*$/
238
+ processor_list[processor_num] = $1 unless processor_num == -1
239
+ processor_num = -1
244
240
  end
245
241
  end
246
242
 
247
243
  when /arm/
248
- Thread::exclusive do
249
- File.readlines(cpuinfo).each do |l|
250
- if l =~ /Processor\s+:\s+(.+)/
244
+ File.readlines(cpuinfo).each do |l|
245
+ if l =~ /Processor\s+:\s+(.+)/
246
+ processor_num += 1
247
+ processor_list[processor_num] = $1.chomp
248
+ elsif l =~ /processor\s+:\s+(\d+)\s*$/
249
+ proc_num = $1.to_i
250
+ if proc_num != 0
251
251
  processor_num += 1
252
- processor_list[processor_num] = $1.chomp
253
- elsif l =~ /processor\s+:\s+(\d+)\s*$/
254
- proc_num = $1.to_i
255
- if proc_num != 0
256
- processor_num += 1
257
- processor_list[processor_num] = processor_list[processor_num-1]
258
- end
252
+ processor_list[processor_num] = processor_list[processor_num-1]
259
253
  end
260
254
  end
261
255
  end
262
256
 
263
257
  when /sparc/
264
- Thread::exclusive do
265
- File.readlines(cpuinfo).each do |l|
266
- if l =~ /cpu\s+:\s+(.*)\s*$/
267
- processor_num += 1
268
- processor_list[processor_num] = $1
269
- end
258
+ File.readlines(cpuinfo).each do |l|
259
+ if l =~ /cpu\s+:\s+(.*)\s*$/
260
+ processor_num += 1
261
+ processor_list[processor_num] = $1
270
262
  end
271
263
  end
272
264
  end
@@ -10,6 +10,7 @@ require 'timeout'
10
10
 
11
11
  class Facter::Util::Resolution
12
12
  attr_accessor :interpreter, :code, :name, :timeout
13
+ attr_writer :value, :weight
13
14
 
14
15
  INTERPRETER = Facter::Util::Config.is_windows? ? "cmd.exe" : "/bin/sh"
15
16
 
@@ -108,6 +109,39 @@ class Facter::Util::Resolution
108
109
  end
109
110
  end
110
111
 
112
+ #
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.
116
+ #
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
144
+ end
111
145
 
112
146
  # Execute a program and return the output of that program.
113
147
  #
@@ -117,33 +151,38 @@ class Facter::Util::Resolution
117
151
  def self.exec(code, interpreter = nil)
118
152
  Facter.warnonce "The interpreter parameter to 'exec' is deprecated and will be removed in a future version." if interpreter
119
153
 
120
- if expanded_code = expand_command(code)
121
- # if we can find the binary, we'll run the command with the expanded path to the binary
122
- code = expanded_code
123
- else
124
- # if we cannot find the binary return nil on posix. On windows we'll still try to run the
125
- # command in case it is a shell-builtin. In case it is not, windows will raise Errno::ENOENT
126
- return nil unless Facter::Util::Config.is_windows?
127
- return nil if absolute_path?(code)
128
- end
129
-
130
- out = nil
131
-
132
- begin
133
- out = %x{#{code}}.chomp
134
- 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' unless expanded_code
135
- rescue Errno::ENOENT => detail
136
- # command not found on Windows
137
- return nil
138
- rescue => detail
139
- $stderr.puts detail
140
- return nil
141
- end
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
142
169
 
143
- if out == ""
144
- return nil
145
- else
146
- return out
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' unless expanded_code
173
+ rescue Errno::ENOENT => detail
174
+ # command not found on Windows
175
+ return nil
176
+ rescue => detail
177
+ $stderr.puts detail
178
+ return nil
179
+ end
180
+
181
+ if out == ""
182
+ return nil
183
+ else
184
+ return out
185
+ end
147
186
  end
148
187
  end
149
188
 
@@ -197,6 +236,37 @@ class Facter::Util::Resolution
197
236
  end
198
237
  end
199
238
 
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.
250
+ #
251
+ # @see Facter::Util::Fact#flush
252
+ # @see Facter::Util::Resolution#flush
253
+ #
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
261
+ #
262
+ # @see Facter::Util::Fact#flush
263
+ # @see Facter::Util::Resolution#on_flush
264
+ #
265
+ # @api private
266
+ def flush
267
+ @on_flush_block.call if @on_flush_block
268
+ end
269
+
200
270
  def interpreter
201
271
  Facter.warnonce "The 'Facter::Util::Resolution.interpreter' method is deprecated and will be removed in a future version."
202
272
  @interpreter
@@ -222,6 +292,7 @@ class Facter::Util::Resolution
222
292
 
223
293
  # How we get a value for our resolution mechanism.
224
294
  def value
295
+ return @value if @value
225
296
  result = nil
226
297
  return result if @code == nil
227
298
 
@@ -0,0 +1,153 @@
1
+ require 'facter/util/resolution'
2
+
3
+ module Facter
4
+ module Util
5
+ ##
6
+ # Provide a set of utility methods to interact with Solaris zones. This class
7
+ # is expected to be instantiated once per set of resolutions in order to
8
+ # cache the output of the zoneadm command, which can be quite expensive.
9
+ #
10
+ # @api private
11
+ class SolarisZones
12
+ attr_reader :zone_hash
13
+ attr_reader :zoneadm_cmd
14
+ attr_reader :zoneadm_output
15
+ attr_reader :zoneadm_keys
16
+
17
+ ##
18
+ # add_facts defines all of the facts for solaris zones, for example `zones`,
19
+ # `zone_global_id`, `zone_global_status`, etc... This method defines the
20
+ # static fact named `zones`. The value of this fact is the numver of zones
21
+ # reported by the zoneadm system command. The `zones` fact also defines
22
+ # all of the dynamic facts describing the following seven attribute values
23
+ # for each zone.
24
+ #
25
+ # Zones may be added to the system while Facter is loaded. In order to
26
+ # define new dynamic facts that reflect this new information, the `virtual`
27
+ # will define new facts as a side effect of refreshing it's own value.
28
+ #
29
+ # @api private
30
+ def self.add_facts
31
+ model = new
32
+ model.refresh
33
+ model.add_dynamic_facts
34
+ Facter.add("zones") do
35
+ setcode do
36
+ model.refresh if model.flushed?
37
+ model.add_dynamic_facts
38
+ model.count
39
+ end
40
+ on_flush do
41
+ model.flush!
42
+ end
43
+ end
44
+ end
45
+
46
+ ##
47
+ # @param [Hash] opts the options to create the instance with
48
+ # @option opts [String] :zoneadm_cmd ('/usr/sbin/zoneadm list -cp') the
49
+ # system command to inspect zones
50
+ # @option opts [String] :zoneadm_output (nil) the cached output of the
51
+ # zoneadm_cmd
52
+ def initialize(opts = {})
53
+ @zoneadm_keys = [:id, :name, :status, :path, :uuid, :brand, :iptype]
54
+ @zoneadm_cmd = opts[:zoneadm_cmd] || '/usr/sbin/zoneadm list -cp'
55
+ if opts[:zoneadm_output]
56
+ @zoneadm_output = opts[:zoneadm_output]
57
+ end
58
+ end
59
+
60
+ ##
61
+ # add_dynamic_facts defines all of the dynamic facts derived from parsing
62
+ # the output of the zoneadm command. The zone facts are dynamic, so this
63
+ # method has the behavior of figuring out what dynamic zone facts need to
64
+ # be defined and how they should be resolved.
65
+ #
66
+ # @param model [SolarisZones] the model used to store data from the system
67
+ #
68
+ # @api private
69
+ def add_dynamic_facts
70
+ model = self
71
+ zone_hash.each_pair do |zone, attr_hsh|
72
+ attr_hsh.keys.each do |attr|
73
+ Facter.add("zone_#{zone}_#{attr}") do
74
+ setcode do
75
+ model.refresh if model.flushed?
76
+ # Don't resolve if the zone has since been deleted
77
+ if zone_hsh = model.zone_hash[zone]
78
+ zone_hsh[attr] # the value
79
+ end
80
+ end
81
+ on_flush do
82
+ model.flush!
83
+ end
84
+ end
85
+ end
86
+ end
87
+ end
88
+
89
+ ##
90
+ # refresh executes the zoneadm_cmd and stores the output data.
91
+ #
92
+ # @api private
93
+ #
94
+ # @return [Hash] the parsed output of the zoneadm command
95
+ def refresh
96
+ @zoneadm_output = Facter::Util::Resolution.exec(zoneadm_cmd)
97
+ parse!
98
+ end
99
+
100
+ ##
101
+ # parse! parses the string stored in {@zoneadm_output} and stores the
102
+ # resulting Hash data structure in {@zone_hash}
103
+ #
104
+ # @api private
105
+ def parse!
106
+ rows = @zoneadm_output.split("\n").collect { |line| line.split(':') }
107
+
108
+ @zone_hash = rows.inject({}) do |memo, fields|
109
+ zone = fields[1].intern
110
+ # Transform the row into a hash with keys named by the column names
111
+ memo[zone] = Hash[*@zoneadm_keys.zip(fields).flatten]
112
+ memo
113
+ end
114
+ end
115
+ private :parse!
116
+
117
+ ##
118
+ # count returns the number of running zones, including the global zone.
119
+ # This method is intended to be used from the setcode block of the `zones`
120
+ # fact.
121
+ #
122
+ # @api private
123
+ #
124
+ # @return [Fixnum, nil] the number of running zones or nil if the number
125
+ # could not be determined.
126
+ def count
127
+ if @zone_hash
128
+ @zone_hash.size
129
+ end
130
+ end
131
+
132
+ ##
133
+ # flush! purges the saved data from the zoneadm_cmd output
134
+ #
135
+ # @api private
136
+ def flush!
137
+ @zoneadm_output = nil
138
+ @zone_hash = nil
139
+ end
140
+
141
+ ##
142
+ # flushed? returns true if the instance has no parsed data accessible via
143
+ # the {zone_hash} method.
144
+ #
145
+ # @api private
146
+ #
147
+ # @return [Boolean] true if there is no parsed data, false otherwise
148
+ def flushed?
149
+ !@zone_hash
150
+ end
151
+ end
152
+ end
153
+ end