facter 4.2.3 → 4.2.7

Sign up to get free protection for your applications and to get access to all the features.
Files changed (41) hide show
  1. checksums.yaml +4 -4
  2. data/lib/facter/config.rb +1 -0
  3. data/lib/facter/custom_facts/core/execution/base.rb +16 -9
  4. data/lib/facter/custom_facts/core/execution.rb +25 -17
  5. data/lib/facter/custom_facts/util/fact.rb +1 -1
  6. data/lib/facter/custom_facts/util/loader.rb +2 -2
  7. data/lib/facter/custom_facts/util/resolution.rb +1 -1
  8. data/lib/facter/facts/linux/disks.rb +1 -1
  9. data/lib/facter/facts/linux/hypervisors/kvm.rb +3 -1
  10. data/lib/facter/facts/windows/os/windows/display_version.rb +20 -0
  11. data/lib/facter/facts/windows/timezone.rb +1 -1
  12. data/lib/facter/framework/cli/cli.rb +4 -0
  13. data/lib/facter/framework/core/cache_manager.rb +17 -1
  14. data/lib/facter/framework/core/fact_manager.rb +3 -1
  15. data/lib/facter/framework/core/options/option_store.rb +3 -1
  16. data/lib/facter/framework/core/options.rb +16 -1
  17. data/lib/facter/framework/detector/os_detector.rb +2 -3
  18. data/lib/facter/models/resolved_fact.rb +4 -0
  19. data/lib/facter/resolvers/aix/disks.rb +1 -1
  20. data/lib/facter/resolvers/aix/mountpoints.rb +14 -8
  21. data/lib/facter/resolvers/aix/partitions.rb +1 -1
  22. data/lib/facter/resolvers/aix/processors.rb +2 -1
  23. data/lib/facter/resolvers/disks.rb +86 -0
  24. data/lib/facter/resolvers/dmi.rb +1 -0
  25. data/lib/facter/resolvers/ec2.rb +1 -1
  26. data/lib/facter/resolvers/lsb_release.rb +0 -2
  27. data/lib/facter/resolvers/macosx/{processor.rb → processors.rb} +21 -21
  28. data/lib/facter/resolvers/os_release.rb +51 -20
  29. data/lib/facter/resolvers/redhat_release.rb +5 -5
  30. data/lib/facter/resolvers/suse_release.rb +1 -1
  31. data/lib/facter/resolvers/windows/ffi/winnls_ffi.rb +11 -0
  32. data/lib/facter/resolvers/windows/product_release.rb +13 -4
  33. data/lib/facter/resolvers/windows/timezone.rb +41 -0
  34. data/lib/facter/util/aix/info_extractor.rb +60 -9
  35. data/lib/facter/util/facts/windows_release_finder.rb +4 -2
  36. data/lib/facter/util/file_helper.rb +10 -0
  37. data/lib/facter/util/resolvers/http.rb +3 -0
  38. data/lib/facter/version.rb +1 -1
  39. data/lib/facter.rb +21 -2
  40. metadata +7 -4
  41. data/lib/facter/resolvers/disk.rb +0 -64
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: 353d24b45ac4083ff9a2f7de05702a93e8701487cc6020de93ed974d527e7f53
4
- data.tar.gz: c02329462e40922777fac34fab775ea1d68b884b7d4d8f8e99544bdb737f57e8
3
+ metadata.gz: 5648b99a92b05c556d728186e4df5fed506b8edf3ca2ac1e77708a2ebe9b8359
4
+ data.tar.gz: 9e58e46ab92d8d4aeffcf54a4b13b8d25ca751e08669f73c067a245cb96bdd93
5
5
  SHA512:
6
- metadata.gz: 1ccd6eb8e93836cf558c7ed12c943d8c30d14bad521f2de821b014ceb30b3d1f4fc92cc3c54690ff853b5d62dd4c3521293127f87d08fe6525d5ff2f317e3637
7
- data.tar.gz: d4a75033160dac87a2244855c7a2ff8d34fb733b527cc8ffd45279c3e2821b0860f3b1982ab1e0e4182c9c75cf45129ebe0c68738eeb605b9e435b5d460da309
6
+ metadata.gz: 467e8ef226865d0ae6d88fca05bc8c44e4dbbc10dec1e8117947e68dac09b5046878eaf3e2a45d1dd572b9b3b76f44ff14a7c62653df83854ee38172a0e576cf
7
+ data.tar.gz: 71405bf059cbba0572136a4d67db8700542f62f668a4f50f7fe99a818f82bc5f114bb8f12baa3a86be5bd59e50337e23e34f485dd5bae522c0d0a8ee8206a928
data/lib/facter/config.rb CHANGED
@@ -167,6 +167,7 @@ module Facter
167
167
  macosx_productversion_major
168
168
  macosx_productversion_minor
169
169
  macosx_productversion_patch
170
+ windows_display_version
170
171
  windows_edition_id
171
172
  windows_installation_type
172
173
  windows_product_name
@@ -7,6 +7,7 @@ module Facter
7
7
  module Execution
8
8
  class Base
9
9
  STDERR_MESSAGE = 'Command %s completed with the following stderr message: %s'
10
+ VALID_OPTIONS = %i[on_fail expand logger timeout].freeze
10
11
 
11
12
  def initialize
12
13
  @log = Log.new(self)
@@ -42,7 +43,7 @@ module Facter
42
43
  end
43
44
 
44
45
  def execute(command, options = {})
45
- on_fail, expand, logger, time_limit = extract_options(options)
46
+ on_fail, expand, logger, timeout = extract_options(options)
46
47
 
47
48
  expanded_command = if !expand && builtin_command?(command) || logger
48
49
  command
@@ -59,12 +60,12 @@ module Facter
59
60
  return on_fail
60
61
  end
61
62
 
62
- out, = execute_command(expanded_command, on_fail, logger, time_limit)
63
+ out, = execute_command(expanded_command, on_fail, logger, timeout)
63
64
  out
64
65
  end
65
66
 
66
- def execute_command(command, on_fail = nil, logger = nil, time_limit = nil)
67
- time_limit ||= 300
67
+ def execute_command(command, on_fail = nil, logger = nil, timeout = nil)
68
+ timeout ||= 300
68
69
  begin
69
70
  # Set LC_ALL and LANG to force i18n to C for the duration of this exec;
70
71
  # this ensures that any code that parses the
@@ -78,12 +79,12 @@ module Facter
78
79
  out_reader = Thread.new { stdout.read }
79
80
  err_reader = Thread.new { stderr.read }
80
81
  begin
81
- Timeout.timeout(time_limit) do
82
+ Timeout.timeout(timeout) do
82
83
  stdout_messages << out_reader.value
83
84
  stderr_messages << err_reader.value
84
85
  end
85
86
  rescue Timeout::Error
86
- message = "Timeout encounter after #{time_limit}s, killing process with pid: #{pid}"
87
+ message = "Timeout encounter after #{timeout}s, killing process with pid: #{pid}"
87
88
  Process.kill('KILL', pid)
88
89
  on_fail == :raise ? (raise StandardError, message) : @log.debug(message)
89
90
  ensure
@@ -114,10 +115,16 @@ module Facter
114
115
  on_fail = options.fetch(:on_fail, :raise)
115
116
  expand = options.fetch(:expand, true)
116
117
  logger = options[:logger]
117
- time_limit = options[:limit].to_i
118
- time_limit = time_limit.positive? ? time_limit : nil
118
+ timeout = (options[:timeout] || options[:time_limit] || options[:limit]).to_i
119
+ timeout = timeout.positive? ? timeout : nil
119
120
 
120
- [on_fail, expand, logger, time_limit]
121
+ extra_keys = options.keys - VALID_OPTIONS
122
+ unless extra_keys.empty?
123
+ @log.warn("Unexpected key passed to Facter::Core::Execution.execute option: #{extra_keys.join(',')}" \
124
+ " - valid keys: #{VALID_OPTIONS.join(',')}")
125
+ end
126
+
127
+ [on_fail, expand, logger, timeout]
121
128
  end
122
129
 
123
130
  def log_stderr(msg, command, logger)
@@ -15,9 +15,9 @@ module Facter
15
15
 
16
16
  module_function
17
17
 
18
- # Returns the locations to be searched when looking for a binary. This
19
- # is currently determined by the +PATH+ environment variable plus
20
- # `/sbin` and `/usr/sbin` when run on unix
18
+ # Returns the locations to be searched when looking for a binary. This is
19
+ # currently determined by the +PATH+ environment variable plus `/sbin`
20
+ # and `/usr/sbin` when run on unix
21
21
  #
22
22
  # @return [Array<String>] The paths to be searched for binaries
23
23
  #
@@ -27,8 +27,9 @@ module Facter
27
27
  end
28
28
 
29
29
  # Determines the full path to a binary. If the supplied filename does not
30
- # already describe an absolute path then different locations (determined
31
- # by {search_paths}) will be searched for a match.
30
+ # already describe an absolute path then different locations (determined
31
+ # by {search_paths}) will be searched for a match.
32
+ #
32
33
  # @param bin [String] The executable to locate
33
34
  #
34
35
  # @return [String/nil] The full path to the executable or nil if not
@@ -40,7 +41,8 @@ module Facter
40
41
  end
41
42
 
42
43
  # Determine in a platform-specific way whether a path is absolute. This
43
- # defaults to the local platform if none is specified.
44
+ # defaults to the local platform if none is specified.
45
+ #
44
46
  # @param path [String] The path to check
45
47
 
46
48
  # @param platform [:posix/:windows/nil] The platform logic to use
@@ -58,8 +60,9 @@ module Facter
58
60
  end
59
61
 
60
62
  # Given a command line, this returns the command line with the
61
- # executable written as an absolute path. If the executable contains
62
- # spaces, it has to be put in double quotes to be properly recognized.
63
+ # executable written as an absolute path. If the executable contains
64
+ # spaces, it has to be put in double quotes to be properly recognized.
65
+ #
63
66
  # @param command [String] the command line
64
67
  #
65
68
  # @return [String/nil] The command line with the executable's path
@@ -71,8 +74,9 @@ module Facter
71
74
  end
72
75
 
73
76
  # Overrides environment variables within a block of code. The
74
- # specified values will be set for the duration of the block, after
75
- # which the original values (if any) will be restored.
77
+ # specified values will be set for the duration of the block, after
78
+ # which the original values (if any) will be restored.
79
+ #
76
80
  # @param values [Hash<String=>String>] A hash of the environment
77
81
  # variables to override
78
82
  #
@@ -84,6 +88,7 @@ module Facter
84
88
  end
85
89
 
86
90
  # Try to execute a command and return the output.
91
+ #
87
92
  # @param command [String] Command to run
88
93
  #
89
94
  # @return [String/nil] Output of the program, or nil if the command does
@@ -96,16 +101,18 @@ module Facter
96
101
  end
97
102
 
98
103
  # Execute a command and return the output of that program.
104
+ #
99
105
  # @param command [String] Command to run
100
106
  #
101
107
  # @param options [Hash] Hash with options for the command
102
108
  #
103
- # Options accepted values :on_fail How to behave when the command could
109
+ # @option options [Object] :on_fail How to behave when the command could
104
110
  # not be run. Specifying :raise will raise an error, anything else will
105
111
  # return that object on failure. Default is :raise.
106
- # :logger Optional logger used to log the command's stderr.
107
- # :time_limit Optional time out for the specified command. If no time_limit is passed,
108
- # a default of 300 seconds is used.
112
+ # @option options [Object] :logger Optional logger used to log the
113
+ # command's stderr.
114
+ # @option options :timeout Optional time out for the specified
115
+ # command. If no timeout is passed, a default of 300 seconds is used.
109
116
  #
110
117
  # @raise [Facter::Core::Execution::ExecutionFailure] If the command does
111
118
  # not exist or could not be executed and :on_fail is set to :raise
@@ -119,13 +126,14 @@ module Facter
119
126
  end
120
127
 
121
128
  # Execute a command and return the stdout and stderr of that program.
129
+ #
122
130
  # @param command [String] Command to run
123
131
  #
124
132
  # @param on_fail[Object] How to behave when the command could
125
133
  # not be run. Specifying :raise will raise an error, anything else will
126
134
  # return that object on failure. Default is :raise.
127
135
  # @param logger Optional logger used to log the command's stderr.
128
- # @param time_limit Optional time out for the specified command. If no time_limit is passed,
136
+ # @param timeout Optional time out for the specified command. If no timeout is passed,
129
137
  # a default of 300 seconds is used.
130
138
  #
131
139
  # @raise [Facter::Core::Execution::ExecutionFailure] If the command does
@@ -135,8 +143,8 @@ module Facter
135
143
  # :on_fail if command execution failed and :on_fail was specified.
136
144
  #
137
145
  # @api private
138
- def execute_command(command, on_fail = nil, logger = nil, time_limit = nil)
139
- @@impl.execute_command(command, on_fail, logger, time_limit)
146
+ def execute_command(command, on_fail = nil, logger = nil, timeout = nil)
147
+ @@impl.execute_command(command, on_fail, logger, timeout)
140
148
  end
141
149
 
142
150
  class ExecutionFailure < StandardError; end
@@ -117,7 +117,7 @@ module Facter
117
117
  #
118
118
  # @api public
119
119
  def value
120
- return @value if @value
120
+ return @value unless @value.nil?
121
121
 
122
122
  if @resolves.empty?
123
123
  log.debug format('No resolves for %<name>s', name: @name)
@@ -141,13 +141,13 @@ module LegacyFacter
141
141
  # Skip anything that doesn't match our regex.
142
142
  next unless name =~ /^facter_?(\w+)$/i
143
143
 
144
- env_name = Regexp.last_match(1)
144
+ env_name = Regexp.last_match(1).downcase
145
145
 
146
146
  # If a fact name was specified, skip anything that doesn't
147
147
  # match it.
148
148
  next if fact && (env_name != fact)
149
149
 
150
- LegacyFacter.add(Regexp.last_match(1), fact_type: :external, is_env: true) do
150
+ LegacyFacter.add(env_name, fact_type: :external, is_env: true) do
151
151
  has_weight 1_000_000
152
152
  setcode { value }
153
153
  end
@@ -188,7 +188,7 @@ module Facter
188
188
  end
189
189
 
190
190
  def resolve_value
191
- if @value
191
+ if !@value.nil?
192
192
  @value
193
193
  elsif @code.nil?
194
194
  nil
@@ -8,7 +8,7 @@ module Facts
8
8
 
9
9
  def call_the_resolver
10
10
  facts = []
11
- disks = Facter::Resolvers::Linux::Disk.resolve(:disks)
11
+ disks = Facter::Resolvers::Linux::Disks.resolve(:disks)
12
12
 
13
13
  return Facter::ResolvedFact.new(FACT_NAME, nil) if disks.nil? || disks.empty?
14
14
 
@@ -35,13 +35,15 @@ module Facts
35
35
  end
36
36
 
37
37
  def kvm?
38
+ product_name = discover_hypervisor
38
39
  bios_vendor = Facter::Resolvers::Linux::DmiBios.resolve(:bios_vendor)
39
40
  @log.debug("Detected bios vendor: #{bios_vendor}")
40
41
 
41
42
  Facter::Resolvers::VirtWhat.resolve(:vm) == 'kvm' ||
42
43
  Facter::Resolvers::Lspci.resolve(:vm) == 'kvm' ||
43
44
  bios_vendor&.include?('Amazon EC2') ||
44
- bios_vendor&.include?('Google')
45
+ bios_vendor&.include?('Google') ||
46
+ product_name&.include?('OpenStack')
45
47
  end
46
48
 
47
49
  def discover_provider
@@ -0,0 +1,20 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Facts
4
+ module Windows
5
+ module Os
6
+ module Windows
7
+ class DisplayVersion
8
+ FACT_NAME = 'os.windows.display_version'
9
+ ALIASES = 'windows_display_version'
10
+
11
+ def call_the_resolver
12
+ fact_value = Facter::Resolvers::ProductRelease.resolve(:display_version)
13
+
14
+ [Facter::ResolvedFact.new(FACT_NAME, fact_value), Facter::ResolvedFact.new(ALIASES, fact_value, :legacy)]
15
+ end
16
+ end
17
+ end
18
+ end
19
+ end
20
+ end
@@ -6,7 +6,7 @@ module Facts
6
6
  FACT_NAME = 'timezone'
7
7
 
8
8
  def call_the_resolver
9
- fact_value = Facter::Resolvers::Timezone.resolve(:timezone)
9
+ fact_value = Facter::Resolvers::Windows::Timezone.resolve(:timezone)
10
10
 
11
11
  Facter::ResolvedFact.new(FACT_NAME, fact_value)
12
12
  end
@@ -97,6 +97,10 @@ module Facter
97
97
  type: :boolean,
98
98
  desc: 'Resolve facts sequentially'
99
99
 
100
+ class_option :http_debug,
101
+ type: :boolean,
102
+ desc: 'Whether to write HTTP request and responses to stderr. This should never be used in production.'
103
+
100
104
  class_option :puppet,
101
105
  type: :boolean,
102
106
  aliases: '-p',
@@ -167,7 +167,8 @@ cache_format_version is incorrect!")
167
167
  next unless check_ttls?(group_name, @fact_groups.get_group_ttls(group_name))
168
168
 
169
169
  cache_file_name = File.join(@cache_dir, group_name)
170
- next if File.readable?(cache_file_name)
170
+
171
+ next if facts_already_cached?(cache_file_name, data)
171
172
 
172
173
  @log.debug("caching values for #{group_name} facts")
173
174
 
@@ -176,6 +177,21 @@ cache_format_version is incorrect!")
176
177
  end
177
178
  end
178
179
 
180
+ def facts_already_cached?(cache_file_name, data)
181
+ if File.readable?(cache_file_name)
182
+ file = Facter::Util::FileHelper.safe_read(cache_file_name)
183
+ begin
184
+ cached_data = JSON.parse(file) unless file.nil?
185
+ return true if (data.keys - cached_data.keys).empty?
186
+ rescue JSON::ParserError => e
187
+ @log.debug("Failed to read cache file #{cache_file_name}. Detail: #{e.message}")
188
+ rescue NoMethodError => e
189
+ @log.debug("No keys found in #{cache_file_name}. Detail: #{e.message}")
190
+ end
191
+ end
192
+ false
193
+ end
194
+
179
195
  def read_group_json(group_name)
180
196
  return @groups[group_name] if @groups.key?(group_name)
181
197
 
@@ -51,7 +51,9 @@ module Facter
51
51
  core_and_external_facts = core_or_external_fact(user_query) || []
52
52
  resolved_facts = core_and_external_facts + custom_facts
53
53
 
54
- resolved_facts = all_custom_facts(user_query) if resolved_facts.empty?
54
+ if resolved_facts.empty? || resolved_facts.none? { |rf| rf.resolves?(user_query) }
55
+ resolved_facts.concat(all_custom_facts(user_query))
56
+ end
55
57
 
56
58
  @cache_manager.cache_facts(resolved_facts)
57
59
 
@@ -36,6 +36,7 @@ module Facter
36
36
  @hocon = false
37
37
  @allow_external_loggers = true
38
38
  @force_dot_resolution = false
39
+ @http_debug = false
39
40
 
40
41
  class << self
41
42
  attr_reader :debug, :verbose, :log_level, :show_legacy,
@@ -44,7 +45,7 @@ module Facter
44
45
  attr_accessor :config, :strict, :json,
45
46
  :cache, :yaml, :puppet, :ttls, :block, :cli, :config_file_custom_dir,
46
47
  :config_file_external_dir, :default_external_dir, :fact_groups, :force_dot_resolution,
47
- :block_list, :color, :trace, :sequential, :timing, :hocon, :allow_external_loggers
48
+ :block_list, :color, :trace, :sequential, :timing, :hocon, :allow_external_loggers, :http_debug
48
49
 
49
50
  attr_writer :external_dir
50
51
 
@@ -179,6 +180,7 @@ module Facter
179
180
  @ttls = []
180
181
  @block = true
181
182
  @cli = nil
183
+ @http_debug = false
182
184
  reset_config
183
185
  end
184
186
 
@@ -55,10 +55,25 @@ module Facter
55
55
 
56
56
  def store(options)
57
57
  options.each do |key, value|
58
- value = '' if key == 'log_level' && value == 'log_level'
58
+ value = munge_option(key, value)
59
59
  OptionStore.set(key, value)
60
60
  end
61
61
  end
62
+
63
+ private
64
+
65
+ def munge_option(key, value)
66
+ return value unless key.to_sym == :log_level
67
+
68
+ case value.to_sym
69
+ when :log_level
70
+ ''
71
+ when :none
72
+ 'unknown'
73
+ else
74
+ value
75
+ end
76
+ end
62
77
  end
63
78
  end
64
79
  end
@@ -5,7 +5,7 @@ require 'rbconfig'
5
5
  class OsDetector
6
6
  include Singleton
7
7
 
8
- attr_reader :identifier, :version, :hierarchy
8
+ attr_reader :identifier, :hierarchy
9
9
 
10
10
  def initialize(*_args)
11
11
  @log = Facter::Log.new(self)
@@ -75,8 +75,7 @@ class OsDetector
75
75
  [Facter::Resolvers::OsRelease,
76
76
  Facter::Resolvers::RedHatRelease,
77
77
  Facter::Resolvers::SuseRelease].each do |resolver|
78
- @identifier = resolver.resolve(:identifier)
79
- @version = resolver.resolve(:version)
78
+ @identifier = resolver.resolve(:id)
80
79
  break if @identifier
81
80
  end
82
81
 
@@ -23,5 +23,9 @@ module Facter
23
23
  def to_s
24
24
  @value.to_s
25
25
  end
26
+
27
+ def resolves?(user_query)
28
+ @name == user_query
29
+ end
26
30
  end
27
31
  end
@@ -34,7 +34,7 @@ module Facter
34
34
 
35
35
  return if stdout.empty?
36
36
 
37
- info_size = Facter::Util::Aix::InfoExtractor.extract(stdout, /PP SIZE:|TOTAL PPs:|FREE PPs:|PV STATE:/)
37
+ info_size = Facter::Util::Aix::InfoExtractor.extract(stdout, :lspv)
38
38
 
39
39
  return unless info_size['PV STATE']
40
40
 
@@ -18,23 +18,29 @@ module Facter
18
18
  def read_mount(fact_name)
19
19
  @fact_list[:mountpoints] = {}
20
20
  output = Facter::Core::Execution.execute('mount', logger: log)
21
- output.split("\n").map do |line|
22
- next if line =~ /node|---|procfs|ahafs/
21
+ output.split("\n").drop(2).map do |line|
22
+ next if line =~ /procfs|ahafs/
23
23
 
24
- elem = line.split("\s")
25
-
26
- @fact_list[:mountpoints][elem[1]] = { device: elem[0], filesystem: elem[2],
27
- options: elem.last.split(',') }
24
+ add_mount_points_fact(line)
28
25
  end
29
26
 
30
27
  retrieve_sizes_for_mounts
31
28
  @fact_list[fact_name]
32
29
  end
33
30
 
31
+ def add_mount_points_fact(line)
32
+ elem = line.split("\s")
33
+
34
+ elem.shift unless line[0] == ' '
35
+
36
+ @fact_list[:mountpoints][elem[1]] = { device: elem[0], filesystem: elem[2],
37
+ options: elem.last.include?(':') ? [] : elem.last.split(',') }
38
+ end
39
+
34
40
  def retrieve_sizes_for_mounts
35
41
  output = Facter::Core::Execution.execute('df -P', logger: log)
36
- output.split("\n").map do |line|
37
- next if line =~ /Filesystem|-\s+-\s+-/
42
+ output.split("\n").drop(1).map do |line|
43
+ next if line =~ /-\s+-\s+-/
38
44
 
39
45
  mount_info = line.split("\s")
40
46
  mount_info[3] = translate_to_bytes(mount_info[3])
@@ -40,7 +40,7 @@ module Facter
40
40
 
41
41
  return if stdout.empty?
42
42
 
43
- info_hash = Facter::Util::Aix::InfoExtractor.extract(stdout, /PPs:|PP SIZE|TYPE:|LABEL:|MOUNT/)
43
+ info_hash = Facter::Util::Aix::InfoExtractor.extract(stdout, :lslv)
44
44
  size_bytes = compute_size(info_hash)
45
45
 
46
46
  part_info = {
@@ -42,8 +42,9 @@ module Facter
42
42
  return unless result
43
43
 
44
44
  names = retrieve_from_array(result.scan(/name\s=\s.*/), 1)
45
+ status = retrieve_from_array(result.scan(/\s+status\s=\s.*/), 1)
45
46
 
46
- names.each { |elem| query_cuat(elem) }
47
+ names.each_with_index { |elem, idx| query_cuat(elem) if status[idx] == '1' }
47
48
  end
48
49
 
49
50
  def query_cuat(name)
@@ -0,0 +1,86 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Facter
4
+ module Resolvers
5
+ module Linux
6
+ class Disks < BaseResolver
7
+ @log = Facter::Log.new(self)
8
+
9
+ init_resolver
10
+
11
+ DIR = '/sys/block'
12
+ FILE_PATHS = { model: 'device/model',
13
+ size: 'size',
14
+ vendor: 'device/vendor',
15
+ type: 'queue/rotational',
16
+ serial: 'false',
17
+ wwn: 'false' }.freeze
18
+
19
+ class << self
20
+ private
21
+
22
+ def post_resolve(fact_name, _options)
23
+ @fact_list.fetch(fact_name) do
24
+ return unless @fact_list.empty?
25
+
26
+ build_disks_hash
27
+
28
+ read_facts
29
+
30
+ @fact_list[:disks] = nil if @fact_list[:disks].empty?
31
+ @fact_list[fact_name]
32
+ end
33
+ end
34
+
35
+ def lsblk(option, disk)
36
+ result = Facter::Core::Execution.execute(
37
+ "/usr/bin/lsblk -dn -o #{option} /dev/#{disk}", on_fail: '', timeout: 1
38
+ ).strip
39
+ result.empty? ? nil : result
40
+ end
41
+
42
+ def read_facts
43
+ FILE_PATHS.each do |key, file|
44
+ @fact_list[:disks].each do |disk, value|
45
+ file_path = File.join(DIR, disk, file)
46
+
47
+ result = if file == 'false'
48
+ lsblk(key, disk)
49
+ else
50
+ Facter::Util::FileHelper.safe_read(file_path, nil)&.strip
51
+ end
52
+
53
+ next unless result
54
+
55
+ value[key] = case key
56
+ when :size
57
+ # Linux always considers sectors to be 512 bytes long
58
+ # independently of the devices real block size.
59
+ construct_size(value, result)
60
+ when :type
61
+ result == '0' ? 'ssd' : 'hdd'
62
+ else
63
+ result
64
+ end
65
+ end
66
+ end
67
+ end
68
+
69
+ def build_disks_hash
70
+ valid_disks = Facter::Util::FileHelper.dir_children(DIR)
71
+ .select { |disk| File.readable?(File.join(DIR, disk, 'device')) }
72
+
73
+ @fact_list[:disks] = {}
74
+ valid_disks.each { |disk| @fact_list[:disks][disk] = {} }
75
+ end
76
+
77
+ def construct_size(facts, value)
78
+ value = value.to_i * 512
79
+ facts[:size_bytes] = value
80
+ facts[:size] = Facter::Util::Facts::UnitConverter.bytes_to_human_readable(value)
81
+ end
82
+ end
83
+ end
84
+ end
85
+ end
86
+ end
@@ -36,6 +36,7 @@ module Facter
36
36
  return unless File.directory?('/sys/class/dmi')
37
37
 
38
38
  file_content = Facter::Util::FileHelper.safe_read("/sys/class/dmi/id/#{fact_name}", nil)
39
+ .encode('UTF-8', invalid: :replace)
39
40
  if files.include?(fact_name.to_s) && file_content
40
41
  file_content = file_content.strip
41
42
  @fact_list[fact_name] = file_content unless file_content.empty?
@@ -20,7 +20,7 @@ module Facter
20
20
  def read_facts(fact_name)
21
21
  @fact_list[:metadata] = {}
22
22
  query_for_metadata(EC2_METADATA_ROOT_URL, @fact_list[:metadata])
23
- @fact_list[:userdata] = get_data_from(EC2_USERDATA_ROOT_URL).strip
23
+ @fact_list[:userdata] = get_data_from(EC2_USERDATA_ROOT_URL).strip.force_encoding('UTF-8')
24
24
  @fact_list[fact_name]
25
25
  end
26
26
 
@@ -38,8 +38,6 @@ module Facter
38
38
 
39
39
  result = Hash[*release_info.flatten]
40
40
  result.each { |k, v| @fact_list[k.downcase.gsub(/\s/, '_').to_sym] = v }
41
-
42
- @fact_list[:identifier] = @fact_list[:distributor_id]
43
41
  end
44
42
  end
45
43
  end
@@ -29,42 +29,42 @@ module Facter
29
29
 
30
30
  def read_processor_data(fact_name)
31
31
  output = Facter::Core::Execution.execute("sysctl #{ITEMS.values.join(' ')}", logger: log)
32
- build_fact_list(output.split("\n"))
32
+ processors_hash = Hash[*output.split("\n").collect { |v| [v.split(': ')[0], v.split(': ')[1]] }.flatten]
33
+ build_fact_list(processors_hash)
33
34
  @fact_list[fact_name]
34
35
  end
35
36
 
36
- def build_fact_list(processors_data)
37
- build_logical_count(processors_data[0])
38
- build_physical_count(processors_data[1])
39
- build_models(processors_data[2])
40
- build_speed(processors_data[3])
41
- build_cores_per_socket(processors_data[4])
42
- build_threads_per_core(processors_data[5], processors_data[4])
37
+ def build_fact_list(hash)
38
+ build_logical_count(hash)
39
+ build_physical_count(hash)
40
+ build_models(hash)
41
+ build_speed(hash)
42
+ build_cores_per_socket(hash)
43
+ build_threads_per_core(hash)
43
44
  end
44
45
 
45
- def build_logical_count(count)
46
- @fact_list[:logicalcount] = count.split(': ')[1].to_i
46
+ def build_logical_count(hash)
47
+ @fact_list[:logicalcount] = hash[ITEMS[:logical_count]].to_i
47
48
  end
48
49
 
49
- def build_physical_count(count)
50
- @fact_list[:physicalcount] = count.split(': ')[1].to_i
50
+ def build_physical_count(hash)
51
+ @fact_list[:physicalcount] = hash[ITEMS[:physical_count]].to_i
51
52
  end
52
53
 
53
- def build_models(model)
54
- brand = model.split(': ').fetch(1)
55
- @fact_list[:models] = Array.new(@fact_list[:logicalcount].to_i, brand)
54
+ def build_models(hash)
55
+ @fact_list[:models] = Array.new(@fact_list[:logicalcount].to_i, hash[ITEMS[:brand]])
56
56
  end
57
57
 
58
- def build_speed(value)
59
- @fact_list[:speed] = value.split(': ')[1].to_i
58
+ def build_speed(hash)
59
+ @fact_list[:speed] = hash[ITEMS[:speed]].to_i
60
60
  end
61
61
 
62
- def build_cores_per_socket(count)
63
- @fact_list[:cores_per_socket] = count.split(': ')[1].to_i
62
+ def build_cores_per_socket(hash)
63
+ @fact_list[:cores_per_socket] = hash[ITEMS[:cores_per_socket]].to_i
64
64
  end
65
65
 
66
- def build_threads_per_core(number_of_threads, number_of_cores)
67
- @fact_list[:threads_per_core] = number_of_threads.split(': ')[1].to_i / number_of_cores.split(': ')[1].to_i
66
+ def build_threads_per_core(hash)
67
+ @fact_list[:threads_per_core] = hash[ITEMS[:threads_per_core]].to_i / hash[ITEMS[:cores_per_socket]].to_i
68
68
  end
69
69
  end
70
70
  end
@@ -20,53 +20,84 @@ module Facter
20
20
  private
21
21
 
22
22
  def post_resolve(fact_name, _options)
23
- @fact_list.fetch(fact_name) { read_os_release_file(fact_name) }
23
+ @fact_list.fetch(fact_name) do
24
+ # If we get here multiple times per run it's probably because
25
+ # someone's asking for a os-release value not present in the file
26
+ # (e.g. VERSION is not a thing on rolling distributions, so this
27
+ # code will always run if the resolver is being asked for :version,
28
+ # because it'll never get cached).
29
+ #
30
+ # Just return early to avoid reparsing the file.
31
+ return unless @fact_list.empty?
32
+
33
+ pairs = read_and_parse_os_release_file
34
+ return unless pairs
35
+
36
+ fill_fact_list(pairs)
37
+
38
+ process_name
39
+ process_version_id
40
+ process_id
41
+
42
+ @fact_list[fact_name]
43
+ end
24
44
  end
25
45
 
26
- def read_os_release_file(fact_name)
27
- output = Facter::Util::FileHelper.safe_readlines('/etc/os-release')
28
- return @fact_list[:name] = nil if output.empty?
46
+ def read_and_parse_os_release_file
47
+ content = Facter::Util::FileHelper.safe_readlines('/etc/os-release')
48
+ return nil if content.empty?
29
49
 
30
50
  pairs = []
31
-
32
- output.each do |line|
51
+ content.each do |line|
33
52
  pairs << line.strip.delete('"').split('=', 2)
34
53
  end
35
54
 
36
- fill_fact_list(pairs)
37
- process_name
38
- pad_version_id
39
- normalize_opensuse_identifier
40
-
41
- @fact_list[fact_name]
55
+ pairs
42
56
  end
43
57
 
44
58
  def fill_fact_list(pairs)
45
59
  result = Hash[*pairs.flatten]
46
60
  result.each { |k, v| @fact_list[k.downcase.to_sym] = v }
47
-
48
- @fact_list[:identifier] = @fact_list[:id]
49
61
  end
50
62
 
51
- def pad_version_id
63
+ def process_version_id
64
+ return unless @fact_list[:version_id]
65
+
52
66
  @fact_list[:version_id] = "#{@fact_list[:version_id]}.0" unless @fact_list[:version_id] =~ /\./
53
67
  end
54
68
 
69
+ def process_id
70
+ return unless @fact_list[:id]
71
+
72
+ @fact_list[:id] = 'opensuse' if @fact_list[:id] =~ /opensuse/i
73
+ end
74
+
55
75
  def process_name
56
- os_name = @fact_list[:name]
57
- return unless os_name
76
+ return unless @fact_list[:name]
77
+
78
+ join_os_name
79
+ capitalize_os_name
80
+ append_linux_to_os_name
81
+ end
58
82
 
83
+ def join_os_name
84
+ os_name = @fact_list[:name]
59
85
  @fact_list[:name] = if os_name.downcase.start_with?('red', 'oracle', 'arch', 'manjaro')
60
86
  os_name = os_name.split(' ')[0..1].join
61
- os_name = os_name.capitalize if os_name.downcase.start_with?('arch', 'manjaro')
62
87
  os_name
63
88
  else
64
89
  os_name.split(' ')[0].strip
65
90
  end
66
91
  end
67
92
 
68
- def normalize_opensuse_identifier
69
- @fact_list[:identifier] = 'opensuse' if @fact_list[:identifier] =~ /opensuse/i
93
+ def capitalize_os_name
94
+ os_name = @fact_list[:name]
95
+ @fact_list[:name] = os_name.capitalize if os_name.downcase.start_with?('arch', 'manjaro')
96
+ end
97
+
98
+ def append_linux_to_os_name
99
+ os_name = @fact_list[:name]
100
+ @fact_list[:name] = os_name + 'Linux' if os_name.downcase.start_with?('virtuozzo')
70
101
  end
71
102
  end
72
103
  end
@@ -36,18 +36,18 @@ module Facter
36
36
  @fact_list[:distributor_id] = distributor_id(output_strings[0])
37
37
  @fact_list[:name] = release_name(output_strings[0])
38
38
  @fact_list[:version] = version(output_strings)
39
- @fact_list[:identifier] = identifier(@fact_list[:name])
39
+ @fact_list[:id] = id(@fact_list[:name])
40
40
  end
41
41
 
42
42
  def release_name(value)
43
43
  value.split.reject { |el| el.casecmp('linux').zero? }[0..1].join
44
44
  end
45
45
 
46
- def identifier(value)
47
- identifier = value.downcase
48
- identifier = 'rhel' if @fact_list[:name].casecmp('Red Hat Enterprise Linux')
46
+ def id(value)
47
+ id = value.downcase
48
+ id = 'rhel' if @fact_list[:name].casecmp('Red Hat Enterprise Linux')
49
49
 
50
- identifier
50
+ id
51
51
  end
52
52
 
53
53
  def codename(value)
@@ -24,7 +24,7 @@ module Facter
24
24
 
25
25
  @fact_list[:name] = output_strings[0]
26
26
  @fact_list[:version] = output_strings[1]
27
- @fact_list[:identifier] = @fact_list[:name].downcase
27
+ @fact_list[:id] = @fact_list[:name].downcase
28
28
 
29
29
  @fact_list[fact_name]
30
30
  end
@@ -0,0 +1,11 @@
1
+ # frozen_string_literal: true
2
+
3
+ require 'facter/resolvers/windows/ffi/ffi'
4
+
5
+ module WinnlsFFI
6
+ extend FFI::Library
7
+
8
+ ffi_convention :stdcall
9
+ ffi_lib :kernel32
10
+ attach_function :GetACP, [], :uint
11
+ end
@@ -24,10 +24,19 @@ module Facter
24
24
 
25
25
  def build_fact_list(reg)
26
26
  reg.each do |name, _value|
27
- @fact_list[:edition_id] = reg[name] if name == 'EditionID'
28
- @fact_list[:installation_type] = reg[name] if name == 'InstallationType'
29
- @fact_list[:product_name] = reg[name] if name == 'ProductName'
30
- @fact_list[:release_id] = reg[name] if name == 'ReleaseId'
27
+ case name
28
+ when 'EditionID'
29
+ @fact_list[:edition_id] = reg[name]
30
+ when 'InstallationType'
31
+ @fact_list[:installation_type] = reg[name]
32
+ when 'ProductName'
33
+ @fact_list[:product_name] = reg[name]
34
+ when 'DisplayVersion'
35
+ @fact_list[:release_id] = reg[name]
36
+ @fact_list[:display_version] = reg[name]
37
+ when 'ReleaseId'
38
+ @fact_list[:release_id] = reg[name] unless @fact_list[:release_id]
39
+ end
31
40
  end
32
41
  end
33
42
  end
@@ -0,0 +1,41 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Facter
4
+ module Resolvers
5
+ module Windows
6
+ class Timezone < BaseResolver
7
+ init_resolver
8
+
9
+ class << self
10
+ private
11
+
12
+ def post_resolve(fact_name, _options)
13
+ @fact_list.fetch(fact_name) { determine_timezone }
14
+ end
15
+
16
+ def determine_timezone
17
+ timezone = Time.now.zone
18
+ @fact_list[:timezone] = timezone.force_encoding("CP#{codepage}").encode('UTF-8', invalid: :replace)
19
+ rescue ArgumentError
20
+ @fact_list[:timezone] = timezone
21
+ end
22
+
23
+ def codepage
24
+ result = codepage_from_api
25
+ result.empty? ? codepage_from_registry : result
26
+ end
27
+
28
+ def codepage_from_registry
29
+ require 'win32/registry'
30
+ ::Win32::Registry::HKEY_LOCAL_MACHINE.open('SYSTEM\CurrentControlSet\Control\Nls\CodePage')['ACP']
31
+ end
32
+
33
+ def codepage_from_api
34
+ require 'facter/resolvers/windows/ffi/winnls_ffi'
35
+ WinnlsFFI.GetACP.to_s
36
+ end
37
+ end
38
+ end
39
+ end
40
+ end
41
+ end
@@ -6,17 +6,68 @@ module Facter
6
6
  module InfoExtractor
7
7
  MEGABYTES_EXPONENT = 1024**2
8
8
  GIGABYTES_EXPONENT = 1024**3
9
+ PROPERTIES = {
10
+ lslv: [
11
+ 'LOGICAL VOLUME:',
12
+ 'VOLUME GROUP:',
13
+ 'LV IDENTIFIER:',
14
+ 'PERMISSION:',
15
+ 'VG STATE:',
16
+ 'LV STATE:',
17
+ 'TYPE:',
18
+ 'WRITE VERIFY:',
19
+ 'MAX LPs:',
20
+ 'PP SIZE:',
21
+ 'COPIES:',
22
+ 'SCHED POLICY:',
23
+ 'LPs:',
24
+ 'PPs:',
25
+ 'STALE PPs:',
26
+ 'BB POLICY:',
27
+ 'INTER-POLICY:',
28
+ 'RELOCATABLE:',
29
+ 'INTRA-POLICY:',
30
+ 'UPPER BOUND:',
31
+ 'MOUNT POINT:',
32
+ 'LABEL:',
33
+ 'MIRROR WRITE CONSISTENCY:',
34
+ 'EACH LP COPY ON A SEPARATE PV ?:',
35
+ 'Serialize IO ?:'
36
+ ],
37
+ lspv: [
38
+ 'PHYSICAL VOLUME:',
39
+ 'VOLUME GROUP:',
40
+ 'PV IDENTIFIER:',
41
+ 'VG IDENTIFIER',
42
+ 'PV STATE:',
43
+ 'STALE PARTITIONS:',
44
+ 'ALLOCATABLE:',
45
+ 'PP SIZE:',
46
+ 'LOGICAL VOLUMES:',
47
+ 'TOTAL PPs:',
48
+ 'VG DESCRIPTORS:',
49
+ 'FREE PPs:',
50
+ 'HOT SPARE:',
51
+ 'USED PPs:',
52
+ 'MAX REQUEST:',
53
+ 'FREE DISTRIBUTION:',
54
+ 'USED DISTRIBUTION:',
55
+ 'MIRROR POOL:'
56
+ ]
57
+ }.freeze
9
58
 
10
- def self.extract(content, regex)
11
- content = content.each_line.map do |line|
12
- next unless regex =~ line
13
-
14
- line.split(/:\s*|\s{2,}/)
59
+ def self.extract(content, cmd)
60
+ property_hash = {}
61
+ properties = PROPERTIES[cmd]
62
+ properties.each do |property|
63
+ str = (properties - [property]).join('|')
64
+ matcher = content.match(/#{Regexp.escape(property)}([^\n]*?)(#{str}|\n|$)/s)
65
+ if matcher
66
+ value = matcher[1].strip
67
+ property_hash[property.split(':').first] = value
68
+ end
15
69
  end
16
-
17
- content.flatten!.reject!(&:nil?)
18
-
19
- Hash[*content]
70
+ property_hash
20
71
  end
21
72
  end
22
73
  end
@@ -23,9 +23,11 @@ module Facter
23
23
  private
24
24
 
25
25
  def check_version_10(consumerrel, kernel_version)
26
+ return '10' if consumerrel
27
+
26
28
  build_number = kernel_version[/([^.]*)$/].to_i
27
- if consumerrel
28
- '10'
29
+ if build_number >= 20_348
30
+ '2022'
29
31
  elsif build_number >= 17_623
30
32
  '2019'
31
33
  else
@@ -22,6 +22,16 @@ module Facter
22
22
  default_return
23
23
  end
24
24
 
25
+ def dir_children(path)
26
+ children = if RUBY_VERSION.to_f < 2.5
27
+ Dir.entries(path).reject { |dir| ['.', '..'].include?(dir) }
28
+ else
29
+ Dir.children(path)
30
+ end
31
+
32
+ children
33
+ end
34
+
25
35
  private
26
36
 
27
37
  def log_failed_to_read(path)
@@ -60,6 +60,9 @@ module Facter
60
60
  http = Net::HTTP.new(parsed_url.host)
61
61
  http.read_timeout = timeouts[:session] || SESSION_TIMEOUT
62
62
  http.open_timeout = timeouts[:connection] || CONNECTION_TIMEOUT
63
+
64
+ http.set_debug_output($stderr) if Options[:http_debug]
65
+
63
66
  http
64
67
  end
65
68
 
@@ -1,5 +1,5 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  module Facter
4
- VERSION = '4.2.3' unless defined?(VERSION)
4
+ VERSION = '4.2.7' unless defined?(VERSION)
5
5
  end
data/lib/facter.rb CHANGED
@@ -205,6 +205,25 @@ module Facter
205
205
  Facter::Options[:debug] = debug_bool
206
206
  end
207
207
 
208
+ # Check whether http debugging is enabled
209
+ #
210
+ # @return [bool]
211
+ #
212
+ # @api public
213
+ def http_debug?
214
+ Options[:http_debug]
215
+ end
216
+
217
+ # Enable or disable http debugging
218
+ # @param debug_bool [bool] State which http debugging should have
219
+ #
220
+ # @return [type] [description]
221
+ #
222
+ # @api public
223
+ def http_debug(http_debug_bool)
224
+ Facter::Options[:http_debug] = http_debug_bool
225
+ end
226
+
208
227
  # Enable sequential resolving of facts
209
228
  #
210
229
  # @return [bool]
@@ -390,7 +409,7 @@ module Facter
390
409
  #
391
410
  # @api public
392
411
  def value(user_query)
393
- user_query = user_query.to_s
412
+ user_query = user_query.to_s.downcase
394
413
  resolve_fact(user_query)
395
414
 
396
415
  @already_searched[user_query]&.value
@@ -407,7 +426,7 @@ module Facter
407
426
  #
408
427
  # @api public
409
428
  def fact(user_query)
410
- user_query = user_query.to_s
429
+ user_query = user_query.to_s.downcase
411
430
  resolve_fact(user_query)
412
431
 
413
432
  @already_searched[user_query]
metadata CHANGED
@@ -1,14 +1,14 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: facter
3
3
  version: !ruby/object:Gem::Version
4
- version: 4.2.3
4
+ version: 4.2.7
5
5
  platform: ruby
6
6
  authors:
7
7
  - Puppet
8
8
  autorequire:
9
9
  bindir: bin
10
10
  cert_chain: []
11
- date: 2021-08-16 00:00:00.000000000 Z
11
+ date: 2022-01-12 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: rake
@@ -846,6 +846,7 @@ files:
846
846
  - lib/facter/facts/windows/os/hardware.rb
847
847
  - lib/facter/facts/windows/os/name.rb
848
848
  - lib/facter/facts/windows/os/release.rb
849
+ - lib/facter/facts/windows/os/windows/display_version.rb
849
850
  - lib/facter/facts/windows/os/windows/edition_id.rb
850
851
  - lib/facter/facts/windows/os/windows/installation_type.rb
851
852
  - lib/facter/facts/windows/os/windows/product_name.rb
@@ -933,7 +934,7 @@ files:
933
934
  - lib/facter/resolvers/bsd/processors.rb
934
935
  - lib/facter/resolvers/containers.rb
935
936
  - lib/facter/resolvers/debian_version.rb
936
- - lib/facter/resolvers/disk.rb
937
+ - lib/facter/resolvers/disks.rb
937
938
  - lib/facter/resolvers/dmi.rb
938
939
  - lib/facter/resolvers/dmi_decode.rb
939
940
  - lib/facter/resolvers/ec2.rb
@@ -964,7 +965,7 @@ files:
964
965
  - lib/facter/resolvers/macosx/filesystems.rb
965
966
  - lib/facter/resolvers/macosx/load_averages.rb
966
967
  - lib/facter/resolvers/macosx/mountpoints.rb
967
- - lib/facter/resolvers/macosx/processor.rb
968
+ - lib/facter/resolvers/macosx/processors.rb
968
969
  - lib/facter/resolvers/macosx/swap_memory.rb
969
970
  - lib/facter/resolvers/macosx/system_memory.rb
970
971
  - lib/facter/resolvers/macosx/system_profiler.rb
@@ -1021,6 +1022,7 @@ files:
1021
1022
  - lib/facter/resolvers/windows/ffi/performance_information.rb
1022
1023
  - lib/facter/resolvers/windows/ffi/system32_ffi.rb
1023
1024
  - lib/facter/resolvers/windows/ffi/system_info.rb
1025
+ - lib/facter/resolvers/windows/ffi/winnls_ffi.rb
1024
1026
  - lib/facter/resolvers/windows/fips.rb
1025
1027
  - lib/facter/resolvers/windows/hardware_architecture.rb
1026
1028
  - lib/facter/resolvers/windows/identity.rb
@@ -1032,6 +1034,7 @@ files:
1032
1034
  - lib/facter/resolvers/windows/product_release.rb
1033
1035
  - lib/facter/resolvers/windows/ssh.rb
1034
1036
  - lib/facter/resolvers/windows/system32.rb
1037
+ - lib/facter/resolvers/windows/timezone.rb
1035
1038
  - lib/facter/resolvers/windows/uptime.rb
1036
1039
  - lib/facter/resolvers/windows/virtualization.rb
1037
1040
  - lib/facter/resolvers/windows/win_os_description.rb
@@ -1,64 +0,0 @@
1
- # frozen_string_literal: true
2
-
3
- module Facter
4
- module Resolvers
5
- module Linux
6
- class Disk < BaseResolver
7
- @log = Facter::Log.new(self)
8
-
9
- init_resolver
10
-
11
- DIR = '/sys/block'
12
- FILE_PATHS = { model: 'device/model', size: 'size', vendor: 'device/vendor', type: 'queue/rotational' }.freeze
13
-
14
- class << self
15
- private
16
-
17
- def post_resolve(fact_name, _options)
18
- @fact_list.fetch(fact_name) { read_facts(fact_name) }
19
- end
20
-
21
- def read_facts(fact_name)
22
- build_disks_hash
23
-
24
- FILE_PATHS.each do |key, file|
25
- @fact_list[:disks].each do |disk, value|
26
- file_path = File.join(DIR, disk, file)
27
-
28
- result = Facter::Util::FileHelper.safe_read(file_path).strip
29
- next if result.empty?
30
-
31
- value[key] = case key
32
- when :size
33
- # Linux always considers sectors to be 512 bytes long
34
- # independently of the devices real block size.
35
- construct_size(value, result)
36
- when :type
37
- result == '0' ? 'ssd' : 'hdd'
38
- else
39
- result
40
- end
41
- end
42
- end
43
-
44
- @fact_list[:disks] = nil if @fact_list[:disks].empty?
45
- @fact_list[fact_name]
46
- end
47
-
48
- def build_disks_hash
49
- @fact_list[:disks] = {}
50
- directories = Dir.entries(DIR).reject { |dir| dir =~ /\.+/ }
51
- directories.each { |disk| @fact_list[:disks].merge!(disk => {}) }
52
- @fact_list[:disks].select! { |disk, _fact| File.readable?(File.join(DIR, disk, 'device')) }
53
- end
54
-
55
- def construct_size(facts, value)
56
- value = value.to_i * 512
57
- facts[:size_bytes] = value
58
- facts[:size] = Facter::Util::Facts::UnitConverter.bytes_to_human_readable(value)
59
- end
60
- end
61
- end
62
- end
63
- end
64
- end