facter 4.5.0 → 4.5.2
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.
- checksums.yaml +4 -4
- data/lib/facter/custom_facts/util/directory_loader.rb +6 -1
- data/lib/facter/facts/amzn/os/distro/release.rb +6 -1
- data/lib/facter/facts/amzn/os/release.rb +5 -0
- data/lib/facter/framework/core/fact_loaders/class_discoverer.rb +4 -3
- data/lib/facter/framework/core/file_loader.rb +2 -0
- data/lib/facter/framework/parsers/query_parser.rb +24 -20
- data/lib/facter/resolvers/aix/disks.rb +1 -1
- data/lib/facter/resolvers/aix/partitions.rb +2 -2
- data/lib/facter/resolvers/amzn/os_release_rpm.rb +40 -0
- data/lib/facter/resolvers/freebsd/system_memory.rb +15 -7
- data/lib/facter/resolvers/solaris/dmi_sparc.rb +6 -0
- data/lib/facter/resolvers/solaris/ffi/functions.rb +2 -2
- data/lib/facter/resolvers/solaris/networking.rb +1 -1
- data/lib/facter/resolvers/uname.rb +10 -6
- data/lib/facter/resolvers/xen.rb +3 -1
- data/lib/facter/util/aix/info_extractor.rb +1 -1
- data/lib/facter/util/facts/facts_utils.rb +2 -1
- data/lib/facter/util/resolvers/fingerprint.rb +1 -7
- data/lib/facter/util/resolvers/networking/primary_interface.rb +2 -0
- data/lib/facter/util/resolvers/ssh.rb +1 -9
- data/lib/facter/util/resolvers/ssh_helper.rb +2 -0
- data/lib/facter/version.rb +1 -1
- metadata +4 -3
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: d43b50edf3b5f7815e25fb42dc5cae9f15d2fb2dcbb5fd7146957667f48fa9cb
|
4
|
+
data.tar.gz: 2792c74d6509bddd13f391e9eaac528a5e8a8852fa6f276a433d2bddeb58d3d8
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: bac838d93cb84e7e3934e048368e69106182d25450979749228e2c32a8f2bd21792fd58c245e0cd7b18bbd9e5a11b6bf0edb7067435dd0948b4df25cef1517a8
|
7
|
+
data.tar.gz: b800f80520161b8ec33eb241811227565525c6bff880d271e78906b364c2e7d0fd4f54ac3984d7a6c93084c11de47788a0cd5549af770e91fbc3db9a65415deb
|
@@ -93,7 +93,12 @@ module LegacyFacter
|
|
93
93
|
if data == false
|
94
94
|
log.warn "Could not interpret fact file #{fact.file}"
|
95
95
|
elsif (data == {}) || data.nil?
|
96
|
-
log.debug(
|
96
|
+
log.debug(
|
97
|
+
"Structured data fact file #{fact.file} was parsed but was either empty or an invalid filetype "\
|
98
|
+
'(valid filetypes are .yaml, .json, and .txt).'
|
99
|
+
)
|
100
|
+
elsif !data.is_a?(Hash)
|
101
|
+
log.error("Structured data fact file #{fact.file} was parsed but no key=>value data was returned.")
|
97
102
|
else
|
98
103
|
add_data(data, collection, fact, weight)
|
99
104
|
end
|
@@ -20,10 +20,15 @@ module Facts
|
|
20
20
|
end
|
21
21
|
|
22
22
|
def determine_release_version
|
23
|
+
# For backwards compatibility, use system-release for AL1/AL2
|
23
24
|
version = Facter::Resolvers::ReleaseFromFirstLine.resolve(:release, release_file: '/etc/system-release')
|
25
|
+
if !version.nil? && version != '2'
|
26
|
+
# Use os-release for AL2023 and up
|
27
|
+
version = Facter::Resolvers::Amzn::OsReleaseRpm.resolve(:version)
|
28
|
+
end
|
24
29
|
version ||= Facter::Resolvers::OsRelease.resolve(:version_id)
|
25
30
|
|
26
|
-
Facter::Util::Facts.release_hash_from_string(version)
|
31
|
+
Facter::Util::Facts.release_hash_from_string(version, include_patch: true)
|
27
32
|
end
|
28
33
|
end
|
29
34
|
end
|
@@ -18,7 +18,12 @@ module Facts
|
|
18
18
|
end
|
19
19
|
|
20
20
|
def determine_release_version
|
21
|
+
# For backwards compatibility, use system-release for AL1/AL2
|
21
22
|
version = Facter::Resolvers::ReleaseFromFirstLine.resolve(:release, release_file: '/etc/system-release')
|
23
|
+
if !version.nil? && version != '2'
|
24
|
+
# Use os-release for AL2023 and up
|
25
|
+
version = Facter::Resolvers::Amzn::OsReleaseRpm.resolve(:version)
|
26
|
+
end
|
22
27
|
version ||= Facter::Resolvers::OsRelease.resolve(:version_id)
|
23
28
|
|
24
29
|
Facter::Util::Facts.release_hash_from_string(version)
|
@@ -21,9 +21,10 @@ module Facter
|
|
21
21
|
|
22
22
|
def find_nested_classes(mod, discovered_classes)
|
23
23
|
mod.constants.each do |constant_name|
|
24
|
-
|
25
|
-
|
26
|
-
|
24
|
+
obj = mod.const_get(constant_name)
|
25
|
+
if obj.instance_of? Class
|
26
|
+
discovered_classes << obj
|
27
|
+
elsif obj.instance_of? Module
|
27
28
|
find_nested_classes(Module.const_get("#{mod.name}::#{constant_name}"), discovered_classes)
|
28
29
|
end
|
29
30
|
end
|
@@ -244,6 +244,8 @@ os_hierarchy.each do |os|
|
|
244
244
|
require_relative '../../facts/amzn/os/distro/release.rb'
|
245
245
|
require_relative '../../facts/amzn/os/release.rb'
|
246
246
|
|
247
|
+
require_relative '../../resolvers/amzn/os_release_rpm'
|
248
|
+
|
247
249
|
when 'bsd'
|
248
250
|
require_relative '../../facts/bsd/kernelmajversion.rb'
|
249
251
|
require_relative '../../facts/bsd/kernelversion.rb'
|
@@ -18,18 +18,18 @@ module Facter
|
|
18
18
|
# Because a root fact will always be resolved by a collection of child facts,
|
19
19
|
# we can return one or more child facts for each parent.
|
20
20
|
#
|
21
|
-
#
|
22
|
-
#
|
21
|
+
# @param query_list [Array] The list of facts to search for
|
22
|
+
# @param loaded_facts [Array] All of the fact definitions for the current operating system
|
23
23
|
#
|
24
|
-
#
|
25
|
-
def parse(query_list,
|
24
|
+
# @return [Array<SearchedFact>] a list of searchable facts that resolve the user's query
|
25
|
+
def parse(query_list, loaded_facts)
|
26
26
|
matched_facts = []
|
27
27
|
@query_list = query_list
|
28
28
|
|
29
|
-
return no_user_query(
|
29
|
+
return no_user_query(loaded_facts) unless query_list.any?
|
30
30
|
|
31
31
|
query_list.each do |query|
|
32
|
-
found_facts = search_for_facts(query,
|
32
|
+
found_facts = search_for_facts(query, loaded_facts)
|
33
33
|
matched_facts << found_facts
|
34
34
|
end
|
35
35
|
|
@@ -44,15 +44,17 @@ module Facter
|
|
44
44
|
searched_facts
|
45
45
|
end
|
46
46
|
|
47
|
-
def search_for_facts(query,
|
47
|
+
def search_for_facts(query, loaded_facts)
|
48
48
|
resolvable_fact_list = []
|
49
49
|
query = query.to_s
|
50
50
|
query_tokens = query.end_with?('.*') ? [query] : query.split('.')
|
51
51
|
size = query_tokens.size
|
52
52
|
|
53
|
+
# Try to match the most specific query_tokens to the least, returning the first match
|
53
54
|
size.times do |i|
|
54
55
|
query_token_range = 0..size - i - 1
|
55
|
-
|
56
|
+
query_fact = query_tokens[query_token_range].join('.')
|
57
|
+
resolvable_fact_list = get_facts_matching_tokens(query_tokens, query_fact, loaded_facts)
|
56
58
|
|
57
59
|
return resolvable_fact_list if resolvable_fact_list.any?
|
58
60
|
end
|
@@ -62,12 +64,10 @@ module Facter
|
|
62
64
|
resolvable_fact_list
|
63
65
|
end
|
64
66
|
|
65
|
-
def get_facts_matching_tokens(query_tokens,
|
67
|
+
def get_facts_matching_tokens(query_tokens, query_fact, loaded_facts)
|
66
68
|
resolvable_fact_list = []
|
67
69
|
|
68
|
-
|
69
|
-
query_fact = query_tokens[query_token_range].join('.')
|
70
|
-
|
70
|
+
loaded_facts.each do |loaded_fact|
|
71
71
|
next unless found_fact?(loaded_fact.name, query_fact)
|
72
72
|
|
73
73
|
searched_fact = construct_loaded_fact(query_tokens, loaded_fact)
|
@@ -79,16 +79,20 @@ module Facter
|
|
79
79
|
end
|
80
80
|
|
81
81
|
def found_fact?(fact_name, query_fact)
|
82
|
+
# This is the case where the fact_name contains a wildcard like
|
83
|
+
# blockdevice_.*_model and we're querying for the legacy fact
|
84
|
+
# specifically using 'blockdevice_sba_model' and we don't want the query
|
85
|
+
# 'blockdevice.sba.model' to match
|
82
86
|
fact_with_wildcard = fact_name.include?('.*') && !query_fact.include?('.')
|
83
87
|
|
84
|
-
|
85
|
-
|
86
|
-
|
87
|
-
|
88
|
-
|
89
|
-
|
90
|
-
|
91
|
-
|
88
|
+
if fact_with_wildcard
|
89
|
+
# fact_name contains wildcard, so we're intentially not escaping.
|
90
|
+
query_fact.match("^#{fact_name}$")
|
91
|
+
else
|
92
|
+
processed_equery_fact = query_fact.gsub('\\', '\\\\\\\\')
|
93
|
+
# Must escape metacharacters (like dots) to ensure the correct fact is found
|
94
|
+
fact_name.match("^#{Regexp.escape(processed_equery_fact)}($|\\.)")
|
95
|
+
end
|
92
96
|
end
|
93
97
|
|
94
98
|
def construct_loaded_fact(query_tokens, loaded_fact)
|
@@ -47,7 +47,7 @@ module Facter
|
|
47
47
|
end
|
48
48
|
|
49
49
|
def compute_size(size_hash)
|
50
|
-
physical_partitions = size_hash['TOTAL PPs'].to_i
|
50
|
+
physical_partitions = size_hash['TOTAL PPs'].to_i
|
51
51
|
size_physical_partition = size_hash['PP SIZE']
|
52
52
|
exp = if size_physical_partition[/mega/]
|
53
53
|
Facter::Util::Aix::InfoExtractor::MEGABYTES_EXPONENT
|
@@ -56,14 +56,14 @@ module Facter
|
|
56
56
|
end
|
57
57
|
|
58
58
|
def compute_size(info_hash)
|
59
|
-
|
59
|
+
logical_partitions = info_hash['LPs'].to_i
|
60
60
|
size_physical_partition = info_hash['PP SIZE']
|
61
61
|
exp = if size_physical_partition[/mega/]
|
62
62
|
Facter::Util::Aix::InfoExtractor::MEGABYTES_EXPONENT
|
63
63
|
else
|
64
64
|
Facter::Util::Aix::InfoExtractor::GIGABYTES_EXPONENT
|
65
65
|
end
|
66
|
-
size_physical_partition.to_i *
|
66
|
+
size_physical_partition.to_i * logical_partitions * exp
|
67
67
|
end
|
68
68
|
end
|
69
69
|
end
|
@@ -0,0 +1,40 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module Facter
|
4
|
+
module Resolvers
|
5
|
+
module Amzn
|
6
|
+
class OsReleaseRpm < BaseResolver
|
7
|
+
init_resolver
|
8
|
+
|
9
|
+
class << self
|
10
|
+
private
|
11
|
+
|
12
|
+
def post_resolve(fact_name, _options)
|
13
|
+
@fact_list.fetch(fact_name) { rpm_system_call(fact_name) }
|
14
|
+
end
|
15
|
+
|
16
|
+
def rpm_system_call(fact_name)
|
17
|
+
output = Facter::Core::Execution.execute(
|
18
|
+
'rpm -q --qf \'%{NAME}\n%{VERSION}\n%{RELEASE}\n%{VENDOR}\' -f /etc/os-release',
|
19
|
+
logger: log
|
20
|
+
)
|
21
|
+
build_fact_list(output)
|
22
|
+
|
23
|
+
@fact_list[fact_name]
|
24
|
+
end
|
25
|
+
|
26
|
+
def build_fact_list(output)
|
27
|
+
rpm_results = output.split("\n")
|
28
|
+
|
29
|
+
return if rpm_results.empty?
|
30
|
+
|
31
|
+
@fact_list[:package],
|
32
|
+
@fact_list[:version],
|
33
|
+
@fact_list[:release],
|
34
|
+
@fact_list[:vendor] = rpm_results.map(&:strip)
|
35
|
+
end
|
36
|
+
end
|
37
|
+
end
|
38
|
+
end
|
39
|
+
end
|
40
|
+
end
|
@@ -15,25 +15,33 @@ module Facter
|
|
15
15
|
|
16
16
|
def calculate_system_memory(fact_name)
|
17
17
|
read_total_memory_in_bytes
|
18
|
-
|
18
|
+
read_used_memory_in_bytes
|
19
19
|
|
20
|
-
@fact_list[:
|
20
|
+
@fact_list[:available_bytes] = @fact_list[:total_bytes] - @fact_list[:used_bytes]
|
21
21
|
@fact_list[:capacity] = Facter::Util::Resolvers::FilesystemHelper
|
22
22
|
.compute_capacity(@fact_list[:used_bytes], @fact_list[:total_bytes])
|
23
23
|
|
24
24
|
@fact_list[fact_name]
|
25
25
|
end
|
26
26
|
|
27
|
-
def
|
28
|
-
|
29
|
-
|
30
|
-
|
27
|
+
def pagesize
|
28
|
+
@pagesize ||= Facter::Freebsd::FfiHelper.sysctl_by_name(:long, 'vm.stats.vm.v_page_size')
|
29
|
+
end
|
30
|
+
|
31
|
+
def read_used_memory_in_bytes
|
32
|
+
require_relative 'ffi/ffi_helper'
|
33
|
+
|
34
|
+
@fact_list[:used_bytes] = pagesize * (
|
35
|
+
Facter::Freebsd::FfiHelper.sysctl_by_name(:long, 'vm.stats.vm.v_active_count') +
|
36
|
+
Facter::Freebsd::FfiHelper.sysctl_by_name(:long, 'vm.stats.vm.v_wire_count')
|
37
|
+
)
|
31
38
|
end
|
32
39
|
|
33
40
|
def read_total_memory_in_bytes
|
34
41
|
require_relative 'ffi/ffi_helper'
|
35
42
|
|
36
|
-
@fact_list[:total_bytes] =
|
43
|
+
@fact_list[:total_bytes] = pagesize *
|
44
|
+
Facter::Freebsd::FfiHelper.sysctl_by_name(:long, 'vm.stats.vm.v_page_count')
|
37
45
|
end
|
38
46
|
end
|
39
47
|
end
|
@@ -19,6 +19,12 @@ module Facter
|
|
19
19
|
|
20
20
|
matches = output.match(/System Configuration:\s+(.+?)\s+sun\d+\S+\s+(.+)/)&.captures
|
21
21
|
|
22
|
+
# There are circumstances (e.g. in non-global zones) when prtdiag
|
23
|
+
# will return text, but it's an error message or some other string
|
24
|
+
# that isn't parsed by the above match/capture. In that case, we
|
25
|
+
# simply return.
|
26
|
+
return if matches.nil?
|
27
|
+
|
22
28
|
@fact_list[:manufacturer] = matches[0]&.strip
|
23
29
|
@fact_list[:product_name] = matches[1]&.strip
|
24
30
|
|
@@ -10,7 +10,7 @@ module Facter
|
|
10
10
|
|
11
11
|
attach_function :ioctl_base, :ioctl, %i[int int pointer], :int
|
12
12
|
attach_function :open_socket, :socket, %i[int int int], :int
|
13
|
-
attach_function :close_socket, :
|
13
|
+
attach_function :close_socket, :close, %i[int], :int
|
14
14
|
attach_function :inet_ntop, %i[int pointer pointer uint], :string
|
15
15
|
|
16
16
|
def self.ioctl(call_const, pointer, address_family = AF_INET)
|
@@ -18,7 +18,7 @@ module Facter
|
|
18
18
|
begin
|
19
19
|
ioctl_base(fd, call_const, pointer)
|
20
20
|
ensure
|
21
|
-
Ioctl.close_socket(fd
|
21
|
+
Ioctl.close_socket(fd)
|
22
22
|
end
|
23
23
|
end
|
24
24
|
end
|
@@ -79,7 +79,7 @@ module Facter
|
|
79
79
|
ioctl = FFI::Ioctl.ioctl(FFI::SIOCGLIFMTU, lifreq, lifreq.ss_family)
|
80
80
|
|
81
81
|
if ioctl == -1
|
82
|
-
@log.error("
|
82
|
+
@log.error("Could not read MTU, error code is: #{::FFI::LastError.error}")
|
83
83
|
return
|
84
84
|
end
|
85
85
|
|
@@ -28,12 +28,16 @@ module Facter
|
|
28
28
|
def build_fact_list(output)
|
29
29
|
uname_results = output.split("\n")
|
30
30
|
|
31
|
-
|
32
|
-
|
33
|
-
|
34
|
-
|
35
|
-
|
36
|
-
|
31
|
+
if !uname_results.empty?
|
32
|
+
@fact_list[:machine],
|
33
|
+
@fact_list[:nodename],
|
34
|
+
@fact_list[:processor],
|
35
|
+
@fact_list[:kernelrelease],
|
36
|
+
@fact_list[:kernelname],
|
37
|
+
@fact_list[:kernelversion] = uname_results.map(&:strip)
|
38
|
+
else
|
39
|
+
log.warn('Request to uname returned no output. Uname related facts are not populated.')
|
40
|
+
end
|
37
41
|
end
|
38
42
|
end
|
39
43
|
end
|
data/lib/facter/resolvers/xen.rb
CHANGED
@@ -26,7 +26,9 @@ module Facter
|
|
26
26
|
|
27
27
|
def detect_xen_type
|
28
28
|
xen_type = 'xen0' if File.exist?('/dev/xen/evtchn')
|
29
|
-
|
29
|
+
if !xen_type && (File.exist?('/proc/xen') || (File.exist?('/dev/xvda1') && !File.symlink?('/dev/xvda1')))
|
30
|
+
xen_type = 'xenu'
|
31
|
+
end
|
30
32
|
|
31
33
|
xen_type
|
32
34
|
end
|
@@ -61,7 +61,7 @@ module Facter
|
|
61
61
|
properties = PROPERTIES[cmd]
|
62
62
|
properties.each do |property|
|
63
63
|
str = (properties - [property]).join('|')
|
64
|
-
matcher = content.match(
|
64
|
+
matcher = content.match(/(?:^|^[^:]+:[^:]+)#{Regexp.escape(property)}([^\n]*?)(#{str}|\n|$)/s)
|
65
65
|
if matcher
|
66
66
|
value = matcher[1].strip
|
67
67
|
property_hash[property.split(':').first] = value
|
@@ -25,7 +25,7 @@ module Facter
|
|
25
25
|
os
|
26
26
|
end
|
27
27
|
|
28
|
-
def release_hash_from_string(output)
|
28
|
+
def release_hash_from_string(output, include_patch: false)
|
29
29
|
return unless output
|
30
30
|
|
31
31
|
versions = output.split('.')
|
@@ -33,6 +33,7 @@ module Facter
|
|
33
33
|
release['full'] = output
|
34
34
|
release['major'] = versions[0]
|
35
35
|
release['minor'] = versions[1] if versions[1]
|
36
|
+
release['patch'] = versions[2] if versions[2] && include_patch
|
36
37
|
end
|
37
38
|
end
|
38
39
|
|
@@ -57,6 +57,8 @@ module Facter
|
|
57
57
|
end
|
58
58
|
|
59
59
|
def find_in_interfaces(interfaces)
|
60
|
+
return if interfaces.nil?
|
61
|
+
|
60
62
|
interfaces.each do |iface_name, interface|
|
61
63
|
interface[:bindings]&.each do |binding|
|
62
64
|
return iface_name unless Facter::Util::Resolvers::Networking.ignored_ip_address(binding[:address])
|
@@ -3,15 +3,7 @@
|
|
3
3
|
module Facter
|
4
4
|
module Util
|
5
5
|
module Resolvers
|
6
|
-
|
7
|
-
attr_accessor :fingerprint, :type, :key, :name
|
8
|
-
def initialize(fingerprint, type, key, name)
|
9
|
-
@fingerprint = fingerprint
|
10
|
-
@type = type
|
11
|
-
@key = key
|
12
|
-
@name = name
|
13
|
-
end
|
14
|
-
end
|
6
|
+
Ssh = Struct.new(:fingerprint, :type, :key, :name)
|
15
7
|
end
|
16
8
|
end
|
17
9
|
end
|
@@ -9,6 +9,7 @@ module Facter
|
|
9
9
|
class SshHelper
|
10
10
|
class << self
|
11
11
|
SSH_NAME = { 'ssh-dss' => 'dsa', 'ecdsa-sha2-nistp256' => 'ecdsa',
|
12
|
+
'ecdsa-sha2-nistp384' => 'ecdsa', 'ecdsa-sha2-nistp521' => 'ecdsa',
|
12
13
|
'ssh-ed25519' => 'ed25519', 'ssh-rsa' => 'rsa' }.freeze
|
13
14
|
SSH_FINGERPRINT = { 'rsa' => 1, 'dsa' => 2, 'ecdsa' => 3, 'ed25519' => 4 }.freeze
|
14
15
|
|
@@ -16,6 +17,7 @@ module Facter
|
|
16
17
|
key_name = SSH_NAME[key_type]
|
17
18
|
return unless key_name
|
18
19
|
|
20
|
+
# decode64 ignores non-base64 characters including newlines
|
19
21
|
decoded_key = Base64.decode64(key)
|
20
22
|
ssh_fp = SSH_FINGERPRINT[key_name]
|
21
23
|
sha1 = "SSHFP #{ssh_fp} 1 #{Digest::SHA1.new.update(decoded_key)}"
|
data/lib/facter/version.rb
CHANGED
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.5.
|
4
|
+
version: 4.5.2
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Puppet
|
8
8
|
autorequire:
|
9
9
|
bindir: bin
|
10
10
|
cert_chain: []
|
11
|
-
date:
|
11
|
+
date: 2024-01-15 00:00:00.000000000 Z
|
12
12
|
dependencies:
|
13
13
|
- !ruby/object:Gem::Dependency
|
14
14
|
name: ffi
|
@@ -934,6 +934,7 @@ files:
|
|
934
934
|
- lib/facter/resolvers/aix/partitions.rb
|
935
935
|
- lib/facter/resolvers/aix/processors.rb
|
936
936
|
- lib/facter/resolvers/aix/serialnumber.rb
|
937
|
+
- lib/facter/resolvers/amzn/os_release_rpm.rb
|
937
938
|
- lib/facter/resolvers/augeas.rb
|
938
939
|
- lib/facter/resolvers/az.rb
|
939
940
|
- lib/facter/resolvers/base_resolver.rb
|
@@ -1101,7 +1102,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
|
|
1101
1102
|
- !ruby/object:Gem::Version
|
1102
1103
|
version: '0'
|
1103
1104
|
requirements: []
|
1104
|
-
rubygems_version: 3.4.
|
1105
|
+
rubygems_version: 3.4.20
|
1105
1106
|
signing_key:
|
1106
1107
|
specification_version: 4
|
1107
1108
|
summary: Facter, a system inventory tool
|