facter 4.0.52 → 4.1.0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (42) hide show
  1. checksums.yaml +4 -4
  2. data/lib/facter.rb +20 -20
  3. data/lib/facter/custom_facts/core/execution/base.rb +7 -3
  4. data/lib/facter/custom_facts/core/execution/popen3.rb +13 -1
  5. data/lib/facter/custom_facts/util/collection.rb +5 -0
  6. data/lib/facter/custom_facts/util/normalization.rb +7 -2
  7. data/lib/facter/facts/linux/cloud/provider.rb +9 -2
  8. data/lib/facter/facts/linux/hypervisors/xen.rb +2 -4
  9. data/lib/facter/facts/solaris/hypervisors/ldom.rb +1 -1
  10. data/lib/facter/facts/solaris/hypervisors/zone.rb +1 -1
  11. data/lib/facter/facts/solaris/mountpoints.rb +1 -1
  12. data/lib/facter/facts/windows/az_metadata.rb +1 -5
  13. data/lib/facter/facts/windows/cloud/provider.rb +6 -2
  14. data/lib/facter/framework/core/fact_augmenter.rb +21 -4
  15. data/lib/facter/framework/core/fact_loaders/external_fact_loader.rb +9 -6
  16. data/lib/facter/framework/core/fact_loaders/fact_loader.rb +36 -33
  17. data/lib/facter/framework/core/fact_manager.rb +85 -14
  18. data/lib/facter/framework/core/options/config_file_options.rb +7 -0
  19. data/lib/facter/framework/core/options/option_store.rb +3 -1
  20. data/lib/facter/framework/formatters/formatter_helper.rb +3 -5
  21. data/lib/facter/framework/parsers/query_parser.rb +7 -14
  22. data/lib/facter/models/fact_collection.rb +32 -5
  23. data/lib/facter/resolvers/aix/ffi/ffi_helper.rb +1 -1
  24. data/lib/facter/resolvers/base_resolver.rb +2 -2
  25. data/lib/facter/resolvers/dmi_decode.rb +0 -1
  26. data/lib/facter/resolvers/linux/hostname.rb +16 -5
  27. data/lib/facter/resolvers/macosx/mountpoints.rb +14 -1
  28. data/lib/facter/resolvers/networking.rb +1 -1
  29. data/lib/facter/resolvers/selinux.rb +5 -7
  30. data/lib/facter/resolvers/solaris/ffi/structs.rb +12 -0
  31. data/lib/facter/resolvers/solaris/mountpoints.rb +22 -16
  32. data/lib/facter/resolvers/solaris/networking.rb +20 -5
  33. data/lib/facter/resolvers/solaris/zone.rb +0 -1
  34. data/lib/facter/resolvers/windows/uptime.rb +3 -22
  35. data/lib/facter/templates/man.erb +6 -6
  36. data/lib/facter/util/facts/unit_converter.rb +2 -2
  37. data/lib/facter/util/facts/virtual_detector.rb +6 -13
  38. data/lib/facter/util/resolvers/http.rb +7 -1
  39. data/lib/facter/util/resolvers/networking/primary_interface.rb +11 -5
  40. data/lib/facter/util/utils.rb +18 -1
  41. data/lib/facter/version.rb +1 -1
  42. metadata +3 -3
@@ -8,18 +8,16 @@ module Facter
8
8
  @internal_fact_mgr = InternalFactManager.new
9
9
  @external_fact_mgr = ExternalFactManager.new
10
10
  @fact_loader = FactLoader.instance
11
+ @options = Options.get
11
12
  @log = Log.new(self)
12
13
  end
13
14
 
14
- def searched_facts(user_query = [])
15
- loaded_facts = @fact_loader.load(Options.get)
16
- QueryParser.parse(user_query, loaded_facts)
17
- end
18
-
19
15
  def resolve_facts(user_query = [])
20
- searched_facts = parse_user_query(user_query)
21
-
16
+ @options[:user_query] = user_query
22
17
  cache_manager = Facter::CacheManager.new
18
+
19
+ searched_facts = QueryParser.parse(user_query, @fact_loader.load(@options))
20
+
23
21
  searched_facts, cached_facts = cache_manager.resolve_facts(searched_facts)
24
22
  internal_facts = @internal_fact_mgr.resolve_facts(searched_facts)
25
23
  external_facts = @external_fact_mgr.resolve_facts(searched_facts)
@@ -35,23 +33,96 @@ module Facter
35
33
  resolved_facts
36
34
  end
37
35
 
38
- def resolve_core(user_query = [])
39
- loaded_facts_hash = @fact_loader.internal_facts
36
+ # resolve a fact by name, in a similar way that facter 3 does.
37
+ # search is done in multiple steps, and the next step is executed
38
+ # only if the previous one was not able to resolve the fact
39
+ # - load the `fact_name.rb` from the configured custom directories
40
+ # - load all the core facts, external facts and env facts
41
+ # - load all custom facts
42
+ def resolve_fact(user_query)
43
+ @options[:user_query] = user_query
44
+ @log.debug("resolving fact with user_query: #{user_query}")
45
+
46
+ @cache_manager = Facter::CacheManager.new
47
+
48
+ custom_facts = custom_fact_by_filename || []
49
+ core_and_external_facts = core_or_external_fact || []
50
+ resolved_facts = core_and_external_facts + custom_facts
51
+
52
+ resolved_facts = all_custom_facts if resolved_facts.empty?
53
+
54
+ @cache_manager.cache_facts(resolved_facts)
55
+
56
+ log_resolved_facts(resolved_facts)
57
+ resolved_facts
58
+ end
59
+
60
+ def resolve_core(user_query = [], options = {})
61
+ @cache_manager = CacheManager.new
62
+ core_fact(user_query, options)
63
+ end
64
+
65
+ private
66
+
67
+ def core_fact(user_query, options)
68
+ loaded_facts_hash = @fact_loader.load_internal_facts(options)
40
69
 
41
70
  searched_facts = QueryParser.parse(user_query, loaded_facts_hash)
71
+ searched_facts, cached_facts = @cache_manager.resolve_facts(searched_facts)
72
+
42
73
  resolved_facts = @internal_fact_mgr.resolve_facts(searched_facts)
74
+ resolved_facts = resolved_facts.concat(cached_facts)
75
+
43
76
  FactFilter.new.filter_facts!(resolved_facts, user_query)
44
77
 
45
78
  resolved_facts
46
79
  end
47
80
 
48
- private
81
+ def custom_fact_by_filename
82
+ user_query = @options[:user_query]
83
+ @log.debug("Searching fact: #{user_query} in file: #{user_query}.rb")
84
+
85
+ custom_fact = @fact_loader.load_custom_fact(@options, user_query)
86
+ return unless custom_fact.any?
87
+
88
+ searched_facts = parse_user_query(custom_fact, @options)
89
+ searched_facts, cached_facts = @cache_manager.resolve_facts(searched_facts)
90
+
91
+ resolved_facts = @external_fact_mgr.resolve_facts(searched_facts)
92
+ resolved_facts = resolved_facts.concat(cached_facts)
93
+ resolved_facts if resolved_facts.any?
94
+ end
49
95
 
50
- def parse_user_query(user_query)
51
- options = Options.get
52
- options[:user_query] = user_query
53
- loaded_facts = @fact_loader.load(options)
96
+ def core_or_external_fact
97
+ user_query = @options[:user_query]
98
+ @log.debug("Searching fact: #{user_query} in core facts and external facts")
99
+
100
+ core_facts = core_fact([user_query], @options)
101
+ external_facts = @fact_loader.load_external_facts(@options)
102
+ searched_facts = parse_user_query(external_facts, @options)
103
+ searched_facts, cached_facts = @cache_manager.resolve_facts(searched_facts)
104
+
105
+ resolved_facts = @external_fact_mgr.resolve_facts(searched_facts)
106
+ resolved_facts = override_core_facts(core_facts, resolved_facts)
107
+ resolved_facts = resolved_facts.concat(cached_facts)
108
+
109
+ resolved_facts unless resolved_facts.map(&:value).compact.empty?
110
+ end
111
+
112
+ def all_custom_facts
113
+ user_query = @options[:user_query]
114
+ @log.debug("Searching fact: #{user_query} in all custom facts")
115
+
116
+ custom_facts = @fact_loader.load_custom_facts(@options)
117
+ searched_facts = parse_user_query(custom_facts, @options)
118
+ searched_facts, cached_facts = @cache_manager.resolve_facts(searched_facts)
119
+
120
+ resolved_facts = @external_fact_mgr.resolve_facts(searched_facts)
121
+ resolved_facts.concat(cached_facts)
122
+ end
54
123
 
124
+ def parse_user_query(loaded_facts, options)
125
+ user_query = Array(options[:user_query])
55
126
  QueryParser.parse(user_query, loaded_facts)
56
127
  end
57
128
 
@@ -18,6 +18,12 @@ module Facter
18
18
 
19
19
  private
20
20
 
21
+ def augment_structured_facts(global_conf)
22
+ return if !global_conf || global_conf['force-dot-resolution'].nil?
23
+
24
+ @options[:force_dot_resolution] = global_conf['force-dot-resolution']
25
+ end
26
+
21
27
  def augment_all
22
28
  augment_cli(Facter::ConfigReader.cli) if Options.cli?
23
29
  augment_globals
@@ -27,6 +33,7 @@ module Facter
27
33
  def augment_globals
28
34
  augment_ruby(Facter::ConfigReader.global)
29
35
 
36
+ augment_structured_facts(Facter::ConfigReader.global)
30
37
  augment_custom(Facter::ConfigReader.global)
31
38
  augment_external(Facter::ConfigReader.global)
32
39
  augment_show_legacy(Facter::ConfigReader.global)
@@ -35,6 +35,7 @@ module Facter
35
35
  @custom_dir = []
36
36
  @hocon = false
37
37
  @allow_external_loggers = true
38
+ @force_dot_resolution = false
38
39
 
39
40
  class << self
40
41
  attr_reader :debug, :verbose, :log_level, :show_legacy,
@@ -42,7 +43,7 @@ module Facter
42
43
 
43
44
  attr_accessor :config, :strict, :json,
44
45
  :cache, :yaml, :puppet, :ttls, :block, :cli, :config_file_custom_dir,
45
- :config_file_external_dir, :default_external_dir, :fact_groups,
46
+ :config_file_external_dir, :default_external_dir, :fact_groups, :force_dot_resolution,
46
47
  :block_list, :color, :trace, :sequential, :timing, :hocon, :allow_external_loggers
47
48
 
48
49
  attr_writer :external_dir
@@ -202,6 +203,7 @@ module Facter
202
203
 
203
204
  def reset_facts
204
205
  @custom_facts = true
206
+ @force_dot_resolution = false
205
207
  @external_dir = []
206
208
  @custom_dir = []
207
209
  end
@@ -4,12 +4,11 @@ module Facter
4
4
  class FormatterHelper
5
5
  class << self
6
6
  def retrieve_facts_to_display_for_user_query(user_queries, resolved_facts)
7
- facts_to_display = {}
7
+ facts_to_display = FactCollection.new
8
8
  user_queries.each do |user_query|
9
9
  fact_collection = build_fact_collection_for_user_query(user_query, resolved_facts)
10
10
 
11
- splitted_user_query = Utils.split_user_query(user_query)
12
- printable_value = fact_collection.dig(*splitted_user_query)
11
+ printable_value = fact_collection.dig_fact(user_query)
13
12
  facts_to_display.merge!(user_query => printable_value)
14
13
  end
15
14
 
@@ -24,8 +23,7 @@ module Facter
24
23
  def retrieve_fact_value_for_single_query(user_query, resolved_facts)
25
24
  fact_collection = build_fact_collection_for_user_query(user_query, resolved_facts)
26
25
  fact_collection = Utils.sort_hash_by_key(fact_collection)
27
- splitted_user_query = Utils.split_user_query(user_query)
28
- fact_collection.dig(*splitted_user_query)
26
+ fact_collection.dig_fact(user_query)
29
27
  end
30
28
 
31
29
  private
@@ -69,19 +69,12 @@ module Facter
69
69
  resolvable_fact_list = []
70
70
 
71
71
  loaded_fact_hash.each do |loaded_fact|
72
- # return the fact if toplevel namespace is found in the first query token
73
- # eg. query: 'os.name' and loaded_fact_hash contains a fact with 'os' name
74
- # it will construt and return the 'os' fact
75
- if loaded_fact.name == query_tokens[0]
76
- resolvable_fact_list = [construct_loaded_fact(query_tokens, query_token_range, loaded_fact)]
77
- else
78
- query_fact = query_tokens[query_token_range].join('.')
79
-
80
- next unless found_fact?(loaded_fact.name, query_fact)
81
-
82
- searched_fact = construct_loaded_fact(query_tokens, query_token_range, loaded_fact)
83
- resolvable_fact_list << searched_fact
84
- end
72
+ query_fact = query_tokens[query_token_range].join('.')
73
+
74
+ next unless found_fact?(loaded_fact.name, query_fact)
75
+
76
+ searched_fact = construct_loaded_fact(query_tokens, query_token_range, loaded_fact)
77
+ resolvable_fact_list << searched_fact
85
78
  end
86
79
 
87
80
  @log.debug "List of resolvable facts: #{resolvable_fact_list.inspect}"
@@ -113,7 +106,7 @@ module Facter
113
106
  end
114
107
 
115
108
  def construct_filter_tokens(query_tokens, query_token_range)
116
- (query_tokens - query_tokens[query_token_range]).map do |token|
109
+ query_tokens.drop(query_token_range.size).map do |token|
117
110
  token =~ /^[0-9]+$/ ? token.to_i : token
118
111
  end
119
112
  end
@@ -7,6 +7,10 @@ module Facter
7
7
  @log = Log.new(self)
8
8
  end
9
9
 
10
+ def to_yaml
11
+ deep_to_h.to_yaml
12
+ end
13
+
10
14
  def build_fact_collection!(facts)
11
15
  facts.each do |fact|
12
16
  next if %i[core legacy].include?(fact.type) && fact.value.nil?
@@ -17,10 +21,20 @@ module Facter
17
21
  self
18
22
  end
19
23
 
20
- def value(*keys)
21
- keys.reduce(self) do |memo, key|
22
- memo.fetch(key.to_s)
23
- end
24
+ def dig_fact(user_query)
25
+ split_user_query = Facter::Utils.split_user_query(user_query)
26
+ fact = dig(user_query) || dig(*split_user_query)
27
+ rescue TypeError
28
+ # An incorrect user query (e.g. mountpoints./.available.asd) can cause
29
+ # Facter to call dig on a string, which raises a type error.
30
+ # If this happens, we assume the query is wrong and silently continue.
31
+ ensure
32
+ @log.debug("Fact \"#{user_query}\" does not exist") unless fact
33
+ fact
34
+ end
35
+
36
+ def value(user_query)
37
+ dig_fact(user_query)
24
38
  end
25
39
 
26
40
  def bury(*args)
@@ -39,6 +53,12 @@ module Facter
39
53
 
40
54
  private
41
55
 
56
+ def deep_to_h(collection = self)
57
+ collection.each_pair.with_object({}) do |(key, value), hash|
58
+ hash[key] = value.is_a?(FactCollection) ? deep_to_h(value) : value
59
+ end
60
+ end
61
+
42
62
  def bury_fact(fact)
43
63
  split_fact_name = extract_fact_name(fact)
44
64
  bury(*split_fact_name + fact.filter_tokens << fact.value)
@@ -49,7 +69,14 @@ module Facter
49
69
  end
50
70
 
51
71
  def extract_fact_name(fact)
52
- fact.type == :legacy ? [fact.name] : fact.name.split('.')
72
+ case fact.type
73
+ when :legacy
74
+ [fact.name]
75
+ when :custom, :external
76
+ Options[:force_dot_resolution] == true ? fact.name.split('.') : [fact.name]
77
+ else
78
+ fact.name.split('.')
79
+ end
53
80
  end
54
81
  end
55
82
  end
@@ -33,7 +33,7 @@ module Facter
33
33
 
34
34
  return if Libc.getkerninfo(KINFO_READ | KINFO_GET_AVENRUN, averages, averages_size, 0).negative?
35
35
 
36
- averages.read_array_of_long_long(3).map { |x| (x / 65_536.0).round(5) }
36
+ averages.read_array_of_long_long(3).map { |x| (x / 65_536.0) }
37
37
  end
38
38
 
39
39
  def self.read_interfaces
@@ -28,10 +28,10 @@ module Facter
28
28
  cache_nil_for_unresolved_facts(fact_name)
29
29
  end
30
30
  rescue NoMethodError => e
31
- log.debug("Could not resolve #{fact_name}, got #{e}")
31
+ log.debug("Could not resolve #{fact_name}, got #{e} at #{e.backtrace[0]}")
32
32
  @fact_list[fact_name] = nil
33
33
  rescue LoadError, NameError => e
34
- log.debug("resolving fact #{fact_name}, but #{e}")
34
+ log.debug("Resolving fact #{fact_name}, but got #{e} at #{e.backtrace[0]}")
35
35
  @fact_list[fact_name] = nil
36
36
  end
37
37
 
@@ -37,7 +37,6 @@ module Facter
37
37
  @fact_list[:virtualbox_version] = output.match(/vboxVer_(\S+)/)&.captures&.first
38
38
  @fact_list[:virtualbox_revision] = output.match(/vboxRev_(\S+)/)&.captures&.first
39
39
  @fact_list[:vmware_version] = extract_vmware_version(output)
40
- @fact_list[:vendor] = output.match(/Vendor: (\S+)/)&.captures&.first
41
40
 
42
41
  @fact_list[fact_name]
43
42
  end
@@ -19,7 +19,6 @@ module Facter
19
19
 
20
20
  def retrieve_info(fact_name)
21
21
  require 'socket'
22
- require 'facter/util/resolvers/ffi/hostname'
23
22
 
24
23
  output = retrieving_hostname
25
24
  return nil unless output
@@ -38,13 +37,20 @@ module Facter
38
37
  end
39
38
 
40
39
  def retrieving_hostname
41
- output = Socket.gethostname
42
- if !output || output.empty? || output['0.0.0.0']
43
- output = Facter::Util::Resolvers::Ffi::Hostname.getffihostname
40
+ output = Socket.gethostname || ''
41
+ if output.empty? || output['0.0.0.0']
42
+ begin
43
+ require 'facter/util/resolvers/ffi/hostname'
44
+
45
+ output = Facter::Util::Resolvers::Ffi::Hostname.getffihostname
46
+ rescue LoadError => e
47
+ log.debug(e.message)
48
+ output = nil
49
+ end
44
50
  end
45
51
 
46
52
  log.debug("Tried to retrieve hostname and got: #{output}")
47
- output && !output.empty? ? output : nil
53
+ return output unless output&.empty?
48
54
  end
49
55
 
50
56
  def parse_fqdn(output)
@@ -70,9 +76,14 @@ module Facter
70
76
  end
71
77
 
72
78
  def retrieve_fqdn_for_host_with_ffi(host)
79
+ require 'facter/util/resolvers/ffi/hostname'
80
+
73
81
  fqdn = Facter::Util::Resolvers::Ffi::Hostname.getffiaddrinfo(host)
74
82
  log.debug("FFI getaddrinfo was called and it retrieved: #{fqdn}")
75
83
  fqdn
84
+ rescue LoadError => e
85
+ log.debug(e.message)
86
+ nil
76
87
  end
77
88
 
78
89
  def exists_and_valid_fqdn?(fqdn, hostname)
@@ -21,7 +21,7 @@ module Facter
21
21
  device = fs.name
22
22
  filesystem = fs.mount_type
23
23
  path = fs.mount_point
24
- options = fs.options.split(',').map(&:strip).map { |o| o == 'rootfs' ? 'root' : o }
24
+ options = read_options(fs.options)
25
25
 
26
26
  mounts[path] = read_stats(path).tap do |hash|
27
27
  hash[:device] = device
@@ -53,6 +53,19 @@ module Facter
53
53
  used: Facter::Util::Facts::UnitConverter.bytes_to_human_readable(used_bytes)
54
54
  }
55
55
  end
56
+
57
+ def read_options(options)
58
+ options_map = {
59
+ 'read-only' => 'readonly',
60
+ 'asynchronous' => 'async',
61
+ 'synchronous' => 'noasync',
62
+ 'quotas' => 'quota',
63
+ 'rootfs' => 'root',
64
+ 'defwrite' => 'deferwrites'
65
+ }
66
+
67
+ options.split(',').map(&:strip).map { |o| options_map.key?(o) ? options_map[o] : o }
68
+ end
56
69
  end
57
70
  end
58
71
  end
@@ -42,7 +42,7 @@ module Facter
42
42
 
43
43
  def parse_interfaces_response(response)
44
44
  parsed_interfaces_data = {}
45
- interfaces_data = Hash[*response.split(/^([A-Za-z0-9_]+): /)[1..-1]]
45
+ interfaces_data = Hash[*response.split(/^([A-Za-z0-9_\.]+): /)[1..-1]]
46
46
 
47
47
  interfaces_data.each do |interface_name, raw_data|
48
48
  parsed_interface_data = {}
@@ -13,21 +13,21 @@ module Facter
13
13
  end
14
14
 
15
15
  def retrieve_facts(fact_name)
16
- mountpoint = read_mounts_file if @fact_list[:enabled].nil?
16
+ mountpoint = selinux_mountpoint
17
+
18
+ @fact_list[:enabled] = !mountpoint.empty? && read_selinux_config
17
19
  read_other_selinux_facts(mountpoint) if @fact_list[:enabled]
18
20
 
19
21
  @fact_list[fact_name]
20
22
  end
21
23
 
22
- def read_mounts_file
24
+ def selinux_mountpoint
23
25
  output = Facter::Core::Execution.execute('cat /proc/self/mounts', logger: log)
24
- @fact_list[:enabled] = false
25
26
  mountpoint = ''
26
27
 
27
28
  output.each_line do |line|
28
29
  next unless line =~ /selinuxfs/
29
30
 
30
- @fact_list[:enabled] = true
31
31
  mountpoint = line.split("\s")[1]
32
32
  break
33
33
  end
@@ -35,8 +35,6 @@ module Facter
35
35
  end
36
36
 
37
37
  def read_other_selinux_facts(mountpoint)
38
- return unless read_selinux_config
39
-
40
38
  enforce_file = "#{mountpoint}/enforce"
41
39
  policy_file = "#{mountpoint}/policyvers"
42
40
 
@@ -60,7 +58,7 @@ module Facter
60
58
  @fact_list[:config_policy] = line.split('=').last.strip if line =~ /^SELINUXTYPE=/
61
59
  end
62
60
 
63
- true unless file_lines.empty?
61
+ !file_lines.empty? ? true : false
64
62
  end
65
63
  end
66
64
  end