facter 4.2.3 → 4.2.7

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
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