facter 4.0.15 → 4.0.16

Sign up to get free protection for your applications and to get access to all the features.
Files changed (49) hide show
  1. checksums.yaml +4 -4
  2. data/.rubocop.yml +1 -0
  3. data/.rubocop_todo.yml +5 -7
  4. data/CHANGELOG.md +33 -0
  5. data/VERSION +1 -1
  6. data/lib/custom_facts/util/parser.rb +1 -1
  7. data/lib/custom_facts/version.rb +1 -1
  8. data/lib/facter.rb +13 -17
  9. data/lib/facts/aix/partitions.rb +15 -0
  10. data/lib/facts/windows/ssh.rb +27 -0
  11. data/lib/facts_utils/uptime_parser.rb +3 -3
  12. data/lib/framework/cli/cli.rb +1 -1
  13. data/lib/framework/config/block_list.rb +1 -4
  14. data/lib/framework/config/config_reader.rb +29 -26
  15. data/lib/framework/core/fact_filter.rb +2 -2
  16. data/lib/framework/core/file_loader.rb +3 -3
  17. data/lib/framework/core/options.rb +57 -64
  18. data/lib/framework/core/options/config_file_options.rb +58 -48
  19. data/lib/framework/core/options/option_store.rb +156 -0
  20. data/lib/framework/core/options/options_validator.rb +42 -2
  21. data/lib/framework/logging/logger.rb +2 -0
  22. data/lib/framework/parsers/query_parser.rb +1 -1
  23. data/lib/models/fact_collection.rb +1 -1
  24. data/lib/resolvers/aix/filesystem_resolver.rb +3 -2
  25. data/lib/resolvers/aix/partitions.rb +86 -0
  26. data/lib/resolvers/aix/utils/odm_query.rb +2 -2
  27. data/lib/resolvers/debian_version.rb +2 -5
  28. data/lib/resolvers/disk_resolver.rb +2 -3
  29. data/lib/resolvers/dmi_resolver.rb +3 -3
  30. data/lib/resolvers/filesystems_resolver.rb +3 -3
  31. data/lib/resolvers/fips_enabled_resolver.rb +1 -4
  32. data/lib/resolvers/hostname_resolver.rb +4 -4
  33. data/lib/resolvers/load_averages_resolver.rb +1 -3
  34. data/lib/resolvers/memory_resolver.rb +2 -2
  35. data/lib/resolvers/mountpoints_resolver.rb +1 -3
  36. data/lib/resolvers/os_release_resolver.rb +3 -6
  37. data/lib/resolvers/partitions.rb +11 -6
  38. data/lib/resolvers/processors_resolver.rb +3 -3
  39. data/lib/resolvers/selinux_resolver.rb +9 -9
  40. data/lib/resolvers/solaris/filesystems_resolver.rb +1 -1
  41. data/lib/resolvers/ssh_resolver.rb +4 -43
  42. data/lib/resolvers/utils/ssh_helper.rb +27 -0
  43. data/lib/resolvers/windows/ssh.rb +47 -0
  44. data/lib/util/file_helper.rb +24 -0
  45. metadata +9 -6
  46. data/lib/framework/core/options/default_options.rb +0 -35
  47. data/lib/framework/core/options/helper_options.rb +0 -52
  48. data/lib/framework/core/options/priority_options.rb +0 -12
  49. data/lib/framework/core/options/validate_options.rb +0 -49
@@ -27,9 +27,9 @@ module Facter
27
27
  def execute
28
28
  result = nil
29
29
  REPOS.each do |repo|
30
- break if result
30
+ break if result && !result.empty?
31
31
 
32
- result, _s = Open3.capture2("#{query} #{repo}")
32
+ result, _stderr, _s = Open3.capture3("#{query} #{repo}")
33
33
  end
34
34
  result
35
35
  end
@@ -18,11 +18,8 @@ module Facter
18
18
  end
19
19
 
20
20
  def read_debian_version(fact_name)
21
- return unless File.readable?('/etc/debian_version')
22
-
23
- verion = File.read('/etc/debian_version')
24
-
25
- @fact_list[:version] = verion.strip
21
+ file_content = Util::FileHelper.safe_read('/etc/debian_version')
22
+ @fact_list[:version] = file_content.strip unless file_content.empty?
26
23
 
27
24
  @fact_list[fact_name]
28
25
  end
@@ -23,9 +23,8 @@ module Facter
23
23
  @fact_list[:disks].each do |disk, value|
24
24
  file_path = File.join(DIR, disk, file)
25
25
 
26
- next unless File.readable?(file_path)
27
-
28
- result = File.read(file_path).strip
26
+ result = Util::FileHelper.safe_read(file_path).strip
27
+ next if result.empty?
29
28
 
30
29
  # Linux always considers sectors to be 512 bytes long independently of the devices real block size.
31
30
  value[key] = file =~ /size/ ? construct_size(value, result) : result
@@ -28,15 +28,15 @@ module Facter
28
28
  @fact_list.fetch(fact_name) { read_facts(fact_name) }
29
29
  end
30
30
 
31
- # File.read("/sys/class/dmi/id/#{file}")
32
31
  def read_facts(fact_name)
33
32
  files = %w[bios_date bios_vendor bios_version board_vendor board_name board_serial
34
33
  chassis_asset_tag chassis_type sys_vendor product_name product_serial
35
34
  product_uuid]
36
35
  return unless File.directory?('/sys/class/dmi')
37
36
 
38
- if files.include?(fact_name.to_s) && File.readable?("/sys/class/dmi/id/#{fact_name}")
39
- @fact_list[fact_name] = File.read("/sys/class/dmi/id/#{fact_name}").strip
37
+ file_content = Util::FileHelper.safe_read("/sys/class/dmi/id/#{fact_name}", nil)
38
+ if files.include?(fact_name.to_s) && file_content
39
+ @fact_list[fact_name] = file_content.strip
40
40
  chassis_to_name(@fact_list[fact_name]) if fact_name == :chassis_type
41
41
 
42
42
  end
@@ -16,11 +16,11 @@ module Facter
16
16
  end
17
17
 
18
18
  def read_filesystems(fact_name)
19
- return unless File.readable?('/proc/filesystems')
19
+ output = Util::FileHelper.safe_readlines('/proc/filesystems', nil)
20
+ return unless output
20
21
 
21
- output = File.read('/proc/filesystems')
22
22
  filesystems = []
23
- output.each_line do |line|
23
+ output.each do |line|
24
24
  tokens = line.split(' ')
25
25
  filesystems << tokens if tokens.size == 1
26
26
  end
@@ -16,10 +16,7 @@ module Facter
16
16
  end
17
17
 
18
18
  def read_fips_file(fact_name)
19
- return @fact_list[fact_name] = false unless File.directory?('/proc/sys/crypto') ||
20
- File.readable?('/proc/sys/crypto/fips_enabled')
21
-
22
- file_output = File.read('/proc/sys/crypto/fips_enabled')
19
+ file_output = Util::FileHelper.safe_read('/proc/sys/crypto/fips_enabled')
23
20
  @fact_list[:fips_enabled] = file_output.strip == '1'
24
21
  @fact_list[fact_name]
25
22
  end
@@ -30,11 +30,11 @@ module Facter
30
30
  def read_domain(output)
31
31
  if output =~ /.*?\.(.+$)/
32
32
  domain = Regexp.last_match(1)
33
- elsif File.readable?('/etc/resolv.conf')
34
- file = File.read('/etc/resolv.conf')
35
- if file =~ /^search\s+(\S+)/
33
+ else
34
+ file_content = Util::FileHelper.safe_read('/etc/resolv.conf')
35
+ if file_content =~ /^search\s+(\S+)/
36
36
  domain = Regexp.last_match(1)
37
- elsif file =~ /^domain\s+(\S+)/
37
+ elsif file_content =~ /^domain\s+(\S+)/
38
38
  domain = Regexp.last_match(1)
39
39
  end
40
40
  end
@@ -15,9 +15,7 @@ module Facter
15
15
  end
16
16
 
17
17
  def read_load_averages_file(fact_name)
18
- return unless File.readable?('/proc/loadavg')
19
-
20
- output = File.read('/proc/loadavg')
18
+ output = Util::FileHelper.safe_read('/proc/loadavg')
21
19
  @fact_list[:load_averages] = {}.tap { |h| h['1m'], h['5m'], h['15m'], = output.split.map(&:to_f) }
22
20
 
23
21
  @fact_list[fact_name]
@@ -15,9 +15,9 @@ module Facter
15
15
  end
16
16
 
17
17
  def read_meminfo_file(fact_name)
18
- return unless File.readable?('/proc/meminfo')
18
+ meminfo_output = Util::FileHelper.safe_read('/proc/meminfo', nil)
19
+ return unless meminfo_output
19
20
 
20
- meminfo_output = File.read('/proc/meminfo')
21
21
  read_system(meminfo_output)
22
22
  read_swap(meminfo_output)
23
23
 
@@ -16,9 +16,7 @@ module Facter
16
16
  end
17
17
 
18
18
  def root_device
19
- return unless File.readable?('/proc/cmdline')
20
-
21
- cmdline = File.read('/proc/cmdline')
19
+ cmdline = Util::FileHelper.safe_read('/proc/cmdline')
22
20
  match = cmdline.match(/root=([^\s]+)/)
23
21
  match&.captures&.first
24
22
  end
@@ -25,15 +25,12 @@ module Facter
25
25
  end
26
26
 
27
27
  def read_os_release_file(fact_name)
28
- unless File.readable?('/etc/os-release')
29
- @fact_list[:name] = nil
30
- return
31
- end
28
+ output = Util::FileHelper.safe_readlines('/etc/os-release')
29
+ return @fact_list[:name] = nil if output.empty?
32
30
 
33
- output = File.read('/etc/os-release')
34
31
  pairs = []
35
32
 
36
- output.each_line do |line|
33
+ output.each do |line|
37
34
  pairs << line.strip.delete('"').split('=', 2)
38
35
  end
39
36
 
@@ -6,6 +6,7 @@ module Facter
6
6
  @semaphore = Mutex.new
7
7
  @fact_list ||= {}
8
8
  BLOCK_PATH = '/sys/block'
9
+ BLOCK_SIZE = 512
9
10
 
10
11
  class << self
11
12
  private
@@ -41,23 +42,27 @@ module Facter
41
42
  end
42
43
 
43
44
  def extract_from_dm(block_path)
44
- map_name = File.readable?("#{block_path}/dm/name") ? File.read("#{block_path}/dm/name").chomp : ''
45
+ map_name = Util::FileHelper.safe_read("#{block_path}/dm/name").chomp
45
46
  if map_name.empty?
46
- populate_partitions("/dev/#{block_path}", block_path)
47
+ populate_partitions("/dev#{block_path}", block_path)
47
48
  else
48
49
  populate_partitions("/dev/mapper/#{map_name}", block_path)
49
50
  end
50
51
  end
51
52
 
52
53
  def extract_from_loop(block_path)
53
- populate_partitions("/dev/#{block_path}", block_path) if File.readable?("#{block_path}/loop/backing_file")
54
- backing_file = File.read("#{block_path}/loop/backing_file").chomp
55
- populate_partitions("/dev/#{block_path}", block_path, backing_file)
54
+ backing_file = Util::FileHelper.safe_read("#{block_path}/loop/backing_file").chomp
55
+ if backing_file.empty?
56
+ populate_partitions("/dev#{block_path}", block_path)
57
+ else
58
+ populate_partitions("/dev#{block_path}", block_path, backing_file)
59
+ end
56
60
  end
57
61
 
58
62
  def populate_partitions(partition_name, block_path, backing_file = nil)
59
63
  @fact_list[:partitions][partition_name] = {}
60
- size_bytes = File.readable?("#{block_path}/size") ? File.read("#{block_path}/size").chomp.to_i * 512 : 0
64
+ size_bytes = Util::FileHelper.safe_read("#{block_path}/size", '0')
65
+ .chomp.to_i * BLOCK_SIZE
61
66
  info_hash = { size_bytes: size_bytes,
62
67
  size: Facter::BytesToHumanReadable.convert(size_bytes), backing_file: backing_file }
63
68
  info_hash.merge!(populate_from_blkid(partition_name))
@@ -19,9 +19,9 @@ module Facter
19
19
  end
20
20
 
21
21
  def read_cpuinfo(fact_name)
22
- return unless File.readable?('/proc/cpuinfo')
22
+ cpuinfo_output = Util::FileHelper.safe_readlines('/proc/cpuinfo')
23
+ return if cpuinfo_output.empty?
23
24
 
24
- cpuinfo_output = File.read('/proc/cpuinfo')
25
25
  read_processors(cpuinfo_output) # + model names
26
26
 
27
27
  @fact_list[:physical_count] = @fact_list[:physical_processors].uniq.length
@@ -32,7 +32,7 @@ module Facter
32
32
  @fact_list[:processors] = 0
33
33
  @fact_list[:models] = []
34
34
  @fact_list[:physical_processors] = []
35
- cpuinfo_output.each_line do |line|
35
+ cpuinfo_output.each do |line|
36
36
  tokens = line.split(':')
37
37
  count_processors(tokens)
38
38
  construct_models_list(tokens)
@@ -3,10 +3,6 @@
3
3
  module Facter
4
4
  module Resolvers
5
5
  class SELinux < BaseResolver
6
- # :name
7
- # :version
8
- # :codename
9
-
10
6
  @semaphore = Mutex.new
11
7
  @fact_list ||= {}
12
8
 
@@ -19,7 +15,7 @@ module Facter
19
15
 
20
16
  def retrieve_facts(fact_name)
21
17
  mountpoint = read_mounts_file if @fact_list[:enabled].nil?
22
- read_other_selinux_facts(mountpoint) if @fact_list[:enabled] && File.readable?('/etc/selinux/config')
18
+ read_other_selinux_facts(mountpoint) if @fact_list[:enabled]
23
19
 
24
20
  @fact_list[fact_name]
25
21
  end
@@ -40,14 +36,14 @@ module Facter
40
36
  end
41
37
 
42
38
  def read_other_selinux_facts(mountpoint)
43
- read_selinux_config
39
+ return unless read_selinux_config
44
40
 
45
41
  enforce_file = "#{mountpoint}/enforce"
46
42
  policy_file = "#{mountpoint}/policyvers"
47
43
 
48
- @fact_list[:policy_version] = File.read(policy_file) if File.readable?(policy_file)
44
+ @fact_list[:policy_version] = Util::FileHelper.safe_read(policy_file, nil)
49
45
 
50
- enforce = File.read(enforce_file) if File.readable?(enforce_file)
46
+ enforce = Util::FileHelper.safe_read(enforce_file)
51
47
  if enforce.eql?('1')
52
48
  @fact_list[:enforced] = true
53
49
  @fact_list[:current_mode] = 'enforcing'
@@ -58,10 +54,14 @@ module Facter
58
54
  end
59
55
 
60
56
  def read_selinux_config
61
- File.readlines('/etc/selinux/config').map do |line|
57
+ file_lines = Util::FileHelper.safe_readlines('/etc/selinux/config')
58
+
59
+ file_lines.map do |line|
62
60
  @fact_list[:config_mode] = line.split('=').last.strip if line =~ /^SELINUX=/
63
61
  @fact_list[:config_policy] = line.split('=').last.strip if line =~ /^SELINUXTYPE=/
64
62
  end
63
+
64
+ true unless file_lines.empty?
65
65
  end
66
66
  end
67
67
  end
@@ -15,7 +15,7 @@ module Facter
15
15
  end
16
16
 
17
17
  def read_sysdef_file(fact_name)
18
- return unless File.readable?('/usr/sbin/sysdef')
18
+ return unless File.executable?('/usr/sbin/sysdef')
19
19
 
20
20
  file_content, _status = Open3.capture2('/usr/sbin/sysdef')
21
21
  files = file_content.split("\n").map do |line|
@@ -1,8 +1,5 @@
1
1
  # frozen_string_literal: true
2
2
 
3
- require 'base64'
4
- require 'digest/sha1'
5
-
6
3
  module Facter
7
4
  module Resolvers
8
5
  class SshResolver < BaseResolver
@@ -24,52 +21,16 @@ module Facter
24
21
  next unless File.directory?(file_path)
25
22
 
26
23
  @file_names.each do |file_name|
27
- next unless FileTest.file?(File.join(file_path, file_name))
24
+ file_content = Util::FileHelper.safe_read(File.join(file_path, file_name), nil)
25
+ next unless file_content
28
26
 
29
- key_type, key = File.read(File.join(file_path, file_name)).split(' ')
30
- key_name = determine_ssh_key_name(key_type)
31
- ssh_list << create_ssh(key_name, key_type, key)
27
+ key_type, key = file_content.split(' ')
28
+ ssh_list << ::Resolvers::Utils::SshHelper.create_ssh(key_type, key)
32
29
  end
33
30
  end
34
31
  @fact_list[:ssh] = ssh_list
35
32
  @fact_list[fact_name]
36
33
  end
37
-
38
- def create_ssh(key_name, key_type, key)
39
- decoded_key = Base64.decode64(key)
40
- ssh_fa = determine_ssh_fingerprint(key_name)
41
- sha1 = "SSHFP #{ssh_fa} 1 #{Digest::SHA1.new.update(decoded_key)}"
42
- sha256 = "SSHFP #{ssh_fa} 2 #{Digest::SHA2.new.update(decoded_key)}"
43
-
44
- fingerprint = FingerPrint.new(sha1, sha256)
45
- Ssh.new(fingerprint, key_type, key, key_name)
46
- end
47
-
48
- def determine_ssh_key_name(key)
49
- case key
50
- when 'ssh-dss'
51
- 'dsa'
52
- when 'ecdsa-sha2-nistp256'
53
- 'ecdsa'
54
- when 'ssh-ed25519'
55
- 'ed25519'
56
- when 'ssh-rsa'
57
- 'rsa'
58
- end
59
- end
60
-
61
- def determine_ssh_fingerprint(key_name)
62
- case key_name
63
- when 'rsa'
64
- 1
65
- when 'dsa'
66
- 2
67
- when 'ecdsa'
68
- 3
69
- when 'ed25519'
70
- 4
71
- end
72
- end
73
34
  end
74
35
  end
75
36
  end
@@ -0,0 +1,27 @@
1
+ # frozen_string_literal: true
2
+
3
+ require 'base64'
4
+ require 'digest/sha1'
5
+
6
+ module Resolvers
7
+ module Utils
8
+ class SshHelper
9
+ class << self
10
+ SSH_NAME = { 'ssh-dss' => 'dsa', 'ecdsa-sha2-nistp256' => 'ecdsa',
11
+ 'ssh-ed25519' => 'ed25519', 'ssh-rsa' => 'rsa' }.freeze
12
+ SSH_FINGERPRINT = { 'rsa' => 1, 'dsa' => 2, 'ecdsa' => 3, 'ed25519' => 4 }.freeze
13
+
14
+ def create_ssh(key_type, key)
15
+ key_name = SSH_NAME[key_type]
16
+ decoded_key = Base64.decode64(key)
17
+ ssh_fp = SSH_FINGERPRINT[key_name]
18
+ sha1 = "SSHFP #{ssh_fp} 1 #{Digest::SHA1.new.update(decoded_key)}"
19
+ sha256 = "SSHFP #{ssh_fp} 2 #{Digest::SHA2.new.update(decoded_key)}"
20
+
21
+ fingerprint = Facter::FingerPrint.new(sha1, sha256)
22
+ Facter::Ssh.new(fingerprint, key_type, key, key_name)
23
+ end
24
+ end
25
+ end
26
+ end
27
+ end
@@ -0,0 +1,47 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Facter
4
+ module Resolvers
5
+ module Windows
6
+ class Ssh < BaseResolver
7
+ @log = Facter::Log.new(self)
8
+ @semaphore = Mutex.new
9
+ @fact_list ||= {}
10
+ FILE_NAMES = %w[ssh_host_rsa_key.pub ssh_host_dsa_key.pub
11
+ ssh_host_ecdsa_key.pub ssh_host_ed25519_key.pub].freeze
12
+ class << self
13
+ private
14
+
15
+ def post_resolve(fact_name)
16
+ @fact_list.fetch(fact_name) { retrieve_info(fact_name) }
17
+ end
18
+
19
+ def retrieve_info(fact_name)
20
+ ssh_dir = determine_ssh_dir
21
+ return unless ssh_dir && File.directory?(ssh_dir)
22
+
23
+ ssh_list = []
24
+
25
+ FILE_NAMES.each do |file_name|
26
+ output = Util::FileHelper.safe_read(File.join(ssh_dir, file_name))
27
+ next if output.empty?
28
+
29
+ key_type, key = output.split(' ')
30
+ ssh_list << ::Resolvers::Utils::SshHelper.create_ssh(key_type, key)
31
+ end
32
+ @fact_list[:ssh] = ssh_list.empty? ? nil : ssh_list
33
+ @fact_list[fact_name]
34
+ end
35
+
36
+ def determine_ssh_dir
37
+ progdata_dir = ENV['programdata']
38
+
39
+ return if !progdata_dir || progdata_dir.empty?
40
+
41
+ File.join(progdata_dir, 'ssh')
42
+ end
43
+ end
44
+ end
45
+ end
46
+ end
47
+ end
@@ -0,0 +1,24 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Facter
4
+ module Util
5
+ class FileHelper
6
+ @log = Log.new(self)
7
+ class << self
8
+ DEBUG_MESSAGE = 'File at: %s is not accessible.'
9
+
10
+ def safe_read(path, result_if_not_readable = '')
11
+ return File.read(path) if File.readable?(path)
12
+
13
+ result_if_not_readable
14
+ end
15
+
16
+ def safe_readlines(path, result_if_not_readable = [])
17
+ return File.readlines(path) if File.readable?(path)
18
+
19
+ result_if_not_readable
20
+ end
21
+ end
22
+ end
23
+ end
24
+ end